@cortical/core
Version:
A RESTful API framework for your apps based on GraphQL type system.
342 lines • 14.2 kB
JavaScript
"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