Skip to content

Commit

Permalink
feat(typings): script to generate declaration file for current API (#313
Browse files Browse the repository at this point in the history
)

This is the first PR that begins to address #249
  • Loading branch information
silasbw committed Aug 28, 2018
1 parent f0cd4c7 commit a8e399c
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"check-coverage": "istanbul check-coverage",
"coverage": "istanbul cover ./node_modules/.bin/_mocha && istanbul report cobertura",
"docs": "node scripts/docs.js --builtins",
"typings": "node scripts/typings.js --spec lib/specs/swagger-1.10.json.gz --output typings/index.d.ts",
"jsdoc": "jsdoc -d doc lib",
"lint": "eslint-godaddy examples/ lib/ scripts/ test/",
"release": "standard-version --tag-prefix=''",
Expand Down
14 changes: 14 additions & 0 deletions scripts/templates/ts-interface.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
interface {{tsName}} {
// Path templating
{{#callable}}
({{name}}: string): {{type}}
{{/callable}}
// Sub-paths
{{#properties}}
'{{name}}': {{type}}
{{/properties}}
// Calls
{{#calls}}
{{method}}(options ?: {{parameterType}}): {{returnType}}
{{/calls}}
}
48 changes: 48 additions & 0 deletions scripts/templates/ts-namespace.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
declare namespace KubernetesClient {
const Client{{clientSuffix}}: ApiClient
const config: Configuration

interface AuthorizationConfiguration {
bearer?: string;
user?: {
username: string;
password: string;
}
}

interface ClientConfiguration {
url: string;
ca?: string;
key?: string;
auth?: AuthorizationConfiguration;
namespace?: string;
insecureSkipTlsVerify: boolean;
}

interface ClusterConfiguration {
url: string;
ca: string;
key?: string;
auth: AuthorizationConfiguration;
namespace?: string;
insecureSkipTlsVerify?: boolean;
}

interface Configuration {
fromKubeconfig(kubeconfig?: any, currentContext?: string): ClientConfiguration;
loadKubeconfig(cfgPath?: string): any;
getInCluster() : ClusterConfiguration;
}

{{#interfaces}}
{{> tsInterface }}
{{/interfaces}}

interface ApiClient {
new(options: any): Api
}
}

declare module "kubernetes-client" {
export = KubernetesClient
}
123 changes: 123 additions & 0 deletions scripts/typings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#!/usr/bin/env node
'use strict';
/* eslint-disable no-sync, no-console */

const fs = require('fs');
const mustache = require('mustache');
const path = require('path');
const zlib = require('zlib');

const Client = require('..').Client;

/**
* Get the typescript interface name for a fluent-openapi component.
* @param {object} component - fluent-openapi Component.
* @returns {string} TypeScript interface name
*/
function interfaceName(component) {
// Root doesn't have splits
if (component.splits.length === 0) {
return 'Api';
}

//
// Replace '.'s with '_'s and CamelCase.
//
return component.splits
.map(split => split.replace(/\./g, '_').replace(/./, first => first.toUpperCase()))
.join('');
}

function walk(component, interfaces) {
const properties = [];
let callable = null;

if (component.children.length) {
component.children.forEach(child => {
const type = walk(component[child], interfaces);
properties.push({ name: child, type });
});
}
if (component.parameter) {
const type = walk(component('name'), interfaces);
callable = { name: 'name', type };
}

const calls = [
'get',
'getStream',
'delete',
'patch',
'post',
'put'
].filter(call => call in component)
.map(method => ({
method,
parameterType: 'any',
returnType: 'any'
}));

const tsName = interfaceName(component);
const templateOptions = {
callable,
calls,
properties,
tsName
};
if (!interfaces.find(iface => iface.tsName === tsName)) {
interfaces.push(templateOptions);
}

return tsName;
}

function main(args) {
let raw = fs.readFileSync(args.spec);
if (args.spec.endsWith('.gz')) {
raw = zlib.gunzipSync(raw);
}
const spec = JSON.parse(raw);
let clientSuffix = '';
if (spec.info.version) {
clientSuffix = spec.info.version.replace(/v/, '').split('.').slice(0, 2).join('_')
}
const interfaces = [];

const client = new Client({ spec, config: {}});
walk(client, interfaces);

const templateOptions = {
clientSuffix,
interfaces
};

const source = mustache.render(
fs.readFileSync(path.join(__dirname, 'templates/ts-namespace.mustache')).toString(),
templateOptions,
{
tsInterface: fs.readFileSync(path.join(__dirname, 'templates/ts-interface.mustache')).toString()
});

if (args.output) {
fs.writeFileSync(args.output, source);
} else {
console.log(source);
}
}

const argv = require('yargs')
.usage('Usage: $0 [options]')
.option('spec', {
alias: 's',
default: './lib/specs/swagger-1.10.json.gz',
describe: 'Swagger / OpenAPI specification'
})
.option('output', {
alias: 'o',
describe: 'Declaration file'
})
.strict()
.help()
.argv;

main(argv);

0 comments on commit a8e399c

Please sign in to comment.