Skip to content

Commit

Permalink
circle, renovate, prettier, semantic-release, tests
Browse files Browse the repository at this point in the history
  • Loading branch information
maticzav committed Dec 9, 2018
1 parent 686a97b commit 63c46a9
Show file tree
Hide file tree
Showing 12 changed files with 12,643 additions and 114 deletions.
19 changes: 19 additions & 0 deletions .circleci/config.yml
@@ -0,0 +1,19 @@
version: 2
jobs:
build:
docker:
- image: 'circleci/node:latest'
steps:
- checkout
- run:
name: install
command: yarn install
- run:
name: test
command: yarn test
- run:
name: coverage
command: yarn coverage
- run:
name: release
command: yarn release
3 changes: 3 additions & 0 deletions .gitignore
@@ -1,2 +1,5 @@
node_modules
dist
coverage

.DS_Store
20 changes: 20 additions & 0 deletions jest.config.js
@@ -0,0 +1,20 @@
module.exports = {
roots: ['<rootDir>/src'],
testEnvironment: 'node',
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
testPathIgnorePatterns: ['/node_modules/', '/__fixtures__/'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverage: true,
collectCoverageFrom: [
'**/*.{ts,tsx}',
'!**/node_modules/**',
'!**/vendor/**',
'!**/generated/**',
],
verbose: true,
coverageDirectory: './coverage',
coverageReporters: ['json', 'lcov', 'text', 'clover', 'html'],
}
42 changes: 29 additions & 13 deletions package.json
@@ -1,35 +1,51 @@
{
"name": "get-graphql-schema",
"bin": "dist/index.js",
"files": [
"README.md",
"dist/"
],
"version": "2.1.2",
"version": "0.0.0-semantic-release",
"description": "Downloads the GraphQL Schema of an GraphQL endpoint URL",
"scripts": {
"build": "tsc",
"prepublish": "npm run build && chmod +x dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
"author": "Johannes Schickling <johannes@graph.cool>",
"bin": {
"get-schema": "dist/index.js"
},
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "git+https://github.com/graphcool/get-graphql-schema.git"
},
"author": "Johannes Schickling <johannes@graph.cool>",
"license": "MIT",
"scripts": {
"build": "tsc -d",
"prepublish": "npm run build",
"test": "NODE_ENV=test jest",
"release": "semantic-release"
},
"dependencies": {
"chalk": "^2.4.1",
"graphql": "^14.0.2",
"meow": "^5.0.0",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"node-fetch": "^2.2.0"
},
"devDependencies": {
"@types/chalk": "^2.2.0",
"@types/graphql": "^14.0.3",
"@types/jest": "^23.3.10",
"@types/meow": "^5.0.0",
"@types/minimist": "^1.2.0",
"@types/mkdirp": "^0.5.2",
"@types/node": "^10.12.0",
"@types/node-fetch": "^2.1.2",
"jest": "^23.6.0",
"jest-fetch-mock": "^2.0.1",
"prettier": "^1.15.3",
"semantic-release": "^15.12.4",
"ts-jest": "^23.10.5",
"ts-node": "^7.0.1",
"typescript": "^3.1.3"
}
},
"release": {
"branch": "master"
},
"license": "MIT"
}
5 changes: 5 additions & 0 deletions prettier.config.js
@@ -0,0 +1,5 @@
module.exports = {
semi: false,
trailingComma: 'all',
singleQuote: true,
}
7 changes: 7 additions & 0 deletions renovate.json
@@ -0,0 +1,7 @@
{
"extends": ["config:base", "docker:disable"],
"automerge": true,
"major": {
"automerge": false
}
}
83 changes: 83 additions & 0 deletions src/bin.ts
@@ -0,0 +1,83 @@
#!/usr/bin/env node

import meow = require('meow')
import { getHeadersFromInput, getRemoteSchema, printToFile } from '.'

const cli = meow(
`
Usage:
$ get-graphql-schema ENDPOINT_URL > schema.graphql
Fetch and print the GraphQL schema from a GraphQL HTTP endpoint (Outputs schema in IDL syntax by default).
Options:
--header, -h Add a custom header (ex. 'X-API-KEY=ABC123'), can be used multiple times
--json, -j Output in JSON format (based on introspection query)
--method Use method (GET,POST, PUT, DELETE)
--output Save schema to file.
`,
{
flags: {
header: {
type: 'string',
alias: 'h',
},
json: {
type: 'boolean',
alias: 'j',
default: false,
},
method: {
type: 'string',
default: 'POST',
},
output: {
type: 'string',
},
},
},
)

/* istanbul ignore next */
if (process.env.NODE_ENV !== 'test') main(cli)

