From b71307a6c745ab177889603610658111dd089140 Mon Sep 17 00:00:00 2001 From: Stephen J Radachy Date: Thu, 7 Dec 2017 08:30:05 -0600 Subject: [PATCH 1/5] add snake->camel case + type prefixing --- package.json | 2 +- src/parsers/swagger/index.ts | 66 ++++++++++++++++++++++++++++++++++-- src/utils/index.ts | 4 +++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 49585a6..b543ab9 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "Copyright (c) 2017 Target Brands, Inc." ], "name": "graphql-liftoff", - "version": "1.0.0", + "version": "1.0.2", "description": "generates GraphQL schema language from API specifications and more", "engineStrict": true, "engines": { diff --git a/src/parsers/swagger/index.ts b/src/parsers/swagger/index.ts index 26bfe2a..a7ff9f8 100644 --- a/src/parsers/swagger/index.ts +++ b/src/parsers/swagger/index.ts @@ -1,10 +1,19 @@ import * as yml from 'js-yaml' import * as converter from 'oas-raml-converter' -import { parserUsage } from '../../utils' +import { convertSnakeToCamel, parserUsage } from '../../utils' +let isYAML: boolean = false +let isPrefix: boolean = false +let isCamelCase: boolean = false + +let prefix: string = '' export async function parse(content: string, options: any): Promise { - let isYAML = false - if (options.hasOwnProperty('y') || options.hasOwnProperty('yaml')) { isYAML = true } + if (optionSet(options, 'c', 'camel-case')) { isCamelCase = true } + if (optionSet(options, 'p', 'prefix-type-name')) { + prefix = String(getOptionValue(options, 'p', 'prefix-type-name')) + isPrefix = true + } + if (optionSet(options, 'y', 'yaml')) { isYAML = true } const transformer = new converter.Converter(converter.Formats.OAS20, converter.Formats.RAML) if (!isYAML) { content = yml.safeDump(JSON.parse(content)) @@ -16,6 +25,16 @@ export async function parse(content: string, options: any): Promise { export function usage(parser: string): void { const args = [ + { + short: 'c', + long: 'camel-case', + description: 'convert all keys from snake_case to camelCase' + }, + { + short: 'p', + long: 'prefix-type-name', + description: '-p "prefix" prefix type names' + }, { short: 'y', long: 'yaml', @@ -27,6 +46,30 @@ export function usage(parser: string): void { function parseDefinitions(definitions: any[], ast: AST = {} as AST): AST { definitions.map((d: any) => parseDefinition(d, ast)) + if (isCamelCase) { ast = toCamel(ast) } + if (isPrefix) { ast = addPrefix(ast) } + return ast +} + +function toCamel(ast: AST): AST { + Object + .keys(ast) + .map((n: string, i: number) => { + ast[n].name = convertSnakeToCamel(ast[n].name) + ast[n].fields + .map((f: GraphQLField, fi: number) => { + ast[n].fields[fi].name = convertSnakeToCamel(ast[n].fields[fi].name) + }) + }) + return ast +} + +function addPrefix(ast: AST): AST { + Object + .keys(ast) + .map((n: string, i: number) => { + ast[n].name = prefix + ast[n].name + }) return ast } @@ -98,3 +141,20 @@ function convertType(type: string): string { return 'String' } } + +function optionSet(options: any, short: string, long: string): boolean { + if (options.hasOwnProperty(short) || options.hasOwnProperty(long)) { + return true + } + return false +} + +function getOptionValue(options: any, short: string, long: string): any { + let i = -1 + if (options.hasOwnProperty(short)) { + i = Object.keys(options).findIndex(x => x === short) + } else if (options.hasOwnProperty(long)) { + i = Object.keys(options).findIndex(x => x === long) + } + return Object.keys(options)[i + 1] +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 4f1d60b..bdbef34 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -46,3 +46,7 @@ export function parserUsage(parser: string, args: Arg[]) { console.log(`\t--${a.long},-${a.short}\t${a.description}`) }) } + +export function convertSnakeToCamel(s: string) { + return s.replace(/(\_\w)/g, m => m[1].toUpperCase()) +} From cf5da18e1435a88ff2e1e903c96bb5c23997c7d3 Mon Sep 17 00:00:00 2001 From: Stephen J Radachy Date: Wed, 13 Dec 2017 05:30:04 -0600 Subject: [PATCH 2/5] refactor + tests --- src/bin/cli.ts | 2 +- src/parsers/swagger/index.ts | 51 ++--------------- src/utils/index.ts | 39 ++++++++++++- test/unit/utils/index.spec.js | 102 +++++++++++++++++++++++++++++++++- 4 files changed, 146 insertions(+), 48 deletions(-) diff --git a/src/bin/cli.ts b/src/bin/cli.ts index 0ddf92f..05c45f2 100644 --- a/src/bin/cli.ts +++ b/src/bin/cli.ts @@ -86,7 +86,7 @@ async function exec(parser: string, mArgs: string[], stdin: boolean): Promise [--key=value|--key|-k] + console.log(`usage: ${(pkg as any).name} [--help|-h] [--key=value|-k=value|--key|-k] description: \t${(pkg as any).name} ${(pkg as any).description} diff --git a/src/parsers/swagger/index.ts b/src/parsers/swagger/index.ts index a7ff9f8..bef3f6a 100644 --- a/src/parsers/swagger/index.ts +++ b/src/parsers/swagger/index.ts @@ -1,6 +1,6 @@ import * as yml from 'js-yaml' import * as converter from 'oas-raml-converter' -import { convertSnakeToCamel, parserUsage } from '../../utils' +import { addPrefix, getOptionValue, isOptionSet, parserUsage, toCamel } from '../../utils' let isYAML: boolean = false let isPrefix: boolean = false @@ -8,12 +8,12 @@ let isCamelCase: boolean = false let prefix: string = '' export async function parse(content: string, options: any): Promise { - if (optionSet(options, 'c', 'camel-case')) { isCamelCase = true } - if (optionSet(options, 'p', 'prefix-type-name')) { + if (isOptionSet(options, 'c', 'camel-case')) { isCamelCase = true } + if (isOptionSet(options, 'p', 'prefix-type-name')) { prefix = String(getOptionValue(options, 'p', 'prefix-type-name')) isPrefix = true } - if (optionSet(options, 'y', 'yaml')) { isYAML = true } + if (isOptionSet(options, 'y', 'yaml')) { isYAML = true } const transformer = new converter.Converter(converter.Formats.OAS20, converter.Formats.RAML) if (!isYAML) { content = yml.safeDump(JSON.parse(content)) @@ -33,7 +33,7 @@ export function usage(parser: string): void { { short: 'p', long: 'prefix-type-name', - description: '-p "prefix" prefix type names' + description: '[--prefix-type-name|-prefix]="prefix" prefix type names' }, { short: 'y', @@ -47,29 +47,7 @@ export function usage(parser: string): void { function parseDefinitions(definitions: any[], ast: AST = {} as AST): AST { definitions.map((d: any) => parseDefinition(d, ast)) if (isCamelCase) { ast = toCamel(ast) } - if (isPrefix) { ast = addPrefix(ast) } - return ast -} - -function toCamel(ast: AST): AST { - Object - .keys(ast) - .map((n: string, i: number) => { - ast[n].name = convertSnakeToCamel(ast[n].name) - ast[n].fields - .map((f: GraphQLField, fi: number) => { - ast[n].fields[fi].name = convertSnakeToCamel(ast[n].fields[fi].name) - }) - }) - return ast -} - -function addPrefix(ast: AST): AST { - Object - .keys(ast) - .map((n: string, i: number) => { - ast[n].name = prefix + ast[n].name - }) + if (isPrefix) { ast = addPrefix(ast, prefix) } return ast } @@ -141,20 +119,3 @@ function convertType(type: string): string { return 'String' } } - -function optionSet(options: any, short: string, long: string): boolean { - if (options.hasOwnProperty(short) || options.hasOwnProperty(long)) { - return true - } - return false -} - -function getOptionValue(options: any, short: string, long: string): any { - let i = -1 - if (options.hasOwnProperty(short)) { - i = Object.keys(options).findIndex(x => x === short) - } else if (options.hasOwnProperty(long)) { - i = Object.keys(options).findIndex(x => x === long) - } - return Object.keys(options)[i + 1] -} diff --git a/src/utils/index.ts b/src/utils/index.ts index bdbef34..5b52471 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -47,6 +47,43 @@ export function parserUsage(parser: string, args: Arg[]) { }) } -export function convertSnakeToCamel(s: string) { +function convertSnakeToCamel(s: string): string { return s.replace(/(\_\w)/g, m => m[1].toUpperCase()) } + +export function toCamel(ast: AST): AST { + Object + .keys(ast) + .map((n: string, i: number) => { + ast[n].name = convertSnakeToCamel(ast[n].name) + ast[n].fields + .map((f: GraphQLField, fi: number) => { + ast[n].fields[fi].name = convertSnakeToCamel(ast[n].fields[fi].name) + }) + }) + return ast +} + +export function addPrefix(ast: AST, prefix: String): AST { + Object + .keys(ast) + .map((n: string, i: number) => { + ast[n].name = prefix + ast[n].name + }) + return ast +} + +export function isOptionSet(options: any, short: string, long: string): boolean { + if (options.hasOwnProperty(short) || options.hasOwnProperty(long)) { + return true + } + return false +} + +export function getOptionValue(options: any, short: string, long: string): any { + if (options.hasOwnProperty(short)) { + return options[short] + } else if (options.hasOwnProperty(long)) { + return options[long] + } +} diff --git a/test/unit/utils/index.spec.js b/test/unit/utils/index.spec.js index 83f6102..84251b0 100644 --- a/test/unit/utils/index.spec.js +++ b/test/unit/utils/index.spec.js @@ -1,4 +1,4 @@ -import { getContent, parserUsage } from '../../../src/utils/index.ts' +import { addPrefix, getContent, getOptionValue, isOptionSet, parserUsage, toCamel } from '../../../src/utils/index.ts' import packageJSON from '../../../package.json' describe('getContent', () => { @@ -106,3 +106,103 @@ options: expect(consoleOutput).toEqual(expected) }) }) + +describe('toCamel', () => { + it('should convert snake to camel case', () => { + // given + const input = { + test: { + name: 'snake_case', + fields: [ + { + name: 'field_snake_case' + } + ] + } + } + + // when + const result = toCamel(input) + + // then + expect(result).toEqual({ test: { name: 'snakeCase', fields: [ { name: 'fieldSnakeCase'}]}}) + }) +}) + +describe('addPrefix', () => { + it('should add a prefix', () => { + // given + const input = { + test: { + name: 'snake_case', + fields: [ + { + name: 'field_snake_case' + } + ] + } + } + + // when + const result = addPrefix(input, 'TEST_') + + // then + expect(result).toEqual({ test: { name: 'TEST_snake_case', fields: [ { name: 'field_snake_case'}]}}) + }) +}) + +describe('isOptionSet', () => { + it('should find the short option value', () => { + // given + const input = { + v: 'asdf' + } + + // when + const result = isOptionSet(input, 'v', 'verylongargname') + + // then + expect(result).toEqual(true) + }) + + it('should find the long option value', () => { + // given + const input = { + verylongargname: 'asdf' + } + + // when + const result = isOptionSet(input, 'v', 'verylongargname') + + // then + expect(result).toEqual(true) + }) +}) + +describe('getOptionValue', () => { + it('should get the short option value', () => { + // given + const input = { + v: 'asdf' + } + + // when + const result = getOptionValue(input, 'v', 'verylongargname') + + // then + expect(result).toEqual('asdf') + }) + + it('should get the long option value', () => { + // given + const input = { + verylongargname: 'asdf' + } + + // when + const result = getOptionValue(input, 'v', 'verylongargname') + + // then + expect(result).toEqual('asdf') + }) +}) From a258afd001df8bfa10a3da8182d8ba460b046b18 Mon Sep 17 00:00:00 2001 From: Stephen J Radachy Date: Thu, 14 Dec 2017 08:32:28 -0600 Subject: [PATCH 3/5] review fixes --- src/parsers/swagger/index.ts | 2 +- src/utils/index.ts | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/parsers/swagger/index.ts b/src/parsers/swagger/index.ts index bef3f6a..145fe7c 100644 --- a/src/parsers/swagger/index.ts +++ b/src/parsers/swagger/index.ts @@ -10,7 +10,7 @@ let prefix: string = '' export async function parse(content: string, options: any): Promise { if (isOptionSet(options, 'c', 'camel-case')) { isCamelCase = true } if (isOptionSet(options, 'p', 'prefix-type-name')) { - prefix = String(getOptionValue(options, 'p', 'prefix-type-name')) + prefix = getOptionValue(options, 'p', 'prefix-type-name') isPrefix = true } if (isOptionSet(options, 'y', 'yaml')) { isYAML = true } diff --git a/src/utils/index.ts b/src/utils/index.ts index 5b52471..312ae5c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -41,7 +41,7 @@ export function parserUsage(parser: string, args: Arg[]) { description: 'Show this help documentation' } as Arg) const sorted = args.sort((a: Arg, b: Arg) => a.long.localeCompare(b.long)) - console.log(`usage: ${(pkg as any).name} ${parser} [--key=value|--key|-k] \n\noptions:`) + console.log(`usage: ${(pkg as any).name} ${parser} [--key=value|-k=value|--key|-k] \n\noptions:`) sorted.map(a => { console.log(`\t--${a.long},-${a.short}\t${a.description}`) }) @@ -74,10 +74,7 @@ export function addPrefix(ast: AST, prefix: String): AST { } export function isOptionSet(options: any, short: string, long: string): boolean { - if (options.hasOwnProperty(short) || options.hasOwnProperty(long)) { - return true - } - return false + return options.hasOwnProperty(short) || options.hasOwnProperty(long) } export function getOptionValue(options: any, short: string, long: string): any { From 74dc11e918cdddeb39e4adef1f98c7d64c642f7a Mon Sep 17 00:00:00 2001 From: Stephen J Radachy Date: Thu, 14 Dec 2017 08:40:41 -0600 Subject: [PATCH 4/5] fix tests --- test/unit/utils/index.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/utils/index.spec.js b/test/unit/utils/index.spec.js index 84251b0..0420656 100644 --- a/test/unit/utils/index.spec.js +++ b/test/unit/utils/index.spec.js @@ -71,7 +71,7 @@ describe('parser usage', () => { // given const parser = 'example' const args = [] - const expected = `usage: graphql-liftoff example [--key=value|--key|-k] + const expected = `usage: graphql-liftoff example [--key=value|-k=value|--key|-k] options: \t--help,-h Show this help documentation @@ -93,7 +93,7 @@ options: description: 'test description' } ] - const expected = `usage: graphql-liftoff example [--key=value|--key|-k] + const expected = `usage: graphql-liftoff example [--key=value|-k=value|--key|-k] options: \t--help,-h Show this help documentation From bb8a74d8b1ebcd6c57cbe10218dfb8a325803086 Mon Sep 17 00:00:00 2001 From: Stephen J Radachy Date: Mon, 18 Dec 2017 02:26:13 -0600 Subject: [PATCH 5/5] switch to functional style --- src/utils/index.ts | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/utils/index.ts b/src/utils/index.ts index 312ae5c..d0217b0 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -52,25 +52,32 @@ function convertSnakeToCamel(s: string): string { } export function toCamel(ast: AST): AST { - Object + return Object .keys(ast) - .map((n: string, i: number) => { - ast[n].name = convertSnakeToCamel(ast[n].name) - ast[n].fields - .map((f: GraphQLField, fi: number) => { - ast[n].fields[fi].name = convertSnakeToCamel(ast[n].fields[fi].name) + .reduce((acc: AST, cur: string, idx: number) => { + acc[cur] = { + name: convertSnakeToCamel(ast[cur].name), + description: ast[cur].description, + fields: ast[cur].fields.map(f => { + f.name = convertSnakeToCamel(f.name) + return f }) - }) - return ast + } as GraphQLType + return acc + }, {} as AST) } export function addPrefix(ast: AST, prefix: String): AST { - Object + return Object .keys(ast) - .map((n: string, i: number) => { - ast[n].name = prefix + ast[n].name - }) - return ast + .reduce((acc: AST, cur: string) => { + acc[cur] = { + name: prefix + ast[cur].name, + description: ast[cur].description, + fields: ast[cur].fields + } as GraphQLType + return acc + }, {} as AST) } export function isOptionSet(options: any, short: string, long: string): boolean {