UNPKG

@proofkit/fmodata

Version:

FileMaker OData API client

203 lines (202 loc) 6.82 kB
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