stepzen 0.29.0 → 0.30.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +417 -54
- package/lib/commands/import/curl.d.ts +48 -0
- package/lib/commands/import/curl.d.ts.map +1 -0
- package/lib/commands/import/curl.js +246 -0
- package/lib/commands/import/curl.js.map +1 -0
- package/lib/commands/import/graphql.d.ts +34 -0
- package/lib/commands/import/graphql.d.ts.map +1 -0
- package/lib/commands/import/graphql.js +118 -0
- package/lib/commands/import/graphql.js.map +1 -0
- package/lib/commands/import/index.d.ts +22 -0
- package/lib/commands/import/index.d.ts.map +1 -0
- package/lib/commands/import/index.js +165 -0
- package/lib/commands/import/index.js.map +1 -0
- package/lib/commands/import/mysql.d.ts +48 -0
- package/lib/commands/import/mysql.d.ts.map +1 -0
- package/lib/commands/import/mysql.js +145 -0
- package/lib/commands/import/mysql.js.map +1 -0
- package/lib/commands/import/postgresql.d.ts +51 -0
- package/lib/commands/import/postgresql.d.ts.map +1 -0
- package/lib/commands/import/postgresql.js +150 -0
- package/lib/commands/import/postgresql.js.map +1 -0
- package/lib/commands/import/presto.d.ts +49 -0
- package/lib/commands/import/presto.d.ts.map +1 -0
- package/lib/commands/import/presto.js +152 -0
- package/lib/commands/import/presto.js.map +1 -0
- package/lib/commands/import/snowflake.d.ts +53 -0
- package/lib/commands/import/snowflake.d.ts.map +1 -0
- package/lib/commands/import/snowflake.js +181 -0
- package/lib/commands/import/snowflake.js.map +1 -0
- package/lib/commands/list.d.ts.map +1 -1
- package/lib/commands/list.js +0 -2
- package/lib/commands/list.js.map +1 -1
- package/lib/commands/request.js +6 -6
- package/lib/commands/request.js.map +1 -1
- package/lib/commands/service.d.ts +1 -1
- package/lib/commands/service.d.ts.map +1 -1
- package/lib/commands/service.js +12 -12
- package/lib/commands/service.js.map +1 -1
- package/lib/generate/curl2sdl.d.ts +13 -25
- package/lib/generate/curl2sdl.d.ts.map +1 -1
- package/lib/generate/curl2sdl.js +52 -114
- package/lib/generate/curl2sdl.js.map +1 -1
- package/lib/generate/flags.d.ts +19 -0
- package/lib/generate/flags.d.ts.map +1 -0
- package/lib/generate/flags.js +74 -0
- package/lib/generate/flags.js.map +1 -0
- package/lib/generate/graphql2sdl.d.ts +5 -16
- package/lib/generate/graphql2sdl.d.ts.map +1 -1
- package/lib/generate/graphql2sdl.js +4 -49
- package/lib/generate/graphql2sdl.js.map +1 -1
- package/lib/generate/helpers.d.ts +5 -9
- package/lib/generate/helpers.d.ts.map +1 -1
- package/lib/generate/helpers.js +30 -37
- package/lib/generate/helpers.js.map +1 -1
- package/lib/generate/import-command.d.ts +25 -0
- package/lib/generate/import-command.d.ts.map +1 -0
- package/lib/generate/import-command.js +176 -0
- package/lib/generate/import-command.js.map +1 -0
- package/lib/generate/questions.d.ts +30 -0
- package/lib/generate/questions.d.ts.map +1 -0
- package/lib/generate/questions.js +50 -0
- package/lib/generate/questions.js.map +1 -0
- package/lib/generate/sql2sdl.d.ts +14 -18
- package/lib/generate/sql2sdl.d.ts.map +1 -1
- package/lib/generate/sql2sdl.js +29 -92
- package/lib/generate/sql2sdl.js.map +1 -1
- package/lib/hooks/prerun/check-upgrade.js +4 -4
- package/lib/hooks/prerun/check-upgrade.js.map +1 -1
- package/lib/shared/constants.d.ts +1 -0
- package/lib/shared/constants.d.ts.map +1 -1
- package/lib/shared/constants.js +2 -1
- package/lib/shared/constants.js.map +1 -1
- package/lib/shared/curl-parser.d.ts +3 -3
- package/lib/shared/curl-parser.d.ts.map +1 -1
- package/lib/shared/curl-parser.js +52 -39
- package/lib/shared/curl-parser.js.map +1 -1
- package/lib/shared/docker.d.ts +8 -36
- package/lib/shared/docker.d.ts.map +1 -1
- package/lib/shared/docker.js +44 -42
- package/lib/shared/docker.js.map +1 -1
- package/lib/shared/dsn-parser.d.ts +3 -0
- package/lib/shared/dsn-parser.d.ts.map +1 -0
- package/lib/shared/dsn-parser.js +28 -0
- package/lib/shared/dsn-parser.js.map +1 -0
- package/lib/shared/header-params-parser.d.ts +7 -5
- package/lib/shared/header-params-parser.d.ts.map +1 -1
- package/lib/shared/header-params-parser.js +55 -43
- package/lib/shared/header-params-parser.js.map +1 -1
- package/lib/shared/header.d.ts +2 -2
- package/lib/shared/header.d.ts.map +1 -1
- package/lib/shared/header.js +13 -7
- package/lib/shared/header.js.map +1 -1
- package/lib/shared/inquirer.d.ts +2 -0
- package/lib/shared/inquirer.d.ts.map +1 -1
- package/lib/shared/inquirer.js +12 -4
- package/lib/shared/inquirer.js.map +1 -1
- package/lib/shared/path-params-parser.d.ts +2 -2
- package/lib/shared/path-params-parser.d.ts.map +1 -1
- package/lib/shared/path-params-parser.js +11 -7
- package/lib/shared/path-params-parser.js.map +1 -1
- package/lib/shared/request-variables-parser.d.ts +2 -9
- package/lib/shared/request-variables-parser.d.ts.map +1 -1
- package/lib/shared/request-variables-parser.js +8 -8
- package/lib/shared/request-variables-parser.js.map +1 -1
- package/lib/shared/types.d.ts +33 -6
- package/lib/shared/types.d.ts.map +1 -1
- package/lib/shared/utils.d.ts +5 -13
- package/lib/shared/utils.d.ts.map +1 -1
- package/lib/shared/utils.js.map +1 -1
- package/oclif.manifest.json +684 -197
- package/package.json +6 -6
- package/lib/commands/import.d.ts +0 -85
- package/lib/commands/import.d.ts.map +0 -1
- package/lib/commands/import.js +0 -645
- package/lib/commands/import.js.map +0 -1
- package/lib/generate/index.d.ts +0 -6
- package/lib/generate/index.d.ts.map +0 -1
- package/lib/generate/index.js +0 -4
- package/lib/generate/index.js.map +0 -1
package/lib/commands/import.js
DELETED
|
@@ -1,645 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright (c) 2020,2021,2022,2023, StepZen, Inc.
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const chalk = require("chalk");
|
|
5
|
-
const fsx = require("fs-extra");
|
|
6
|
-
const path = require("path");
|
|
7
|
-
const core_1 = require("@oclif/core");
|
|
8
|
-
const errors_1 = require("@oclif/core/lib/errors");
|
|
9
|
-
const errors_2 = require("@oclif/core/lib/parser/errors");
|
|
10
|
-
const dsn_parser_1 = require("@soluble/dsn-parser");
|
|
11
|
-
const lodash_1 = require("lodash");
|
|
12
|
-
const inquirer = require("../shared/inquirer");
|
|
13
|
-
const utils_1 = require("../shared/utils");
|
|
14
|
-
const helpers_1 = require("../generate/helpers");
|
|
15
|
-
const curl2sdl_1 = require("../generate/curl2sdl");
|
|
16
|
-
const curl_parser_1 = require("../shared/curl-parser");
|
|
17
|
-
const zen_command_1 = require("../shared/zen-command");
|
|
18
|
-
const graphql2sdl_1 = require("../generate/graphql2sdl");
|
|
19
|
-
const sql2sdl_1 = require("../generate/sql2sdl");
|
|
20
|
-
const constants_1 = require("../shared/constants");
|
|
21
|
-
const path_params_parser_1 = require("../shared/path-params-parser");
|
|
22
|
-
const header_params_parser_1 = require("../shared/header-params-parser");
|
|
23
|
-
const header_1 = require("../shared/header");
|
|
24
|
-
const configuration_1 = require("../shared/configuration");
|
|
25
|
-
const rmtemp_1 = require("../shared/rmtemp");
|
|
26
|
-
const _addDataTypesToDescription = (options) => {
|
|
27
|
-
const prefix = options?.dataSources
|
|
28
|
-
? `[${options?.dataSources.join(', ')}]`
|
|
29
|
-
: '';
|
|
30
|
-
return prefix && options?.description
|
|
31
|
-
? {
|
|
32
|
-
...options,
|
|
33
|
-
description: `${prefix} ${options.description}`,
|
|
34
|
-
}
|
|
35
|
-
: options;
|
|
36
|
-
};
|
|
37
|
-
// extension of Flags from '@oclif/core'
|
|
38
|
-
const Flags = {
|
|
39
|
-
// Can't figure how to make TS happy. The `options` value is passed
|
|
40
|
-
// to the original function and the original result is returned
|
|
41
|
-
// with minimal changes, yet TS is unable to figure out the types.
|
|
42
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
43
|
-
// @ts-ignore
|
|
44
|
-
string: options => ({
|
|
45
|
-
...core_1.Flags.string(_addDataTypesToDescription(options)),
|
|
46
|
-
dataSources: options?.dataSources,
|
|
47
|
-
}),
|
|
48
|
-
boolean: options => ({
|
|
49
|
-
...core_1.Flags.boolean(_addDataTypesToDescription(options)),
|
|
50
|
-
dataSources: options?.dataSources,
|
|
51
|
-
}),
|
|
52
|
-
};
|
|
53
|
-
class Import extends zen_command_1.default {
|
|
54
|
-
// special parsing to support curl flags and options
|
|
55
|
-
async parse(options, argv = this.argv) {
|
|
56
|
-
let parsed;
|
|
57
|
-
try {
|
|
58
|
-
parsed = await super.parse(options, argv);
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
if (!(error instanceof errors_2.CLIParseError)) {
|
|
62
|
-
throw error;
|
|
63
|
-
}
|
|
64
|
-
const { parse: { output }, } = error;
|
|
65
|
-
const isCurl = output?.args?.source?.toLowerCase() === 'curl';
|
|
66
|
-
const hasUnsupportedFlags = Boolean(output?.nonExistentFlags?.length);
|
|
67
|
-
if (isCurl && hasUnsupportedFlags) {
|
|
68
|
-
const unsupportedFlags = output.nonExistentFlags;
|
|
69
|
-
throw new errors_1.CLIError(`Unsupported curl flag${unsupportedFlags.length > 1 ? 's' : ''}: ${unsupportedFlags.join(', ')}.` +
|
|
70
|
-
` If this is a blocker for you, please let us know on Discord (${constants_1.STEPZEN_DISCORD_URL})`);
|
|
71
|
-
}
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
const isCurl = parsed.args.source.toLowerCase() === 'curl';
|
|
75
|
-
if (isCurl) {
|
|
76
|
-
// "unparse" the curl flags and args into the argv array
|
|
77
|
-
// (to be parsed again by the curl-parser code later)
|
|
78
|
-
//
|
|
79
|
-
// The main reason for this non-optimal approach:
|
|
80
|
-
// - In StepZen CLI, a curl command parser with support for
|
|
81
|
-
// curl-specific validation and error messages has been historically
|
|
82
|
-
// implemented without using the oclif parser. (This was done
|
|
83
|
-
// because the curl syntax conventions for command line flags and
|
|
84
|
-
// options slightly differ from those in oclif.)
|
|
85
|
-
// - The custom curl parser is used in 2 places: to initially parse
|
|
86
|
-
// the command line when running the Import command, and also to
|
|
87
|
-
// parse the user answer to the "URL or a whole curl command"
|
|
88
|
-
// question in the `askCurlQuestions()` function.
|
|
89
|
-
// - The workarounds added to the oclif's `parse()` method in the
|
|
90
|
-
// ZenCommand base class make it feasible to use the customised
|
|
91
|
-
// oclif parser also for the curl command line. However, considering
|
|
92
|
-
// the two options:
|
|
93
|
-
// 1. "unparse" the oclif parser output and keep using a custom
|
|
94
|
-
// curl parser as before
|
|
95
|
-
// 2. remove the custom curl parser and use the oclif parser in
|
|
96
|
-
// both places
|
|
97
|
-
// the first option seems to provide more specific error messages,
|
|
98
|
-
// and it is not drastically more complex than the second one
|
|
99
|
-
// (calling the oclif parser directly outisde of the `parse()`
|
|
100
|
-
// method is non-trivial).
|
|
101
|
-
parsed.argv.push(...(parsed.flags.data
|
|
102
|
-
? parsed.flags.data.flatMap((data) => ['-d', data])
|
|
103
|
-
: []), ...(parsed.flags.request ? ['-X', parsed.flags.request] : []), ...(parsed.flags.url ? [parsed.flags.url] : []));
|
|
104
|
-
}
|
|
105
|
-
return parsed;
|
|
106
|
-
}
|
|
107
|
-
async run() {
|
|
108
|
-
const { args, argv, flags } = await this.parse(Import);
|
|
109
|
-
// Get the schema you're asking for; may be a DSN
|
|
110
|
-
const { source, dsn } = (0, helpers_1.getSource)(args.source);
|
|
111
|
-
// If using a local StepZen service instance, make sure it's running
|
|
112
|
-
await this.awaitIntrospectionServiceUpIfLocal();
|
|
113
|
-
// Get or create a StepZen workspace (possibly interactive)
|
|
114
|
-
const workspace = await this.ensureStepZenWorkspace({ directory: flags.dir });
|
|
115
|
-
// Define a sane onConflict behaviour (possibly interactive)
|
|
116
|
-
const onConflict = await this.ensureOnConflictBehavior(workspace, source, flags);
|
|
117
|
-
this.warnAboutIgnoredFlags(source, flags);
|
|
118
|
-
// Common options for all import source
|
|
119
|
-
const commonOptions = {
|
|
120
|
-
name: flags.name,
|
|
121
|
-
source: workspace.schema,
|
|
122
|
-
onConflict,
|
|
123
|
-
};
|
|
124
|
-
let importFn = undefined;
|
|
125
|
-
switch (source) {
|
|
126
|
-
case 'curl':
|
|
127
|
-
if (dsn) {
|
|
128
|
-
throw new errors_1.CLIError(`Please use ${chalk.bold('stepzen import curl [url]')}`);
|
|
129
|
-
}
|
|
130
|
-
importFn = () => this.importCurl(argv, flags, commonOptions);
|
|
131
|
-
break;
|
|
132
|
-
case 'graphql':
|
|
133
|
-
if (dsn) {
|
|
134
|
-
throw new errors_1.CLIError(`Please use ${chalk.bold('stepzen import graphql [url]')}`);
|
|
135
|
-
}
|
|
136
|
-
importFn = () => this.importGraphQL(argv, flags, commonOptions);
|
|
137
|
-
break;
|
|
138
|
-
case 'mysql':
|
|
139
|
-
case 'postgresql':
|
|
140
|
-
case 'snowflake':
|
|
141
|
-
importFn = () => this.importSql(source, dsn, flags, commonOptions);
|
|
142
|
-
break;
|
|
143
|
-
default:
|
|
144
|
-
throw new errors_1.CLIError(chalk `Unsupported data source: {bold ${source}}.` +
|
|
145
|
-
chalk `\n\tStepZen CLI does not support introspection and automatic` +
|
|
146
|
-
chalk ` GraphQL schema generation for {bold ${source}} data sources.` +
|
|
147
|
-
chalk ` Please write a schema for it manually.` +
|
|
148
|
-
chalk `\n\tSee {bold stepzen import --help} for supported data sources.`);
|
|
149
|
-
}
|
|
150
|
-
const result = await importFn();
|
|
151
|
-
try {
|
|
152
|
-
// Housekeeping
|
|
153
|
-
fsx.copySync(result, workspace.schema);
|
|
154
|
-
// Nice message
|
|
155
|
-
this.log(chalk.green(`Successfully imported ${chalk.bold(source)} data source into your GraphQL schema`));
|
|
156
|
-
}
|
|
157
|
-
finally {
|
|
158
|
-
(0, rmtemp_1.rmtemp)(result);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
async importCurl(argv, flags, commonOptions) {
|
|
162
|
-
const headers = this.parseHeaderFlags(flags.header, flags['header-param']);
|
|
163
|
-
let curl2sdlOptions;
|
|
164
|
-
const curlFlagValues = {
|
|
165
|
-
queryName: flags['query-name'],
|
|
166
|
-
rootType: flags['query-type'],
|
|
167
|
-
typePrefix: flags.prefix,
|
|
168
|
-
pathParams: flags['path-params'],
|
|
169
|
-
};
|
|
170
|
-
if (argv.length === 1) {
|
|
171
|
-
// no parameters given: start an interactive prompt
|
|
172
|
-
console.log();
|
|
173
|
-
console.log('This command introspects the response of a REST endpoint and generates' +
|
|
174
|
-
' a GraphQL schema allowing you to access this endpoint through your ' +
|
|
175
|
-
'StepZen API. curl syntax is supported so that you copy and paste a ' +
|
|
176
|
-
'curl command instead of the URL.');
|
|
177
|
-
console.log();
|
|
178
|
-
const interactiveOptions = await (0, curl2sdl_1.askCurlQuestions)(curlFlagValues);
|
|
179
|
-
curl2sdlOptions = {
|
|
180
|
-
...commonOptions,
|
|
181
|
-
...interactiveOptions,
|
|
182
|
-
// include boths headers passed via flags and headers entered interactively
|
|
183
|
-
headers: headers.concat(interactiveOptions.curlArgs.headers),
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
// run non-interactively
|
|
188
|
-
const argsOrError = (0, curl_parser_1.parseCurlArgv)(argv);
|
|
189
|
-
if ('error' in argsOrError) {
|
|
190
|
-
throw new errors_1.CLIError(argsOrError.error);
|
|
191
|
-
}
|
|
192
|
-
const parsedPathParamsOrError = (0, path_params_parser_1.parsePathParamsPattern)(argsOrError.url, flags['path-params']);
|
|
193
|
-
if ('error' in parsedPathParamsOrError) {
|
|
194
|
-
throw new errors_1.CLIError(parsedPathParamsOrError.error);
|
|
195
|
-
}
|
|
196
|
-
const maybeDuplicateParamsMessage = (0, curl2sdl_1.makeDuplicateParamsMessage)(headers, parsedPathParamsOrError, argsOrError.url);
|
|
197
|
-
if (maybeDuplicateParamsMessage) {
|
|
198
|
-
throw new errors_1.CLIError(maybeDuplicateParamsMessage);
|
|
199
|
-
}
|
|
200
|
-
curl2sdlOptions = {
|
|
201
|
-
...commonOptions,
|
|
202
|
-
...curlFlagValues,
|
|
203
|
-
pathParams: parsedPathParamsOrError,
|
|
204
|
-
headers: headers,
|
|
205
|
-
curlArgs: argsOrError,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
const configuration = await (0, configuration_1.readConfiguration)();
|
|
209
|
-
curl2sdlOptions.curlArgs.url = (0, utils_1.rewriteLocalhostToHostGatewayInURL)(configuration, curl2sdlOptions.curlArgs.url);
|
|
210
|
-
const { outPath } = await this.runImportAndExitOnError(() => (0, curl2sdl_1.curl2sdl)(curl2sdlOptions, configuration), 'Please check that the given curl command is valid.');
|
|
211
|
-
return outPath;
|
|
212
|
-
}
|
|
213
|
-
async importGraphQL(argv, flags, commonOptions) {
|
|
214
|
-
const headers = this.parseHeaderFlags(flags.header, flags['header-param']);
|
|
215
|
-
let graphql2sdlOptions;
|
|
216
|
-
const graphqlFlagValues = {
|
|
217
|
-
typePrefix: flags.prefix,
|
|
218
|
-
};
|
|
219
|
-
if (argv.length === 1) {
|
|
220
|
-
// no parameters given: start an interactive prompt
|
|
221
|
-
const interactiveOptions = await (0, graphql2sdl_1.askGraphQLQuestions)(graphqlFlagValues);
|
|
222
|
-
graphql2sdlOptions = {
|
|
223
|
-
...commonOptions,
|
|
224
|
-
...interactiveOptions,
|
|
225
|
-
// include boths headers passed via flags and headers entered interactively
|
|
226
|
-
headers: headers.concat(interactiveOptions.headers),
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
// run non-interactively
|
|
231
|
-
graphql2sdlOptions = {
|
|
232
|
-
...commonOptions,
|
|
233
|
-
...graphqlFlagValues,
|
|
234
|
-
endpoint: argv[1],
|
|
235
|
-
headers: headers,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
const configuration = await (0, configuration_1.readConfiguration)();
|
|
239
|
-
graphql2sdlOptions.endpoint = (0, utils_1.rewriteLocalhostToHostGatewayInURL)(configuration, graphql2sdlOptions.endpoint);
|
|
240
|
-
const { outPath } = await this.runImportAndExitOnError(() => (0, graphql2sdl_1.graphql2sdl)(graphql2sdlOptions, configuration));
|
|
241
|
-
return outPath;
|
|
242
|
-
}
|
|
243
|
-
async importSql(source, dsn, flags, fixedOptions) {
|
|
244
|
-
let sql2sdlOptions;
|
|
245
|
-
const sqlFlagValues = {
|
|
246
|
-
...(dsn ? this.getSqlOptionsFromDSN(dsn) : {}),
|
|
247
|
-
...this.getSqlOptionsFromFlags(flags),
|
|
248
|
-
dbType: source,
|
|
249
|
-
};
|
|
250
|
-
const requiredOptions = sqlFlagValues.dbType === 'snowflake'
|
|
251
|
-
? ['accountId', 'user', 'password', 'database', 'warehouse']
|
|
252
|
-
: ['host', 'user', 'password', 'database'];
|
|
253
|
-
const hasRequiredOptions = requiredOptions.every(option => sqlFlagValues[option] !== undefined);
|
|
254
|
-
if (hasRequiredOptions) {
|
|
255
|
-
// an ugly hint for the TS compiler because it is not smart enough to figure this :(
|
|
256
|
-
const typedSqlFlagValues = sqlFlagValues.dbType === 'snowflake'
|
|
257
|
-
? sqlFlagValues
|
|
258
|
-
: sqlFlagValues;
|
|
259
|
-
// run non-interactively
|
|
260
|
-
sql2sdlOptions = {
|
|
261
|
-
...fixedOptions,
|
|
262
|
-
...typedSqlFlagValues,
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
// one or more required parameters are missing: start an interactive prompt
|
|
267
|
-
const interactiveOptions = source === 'mysql'
|
|
268
|
-
? {
|
|
269
|
-
...(await (0, sql2sdl_1.askSqlQuestions)(source, sqlFlagValues)),
|
|
270
|
-
dbType: source,
|
|
271
|
-
}
|
|
272
|
-
: source === 'postgresql'
|
|
273
|
-
? {
|
|
274
|
-
...(await (0, sql2sdl_1.askSqlQuestions)(source, sqlFlagValues)),
|
|
275
|
-
dbType: source,
|
|
276
|
-
}
|
|
277
|
-
: {
|
|
278
|
-
...(await (0, sql2sdl_1.askSqlQuestions)(source, sqlFlagValues)),
|
|
279
|
-
dbType: source,
|
|
280
|
-
};
|
|
281
|
-
sql2sdlOptions = {
|
|
282
|
-
...fixedOptions,
|
|
283
|
-
...interactiveOptions,
|
|
284
|
-
include: sqlFlagValues.include,
|
|
285
|
-
introspectionOptions: sqlFlagValues.introspectionOptions,
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
const configuration = await (0, configuration_1.readConfiguration)();
|
|
289
|
-
if ('host' in sql2sdlOptions && sql2sdlOptions.host) {
|
|
290
|
-
sql2sdlOptions.host = (0, utils_1.rewriteLocalhostToHostGateway)(configuration, sql2sdlOptions.host);
|
|
291
|
-
}
|
|
292
|
-
if (sql2sdlOptions.dbType === 'snowflake') {
|
|
293
|
-
// Snowflake is very particular about the case in the database and schema names
|
|
294
|
-
sql2sdlOptions.schema = sql2sdlOptions.schema
|
|
295
|
-
? sql2sdlOptions.schema.toUpperCase()
|
|
296
|
-
: 'PUBLIC';
|
|
297
|
-
sql2sdlOptions.database = sql2sdlOptions.database.toUpperCase();
|
|
298
|
-
}
|
|
299
|
-
const { outPath } = await this.runImportAndExitOnError(() => (0, sql2sdl_1.sql2sdl)(source, sql2sdlOptions, configuration), 'Please check that the given database details are valid.');
|
|
300
|
-
return outPath;
|
|
301
|
-
}
|
|
302
|
-
async ensureOnConflictBehavior(workspace, source, flags) {
|
|
303
|
-
const detectNameConflicts = (0, utils_1.getFeatureFlag)(constants_1.DETECT_NAME_CONFLICTS);
|
|
304
|
-
if (!detectNameConflicts) {
|
|
305
|
-
return 'append';
|
|
306
|
-
}
|
|
307
|
-
const name = flags.name || source;
|
|
308
|
-
const hasConflict = fsx.existsSync(path.join(workspace.schema, name));
|
|
309
|
-
if (!hasConflict) {
|
|
310
|
-
return 'append';
|
|
311
|
-
}
|
|
312
|
-
// No `--name` is given (i.e. naming is automatic)
|
|
313
|
-
// Using the default name is going to lead to a conflict, and it should
|
|
314
|
-
// be resolved by inventing a new unique name for the new schema.
|
|
315
|
-
if (!flags.name) {
|
|
316
|
-
return 'append';
|
|
317
|
-
}
|
|
318
|
-
// `--name` is given and (i.e. naming is namaged by the user)
|
|
319
|
-
// There is a naming conflict.
|
|
320
|
-
// The user has explicitly asked to overwrite the existing schema.
|
|
321
|
-
if (flags.overwrite) {
|
|
322
|
-
return 'overwrite';
|
|
323
|
-
}
|
|
324
|
-
// `--name` is given and (i.e. naming is namaged by the user)
|
|
325
|
-
// There is a naming conflict.
|
|
326
|
-
// The user has NOT given an explicit permission to overwrite the existing
|
|
327
|
-
// schema.
|
|
328
|
-
// In an interactive mode the CLI asks them for a confirmation.
|
|
329
|
-
// In a non-interactive mode the CLI throws an error.
|
|
330
|
-
if (flags['non-interactive']) {
|
|
331
|
-
throw new errors_1.CLIError(`A schema named ${chalk.bold(flags.name)} already exists in this workspace. Add an ${chalk.bold('--overwrite')} flag to overwrite it.`);
|
|
332
|
-
}
|
|
333
|
-
this.log(`A schema named ${chalk.bold(flags.name)} already exists in this workspace. Would you like to overwrite it?`);
|
|
334
|
-
const answers = await inquirer.prompt('import', [
|
|
335
|
-
{
|
|
336
|
-
name: 'overwrite',
|
|
337
|
-
type: 'confirm',
|
|
338
|
-
default: false,
|
|
339
|
-
message: `Overwrite ${flags.name}?`,
|
|
340
|
-
},
|
|
341
|
-
]);
|
|
342
|
-
if (answers.overwrite) {
|
|
343
|
-
return 'overwrite';
|
|
344
|
-
}
|
|
345
|
-
return this.exit();
|
|
346
|
-
}
|
|
347
|
-
// notify the user about any ignored flags
|
|
348
|
-
warnAboutIgnoredFlags(source, usedFlags) {
|
|
349
|
-
Object.keys(usedFlags).forEach(flag => {
|
|
350
|
-
const definition = Import.flags[flag];
|
|
351
|
-
if (definition &&
|
|
352
|
-
'dataSources' in definition &&
|
|
353
|
-
definition.dataSources &&
|
|
354
|
-
!definition.dataSources.includes(source)) {
|
|
355
|
-
this.log(chalk.gray(`The ${chalk.bold(`--${flag}`)} flag only applies when importing ${definition.dataSources
|
|
356
|
-
.map(source => chalk.bold(source))
|
|
357
|
-
.join(', ')}. It will be ignored now.`));
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
parseHeaderFlags(headerFlagValues, headerParamFlagValues) {
|
|
362
|
-
const headers = [];
|
|
363
|
-
if (headerFlagValues) {
|
|
364
|
-
headerFlagValues.forEach(value => {
|
|
365
|
-
const headerOrError = (0, header_1.parseHeader)(value);
|
|
366
|
-
if (headerOrError && 'error' in headerOrError) {
|
|
367
|
-
throw new errors_1.CLIError(headerOrError.error);
|
|
368
|
-
}
|
|
369
|
-
// A `null` from parseCurlHeaderString() means a header should NOT be
|
|
370
|
-
// sent. This is not supported by zenserv / the introspection service
|
|
371
|
-
// so the CLI simply omits such headers
|
|
372
|
-
if (headerOrError) {
|
|
373
|
-
headers.push(headerOrError); // header
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
const headersOrError = (0, header_params_parser_1.makeHeaders)(headers, headerParamFlagValues);
|
|
378
|
-
if ('error' in headersOrError) {
|
|
379
|
-
throw new errors_1.CLIError(headersOrError.error);
|
|
380
|
-
}
|
|
381
|
-
return headersOrError;
|
|
382
|
-
}
|
|
383
|
-
getSqlOptionsFromFlags(flags) {
|
|
384
|
-
const options = {
|
|
385
|
-
host: flags['db-host'],
|
|
386
|
-
user: flags['db-user'],
|
|
387
|
-
password: flags['db-password'],
|
|
388
|
-
database: flags['db-database'],
|
|
389
|
-
linkTypes: flags['db-link-types'],
|
|
390
|
-
schema: flags['db-schema'],
|
|
391
|
-
include: flags['db-include'],
|
|
392
|
-
introspectionOptions: {
|
|
393
|
-
// Use the 2023 naming convention by default, and the 2022 naming
|
|
394
|
-
// convention only if explicitly requested by the user.
|
|
395
|
-
naming: flags['db-use-deprecated-2022-naming'] ? '2022' : '2023',
|
|
396
|
-
},
|
|
397
|
-
accountId: flags['snowflake-account-id'],
|
|
398
|
-
warehouse: flags['snowflake-warehouse'],
|
|
399
|
-
};
|
|
400
|
-
// Remove `undefined`-valued properties
|
|
401
|
-
return (0, lodash_1.pickBy)(options, value => value !== undefined);
|
|
402
|
-
}
|
|
403
|
-
getSqlOptionsFromDSN(possibleDsn) {
|
|
404
|
-
const parsed = (0, dsn_parser_1.parseDsn)(possibleDsn);
|
|
405
|
-
if (parsed.success) {
|
|
406
|
-
const value = parsed.value;
|
|
407
|
-
const options = {
|
|
408
|
-
host: value.driver === 'snowflake'
|
|
409
|
-
? undefined
|
|
410
|
-
: value.host + (value.port ? `:${value.port}` : ''),
|
|
411
|
-
accountId: value.driver === 'snowflake' ? value.host : undefined,
|
|
412
|
-
user: value.user,
|
|
413
|
-
password: value.pass,
|
|
414
|
-
database: value.db,
|
|
415
|
-
schema: value.params?.schema === undefined
|
|
416
|
-
? undefined
|
|
417
|
-
: `${value.params?.schema}`,
|
|
418
|
-
warehouse: value.params?.warehouse === undefined
|
|
419
|
-
? undefined
|
|
420
|
-
: `${value.params?.warehouse}`,
|
|
421
|
-
};
|
|
422
|
-
// Remove `undefined`-valued properties
|
|
423
|
-
return (0, lodash_1.pickBy)(options, value => value !== undefined);
|
|
424
|
-
}
|
|
425
|
-
throw new errors_1.CLIError(parsed.message);
|
|
426
|
-
}
|
|
427
|
-
async runImportAndExitOnError(importFn, ctaOnError = '') {
|
|
428
|
-
try {
|
|
429
|
-
const importPromise = importFn();
|
|
430
|
-
let timeoutPromise;
|
|
431
|
-
let resultOrError;
|
|
432
|
-
core_1.ux.action.start('Starting');
|
|
433
|
-
timeoutPromise = (0, utils_1.getTimeout)(5 * 1000);
|
|
434
|
-
resultOrError = await Promise.race([importPromise, timeoutPromise]);
|
|
435
|
-
if (!resultOrError) {
|
|
436
|
-
// 5 sec passed, but no result yet
|
|
437
|
-
core_1.ux.action.start('Creating a GraphQL schema');
|
|
438
|
-
timeoutPromise = (0, utils_1.getTimeout)(5 * 1000);
|
|
439
|
-
resultOrError = await Promise.race([importPromise, timeoutPromise]);
|
|
440
|
-
}
|
|
441
|
-
if (!resultOrError) {
|
|
442
|
-
// another 5 sec passed (10 sec total)
|
|
443
|
-
core_1.ux.action.start('Creating a GraphQL schema (this is taking longer than usual)');
|
|
444
|
-
timeoutPromise = (0, utils_1.getTimeout)(30 * 1000);
|
|
445
|
-
resultOrError = await Promise.race([importPromise, timeoutPromise]);
|
|
446
|
-
}
|
|
447
|
-
if (!resultOrError) {
|
|
448
|
-
// another 30 sec passed (40 sec total)
|
|
449
|
-
core_1.ux.action.start('Creating a GraphQL schema (it may take up to 10 minutes)');
|
|
450
|
-
timeoutPromise = (0, utils_1.getTimeout)((8 * 60 + 20) * 1000);
|
|
451
|
-
resultOrError = await Promise.race([importPromise, timeoutPromise]);
|
|
452
|
-
}
|
|
453
|
-
if (resultOrError) {
|
|
454
|
-
timeoutPromise.cancel();
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
// another 8 min 20 sec passed (9 min total)
|
|
458
|
-
// 9 minutes is the max timeout in the introspection service. If it has
|
|
459
|
-
// not responsed withing this time, there is no point in waiting longer.
|
|
460
|
-
resultOrError = {
|
|
461
|
-
error: 'The StepZen introspection service has timed out.',
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
if ('error' in resultOrError) {
|
|
465
|
-
core_1.ux.action.stop(chalk.red('fail'));
|
|
466
|
-
this.log('A problem occurred while processing your import. ' + ctaOnError);
|
|
467
|
-
this.log(resultOrError.error);
|
|
468
|
-
this.exit(1);
|
|
469
|
-
}
|
|
470
|
-
core_1.ux.action.stop('done');
|
|
471
|
-
return resultOrError;
|
|
472
|
-
}
|
|
473
|
-
catch (error) {
|
|
474
|
-
core_1.ux.action.stop(chalk.red('fail'));
|
|
475
|
-
throw error;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
exports.default = Import;
|
|
480
|
-
Import.description = 'Import a schema for an external data source or an API endpoint to your GraphQL API.';
|
|
481
|
-
Import.flags = {
|
|
482
|
-
...zen_command_1.default.flags,
|
|
483
|
-
dir: Flags.string({ description: 'Working directory' }),
|
|
484
|
-
silent: Flags.boolean({ hidden: true }),
|
|
485
|
-
name: Flags.string({
|
|
486
|
-
description: 'Subfolder inside the workspace folder to save the imported' +
|
|
487
|
-
' schema files to. Defaults to the name of the imported schema.',
|
|
488
|
-
}),
|
|
489
|
-
overwrite: Flags.boolean({
|
|
490
|
-
description: 'Overwrite any existing schema with the same name. Cannot be used' +
|
|
491
|
-
' without also providing a --name flag.',
|
|
492
|
-
dependsOn: ['name'],
|
|
493
|
-
hidden: true,
|
|
494
|
-
}),
|
|
495
|
-
prefix: Flags.string({
|
|
496
|
-
dataSources: ['curl', 'graphql'],
|
|
497
|
-
description: 'Prefix to add to every type in the generated schema',
|
|
498
|
-
}),
|
|
499
|
-
data: Flags.string({
|
|
500
|
-
char: 'd',
|
|
501
|
-
dataSources: ['curl'],
|
|
502
|
-
hidden: true,
|
|
503
|
-
aliases: ['data-ascii', 'data-raw', 'data-binary'],
|
|
504
|
-
multiple: true,
|
|
505
|
-
}),
|
|
506
|
-
request: Flags.string({
|
|
507
|
-
char: 'X',
|
|
508
|
-
dataSources: ['curl'],
|
|
509
|
-
hidden: true,
|
|
510
|
-
}),
|
|
511
|
-
url: Flags.string({
|
|
512
|
-
dataSources: ['curl'],
|
|
513
|
-
hidden: true,
|
|
514
|
-
multiple: true,
|
|
515
|
-
}),
|
|
516
|
-
header: Flags.string({
|
|
517
|
-
char: 'H',
|
|
518
|
-
dataSources: ['curl', 'graphql'],
|
|
519
|
-
description: `Specifies a request header to pass` +
|
|
520
|
-
`\n` +
|
|
521
|
-
`\nExample:` +
|
|
522
|
-
`\nstepzen import curl https://example.com/api/customers \\` +
|
|
523
|
-
`\n\t-H ${chalk.bold('"Authorization: apikey SecretAPIKeyValue"')}`,
|
|
524
|
-
multiple: true,
|
|
525
|
-
}),
|
|
526
|
-
'header-param': Flags.string({
|
|
527
|
-
dataSources: ['curl', 'graphql'],
|
|
528
|
-
description: `Specifies a parameter in a header value.` +
|
|
529
|
-
` Can be formed by taking a ${chalk.bold('-H, --header')} flag and replacing the` +
|
|
530
|
-
` variable part of the header value with a ${chalk.bold('$paramName')} placeholder. Repeat this flag once for each header with a parameter.` +
|
|
531
|
-
`\n` +
|
|
532
|
-
`\nExample:` +
|
|
533
|
-
`\nstepzen import curl https://example.com/api/customers \\` +
|
|
534
|
-
`\n\t-H "Authorization: apikey SecretAPIKeyValue" \\` +
|
|
535
|
-
`\n\t--header-param 'Authorization: apikey ${chalk.bold('$apikey')}'`,
|
|
536
|
-
multiple: true,
|
|
537
|
-
}),
|
|
538
|
-
'query-name': Flags.string({
|
|
539
|
-
dataSources: ['curl'],
|
|
540
|
-
description: '[curl] Property name to add to the Query type as a way to' +
|
|
541
|
-
' access the imported endpoint',
|
|
542
|
-
}),
|
|
543
|
-
'query-type': Flags.string({
|
|
544
|
-
dataSources: ['curl'],
|
|
545
|
-
description: 'Name for the type returned by the curl request in the ' +
|
|
546
|
-
`generated schema. The name specified by ${chalk.bold('--query-type')} is not prefixed by ${chalk.bold('--prefix')} if both flags are present.`,
|
|
547
|
-
}),
|
|
548
|
-
'path-params': Flags.string({
|
|
549
|
-
dataSources: ['curl'],
|
|
550
|
-
description: `Specifies path parameters in the URL path.` +
|
|
551
|
-
` Can be formed by taking the original path and replacing the` +
|
|
552
|
-
` variable segments with ${chalk.bold('$paramName')} placeholders.` +
|
|
553
|
-
`\n` +
|
|
554
|
-
`\nExample:` +
|
|
555
|
-
`\nstepzen import curl https://example.com/users/jane/posts/12` +
|
|
556
|
-
` --path-params` +
|
|
557
|
-
` '/users/${chalk.bold('$userId')}/posts/${chalk.bold('$postId')}'`,
|
|
558
|
-
}),
|
|
559
|
-
'db-host': Flags.string({
|
|
560
|
-
dataSources: ['mysql', 'postgresql'],
|
|
561
|
-
description: 'Database host and optional port (as HOST[:PORT])',
|
|
562
|
-
}),
|
|
563
|
-
'db-user': Flags.string({
|
|
564
|
-
dataSources: ['mysql', 'postgresql', 'snowflake'],
|
|
565
|
-
description: 'Database user name',
|
|
566
|
-
}),
|
|
567
|
-
'db-password': Flags.string({
|
|
568
|
-
dataSources: ['mysql', 'postgresql', 'snowflake'],
|
|
569
|
-
description: 'Database password',
|
|
570
|
-
}),
|
|
571
|
-
'db-database': Flags.string({
|
|
572
|
-
dataSources: ['mysql', 'postgresql', 'snowflake'],
|
|
573
|
-
description: 'Name of database to import',
|
|
574
|
-
}),
|
|
575
|
-
'db-link-types': Flags.boolean({
|
|
576
|
-
dataSources: ['mysql', 'postgresql', 'snowflake'],
|
|
577
|
-
description: `Automatically link types based on` +
|
|
578
|
-
` foreign key relationships using @materializer` +
|
|
579
|
-
` ${chalk.dim('(https://stepzen.com/docs/features/linking-types)')}`,
|
|
580
|
-
}),
|
|
581
|
-
'db-include': Flags.string({
|
|
582
|
-
dataSources: ['mysql', 'postgresql', 'snowflake'],
|
|
583
|
-
description: `Should the generated GraphQL schema` +
|
|
584
|
-
` be based only on database views, only on tables or on both`,
|
|
585
|
-
options: ['tables-only', 'views-only', 'tables-and-views'],
|
|
586
|
-
}),
|
|
587
|
-
'db-schema': Flags.string({
|
|
588
|
-
dataSources: ['postgresql', 'snowflake'],
|
|
589
|
-
description: 'Database schema to import tables from (default: public)',
|
|
590
|
-
}),
|
|
591
|
-
'db-use-deprecated-2022-naming': Flags.boolean({
|
|
592
|
-
description: chalk `[mysql, postgresql] Use the {yellow deprecated} pre-2023 naming` +
|
|
593
|
-
chalk ` convention in the generated GraphQL schema:` +
|
|
594
|
-
chalk `\n\t- the generated type and property names are auto-capitalized` +
|
|
595
|
-
chalk ` into PascalCase` +
|
|
596
|
-
chalk `\n\t- the generated field names use the {italic getCustomer} and` +
|
|
597
|
-
chalk ` {italic getCustomerList} style.` +
|
|
598
|
-
chalk `\n` +
|
|
599
|
-
chalk `\nOn the other hand, when using the default naming convention:` +
|
|
600
|
-
chalk `\n\t- the generated type and property names match exactly the` +
|
|
601
|
-
chalk ` DB table and column names` +
|
|
602
|
-
chalk `\n\t- the generated field names use the {italic customer} and` +
|
|
603
|
-
chalk ` {italic customerList} style.`,
|
|
604
|
-
}),
|
|
605
|
-
'snowflake-account-id': Flags.string({
|
|
606
|
-
dataSources: ['snowflake'],
|
|
607
|
-
description: chalk `Snowflake account identifier in the {bold orgname-accountname}` +
|
|
608
|
-
chalk ` format. For more information, see the Snowflake documentation at` +
|
|
609
|
-
chalk ` https://docs.snowflake.com/en/user-guide/admin-account-identifier.html.`,
|
|
610
|
-
}),
|
|
611
|
-
'snowflake-warehouse': Flags.string({
|
|
612
|
-
dataSources: ['snowflake'],
|
|
613
|
-
description: 'Snowflake warehouse',
|
|
614
|
-
}),
|
|
615
|
-
};
|
|
616
|
-
Import.args = {
|
|
617
|
-
source: core_1.Args.string({ required: true }),
|
|
618
|
-
};
|
|
619
|
-
Import.flagHelpForSource = (source, exclude) => Object.entries(Import.flags)
|
|
620
|
-
.filter(([name, value]) => 'dataSources' in value &&
|
|
621
|
-
value.dataSources?.includes(source) &&
|
|
622
|
-
(!exclude || !exclude.includes(name)))
|
|
623
|
-
.map(([f, d]) => `[--${f}${d.type === 'option' ? `=${f.toUpperCase()}` : ''}]`)
|
|
624
|
-
.join(' ');
|
|
625
|
-
Import.usage = [
|
|
626
|
-
`import curl [[CURLOPTS] URL] ${Import.flagHelpForSource('curl')}`,
|
|
627
|
-
`import graphql [URL] ${Import.flagHelpForSource('graphql')}`,
|
|
628
|
-
`import mysql ${Import.flagHelpForSource('mysql')}`,
|
|
629
|
-
`import mysql://[USER:PASSWORD@]HOST[:PORT][/DATABASE] ${Import.flagHelpForSource('mysql', ['db-host', 'db-user', 'db-password', 'db-database'])}`,
|
|
630
|
-
`import postgresql ${Import.flagHelpForSource('postgresql')}`,
|
|
631
|
-
`import postgresql://[USER:PASSWORD@]HOST[:PORT][/DATABASE] ${Import.flagHelpForSource('postgresql', ['db-host', 'db-user', 'db-password', 'db-database'])}`,
|
|
632
|
-
`import snowflake ${Import.flagHelpForSource('snowflake', ['db-host'])}`,
|
|
633
|
-
`import snowflake://[USER[:PASSWORD]@]ACCOUNT_IDENTIFIER[/DATABASE][?schema=<SCHEMA>[&warehouse=<WAREHOUSE>]] ${Import.flagHelpForSource('snowflake', [
|
|
634
|
-
'db-host',
|
|
635
|
-
'db-user',
|
|
636
|
-
'db-password',
|
|
637
|
-
'db-database',
|
|
638
|
-
'db-schema',
|
|
639
|
-
'snowflake-account-id',
|
|
640
|
-
'snowflake-warehouse',
|
|
641
|
-
])}`,
|
|
642
|
-
];
|
|
643
|
-
// allow any number of arguments to support `import curl [url] [curl flags and options]`
|
|
644
|
-
Import.strict = false;
|
|
645
|
-
//# sourceMappingURL=import.js.map
|