/**
* Main
*/
export async function main(cli: meow.Result): Promise<void> {
/* Get remote endpoint from args */
const [endpoint] = cli.input

if (!endpoint) {
console.warn('No endpoint provided')
return
}

/* Headers */
const defaultHeaders = {
'Content-Type': 'application/json',
}

const headers = getHeadersFromInput(cli).reduce(
(acc, { key, value }) => ({ ...acc, [key]: value }),
defaultHeaders,
)

/* Fetch schema */
const schema = await getRemoteSchema(endpoint, {
method: cli.flags.method,
headers,
json: cli.flags.json,
})

if (schema.status === 'err') {
console.warn(schema.message)
return
}

if (cli.flags.output !== undefined) {
printToFile(cli.flags.output, schema.schema)
} else {
console.log(schema.schema)
}
}
158 changes: 94 additions & 64 deletions src/index.ts
@@ -1,76 +1,106 @@
#!/usr/bin/env node

import fetch from 'node-fetch'
import * as fs from 'fs'
import * as path from 'path'
import meow = require('meow')
import mkdirp = require('mkdirp')
import { introspectionQuery } from 'graphql/utilities/introspectionQuery'
import { buildClientSchema } from 'graphql/utilities/buildClientSchema'
import { printSchema } from 'graphql/utilities/schemaPrinter'
import * as minimist from 'minimist'
import chalk from 'chalk'

const { version } = require('../package.json')

const usage = ` Usage: get-graphql-schema ENDPOINT_URL > schema.graphql
${chalk.bold(
'Fetch and print the GraphQL schema from a GraphQL HTTP endpoint',
)}
(Outputs schema in IDL syntax by default)
Options:
--header, -h Add a custom header (ex. 'X-API-KEY=ABC123'), can be used multiple times
--json, -j Output in JSON format (based on introspection query)
--version, -v Print version of get-graphql-schema
`

async function main() {
const argv = minimist(process.argv.slice(2))

if (argv._.length < 1) {
console.log(usage)
return
}

if (argv['version'] || argv['v']) {
console.log(version)
process.exit(0)
/**
*
* Normalizes header input from CLI
*
* @param cli
*/
export function getHeadersFromInput(
cli: meow.Result,
): { key: string; value: string }[] {
switch (typeof cli.flags.header) {
case 'string': {
const [key, value] = cli.flags.header.split('=')
return [{ key, value }]
}
case 'object': {
return cli.flags.header.map(header => {
const [key, value] = header.split('=')
return { key, value }
})
}
default: {
return []
}
}
}

const endpoint = argv._[0]

const defaultHeaders = {
'Content-Type': 'application/json',
}

const headers = toArray(argv['header'])
.concat(toArray(argv['h']))
.reduce((obj, header: string) => {
const [key, value] = header.split('=')
obj[key] = value
return obj
}, defaultHeaders)

const response = await fetch(endpoint, {
method: 'POST',
headers: headers,
body: JSON.stringify({ query: introspectionQuery }),
})

const { data, errors } = await response.json()

if (errors) {
throw new Error(JSON.stringify(errors, null, 2))
}
interface Options {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
headers?: { [key: string]: string }
json?: boolean
}

if (argv['j'] || argv['json']) {
console.log(JSON.stringify(data, null, 2))
} else {
const schema = buildClientSchema(data)
console.log(printSchema(schema))
/**
*
* Fetch remote schema and turn it into string
*
* @param endpoint
* @param options
*/
export async function getRemoteSchema(
endpoint: string,
options: Options,
): Promise<
{ status: 'ok'; schema: string } | { status: 'err'; message: string }
> {
try {
const { data, errors } = await fetch(endpoint, {
method: options.method,
headers: options.headers,
body: JSON.stringify({ query: introspectionQuery }),
}).then(res => res.json())

if (errors) {
return { status: 'err', message: JSON.stringify(errors, null, 2) }
}

if (options.json) {
return {
status: 'ok',
schema: JSON.stringify(data, null, 2),
}
} else {
const schema = buildClientSchema(data)
return {
status: 'ok',
schema: printSchema(schema),
}
}
} catch (err) {
return { status: 'err', message: err.message }
}
}

function toArray(value = []) {
return Array.isArray(value) ? value : [value]
/**
*
* Prints schema to file.
*
* @param dist
* @param schema
*/
export function printToFile(
dist: string,
schema: string,
): { status: 'ok'; path: string } | { status: 'err'; message: string } {
try {
const output = path.resolve(process.cwd(), dist)

if (!fs.existsSync(output)) {
mkdirp.sync(output)
}
fs.writeFileSync(output, schema)

return { status: 'ok', path: output }
} catch (err) {
return { status: 'err', message: err.message }
}
}

main().catch(e => console.error(e))

0 comments on commit 63c46a9

Please sign in to comment.