Add subgraphs type to cli schema:fetch (#6255)

This commit is contained in:
jdolle 2025-01-08 04:16:55 -08:00 committed by GitHub
parent a509811736
commit 29c45dfbfc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 166 additions and 29 deletions

View file

@ -0,0 +1,5 @@
---
'@graphql-hive/cli': minor
---
Added subgraph type to schema:fetch cmd to print subgraph details

View file

@ -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,
};
}

View file

@ -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}`),
);
});
});

View file

@ -1 +1 @@
export const version = '0.36.2';
export const version = '0.36.3';

View file

@ -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:

View file

@ -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"

View file

@ -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);
}
}

View 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);
}

View file

@ -1 +1 @@
export const version = '0.33.10';
export const version = '0.33.11';

View file

@ -1 +1 @@
export const version = '0.39.0';
export const version = '0.39.1';

View file

@ -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).