Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server cli #92

Open
wants to merge 18 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"test:jest": "jest",
"test:no-system": "yarn test:jest --testMatch \"<rootDir>/test/!(system/)**/?(*.)test.(t|j)s?(x)\"",
"ci-test": "yarn checker:types && yarn transpile && yarn test:no-system",
"init-db": "node ./scripts/initDb.js"
"init-db": "node ./scripts/initDb.js",
"oap-cli": "node ./packages/server-cli/lib/index.js"
},
"husky": {
"hooks": {
Expand Down
8 changes: 5 additions & 3 deletions packages/logger/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,18 @@ export function openapiLogger(options: LoggerOptions = {}) {
});
return logger;
}
// Since we're using splat we have to create placeholders for the arguments to go into
// TODO: Note that string interpolation with console.log won't work (E.g. console.log("%s", "test") will print "%stest")
function createPlaceholders(args) {
return new Array(args.length).fill('%s').join(' ');
}

/**
* Replaces the console.log type methods with our own logger methods.
* It's not recommended to use console methods to print. Use the logger variable itself to log messages.
* However, this method is useful when external packages have console.log(...) calls inside of them.
*/
export function overrideConsoleLogger(aLogger) {
// Since we're using splat we have to create placeholders for the arguments to go into
// TODO: Note that string interpolation with console.log won't work (E.g. console.log("%s", "test") will print "%stest")
const createPlaceholders = args => new Array(args.length).fill('%s').join(' ');
Object.keys(aLogger.levels).forEach(level => {
console[level] = (...args) => aLogger[level](createPlaceholders(args), ...args);
});
Expand Down
1 change: 1 addition & 0 deletions packages/server-cli/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@openapi-platform/build-util').jest.settings();
12 changes: 12 additions & 0 deletions packages/server-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "@openapi-platform/server-cli",
"version": "1.0.0-alpha.0",
"main": "./lib/index.js",
"types": "./src/index.ts",
"license": "Apache-2",
"private": false,
"dependencies": {
"@types/commander": "^2.12.2",
"commander": "^2.19.0"
}
}
87 changes: 87 additions & 0 deletions packages/server-cli/src/add.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { client, socket } from './client';
import { config } from './config';
import { specBuilder, configBuilder } from './itemBuilder';
import { logger } from './logger';

/**
* Creates an item in the backend.
* Manges Server client and closes connection afterwards to avoid the program not finishing
*/
async function createItem(serviceName, data) {
const response = await client
.service(serviceName)
.create(data)
.catch(err => {
if (err.name === 'Timeout') {
logger.error(`Unable to connect to server on port ${config.get('server.port')}`);
} else {
logger.error(err);
}
});
socket.disconnect();
return response;
}

/**
* Switches the type being inserted based on command line inputs
*/
async function addItem(type: string, data: string) {
switch (type.toLowerCase()) {
case 'specification':
await addSpec(data);
break;

case 'sdkconfig':
await addConfig(data);
break;
default:
logger.error(
`Invalid type '${type}'. Supported types: 'specification', 'sdkconfig'`,
);
break;
}
}

async function addSpec(data: string) {
logger.info('Adding specification');
if (data === '') {
data = await specBuilder();
}
const response = await createItem('specifications', data);
logger.info(response);
}

async function addConfig(data: string) {
logger.info('Adding SDK Config');
if (data === '') {
data = await configBuilder();
}
const response = await createItem('sdkConfigs', data);
logger.info(response);
}

export function addItems(type: string) {
const stdin = process.stdin;

if (!stdin.isTTY) {
stdin.resume();
stdin.setEncoding('utf8');

stdin.on('data', chunk => {
const data = JSON.parse(chunk);
if (isArray(data)) {
data.forEach(element => {
addItem(type, element);
});
} else {
addItem(type, data);
}
});
} else {
addItem(type, '');
}
}

function isArray(what) {
return Object.prototype.toString.call(what) === '[object Array]';
}
103 changes: 103 additions & 0 deletions packages/server-cli/src/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Spec, SdkConfig, HasId } from '@openapi-platform/model';
import { generateSdk } from '@openapi-platform/openapi-sdk-gen-client';

import { client, socket } from './client';
import { config } from './config';
import { logger } from './logger';

export async function buildSpecs(specId: string, sdkConfigs: string[]) {
let specs: Array<HasId<Spec>>;
if (specId === '*') {
specs = await client
.service('specifications')
.find({
query: {
$sort: {
createdAt: 1,
},
},
})
.catch(err => {
if (err.name === 'Timeout') {
logger.error(
`Unable to connect to server on port ${config.get('server.port')}`,
);
} else {
logger.error(err);
}
});
} else {
const spec: HasId<Spec> = await client
.service('specifications')
.get(specId)
.catch(err => {
switch (err.name) {
case 'NotFound':
logger.error(err.message);
break;
case 'Timeout':
logger.error(
`Unable to connect to server on port ${config.get('server.port')}`,
);
break;
default:
logger.error(err);
break;
}
});
specs = [spec];
}
if (specs.length > 0) {
for (const spec of specs) {
await buildSpecConfigs(spec, sdkConfigs);
}
}
}

