Skip to content
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

122 - Extend dataset and file models to contain owner information #127

3 changes: 3 additions & 0 deletions src/datasets/domain/models/Dataset.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { DvObjectOwnerNode } from '../../../dv-object/domain/models/DvObjectOwnerNode';

export interface Dataset {
id: number;
persistentId: string;
Expand All @@ -8,6 +10,7 @@ export interface Dataset {
publicationDate?: string;
citationDate?: string;
metadataBlocks: DatasetMetadataBlocks;
isPartOf: DvObjectOwnerNode;
}

export interface DatasetVersionInfo {
Expand Down
4 changes: 2 additions & 2 deletions src/datasets/domain/useCases/CreateDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ export class CreateDataset implements UseCase<CreatedDatasetIdentifiers> {
this.newDatasetValidator = newDatasetValidator;
}

async execute(newDataset: NewDatasetDTO, collectionId: string = 'root'): Promise<CreatedDatasetIdentifiers> {
async execute(newDataset: NewDatasetDTO, collectionId = 'root'): Promise<CreatedDatasetIdentifiers> {
const metadataBlocks = await this.getNewDatasetMetadataBlocks(newDataset);
this.newDatasetValidator.validate(newDataset, metadataBlocks);
return this.datasetsRepository.createDataset(newDataset, metadataBlocks, collectionId);
}

async getNewDatasetMetadataBlocks(newDataset: NewDatasetDTO): Promise<MetadataBlock[]> {
let metadataBlocks: MetadataBlock[] = [];
const metadataBlocks: MetadataBlock[] = [];
await Promise.all(
newDataset.metadataBlockValues.map(async (metadataBlockValue: NewDatasetMetadataBlockValuesDTO) => {
metadataBlocks.push(await this.metadataBlocksRepository.getMetadataBlockByName(metadataBlockValue.name));
Expand Down
5 changes: 4 additions & 1 deletion src/datasets/infra/repositories/DatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
}

public async getPrivateUrlDataset(token: string): Promise<Dataset> {
return this.doGet(this.buildApiEndpoint(this.datasetsResourceName, `privateUrlDatasetVersion/${token}`))
return this.doGet(this.buildApiEndpoint(this.datasetsResourceName, `privateUrlDatasetVersion/${token}`), false, {
returnOwners: true,
})
.then((response) => transformVersionResponseToDataset(response))
.catch((error) => {
throw error;
Expand All @@ -48,6 +50,7 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
{
includeDeaccessioned: includeDeaccessioned,
excludeFiles: true,
returnOwners: true,
},
)
.then((response) => transformVersionResponseToDataset(response))
Expand Down
53 changes: 53 additions & 0 deletions src/datasets/infra/repositories/transformers/DatasetPayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { FilePayload } from '../../../../files/infra/repositories/transformers/FilePayload';
import { OwnerNodePayload } from '../../../../dv-object/infra/repositories/transformers/OwnerNodePayload';

export interface DatasetPayload {
datasetId: number;
datasetPersistentId: string;
id: number;
versionNumber: number;
versionMinorNumber: number;
versionState: string;
createTime: string;
lastUpdateTime: string;
releaseTime: string;
metadataBlocks: MetadataBlocksPayload;
license?: LicensePayload;
alternativePersistentId?: string;
publicationDate?: string;
citationDate?: string;
files: FilePayload[];
isPartOf: OwnerNodePayload;
}

export interface LicensePayload {
name: string;
uri: string;
iconUri?: string;
}

export interface MetadataBlocksPayload {
[blockName: string]: MetadataBlockPayload;
}

export interface MetadataBlockPayload {
name: string;
fields: MetadataFieldPayload[];
}

export interface MetadataFieldPayload {
typeName: string;
value: MetadataFieldValuePayload;
typeClass: string;
multiple: boolean;
}

export type MetadataFieldValuePayload =
| string
| string[]
| MetadataSubfieldValuePayload
| MetadataSubfieldValuePayload[];

export interface MetadataSubfieldValuePayload {
[key: string]: { value: string; typeName: string; multiple: boolean; typeClass: string };
}
100 changes: 63 additions & 37 deletions src/datasets/infra/repositories/transformers/datasetTransformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,28 @@ import {
DatasetMetadataFieldValue,
DatasetLicense,
DatasetMetadataBlocks,
ANONYMIZED_FIELD_VALUE,
} from '../../../domain/models/Dataset';
import { AxiosResponse } from 'axios';
import TurndownService from 'turndown';
import {
DatasetPayload,
LicensePayload,
MetadataBlocksPayload,
MetadataSubfieldValuePayload,
MetadataFieldPayload,
MetadataFieldValuePayload,
} from './DatasetPayload';
import { transformPayloadToOwnerNode } from '../../../../dv-object/infra/repositories/transformers/dvObjectOwnerNodeTransformer';

const turndownService = new TurndownService();

export const transformVersionResponseToDataset = (response: AxiosResponse): Dataset => {
const versionPayload = response.data.data;
return transformVersionPayloadToDataset(versionPayload);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const transformVersionPayloadToDataset = (versionPayload: any): Dataset => {

const transformVersionPayloadToDataset = (versionPayload: DatasetPayload): Dataset => {
const datasetModel: Dataset = {
id: versionPayload.datasetId,
persistentId: versionPayload.datasetPersistentId,
Expand All @@ -31,6 +41,7 @@ const transformVersionPayloadToDataset = (versionPayload: any): Dataset => {
releaseTime: new Date(versionPayload.releaseTime),
},
metadataBlocks: transformPayloadToDatasetMetadataBlocks(versionPayload.metadataBlocks),
isPartOf: transformPayloadToOwnerNode(versionPayload.isPartOf),
};
if ('license' in versionPayload) {
datasetModel.license = transformPayloadToDatasetLicense(versionPayload.license);
Expand All @@ -46,8 +57,8 @@ const transformVersionPayloadToDataset = (versionPayload: any): Dataset => {
}
return datasetModel;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const transformPayloadToDatasetLicense = (licensePayload: any): DatasetLicense => {

const transformPayloadToDatasetLicense = (licensePayload: LicensePayload): DatasetLicense => {
const datasetLicense: DatasetLicense = {
name: licensePayload.name,
uri: licensePayload.uri,
Expand All @@ -58,49 +69,64 @@ const transformPayloadToDatasetLicense = (licensePayload: any): DatasetLicense =
}
return datasetLicense;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const transformPayloadToDatasetMetadataBlocks = (metadataBlocksPayload: any): DatasetMetadataBlocks => {

const transformPayloadToDatasetMetadataBlocks = (
metadataBlocksPayload: MetadataBlocksPayload,
): DatasetMetadataBlocks => {
return Object.keys(metadataBlocksPayload).map((metadataBlockKey) => {
const metadataBlock = metadataBlocksPayload[metadataBlockKey];
return {
name: metadataBlockKey,
fields: transformPayloadToDatasetMetadataFields(metadataBlocksPayload[metadataBlockKey].fields),
name: metadataBlock.name,
fields: transformPayloadToDatasetMetadataFields(metadataBlock.fields),
};
}) as DatasetMetadataBlocks;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const transformPayloadToDatasetMetadataFields = (metadataFieldsPayload: any): DatasetMetadataFields => {
const metadataFieldKeys = Object.keys(metadataFieldsPayload);
const metadataFields: DatasetMetadataFields = {};
for (const metadataFieldKey of metadataFieldKeys) {
const metadataField = metadataFieldsPayload[metadataFieldKey];
const metadataFieldTypeName = metadataField.typeName;
metadataFields[metadataFieldTypeName] = transformPayloadToDatasetMetadataFieldValue(metadataField.value);
}
return metadataFields;

const transformPayloadToDatasetMetadataFields = (
metadataFieldsPayload: MetadataFieldPayload[],
): DatasetMetadataFields => {
return metadataFieldsPayload.reduce((acc: DatasetMetadataFields, field: MetadataFieldPayload) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor detail - Can we rename "acc" to something more understandable? Like datasetMetadataFieldsResult or simply result

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like result because it means finished, but the variable in this case is the accumulator so it's not really the result until the reduce it's finished, I rename it to datasetMetadataFieldsMap, hope that's ok

acc[field.typeName] = transformPayloadToDatasetMetadataFieldValue(field.value, field.typeClass);
return acc;
}, {});
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const transformPayloadToDatasetMetadataFieldValue = (metadataFieldValuePayload: any): DatasetMetadataFieldValue => {
let metadataFieldValue: DatasetMetadataFieldValue;
if (Array.isArray(metadataFieldValuePayload)) {
const isArrayOfObjects = typeof metadataFieldValuePayload[0] === 'object';
if (!isArrayOfObjects) {
metadataFieldValue = metadataFieldValuePayload.map(transformHtmlToMarkdown);

const transformPayloadToDatasetMetadataFieldValue = (
metadataFieldValuePayload: MetadataFieldValuePayload,
typeClass: string,
): DatasetMetadataFieldValue => {
function isArrayOfSubfieldValue(
array: (string | MetadataSubfieldValuePayload)[],
): array is MetadataSubfieldValuePayload[] {
return array.length > 0 && typeof array[0] !== 'string';
}

if (typeClass === 'anonymized') {
return ANONYMIZED_FIELD_VALUE;
}

if (typeof metadataFieldValuePayload === 'string') {
return transformHtmlToMarkdown(metadataFieldValuePayload);
} else if (Array.isArray(metadataFieldValuePayload)) {
if (isArrayOfSubfieldValue(metadataFieldValuePayload)) {
return metadataFieldValuePayload.map((v) => transformPayloadToDatasetMetadataSubfieldValue(v));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor detail - Can we rename "v" to something more understandable?

} else {
const datasetMetadataSubfields: DatasetMetadataSubField[] = [];
metadataFieldValuePayload.forEach(function (metadataSubFieldValuePayload) {
const subFieldKeys = Object.keys(metadataSubFieldValuePayload);
const record: DatasetMetadataSubField = {};
for (const subFieldKey of subFieldKeys) {
record[subFieldKey] = transformHtmlToMarkdown(metadataSubFieldValuePayload[subFieldKey].value);
}
datasetMetadataSubfields.push(record);
});
metadataFieldValue = datasetMetadataSubfields;
return metadataFieldValuePayload.map(transformHtmlToMarkdown);
}
} else {
metadataFieldValue = transformHtmlToMarkdown(metadataFieldValuePayload);
return transformPayloadToDatasetMetadataSubfieldValue(metadataFieldValuePayload as MetadataSubfieldValuePayload);
}
return metadataFieldValue;
};

const transformPayloadToDatasetMetadataSubfieldValue = (
metadataSubfieldValuePayload: MetadataSubfieldValuePayload,
): DatasetMetadataSubField => {
const result: DatasetMetadataSubField = {};
Object.keys(metadataSubfieldValuePayload).forEach((key) => {
const subFieldValue = metadataSubfieldValuePayload[key].value;
result[key] = transformHtmlToMarkdown(subFieldValue);
});
return result;
};

const transformHtmlToMarkdown = (source: string): string => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const transformMetadataBlockModelsToRequestPayload = (
newDatasetMetadataBlocksValues: NewDatasetMetadataBlockValuesDTO[],
metadataBlocks: MetadataBlock[],
): Record<string, MetadataBlockRequestPayload> => {
let metadataBlocksRequestPayload: Record<string, MetadataBlockRequestPayload> = {};
const metadataBlocksRequestPayload: Record<string, MetadataBlockRequestPayload> = {};
newDatasetMetadataBlocksValues.forEach(function (newDatasetMetadataBlockValues: NewDatasetMetadataBlockValuesDTO) {
const metadataBlock: MetadataBlock = metadataBlocks.find(
(metadataBlock) => metadataBlock.name == newDatasetMetadataBlockValues.name,
Expand All @@ -69,7 +69,7 @@ export const transformMetadataFieldModelsToRequestPayload = (
newDatasetMetadataFields: NewDatasetMetadataFieldsDTO,
metadataBlockFields: Record<string, MetadataFieldInfo>,
): MetadataFieldRequestPayload[] => {
let metadataFieldsRequestPayload: MetadataFieldRequestPayload[] = [];
const metadataFieldsRequestPayload: MetadataFieldRequestPayload[] = [];
for (const metadataFieldKey of Object.keys(newDatasetMetadataFields)) {
const newDatasetMetadataChildFieldValue: NewDatasetMetadataFieldValueDTO =
newDatasetMetadataFields[metadataFieldKey];
Expand Down Expand Up @@ -124,7 +124,7 @@ export const transformMetadataChildFieldValueToRequestPayload = (
newDatasetMetadataChildFieldValue: NewDatasetMetadataChildFieldValueDTO,
metadataBlockFieldInfo: MetadataFieldInfo,
): Record<string, MetadataFieldRequestPayload> => {
let metadataChildFieldRequestPayload: Record<string, MetadataFieldRequestPayload> = {};
const metadataChildFieldRequestPayload: Record<string, MetadataFieldRequestPayload> = {};
for (const metadataChildFieldKey of Object.keys(newDatasetMetadataChildFieldValue)) {
const childMetadataFieldInfo: MetadataFieldInfo = metadataBlockFieldInfo.childMetadataFields[metadataChildFieldKey];
const value: string = newDatasetMetadataChildFieldValue[metadataChildFieldKey] as unknown as string;
Expand Down
12 changes: 12 additions & 0 deletions src/dv-object/domain/models/DvObjectOwnerNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface DvObjectOwnerNode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think dv-object should be considered as a sub-domain in the package. I would place this file and any other dvObject related file in the core subdomain, as the dvObject is a concept shared across the different subdomains (collections, files and datasets).

type: DvObjectType;
identifier: string;
displayName: string;
isPartOf?: DvObjectOwnerNode;
}

export enum DvObjectType {
DATAVERSE = 'DATAVERSE',
DATASET = 'DATASET',
FILE = 'FILE',
}
1 change: 1 addition & 0 deletions src/dv-object/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { DvObjectOwnerNode } from './domain/models/DvObjectOwnerNode';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface OwnerNodePayload {
type: string;
identifier: string;
displayName: string;
isPartOf?: OwnerNodePayload;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { OwnerNodePayload } from './OwnerNodePayload';
import { DvObjectOwnerNode, DvObjectType } from '../../../domain/models/DvObjectOwnerNode';

export const transformPayloadToOwnerNode = (ownerNodePayload: OwnerNodePayload): DvObjectOwnerNode => {
return {
type: ownerNodePayload.type as DvObjectType,
identifier: ownerNodePayload.identifier,
displayName: ownerNodePayload.displayName,
...(ownerNodePayload.isPartOf && { isPartOf: transformPayloadToOwnerNode(ownerNodePayload.isPartOf) }),
};
};
3 changes: 3 additions & 0 deletions src/files/domain/models/File.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { DvObjectOwnerNode } from '../../../dv-object/domain/models/DvObjectOwnerNode';

export interface File {
id: number;
persistentId: string;
Expand Down Expand Up @@ -31,6 +33,7 @@ export interface File {
deleted: boolean;
tabularData: boolean;
fileAccessRequest?: boolean;
isPartOf?: DvObjectOwnerNode;
}

export interface FileEmbargo {
Expand Down
2 changes: 1 addition & 1 deletion src/files/infra/repositories/FilesRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export class FilesRepository extends ApiRepository implements IFilesRepository {
}

public async getFile(fileId: number | string, datasetVersionId: string): Promise<File> {
return this.doGet(this.getFileEndpoint(fileId, datasetVersionId), true)
return this.doGet(this.getFileEndpoint(fileId, datasetVersionId), true, { returnOwners: true })
.then((response) => transformFileResponseToFile(response))
.catch((error) => {
throw error;
Expand Down
52 changes: 52 additions & 0 deletions src/files/infra/repositories/transformers/FilePayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { OwnerNodePayload } from '../../../../dv-object/infra/repositories/transformers/OwnerNodePayload';

export interface FilePayload {
dataFile: {
id: number;
persistentId: string;
filename: string;
pidURL?: string;
filesize: number;
version: number;
description?: string;
restricted: boolean;
directoryLabel?: string;
datasetVersionId?: number;
categories?: string[];
contentType: string;
friendlyType: string;
embargo?: EmbargoPayload;
storageIdentifier?: string;
originalFormat?: string;
originalFormatLabel?: string;
originalSize?: number;
originalName?: string;
UNF?: string;
rootDataFileId?: number;
previousDataFileId?: number;
md5?: string;
checksum?: ChecksumPayload;
fileMetadataId?: number;
tabularTags?: string[];
creationDate?: string;
publicationDate?: string;
deleted: boolean;
tabularData: boolean;
fileAccessRequest?: boolean;
isPartOf?: OwnerNodePayload;
};
version: number;
restricted: boolean;
label: string;
datasetVersionId: number;
}

export interface EmbargoPayload {
dateAvailable: string;
reason?: string;
}

export interface ChecksumPayload {
type: string;
value: string;
}
Loading
Loading