This code converts an OpenAPI specification into a format compatible with Google Discovery, facilitating integration with Google APIs.
npm run import -- "get rpc from spec"
var url = require('url')
var util = require('util')
var importer = require('../Core')
var {request} = importer.import("http request")
function getRpcFromSpec(spec, req, base) {
if(req && req.request)
req = req.request.bind(req)
base = spec.baseUrl || base;
var GoogleSpec = Object.keys(spec.resources || {}).reduce((obj, key) => {
obj[key] = Object.keys(spec.resources[key].methods || {}).reduce((o, k) => {
spec.resources[key].methods[k].parameters2 = spec.parameters
o[k] = assignAndRequest.bind(spec,
base,
spec.resources[key].methods[k],
req || request);
return o;
}, {})
// combine parent parameters with child paramters
Object.assign(obj[key], getRpcFromSpec(spec.resources[key], req, base))
return obj;
}, {})
var version = ((spec.info || {}).version || '1').split('.')[0]
// convert stupid OpenAPI to Google Discovery format
var OpenAPI = Object.keys(spec.paths || {}).reduce((obj, key) => {
var method = {
path: '',
parameters2: {}
}
var keys = key.replace(/^\/|\/$/ig, '').split('/')
.reduce((keylist, k) => {
if(k == 'json') {
method.parameters2['format'] = {
"enum": [
"json"
],
"type": "string",
"enumDescriptions": [
"Responses with Content-Type of application/json"
],
"location": "path",
"description": "Data format for response.",
"default": "json"
}
method.path += '/{format}'
} else if (k == 'v' + version) {
method.parameters2['version'] = {
"enum": [
"json"
],
"type": "string",
"location": "path",
"default": k
}
method.path += '/{version}'
} else if (k == 'api' || k == '@' || k.length < 0) {
method.path += '/' + k
} else {
keylist[keylist.length] = k
method.path += '/' + k
}
return keylist
}, [])
var currentPath = obj
keys.forEach(k => {
if(!currentPath[k]) currentPath[k] = {}
currentPath = currentPath[k]
})
Object.keys(spec.paths[key]).forEach(k => {
var parameters = (spec.paths[key][k].parameters || [])
.reduce((o, p) => {
o[p.name] = {
location: p.in,
type: p.schema ? p.schema['$ref'] || p.schema.type || 'string' : 'string',
description: p.description,
required: p.required
}
return o
}, {})
var methodSpec = Object.assign({httpMethod: k.toUpperCase(), parameters: parameters}, method)
var requestFunc = assignAndRequest.bind(spec,
spec.paths[key][k].servers[0].url,
methodSpec,
req || request);
currentPath[k] = requestFunc
})
return obj
}, GoogleSpec)
return OpenAPI
}
function assignAndRequest(base, resource, request, input) {
// TODO: get path parameters
var path = getResourceParameters(resource, input, 'path')
var address = `${base}${resource.path.replace(/\{(.*?)\}/ig, ($0, $1) => {
if(!path[$1]) {
throw new Error(`path parameter ${$1} not defined!`);
}
return path[$1];
})}`;
// TODO: move this to polyfills
var location = url.parse(address)
var params = Object.assign(
getResourceParameters(resource, input, 'query'),
location.search
? querystring.parse((/\?(.*)/ig).exec(location.search)[1])
: {});
//console.log(`requesting ${address} ${JSON.stringify(params)}`);
var data = getResourceParameters(resource, input, 'body')
if(Object.values(data).length === 0) data = null
var finalURL = address.replace(/\?.*$/ig, '') + '?' + querystring.stringify(params)
console.log('Requesting: ' + finalURL)
return request({
method: resource.httpMethod,
url: finalURL,
data: data,
body: JSON.stringify(input.resource),
params: params
})
}
function getResourceParameters(resource, input, type) {
var paramters = {}
Object.assign(paramters, resource.parameters2)
Object.assign(paramters, resource.parameters)
return Object.keys(paramters)
.filter(k => paramters[k].location === type)
.reduce((obj, key) => {
if(paramters[key].required
&& (!input || typeof input[key] === 'undefined')) {
throw new Error(`required field ${key} not defined!`);
}
if(typeof input[key] !== 'undefined')
obj[key] = input[key];
return obj;
}, {})
}
module.exports = getRpcFromSpec;
const { url } = require('url');
const { querystring } = require('querystring');
const importer = require('../Core');
const { request } = importer.import('http request');
/**
* Converts spec to Google Discovery format.
* @param {Object} spec - API specification.
* @param {Object} req - Request object.
* @param {string} base - Base URL.
* @returns {Object} OpenAPI in Google Discovery format.
*/
function getRpcFromSpec(spec, req, base) {
// Check if req is a function and bind it to the request object
if (req && req.request) {
req = req.request.bind(req);
}
// Set base URL
base = spec.baseUrl || base;
// Reduce spec to Google Discovery format
return Object.keys(spec.resources || {}).reduce((obj, key) => {
obj[key] = Object.keys(spec.resources[key].methods || {}).reduce((o, k) => {
spec.resources[key].methods[k].parameters2 = spec.parameters;
o[k] = assignAndRequest.bind(
spec,
base,
spec.resources[key].methods[k],
req || request
);
return o;
}, {});
// Combine parent parameters with child parameters
Object.assign(obj[key], getRpcFromSpec(spec.resources[key], req, base));
return obj;
}, {});
}
/**
* Assigns request parameters and makes a request.
* @param {Object} spec - API specification.
* @param {Object} resource - Resource object.
* @param {Object} request - Request function.
* @param {Object} input - Input object.
* @returns {Promise} Request promise.
*/
function assignAndRequest(spec, resource, request, input) {
try {
// Get path parameters
const pathParams = getResourceParameters(resource, input, 'path');
const address = `${resource.base}${resource.path.replace(
/\{(.*?)\}/ig,
(match, param) => {
if (!pathParams[param]) {
throw new Error(`Path parameter ${param} not defined!`);
}
return pathParams[param];
}
)}`;
// Parse URL and query parameters
const location = url.parse(address);
const queryParams = getResourceParameters(resource, input, 'query');
const params = Object.assign(queryParams, location.search? querystring.parse(location.search.slice(1)) : {});
// Get body parameters
const bodyParams = getResourceParameters(resource, input, 'body');
const data = bodyParams.length === 0? null : bodyParams;
// Construct final URL
const finalURL = `${address}?${querystring.stringify(params)}`;
// Log request
console.log('Requesting:', finalURL);
// Make request
return request({
method: resource.httpMethod,
url: finalURL,
data: data? JSON.stringify(input.resource) : data,
params: params,
});
} catch (error) {
// Handle errors
console.error(error);
throw error;
}
}
/**
* Gets resource parameters.
* @param {Object} resource - Resource object.
* @param {Object} input - Input object.
* @param {string} type - Parameter type (path, query, or body).
* @returns {Object} Resource parameters.
*/
function getResourceParameters(resource, input, type) {
const parameters = {...resource.parameters2,...resource.parameters };
return Object.keys(parameters)
.filter((k) => parameters[k].location === type)
.reduce((obj, key) => {
if (parameters[key].required &&!input[key]) {
throw new Error(`Required field ${key} not defined!`);
}
if (input[key]) obj[key] = input[key];
return obj;
}, {});
}
module.exports = getRpcFromSpec;
This code snippet focuses on transforming an OpenAPI specification into a format compatible with Google Discovery.
Here's a breakdown:
Imports:
getRpcFromSpec
Function:
spec
), an optional request object (req
), and a base URL (base
).assignAndRequest
) that handles the actual request based on the provided base URL, method details, and request object.OpenAPI to Google Discovery Conversion:
method
object for each path, including path details and parameters.In essence, this code acts as a translator, converting an OpenAPI specification into a format suitable for use with Google Discovery, enabling seamless integration with Google APIs.