diff --git a/docs/useCases.md b/docs/useCases.md index 773c9122..ff7a266a 100644 --- a/docs/useCases.md +++ b/docs/useCases.md @@ -89,6 +89,8 @@ The different use cases currently available in the package are classified below, - [Get Maximum Embargo Duration In Months](#get-maximum-embargo-duration-in-months) - [Get ZIP Download Limit](#get-zip-download-limit) - [Get Application Terms of Use](#get-application-terms-of-use) +- [Licenses](#Licenses) + - [Get Available Standard License Terms](#get-available-standard-license-terms) - [Contact](#Contact) - [Send Feedback to Object Contacts](#send-feedback-to-object-contacts) - [Search](#Search) @@ -2084,6 +2086,26 @@ getApplicationTermsOfUse.execute().then((termsOfUse: string) => { _See [use case](../src/info/domain/useCases/GetApplicationTermsOfUse.ts) implementation_. +## Licenses + +### Get Available Standard License Terms + +Returns a list of available standard licenses that can be selected for a dataset. + +##### Example call: + +```typescript +import { getAvailableStandardLicenses, License } from '@iqss/dataverse-client-javascript' + +/* ... */ + +getAvailableStandardLicenses.execute().then((licenses: License[]) => { + /* ... */ +}) +``` + +_See [use case](../src/licenses/domain/useCases/GetAvailableStandardLicenses.ts) implementation_. + ## Contact #### Send Feedback to Object Contacts diff --git a/src/index.ts b/src/index.ts index 2fb70d9e..160e39e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,3 +9,4 @@ export * from './metadataBlocks' export * from './files' export * from './contactInfo' export * from './search' +export * from './licenses' diff --git a/src/licenses/domain/models/License.ts b/src/licenses/domain/models/License.ts new file mode 100644 index 00000000..7f16442e --- /dev/null +++ b/src/licenses/domain/models/License.ts @@ -0,0 +1,14 @@ +export interface License { + id: number + name: string + shortDescription?: string + uri: string + iconUri?: string + active: boolean + isDefault: boolean + sortOrder: number + rightsIdentifier?: string + rightsIdentifierScheme?: string + schemeUri?: string + languageCode?: string +} diff --git a/src/licenses/domain/repositories/ILicensesRepository.ts b/src/licenses/domain/repositories/ILicensesRepository.ts new file mode 100644 index 00000000..45309a89 --- /dev/null +++ b/src/licenses/domain/repositories/ILicensesRepository.ts @@ -0,0 +1,5 @@ +import { License } from '../models/License' + +export interface ILicensesRepository { + getAvailableStandardLicenses(): Promise +} diff --git a/src/licenses/domain/repositories/transformers/LicensePayload.ts b/src/licenses/domain/repositories/transformers/LicensePayload.ts new file mode 100644 index 00000000..a67228da --- /dev/null +++ b/src/licenses/domain/repositories/transformers/LicensePayload.ts @@ -0,0 +1,14 @@ +export interface LicensePayload { + id: number + name: string + shortDescription?: string + uri: string + iconUrl?: string + active: boolean + isDefault: boolean + sortOrder: number + rightsIdentifier?: string + rightsIdentifierScheme?: string + schemeUri?: string + languageCode?: string +} diff --git a/src/licenses/domain/repositories/transformers/licenseTransformers.ts b/src/licenses/domain/repositories/transformers/licenseTransformers.ts new file mode 100644 index 00000000..38883f3c --- /dev/null +++ b/src/licenses/domain/repositories/transformers/licenseTransformers.ts @@ -0,0 +1,22 @@ +import { AxiosResponse } from 'axios' +import { License } from '../../models/License' +import { LicensePayload } from './LicensePayload' + +export const transformPayloadToLicense = (response: AxiosResponse): License[] => { + const payload = response.data.data as LicensePayload[] + + return payload.map((license: LicensePayload) => ({ + id: license.id, + name: license.name, + shortDescription: license.shortDescription, + uri: license.uri, + iconUri: license.iconUrl, // in payload, it is called iconUrl, but iconUri is the name matching everywhere else + active: license.active, + isDefault: license.isDefault, + sortOrder: license.sortOrder, + rightsIdentifier: license.rightsIdentifier, + rightsIdentifierScheme: license.rightsIdentifierScheme, + schemeUri: license.schemeUri, + languageCode: license.languageCode + })) +} diff --git a/src/licenses/domain/useCases/GetAvailableStandardLicenses.ts b/src/licenses/domain/useCases/GetAvailableStandardLicenses.ts new file mode 100644 index 00000000..00517770 --- /dev/null +++ b/src/licenses/domain/useCases/GetAvailableStandardLicenses.ts @@ -0,0 +1,20 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase' +import { License } from '../models/License' +import { ILicensesRepository } from '../repositories/ILicensesRepository' + +export class GetAvailableStandardLicenses implements UseCase { + private licensesRepository: ILicensesRepository + + constructor(licensesRepository: ILicensesRepository) { + this.licensesRepository = licensesRepository + } + + /** + * Returns the list of available standard license terms that can be selected for a dataset. + * + * @returns {Promise} + */ + async execute(): Promise { + return await this.licensesRepository.getAvailableStandardLicenses() + } +} diff --git a/src/licenses/index.ts b/src/licenses/index.ts new file mode 100644 index 00000000..0d9158f3 --- /dev/null +++ b/src/licenses/index.ts @@ -0,0 +1,10 @@ +import { LicensesRepository } from './infra/repositories/LicensesRepository' +import { GetAvailableStandardLicenses } from './domain/useCases/GetAvailableStandardLicenses' + +const licensesRepository = new LicensesRepository() + +const getAvailableStandardLicenses = new GetAvailableStandardLicenses(licensesRepository) + +export { getAvailableStandardLicenses } + +export { License } from './domain/models/License' diff --git a/src/licenses/infra/repositories/LicensesRepository.ts b/src/licenses/infra/repositories/LicensesRepository.ts new file mode 100644 index 00000000..042fce56 --- /dev/null +++ b/src/licenses/infra/repositories/LicensesRepository.ts @@ -0,0 +1,15 @@ +import { ApiRepository } from '../../../core/infra/repositories/ApiRepository' +import { ILicensesRepository } from '../../domain/repositories/ILicensesRepository' +import { License } from '../../domain/models/License' + +export class LicensesRepository extends ApiRepository implements ILicensesRepository { + private readonly licensesResourceName: string = 'licenses' + + public async getAvailableStandardLicenses(): Promise { + return this.doGet(this.buildApiEndpoint(this.licensesResourceName)) + .then((response) => response.data.data) + .catch((error) => { + throw error + }) + } +} diff --git a/test/functional/licenses/GetAvailableStandardLicenses.test.ts b/test/functional/licenses/GetAvailableStandardLicenses.test.ts new file mode 100644 index 00000000..6e54d24c --- /dev/null +++ b/test/functional/licenses/GetAvailableStandardLicenses.test.ts @@ -0,0 +1,51 @@ +import { ApiConfig, getAvailableStandardLicenses, License } from '../../../src' +import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' +import { TestConstants } from '../../testHelpers/TestConstants' + +describe('getAvailableStandardLicenses', () => { + describe('execute', () => { + beforeAll(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + test('should return available standard license terms', async () => { + const actualLicenses: License[] = await getAvailableStandardLicenses.execute() + const expectedLicenses = [ + { + id: 1, + name: 'CC0 1.0', + shortDescription: 'Creative Commons CC0 1.0 Universal Public Domain Dedication.', + uri: 'http://creativecommons.org/publicdomain/zero/1.0', + iconUrl: 'https://licensebuttons.net/p/zero/1.0/88x31.png', + active: true, + isDefault: true, + sortOrder: 0, + rightsIdentifier: 'CC0-1.0', + rightsIdentifierScheme: 'SPDX', + schemeUri: 'https://spdx.org/licenses/', + languageCode: 'en' + }, + { + id: 2, + name: 'CC BY 4.0', + shortDescription: 'Creative Commons Attribution 4.0 International License.', + uri: 'http://creativecommons.org/licenses/by/4.0', + iconUrl: 'https://licensebuttons.net/l/by/4.0/88x31.png', + active: true, + isDefault: false, + sortOrder: 2, + rightsIdentifier: 'CC-BY-4.0', + rightsIdentifierScheme: 'SPDX', + schemeUri: 'https://spdx.org/licenses/', + languageCode: 'en' + } + ] + + expect(actualLicenses).toEqual(expectedLicenses) + }) + }) +}) diff --git a/test/integration/licenses/LicensesRepository.test.ts b/test/integration/licenses/LicensesRepository.test.ts new file mode 100644 index 00000000..26f27228 --- /dev/null +++ b/test/integration/licenses/LicensesRepository.test.ts @@ -0,0 +1,57 @@ +import { + ApiConfig, + DataverseApiAuthMechanism +} from '../../../src/core/infra/repositories/ApiConfig' +import { TestConstants } from '../../testHelpers/TestConstants' +import { LicensesRepository } from '../../../src/licenses/infra/repositories/LicensesRepository' + +describe('LicensesRepository', () => { + const sut: LicensesRepository = new LicensesRepository() + + describe('getAvailableStandardLicenses', () => { + beforeAll(async () => { + ApiConfig.init( + TestConstants.TEST_API_URL, + DataverseApiAuthMechanism.API_KEY, + process.env.TEST_API_KEY + ) + }) + + test('should return list of available standard license terms', async () => { + const actual = await sut.getAvailableStandardLicenses() + + const licenses = [ + { + id: 1, + name: 'CC0 1.0', + shortDescription: 'Creative Commons CC0 1.0 Universal Public Domain Dedication.', + uri: 'http://creativecommons.org/publicdomain/zero/1.0', + iconUrl: 'https://licensebuttons.net/p/zero/1.0/88x31.png', + active: true, + isDefault: true, + sortOrder: 0, + rightsIdentifier: 'CC0-1.0', + rightsIdentifierScheme: 'SPDX', + schemeUri: 'https://spdx.org/licenses/', + languageCode: 'en' + }, + { + id: 2, + name: 'CC BY 4.0', + shortDescription: 'Creative Commons Attribution 4.0 International License.', + uri: 'http://creativecommons.org/licenses/by/4.0', + iconUrl: 'https://licensebuttons.net/l/by/4.0/88x31.png', + active: true, + isDefault: false, + sortOrder: 2, + rightsIdentifier: 'CC-BY-4.0', + rightsIdentifierScheme: 'SPDX', + schemeUri: 'https://spdx.org/licenses/', + languageCode: 'en' + } + ] + + expect(actual).toEqual(licenses) + }) + }) +}) diff --git a/test/unit/licenses/GetAvailableStandardLicenses.test.ts b/test/unit/licenses/GetAvailableStandardLicenses.test.ts new file mode 100644 index 00000000..4c6857b4 --- /dev/null +++ b/test/unit/licenses/GetAvailableStandardLicenses.test.ts @@ -0,0 +1,62 @@ +import { License, ReadError } from '../../../src' +import { ILicensesRepository } from '../../../src/licenses/domain/repositories/ILicensesRepository' +import { GetAvailableStandardLicenses } from '../../../src/licenses/domain/useCases/GetAvailableStandardLicenses' + +describe('GetAvailableStandardLicenses', () => { + describe('execute', () => { + test('should return licenses array on repository success', async () => { + const licensesRepositoryStub: ILicensesRepository = {} as ILicensesRepository + + const testLicenses: License[] = [ + { + id: 1, + name: 'CC0 1.0', + uri: 'http://creativecommons.org/publicdomain/zero/1.0', + iconUri: 'https://licensebuttons.net/p/zero/1.0/88x31.png', + active: true, + isDefault: true, + sortOrder: 0, + rightsIdentifier: 'CC0-1.0', + rightsIdentifierScheme: 'SPDX', + schemeUri: 'https://spdx.org/licenses/', + languageCode: 'en' + }, + { + id: 2, + name: 'CC BY 4.0', + uri: 'http://creativecommons.org/licenses/by/4.0', + iconUri: 'https://licensebuttons.net/l/by/4.0/88x31.png', + active: true, + isDefault: false, + sortOrder: 2, + rightsIdentifier: 'CC-BY-4.0', + rightsIdentifierScheme: 'SPDX', + schemeUri: 'https://spdx.org/licenses/', + languageCode: 'en' + } + ] + + licensesRepositoryStub.getAvailableStandardLicenses = jest + .fn() + .mockResolvedValue(testLicenses) + const sut = new GetAvailableStandardLicenses(licensesRepositoryStub) + + const actual = await sut.execute() + + expect(actual).toEqual(testLicenses) + expect(licensesRepositoryStub.getAvailableStandardLicenses).toHaveBeenCalledTimes(1) + }) + + test('should return error result on repository error', async () => { + const licensesRepositoryStub: ILicensesRepository = {} as ILicensesRepository + const expectedError = new ReadError('Failed to fetch licenses') + licensesRepositoryStub.getAvailableStandardLicenses = jest + .fn() + .mockRejectedValue(expectedError) + const sut = new GetAvailableStandardLicenses(licensesRepositoryStub) + + await expect(sut.execute()).rejects.toThrow(ReadError) + expect(licensesRepositoryStub.getAvailableStandardLicenses).toHaveBeenCalledTimes(1) + }) + }) +})