diff --git a/src/modules/stats/router/decorators/api-stats-rigtch-query.decorator.ts b/src/modules/stats/router/decorators/api-stats-rigtch-query.decorator.ts index f069f323..5a11ade8 100644 --- a/src/modules/stats/router/decorators/api-stats-rigtch-query.decorator.ts +++ b/src/modules/stats/router/decorators/api-stats-rigtch-query.decorator.ts @@ -22,6 +22,12 @@ export const ApiStatsRigtchQuery = () => required: false, description: 'The amount of tracks to be returned.', }), + ApiQuery({ + name: 'offset', + type: Number, + required: false, + description: 'The offset of the items returned.', + }), ApiQuery({ name: 'measurement', type: String, diff --git a/src/modules/stats/router/dtos/stats-rigtch-query.dto.ts b/src/modules/stats/router/dtos/stats-rigtch-query.dto.ts index 7c2ff5ab..166e496f 100644 --- a/src/modules/stats/router/dtos/stats-rigtch-query.dto.ts +++ b/src/modules/stats/router/dtos/stats-rigtch-query.dto.ts @@ -22,6 +22,12 @@ export class StatsRigtchQuery { ) readonly limit?: number + @IsOptional() + @IsNumber() + @Max(100) + @Transform(({ value }: { value: number }) => (value > 100 ? 100 : value)) + readonly offset?: number + @IsOptional() @IsEnum(StatsMeasurement) readonly measurement?: StatsMeasurement diff --git a/src/modules/stats/router/stats-rigtch.controller.spec.ts b/src/modules/stats/router/stats-rigtch.controller.spec.ts index 0e6a353e..ccee3555 100644 --- a/src/modules/stats/router/stats-rigtch.controller.spec.ts +++ b/src/modules/stats/router/stats-rigtch.controller.spec.ts @@ -11,6 +11,8 @@ import { albumMock, artistMock, trackMock, userMock } from '@common/mocks' import { UsersRepository } from '@modules/users' describe('StatsRigtchController', () => { + const limit = 10 + const offset = 0 const date = new Date() let moduleRef: TestingModule @@ -64,7 +66,6 @@ describe('StatsRigtchController', () => { }) test('should get top tracks', async () => { - const limit = 10 const result = Array.from({ length: limit }, () => trackMock) getTopTracksSpy.mockResolvedValue(result) @@ -73,8 +74,9 @@ describe('StatsRigtchController', () => { await statsRigtchController.getTopTracks(userMock, { before: date, after: date, - limit: 10, + limit, measurement: StatsMeasurement.PLAYS, + offset, }) ).toEqual(result) @@ -84,6 +86,7 @@ describe('StatsRigtchController', () => { after: date, limit, measurement: StatsMeasurement.PLAYS, + offset, }, userMock ) @@ -98,7 +101,6 @@ describe('StatsRigtchController', () => { }) test('should get top artists', async () => { - const limit = 10 const result = Array.from({ length: limit }, () => artistMock) getTopArtistsSpy.mockResolvedValue(result) @@ -107,8 +109,9 @@ describe('StatsRigtchController', () => { await statsRigtchController.getTopArtists(userMock, { before: date, after: date, - limit: 10, + limit, measurement: StatsMeasurement.PLAYS, + offset, }) ).toEqual(result) @@ -117,6 +120,7 @@ describe('StatsRigtchController', () => { before: date, after: date, limit, + offset, measurement: StatsMeasurement.PLAYS, }, userMock @@ -132,7 +136,6 @@ describe('StatsRigtchController', () => { }) test('should get top albums', async () => { - const limit = 10 const result = Array.from({ length: limit }, () => albumMock) getTopAlbumsSpy.mockResolvedValue(result) @@ -141,7 +144,8 @@ describe('StatsRigtchController', () => { await statsRigtchController.getTopAlbums(userMock, { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAYS, }) ).toEqual(result) @@ -151,6 +155,7 @@ describe('StatsRigtchController', () => { before: date, after: date, limit, + offset, measurement: StatsMeasurement.PLAYS, }, userMock @@ -175,7 +180,8 @@ describe('StatsRigtchController', () => { await statsRigtchController.getTopGenres(userMock, { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAYS, }) ).toEqual(result) @@ -185,6 +191,7 @@ describe('StatsRigtchController', () => { before: date, after: date, limit, + offset, measurement: StatsMeasurement.PLAYS, }, userMock diff --git a/src/modules/stats/router/stats-rigtch.controller.ts b/src/modules/stats/router/stats-rigtch.controller.ts index e567d40c..5af5e7a4 100644 --- a/src/modules/stats/router/stats-rigtch.controller.ts +++ b/src/modules/stats/router/stats-rigtch.controller.ts @@ -54,6 +54,7 @@ export class StatsRigtchController { before = new Date(), after, limit = 10, + offset = 0, measurement = StatsMeasurement.PLAYS, }: StatsRigtchQuery ) { @@ -63,6 +64,7 @@ export class StatsRigtchController { after, limit, measurement, + offset, }, user ) @@ -85,6 +87,7 @@ export class StatsRigtchController { before = new Date(), after, limit = 10, + offset = 0, measurement = StatsMeasurement.PLAYS, }: StatsRigtchQuery ) { @@ -94,6 +97,7 @@ export class StatsRigtchController { after, limit, measurement, + offset, }, user ) @@ -116,6 +120,7 @@ export class StatsRigtchController { before = new Date(), after, limit = 10, + offset = 0, measurement = StatsMeasurement.PLAYS, }: StatsRigtchQuery ) { @@ -125,6 +130,7 @@ export class StatsRigtchController { after, limit, measurement, + offset, }, user ) @@ -147,6 +153,7 @@ export class StatsRigtchController { before = new Date(), after, limit = 10, + offset = 0, measurement = StatsMeasurement.PLAYS, }: StatsRigtchQuery ) { @@ -156,6 +163,7 @@ export class StatsRigtchController { after, limit, measurement, + offset, }, user ) diff --git a/src/modules/stats/stats-rigtch.service.spec.ts b/src/modules/stats/stats-rigtch.service.spec.ts index 70c9e23d..8b19423a 100644 --- a/src/modules/stats/stats-rigtch.service.spec.ts +++ b/src/modules/stats/stats-rigtch.service.spec.ts @@ -21,6 +21,8 @@ import type { Track } from '@modules/items/tracks' vi.mock('@common/utils') describe('StatsRigtchService', () => { + const limit = 10 + const offset = 0 const date = new Date() let moduleRef: TestingModule @@ -109,7 +111,8 @@ describe('StatsRigtchService', () => { { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAYS, }, userMock @@ -127,7 +130,10 @@ describe('StatsRigtchService', () => { date, relations ) - expect(getMostFrequentItemsSpy).toHaveBeenCalled() + expect(getMostFrequentItemsSpy).toHaveBeenCalledWith( + expect.anything(), + limit + offset + ) expect(getMostListenedItemsByDurationSpy).not.toHaveBeenCalled() }) @@ -145,7 +151,8 @@ describe('StatsRigtchService', () => { { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAY_TIME, }, userMock @@ -163,7 +170,10 @@ describe('StatsRigtchService', () => { date, relations ) - expect(getMostListenedItemsByDurationSpy).toHaveBeenCalled() + expect(getMostListenedItemsByDurationSpy).toHaveBeenCalledWith( + expect.anything(), + limit + offset + ) }) }) @@ -196,7 +206,8 @@ describe('StatsRigtchService', () => { { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAYS, }, userMock @@ -214,7 +225,10 @@ describe('StatsRigtchService', () => { date, relations ) - expect(getMostFrequentItemsSpy).toHaveBeenCalled() + expect(getMostFrequentItemsSpy).toHaveBeenCalledWith( + expect.anything(), + limit + offset + ) }) test('should get top artists with play time measurement', async () => { @@ -232,7 +246,8 @@ describe('StatsRigtchService', () => { { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAY_TIME, }, userMock @@ -250,7 +265,10 @@ describe('StatsRigtchService', () => { date, relations ) - expect(getMostListenedItemsByDurationSpy).toHaveBeenCalled() + expect(getMostListenedItemsByDurationSpy).toHaveBeenCalledWith( + expect.anything(), + limit + offset + ) }) }) @@ -285,7 +303,8 @@ describe('StatsRigtchService', () => { { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAYS, }, userMock @@ -303,7 +322,10 @@ describe('StatsRigtchService', () => { date, relations ) - expect(getMostFrequentItemsSpy).toHaveBeenCalled() + expect(getMostFrequentItemsSpy).toHaveBeenCalledWith( + expect.anything(), + limit + offset + ) }) test('should get top albums with play time measurement', async () => { @@ -320,7 +342,8 @@ describe('StatsRigtchService', () => { { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAY_TIME, }, userMock @@ -338,7 +361,10 @@ describe('StatsRigtchService', () => { date, relations ) - expect(getMostListenedItemsByDurationSpy).toHaveBeenCalled() + expect(getMostListenedItemsByDurationSpy).toHaveBeenCalledWith( + expect.anything(), + limit + offset + ) }) }) @@ -372,7 +398,8 @@ describe('StatsRigtchService', () => { { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAYS, }, userMock @@ -390,7 +417,10 @@ describe('StatsRigtchService', () => { date, relations ) - expect(getMostFrequentItemsSpy).toHaveBeenCalled() + expect(getMostFrequentItemsSpy).toHaveBeenCalledWith( + expect.anything(), + limit + offset + ) }) test('should get top genres with play time measurement', async () => { @@ -407,7 +437,8 @@ describe('StatsRigtchService', () => { { before: date, after: date, - limit: 10, + limit, + offset, measurement: StatsMeasurement.PLAY_TIME, }, userMock @@ -425,7 +456,10 @@ describe('StatsRigtchService', () => { date, relations ) - expect(getMostListenedItemsByDurationSpy).toHaveBeenCalled() + expect(getMostListenedItemsByDurationSpy).toHaveBeenCalledWith( + expect.anything(), + limit + offset + ) }) }) }) diff --git a/src/modules/stats/stats-rigtch.service.ts b/src/modules/stats/stats-rigtch.service.ts index db617445..4791107c 100644 --- a/src/modules/stats/stats-rigtch.service.ts +++ b/src/modules/stats/stats-rigtch.service.ts @@ -21,7 +21,7 @@ export class StatsRigtchService { ) {} async getTopTracks( - { before, after, limit, measurement }: Required, + { before, after, limit, measurement, offset }: Required, user: User ): Promise[]> { const historyTracks = @@ -41,8 +41,8 @@ export class StatsRigtchService { if (measurement === StatsMeasurement.PLAYS) { const mostFrequentItems = getMostFrequentItems( tracks.map(track => track.id), - limit - ) + limit + offset + ).slice(offset) return mostFrequentItems.map(({ item: id, count }) => ({ item: tracks.find(track => track.id === id)!, @@ -52,8 +52,8 @@ export class StatsRigtchService { const mostListenedTrackByDuration = getMostListenedItemsByDuration( tracks, - limit - ) + limit + offset + ).slice(offset) return mostListenedTrackByDuration.map(({ id, totalDuration }) => ({ item: tracks.find(track => track.id === id)!, @@ -62,7 +62,7 @@ export class StatsRigtchService { } async getTopArtists( - { before, after, limit, measurement }: Required, + { before, after, limit, measurement, offset }: Required, user: User ): Promise[]> { const historyTracks = @@ -82,8 +82,8 @@ export class StatsRigtchService { if (measurement === StatsMeasurement.PLAYS) { const mostFrequentItems = getMostFrequentItems( artists.map(artist => artist.id), - limit - ) + limit + offset + ).slice(offset) return mostFrequentItems.map(({ item: id, count }) => ({ item: artists.find(artist => artist.id === id)!, @@ -95,8 +95,8 @@ export class StatsRigtchService { tracks.flatMap(({ artists, duration }) => artists.map(({ id }) => ({ id, duration })) ), - limit - ) + limit + offset + ).slice(offset) return mostListenedArtistByDuration.map(({ id, totalDuration }) => ({ item: artists.find(artist => artist.id === id)!, @@ -105,7 +105,7 @@ export class StatsRigtchService { } async getTopAlbums( - { before, after, limit, measurement }: Required, + { before, after, limit, measurement, offset }: Required, user: User ): Promise[]> { const historyTracks = @@ -127,8 +127,8 @@ export class StatsRigtchService { if (measurement === StatsMeasurement.PLAYS) { const mostFrequentItems = getMostFrequentItems( albums.map(album => album!.id), - limit - ) + limit + offset + ).slice(offset) return mostFrequentItems.map(({ item: id, count }) => ({ item: albums.find(album => album!.id === id)!, @@ -141,8 +141,8 @@ export class StatsRigtchService { id: album!.id, duration, })), - limit - ) + limit + offset + ).slice(offset) return mostListenedAlbumByDuration.map(({ id, totalDuration }) => ({ item: albums.find(album => album!.id === id)!, @@ -151,7 +151,7 @@ export class StatsRigtchService { } async getTopGenres( - { before, after, limit, measurement }: Required, + { before, after, limit, measurement, offset }: Required, user: User ): Promise[]> { const historyTracks = @@ -170,7 +170,10 @@ export class StatsRigtchService { const genres = artists.flatMap(({ genres }) => genres) if (measurement === StatsMeasurement.PLAYS) { - const mostFrequentItems = getMostFrequentItems(genres, limit) + const mostFrequentItems = getMostFrequentItems( + genres, + limit + offset + ).slice(offset) return mostFrequentItems.map(({ item, count }) => ({ item, @@ -184,8 +187,8 @@ export class StatsRigtchService { .flatMap(({ genres }) => genres) .map(genre => ({ id: genre, duration })) ), - limit - ) + limit + offset + ).slice(offset) return mostListenedGenreByDuration.map(({ id, totalDuration }) => ({ item: id,