Skip to content
Open
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
15 changes: 14 additions & 1 deletion src/common/pipes/include-validation.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export class IncludeValidationPipe implements PipeTransform<
? inValue
: isJsonString(inValue)
? JSON.parse(inValue ?? "{}").include
: Array(inValue);
: inValue.includes(",")
? IncludeValidationPipe.splitCsv(inValue)
: Array(inValue);

includeValueParsed?.map((field) => {
let relationField = field;
Expand All @@ -46,4 +48,15 @@ export class IncludeValidationPipe implements PipeTransform<

return inValue;
}

/**
* Split a comma-separated string into individual relation values.
* Handles CSV-style serialization used by some OpenAPI client generators.
*/
static splitCsv(inValue: string): string[] {
return inValue
.split(",")
.map((s) => s.trim())
.filter(Boolean);
}
}
7 changes: 7 additions & 0 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,13 @@ export function decodeMetadataKeyStrings(keys: string[]): string[] {
return keys.map((key) => decodeURIComponentExtended(key));
}

export function filterNullFromArray<T>(
value: (T | null | undefined)[] | undefined,
): T[] {
if (!value) return [];
return value.filter((item): item is T => item !== null && item !== undefined);
}

export function parseDate(dateString?: string): Date | undefined {
if (!dateString) return undefined;
const parsedDate = new Date(dateString);
Expand Down
2 changes: 2 additions & 0 deletions src/datasets/datasets-public.v4.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ export class DatasetsPublicV4Controller {
type: String,
required: false,
isArray: true,
style: "form",
explode: true,
})
async findByIdPublic(
@Param("pid") id: string,
Expand Down
2 changes: 2 additions & 0 deletions src/datasets/datasets.v4.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,8 @@ export class DatasetsV4Controller {
type: String,
required: false,
isArray: true,
style: "form",
explode: true,
})
async findById(
@Req() request: Request,
Expand Down
122 changes: 116 additions & 6 deletions src/datasets/dto/output-dataset.dto.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { ApiProperty, PartialType } from "@nestjs/swagger";
import { ApiProperty, PartialType, getSchemaPath } from "@nestjs/swagger";
import { CreateDatasetDto } from "./create-dataset.dto";
import { IsDateString, IsString } from "class-validator";
import { decodeScientificMetadataKeys } from "src/common/utils";
import { Transform } from "class-transformer";
import { IsArray, IsDateString, IsOptional, IsString } from "class-validator";
import {
decodeScientificMetadataKeys,
filterNullFromArray,
} from "src/common/utils";
import { Transform, Type } from "class-transformer";
import { OutputOrigDatablockDto } from "src/origdatablocks/dto/output-origdatablock.dto";
import { Datablock } from "src/datablocks/schemas/datablock.schema";
import { OutputAttachmentV4Dto } from "src/attachments/dto/output-attachment.v4.dto";
import { Instrument } from "src/instruments/schemas/instrument.schema";
import { ProposalClass } from "src/proposals/schemas/proposal.schema";
import { OutputSampleDto } from "src/samples/dto/output-sample.dto";

export class OutputDatasetDto extends CreateDatasetDto {
@ApiProperty({
Expand Down Expand Up @@ -51,12 +60,13 @@ export class OutputDatasetDto extends CreateDatasetDto {

@ApiProperty({
type: String,
required: true,
required: false,
description:
"Version of the API used when the dataset was created or last updated. API version is defined in code for each release. Managed by the system.",
})
@IsOptional()
@IsString()
version: string;
version?: string;

@ApiProperty({
type: Object,
Expand All @@ -66,6 +76,106 @@ export class OutputDatasetDto extends CreateDatasetDto {
})
@Transform(({ value }) => decodeScientificMetadataKeys(value))
declare scientificMetadata?: Record<string, unknown>;

@Transform(({ value }) => filterNullFromArray<string>(value))
declare keywords?: string[];

@Transform(({ value }) => filterNullFromArray<string>(value))
declare sharedWith?: string[];

@Transform(({ value }) => filterNullFromArray<string>(value))
declare proposalIds?: string[];

@Transform(({ value }) => filterNullFromArray<string>(value))
declare sampleIds?: string[];

@Transform(({ value }) => filterNullFromArray<string>(value))
declare instrumentIds?: string[];

@Transform(({ value }) => filterNullFromArray<string>(value))
declare inputDatasets?: string[];

@Transform(({ value }) => filterNullFromArray<string>(value))
declare usedSoftware?: string[];

@Transform(({ value }) => filterNullFromArray<string>(value))
declare principalInvestigators?: string[];

// ---------------------------------------------------------------------------
// Includable relation fields — populated via ?include query parameter
// ---------------------------------------------------------------------------

@ApiProperty({
type: "array",
items: { $ref: getSchemaPath(OutputOrigDatablockDto) },
required: false,
description:
"Containers that list all files and their attributes which make up a dataset. Included when ?include=origdatablocks is used.",
})
@IsOptional()
@IsArray()
@Type(() => OutputOrigDatablockDto)
origdatablocks?: OutputOrigDatablockDto[];

@ApiProperty({
type: "array",
items: { $ref: getSchemaPath(Datablock) },
required: false,
description:
"Archived file blocks with checksums. Included when ?include=datablocks is used.",
})
@IsOptional()
@IsArray()
@Type(() => Datablock)
datablocks?: Datablock[];

@ApiProperty({
type: "array",
items: { $ref: getSchemaPath(OutputAttachmentV4Dto) },
required: false,
description:
"Small attachments such as preview images. Included when ?include=attachments is used.",
})
@IsOptional()
@IsArray()
@Type(() => OutputAttachmentV4Dto)
attachments?: OutputAttachmentV4Dto[];

@ApiProperty({
type: "array",
items: { $ref: getSchemaPath(Instrument) },
required: false,
description:
"Instruments associated with the dataset. Included when ?include=instruments is used.",
})
@IsOptional()
@IsArray()
@Type(() => Instrument)
instruments?: Instrument[];

@ApiProperty({
type: "array",
items: { $ref: getSchemaPath(ProposalClass) },
required: false,
description:
"Proposals associated with the dataset. Included when ?include=proposals is used.",
})
@IsOptional()
@IsArray()
@Type(() => ProposalClass)
proposals?: ProposalClass[];

@ApiProperty({
type: "array",
items: { $ref: getSchemaPath(OutputSampleDto) },
required: false,
description:
"Samples associated with the dataset. Included when ?include=samples is used.",
})
@IsOptional()
@IsArray()
@Type(() => OutputSampleDto)
samples?: OutputSampleDto[];
}

export class PartialOutputDatasetDto extends PartialType(OutputDatasetDto) {}
16 changes: 8 additions & 8 deletions src/datasets/schemas/dataset.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ export class DatasetClass extends OwnableClass {
description:
"Array of tags associated with the meaning or contents of this dataset. Values should ideally come from defined vocabularies, taxonomies, ontologies or knowledge graphs.",
})
@Prop({ type: [String], required: false })
keywords: string[];
@Prop({ type: [String], required: false, default: [] })
keywords: string[] = [];

@ApiProperty({
type: String,
Expand Down Expand Up @@ -366,7 +366,7 @@ export class DatasetClass extends OwnableClass {
description:
"First and last name of principal investigator(s). Multiple PIs can be provided as separate strings in the array. This field is required if the dataset is a Raw dataset.",
})
@Prop({ type: [String], required: false })
@Prop({ type: [String], required: false, default: [] })
principalInvestigators?: string[];

@ApiProperty({
Expand Down Expand Up @@ -420,7 +420,7 @@ export class DatasetClass extends OwnableClass {
description:
"The ID of the proposal to which the dataset belongs to and it has been acquired under.",
})
@Prop({ type: [String], ref: "Proposal", required: false })
@Prop({ type: [String], ref: "Proposal", required: false, default: [] })
proposalIds?: string[];

@ApiProperty({
Expand All @@ -429,7 +429,7 @@ export class DatasetClass extends OwnableClass {
description:
"Single ID or array of IDS of the samples used when collecting the data.",
})
@Prop({ type: [String], ref: "Sample", required: false })
@Prop({ type: [String], ref: "Sample", required: false, default: [] })
sampleIds?: string[];

@ApiProperty({
Expand All @@ -438,7 +438,7 @@ export class DatasetClass extends OwnableClass {
description:
"Id of the instrument or array of IDS of the instruments where the data contained in this dataset was created/acquired.",
})
@Prop({ type: [String], ref: "Instrument", required: false })
@Prop({ type: [String], ref: "Instrument", required: false, default: [] })
instrumentIds?: string[];

@ApiProperty({
Expand All @@ -447,7 +447,7 @@ export class DatasetClass extends OwnableClass {
description:
"Array of input dataset identifiers used in producing the derived dataset. Ideally these are the global identifier to existing datasets inside this or federated data catalogs.",
})
@Prop({ type: [String], required: false })
@Prop({ type: [String], required: false, default: [] })
inputDatasets?: string[];

@ApiProperty({
Expand All @@ -456,7 +456,7 @@ export class DatasetClass extends OwnableClass {
description:
"A list of links to software repositories which uniquely identifies the pieces of software, including versions, used for yielding the derived data.",
})
@Prop({ type: [String], required: false })
@Prop({ type: [String], required: false, default: [] })
usedSoftware?: string[];

@ApiProperty({
Expand Down
2 changes: 1 addition & 1 deletion src/datasets/types/dataset-lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const DATASET_LOOKUP_FIELDS: Record<
$expr: {
$anyElementTrue: {
$map: {
input: "$relationships",
input: { $ifNull: ["$relationships", []] },
as: "relationship",
in: {
$and: [
Expand Down
7 changes: 4 additions & 3 deletions src/origdatablocks/dto/output-origdatablock.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiProperty, PartialType } from "@nestjs/swagger";
import { CreateOrigDatablockDto } from "./create-origdatablock.dto";
import { IsDateString, IsString } from "class-validator";
import { IsDateString, IsOptional, IsString } from "class-validator";

export class OutputOrigDatablockDto extends CreateOrigDatablockDto {
@ApiProperty({
Expand Down Expand Up @@ -41,12 +41,13 @@ export class OutputOrigDatablockDto extends CreateOrigDatablockDto {

@ApiProperty({
type: String,
required: true,
required: false,
description:
"Version of the API used when the origdatablock was created or last updated. API version is defined in code for each release. Managed by the system.",
})
@IsOptional()
@IsString()
version: string;
version?: string;
}

export class PartialOutputOrigDatablockDto extends PartialType(
Expand Down
2 changes: 2 additions & 0 deletions src/origdatablocks/origdatablocks.v4.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,8 @@ export class OrigDatablocksV4Controller {
type: String,
required: false,
isArray: true,
style: "form",
explode: true,
})
async findById(
@Req() request: Request,
Expand Down
3 changes: 2 additions & 1 deletion src/samples/dto/output-sample.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsDateString, IsString } from "class-validator";
import { IsDateString, IsOptional, IsString } from "class-validator";
import { CreateSampleDto } from "./create-sample.dto";
import { Transform } from "class-transformer";
import { decodeScientificMetadataKeys } from "src/common/utils";
Expand Down Expand Up @@ -47,6 +47,7 @@ export class OutputSampleDto extends CreateSampleDto {
description:
"Version of the API used when the dataset was created or last updated. API version is defined in code for each release. Managed by the system.",
})
@IsOptional()
@IsString()
version?: string;

Expand Down
Loading