mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 14:19:58 +00:00
Headless CLI POC (#2258)
This commit is contained in:
parent
208aec6ec8
commit
f38bf20e13
@ -11,6 +11,7 @@ screenshots/
|
||||
**/webpack/
|
||||
**/bin/
|
||||
**/__fixtures__/
|
||||
**/__snapshots__/
|
||||
**/flow-typed/
|
||||
**/dist/
|
||||
**/.cache/
|
||||
|
@ -4,3 +4,4 @@
|
||||
**/__fixtures__/*
|
||||
**/flow-typed/*
|
||||
*.md
|
||||
**/__snapshots__/
|
||||
|
@ -14,6 +14,7 @@
|
||||
"clean": "lerna clean --yes && rimraf node_modules",
|
||||
"typecheck": "lerna run --parallel --stream typecheck",
|
||||
"test": "npm run lint && npm run typecheck && lerna run --stream --parallel test",
|
||||
"cli-start": "npm start --prefix packages/insomnia-cli",
|
||||
"app-start": "npm start --prefix packages/insomnia-app",
|
||||
"app-build": "npm run build --prefix packages/insomnia-app",
|
||||
"app-package": "npm run package --prefix packages/insomnia-app",
|
||||
|
16
packages/insomnia-cli/.babelrc
Normal file
16
packages/insomnia-cli/.babelrc
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "10"
|
||||
}
|
||||
}
|
||||
],
|
||||
"@babel/preset-flow"
|
||||
],
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-optional-chaining"]
|
||||
]
|
||||
}
|
14
packages/insomnia-cli/.flowconfig
Normal file
14
packages/insomnia-cli/.flowconfig
Normal file
@ -0,0 +1,14 @@
|
||||
[ignore]
|
||||
.*/node_modules/.*
|
||||
./.cache
|
||||
./dist
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
./flow-typed
|
||||
|
||||
[options]
|
||||
esproposal.optional_chaining=enable
|
||||
|
||||
[lints]
|
1
packages/insomnia-cli/.gitignore
vendored
Normal file
1
packages/insomnia-cli/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
dist
|
7
packages/insomnia-cli/.npmignore
Normal file
7
packages/insomnia-cli/.npmignore
Normal file
@ -0,0 +1,7 @@
|
||||
# Ignore everything by default
|
||||
# NOTE: NPM publish will never ignore package.json, package-lock.json, README, LICENSE, CHANGELOG
|
||||
# https://npm.github.io/publishing-pkgs-docs/publishing/the-npmignore-file.html
|
||||
*
|
||||
|
||||
# Don't ignore dist folder
|
||||
!dist/*
|
12
packages/insomnia-cli/README.md
Normal file
12
packages/insomnia-cli/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# insomnia-cli
|
||||
|
||||
### Usage
|
||||
```
|
||||
npm install -g insomnia-cli
|
||||
inso --version
|
||||
```
|
||||
|
||||
### Development
|
||||
- Bootstrap: `npm run bootstrap`
|
||||
- Start the compiler in watch mode: `npm run watch`
|
||||
- Run: `./bin/inso -v`
|
7
packages/insomnia-cli/__mocks__/openapi-2-kong.js
Normal file
7
packages/insomnia-cli/__mocks__/openapi-2-kong.js
Normal file
@ -0,0 +1,7 @@
|
||||
const mod = jest.requireActual('openapi-2-kong');
|
||||
|
||||
module.exports = {
|
||||
...mod,
|
||||
generate: jest.fn(),
|
||||
generateFromString: jest.fn(),
|
||||
};
|
3
packages/insomnia-cli/bin/inso
Executable file
3
packages/insomnia-cli/bin/inso
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('../dist/cli').go();
|
5
packages/insomnia-cli/flow-typed/commander.js
vendored
Normal file
5
packages/insomnia-cli/flow-typed/commander.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// @flow
|
||||
|
||||
declare module 'commander' {
|
||||
declare module.exports: *;
|
||||
}
|
5
packages/insomnia-cli/flow-typed/execa.js
vendored
Normal file
5
packages/insomnia-cli/flow-typed/execa.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// @flow
|
||||
|
||||
declare module 'execa' {
|
||||
declare module.exports: *;
|
||||
}
|
5
packages/insomnia-cli/flow-typed/get-bin-path.js
vendored
Normal file
5
packages/insomnia-cli/flow-typed/get-bin-path.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// @flow
|
||||
|
||||
declare module 'get-bin-path' {
|
||||
declare module.exports: *;
|
||||
}
|
1186
packages/insomnia-cli/flow-typed/jest.js
vendored
Normal file
1186
packages/insomnia-cli/flow-typed/jest.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
38
packages/insomnia-cli/flow-typed/openapi-2-kong.js
vendored
Normal file
38
packages/insomnia-cli/flow-typed/openapi-2-kong.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// @flow
|
||||
|
||||
declare type ConversionResultType = 'kong-declarative-config' | 'kong-for-kubernetes';
|
||||
|
||||
declare type DeclarativeConfigResult = {
|
||||
type: 'kong-declarative-config',
|
||||
label: string,
|
||||
documents: Object,
|
||||
warnings: Array<{
|
||||
severity: number,
|
||||
message: string,
|
||||
range: {
|
||||
/* TODO */
|
||||
},
|
||||
}>,
|
||||
};
|
||||
|
||||
declare type KongForKubernetesResult = {
|
||||
type: 'kong-for-kubernetes',
|
||||
label: string,
|
||||
documents: Array<Object>,
|
||||
warnings: Array<{
|
||||
severity: number,
|
||||
message: string,
|
||||
range: {
|
||||
/* TODO */
|
||||
},
|
||||
}>,
|
||||
};
|
||||
|
||||
declare type ConversionResult = DeclarativeConfigResult | KongForKubernetesResult;
|
||||
|
||||
declare module 'openapi-2-kong' {
|
||||
declare module.exports: {
|
||||
generate: (specPath: string, type: ConversionResultType, tags?: Array<string>) => Promise<ConversionResult>,
|
||||
generateFromString: (specStr: string, type: ConversionResultType, tags?: Array<string>) => Promise<ConversionResult>,
|
||||
};
|
||||
}
|
9
packages/insomnia-cli/flow-typed/yaml.js
vendored
Normal file
9
packages/insomnia-cli/flow-typed/yaml.js
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// @flow
|
||||
|
||||
declare module 'yaml' {
|
||||
declare module.exports: {
|
||||
stringify: Object => string,
|
||||
parse: string => Object,
|
||||
parseCST: string => Object,
|
||||
};
|
||||
}
|
7639
packages/insomnia-cli/package-lock.json
generated
Normal file
7639
packages/insomnia-cli/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
44
packages/insomnia-cli/package.json
Normal file
44
packages/insomnia-cli/package.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "insomnia-cli",
|
||||
"version": "0.0.1",
|
||||
"bin": {
|
||||
"inso": "bin/inso"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:snapshots": "npm run build && jest -u",
|
||||
"start": "npm run build:watch",
|
||||
"build": "rimraf dist && babel src --out-dir dist --ignore \"src/**/*.test.js\",\"src/**/*.test.js.snap\"",
|
||||
"build:watch": "npm run build -- --watch",
|
||||
"bootstrap": "npm run build",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"jest": {
|
||||
"testMatch": [
|
||||
"**/__tests__/**/*.test.js"
|
||||
],
|
||||
"verbose": false,
|
||||
"resetMocks": true,
|
||||
"resetModules": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.10.1",
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
|
||||
"@babel/preset-env": "^7.10.2",
|
||||
"@babel/preset-flow": "^7.10.1",
|
||||
"execa": "^4.0.2",
|
||||
"flow-bin": "^0.126.1",
|
||||
"get-bin-path": "^5.1.0",
|
||||
"husky": "^3.1.0",
|
||||
"jest": "^26.0.1",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^5.1.0",
|
||||
"openapi-2-kong": "^2.2.6",
|
||||
"yaml": "^1.10.0"
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Snapshot for "inso --help" 1`] = `
|
||||
"Usage: inso [options] [command]
|
||||
|
||||
A CLI for Insomnia!
|
||||
|
||||
Options:
|
||||
-v, --version output the version number
|
||||
-h, --help display help for command
|
||||
|
||||
Commands:
|
||||
generate Code generation utilities
|
||||
help [command] display help for command"
|
||||
`;
|
||||
|
||||
exports[`Snapshot for "inso -h" 1`] = `
|
||||
"Usage: inso [options] [command]
|
||||
|
||||
A CLI for Insomnia!
|
||||
|
||||
Options:
|
||||
-v, --version output the version number
|
||||
-h, --help display help for command
|
||||
|
||||
Commands:
|
||||
generate Code generation utilities
|
||||
help [command] display help for command"
|
||||
`;
|
||||
|
||||
exports[`Snapshot for "inso generate -h" 1`] = `
|
||||
"Usage: inso generate [options] [command]
|
||||
|
||||
Code generation utilities
|
||||
|
||||
Options:
|
||||
-h, --help display help for command
|
||||
|
||||
Commands:
|
||||
config [options] <filePath> Generate configuration from an api spec
|
||||
help [command] display help for command"
|
||||
`;
|
||||
|
||||
exports[`Snapshot for "inso generate config -h" 1`] = `
|
||||
"Usage: inso generate config [options] <filePath>
|
||||
|
||||
Generate configuration from an api spec
|
||||
|
||||
Options:
|
||||
-t, --type <value> the type of configuration to generate, options are
|
||||
[kubernetes, declarative]
|
||||
-o, --output <path> the output path
|
||||
-h, --help display help for command"
|
||||
`;
|
||||
|
||||
exports[`Snapshot for "inso help" 1`] = `
|
||||
"Usage: inso [options] [command]
|
||||
|
||||
A CLI for Insomnia!
|
||||
|
||||
Options:
|
||||
-v, --version output the version number
|
||||
-h, --help display help for command
|
||||
|
||||
Commands:
|
||||
generate Code generation utilities
|
||||
help [command] display help for command"
|
||||
`;
|
54
packages/insomnia-cli/src/__tests__/cli.test.js
Normal file
54
packages/insomnia-cli/src/__tests__/cli.test.js
Normal file
@ -0,0 +1,54 @@
|
||||
// @flow
|
||||
import * as cli from '../cli';
|
||||
import { generateConfig } from '../commands/generate';
|
||||
|
||||
jest.mock('../commands/generate');
|
||||
const originalError = console.error;
|
||||
|
||||
const initInso = () => {
|
||||
return (args: string): void => {
|
||||
const cliArgs = `node test ${args}`
|
||||
.split(' ')
|
||||
.map(t => t.trim())
|
||||
.filter(t => t);
|
||||
|
||||
// console.log('calling cli.go with: %o', cliArgs);
|
||||
return cli.go(cliArgs, true);
|
||||
};
|
||||
};
|
||||
|
||||
describe('cli', () => {
|
||||
let inso = initInso();
|
||||
beforeEach(() => {
|
||||
inso = initInso();
|
||||
(console: any).error = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(console: any).error = originalError;
|
||||
});
|
||||
|
||||
it('should error when required --type option is missing', () =>
|
||||
expect(() => inso('generate config file.yaml')).toThrowError());
|
||||
|
||||
it('should error when filePath is missing', () =>
|
||||
expect(() => inso('generate config -t declarative')).toThrowError());
|
||||
|
||||
it('should call generateConfig with undefined output argument', () => {
|
||||
inso('generate config -t declarative file.yaml');
|
||||
expect(generateConfig).toHaveBeenCalledWith({
|
||||
filePath: 'file.yaml',
|
||||
type: 'declarative',
|
||||
output: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should call generateConfig with all expected arguments', () => {
|
||||
inso('generate config -t kubernetes -o output.yaml file.yaml');
|
||||
expect(generateConfig).toHaveBeenCalledWith({
|
||||
filePath: 'file.yaml',
|
||||
type: 'kubernetes',
|
||||
output: 'output.yaml',
|
||||
});
|
||||
});
|
||||
});
|
29
packages/insomnia-cli/src/__tests__/inso-snapshot.test.js
Normal file
29
packages/insomnia-cli/src/__tests__/inso-snapshot.test.js
Normal file
@ -0,0 +1,29 @@
|
||||
// @flow
|
||||
import execa from 'execa';
|
||||
import { getBinPathSync } from 'get-bin-path';
|
||||
import * as packageJson from '../../package.json';
|
||||
|
||||
// MAKE SURE YOU BUILD THE PROJECT BEFORE RUNNING THESE TESTS.
|
||||
// These tests use the executable /bin/inso, which relies on /dist.
|
||||
|
||||
describe('Snapshot for', () => {
|
||||
it.each(['-h', '--help', 'help', 'generate -h', 'generate config -h'])(
|
||||
'"inso %s"',
|
||||
async args => {
|
||||
const { stdout } = await execa(getBinPathSync(), args.split(' '));
|
||||
expect(stdout).toMatchSnapshot();
|
||||
},
|
||||
30000,
|
||||
);
|
||||
});
|
||||
|
||||
describe('Inso version', () => {
|
||||
it.each(['-v', '--version'])(
|
||||
'inso %s should print version from package.json',
|
||||
async args => {
|
||||
const { stdout } = await execa(getBinPathSync(), args.split(' '));
|
||||
expect(stdout).toBe(packageJson.version);
|
||||
},
|
||||
30000,
|
||||
);
|
||||
});
|
36
packages/insomnia-cli/src/cli.js
Executable file
36
packages/insomnia-cli/src/cli.js
Executable file
@ -0,0 +1,36 @@
|
||||
// @flow
|
||||
import { ConversionTypeMap, generateConfig } from './commands/generate';
|
||||
import { getVersion, createCommand } from './util';
|
||||
|
||||
function makeGenerateCommand(exitOverride: boolean) {
|
||||
// inso generate
|
||||
const generate = createCommand(exitOverride, 'generate').description('Code generation utilities');
|
||||
|
||||
const conversionTypes = Object.keys(ConversionTypeMap).join(', ');
|
||||
|
||||
// inso generate config -t kubernetes config.yaml
|
||||
generate
|
||||
.command('config <filePath>')
|
||||
.description('Generate configuration from an api spec')
|
||||
.requiredOption(
|
||||
'-t, --type <value>',
|
||||
`the type of configuration to generate, options are [${conversionTypes}]`,
|
||||
)
|
||||
.option('-o, --output <path>', 'the output path')
|
||||
.action((filePath, opts) => generateConfig({ filePath, ...opts }));
|
||||
|
||||
return generate;
|
||||
}
|
||||
|
||||
export function go(args?: Array<string>, exitOverride?: boolean): void {
|
||||
if (!args) {
|
||||
args = process.argv;
|
||||
}
|
||||
|
||||
// inso -v
|
||||
createCommand(!!exitOverride)
|
||||
.version(getVersion(), '-v, --version')
|
||||
.description('A CLI for Insomnia!')
|
||||
.addCommand(makeGenerateCommand(!!exitOverride))
|
||||
.parse(args);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
// @flow
|
||||
import { ConversionTypeMap, generateConfig } from '../generate';
|
||||
import type { GenerateConfigOptions } from '../generate';
|
||||
import o2k from 'openapi-2-kong';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
jest.mock('openapi-2-kong');
|
||||
jest.mock('fs');
|
||||
|
||||
const base: GenerateConfigOptions = {
|
||||
filePath: 'file.yaml',
|
||||
type: 'kubernetes',
|
||||
output: undefined,
|
||||
};
|
||||
|
||||
describe('generateConfig()', () => {
|
||||
// make flow happy
|
||||
const mock = (mockFn: any) => mockFn;
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
it('should should not generate if type arg is invalid', async () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
|
||||
await generateConfig({ ...base, type: 'invalid' });
|
||||
|
||||
expect(o2k.generate).not.toHaveBeenCalled();
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'Config type "invalid" not unrecognized. Options are [kubernetes, declarative].',
|
||||
);
|
||||
});
|
||||
|
||||
it('should print conversion documents to console', async () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
mock(o2k.generate).mockResolvedValue({ documents: ['a', 'b'] });
|
||||
|
||||
await generateConfig(base);
|
||||
|
||||
expect(o2k.generate).toHaveBeenCalledWith(base.filePath, ConversionTypeMap[base.type]);
|
||||
expect(consoleSpy).toHaveBeenCalledWith('a\n---\nb\n');
|
||||
});
|
||||
|
||||
it('should write converted documents to file system', async () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
mock(o2k.generate).mockResolvedValue({ documents: ['a', 'b'] });
|
||||
|
||||
await generateConfig({ ...base, output: 'output.yaml' });
|
||||
|
||||
expect(o2k.generate).toHaveBeenCalledWith(base.filePath, ConversionTypeMap[base.type]);
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(path.resolve('output.yaml'), 'a\n---\nb\n');
|
||||
expect(consoleSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
48
packages/insomnia-cli/src/commands/generate.js
Normal file
48
packages/insomnia-cli/src/commands/generate.js
Normal file
@ -0,0 +1,48 @@
|
||||
// @flow
|
||||
import o2k from 'openapi-2-kong';
|
||||
import YAML from 'yaml';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
export const ConversionTypeMap: { [string]: ConversionResultType } = {
|
||||
kubernetes: 'kong-for-kubernetes',
|
||||
declarative: 'kong-declarative-config',
|
||||
};
|
||||
|
||||
export type GenerateConfigOptions = {|
|
||||
filePath: string,
|
||||
type: $Keys<typeof ConversionTypeMap>,
|
||||
output?: string,
|
||||
|};
|
||||
|
||||
function validateOptions({ type }: GenerateConfigOptions): boolean {
|
||||
if (!ConversionTypeMap[type]) {
|
||||
const conversionTypes = Object.keys(ConversionTypeMap).join(', ');
|
||||
console.log(`Config type "${type}" not unrecognized. Options are [${conversionTypes}].`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function generateConfig(options: GenerateConfigOptions): Promise<void> {
|
||||
if (!validateOptions(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { type, output, filePath } = options;
|
||||
|
||||
const result = await o2k.generate(filePath, ConversionTypeMap[type]);
|
||||
|
||||
const yamlDocs = result.documents.map(d => YAML.stringify(d));
|
||||
|
||||
// Join the YAML docs with "---" and strip any extra newlines surrounding them
|
||||
const document = yamlDocs.join('\n---\n').replace(/\n+---\n+/g, '\n---\n');
|
||||
|
||||
if (output) {
|
||||
const fullOutputPath = path.resolve(output);
|
||||
fs.writeFileSync(fullOutputPath, document);
|
||||
} else {
|
||||
console.log(document);
|
||||
}
|
||||
}
|
19
packages/insomnia-cli/src/util.js
Normal file
19
packages/insomnia-cli/src/util.js
Normal file
@ -0,0 +1,19 @@
|
||||
// @flow
|
||||
import commander from 'commander';
|
||||
import * as packageJson from '../package.json';
|
||||
|
||||
export function createCommand(exitOverride: boolean, cmd?: string) {
|
||||
const command = new commander.Command(cmd)
|
||||
.storeOptionsAsProperties(false)
|
||||
.passCommandToAction(false);
|
||||
|
||||
if (exitOverride) {
|
||||
return command.exitOverride();
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
export function getVersion() {
|
||||
return packageJson.version;
|
||||
}
|
Loading…
Reference in New Issue
Block a user