From d31e0cc973337b977229150c6258c5af0481b7b5 Mon Sep 17 00:00:00 2001 From: Mnigos Date: Mon, 18 Dec 2023 17:03:34 +0100 Subject: [PATCH 1/2] refactor(modules): parse `httpService` methods to promise --- src/modules/auth/auth.controller.spec.ts | 25 ++- src/modules/auth/auth.controller.ts | 10 +- src/modules/auth/auth.service.spec.ts | 20 +-- src/modules/auth/auth.service.ts | 60 ++++--- src/modules/player/player.service.spec.ts | 59 +++---- src/modules/player/player.service.ts | 156 +++++++++-------- .../statistics/statistics.service.spec.ts | 38 ++-- src/modules/statistics/statistics.service.ts | 164 ++++++++++-------- 8 files changed, 264 insertions(+), 268 deletions(-) diff --git a/src/modules/auth/auth.controller.spec.ts b/src/modules/auth/auth.controller.spec.ts index 4b05d477..7d413daf 100644 --- a/src/modules/auth/auth.controller.spec.ts +++ b/src/modules/auth/auth.controller.spec.ts @@ -1,7 +1,6 @@ import { HttpStatus } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' -import { firstValueFrom, of } from 'rxjs' import { AuthController } from './auth.controller' import { AuthService } from './auth.service' @@ -82,10 +81,10 @@ describe('AuthController', () => { test('callback should return valid redirect path', async () => { const tokenSpy = vi .spyOn(authService, 'token') - .mockReturnValue(of(tokenResponse)) + .mockResolvedValue(tokenResponse) const profileSpy = vi .spyOn(authService, 'profile') - .mockReturnValue(of(profileMock)) + .mockResolvedValue(profileMock) expect(await authController.callback(code)).toEqual({ url: `${redirectUrl}/api/authorize?${new URLSearchParams({ @@ -99,8 +98,8 @@ describe('AuthController', () => { }) test('should find profile by id', async () => { - vi.spyOn(authService, 'token').mockReturnValue(of(tokenResponse)) - vi.spyOn(authService, 'profile').mockReturnValue(of(profileMock)) + vi.spyOn(authService, 'token').mockResolvedValue(tokenResponse) + vi.spyOn(authService, 'profile').mockResolvedValue(profileMock) const findUserByProfileId = vi .spyOn(usersRepository, 'findOneByProfileId') @@ -122,8 +121,8 @@ describe('AuthController', () => { }) test('should create profile and user', async () => { - vi.spyOn(authService, 'token').mockReturnValue(of(tokenResponse)) - vi.spyOn(authService, 'profile').mockReturnValue(of(profileMock)) + vi.spyOn(authService, 'token').mockResolvedValue(tokenResponse) + vi.spyOn(authService, 'profile').mockResolvedValue(profileMock) const findUserByProfileId = vi.spyOn( usersRepository, @@ -159,18 +158,14 @@ describe('AuthController', () => { expiresIn: 3600, } - vi.spyOn(authService, 'token').mockReturnValue(of(secretData)) + vi.spyOn(authService, 'token').mockResolvedValue(secretData) - expect(await firstValueFrom(authController.refresh('123'))).toEqual( - secretData - ) + expect(await authController.refresh('123')).toEqual(secretData) }) test('should return profile', async () => { - vi.spyOn(authService, 'profile').mockReturnValue(of(profileMock)) + vi.spyOn(authService, 'profile').mockResolvedValue(profileMock) - expect(await firstValueFrom(authController.profile('123'))).toEqual( - profileMock - ) + expect(await authController.profile('123')).toEqual(profileMock) }) }) diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 8959cfb2..062ef17c 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -8,7 +8,6 @@ import { forwardRef, } from '@nestjs/common' import { ConfigService } from '@nestjs/config' -import { firstValueFrom } from 'rxjs' import { ApiExcludeEndpoint, ApiOkResponse, @@ -70,13 +69,8 @@ export class AuthController { async callback( @Query('code') code: string ): Promise { - const { accessToken, refreshToken } = await firstValueFrom( - this.authService.token({ code }) - ) - - const spotifyProfile = await firstValueFrom( - this.authService.profile(accessToken) - ) + const { accessToken, refreshToken } = await this.authService.token({ code }) + const spotifyProfile = await this.authService.profile(accessToken) const foundUser = await this.usersRepository.findOneByProfileId( spotifyProfile.id diff --git a/src/modules/auth/auth.service.spec.ts b/src/modules/auth/auth.service.spec.ts index 46aeddb5..db236aff 100644 --- a/src/modules/auth/auth.service.spec.ts +++ b/src/modules/auth/auth.service.spec.ts @@ -5,7 +5,7 @@ import { ConfigService } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' import { Profile } from 'passport-spotify' -import { firstValueFrom, of } from 'rxjs' +import { of } from 'rxjs' import { AuthService } from './auth.service' @@ -93,14 +93,13 @@ describe('AuthService', () => { httpService.post = vi .fn() .mockImplementation((_url, parameters: URLSearchParams) => { - if (parameters.get('grant_type') === 'refresh_token') { + if (parameters.get('grant_type') === 'refresh_token') return of(response) - } }) - expect( - await firstValueFrom(authService.token({ refreshToken: 'refresh' })) - ).toEqual(expectedResponse) + expect(await authService.token({ refreshToken: 'refresh' })).toEqual( + expectedResponse + ) }) test('should authorize and get tokens', async () => { @@ -123,12 +122,11 @@ describe('AuthService', () => { httpService.post = vi .fn() .mockImplementation((_url, parameters: URLSearchParams) => { - if (parameters.get('grant_type') === 'authorization_code') { + if (parameters.get('grant_type') === 'authorization_code') return of(response) - } }) - expect(await firstValueFrom(authService.token({ code: 'code' }))).toEqual( + expect(await authService.token({ code: 'code' })).toEqual( expectedResponse ) }) @@ -141,8 +139,6 @@ describe('AuthService', () => { httpService.get = vi.fn().mockReturnValue(of(response)) - expect(await firstValueFrom(authService.profile('token'))).toEqual( - profileMock - ) + expect(await authService.profile('token')).toEqual(profileMock) }) }) diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 6c4fa9a7..3e7945e5 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Profile as PassportSpotifyProfile } from 'passport-spotify' -import { Observable, map, catchError } from 'rxjs' +import { map, catchError, firstValueFrom } from 'rxjs' import { SecretData } from './dtos' import { TokenOptions } from './types' @@ -30,7 +30,7 @@ export class AuthService { return this.jwtService.sign(payload) } - token({ refreshToken, code }: TokenOptions): Observable { + token({ refreshToken, code }: TokenOptions): Promise { const url = `${this.configService.get( Environment.SPOTIFY_ACCOUNTS_URL )}/api/token` @@ -47,39 +47,43 @@ export class AuthService { const bufferedCredentials = Buffer.from( `${cliendId}:${clientSecret}` ).toString('base64') - const parameters = new URLSearchParams() + const params = new URLSearchParams() if (refreshToken) { - parameters.append('refresh_token', refreshToken) - parameters.append('grant_type', 'refresh_token') + params.append('refresh_token', refreshToken) + params.append('grant_type', 'refresh_token') } if (code) { - parameters.append('code', code) - parameters.append('grant_type', 'authorization_code') - callbackUrl && parameters.append('redirect_uri', callbackUrl) + params.append('code', code) + params.append('grant_type', 'authorization_code') + callbackUrl && params.append('redirect_uri', callbackUrl) } - return this.httpService - .post(url, parameters, { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: `Basic ${bufferedCredentials}`, - }, - }) - .pipe( - map(response => response.data), - map(adaptSecretData), - catchError(catchSpotifyError) - ) + return firstValueFrom( + this.httpService + .post(url, params, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Basic ${bufferedCredentials}`, + }, + }) + .pipe( + map(response => response.data), + map(adaptSecretData), + catchError(catchSpotifyError) + ) + ) } - profile(accessToken: string): Observable { - return this.httpService - .get('/me', applyAuthorizationHeader(accessToken)) - .pipe( - map(response => response.data), - map(adaptProfile), - catchError(catchSpotifyError) - ) + profile(accessToken: string): Promise { + return firstValueFrom( + this.httpService + .get('/me', applyAuthorizationHeader(accessToken)) + .pipe( + map(response => response.data), + map(adaptProfile), + catchError(catchSpotifyError) + ) + ) } } diff --git a/src/modules/player/player.service.spec.ts b/src/modules/player/player.service.spec.ts index ea290659..5ade831e 100644 --- a/src/modules/player/player.service.spec.ts +++ b/src/modules/player/player.service.spec.ts @@ -1,6 +1,6 @@ import { HttpService } from '@nestjs/axios' import { TestingModule, Test } from '@nestjs/testing' -import { of, firstValueFrom, throwError, catchError } from 'rxjs' +import { of, throwError } from 'rxjs' import { ForbiddenException } from '@nestjs/common' import { PlayerService } from './player.service' @@ -12,7 +12,6 @@ import { spotifyPlaybackStateMock, axiosResponseMockFactory, } from '@common/mocks' -import { SpotifyResponseError } from '@common/utils' const forbiddenExceptionObserver = throwError(() => ({ data: { @@ -59,9 +58,7 @@ describe('PlayerService', () => { ) ) - expect( - await firstValueFrom(playerService.availableDevices('awd')) - ).toEqual(devicesMock) + expect(await playerService.availableDevices('awd')).toEqual(devicesMock) }) test('should throw Forbidden expception because no device is currently playing', async () => { @@ -73,13 +70,9 @@ describe('PlayerService', () => { ) ) - expect( - await firstValueFrom( - playerService - .availableDevices('awd') - .pipe(catchError((error: SpotifyResponseError) => [error])) - ) - ).toBeInstanceOf(ForbiddenException) + await expect(playerService.availableDevices('awd')).rejects.toThrowError( + ForbiddenException + ) }) }) @@ -89,29 +82,25 @@ describe('PlayerService', () => { of(axiosResponseMockFactory(spotifyPlaybackStateMock)) ) - expect( - await firstValueFrom(playerService.currentPlaybackState('awd')) - ).toEqual(playbackStateMock) + expect(await playerService.currentPlaybackState('awd')).toEqual( + playbackStateMock + ) }) test.skip('should throw Forbidden expception because No device is currently playing', async () => { vi.spyOn(httpService, 'get').mockReturnValue( - of(axiosResponseMockFactory('')) + of(axiosResponseMockFactory(forbiddenExceptionObserver)) ) - expect( - await firstValueFrom( - playerService - .currentPlaybackState('awd') - .pipe(catchError((error: SpotifyResponseError) => [error])) - ) - ).toBeInstanceOf(ForbiddenException) + await expect( + playerService.currentPlaybackState('awd') + ).rejects.toThrowError(ForbiddenException) }) }) describe('pausePlayer', () => { test('should pause player', async () => { - expect(await firstValueFrom(playerService.pausePlayer('awd'))).toEqual({ + expect(await playerService.pausePlayer('awd')).toEqual({ success: true, }) }) @@ -119,19 +108,15 @@ describe('PlayerService', () => { test('should throw Forbidden expception because no device is currently playing', async () => { vi.spyOn(httpService, 'put').mockReturnValue(forbiddenExceptionObserver) - expect( - await firstValueFrom( - playerService - .pausePlayer('awd') - .pipe(catchError((error: SpotifyResponseError) => [error])) - ) - ).toBeInstanceOf(ForbiddenException) + await expect(playerService.pausePlayer('awd')).rejects.toThrowError( + ForbiddenException + ) }) }) describe('resumePlayer', () => { test('should resume player', async () => { - expect(await firstValueFrom(playerService.resumePlayer('awd'))).toEqual({ + expect(await playerService.resumePlayer('awd')).toEqual({ success: true, }) }) @@ -139,13 +124,9 @@ describe('PlayerService', () => { test('should throw Forbidden expception because no device is currently playing', async () => { vi.spyOn(httpService, 'put').mockReturnValue(forbiddenExceptionObserver) - expect( - await firstValueFrom( - playerService - .resumePlayer('awd') - .pipe(catchError((error: SpotifyResponseError) => [error])) - ) - ).toBeInstanceOf(ForbiddenException) + await expect(playerService.resumePlayer('awd')).rejects.toThrowError( + ForbiddenException + ) }) }) }) diff --git a/src/modules/player/player.service.ts b/src/modules/player/player.service.ts index 91945371..80e3b852 100644 --- a/src/modules/player/player.service.ts +++ b/src/modules/player/player.service.ts @@ -1,6 +1,6 @@ import { HttpService } from '@nestjs/axios' import { Injectable, ForbiddenException } from '@nestjs/common' -import { Observable, map, catchError, tap, timer, exhaustMap } from 'rxjs' +import { map, catchError, tap, timer, exhaustMap, firstValueFrom } from 'rxjs' import { PlayerMessage } from './messages' @@ -22,95 +22,103 @@ import { adaptDevices, adaptPlaybackState } from '@common/adapters' export class PlayerService { constructor(private readonly httpService: HttpService) {} - availableDevices(accessToken: string): Observable { - return this.httpService - .get<{ devices: SpotifyDevice[] }>( - '/me/player/devices', - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(response => response.data.devices), - catchError(catchSpotifyError), - tap(devices => { - if (devices.length <= 0) - throw new ForbiddenException(PlayerMessage.NO_AVAIBLE_DEVICES) - }), - map(adaptDevices) - ) + availableDevices(accessToken: string): Promise { + return firstValueFrom( + this.httpService + .get<{ devices: SpotifyDevice[] }>( + '/me/player/devices', + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(response => response.data.devices), + catchError(catchSpotifyError), + tap(devices => { + if (devices.length <= 0) + throw new ForbiddenException(PlayerMessage.NO_AVAIBLE_DEVICES) + }), + map(adaptDevices) + ) + ) } - currentPlaybackState(accessToken: string): Observable { - return this.httpService - .get( - '/me/player', - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(response => response.data), - tap(playbackState => { - if (!playbackState.device.is_active) - throw new ForbiddenException(PlayerMessage.NO_PLAYING_DEVICE) - }), - map(adaptPlaybackState), - catchError(catchSpotifyError) - ) + currentPlaybackState(accessToken: string): Promise { + return firstValueFrom( + this.httpService + .get( + '/me/player', + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(response => response.data), + tap(playbackState => { + if (!playbackState.device.is_active) + throw new ForbiddenException(PlayerMessage.NO_PLAYING_DEVICE) + }), + map(adaptPlaybackState), + catchError(catchSpotifyError) + ) + ) } pausePlayer( accessToken: string, afterTime = 0, deviceId?: string - ): Observable { + ): Promise { const deviceIdQuery = `?device_id=${deviceId}` - return timer(afterTime).pipe( - exhaustMap(() => { - return this.httpService - .put( - `/me/player/pause${deviceId ? deviceIdQuery : ''}`, - {}, - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(() => ({ - success: true, - })), - catchError((response: SpotifyResponseError) => { - if ( - !('error_description' in response.data) && - response.data.error.status === 403 - ) - throw new ForbiddenException(PlayerMessage.NO_PLAYING_DEVICE) + return firstValueFrom( + timer(afterTime).pipe( + exhaustMap(() => { + return this.httpService + .put( + `/me/player/pause${deviceId ? deviceIdQuery : ''}`, + {}, + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(() => ({ + success: true, + })), + catchError((response: SpotifyResponseError) => { + if ( + !('error_description' in response.data) && + response.data.error.status === 403 + ) + throw new ForbiddenException(PlayerMessage.NO_PLAYING_DEVICE) - return catchSpotifyError(response) - }) - ) - }) + return catchSpotifyError(response) + }) + ) + }) + ) ) } - resumePlayer(accessToken: string, deviceId?: string): Observable { + resumePlayer(accessToken: string, deviceId?: string): Promise { const deviceIdQuery = `?device_id=${deviceId}` - return this.httpService - .put( - `/me/player/play${deviceId ? deviceIdQuery : ''}`, - {}, - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(() => ({ - success: true, - })), - catchError((response: SpotifyResponseError) => { - if ( - !('error_description' in response.data) && - response.data.error.status === 403 - ) - throw new ForbiddenException(PlayerMessage.DEVICE_ALREADY_PLAYING) + return firstValueFrom( + this.httpService + .put( + `/me/player/play${deviceId ? deviceIdQuery : ''}`, + {}, + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(() => ({ + success: true, + })), + catchError((response: SpotifyResponseError) => { + if ( + !('error_description' in response.data) && + response.data.error.status === 403 + ) + throw new ForbiddenException(PlayerMessage.DEVICE_ALREADY_PLAYING) - return catchSpotifyError(response) - }) - ) + return catchSpotifyError(response) + }) + ) + ) } } diff --git a/src/modules/statistics/statistics.service.spec.ts b/src/modules/statistics/statistics.service.spec.ts index c9800fc7..ab41f236 100644 --- a/src/modules/statistics/statistics.service.spec.ts +++ b/src/modules/statistics/statistics.service.spec.ts @@ -61,7 +61,7 @@ describe('StatisticsService', () => { ) ) - expect(await firstValueFrom(statisticsService.lastTracks('awd'))).toEqual( + expect(await statisticsService.lastTracks('awd')).toEqual( spotifyResponseWithCursorsMockFactory(tracksMock) ) }) @@ -75,7 +75,7 @@ describe('StatisticsService', () => { ) ) - expect(await firstValueFrom(statisticsService.topArtists('awd'))).toEqual( + expect(await statisticsService.topArtists('awd')).toEqual( spotifyResponseWithOffsetMockFactory(artistsMock) ) }) @@ -87,9 +87,7 @@ describe('StatisticsService', () => { ) ) - expect(await firstValueFrom(statisticsService.topGenres('awd', 3))).toEqual( - topGenresMock - ) + expect(await statisticsService.topGenres('awd', 3)).toEqual(topGenresMock) }) test('should get top tracks', async () => { @@ -101,7 +99,7 @@ describe('StatisticsService', () => { ) ) - expect(await firstValueFrom(statisticsService.topTracks('awd'))).toEqual( + expect(await statisticsService.topTracks('awd')).toEqual( spotifyResponseWithOffsetMockFactory(tracksMock) ) }) @@ -117,20 +115,20 @@ describe('StatisticsService', () => { }) test('should generate analysis', async () => { - vi.spyOn(statisticsService, 'topTracks').mockReturnValue( - of(spotifyResponseWithOffsetMockFactory(tracksMock)) - ) - - vi.spyOn(httpService, 'get').mockReturnValue( - of( - axiosResponseMockFactory({ - audio_features: [spotifyAudioFeaturesMock], - }) - ) - ) + vi.spyOn(httpService, 'get').mockImplementation((path: string) => { + return path === '/me/top/artists?limit=50' + ? of( + axiosResponseMockFactory( + spotifyResponseMockFactory(spotifyArtistsMock) + ) + ) + : of( + axiosResponseMockFactory({ + audio_features: [spotifyAudioFeaturesMock], + }) + ) + }) - expect(await firstValueFrom(statisticsService.analysis('awd'))).toEqual( - analysisMock - ) + expect(await statisticsService.analysis('awd')).toEqual(analysisMock) }) }) diff --git a/src/modules/statistics/statistics.service.ts b/src/modules/statistics/statistics.service.ts index fe9f66a9..a928887c 100644 --- a/src/modules/statistics/statistics.service.ts +++ b/src/modules/statistics/statistics.service.ts @@ -1,6 +1,6 @@ import { HttpService } from '@nestjs/axios' import { Injectable } from '@nestjs/common' -import { Observable, map, catchError, mergeMap } from 'rxjs' +import { map, catchError, mergeMap, firstValueFrom } from 'rxjs' import { analysisFactory } from './utils' import { TimeRange } from './enums' @@ -36,7 +36,7 @@ export class StatisticsService { limit = 20, before?: string, after?: string - ): Observable> { + ): Promise> { const urlSearchParameters = new URLSearchParams({ limit: limit + '', }) @@ -44,25 +44,27 @@ export class StatisticsService { if (before) urlSearchParameters.append('before', before) if (after) urlSearchParameters.append('after', after) - return this.httpService - .get< - SpotifyResponseWithCursors<{ track: SpotifyTrack; played_at: string }> - >( - `/me/player/recently-played?${urlSearchParameters.toString()}`, - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(response => response.data), - map(({ items, ...data }) => ({ - ...data, - items: items.map(({ track, played_at }) => ({ - ...track, - played_at, + return firstValueFrom( + this.httpService + .get< + SpotifyResponseWithCursors<{ track: SpotifyTrack; played_at: string }> + >( + `/me/player/recently-played?${urlSearchParameters.toString()}`, + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(response => response.data), + map(({ items, ...data }) => ({ + ...data, + items: items.map(({ track, played_at }) => ({ + ...track, + played_at, + })), })), - })), - map(adaptLastTracks), - catchError(catchSpotifyError) - ) + map(adaptLastTracks), + catchError(catchSpotifyError) + ) + ) } topGenres( @@ -70,23 +72,25 @@ export class StatisticsService { limit = 10, timeRange = TimeRange.LONG_TERM, offset = 1 - ): Observable { + ): Promise { const urlSearchParameters = new URLSearchParams({ limit: limit + '', offset: offset + '', time_range: timeRange, }) - return this.httpService - .get>( - `/me/top/artists?${urlSearchParameters.toString()}`, - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(response => response.data.items), - map(items => adaptGenres(items, limit)), - catchError(catchSpotifyError) - ) + return firstValueFrom( + this.httpService + .get>( + `/me/top/artists?${urlSearchParameters.toString()}`, + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(response => response.data.items), + map(items => adaptGenres(items, limit)), + catchError(catchSpotifyError) + ) + ) } topArtists( @@ -94,23 +98,25 @@ export class StatisticsService { limit = 10, timeRange = TimeRange.LONG_TERM, offset = 1 - ): Observable> { + ): Promise> { const urlSearchParameters = new URLSearchParams({ limit: limit + '', offset: offset + '', time_range: timeRange, }) - return this.httpService - .get>( - `/me/top/artists?${urlSearchParameters.toString()}`, - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(response => response.data), - map(adaptPaginatedArtists), - catchError(catchSpotifyError) - ) + return firstValueFrom( + this.httpService + .get>( + `/me/top/artists?${urlSearchParameters.toString()}`, + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(response => response.data), + map(adaptPaginatedArtists), + catchError(catchSpotifyError) + ) + ) } topTracks( @@ -118,23 +124,25 @@ export class StatisticsService { limit = 10, timeRange = TimeRange.LONG_TERM, offset = 1 - ): Observable> { + ): Promise> { const urlSearchParameters = new URLSearchParams({ limit: limit + '', offset: offset + '', time_range: timeRange, }) - return this.httpService - .get>( - `/me/top/tracks?${urlSearchParameters.toString()}`, - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(response => response.data), - map(adaptPaginatedTracks), - catchError(catchSpotifyError) - ) + return firstValueFrom( + this.httpService + .get>( + `/me/top/tracks?${urlSearchParameters.toString()}`, + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(response => response.data), + map(adaptPaginatedTracks), + catchError(catchSpotifyError) + ) + ) } artist(accessToken: string, id: string) { @@ -150,27 +158,39 @@ export class StatisticsService { ) } - analysis(accessToken: string): Observable { - return this.topTracks(accessToken, 50).pipe( - mergeMap(tracks => { - const tracksIds = tracks.items.map(({ id }) => id).join(',') + analysis(accessToken: string): Promise { + return firstValueFrom( + this.httpService + .get>( + `/me/top/artists?limit=50`, + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(response => response.data), + map(adaptPaginatedArtists), + catchError(catchSpotifyError) + ) + .pipe( + mergeMap(tracks => { + const tracksIds = tracks.items.map(({ id }) => id).join(',') - return this.httpService - .get<{ audio_features: SpotifyAudioFeatures[] }>( - `/audio-features?ids=${tracksIds}`, - applyAuthorizationHeader(accessToken) - ) - .pipe( - map(response => response.data.audio_features), - map(audioFeatures => - audioFeatures.map(audioFeature => - adaptAudioFeatures(audioFeature) + return this.httpService + .get<{ audio_features: SpotifyAudioFeatures[] }>( + `/audio-features?ids=${tracksIds}`, + applyAuthorizationHeader(accessToken) + ) + .pipe( + map(response => response.data.audio_features), + map(audioFeatures => + audioFeatures.map(audioFeature => + adaptAudioFeatures(audioFeature) + ) + ), + map(analysisFactory), + catchError(catchSpotifyError) ) - ), - map(analysisFactory), - catchError(catchSpotifyError) - ) - }) + }) + ) ) } } From 0ca25090f6708f3418941db9d3e59adee7aad571 Mon Sep 17 00:00:00 2001 From: Mnigos Date: Mon, 18 Dec 2023 17:04:03 +0100 Subject: [PATCH 2/2] style(common/utils/catch-spotify-error): remove console logs --- src/common/utils/catch-spotify-error.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/common/utils/catch-spotify-error.ts b/src/common/utils/catch-spotify-error.ts index 61667e21..e6533fc1 100644 --- a/src/common/utils/catch-spotify-error.ts +++ b/src/common/utils/catch-spotify-error.ts @@ -21,12 +21,9 @@ export const SPOTIFY_DEFAULT_ERROR_MESSAGE = 'Something went wrong with fetching data from spotify API:' export const catchSpotifyError = (response: SpotifyResponseError) => { - console.log(response.data.error) - const { data, status } = response if ('error_description' in data) { - console.log('ee') if (data.error === 'invalid_grant') throw new UnauthorizedException('Invalid token') @@ -35,8 +32,6 @@ export const catchSpotifyError = (response: SpotifyResponseError) => { ) } - console.log(status) - if (status === 401) throw new UnauthorizedException(data.error.message) throw new BadGatewayException(