Skip to content

Commit 84b71a4

Browse files
authored
Add email flag validation (#181)
* Moved float validation to a seperate flag type. * Moved uuidCsv validation to a new flag type. Added email custom flag. Added integration tests for testing email flags. * Removed validateFloat export from custom-flags/float.ts. * Updated float custom flag to return a number instead of a string. * Added expected format to float custom flag error message.
1 parent 676b484 commit 84b71a4

16 files changed

Lines changed: 110 additions & 78 deletions

File tree

integration-tests/access-control/group.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ const tests = () => {
152152
);
153153
expect(deleteError?.message).to.contain("'another-invalid-uuid' is not a valid UUID.");
154154
});
155+
156+
it("Should return an error when invalid email is provided as --member", async () => {
157+
const { error: updateError } = await runCommand<Group>(
158+
`access-control group update -i ${crypto.randomUUID()} -g ${crypto.randomUUID()} --member not-a-valid-email`,
159+
);
160+
expect(updateError?.message).to.contain("'not-a-valid-email' is not a valid email.");
161+
});
155162
};
156163

157164
export default tests;

integration-tests/access-control/member/owner.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ const tests = () => {
9393
);
9494
expect(deleteError?.message).to.contain("'an-invalid-uuid' is not a valid UUID.");
9595
});
96+
97+
it("Should return an error when invalid email is provided as --email", async () => {
98+
const { error: updateError } = await runCommand<OwnerResponse>(`access-control member owner add -i ${crypto.randomUUID()} --email not-a-valid-email`);
99+
expect(updateError?.message).to.contain("'not-a-valid-email' is not a valid email.");
100+
});
96101
};
97102

98103
export default tests;

integration-tests/access-control/member/user.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,13 @@ const tests = () => {
365365
);
366366
expect(updateError?.message).to.contain("'an-invalid-uuid' is not a valid UUID.");
367367
});
368+
369+
it("Should return an error when invalid email is provided as --email", async () => {
370+
const { error: updateError } = await runCommand<Member>(
371+
`access-control member user add -i ${crypto.randomUUID()} --email not-a-valid-email --role-ids ${crypto.randomUUID()}`,
372+
);
373+
expect(updateError?.message).to.contain("'not-a-valid-email' is not a valid email.");
374+
});
368375
};
369376

370377
export default tests;

integration-tests/imodel/create-delete.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ const tests = () =>
9696
);
9797

9898
expect(createError).to.not.be.undefined;
99-
expect(createError?.message).to.match(/--extent=\[object Object] cannot also be provided when using --ne-latitude/);
100-
expect(createError?.message).to.match(/--extent=\[object Object] cannot also be provided when using --ne-longitude/);
101-
expect(createError?.message).to.match(/--extent=\[object Object] cannot also be provided when using --sw-latitude/);
102-
expect(createError?.message).to.match(/--extent=\[object Object] cannot also be provided when using --sw-longitude/);
99+
expect(createError?.message).to.contain("--extent=[object Object] cannot also be provided when using --ne-latitude");
100+
expect(createError?.message).to.contain("--extent=[object Object] cannot also be provided when using --ne-longitude");
101+
expect(createError?.message).to.contain("--extent=[object Object] cannot also be provided when using --sw-latitude");
102+
expect(createError?.message).to.contain("--extent=[object Object] cannot also be provided when using --sw-longitude");
103103
});
104104

105105
it("should return an error if user does not provide all extent flags", async () => {
@@ -120,7 +120,7 @@ const tests = () =>
120120
);
121121

122122
expect(createError).to.not.be.undefined;
123-
expect(createError?.message).to.match(/All of the following must be provided when using --sw-latitude: --ne-latitude, --ne-longitude, --sw-longitude/);
123+
expect(createError?.message).to.contain("All of the following must be provided when using --sw-latitude: --ne-latitude, --ne-longitude, --sw-longitude");
124124
});
125125

126126
it("should return an error if a component of the provided extent is not a valid number", async () => {
@@ -130,7 +130,7 @@ const tests = () =>
130130
);
131131

132132
expect(createError).to.not.be.undefined;
133-
expect(createError?.message).to.match(/46.302abc is not a valid number./);
133+
expect(createError?.message).to.contain("46.302abc is not a valid number. Expected format: '1234.56'.");
134134
});
135135

136136
it("should return an error if a component of the provided extent is not valid JSON", async () => {
@@ -140,7 +140,7 @@ const tests = () =>
140140
);
141141

142142
expect(createError).to.not.be.undefined;
143-
expect(createError?.message).to.match(/'not-valid-json' is not valid serialized JSON./);
143+
expect(createError?.message).to.contain("'not-valid-json' is not valid serialized JSON.");
144144
});
145145

146146
it("should return an error if a component of the provided extent is not of valid JSON schema", async () => {
@@ -160,8 +160,8 @@ const tests = () =>
160160
);
161161

162162
expect(createError).to.not.be.undefined;
163-
expect(createError?.message).to.match(/missing required property 'southWest.latitude' of type 'number'/);
164-
expect(createError?.message).to.match(/northEast.longitude: expected type 'number', received 'string'/);
163+
expect(createError?.message).to.contain("missing required property 'southWest.latitude' of type 'number'");
164+
expect(createError?.message).to.contain("northEast.longitude: expected type 'number', received 'string'");
165165
});
166166

