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

WIP getSchema #1474

Open
wants to merge 2 commits into
base: master
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
6 changes: 6 additions & 0 deletions packages/commands/getschema/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
npm-debug.log
dist
temp
yarn-error.log
tests/coverage/
4 changes: 4 additions & 0 deletions packages/commands/getschema/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## GraphQL CLI GetSchema command

This package is part of the GraphQL CLI ecosystem and it is not designed to be consumed separately.
Go to: https://github.com/Urigo/graphql-cli for more information
29 changes: 29 additions & 0 deletions packages/commands/getschema/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@graphql-cli/getschema",
"description": "Get schema from url",
"version": "4.0.0",
"license": "MIT",
"main": "dist/index.js",
"repository": {
"type": "git",
"url": "https://github.com/Urigo/graphql-cli",
"directory": "packages/commands/getschema"
},
"publishConfig": {
"access": "public"
},
"keywords": [
"graphql-cli",
"graphql-cli-plugin"
],
"scripts": {
"build": "tsc"
},
"peerDependencies": {
"graphql": "15.3.0"
},
"dependencies": {
"@graphql-cli/common": "4.0.0",
"tslib": "2.0.1"
}
}
201 changes: 201 additions & 0 deletions packages/commands/getschema/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import * as fs from 'fs'
import * as os from 'os'
import * as mkdirp from 'mkdirp'
import { relative, dirname } from 'path'
import { printSchema, GraphQLSchema, buildClientSchema, validateSchema } from 'graphql'
import chalk from 'chalk'
import { Arguments } from 'yargs'

import { defineCommand } from '@graphql-cli/common';

export default defineCommand(() => {
return {
command: 'get-schema',
builder(builder: any) {
return builder.options({
endpoint: {
alias: 'e',
describe: 'Endpoint name or URL',
type: 'string',
},
json: {
alias: 'j',
describe: 'Output as JSON',
type: 'boolean',
},
output: {
alias: 'o',
describe: 'Output file name',
type: 'string',
},
console: {
alias: 'c',
describe: 'Output to console',
default: false,
},
insecure: {
alias: 'i',
describe: 'Allow insecure (self-signed) certificates',
type: 'boolean',
},
header: {
describe:
'Header to use for downloading (with endpoint URL). Format: Header=Value',
type: 'string',
},
});
},
handler,
};
});

const handler = async (args: any) => {
if (args.endpoint) {
args.all = false
}

if (args.all && !args.project) {
args.project = args.endpoint = '*'
}

if (args.insecure) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
}

if (!args.watch) {
await updateWrapper(args)
}
}

async function updateWrapper(argv: any) {
try {
await update(argv)
} catch (err) {
console.error(err)
}
}

async function update(argv: Arguments) {
if (argv.endpoint) {
if (argv.output || argv.console) {
await downloadFromEndpointUrl(argv)
return
} else {
console.log('No output file specified!');
}
}
}

async function downloadFromEndpointUrl(argv: Arguments) {
const endpointHeaders = {}
if (argv.header) {
const headers = Array.isArray(argv.header) ? argv.header : [argv.header]
Object.assign(
endpointHeaders,
...headers.map(h => ({ [h.split('=')[0]]: h.split('=')[1] })),
)
}

const endpoint = {
url: argv.endpoint,
headers: endpointHeaders,
}

await updateSingleProjectEndpoint(endpoint, argv)
}

async function updateSingleProjectEndpoint(
endpoint: { url: string, headers: string[] },
argv: Arguments,
): Promise<void> {
console.info(`Downloading introspection from ${chalk.blue(endpoint.url)}`)
let newSchemaResult
try {
newSchemaResult = argv.json
// TODO figure out how to resolve it using graphql-tools
? await endpoint.resolveIntrospection()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the code will stay here anyway.. This is the part that needs to be swapped.

: await endpoint.resolveSchema()

// Do not save an invalid schema
const clientSchema = argv.json
? buildClientSchema(newSchemaResult)
: newSchemaResult
const errors = validateSchema(clientSchema)
if (errors.length > 0) {
console.error(chalk.red(`${os.EOL}GraphQL endpoint generated invalid schema: ${errors}`))
setTimeout(() => {
process.exit(1)
}, 500)
return
}
} catch (err) {
console.log('warning', err.message)
return
}

let oldSchema: string | undefined
if (!argv.console) {
try {
oldSchema = argv.output
? fs.readFileSync(argv.output as string, 'utf-8') : ""
} catch (e) {
// ignore error if no previous schema file existed
if (e.message === 'Unsupported schema file extention. Only ".graphql" and ".json" are supported') {
console.error(e.message)
setTimeout(() => {
process.exit(1)
}, 500)
}
// TODO: Add other non-blocking errors to this list
if (e.message.toLowerCase().indexOf('syntax error') > -1) {
console.log(`${os.EOL}Ignoring existing schema because it is invalid: ${chalk.red(e.message)}`)
} else if (e.code !== 'ENOENT') {
throw e
}
}

if (oldSchema) {
const newSchema = argv.json
? JSON.stringify(newSchemaResult, null, 2)
: printSchema(newSchemaResult as GraphQLSchema)
if (newSchema === oldSchema) {
console.log(
chalk.green(
`No changes in schema`,
),
)
return
}
}
}

let schemaPath: any = argv.output
if (argv.console) {
console.log(
argv.json
? JSON.stringify(newSchemaResult, null, 2)
: printSchema(newSchemaResult as GraphQLSchema),
)
} else if (argv.json) {
if (!fs.existsSync(schemaPath)) {
mkdirp.sync(dirname(schemaPath))
}
fs.writeFileSync(argv.output as any, JSON.stringify(newSchemaResult, null, 2))
} else {
console.info("No output provided. Using JSON");
if (!fs.existsSync(schemaPath)) {
mkdirp.sync(dirname(schemaPath))
}
fs.writeFileSync(argv.output as any, JSON.stringify(newSchemaResult, null, 2))
}

if (schemaPath) {
console.log(
chalk.green(
`Schema file was ${oldSchema ? 'updated' : 'created'}: ${chalk.blue(
relative(process.cwd(), schemaPath),
)}`,
),
)
}
}
25 changes: 25 additions & 0 deletions packages/commands/getschema/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"compilerOptions": {
"skipLibCheck": true,
"esModuleInterop": true,
"importHelpers": true,
"experimentalDecorators": true,
"module": "commonjs",
"target": "es2018",
"lib": ["es6", "esnext", "es2015", "dom"],
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"sourceMap": true,
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"noImplicitAny": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": false
},
"files": ["src/index.ts"],
"exclude": ["node_modules"]
}
Loading