[core] API limit, offset, sort
This commit is contained in:
1 parent
35628aa93b
commit
f16ace6169
23 files changed
+18627
-93
No files matched your search
@@ -72,4 +72,5 @@ repos:
|
||||
entrypoint.sh|
|
||||
.pre-commit-config.yaml|
|
||||
^src/core/engines/webjs/session.webjs.core.ts|
|
||||
^src/core/engines/webjs/_lodash.js|
|
||||
)$
|
||||
@@ -17,11 +17,15 @@ import {
|
||||
SessionApiParam,
|
||||
WorkingSessionParam,
|
||||
} from '@waha/nestjs/params/SessionApiParam';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { parseBool } from '../helpers';
|
||||
import { GetChatMessagesQuery, GetChatsQuery } from '../structures/chats.dto';
|
||||
import {
|
||||
ChatsPaginationParams,
|
||||
GetChatMessagesQuery,
|
||||
} from '../structures/chats.dto';
|
||||
import { EditMessageRequest } from '../structures/chatting.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@@ -36,9 +40,9 @@ class ChatsController {
|
||||
@ApiOperation({ summary: 'Get chats' })
|
||||
getChats(
|
||||
@WorkingSessionParam session: WhatsappSession,
|
||||
@Query() query: GetChatsQuery,
|
||||
@Query() pagination: ChatsPaginationParams,
|
||||
) {
|
||||
return session.getChats(query);
|
||||
return session.getChats(pagination);
|
||||
}
|
||||
|
||||
@Delete(':chatId')
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Query,
|
||||
UsePipes,
|
||||
ValidationPipe,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
@@ -7,7 +15,11 @@ import {
|
||||
CheckNumberStatusQuery,
|
||||
WANumberExistResult,
|
||||
} from '../structures/chatting.dto';
|
||||
import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
|
||||
import {
|
||||
ContactQuery,
|
||||
ContactRequest,
|
||||
ContactsPaginationParams,
|
||||
} from '../structures/contacts.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/contacts')
|
||||
@@ -15,6 +27,17 @@ import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
|
||||
export class ContactsController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('/all')
|
||||
@ApiOperation({ summary: 'Get all contacts' })
|
||||
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
async getAll(
|
||||
@Query() query: SessionQuery,
|
||||
@Query() pagination: ContactsPaginationParams,
|
||||
) {
|
||||
const whatsapp = await this.manager.getWorkingSession(query.session);
|
||||
return whatsapp.getContacts(pagination);
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
@ApiOperation({
|
||||
summary: 'Get contact basic info',
|
||||
@@ -26,13 +49,6 @@ export class ContactsController {
|
||||
return whatsapp.getContact(query);
|
||||
}
|
||||
|
||||
@Get('/all')
|
||||
@ApiOperation({ summary: 'Get all contacts' })
|
||||
async getAll(@Query() query: SessionQuery) {
|
||||
const whatsapp = await this.manager.getWorkingSession(query.session);
|
||||
return whatsapp.getContacts();
|
||||
}
|
||||
|
||||
@Get('/check-exists')
|
||||
@ApiOperation({ summary: 'Check phone number is registered in WhatsApp.' })
|
||||
async checkExists(
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
CreateChannelRequest,
|
||||
ListChannelsQuery,
|
||||
} from '@waha/structures/channels.dto';
|
||||
import { GetChatsQuery } from '@waha/structures/chats.dto';
|
||||
import { SendButtonsRequest } from '@waha/structures/chatting.buttons.dto';
|
||||
import { Label, LabelID } from '@waha/structures/labels.dto';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
import { LoggerBuilder } from '@waha/utils/logging';
|
||||
import { EventEmitter } from 'events';
|
||||
@@ -319,7 +319,7 @@ export abstract class WhatsappSession {
|
||||
/**
|
||||
* Chats methods
|
||||
*/
|
||||
public getChats(query: GetChatsQuery) {
|
||||
public getChats(pagination: PaginationParams) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getContacts() {
|
||||
public getContacts(pagination: PaginationParams) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
CreateChannelRequest,
|
||||
ListChannelsQuery,
|
||||
} from '@waha/structures/channels.dto';
|
||||
import { GetChatsQuery } from '@waha/structures/chats.dto';
|
||||
import { SendButtonsRequest } from '@waha/structures/chatting.buttons.dto';
|
||||
import { ContactQuery, ContactRequest } from '@waha/structures/contacts.dto';
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
@@ -48,6 +47,7 @@ import {
|
||||
LabelID,
|
||||
} from '@waha/structures/labels.dto';
|
||||
import { ReplyToMessage } from '@waha/structures/message.dto';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import {
|
||||
PollVote,
|
||||
PollVotePayload,
|
||||
@@ -806,8 +806,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
* Chats methods
|
||||
*/
|
||||
|
||||
async getChats(query: GetChatsQuery) {
|
||||
const chats = await this.store.getChats(query.limit, query.offset);
|
||||
async getChats(pagination: PaginationParams) {
|
||||
const chats = await this.store.getChats(pagination);
|
||||
// Remove unreadCount, it's not ready yet
|
||||
chats.forEach((chat) => delete chat.unreadCount);
|
||||
return chats;
|
||||
@@ -901,8 +901,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.toWAContact(contact);
|
||||
}
|
||||
|
||||
async getContacts() {
|
||||
const contacts = await this.store.getContacts();
|
||||
async getContacts(pagination: PaginationParams) {
|
||||
const contacts = await this.store.getContacts(pagination);
|
||||
return contacts.map(this.toWAContact);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Chat } from '@adiwajshing/baileys';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
|
||||
export interface IChatRepository {
|
||||
getAll(): Promise<Chat[]>;
|
||||
|
||||
getAllByIds(ids: string[]): Promise<Chat[]>;
|
||||
|
||||
getAllWithMessages(limit?: number, offset?: number): Promise<Chat[]>;
|
||||
getAllWithMessages(pagination: PaginationParams): Promise<Chat[]>;
|
||||
|
||||
getById(id: string): Promise<Chat | null>;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Contact } from '@adiwajshing/baileys';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
|
||||
export interface IContactRepository {
|
||||
getAll(): Promise<Contact[]>;
|
||||
getAll(pagination?: PaginationParams): Promise<Contact[]>;
|
||||
|
||||
getById(id: string): Promise<Contact | null>;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
proto,
|
||||
} from '@adiwajshing/baileys';
|
||||
import { Label } from '@adiwajshing/baileys/lib/Types/Label';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
|
||||
export interface INowebStore {
|
||||
presences: any;
|
||||
@@ -19,9 +20,9 @@ export interface INowebStore {
|
||||
|
||||
getMessagesByJid(chatId: string, limit: number): Promise<any>;
|
||||
|
||||
getChats(limit?: number, offset?: number): Promise<Chat[]>;
|
||||
getChats(pagination: PaginationParams): Promise<Chat[]>;
|
||||
|
||||
getContacts(): Promise<Contact[]>;
|
||||
getContacts(pagination: PaginationParams): Promise<Contact[]>;
|
||||
|
||||
getContactById(jid: string): Promise<Contact>;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Chat, Contact, makeInMemoryStore, proto } from '@adiwajshing/baileys';
|
||||
import { Label } from '@adiwajshing/baileys/lib/Types/Label';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
|
||||
import { INowebStore } from './INowebStore';
|
||||
|
||||
@@ -41,11 +42,11 @@ export class NowebInMemoryStore implements INowebStore {
|
||||
throw new BadRequestException(this.errorMessage);
|
||||
}
|
||||
|
||||
getChats(limit?: number, offset?: number): Promise<Chat[]> {
|
||||
getChats(pagination: PaginationParams): Promise<Chat[]> {
|
||||
throw new BadRequestException(this.errorMessage);
|
||||
}
|
||||
|
||||
getContacts(): Promise<Contact[]> {
|
||||
getContacts(pagination: PaginationParams): Promise<Contact[]> {
|
||||
throw new BadRequestException(this.errorMessage);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '@adiwajshing/baileys/lib/Types/LabelAssociation';
|
||||
import { ILabelAssociationRepository } from '@waha/core/engines/noweb/store/ILabelAssociationsRepository';
|
||||
import { ILabelsRepository } from '@waha/core/engines/noweb/store/ILabelsRepository';
|
||||
import { PaginationParams, SortOrder } from '@waha/structures/pagination.dto';
|
||||
import { toNumber } from 'lodash';
|
||||
import { Logger } from 'pino';
|
||||
|
||||
@@ -326,16 +327,18 @@ export class NowebPersistentStore implements INowebStore {
|
||||
return this.messagesRepo.getAllByJid(toJID(chatId), toNumber(limit));
|
||||
}
|
||||
|
||||
getChats(limit?: number, offset?: number): Promise<Chat[]> {
|
||||
return this.chatRepo.getAllWithMessages(limit, offset);
|
||||
getChats(pagination: PaginationParams): Promise<Chat[]> {
|
||||
pagination.sortBy ||= 'conversationTimestamp';
|
||||
pagination.sortOrder ||= SortOrder.DESC;
|
||||
return this.chatRepo.getAllWithMessages(pagination);
|
||||
}
|
||||
|
||||
getContactById(jid) {
|
||||
return this.contactRepo.getById(jid);
|
||||
}
|
||||
|
||||
getContacts() {
|
||||
return this.contactRepo.getAll();
|
||||
getContacts(pagination: PaginationParams) {
|
||||
return this.contactRepo.getAll(pagination);
|
||||
}
|
||||
|
||||
getLabels(): Promise<Label[]> {
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import { Chat } from '@adiwajshing/baileys';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { KnexPaginator } from '@waha/utils/Paginator';
|
||||
|
||||
import { IChatRepository } from '../IChatRepository';
|
||||
import { NOWEBSqlite3KVRepository } from './NOWEBSqlite3KVRepository';
|
||||
|
||||
class ChatPaginator extends KnexPaginator {
|
||||
indexes = ['id', 'conversationTimestamp'];
|
||||
}
|
||||
|
||||
export class Sqlite3ChatRepository
|
||||
extends NOWEBSqlite3KVRepository<Chat>
|
||||
implements IChatRepository
|
||||
{
|
||||
async getAllWithMessages(limit?: number, offset?: number): Promise<Chat[]> {
|
||||
protected Paginator = ChatPaginator;
|
||||
|
||||
async getAllWithMessages(pagination: PaginationParams): Promise<Chat[]> {
|
||||
// Get chats with conversationTimestamp is not Null
|
||||
let query = this.select()
|
||||
.whereNotNull('conversationTimestamp')
|
||||
.orderBy('conversationTimestamp', 'desc');
|
||||
if (limit != null) {
|
||||
query = query.limit(limit);
|
||||
}
|
||||
if (offset != null) {
|
||||
query = query.offset(offset);
|
||||
}
|
||||
let query = this.select().whereNotNull('conversationTimestamp');
|
||||
query = this.pagination(query, pagination);
|
||||
return await this.all(query);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
import { Contact } from '@adiwajshing/baileys';
|
||||
import { KnexPaginator } from '@waha/utils/Paginator';
|
||||
|
||||
import { IContactRepository } from '../IContactRepository';
|
||||
import { NOWEBSqlite3KVRepository } from './NOWEBSqlite3KVRepository';
|
||||
|
||||
class ContactPaginator extends KnexPaginator {
|
||||
indexes = ['id'];
|
||||
}
|
||||
|
||||
export class Sqlite3ContactRepository
|
||||
extends NOWEBSqlite3KVRepository<Contact>
|
||||
implements IContactRepository {}
|
||||
implements IContactRepository
|
||||
{
|
||||
protected Paginator = ContactPaginator;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
exports.LoadUtils = () => {
|
||||
window.WAHA = {};
|
||||
|
||||
window.WAHA.getChats = async (limit, offset) => {
|
||||
let chats = window.Store.Chat.getModelsArray();
|
||||
if (limit || offset) {
|
||||
offset = offset || 0;
|
||||
limit = limit || Infinity;
|
||||
chats = chats.slice(offset, offset + limit);
|
||||
}
|
||||
|
||||
const chatPromises = chats.map((chat) => window.WWebJS.getChatModel(chat));
|
||||
return await Promise.all(chatPromises);
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,15 @@
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import * as lodash from 'lodash';
|
||||
import { Client } from 'whatsapp-web.js';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { LoadUtils } = require('./WahaInjected');
|
||||
const { LoadWAHA } = require('./_WAHA.js');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { LoadLodash } = require('./_lodash.js');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { LoadPaginator } = require('./_Paginator.js');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const ChatFactory = require('whatsapp-web.js/src/factories/ChatFactory');
|
||||
@@ -12,26 +20,26 @@ export class WebjsClient extends Client {
|
||||
// We can still call and override it
|
||||
// @ts-ignore
|
||||
await super.inject();
|
||||
//Load util functions (serializers, helper functions)
|
||||
await this.pupPage.evaluate(LoadUtils);
|
||||
|
||||
// Load util functions (serializers, helper functions)
|
||||
await this.pupPage.evaluate(LoadLodash);
|
||||
await this.pupPage.evaluate(LoadPaginator);
|
||||
await this.pupPage.evaluate(LoadWAHA);
|
||||
}
|
||||
|
||||
async getChats(limit?: number, offset?: number) {
|
||||
if (limit == null && offset == null) {
|
||||
async getChats(pagination?: PaginationParams) {
|
||||
if (lodash.isEmpty(pagination)) {
|
||||
return await super.getChats();
|
||||
}
|
||||
return await this.getChats2(limit, offset);
|
||||
}
|
||||
|
||||
private async getChats2(limit?: number, offset?: number) {
|
||||
const chats = await this.pupPage.evaluate(
|
||||
async (limit, offset) => {
|
||||
// @ts-ignore
|
||||
return await window.WAHA.getChats(limit, offset);
|
||||
},
|
||||
limit,
|
||||
offset,
|
||||
);
|
||||
// Get paginated chats
|
||||
pagination.limit ||= Infinity;
|
||||
pagination.offset ||= 0;
|
||||
|
||||
const chats = await this.pupPage.evaluate(async (pagination) => {
|
||||
// @ts-ignore
|
||||
return await window.WAHA.getChats(pagination);
|
||||
}, pagination);
|
||||
|
||||
return chats.map((chat) => ChatFactory.create(this, chat));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
exports.LoadPaginator = () => {
|
||||
class Paginator {
|
||||
constructor(pagination) {
|
||||
this.pagination = pagination;
|
||||
}
|
||||
|
||||
apply(data) {
|
||||
if (window.lodash.isEmpty(this.pagination)) {
|
||||
return data;
|
||||
}
|
||||
return this.limit(this.sort(data));
|
||||
}
|
||||
|
||||
sort(data) {
|
||||
if (!this.pagination?.sortBy) {
|
||||
return data;
|
||||
}
|
||||
return window.lodash.orderBy(
|
||||
data,
|
||||
[this.pagination.sortBy],
|
||||
[this.pagination.sortOrder || 'asc'],
|
||||
);
|
||||
}
|
||||
|
||||
limit(data) {
|
||||
if (!this.pagination?.limit) {
|
||||
return data;
|
||||
}
|
||||
const offset = this.pagination.offset || 0;
|
||||
const limit = this.pagination.limit || Infinity;
|
||||
return data.slice(offset, offset + limit);
|
||||
}
|
||||
}
|
||||
window.Paginator = Paginator;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
exports.LoadWAHA = () => {
|
||||
window.WAHA = {};
|
||||
|
||||
window.WAHA.getChats = async (pagination) => {
|
||||
let chats = window.Store.Chat.getModelsArray().slice();
|
||||
const paginator = new window.Paginator(pagination);
|
||||
chats = paginator.apply(chats);
|
||||
const chatPromises = chats.map((chat) => window.WWebJS.getChatModel(chat));
|
||||
return await Promise.all(chatPromises);
|
||||
};
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -19,7 +19,7 @@ import {
|
||||
CreateChannelRequest,
|
||||
ListChannelsQuery,
|
||||
} from '@waha/structures/channels.dto';
|
||||
import { ChatArchiveEvent, GetChatsQuery } from '@waha/structures/chats.dto';
|
||||
import { ChatArchiveEvent, ChatSortField } from '@waha/structures/chats.dto';
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
@@ -54,9 +54,11 @@ import {
|
||||
} from '@waha/structures/groups.dto';
|
||||
import { Label, LabelID } from '@waha/structures/labels.dto';
|
||||
import { ReplyToMessage } from '@waha/structures/message.dto';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
|
||||
import { MeInfo } from '@waha/structures/sessions.dto';
|
||||
import { WAMessageRevokedBody } from '@waha/structures/webhooks.dto';
|
||||
import { PaginatorInMemory } from '@waha/utils/Paginator';
|
||||
import { waitUntil } from '@waha/utils/promiseTimeout';
|
||||
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
|
||||
import {
|
||||
@@ -525,8 +527,16 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
/**
|
||||
* Chats methods
|
||||
*/
|
||||
getChats(query: GetChatsQuery) {
|
||||
return this.whatsapp.getChats(query.limit, query.offset);
|
||||
getChats(pagination: PaginationParams) {
|
||||
switch (pagination.sortBy) {
|
||||
case ChatSortField.ID:
|
||||
pagination.sortBy = 'id._serialized';
|
||||
break;
|
||||
case ChatSortField.CONVERSATION_TIMESTAMP:
|
||||
pagination.sortBy = 't';
|
||||
break;
|
||||
}
|
||||
return this.whatsapp.getChats(pagination);
|
||||
}
|
||||
|
||||
async getChatMessages(chatId: string, limit: number, downloadMedia: boolean) {
|
||||
@@ -616,10 +626,11 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
.then(this.toWAContact);
|
||||
}
|
||||
|
||||
getContacts() {
|
||||
return this.whatsapp
|
||||
.getContacts()
|
||||
.then((contacts) => contacts.map(this.toWAContact));
|
||||
async getContacts(pagination: PaginationParams) {
|
||||
const contactsWEBJS = await this.whatsapp.getContacts();
|
||||
const contacts = contactsWEBJS.map(this.toWAContact);
|
||||
const paginator = new PaginatorInMemory(pagination);
|
||||
return paginator.apply(contacts);
|
||||
}
|
||||
|
||||
public async getContactAbout(query: ContactQuery) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { KnexPaginator } from '@waha/utils/Paginator';
|
||||
import { Database } from 'better-sqlite3';
|
||||
import Knex from 'knex';
|
||||
import Knex, { QueryBuilder } from 'knex';
|
||||
|
||||
import { Field, Schema } from './Schema';
|
||||
|
||||
@@ -15,6 +17,8 @@ export class Sqlite3KVRepository<Entity> {
|
||||
private readonly columns: Field[];
|
||||
private knex: Knex.Knex;
|
||||
|
||||
protected Paginator = KnexPaginator;
|
||||
|
||||
constructor(
|
||||
db: Database,
|
||||
schema: Schema,
|
||||
@@ -29,8 +33,10 @@ export class Sqlite3KVRepository<Entity> {
|
||||
this.knex = Knex({ client: 'better-sqlite3', useNullAsDefault: true });
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return this.all(this.select());
|
||||
getAll(pagination?: PaginationParams) {
|
||||
let query = this.select();
|
||||
query = this.pagination(query, pagination);
|
||||
return this.all(query);
|
||||
}
|
||||
|
||||
getAllByIds(ids: string[]) {
|
||||
@@ -163,4 +169,9 @@ export class Sqlite3KVRepository<Entity> {
|
||||
protected parse(row: any) {
|
||||
return JSON.parse(row.data);
|
||||
}
|
||||
|
||||
protected pagination(query: any, pagination?: PaginationParams) {
|
||||
const paginator = new this.Paginator(pagination);
|
||||
return paginator.apply(query);
|
||||
}
|
||||
}
|
||||
+15
-10
@@ -1,7 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { ChatIdProperty } from '@waha/structures/properties.dto';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsNumber, IsOptional } from 'class-validator';
|
||||
import { getEngineName } from '@waha/version';
|
||||
import { IsEnum, IsNumber, IsOptional } from 'class-validator';
|
||||
|
||||
import { SessionQuery } from './base.dto';
|
||||
|
||||
@@ -20,16 +21,20 @@ export class GetChatMessagesQuery extends SessionQuery {
|
||||
downloadMedia: boolean = true;
|
||||
}
|
||||
|
||||
export class GetChatsQuery {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit?: number;
|
||||
export enum ChatSortField {
|
||||
CONVERSATION_TIMESTAMP = 'conversationTimestamp',
|
||||
ID = 'id',
|
||||
NAME = 'name',
|
||||
}
|
||||
|
||||
@IsNumber()
|
||||
export class ChatsPaginationParams extends PaginationParams {
|
||||
@ApiProperty({
|
||||
description: 'Sort by field',
|
||||
enum: ChatSortField,
|
||||
})
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset?: number;
|
||||
@IsEnum(ChatSortField)
|
||||
sortBy?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { ChatIdProperty } from '@waha/structures/properties.dto';
|
||||
import { IsString } from 'class-validator';
|
||||
import { IsEnum, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
import { SessionBaseRequest, SessionQuery } from './base.dto';
|
||||
|
||||
@@ -13,6 +15,21 @@ export class ContactQuery extends SessionQuery {
|
||||
contactId: string;
|
||||
}
|
||||
|
||||
enum ContactSortField {
|
||||
ID = 'id',
|
||||
NAME = 'name',
|
||||
}
|
||||
|
||||
export class ContactsPaginationParams extends PaginationParams {
|
||||
@ApiProperty({
|
||||
description: 'Sort by field',
|
||||
enum: ContactSortField,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsEnum(ContactSortField)
|
||||
sortBy?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsNumber, IsOptional, ValidateIf } from 'class-validator';
|
||||
|
||||
export enum SortOrder {
|
||||
DESC = 'desc',
|
||||
ASC = 'asc',
|
||||
}
|
||||
|
||||
export class PaginationParams {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit?: number;
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset?: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Sort by field',
|
||||
})
|
||||
@IsOptional()
|
||||
sortBy?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Sort order - <b>desc</b>ending (Z => A, New first) or <b>asc</b>ending (A => Z, Old first)',
|
||||
})
|
||||
@IsOptional()
|
||||
sortOrder?: SortOrder;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
export abstract class Paginator {
|
||||
constructor(protected readonly pagination: PaginationParams) {}
|
||||
|
||||
apply<T>(data: T): T {
|
||||
if (lodash.isEmpty(this.pagination)) {
|
||||
return data;
|
||||
}
|
||||
return this.limit(this.sort(data));
|
||||
}
|
||||
|
||||
protected abstract sort(data: any);
|
||||
|
||||
protected abstract limit(data: any);
|
||||
}
|
||||
|
||||
export class PaginatorInMemory extends Paginator {
|
||||
protected sort(data: any[]) {
|
||||
if (!this.pagination?.sortBy) {
|
||||
return data;
|
||||
}
|
||||
return lodash.orderBy(
|
||||
data,
|
||||
this.pagination.sortBy,
|
||||
this.pagination.sortOrder || 'asc',
|
||||
);
|
||||
}
|
||||
|
||||
protected limit(data: any[]) {
|
||||
if (!this.pagination?.limit) {
|
||||
return data;
|
||||
}
|
||||
const offset = this.pagination.offset || 0;
|
||||
const limit = this.pagination.limit || Infinity;
|
||||
return data.slice(offset, offset + limit);
|
||||
}
|
||||
}
|
||||
|
||||
export class KnexPaginator extends Paginator {
|
||||
indexes: string[] = [];
|
||||
dataField: string = 'data';
|
||||
|
||||
protected sort(query: any) {
|
||||
if (!this.pagination?.sortBy) {
|
||||
return query;
|
||||
}
|
||||
const sortBy = this.pagination.sortBy;
|
||||
const direction = this.pagination.sortOrder || 'asc';
|
||||
if (this.indexes.includes(sortBy)) {
|
||||
return query.orderBy(sortBy, direction);
|
||||
}
|
||||
// Use data->"$.{field}"
|
||||
// Make sure to sanitize sortBy to prevent SQL injection
|
||||
// sqlite3 doesn't support binding for column names and direction
|
||||
return query.orderByRaw(`${this.dataField}->'$.${sortBy}' ${direction}`);
|
||||
}
|
||||
|
||||
protected limit(query: any) {
|
||||
const limit = this.pagination.limit;
|
||||
const offset = this.pagination.offset;
|
||||
if (limit != null) {
|
||||
query = query.limit(limit);
|
||||
}
|
||||
if (offset != null) {
|
||||
query = query.offset(offset);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
export class MongoPaginator extends Paginator {
|
||||
protected sort(query: any) {
|
||||
if (!this.pagination?.sortBy) {
|
||||
return query;
|
||||
}
|
||||
const mongoDirection = this.pagination.sortOrder === 'asc' ? 1 : -1;
|
||||
return query.sort({ [this.pagination.sortBy]: mongoDirection });
|
||||
}
|
||||
|
||||
protected limit(query: any) {
|
||||
const limit = this.pagination.limit;
|
||||
const offset = this.pagination.offset;
|
||||
if (limit != null) {
|
||||
query = query.limit(limit);
|
||||
}
|
||||
if (offset != null) {
|
||||
query = query.skip(offset);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user