-
Notifications
You must be signed in to change notification settings - Fork 10
Use Case for File Citation Formats #446
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| export enum FileCitationFormat { | ||
| ENDNOTE = 'EndNote', | ||
| RIS = 'RIS', | ||
| BIBTEX = 'BibTeX', | ||
| CSL = 'CSL', | ||
| INTERNAL = 'Internal' | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { UseCase } from '../../../core/domain/useCases/UseCase' | ||
| import { IFilesRepository } from '../repositories/IFilesRepository' | ||
| import { FileCitationFormat } from '../models/FileCitationFormat' | ||
|
|
||
| export class GetFileCitationByFormat implements UseCase<string> { | ||
| private filesRepository: IFilesRepository | ||
|
|
||
| constructor(filesRepository: IFilesRepository) { | ||
| this.filesRepository = filesRepository | ||
| } | ||
|
|
||
| /** | ||
| * Returns the File citation in the requested format (EndNote XML, RIS, BibTeX, CSL JSON, or Internal HTML). | ||
| * | ||
| * @param {number | string} [fileId] - The File identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). | ||
| * @param {FileCitationFormat} [format] - The citation format to return. | ||
| * @returns {Promise<string>} | ||
| */ | ||
| async execute(fileId: number | string, format: FileCitationFormat): Promise<string> { | ||
| return await this.filesRepository.getFileCitationByFormat(fileId, format) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,6 @@ | ||||||||||
| POSTGRES_VERSION=17 | ||||||||||
| DATAVERSE_DB_USER=dataverse | ||||||||||
| SOLR_VERSION=9.8.0 | ||||||||||
| DATAVERSE_IMAGE_REGISTRY=docker.io | ||||||||||
| DATAVERSE_IMAGE_TAG=unstable | ||||||||||
| DATAVERSE_IMAGE_REGISTRY=ghcr.io | ||||||||||
| DATAVERSE_IMAGE_TAG=11733-api-get-file-citation-format | ||||||||||
|
Comment on lines
+4
to
+5
|
||||||||||
| DATAVERSE_IMAGE_REGISTRY=ghcr.io | |
| DATAVERSE_IMAGE_TAG=11733-api-get-file-citation-format | |
| DATAVERSE_IMAGE_REGISTRY=docker.io | |
| DATAVERSE_IMAGE_TAG=unstable |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| import { | ||
| ApiConfig, | ||
| createDataset, | ||
| CreatedDatasetIdentifiers, | ||
| FileCitationFormat, | ||
| getDatasetFiles, | ||
| getFileCitationByFormat, | ||
| ReadError | ||
| } from '../../../src' | ||
| import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' | ||
| import { | ||
| createCollectionViaApi, | ||
| deleteCollectionViaApi | ||
| } from '../../testHelpers/collections/collectionHelper' | ||
| import { deleteUnpublishedDatasetViaApi } from '../../testHelpers/datasets/datasetHelper' | ||
| import { uploadFileViaApi } from '../../testHelpers/files/filesHelper' | ||
| import { TestConstants } from '../../testHelpers/TestConstants' | ||
|
|
||
| describe('execute', () => { | ||
| const testCollectionAlias = 'getFileCitationByFormatFunctionalTest' | ||
| const testTextFile1Name = 'test-file-1.txt' | ||
| let testDatasetIds: CreatedDatasetIdentifiers | ||
|
|
||
| beforeAll(async () => { | ||
| ApiConfig.init( | ||
| TestConstants.TEST_API_URL, | ||
| DataverseApiAuthMechanism.API_KEY, | ||
| process.env.TEST_API_KEY | ||
| ) | ||
| await createCollectionViaApi(testCollectionAlias) | ||
|
|
||
| try { | ||
| testDatasetIds = await createDataset.execute( | ||
| TestConstants.TEST_NEW_DATASET_DTO, | ||
| testCollectionAlias | ||
| ) | ||
| } catch (error) { | ||
| throw new Error('Tests beforeAll(): Error while creating test dataset') | ||
| } | ||
|
|
||
| await uploadFileViaApi(testDatasetIds.numericId, testTextFile1Name).catch(() => { | ||
| throw new Error(`Tests beforeAll(): Error while uploading file ${testTextFile1Name}`) | ||
| }) | ||
| }) | ||
|
|
||
| afterAll(async () => { | ||
| try { | ||
| await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) | ||
| } catch (error) { | ||
| throw new Error('Tests afterAll(): Error while deleting test dataset') | ||
| } | ||
|
|
||
| try { | ||
| await deleteCollectionViaApi(testCollectionAlias) | ||
| } catch (error) { | ||
| throw new Error('Tests afterAll(): Error while deleting test collection') | ||
| } | ||
| }) | ||
|
|
||
| const getTestFileId = async (): Promise<number> => { | ||
| const datasetFiles = await getDatasetFiles.execute(testDatasetIds.numericId) | ||
| return datasetFiles.files[0].id | ||
| } | ||
|
|
||
| test('should successfully get file citation in EndNote (XML) format', async () => { | ||
| const fileId = await getTestFileId() | ||
|
|
||
| const citation = await getFileCitationByFormat.execute(fileId, FileCitationFormat.ENDNOTE) | ||
|
|
||
| expect(typeof citation).toBe('string') | ||
| expect(citation.trimStart()).toMatch(/^<\?xml/) | ||
| }) | ||
|
|
||
| test('should successfully get file citation in RIS (plain text) format', async () => { | ||
| const fileId = await getTestFileId() | ||
|
|
||
| const citation = await getFileCitationByFormat.execute(fileId, FileCitationFormat.RIS) | ||
|
|
||
| expect(typeof citation).toBe('string') | ||
| // RIS records use TY (type) and ER (end of record) tags | ||
| expect(citation).toMatch(/TY\s+-/) | ||
| expect(citation).toMatch(/ER\s+-/) | ||
| }) | ||
|
|
||
| test('should successfully get file citation in BibTeX (plain text) format', async () => { | ||
| const fileId = await getTestFileId() | ||
|
|
||
| const citation = await getFileCitationByFormat.execute(fileId, FileCitationFormat.BIBTEX) | ||
|
|
||
| expect(typeof citation).toBe('string') | ||
| // BibTeX entries start with @<entry-type>{ | ||
| expect(citation.trimStart()).toMatch(/^@\w+\{/) | ||
| }) | ||
|
|
||
| test('should successfully get file citation in CSL (JSON) format', async () => { | ||
| const fileId = await getTestFileId() | ||
|
|
||
| const citation = await getFileCitationByFormat.execute(fileId, FileCitationFormat.CSL) | ||
|
|
||
| expect(typeof citation).toBe('string') | ||
| const parsed = JSON.parse(citation) | ||
| expect(typeof parsed).toBe('object') | ||
| expect(parsed).not.toBeNull() | ||
| }) | ||
|
|
||
| test('should successfully get file citation in Internal (HTML) format', async () => { | ||
| const fileId = await getTestFileId() | ||
|
|
||
| const citation = await getFileCitationByFormat.execute(fileId, FileCitationFormat.INTERNAL) | ||
|
|
||
| expect(typeof citation).toBe('string') | ||
| // Internal HTML format includes anchor tags linking to the dataset | ||
| expect(citation).toMatch(/<a\s+href=/i) | ||
| }) | ||
|
|
||
| test('should throw an error when the file id does not exist', async () => { | ||
| const nonExistentFileId = 5 | ||
|
|
||
| await expect( | ||
| getFileCitationByFormat.execute(nonExistentFileId, FileCitationFormat.BIBTEX) | ||
| ).rejects.toThrow(ReadError) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs list the allowed
formatvalues as raw strings (EndNote,RIS, etc.). Since the public API expects aFileCitationFormatenum, it would be clearer/less error-prone to document the enum members (FileCitationFormat.ENDNOTE,...RIS,...BIBTEX,...CSL,...INTERNAL) rather than (or in addition to) the underlying string values.