airdcpp-apisocket
Version:
Javascript connector for AirDC++ Web API
187 lines • 7.02 kB
JavaScript
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