-
Notifications
You must be signed in to change notification settings - Fork 249
feat: add cli commands to create,publish and delete grpc subgraphs #2179
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
Merged
Merged
Changes from 2 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d49fbb2
feat: add cli commands to create,publish and delete grpc subgraphs
JivusAyrus 586f83c
fix: correct formatting in error message for unsupported subgraph types
JivusAyrus c301fba
fix: pr suggestions
JivusAyrus 83b6df1
fix: improvements
JivusAyrus 66bcbc8
fix: add headers to publish requests and refactor parameter handling
JivusAyrus 12130c5
fix: lint
JivusAyrus c537c4c
Merge branch 'main' into suvij/eng-7953-grpc-services
JivusAyrus 22e3aca
Merge branch 'main' into suvij/eng-7953-grpc-services
JivusAyrus 5519107
Merge branch 'main' into suvij/eng-7953-grpc-services
JivusAyrus b8e6671
Merge branch 'main' into suvij/eng-7953-grpc-services
JivusAyrus e8574e0
fix: pr suggestions
JivusAyrus f43bd07
fix: lint
JivusAyrus 3638f9a
Merge branch 'main' into suvij/eng-7953-grpc-services
JivusAyrus cf2f4f1
Merge branch 'main' into suvij/eng-7953-grpc-services
JivusAyrus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import { existsSync } from 'node:fs'; | ||
| import { readFile } from 'node:fs/promises'; | ||
| import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb'; | ||
| import { SubgraphType } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb'; | ||
| import { splitLabel } from '@wundergraph/cosmo-shared'; | ||
| import { Command, program } from 'commander'; | ||
| import ora from 'ora'; | ||
| import { resolve } from 'pathe'; | ||
| import pc from 'picocolors'; | ||
| import { getBaseHeaders } from '../../../core/config.js'; | ||
| import { BaseCommandOptions } from '../../../core/types/types.js'; | ||
|
|
||
| export default (opts: BaseCommandOptions) => { | ||
| const command = new Command('create'); | ||
| command.description('Creates a federated grpc subgraph on the control plane.'); | ||
| command.argument( | ||
| '<name>', | ||
| 'The name of the grpc subgraph to create. It is usually in the format of <org>.<service.name> and is used to uniquely identify your grpc subgraph.', | ||
| ); | ||
| command.option('-n, --namespace [string]', 'The namespace of the grpc subgraph.'); | ||
| command.requiredOption( | ||
| '-r, --routing-url <url>', | ||
| 'The routing URL of your subgraph. This is the url at which the subgraph will be accessible.', | ||
| ); | ||
| command.option( | ||
| '--label [labels...]', | ||
| 'The labels to apply to the subgraph. The labels are passed in the format <key>=<value> <key>=<value>.', | ||
| ); | ||
| command.option('--readme <path-to-readme>', 'The markdown file which describes the subgraph.'); | ||
|
|
||
| command.action(async (name, options) => { | ||
| let readmeFile; | ||
| if (options.readme) { | ||
| readmeFile = resolve(options.readme); | ||
| if (!existsSync(readmeFile)) { | ||
| program.error( | ||
| pc.red( | ||
| pc.bold(`The readme file '${pc.bold(readmeFile)}' does not exist. Please check the path and try again.`), | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| const spinner = ora('GRPC Subgraph is being created...').start(); | ||
| const resp = await opts.client.platform.createFederatedSubgraph( | ||
| { | ||
| name, | ||
| namespace: options.namespace, | ||
| labels: options.label ? options.label.map((label: string) => splitLabel(label)) : [], | ||
| routingUrl: options.routingUrl, | ||
| readme: readmeFile ? await readFile(readmeFile, 'utf8') : undefined, | ||
| type: SubgraphType.GRPC_SERVICE, | ||
| }, | ||
| { | ||
| headers: getBaseHeaders(), | ||
| }, | ||
| ); | ||
|
|
||
| if (resp.response?.code === EnumStatusCode.OK) { | ||
| spinner.succeed('GRPC subgraph was created successfully.'); | ||
| } else { | ||
| spinner.fail('Failed to create grpc subgraph.'); | ||
| if (resp.response?.details) { | ||
| console.log(pc.red(pc.bold(resp.response?.details))); | ||
| } | ||
| process.exitCode = 1; | ||
| // eslint-disable-next-line no-useless-return | ||
| return; | ||
| } | ||
| }); | ||
|
|
||
| return command; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| import { Command } from 'commander'; | ||
| import pc from 'picocolors'; | ||
| import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb'; | ||
| import inquirer from 'inquirer'; | ||
| import Table from 'cli-table3'; | ||
| import ora from 'ora'; | ||
| import { BaseCommandOptions } from '../../../core/types/types.js'; | ||
| import { getBaseHeaders } from '../../../core/config.js'; | ||
|
|
||
| export default (opts: BaseCommandOptions) => { | ||
| const command = new Command('delete'); | ||
| command.description('Deletes a grpc subgraph on the control plane.'); | ||
| command.argument('<name>', 'The name of the grpc subgraph to delete.'); | ||
| command.option('-n, --namespace [string]', 'The namespace of the grpc subgraph.'); | ||
| command.option('-f --force', 'Flag to force the deletion (skip confirmation).'); | ||
| command.option('--suppress-warnings', 'This flag suppresses any warnings produced by composition.'); | ||
| command.action(async (name, options) => { | ||
| if (!options.force) { | ||
| const deletionConfirmed = await inquirer.prompt({ | ||
| name: 'confirmDeletion', | ||
| type: 'confirm', | ||
| message: `Are you sure you want to delete the plugin subgraph "${name}"?`, | ||
| }); | ||
| if (!deletionConfirmed.confirmDeletion) { | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| process.exitCode = 1; | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| const spinner = ora(`The grpc subgraph "${name}" is being deleted...`).start(); | ||
|
|
||
| const resp = await opts.client.platform.deleteFederatedSubgraph( | ||
| { | ||
| subgraphName: name, | ||
| namespace: options.namespace, | ||
| }, | ||
| { | ||
| headers: getBaseHeaders(), | ||
| }, | ||
| ); | ||
|
|
||
| switch (resp.response?.code) { | ||
| case EnumStatusCode.OK: { | ||
| spinner.succeed(`The grpc subgraph "${name}" was deleted successfully.`); | ||
| if (resp.proposalMatchMessage) { | ||
| console.log(pc.yellow(`Warning: Proposal match failed`)); | ||
| console.log(pc.yellow(resp.proposalMatchMessage)); | ||
| } | ||
| break; | ||
| } | ||
| case EnumStatusCode.ERR_SCHEMA_MISMATCH_WITH_APPROVED_PROPOSAL: { | ||
| spinner.fail(`Failed to delete grpc subgraph "${name}".`); | ||
| console.log(pc.red(`Error: Proposal match failed`)); | ||
| console.log(pc.red(resp.proposalMatchMessage)); | ||
| break; | ||
| } | ||
| case EnumStatusCode.ERR_SUBGRAPH_COMPOSITION_FAILED: { | ||
| spinner.fail(`The grpc subgraph "${name}" was deleted but with composition errors.`); | ||
|
|
||
| const compositionErrorsTable = new Table({ | ||
| head: [ | ||
| pc.bold(pc.white('FEDERATED_GRAPH_NAME')), | ||
| pc.bold(pc.white('NAMESPACE')), | ||
| pc.bold(pc.white('FEATURE_FLAG')), | ||
| pc.bold(pc.white('ERROR_MESSAGE')), | ||
| ], | ||
| colWidths: [30, 30, 30, 120], | ||
| wordWrap: true, | ||
| }); | ||
|
|
||
| console.log( | ||
| pc.red( | ||
| `There were composition errors when composing at least one federated graph related to the` + | ||
| ` grpc subgraph "${name}".\nThe router will continue to work with the latest valid schema.` + | ||
| `\n${pc.bold('Please check the errors below:')}`, | ||
| ), | ||
| ); | ||
| for (const compositionError of resp.compositionErrors) { | ||
| compositionErrorsTable.push([ | ||
| compositionError.federatedGraphName, | ||
| compositionError.namespace, | ||
| compositionError.featureFlag || '-', | ||
| compositionError.message, | ||
| ]); | ||
| } | ||
| // Don't exit here with 1 because the change was still applied | ||
| console.log(compositionErrorsTable.toString()); | ||
|
|
||
| break; | ||
| } | ||
| case EnumStatusCode.ERR_DEPLOYMENT_FAILED: { | ||
| spinner.warn( | ||
| `The grpc subgraph "${name}" was deleted, but the updated composition could not be deployed.` + | ||
| `\nThis means the updated composition is not accessible to the router.` + | ||
| `\n${pc.bold('Please check the errors below:')}`, | ||
| ); | ||
|
|
||
| const deploymentErrorsTable = new Table({ | ||
| head: [ | ||
| pc.bold(pc.white('FEDERATED_GRAPH_NAME')), | ||
| pc.bold(pc.white('NAMESPACE')), | ||
| pc.bold(pc.white('ERROR_MESSAGE')), | ||
| ], | ||
| colWidths: [30, 30, 120], | ||
| wordWrap: true, | ||
| }); | ||
|
|
||
| for (const deploymentError of resp.deploymentErrors) { | ||
| deploymentErrorsTable.push([ | ||
| deploymentError.federatedGraphName, | ||
| deploymentError.namespace, | ||
| deploymentError.message, | ||
| ]); | ||
| } | ||
| // Don't exit here with 1 because the change was still applied | ||
| console.log(deploymentErrorsTable.toString()); | ||
|
|
||
| break; | ||
| } | ||
| default: { | ||
| spinner.fail(`Failed to delete the grpc subgraph "${name}".`); | ||
| if (resp.response?.details) { | ||
| console.log(pc.red(pc.bold(resp.response?.details))); | ||
| } | ||
| process.exitCode = 1; | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| if (!options.suppressWarnings && resp.compositionWarnings.length > 0) { | ||
| const compositionWarningsTable = new Table({ | ||
| head: [ | ||
| pc.bold(pc.white('FEDERATED_GRAPH_NAME')), | ||
| pc.bold(pc.white('NAMESPACE')), | ||
| pc.bold(pc.white('FEATURE_FLAG')), | ||
| pc.bold(pc.white('WARNING_MESSAGE')), | ||
| ], | ||
| colWidths: [30, 30, 30, 120], | ||
| wordWrap: true, | ||
| }); | ||
|
|
||
| console.log(pc.yellow(`The following warnings were produced while composing the federated graph:`)); | ||
| for (const compositionWarning of resp.compositionWarnings) { | ||
| compositionWarningsTable.push([ | ||
| compositionWarning.federatedGraphName, | ||
| compositionWarning.namespace, | ||
| compositionWarning.featureFlag || '-', | ||
| compositionWarning.message, | ||
| ]); | ||
| } | ||
| console.log(compositionWarningsTable.toString()); | ||
| } | ||
| }); | ||
|
|
||
| return command; | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.