kinvey-flex-sdk
Version:
SDK for creating Kinvey Flex Services
174 lines (149 loc) • 5.66 kB
JavaScript
/**
* Copyright (c) 2018 Kinvey Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
const http = require('http');
const https = require('https');
const data = require('./service/data');
const flex = require('../package.json');
const functions = require('./service/functions');
const auth = require('./service/auth');
const moduleGenerator = require('./service/moduleGenerator');
const logger = require('./service/logger');
const receiver = require('kinvey-code-task-runner');
const kinveyErrors = require('kinvey-datalink-errors');
let terminated = false;
http.globalAgent.maxSockets = 100;
https.globalAgent.maxSockets = 100;
function getPublicConnectionErrorMsg(receiverMsg) {
if (receiverMsg === 'Connection ended by client.') {
return 'Flex Service request timed out.';
}
return 'Unexpected network issue.';
}
class Flex {
constructor(opt, cb) {
const callback = !cb && typeof opt === 'function' ? opt : cb;
const options = typeof opt === 'function' || opt == null ? {} : opt;
let gracefulShutdownTimeout;
options.type = process.env.SDK_RECEIVER === 'tcp' ? 'tcp' : 'http';
if (options.type === 'tcp') {
delete options.host;
delete options.port;
}
this.data = this.dataLink = data;
this.functions = this.businessLogic = functions;
this.auth = auth;
this.logger = logger;
this.moduleGenerator = moduleGenerator;
this.version = flex.version;
if (options.sharedSecret) {
this.sharedSecret = options.sharedSecret;
}
function terminate(err) {
clearTimeout(gracefulShutdownTimeout);
if (err) {
// eslint-disable-next-line no-console
console.log(`Shutdown Error: ${err}`);
process.exit(-1);
}
process.exit();
}
function shutdown() {
if (terminated) {
// non-graceful shutdown, requires two successive signals
// eslint-disable-next-line no-console
console.log('\nForced quit!\n');
return terminate('Force quit!');
}
terminated = true;
// eslint-disable-next-line no-console
console.log(
'Signal received, initiating graceful shutdown. Press ctrl-c or send SIGTERM/SIGINT to force-quit immediately.'
);
gracefulShutdownTimeout = setTimeout(terminate, 50000);
return receiver.stop(err => terminate(err));
}
// TODO Remove legacy taskType values
const taskReceivedCallback = ((task, completionCallback) => {
task.sdkVersion = this.version;
const complete = (task) => {
const errorMsg = completionCallback(null, task);
if (errorMsg && options.type === 'tcp') {
this.logger.error(`Unable to call complete() in ${task.hookType} "${task.taskName}" for request with id "${task.requestId}": ${getPublicConnectionErrorMsg(errorMsg)}`);
}
};
if (this.sharedSecret != null
&& task.taskType !== 'serviceDiscovery'
&& task.taskType !== 'logger'
&& task.taskType !== 'moduleGenerator'
&& this.sharedSecret !== task.authKey
) {
task.response = task.response || {};
const result = task.response;
result.body = kinveyErrors.generateKinveyError(
'Unauthorized', 'The Authorization Key was not valid or missing.'
).toJSON();
result.statusCode = result.body.statusCode;
delete result.body.statusCode;
task.response.continue = false;
return complete(task);
}
if ((!this[task.taskType]
&& task.taskType !== 'serviceDiscovery')
|| task.taskType === 'logger'
|| task.taskType === 'moduleGenerator'
) {
task.response = task.response || {};
const result = task.response;
result.body = kinveyErrors.generateKinveyError('BadRequest', 'Invalid task Type').toJSON();
result.statusCode = result.body.statusCode;
delete result.body.statusCode;
task.response.continue = false;
return complete(task);
}
if (task.taskType === 'serviceDiscovery') {
task.discoveryObjects = {
dataLink: {
serviceObjects: this.data.getServiceObjects()
},
businessLogic: {
handlers: this.functions.getHandlers()
},
auth: {
handlers: this.auth.getHandlers()
}
};
return complete(task);
}
return this[task.taskType].process(task, this.moduleGenerator.generate(task), (taskWithError, task) => {
complete(taskWithError || task);
});
});
receiver.start(options, taskReceivedCallback, (err) => {
if (err != null) {
return callback(new Error(`Could not start task receiver: ${err}`));
}
return callback(null, this);
});
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
}
}
function generateFlexService(opt, cb) {
const callback = !cb && typeof opt === 'function' ? opt : cb;
const options = typeof opt === 'function' ? {} : opt;
return new Flex(options, (err, service) => {
callback(err, service);
});
}
exports.service = generateFlexService;