UNPKG

airdcpp-apisocket

Version:
187 lines 7.02 kB
import { Server } from 'mock-socket'; import { EventEmitter } from 'events'; import { MOCK_SERVER_URL } from './mock-data.js'; import Logger from '../../SocketLogger.js'; ; const toEmitId = (path, method) => { return `${path}_${method}`; }; const getDefaultMockCreatorF = () => ({ fn: () => { }, }); const DEFAULT_MOCK_SERVER_OPTIONS = { url: MOCK_SERVER_URL, reportMissingListeners: true, mockF: getDefaultMockCreatorF(), delayMs: 0, loggerOptions: { logLevel: 'warn', logOutput: console, } }; const getMockServer = (initialOptions = {}) => { const { url, reportMissingListeners, mockF, delayMs, loggerOptions } = { ...DEFAULT_MOCK_SERVER_OPTIONS, ...initialOptions, }; const logger = Logger(loggerOptions); const mockServer = new Server(url); let socket; const emitter = new EventEmitter(); emitter.setMaxListeners(1); const send = (data) => { socket.send(JSON.stringify(data)); }; const handlers = new Map(); const addServerHandler = (method, path, responseData, subscriptionCallback) => { const requestHandler = (request, s) => { const data = typeof responseData === 'function' ? responseData(request, s) : responseData; if (!data || !data.code) { throw new Error(`Mock server: response handler for path ${path} must return a status code`); } const response = { callback_id: request.callback_id, ...data, }; const delay = typeof delayMs === 'function' ? delayMs() : delayMs; setTimeout(() => { logger.verbose(`Mock server: sending response for request ${request.callback_id} (${method} ${path}):`, data); if (subscriptionCallback) { subscriptionCallback(request); } s.send(JSON.stringify(response)); }, delay); }; // Don't add duplicates const emitId = toEmitId(path, method); const removeExisting = handlers.get(emitId); if (removeExisting) { removeExisting(); handlers.delete(emitId); } // Add new emitter.addListener(emitId, requestHandler); // Prepare for removal const removeListener = () => emitter.removeListener(emitId, requestHandler); handlers.set(emitId, removeListener); return removeListener; }; const addDummyDataHandler = (method, path) => { const handler = (request, s) => { // Do nothing }; const emitId = toEmitId(path, method); emitter.addListener(emitId, handler); emitter.setMaxListeners(1); return () => emitter.removeListener(emitId, handler); }; const addRequestHandler = (method, path, data, subscriptionCallback) => { const handlerData = typeof data === 'function' ? data : { data, code: data ? 200 : 204, }; return addServerHandler(method, path, handlerData, subscriptionCallback); }; const addErrorHandler = (method, path, errorStr, errorCode, subscriptionCallback) => { return addServerHandler(method, path, { error: !errorStr ? null : { message: errorStr, }, code: errorCode, }, subscriptionCallback); }; const addSubscriptionHandlerImpl = (moduleName, type, listenerName, entityId) => { const subscribeFn = mockF.fn(); const unsubscribeFn = mockF.fn(); const path = entityId ? `${moduleName}/${entityId}/${type}/${listenerName}` : `${moduleName}/${type}/${listenerName}`; const subscribeRemove = addRequestHandler('POST', path, undefined, subscribeFn); const unsubscribeRemove = addRequestHandler('DELETE', path, undefined, unsubscribeFn); const fire = (data, entityId) => { if (entityId) { logger.verbose(`Mock server: firing subscriber ${moduleName} ${listenerName} for entity ${entityId}:`, data); } else { logger.verbose(`Mock server: firing subscriber ${moduleName} ${listenerName}:`, data); } send({ event: listenerName, data, id: entityId, }); }; const remove = () => { subscribeRemove(); unsubscribeRemove(); }; return { fire, remove, subscribeFn, unsubscribeFn, path, }; }; const addSubscriptionHandler = (moduleName, listenerName, entityId) => { return addSubscriptionHandlerImpl(moduleName, 'listeners', listenerName, entityId); }; const addHookHandler = (moduleName, listenerName) => { const subscriber = addSubscriptionHandlerImpl(moduleName, 'hooks', listenerName); const addResolver = (completionId) => { const resolveFn = mockF.fn(); const rejectFn = mockF.fn(); const resolveRemove = addRequestHandler('POST', `${subscriber.path}/${completionId}/resolve`, undefined, resolveFn); const rejectRemove = addRequestHandler('POST', `${subscriber.path}/${completionId}/reject`, undefined, rejectFn); const remove = () => { resolveRemove(); rejectRemove(); }; const fire = (data) => { logger.verbose(`Mock server: firing hook ${moduleName} ${listenerName}:`, data); send({ event: listenerName, data, completion_id: completionId, }); }; return { fire, remove, resolveFn, rejectFn }; }; return { addResolver, ...subscriber, }; }; const clearHandlers = () => { emitter.removeAllListeners(); handlers.clear(); }; mockServer.on('connection', s => { socket = s; socket.on('message', (messageObj) => { const request = JSON.parse(messageObj); const emitId = toEmitId(request.path, request.method); const processed = emitter.emit(emitId, request, s); if (reportMissingListeners && !processed) { logger.warn(`Mock server: no listeners for event ${request.method} ${request.path}`); } }); }); mockServer.on('close', () => { // Remove handlers only after the socket was disconnected // to allow it to complete all requests clearHandlers(); }); return { addRequestHandler, addErrorHandler, addSubscriptionHandler, addHookHandler, ignoreMissingHandler: addDummyDataHandler, stop: () => { mockServer.stop(); }, send, url, }; }; export { getMockServer }; //# sourceMappingURL=mock-server.js.map