diff --git a/src/services/omdbEnrichedDataService.spec.ts b/src/services/omdbEnrichedDataService.spec.ts index a739cfb..cd4b071 100644 --- a/src/services/omdbEnrichedDataService.spec.ts +++ b/src/services/omdbEnrichedDataService.spec.ts @@ -395,4 +395,79 @@ describe('createOmdbEnrichedDataService', () => { expect(result).toBeUndefined(); }); + + it('returns correct search results for writer = George Lucas', async () => { + const service = await servicePromise; + const result = service.getSearchResults({ writers: 'George Lucas' }); + + expect(result).toMatchObject([ + { title: 'Indiana Jones and the Last Crusade' }, + { title: 'Star Wars: Episode IV - A New Hope' }, + ]); + }); + + it('returns correct search results for writer = George Lucas and actor = Harrison Ford', async () => { + const service = await servicePromise; + const result = service.getSearchResults({ + actors: 'Harrison Ford', + writers: 'George Lucas', + }); + + expect(result).toMatchObject([ + { title: 'Indiana Jones and the Last Crusade' }, + { title: 'Star Wars: Episode IV - A New Hope' }, + ]); + }); + + it('returns correct search results for writer = George Lucas and actor = Mark Hamill', async () => { + const service = await servicePromise; + const result = service.getSearchResults({ + actors: 'Mark Hamill', + writers: 'George Lucas', + }); + + expect(result).toMatchObject([ + { title: 'Star Wars: Episode IV - A New Hope' }, + ]); + }); + + it('returns correct search results for title = The Jungle Book', async () => { + const service = await servicePromise; + const result = service.getSearchResults({ title: 'The Jungle Book' }); + + expect(result).toMatchObject([{ title: 'The Jungle Book' }]); + }); + + it('returns correct search results for local title = Das Dschungelbuch', async () => { + const service = await servicePromise; + const result = service.getSearchResults({ + localTitle: 'Das Dschungelbuch', + }); + + expect(result).toMatchObject([{ title: 'The Jungle Book' }]); + }); + + it('returns correct search results for production year = 1967', async () => { + const service = await servicePromise; + const result = service.getSearchResults({ productionYear: 1967 }); + + expect(result).toMatchObject([{ title: 'The Jungle Book' }]); + }); + + it('returns empty search results for writer = George Lucas and actor = Bruce Willis', async () => { + const service = await servicePromise; + const result = service.getSearchResults({ + actors: 'Bruce Willis', + writers: 'George Lucas', + }); + + expect(result).toMatchObject([]); + }); + + it('returns empty search results for title = Blood Quantum', async () => { + const service = await servicePromise; + const result = service.getSearchResults({ title: 'Blood Quantum' }); + + expect(result).toMatchObject([]); + }); }); diff --git a/src/services/omdbEnrichedDataService.ts b/src/services/omdbEnrichedDataService.ts index 3a1747b..dd87710 100644 --- a/src/services/omdbEnrichedDataService.ts +++ b/src/services/omdbEnrichedDataService.ts @@ -3,6 +3,7 @@ import type { InternalProvider, MovieData, OmdbProvider, + SearchFilters, } from '../types'; export const createOmdbEnrichedDataService = async ( @@ -37,5 +38,26 @@ export const createOmdbEnrichedDataService = async ( ), getMetadataByImdbId: (requestedId) => mergedMetadataList.find(({ imdbId }) => imdbId === requestedId), + getSearchResults: (filters) => { + const nonEmptyFilters = Object.entries(filters).filter( + ([, filterValue]) => filterValue !== undefined, + ); + + return mergedMetadataList.filter((metadata) => { + return nonEmptyFilters.every(([fieldName, filterValue]) => { + const metadataValue = + metadata[fieldName as keyof SearchFilters]; + if (Array.isArray(metadataValue)) { + // filterValue here is filters[fieldName], and is not undefined (because it comes from nonEmptyFilters), + // so it's guaranteed to have a matching type because of SearchFilters / GenericSearchFilters definition, + // but this logic is too complicated for TS to prove it automatically. + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument + return metadataValue.includes(filterValue as any); + } else { + return metadataValue === filterValue; + } + }); + }); + }, }; }; diff --git a/src/types.ts b/src/types.ts index 539d336..6ee9e78 100644 --- a/src/types.ts +++ b/src/types.ts @@ -75,6 +75,30 @@ export type NormalizedInternalData = Omit< | 'writers' >; +type SearchFilterValue = TDataValue extends (infer U)[] + ? U + : TDataValue; + +type GenericSearchFilters = Partial<{ + [TKey in keyof MovieData as TKey extends TKeys ? TKey : never]: + | SearchFilterValue> + | undefined; +}>; + +export type SearchFilters = GenericSearchFilters< + | 'actors' + | 'availableLanguages' + | 'directors' + | 'localTitle' + | 'originalLanguage' + | 'productionCountries' + | 'productionYear' + | 'studios' + | 'title' + | 'type' + | 'writers' +>; + export type InternalProvider = { getMetadataByInternalId( internalId: number, @@ -92,4 +116,5 @@ export type OmdbProvider = { export type EnrichedDataService = { getMetadataByInternalId(internalId: number): MovieData | undefined; getMetadataByImdbId(imdbId: string): MovieData | undefined; + getSearchResults(filters: SearchFilters): MovieData[]; };