@omnigraph/json-schema 1.0.0-alpha-3fc47d119.0 → 1.0.0-alpha-20230420181317-a95037648
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/README.md +5 -4
- package/cjs/addExecutionLogicToComposer.js +208 -0
- package/cjs/addRootFieldResolver.js +395 -0
- package/cjs/bundle.js +65 -0
- package/cjs/directives.js +580 -0
- package/cjs/getComposerFromJSONSchema.js +1297 -0
- package/cjs/getDereferencedJSONSchemaFromOperations.js +33 -0
- package/cjs/getGraphQLSchemaFromDereferencedJSONSchema.js +39 -0
- package/cjs/getJSONSchemaStringFormatScalarMap.js +61 -0
- package/cjs/getReferencedJSONSchemaFromOperations.js +260 -0
- package/cjs/getTypeResolverFromOutputTCs.js +69 -0
- package/cjs/getUnionTypeComposers.js +85 -0
- package/cjs/getValidTypeName.js +20 -0
- package/cjs/index.js +14 -0
- package/cjs/loadGraphQLSchemaFromJSONSchemas.js +59 -0
- package/cjs/package.json +1 -0
- package/cjs/resolveDataByUnionInputType.js +40 -0
- package/cjs/scalars.js +30 -0
- package/cjs/types.js +0 -0
- package/cjs/utils.js +65 -0
- package/esm/addExecutionLogicToComposer.js +204 -0
- package/esm/addRootFieldResolver.js +390 -0
- package/esm/bundle.js +60 -0
- package/esm/directives.js +565 -0
- package/esm/getComposerFromJSONSchema.js +1293 -0
- package/esm/getDereferencedJSONSchemaFromOperations.js +29 -0
- package/esm/getGraphQLSchemaFromDereferencedJSONSchema.js +35 -0
- package/esm/getJSONSchemaStringFormatScalarMap.js +56 -0
- package/esm/getReferencedJSONSchemaFromOperations.js +255 -0
- package/esm/getTypeResolverFromOutputTCs.js +65 -0
- package/esm/getUnionTypeComposers.js +80 -0
- package/esm/getValidTypeName.js +16 -0
- package/esm/index.js +9 -0
- package/esm/loadGraphQLSchemaFromJSONSchemas.js +54 -0
- package/esm/resolveDataByUnionInputType.js +36 -0
- package/esm/scalars.js +27 -0
- package/esm/types.js +0 -0
- package/esm/utils.js +57 -0
- package/package.json +34 -27
- package/typings/addExecutionLogicToComposer.d.cts +14 -0
- package/typings/addExecutionLogicToComposer.d.ts +14 -0
- package/typings/addRootFieldResolver.d.cts +26 -0
- package/typings/addRootFieldResolver.d.ts +26 -0
- package/typings/bundle.d.cts +34 -0
- package/typings/bundle.d.ts +34 -0
- package/typings/directives.d.cts +54 -0
- package/typings/directives.d.ts +54 -0
- package/typings/getComposerFromJSONSchema.d.cts +13 -0
- package/{getComposerFromJSONSchema.d.ts → typings/getComposerFromJSONSchema.d.ts} +7 -4
- package/typings/getDereferencedJSONSchemaFromOperations.d.cts +14 -0
- package/{getDereferencedJSONSchemaFromOperations.d.ts → typings/getDereferencedJSONSchemaFromOperations.d.ts} +5 -3
- package/typings/getGraphQLSchemaFromDereferencedJSONSchema.d.cts +5 -0
- package/typings/getGraphQLSchemaFromDereferencedJSONSchema.d.ts +5 -0
- package/typings/getJSONSchemaStringFormatScalarMap.d.cts +2 -0
- package/typings/getJSONSchemaStringFormatScalarMap.d.ts +2 -0
- package/typings/getReferencedJSONSchemaFromOperations.d.cts +16 -0
- package/{getReferencedJSONSchemaFromOperations.d.ts → typings/getReferencedJSONSchemaFromOperations.d.ts} +6 -4
- package/typings/getTypeResolverFromOutputTCs.d.cts +7 -0
- package/typings/getTypeResolverFromOutputTCs.d.ts +7 -0
- package/typings/getUnionTypeComposers.d.cts +24 -0
- package/typings/getUnionTypeComposers.d.ts +24 -0
- package/{getStringScalarWithMinMaxLength.d.ts → typings/getValidTypeName.d.cts} +4 -3
- package/typings/index.d.cts +9 -0
- package/typings/index.d.ts +9 -0
- package/typings/loadGraphQLSchemaFromJSONSchemas.d.cts +3 -0
- package/typings/loadGraphQLSchemaFromJSONSchemas.d.ts +3 -0
- package/typings/resolveDataByUnionInputType.d.cts +2 -0
- package/typings/resolveDataByUnionInputType.d.ts +2 -0
- package/typings/scalars.d.cts +4 -0
- package/typings/scalars.d.ts +4 -0
- package/typings/types.d.cts +76 -0
- package/typings/types.d.ts +76 -0
- package/typings/utils.d.cts +15 -0
- package/{utils.d.ts → typings/utils.d.ts} +1 -1
- package/addExecutionLogicToComposer.d.ts +0 -15
- package/bundle.d.ts +0 -39
- package/getGenericJSONScalar.d.ts +0 -8
- package/getGraphQLSchemaFromDereferencedJSONSchema.d.ts +0 -5
- package/getJSONSchemaStringFormatScalarMap.d.ts +0 -3
- package/getTypeResolverFromOutputTCs.d.ts +0 -4
- package/getUnionTypeComposers.d.ts +0 -21
- package/getValidateFnForSchemaPath.d.ts +0 -3
- package/index.d.ts +0 -9
- package/index.js +0 -1890
- package/index.mjs +0 -1877
- package/loadGraphQLSchemaFromJSONSchemas.d.ts +0 -2
- package/resolveDataByUnionInputType.d.ts +0 -3
- package/types.d.ts +0 -63
- package/{getValidTypeName.d.ts → typings/getValidTypeName.d.ts} +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { dereferenceObject, healJSONSchema } from 'json-machete';
|
|
2
|
+
import { process } from '@graphql-mesh/cross-helpers';
|
|
3
|
+
import { getInterpolatedHeadersFactory } from '@graphql-mesh/string-interpolation';
|
|
4
|
+
import { getReferencedJSONSchemaFromOperations } from './getReferencedJSONSchemaFromOperations.js';
|
|
5
|
+
export async function getDereferencedJSONSchemaFromOperations({ operations, cwd = process.cwd(), logger, fetchFn, schemaHeaders, ignoreErrorResponses, endpoint, operationHeaders, queryParams, }) {
|
|
6
|
+
const referencedJSONSchema = await getReferencedJSONSchemaFromOperations({
|
|
7
|
+
operations,
|
|
8
|
+
cwd,
|
|
9
|
+
schemaHeaders,
|
|
10
|
+
ignoreErrorResponses,
|
|
11
|
+
fetchFn,
|
|
12
|
+
endpoint,
|
|
13
|
+
operationHeaders,
|
|
14
|
+
queryParams,
|
|
15
|
+
});
|
|
16
|
+
logger.debug(`Dereferencing JSON Schema to resolve all $refs`);
|
|
17
|
+
const schemaHeadersFactory = getInterpolatedHeadersFactory(schemaHeaders);
|
|
18
|
+
const fullyDeferencedSchema = await dereferenceObject(referencedJSONSchema, {
|
|
19
|
+
cwd,
|
|
20
|
+
fetchFn,
|
|
21
|
+
logger: logger.child('dereferenceObject'),
|
|
22
|
+
headers: schemaHeadersFactory({ env: process.env }),
|
|
23
|
+
});
|
|
24
|
+
logger.debug(`Healing JSON Schema`);
|
|
25
|
+
const healedSchema = await healJSONSchema(fullyDeferencedSchema, {
|
|
26
|
+
logger: logger.child('healJSONSchema'),
|
|
27
|
+
});
|
|
28
|
+
return healedSchema;
|
|
29
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { specifiedDirectives } from 'graphql';
|
|
2
|
+
import { SchemaComposer } from 'graphql-compose';
|
|
3
|
+
import { addExecutionDirectivesToComposer, } from './addExecutionLogicToComposer.js';
|
|
4
|
+
import { getComposerFromJSONSchema } from './getComposerFromJSONSchema.js';
|
|
5
|
+
export async function getGraphQLSchemaFromDereferencedJSONSchema(name, opts) {
|
|
6
|
+
const { fullyDeferencedSchema, logger, operations, operationHeaders, endpoint, queryParams, queryStringOptions, } = opts;
|
|
7
|
+
logger.debug(`Generating GraphQL Schema from the bundled JSON Schema`);
|
|
8
|
+
const visitorResult = await getComposerFromJSONSchema(fullyDeferencedSchema, logger.child('getComposerFromJSONSchema'));
|
|
9
|
+
const schemaComposerWithoutExecutionLogic = visitorResult.output;
|
|
10
|
+
if (!(schemaComposerWithoutExecutionLogic instanceof SchemaComposer)) {
|
|
11
|
+
throw new Error('The visitor result should be a SchemaComposer instance.');
|
|
12
|
+
}
|
|
13
|
+
// graphql-compose doesn't add @defer and @stream to the schema
|
|
14
|
+
for (const directive of specifiedDirectives) {
|
|
15
|
+
schemaComposerWithoutExecutionLogic.addDirective(directive);
|
|
16
|
+
}
|
|
17
|
+
const schemaComposerWithExecutionLogic = await addExecutionDirectivesToComposer(name, {
|
|
18
|
+
schemaComposer: schemaComposerWithoutExecutionLogic,
|
|
19
|
+
logger,
|
|
20
|
+
operations,
|
|
21
|
+
operationHeaders,
|
|
22
|
+
endpoint,
|
|
23
|
+
queryParams,
|
|
24
|
+
queryStringOptions,
|
|
25
|
+
});
|
|
26
|
+
if (schemaComposerWithExecutionLogic.Query.getFieldNames().length === 0) {
|
|
27
|
+
schemaComposerWithExecutionLogic.Query.addFields({
|
|
28
|
+
dummy: {
|
|
29
|
+
type: 'String',
|
|
30
|
+
resolve: () => 'dummy',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return schemaComposerWithExecutionLogic.buildSchema();
|
|
35
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import Ajv from 'ajv';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import { GraphQLScalarType, Kind } from 'graphql';
|
|
4
|
+
import { pascalCase } from 'pascal-case';
|
|
5
|
+
const JSONSchemaStringFormats = [
|
|
6
|
+
'date',
|
|
7
|
+
'hostname',
|
|
8
|
+
'regex',
|
|
9
|
+
'json-pointer',
|
|
10
|
+
'relative-json-pointer',
|
|
11
|
+
'uri-reference',
|
|
12
|
+
'uri-template',
|
|
13
|
+
];
|
|
14
|
+
export function getJSONSchemaStringFormatScalarMap() {
|
|
15
|
+
const ajv = new Ajv({
|
|
16
|
+
strict: false,
|
|
17
|
+
});
|
|
18
|
+
addFormats(ajv);
|
|
19
|
+
const map = new Map();
|
|
20
|
+
for (const format of JSONSchemaStringFormats) {
|
|
21
|
+
const schema = {
|
|
22
|
+
type: 'string',
|
|
23
|
+
format,
|
|
24
|
+
};
|
|
25
|
+
let validate;
|
|
26
|
+
try {
|
|
27
|
+
validate = ajv.compile(schema);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
validate = (value) => ajv.validate(schema, value);
|
|
31
|
+
}
|
|
32
|
+
const coerceString = (value) => {
|
|
33
|
+
if (validate(value)) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Expected ${format} but got: ${value}`);
|
|
37
|
+
};
|
|
38
|
+
const scalar = new GraphQLScalarType({
|
|
39
|
+
name: pascalCase(format),
|
|
40
|
+
description: `Represents ${format} values`,
|
|
41
|
+
serialize: coerceString,
|
|
42
|
+
parseValue: coerceString,
|
|
43
|
+
parseLiteral: ast => {
|
|
44
|
+
if (ast.kind === Kind.STRING) {
|
|
45
|
+
return coerceString(ast.value);
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`Expected string in ${format} format but got: ${ast.value}`);
|
|
48
|
+
},
|
|
49
|
+
extensions: {
|
|
50
|
+
codegenScalarType: 'string',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
map.set(format, scalar);
|
|
54
|
+
}
|
|
55
|
+
return map;
|
|
56
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { AnySchema } from 'json-machete';
|
|
2
|
+
import toJsonSchema from 'to-json-schema';
|
|
3
|
+
import { getInterpolationKeys } from '@graphql-mesh/string-interpolation';
|
|
4
|
+
import { defaultImportFn, DefaultLogger, readFileOrUrl, sanitizeNameForGraphQL, } from '@graphql-mesh/utils';
|
|
5
|
+
import { getOperationMetadata } from './utils.js';
|
|
6
|
+
async function handleOperationResponseConfig(operationResponseConfig, { schemaHeaders, cwd, fetchFn, logger = new DefaultLogger('handleOperationResponseConfig'), }) {
|
|
7
|
+
if (operationResponseConfig.responseSchema) {
|
|
8
|
+
const schema = typeof operationResponseConfig.responseSchema === 'string'
|
|
9
|
+
? {
|
|
10
|
+
$ref: operationResponseConfig.responseSchema,
|
|
11
|
+
title: operationResponseConfig.responseTypeName,
|
|
12
|
+
}
|
|
13
|
+
: operationResponseConfig.responseSchema;
|
|
14
|
+
if (operationResponseConfig.responseSample) {
|
|
15
|
+
schema.examples = schema.examples || [operationResponseConfig.responseSample];
|
|
16
|
+
}
|
|
17
|
+
return schema;
|
|
18
|
+
}
|
|
19
|
+
else if (operationResponseConfig.responseSample) {
|
|
20
|
+
const sample = typeof operationResponseConfig.responseSample === 'object'
|
|
21
|
+
? operationResponseConfig.responseSample
|
|
22
|
+
: await readFileOrUrl(operationResponseConfig.responseSample, {
|
|
23
|
+
cwd,
|
|
24
|
+
fetch: fetchFn,
|
|
25
|
+
logger,
|
|
26
|
+
importFn: defaultImportFn,
|
|
27
|
+
headers: schemaHeaders,
|
|
28
|
+
}).catch((e) => {
|
|
29
|
+
throw new Error(`responseSample - ${e.message}`);
|
|
30
|
+
});
|
|
31
|
+
const generatedSchema = toJsonSchema(sample, {
|
|
32
|
+
required: false,
|
|
33
|
+
objects: {
|
|
34
|
+
additionalProperties: false,
|
|
35
|
+
},
|
|
36
|
+
strings: {
|
|
37
|
+
detectFormat: true,
|
|
38
|
+
},
|
|
39
|
+
arrays: {
|
|
40
|
+
mode: 'first',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
generatedSchema.title = operationResponseConfig.responseTypeName;
|
|
44
|
+
generatedSchema.examples = [sample];
|
|
45
|
+
return generatedSchema;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return AnySchema;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function getReferencedJSONSchemaFromOperations({ operations, cwd, schemaHeaders, ignoreErrorResponses, logger = new DefaultLogger('getReferencedJSONSchemaFromOperations'), fetchFn, endpoint, operationHeaders, queryParams, }) {
|
|
52
|
+
const finalJsonSchema = {
|
|
53
|
+
type: 'object',
|
|
54
|
+
title: '_schema',
|
|
55
|
+
properties: {},
|
|
56
|
+
required: ['query'],
|
|
57
|
+
};
|
|
58
|
+
for (const operationConfig of operations) {
|
|
59
|
+
const { operationType, rootTypeName, fieldName } = getOperationMetadata(operationConfig);
|
|
60
|
+
const rootTypeDefinition = (finalJsonSchema.properties[operationType] = finalJsonSchema
|
|
61
|
+
.properties[operationType] || {
|
|
62
|
+
type: 'object',
|
|
63
|
+
title: rootTypeName,
|
|
64
|
+
properties: {},
|
|
65
|
+
readOnly: true,
|
|
66
|
+
});
|
|
67
|
+
rootTypeDefinition.properties = rootTypeDefinition.properties || {};
|
|
68
|
+
const interpolationStrings = [
|
|
69
|
+
...Object.values(operationHeaders || {}),
|
|
70
|
+
...Object.values(queryParams || {}).map(val => val.toString()),
|
|
71
|
+
endpoint,
|
|
72
|
+
];
|
|
73
|
+
if ('pubsubTopic' in operationConfig) {
|
|
74
|
+
interpolationStrings.push(operationConfig.pubsubTopic);
|
|
75
|
+
}
|
|
76
|
+
if ('headers' in operationConfig) {
|
|
77
|
+
interpolationStrings.push(...Object.values(operationConfig.headers || {}));
|
|
78
|
+
}
|
|
79
|
+
if ('path' in operationConfig) {
|
|
80
|
+
interpolationStrings.push(operationConfig.path);
|
|
81
|
+
}
|
|
82
|
+
if ('responseByStatusCode' in operationConfig) {
|
|
83
|
+
rootTypeDefinition.properties[fieldName] = rootTypeDefinition.properties[fieldName] || {};
|
|
84
|
+
const statusCodeOneOfIndexMap = {};
|
|
85
|
+
const responseSchemas = [];
|
|
86
|
+
for (const statusCode in operationConfig.responseByStatusCode) {
|
|
87
|
+
if (ignoreErrorResponses && !statusCode.startsWith('2')) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const responseOperationConfig = operationConfig.responseByStatusCode[statusCode];
|
|
91
|
+
const responseOperationSchema = await handleOperationResponseConfig(responseOperationConfig, {
|
|
92
|
+
cwd,
|
|
93
|
+
schemaHeaders,
|
|
94
|
+
fetchFn,
|
|
95
|
+
logger,
|
|
96
|
+
});
|
|
97
|
+
statusCodeOneOfIndexMap[statusCode] = responseSchemas.length;
|
|
98
|
+
responseOperationSchema.title =
|
|
99
|
+
responseOperationSchema.title || `${fieldName}_${statusCode}_response`;
|
|
100
|
+
responseSchemas.push(responseOperationSchema);
|
|
101
|
+
}
|
|
102
|
+
if (responseSchemas.length === 1) {
|
|
103
|
+
rootTypeDefinition.properties[fieldName] = responseSchemas[0];
|
|
104
|
+
}
|
|
105
|
+
else if (responseSchemas.length === 0) {
|
|
106
|
+
rootTypeDefinition.properties[fieldName] = AnySchema;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
rootTypeDefinition.properties[fieldName] = {
|
|
110
|
+
$comment: `statusCodeOneOfIndexMap:${JSON.stringify(statusCodeOneOfIndexMap)}`,
|
|
111
|
+
title: fieldName + '_response',
|
|
112
|
+
oneOf: responseSchemas,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
rootTypeDefinition.properties[fieldName] = await handleOperationResponseConfig(operationConfig, {
|
|
118
|
+
cwd,
|
|
119
|
+
schemaHeaders,
|
|
120
|
+
fetchFn,
|
|
121
|
+
logger,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const rootTypeInputPropertyName = operationType + 'Input';
|
|
125
|
+
const rootInputTypeName = rootTypeName + 'Input';
|
|
126
|
+
const rootTypeInputTypeDefinition = (finalJsonSchema.properties[rootTypeInputPropertyName] =
|
|
127
|
+
finalJsonSchema.properties[rootTypeInputPropertyName] || {
|
|
128
|
+
type: 'object',
|
|
129
|
+
title: rootInputTypeName,
|
|
130
|
+
properties: {},
|
|
131
|
+
writeOnly: true,
|
|
132
|
+
});
|
|
133
|
+
if ('queryParamsSample' in operationConfig &&
|
|
134
|
+
operationConfig.queryParamsSample &&
|
|
135
|
+
typeof operationConfig.queryParamsSample === 'object') {
|
|
136
|
+
for (const queryParamName in operationConfig.queryParamsSample) {
|
|
137
|
+
const example = operationConfig.queryParamsSample[queryParamName];
|
|
138
|
+
const generatedSchema = toJsonSchema(example, {
|
|
139
|
+
required: false,
|
|
140
|
+
objects: {
|
|
141
|
+
additionalProperties: false,
|
|
142
|
+
},
|
|
143
|
+
strings: {
|
|
144
|
+
detectFormat: true,
|
|
145
|
+
},
|
|
146
|
+
arrays: {
|
|
147
|
+
mode: 'first',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
generatedSchema.examples = [example];
|
|
151
|
+
const argName = sanitizeNameForGraphQL(queryParamName);
|
|
152
|
+
operationConfig.queryParamArgMap = operationConfig.queryParamArgMap || {};
|
|
153
|
+
operationConfig.queryParamArgMap[queryParamName] = argName;
|
|
154
|
+
operationConfig.argTypeMap = operationConfig.argTypeMap || {};
|
|
155
|
+
operationConfig.argTypeMap[argName] = generatedSchema;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const interpolationKeys = getInterpolationKeys(...interpolationStrings);
|
|
159
|
+
if ('queryParamArgMap' in operationConfig) {
|
|
160
|
+
interpolationKeys.push(...Object.values(operationConfig.queryParamArgMap).map(key => `args.${key}`));
|
|
161
|
+
}
|
|
162
|
+
for (const interpolationKey of interpolationKeys) {
|
|
163
|
+
const [initialObjectName, varName] = interpolationKey.split('.');
|
|
164
|
+
if (initialObjectName === 'args') {
|
|
165
|
+
rootTypeInputTypeDefinition.properties[fieldName] = rootTypeInputTypeDefinition.properties[fieldName] || {
|
|
166
|
+
title: `${rootTypeInputPropertyName}_${fieldName}`,
|
|
167
|
+
type: 'object',
|
|
168
|
+
properties: {},
|
|
169
|
+
};
|
|
170
|
+
if (operationConfig.argTypeMap != null && varName in operationConfig.argTypeMap) {
|
|
171
|
+
const argTypeDef = operationConfig.argTypeMap[varName];
|
|
172
|
+
if (typeof argTypeDef === 'object') {
|
|
173
|
+
rootTypeInputTypeDefinition.properties[fieldName].properties[varName] = argTypeDef;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
rootTypeInputTypeDefinition.properties[fieldName].properties[varName] = {
|
|
177
|
+
$ref: argTypeDef,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (!rootTypeInputTypeDefinition.properties[fieldName].properties[varName]) {
|
|
182
|
+
rootTypeInputTypeDefinition.properties[fieldName].properties[varName] = {
|
|
183
|
+
type: 'string',
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if ('binary' in operationConfig) {
|
|
189
|
+
const generatedSchema = {
|
|
190
|
+
type: 'string',
|
|
191
|
+
format: 'binary',
|
|
192
|
+
};
|
|
193
|
+
rootTypeInputTypeDefinition.properties[fieldName] = rootTypeInputTypeDefinition.properties[fieldName] || {
|
|
194
|
+
title: `${rootTypeInputPropertyName}_${fieldName}`,
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {},
|
|
197
|
+
};
|
|
198
|
+
rootTypeInputTypeDefinition.properties[fieldName].properties.input = generatedSchema;
|
|
199
|
+
}
|
|
200
|
+
else if ('requestSchema' in operationConfig && operationConfig.requestSchema) {
|
|
201
|
+
rootTypeInputTypeDefinition.properties[fieldName] = rootTypeInputTypeDefinition.properties[fieldName] || {
|
|
202
|
+
title: `${rootTypeInputPropertyName}_${fieldName}`,
|
|
203
|
+
type: 'object',
|
|
204
|
+
properties: {},
|
|
205
|
+
};
|
|
206
|
+
rootTypeInputTypeDefinition.properties[fieldName].properties.input =
|
|
207
|
+
typeof operationConfig.requestSchema === 'string'
|
|
208
|
+
? {
|
|
209
|
+
$ref: operationConfig.requestSchema,
|
|
210
|
+
title: operationConfig.requestTypeName,
|
|
211
|
+
}
|
|
212
|
+
: operationConfig.requestSchema;
|
|
213
|
+
if (operationConfig.requestSample) {
|
|
214
|
+
rootTypeInputTypeDefinition.properties[fieldName].properties.input.examples =
|
|
215
|
+
rootTypeInputTypeDefinition.properties[fieldName].properties.input.examples || [
|
|
216
|
+
operationConfig.requestSample,
|
|
217
|
+
];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else if ('requestSample' in operationConfig) {
|
|
221
|
+
const sample = typeof operationConfig.requestSample === 'object'
|
|
222
|
+
? operationConfig.requestSample
|
|
223
|
+
: await readFileOrUrl(operationConfig.requestSample, {
|
|
224
|
+
cwd,
|
|
225
|
+
headers: schemaHeaders,
|
|
226
|
+
fetch: fetchFn,
|
|
227
|
+
logger,
|
|
228
|
+
importFn: defaultImportFn,
|
|
229
|
+
}).catch((e) => {
|
|
230
|
+
throw new Error(`${operationConfig.field}.requestSample: ${operationConfig.requestSample}; ${e.message}`);
|
|
231
|
+
});
|
|
232
|
+
const generatedSchema = toJsonSchema(sample, {
|
|
233
|
+
required: false,
|
|
234
|
+
objects: {
|
|
235
|
+
additionalProperties: false,
|
|
236
|
+
},
|
|
237
|
+
strings: {
|
|
238
|
+
detectFormat: true,
|
|
239
|
+
},
|
|
240
|
+
arrays: {
|
|
241
|
+
mode: 'first',
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
generatedSchema.title = operationConfig.requestTypeName;
|
|
245
|
+
generatedSchema.examples = [sample];
|
|
246
|
+
rootTypeInputTypeDefinition.properties[fieldName] = rootTypeInputTypeDefinition.properties[fieldName] || {
|
|
247
|
+
title: `${rootTypeInputPropertyName}_${fieldName}`,
|
|
248
|
+
type: 'object',
|
|
249
|
+
properties: {},
|
|
250
|
+
};
|
|
251
|
+
rootTypeInputTypeDefinition.properties[fieldName].properties.input = generatedSchema;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return finalJsonSchema;
|
|
255
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createGraphQLError } from '@graphql-tools/utils';
|
|
2
|
+
export function getTypeResolverFromOutputTCs({ possibleTypes, discriminatorField, discriminatorMapping, statusCodeTypeNameMap, }) {
|
|
3
|
+
return function resolveType(data) {
|
|
4
|
+
if (data.__typename) {
|
|
5
|
+
return data.__typename;
|
|
6
|
+
}
|
|
7
|
+
else if (discriminatorField != null && data[discriminatorField]) {
|
|
8
|
+
const discriminatorValue = data[discriminatorField];
|
|
9
|
+
return (discriminatorMapping === null || discriminatorMapping === void 0 ? void 0 : discriminatorMapping[discriminatorValue]) || discriminatorValue;
|
|
10
|
+
}
|
|
11
|
+
if (data.$statusCode && statusCodeTypeNameMap) {
|
|
12
|
+
const typeName = statusCodeTypeNameMap[data.$statusCode.toString()] || statusCodeTypeNameMap.default;
|
|
13
|
+
if (typeName) {
|
|
14
|
+
return typeName;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// const validationErrors: Record<string, ErrorObject[]> = {};
|
|
18
|
+
const dataKeys = typeof data === 'object'
|
|
19
|
+
? Object.keys(data)
|
|
20
|
+
// Remove metadata fields used to pass data
|
|
21
|
+
.filter(property => !property.toString().startsWith('$'))
|
|
22
|
+
: null;
|
|
23
|
+
for (const possibleType of possibleTypes) {
|
|
24
|
+
const typeName = possibleType.name;
|
|
25
|
+
if (dataKeys != null) {
|
|
26
|
+
const typeFields = Object.keys(possibleType.getFields());
|
|
27
|
+
if (dataKeys.length <= typeFields.length &&
|
|
28
|
+
dataKeys.every(property => typeFields.includes(property.toString()))) {
|
|
29
|
+
return typeName;
|
|
30
|
+
}
|
|
31
|
+
} /* else {
|
|
32
|
+
const validateFn = possibleType.extensions.validateWithJSONSchema as ValidateFunction;
|
|
33
|
+
if (validateFn) {
|
|
34
|
+
const isValid = validateFn(data);
|
|
35
|
+
if (isValid) {
|
|
36
|
+
return typeName;
|
|
37
|
+
}
|
|
38
|
+
validationErrors[typeName] = ajv.errors || validateFn.errors;
|
|
39
|
+
}
|
|
40
|
+
} */
|
|
41
|
+
}
|
|
42
|
+
if (data.$response) {
|
|
43
|
+
const error = createGraphQLError(`HTTP Error: ${data.$statusCode}`, {
|
|
44
|
+
extensions: {
|
|
45
|
+
http: {
|
|
46
|
+
status: data.$statusCode,
|
|
47
|
+
headers: data.$response.header,
|
|
48
|
+
},
|
|
49
|
+
request: {
|
|
50
|
+
url: data.$url,
|
|
51
|
+
method: data.$method,
|
|
52
|
+
},
|
|
53
|
+
responseJson: data.$response,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
return error;
|
|
57
|
+
}
|
|
58
|
+
/*
|
|
59
|
+
const error = new GraphQLError(`Received data doesn't met the union`, null, null, null, null, null, {
|
|
60
|
+
validationErrors,
|
|
61
|
+
});
|
|
62
|
+
return error;
|
|
63
|
+
*/
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { isSomeInputTypeComposer, } from 'graphql-compose';
|
|
2
|
+
import { StatusCodeTypeNameDirective } from './directives.js';
|
|
3
|
+
export function getContainerTC(schemaComposer, output) {
|
|
4
|
+
const containerTypeName = `${output.getTypeName()}_container`;
|
|
5
|
+
return schemaComposer.getOrCreateOTC(containerTypeName, otc => otc.addFields({
|
|
6
|
+
[output.getTypeName()]: {
|
|
7
|
+
type: output,
|
|
8
|
+
resolve: root => root,
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
export function getUnionTypeComposers({ schemaComposer, typeComposersList, subSchemaAndTypeComposers, logger, }) {
|
|
13
|
+
var _a;
|
|
14
|
+
if (new Set(typeComposersList).size === 1) {
|
|
15
|
+
return typeComposersList[0];
|
|
16
|
+
}
|
|
17
|
+
const unionInputFields = {};
|
|
18
|
+
const outputTypeComposers = [];
|
|
19
|
+
typeComposersList.forEach(typeComposers => {
|
|
20
|
+
const { input, output } = typeComposers;
|
|
21
|
+
if (isSomeInputTypeComposer(output)) {
|
|
22
|
+
outputTypeComposers.push(getContainerTC(schemaComposer, output));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
outputTypeComposers.push(output);
|
|
26
|
+
}
|
|
27
|
+
if (input) {
|
|
28
|
+
unionInputFields[input.getTypeName()] = {
|
|
29
|
+
type: input,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (!input) {
|
|
33
|
+
logger.debug(`No input type composer found for ${output.getTypeName()}, skipping...`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
if (Object.keys(unionInputFields).length === 1) {
|
|
37
|
+
subSchemaAndTypeComposers.input = Object.values(unionInputFields)[0].type;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
subSchemaAndTypeComposers.input.addFields(unionInputFields);
|
|
41
|
+
}
|
|
42
|
+
if (new Set(outputTypeComposers).size === 1) {
|
|
43
|
+
subSchemaAndTypeComposers.output = outputTypeComposers[0];
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const directives = subSchemaAndTypeComposers.output.getDirectives() || [];
|
|
47
|
+
const statusCodeOneOfIndexMap = subSchemaAndTypeComposers.output.getExtension('statusCodeOneOfIndexMap');
|
|
48
|
+
const statusCodeOneOfIndexMapEntries = Object.entries(statusCodeOneOfIndexMap || {});
|
|
49
|
+
for (const outputTypeComposerIndex in outputTypeComposers) {
|
|
50
|
+
const outputTypeComposer = outputTypeComposers[outputTypeComposerIndex];
|
|
51
|
+
const statusCode = (_a = statusCodeOneOfIndexMapEntries.find(([statusCode, index]) => index.toString() === outputTypeComposerIndex.toString())) === null || _a === void 0 ? void 0 : _a[0];
|
|
52
|
+
if ('getFields' in outputTypeComposer) {
|
|
53
|
+
if (statusCode != null) {
|
|
54
|
+
schemaComposer.addDirective(StatusCodeTypeNameDirective);
|
|
55
|
+
directives.push({
|
|
56
|
+
name: 'statusCodeTypeName',
|
|
57
|
+
args: {
|
|
58
|
+
statusCode,
|
|
59
|
+
typeName: outputTypeComposer.getTypeName(),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
subSchemaAndTypeComposers.output.addType(outputTypeComposer);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
for (const possibleType of outputTypeComposer.getTypes()) {
|
|
67
|
+
subSchemaAndTypeComposers.output.addType(possibleType);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
subSchemaAndTypeComposers.output.setDirectives(directives);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
input: subSchemaAndTypeComposers.input,
|
|
75
|
+
output: subSchemaAndTypeComposers.output,
|
|
76
|
+
nullable: subSchemaAndTypeComposers.nullable,
|
|
77
|
+
readOnly: subSchemaAndTypeComposers.readOnly,
|
|
78
|
+
writeOnly: subSchemaAndTypeComposers.writeOnly,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { sanitizeNameForGraphQL } from '@graphql-mesh/utils';
|
|
2
|
+
import { inspect } from '@graphql-tools/utils';
|
|
3
|
+
export function getValidTypeName({ schemaComposer, isInput, subSchema, }) {
|
|
4
|
+
if (!subSchema.title) {
|
|
5
|
+
throw new Error('Missing title for schema; ' + inspect(subSchema));
|
|
6
|
+
}
|
|
7
|
+
const sanitizedName = sanitizeNameForGraphQL(isInput ? subSchema.title + '_Input' : subSchema.title);
|
|
8
|
+
if (schemaComposer.has(sanitizedName)) {
|
|
9
|
+
let i = 2;
|
|
10
|
+
while (schemaComposer.has(sanitizedName + i)) {
|
|
11
|
+
i++;
|
|
12
|
+
}
|
|
13
|
+
return sanitizedName + i;
|
|
14
|
+
}
|
|
15
|
+
return sanitizedName;
|
|
16
|
+
}
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { loadGraphQLSchemaFromJSONSchemas } from './loadGraphQLSchemaFromJSONSchemas.js';
|
|
2
|
+
export default loadGraphQLSchemaFromJSONSchemas;
|
|
3
|
+
export * from './loadGraphQLSchemaFromJSONSchemas.js';
|
|
4
|
+
export * from './getComposerFromJSONSchema.js';
|
|
5
|
+
export * from './getDereferencedJSONSchemaFromOperations.js';
|
|
6
|
+
export * from './getGraphQLSchemaFromDereferencedJSONSchema.js';
|
|
7
|
+
export * from './types.js';
|
|
8
|
+
export { processDirectives } from './directives.js';
|
|
9
|
+
export * from './bundle.js';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DefaultLogger } from '@graphql-mesh/utils';
|
|
2
|
+
import { fetch } from '@whatwg-node/fetch';
|
|
3
|
+
import { createBundleFromDereferencedSchema } from './bundle.js';
|
|
4
|
+
import { processDirectives } from './directives.js';
|
|
5
|
+
import { getDereferencedJSONSchemaFromOperations } from './getDereferencedJSONSchemaFromOperations.js';
|
|
6
|
+
import { getGraphQLSchemaFromDereferencedJSONSchema } from './getGraphQLSchemaFromDereferencedJSONSchema.js';
|
|
7
|
+
export async function loadNonExecutableGraphQLSchemaFromJSONSchemas(name, options) {
|
|
8
|
+
options.logger = options.logger || new DefaultLogger(name);
|
|
9
|
+
options.cwd = options.cwd || process.cwd();
|
|
10
|
+
const fullyDeferencedSchema = await getDereferencedJSONSchemaFromOperations({
|
|
11
|
+
operations: options.operations,
|
|
12
|
+
operationHeaders: typeof options.operationHeaders === 'object' ? options.operationHeaders : {},
|
|
13
|
+
queryParams: options.queryParams,
|
|
14
|
+
endpoint: options.endpoint,
|
|
15
|
+
cwd: options.cwd,
|
|
16
|
+
logger: options.logger,
|
|
17
|
+
fetchFn: options.fetch,
|
|
18
|
+
schemaHeaders: options.schemaHeaders,
|
|
19
|
+
ignoreErrorResponses: options.ignoreErrorResponses,
|
|
20
|
+
});
|
|
21
|
+
const schema = await getGraphQLSchemaFromDereferencedJSONSchema(name, {
|
|
22
|
+
fullyDeferencedSchema,
|
|
23
|
+
logger: options.logger,
|
|
24
|
+
operations: options.operations,
|
|
25
|
+
operationHeaders: options.operationHeaders,
|
|
26
|
+
endpoint: options.endpoint,
|
|
27
|
+
queryParams: options.queryParams,
|
|
28
|
+
queryStringOptions: options.queryStringOptions,
|
|
29
|
+
});
|
|
30
|
+
if (options.bundle) {
|
|
31
|
+
schema.extensions = schema.extensions || {};
|
|
32
|
+
Object.defineProperty(schema.extensions, 'bundle', {
|
|
33
|
+
value: await createBundleFromDereferencedSchema(name, {
|
|
34
|
+
dereferencedSchema: fullyDeferencedSchema,
|
|
35
|
+
endpoint: options.endpoint,
|
|
36
|
+
operations: options.operations,
|
|
37
|
+
operationHeaders: typeof options.operationHeaders === 'object' ? options.operationHeaders : {},
|
|
38
|
+
logger: options.logger,
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return schema;
|
|
43
|
+
}
|
|
44
|
+
export async function loadGraphQLSchemaFromJSONSchemas(name, options) {
|
|
45
|
+
const graphqlSchema = await loadNonExecutableGraphQLSchemaFromJSONSchemas(name, options);
|
|
46
|
+
return processDirectives({
|
|
47
|
+
...options,
|
|
48
|
+
operationHeaders: typeof options.operationHeaders === 'object' ? options.operationHeaders : {},
|
|
49
|
+
schema: graphqlSchema,
|
|
50
|
+
globalFetch: options.fetch || fetch,
|
|
51
|
+
pubsub: options.pubsub,
|
|
52
|
+
logger: options.logger,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isInputObjectType, isListType, isNonNullType, } from 'graphql';
|
|
2
|
+
import { sanitizeNameForGraphQL } from '@graphql-mesh/utils';
|
|
3
|
+
import { asArray, getDirective } from '@graphql-tools/utils';
|
|
4
|
+
export function resolveDataByUnionInputType(data, type, schema) {
|
|
5
|
+
var _a;
|
|
6
|
+
if (data) {
|
|
7
|
+
if (isListType(type)) {
|
|
8
|
+
return asArray(data).map(elem => resolveDataByUnionInputType(elem, type.ofType, schema));
|
|
9
|
+
}
|
|
10
|
+
if (isNonNullType(type)) {
|
|
11
|
+
return resolveDataByUnionInputType(data, type.ofType, schema);
|
|
12
|
+
}
|
|
13
|
+
if (isInputObjectType(type)) {
|
|
14
|
+
const typeOneOfDirectives = getDirective(schema, type, 'oneOf');
|
|
15
|
+
const isOneOf = typeOneOfDirectives === null || typeOneOfDirectives === void 0 ? void 0 : typeOneOfDirectives.length;
|
|
16
|
+
const fieldMap = type.getFields();
|
|
17
|
+
data = asArray(data)[0];
|
|
18
|
+
for (const propertyName in data) {
|
|
19
|
+
const fieldName = sanitizeNameForGraphQL(propertyName);
|
|
20
|
+
const field = fieldMap[fieldName];
|
|
21
|
+
if (field) {
|
|
22
|
+
if (isOneOf) {
|
|
23
|
+
const resolvedData = resolveDataByUnionInputType(data[fieldName], field.type, schema);
|
|
24
|
+
return resolvedData;
|
|
25
|
+
}
|
|
26
|
+
const fieldData = data[fieldName];
|
|
27
|
+
data[fieldName] = undefined;
|
|
28
|
+
const fieldResolveRootFieldDirectives = getDirective(schema, field, 'resolveRootField');
|
|
29
|
+
const realFieldName = ((_a = fieldResolveRootFieldDirectives === null || fieldResolveRootFieldDirectives === void 0 ? void 0 : fieldResolveRootFieldDirectives[0]) === null || _a === void 0 ? void 0 : _a.field) || fieldName;
|
|
30
|
+
data[realFieldName] = resolveDataByUnionInputType(fieldData, field.type, schema);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return data;
|
|
36
|
+
}
|
package/esm/scalars.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GraphQLScalarType } from 'graphql';
|
|
2
|
+
export const GraphQLFile = new GraphQLScalarType({
|
|
3
|
+
name: 'File',
|
|
4
|
+
description: 'The `File` scalar type represents a file upload.',
|
|
5
|
+
extensions: {
|
|
6
|
+
codegenScalarType: 'File',
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
export const GraphQLVoid = new GraphQLScalarType({
|
|
10
|
+
name: 'Void',
|
|
11
|
+
description: 'Represents empty values',
|
|
12
|
+
serialize: () => '',
|
|
13
|
+
extensions: {
|
|
14
|
+
codegenScalarType: 'void',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
export const ObjMapScalar = new GraphQLScalarType({
|
|
18
|
+
name: 'ObjMap',
|
|
19
|
+
serialize: value => JSON.stringify(value),
|
|
20
|
+
parseValue: value => JSON.parse(value.toString()),
|
|
21
|
+
parseLiteral: ast => {
|
|
22
|
+
if (ast.kind === 'StringValue') {
|
|
23
|
+
return JSON.parse(ast.value);
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
},
|
|
27
|
+
});
|
package/esm/types.js
ADDED
|
File without changes
|