diff --git a/docs/useCases.md b/docs/useCases.md index 47e09f6c..6d68e86c 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -33,6 +33,7 @@ The different use cases currently available in the package are classified below, - [Get User Permissions on a Dataset](#get-user-permissions-on-a-dataset) - [Get Differences between Two Dataset Versions](#get-differences-between-two-dataset-versions) - [List All Datasets](#list-all-datasets) + - [Get Dataset Versions Summaries](#get-dataset-versions-summaries) - [Datasets write use cases](#datasets-write-use-cases) - [Create a Dataset](#create-a-dataset) - [Update a Dataset](#update-a-dataset) @@ -577,7 +578,7 @@ const datasetId = 'doi:10.77777/FK2/AAAAAA' const oldVersion = '1.0' const newVersion = '2.0' -lgetDatasetVersionDiff +getDatasetVersionDiff .execute(datasetId, oldVersion, newVersion) .then((versionDiff: DatasetVersionDiff) => { /* ... */ @@ -622,6 +623,32 @@ Note that `collectionId` is an optional parameter to filter datasets by collecti The `DatasetPreviewSubset`returned instance contains a property called `totalDatasetCount` which is necessary for pagination. +#### Get Dataset Versions Summaries + +Returns an array of [DatasetVersionSummaryInfo](../src/datasets/domain/models/DatasetVersionSummaryInfo.ts) that contains information about what changed in every specific version. + +##### Example call: + +```typescript +import { getDatasetVersionsSummaries } from '@iqss/dataverse-client-javascript' + +/* ... */ + +const datasetId = 'doi:10.77777/FK2/AAAAAA' + +getDatasetVersionsSummaries + .execute(datasetId) + .then((datasetVersionsSummaries: DatasetVersionSummaryInfo[]) => { + /* ... */ + }) + +/* ... */ +``` + +_See [use case](../src/datasets/domain/useCases/GetDatasetVersionsSummaries.ts) implementation_. + +The `datasetId` parameter can be a string, for persistent identifiers, or a number, for numeric identifiers. + ### Datasets Write Use Cases #### Create a Dataset diff --git a/jest.config.ts b/jest.config.ts index e4844bd8..d7de0942 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -9,7 +9,7 @@ const config: Config = { moduleFileExtensions: ['ts', 'js', 'json', 'node'], coveragePathIgnorePatterns: ['node_modules', 'testHelpers'], globalSetup: '/test/environment/setup.ts', - testTimeout: 25000, + testTimeout: 35000, coverageThreshold: { global: { branches: 90, diff --git a/src/datasets/domain/models/DatasetVersionSummaryInfo.ts b/src/datasets/domain/models/DatasetVersionSummaryInfo.ts new file mode 100644 index 00000000..faee0e57 --- /dev/null +++ b/src/datasets/domain/models/DatasetVersionSummaryInfo.ts @@ -0,0 +1,36 @@ +export interface DatasetVersionSummaryInfo { + id: number + versionNumber: string + summary?: DatasetVersionSummary | DatasetVersionSummaryStringValues + contributors: string + publishedOn?: string +} + +export type DatasetVersionSummary = { + [key: string]: SummaryUpdates | SummaryUpdatesWithFields | FilesSummaryUpdates | boolean +} + +interface SummaryUpdates { + added: number + deleted: number + changed: number +} + +interface SummaryUpdatesWithFields { + [key: string]: SummaryUpdates +} + +interface FilesSummaryUpdates { + added: number + removed: number + replaced: number + changedFileMetaData: number + changedVariableMetadata: number +} + +export enum DatasetVersionSummaryStringValues { + firstPublished = 'firstPublished', + firstDraft = 'firstDraft', + versionDeaccessioned = 'versionDeaccessioned', + previousVersionDeaccessioned = 'previousVersionDeaccessioned' +} diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index dd2b954d..a5052dc7 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -7,6 +7,7 @@ import { DatasetDTO } from '../dtos/DatasetDTO' import { DatasetDeaccessionDTO } from '../dtos/DatasetDeaccessionDTO' import { MetadataBlock } from '../../../metadataBlocks' import { DatasetVersionDiff } from '../models/DatasetVersionDiff' +import { DatasetVersionSummaryInfo } from '../models/DatasetVersionSummaryInfo' export interface IDatasetsRepository { getDataset( @@ -51,4 +52,5 @@ export interface IDatasetsRepository { datasetVersionId: string, deaccessionDTO: DatasetDeaccessionDTO ): Promise + getDatasetVersionsSummaries(datasetId: number | string): Promise } diff --git a/src/datasets/domain/useCases/GetDatasetVersionsSummaries.ts b/src/datasets/domain/useCases/GetDatasetVersionsSummaries.ts new file mode 100644 index 00000000..24458b00 --- /dev/null +++ b/src/datasets/domain/useCases/GetDatasetVersionsSummaries.ts @@ -0,0 +1,22 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { DatasetVersionSummaryInfo } from '../models/DatasetVersionSummaryInfo' +import { IDatasetsRepository } from '../repositories/IDatasetsRepository' + +export class GetDatasetVersionsSummaries implements UseCase { + private datasetsRepository: IDatasetsRepository + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository + } + + /** + * Returns a list of versions for a given dataset including a summary of differences between consecutive versions where available. + * Draft versions will only be available to users who have permission to view unpublished drafts. + * + * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). + * @returns {Promise} - An array of DatasetVersionSummaryInfo. + */ + async execute(datasetId: number | string): Promise { + return await this.datasetsRepository.getDatasetVersionsSummaries(datasetId) + } +} diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 3667cbf7..121af2f4 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -17,6 +17,7 @@ import { PublishDataset } from './domain/useCases/PublishDataset' import { UpdateDataset } from './domain/useCases/UpdateDataset' import { GetDatasetVersionDiff } from './domain/useCases/GetDatasetVersionDiff' import { DeaccessionDataset } from './domain/useCases/DeaccessionDataset' +import { GetDatasetVersionsSummaries } from './domain/useCases/GetDatasetVersionsSummaries' const datasetsRepository = new DatasetsRepository() @@ -48,6 +49,7 @@ const updateDataset = new UpdateDataset( datasetResourceValidator ) const deaccessionDataset = new DeaccessionDataset(datasetsRepository) +const getDatasetVersionsSummaries = new GetDatasetVersionsSummaries(datasetsRepository) export { getDataset, @@ -62,7 +64,8 @@ export { publishDataset, createDataset, updateDataset, - deaccessionDataset + deaccessionDataset, + getDatasetVersionsSummaries } export { DatasetNotNumberedVersion } from './domain/models/DatasetNotNumberedVersion' export { DatasetUserPermissions } from './domain/models/DatasetUserPermissions' @@ -92,3 +95,7 @@ export { export { DatasetDeaccessionDTO } from './domain/dtos/DatasetDeaccessionDTO' export { CreatedDatasetIdentifiers } from './domain/models/CreatedDatasetIdentifiers' export { VersionUpdateType } from './domain/models/Dataset' +export { + DatasetVersionSummaryInfo, + DatasetVersionSummaryStringValues +} from './domain/models/DatasetVersionSummaryInfo' diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 2107d960..4cd6467e 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -18,6 +18,7 @@ import { transformDatasetLocksResponseToDatasetLocks } from './transformers/data import { transformDatasetPreviewsResponseToDatasetPreviewSubset } from './transformers/datasetPreviewsTransformers' import { DatasetVersionDiff } from '../../domain/models/DatasetVersionDiff' import { transformDatasetVersionDiffResponseToDatasetVersionDiff } from './transformers/datasetVersionDiffTransformers' +import { DatasetVersionSummaryInfo } from '../../domain/models/DatasetVersionSummaryInfo' export interface GetAllDatasetPreviewsQueryParams { per_page?: number @@ -235,4 +236,17 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi throw error }) } + + public async getDatasetVersionsSummaries( + datasetId: string | number + ): Promise { + return this.doGet( + this.buildApiEndpoint(this.datasetsResourceName, 'versions/compareSummary', datasetId), + true + ) + .then((response) => response.data.data) + .catch((error) => { + throw error + }) + } } diff --git a/test/environment/.env b/test/environment/.env index 80e9a14e..0c691d9b 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 +SOLR_VERSION=9.8.0 DATAVERSE_IMAGE_REGISTRY=docker.io DATAVERSE_IMAGE_TAG=unstable DATAVERSE_BOOTSTRAP_TIMEOUT=5m diff --git a/test/integration/datasets/DatasetsRepository.test.ts b/test/integration/datasets/DatasetsRepository.test.ts index 1f8add69..d3e34bde 100644 --- a/test/integration/datasets/DatasetsRepository.test.ts +++ b/test/integration/datasets/DatasetsRepository.test.ts @@ -7,7 +7,8 @@ import { deleteUnpublishedDatasetViaApi, waitForDatasetsIndexedInSolr, deletePublishedDatasetViaApi, - deaccessionDatasetViaApi + deaccessionDatasetViaApi, + createDatasetLicenseModel } from '../../testHelpers/datasets/datasetHelper' import { ReadError } from '../../../src/core/domain/repositories/ReadError' import { @@ -18,7 +19,8 @@ import { createDataset, CreatedDatasetIdentifiers, DatasetDTO, - DatasetDeaccessionDTO + DatasetDeaccessionDTO, + publishDataset } from '../../../src/datasets' import { ApiConfig, WriteError } from '../../../src' import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' @@ -31,9 +33,23 @@ import { import { createCollectionViaApi, deleteCollectionViaApi, - ROOT_COLLECTION_ALIAS + publishCollectionViaApi, + ROOT_COLLECTION_ALIAS, + setStorageDriverViaApi } from '../../testHelpers/collections/collectionHelper' -import { testTextFile1Name, uploadFileViaApi } from '../../testHelpers/files/filesHelper' +import { + calculateBlobChecksum, + createSinglepartFileBlob, + testTextFile1Name, + uploadFileViaApi +} from '../../testHelpers/files/filesHelper' +import { + DatasetVersionSummary, + DatasetVersionSummaryStringValues +} from '../../../src/datasets/domain/models/DatasetVersionSummaryInfo' +import { FilesRepository } from '../../../src/files/infra/repositories/FilesRepository' +import { DirectUploadClient } from '../../../src/files/infra/clients/DirectUploadClient' +import { createTestFileUploadDestination } from '../../testHelpers/files/fileUploadDestinationHelper' const TEST_DIFF_DATASET_DTO: DatasetDTO = { license: { @@ -79,6 +95,9 @@ describe('DatasetsRepository', () => { const sut: DatasetsRepository = new DatasetsRepository() const nonExistentTestDatasetId = 100 + const filesRepositorySut = new FilesRepository() + const directUploadSut: DirectUploadClient = new DirectUploadClient(filesRepositorySut) + beforeAll(async () => { ApiConfig.init( TestConstants.TEST_API_URL, @@ -925,4 +944,179 @@ describe('DatasetsRepository', () => { ).rejects.toBeInstanceOf(WriteError) }) }) + + describe('getDatasetVersionsSummaries', () => { + const testDatasetVersionsCollectionAlias = 'testDatasetVersionsCollection' + + beforeAll(async () => { + await createCollectionViaApi(testDatasetVersionsCollectionAlias) + await publishCollectionViaApi(testDatasetVersionsCollectionAlias) + await setStorageDriverViaApi(testDatasetVersionsCollectionAlias, 'LocalStack') + }) + + afterAll(async () => { + await deleteCollectionViaApi(testDatasetVersionsCollectionAlias) + }) + + test('should return dataset versions when dataset exists', async () => { + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testDatasetVersionsCollectionAlias + ) + + const actual = await sut.getDatasetVersionsSummaries(testDatasetIds.numericId) + + expect(actual.length).toBeGreaterThan(0) + expect(actual[0].versionNumber).toBe('DRAFT') + expect(actual[0].summary).toBe(DatasetVersionSummaryStringValues.firstDraft) + + await deleteUnpublishedDatasetViaApi(testDatasetIds.numericId) + }) + + test('should return dataset versions correctly after first publish', async () => { + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testDatasetVersionsCollectionAlias + ) + await publishDataset.execute(testDatasetIds.numericId, VersionUpdateType.MAJOR) + + await waitForNoLocks(testDatasetIds.numericId, 10) + + const actual = await sut.getDatasetVersionsSummaries(testDatasetIds.numericId) + + expect(actual.length).toBeGreaterThan(0) + expect(actual[0].versionNumber).toBe('1.0') + expect(actual[0].summary).toBe(DatasetVersionSummaryStringValues.firstPublished) + + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) + }) + + test('should return dataset versions correctly after 1st publish and metadata fields update', async () => { + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testDatasetVersionsCollectionAlias + ) + await publishDataset.execute(testDatasetIds.numericId, VersionUpdateType.MAJOR) + + await waitForNoLocks(testDatasetIds.numericId, 10) + + const metadataBlocksRepository = new MetadataBlocksRepository() + const citationMetadataBlock = await metadataBlocksRepository.getMetadataBlockByName( + 'citation' + ) + + await sut.updateDataset( + testDatasetIds.numericId, + { + license: createDatasetLicenseModel(true), + metadataBlockValues: [ + { + name: 'citation', + fields: { + title: 'Updated Dataset Title' + } + } + ] + }, + [citationMetadataBlock] + ) + + const actual = await sut.getDatasetVersionsSummaries(testDatasetIds.numericId) + + expect(actual.length).toEqual(2) + expect(actual[0].versionNumber).toBe('DRAFT') + expect(actual[0].summary).toMatchObject({ + 'Citation Metadata': { + Title: { + added: 0, + deleted: 0, + changed: 1 + } + }, + files: { + added: 0, + removed: 0, + replaced: 0, + changedFileMetaData: 0, + changedVariableMetadata: 0 + }, + termsAccessChanged: false + }) + + expect(actual[1].versionNumber).toBe('1.0') + expect(actual[1].summary).toBe(DatasetVersionSummaryStringValues.firstPublished) + + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) + }) + + test('should return correct files summary', async () => { + const testDatasetIds = await createDataset.execute( + TestConstants.TEST_NEW_DATASET_DTO, + testDatasetVersionsCollectionAlias + ) + await publishDataset.execute(testDatasetIds.numericId, VersionUpdateType.MAJOR) + + await waitForNoLocks(testDatasetIds.numericId, 10) + + const singlepartFile = await createSinglepartFileBlob() + + const destination = await createTestFileUploadDestination( + singlepartFile, + testDatasetIds.numericId + ) + + const actualStorageId = await directUploadSut.uploadFile( + testDatasetIds.numericId, + singlepartFile, + jest.fn(), + new AbortController(), + destination + ) + + const fileArrayBuffer = await singlepartFile.arrayBuffer() + const fileBuffer = Buffer.from(fileArrayBuffer) + + const uploadedFileDTO = { + fileName: singlepartFile.name, + storageId: actualStorageId, + checksumType: 'md5', + checksumValue: calculateBlobChecksum(fileBuffer, 'md5'), + mimeType: singlepartFile.type + } + + await filesRepositorySut.addUploadedFilesToDataset(testDatasetIds.numericId, [ + uploadedFileDTO + ]) + + const actual = await sut.getDatasetVersionsSummaries(testDatasetIds.numericId) + + expect(actual.length).toEqual(2) + + expect(actual[0].versionNumber).toBe('DRAFT') + expect(actual[0].summary).toMatchObject({ + files: { + added: 1, + removed: 0, + replaced: 0, + changedFileMetaData: 0, + changedVariableMetadata: 0 + }, + termsAccessChanged: false + }) + expect(actual[1].versionNumber).toBe('1.0') + expect(actual[1].summary).toBe(DatasetVersionSummaryStringValues.firstPublished) + + await deletePublishedDatasetViaApi(testDatasetIds.persistentId) + }) + + test('should return error when dataset does not exist', async () => { + const expectedError = new ReadError( + `[404] Dataset with ID ${nonExistentTestDatasetId} not found.` + ) + + await expect(sut.getDatasetVersionsSummaries(nonExistentTestDatasetId)).rejects.toThrow( + expectedError + ) + }) + }) }) diff --git a/test/testHelpers/collections/collectionHelper.ts b/test/testHelpers/collections/collectionHelper.ts index b1e129fe..8ccde481 100644 --- a/test/testHelpers/collections/collectionHelper.ts +++ b/test/testHelpers/collections/collectionHelper.ts @@ -147,6 +147,18 @@ export async function setStorageDriverViaApi( } } +export async function publishCollectionViaApi(collectionAlias: string): Promise { + try { + return await axios.post( + `${TestConstants.TEST_API_URL}/dataverses/${collectionAlias}/actions/:publish`, + {}, + DATAVERSE_API_REQUEST_HEADERS + ) + } catch (error) { + throw new Error(`Error while publishing test collection ${collectionAlias}`) + } +} + export const createCollectionDTO = (alias = 'test-collection'): CollectionDTO => { return { alias: alias, diff --git a/test/testHelpers/datasets/datasetVersionsSummariesHelper.ts b/test/testHelpers/datasets/datasetVersionsSummariesHelper.ts new file mode 100644 index 00000000..95f46858 --- /dev/null +++ b/test/testHelpers/datasets/datasetVersionsSummariesHelper.ts @@ -0,0 +1,28 @@ +import { DatasetVersionSummaryInfo } from '../../../src/datasets/domain/models/DatasetVersionSummaryInfo' + +export const createDatasetVersionSummaryModel = ( + props?: Partial +): DatasetVersionSummaryInfo => ({ + id: 1, + contributors: 'John Doe', + versionNumber: 'DRAFT', + publishedOn: '2021-01-01', + summary: { + 'Citation Metadata': { + Title: { + added: 0, + deleted: 0, + changed: 1 + } + }, + files: { + added: 0, + removed: 0, + replaced: 0, + changedFileMetaData: 0, + changedVariableMetadata: 0 + }, + termsAccessChanged: false + }, + ...props +}) diff --git a/test/testHelpers/files/fileUploadDestinationHelper.ts b/test/testHelpers/files/fileUploadDestinationHelper.ts index 102ece1a..defac0c6 100644 --- a/test/testHelpers/files/fileUploadDestinationHelper.ts +++ b/test/testHelpers/files/fileUploadDestinationHelper.ts @@ -1,4 +1,5 @@ import { FileUploadDestination } from '../../../src/files/domain/models/FileUploadDestination' +import { FilesRepository } from '../../../src/files/infra/repositories/FilesRepository' import { FileMultipartUploadDestinationPayload, FileSingleUploadDestinationPayload, @@ -42,3 +43,12 @@ export const createMultipartFileUploadDestinationModel = (): FileUploadDestinati multipartFileUploadDestinationPayload ) } + +export const createTestFileUploadDestination = async (file: File, testDatasetId: number) => { + const filesRepository = new FilesRepository() + const destination = await filesRepository.getFileUploadDestination(testDatasetId, file) + destination.urls.forEach((destinationUrl, index) => { + destination.urls[index] = destinationUrl.replace('localstack', 'localhost') + }) + return destination +} diff --git a/test/testHelpers/files/filesHelper.ts b/test/testHelpers/files/filesHelper.ts index a403b483..62a5f890 100644 --- a/test/testHelpers/files/filesHelper.ts +++ b/test/testHelpers/files/filesHelper.ts @@ -1,8 +1,9 @@ -import { FileModel as FileModel } from '../../../src/files/domain/models/FileModel' -import { File, Blob } from '@web-std/file' +import * as crypto from 'crypto' import axios, { AxiosResponse } from 'axios' -import { TestConstants } from '../TestConstants' +import { File, Blob } from '@web-std/file' import { readFile } from 'fs/promises' +import { FileModel as FileModel } from '../../../src/files/domain/models/FileModel' +import { TestConstants } from '../TestConstants' import { FilesSubset } from '../../../src/files/domain/models/FilesSubset' import { DvObjectType } from '../../../src/core/domain/models/DvObjectOwnerNode' import { FilePayload } from '../../../src/files/infra/repositories/transformers/FilePayload' @@ -235,3 +236,20 @@ export const updateFileTabularTags = async ( } ) } + +export const calculateBlobChecksum = (blob: Buffer, checksumAlgorithm: string): string => { + const hash = crypto.createHash(checksumAlgorithm) + hash.update(blob) + return hash.digest('hex') +} + +export const singlepartFileExistsInBucket = async (fileUrl: string): Promise => { + return axios + .get(fileUrl) + .then(() => { + return true + }) + .catch(() => { + return false + }) +} diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 85ff2f44..0f60fc65 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -30,6 +30,7 @@ import { } from '../../testHelpers/datasets/datasetHelper' import { WriteError } from '../../../src' import { VersionUpdateType } from '../../../src/datasets/domain/models/Dataset' +import { createDatasetVersionSummaryModel } from '../../testHelpers/datasets/datasetVersionsSummariesHelper' describe('DatasetsRepository', () => { const sut: DatasetsRepository = new DatasetsRepository() @@ -962,7 +963,7 @@ describe('DatasetsRepository', () => { }) }) - describe('deaccsionDataset', () => { + describe('deaccessionDataset', () => { const version = '1.0' const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/${version}/deaccession` const expectedApiKeyRequestConfig = { @@ -1014,4 +1015,99 @@ describe('DatasetsRepository', () => { expect(error).toBeInstanceOf(Error) }) }) + + describe('getDatasetVersionSummaries', () => { + const testDatasetVersionSummaries = createDatasetVersionSummaryModel() + + const testDatasetVersionSummariesResponse = { + data: { + status: 'OK', + data: [testDatasetVersionSummaries] + } + } + + describe('by numeric id', () => { + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/versions/compareSummary` + + test('should return dataset version summaries when providing id and response is successful', async () => { + jest.spyOn(axios, 'get').mockResolvedValue(testDatasetVersionSummariesResponse) + + // API Key auth + let actual = await sut.getDatasetVersionsSummaries(testDatasetModel.id) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY + ) + expect(actual).toStrictEqual([testDatasetVersionSummaries]) + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE) + + actual = await sut.getDatasetVersionsSummaries(testDatasetModel.id) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE + ) + expect(actual).toStrictEqual([testDatasetVersionSummaries]) + }) + + test('should return error result on error response', async () => { + jest.spyOn(axios, 'get').mockRejectedValue(TestConstants.TEST_ERROR_RESPONSE) + + let error = undefined as unknown as ReadError + await sut.getDatasetVersionsSummaries(testDatasetModel.id).catch((e) => (error = e)) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY + ) + expect(error).toBeInstanceOf(ReadError) + }) + }) + + describe('by persistent id', () => { + const expectedApiEndpoint = `${TestConstants.TEST_API_URL}/datasets/:persistentId/versions/compareSummary?persistentId=${TestConstants.TEST_DUMMY_PERSISTENT_ID}` + + test('should return dataset version summaries when providing persistent id and response is successful', async () => { + jest.spyOn(axios, 'get').mockResolvedValue(testDatasetVersionSummariesResponse) + + // API Key auth + let actual = await sut.getDatasetVersionsSummaries(TestConstants.TEST_DUMMY_PERSISTENT_ID) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY + ) + expect(actual).toStrictEqual([testDatasetVersionSummaries]) + + // Session cookie auth + ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.SESSION_COOKIE) + + actual = await sut.getDatasetVersionsSummaries(TestConstants.TEST_DUMMY_PERSISTENT_ID) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_SESSION_COOKIE + ) + expect(actual).toStrictEqual([testDatasetVersionSummaries]) + }) + + test('should return error result on error response', async () => { + jest.spyOn(axios, 'get').mockRejectedValue(TestConstants.TEST_ERROR_RESPONSE) + + let error = undefined as unknown as ReadError + await sut + .getDatasetVersionsSummaries(TestConstants.TEST_DUMMY_PERSISTENT_ID) + .catch((e) => (error = e)) + + expect(axios.get).toHaveBeenCalledWith( + expectedApiEndpoint, + TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY + ) + expect(error).toBeInstanceOf(ReadError) + }) + }) + }) }) diff --git a/test/unit/datasets/GetDatasetVersionsSummaries.test.ts b/test/unit/datasets/GetDatasetVersionsSummaries.test.ts new file mode 100644 index 00000000..184ec648 --- /dev/null +++ b/test/unit/datasets/GetDatasetVersionsSummaries.test.ts @@ -0,0 +1,32 @@ +import { ReadError } from '../../../src/core/domain/repositories/ReadError' +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' +import { createDatasetVersionSummaryModel } from '../../testHelpers/datasets/datasetVersionsSummariesHelper' +import { GetDatasetVersionsSummaries } from '../../../src/datasets/domain/useCases/GetDatasetVersionsSummaries' + +const testDatasetId = 1 + +describe('execute', () => { + test('should return dataset versions summaries on repository success', async () => { + const testDatasetVersionsSummaries = [createDatasetVersionSummaryModel()] + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.getDatasetVersionsSummaries = jest + .fn() + .mockResolvedValue(testDatasetVersionsSummaries) + const sut = new GetDatasetVersionsSummaries(datasetsRepositoryStub) + + const actual = await sut.execute(testDatasetId) + + expect(actual).toEqual(testDatasetVersionsSummaries) + expect(datasetsRepositoryStub.getDatasetVersionsSummaries).toHaveBeenCalledWith(testDatasetId) + }) + + test('should return error result on repository error', async () => { + const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository + datasetsRepositoryStub.getDatasetVersionsSummaries = jest + .fn() + .mockRejectedValue(new ReadError()) + const sut = new GetDatasetVersionsSummaries(datasetsRepositoryStub) + + await expect(sut.execute(testDatasetId)).rejects.toThrow(ReadError) + }) +})