@graphql-mesh/compose-cli 0.4.0 → 0.4.1-alpha-20240626143434-7cd2ddcf1e46bd3f52ea1e6276c1d9beeb5171a4
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/cjs/getComposedSchemaFromConfig.js +41 -11
- package/cjs/loadGraphQLHTTPSubgraph.js +73 -50
- package/cjs/run.js +8 -10
- package/cjs/types.js +1 -2
- package/esm/getComposedSchemaFromConfig.js +40 -9
- package/esm/loadGraphQLHTTPSubgraph.js +73 -49
- package/esm/run.js +7 -8
- package/package.json +5 -4
- package/typings/getComposedSchemaFromConfig.d.cts +1 -2
- package/typings/getComposedSchemaFromConfig.d.ts +1 -2
- package/typings/loadGraphQLHTTPSubgraph.d.cts +3 -3
- package/typings/loadGraphQLHTTPSubgraph.d.ts +3 -3
- package/typings/run.d.cts +1 -0
- package/typings/run.d.ts +1 -0
- package/typings/types.d.cts +3 -1
- package/typings/types.d.ts +3 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getComposedSchemaFromConfig =
|
|
3
|
+
exports.getComposedSchemaFromConfig = getComposedSchemaFromConfig;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
4
5
|
const fusion_composition_1 = require("@graphql-mesh/fusion-composition");
|
|
5
6
|
const graphql_file_loader_1 = require("@graphql-tools/graphql-file-loader");
|
|
6
7
|
const load_1 = require("@graphql-tools/load");
|
|
8
|
+
const schema_1 = require("@graphql-tools/schema");
|
|
9
|
+
const utils_1 = require("@graphql-tools/utils");
|
|
7
10
|
const fetch_1 = require("@whatwg-node/fetch");
|
|
8
11
|
async function getComposedSchemaFromConfig(config, logger) {
|
|
9
12
|
const ctx = {
|
|
@@ -20,7 +23,8 @@ async function getComposedSchemaFromConfig(config, logger) {
|
|
|
20
23
|
subgraphSchema = await schema$;
|
|
21
24
|
}
|
|
22
25
|
catch (e) {
|
|
23
|
-
|
|
26
|
+
log.error(`Failed to load subgraph ${subgraphName}`);
|
|
27
|
+
throw e;
|
|
24
28
|
}
|
|
25
29
|
return {
|
|
26
30
|
name: subgraphName,
|
|
@@ -38,15 +42,41 @@ async function getComposedSchemaFromConfig(config, logger) {
|
|
|
38
42
|
});
|
|
39
43
|
additionalTypeDefs = result.map(r => r.document || r.rawSDL);
|
|
40
44
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
const result = (0, fusion_composition_1.composeSubgraphs)(subgraphConfigsForComposition);
|
|
46
|
+
if (result.errors?.length) {
|
|
47
|
+
throw new Error(`Failed to compose subgraphs; \n${result.errors.map(e => `- ${e.message}`).join('\n')}`);
|
|
48
|
+
}
|
|
49
|
+
if (config.subgraph) {
|
|
50
|
+
const subgraph = result.annotatedSubgraphs.find(sg => sg.name === config.subgraph);
|
|
51
|
+
if (!subgraph) {
|
|
52
|
+
throw new Error(`Subgraph ${config.subgraph} not found`);
|
|
48
53
|
}
|
|
54
|
+
return (0, graphql_1.print)(subgraph.typeDefs);
|
|
55
|
+
}
|
|
56
|
+
if (!result.supergraphSdl) {
|
|
57
|
+
throw new Error(`Unknown error: composed schema is empty`);
|
|
49
58
|
}
|
|
50
|
-
|
|
59
|
+
if (additionalTypeDefs?.length || config.transforms?.length) {
|
|
60
|
+
let composedSchema = (0, graphql_1.buildSchema)(result.supergraphSdl, {
|
|
61
|
+
noLocation: true,
|
|
62
|
+
assumeValid: true,
|
|
63
|
+
assumeValidSDL: true,
|
|
64
|
+
});
|
|
65
|
+
if (additionalTypeDefs?.length) {
|
|
66
|
+
composedSchema = (0, schema_1.mergeSchemas)({
|
|
67
|
+
schemas: [composedSchema],
|
|
68
|
+
typeDefs: additionalTypeDefs,
|
|
69
|
+
assumeValid: true,
|
|
70
|
+
assumeValidSDL: true,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (config.transforms?.length) {
|
|
74
|
+
logger.info('Applying transforms');
|
|
75
|
+
for (const transform of config.transforms) {
|
|
76
|
+
composedSchema = transform(composedSchema);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return (0, utils_1.printSchemaWithDirectives)(composedSchema);
|
|
80
|
+
}
|
|
81
|
+
return result.supergraphSdl;
|
|
51
82
|
}
|
|
52
|
-
exports.getComposedSchemaFromConfig = getComposedSchemaFromConfig;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadGraphQLHTTPSubgraph =
|
|
3
|
+
exports.loadGraphQLHTTPSubgraph = loadGraphQLHTTPSubgraph;
|
|
4
4
|
const graphql_1 = require("graphql");
|
|
5
|
-
function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGETForQueries, operationHeaders, credentials, retry, timeout, source, schemaHeaders,
|
|
5
|
+
function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGETForQueries, operationHeaders, credentials, retry, timeout, source, schemaHeaders, federation = false, transportKind = 'http', }) {
|
|
6
6
|
return (ctx) => {
|
|
7
7
|
let schema$;
|
|
8
8
|
function handleFetchedSchema(schema) {
|
|
@@ -10,7 +10,7 @@ function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGETForQuer
|
|
|
10
10
|
kind: transportKind,
|
|
11
11
|
subgraph: subgraphName,
|
|
12
12
|
location: endpoint,
|
|
13
|
-
headers: operationHeaders,
|
|
13
|
+
headers: operationHeaders ? Object.entries(operationHeaders) : undefined,
|
|
14
14
|
options: {
|
|
15
15
|
method,
|
|
16
16
|
useGETForQueries,
|
|
@@ -35,7 +35,7 @@ function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGETForQuer
|
|
|
35
35
|
kind: transportKind,
|
|
36
36
|
subgraph: subgraphName,
|
|
37
37
|
location: endpoint,
|
|
38
|
-
headers: operationHeaders,
|
|
38
|
+
headers: operationHeaders ? Object.entries(operationHeaders) : undefined,
|
|
39
39
|
options: {
|
|
40
40
|
method,
|
|
41
41
|
useGETForQueries,
|
|
@@ -46,50 +46,58 @@ function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGETForQuer
|
|
|
46
46
|
}, schema));
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
49
|
+
const fetchAsRegular = () => ctx
|
|
50
|
+
.fetch(endpoint, {
|
|
51
|
+
method: method || (useGETForQueries ? 'GET' : 'POST'),
|
|
52
|
+
headers: {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
},
|
|
55
|
+
body: JSON.stringify({
|
|
56
|
+
query: (0, graphql_1.getIntrospectionQuery)(),
|
|
57
|
+
}),
|
|
58
|
+
})
|
|
59
|
+
.then(res => {
|
|
60
|
+
assertResponseOk(res);
|
|
61
|
+
return res.json();
|
|
62
|
+
})
|
|
63
|
+
.then((result) => (0, graphql_1.buildClientSchema)(result.data, {
|
|
64
|
+
assumeValid: true,
|
|
65
|
+
}))
|
|
66
|
+
.then(schema => {
|
|
67
|
+
const queryType = schema.getQueryType();
|
|
68
|
+
const queryFields = queryType?.getFields();
|
|
69
|
+
if (queryFields._service) {
|
|
70
|
+
const serviceType = (0, graphql_1.getNamedType)(queryFields._service.type);
|
|
71
|
+
if ((0, graphql_1.isObjectType)(serviceType)) {
|
|
72
|
+
const serviceTypeFields = serviceType.getFields();
|
|
73
|
+
if (serviceTypeFields.sdl) {
|
|
74
|
+
return fetchAsFederation();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return schema;
|
|
79
|
+
});
|
|
80
|
+
const fetchAsFederation = () => ctx
|
|
81
|
+
.fetch(endpoint, {
|
|
82
|
+
method: method || (useGETForQueries ? 'GET' : 'POST'),
|
|
83
|
+
headers: {
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
query: federationIntrospectionQuery,
|
|
88
|
+
}),
|
|
89
|
+
})
|
|
90
|
+
.then(res => {
|
|
91
|
+
assertResponseOk(res);
|
|
92
|
+
return res.json();
|
|
93
|
+
})
|
|
94
|
+
.then((result) => result?.data?._service?.sdl)
|
|
95
|
+
.then(sdl => (0, graphql_1.buildSchema)(sdl, {
|
|
96
|
+
assumeValidSDL: true,
|
|
97
|
+
assumeValid: true,
|
|
98
|
+
noLocation: true,
|
|
99
|
+
}));
|
|
100
|
+
schema$ = federation ? fetchAsFederation() : fetchAsRegular();
|
|
93
101
|
}
|
|
94
102
|
schema$ = schema$.then(handleFetchedSchema);
|
|
95
103
|
return {
|
|
@@ -98,12 +106,27 @@ function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGETForQuer
|
|
|
98
106
|
};
|
|
99
107
|
};
|
|
100
108
|
}
|
|
101
|
-
|
|
109
|
+
const transportDirective = new graphql_1.GraphQLDirective({
|
|
110
|
+
name: 'transport',
|
|
111
|
+
isRepeatable: true,
|
|
112
|
+
locations: [graphql_1.DirectiveLocation.SCHEMA],
|
|
113
|
+
args: {
|
|
114
|
+
kind: { type: new graphql_1.GraphQLNonNull(graphql_1.GraphQLString) },
|
|
115
|
+
subgraph: { type: new graphql_1.GraphQLNonNull(graphql_1.GraphQLString) },
|
|
116
|
+
location: { type: new graphql_1.GraphQLNonNull(graphql_1.GraphQLString) },
|
|
117
|
+
headers: { type: new graphql_1.GraphQLList(new graphql_1.GraphQLList(graphql_1.GraphQLString)) },
|
|
118
|
+
options: { type: new graphql_1.GraphQLScalarType({ name: 'TransportOptions' }) },
|
|
119
|
+
},
|
|
120
|
+
});
|
|
102
121
|
function addAnnotations(transportEntry, schema) {
|
|
103
122
|
const schemaExtensions = (schema.extensions ||= {});
|
|
104
123
|
schemaExtensions.directives ||= {};
|
|
105
124
|
schemaExtensions.directives.transport = transportEntry;
|
|
106
|
-
|
|
125
|
+
const schemaConfig = schema.toConfig();
|
|
126
|
+
return new graphql_1.GraphQLSchema({
|
|
127
|
+
...schemaConfig,
|
|
128
|
+
directives: [...schemaConfig.directives, transportDirective],
|
|
129
|
+
});
|
|
107
130
|
}
|
|
108
131
|
const federationIntrospectionQuery = /* GraphQL */ `
|
|
109
132
|
query GetFederationInfo {
|
package/cjs/run.js
CHANGED
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.run =
|
|
26
|
+
exports.run = run;
|
|
27
27
|
require("tsx/cjs"); // support importing typescript configs in CommonJS
|
|
28
28
|
require("tsx/esm"); // support importing typescript configs in ESM
|
|
29
29
|
require("dotenv/config"); // inject dotenv options to process.env
|
|
@@ -34,12 +34,12 @@ const path_1 = require("path");
|
|
|
34
34
|
const graphql_1 = require("graphql");
|
|
35
35
|
const extra_typings_1 = require("@commander-js/extra-typings");
|
|
36
36
|
const utils_1 = require("@graphql-mesh/utils");
|
|
37
|
-
const utils_2 = require("@graphql-tools/utils");
|
|
38
37
|
const getComposedSchemaFromConfig_js_1 = require("./getComposedSchemaFromConfig.js");
|
|
39
38
|
let program = new extra_typings_1.Command()
|
|
40
39
|
.addOption(new extra_typings_1.Option('-c, --config-path <path>', 'path to the configuration file')
|
|
41
40
|
.env('CONFIG_PATH')
|
|
42
41
|
.default('mesh.config.ts'))
|
|
42
|
+
.option('--subgraph <name>', 'name of the subgraph to compose')
|
|
43
43
|
.option('-o, --output <path>', 'path to the output file');
|
|
44
44
|
async function run({ log: rootLog = new utils_1.DefaultLogger(), productName = 'Mesh Compose', productDescription = 'compose a GraphQL federated schema from any API service(s)', binName = 'mesh-compose', version, }) {
|
|
45
45
|
program = program.name(binName).description(productDescription);
|
|
@@ -76,15 +76,14 @@ async function run({ log: rootLog = new utils_1.DefaultLogger(), productName = '
|
|
|
76
76
|
...opts,
|
|
77
77
|
};
|
|
78
78
|
log.info('Composing');
|
|
79
|
-
const
|
|
80
|
-
const fusiongraph = (0, utils_2.printSchemaWithDirectives)(fusiongraphSchema);
|
|
79
|
+
const supergraphSdl = await (0, getComposedSchemaFromConfig_js_1.getComposedSchemaFromConfig)(config, log);
|
|
81
80
|
let output = config.output;
|
|
82
81
|
if (!output) {
|
|
83
82
|
if (typeof process === 'object') {
|
|
84
|
-
process.stdout.write(
|
|
83
|
+
process.stdout.write(supergraphSdl + '\n');
|
|
85
84
|
}
|
|
86
85
|
else {
|
|
87
|
-
console.log(
|
|
86
|
+
console.log(supergraphSdl);
|
|
88
87
|
}
|
|
89
88
|
log.info('Done!');
|
|
90
89
|
return;
|
|
@@ -93,13 +92,13 @@ async function run({ log: rootLog = new utils_1.DefaultLogger(), productName = '
|
|
|
93
92
|
output = (0, path_1.isAbsolute)(output) ? output : (0, path_1.join)(process.cwd(), output);
|
|
94
93
|
let writtenData;
|
|
95
94
|
if (output.endsWith('.json')) {
|
|
96
|
-
writtenData = JSON.stringify((0, graphql_1.parse)(
|
|
95
|
+
writtenData = JSON.stringify((0, graphql_1.parse)(supergraphSdl, { noLocation: true }), null, 2);
|
|
97
96
|
}
|
|
98
97
|
else if (output.endsWith('.graphql') ||
|
|
99
98
|
output.endsWith('.gql') ||
|
|
100
99
|
output.endsWith('.graphqls') ||
|
|
101
100
|
output.endsWith('.gqls')) {
|
|
102
|
-
writtenData =
|
|
101
|
+
writtenData = supergraphSdl;
|
|
103
102
|
}
|
|
104
103
|
else if (output.endsWith('.ts') ||
|
|
105
104
|
output.endsWith('.cts') ||
|
|
@@ -107,7 +106,7 @@ async function run({ log: rootLog = new utils_1.DefaultLogger(), productName = '
|
|
|
107
106
|
output.endsWith('.js') ||
|
|
108
107
|
output.endsWith('.cjs') ||
|
|
109
108
|
output.endsWith('.mjs')) {
|
|
110
|
-
writtenData = `export default ${JSON.stringify(
|
|
109
|
+
writtenData = `export default ${JSON.stringify(supergraphSdl)}`;
|
|
111
110
|
}
|
|
112
111
|
else {
|
|
113
112
|
throw new Error(`Unsupported file extension for ${output}`);
|
|
@@ -115,4 +114,3 @@ async function run({ log: rootLog = new utils_1.DefaultLogger(), productName = '
|
|
|
115
114
|
await fs_1.promises.writeFile(output, writtenData, 'utf8');
|
|
116
115
|
log.info('Done!');
|
|
117
116
|
}
|
|
118
|
-
exports.run = run;
|
package/cjs/types.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.defineConfig =
|
|
3
|
+
exports.defineConfig = defineConfig;
|
|
4
4
|
/**
|
|
5
5
|
* Type helper for defining the config.
|
|
6
6
|
*/
|
|
7
7
|
function defineConfig(config) {
|
|
8
8
|
return config;
|
|
9
9
|
}
|
|
10
|
-
exports.defineConfig = defineConfig;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { buildSchema, print } from 'graphql';
|
|
1
2
|
import { composeSubgraphs } from '@graphql-mesh/fusion-composition';
|
|
2
3
|
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
|
|
3
4
|
import { loadTypedefs } from '@graphql-tools/load';
|
|
5
|
+
import { mergeSchemas } from '@graphql-tools/schema';
|
|
6
|
+
import { printSchemaWithDirectives } from '@graphql-tools/utils';
|
|
4
7
|
import { fetch as defaultFetch } from '@whatwg-node/fetch';
|
|
5
8
|
export async function getComposedSchemaFromConfig(config, logger) {
|
|
6
9
|
const ctx = {
|
|
@@ -17,7 +20,8 @@ export async function getComposedSchemaFromConfig(config, logger) {
|
|
|
17
20
|
subgraphSchema = await schema$;
|
|
18
21
|
}
|
|
19
22
|
catch (e) {
|
|
20
|
-
|
|
23
|
+
log.error(`Failed to load subgraph ${subgraphName}`);
|
|
24
|
+
throw e;
|
|
21
25
|
}
|
|
22
26
|
return {
|
|
23
27
|
name: subgraphName,
|
|
@@ -35,14 +39,41 @@ export async function getComposedSchemaFromConfig(config, logger) {
|
|
|
35
39
|
});
|
|
36
40
|
additionalTypeDefs = result.map(r => r.document || r.rawSDL);
|
|
37
41
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const result = composeSubgraphs(subgraphConfigsForComposition);
|
|
43
|
+
if (result.errors?.length) {
|
|
44
|
+
throw new Error(`Failed to compose subgraphs; \n${result.errors.map(e => `- ${e.message}`).join('\n')}`);
|
|
45
|
+
}
|
|
46
|
+
if (config.subgraph) {
|
|
47
|
+
const subgraph = result.annotatedSubgraphs.find(sg => sg.name === config.subgraph);
|
|
48
|
+
if (!subgraph) {
|
|
49
|
+
throw new Error(`Subgraph ${config.subgraph} not found`);
|
|
50
|
+
}
|
|
51
|
+
return print(subgraph.typeDefs);
|
|
52
|
+
}
|
|
53
|
+
if (!result.supergraphSdl) {
|
|
54
|
+
throw new Error(`Unknown error: composed schema is empty`);
|
|
55
|
+
}
|
|
56
|
+
if (additionalTypeDefs?.length || config.transforms?.length) {
|
|
57
|
+
let composedSchema = buildSchema(result.supergraphSdl, {
|
|
58
|
+
noLocation: true,
|
|
59
|
+
assumeValid: true,
|
|
60
|
+
assumeValidSDL: true,
|
|
61
|
+
});
|
|
62
|
+
if (additionalTypeDefs?.length) {
|
|
63
|
+
composedSchema = mergeSchemas({
|
|
64
|
+
schemas: [composedSchema],
|
|
65
|
+
typeDefs: additionalTypeDefs,
|
|
66
|
+
assumeValid: true,
|
|
67
|
+
assumeValidSDL: true,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (config.transforms?.length) {
|
|
71
|
+
logger.info('Applying transforms');
|
|
72
|
+
for (const transform of config.transforms) {
|
|
73
|
+
composedSchema = transform(composedSchema);
|
|
74
|
+
}
|
|
45
75
|
}
|
|
76
|
+
return printSchemaWithDirectives(composedSchema);
|
|
46
77
|
}
|
|
47
|
-
return
|
|
78
|
+
return result.supergraphSdl;
|
|
48
79
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { buildClientSchema, buildSchema, getIntrospectionQuery, } from 'graphql';
|
|
2
|
-
export function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGETForQueries, operationHeaders, credentials, retry, timeout, source, schemaHeaders,
|
|
1
|
+
import { buildClientSchema, buildSchema, DirectiveLocation, getIntrospectionQuery, getNamedType, GraphQLDirective, GraphQLList, GraphQLNonNull, GraphQLScalarType, GraphQLSchema, GraphQLString, isObjectType, } from 'graphql';
|
|
2
|
+
export function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGETForQueries, operationHeaders, credentials, retry, timeout, source, schemaHeaders, federation = false, transportKind = 'http', }) {
|
|
3
3
|
return (ctx) => {
|
|
4
4
|
let schema$;
|
|
5
5
|
function handleFetchedSchema(schema) {
|
|
@@ -7,7 +7,7 @@ export function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGET
|
|
|
7
7
|
kind: transportKind,
|
|
8
8
|
subgraph: subgraphName,
|
|
9
9
|
location: endpoint,
|
|
10
|
-
headers: operationHeaders,
|
|
10
|
+
headers: operationHeaders ? Object.entries(operationHeaders) : undefined,
|
|
11
11
|
options: {
|
|
12
12
|
method,
|
|
13
13
|
useGETForQueries,
|
|
@@ -32,7 +32,7 @@ export function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGET
|
|
|
32
32
|
kind: transportKind,
|
|
33
33
|
subgraph: subgraphName,
|
|
34
34
|
location: endpoint,
|
|
35
|
-
headers: operationHeaders,
|
|
35
|
+
headers: operationHeaders ? Object.entries(operationHeaders) : undefined,
|
|
36
36
|
options: {
|
|
37
37
|
method,
|
|
38
38
|
useGETForQueries,
|
|
@@ -43,50 +43,58 @@ export function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGET
|
|
|
43
43
|
}, schema));
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
46
|
+
const fetchAsRegular = () => ctx
|
|
47
|
+
.fetch(endpoint, {
|
|
48
|
+
method: method || (useGETForQueries ? 'GET' : 'POST'),
|
|
49
|
+
headers: {
|
|
50
|
+
'Content-Type': 'application/json',
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
query: getIntrospectionQuery(),
|
|
54
|
+
}),
|
|
55
|
+
})
|
|
56
|
+
.then(res => {
|
|
57
|
+
assertResponseOk(res);
|
|
58
|
+
return res.json();
|
|
59
|
+
})
|
|
60
|
+
.then((result) => buildClientSchema(result.data, {
|
|
61
|
+
assumeValid: true,
|
|
62
|
+
}))
|
|
63
|
+
.then(schema => {
|
|
64
|
+
const queryType = schema.getQueryType();
|
|
65
|
+
const queryFields = queryType?.getFields();
|
|
66
|
+
if (queryFields._service) {
|
|
67
|
+
const serviceType = getNamedType(queryFields._service.type);
|
|
68
|
+
if (isObjectType(serviceType)) {
|
|
69
|
+
const serviceTypeFields = serviceType.getFields();
|
|
70
|
+
if (serviceTypeFields.sdl) {
|
|
71
|
+
return fetchAsFederation();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return schema;
|
|
76
|
+
});
|
|
77
|
+
const fetchAsFederation = () => ctx
|
|
78
|
+
.fetch(endpoint, {
|
|
79
|
+
method: method || (useGETForQueries ? 'GET' : 'POST'),
|
|
80
|
+
headers: {
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
query: federationIntrospectionQuery,
|
|
85
|
+
}),
|
|
86
|
+
})
|
|
87
|
+
.then(res => {
|
|
88
|
+
assertResponseOk(res);
|
|
89
|
+
return res.json();
|
|
90
|
+
})
|
|
91
|
+
.then((result) => result?.data?._service?.sdl)
|
|
92
|
+
.then(sdl => buildSchema(sdl, {
|
|
93
|
+
assumeValidSDL: true,
|
|
94
|
+
assumeValid: true,
|
|
95
|
+
noLocation: true,
|
|
96
|
+
}));
|
|
97
|
+
schema$ = federation ? fetchAsFederation() : fetchAsRegular();
|
|
90
98
|
}
|
|
91
99
|
schema$ = schema$.then(handleFetchedSchema);
|
|
92
100
|
return {
|
|
@@ -95,11 +103,27 @@ export function loadGraphQLHTTPSubgraph(subgraphName, { endpoint, method, useGET
|
|
|
95
103
|
};
|
|
96
104
|
};
|
|
97
105
|
}
|
|
106
|
+
const transportDirective = new GraphQLDirective({
|
|
107
|
+
name: 'transport',
|
|
108
|
+
isRepeatable: true,
|
|
109
|
+
locations: [DirectiveLocation.SCHEMA],
|
|
110
|
+
args: {
|
|
111
|
+
kind: { type: new GraphQLNonNull(GraphQLString) },
|
|
112
|
+
subgraph: { type: new GraphQLNonNull(GraphQLString) },
|
|
113
|
+
location: { type: new GraphQLNonNull(GraphQLString) },
|
|
114
|
+
headers: { type: new GraphQLList(new GraphQLList(GraphQLString)) },
|
|
115
|
+
options: { type: new GraphQLScalarType({ name: 'TransportOptions' }) },
|
|
116
|
+
},
|
|
117
|
+
});
|
|
98
118
|
function addAnnotations(transportEntry, schema) {
|
|
99
119
|
const schemaExtensions = (schema.extensions ||= {});
|
|
100
120
|
schemaExtensions.directives ||= {};
|
|
101
121
|
schemaExtensions.directives.transport = transportEntry;
|
|
102
|
-
|
|
122
|
+
const schemaConfig = schema.toConfig();
|
|
123
|
+
return new GraphQLSchema({
|
|
124
|
+
...schemaConfig,
|
|
125
|
+
directives: [...schemaConfig.directives, transportDirective],
|
|
126
|
+
});
|
|
103
127
|
}
|
|
104
128
|
const federationIntrospectionQuery = /* GraphQL */ `
|
|
105
129
|
query GetFederationInfo {
|
package/esm/run.js
CHANGED
|
@@ -8,12 +8,12 @@ import { isAbsolute, join, resolve } from 'path';
|
|
|
8
8
|
import { parse } from 'graphql';
|
|
9
9
|
import { Command, Option } from '@commander-js/extra-typings';
|
|
10
10
|
import { DefaultLogger } from '@graphql-mesh/utils';
|
|
11
|
-
import { printSchemaWithDirectives } from '@graphql-tools/utils';
|
|
12
11
|
import { getComposedSchemaFromConfig } from './getComposedSchemaFromConfig.js';
|
|
13
12
|
let program = new Command()
|
|
14
13
|
.addOption(new Option('-c, --config-path <path>', 'path to the configuration file')
|
|
15
14
|
.env('CONFIG_PATH')
|
|
16
15
|
.default('mesh.config.ts'))
|
|
16
|
+
.option('--subgraph <name>', 'name of the subgraph to compose')
|
|
17
17
|
.option('-o, --output <path>', 'path to the output file');
|
|
18
18
|
export async function run({ log: rootLog = new DefaultLogger(), productName = 'Mesh Compose', productDescription = 'compose a GraphQL federated schema from any API service(s)', binName = 'mesh-compose', version, }) {
|
|
19
19
|
program = program.name(binName).description(productDescription);
|
|
@@ -50,15 +50,14 @@ export async function run({ log: rootLog = new DefaultLogger(), productName = 'M
|
|
|
50
50
|
...opts,
|
|
51
51
|
};
|
|
52
52
|
log.info('Composing');
|
|
53
|
-
const
|
|
54
|
-
const fusiongraph = printSchemaWithDirectives(fusiongraphSchema);
|
|
53
|
+
const supergraphSdl = await getComposedSchemaFromConfig(config, log);
|
|
55
54
|
let output = config.output;
|
|
56
55
|
if (!output) {
|
|
57
56
|
if (typeof process === 'object') {
|
|
58
|
-
process.stdout.write(
|
|
57
|
+
process.stdout.write(supergraphSdl + '\n');
|
|
59
58
|
}
|
|
60
59
|
else {
|
|
61
|
-
console.log(
|
|
60
|
+
console.log(supergraphSdl);
|
|
62
61
|
}
|
|
63
62
|
log.info('Done!');
|
|
64
63
|
return;
|
|
@@ -67,13 +66,13 @@ export async function run({ log: rootLog = new DefaultLogger(), productName = 'M
|
|
|
67
66
|
output = isAbsolute(output) ? output : join(process.cwd(), output);
|
|
68
67
|
let writtenData;
|
|
69
68
|
if (output.endsWith('.json')) {
|
|
70
|
-
writtenData = JSON.stringify(parse(
|
|
69
|
+
writtenData = JSON.stringify(parse(supergraphSdl, { noLocation: true }), null, 2);
|
|
71
70
|
}
|
|
72
71
|
else if (output.endsWith('.graphql') ||
|
|
73
72
|
output.endsWith('.gql') ||
|
|
74
73
|
output.endsWith('.graphqls') ||
|
|
75
74
|
output.endsWith('.gqls')) {
|
|
76
|
-
writtenData =
|
|
75
|
+
writtenData = supergraphSdl;
|
|
77
76
|
}
|
|
78
77
|
else if (output.endsWith('.ts') ||
|
|
79
78
|
output.endsWith('.cts') ||
|
|
@@ -81,7 +80,7 @@ export async function run({ log: rootLog = new DefaultLogger(), productName = 'M
|
|
|
81
80
|
output.endsWith('.js') ||
|
|
82
81
|
output.endsWith('.cjs') ||
|
|
83
82
|
output.endsWith('.mjs')) {
|
|
84
|
-
writtenData = `export default ${JSON.stringify(
|
|
83
|
+
writtenData = `export default ${JSON.stringify(supergraphSdl)}`;
|
|
85
84
|
}
|
|
86
85
|
else {
|
|
87
86
|
throw new Error(`Unsupported file extension for ${output}`);
|
package/package.json
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphql-mesh/compose-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1-alpha-20240626143434-7cd2ddcf1e46bd3f52ea1e6276c1d9beeb5171a4",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@graphql-mesh/types": "
|
|
6
|
+
"@graphql-mesh/types": "0.98.9-alpha-20240626143434-7cd2ddcf1e46bd3f52ea1e6276c1d9beeb5171a4",
|
|
7
7
|
"graphql": "*"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@commander-js/extra-typings": "^12.0.1",
|
|
11
|
-
"@graphql-mesh/fusion-composition": "
|
|
12
|
-
"@graphql-mesh/utils": "
|
|
11
|
+
"@graphql-mesh/fusion-composition": "0.0.7-alpha-20240626143434-7cd2ddcf1e46bd3f52ea1e6276c1d9beeb5171a4",
|
|
12
|
+
"@graphql-mesh/utils": "0.98.9-alpha-20240626143434-7cd2ddcf1e46bd3f52ea1e6276c1d9beeb5171a4",
|
|
13
13
|
"@graphql-tools/graphql-file-loader": "8.0.1",
|
|
14
14
|
"@graphql-tools/load": "^8.0.1",
|
|
15
|
+
"@graphql-tools/schema": "^10.0.4",
|
|
15
16
|
"@graphql-tools/utils": "^10.2.1",
|
|
16
17
|
"@whatwg-node/fetch": "^0.9.14",
|
|
17
18
|
"commander": "^12.0.0",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { GraphQLSchema } from 'graphql';
|
|
2
1
|
import { Logger } from '@graphql-mesh/types';
|
|
3
2
|
import { MeshComposeCLIConfig } from './types.cjs';
|
|
4
|
-
export declare function getComposedSchemaFromConfig(config: MeshComposeCLIConfig, logger: Logger): Promise<
|
|
3
|
+
export declare function getComposedSchemaFromConfig(config: MeshComposeCLIConfig, logger: Logger): Promise<string>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { GraphQLSchema } from 'graphql';
|
|
2
1
|
import { Logger } from '@graphql-mesh/types';
|
|
3
2
|
import { MeshComposeCLIConfig } from './types.js';
|
|
4
|
-
export declare function getComposedSchemaFromConfig(config: MeshComposeCLIConfig, logger: Logger): Promise<
|
|
3
|
+
export declare function getComposedSchemaFromConfig(config: MeshComposeCLIConfig, logger: Logger): Promise<string>;
|
|
@@ -45,11 +45,11 @@ export interface GraphQLSubgraphLoaderHTTPConfiguration {
|
|
|
45
45
|
*/
|
|
46
46
|
schemaHeaders?: any;
|
|
47
47
|
/**
|
|
48
|
-
* Subgraph
|
|
48
|
+
* Federation Subgraph
|
|
49
49
|
*
|
|
50
50
|
* @default false
|
|
51
51
|
*/
|
|
52
|
-
|
|
52
|
+
federation?: boolean;
|
|
53
53
|
/**
|
|
54
54
|
* Transport kind
|
|
55
55
|
*
|
|
@@ -57,4 +57,4 @@ export interface GraphQLSubgraphLoaderHTTPConfiguration {
|
|
|
57
57
|
*/
|
|
58
58
|
transportKind?: 'http';
|
|
59
59
|
}
|
|
60
|
-
export declare function loadGraphQLHTTPSubgraph(subgraphName: string, { endpoint, method, useGETForQueries, operationHeaders, credentials, retry, timeout, source, schemaHeaders,
|
|
60
|
+
export declare function loadGraphQLHTTPSubgraph(subgraphName: string, { endpoint, method, useGETForQueries, operationHeaders, credentials, retry, timeout, source, schemaHeaders, federation, transportKind, }: GraphQLSubgraphLoaderHTTPConfiguration): MeshComposeCLISourceHandlerDef;
|
|
@@ -45,11 +45,11 @@ export interface GraphQLSubgraphLoaderHTTPConfiguration {
|
|
|
45
45
|
*/
|
|
46
46
|
schemaHeaders?: any;
|
|
47
47
|
/**
|
|
48
|
-
* Subgraph
|
|
48
|
+
* Federation Subgraph
|
|
49
49
|
*
|
|
50
50
|
* @default false
|
|
51
51
|
*/
|
|
52
|
-
|
|
52
|
+
federation?: boolean;
|
|
53
53
|
/**
|
|
54
54
|
* Transport kind
|
|
55
55
|
*
|
|
@@ -57,4 +57,4 @@ export interface GraphQLSubgraphLoaderHTTPConfiguration {
|
|
|
57
57
|
*/
|
|
58
58
|
transportKind?: 'http';
|
|
59
59
|
}
|
|
60
|
-
export declare function loadGraphQLHTTPSubgraph(subgraphName: string, { endpoint, method, useGETForQueries, operationHeaders, credentials, retry, timeout, source, schemaHeaders,
|
|
60
|
+
export declare function loadGraphQLHTTPSubgraph(subgraphName: string, { endpoint, method, useGETForQueries, operationHeaders, credentials, retry, timeout, source, schemaHeaders, federation, transportKind, }: GraphQLSubgraphLoaderHTTPConfiguration): MeshComposeCLISourceHandlerDef;
|
package/typings/run.d.cts
CHANGED
|
@@ -5,6 +5,7 @@ import { Command } from '@commander-js/extra-typings';
|
|
|
5
5
|
import { Logger } from '@graphql-mesh/types';
|
|
6
6
|
declare let program: Command<[], {
|
|
7
7
|
configPath?: string;
|
|
8
|
+
subgraph?: string;
|
|
8
9
|
output?: string;
|
|
9
10
|
}>;
|
|
10
11
|
export interface RunOptions extends ReturnType<typeof program.opts> {
|
package/typings/run.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Command } from '@commander-js/extra-typings';
|
|
|
5
5
|
import { Logger } from '@graphql-mesh/types';
|
|
6
6
|
declare let program: Command<[], {
|
|
7
7
|
configPath?: string;
|
|
8
|
+
subgraph?: string;
|
|
8
9
|
output?: string;
|
|
9
10
|
}>;
|
|
10
11
|
export interface RunOptions extends ReturnType<typeof program.opts> {
|
package/typings/types.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DocumentNode, GraphQLSchema } from 'graphql';
|
|
2
2
|
import { Logger } from '@graphql-mesh/types';
|
|
3
3
|
import { fetch as defaultFetch } from '@whatwg-node/fetch';
|
|
4
|
-
|
|
4
|
+
import { ComposeSubgraphsOptions } from '@graphql-mesh/fusion-composition';
|
|
5
|
+
export interface MeshComposeCLIConfig extends ComposeSubgraphsOptions {
|
|
5
6
|
/**
|
|
6
7
|
* The output destination of the resulting composed GraphQL schema.
|
|
7
8
|
* By default, the CLI will write the result to stdout.
|
|
@@ -10,6 +11,7 @@ export interface MeshComposeCLIConfig {
|
|
|
10
11
|
subgraphs: MeshComposeCLISubgraphConfig[];
|
|
11
12
|
transforms?: MeshComposeCLITransformConfig[];
|
|
12
13
|
additionalTypeDefs?: string | DocumentNode | (string | DocumentNode)[];
|
|
14
|
+
subgraph?: string;
|
|
13
15
|
fetch?: typeof defaultFetch;
|
|
14
16
|
cwd?: string;
|
|
15
17
|
}
|
package/typings/types.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DocumentNode, GraphQLSchema } from 'graphql';
|
|
2
2
|
import { Logger } from '@graphql-mesh/types';
|
|
3
3
|
import { fetch as defaultFetch } from '@whatwg-node/fetch';
|
|
4
|
-
|
|
4
|
+
import { ComposeSubgraphsOptions } from '@graphql-mesh/fusion-composition';
|
|
5
|
+
export interface MeshComposeCLIConfig extends ComposeSubgraphsOptions {
|
|
5
6
|
/**
|
|
6
7
|
* The output destination of the resulting composed GraphQL schema.
|
|
7
8
|
* By default, the CLI will write the result to stdout.
|
|
@@ -10,6 +11,7 @@ export interface MeshComposeCLIConfig {
|
|
|
10
11
|
subgraphs: MeshComposeCLISubgraphConfig[];
|
|
11
12
|
transforms?: MeshComposeCLITransformConfig[];
|
|
12
13
|
additionalTypeDefs?: string | DocumentNode | (string | DocumentNode)[];
|
|
14
|
+
subgraph?: string;
|
|
13
15
|
fetch?: typeof defaultFetch;
|
|
14
16
|
cwd?: string;
|
|
15
17
|
}
|