nestjs-paginate
Version:
Pagination and filtering helper method for TypeORM repositories or query builders using Nest.js framework.
321 lines • 16.3 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("@nestjs/common");
const swagger_1 = require("@nestjs/swagger");
const paginate_1 = require("../paginate");
const testing_1 = require("@nestjs/testing");
const api_paginated_swagger_docs_decorator_1 = require("./api-paginated-swagger-docs.decorator");
const api_paginated_query_decorator_1 = require("./api-paginated-query.decorator");
const api_ok_paginated_response_decorator_1 = require("./api-ok-paginated-response.decorator");
const fs = require("node:fs");
const path = require("node:path");
const BASE_PAGINATION_CONFIG = {
sortableColumns: ['id'],
};
const FULL_CONFIG = Object.assign(Object.assign({}, BASE_PAGINATION_CONFIG), { defaultSortBy: [['id', 'DESC']], defaultLimit: 20, maxLimit: 100, filterableColumns: {
id: true,
name: [paginate_1.FilterOperator.EQ, paginate_1.FilterSuffix.NOT],
}, searchableColumns: ['name'], select: ['id', 'name'] });
class TestDto {
}
// eslint-disable-next-line @typescript-eslint/ban-types
async function getSwaggerDefinitionForEndpoint(entityType, config) {
class TestController {
test() {
//
}
testReferenced() {
//
}
testPost() {
//
}
}
__decorate([
(0, api_paginated_swagger_docs_decorator_1.PaginatedSwaggerDocs)(entityType, config),
(0, common_1.Get)('/test'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], TestController.prototype, "test", null);
__decorate([
(0, api_paginated_query_decorator_1.ApiPaginationQuery)(config),
(0, api_paginated_swagger_docs_decorator_1.PaginatedSwaggerDocs)('TestDto', config),
(0, common_1.Get)('/test-referenced'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], TestController.prototype, "testReferenced", null);
__decorate([
(0, api_paginated_query_decorator_1.ApiPaginationQuery)(config),
(0, api_ok_paginated_response_decorator_1.ApiOkPaginatedResponse)(entityType, config),
(0, common_1.Post)('/test'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], TestController.prototype, "testPost", null);
const fakeAppModule = await testing_1.Test.createTestingModule({
controllers: [TestController],
}).compile();
const fakeApp = fakeAppModule.createNestApplication();
return swagger_1.SwaggerModule.createDocument(fakeApp, new swagger_1.DocumentBuilder().build());
}
describe('PaginatedEndpoint decorator', () => {
it('post and get definition should be the same', async () => {
const openApiDefinition = await getSwaggerDefinitionForEndpoint(TestDto, BASE_PAGINATION_CONFIG);
const testGetParams = openApiDefinition.paths['/test'].get.parameters;
expect(openApiDefinition.paths['/test-referenced'].get.parameters).toStrictEqual(testGetParams);
expect(openApiDefinition.paths['/test'].post.parameters).toStrictEqual(testGetParams);
});
it.each(['/test', '/test-referenced'])('should annotate endpoint with OpenApi documentation with limited config', async (endpoint) => {
const openApiDefinition = await getSwaggerDefinitionForEndpoint(TestDto, BASE_PAGINATION_CONFIG);
const params = openApiDefinition.paths[endpoint].get.parameters;
expect(params).toStrictEqual([
{
name: 'page',
required: false,
in: 'query',
description: 'Page number to retrieve. If you provide invalid value the default page number will applied\n\n**Example:** 1\n\n\n**Default Value:** 1\n\n',
schema: {
type: 'number',
},
},
{
name: 'limit',
required: false,
in: 'query',
description: 'Number of records per page.\n\n\n**Example:** 20\n\n\n\n**Default Value:** 20\n\n\n\n**Max Value:** 100\n\n\nIf provided value is greater than max value, max value will be applied.\n',
schema: {
type: 'number',
},
},
{
name: 'sortBy',
required: false,
in: 'query',
description: 'Parameter to sort by.\nTo sort by multiple fields, just provide query param multiple types. The order in url defines an order of sorting\n\n**Format:** {fieldName}:{DIRECTION}\n\n\n**Example:** sortBy=id:DESC\n\n\n**Default Value:** No default sorting specified, the result order is not guaranteed if not provided\n\n**Available Fields**\n- id\n',
schema: {
type: 'array',
items: {
type: 'string',
enum: ['id:ASC', 'id:DESC'],
},
},
},
]);
expect(openApiDefinition.paths[endpoint].get.responses).toEqual({
'200': {
description: '',
content: {
'application/json': {
schema: {
allOf: [
{
$ref: '#/components/schemas/PaginatedDocumented',
},
{
properties: {
data: {
type: 'array',
items: {
$ref: '#/components/schemas/TestDto',
},
},
meta: {
properties: {
select: {
type: 'array',
items: {
type: 'string',
},
},
filter: {
type: 'object',
properties: {},
},
},
},
},
},
],
},
},
},
},
});
});
it.each(['/test', '/test-referenced'])('should annotate endpoint with OpenApi documentation with full config', async (endpoint) => {
const openApiDefinition = await getSwaggerDefinitionForEndpoint(TestDto, FULL_CONFIG);
const params = openApiDefinition.paths[endpoint].get.parameters;
expect(params).toStrictEqual([
{
name: 'page',
required: false,
in: 'query',
description: 'Page number to retrieve. If you provide invalid value the default page number will applied\n\n**Example:** 1\n\n\n**Default Value:** 1\n\n',
schema: {
type: 'number',
},
},
{
name: 'limit',
required: false,
in: 'query',
description: 'Number of records per page.\n\n\n**Example:** 20\n\n\n\n**Default Value:** 20\n\n\n\n**Max Value:** 100\n\n\nIf provided value is greater than max value, max value will be applied.\n',
schema: {
type: 'number',
},
},
{
name: 'filter.id',
required: false,
in: 'query',
description: 'Filter by id query param.\n\n**Format:** filter.id={$not}:OPERATION:VALUE\n\n\n\n**Example:** filter.id=$btw:John Doe&filter.id=$contains:John Doe\n\n**Available Operations**\n- $eq\n\n- $gt\n\n- $gte\n\n- $in\n\n- $null\n\n- $lt\n\n- $lte\n\n- $btw\n\n- $ilike\n\n- $sw\n\n- $contains\n\n- $not\n\n- $and\n\n- $or',
schema: {
type: 'array',
items: {
type: 'string',
},
},
},
{
name: 'filter.name',
required: false,
in: 'query',
description: 'Filter by name query param.\n\n**Format:** filter.name={$not}:OPERATION:VALUE\n\n\n\n**Example:** filter.name=$eq:John Doe\n\n**Available Operations**\n- $eq\n\n- $not\n\n- $and\n\n- $or',
schema: {
type: 'array',
items: {
type: 'string',
},
},
},
{
name: 'sortBy',
required: false,
in: 'query',
description: 'Parameter to sort by.\nTo sort by multiple fields, just provide query param multiple types. The order in url defines an order of sorting\n\n**Format:** {fieldName}:{DIRECTION}\n\n\n**Example:** sortBy=id:DESC\n\n\n**Default Value:** id:DESC\n\n**Available Fields**\n- id\n',
schema: {
type: 'array',
items: {
type: 'string',
enum: ['id:ASC', 'id:DESC'],
},
},
},
{
name: 'search',
required: false,
in: 'query',
description: 'Search term to filter result values\n\n**Example:** John\n\n\n**Default Value:** No default value\n\n',
schema: {
type: 'string',
},
},
{
name: 'searchBy',
required: false,
in: 'query',
description: 'List of fields to search by term to filter result values\n\n**Example:** name\n\n\n**Default Value:** By default all fields mentioned below will be used to search by term\n\n**Available Fields**\n- name\n',
schema: {
type: 'array',
items: {
type: 'string',
},
},
},
{
name: 'select',
required: false,
in: 'query',
description: 'List of fields to select.\n\n**Example:** id,name\n\n\n**Default Value:** By default all fields returns. If you want to select only some fields, provide them in query param\n\n',
schema: {
type: 'string',
},
},
]);
expect(openApiDefinition.paths[endpoint].get.responses).toEqual({
'200': {
description: '',
content: {
'application/json': {
schema: {
allOf: [
{
$ref: '#/components/schemas/PaginatedDocumented',
},
{
properties: {
data: {
type: 'array',
items: {
$ref: '#/components/schemas/TestDto',
},
},
meta: {
properties: {
select: {
type: 'array',
items: {
type: 'string',
enum: ['id', 'name'],
},
},
filter: {
type: 'object',
properties: {
id: {
oneOf: [
{
type: 'string',
},
{
type: 'array',
items: {
type: 'string',
},
},
],
},
name: {
oneOf: [
{
type: 'string',
},
{
type: 'array',
items: {
type: 'string',
},
},
],
},
},
},
},
},
},
},
],
},
},
},
},
});
});
it('should match a base config, snapshot test for all config', async () => {
const openApiDefinition = await getSwaggerDefinitionForEndpoint(TestDto, FULL_CONFIG);
const fullOpenApiDefinition = JSON.parse(fs.readFileSync(path.join(__dirname, 'resources/full-openapi-definition.json')).toString('utf-8'));
expect(openApiDefinition).toStrictEqual(fullOpenApiDefinition);
});
});
//# sourceMappingURL=pagination-docs.spec.js.map