UNPKG

@proofkit/fmodata

Version:
342 lines (341 loc) 11.2 kB
import { RecordCountMismatchError, ResponseStructureError, ValidationError } from "./errors.js"; async function validateRecord(record, schema, selectedFields, expandConfigs) { var _a, _b; const { "@id": id, "@editLink": editLink, ...rest } = record; const metadata = { "@id": id || "", "@editLink": editLink || "" }; if (!schema) { return { valid: true, data: { ...rest, ...metadata } }; } const { ROWID, ROWMODID, ...restWithoutSystemFields } = rest; if (selectedFields && selectedFields.length > 0) { const validatedRecord2 = {}; for (const field of selectedFields) { const fieldName = String(field); const fieldSchema = schema[fieldName]; if (fieldSchema) { const input = rest[fieldName]; try { let result = fieldSchema["~standard"].validate(input); if (result instanceof Promise) result = await result; if (result.issues) { return { valid: false, error: new ValidationError( `Validation failed for field '${fieldName}'`, result.issues, { field: fieldName, value: input, cause: result.issues } ) }; } validatedRecord2[fieldName] = result.value; } catch (originalError) { return { valid: false, error: new ValidationError( `Validation failed for field '${fieldName}'`, [], { field: fieldName, value: input, cause: originalError } ) }; } } else { validatedRecord2[fieldName] = rest[fieldName]; } } if (expandConfigs && expandConfigs.length > 0) { for (const expandConfig of expandConfigs) { const expandValue = rest[expandConfig.relation]; if (expandValue === void 0) { if (Array.isArray(rest.error) && rest.error.length > 0) { const errorDetail = (_a = rest.error[0]) == null ? void 0 : _a.error; if (errorDetail == null ? void 0 : errorDetail.message) { const errorMessage = errorDetail.message; const isRelatedToExpand = errorMessage.toLowerCase().includes(expandConfig.relation.toLowerCase()) || expandConfig.selectedFields && expandConfig.selectedFields.some( (field) => errorMessage.toLowerCase().includes(field.toLowerCase()) ); if (isRelatedToExpand) { return { valid: false, error: new ValidationError( `Validation failed for expanded relation '${expandConfig.relation}': ${errorMessage}`, [], { field: expandConfig.relation } ) }; } } } } else { if (Array.isArray(expandValue)) { const validatedExpandedItems = []; for (let i = 0; i < expandValue.length; i++) { const item = expandValue[i]; const itemValidation = await validateRecord( item, expandConfig.targetSchema, expandConfig.selectedFields, expandConfig.nestedExpands ); if (!itemValidation.valid) { return { valid: false, error: new ValidationError( `Validation failed for expanded relation '${expandConfig.relation}' at index ${i}: ${itemValidation.error.message}`, itemValidation.error.issues, { field: expandConfig.relation, cause: itemValidation.error.cause } ) }; } validatedExpandedItems.push(itemValidation.data); } validatedRecord2[expandConfig.relation] = validatedExpandedItems; } else { const itemValidation = await validateRecord( expandValue, expandConfig.targetSchema, expandConfig.selectedFields, expandConfig.nestedExpands ); if (!itemValidation.valid) { return { valid: false, error: new ValidationError( `Validation failed for expanded relation '${expandConfig.relation}': ${itemValidation.error.message}`, itemValidation.error.issues, { field: expandConfig.relation, cause: itemValidation.error.cause } ) }; } validatedRecord2[expandConfig.relation] = itemValidation.data; } } } } return { valid: true, data: { ...validatedRecord2, ...metadata } }; } const validatedRecord = { ...restWithoutSystemFields }; for (const [fieldName, fieldSchema] of Object.entries(schema)) { const input = rest[fieldName]; try { let result = fieldSchema["~standard"].validate(input); if (result instanceof Promise) result = await result; if (result.issues) { return { valid: false, error: new ValidationError( `Validation failed for field '${fieldName}'`, result.issues, { field: fieldName, value: input, cause: result.issues } ) }; } validatedRecord[fieldName] = result.value; } catch (originalError) { return { valid: false, error: new ValidationError( `Validation failed for field '${fieldName}'`, [], { field: fieldName, value: input, cause: originalError } ) }; } } if (expandConfigs && expandConfigs.length > 0) { for (const expandConfig of expandConfigs) { const expandValue = rest[expandConfig.relation]; if (expandValue === void 0) { if (Array.isArray(rest.error) && rest.error.length > 0) { const errorDetail = (_b = rest.error[0]) == null ? void 0 : _b.error; if (errorDetail == null ? void 0 : errorDetail.message) { const errorMessage = errorDetail.message; const isRelatedToExpand = errorMessage.toLowerCase().includes(expandConfig.relation.toLowerCase()) || expandConfig.selectedFields && expandConfig.selectedFields.some( (field) => errorMessage.toLowerCase().includes(field.toLowerCase()) ); if (isRelatedToExpand) { return { valid: false, error: new ValidationError( `Validation failed for expanded relation '${expandConfig.relation}': ${errorMessage}`, [], { field: expandConfig.relation } ) }; } } } } else { if (Array.isArray(expandValue)) { const validatedExpandedItems = []; for (let i = 0; i < expandValue.length; i++) { const item = expandValue[i]; const itemValidation = await validateRecord( item, expandConfig.targetSchema, expandConfig.selectedFields, expandConfig.nestedExpands ); if (!itemValidation.valid) { return { valid: false, error: new ValidationError( `Validation failed for expanded relation '${expandConfig.relation}' at index ${i}: ${itemValidation.error.message}`, itemValidation.error.issues, { field: expandConfig.relation, cause: itemValidation.error.cause } ) }; } validatedExpandedItems.push(itemValidation.data); } validatedRecord[expandConfig.relation] = validatedExpandedItems; } else { const itemValidation = await validateRecord( expandValue, expandConfig.targetSchema, expandConfig.selectedFields, expandConfig.nestedExpands ); if (!itemValidation.valid) { return { valid: false, error: new ValidationError( `Validation failed for expanded relation '${expandConfig.relation}': ${itemValidation.error.message}`, itemValidation.error.issues, { field: expandConfig.relation, cause: itemValidation.error.cause } ) }; } validatedRecord[expandConfig.relation] = itemValidation.data; } } } } return { valid: true, data: { ...validatedRecord, ...metadata } }; } async function validateListResponse(response, schema, selectedFields, expandConfigs) { if (!response || typeof response !== "object") { return { valid: false, error: new ResponseStructureError("an object", response) }; } const { "@context": context, value, ...rest } = response; if (!Array.isArray(value)) { return { valid: false, error: new ResponseStructureError( "'value' property to be an array", value ) }; } const validatedRecords = []; for (let i = 0; i < value.length; i++) { const record = value[i]; const validation = await validateRecord( record, schema, selectedFields, expandConfigs ); if (!validation.valid) { return { valid: false, error: validation.error }; } validatedRecords.push(validation.data); } return { valid: true, data: validatedRecords }; } async function validateSingleResponse(response, schema, selectedFields, expandConfigs, mode = "maybe") { var _a; if (response.value && Array.isArray(response.value) && response.value.length > 1) { return { valid: false, error: new RecordCountMismatchError( mode === "exact" ? "one" : "at-most-one", response.value.length ) }; } if (!response || response.value && response.value.length === 0) { if (mode === "exact") { return { valid: false, error: new RecordCountMismatchError("one", 0) }; } return { valid: true, data: null }; } const record = ((_a = response.value) == null ? void 0 : _a[0]) ?? response; const validation = await validateRecord( record, schema, selectedFields, expandConfigs ); if (!validation.valid) { return validation; } return { valid: true, data: validation.data }; } export { validateListResponse, validateRecord, validateSingleResponse }; //# sourceMappingURL=validation.js.map