UNPKG

@smartlyio/oats-mirage-adapter

Version:

Mirage.js adapter for Oats

132 lines (123 loc) 4.25 kB
import * as runtime from '@smartlyio/oats-runtime'; import * as mirage from 'miragejs'; import * as mirageTypes from 'miragejs/-types'; import Schema from 'miragejs/orm/schema'; import { Server } from 'miragejs/server'; import { Registry as MirageRegistry, Registry } from 'miragejs/-types'; function guessContentType(contentTypeHeader: string | undefined, value: any): string { if (contentTypeHeader?.includes('application/json')) { return 'application/json'; } if (contentTypeHeader) { return contentTypeHeader; } if (value instanceof FormData) { return 'multipart/form-data'; } return 'text/html'; } async function guessValue(contentType: string, value: any) { if (value instanceof FormData) { return await handleFormData(value); } if (contentType.match(/application\/json/) && value) { return JSON.parse(value); } return value; } async function handleFormData(value: FormData) { const result: Record<string, string | ArrayBuffer> = {}; const keys: string[] = []; value.forEach((_, key) => keys.push(key)); for (const key of keys) { const keyValue = value.get(key); if (keyValue instanceof Blob) { result[key] = await keyValue.arrayBuffer(); } else if (typeof keyValue === 'string') { result[key] = keyValue; } } return result; } function adapter<Registry extends mirageTypes.AnyRegistry, RequestContext>( mirageServer: mirage.Server<Registry>, requestContextCreator: (schema: Schema<Registry>, request: mirage.Request) => RequestContext, opts?: { logging?: boolean; } ): runtime.server.ServerAdapter { // eslint-disable-next-line no-console, @typescript-eslint/no-empty-function const log = opts?.logging !== false ? console.log : () => {}; return ( path: string, op: string, method: runtime.server.Methods, handler: runtime.server.SafeEndpoint ) => { const miragePath = path.replace(/{([^}]+)}/g, (m, param) => ':' + param); const mirageHandler: typeof mirageServer.get = (mirageServer as any)[method]; mirageHandler(miragePath, async (schema, request) => { const headers = Object.keys(request.requestHeaders).reduce<Record<string, string>>( (lowerCaseHeaders, key) => { lowerCaseHeaders[key.toLowerCase()] = request.requestHeaders[key]; return lowerCaseHeaders; }, {} ); const contentType = guessContentType(headers['content-type'], request.requestBody); const value = await guessValue(contentType, request.requestBody); const body = value != null ? { value, contentType } : undefined; const ctx = { path, method, servers: [], op, headers, params: request.params, query: request.queryParams, body, requestContext: requestContextCreator(schema, request) }; log('oats-mirage-adapter request', ctx); try { const result = await handler(ctx); log('oats-mirage-adapter response', { result }); return new mirage.Response( result.status, { ...result.headers, 'content-type': result.value.contentType }, result.value.contentType.match(/application\/json/) ? JSON.stringify(result.value.value) : result.value.value ); } catch (error: any) { log('oats-mirage-adapter handler threw: ' + error.message, { error }); return new mirage.Response( 400, { 'content-type': 'text/plain' }, 'Error from handler: ' + error.message ); } }); }; } /** * Bind provided handlers for the OpenAPI routes */ export function bind< Spec, Models extends mirageTypes.AnyModels = never, Factories extends mirageTypes.AnyFactories = never, RequestContext extends Record<string, any> = Record<string, any> >(opts: { server: Server<MirageRegistry<Models, Factories>>; handler: runtime.server.HandlerFactory<Spec>; spec: Spec; requestContextCreator?: (schema: Schema<Registry<Models, Factories>>) => RequestContext; logging?: boolean; }): void { opts.handler( adapter(opts.server, opts.requestContextCreator || (() => ({})), { logging: opts.logging ?? true }) )(opts.spec); }