Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './info';
export * from './users';
export * from './auth';
export * from './datasets';
export * from './metadataBlocks';
19 changes: 19 additions & 0 deletions src/metadataBlocks/domain/models/MetadataBlock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface MetadataBlock {
id: number;
name: string;
displayName: string;
metadataFields: Record<string, MetadataFieldInfo>;
}

export interface MetadataFieldInfo {
name: string;
displayName: string;
title: string;
type: string;
watermark: string;
description: string;
multiple: boolean;
isControlledVocabulary: boolean;
displayFormat: string;
childMetadataFields?: Record<string, MetadataFieldInfo>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { MetadataBlock } from '../models/MetadataBlock';

export interface IMetadataBlocksRepository {
getMetadataBlockByName(metadataBlockName: string): Promise<MetadataBlock>;
}
15 changes: 15 additions & 0 deletions src/metadataBlocks/domain/useCases/GetMetadataBlockByName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { UseCase } from '../../../core/domain/useCases/UseCase';
import { IMetadataBlocksRepository } from '../repositories/IMetadataBlocksRepository';
import { MetadataBlock } from '../models/MetadataBlock';

export class GetMetadataBlockByName implements UseCase<MetadataBlock> {
private metadataBlocksRepository: IMetadataBlocksRepository;

constructor(metadataBlocksRepository: IMetadataBlocksRepository) {
this.metadataBlocksRepository = metadataBlocksRepository;
}

async execute(metadataBlockName: string): Promise<MetadataBlock> {
return await this.metadataBlocksRepository.getMetadataBlockByName(metadataBlockName);
}
}
9 changes: 9 additions & 0 deletions src/metadataBlocks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { GetMetadataBlockByName } from './domain/useCases/GetMetadataBlockByName';
import { MetadataBlocksRepository } from './infra/repositories/MetadataBlocksRepository';

const metadataBlocksRepository = new MetadataBlocksRepository();

const getMetadataBlockByName = new GetMetadataBlockByName(metadataBlocksRepository);

export { getMetadataBlockByName };
export { MetadataBlock, MetadataFieldInfo } from './domain/models/MetadataBlock';
14 changes: 14 additions & 0 deletions src/metadataBlocks/infra/repositories/MetadataBlocksRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository';
import { IMetadataBlocksRepository } from '../../domain/repositories/IMetadataBlocksRepository';
import { MetadataBlock } from '../../domain/models/MetadataBlock';
import { transformMetadataBlockResponseToMetadataBlock } from './transformers/metadataBlockTransformers';

export class MetadataBlocksRepository extends ApiRepository implements IMetadataBlocksRepository {
public async getMetadataBlockByName(metadataBlockName: string): Promise<MetadataBlock> {
return this.doGet(`/metadatablocks/${metadataBlockName}`)
.then((response) => transformMetadataBlockResponseToMetadataBlock(response))
.catch((error) => {
throw error;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { AxiosResponse } from 'axios';
import { MetadataBlock, MetadataFieldInfo } from '../../../domain/models/MetadataBlock';

export const transformMetadataBlockResponseToMetadataBlock = (response: AxiosResponse): MetadataBlock => {
const metadataBlockPayload = response.data.data;
let metadataFields: Record<string, MetadataFieldInfo> = {};
const metadataBlockFieldsPayload = metadataBlockPayload.fields;
Object.keys(metadataBlockFieldsPayload).map((metadataFieldKey) => {
const metadataFieldInfoPayload = metadataBlockFieldsPayload[metadataFieldKey];
metadataFields[metadataFieldKey] = transformPayloadMetadataFieldInfo(metadataFieldInfoPayload);
});
return {
id: metadataBlockPayload.id,
name: metadataBlockPayload.name,
displayName: metadataBlockPayload.displayName,
metadataFields: metadataFields,
};
};

const transformPayloadMetadataFieldInfo = (
metadataFieldInfoPayload: any,
isChild: boolean = false,
): MetadataFieldInfo => {
let metadataFieldInfo: MetadataFieldInfo = {
name: metadataFieldInfoPayload.name,
displayName: metadataFieldInfoPayload.displayName,
title: metadataFieldInfoPayload.title,
type: metadataFieldInfoPayload.type,
watermark: metadataFieldInfoPayload.watermark,
description: metadataFieldInfoPayload.description,
multiple: metadataFieldInfoPayload.multiple,
isControlledVocabulary: metadataFieldInfoPayload.isControlledVocabulary,
displayFormat: metadataFieldInfoPayload.displayFormat,
};
if (!isChild && metadataFieldInfoPayload.hasOwnProperty('childFields')) {
const childMetadataFieldsPayload = metadataFieldInfoPayload.childFields;
let childMetadataFields: Record<string, MetadataFieldInfo> = {};
Object.keys(childMetadataFieldsPayload).map((metadataFieldKey) => {
childMetadataFields[metadataFieldKey] = transformPayloadMetadataFieldInfo(
childMetadataFieldsPayload[metadataFieldKey],
true,
);
});
metadataFieldInfo.childMetadataFields = childMetadataFields;
}
return metadataFieldInfo;
};
113 changes: 113 additions & 0 deletions test/testHelpers/metadataBlocks/metadataBlockHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { MetadataBlock } from '../../../src/metadataBlocks/domain/models/MetadataBlock';

export const createMetadataBlockModel = (): MetadataBlock => {
return {
id: 1,
name: 'testName',
displayName: 'testDisplayName',
metadataFields: {
testField1: {
name: 'testName1',
displayName: 'testDisplayName1',
title: 'testTitle1',
type: 'testType1',
watermark: 'testWatermark1',
description: 'testDescription1',
multiple: false,
isControlledVocabulary: false,
displayFormat: '#VALUE',
},
testField2: {
name: 'testName2',
displayName: 'testDisplayName2',
title: 'testTitle2',
type: 'testType2',
watermark: 'testWatermark2',
description: 'testDescription2',
multiple: true,
isControlledVocabulary: false,
displayFormat: '',
childMetadataFields: {
testField3: {
name: 'testName3',
displayName: 'testDisplayName3',
title: 'testTitle3',
type: 'testType3',
watermark: 'testWatermark3',
description: 'testDescription3',
multiple: false,
isControlledVocabulary: false,
displayFormat: '#VALUE',
},
testField4: {
name: 'testName4',
displayName: 'testDisplayName4',
title: 'testTitle4',
type: 'testType4',
watermark: 'testWatermark4',
description: 'testDescription4',
multiple: false,
isControlledVocabulary: false,
displayFormat: '#VALUE',
},
},
},
},
};
};

export const createMetadataBlockPayload = (): any => {
return {
id: 1,
name: 'testName',
displayName: 'testDisplayName',
fields: {
testField1: {
name: 'testName1',
displayName: 'testDisplayName1',
title: 'testTitle1',
type: 'testType1',
watermark: 'testWatermark1',
description: 'testDescription1',
multiple: false,
isControlledVocabulary: false,
displayFormat: '#VALUE',
},
testField2: {
name: 'testName2',
displayName: 'testDisplayName2',
title: 'testTitle2',
type: 'testType2',
watermark: 'testWatermark2',
description: 'testDescription2',
multiple: true,
isControlledVocabulary: false,
displayFormat: '',
childFields: {
testField3: {
name: 'testName3',
displayName: 'testDisplayName3',
title: 'testTitle3',
type: 'testType3',
watermark: 'testWatermark3',
description: 'testDescription3',
multiple: false,
isControlledVocabulary: false,
displayFormat: '#VALUE',
},
testField4: {
name: 'testName4',
displayName: 'testDisplayName4',
title: 'testTitle4',
type: 'testType4',
watermark: 'testWatermark4',
description: 'testDescription4',
multiple: false,
isControlledVocabulary: false,
displayFormat: '#VALUE',
},
},
},
},
};
};
39 changes: 39 additions & 0 deletions test/unit/metadataBlocks/GetMetadataBlockByName.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { GetMetadataBlockByName } from '../../../src/metadataBlocks/domain/useCases/GetMetadataBlockByName';
import { IMetadataBlocksRepository } from '../../../src/metadataBlocks/domain/repositories/IMetadataBlocksRepository';
import { ReadError } from '../../../src/core/domain/repositories/ReadError';
import { assert, createSandbox, SinonSandbox } from 'sinon';
import { createMetadataBlockModel } from '../../testHelpers/metadataBlocks/metadataBlockHelper';

describe('execute', () => {
const sandbox: SinonSandbox = createSandbox();
const testMetadataBlockName = 'test';

afterEach(() => {
sandbox.restore();
});

test('should return metadata block on repository success', async () => {
const testMetadataBlock = createMetadataBlockModel();
const metadataBlocksRepositoryStub = <IMetadataBlocksRepository>{};
const getMetadataBlockByNameStub = sandbox.stub().returns(testMetadataBlock);
metadataBlocksRepositoryStub.getMetadataBlockByName = getMetadataBlockByNameStub;
const sut = new GetMetadataBlockByName(metadataBlocksRepositoryStub);

const actual = await sut.execute(testMetadataBlockName);

assert.match(actual, testMetadataBlock);
assert.calledWithExactly(getMetadataBlockByNameStub, testMetadataBlockName);
});

test('should return error result on repository error', async () => {
const metadataBlocksRepositoryStub = <IMetadataBlocksRepository>{};
const testReadError = new ReadError();
metadataBlocksRepositoryStub.getMetadataBlockByName = sandbox.stub().throwsException(testReadError);
const sut = new GetMetadataBlockByName(metadataBlocksRepositoryStub);

let actualError: ReadError = undefined;
await sut.execute(testMetadataBlockName).catch((e) => (actualError = e));

assert.match(actualError, testReadError);
});
});
58 changes: 58 additions & 0 deletions test/unit/metadataBlocks/MetadataBlocksRepository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { MetadataBlocksRepository } from '../../../src/metadataBlocks/infra/repositories/MetadataBlocksRepository';
import { assert, createSandbox, SinonSandbox } from 'sinon';
import axios from 'axios';
import { expect } from 'chai';
import { ReadError } from '../../../src/core/domain/repositories/ReadError';
import { ApiConfig } from '../../../src/core/infra/repositories/ApiConfig';
import {
createMetadataBlockModel,
createMetadataBlockPayload,
} from '../../testHelpers/metadataBlocks/metadataBlockHelper';

describe('getMetadataBlockByName', () => {
const sandbox: SinonSandbox = createSandbox();
const sut: MetadataBlocksRepository = new MetadataBlocksRepository();
const testApiUrl = 'https://test.dataverse.org/api/v1';
const testMetadataBlockName = 'test';

ApiConfig.init(testApiUrl);

afterEach(() => {
sandbox.restore();
});

test('should return metadata block on successful response', async () => {
const testSuccessfulResponse = {
data: {
status: 'OK',
data: createMetadataBlockPayload(),
},
};
const axiosGetStub = sandbox.stub(axios, 'get').resolves(testSuccessfulResponse);

const actual = await sut.getMetadataBlockByName(testMetadataBlockName);

assert.calledWithExactly(axiosGetStub, `${testApiUrl}/metadatablocks/${testMetadataBlockName}`, {
withCredentials: false,
});
assert.match(actual, createMetadataBlockModel());
});

test('should return error result on error response', async () => {
const testErrorResponse = {
response: {
status: 'ERROR',
message: 'test',
},
};
const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse);

let error: ReadError = undefined;
await sut.getMetadataBlockByName(testMetadataBlockName).catch((e) => (error = e));

assert.calledWithExactly(axiosGetStub, `${testApiUrl}/metadatablocks/${testMetadataBlockName}`, {
withCredentials: false,
});
expect(error).to.be.instanceOf(Error);
});
});