Skip to content

Commit

Permalink
feat(modules/items/albums): create AlbumSubscriber and validate alb…
Browse files Browse the repository at this point in the history
…um properties after load
  • Loading branch information
Mnigos committed Sep 11, 2024
1 parent dc22b99 commit 4fabe7d
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 1 deletion.
149 changes: 149 additions & 0 deletions src/modules/items/albums/album.subscriber.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { Test, TestingModule } from '@nestjs/testing'
import { DataSource, EntityManager } from 'typeorm'
import { MockInstance } from 'vitest'

import { ArtistsService } from '../artists'

import { Album } from './album.entity'
import { AlbumSubscriber } from './album.subscriber'

import {
albumEntityMock,
entityManagerFactoryMock,
sdkAlbumMock,
sdkArtistsMock,
transactionFactoryMock,
} from '@common/mocks'
import { SpotifyService } from '@modules/spotify'

describe('AlbumSubscriber', () => {
let moduleRef: TestingModule
let albumSubscriber: AlbumSubscriber
let spotifyService: SpotifyService
let artistsService: ArtistsService
let entityManagerMock: EntityManager

beforeEach(async () => {
entityManagerMock = entityManagerFactoryMock()

moduleRef = await Test.createTestingModule({
providers: [
AlbumSubscriber,
{
provide: DataSource,
useValue: {
subscribers: [],
transaction: transactionFactoryMock(entityManagerMock),
},
},
{
provide: SpotifyService,
useValue: {
albums: {
get: vi.fn(),
},
artists: {
get: vi.fn(),
},
},
},
{
provide: ArtistsService,
useValue: {
findOrCreate: vi.fn(),
},
},
],
}).compile()

albumSubscriber = moduleRef.get(AlbumSubscriber)
spotifyService = moduleRef.get(SpotifyService)
artistsService = moduleRef.get(ArtistsService)
})

afterEach(() => {
moduleRef.close()
})

test('should be defined', () => {
expect(albumSubscriber).toBeDefined()
})

test('should listen to album entity', () => {
expect(albumSubscriber.listenTo()).toEqual(Album)
})

describe('afterLoad', () => {
let albumMock: Album

let spotifyGetAlbumSpy: MockInstance
let spotifyGetArtistsSpy: MockInstance
let findOrCreateSpy: MockInstance
let saveSpy: MockInstance

beforeEach(() => {
albumMock = albumEntityMock

spotifyGetAlbumSpy = vi.spyOn(spotifyService.albums, 'get')
spotifyGetArtistsSpy = vi.spyOn(spotifyService.artists, 'get')
findOrCreateSpy = vi.spyOn(artistsService, 'findOrCreate')
saveSpy = vi.spyOn(entityManagerMock, 'save')
})

test('should skip if album has artists', async () => {
await albumSubscriber.afterLoad(albumMock)

expect(spotifyGetAlbumSpy).not.toHaveBeenCalled()
expect(spotifyGetArtistsSpy).not.toHaveBeenCalled()
expect(findOrCreateSpy).not.toHaveBeenCalled()
expect(saveSpy).not.toHaveBeenCalled()
})

test('should update album with artists if artists are undefined', async () => {
spotifyGetAlbumSpy.mockResolvedValue(sdkAlbumMock)
spotifyGetArtistsSpy.mockResolvedValue(sdkArtistsMock)

// @ts-expect-error - mocking property
albumMock.artists = undefined

await albumSubscriber.afterLoad(albumMock)

expect(spotifyGetAlbumSpy).toHaveBeenCalledWith(
albumMock.externalId,
false
)
expect(spotifyGetArtistsSpy).toHaveBeenCalledWith(
sdkAlbumMock.artists.map(({ id }) => id),
false
)
expect(findOrCreateSpy).toHaveBeenCalledWith(
sdkArtistsMock,
entityManagerMock
)
expect(saveSpy).toHaveBeenCalledWith(albumMock)
})

test('should update album with artists if artists are empty', async () => {
spotifyGetAlbumSpy.mockResolvedValue(sdkAlbumMock)
spotifyGetArtistsSpy.mockResolvedValue(sdkArtistsMock)

albumMock.artists = []

await albumSubscriber.afterLoad(albumMock)

expect(spotifyGetAlbumSpy).toHaveBeenCalledWith(
albumMock.externalId,
false
)
expect(spotifyGetArtistsSpy).toHaveBeenCalledWith(
sdkAlbumMock.artists.map(({ id }) => id),
false
)
expect(findOrCreateSpy).toHaveBeenCalledWith(
sdkArtistsMock,
entityManagerMock
)
expect(saveSpy).toHaveBeenCalledWith(albumMock)
})
})
})
51 changes: 51 additions & 0 deletions src/modules/items/albums/album.subscriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { DataSource, EntitySubscriberInterface, EventSubscriber } from 'typeorm'
import { Logger } from '@nestjs/common'

import { Album } from './album.entity'

import { ArtistsService } from '@modules/items/artists'
import { SpotifyService } from '@modules/spotify'

@EventSubscriber()
export class AlbumSubscriber implements EntitySubscriberInterface<Album> {
private readonly logger = new Logger(AlbumSubscriber.name)

constructor(
private readonly dataSource: DataSource,
private readonly spotifyService: SpotifyService,
private readonly artistsService: ArtistsService
) {
dataSource.subscribers.push(this)
}

listenTo() {
return Album
}

async afterLoad(albumEntity: Album) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!albumEntity.artists || albumEntity.artists.length === 0) {
this.logger.log(`Inserting artists for album ${albumEntity.name}`)

const sdkAlbum = await this.spotifyService.albums.get(
albumEntity.externalId,
false
)
const sdkArtists = await this.spotifyService.artists.get(
sdkAlbum.artists.map(({ id }) => id),
false
)

this.dataSource.transaction(async manager => {
const artists = await this.artistsService.findOrCreate(
sdkArtists,
manager
)

albumEntity.artists = artists

await manager.save(albumEntity)
})
}
}
}
3 changes: 2 additions & 1 deletion src/modules/items/albums/albums.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Album } from './album.entity'
import { AlbumsController } from './albums.controller'
import { AlbumsRepository } from './albums.repository'
import { AlbumsService } from './albums.service'
import { AlbumSubscriber } from './album.subscriber'

import { ImagesModule } from '@modules/items/images'
import { ArtistsModule } from '@modules/items/artists'
Expand All @@ -19,7 +20,7 @@ import { SpotifyModule } from '@modules/spotify'
ArtistsModule,
ImagesModule,
],
providers: [AlbumsRepository, AlbumsService],
providers: [AlbumsRepository, AlbumsService, AlbumSubscriber],
controllers: [AlbumsController],
exports: [AlbumsRepository, AlbumsService],
})
Expand Down

0 comments on commit 4fabe7d

Please sign in to comment.