-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
N21-2103 adapt vidis sync code to use vidis client
- Loading branch information
1 parent
2fe346b
commit 650cb18
Showing
26 changed files
with
482 additions
and
581 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 was deleted.
Oops, something went wrong.
37 changes: 0 additions & 37 deletions
37
apps/server/src/infra/sync/media-licenses/response/vidis-item.response.ts
This file was deleted.
Oops, something went wrong.
11 changes: 0 additions & 11 deletions
11
apps/server/src/infra/sync/media-licenses/response/vidis.response.ts
This file was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -1 +1,2 @@ | ||
export { VidisSyncService } from './vidis-sync.service'; | ||
export { VidisFetchService } from './vidis-fetch.service'; |
222 changes: 222 additions & 0 deletions
222
apps/server/src/infra/sync/media-licenses/service/vidis-fetch.service.spec.ts
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,222 @@ | ||
import { DefaultEncryptionService, EncryptionService, SymetricKeyEncryptionService } from '@infra/encryption'; | ||
import { IDMBetreiberApiInterface, PageOfferDTO, VidisClientFactory } from '@infra/vidis-client'; | ||
import { MediaSourceDataFormat } from '@modules/media-source'; | ||
import { MediaSourceBasicAuthConfig } from '@modules/media-source/domain'; | ||
import { MediaSourceBasicAuthConfigNotFoundLoggableException } from '@modules/media-source/loggable'; | ||
import { mediaSourceFactory } from '@modules/media-source/testing'; | ||
import { AxiosErrorLoggable } from '@src/core/error/loggable'; | ||
import { axiosErrorFactory, axiosResponseFactory } from '@shared/testing'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { createMock, DeepMocked } from '@golevelup/ts-jest'; | ||
import { AxiosResponse } from 'axios'; | ||
import { vidisPageOfferFactory } from '../testing'; | ||
import { VidisFetchService } from './vidis-fetch.service'; | ||
|
||
describe(VidisFetchService.name, () => { | ||
let module: TestingModule; | ||
let service: VidisFetchService; | ||
let vidisClientFactory: DeepMocked<VidisClientFactory>; | ||
let encryptionService: DeepMocked<SymetricKeyEncryptionService>; | ||
|
||
beforeAll(async () => { | ||
module = await Test.createTestingModule({ | ||
providers: [ | ||
VidisFetchService, | ||
{ | ||
provide: VidisClientFactory, | ||
useValue: createMock<VidisClientFactory>(), | ||
}, | ||
{ | ||
provide: DefaultEncryptionService, | ||
useValue: createMock<EncryptionService>(), | ||
}, | ||
], | ||
}).compile(); | ||
|
||
service = module.get(VidisFetchService); | ||
vidisClientFactory = module.get(VidisClientFactory); | ||
encryptionService = module.get(DefaultEncryptionService); | ||
}); | ||
|
||
afterAll(async () => { | ||
await module.close(); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe('getOfferItemsFromVidis', () => { | ||
describe('when the media source has basic auth config', () => { | ||
describe('when vidis returns the offer items successfully', () => { | ||
const setup = () => { | ||
const mediaSource = mediaSourceFactory.withBasicAuthConfig().build(); | ||
|
||
const axiosResponse = axiosResponseFactory.build({ | ||
data: vidisPageOfferFactory.build(), | ||
}) as AxiosResponse<PageOfferDTO>; | ||
|
||
const vidisApiClientMock = createMock<IDMBetreiberApiInterface>(); | ||
vidisApiClientMock.getActivatedOffersByRegion.mockResolvedValueOnce(axiosResponse); | ||
vidisClientFactory.createVidisClient.mockReturnValueOnce(vidisApiClientMock); | ||
|
||
const decryptedUsername = 'un-decrypted'; | ||
const decryptedPassword = 'pw-decrypted'; | ||
encryptionService.decrypt.mockReturnValueOnce(decryptedUsername); | ||
encryptionService.decrypt.mockReturnValueOnce(decryptedPassword); | ||
|
||
return { | ||
mediaSource, | ||
vidisOfferItems: axiosResponse.data.items, | ||
decryptedUsername, | ||
decryptedPassword, | ||
vidisApiClientMock, | ||
}; | ||
}; | ||
|
||
it('should return the vidis offer items', async () => { | ||
const { mediaSource, vidisOfferItems } = setup(); | ||
|
||
const result = await service.getOfferItemsFromVidis(mediaSource); | ||
|
||
expect(result).toEqual(vidisOfferItems); | ||
}); | ||
|
||
it('should decrypt the credentials from basic auth config', async () => { | ||
const { mediaSource } = setup(); | ||
|
||
await service.getOfferItemsFromVidis(mediaSource); | ||
|
||
expect(encryptionService.decrypt).toBeCalledTimes(2); | ||
expect(encryptionService.decrypt).toBeCalledWith(mediaSource.basicAuthConfig?.username); | ||
expect(encryptionService.decrypt).toBeCalledWith(mediaSource.basicAuthConfig?.password); | ||
}); | ||
|
||
it('should create a vidis api client', async () => { | ||
const { mediaSource, decryptedUsername, decryptedPassword } = setup(); | ||
|
||
await service.getOfferItemsFromVidis(mediaSource); | ||
|
||
expect(vidisClientFactory.createVidisClient).toBeCalledWith({ | ||
username: decryptedUsername, | ||
password: decryptedPassword, | ||
}); | ||
}); | ||
|
||
it('should call the vidis endpoint for activated offer items', async () => { | ||
const { mediaSource, vidisApiClientMock } = setup(); | ||
|
||
await service.getOfferItemsFromVidis(mediaSource); | ||
|
||
expect(vidisApiClientMock.getActivatedOffersByRegion).toBeCalledWith('test-region'); | ||
}); | ||
}); | ||
|
||
describe('when vidis returns the no offer items', () => { | ||
const setup = () => { | ||
const mediaSource = mediaSourceFactory.withBasicAuthConfig().build(); | ||
|
||
const axiosResponse = axiosResponseFactory.build({ | ||
data: vidisPageOfferFactory.build({ items: undefined }), | ||
}) as AxiosResponse<PageOfferDTO>; | ||
|
||
const vidisApiClientMock = createMock<IDMBetreiberApiInterface>(); | ||
vidisApiClientMock.getActivatedOffersByRegion.mockResolvedValueOnce(axiosResponse); | ||
vidisClientFactory.createVidisClient.mockReturnValueOnce(vidisApiClientMock); | ||
|
||
const basicAuth = mediaSource.basicAuthConfig as MediaSourceBasicAuthConfig; | ||
encryptionService.decrypt.mockReturnValueOnce(basicAuth.username); | ||
encryptionService.decrypt.mockReturnValueOnce(basicAuth.password); | ||
|
||
return { | ||
mediaSource, | ||
}; | ||
}; | ||
|
||
it('should return an empty array', async () => { | ||
const { mediaSource } = setup(); | ||
|
||
const result = await service.getOfferItemsFromVidis(mediaSource); | ||
|
||
expect(result.length).toEqual(0); | ||
}); | ||
}); | ||
|
||
describe('when an axios error is thrown', () => { | ||
const setup = () => { | ||
const mediaSource = mediaSourceFactory.withBasicAuthConfig().build(); | ||
|
||
const axiosError = axiosErrorFactory.build(); | ||
|
||
const vidisApiClientMock = createMock<IDMBetreiberApiInterface>(); | ||
vidisApiClientMock.getActivatedOffersByRegion.mockRejectedValueOnce(axiosError); | ||
vidisClientFactory.createVidisClient.mockReturnValueOnce(vidisApiClientMock); | ||
|
||
const basicAuth = mediaSource.basicAuthConfig as MediaSourceBasicAuthConfig; | ||
encryptionService.decrypt.mockReturnValueOnce(basicAuth.username); | ||
encryptionService.decrypt.mockReturnValueOnce(basicAuth.password); | ||
|
||
return { | ||
mediaSource, | ||
axiosError, | ||
}; | ||
}; | ||
|
||
it('should throw a AxiosErrorLoggable', async () => { | ||
const { mediaSource, axiosError } = setup(); | ||
|
||
const promise = service.getOfferItemsFromVidis(mediaSource); | ||
|
||
await expect(promise).rejects.toThrow(new AxiosErrorLoggable(axiosError, 'VIDIS_GET_OFFER_ITEMS_FAILED')); | ||
}); | ||
}); | ||
|
||
describe('when an unknown error is thrown', () => { | ||
const setup = () => { | ||
const mediaSource = mediaSourceFactory.withBasicAuthConfig().build(); | ||
|
||
const unknownError = new Error(); | ||
|
||
const vidisApiClientMock = createMock<IDMBetreiberApiInterface>(); | ||
vidisApiClientMock.getActivatedOffersByRegion.mockRejectedValueOnce(unknownError); | ||
vidisClientFactory.createVidisClient.mockReturnValueOnce(vidisApiClientMock); | ||
|
||
const basicAuth = mediaSource.basicAuthConfig as MediaSourceBasicAuthConfig; | ||
encryptionService.decrypt.mockReturnValueOnce(basicAuth.username); | ||
encryptionService.decrypt.mockReturnValueOnce(basicAuth.password); | ||
|
||
return { | ||
mediaSource, | ||
unknownError, | ||
}; | ||
}; | ||
|
||
it('should throw the unknown error', async () => { | ||
const { mediaSource, unknownError } = setup(); | ||
|
||
const promise = service.getOfferItemsFromVidis(mediaSource); | ||
|
||
await expect(promise).rejects.toThrow(unknownError); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('when the media source has no basic auth config ', () => { | ||
const setup = () => { | ||
const mediaSource = mediaSourceFactory.build({ basicAuthConfig: undefined }); | ||
|
||
return { mediaSource }; | ||
}; | ||
|
||
it('should throw an MediaSourceBasicAuthConfigNotFoundLoggableException', async () => { | ||
const { mediaSource } = setup(); | ||
|
||
const promise = service.getOfferItemsFromVidis(mediaSource); | ||
|
||
await expect(promise).rejects.toThrow( | ||
new MediaSourceBasicAuthConfigNotFoundLoggableException(mediaSource.id, MediaSourceDataFormat.VIDIS) | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
49 changes: 49 additions & 0 deletions
49
apps/server/src/infra/sync/media-licenses/service/vidis-fetch.service.ts
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,49 @@ | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
import { DefaultEncryptionService, EncryptionService } from '@infra/encryption'; | ||
import { VidisClientFactory, IDMBetreiberApiInterface, PageOfferDTO, OfferDTO } from '@infra/vidis-client'; | ||
import { MediaSource, MediaSourceDataFormat } from '@modules/media-source'; | ||
import { MediaSourceBasicAuthConfigNotFoundLoggableException } from '@modules/media-source/loggable'; | ||
import { AxiosResponse, isAxiosError } from 'axios'; | ||
import { AxiosErrorLoggable } from '@src/core/error/loggable'; | ||
|
||
@Injectable() | ||
export class VidisFetchService { | ||
constructor( | ||
private readonly vidisClientFactory: VidisClientFactory, | ||
@Inject(DefaultEncryptionService) private readonly encryptionService: EncryptionService | ||
) {} | ||
|
||
public async getOfferItemsFromVidis(mediaSource: MediaSource): Promise<OfferDTO[]> { | ||
if (!mediaSource.basicAuthConfig) { | ||
throw new MediaSourceBasicAuthConfigNotFoundLoggableException(mediaSource.id, MediaSourceDataFormat.VIDIS); | ||
} | ||
|
||
const vidisClient: IDMBetreiberApiInterface = this.vidisClientFactory.createVidisClient(); | ||
|
||
// TODO: env var | ||
const region = 'test-region'; | ||
const decryptedUsername = this.encryptionService.decrypt(mediaSource.basicAuthConfig.username); | ||
const decryptedPassword = this.encryptionService.decrypt(mediaSource.basicAuthConfig.password); | ||
const basicAuthEncoded = btoa(`${decryptedUsername}:${decryptedPassword}`); | ||
|
||
try { | ||
const axiosResponse: AxiosResponse<PageOfferDTO> = await vidisClient.getActivatedOffersByRegion( | ||
region, | ||
undefined, | ||
undefined, | ||
{ | ||
headers: { Authorization: `Basic ${basicAuthEncoded}` }, | ||
} | ||
); | ||
const offerItems: OfferDTO[] = axiosResponse.data.items ?? []; | ||
|
||
return offerItems; | ||
} catch (error: unknown) { | ||
if (isAxiosError(error)) { | ||
throw new AxiosErrorLoggable(error, 'VIDIS_GET_OFFER_ITEMS_FAILED'); | ||
} else { | ||
throw error; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.