@proofkit/fmodata
Version:
FileMaker OData API client
203 lines (202 loc) • 6.82 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { FMServerConnection } from "./client/filemaker-odata.js";
function getLegacyCompatiblePattern(pattern) {
return pattern.replace(/\.fmp12(?=\/|$)/gi, "");
}
function stripODataAnnotations(data) {
if (Array.isArray(data)) {
return data.map(stripODataAnnotations);
}
if (data && typeof data === "object") {
const { "@id": _id, "@editLink": _editLink, ...rest } = data;
const result = {};
for (const [key, value] of Object.entries(rest)) {
result[key] = stripODataAnnotations(value);
}
return result;
}
return data;
}
function createRouterFetch(routes, spy) {
return async (input, init) => {
var _a;
let url;
if (typeof input === "string") {
url = input;
} else if (input instanceof URL) {
url = input.toString();
} else {
url = input.url;
}
const method = (init == null ? void 0 : init.method) ?? (input instanceof Request ? input.method : "GET");
if (spy) {
let body2;
if (typeof (init == null ? void 0 : init.body) === "string") {
body2 = init.body;
} else if (input instanceof Request) {
try {
body2 = await input.clone().text();
if (body2 === "") {
body2 = void 0;
}
} catch {
}
}
const headers = {};
if (init == null ? void 0 : init.headers) {
if (init.headers instanceof Headers) {
init.headers.forEach((v, k) => {
headers[k] = v;
});
} else if (!Array.isArray(init.headers)) {
Object.assign(headers, init.headers);
}
} else if (input instanceof Request) {
input.headers.forEach((v, k) => {
headers[k] = v;
});
}
spy.calls.push({ url, method, body: body2, headers });
}
const route = routes.find((r) => {
const urlMatch = typeof r.urlPattern === "string" ? url.includes(r.urlPattern) || url.includes(getLegacyCompatiblePattern(r.urlPattern)) : r.urlPattern.test(url);
const methodMatch = !r.method || r.method.toUpperCase() === method.toUpperCase();
return urlMatch && methodMatch;
});
if (route == null ? void 0 : route.throwError) {
throw route.throwError;
}
if (!route) {
return new Response(
JSON.stringify({ error: { message: `No mock route for ${method} ${url}`, code: "MOCK_NOT_FOUND" } }),
{
status: 404,
statusText: "Not Found (No Mock Route)",
headers: { "content-type": "application/json" }
}
);
}
const status = route.status ?? 200;
const contentType = ((_a = route.headers) == null ? void 0 : _a["content-type"]) ?? "application/json";
const responseHeaders = new Headers({ "content-type": contentType });
if (route.headers) {
for (const [key, value] of Object.entries(route.headers)) {
if (key !== "content-type" && value) {
responseHeaders.set(key, value);
}
}
}
if (status === 204) {
return new Response(null, { status, statusText: "No Content", headers: responseHeaders });
}
let acceptHeader = "";
if (input instanceof Request) {
acceptHeader = input.headers.get("Accept") ?? "";
} else if (init == null ? void 0 : init.headers) {
if (init.headers instanceof Headers) {
acceptHeader = init.headers.get("Accept") ?? "";
} else if (Array.isArray(init.headers)) {
const acceptEntry = init.headers.find(([key]) => key.toLowerCase() === "accept");
acceptHeader = (acceptEntry == null ? void 0 : acceptEntry[1]) ?? "";
} else {
acceptHeader = init.headers.Accept ?? init.headers.accept ?? "";
}
}
const shouldStripAnnotations = acceptHeader.includes("odata.metadata=none");
let responseData = route.response;
if (Array.isArray(responseData)) {
responseData = { value: responseData };
}
if (shouldStripAnnotations && responseData) {
responseData = stripODataAnnotations(responseData);
}
let body;
if (responseData === null || responseData === void 0) {
body = null;
} else if (typeof responseData === "string") {
body = responseData;
} else {
body = JSON.stringify(responseData);
}
return new Response(body, {
status,
statusText: status >= 200 && status < 300 ? "OK" : "Error",
headers: responseHeaders
});
};
}
class MockFMServerConnection {
constructor(config) {
__publicField(this, "routes");
__publicField(this, "connection");
__publicField(this, "_spy");
this.routes = (config == null ? void 0 : config.routes) ? [...config.routes] : [];
this._spy = (config == null ? void 0 : config.enableSpy) ? { calls: [] } : void 0;
this.connection = new FMServerConnection({
serverUrl: (config == null ? void 0 : config.baseUrl) ?? "https://test.example.com",
auth: { apiKey: "test-api-key" },
logger: config == null ? void 0 : config.logger,
fetchClientOptions: {
retries: 0,
fetchHandler: createRouterFetch(this.routes, this._spy)
}
});
}
/**
* Add a route to the mock. Routes added after construction are picked up
* automatically by subsequent requests. Routes are matched in order,
* and the first matching route wins.
*/
addRoute(route) {
this.routes.push(route);
return this;
}
/**
* Set multiple routes, replacing any existing routes.
*/
setRoutes(routes) {
this.routes.length = 0;
this.routes.push(...routes);
return this;
}
/**
* Get the request spy (only available if `enableSpy: true` was passed to constructor).
*/
get spy() {
if (!this._spy) {
return void 0;
}
const spy = this._spy;
return {
get calls() {
return spy.calls;
},
clear() {
spy.calls.length = 0;
},
forUrl(pattern) {
return spy.calls.filter(
(c) => typeof pattern === "string" ? c.url.includes(pattern) || c.url.includes(getLegacyCompatiblePattern(pattern)) : pattern.test(c.url)
);
}
};
}
/**
* Create a Database instance, same API as FMServerConnection.database().
*/
database(name, config) {
return this.connection.database(name, config);
}
/**
* Get the underlying FMServerConnection (for cases that need the real type).
*/
get asConnection() {
return this.connection;
}
}
export {
MockFMServerConnection
};
//# sourceMappingURL=testing.js.map