diff --git a/lib/gateway.js b/lib/gateway.js index eccfe3d..7b229ae 100644 --- a/lib/gateway.js +++ b/lib/gateway.js @@ -1341,7 +1341,7 @@ class Gateway extends EventEmitter { } let plugin; try { - plugin = wellKnowns.validatePlugin(data.plugin); + plugin = wellKnowns.validatePlugin(data.plugin, data.origin); } catch (e) { return this.__wellKnownError__(req, res, `Failed to validate plugin: ${e.message}`, 502); } @@ -1962,6 +1962,28 @@ class Gateway extends EventEmitter { } } + jsonify (obj) { + if ( + obj !== null && + typeof obj === 'object' && + !Buffer.isBuffer(obj) + ) { + if (typeof obj.toJSON === 'function') { + obj = obj.toJSON(); + } + if (Array.isArray(obj)) { + obj = obj.map(item => this.jsonify(item)); + } else if ( + obj !== null && + typeof obj === 'object' && + !Buffer.isBuffer(obj) + ) { + Object.keys(obj).forEach(key => obj[key] = this.jsonify(obj[key])); + } + } + return obj; + } + execute (definition, functionArgs, data, headers, callback) { headers = headers || {}; let fn; @@ -1969,16 +1991,8 @@ class Gateway extends EventEmitter { let callbackWrapper = (err, result, headers, executionUuid) => { if (!complete) { complete = true; - if ( - result !== null && - typeof result === 'object' && - !Buffer.isBuffer(result) && - typeof result.toJSON === 'function' - ) { - callback(err, result.toJSON(), headers, executionUuid); - } else { - callback(err, result, headers, executionUuid); - } + result = this.jsonify(result); + callback(err, result, headers, executionUuid); } }; setTimeout(() => { diff --git a/lib/well-knowns.js b/lib/well-knowns.js index 1edbb68..c5a21b1 100644 --- a/lib/well-knowns.js +++ b/lib/well-knowns.js @@ -174,9 +174,14 @@ const generateOpenAPISpec = (definitions, plugin, server, origin, identifier) => } else if (!route.endsWith('/')) { route = route + '/'; } + let opIdentifier = identifier.replace(/\[(.*)\]/gi, ''); + let operationId = `${opIdentifier}${parts.length ? ('.' + parts.join('.')) : ''}`; + operationId = operationId.replace(/[^A-Z0-9_]+/gi, '_'); + operationId = operationId.replace(/^_+/gi, ''); + operationId = operationId.replace(/_+$/gi, ''); let pathData = { description: def.description, - operationId: `${identifier}${parts.length ? ('.' + parts.join('.')) : ''}`, + operationId: operationId }; // If we have at least one required parameter... if (def.params.filter(param => !param.hasOwnProperty('defaultValue').length > 0)) { @@ -231,6 +236,9 @@ const generateOpenAPISpec = (definitions, plugin, server, origin, identifier) => if (plugin.contact) { const {name, url, email} = plugin.contact; + if (name || url || email) { + spec.info.contact = {}; + } name && (spec.info.contact.name = name); url && (spec.info.contact.url = url); email && (spec.info.contact.email = email); @@ -253,9 +261,6 @@ const wellKnowns = { plugin.name = rawPlugin.name || '(No name provided)'; plugin.description = rawPlugin.description || '(No description provided)'; plugin.version = rawPlugin.version || 'local'; - plugin.enabled = rawPlugin.hasOwnProperty('enabled') - ? rawPlugin.enabled - : false; plugin.image_url = rawPlugin.image_url || null; if ( typeof plugin.image_url === 'string' && @@ -269,6 +274,13 @@ const wellKnowns = { plugin.forModel.description = plugin.forModel.description || plugin.description; plugin.termsOfService = rawPlugin.termsOfService || null; + if ( + typeof plugin.termsOfService === 'string' && + plugin.termsOfService.startsWith('/') + ) { + plugin.termsOfService = `${origin}${plugin.termsOfService}`; + } + plugin.contact = rawPlugin.contact || {}; const checkPluginValue = (name, isRequired = false) => { @@ -289,10 +301,6 @@ const wellKnowns = { } }; - if (typeof plugin.enabled !== 'boolean') { - throw new Error(`plugin.enabled must be a boolean`); - } - [ 'name', 'description', @@ -331,14 +339,23 @@ const wellKnowns = { const AIPlugin = {}; AIPlugin.schema_version = 'v1'; - AIPlugin.name_for_human = plugin.name; - AIPlugin.name_for_model = plugin.forModel.name; - AIPlugin.description_for_human = plugin.description; - AIPlugin.description_for_model = plugin.forModel.description; + AIPlugin.name_for_human = plugin.name.slice(0, 20); + AIPlugin.name_for_model = plugin.forModel.name.slice(0, 50); + AIPlugin.name_for_model = AIPlugin.name_for_model.replace(/[^A-Z0-9_]+/gi, '_'); + AIPlugin.name_for_model = AIPlugin.name_for_model.replace(/^_+/gi, ''); + AIPlugin.name_for_model = AIPlugin.name_for_model.replace(/_+$/gi, ''); + AIPlugin.description_for_human = plugin.description.slice(0, 100); + AIPlugin.description_for_model = plugin.forModel.description.slice(0, 8000); + AIPlugin.auth = { + type: 'none' + }; AIPlugin.api = { type: 'openapi', url: `${origin}/.well-known/openapi.yaml` }; + AIPlugin.logo_url = `${origin}/logo.png`; + AIPlugin.contact_email = `noreply@${origin.replace(/^https?\:\/\/(.*)(:\d+)/gi, '$1')}`; + AIPlugin.legal_info_url = `${origin}/tos.txt`; if (plugin.image_url) { AIPlugin.logo_url = plugin.image_url; } diff --git a/package.json b/package.json index d571a95..74e2a23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "functionscript", - "version": "2.10.0", + "version": "2.10.6", "description": "An API gateway and framework for turning functions into web services", "author": "Keith Horwood ", "main": "index.js", diff --git a/tests/gateway/tests.js b/tests/gateway/tests.js index e0dc59c..7571227 100644 --- a/tests/gateway/tests.js +++ b/tests/gateway/tests.js @@ -5594,7 +5594,7 @@ module.exports = (expect) => { expect(result).to.exist; expect(result.schema_version).to.equal('v1'); expect(result.name_for_human).to.equal('(No name provided)'); - expect(result.name_for_model).to.equal('(No name provided)'); + expect(result.name_for_model).to.equal('No_name_provided'); expect(result.description_for_human).to.equal('(No description provided)'); expect(result.description_for_model).to.equal('(No description provided)'); expect(result.api).to.exist; @@ -5627,7 +5627,7 @@ module.exports = (expect) => { expect(result.paths['/my_function/']).to.exist; expect(result.paths['/my_function/'].post).to.exist; expect(result.paths['/my_function/'].post.description).to.equal('My function'); - expect(result.paths['/my_function/'].post.operationId).to.equal('service.localhost.my_function'); + expect(result.paths['/my_function/'].post.operationId).to.equal('service_localhost_my_function'); expect(result.paths['/my_function/'].post.requestBody).to.exist; expect(result.paths['/my_function/'].post.requestBody.content).to.exist; expect(result.paths['/my_function/'].post.requestBody.content['application/json']).to.exist;