core/routemgmt/deleteApi/deleteApi.js (162 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ /** * * Delete an API Gateway to action mapping document from the database: * https://docs.cloudant.com/document.html#delete * * Parameters (all as fields in the message JSON object) * gwUrlV2 Required when accesstoken is provided. The V2 API Gateway base path (i.e. http://gw.com) * gwUrl Required. The API Gateway base path (i.e. http://gw.com) * gwUser Optional. The API Gateway authentication * gwPwd Optional. The API Gateway authentication * __ow_user Optional. Set to the authenticated API authors's namespace when valid authentication is supplied. * namespace Required if __ow_user not specified. Namespace of API author * accesstoken Optional. Dynamic API GW auth. Overrides gwUser/gwPwd * spaceguid Optional. Namespace unique id. * tenantInstance Optional. Instance identifier used when creating the specific API GW Tenant * basepath Required. Base path or API name of the API * relpath Optional. Delete just this relative path from the API. Required if operation is specified * operation Optional. Delete just this relpath's operation from the API. * * NOTE: The package containing this action will be bound to the following values: * gwUrl, gwAuth * As such, the caller to this action should normally avoid explicitly setting * these values **/ var utils = require('./utils.js'); var utils2 = require('./apigw-utils.js'); var _ = require('lodash'); function main(message) { //console.log('message: '+JSON.stringify(message)); // ONLY FOR TEMPORARY/LOCAL DEBUG; DON'T ENABLE PERMANENTLY var badArgMsg = validateArgs(message); if (badArgMsg) { return Promise.reject(utils2.makeErrorResponseObject(badArgMsg, (message.__ow_method != undefined))); } var gwInfo = { gwUrl: message.gwUrl, }; if (message.gwUser && message.gwPwd) { gwInfo.gwAuth = Buffer.from(message.gwUser+':'+message.gwPwd,'ascii').toString('base64'); } // Set the User-Agent header value if (message.__ow_headers && message.__ow_headers['user-agent']) { utils2.setSubUserAgent(message.__ow_headers['user-agent']); } // Set namespace override if provided message.namespace = message.__ow_user || message.namespace; var tenantInstance = message.tenantInstance || 'openwhisk'; // This can be invoked as either a web action or as a normal action var calledAsWebAction = message.__ow_method !== undefined; // Log parameter values console.log('GW URL : '+message.gwUrl); console.log('GW URL V2 : '+message.gwUrlV2); console.log('GW User : '+utils.confidentialPrint(message.gwUser)); console.log('GW Pwd : '+utils.confidentialPrint(message.gwPwd)); console.log('__ow_user : '+message.__ow_user); console.log('namespace : '+message.namespace); console.log('tenantInstance: '+message.tenantInstance+' / '+tenantInstance); console.log('accesstoken : '+message.accesstoken); console.log('spaceguid : '+message.spaceguid); console.log('basepath/name : '+message.basepath); console.log('relpath : '+message.relpath); console.log('operation : '+message.operation); console.log('calledAsWebAction: '+calledAsWebAction); // If no relpath (or relpath/operation) is specified, delete the entire API var deleteEntireApi = !message.relpath; if (message.accesstoken) { // Delete an API route // 1. Use the spaceguid and basepath to obtain the API from the API GW // 2. If a relpath or relpath/operation is specified (i.e. delete subset of API) // a. Remove that section from the API config // b. Update API GW with updated API config // 3. If relpath or replath/operation is NOT specified (i.e. delete entire API) // a. Delete entire API from API GW gwInfo.gwUrl = message.gwUrlV2; gwInfo.gwAuth = message.accesstoken; return utils2.getApis(gwInfo, message.spaceguid, message.basepath) .then(function(endpointDocs) { console.log('Got '+endpointDocs.length+' APIs'); if (endpointDocs.length === 0) { console.log('No API found for namespace '+message.namespace + ' with basePath '+ message.basepath); return Promise.reject('API \''+message.basepath+'\' does not exist.'); } else if (endpointDocs.length > 1) { console.error('Multiple APIs found for namespace '+message.namespace+' with basepath/apiname '+message.basepath); } return Promise.resolve(endpointDocs[0]); }) .then(function(endpointDoc) { console.log('Got API'); if (deleteEntireApi) { console.log('Removing entire API '+message.basepath+' from API GW'); return utils2.deleteApiFromGateway(gwInfo, message.spaceguid, endpointDoc.artifact_id); } else { console.log('Removing path '+message.relpath+' with operation '+message.operation+' from API '+message.basepath); var endpointToRemove = { gatewayMethod: message.operation, gatewayPath: message.relpath }; var swaggerOrErrMsg = utils2.removeEndpointFromSwaggerApi(endpointDoc.open_api_doc, endpointToRemove); if (typeof swaggerOrErrMsg === 'string' ) { return Promise.reject(swaggerOrErrMsg); } if (_.isEmpty(swaggerOrErrMsg.paths)) { console.log('After path/operation removal, no paths exist in API; so removing entire API '+message.basepath+' from API GW'); return utils2.deleteApiFromGateway(gwInfo, message.spaceguid, endpointDoc.artifact_id); } return utils2.addApiToGateway(gwInfo, message.spaceguid, swaggerOrErrMsg, endpointDoc.artifact_id); } }) .then(function() { console.log('deleteApi success'); return Promise.resolve(utils2.makeResponseObject({}, calledAsWebAction)); }) .catch(function(reason) { var rejmsg = 'API deletion failure: ' + JSON.parse(utils2.makeJsonString(reason)); // Avoid unnecessary JSON escapes console.error(rejmsg); return Promise.reject(utils2.makeErrorResponseObject(rejmsg, calledAsWebAction)); }); } else { // Delete an API route // 1. Get the tenant ID associated with the specified namespace and optional tenant instance // 2. Obtain the tenantId/basepath/apiName associated API configuration from the API GW // 3. If a relpath or relpath/operation is specified (i.e. delete subset of API) // a. Remove that section from the API config // b. Update API GW with updated API config // 4. If relpath or replath/operation is NOT specified (i.e. delete entire API) // a. Delete entire API from API GW var tenantId; return utils.getTenants(gwInfo, message.namespace, tenantInstance) .then(function(tenants) { // If a non-empty tenant array was returned, pick the first one from the list if (tenants.length === 0) { console.error('No Tenant found for namespace '+message.namespace); return Promise.reject('No Tenant found for namespace '+message.namespace); } else if (tenants.length > 1 ) { console.error('Multiple tenants found for namespace '+message.namespace+' and tenant instance '+tenantInstance); return Promise.reject('Internal error. Multiple API Gateway tenants found for namespace '+message.namespace+' and tenant instance '+tenantInstance); } console.log('Got a tenant: '+JSON.stringify(tenants[0])); tenantId = tenants[0].id; return Promise.resolve(tenants[0].id); }) .then(function(tenantId) { console.log('Got Tenant ID: '+tenantId); return utils.getApis(gwInfo, tenantId, message.basepath); }) .then(function(apis) { console.log('Got '+apis.length+' APIs'); if (apis.length === 0) { console.log('No APIs found for namespace '+message.namespace+' with basepath/apiname '+message.basepath); return Promise.reject('API \''+message.basepath+'\' does not exist.'); } else if (apis.length > 1) { console.error('Multiple APIs found for namespace '+message.namespace+' with basepath/apiname '+message.basepath); Promise.reject('Internal error. Multiple APIs found for namespace '+message.namespace+' with basepath '+message.basepath); } return Promise.resolve(apis[0]); }) .then(function(gwApi) { if (deleteEntireApi) { console.log('Removing entire API '+gwApi.basePath+' from API GW'); return utils.deleteApiFromGateway(gwInfo, gwApi.id); } else { console.log('Removing path '+message.relpath+'; operation '+message.operation+' from API '+gwApi.basePath); var swaggerApi = utils.generateSwaggerApiFromGwApi(gwApi); var endpoint = { gatewayMethod: message.operation, gatewayPath: message.relpath }; var swaggerOrErrMsg = utils.removeEndpointFromSwaggerApi(swaggerApi, endpoint); if (typeof swaggerOrErrMsg === 'string' ) { return Promise.reject(swaggerOrErrMsg); } return utils.addApiToGateway(gwInfo, gwApi.tenantId, swaggerOrErrMsg, gwApi.id); } }) .then(function() { console.log('deleteApi success'); return Promise.resolve(utils2.makeResponseObject({}, calledAsWebAction)); }) .catch(function(reason) { var rejmsg = 'API deletion failure: ' + JSON.parse(utils2.makeJsonString(reason)); // Avoid unnecessary JSON escapes console.error(rejmsg); return Promise.reject(utils2.makeErrorResponseObject(rejmsg, calledAsWebAction)); }); } } function validateArgs(message) { var tmpdoc; if(!message) { console.error('No message argument!'); return 'Internal error. A message parameter was not supplied.'; } if (!message.gwUrl && !message.gwUrlV2) { return 'gwUrl is required.'; } if (!message.__ow_user && !message.namespace) { return 'Invalid authentication.'; } if (!message.basepath) { return 'basepath is required.'; } if (!message.relpath && message.operation) { return 'When specifying an operation, the path is required.'; } if (message.operation) { message.operation = message.operation.toLowerCase(); } return ''; } module.exports.main = main;