Skip to content

Commit 540088d

Browse files
committed
[DAPS-1517] Re
1 parent ae0be3c commit 540088d

File tree

3 files changed

+374
-14
lines changed

3 files changed

+374
-14
lines changed

core/database/foxx/api/repository/validation.js

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ const { Result } = require("./types");
44
const { RepositoryOps } = require("./operations");
55
const g_lib = require("../support");
66

7+
// Define error code constant if not available from g_lib
8+
const ERR_INVALID_PARAM = g_lib.ERR_INVALID_PARAM !== undefined ? g_lib.ERR_INVALID_PARAM : 2;
9+
const ERR_INVALID_OPERATION = g_lib.ERR_INVALID_OPERATION !== undefined ? g_lib.ERR_INVALID_OPERATION : 400;
10+
711
/**
812
* Standalone validation functions following Rust patterns
913
* Pure functions that return Result types for error handling
@@ -20,7 +24,7 @@ const g_lib = require("../support");
2024
const validateNonEmptyString = (value, fieldName) => {
2125
if (!value || typeof value !== "string" || value.trim() === "") {
2226
return Result.err({
23-
code: g_lib.ERR_INVALID_PARAM,
27+
code: ERR_INVALID_PARAM,
2428
message: `${fieldName} is required and must be a non-empty string`,
2529
});
2630
}
@@ -46,15 +50,17 @@ const validateCommonFields = (config) => {
4650
errors.push("Repository capacity must be a positive number");
4751
}
4852

49-
if (!Array.isArray(config.admins) || config.admins.length === 0) {
53+
// Check for both 'admin' and 'admins' fields for backward compatibility
54+
const adminField = config.admins || config.admin;
55+
if (!Array.isArray(adminField) || adminField.length === 0) {
5056
errors.push("Repository must have at least one admin");
5157
}
5258

5359
if (errors.length > 0) {
5460
// See: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#propagating-errors
5561
// Early return with error - similar to Rust's ? operator
5662
return Result.err({
57-
code: g_lib.ERR_INVALID_PARAM,
63+
code: ERR_INVALID_PARAM,
5864
message: errors.join("; "),
5965
});
6066
}
@@ -66,22 +72,22 @@ const validateCommonFields = (config) => {
6672
const validatePOSIXPath = (path, fieldName) => {
6773
if (!path || typeof path !== "string") {
6874
return Result.err({
69-
code: g_lib.ERR_INVALID_PARAM,
75+
code: ERR_INVALID_PARAM,
7076
message: `${fieldName} must be a non-empty string`,
7177
});
7278
}
7379

7480
if (!path.startsWith("/")) {
7581
return Result.err({
76-
code: g_lib.ERR_INVALID_PARAM,
82+
code: ERR_INVALID_PARAM,
7783
message: `${fieldName} must be an absolute path (start with '/')`,
7884
});
7985
}
8086

8187
// Check for invalid characters in path
8288
if (path.includes("..") || path.includes("//")) {
8389
return Result.err({
84-
code: g_lib.ERR_INVALID_PARAM,
90+
code: ERR_INVALID_PARAM,
8591
message: `${fieldName} contains invalid path sequences`,
8692
});
8793
}
@@ -105,7 +111,7 @@ const validateRepositoryPath = (path, repoId) => {
105111

106112
if (lastComponent !== repoId) {
107113
return Result.err({
108-
code: g_lib.ERR_INVALID_PARAM,
114+
code: ERR_INVALID_PARAM,
109115
message: `Repository path must end with repository ID (${repoId})`,
110116
});
111117
}
@@ -115,7 +121,13 @@ const validateRepositoryPath = (path, repoId) => {
115121

116122
// Validate Globus-specific configuration
117123
const validateGlobusConfig = (config) => {
118-
const commonResult = validateCommonFields(config);
124+
// Normalize admin/admins field for backward compatibility
125+
const normalizedConfig = { ...config };
126+
if (config.admin && !config.admins) {
127+
normalizedConfig.admins = config.admin;
128+
}
129+
130+
const commonResult = validateCommonFields(normalizedConfig);
119131
if (!commonResult.ok) {
120132
return commonResult;
121133
}
@@ -140,7 +152,7 @@ const validateGlobusConfig = (config) => {
140152

141153
if (errors.length > 0) {
142154
return Result.err({
143-
code: g_lib.ERR_INVALID_PARAM,
155+
code: ERR_INVALID_PARAM,
144156
message: errors.join("; "),
145157
});
146158
}
@@ -164,7 +176,13 @@ const validateGlobusConfig = (config) => {
164176

165177
// Validate metadata-only repository configuration
166178
const validateMetadataConfig = (config) => {
167-
const commonResult = validateCommonFields(config);
179+
// Normalize admin/admins field for backward compatibility
180+
const normalizedConfig = { ...config };
181+
if (config.admin && !config.admins) {
182+
normalizedConfig.admins = config.admin;
183+
}
184+
185+
const commonResult = validateCommonFields(normalizedConfig);
168186
if (!commonResult.ok) {
169187
return commonResult;
170188
}
@@ -176,7 +194,7 @@ const validateMetadataConfig = (config) => {
176194

177195
if (presentInvalidFields.length > 0) {
178196
return Result.err({
179-
code: g_lib.ERR_INVALID_PARAM,
197+
code: ERR_INVALID_PARAM,
180198
message: `Metadata-only repositories should not have: ${presentInvalidFields.join(", ")}`,
181199
});
182200
}
@@ -203,7 +221,7 @@ const validateAllocationParams = (params) => {
203221

204222
if (errors.length > 0) {
205223
return Result.err({
206-
code: g_lib.ERR_INVALID_PARAM,
224+
code: ERR_INVALID_PARAM,
207225
message: errors.join("; "),
208226
});
209227
}
@@ -222,7 +240,7 @@ const validateRepositorySupportsDataOperations = (repoId, dataId, errorMessage)
222240
const defaultMessage =
223241
errorMessage || `Data operations not supported for ${repository.type} repository`;
224242
throw [
225-
g_lib.ERR_INVALID_OPERATION,
243+
ERR_INVALID_OPERATION,
226244
defaultMessage,
227245
{
228246
repo_type: repository.type,
@@ -234,12 +252,90 @@ const validateRepositorySupportsDataOperations = (repoId, dataId, errorMessage)
234252
}
235253
};
236254

255+
// Validate partial Globus configuration (for updates)
256+
const validatePartialGlobusConfig = (config, repoId) => {
257+
// For partial updates, we don't require all fields
258+
// Only validate the fields that are provided
259+
const errors = [];
260+
261+
// Normalize admin/admins field for backward compatibility
262+
const normalizedConfig = { ...config };
263+
if (config.admin && !config.admins) {
264+
normalizedConfig.admins = config.admin;
265+
}
266+
267+
// Validate provided fields
268+
if (normalizedConfig.title !== undefined) {
269+
const titleValidation = validateNonEmptyString(normalizedConfig.title, "Repository title");
270+
if (!titleValidation.ok) {
271+
errors.push(titleValidation.error.message);
272+
}
273+
}
274+
275+
if (normalizedConfig.capacity !== undefined) {
276+
if (typeof normalizedConfig.capacity !== "number" || normalizedConfig.capacity <= 0) {
277+
errors.push("Repository capacity must be a positive number");
278+
}
279+
}
280+
281+
if (normalizedConfig.admins !== undefined) {
282+
if (!Array.isArray(normalizedConfig.admins) || normalizedConfig.admins.length === 0) {
283+
errors.push("Repository must have at least one admin");
284+
}
285+
}
286+
287+
if (normalizedConfig.pub_key !== undefined) {
288+
const pubKeyValidation = validateNonEmptyString(normalizedConfig.pub_key, "Public key");
289+
if (!pubKeyValidation.ok) {
290+
errors.push(pubKeyValidation.error.message);
291+
}
292+
}
293+
294+
if (normalizedConfig.address !== undefined) {
295+
const addressValidation = validateNonEmptyString(normalizedConfig.address, "Address");
296+
if (!addressValidation.ok) {
297+
errors.push(addressValidation.error.message);
298+
}
299+
}
300+
301+
if (normalizedConfig.endpoint !== undefined) {
302+
const endpointValidation = validateNonEmptyString(normalizedConfig.endpoint, "Endpoint");
303+
if (!endpointValidation.ok) {
304+
errors.push(endpointValidation.error.message);
305+
}
306+
}
307+
308+
if (normalizedConfig.path !== undefined && repoId) {
309+
const pathResult = validateRepositoryPath(normalizedConfig.path, repoId);
310+
if (!pathResult.ok) {
311+
return pathResult;
312+
}
313+
}
314+
315+
if (normalizedConfig.exp_path !== undefined) {
316+
const expPathResult = validatePOSIXPath(normalizedConfig.exp_path, "Export path");
317+
if (!expPathResult.ok) {
318+
return expPathResult;
319+
}
320+
}
321+
322+
if (errors.length > 0) {
323+
return Result.err({
324+
code: ERR_INVALID_PARAM,
325+
message: errors.join("; "),
326+
});
327+
}
328+
329+
return Result.ok(true);
330+
};
331+
237332
module.exports = {
238333
validateNonEmptyString,
239334
validateCommonFields,
240335
validatePOSIXPath,
241336
validateRepositoryPath,
242337
validateGlobusConfig,
338+
validatePartialGlobusConfig,
243339
validateMetadataConfig,
244340
validateAllocationParams,
245341
validateRepositorySupportsDataOperations,

core/database/foxx/api/support.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,12 @@ module.exports = (function () {
461461
if (obj.isInteger(e) && e >= 0 && e < obj.ERR_COUNT) {
462462
res.throw(obj.ERR_INFO[e][0], obj.ERR_INFO[e][1]);
463463
} else if (Array.isArray(e)) {
464-
res.throw(obj.ERR_INFO[e[0]][0], e[1]);
464+
// Handle undefined error codes
465+
if (e[0] === undefined || e[0] === null || !obj.ERR_INFO[e[0]]) {
466+
res.throw(400, e[1] || "Invalid request");
467+
} else {
468+
res.throw(obj.ERR_INFO[e[0]][0], e[1]);
469+
}
465470
//} else if ( e.hasOwnProperty( "errorNum" )) {
466471
} else if (Object.prototype.hasOwnProperty.call(e, "errorNum")) {
467472
switch (e.errorNum) {

0 commit comments

Comments
 (0)