mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
Add subgraphs type to cli schema:fetch (#6255)
This commit is contained in:
parent
a509811736
commit
29c45dfbfc
11 changed files with 166 additions and 29 deletions
5
.changeset/big-experts-melt.md
Normal file
5
.changeset/big-experts-melt.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@graphql-hive/cli': minor
|
||||
---
|
||||
|
||||
Added subgraph type to schema:fetch cmd to print subgraph details
|
||||
|
|
@ -62,6 +62,14 @@ export async function schemaDelete(args: string[]) {
|
|||
);
|
||||
}
|
||||
|
||||
export async function schemaFetch(args: string[]) {
|
||||
const registryAddress = await getServiceHost('server', 8082);
|
||||
|
||||
return await exec(
|
||||
['schema:fetch', `--registry.endpoint`, `http://${registryAddress}/graphql`, ...args].join(' '),
|
||||
);
|
||||
}
|
||||
|
||||
async function dev(args: string[]) {
|
||||
const registryAddress = await getServiceHost('server', 8082);
|
||||
|
||||
|
|
@ -288,10 +296,17 @@ export function createCLI(tokens: { readwrite: string; readonly: string }) {
|
|||
]);
|
||||
}
|
||||
|
||||
async function fetchCmd(input: { type: 'subgraphs' | 'supergraph' | 'sdl'; actionId: string }) {
|
||||
const cmd = schemaFetch(['--token', tokens.readwrite, '--type', input.type, input.actionId]);
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
return {
|
||||
publish,
|
||||
check,
|
||||
delete: deleteCommand,
|
||||
dev: devCmd,
|
||||
fetch: fetchCmd,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable no-process-env */
|
||||
import { createHash } from 'node:crypto';
|
||||
import { ProjectType } from 'testkit/gql/graphql';
|
||||
import { createCLI, schemaCheck, schemaPublish } from '../../testkit/cli';
|
||||
import { createCLI, schemaCheck, schemaFetch, schemaPublish } from '../../testkit/cli';
|
||||
import { initSeed } from '../../testkit/seed';
|
||||
|
||||
describe.each`
|
||||
|
|
@ -281,4 +281,45 @@ describe.each`
|
|||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test.concurrent('schema:fetch can fetch a schema with target:registry:read access', async () => {
|
||||
const { createOrg } = await initSeed().createOwner();
|
||||
const { inviteAndJoinMember, createProject } = await createOrg();
|
||||
await inviteAndJoinMember();
|
||||
const { createTargetAccessToken } = await createProject(projectType, {
|
||||
useLegacyRegistryModels: model === 'legacy',
|
||||
});
|
||||
const { secret, latestSchema } = await createTargetAccessToken({});
|
||||
|
||||
const cli = createCLI({
|
||||
readonly: secret,
|
||||
readwrite: secret,
|
||||
});
|
||||
|
||||
await schemaPublish([
|
||||
'--registry.accessToken',
|
||||
secret,
|
||||
'--author',
|
||||
'Kamil',
|
||||
'--commit',
|
||||
'abc123',
|
||||
...serviceNameArgs,
|
||||
...serviceUrlArgs,
|
||||
'fixtures/init-schema.graphql',
|
||||
]);
|
||||
|
||||
const schema = await latestSchema();
|
||||
const numSchemas = schema.latestVersion?.schemas.nodes.length;
|
||||
const fetchCmd = cli.fetch({
|
||||
type: 'subgraphs',
|
||||
actionId: 'abc123',
|
||||
});
|
||||
const rHeader = `service\\s+url\\s+date`;
|
||||
const rUrl = `http:\\/\\/\\S+(:\\d+)?|n/a`;
|
||||
const rSubgraph = `[-]+\\s+\\S+\\s+(${rUrl})\\s+\\S+Z\\s+`;
|
||||
const rFooter = `subgraphs length: ${numSchemas}`;
|
||||
await expect(fetchCmd).resolves.toMatch(
|
||||
new RegExp(`${rHeader}\\s+(${rSubgraph}){${numSchemas}}${rFooter}`),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
export const version = '0.36.2';
|
||||
export const version = '0.36.3';
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ _See code:
|
|||
|
||||
## `hive schema:fetch ACTIONID`
|
||||
|
||||
fetch schema or supergraph from the Hive API
|
||||
fetch a schema, supergraph, or list of subgraphs from the Hive API
|
||||
|
||||
```
|
||||
USAGE
|
||||
|
|
@ -399,11 +399,11 @@ FLAGS
|
|||
--registry.accessToken=<value> registry access token
|
||||
--registry.endpoint=<value> registry endpoint
|
||||
--token=<value> api token
|
||||
--type=<value> Type to fetch (possible types: sdl, supergraph)
|
||||
--type=<value> Type to fetch (possible types: sdl, supergraph, subgraphs)
|
||||
--write=<value> Write to a file (possible extensions: .graphql, .gql, .gqls, .graphqls)
|
||||
|
||||
DESCRIPTION
|
||||
fetch schema or supergraph from the Hive API
|
||||
fetch a schema, supergraph, or list of subgraphs from the Hive API
|
||||
```
|
||||
|
||||
_See code:
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
"schema:check:federation": "pnpm start schema:check examples/federation.graphql --service reviews",
|
||||
"schema:check:single": "pnpm start schema:check examples/single.graphql",
|
||||
"schema:check:stitching": "pnpm start schema:check --service posts examples/stitching.posts.graphql",
|
||||
"schema:fetch:subgraphs": "pnpm start schema:fetch --type=subgraphs",
|
||||
"schema:publish:federation": "pnpm start schema:publish --service reviews --url reviews.com/graphql examples/federation.reviews.graphql",
|
||||
"start": "./bin/dev",
|
||||
"version": "oclif readme && git add README.md"
|
||||
|
|
|
|||
|
|
@ -5,24 +5,42 @@ import Command from '../../base-command';
|
|||
import { graphql } from '../../gql';
|
||||
import { graphqlEndpoint } from '../../helpers/config';
|
||||
import { ACCESS_TOKEN_MISSING } from '../../helpers/errors';
|
||||
import { printTable } from '../../helpers/print-table';
|
||||
|
||||
const SchemaVersionForActionIdQuery = graphql(/* GraphQL */ `
|
||||
query SchemaVersionForActionId(
|
||||
$actionId: ID!
|
||||
$includeSDL: Boolean!
|
||||
$includeSupergraph: Boolean!
|
||||
$includeSubgraphs: Boolean!
|
||||
) {
|
||||
schemaVersionForActionId(actionId: $actionId) {
|
||||
id
|
||||
valid
|
||||
sdl @include(if: $includeSDL)
|
||||
supergraph @include(if: $includeSupergraph)
|
||||
schemas @include(if: $includeSubgraphs) {
|
||||
nodes {
|
||||
__typename
|
||||
... on SingleSchema {
|
||||
id
|
||||
date
|
||||
}
|
||||
... on CompositeSchema {
|
||||
id
|
||||
date
|
||||
url
|
||||
service
|
||||
}
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
export default class SchemaFetch extends Command<typeof SchemaFetch> {
|
||||
static description = 'fetch schema or supergraph from the Hive API';
|
||||
static description = 'fetch a schema, supergraph, or list of subgraphs from the Hive API';
|
||||
static flags = {
|
||||
/** @deprecated */
|
||||
registry: Flags.string({
|
||||
|
|
@ -48,7 +66,7 @@ export default class SchemaFetch extends Command<typeof SchemaFetch> {
|
|||
}),
|
||||
type: Flags.string({
|
||||
aliases: ['T'],
|
||||
description: 'Type to fetch (possible types: sdl, supergraph)',
|
||||
description: 'Type to fetch (possible types: sdl, supergraph, subgraphs)',
|
||||
}),
|
||||
write: Flags.string({
|
||||
aliases: ['W'],
|
||||
|
|
@ -105,6 +123,7 @@ export default class SchemaFetch extends Command<typeof SchemaFetch> {
|
|||
actionId,
|
||||
includeSDL: sdlType === 'sdl',
|
||||
includeSupergraph: sdlType === 'supergraph',
|
||||
includeSubgraphs: sdlType === 'subgraphs',
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -116,28 +135,49 @@ export default class SchemaFetch extends Command<typeof SchemaFetch> {
|
|||
return this.error(`Schema is invalid for action id ${actionId}`);
|
||||
}
|
||||
|
||||
const schema =
|
||||
result.schemaVersionForActionId.sdl ?? result.schemaVersionForActionId.supergraph;
|
||||
if (result.schemaVersionForActionId?.schemas) {
|
||||
const { total, nodes } = result.schemaVersionForActionId.schemas;
|
||||
const table = [
|
||||
['service', 'url', 'date'],
|
||||
...nodes.map(node => [
|
||||
/** @ts-expect-error: If service is undefined then use id. */
|
||||
node.service ?? node.id,
|
||||
node.__typename === 'CompositeSchema' ? node.url : 'n/a',
|
||||
node.date as string,
|
||||
]),
|
||||
];
|
||||
const stats = `subgraphs length: ${total}`;
|
||||
const printed = `${printTable(table)}\n\r${stats}`;
|
||||
|
||||
if (schema == null) {
|
||||
return this.error(`No ${sdlType} found for action id ${actionId}`);
|
||||
}
|
||||
|
||||
if (flags.write) {
|
||||
const filepath = resolve(process.cwd(), flags.write);
|
||||
switch (extname(flags.write.toLowerCase())) {
|
||||
case '.graphql':
|
||||
case '.gql':
|
||||
case '.gqls':
|
||||
case '.graphqls':
|
||||
await writeFile(filepath, schema, 'utf8');
|
||||
break;
|
||||
default:
|
||||
this.fail(`Unsupported file extension ${extname(flags.write)}`);
|
||||
this.exit(1);
|
||||
if (flags.write) {
|
||||
const filepath = resolve(process.cwd(), flags.write);
|
||||
await writeFile(filepath, printed, 'utf8');
|
||||
}
|
||||
return;
|
||||
this.log(printed);
|
||||
} else {
|
||||
const schema =
|
||||
result.schemaVersionForActionId.sdl ?? result.schemaVersionForActionId.supergraph;
|
||||
|
||||
if (schema == null) {
|
||||
return this.error(`No ${sdlType} found for action id ${actionId}`);
|
||||
}
|
||||
|
||||
if (flags.write) {
|
||||
const filepath = resolve(process.cwd(), flags.write);
|
||||
switch (extname(flags.write.toLowerCase())) {
|
||||
case '.graphql':
|
||||
case '.gql':
|
||||
case '.gqls':
|
||||
case '.graphqls':
|
||||
await writeFile(filepath, schema, 'utf8');
|
||||
break;
|
||||
default:
|
||||
this.fail(`Unsupported file extension ${extname(flags.write)}`);
|
||||
this.exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.log(schema);
|
||||
}
|
||||
this.log(schema);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
packages/libraries/cli/src/helpers/print-table.ts
Normal file
29
packages/libraries/cli/src/helpers/print-table.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
type TableCell = string | number | Date;
|
||||
type Table = TableCell[][];
|
||||
|
||||
function printCell(cell: TableCell): string {
|
||||
if (cell instanceof Date) {
|
||||
return cell.toISOString();
|
||||
}
|
||||
return String(cell);
|
||||
}
|
||||
|
||||
function mapTable<T, O>(table: T[][], map: (cell: T, row: number, col: number) => O): O[][] {
|
||||
return table.map((row, r) => row.map((cell, c) => map(cell, r, c)));
|
||||
}
|
||||
|
||||
export function printTable(table: Table) {
|
||||
const printedTable = mapTable(table, printCell);
|
||||
const columnWidths: number[] = [];
|
||||
for (let row of printedTable) {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
const cell = row[i];
|
||||
columnWidths[i] = Math.max(columnWidths[i] || 0, cell.length);
|
||||
}
|
||||
}
|
||||
const totalWidth = columnWidths.reduce((acc, n) => acc + n, 0) + (columnWidths.length - 1) * 2;
|
||||
const divider = `${'-'.repeat(totalWidth)}\n`;
|
||||
|
||||
const paddedTable = mapTable(printedTable, (cell, r, c) => cell.padEnd(columnWidths[c] + 2));
|
||||
return paddedTable.map(row => `${row.join('')}\n`).join(divider);
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
export const version = '0.33.10';
|
||||
export const version = '0.33.11';
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
export const version = '0.39.0';
|
||||
export const version = '0.39.1';
|
||||
|
|
|
|||
|
|
@ -398,6 +398,12 @@ For projects with a supergraph it is also possible to fetch the supergraph.
|
|||
hive schema:fetch --type supergraph --write supergraph.graphql feb8aa9ec8932eb
|
||||
```
|
||||
|
||||
It is also possible to print a list of subgraph details in an ascii table.
|
||||
|
||||
```bash
|
||||
hive schema:fetch --type subgraphs feb8aa9ec8932eb
|
||||
```
|
||||
|
||||
For more information please refer to the
|
||||
[CLI readme](https://github.com/graphql-hive/platform/blob/main/packages/libraries/cli/README.md#commands).
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue