From c51399390828294eac903dd7b8790bd2320d69b1 Mon Sep 17 00:00:00 2001 From: albert-anderberg <46849153+albert-anderberg@users.noreply.github.com> Date: Fri, 30 Sep 2022 13:11:55 +0200 Subject: [PATCH] feat: customized links (#331) --- README.md | 15 +++++++++++ src/paginate.spec.ts | 64 ++++++++++++++++++++++++++++++++++++++++++++ src/paginate.ts | 23 +++++++++++++++- 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de27a29..49fe938 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,21 @@ const paginateConfig: PaginateConfig { * https://typeorm.io/select-query-builder#querying-deleted-rows */ withDeleted: false, + + /** + * Required: false + * Type: boolean + * Default: false + * Description: Generate relative paths in the resource links. + */ + relativePath: true, + + /** + * Required: false + * Type: string + * Description: Overrides the origin of absolute resource links if set. + */ + origin: 'http://cats.example', } ``` diff --git a/src/paginate.spec.ts b/src/paginate.spec.ts index c6d80a9..2ec90da 100644 --- a/src/paginate.spec.ts +++ b/src/paginate.spec.ts @@ -160,6 +160,70 @@ describe('paginate', () => { expect(links.last).toBe('?page=3&limit=2&sortBy=id:ASC') }) + it('should return a relative path', async () => { + const config: PaginateConfig = { + sortableColumns: ['id'], + relativePath: true, + } + + const query: PaginateQuery = { + path: 'http://localhost/cats', + page: 2, + limit: 2, + } + + const { links } = await paginate(query, catRepo, config) + + expect(links.first).toBe('/cats?page=1&limit=2&sortBy=id:ASC') + expect(links.previous).toBe('/cats?page=1&limit=2&sortBy=id:ASC') + expect(links.current).toBe('/cats?page=2&limit=2&sortBy=id:ASC') + expect(links.next).toBe('/cats?page=3&limit=2&sortBy=id:ASC') + expect(links.last).toBe('/cats?page=3&limit=2&sortBy=id:ASC') + }) + + it('should return an absolute path', async () => { + const config: PaginateConfig = { + sortableColumns: ['id'], + relativePath: false, + } + + const query: PaginateQuery = { + path: 'http://localhost/cats', + page: 2, + limit: 2, + } + + const { links } = await paginate(query, catRepo, config) + + expect(links.first).toBe('http://localhost/cats?page=1&limit=2&sortBy=id:ASC') + expect(links.previous).toBe('http://localhost/cats?page=1&limit=2&sortBy=id:ASC') + expect(links.current).toBe('http://localhost/cats?page=2&limit=2&sortBy=id:ASC') + expect(links.next).toBe('http://localhost/cats?page=3&limit=2&sortBy=id:ASC') + expect(links.last).toBe('http://localhost/cats?page=3&limit=2&sortBy=id:ASC') + }) + + it('should return an absolute path with new origin', async () => { + const config: PaginateConfig = { + sortableColumns: ['id'], + relativePath: false, + origin: 'http://cats.example', + } + + const query: PaginateQuery = { + path: 'http://localhost/cats', + page: 2, + limit: 2, + } + + const { links } = await paginate(query, catRepo, config) + + expect(links.first).toBe('http://cats.example/cats?page=1&limit=2&sortBy=id:ASC') + expect(links.previous).toBe('http://cats.example/cats?page=1&limit=2&sortBy=id:ASC') + expect(links.current).toBe('http://cats.example/cats?page=2&limit=2&sortBy=id:ASC') + expect(links.next).toBe('http://cats.example/cats?page=3&limit=2&sortBy=id:ASC') + expect(links.last).toBe('http://cats.example/cats?page=3&limit=2&sortBy=id:ASC') + }) + it('should return only current link if zero results', async () => { const config: PaginateConfig = { sortableColumns: ['id'], diff --git a/src/paginate.ts b/src/paginate.ts index 448146b..47dbaf6 100644 --- a/src/paginate.ts +++ b/src/paginate.ts @@ -55,6 +55,8 @@ export interface PaginateConfig { where?: FindOptionsWhere | FindOptionsWhere[] filterableColumns?: { [key in Column]?: FilterOperator[] } withDeleted?: boolean + relativePath?: boolean + origin?: string } export enum FilterOperator { @@ -165,7 +167,26 @@ export async function paginate( const limit = Math.min(query.limit || config.defaultLimit || 20, config.maxLimit || 100) const sortBy = [] as SortBy const searchBy: Column[] = [] - const path = query.path + let path + + const r = new RegExp('^(?:[a-z+]+:)?//', 'i') + let queryOrigin = '' + let queryPath = '' + if (r.test(query.path)) { + const url = new URL(query.path) + queryOrigin = url.origin + queryPath = url.pathname + } else { + queryPath = query.path + } + + if (config.relativePath) { + path = queryPath + } else if (config.origin) { + path = config.origin + queryPath + } else { + path = queryOrigin + queryPath + } function isEntityKey(entityColumns: Column[], column: string): column is Column { return !!entityColumns.find((c) => c === column)