Skip to content

Commit 3ec12e2

Browse files
committed
User defined id for MeasurementPeriods
1 parent b365cd3 commit 3ec12e2

7 files changed

+132
-6
lines changed

Diff for: src/proposals/dto/create-measurement-period.dto.ts

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ import { ApiProperty } from "@nestjs/swagger";
22
import { IsDateString, IsOptional, IsString } from "class-validator";
33

44
export class CreateMeasurementPeriodDto {
5+
@ApiProperty({
6+
type: String,
7+
description:
8+
"Unique identifier (relative to the proposal) of a MeasurementPeriod",
9+
})
10+
@IsString()
11+
@IsOptional()
12+
id?: string;
13+
514
@ApiProperty({
615
type: String,
716
required: true,

Diff for: src/proposals/dto/update-proposal.dto.ts

+3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import {
77
IsObject,
88
IsOptional,
99
IsString,
10+
Validate,
1011
ValidateNested,
1112
} from "class-validator";
1213
import { OwnableDto } from "../../common/dto/ownable.dto";
1314
import { CreateMeasurementPeriodDto } from "./create-measurement-period.dto";
15+
import { UniqueMeasurementPeriodIdConstraint } from "./validators/unique-measurement-period-id";
1416

1517
@ApiTags("proposals")
1618
export class UpdateProposalDto extends OwnableDto {
@@ -113,6 +115,7 @@ export class UpdateProposalDto extends OwnableDto {
113115
@IsOptional()
114116
@ValidateNested({ each: true })
115117
@Type(() => CreateMeasurementPeriodDto)
118+
@Validate(UniqueMeasurementPeriodIdConstraint)
116119
readonly MeasurementPeriodList?: CreateMeasurementPeriodDto[];
117120

118121
@ApiProperty({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {
2+
ValidationArguments,
3+
ValidatorConstraint,
4+
ValidatorConstraintInterface,
5+
} from "class-validator";
6+
import { MeasurementPeriodClass } from "src/proposals/schemas/measurement-period.schema";
7+
8+
@ValidatorConstraint({ async: false })
9+
export class UniqueMeasurementPeriodIdConstraint
10+
implements ValidatorConstraintInterface
11+
{
12+
validate(
13+
measurementPeriods: MeasurementPeriodClass[],
14+
args: ValidationArguments,
15+
) {
16+
const ids = measurementPeriods.map((period) => period.id);
17+
return ids.length === new Set(ids).size;
18+
}
19+
20+
defaultMessage(args: ValidationArguments) {
21+
return "MeasurementPeriod id must be unique within the MeasurementPeriodList";
22+
}
23+
}

Diff for: src/proposals/schemas/measurement-period.schema.ts

+15
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,26 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
22
import { ApiProperty } from "@nestjs/swagger";
33
import { Document } from "mongoose";
44
import { QueryableClass } from "src/common/schemas/queryable.schema";
5+
import { v4 as uuidv4 } from "uuid";
56

67
export type MeasurementPeriodDocument = MeasurementPeriodClass & Document;
78

89
@Schema()
910
export class MeasurementPeriodClass extends QueryableClass {
11+
@ApiProperty({
12+
type: String,
13+
default: () => uuidv4(),
14+
required: true,
15+
description:
16+
"Unique identifier (relative to the proposal) of a MeasurementPeriod",
17+
})
18+
@Prop({
19+
type: String,
20+
required: true,
21+
default: () => uuidv4(),
22+
})
23+
id: string;
24+
1025
@ApiProperty({
1126
type: String,
1227
required: true,

Diff for: src/proposals/schemas/proposal.schema.ts

+10
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@ export class ProposalClass extends OwnableClass {
156156
@Prop({
157157
type: [MeasurementPeriodSchema],
158158
required: false,
159+
validate: {
160+
validator: function (measurementPeriodList: MeasurementPeriodClass[]) {
161+
const ids = measurementPeriodList.map(
162+
(measurementPeriod) => measurementPeriod.id,
163+
);
164+
return ids.length === new Set(ids).size;
165+
},
166+
message:
167+
"MeasurementPeriod id must be unique within the MeasurementPeriodList",
168+
},
159169
})
160170
MeasurementPeriodList?: MeasurementPeriodClass[];
161171

Diff for: test/Proposal.js

+19-6
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,20 @@ describe("1500: Proposal: Simple Proposal", () => {
163163
});
164164
});
165165

166-
it("0080: adds a new complete proposal with an extra field, which should fail", async () => {
166+
it("0080: check if complete proposal with two similar MeasurementPeriod.id is valid", async () => {
167+
return request(appUrl)
168+
.post("/api/v3/Proposals/isValid")
169+
.send(TestData.ProposalWrong_2)
170+
.set("Accept", "application/json")
171+
.set({ Authorization: `Bearer ${accessTokenProposalIngestor}` })
172+
.expect(TestData.EntryValidStatusCode)
173+
.expect("Content-Type", /json/)
174+
.then((res) => {
175+
res.body.should.have.property("valid").and.equal(false);
176+
});
177+
});
178+
179+
it("0090: adds a new complete proposal with an extra field, which should fail", async () => {
167180
return request(appUrl)
168181
.post("/api/v3/Proposals")
169182
.send(TestData.ProposalWrong_1)
@@ -176,7 +189,7 @@ describe("1500: Proposal: Simple Proposal", () => {
176189
});
177190
});
178191

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

192-
it("0100: should add a new attachment to this proposal", async () => {
205+
it("0110: should add a new attachment to this proposal", async () => {
193206
let testAttachment = { ...TestData.AttachmentCorrect };
194207
testAttachment.proposalId = defaultProposalId;
195208
return request(appUrl)
@@ -220,7 +233,7 @@ describe("1500: Proposal: Simple Proposal", () => {
220233
});
221234
});
222235

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

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

246-
it("0130: admin can remove all existing proposals", async () => {
259+
it("0140: admin can remove all existing proposals", async () => {
247260
return await request(appUrl)
248261
.get("/api/v3/Proposals")
249262
.set("Accept", "application/json")

Diff for: test/TestData.js

+53
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const TestData = {
5050
accessGroups: [],
5151
MeasurementPeriodList: [
5252
{
53+
id: "1",
5354
instrument: "ESS3-1",
5455
start: "2017-07-24T13:56:30.000Z",
5556
end: "2017-07-25T13:56:30.000Z",
@@ -63,6 +64,29 @@ const TestData = {
6364
],
6465
},
6566

67+
ProposalCorrectComplete2: {
68+
proposalId: "20170268",
69+
pi_email: "[email protected]",
70+
pi_firstname: "principal",
71+
pi_lastname: "investigator",
72+
73+
firstname: "proposal",
74+
lastname: "proposer",
75+
title: "A complete test proposal",
76+
abstract: "Abstract of test proposal",
77+
ownerGroup: "proposalingestor",
78+
accessGroups: [],
79+
MeasurementPeriodList: [
80+
{
81+
id: "1",
82+
instrument: "ESS3-1",
83+
start: "2017-07-24T13:56:30.000Z",
84+
end: "2017-07-25T13:56:30.000Z",
85+
comment: "Some comment",
86+
}
87+
],
88+
},
89+
6690
ProposalWrong_1: {
6791
proposalId: "20170267",
6892
pi_email: "[email protected]",
@@ -79,6 +103,35 @@ const TestData = {
79103
createdBy: "This should not be here",
80104
},
81105

106+
ProposalWrong_2: {
107+
proposalId: "20170267",
108+
pi_email: "[email protected]",
109+
pi_firstname: "principal",
110+
pi_lastname: "investigator",
111+
112+
firstname: "proposal",
113+
lastname: "proposer",
114+
title: "A complete test proposal with an extra field",
115+
abstract: "Abstract of test proposal",
116+
ownerGroup: "20170251-group",
117+
accessGroups: [],
118+
MeasurementPeriodList: [
119+
{
120+
id: "1",
121+
instrument: "ESS3-1",
122+
start: "2017-07-24T13:56:30.000Z",
123+
end: "2017-07-25T13:56:30.000Z",
124+
comment: "Some comment",
125+
},
126+
{
127+
id: "1",
128+
instrument: "ESS3-2",
129+
start: "2017-07-28T13:56:30.000Z",
130+
end: "2017-07-29T13:56:30.000Z",
131+
},
132+
],
133+
},
134+
82135
AttachmentCorrect: {
83136
thumbnail: "data/abc123",
84137
caption: "Some caption",

0 commit comments

Comments
 (0)