Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dossiers #516

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/dossiers/application/DossiersService.test.ts
Original file line number Diff line number Diff line change
@@ -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<DossiersService>[] = [
new TestData(
'queryByIds',
[stringify(query)],
dossiersRepository.queryByIds,
[entry],
[stringify(query)],
Promise.resolve([entry])
),
]

describe('DossiersService', () => {
testDelegation(dossiersService, testData)
})
18 changes: 18 additions & 0 deletions src/dossiers/application/DossiersService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import DossiersRepository from 'dossiers/infrastructure/DossiersRepository'
import DossierRecord from 'dossiers/domain/DossierRecord'

export interface DossiersSearch {
queryByIds(query: string[]): Promise<readonly DossierRecord[]>
}

export default class DossiersService implements DossiersSearch {
private readonly dossiersRepository: DossiersRepository

constructor(afoRegisterRepository: DossiersRepository) {
this.dossiersRepository = afoRegisterRepository
}

queryByIds(query: string[]): Promise<readonly DossierRecord[]> {
return this.dossiersRepository.queryByIds(query)
}
}
41 changes: 41 additions & 0 deletions src/dossiers/domain/DossierRecord.test.ts
Original file line number Diff line number Diff line change
@@ -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'])
})
})
})
94 changes: 94 additions & 0 deletions src/dossiers/domain/DossierRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
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
}

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}`
}
}
4 changes: 4 additions & 0 deletions src/dossiers/domain/DossierReference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface DossierReference {
readonly dossierId: string
readonly isUncertain?: boolean
}
91 changes: 91 additions & 0 deletions src/dossiers/infrastructure/DossiersRepository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
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<jest.Mocked<ApiClient>>)()
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 = ['test', 'test2']
const record = new DossierRecord(resultStub)

const testData: TestData<DossiersRepository>[] = [
new TestData(
'queryByIds',
[stringify(query)],
apiClient.fetchJson,
[record],
['/dossiers?ids=0%3Dtest%261%3Dtest2', false],
Promise.resolve([resultStub])
),
]

describe('dossiersService', () => testDelegation(dossiersRepository, testData))

describe('DossiersRepository - search by ids', () => {
it('handles search without errors', () => {
apiClient.fetchJson.mockResolvedValueOnce([resultStub])
const response = dossiersRepository.queryByIds(query)
response.then((resolvedResponse) => {
expect(resolvedResponse).toEqual([record])
})

expect(apiClient.fetchJson).toHaveBeenCalledWith(
'/dossiers?ids=test&ids=test2',
false
)
})

it('handles different query strings', async () => {
const query2 = ['test2']
const resultStub2 = {
...resultStub,
id: 'test2',
description: 'another description',
}
const record2 = new DossierRecord(resultStub2)
apiClient.fetchJson.mockResolvedValueOnce([resultStub, resultStub2])
const response = dossiersRepository.queryByIds(query2)
response.then((resolvedResponse) => {
expect(resolvedResponse).toEqual([record, record2])
})
})

it('handles empty response', async () => {
apiClient.fetchJson.mockResolvedValueOnce([])
const response = dossiersRepository.queryByIds(query)
response.then((resolvedResponse) => {
expect(resolvedResponse).toEqual([])
})
})

it('handles API errors', async () => {
apiClient.fetchJson.mockRejectedValueOnce(new Error('API Error'))
await expect(dossiersRepository.queryByIds(query)).rejects.toThrow(
'API Error'
)
})
})
19 changes: 19 additions & 0 deletions src/dossiers/infrastructure/DossiersRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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

constructor(apiClient: ApiClient) {
this.apiClient = apiClient
}

queryByIds(query: string[]): Promise<DossierRecord[]> {
const queryString = stringify({ ids: query }, { arrayFormat: 'index' })
return this.apiClient
.fetchJson(`/dossiers?${queryString}`, false)
.then((result) => result.map((data) => new DossierRecord(data)))
}
}
Loading
Loading