@axway/amplify-central-github-extension
Version:
Create Amplify Central resources from Github API Management APIs
332 lines (303 loc) • 9.79 kB
JavaScript
const { Octokit } = require("@octokit/rest");
const SwaggerParser = require("@apidevtools/swagger-parser");
const yaml = require("js-yaml");
import isEmpty from 'lodash/isEmpty';
import { commitToFs, getIconData } from './utils';
import { loadConfig } from '@axway/amplify-cli-utils';
import https from 'https';
import HttpsProxyAgent from 'https-proxy-agent';
module.exports = class GithubService {
constructor(config) {
this.config = config || {};
let missingParam = [
'gitToken',
'gitUserName',
'repo',
'branch',
'environmentName',
].reduce((acc, cur) => {
if (!this.config[cur]) {
acc.push(cur);
return acc;
}
return acc;
}, []);
if (missingParam.length) {
console.log(`Missing required config: [${missingParam.join(', ')}]. Run 'axway central github-extension config set -h' to see a list of params`);
process.exit(1);
}
const network = loadConfig().get('network') || undefined;
if (network && !isEmpty(network)) {
// using strict ssl mode by default
const sslConfig = { rejectUnauthorized: network.strictSSL === undefined || network.strictSSL };
const proxyUrl = network.httpProxy || network.httpsProxy || network.proxy;
// using HttpsProxyAgent if proxy url configured, else - regular node agent.
let agent;
if (proxyUrl) {
let parsedProxyUrl;
try {
parsedProxyUrl = new URL(proxyUrl);
} catch {
throw new Error(`Could not parse provided network proxy url: ${proxyUrl}`);
}
agent = new HttpsProxyAgent({
host: parsedProxyUrl.hostname,
port: parsedProxyUrl.port,
protocol: parsedProxyUrl.protocol,
auth: `${parsedProxyUrl.username}:${parsedProxyUrl.password}`,
...sslConfig,
});
console.log(`Connecting using proxy settings protocol:${parsedProxyUrl.protocol}, host:${parsedProxyUrl.hostname}, port: ${parsedProxyUrl.port}, username: ${parsedProxyUrl.username}, rejectUnauthorized: ${sslConfig.rejectUnauthorized}`);
} else {
console.log(`Connecting using rejectUnauthorized: ${sslConfig.rejectUnauthorized}`);
agent = new https.Agent(sslConfig);
}
this.octokit = new Octokit({
auth: this.config.gitToken,
request: {agent}
});
} else {
this.octokit = new Octokit({
auth: this.config.gitToken,
});
}
}
async generateResources() {
const {
gitToken,
gitUserName,
repo,
branch,
icon,
environmentName,
outputDir = './resources'
} = this.config;
const github = {
owner: gitUserName,
repo,
branch,
};
let path = "";
let owner = github.owner;
let ref = github.commit
? github.commit
: github.tag
? github.tag
: github.branch
? github.branch
: "master";
console.log('Listing Github APIs');
return this.processRepo(owner, github.repo, ref, path, path);
}
async processRepo(owner, repo, ref, path, sourceRoot) {
await this.octokit.rateLimit.get();
var files = await this.octokit.repos
.getContent({
owner: owner,
repo: repo,
path: path,
ref: ref,
})
.catch((e) => {
console.error(e);
process.exit(1);
});
if (path) console.log(`Checking for resources on path: ${path}`);
for (var item of (files || { data: [] }).data) {
if (item.type === "dir") {
await this.processRepo(owner, repo, ref, item.path, sourceRoot);
}
if (item.type === "file") {
if (isOASExtension(item.name)) {
try {
var contents = await this.getContents(owner, repo, item, ref);
if (peek(item.name, contents)) {
var api = await SwaggerParser.bundle(item.download_url);
await this.writeAPI4Central(repo, item, api);
}
} catch (err) {
console.error(`Could not build resources for path ${item.path}. Error: ${err}`);
}
}
}
}
function isOASExtension(filename) {
if ([
"package-lock.json",
"package.json",
".travis.yml",
"fixup.yaml",
"jpdiff.yaml"
].includes(filename)) {
return false
}
var pattern = /\.(yaml|yml|json)$/i;
return pattern.test(filename);
}
function isYaml(filename) {
var patten = /\.(yaml|yml)$/i;
return patten.test(filename);
}
function isJSON(filename) {
var patten = /\.(json)$/i;
return patten.test(filename);
}
function peek(name, contents) {
let isOAS = false;
if (isYaml(name)) {
// see if root of YAML is swagger for v2.0 or openapi for v3.0
const obj = yaml.safeLoad(contents);
if (obj.swagger || obj.openapi) {
isOAS = true;
}
} else if (isJSON(name)) {
// see if root of JSON is XXX
const obj = JSON.parse(contents);
if (obj.swagger || obj.openapi) {
isOAS = true;
}
}
return isOAS;
}
}
async getContents(owner, repo, item, ref) {
console.log(`Downloading spec from ${item.path}`);
const result = await this.octokit.git.getBlob({ "owner": owner, "repo": repo, file_sha: item.sha })
return Buffer.from(result.data.content, 'base64').toString('utf8')
}
async writeAPI4Central(repo, item, api) {
const resources = [];
let attributes = {
repo: repo,
download_url: item.download_url,
sha: item.sha,
html_url: item.html_url,
};
let iconData = getIconData(this.config.icon);
const apiName = api.info.title + "-" + api.info.version;
const apiServiceName = `${apiName}`.toLowerCase().replace(/\W+/g, "-");
// APIService
const apiService = {
apiVersion: "v1alpha1",
kind: "APIService",
name: apiServiceName,
title: api.info.title,
attributes: attributes,
metadata: {
scope: {
kind: "Environment",
name: this.config.environmentName,
},
},
spec: {
description: api.info.description,
icon: iconData
},
};
resources.push(apiService);
// APIServiceRevision
const apiServiceRevisionName = apiServiceName; //`${api.info.title}-${api.info.version}`;
let type = "oas3";
if (api.swagger) {
type = "oas2";
}
const apiServiceRevision = {
apiVersion: "v1alpha1",
kind: "APIServiceRevision",
name: apiServiceName,
attributes: attributes,
metadata: {
scope: {
kind: "Environment",
name: this.config.environmentName,
},
},
spec: {
apiService: apiServiceName,
definition: {
type: type,
value: Buffer.from(JSON.stringify(api)).toString("base64"),
},
},
};
resources.push(apiServiceRevision);
let endpoints = [];
if (type === "oas2") {
endpoints = [{
host: api.host,
protocol: (api.schemes || []).includes('https') ? 'https': 'http',
port: (api.schemes || []).includes('https') ? 443: 80,
...(api.basePath ? { routing: { basePath: api.basePath } } : {})
}]
}
else {
for (const server of api.servers) {
endpoints.push(this.getEndpoint(server.url));
}
}
// APIServiceInstance
const apiServiceInstance = {
apiVersion: "v1alpha1",
kind: "APIServiceInstance",
name: apiServiceName,
attributes: attributes,
metadata: {
scope: {
kind: "Environment",
name: this.config.environmentName,
},
},
spec: {
apiServiceRevision: apiServiceRevisionName,
endpoint: endpoints
},
};
resources.push(apiServiceInstance);
// ConsumerInstance
const consumerInstance = {
apiVersion: "v1alpha1",
kind: "ConsumerInstance",
name: apiServiceName,
attributes: attributes,
metadata: {
scope: {
kind: "Environment",
name: this.config.environmentName,
},
},
spec: {
state: "PUBLISHED",
name: apiServiceName,
apiServiceInstance: apiServiceName,
subscription: {
enabled: false,
autoSubscribe: false,
},
},
};
resources.push(consumerInstance);
await commitToFs(consumerInstance, this.config.outputDir, resources)
}
getEndpoint(url) {
var endpoint = {
host: 'localhost',
protocol: 'https',
port: 443
};
try {
let parsedUrl = new URL(url);
endpoint.host = parsedUrl.hostname;
endpoint.protocol = parsedUrl.protocol.substring(0, parsedUrl.protocol.indexOf(':'));
if (parsedUrl.port) {
endpoint.port = parseInt(parsedUrl.port, 10);
} else {
delete endpoint.port;
}
endpoint.routing = { basePath: parsedUrl.pathname };
}
catch (error) {
endpoint.routing = { basePath: url};
}
return endpoint;
}
}