@proofkit/fmodata
Version:
FileMaker OData API client
342 lines (341 loc) • 11.2 kB
JavaScript
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