167167
it("should delete the iModel", async () => {

integration-tests/imodel/update.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,10 @@ const tests = () =>
103103
);
104104

105105
expect(updateError).to.not.be.undefined;
106-
expect(updateError?.message).to.match(/--extent=\[object Object] cannot also be provided when using --ne-latitude/);
107-
expect(updateError?.message).to.match(/--extent=\[object Object] cannot also be provided when using --ne-longitude/);
108-
expect(updateError?.message).to.match(/--extent=\[object Object] cannot also be provided when using --sw-latitude/);
109-
expect(updateError?.message).to.match(/--extent=\[object Object] cannot also be provided when using --sw-longitude/);
106+
expect(updateError?.message).to.contain("--extent=[object Object] cannot also be provided when using --ne-latitude");
107+
expect(updateError?.message).to.contain("--extent=[object Object] cannot also be provided when using --ne-longitude");
108+
expect(updateError?.message).to.contain("--extent=[object Object] cannot also be provided when using --sw-latitude");
109+
expect(updateError?.message).to.contain("--extent=[object Object] cannot also be provided when using --sw-longitude");
110110
});
111111

112112
it("should return an error if user does not provide all extent flags", async () => {
@@ -128,7 +128,7 @@ const tests = () =>
128128
);
129129

130130
expect(updateError).to.not.be.undefined;
131-
expect(updateError?.message).to.match(/All of the following must be provided when using --sw-latitude: --ne-latitude, --ne-longitude, --sw-longitude/);
131+
expect(updateError?.message).to.contain("All of the following must be provided when using --sw-latitude: --ne-latitude, --ne-longitude, --sw-longitude");
132132
});
133133

134134
it("should return an error if a component of the provided extent is not a valid number", async () => {
@@ -137,7 +137,7 @@ const tests = () =>
137137
);
138138

139139
expect(createError).to.not.be.undefined;
140-
expect(createError?.message).to.match(/46.302abc is not a valid number./);
140+
expect(createError?.message).to.contain("46.302abc is not a valid number. Expected format: '1234.56'.");
141141
});
142142

