@proofkit/fmodata
Version:
FileMaker OData API client
226 lines (225 loc) • 6.85 kB
JavaScript
import { ResponseStructureError, RecordCountMismatchError } from "../../errors.js";
import { getBaseTableConfig } from "../../orm/table.js";
import { transformResponseFields } from "../../transform.js";
import { validateSingleResponse, validateListResponse } from "../../validation.js";
import { ExpandBuilder } from "./expand-builder.js";
async function processODataResponse(rawResponse, config) {
const {
table,
schema,
singleMode,
selectedFields,
expandValidationConfigs,
skipValidation,
useEntityIds,
includeSpecialColumns,
fieldMapping
} = config;
let response = rawResponse;
if (table && useEntityIds) {
response = transformResponseFields(response, table, expandValidationConfigs);
}
if (skipValidation) {
const result = extractRecords(response, singleMode);
if (result.data && fieldMapping && Object.keys(fieldMapping).length > 0) {
if (result.error) {
return { data: void 0, error: result.error };
}
return {
data: renameFieldsInResponse(result.data, fieldMapping),
error: void 0
};
}
return result;
}
if (singleMode !== false) {
const validation2 = await validateSingleResponse(
response,
schema,
// biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type parameter
selectedFields,
expandValidationConfigs,
singleMode,
includeSpecialColumns
);
if (!validation2.valid) {
return { data: void 0, error: validation2.error };
}
if (fieldMapping && Object.keys(fieldMapping).length > 0) {
return {
data: renameFieldsInResponse(validation2.data, fieldMapping),
error: void 0
};
}
return { data: validation2.data, error: void 0 };
}
const validation = await validateListResponse(
response,
schema,
// biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic type parameter
selectedFields,
expandValidationConfigs,
includeSpecialColumns
);
if (!validation.valid) {
return { data: void 0, error: validation.error };
}
if (fieldMapping && Object.keys(fieldMapping).length > 0) {
return {
data: renameFieldsInResponse(validation.data, fieldMapping),
error: void 0
};
}
return { data: validation.data, error: void 0 };
}
function extractRecords(response, singleMode) {
if (singleMode === false) {
const records2 = response.value ?? [];
return { data: records2, error: void 0 };
}
const records = response.value ?? [response];
const count = Array.isArray(records) ? records.length : 1;
if (count > 1) {
return {
data: void 0,
error: new RecordCountMismatchError(singleMode === "exact" ? "one" : "at-most-one", count)
};
}
if (count === 0) {
if (singleMode === "exact") {
return { data: void 0, error: new RecordCountMismatchError("one", 0) };
}
return { data: null, error: void 0 };
}
const record = Array.isArray(records) ? records[0] : records;
return { data: record, error: void 0 };
}
function getSchemaFromTable(table) {
if (!table) {
return void 0;
}
const baseTableConfig = getBaseTableConfig(table);
const containerFields = baseTableConfig.containerFields || [];
const schema = { ...baseTableConfig.schema };
for (const containerField of containerFields) {
delete schema[containerField];
}
return schema;
}
function renameFieldsInResponse(data, fieldMapping) {
if (!data || typeof data !== "object") {
return data;
}
if (Array.isArray(data)) {
return data.map((item) => renameFieldsInResponse(item, fieldMapping));
}
if ("value" in data && Array.isArray(data.value)) {
return {
...data,
// biome-ignore lint/suspicious/noExplicitAny: Dynamic record transformation
value: data.value.map((item) => renameFieldsInResponse(item, fieldMapping))
};
}
const renamed = {};
for (const [key, value] of Object.entries(data)) {
const outputKey = fieldMapping[key];
if (outputKey) {
renamed[outputKey] = value;
} else {
renamed[key] = value;
}
}
return renamed;
}
async function processQueryResponse(response, config) {
const {
occurrence,
singleMode,
queryOptions,
expandConfigs,
skipValidation,
useEntityIds,
includeSpecialColumns,
includeCount,
fieldMapping,
logger
} = config;
const expandBuilder = new ExpandBuilder(useEntityIds ?? false, logger);
const expandValidationConfigs = expandBuilder.buildValidationConfigs(expandConfigs);
let selectedFields;
if (queryOptions.select) {
selectedFields = Array.isArray(queryOptions.select) ? queryOptions.select.map(String) : [String(queryOptions.select)];
}
let processedResponse = await processODataResponse(response, {
table: occurrence,
schema: getSchemaFromTable(occurrence),
singleMode,
selectedFields,
expandValidationConfigs,
skipValidation,
useEntityIds,
includeSpecialColumns
});
if (processedResponse.data && fieldMapping && Object.keys(fieldMapping).length > 0) {
processedResponse = {
...processedResponse,
data: renameFieldsInResponse(processedResponse.data, fieldMapping)
};
}
if (includeCount) {
if (processedResponse.error) {
return {
data: void 0,
error: processedResponse.error
};
}
if (singleMode !== false) {
return {
data: void 0,
error: new ResponseStructureError("list response for count-enabled query", response)
};
}
const rawCount = response == null ? void 0 : response["@odata.count"];
let parsedCount = Number.NaN;
if (typeof rawCount === "number") {
parsedCount = rawCount;
} else if (typeof rawCount === "string" && rawCount.trim() !== "") {
parsedCount = Number(rawCount);
}
if (!Number.isFinite(parsedCount)) {
return {
data: void 0,
error: new ResponseStructureError("response with valid @odata.count", response)
};
}
return {
data: {
records: processedResponse.data,
count: parsedCount
},
error: void 0
};
}
return processedResponse;
}
async function processRecordResponse(response, config) {
const result = await processQueryResponse(response, {
occurrence: config.table,
singleMode: "exact",
queryOptions: { select: config.selectedFields },
expandConfigs: config.expandConfigs,
skipValidation: config.skipValidation,
useEntityIds: config.useEntityIds,
includeSpecialColumns: config.includeSpecialColumns,
fieldMapping: config.fieldMapping,
logger: config.logger
});
return result;
}
export {
getSchemaFromTable,
processODataResponse,
processQueryResponse,
processRecordResponse
};
//# sourceMappingURL=response-processor.js.map