From 6fb7ab10af398dcc81f032aec4d6baee9bfa9c11 Mon Sep 17 00:00:00 2001 From: Ilya Khait Date: Tue, 17 Dec 2024 15:57:31 +0000 Subject: [PATCH 1/4] Implemet serialization, repo & service, add tests --- .../application/DossiersService.test.ts | 47 +++++++++++ src/dossiers/application/DossiersService.ts | 18 ++++ src/dossiers/domain/DossierRecord.test.ts | 41 +++++++++ src/dossiers/domain/DossierRecord.ts | 52 ++++++++++++ .../infrastructure/DossiersRepository.test.ts | 84 +++++++++++++++++++ .../infrastructure/DossiersRepository.ts | 17 ++++ src/dossiers/ui/DossierDisplay.test.tsx | 0 src/dossiers/ui/DossiersDisplay.tsx | 0 8 files changed, 259 insertions(+) create mode 100644 src/dossiers/application/DossiersService.test.ts create mode 100644 src/dossiers/application/DossiersService.ts create mode 100644 src/dossiers/domain/DossierRecord.test.ts create mode 100644 src/dossiers/domain/DossierRecord.ts create mode 100644 src/dossiers/infrastructure/DossiersRepository.test.ts create mode 100644 src/dossiers/infrastructure/DossiersRepository.ts create mode 100644 src/dossiers/ui/DossierDisplay.test.tsx create mode 100644 src/dossiers/ui/DossiersDisplay.tsx diff --git a/src/dossiers/application/DossiersService.test.ts b/src/dossiers/application/DossiersService.test.ts new file mode 100644 index 000000000..3c22589d4 --- /dev/null +++ b/src/dossiers/application/DossiersService.test.ts @@ -0,0 +1,47 @@ +import { testDelegation, TestData } from 'test-support/utils' +import DossiersRepository from 'dossiers/infrastructure/DossiersRepository' +import DossierRecord from 'dossiers/domain/DossierRecord' +import DossiersService from 'dossiers/application/DossiersService' +import { stringify } from 'query-string' +import { ReferenceType } from 'bibliography/domain/Reference' +import { Provenances } from 'corpus/domain/provenance' +import { PeriodModifiers, Periods } from 'common/period' + +jest.mock('dossiers/infrastructure/DossiersRepository') +const dossiersRepository = new (DossiersRepository as jest.Mock)() + +const dossiersService = new DossiersService(dossiersRepository) + +const resultStub = { + id: 'test', + description: 'some desciption', + isApproximateDate: true, + yearRangeFrom: -500, + yearRangeTo: -470, + relatedKings: [10.2, 11], + provenance: Provenances.Assyria, + script: { + period: Periods['Neo-Assyrian'], + periodModifier: PeriodModifiers.None, + uncertain: false, + }, + references: ['EDITION' as ReferenceType, 'DISCUSSION' as ReferenceType], +} + +const query = { ids: ['test'] } +const entry = new DossierRecord(resultStub) + +const testData: TestData[] = [ + new TestData( + 'searchByIds', + [stringify(query)], + dossiersRepository.searchByIds, + [entry], + [stringify(query)], + Promise.resolve([entry]) + ), +] + +describe('DossiersService', () => { + testDelegation(dossiersService, testData) +}) diff --git a/src/dossiers/application/DossiersService.ts b/src/dossiers/application/DossiersService.ts new file mode 100644 index 000000000..ac02664d2 --- /dev/null +++ b/src/dossiers/application/DossiersService.ts @@ -0,0 +1,18 @@ +import DossiersRepository from 'dossiers/infrastructure/DossiersRepository' +import DossierRecord from 'dossiers/domain/DossierRecord' + +export interface DossiersSearch { + searchByIds(query: string): Promise +} + +export default class DossiersService implements DossiersSearch { + private readonly dossiersRepository: DossiersRepository + + constructor(afoRegisterRepository: DossiersRepository) { + this.dossiersRepository = afoRegisterRepository + } + + searchByIds(query: string): Promise { + return this.dossiersRepository.searchByIds(query) + } +} diff --git a/src/dossiers/domain/DossierRecord.test.ts b/src/dossiers/domain/DossierRecord.test.ts new file mode 100644 index 000000000..7ac5438da --- /dev/null +++ b/src/dossiers/domain/DossierRecord.test.ts @@ -0,0 +1,41 @@ +import DossierRecord from 'dossiers/domain/DossierRecord' +import { ReferenceType } from 'bibliography/domain/Reference' +import { PeriodModifiers, Periods } from 'common/period' +import { Provenances } from 'corpus/domain/provenance' + +describe('DossierRecord', () => { + const mockRecord = { + id: 'test', + description: 'some desciption', + isApproximateDate: true, + yearRangeFrom: -500, + yearRangeTo: -470, + relatedKings: [10.2, 11], + provenance: Provenances.Assyria, + script: { + period: Periods['Neo-Assyrian'], + periodModifier: PeriodModifiers.None, + uncertain: false, + }, + references: ['EDITION' as ReferenceType, 'DISCUSSION' as ReferenceType], + } + + describe('constructor', () => { + it('should initialize properties correctly', () => { + const record = new DossierRecord(mockRecord) + expect(record.id).toEqual('test') + expect(record.description).toEqual('some desciption') + expect(record.isApproximateDate).toEqual(true) + expect(record.yearRangeFrom).toEqual(-500) + expect(record.yearRangeTo).toEqual(-470) + expect(record.relatedKings).toEqual([10.2, 11]) + expect(record.provenance).toEqual(Provenances.Assyria) + expect(record.script).toEqual({ + period: Periods['Neo-Assyrian'], + periodModifier: PeriodModifiers.None, + uncertain: false, + }) + expect(record.references).toEqual(['EDITION', 'DISCUSSION']) + }) + }) +}) diff --git a/src/dossiers/domain/DossierRecord.ts b/src/dossiers/domain/DossierRecord.ts new file mode 100644 index 000000000..2045e2b00 --- /dev/null +++ b/src/dossiers/domain/DossierRecord.ts @@ -0,0 +1,52 @@ +import { immerable } from 'immer' +import { ReferenceType } from 'bibliography/domain/Reference' +import { Provenance } from 'corpus/domain/provenance' +import { Script } from 'fragmentarium/domain/fragment' + +interface DossierRecordData { + readonly id: string + readonly description?: string + readonly isApproximateDate?: boolean + readonly yearRangeFrom?: number + readonly yearRangeTo?: number + readonly relatedKings?: number[] + readonly provenance?: Provenance + readonly script?: Script + readonly references?: ReferenceType[] +} + +export default class DossierRecord { + [immerable] = true + + readonly id: string + readonly description?: string + readonly isApproximateDate: boolean + readonly yearRangeFrom?: number + readonly yearRangeTo?: number + readonly relatedKings: number[] + readonly provenance?: Provenance + readonly script?: Script + readonly references: ReferenceType[] + + constructor({ + id, + description, + isApproximateDate = false, + yearRangeFrom, + yearRangeTo, + relatedKings = [], + provenance, + script, + references = [], + }: DossierRecordData) { + this.id = id + this.description = description + this.isApproximateDate = isApproximateDate + this.yearRangeFrom = yearRangeFrom + this.yearRangeTo = yearRangeTo + this.relatedKings = relatedKings + this.provenance = provenance + this.script = script + this.references = references + } +} diff --git a/src/dossiers/infrastructure/DossiersRepository.test.ts b/src/dossiers/infrastructure/DossiersRepository.test.ts new file mode 100644 index 000000000..e68498838 --- /dev/null +++ b/src/dossiers/infrastructure/DossiersRepository.test.ts @@ -0,0 +1,84 @@ +import { testDelegation, TestData } from 'test-support/utils' +import DossiersRepository from 'dossiers/infrastructure/DossiersRepository' +import DossierRecord from 'dossiers/domain/DossierRecord' +import { stringify } from 'query-string' +import ApiClient from 'http/ApiClient' +import { PeriodModifiers, Periods } from 'common/period' +import { ReferenceType } from 'bibliography/domain/Reference' +import { Provenances } from 'corpus/domain/provenance' + +jest.mock('http/ApiClient') +jest.mock('dossiers/application/DossiersService') + +const apiClient = new (ApiClient as jest.Mock>)() +const dossiersRepository = new DossiersRepository(apiClient) + +const resultStub = { + id: 'test', + description: 'some description', + isApproximateDate: true, + yearRangeFrom: -500, + yearRangeTo: -470, + relatedKings: [10.2, 11], + provenance: Provenances.Assyria, + script: { + period: Periods['Neo-Assyrian'], + periodModifier: PeriodModifiers.None, + uncertain: false, + }, + references: ['EDITION' as ReferenceType, 'DISCUSSION' as ReferenceType], +} + +const query = { ids: ['test'] } +const record = new DossierRecord(resultStub) + +const testData: TestData[] = [ + new TestData( + 'searchByIds', + [stringify(query)], + apiClient.fetchJson, + [record], + [`/dossiers?${stringify(query)}`, false], + Promise.resolve([resultStub]) + ), +] + +describe('dossiersService', () => testDelegation(dossiersRepository, testData)) + +describe('DossiersRepository - search by ids', () => { + it('handles search without errors', async () => { + apiClient.fetchJson.mockResolvedValueOnce([resultStub]) + const response = await dossiersRepository.searchByIds(stringify(query)) + expect(response).toEqual([record]) + expect(apiClient.fetchJson).toHaveBeenCalledWith( + `/dossiers?${stringify(query)}`, + false + ) + }) + + it('handles different query strings', async () => { + const query2 = { id: 'test2' } + const resultStub2 = { + ...resultStub, + id: 'test2', + description: 'another description', + } + const record2 = new DossierRecord(resultStub2) + apiClient.fetchJson.mockResolvedValueOnce([resultStub, resultStub2]) + const response = await dossiersRepository.searchByIds(stringify(query2)) + expect(response).toEqual([record, record2]) + }) + + it('handles empty response', async () => { + apiClient.fetchJson.mockResolvedValueOnce([]) + const response = await dossiersRepository.searchByIds(stringify(query)) + expect(response).toEqual([]) + }) + + it('handles API errors', async () => { + apiClient.fetchJson.mockRejectedValueOnce(new Error('API Error')) + await expect( + dossiersRepository.searchByIds(stringify(query)) + ).rejects.toThrow('API Error') + }) +}) diff --git a/src/dossiers/infrastructure/DossiersRepository.ts b/src/dossiers/infrastructure/DossiersRepository.ts new file mode 100644 index 000000000..d77021c76 --- /dev/null +++ b/src/dossiers/infrastructure/DossiersRepository.ts @@ -0,0 +1,17 @@ +import DossierRecord from 'dossiers/domain/DossierRecord' +import Promise from 'bluebird' +import ApiClient from 'http/ApiClient' + +export default class DossiersRepository { + private readonly apiClient: ApiClient + + constructor(apiClient: ApiClient) { + this.apiClient = apiClient + } + + searchByIds(query: string): Promise { + return this.apiClient + .fetchJson(`/dossiers?${query}`, false) + .then((result) => result.map((data) => new DossierRecord(data))) + } +} diff --git a/src/dossiers/ui/DossierDisplay.test.tsx b/src/dossiers/ui/DossierDisplay.test.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/dossiers/ui/DossiersDisplay.tsx b/src/dossiers/ui/DossiersDisplay.tsx new file mode 100644 index 000000000..e69de29bb From 6d56e1568e48dad31ed538695d3101c86c2587ac Mon Sep 17 00:00:00 2001 From: Ilya Khait Date: Wed, 18 Dec 2024 18:04:29 +0000 Subject: [PATCH 2/4] Implement display & update (WiP) --- .../application/DossiersService.test.ts | 4 +- src/dossiers/application/DossiersService.ts | 6 +- src/dossiers/domain/DossierRecord.ts | 42 +++++++++++ .../infrastructure/DossiersRepository.test.ts | 22 +++--- .../infrastructure/DossiersRepository.ts | 5 +- src/dossiers/ui/DossiersDisplay.tsx | 69 +++++++++++++++++++ src/fragmentarium/domain/fragment.ts | 3 + src/fragmentarium/ui/info/Details.tsx | 4 ++ 8 files changed, 137 insertions(+), 18 deletions(-) diff --git a/src/dossiers/application/DossiersService.test.ts b/src/dossiers/application/DossiersService.test.ts index 3c22589d4..002304fe4 100644 --- a/src/dossiers/application/DossiersService.test.ts +++ b/src/dossiers/application/DossiersService.test.ts @@ -33,9 +33,9 @@ const entry = new DossierRecord(resultStub) const testData: TestData[] = [ new TestData( - 'searchByIds', + 'queryByIds', [stringify(query)], - dossiersRepository.searchByIds, + dossiersRepository.queryByIds, [entry], [stringify(query)], Promise.resolve([entry]) diff --git a/src/dossiers/application/DossiersService.ts b/src/dossiers/application/DossiersService.ts index ac02664d2..7a543c6a1 100644 --- a/src/dossiers/application/DossiersService.ts +++ b/src/dossiers/application/DossiersService.ts @@ -2,7 +2,7 @@ import DossiersRepository from 'dossiers/infrastructure/DossiersRepository' import DossierRecord from 'dossiers/domain/DossierRecord' export interface DossiersSearch { - searchByIds(query: string): Promise + queryByIds(query: string[]): Promise } export default class DossiersService implements DossiersSearch { @@ -12,7 +12,7 @@ export default class DossiersService implements DossiersSearch { this.dossiersRepository = afoRegisterRepository } - searchByIds(query: string): Promise { - return this.dossiersRepository.searchByIds(query) + queryByIds(query: string[]): Promise { + return this.dossiersRepository.queryByIds(query) } } diff --git a/src/dossiers/domain/DossierRecord.ts b/src/dossiers/domain/DossierRecord.ts index 2045e2b00..31ba8d2f9 100644 --- a/src/dossiers/domain/DossierRecord.ts +++ b/src/dossiers/domain/DossierRecord.ts @@ -49,4 +49,46 @@ export default class DossierRecord { this.script = script this.references = references } + + toMarkdownString(): string { + return `${this.YearsToMarkdownString()}` + } + + private YearsToMarkdownString(): string { + const yearRangeFrom = this.formatYear(this.yearRangeFrom) + const yearRangeTo = this.formatYear(this.yearRangeTo) + + if (!yearRangeFrom) { + return '' + } + + if (this.isApproximateDate) { + return this.formatApproximateRange(yearRangeFrom, yearRangeTo) + } + + return this.formatExactRange(yearRangeFrom, yearRangeTo) + } + + private formatYear(year: number | undefined): string { + if (year === undefined) return '' + const prefix = year < 0 ? 'BCE' : 'CE' + return `${Math.abs(year)} ${prefix}` + } + + private formatApproximateRange( + yearRangeFrom: string, + yearRangeTo: string + ): string { + if (yearRangeFrom === yearRangeTo || !yearRangeTo) { + return `ca. ${yearRangeFrom}` + } + return `ca. ${yearRangeFrom} - ${yearRangeTo}` + } + + private formatExactRange(yearRangeFrom: string, yearRangeTo: string): string { + if (yearRangeFrom === yearRangeTo || !yearRangeTo) { + return yearRangeFrom + } + return `${yearRangeFrom} - ${yearRangeTo}` + } } diff --git a/src/dossiers/infrastructure/DossiersRepository.test.ts b/src/dossiers/infrastructure/DossiersRepository.test.ts index e68498838..1943e5171 100644 --- a/src/dossiers/infrastructure/DossiersRepository.test.ts +++ b/src/dossiers/infrastructure/DossiersRepository.test.ts @@ -29,16 +29,16 @@ const resultStub = { references: ['EDITION' as ReferenceType, 'DISCUSSION' as ReferenceType], } -const query = { ids: ['test'] } +const query = ['test'] const record = new DossierRecord(resultStub) const testData: TestData[] = [ new TestData( - 'searchByIds', + 'queryByIds', [stringify(query)], apiClient.fetchJson, [record], - [`/dossiers?${stringify(query)}`, false], + [`/dossiers?${stringify({ ids: query })}`, false], Promise.resolve([resultStub]) ), ] @@ -48,16 +48,16 @@ describe('dossiersService', () => testDelegation(dossiersRepository, testData)) describe('DossiersRepository - search by ids', () => { it('handles search without errors', async () => { apiClient.fetchJson.mockResolvedValueOnce([resultStub]) - const response = await dossiersRepository.searchByIds(stringify(query)) + const response = dossiersRepository.queryByIds(query) expect(response).toEqual([record]) expect(apiClient.fetchJson).toHaveBeenCalledWith( - `/dossiers?${stringify(query)}`, + `/dossiers?${stringify({ ids: query })}`, false ) }) it('handles different query strings', async () => { - const query2 = { id: 'test2' } + const query2 = ['test2'] const resultStub2 = { ...resultStub, id: 'test2', @@ -65,20 +65,20 @@ describe('DossiersRepository - search by ids', () => { } const record2 = new DossierRecord(resultStub2) apiClient.fetchJson.mockResolvedValueOnce([resultStub, resultStub2]) - const response = await dossiersRepository.searchByIds(stringify(query2)) + const response = dossiersRepository.queryByIds(query2) expect(response).toEqual([record, record2]) }) it('handles empty response', async () => { apiClient.fetchJson.mockResolvedValueOnce([]) - const response = await dossiersRepository.searchByIds(stringify(query)) + const response = dossiersRepository.queryByIds(query) expect(response).toEqual([]) }) it('handles API errors', async () => { apiClient.fetchJson.mockRejectedValueOnce(new Error('API Error')) - await expect( - dossiersRepository.searchByIds(stringify(query)) - ).rejects.toThrow('API Error') + await expect(dossiersRepository.queryByIds(query)).rejects.toThrow( + 'API Error' + ) }) }) diff --git a/src/dossiers/infrastructure/DossiersRepository.ts b/src/dossiers/infrastructure/DossiersRepository.ts index d77021c76..735dc3940 100644 --- a/src/dossiers/infrastructure/DossiersRepository.ts +++ b/src/dossiers/infrastructure/DossiersRepository.ts @@ -1,6 +1,7 @@ import DossierRecord from 'dossiers/domain/DossierRecord' import Promise from 'bluebird' import ApiClient from 'http/ApiClient' +import { stringify } from 'query-string' export default class DossiersRepository { private readonly apiClient: ApiClient @@ -9,9 +10,9 @@ export default class DossiersRepository { this.apiClient = apiClient } - searchByIds(query: string): Promise { + queryByIds(query: string[]): Promise { return this.apiClient - .fetchJson(`/dossiers?${query}`, false) + .fetchJson(`/dossiers?${stringify({ ids: query })}`, false) .then((result) => result.map((data) => new DossierRecord(data))) } } diff --git a/src/dossiers/ui/DossiersDisplay.tsx b/src/dossiers/ui/DossiersDisplay.tsx index e69de29bb..88cf9a77d 100644 --- a/src/dossiers/ui/DossiersDisplay.tsx +++ b/src/dossiers/ui/DossiersDisplay.tsx @@ -0,0 +1,69 @@ +import React from 'react' +import DossierRecord from 'dossiers/domain/DossierRecord' +import MarkdownAndHtmlToHtml from 'common/MarkdownAndHtmlToHtml' +import { Fragment } from 'fragmentarium/domain/fragment' +import withData from 'http/withData' +import DossiersService from 'dossiers/application/DossiersService' +import _ from 'lodash' +import Bluebird from 'bluebird' + +export function DossierRecordDisplay({ + record, + index, +}: { + record: DossierRecord + index: string | number +}): JSX.Element { + return ( + + ) +} + +export function DossierRecordsListDisplay({ + data, + ...props +}: { + data: { records: readonly DossierRecord[] } +} & React.OlHTMLAttributes): JSX.Element { + const { records } = data + + if (records.length < 1) { + return <> + } + return ( +
    + {records.map((record, index) => ( +
  1. + +
  2. + ))} +
+ ) +} + +export default withData< + unknown, + { + dossiersService: DossiersService + fragment: Fragment + }, + { records: readonly DossierRecord[] } +>( + DossierRecordsListDisplay, + (props) => { + return Bluebird.resolve( + props.dossiersService + .queryByIds(props.fragment.dossiers.map((dossier) => dossier)) + .then((records) => ({ records })) + ) + }, + { + watch: (props) => [...props.fragment.dossiers], + filter: (props) => !_.isEmpty(props.fragment.dossiers), + defaultData: () => ({ records: [] }), + } +) diff --git a/src/fragmentarium/domain/fragment.ts b/src/fragmentarium/domain/fragment.ts index 645b4065b..8c036d74b 100644 --- a/src/fragmentarium/domain/fragment.ts +++ b/src/fragmentarium/domain/fragment.ts @@ -106,6 +106,7 @@ interface FragmentProps { archaeology?: Archaeology colophon?: Colophon authorizedScopes?: string[] + dossiers: readonly string[] } export class Fragment { @@ -135,6 +136,7 @@ export class Fragment { readonly script: Script, readonly externalNumbers: ExternalNumbers, readonly projects: ReadonlyArray, + readonly dossiers: readonly string[], readonly date?: MesopotamianDate, readonly datesInText?: ReadonlyArray, readonly archaeology?: Archaeology, @@ -167,6 +169,7 @@ export class Fragment { props.script, props.externalNumbers, props.projects, + props.dossiers, props.date, props.datesInText, props.archaeology, diff --git a/src/fragmentarium/ui/info/Details.tsx b/src/fragmentarium/ui/info/Details.tsx index 789eec8cc..8997ce8a6 100644 --- a/src/fragmentarium/ui/info/Details.tsx +++ b/src/fragmentarium/ui/info/Details.tsx @@ -13,6 +13,7 @@ import Bluebird from 'bluebird' import { MesopotamianDate } from 'chronology/domain/Date' import DatesInTextSelection from 'chronology/ui/DateEditor/DatesInTextSelection' import { DateRange, PartialDate } from 'fragmentarium/domain/archaeology' +import { FragmentDossierRecordsDisplay } from 'dossiers/ui/DossiersDisplay' interface Props { readonly fragment: Fragment @@ -194,6 +195,9 @@ function Details({ )}
  • {`Findspot: ${findspotString || '-'}`}
  • +
  • + +
  • Date: Thu, 19 Dec 2024 19:21:18 +0000 Subject: [PATCH 3/4] Update infrastructure (WiP) --- .../infrastructure/DossiersRepository.test.ts | 21 ++-- .../infrastructure/DossiersRepository.ts | 3 +- src/dossiers/ui/DossierDisplay.test.tsx | 105 ++++++++++++++++++ src/dossiers/ui/DossiersDisplay.tsx | 5 +- src/fragmentarium/domain/FragmentDtos.ts | 1 + src/test-support/fragment-fixtures.ts | 1 + src/test-support/test-fragment.ts | 2 + 7 files changed, 127 insertions(+), 11 deletions(-) diff --git a/src/dossiers/infrastructure/DossiersRepository.test.ts b/src/dossiers/infrastructure/DossiersRepository.test.ts index 1943e5171..a0eb01538 100644 --- a/src/dossiers/infrastructure/DossiersRepository.test.ts +++ b/src/dossiers/infrastructure/DossiersRepository.test.ts @@ -29,7 +29,7 @@ const resultStub = { references: ['EDITION' as ReferenceType, 'DISCUSSION' as ReferenceType], } -const query = ['test'] +const query = ['test', 'test2'] const record = new DossierRecord(resultStub) const testData: TestData[] = [ @@ -38,7 +38,7 @@ const testData: TestData[] = [ [stringify(query)], apiClient.fetchJson, [record], - [`/dossiers?${stringify({ ids: query })}`, false], + ['/dossiers?ids=0%3Dtest%261%3Dtest2', false], Promise.resolve([resultStub]) ), ] @@ -46,12 +46,15 @@ const testData: TestData[] = [ describe('dossiersService', () => testDelegation(dossiersRepository, testData)) describe('DossiersRepository - search by ids', () => { - it('handles search without errors', async () => { + it('handles search without errors', () => { apiClient.fetchJson.mockResolvedValueOnce([resultStub]) const response = dossiersRepository.queryByIds(query) - expect(response).toEqual([record]) + response.then((resolvedResponse) => { + expect(resolvedResponse).toEqual([record]) + }) + expect(apiClient.fetchJson).toHaveBeenCalledWith( - `/dossiers?${stringify({ ids: query })}`, + '/dossiers?ids=test&ids=test2', false ) }) @@ -66,13 +69,17 @@ describe('DossiersRepository - search by ids', () => { const record2 = new DossierRecord(resultStub2) apiClient.fetchJson.mockResolvedValueOnce([resultStub, resultStub2]) const response = dossiersRepository.queryByIds(query2) - expect(response).toEqual([record, record2]) + response.then((resolvedResponse) => { + expect(resolvedResponse).toEqual([record, record2]) + }) }) it('handles empty response', async () => { apiClient.fetchJson.mockResolvedValueOnce([]) const response = dossiersRepository.queryByIds(query) - expect(response).toEqual([]) + response.then((resolvedResponse) => { + expect(resolvedResponse).toEqual([]) + }) }) it('handles API errors', async () => { diff --git a/src/dossiers/infrastructure/DossiersRepository.ts b/src/dossiers/infrastructure/DossiersRepository.ts index 735dc3940..52d99abb6 100644 --- a/src/dossiers/infrastructure/DossiersRepository.ts +++ b/src/dossiers/infrastructure/DossiersRepository.ts @@ -11,8 +11,9 @@ export default class DossiersRepository { } queryByIds(query: string[]): Promise { + const queryString = stringify({ ids: query }) return this.apiClient - .fetchJson(`/dossiers?${stringify({ ids: query })}`, false) + .fetchJson(`/dossiers?${queryString}`, false) .then((result) => result.map((data) => new DossierRecord(data))) } } diff --git a/src/dossiers/ui/DossierDisplay.test.tsx b/src/dossiers/ui/DossierDisplay.test.tsx index e69de29bb..858f2daf3 100644 --- a/src/dossiers/ui/DossierDisplay.test.tsx +++ b/src/dossiers/ui/DossierDisplay.test.tsx @@ -0,0 +1,105 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import '@testing-library/jest-dom' +import DossierRecord from 'dossiers/domain/DossierRecord' +import DossiersService from 'dossiers/application/DossiersService' +import { Fragment } from 'fragmentarium/domain/fragment' +import { + DossierRecordDisplay, + DossierRecordsListDisplay, +} from './DossiersDisplay' +import Bluebird from 'bluebird' +import withData from 'http/withData' +import { Provenances } from 'corpus/domain/provenance' +import { PeriodModifiers, Periods } from 'common/period' +import { fragmentFactory } from 'test-support/fragment-fixtures' + +jest.mock('common/MarkdownAndHtmlToHtml', () => ({ + __esModule: true, + default: ({ markdownAndHtml }: { markdownAndHtml: string }) => ( +
    {markdownAndHtml}
    + ), +})) + +const mockRecord = new DossierRecord({ + id: 'test', + description: 'Test description', + isApproximateDate: true, + yearRangeFrom: -500, + yearRangeTo: -470, + relatedKings: [10.2, 11], + provenance: Provenances['Assyria'], + script: { + period: Periods['Neo-Assyrian'], + periodModifier: PeriodModifiers.None, + uncertain: false, + }, + references: ['EDITION', 'DISCUSSION'], +}) + +describe('DossierRecordDisplay', () => { + it('renders correctly with a record', () => { + render() + expect(screen.getByText(mockRecord.toMarkdownString())).toBeInTheDocument() + }) +}) + +describe('DossierRecordsListDisplay', () => { + it('renders an empty component when no records are present', () => { + render() + expect(screen.queryByRole('list')).not.toBeInTheDocument() + }) + + it('renders a list of records', () => { + const records = [ + mockRecord, + new DossierRecord({ ...mockRecord, id: 'test2' }), + ] + render() + + expect(screen.getAllByRole('listitem')).toHaveLength(records.length) + expect(screen.getAllByText('ca. 500 BCE - 470 BCE')).toHaveLength( + records.length + ) + }) +}) + +describe('withData HOC integration', () => { + it('fetches data and passes it to the wrapped component', async () => { + const mockDossiersService = { + queryByIds: jest.fn().mockResolvedValueOnce([mockRecord]), + } + const mockFragment = fragmentFactory.build({ + dossiers: ['test'], + }) + + const WrappedComponent = withData< + unknown, + { dossiersService: DossiersService; fragment: Fragment }, + { records: readonly DossierRecord[] } + >( + DossierRecordsListDisplay, + (props) => + Bluebird.resolve( + props.dossiersService + .queryByIds([...props.fragment.dossiers]) + .then((records) => ({ records })) + ), + { + watch: (props) => [...props.fragment.dossiers], + filter: (props) => !!props.fragment.dossiers.length, + defaultData: () => ({ records: [] }), + } + ) + + render( + + ) + + await screen.findByText(mockRecord.toMarkdownString()) + expect(mockDossiersService.queryByIds).toHaveBeenCalledWith(['test']) + }) +}) diff --git a/src/dossiers/ui/DossiersDisplay.tsx b/src/dossiers/ui/DossiersDisplay.tsx index 88cf9a77d..0e6741ab4 100644 --- a/src/dossiers/ui/DossiersDisplay.tsx +++ b/src/dossiers/ui/DossiersDisplay.tsx @@ -25,7 +25,6 @@ export function DossierRecordDisplay({ export function DossierRecordsListDisplay({ data, - ...props }: { data: { records: readonly DossierRecord[] } } & React.OlHTMLAttributes): JSX.Element { @@ -35,7 +34,7 @@ export function DossierRecordsListDisplay({ return <> } return ( -
      +
        {records.map((record, index) => (
      1. @@ -57,7 +56,7 @@ export default withData< (props) => { return Bluebird.resolve( props.dossiersService - .queryByIds(props.fragment.dossiers.map((dossier) => dossier)) + .queryByIds([...props.fragment.dossiers]) .then((records) => ({ records })) ) }, diff --git a/src/fragmentarium/domain/FragmentDtos.ts b/src/fragmentarium/domain/FragmentDtos.ts index 16600e15a..047c334dc 100644 --- a/src/fragmentarium/domain/FragmentDtos.ts +++ b/src/fragmentarium/domain/FragmentDtos.ts @@ -116,6 +116,7 @@ export default interface FragmentDto { script: ScriptDto externalNumbers: ExternalNumbers projects: readonly string[] + dossiers: readonly string[] date?: MesopotamianDateDto datesInText?: readonly MesopotamianDateDto[] archaeology?: Omit & { diff --git a/src/test-support/fragment-fixtures.ts b/src/test-support/fragment-fixtures.ts index 7ebc67c0b..2b297af12 100644 --- a/src/test-support/fragment-fixtures.ts +++ b/src/test-support/fragment-fixtures.ts @@ -86,6 +86,7 @@ export const fragmentFactory = Factory.define( externalNumbersFactory.build({}, { transient: { chance } }), associations.projects ?? [], + associations.dossiers ?? [], associations.date ?? new MesopotamianDate({ year: { value: '1' }, diff --git a/src/test-support/test-fragment.ts b/src/test-support/test-fragment.ts index aa4096d2b..556c60daf 100644 --- a/src/test-support/test-fragment.ts +++ b/src/test-support/test-fragment.ts @@ -415,6 +415,7 @@ export const fragmentDto: FragmentDto = { }, externalNumbers, projects: [], + dossiers: [], date: { year: { value: '1' }, month: { value: '1' }, @@ -518,6 +519,7 @@ export const fragment = new Fragment( }, externalNumbers, [], + [], new MesopotamianDate({ year: { value: '1' }, month: { value: '1' }, From 6fa70358906e75408a6aa79ed61693ab30aaa061 Mon Sep 17 00:00:00 2001 From: Ilya Khait Date: Mon, 23 Dec 2024 22:52:08 +0000 Subject: [PATCH 4/4] Update (WiP) --- src/dossiers/domain/DossierReference.ts | 4 +++ .../infrastructure/DossiersRepository.ts | 2 +- src/dossiers/ui/DossierDisplay.test.tsx | 27 +++---------------- src/dossiers/ui/DossiersDisplay.tsx | 8 ++++-- src/fragmentarium/domain/FragmentDtos.ts | 3 ++- src/fragmentarium/domain/fragment.ts | 5 ++-- .../ui/fragment/CuneiformFragment.tsx | 7 +++++ .../ui/fragment/FragmentView.tsx | 4 +++ src/fragmentarium/ui/info/Details.test.tsx | 6 +++++ src/fragmentarium/ui/info/Details.tsx | 10 +++++-- src/fragmentarium/ui/info/Info.tsx | 4 +++ src/index.tsx | 5 ++++ src/router/fragmentariumRoutes.tsx | 4 +++ src/router/router.tsx | 2 ++ src/test-support/AppDriver.tsx | 6 +++++ 15 files changed, 65 insertions(+), 32 deletions(-) create mode 100644 src/dossiers/domain/DossierReference.ts diff --git a/src/dossiers/domain/DossierReference.ts b/src/dossiers/domain/DossierReference.ts new file mode 100644 index 000000000..1c677dd90 --- /dev/null +++ b/src/dossiers/domain/DossierReference.ts @@ -0,0 +1,4 @@ +export interface DossierReference { + readonly dossierId: string + readonly isUncertain?: boolean +} diff --git a/src/dossiers/infrastructure/DossiersRepository.ts b/src/dossiers/infrastructure/DossiersRepository.ts index 52d99abb6..8fa947430 100644 --- a/src/dossiers/infrastructure/DossiersRepository.ts +++ b/src/dossiers/infrastructure/DossiersRepository.ts @@ -11,7 +11,7 @@ export default class DossiersRepository { } queryByIds(query: string[]): Promise { - const queryString = stringify({ ids: query }) + const queryString = stringify({ ids: query }, { arrayFormat: 'index' }) return this.apiClient .fetchJson(`/dossiers?${queryString}`, false) .then((result) => result.map((data) => new DossierRecord(data))) diff --git a/src/dossiers/ui/DossierDisplay.test.tsx b/src/dossiers/ui/DossierDisplay.test.tsx index 858f2daf3..c3e9b1243 100644 --- a/src/dossiers/ui/DossierDisplay.test.tsx +++ b/src/dossiers/ui/DossierDisplay.test.tsx @@ -4,12 +4,10 @@ import '@testing-library/jest-dom' import DossierRecord from 'dossiers/domain/DossierRecord' import DossiersService from 'dossiers/application/DossiersService' import { Fragment } from 'fragmentarium/domain/fragment' -import { +import FragmentDossierRecordsDisplay, { DossierRecordDisplay, DossierRecordsListDisplay, } from './DossiersDisplay' -import Bluebird from 'bluebird' -import withData from 'http/withData' import { Provenances } from 'corpus/domain/provenance' import { PeriodModifiers, Periods } from 'common/period' import { fragmentFactory } from 'test-support/fragment-fixtures' @@ -70,30 +68,11 @@ describe('withData HOC integration', () => { queryByIds: jest.fn().mockResolvedValueOnce([mockRecord]), } const mockFragment = fragmentFactory.build({ - dossiers: ['test'], + dossiers: [{ dossierId: 'test', isUncertain: true }], }) - const WrappedComponent = withData< - unknown, - { dossiersService: DossiersService; fragment: Fragment }, - { records: readonly DossierRecord[] } - >( - DossierRecordsListDisplay, - (props) => - Bluebird.resolve( - props.dossiersService - .queryByIds([...props.fragment.dossiers]) - .then((records) => ({ records })) - ), - { - watch: (props) => [...props.fragment.dossiers], - filter: (props) => !!props.fragment.dossiers.length, - defaultData: () => ({ records: [] }), - } - ) - render( - diff --git a/src/dossiers/ui/DossiersDisplay.tsx b/src/dossiers/ui/DossiersDisplay.tsx index 0e6741ab4..76a9758eb 100644 --- a/src/dossiers/ui/DossiersDisplay.tsx +++ b/src/dossiers/ui/DossiersDisplay.tsx @@ -44,7 +44,7 @@ export function DossierRecordsListDisplay({ ) } -export default withData< +const FragmentDossierRecordsDisplay = withData< unknown, { dossiersService: DossiersService @@ -56,7 +56,9 @@ export default withData< (props) => { return Bluebird.resolve( props.dossiersService - .queryByIds([...props.fragment.dossiers]) + .queryByIds([ + ...props.fragment.dossiers.map((record) => record.dossierId), + ]) .then((records) => ({ records })) ) }, @@ -66,3 +68,5 @@ export default withData< defaultData: () => ({ records: [] }), } ) + +export default FragmentDossierRecordsDisplay diff --git a/src/fragmentarium/domain/FragmentDtos.ts b/src/fragmentarium/domain/FragmentDtos.ts index 047c334dc..dbffa8065 100644 --- a/src/fragmentarium/domain/FragmentDtos.ts +++ b/src/fragmentarium/domain/FragmentDtos.ts @@ -11,6 +11,7 @@ import { import { ArchaeologyDto } from './archaeologyDtos' import { MuseumKey } from './museum' import { ColophonDto } from 'fragmentarium/domain/Colophon' +import { DossierReference } from 'dossiers/domain/DossierReference' interface MeasureDto { value?: number @@ -116,7 +117,7 @@ export default interface FragmentDto { script: ScriptDto externalNumbers: ExternalNumbers projects: readonly string[] - dossiers: readonly string[] + dossiers: readonly DossierReference[] date?: MesopotamianDateDto datesInText?: readonly MesopotamianDateDto[] archaeology?: Omit & { diff --git a/src/fragmentarium/domain/fragment.ts b/src/fragmentarium/domain/fragment.ts index 8c036d74b..52e0f166f 100644 --- a/src/fragmentarium/domain/fragment.ts +++ b/src/fragmentarium/domain/fragment.ts @@ -20,6 +20,7 @@ import { ResearchProject } from 'research-projects/researchProject' import { MesopotamianDate } from 'chronology/domain/Date' import { Archaeology } from './archaeology' import { Colophon } from 'fragmentarium/domain/Colophon' +import { DossierReference } from 'dossiers/domain/DossierReference' export interface FragmentInfo { readonly number: string @@ -106,7 +107,7 @@ interface FragmentProps { archaeology?: Archaeology colophon?: Colophon authorizedScopes?: string[] - dossiers: readonly string[] + dossiers: ReadonlyArray } export class Fragment { @@ -136,7 +137,7 @@ export class Fragment { readonly script: Script, readonly externalNumbers: ExternalNumbers, readonly projects: ReadonlyArray, - readonly dossiers: readonly string[], + readonly dossiers: ReadonlyArray, readonly date?: MesopotamianDate, readonly datesInText?: ReadonlyArray, readonly archaeology?: Archaeology, diff --git a/src/fragmentarium/ui/fragment/CuneiformFragment.tsx b/src/fragmentarium/ui/fragment/CuneiformFragment.tsx index aa6625ce9..cc60d9a78 100644 --- a/src/fragmentarium/ui/fragment/CuneiformFragment.tsx +++ b/src/fragmentarium/ui/fragment/CuneiformFragment.tsx @@ -16,11 +16,13 @@ import ErrorBoundary from 'common/ErrorBoundary' import { FindspotService } from 'fragmentarium/application/FindspotService' import AfoRegisterService from 'afo-register/application/AfoRegisterService' import { EditorTabs } from 'fragmentarium/ui/fragment/CuneiformFragmentEditor' +import DossiersService from 'dossiers/application/DossiersService' type CuneiformFragmentProps = { fragment: Fragment fragmentService: FragmentService fragmentSearchService: FragmentSearchService + dossiersService: DossiersService afoRegisterService: AfoRegisterService wordService: WordService findspotService: FindspotService @@ -35,6 +37,7 @@ const CuneiformFragment: FunctionComponent = ({ fragment, fragmentService, fragmentSearchService, + dossiersService, afoRegisterService, wordService, findspotService, @@ -53,6 +56,7 @@ const CuneiformFragment: FunctionComponent = ({ @@ -97,6 +101,7 @@ type ControllerProps = { fragment: Fragment fragmentService: FragmentService fragmentSearchService: FragmentSearchService + dossiersService: DossiersService afoRegisterService: AfoRegisterService wordService: WordService findspotService: FindspotService @@ -108,6 +113,7 @@ const CuneiformFragmentController: FunctionComponent = ({ fragment, fragmentService, fragmentSearchService, + dossiersService, afoRegisterService, wordService, findspotService, @@ -145,6 +151,7 @@ const CuneiformFragmentController: FunctionComponent = ({ fragment={currentFragment} fragmentService={fragmentService} fragmentSearchService={fragmentSearchService} + dossiersService={dossiersService} afoRegisterService={afoRegisterService} wordService={wordService} findspotService={findspotService} diff --git a/src/fragmentarium/ui/fragment/FragmentView.tsx b/src/fragmentarium/ui/fragment/FragmentView.tsx index 27bac7a42..6a6df868f 100644 --- a/src/fragmentarium/ui/fragment/FragmentView.tsx +++ b/src/fragmentarium/ui/fragment/FragmentView.tsx @@ -19,6 +19,7 @@ import { Session } from 'auth/Session' import { HeadTags } from 'router/head' import { FindspotService } from 'fragmentarium/application/FindspotService' import AfoRegisterService from 'afo-register/application/AfoRegisterService' +import DossiersService from 'dossiers/application/DossiersService' function TagSignsButton({ number, @@ -40,6 +41,7 @@ type Props = { fragment: Fragment fragmentService: FragmentService fragmentSearchService: FragmentSearchService + dossiersService: DossiersService wordService: WordService findspotService: FindspotService afoRegisterService: AfoRegisterService @@ -67,6 +69,7 @@ function FragmentView({ fragment, fragmentService, fragmentSearchService, + dossiersService, afoRegisterService, wordService, findspotService, @@ -109,6 +112,7 @@ function FragmentView({ fragment={fragment} fragmentService={fragmentService} fragmentSearchService={fragmentSearchService} + dossiersService={dossiersService} wordService={wordService} findspotService={findspotService} activeFolio={activeFolio} diff --git a/src/fragmentarium/ui/info/Details.test.tsx b/src/fragmentarium/ui/info/Details.test.tsx index 28c1a2f4c..d19301f9f 100644 --- a/src/fragmentarium/ui/info/Details.test.tsx +++ b/src/fragmentarium/ui/info/Details.test.tsx @@ -19,13 +19,18 @@ import { joinFactory } from 'test-support/join-fixtures' import { PartialDate } from 'fragmentarium/domain/archaeology' import { Periods } from 'common/period' import FragmentService from 'fragmentarium/application/FragmentService' +import DossiersService from 'dossiers/application/DossiersService' jest.mock('fragmentarium/application/FragmentService') const MockFragmentService = FragmentService as jest.Mock< jest.Mocked > +const MockDossiersService = DossiersService as jest.Mock< + jest.Mocked +> const fragmentService = new MockFragmentService() +const dossiersService = new MockDossiersService() const updateGenres = jest.fn() const updateScript = jest.fn() @@ -44,6 +49,7 @@ async function renderDetails() { updateDate={updateDate} updateDatesInText={updateDatesInText} fragmentService={fragmentService} + dossiersService={dossiersService} /> ) diff --git a/src/fragmentarium/ui/info/Details.tsx b/src/fragmentarium/ui/info/Details.tsx index 8997ce8a6..cc823e7b3 100644 --- a/src/fragmentarium/ui/info/Details.tsx +++ b/src/fragmentarium/ui/info/Details.tsx @@ -13,7 +13,8 @@ import Bluebird from 'bluebird' import { MesopotamianDate } from 'chronology/domain/Date' import DatesInTextSelection from 'chronology/ui/DateEditor/DatesInTextSelection' import { DateRange, PartialDate } from 'fragmentarium/domain/archaeology' -import { FragmentDossierRecordsDisplay } from 'dossiers/ui/DossiersDisplay' +import FragmentDossierRecordsDisplay from 'dossiers/ui/DossiersDisplay' +import DossiersService from 'dossiers/application/DossiersService' interface Props { readonly fragment: Fragment @@ -153,6 +154,7 @@ interface DetailsProps { datesInText: readonly MesopotamianDate[] ) => Bluebird readonly fragmentService: FragmentService + readonly dossiersService: DossiersService } function Details({ @@ -162,6 +164,7 @@ function Details({ updateDate, updateDatesInText, fragmentService, + dossiersService, }: DetailsProps): JSX.Element { const findspotString = fragment.archaeology?.findspot?.toString() return ( @@ -196,7 +199,10 @@ function Details({
      2. {`Findspot: ${findspotString || '-'}`}
      3. - +
      4. ) => void } @@ -29,6 +31,7 @@ interface Props { export default function Info({ fragment, fragmentService, + dossiersService, afoRegisterService, onSave, }: Props): JSX.Element { @@ -49,6 +52,7 @@ export default function Info({ updateDate={updateDate} updateDatesInText={updateDatesInText} fragmentService={fragmentService} + dossiersService={dossiersService} />
        diff --git a/src/index.tsx b/src/index.tsx index 8998d7064..18c4f906a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -31,6 +31,8 @@ import AfoRegisterService from 'afo-register/application/AfoRegisterService' import './index.sass' import { FindspotService } from 'fragmentarium/application/FindspotService' import { ApiFindspotRepository } from 'fragmentarium/infrastructure/FindspotRepository' +import DossiersService from 'dossiers/application/DossiersService' +import DossiersRepository from 'dossiers/infrastructure/DossiersRepository' if (process.env.REACT_APP_SENTRY_DSN && process.env.NODE_ENV) { SentryErrorReporter.init( @@ -64,6 +66,7 @@ function InjectedApp(): JSX.Element { const bibliographyRepository = new BibliographyRepository(apiClient) const afoRegisterRepository = new AfoRegisterRepository(apiClient) const findspotRepository = new ApiFindspotRepository(apiClient) + const dossiersRepository = new DossiersRepository(apiClient) const bibliographyService = new BibliographyService(bibliographyRepository) const fragmentService = new FragmentService( @@ -87,6 +90,7 @@ function InjectedApp(): JSX.Element { bibliographyService ) const afoRegisterService = new AfoRegisterService(afoRegisterRepository) + const dossiersService = new DossiersService(dossiersRepository) const findspotService = new FindspotService(findspotRepository) return ( ) diff --git a/src/router/fragmentariumRoutes.tsx b/src/router/fragmentariumRoutes.tsx index a5df00dff..0a9aeea14 100644 --- a/src/router/fragmentariumRoutes.tsx +++ b/src/router/fragmentariumRoutes.tsx @@ -22,6 +22,7 @@ import BibliographyService from 'bibliography/application/BibliographyService' import { FindspotService } from 'fragmentarium/application/FindspotService' import AfoRegisterService from 'afo-register/application/AfoRegisterService' import NotFoundPage from 'NotFoundPage' +import DossiersService from 'dossiers/application/DossiersService' function parseStringParam(location: Location, param: string): string | null { const value = parse(location.search)[param] return _.isArray(value) ? value.join('') : value @@ -51,6 +52,7 @@ export default function FragmentariumRoutes({ fragmentService, fragmentSearchService, afoRegisterService, + dossiersService, textService, wordService, findspotService, @@ -65,6 +67,7 @@ export default function FragmentariumRoutes({ wordService: WordService findspotService: FindspotService afoRegisterService: AfoRegisterService + dossiersService: DossiersService signService: SignService bibliographyService: BibliographyService fragmentSlugs?: FragmentSlugs @@ -141,6 +144,7 @@ export default function FragmentariumRoutes({ wordService={wordService} findspotService={findspotService} afoRegisterService={afoRegisterService} + dossiersService={dossiersService} session={session} {...parseFragmentParams(match, location)} /> diff --git a/src/router/router.tsx b/src/router/router.tsx index 871534cf4..f927ce0eb 100644 --- a/src/router/router.tsx +++ b/src/router/router.tsx @@ -32,6 +32,7 @@ import { HelmetProvider } from 'react-helmet-async' import { FindspotService } from 'fragmentarium/application/FindspotService' import Footer from 'Footer' import './router.sass' +import DossiersService from 'dossiers/application/DossiersService' export interface Services { wordService: WordService @@ -43,6 +44,7 @@ export interface Services { markupService: MarkupService cachedMarkupService: CachedMarkupService afoRegisterService: AfoRegisterService + dossiersService: DossiersService findspotService: FindspotService } diff --git a/src/test-support/AppDriver.tsx b/src/test-support/AppDriver.tsx index dc04e688c..ac7b85f94 100644 --- a/src/test-support/AppDriver.tsx +++ b/src/test-support/AppDriver.tsx @@ -35,6 +35,8 @@ import AfoRegisterService from 'afo-register/application/AfoRegisterService' import { FindspotService } from 'fragmentarium/application/FindspotService' import { ApiFindspotRepository } from 'fragmentarium/infrastructure/FindspotRepository' import FakeApi from 'test-support/FakeApi' +import DossiersService from 'dossiers/application/DossiersService' +import DossiersRepository from 'dossiers/infrastructure/DossiersRepository' export function getServices( api: any = FakeApi @@ -48,6 +50,7 @@ export function getServices( markupService: MarkupService cachedMarkupService: CachedMarkupService afoRegisterService: AfoRegisterService + dossiersService: DossiersService findspotService: FindspotService } { const wordRepository = new WordRepository(api) @@ -73,10 +76,12 @@ export function getServices( ) const signsRepository = new SignRepository(api) const afoRegisterRepository = new AfoRegisterRepository(api) + const dossiersRepository = new DossiersRepository(api) const signService = new SignService(signsRepository) const markupService = new MarkupService(api, bibliographyService) const cachedMarkupService = new CachedMarkupService(api, bibliographyService) const afoRegisterService = new AfoRegisterService(afoRegisterRepository) + const dossiersService = new DossiersService(dossiersRepository) const findspotService = new FindspotService(findspotRepository) return { signService, @@ -88,6 +93,7 @@ export function getServices( markupService, cachedMarkupService, afoRegisterService, + dossiersService, findspotService, } }