async function buildSpecConfigs(spec: HasId<Spec>, sdkConfigIds: string[]) {
const sdkConfigs: Array<HasId<SdkConfig>> = await client
.service('sdkConfigs')
.find({
query: {
$sort: {
createdAt: 1,
},
specId: spec.id,
},
})
.catch(err => {
if (err.name === 'Timeout') {
logger.error(`Unable to connect to server on port ${config.get('server.port')}`);
} else {
logger.error(err);
}
});

for (const sdkConfig of sdkConfigs) {
if (sdkConfigIds.indexOf(sdkConfig.id.toString()) >= 0 || sdkConfigIds[0] === '*') {
try {
logger.info(await generateSdk(logger, spec, sdkConfig));
logger.info(
`Succesfully built SDK from specification: ${spec.id.toString()}, sdkConfig: ${sdkConfig.id.toString()}`,
);
} catch (err) {
logger.error(
`Failed to build SDK (specId: ${spec.id.toString()}, sdkConfigId: ${sdkConfig.id.toString()})`,
);
}
}
}
}

export async function buildSdks(specId: string, sdkConfigs: string[]) {
if (specId != null && sdkConfigs != null) {
await buildSpecs(specId, sdkConfigs);
socket.disconnect();
} else {
if (specId === null) {
logger.error('Must specify one specification id and at least one sdk config id');
} else {
logger.error('Must specify at least one sdk config id');
}
}
}
5 changes: 5 additions & 0 deletions packages/server-cli/src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { apiBaseUrl } from '@openapi-platform/config';
import { createServerClient } from '@openapi-platform/server-client';

import { config } from './config';
export const { socket, client } = createServerClient(apiBaseUrl(config));
2 changes: 2 additions & 0 deletions packages/server-cli/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { readConfig } from '@openapi-platform/config';
export const config = readConfig();
34 changes: 34 additions & 0 deletions packages/server-cli/src/createArgParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Command } from 'commander';

import { addItems } from './add';
import { buildSdks } from './build';
import { listItems } from './list';
import { remove } from './remove';

export function createArgParser(): Command {
const root = new Command();

root.version('1.0.0-alpha.0');

root
.command('add <type>')
.description('Add an item to the db')
.action(addItems);

root
.command('list <type> [filters...]')
.description('List items in db')
.action(listItems);

root
.command('remove <type> [ids...]')
.description('Remove items from db')
.action(remove);

root
.command('build <specId> <sdkConfigIds...>')
.description('Runs the build process for an SDK')
.action(buildSdks);

return root;
}
3 changes: 3 additions & 0 deletions packages/server-cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createArgParser } from './createArgParser';

createArgParser().parse(process.argv);
59 changes: 59 additions & 0 deletions packages/server-cli/src/itemBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import prompts from 'prompts';

// TODO - Add input validation to these

export async function specBuilder() {
const questions = [
{
type: 'text',
name: 'title',
message: 'title',
},
{
type: 'text',
name: 'description',
message: 'description',
},
{
type: 'text',
name: 'path',
message: 'path',
},
];

const response = await prompts(questions);
return response;
}

export async function configBuilder() {
const questions = [
{
type: 'number',
name: 'specId',
message: 'Specification ID',
},
{
type: 'text',
name: 'target',
message: 'Language',
},
{
type: 'text',
name: 'version',
message: 'SDK Version',
},
{
type: 'text',
name: 'options',
message: 'Options',
},
{
type: 'text',
name: 'gitInfo',
message: 'Git Info',
},
];

const response = await prompts(questions);
return response;
}
37 changes: 37 additions & 0 deletions packages/server-cli/src/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { client, socket } from './client';
import { config } from './config';
import { logger } from './logger';

export async function getItems(type: string) {
const itemIds = await client
.service(type)
.find({
query: {
$sort: {
createdAt: 1,
},
},
})
.catch(err => {
if (err.name === 'Timeout') {
logger.error(`Unable to connect to server on port ${config.get('server.port')}`);
} else {
logger.error(err);
}
});
socket.disconnect();
return itemIds;
}

export async function listItems(type, filters) {
if (['specifications', 'sdkConfigs', 'sdks'].indexOf(type) >= 0) {
const items = await getItems(type);
for (const item of items) {
logger.info(item);
}
} else {
logger.error(
`Invalid type '${type}'. Supported types: 'specifications', 'sdkConfigs', 'sdks'`,
);
}
}
6 changes: 6 additions & 0 deletions packages/server-cli/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { openapiLogger, consoleTransport, overrideConsoleLogger, overrideUtilInspectStyle } from '@openapi-platform/logger';
const logger = openapiLogger().add(consoleTransport({ level: 'verbose' }));
overrideConsoleLogger(logger);
overrideUtilInspectStyle();
logger.exceptions.handle(consoleTransport());
export { logger };
Loading