-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: Add users collection pagination (#636)
* Feat: Add users collection pagination Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Update: Paginate using the db query Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Refactor: cast page to Number Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Test: findAll query Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Fix: Add conditional use of `.env.test` file Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Delete: hal.collection test Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Feat: first and last page links Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Refactor: Pass db related env var to `make test` target Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Feat: Add generic search function Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Refactor: Use `principalService.save()` for integration test Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Test: Run migrations on test database Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> * Feat: Add `getUserPageHref` util and tests Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> --------- Signed-off-by: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com>
- Loading branch information
1 parent
1c330bc
commit 737be45
Showing
7 changed files
with
242 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import { strict as assert } from 'node:assert'; | ||
import { after, before, describe, it } from 'node:test'; | ||
|
||
import { PrincipalService } from '../../src/principal/service.ts'; | ||
import { PrincipalNew } from '../../src/api-types.ts'; | ||
import * as hal from '../../src/user/formats/hal.ts'; | ||
import db, { init } from '../../src/database.ts'; | ||
import { User } from '../../src/types.ts'; | ||
import { HalResource } from 'hal-types'; | ||
|
||
describe('users pagination', () => { | ||
const principalService = new PrincipalService('insecure'); | ||
let users: User[] = []; | ||
|
||
before(async () => { | ||
await init(); | ||
|
||
const hasTable = await db.schema.hasTable('principals'); | ||
if (!hasTable) { | ||
await db.schema.createTable('principals', (table) => { | ||
table.increments('id').primary(); | ||
table.string('identity').nullable(); | ||
table.string('external_id').notNullable(); | ||
table.string('nickname').notNullable(); | ||
table.integer('type').notNullable(); | ||
table.bigInteger('created_at').defaultTo(db.fn.now()); | ||
table.bigInteger('modified_at').defaultTo(db.fn.now()); | ||
table.boolean('active').notNullable().defaultTo(false); | ||
table.tinyint('system').notNullable().defaultTo(0); | ||
}); | ||
} | ||
|
||
// 3 pages worth of users | ||
for(let i = 1; i < 251; i++){ | ||
const data = { | ||
type: 'user' as PrincipalNew['type'], | ||
nickname: `User ${i}`, | ||
createdAt: new Date(Date.now()), | ||
modifiedAt: new Date(Date.now()), | ||
active: true, | ||
}; | ||
await principalService.save(data); | ||
} | ||
|
||
users = await principalService.findAll('user'); | ||
}); | ||
|
||
after(async () => { | ||
await db.destroy(); | ||
}); | ||
|
||
describe('search service', () => { | ||
it('should display first page', async () => { | ||
const currentPage = 1; | ||
const { items, pageSize, page, hasNextPage, total } = await principalService.search('user', currentPage); | ||
const expectedUsers = users.slice(0, pageSize); | ||
|
||
assert.equal(page, currentPage); | ||
assert.equal(hasNextPage, true); | ||
assert.equal(total, users.length); | ||
assert.deepEqual(items, expectedUsers); | ||
}); | ||
|
||
it('should display second page', async () => { | ||
const currentPage = 2; | ||
const { items, pageSize, page, hasNextPage, total } = await principalService.search('user', currentPage); | ||
const expectedUsers = users.slice(pageSize, 200); | ||
|
||
assert.equal(page, currentPage); | ||
assert.equal(hasNextPage, true); | ||
assert.equal(total, users.length); | ||
assert.deepEqual(items, expectedUsers); | ||
}); | ||
|
||
it('should display last (third) page', async () => { | ||
const currentPage = 3; | ||
const { items, page, hasNextPage, total } = await principalService.search('user', currentPage); | ||
const expectedUsers = users.slice(200, users.length); | ||
|
||
assert.equal(page, currentPage); | ||
assert.equal(hasNextPage, false); | ||
assert.equal(total, users.length); | ||
assert.deepEqual(items, expectedUsers); | ||
}); | ||
}); | ||
|
||
describe('hal.collection links', () => { | ||
const embeddedUsers: HalResource[] = []; | ||
|
||
it('should not display `previous` link on first page', async () => { | ||
const currentPage = 1; | ||
|
||
const paginatedResult = await principalService.search<User>('user', currentPage); | ||
const halRes = hal.collection(paginatedResult, embeddedUsers); | ||
|
||
assert.equal(halRes._links.previous, undefined); | ||
assert.deepEqual(halRes._links.self, { | ||
href: '/user', | ||
}); | ||
assert.deepEqual(halRes._links.first, { | ||
href: '/user', | ||
}); | ||
assert.deepEqual(halRes._links.last, { | ||
href: '/user?page=3', | ||
}); | ||
assert.deepEqual(halRes._links.next, { | ||
href: '/user?page=2', | ||
}); | ||
}); | ||
|
||
it('should not display `next` link on last page', async () => { | ||
const currentPage = 3; | ||
|
||
const paginatedResult = await principalService.search<User>('user', currentPage); | ||
const halRes = hal.collection(paginatedResult, embeddedUsers); | ||
|
||
assert.equal(halRes._links.next, undefined); | ||
assert.deepEqual(halRes._links.self, { | ||
href: '/user?page=3', | ||
}); | ||
assert.deepEqual(halRes._links.first, { | ||
href: '/user', | ||
}); | ||
assert.deepEqual(halRes._links.last, { | ||
href: '/user?page=3', | ||
}); | ||
assert.deepEqual(halRes._links.previous, { | ||
href: '/user?page=2', | ||
}); | ||
}); | ||
|
||
it('should display both `previous` & `next` links on middle page', async () => { | ||
const currentPage = 2; | ||
|
||
const paginatedResult = await principalService.search<User>('user', currentPage); | ||
const halRes = hal.collection(paginatedResult, embeddedUsers); | ||
|
||
assert.deepEqual(halRes._links.self, { | ||
href: '/user?page=2', | ||
}); | ||
assert.deepEqual(halRes._links.first, { | ||
href: '/user', | ||
}); | ||
assert.deepEqual(halRes._links.last, { | ||
href: '/user?page=3', | ||
}); | ||
assert.deepEqual(halRes._links.previous, { | ||
href: '/user', | ||
}); | ||
assert.deepEqual(halRes._links.next, { | ||
href: '/user?page=3', | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ | |
"DOM", | ||
"ES2022" | ||
], | ||
"declaration": true | ||
"declaration": true, | ||
}, | ||
"include": [ | ||
"src/**/*" | ||
|