Skip to content

Commit

Permalink
User defined id for MeasurementPeriods
Browse files Browse the repository at this point in the history
  • Loading branch information
fpotier committed Jun 20, 2024
1 parent b365cd3 commit 3ec12e2
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 6 deletions.
9 changes: 9 additions & 0 deletions src/proposals/dto/create-measurement-period.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import { ApiProperty } from "@nestjs/swagger";
import { IsDateString, IsOptional, IsString } from "class-validator";

export class CreateMeasurementPeriodDto {
@ApiProperty({
type: String,
description:
"Unique identifier (relative to the proposal) of a MeasurementPeriod",
})
@IsString()
@IsOptional()
id?: string;

@ApiProperty({
type: String,
required: true,
Expand Down
3 changes: 3 additions & 0 deletions src/proposals/dto/update-proposal.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import {
IsObject,
IsOptional,
IsString,
Validate,
ValidateNested,
} from "class-validator";
import { OwnableDto } from "../../common/dto/ownable.dto";
import { CreateMeasurementPeriodDto } from "./create-measurement-period.dto";
import { UniqueMeasurementPeriodIdConstraint } from "./validators/unique-measurement-period-id";

@ApiTags("proposals")
export class UpdateProposalDto extends OwnableDto {
Expand Down Expand Up @@ -113,6 +115,7 @@ export class UpdateProposalDto extends OwnableDto {
@IsOptional()
@ValidateNested({ each: true })
@Type(() => CreateMeasurementPeriodDto)
@Validate(UniqueMeasurementPeriodIdConstraint)
readonly MeasurementPeriodList?: CreateMeasurementPeriodDto[];

@ApiProperty({
Expand Down
23 changes: 23 additions & 0 deletions src/proposals/dto/validators/unique-measurement-period-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
ValidationArguments,
ValidatorConstraint,
ValidatorConstraintInterface,
} from "class-validator";
import { MeasurementPeriodClass } from "src/proposals/schemas/measurement-period.schema";

@ValidatorConstraint({ async: false })
export class UniqueMeasurementPeriodIdConstraint
implements ValidatorConstraintInterface
{
validate(
measurementPeriods: MeasurementPeriodClass[],
args: ValidationArguments,

Check warning on line 14 in src/proposals/dto/validators/unique-measurement-period-id.ts

View workflow job for this annotation

GitHub Actions / eslint

'args' is defined but never used
) {
const ids = measurementPeriods.map((period) => period.id);
return ids.length === new Set(ids).size;
}

defaultMessage(args: ValidationArguments) {

Check warning on line 20 in src/proposals/dto/validators/unique-measurement-period-id.ts

View workflow job for this annotation

GitHub Actions / eslint

'args' is defined but never used
return "MeasurementPeriod id must be unique within the MeasurementPeriodList";
}
}
15 changes: 15 additions & 0 deletions src/proposals/schemas/measurement-period.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,26 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { ApiProperty } from "@nestjs/swagger";
import { Document } from "mongoose";
import { QueryableClass } from "src/common/schemas/queryable.schema";
import { v4 as uuidv4 } from "uuid";

export type MeasurementPeriodDocument = MeasurementPeriodClass & Document;

@Schema()
export class MeasurementPeriodClass extends QueryableClass {
@ApiProperty({
type: String,
default: () => uuidv4(),
required: true,
description:
"Unique identifier (relative to the proposal) of a MeasurementPeriod",
})
@Prop({
type: String,
required: true,
default: () => uuidv4(),
})
id: string;

@ApiProperty({
type: String,
required: true,
Expand Down
10 changes: 10 additions & 0 deletions src/proposals/schemas/proposal.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ export class ProposalClass extends OwnableClass {
@Prop({
type: [MeasurementPeriodSchema],
required: false,
validate: {
validator: function (measurementPeriodList: MeasurementPeriodClass[]) {
const ids = measurementPeriodList.map(
(measurementPeriod) => measurementPeriod.id,
);
return ids.length === new Set(ids).size;
},
message:
"MeasurementPeriod id must be unique within the MeasurementPeriodList",
},
})
MeasurementPeriodList?: MeasurementPeriodClass[];

Expand Down
25 changes: 19 additions & 6 deletions test/Proposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,20 @@ describe("1500: Proposal: Simple Proposal", () => {
});
});

it("0080: adds a new complete proposal with an extra field, which should fail", async () => {
it("0080: check if complete proposal with two similar MeasurementPeriod.id is valid", async () => {
return request(appUrl)
.post("/api/v3/Proposals/isValid")
.send(TestData.ProposalWrong_2)
.set("Accept", "application/json")
.set({ Authorization: `Bearer ${accessTokenProposalIngestor}` })
.expect(TestData.EntryValidStatusCode)
.expect("Content-Type", /json/)
.then((res) => {
res.body.should.have.property("valid").and.equal(false);
});
});

it("0090: adds a new complete proposal with an extra field, which should fail", async () => {
return request(appUrl)
.post("/api/v3/Proposals")
.send(TestData.ProposalWrong_1)
Expand All @@ -176,7 +189,7 @@ describe("1500: Proposal: Simple Proposal", () => {
});
});

it("0090: should fetch this new proposal", async () => {
it("0100: should fetch this new proposal", async () => {
return request(appUrl)
.get("/api/v3/Proposals/" + proposalId)
.set("Accept", "application/json")
Expand All @@ -189,7 +202,7 @@ describe("1500: Proposal: Simple Proposal", () => {
});
});

it("0100: should add a new attachment to this proposal", async () => {
it("0110: should add a new attachment to this proposal", async () => {
let testAttachment = { ...TestData.AttachmentCorrect };
testAttachment.proposalId = defaultProposalId;
return request(appUrl)
Expand Down Expand Up @@ -220,7 +233,7 @@ describe("1500: Proposal: Simple Proposal", () => {
});
});

it("0110: should fetch this proposal attachment", async () => {
it("0120: should fetch this proposal attachment", async () => {
return request(appUrl)
.get("/api/v3/Proposals/" + proposalId + "/attachments")
.set("Accept", "application/json")
Expand All @@ -233,7 +246,7 @@ describe("1500: Proposal: Simple Proposal", () => {
});
});

it("0120: should delete this proposal attachment", async () => {
it("0130: should delete this proposal attachment", async () => {
return request(appUrl)
.delete(
"/api/v3/Proposals/" + proposalId + "/attachments/" + attachmentId,
Expand All @@ -243,7 +256,7 @@ describe("1500: Proposal: Simple Proposal", () => {
.expect(TestData.SuccessfulDeleteStatusCode);
});

it("0130: admin can remove all existing proposals", async () => {
it("0140: admin can remove all existing proposals", async () => {
return await request(appUrl)
.get("/api/v3/Proposals")
.set("Accept", "application/json")
Expand Down
53 changes: 53 additions & 0 deletions test/TestData.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const TestData = {
accessGroups: [],
MeasurementPeriodList: [
{
id: "1",
instrument: "ESS3-1",
start: "2017-07-24T13:56:30.000Z",
end: "2017-07-25T13:56:30.000Z",
Expand All @@ -63,6 +64,29 @@ const TestData = {
],
},

ProposalCorrectComplete2: {
proposalId: "20170268",
pi_email: "[email protected]",
pi_firstname: "principal",
pi_lastname: "investigator",
email: "[email protected]",
firstname: "proposal",
lastname: "proposer",
title: "A complete test proposal",
abstract: "Abstract of test proposal",
ownerGroup: "proposalingestor",
accessGroups: [],
MeasurementPeriodList: [
{
id: "1",
instrument: "ESS3-1",
start: "2017-07-24T13:56:30.000Z",
end: "2017-07-25T13:56:30.000Z",
comment: "Some comment",
}
],
},

ProposalWrong_1: {
proposalId: "20170267",
pi_email: "[email protected]",
Expand All @@ -79,6 +103,35 @@ const TestData = {
createdBy: "This should not be here",
},

ProposalWrong_2: {
proposalId: "20170267",
pi_email: "[email protected]",
pi_firstname: "principal",
pi_lastname: "investigator",
email: "[email protected]",
firstname: "proposal",
lastname: "proposer",
title: "A complete test proposal with an extra field",
abstract: "Abstract of test proposal",
ownerGroup: "20170251-group",
accessGroups: [],
MeasurementPeriodList: [
{
id: "1",
instrument: "ESS3-1",
start: "2017-07-24T13:56:30.000Z",
end: "2017-07-25T13:56:30.000Z",
comment: "Some comment",
},
{
id: "1",
instrument: "ESS3-2",
start: "2017-07-28T13:56:30.000Z",
end: "2017-07-29T13:56:30.000Z",
},
],
},

AttachmentCorrect: {
thumbnail: "data/abc123",
caption: "Some caption",
Expand Down

0 comments on commit 3ec12e2

Please sign in to comment.