UNPKG

@proofkit/fmodata

Version:

FileMaker OData API client

226 lines (225 loc) 6.85 kB
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