From 6548b310db3aa06ce27cfef68221095d7bedb26e Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 18 Jun 2024 10:10:05 +0100 Subject: [PATCH 01/11] Added: optional props to CollectionDTO --- src/collections/domain/dtos/CollectionDTO.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/collections/domain/dtos/CollectionDTO.ts b/src/collections/domain/dtos/CollectionDTO.ts index 86c0f0bc..87363c05 100644 --- a/src/collections/domain/dtos/CollectionDTO.ts +++ b/src/collections/domain/dtos/CollectionDTO.ts @@ -3,6 +3,8 @@ export interface CollectionDTO { name: string contacts: string[] type: CollectionType + affiliation?: string + description?: string } export enum CollectionType { From c43f17c17feab408d206c4f2463a599d7b8243cd Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 22 Jul 2024 11:52:00 +0100 Subject: [PATCH 02/11] Added: extended properties to CollectionDTO for creation --- src/collections/domain/dtos/CollectionDTO.ts | 9 ++++++ .../domain/useCases/GetCollectionFacets.ts | 1 + src/collections/index.ts | 2 +- .../repositories/CollectionsRepository.ts | 29 ++++++++++++++++++- .../collections/collectionHelper.ts | 24 +++++++++++++-- 5 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 src/collections/domain/useCases/GetCollectionFacets.ts diff --git a/src/collections/domain/dtos/CollectionDTO.ts b/src/collections/domain/dtos/CollectionDTO.ts index 87363c05..88e05656 100644 --- a/src/collections/domain/dtos/CollectionDTO.ts +++ b/src/collections/domain/dtos/CollectionDTO.ts @@ -5,6 +5,15 @@ export interface CollectionDTO { type: CollectionType affiliation?: string description?: string + metadataBlockNames: string[] + facetIds?: string[] + inputLevels?: DatasetFieldTypeInputLevelDTO[] +} + +export interface DatasetFieldTypeInputLevelDTO { + datasetFieldName: string + include: boolean + required: boolean } export enum CollectionType { diff --git a/src/collections/domain/useCases/GetCollectionFacets.ts b/src/collections/domain/useCases/GetCollectionFacets.ts new file mode 100644 index 00000000..70b786d1 --- /dev/null +++ b/src/collections/domain/useCases/GetCollectionFacets.ts @@ -0,0 +1 @@ +// TODO diff --git a/src/collections/index.ts b/src/collections/index.ts index 74c92bd2..31b4f8f0 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -10,4 +10,4 @@ const createCollection = new CreateCollection(collectionsRepository) export { getCollection, createCollection } export { Collection } from './domain/models/Collection' -export { CollectionDTO } from './domain/dtos/CollectionDTO' +export { CollectionDTO, DatasetFieldTypeInputLevelDTO } from './domain/dtos/CollectionDTO' diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 29ed421a..8b951690 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -9,12 +9,25 @@ export interface NewCollectionRequestPayload { name: string dataverseContacts: NewCollectionContactRequestPayload[] dataverseType: string + metadataBlocks: NewCollectionMetadataBlocksRequestPayload } export interface NewCollectionContactRequestPayload { contactEmail: string } +export interface NewCollectionMetadataBlocksRequestPayload { + metadataBlockNames: string[] + facetIds: string[] + inputLevels: NewCollectionInputLevelRequestPayload[] +} + +export interface NewCollectionInputLevelRequestPayload { + datasetFieldTypeName: string + include: boolean + required: boolean +} + export class CollectionsRepository extends ApiRepository implements ICollectionsRepository { private readonly collectionsResourceName: string = 'dataverses' @@ -40,11 +53,25 @@ export class CollectionsRepository extends ApiRepository implements ICollections }) ) + const inputLevelsRequestBody: NewCollectionInputLevelRequestPayload[] = [] + collectionDTO.inputLevels.forEach((element) => { + inputLevelsRequestBody.push({ + datasetFieldTypeName: element.datasetFieldName, + include: element.include, + required: element.required + }) + }) + const requestBody: NewCollectionRequestPayload = { alias: collectionDTO.alias, name: collectionDTO.name, dataverseContacts: dataverseContacts, - dataverseType: collectionDTO.type.toString() + dataverseType: collectionDTO.type.toString(), + metadataBlocks: { + metadataBlockNames: collectionDTO.metadataBlockNames, + facetIds: collectionDTO.facetIds, + inputLevels: inputLevelsRequestBody + } } return this.doPost(`/${this.collectionsResourceName}/${parentCollectionId}`, requestBody) diff --git a/test/testHelpers/collections/collectionHelper.ts b/test/testHelpers/collections/collectionHelper.ts index 575c2734..6c03f86c 100644 --- a/test/testHelpers/collections/collectionHelper.ts +++ b/test/testHelpers/collections/collectionHelper.ts @@ -108,7 +108,16 @@ export const createCollectionDTO = (alias = 'test-collection'): CollectionDTO => alias: alias, name: 'Test Collection', contacts: ['dataverse@test.com'], - type: CollectionType.DEPARTMENT + type: CollectionType.DEPARTMENT, + metadataBlockNames: ['citation', 'geospatial'], + facetIds: ['authorName', 'authorAffiliation'], + inputLevels: [ + { + datasetFieldName: 'geographicCoverage', + required: true, + include: true + } + ] } } @@ -121,6 +130,17 @@ export const createNewCollectionRequestPayload = (): NewCollectionRequestPayload contactEmail: 'dataverse@test.com' } ], - dataverseType: 'DEPARTMENT' + dataverseType: 'DEPARTMENT', + metadataBlocks: { + metadataBlockNames: ['citation', 'geospatial'], + facetIds: ['authorName', 'authorAffiliation'], + inputLevels: [ + { + datasetFieldTypeName: 'geographicCoverage', + include: true, + required: true + } + ] + } } } From 5ec471f57bc729b7bd9ec2d0aef1b7f5053941be Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 22 Jul 2024 14:22:06 +0100 Subject: [PATCH 03/11] Added: GetCollectionFacets use case --- .../repositories/ICollectionsRepository.ts | 1 + .../domain/useCases/GetCollectionFacets.ts | 23 +++++++- src/collections/index.ts | 4 +- .../repositories/CollectionsRepository.ts | 8 +++ .../collections/GetCollectionFacets.test.ts | 44 +++++++++++++++ .../collections/CollectionsRepository.test.ts | 20 +++++++ .../collections/CollectionsRepository.test.ts | 56 +++++++++++++++++++ .../collections/GetCollectionFacets.test.ts | 24 ++++++++ 8 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 test/functional/collections/GetCollectionFacets.test.ts create mode 100644 test/unit/collections/GetCollectionFacets.test.ts diff --git a/src/collections/domain/repositories/ICollectionsRepository.ts b/src/collections/domain/repositories/ICollectionsRepository.ts index 9b009a1e..c449388d 100644 --- a/src/collections/domain/repositories/ICollectionsRepository.ts +++ b/src/collections/domain/repositories/ICollectionsRepository.ts @@ -7,4 +7,5 @@ export interface ICollectionsRepository { collectionDTO: CollectionDTO, parentCollectionId: number | string ): Promise + getCollectionFacets(collectionIdOrAlias: number | string): Promise } diff --git a/src/collections/domain/useCases/GetCollectionFacets.ts b/src/collections/domain/useCases/GetCollectionFacets.ts index 70b786d1..a6e2f8fd 100644 --- a/src/collections/domain/useCases/GetCollectionFacets.ts +++ b/src/collections/domain/useCases/GetCollectionFacets.ts @@ -1 +1,22 @@ -// TODO +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { ICollectionsRepository } from '../repositories/ICollectionsRepository' +import { ROOT_COLLECTION_ALIAS } from '../models/Collection' + +export class GetCollectionFacets implements UseCase { + private collectionsRepository: ICollectionsRepository + + constructor(collectionsRepository: ICollectionsRepository) { + this.collectionsRepository = collectionsRepository + } + + /** + * Returns the names of the configured collection facets, given a collection identifier or alias. + * + * @param {number | string} [collectionIdOrAlias = 'root'] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId) + * If this parameter is not set, the default value is: 'root' + * @returns {Promise} + */ + async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ALIAS): Promise { + return await this.collectionsRepository.getCollectionFacets(collectionIdOrAlias) + } +} diff --git a/src/collections/index.ts b/src/collections/index.ts index 31b4f8f0..edaa9270 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -1,5 +1,6 @@ import { CreateCollection } from './domain/useCases/CreateCollection' import { GetCollection } from './domain/useCases/GetCollection' +import { GetCollectionFacets } from './domain/useCases/GetCollectionFacets' import { CollectionsRepository } from './infra/repositories/CollectionsRepository' @@ -7,7 +8,8 @@ const collectionsRepository = new CollectionsRepository() const getCollection = new GetCollection(collectionsRepository) const createCollection = new CreateCollection(collectionsRepository) +const getCollectionFacets = new GetCollectionFacets(collectionsRepository) -export { getCollection, createCollection } +export { getCollection, createCollection, getCollectionFacets } export { Collection } from './domain/models/Collection' export { CollectionDTO, DatasetFieldTypeInputLevelDTO } from './domain/dtos/CollectionDTO' diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 8b951690..408f73b6 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -80,4 +80,12 @@ export class CollectionsRepository extends ApiRepository implements ICollections throw error }) } + + public async getCollectionFacets(collectionIdOrAlias: string | number): Promise { + return this.doGet(`/${this.collectionsResourceName}/${collectionIdOrAlias}/facets`, true) + .then((response) => response.data.data) + .catch((error) => { + throw error + }) + } } diff --git a/test/functional/collections/GetCollectionFacets.test.ts b/test/functional/collections/GetCollectionFacets.test.ts new file mode 100644 index 00000000..a711c2b6 --- /dev/null +++ b/test/functional/collections/GetCollectionFacets.test.ts @@ -0,0 +1,44 @@ +import { ApiConfig, ReadError, getCollectionFacets } from '../../../src' +import { TestConstants } from '../../testHelpers/TestConstants' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { ROOT_COLLECTION_ALIAS } from '../../../src/collections/domain/models/Collection' + +describe('execute', () => { + beforeEach(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + test('should return facets when a valid collection alias is provided', async () => { + let actual: string[] = [] + try { + actual = await getCollectionFacets.execute(ROOT_COLLECTION_ALIAS) + } catch (error) { + throw new Error('Facets should be retrieved') + } finally { + expect(actual).toContain('authorName') + expect(actual).toContain('subject') + expect(actual).toContain('keywordValue') + expect(actual).toContain('dateOfDeposit') + } + }) + + test('should throw an error when collection does not exist', async () => { + expect.assertions(2) + let readError: ReadError + try { + await getCollectionFacets.execute(TestConstants.TEST_DUMMY_COLLECTION_ID) + throw new Error('Use case should throw an error') + } catch (error) { + readError = error + } finally { + expect(readError).toBeInstanceOf(ReadError) + expect(readError.message).toEqual( + `There was an error when reading the resource. Reason was: [404] Can't find dataverse with identifier='${TestConstants.TEST_DUMMY_COLLECTION_ID}'` + ) + } + }) +}) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index f1f610ab..0a4d5da8 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -122,4 +122,24 @@ describe('CollectionsRepository', () => { ).rejects.toThrow(expectedError) }) }) + + describe('getCollectionFacets', () => { + test('should return collection facets given a valid collection alias', async () => { + const actual = await sut.getCollectionFacets(testCollectionAlias) + expect(actual).toContain('authorName') + expect(actual).toContain('subject') + expect(actual).toContain('keywordValue') + expect(actual).toContain('dateOfDeposit') + }) + + test('should return error when collection does not exist', async () => { + const expectedError = new ReadError( + `[404] Can't find dataverse with identifier='${TestConstants.TEST_DUMMY_COLLECTION_ALIAS}'` + ) + + await expect( + sut.getCollectionFacets(TestConstants.TEST_DUMMY_COLLECTION_ALIAS) + ).rejects.toThrow(expectedError) + }) + }) }) diff --git a/test/unit/collections/CollectionsRepository.test.ts b/test/unit/collections/CollectionsRepository.test.ts index 19c6f7e5..46b27ec4 100644 --- a/test/unit/collections/CollectionsRepository.test.ts +++ b/test/unit/collections/CollectionsRepository.test.ts @@ -172,4 +172,60 @@ describe('CollectionsRepository', () => { expect(error).toBeInstanceOf(Error) }) }) + + describe('getCollectionFacets', () => { + const testFacetsSuccessfulResponse = { + data: { + status: 'OK', + data: ['authorName', 'subject', 'keywordValue', 'dateOfDeposit'] + } + } + + describe('by numeric id', () => { + test('should return facets when providing a valid id', async () => { + jest.spyOn(axios, 'get').mockResolvedValue(testFacetsSuccessfulResponse) + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/dataverses/${testCollectionModel.id}/facets` + + // API Key auth + let actual = await sut.getCollectionFacets(testCollectionModel.id) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY + ) + expect(actual).toContain('authorName') + expect(actual).toContain('subject') + expect(actual).toContain('keywordValue') + expect(actual).toContain('dateOfDeposit') + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE) + + actual = await sut.getCollectionFacets(testCollectionModel.id) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE + ) + expect(actual).toContain('authorName') + expect(actual).toContain('subject') + expect(actual).toContain('keywordValue') + expect(actual).toContain('dateOfDeposit') + }) + + test('should return error on repository read error', async () => { + jest.spyOn(axios, 'get').mockRejectedValue(TestConstants.TEST_ERROR_RESPONSE) + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/dataverses/${testCollectionModel.id}/facets` + let error = undefined as unknown as ReadError + + await sut.getCollectionFacets(testCollectionModel.id).catch((e) => (error = e)) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY + ) + expect(error).toBeInstanceOf(Error) + }) + }) + }) }) diff --git a/test/unit/collections/GetCollectionFacets.test.ts b/test/unit/collections/GetCollectionFacets.test.ts new file mode 100644 index 00000000..e87d8ea6 --- /dev/null +++ b/test/unit/collections/GetCollectionFacets.test.ts @@ -0,0 +1,24 @@ +import { GetCollectionFacets } from '../../../src/collections/domain/useCases/GetCollectionFacets' +import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' +import { ReadError } from '../../../src' + +describe('execute', () => { + test('should return collection facets on repository success', async () => { + const testFacets = ['test1', 'test2'] + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.getCollectionFacets = jest.fn().mockResolvedValue(testFacets) + const testGetCollection = new GetCollectionFacets(collectionRepositoryStub) + + const actual = await testGetCollection.execute(1) + + expect(actual).toEqual(testFacets) + }) + + test('should return error result on repository error', async () => { + const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository + collectionRepositoryStub.getCollectionFacets = jest.fn().mockRejectedValue(new ReadError()) + const testGetCollection = new GetCollectionFacets(collectionRepositoryStub) + + await expect(testGetCollection.execute(1)).rejects.toThrow(ReadError) + }) +}) From 8a93249f6927c656759136741ada5a569f276283 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 22 Jul 2024 14:46:24 +0100 Subject: [PATCH 04/11] Added: inputLevels to Collection model --- src/collections/domain/dtos/CollectionDTO.ts | 4 ++-- src/collections/domain/models/Collection.ts | 8 +++++++ src/collections/index.ts | 4 ++-- .../transformers/CollectionPayload.ts | 8 +++++++ .../transformers/collectionTransformers.ts | 21 +++++++++++++++++-- .../collections/collectionHelper.ts | 18 ++++++++++++++-- 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/collections/domain/dtos/CollectionDTO.ts b/src/collections/domain/dtos/CollectionDTO.ts index 88e05656..8606d646 100644 --- a/src/collections/domain/dtos/CollectionDTO.ts +++ b/src/collections/domain/dtos/CollectionDTO.ts @@ -7,10 +7,10 @@ export interface CollectionDTO { description?: string metadataBlockNames: string[] facetIds?: string[] - inputLevels?: DatasetFieldTypeInputLevelDTO[] + inputLevels?: CollectionInputLevelDTO[] } -export interface DatasetFieldTypeInputLevelDTO { +export interface CollectionInputLevelDTO { datasetFieldName: string include: boolean required: boolean diff --git a/src/collections/domain/models/Collection.ts b/src/collections/domain/models/Collection.ts index ad69e264..1ba0f8d2 100644 --- a/src/collections/domain/models/Collection.ts +++ b/src/collections/domain/models/Collection.ts @@ -1,4 +1,5 @@ import { DvObjectOwnerNode } from '../../../core' + export interface Collection { id: number alias: string @@ -7,6 +8,13 @@ export interface Collection { affiliation?: string description?: string isPartOf: DvObjectOwnerNode + inputLevels?: CollectionInputLevel[] +} + +export interface CollectionInputLevel { + datasetFieldName: string + include: boolean + required: boolean } export const ROOT_COLLECTION_ALIAS = 'root' diff --git a/src/collections/index.ts b/src/collections/index.ts index edaa9270..53ee454b 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -11,5 +11,5 @@ const createCollection = new CreateCollection(collectionsRepository) const getCollectionFacets = new GetCollectionFacets(collectionsRepository) export { getCollection, createCollection, getCollectionFacets } -export { Collection } from './domain/models/Collection' -export { CollectionDTO, DatasetFieldTypeInputLevelDTO } from './domain/dtos/CollectionDTO' +export { Collection, CollectionInputLevel } from './domain/models/Collection' +export { CollectionDTO, CollectionInputLevelDTO } from './domain/dtos/CollectionDTO' diff --git a/src/collections/infra/repositories/transformers/CollectionPayload.ts b/src/collections/infra/repositories/transformers/CollectionPayload.ts index 38472d86..83f1604f 100644 --- a/src/collections/infra/repositories/transformers/CollectionPayload.ts +++ b/src/collections/infra/repositories/transformers/CollectionPayload.ts @@ -1,4 +1,5 @@ import { OwnerNodePayload } from '../../../../core/infra/repositories/transformers/OwnerNodePayload' + export interface CollectionPayload { id: number alias: string @@ -7,4 +8,11 @@ export interface CollectionPayload { isReleased: string description?: string isPartOf: OwnerNodePayload + inputLevels?: CollectionInputLevelPayload[] +} + +export interface CollectionInputLevelPayload { + datasetFieldTypeName: string + required: boolean + include: boolean } diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index 59f59a06..87d08e30 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -1,6 +1,6 @@ -import { Collection } from '../../../domain/models/Collection' +import { Collection, CollectionInputLevel } from '../../../domain/models/Collection' import { AxiosResponse } from 'axios' -import { CollectionPayload } from './CollectionPayload' +import { CollectionInputLevelPayload, CollectionPayload } from './CollectionPayload' import { transformPayloadToOwnerNode } from '../../../../core/infra/repositories/transformers/dvObjectOwnerNodeTransformer' import { transformHtmlToMarkdown } from '../../../../datasets/infra/repositories/transformers/datasetTransformers' @@ -21,7 +21,24 @@ const transformPayloadToCollection = (collectionPayload: CollectionPayload): Col }), ...(collectionPayload.isPartOf && { isPartOf: transformPayloadToOwnerNode(collectionPayload.isPartOf) + }), + ...(collectionPayload.inputLevels && { + inputLevels: transformInputLevelsPayloadToInputLevels(collectionPayload.inputLevels) }) } return collectionModel } + +const transformInputLevelsPayloadToInputLevels = ( + inputLevelsPayload: CollectionInputLevelPayload[] +): CollectionInputLevel[] => { + let collectionInputLevels: CollectionInputLevel[] = [] + inputLevelsPayload.forEach((element) => { + collectionInputLevels.push({ + datasetFieldName: element.datasetFieldTypeName, + include: element.include, + required: element.required + }) + }) + return collectionInputLevels +} diff --git a/test/testHelpers/collections/collectionHelper.ts b/test/testHelpers/collections/collectionHelper.ts index 6c03f86c..993a8c25 100644 --- a/test/testHelpers/collections/collectionHelper.ts +++ b/test/testHelpers/collections/collectionHelper.ts @@ -27,7 +27,14 @@ export const createCollectionModel = (): Collection => { isReleased: COLLECTION_IS_RELEASED, affiliation: COLLECTION_AFFILIATION_STR, description: COLLECTION_DESCRIPTION_MARKDOWN, - isPartOf: { type: DvObjectType.DATAVERSE, identifier: 'root', displayName: 'Root' } + isPartOf: { type: DvObjectType.DATAVERSE, identifier: 'root', displayName: 'Root' }, + inputLevels: [ + { + datasetFieldName: 'test', + required: true, + include: true + } + ] } return collectionModel } @@ -40,7 +47,14 @@ export const createCollectionPayload = (): CollectionPayload => { isReleased: COLLECTION_IS_RELEASED, affiliation: COLLECTION_AFFILIATION_STR, description: COLLECTION_DESCRIPTION_HTML, - isPartOf: { type: DvObjectType.DATAVERSE, identifier: 'root', displayName: 'Root' } + isPartOf: { type: DvObjectType.DATAVERSE, identifier: 'root', displayName: 'Root' }, + inputLevels: [ + { + datasetFieldTypeName: 'test', + required: true, + include: true + } + ] } return collectionPayload } From cc27ae20e423a7d295147d45d26fe04e6add40a0 Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 23 Jul 2024 10:52:00 +0100 Subject: [PATCH 05/11] Changed: using const instead of let in collectionTransformers --- .../infra/repositories/transformers/collectionTransformers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index 87d08e30..59cf9264 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -32,7 +32,7 @@ const transformPayloadToCollection = (collectionPayload: CollectionPayload): Col const transformInputLevelsPayloadToInputLevels = ( inputLevelsPayload: CollectionInputLevelPayload[] ): CollectionInputLevel[] => { - let collectionInputLevels: CollectionInputLevel[] = [] + const collectionInputLevels: CollectionInputLevel[] = [] inputLevelsPayload.forEach((element) => { collectionInputLevels.push({ datasetFieldName: element.datasetFieldTypeName, From e1b0b819f833a56c6287639af481dbe6e1572c2f Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 23 Jul 2024 14:11:17 +0100 Subject: [PATCH 06/11] Added: enhanced IT coverage for getCollection and createCollection --- test/environment/.env | 4 +-- .../collections/CollectionsRepository.test.ts | 32 ++++++++++++++++--- .../collections/GetCollectionFacets.test.ts | 8 ++--- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/test/environment/.env b/test/environment/.env index 80e9a14e..2195f049 100644 --- a/test/environment/.env +++ b/test/environment/.env @@ -1,6 +1,6 @@ POSTGRES_VERSION=13 DATAVERSE_DB_USER=dataverse SOLR_VERSION=9.3.0 -DATAVERSE_IMAGE_REGISTRY=docker.io -DATAVERSE_IMAGE_TAG=unstable +DATAVERSE_IMAGE_REGISTRY=ghcr.io +DATAVERSE_IMAGE_TAG=10633-create-collection-api-ext DATAVERSE_BOOTSTRAP_TIMEOUT=5m diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 0a4d5da8..2d166769 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -41,6 +41,13 @@ describe('CollectionsRepository', () => { test('should return the root collection of the Dataverse installation if no parameter is passed AS `root`', async () => { const actual = await sut.getCollection() expect(actual.alias).toBe(ROOT_COLLECTION_ALIAS) + expect(actual.id).toBe(1) + expect(actual.name).toBe('Root') + expect(actual.alias).toBe('root') + expect(actual.isReleased).toBe(true) + expect(actual.affiliation).toBe(undefined) + expect(actual.description).toBe('The root dataverse.') + expect(actual.inputLevels).toBe(undefined) }) test('should return isReleased is true for root collection', async () => { @@ -97,16 +104,33 @@ describe('CollectionsRepository', () => { }) test('should create collection in root when no parent collection is set', async () => { - const actual = await sut.createCollection(createCollectionDTO(testCreateCollectionAlias1)) - expect(typeof actual).toBe('number') + const newCollectionDTO = createCollectionDTO(testCreateCollectionAlias1) + const actualId = await sut.createCollection(newCollectionDTO) + expect(typeof actualId).toBe('number') + + const createdCollection = await sut.getCollection(actualId) + + expect(createdCollection.id).toBe(actualId) + expect(createdCollection.alias).toBe(newCollectionDTO.alias) + expect(createdCollection.name).toBe(newCollectionDTO.name) + expect(createdCollection.affiliation).toBe(newCollectionDTO.affiliation) + expect(createdCollection.isPartOf.type).toBe('DATAVERSE') + expect(createdCollection.isPartOf.displayName).toBe('Root') + expect(createdCollection.isPartOf.identifier).toBe('root') + + expect(createdCollection.inputLevels?.length).toBe(1) + const inputLevel = createdCollection.inputLevels?.[0] + expect(inputLevel?.datasetFieldName).toBe('geographicCoverage') + expect(inputLevel?.include).toBe(true) + expect(inputLevel?.required).toBe(true) }) test('should create collection in parent collection when parent collection is set', async () => { - const actual = await sut.createCollection( + const actualId = await sut.createCollection( createCollectionDTO(testCreateCollectionAlias2), testCollectionId ) - expect(typeof actual).toBe('number') + expect(typeof actualId).toBe('number') }) test('should return error when parent collection does not exist', async () => { diff --git a/test/unit/collections/GetCollectionFacets.test.ts b/test/unit/collections/GetCollectionFacets.test.ts index e87d8ea6..76f736b5 100644 --- a/test/unit/collections/GetCollectionFacets.test.ts +++ b/test/unit/collections/GetCollectionFacets.test.ts @@ -7,9 +7,9 @@ describe('execute', () => { const testFacets = ['test1', 'test2'] const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository collectionRepositoryStub.getCollectionFacets = jest.fn().mockResolvedValue(testFacets) - const testGetCollection = new GetCollectionFacets(collectionRepositoryStub) + const testGetCollectionFacets = new GetCollectionFacets(collectionRepositoryStub) - const actual = await testGetCollection.execute(1) + const actual = await testGetCollectionFacets.execute(1) expect(actual).toEqual(testFacets) }) @@ -17,8 +17,8 @@ describe('execute', () => { test('should return error result on repository error', async () => { const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository collectionRepositoryStub.getCollectionFacets = jest.fn().mockRejectedValue(new ReadError()) - const testGetCollection = new GetCollectionFacets(collectionRepositoryStub) + const testGetCollectionFacets = new GetCollectionFacets(collectionRepositoryStub) - await expect(testGetCollection.execute(1)).rejects.toThrow(ReadError) + await expect(testGetCollectionFacets.execute(1)).rejects.toThrow(ReadError) }) }) From 65a21032cc9d42b8df7e9eb47af835efe991ef69 Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 23 Jul 2024 17:25:10 +0100 Subject: [PATCH 07/11] Added: docs and minor tweaks --- docs/useCases.md | 28 +++++++++++++++++++ src/collections/domain/dtos/CollectionDTO.ts | 2 +- .../domain/useCases/GetCollectionFacets.ts | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/useCases.md b/docs/useCases.md index d70d9587..184f989b 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -11,6 +11,7 @@ The different use cases currently available in the package are classified below, - [Collections](#Collections) - [Collections read use cases](#collections-read-use-cases) - [Get a Collection](#get-a-collection) + - [Get Collection Facets](#get-collection-facets) - [Collections write use cases](#collections-write-use-cases) - [Create a Collection](#create-a-collection) - [Datasets](#Datasets) @@ -100,6 +101,33 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe If no collection identifier is specified, the default collection identifier; `root` will be used. If you want to search for a different collection, you must add the collection identifier as a parameter in the use case call. +#### Get Collection Facets + +Returns the names of the configured collection facets, given a collection identifier or alias. + +##### Example call: + +```typescript +import { getCollectionFacets } from '@iqss/dataverse-client-javascript' + +const collectionIdOrAlias = 12345 + +getCollectionFacets + .execute(collectionId) + .then((facets: string[]) => { + /* ... */ + }) + .catch((error: Error) => { + /* ... */ + }) +``` + +_See [use case](../src/collections/domain/useCases/GetCollectionFacets.ts)_ definition. + +The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId). + +If no collection identifier is specified, the default collection identifier; `root` will be used. If you want to search for a different collection, you must add the collection identifier as a parameter in the use case call. + ### Collections Write Use Cases #### Create a Collection diff --git a/src/collections/domain/dtos/CollectionDTO.ts b/src/collections/domain/dtos/CollectionDTO.ts index 8606d646..f732afea 100644 --- a/src/collections/domain/dtos/CollectionDTO.ts +++ b/src/collections/domain/dtos/CollectionDTO.ts @@ -5,7 +5,7 @@ export interface CollectionDTO { type: CollectionType affiliation?: string description?: string - metadataBlockNames: string[] + metadataBlockNames?: string[] facetIds?: string[] inputLevels?: CollectionInputLevelDTO[] } diff --git a/src/collections/domain/useCases/GetCollectionFacets.ts b/src/collections/domain/useCases/GetCollectionFacets.ts index a6e2f8fd..19552f03 100644 --- a/src/collections/domain/useCases/GetCollectionFacets.ts +++ b/src/collections/domain/useCases/GetCollectionFacets.ts @@ -14,7 +14,7 @@ export class GetCollectionFacets implements UseCase { * * @param {number | string} [collectionIdOrAlias = 'root'] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId) * If this parameter is not set, the default value is: 'root' - * @returns {Promise} + * @returns {Promise} */ async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ALIAS): Promise { return await this.collectionsRepository.getCollectionFacets(collectionIdOrAlias) From 8e5c736e27f0373795c62c5c150dd5b14f353f14 Mon Sep 17 00:00:00 2001 From: GPortas Date: Thu, 25 Jul 2024 17:38:04 +0100 Subject: [PATCH 08/11] Refactor: code styling --- .../infra/repositories/CollectionsRepository.ts | 16 +++++++--------- .../transformers/collectionTransformers.ts | 14 +++++--------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/collections/infra/repositories/CollectionsRepository.ts b/src/collections/infra/repositories/CollectionsRepository.ts index 408f73b6..e9e3bfea 100644 --- a/src/collections/infra/repositories/CollectionsRepository.ts +++ b/src/collections/infra/repositories/CollectionsRepository.ts @@ -53,20 +53,18 @@ export class CollectionsRepository extends ApiRepository implements ICollections }) ) - const inputLevelsRequestBody: NewCollectionInputLevelRequestPayload[] = [] - collectionDTO.inputLevels.forEach((element) => { - inputLevelsRequestBody.push({ - datasetFieldTypeName: element.datasetFieldName, - include: element.include, - required: element.required - }) - }) + const inputLevelsRequestBody: NewCollectionInputLevelRequestPayload[] = + collectionDTO.inputLevels.map((inputLevel) => ({ + datasetFieldTypeName: inputLevel.datasetFieldName, + include: inputLevel.include, + required: inputLevel.required + })) const requestBody: NewCollectionRequestPayload = { alias: collectionDTO.alias, name: collectionDTO.name, dataverseContacts: dataverseContacts, - dataverseType: collectionDTO.type.toString(), + dataverseType: collectionDTO.type, metadataBlocks: { metadataBlockNames: collectionDTO.metadataBlockNames, facetIds: collectionDTO.facetIds, diff --git a/src/collections/infra/repositories/transformers/collectionTransformers.ts b/src/collections/infra/repositories/transformers/collectionTransformers.ts index 59cf9264..fbc59d84 100644 --- a/src/collections/infra/repositories/transformers/collectionTransformers.ts +++ b/src/collections/infra/repositories/transformers/collectionTransformers.ts @@ -32,13 +32,9 @@ const transformPayloadToCollection = (collectionPayload: CollectionPayload): Col const transformInputLevelsPayloadToInputLevels = ( inputLevelsPayload: CollectionInputLevelPayload[] ): CollectionInputLevel[] => { - const collectionInputLevels: CollectionInputLevel[] = [] - inputLevelsPayload.forEach((element) => { - collectionInputLevels.push({ - datasetFieldName: element.datasetFieldTypeName, - include: element.include, - required: element.required - }) - }) - return collectionInputLevels + return inputLevelsPayload.map((inputLevel) => ({ + datasetFieldName: inputLevel.datasetFieldTypeName, + include: inputLevel.include, + required: inputLevel.required + })) } From ecd4b6def9a84c1ea76cef83e4b409247ca7428d Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 26 Jul 2024 12:23:56 -0400 Subject: [PATCH 09/11] change dataverse image to 'unstable' --- test/environment/.env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/environment/.env b/test/environment/.env index 2195f049..80e9a14e 100644 --- a/test/environment/.env +++ b/test/environment/.env @@ -1,6 +1,6 @@ POSTGRES_VERSION=13 DATAVERSE_DB_USER=dataverse SOLR_VERSION=9.3.0 -DATAVERSE_IMAGE_REGISTRY=ghcr.io -DATAVERSE_IMAGE_TAG=10633-create-collection-api-ext +DATAVERSE_IMAGE_REGISTRY=docker.io +DATAVERSE_IMAGE_TAG=unstable DATAVERSE_BOOTSTRAP_TIMEOUT=5m From 393bfd54911bb2aa25c5109ab26d5b5ca69aa25e Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 26 Jul 2024 12:40:45 -0400 Subject: [PATCH 10/11] change dataverse image to 'unstable' --- test/integration/collections/CollectionsRepository.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 2d166769..7d51327a 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -109,7 +109,7 @@ describe('CollectionsRepository', () => { expect(typeof actualId).toBe('number') const createdCollection = await sut.getCollection(actualId) - + console.log('returned collection') expect(createdCollection.id).toBe(actualId) expect(createdCollection.alias).toBe(newCollectionDTO.alias) expect(createdCollection.name).toBe(newCollectionDTO.name) From eee6302235059a3ee0a86ebc7962688dc51360c9 Mon Sep 17 00:00:00 2001 From: Ellen Kraffmiller Date: Fri, 26 Jul 2024 12:56:54 -0400 Subject: [PATCH 11/11] Update CollectionsRepository.test.ts Remove console.log() --- test/integration/collections/CollectionsRepository.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/collections/CollectionsRepository.test.ts b/test/integration/collections/CollectionsRepository.test.ts index 7d51327a..80dd77d3 100644 --- a/test/integration/collections/CollectionsRepository.test.ts +++ b/test/integration/collections/CollectionsRepository.test.ts @@ -109,7 +109,6 @@ describe('CollectionsRepository', () => { expect(typeof actualId).toBe('number') const createdCollection = await sut.getCollection(actualId) - console.log('returned collection') expect(createdCollection.id).toBe(actualId) expect(createdCollection.alias).toBe(newCollectionDTO.alias) expect(createdCollection.name).toBe(newCollectionDTO.name)