@proofkit/fmdapi
Version:
FileMaker Data API client
297 lines (296 loc) • 9.66 kB
JavaScript
import { FileMakerError } from "./client-types.js";
function asNumber(input) {
return typeof input === "string" ? parseInt(input) : input;
}
function DataApi(options) {
if ("zodValidators" in options) {
throw new Error(
"zodValidators is no longer supported. Use schema instead, or re-run the typegen command"
);
}
const schema = options.schema;
const layout = options.layout;
const {
create,
delete: _adapterDelete,
find,
get,
list,
update,
layoutMetadata,
containerUpload,
...otherMethods
} = options.adapter;
async function _list(args) {
const { fetch, timeout, ...params } = args ?? {};
if ("limit" in params && params.limit !== void 0)
delete Object.assign(params, { _limit: params.limit })["limit"];
if ("offset" in params && params.offset !== void 0) {
if (params.offset <= 1) delete params.offset;
else delete Object.assign(params, { _offset: params.offset })["offset"];
}
if ("sort" in params && params.sort !== void 0)
delete Object.assign(params, {
_sort: Array.isArray(params.sort) ? params.sort : [params.sort]
})["sort"];
const result = await list({
layout,
data: params,
fetch,
timeout
});
if (result.dataInfo.foundCount > result.dataInfo.returnedCount) {
if ((args == null ? void 0 : args.limit) === void 0 && (args == null ? void 0 : args.offset) === void 0) {
console.warn(
`🚨 @proofkit/fmdapi: Loaded only ${result.dataInfo.returnedCount} of the ${result.dataInfo.foundCount} records from your "${layout}" layout. Use the "listAll" method to automatically paginate through all records, or specify a "limit" and "offset" to handle pagination yourself.`
);
}
}
return await runSchemaValidationAndTransform(
schema,
result
);
}
async function listAll(args) {
let runningData = [];
const limit = (args == null ? void 0 : args.limit) ?? 100;
let offset = (args == null ? void 0 : args.offset) ?? 1;
while (true) {
const data = await _list({
...args,
offset
// eslint-disable-next-line @typescript-eslint/no-explicit-any
});
runningData = [...runningData, ...data.data];
if (runningData.length >= data.dataInfo.foundCount) break;
offset = offset + limit;
}
return runningData;
}
async function _create(args) {
const { fetch, timeout, ...params } = args ?? {};
return await create({
layout,
data: params,
fetch,
timeout
});
}
async function _get(args) {
args.recordId = asNumber(args.recordId);
const { recordId, fetch, timeout, ...params } = args;
const result = await get({
layout,
data: { ...params, recordId },
fetch,
timeout
});
return await runSchemaValidationAndTransform(
schema,
result
);
}
async function _update(args) {
args.recordId = asNumber(args.recordId);
const { recordId, fetch, timeout, ...params } = args;
return await update({
layout,
data: { ...params, recordId },
fetch,
timeout
});
}
async function deleteRecord(args) {
args.recordId = asNumber(args.recordId);
const { recordId, fetch, timeout, ...params } = args;
return _adapterDelete({
layout,
data: { ...params, recordId },
fetch,
timeout
});
}
async function _find(args) {
const {
query: queryInput,
ignoreEmptyResult = false,
timeout,
fetch,
...params
} = args;
const query = !Array.isArray(queryInput) ? [queryInput] : queryInput;
if ("offset" in params && params.offset !== void 0) {
if (params.offset <= 1) delete params.offset;
}
if ("dateformats" in params && params.dateformats !== void 0) {
params.dateformats = (params.dateformats === "US" ? 0 : params.dateformats === "file_locale" ? 1 : params.dateformats === "ISO8601" ? 2 : 0).toString();
}
const result = await find({
data: { ...params, query },
layout,
fetch,
timeout
}).catch((e) => {
if (ignoreEmptyResult && e instanceof FileMakerError && e.code === "401")
return { data: [], dataInfo: { foundCount: 0, returnedCount: 0 } };
throw e;
});
if (result.dataInfo.foundCount > result.dataInfo.returnedCount) {
if ((args == null ? void 0 : args.limit) === void 0 && (args == null ? void 0 : args.offset) === void 0) {
console.warn(
`🚨 @proofkit/fmdapi: Loaded only ${result.dataInfo.returnedCount} of the ${result.dataInfo.foundCount} records from your "${layout}" layout. Use the "findAll" method to automatically paginate through all records, or specify a "limit" and "offset" to handle pagination yourself.`
);
}
}
return await runSchemaValidationAndTransform(schema, result);
}
async function findOne(args) {
const result = await _find(args);
if (result.data.length !== 1)
throw new Error(
`${result.data.length} records found; expecting exactly 1`
);
const transformedResult = await runSchemaValidationAndTransform(
schema,
result
);
if (!transformedResult.data[0]) throw new Error("No data found");
return { ...transformedResult, data: transformedResult.data[0] };
}
async function findFirst(args) {
const result = await _find(args);
const transformedResult = await runSchemaValidationAndTransform(
schema,
result
);
if (!transformedResult.data[0]) throw new Error("No data found");
return { ...transformedResult, data: transformedResult.data[0] };
}
async function maybeFindFirst(args) {
const result = await _find({ ...args, ignoreEmptyResult: true });
const transformedResult = await runSchemaValidationAndTransform(
schema,
result
);
if (!transformedResult.data[0]) return null;
return { ...transformedResult, data: transformedResult.data[0] };
}
async function findAll(args) {
let runningData = [];
const limit = args.limit ?? 100;
let offset = args.offset ?? 1;
while (true) {
const data = await _find({
...args,
offset,
ignoreEmptyResult: true
});
runningData = [...runningData, ...data.data];
if (runningData.length === 0 || runningData.length >= data.dataInfo.foundCount)
break;
offset = offset + limit;
}
return runningData;
}
async function _layoutMetadata(args) {
const { ...restArgs } = args ?? {};
const params = restArgs;
return await layoutMetadata({
layout,
fetch: params.fetch,
// Now should correctly resolve to undefined if not present
timeout: params.timeout
// Now should correctly resolve to undefined if not present
});
}
async function _containerUpload(args) {
const { ...params } = args;
return await containerUpload({
layout,
data: {
...params,
containerFieldName: params.containerFieldName,
repetition: params.containerFieldRepetition
},
fetch: params.fetch,
timeout: params.timeout
});
}
async function runSchemaValidationAndTransform(schema2, result) {
var _a;
const fieldDataIssues = [];
const portalDataIssues = [];
if (!schema2) return result;
const transformedData = [];
for (const record of result.data) {
let fieldResult = schema2.fieldData["~standard"].validate(
record.fieldData
);
if (fieldResult instanceof Promise) fieldResult = await fieldResult;
if ("value" in fieldResult) {
record.fieldData = fieldResult.value;
} else {
fieldDataIssues.push(...fieldResult.issues);
}
if (schema2.portalData) {
for (const [portalName, portalRecords] of Object.entries(
record.portalData
)) {
const validatedPortalRecords = [];
for (const portalRecord of portalRecords) {
let portalResult = (_a = schema2.portalData[portalName]) == null ? void 0 : _a["~standard"].validate(
portalRecord
);
if (portalResult instanceof Promise)
portalResult = await portalResult;
if (portalResult && "value" in portalResult) {
validatedPortalRecords.push({
...portalResult.value,
recordId: portalRecord.recordId,
modId: portalRecord.modId
});
} else {
portalDataIssues.push(...(portalResult == null ? void 0 : portalResult.issues) ?? []);
}
}
record.portalData[portalName] = validatedPortalRecords;
}
}
transformedData.push(record);
}
result.data = transformedData;
if (fieldDataIssues.length > 0 || portalDataIssues.length > 0) {
console.error(
`🚨 @proofkit/fmdapi: Validation issues for layout "${layout}". Run the typegen command again to generate the latest field definitions from your layout.`,
{
fieldDataIssues,
portalDataIssues
}
);
throw new Error("Schema validation issues");
}
return result;
}
return {
...otherMethods,
layout: options.layout,
list: _list,
listAll,
create: _create,
get: _get,
update: _update,
delete: deleteRecord,
find: _find,
findOne,
findFirst,
maybeFindFirst,
findAll,
layoutMetadata: _layoutMetadata,
containerUpload: _containerUpload
};
}
export {
DataApi,
DataApi as default
};
//# sourceMappingURL=client.js.map