143143
it("should return an error when invalid uuid is provided as --imodel-id", async () => {

src/commands/access-control/group/update.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export default class UpdateAccessControlGroup extends BaseCommand {
4949
"itwin-id": CustomFlags.iTwinIDFlag({
5050
description: "The ID of the iTwin where the group exists.",
5151
}),
52-
member: Flags.string({
52+
member: CustomFlags.email({
5353
description: "A list of members (emails) to be assigned to the group. Max amount of 50.",
5454
helpValue: "<string>",
5555
multiple: true,

src/commands/access-control/member/group/add.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
* See LICENSE.md in the project root for license terms and full copyright notice.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Flags } from "@oclif/core";
7-
86
import { ApiReference } from "../../../../extensions/api-reference.js";
97
import BaseCommand from "../../../../extensions/base-command.js";
108
import { CustomFlags } from "../../../../extensions/custom-flags.js";
11-
import { validateUuidCSV } from "../../../../extensions/validation/validate-uuid-csv.js";
129
import { GroupMember, GroupMemberInfo } from "../../../../services/access-control-client/models/group.js";
1310

1411
export default class AddGroupMembers extends BaseCommand {
@@ -53,11 +50,10 @@ export default class AddGroupMembers extends BaseCommand {
5350
"itwin-id": CustomFlags.iTwinIDFlag({
5451
description: "The ID of the iTwin to which the groups will be added.",
5552
}),
56-
"role-ids": Flags.string({
53+
"role-ids": CustomFlags.uuidCsv({
5754
dependsOn: ["group-id"],
5855
description: `Specify a list of role IDs to be assigned to all of 'group-id' groups. Provided in CSV format without whitespaces.`,
5956
helpValue: "<string>",
60-
parse: async (input) => validateUuidCSV(input),
6157
required: false,
6258
}),
6359
};

src/commands/access-control/member/owner/add.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
* See LICENSE.md in the project root for license terms and full copyright notice.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Flags } from "@oclif/core";
7-
86
import { ApiReference } from "../../../../extensions/api-reference.js";
97
import BaseCommand from "../../../../extensions/base-command.js";
108
import { CustomFlags } from "../../../../extensions/custom-flags.js";
@@ -27,7 +25,7 @@ export default class AddOwner extends BaseCommand {
2725
];
2826

2927
public static flags = {
30-
email: Flags.string({
28+
email: CustomFlags.email({
3129
description: "The email address of the new owner.",
3230
helpValue: "<string>",
3331
required: true,

src/commands/access-control/member/user/add.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
* See LICENSE.md in the project root for license terms and full copyright notice.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Flags } from "@oclif/core";
7-
86
import { ApiReference } from "../../../../extensions/api-reference.js";
97
import BaseCommand from "../../../../extensions/base-command.js";
108
import { CustomFlags } from "../../../../extensions/custom-flags.js";
11-
import { validateUuidCSV } from "../../../../extensions/validation/validate-uuid-csv.js";
129
import { MembersResponse, UserMember } from "../../../../services/access-control-client/models/members.js";
1310

1411
export default class AddUserMembers extends BaseCommand {
@@ -37,7 +34,7 @@ export default class AddUserMembers extends BaseCommand {
3734
];
3835

3936
public static flags = {
40-
email: Flags.string({
37+
email: CustomFlags.email({
4138
dependsOn: ["role-ids"],
4239
description: "Specify emails of the user to add roles to. This flag can be provided multiple times.",
4340
helpValue: "<string>",
@@ -55,14 +52,12 @@ export default class AddUserMembers extends BaseCommand {
5552
helpValue: "<string>",
5653
required: false,
5754
}),
58-
"role-ids": Flags.string({
55+
"role-ids": CustomFlags.uuidCsv({
5956
dependsOn: ["email"],
6057
description:
6158
"Specify IDs of roles to be assigned to a user in CSV format without any whitespaces. This flag can be provided multiple times. If the flag is provided only once, the contained list of role IDs will be assigned to all provided group-ids list. If flag is provided multiple times, each role-ids will be used for the corresponding group-id (fist role-ids list for the first group-id, second role-ids list for the second group-id and so on).",
6259
helpValue: "<string>",
6360
multiple: true,
64-
// eslint-disable-next-line @typescript-eslint/promise-function-async
65-
parse: (input) => validateUuidCSV(input),
6661
required: false,
6762
}),
6863
};

src/commands/imodel/create.ts

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { Flags } from "@oclif/core";
99
import { ApiReference } from "../../extensions/api-reference.js";
1010
import BaseCommand from "../../extensions/base-command.js";
1111
import { CustomFlags } from "../../extensions/custom-flags.js";
12-
import { validateFloat } from "../../extensions/validation/validate-float.js";
1312

1413
export default class CreateIModel extends BaseCommand {
1514
public static apiReference: ApiReference = {
@@ -63,44 +62,36 @@ export default class CreateIModel extends BaseCommand {
6362
helpValue: "<string>",
6463
required: true,
6564
}),
66-
"ne-latitude": Flags.string({
65+
"ne-latitude": CustomFlags.float({
6766
dependsOn: ["ne-longitude", "sw-latitude", "sw-longitude"],
6867
description: "Northeast latitude of the extent.",
6968
exclusive: ["extent"],
7069
helpValue: "<float>",
71-
// eslint-disable-next-line @typescript-eslint/promise-function-async
72-
parse: (input) => validateFloat(input),
7370
required: false,
7471
}),
75-
"ne-longitude": Flags.string({
72+
"ne-longitude": CustomFlags.float({
7673
dependsOn: ["ne-latitude", "sw-latitude", "sw-longitude"],
7774
description: "Northeast longitude of the extent.",
7875
exclusive: ["extent"],
7976
helpValue: "<float>",
80-
// eslint-disable-next-line @typescript-eslint/promise-function-async
81-
parse: (input) => validateFloat(input),
8277
required: false,
8378
}),
8479
save: Flags.boolean({
8580
description: "Save the iModel id to the context.",
8681
required: false,
8782
}),
88-
"sw-latitude": Flags.string({
83+
"sw-latitude": CustomFlags.float({
8984
dependsOn: ["ne-latitude", "ne-longitude", "sw-longitude"],
9085
description: "Southwest latitude of the extent.",
9186
exclusive: ["extent"],
9287
helpValue: "<float>",
93-
// eslint-disable-next-line @typescript-eslint/promise-function-async
94-
parse: (input) => validateFloat(input),
9588
required: false,
9689
}),
97-
"sw-longitude": Flags.string({
90+
"sw-longitude": CustomFlags.float({
9891
dependsOn: ["ne-latitude", "ne-longitude", "sw-latitude"],
9992
description: "Southwest longitude of the extent.",
10093
exclusive: ["extent"],
10194
helpValue: "<float>",
102-
// eslint-disable-next-line @typescript-eslint/promise-function-async
103-
parse: (input) => validateFloat(input),
10495
required: false,
10596
}),
10697
};
@@ -116,12 +107,12 @@ export default class CreateIModel extends BaseCommand {
116107
) {
117108
flags.extent ??= {
118109
northEast: {
119-
latitude: Number.parseFloat(flags["ne-latitude"]),
120-
longitude: Number.parseFloat(flags["ne-longitude"]),
110+
latitude: flags["ne-latitude"],
111+
longitude: flags["ne-longitude"],
121112
},
122113
southWest: {
123-
latitude: Number.parseFloat(flags["sw-latitude"]),
124-
longitude: Number.parseFloat(flags["sw-longitude"]),
114+
latitude: flags["sw-latitude"],
115+
longitude: flags["sw-longitude"],
125116
},
126117
};
127118
}

0 commit comments

Comments
 (0)