Skip to content

Commit

Permalink
feat(generateClient): GraphQL Admin Client Generation and overall fix…
Browse files Browse the repository at this point in the history
…es (#26)
  • Loading branch information
kon14 authored Sep 9, 2022
1 parent 5e459e0 commit 3eadc7e
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 103 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ The CLI to help you when developing conduit.
[//]: # ([![License](https://img.shields.io/npm/l/conduit-cli.svg)](https://github.com/ConduitPlatform/CLI/blob/main/package.json))

<!-- toc -->
* [Limitations:](#limitations)
* [Limitations](#limitations)
* [Usage](#usage)
* [Commands](#commands)
* [Roadmap](#roadmap)
<!-- tocstop -->

# Limitations:
* Currently, only creates schemas and only for TypeScript
# Limitations

While the CLI is capable of bootstrapping any Conduit release, including legacy ones,
`generateSchema` and `generateClient` commands currently require that you target >= v0.14.5.

# Usage
<!-- usage -->
Expand All @@ -29,7 +31,7 @@ $ npm install -g @conduitplatform/cli
$ conduit COMMAND
running command...
$ conduit (--version|-v)
@conduitplatform/cli/0.0.6 linux-x64 node-v16.15.0
@conduitplatform/cli/0.0.7 linux-x64 node-v16.15.0
$ conduit --help [COMMAND]
USAGE
$ conduit COMMAND
Expand Down Expand Up @@ -189,7 +191,7 @@ USAGE
$ conduit init [-r]
FLAGS
-r, --relogin Reuses url and master key from existing configuration
-r, --relogin Reuses API urls and master key from existing configuration
DESCRIPTION
Initialize the CLI to communicate with Conduit
Expand Down
55 changes: 49 additions & 6 deletions src/commands/generateClient/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import { Command, Flags, CliUx } from '@oclif/core';
import { generate } from '@graphql-codegen/cli';
import { isEmpty } from 'lodash';
import { Requests } from '../../http/http';
import { booleanPrompt } from '../../utils/cli';
import { booleanPrompt, promptWithOptions } from '../../utils/cli';
import { getRequestClient } from '../../utils/requestUtils';
import { getClientType, getOutputPath, getBaseUrl } from '../../utils/generateClient';
import {
getClientType,
getOutputPath,
recoverApiConfigSafe,
} from '../../utils/generateClient';
import { recoverAdminCredentials } from '../../utils/requestUtils';
import { Init } from '../init';

type CONFIG_OPTIONS_BASE_T =
| 'avoidOptionals'
Expand Down Expand Up @@ -54,7 +60,7 @@ export class GenerateClientGraphql extends Command {
];

async run() {
const url = await getBaseUrl(this);
const { adminUrl, appUrl, masterKey } = await recoverApiConfigSafe(this);
const parsedFlags = (await this.parse(GenerateClientGraphql)).flags;
CliUx.ux.action.start('Recovering credentials');
let requestClient: Requests;
Expand All @@ -66,11 +72,32 @@ export class GenerateClientGraphql extends Command {
return;
}
let headers = {};
if (requestClient.securityClient) {
let url: string;
console.log(
`Conduit's GraphQL API supports both application and administration requests.`,
);
const requestType = (await promptWithOptions(
'Specify target request type',
['app', 'admin'],
'app',
)) as 'app' | 'admin';
if (requestType === 'admin') {
const { admin, password } = await this.recoverAdminCredentialsSafe(this);
await requestClient.initialize(admin, password, masterKey, false);
const authToken = await requestClient.loginRequest(admin, password);
url = adminUrl;
headers = {
clientid: requestClient.securityClient.clientId,
clientsecret: requestClient.securityClient.clientSecret,
masterkey: masterKey,
Authorization: `Bearer ${authToken}`,
};
} else {
url = appUrl;
if (requestClient.securityClient) {
headers = {
clientid: requestClient.securityClient.clientId,
clientsecret: requestClient.securityClient.clientSecret,
};
}
}
const clientType = await getClientType(parsedFlags, this.supportedClientTypes);
await this.getConfig(clientType);
Expand Down Expand Up @@ -145,4 +172,20 @@ export class GenerateClientGraphql extends Command {
const validOptions = configOptions.filter(opt => this.genConfig[opt] === true);
this.fileNameSuffix = `.${validOptions.join('.')}`;
}

private async recoverAdminCredentialsSafe(command: Command) {
return await recoverAdminCredentials(command).catch(async () => {
const runInit = await booleanPrompt(
'No configuration found. Run init and proceed?',
'yes',
);
if (!runInit) {
console.log('Aborting');
process.exit(0);
}
const init = new Init(command.argv, command.config);
await init.run();
return await recoverAdminCredentials(command);
});
}
}
20 changes: 10 additions & 10 deletions src/commands/generateClient/rest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Command, Flags, CliUx } from '@oclif/core';
import { promptWithOptions } from '../../utils/cli';
import { getClientType, getOutputPath, getBaseUrl } from '../../utils/generateClient';
import {
getClientType,
getOutputPath,
recoverApiConfigSafe,
} from '../../utils/generateClient';
import * as fs from 'fs';
const { execSync } = require('child_process');

Expand All @@ -25,7 +29,7 @@ export class GenerateClientRest extends Command {
console.log('Failed to detect Java installation.');
CliUx.ux.exit(-1);
}
const url = await getBaseUrl(this);
const { adminUrl, appUrl } = await recoverApiConfigSafe(this);
const parsedFlags = (await this.parse(GenerateClientRest)).flags;
console.log(
`Conduit's REST API supports both application and administration requests.`,
Expand All @@ -38,11 +42,11 @@ export class GenerateClientRest extends Command {
this.getSupportedClientTypes();
const clientType = await getClientType(parsedFlags, this.supportedClientTypes);
const libPath = await getOutputPath(parsedFlags, 'rest', requestType);
const inputSpec = requestType === 'admin' ? 'admin/swagger.json' : 'swagger.json';
const url = `${requestType === 'admin' ? adminUrl : appUrl}/swagger.json`;
try {
execSync(
`npx @openapitools/openapi-generator-cli generate \
-i ${url}/${inputSpec} -g ${clientType} -o ${libPath} --skip-validate-spec`,
-i ${url} -g ${clientType} -o ${libPath} --skip-validate-spec`,
);
const zipPath = await this.convertToZip(libPath);
console.log(`\nClient library archive available in: ${zipPath}`);
Expand All @@ -54,7 +58,7 @@ export class GenerateClientRest extends Command {

private async convertToZip(libPath: string) {
const zipPath = `${libPath}.zip`;
execSync(`zip -r ${zipPath} ${libPath}`);
execSync(`zip -rj ${zipPath} ${libPath}`);
fs.rm(libPath, { recursive: true, force: true }, err => {
if (err) {
console.error(err);
Expand All @@ -79,11 +83,7 @@ export class GenerateClientRest extends Command {

private getJavaVersion() {
try {
const javaVersion = execSync('java --version 2> /dev/null')
.toString()
.split(' ')[1];
console.log(javaVersion);
return javaVersion;
return execSync('java --version 2> /dev/null').toString().split(' ')[1];
} catch (error) {
return false;
}
Expand Down
62 changes: 43 additions & 19 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Command, Flags, CliUx } from '@oclif/core';
import { Requests } from '../http/http';
import { recoverApiConfig, storeConfiguration } from '../utils/requestUtils';
import { booleanPrompt } from '../utils/cli';

export class Init extends Command {
static description = 'Initialize the CLI to communicate with Conduit';
Expand All @@ -16,49 +17,72 @@ Login Successful!
static flags = {
relogin: Flags.boolean({
char: 'r',
description: 'Reuses url and master key from existing configuration',
description: 'Reuses API urls and master key from existing configuration',
}),
};

async run() {
const { flags } = await this.parse(Init);
let url, masterKey;
let adminUrl, appUrl, _masterKey;
if (flags.relogin) {
const obj = await recoverApiConfig(this);
url = obj.url;
masterKey = obj.masterKey;
adminUrl = obj.adminUrl;
appUrl = obj.appUrl;
_masterKey = obj.masterKey;
}
let requestInstance: Requests | undefined;
while (true) {
if (!url) {
url = await CliUx.ux.prompt('Specify the API url of your Conduit installation');
}
if (!masterKey) {
masterKey = await CliUx.ux.prompt(
'Add the master key of your Conduit installation',
if (!adminUrl) {
adminUrl = await CliUx.ux.prompt(
'Specify the Administrative API url of your Conduit installation',
);
}
requestInstance = new Requests(this, url, masterKey);
const pingSuccessful = await requestInstance.httpHealthCheck();
requestInstance = new Requests(this, adminUrl);
const pingSuccessful = await requestInstance.httpHealthCheck('admin');
if (pingSuccessful) break;
console.log(`Could not ping Conduit's HTTP server at ${url}`);
url = undefined;
CliUx.ux.log(
`Could not ping Conduit's Administrative HTTP server at ${adminUrl}\n`,
);
adminUrl = undefined;
}
if (!flags.relogin && !appUrl) {
const usesRouter = await booleanPrompt(
'Does your deployment utilize Conduit Router?',
);
if (usesRouter)
while (true) {
appUrl = await CliUx.ux.prompt(
'Specify the Application API url of your Conduit installation',
);
requestInstance = new Requests(this, adminUrl, appUrl);
const pingSuccessful = await requestInstance.httpHealthCheck('app');
if (pingSuccessful) break;
CliUx.ux.log(`Could not ping Conduit's Application HTTP server at ${appUrl}\n`);
appUrl = undefined;
}
}
let admin: string;
let password: string;
let masterKey: string;
while (true) {
if (!_masterKey) {
masterKey = await CliUx.ux.prompt(
'Add the master key of your Conduit installation',
);
} else {
masterKey = _masterKey;
}
admin = await CliUx.ux.prompt('Specify the admin username');
password = await CliUx.ux.prompt('Specify the admin password');
CliUx.ux.action.start('Attempting login');
try {
await requestInstance.loginRequest(admin, password);
await requestInstance.initialize(admin, password, masterKey, !!appUrl);
CliUx.ux.action.stop('Login Successful!');
break;
} catch (e) {
CliUx.ux.action.stop('Login failed!\n\n');
} catch {
CliUx.ux.action.stop('Login failed!\n');
}
}
await requestInstance.initialize(admin, password); // handle additional configuration
await storeConfiguration(this, { url, masterKey }, { admin, password });
await storeConfiguration(this, { adminUrl, appUrl, masterKey }, { admin, password });
}
}
Loading

0 comments on commit 3eadc7e

Please sign in to comment.