UNPKG

@cortical/core

Version:

A RESTful API framework for your apps based on GraphQL type system.

342 lines 14.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const path_1 = require("path"); const __1 = require(".."); const url_1 = require("url"); const logger_1 = require("@spine/logger"); const paths_1 = require("../paths"); const expressRequestContextParams_1 = require("../hooks/expressRequestContextParams"); const webSocketTransportContextParams_1 = require("../hooks/webSocketTransportContextParams"); const context_1 = require("../hooks/context"); const contextExecution_1 = require("../hooks/contextExecution"); const __2 = require(".."); const url_2 = require("./url"); const config_1 = require("../config"); const getSchema_1 = require("../api/getSchema"); const contextId_1 = require("../hooks/contextId"); const contextExecutionId_1 = require("../hooks/contextExecutionId"); contextId_1.default.addFilter('default', () => { return `${(new Date()).getTime()}.${Math.floor(Math.random() * Math.pow(10, 5))}`; }); const $$executions = Symbol(); const $$executionSeq = Symbol(); const $$end = Symbol(); contextExecutionId_1.default.addFilter('default', (value, { context }) => { if (typeof context[$$executionSeq] === 'undefined') { context[$$executionSeq] = -1; } context[$$executionSeq] = context[$$executionSeq] + 1; return `${context.id}.${context[$$executionSeq]}`; }); function createContextId(context) { return contextId_1.default.filter(null, { context }); } exports.createContextId = createContextId; function createExecutionContextId(context, info) { return contextExecutionId_1.default.filter(null, { context, info }); } exports.createExecutionContextId = createExecutionContextId; const connections = new Set(); __1.hooks.bootstrap.addAction('connections', () => __awaiter(this, void 0, void 0, function* () { const closeConnections = () => { while (connections.size > 0) { for (const connection of connections.values()) { if (!connection.completed) { connection.endAll(); } } } }; if (__1.hooks.bootstrap.has('server')) { __1.hooks.death.addActionAfter('server', 'connections', closeConnections); } else { __1.hooks.death.addAction('connections', closeConnections); } })); function createContextNamespace(context, info, id) { let { operationName, operationType, } = context; let namespace; if (typeof info !== 'undefined') { if (typeof operationType === 'undefined') { operationType = info.operation.operation; } if (typeof operationName === 'undefined' && typeof info.operation.name !== 'undefined') { operationName = info.operation.name.value; } operationType = `${operationType}.${info.fieldName}`; if (info.path.key !== info.fieldName) { if (typeof operationName === 'undefined') { operationName = ''; } operationName = `${operationName}.${info.path.key}`; } } if (typeof operationType !== 'undefined') { namespace = `${operationType}`; } else { namespace = ''; } if (typeof operationName !== 'undefined') { namespace = `${namespace}@${operationName}`; } return `${namespace}::${typeof id !== 'undefined' ? id : context.id}`; } exports.createContextNamespace = createContextNamespace; class BaseContext { constructor(url, headers, params, operationName, operationType, id) { this.url = url; this.headers = headers; this.params = params; this.operationName = operationName; this.operationType = operationType; this.id = id; this.context = undefined; this.log = logger_1.logger(createContextNamespace(this)); this.debug = this.log.debug.bind(this.log); this.event = new events_1.EventEmitter(); this.schema = getSchema_1.getSchema(); this[$$end] = []; this.execute = (document, variableValues, operationName) => __awaiter(this, void 0, void 0, function* () { return __2.execute(document, this.url, this.headers, this.params, variableValues, operationName); }); this.subscribe = (document, variableValues, operationName) => __awaiter(this, void 0, void 0, function* () { return __2.subscribe(document, this.url, this.headers, this.params, variableValues, operationName); }); Object.defineProperty(this, 'completed', { value: false, configurable: true }); if (typeof this.id === 'undefined') { this.id = createContextId(this); } connections.add(this); } destroy(handler) { return this.once('destroy', handler); } on(event, handler) { return this.event.on(event, handler); } once(event, handler) { return this.event.once(event, handler); } removeListener(event, handler) { return this.event.removeListener(event, handler); } removeAllListeners(event) { return this.event.removeAllListeners(event); } emit(event, ...args) { return this.event.emit(event, ...args); } execution(info) { return createExecutionContext(this, info); } endAll(value, reason) { if (typeof value === 'function') { if (this.completed) { process.nextTick(value); } else { this[$$end].push(value); } } else { let self = this; if (self.completed) { throw new Error('The execution has already ended'); } Object.defineProperty(self, 'completed', { value: true }); process.nextTick(() => { (() => __awaiter(this, void 0, void 0, function* () { self.completionReason = reason; if (Array.isArray(value)) { self.completionErrors = value; } else if (value instanceof Error) { self.completionErrors = [value]; } const handlers = self[$$end].splice(0, self[$$end].length); yield Promise.all(handlers .map((handler) => __awaiter(this, void 0, void 0, function* () { return handler(self.completionErrors, self.completionReason); })) .map((promise) => promise.catch(error => console.log(error.stack)))); connections.delete(this); self.event.emit('destroy', self.completionErrors, this.completionReason); }))().catch(error => { console.log(error.stack); }); }); } } end(value, reason) { return this.endAll(value, reason); } } exports.BaseContext = BaseContext; ; class BaseExecutionContext { constructor(context, info) { this.context = context; this.info = info; this.executionEvent = new events_1.EventEmitter(); this.isExecution = true; this.id = createExecutionContextId(context, info); const namespace = createContextNamespace(context, info, this.id); this.log = logger_1.logger(`cortical:${namespace}`); this.debug = this.log.debug.bind(this.log); this[$$end] = []; if (typeof this[$$executions] === 'undefined') { this[$$executions] = []; } this[$$executions].push(this); Object.defineProperty(this, 'completed', { value: false, configurable: true }); this.context.end((error, reason) => { if (!this.completed) { this.end(error, reason); } }); } execution(info = this.info) { return this.context.execution(info); } destroy(handler) { return this.once('destroy', handler); } on(event, handler) { return this.executionEvent.on(event, handler); } once(event, handler) { return this.executionEvent.once(event, handler); } removeListener(event, handler) { return this.executionEvent.removeListener(event, handler); } removeAllListeners(event) { return this.executionEvent.removeAllListeners(event); } emit(event, ...args) { return this.executionEvent.emit(event, ...args); } end(value, reason) { if (typeof value === 'function') { if (this.completed) { process.nextTick(value); } else { this[$$end].push(value); } } else { const self = this; if (self.completed) { throw new Error('The execution has already ended'); } Object.defineProperty(self, 'completed', { value: true }); process.nextTick(() => { (() => __awaiter(this, void 0, void 0, function* () { self.completionReason = reason; if (value instanceof Error) { self.completionError = value; } const handlers = self[$$end].splice(0, self[$$end].length); yield Promise.all(handlers .map((handler) => __awaiter(this, void 0, void 0, function* () { return handler(self.completionError, self.completionReason); })) .map((promise) => promise.catch(error => console.log(error.stack)))); self.executionEvent.emit('destroy', self.completionError, this.completionReason); }))().catch(error => { console.log(error.stack); }); }); } } } exports.BaseExecutionContext = BaseExecutionContext; function createContext(url, headers, params, operationName, operationType, executionId) { return __awaiter(this, void 0, void 0, function* () { const { Context } = require(path_1.resolve(paths_1.getApiPath(), 'context')); const context = new Context(url, headers, params, operationName, operationType, executionId); yield context_1.default.do({ context, params }); return context; }); } exports.createContext = createContext; function createExecutionContext(context, info) { return __awaiter(this, void 0, void 0, function* () { const execution = new BaseExecutionContext(context, info); yield contextExecution_1.default.do({ execution, context, info }); for (const key in context) { if (!(key in execution) && typeof execution[key] === 'undefined') { const contextMethod = context[key]; execution[key] = typeof contextMethod === 'function' ? contextMethod.bind(context) : contextMethod; } } return execution; }); } exports.createExecutionContext = createExecutionContext; function getOperationFromQuery(query) { const match = typeof query === 'string' ? query.match(/^\s*([^\s]+)(?:\s+([^\s]+))?\s*{/) : null; if (match != null) { return { operationName: match[2], operationType: match[1], }; } return { operationName: undefined, operationType: undefined, }; } exports.getOperationFromQuery = getOperationFromQuery; function createContextFromExpressRequest(req, params) { return __awaiter(this, void 0, void 0, function* () { let contextParams = Object.assign({}, params); contextParams = expressRequestContextParams_1.default.filter(contextParams, { req, params }); let operationName; let operationType; if (req.path === url_2.basePath(config_1.config.server.graphqlPath)) { let query; if (req.method === 'POST') { query = typeof req.body === 'object' && req.body != null ? req.body.query : undefined; } else { query = typeof req.query === 'object' && req.query != null ? req.query.query : undefined; } if (query != null) { ({ operationName, operationType, } = getOperationFromQuery(query)); } } return yield createContext(url_1.parse(req.originalUrl, true), req.headers, contextParams, operationName, operationType); }); } exports.createContextFromExpressRequest = createContextFromExpressRequest; function createContextFromWebSocketTransport(webSocket, message, params) { return __awaiter(this, void 0, void 0, function* () { const upgradeReq = webSocket.upgradeReq; let contextParams = Object.assign({}, (typeof message !== 'undefined' && typeof message.payload !== 'undefined' ? message.payload.context : null)); contextParams = webSocketTransportContextParams_1.default.filter(contextParams, { webSocket, message, params }); let operationName; let operationType; if (typeof params.query === 'string') { ({ operationName, operationType } = getOperationFromQuery(params.query)); } return yield createContext(url_1.parse(upgradeReq.url || '', true), upgradeReq.headers, contextParams, operationName, operationType); }); } exports.createContextFromWebSocketTransport = createContextFromWebSocketTransport; //# sourceMappingURL=context.js.map