vovk 3.0.0-draft.323 → 3.0.0-draft.324
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/openapi/openAPIToVovkSchema/index.d.ts +1 -1
- package/cjs/openapi/openAPIToVovkSchema/index.js +2 -1
- package/cjs/openapi/vovkSchemaToOpenAPI.d.ts +2 -3
- package/cjs/openapi/vovkSchemaToOpenAPI.js +5 -5
- package/cjs/types.d.ts +2 -0
- package/cjs/utils/createCodeExamples.d.ts +1 -2
- package/cjs/utils/createCodeExamples.js +39 -43
- package/cjs/utils/getJSONSchemaExample.d.ts +8 -0
- package/cjs/utils/getJSONSchemaExample.js +234 -0
- package/cjs/utils/getJSONSchemaSample.d.ts +2 -0
- package/cjs/utils/{jsonSchemaSampler.js → getJSONSchemaSample.js} +9 -9
- package/mjs/openapi/openAPIToVovkSchema/index.d.ts +1 -1
- package/mjs/openapi/openAPIToVovkSchema/index.js +2 -1
- package/mjs/openapi/vovkSchemaToOpenAPI.d.ts +2 -3
- package/mjs/openapi/vovkSchemaToOpenAPI.js +5 -5
- package/mjs/types.d.ts +2 -0
- package/mjs/utils/createCodeExamples.d.ts +1 -2
- package/mjs/utils/createCodeExamples.js +39 -43
- package/mjs/utils/getJSONSchemaExample.d.ts +8 -0
- package/mjs/utils/getJSONSchemaExample.js +234 -0
- package/mjs/utils/getJSONSchemaSample.d.ts +2 -0
- package/mjs/utils/{jsonSchemaSampler.js → getJSONSchemaSample.js} +9 -9
- package/package.json +1 -1
- package/cjs/utils/jsonSchemaSampler.d.ts +0 -2
- package/mjs/utils/jsonSchemaSampler.d.ts +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type VovkSchema, VovkStrictConfig } from '../../types';
|
|
2
|
-
export declare function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName, getMethodName, errorMessageKey, mixinName, }: VovkStrictConfig['openApiMixins'][string] & {
|
|
2
|
+
export declare function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName, getMethodName, errorMessageKey, package: packageJson, mixinName, }: VovkStrictConfig['openApiMixins'][string] & {
|
|
3
3
|
mixinName: string;
|
|
4
4
|
}): VovkSchema;
|
|
@@ -57,7 +57,7 @@ const normalizeGetMethodName = (getMethodName) => {
|
|
|
57
57
|
}
|
|
58
58
|
return getMethodName;
|
|
59
59
|
};
|
|
60
|
-
function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName = 'api', getMethodName = 'auto', errorMessageKey, mixinName, }) {
|
|
60
|
+
function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName = 'api', getMethodName = 'auto', errorMessageKey, package: packageJson, mixinName, }) {
|
|
61
61
|
const forceApiRoot = apiRoot ??
|
|
62
62
|
openAPIObject.servers?.[0]?.url ??
|
|
63
63
|
('host' in openAPIObject
|
|
@@ -77,6 +77,7 @@ function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getMo
|
|
|
77
77
|
controllers: {},
|
|
78
78
|
meta: {
|
|
79
79
|
components: openAPIObject.components,
|
|
80
|
+
package: packageJson,
|
|
80
81
|
},
|
|
81
82
|
},
|
|
82
83
|
},
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
2
2
|
import { type CodeSamplePackageJson } from '../utils/createCodeExamples';
|
|
3
|
-
import { type VovkSchema
|
|
4
|
-
export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson,
|
|
3
|
+
import { type VovkSchema } from '../types';
|
|
4
|
+
export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson, }: {
|
|
5
5
|
rootEntry: string;
|
|
6
6
|
schema: VovkSchema;
|
|
7
7
|
openAPIObject?: Partial<OpenAPIObject>;
|
|
8
8
|
package?: CodeSamplePackageJson;
|
|
9
|
-
sampler?: (schema: KnownAny) => KnownAny;
|
|
10
9
|
}): OpenAPIObject;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.vovkSchemaToOpenAPI = vovkSchemaToOpenAPI;
|
|
4
4
|
const createCodeExamples_1 = require("../utils/createCodeExamples");
|
|
5
|
-
const jsonSchemaSampler_1 = require("../utils/jsonSchemaSampler");
|
|
6
5
|
const types_1 = require("../types");
|
|
6
|
+
const getJSONSchemaSample_1 = require("../utils/getJSONSchemaSample");
|
|
7
7
|
function extractComponents(schema) {
|
|
8
8
|
if (!schema)
|
|
9
9
|
return [undefined, {}];
|
|
@@ -43,7 +43,7 @@ function extractComponents(schema) {
|
|
|
43
43
|
const processedSchema = process(schema);
|
|
44
44
|
return [processedSchema, components];
|
|
45
45
|
}
|
|
46
|
-
function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'my-rpc-client' },
|
|
46
|
+
function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'my-rpc-client' }, }) {
|
|
47
47
|
const paths = {};
|
|
48
48
|
const components = {};
|
|
49
49
|
for (const [segmentName, segmentSchema] of Object.entries(fullSchema.segments ?? {})) {
|
|
@@ -141,9 +141,9 @@ function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}
|
|
|
141
141
|
...iterationValidation,
|
|
142
142
|
examples: iterationValidation.examples ?? [
|
|
143
143
|
[
|
|
144
|
-
JSON.stringify(
|
|
145
|
-
JSON.stringify(
|
|
146
|
-
JSON.stringify(
|
|
144
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
145
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
146
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
147
147
|
].join('\n'),
|
|
148
148
|
],
|
|
149
149
|
},
|
package/cjs/types.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export type VovkSegmentSchema<T = KnownAny> = {
|
|
|
34
34
|
controllers: Record<string, VovkControllerSchema<T>>;
|
|
35
35
|
meta?: {
|
|
36
36
|
components?: OpenAPIObject['components'];
|
|
37
|
+
package?: PackageJson;
|
|
37
38
|
[key: string]: KnownAny;
|
|
38
39
|
};
|
|
39
40
|
};
|
|
@@ -241,6 +242,7 @@ export interface VovkLLMTool {
|
|
|
241
242
|
export type SimpleJSONSchema = {
|
|
242
243
|
type: 'object';
|
|
243
244
|
$ref?: string;
|
|
245
|
+
items?: SimpleJSONSchema;
|
|
244
246
|
description?: string;
|
|
245
247
|
properties: Record<string, SimpleJSONSchema>;
|
|
246
248
|
required?: string[];
|
|
@@ -7,12 +7,11 @@ export type CodeSamplePackageJson = {
|
|
|
7
7
|
py_name?: string;
|
|
8
8
|
[key: string]: KnownAny;
|
|
9
9
|
};
|
|
10
|
-
export declare function createCodeExamples({ handlerName, handlerSchema, controllerSchema, package: packageJson,
|
|
10
|
+
export declare function createCodeExamples({ handlerName, handlerSchema, controllerSchema, package: packageJson, }: {
|
|
11
11
|
handlerName: string;
|
|
12
12
|
handlerSchema: VovkHandlerSchema;
|
|
13
13
|
controllerSchema: VovkControllerSchema;
|
|
14
14
|
package?: CodeSamplePackageJson;
|
|
15
|
-
sampler?: (schema: KnownAny) => KnownAny;
|
|
16
15
|
}): {
|
|
17
16
|
ts: string;
|
|
18
17
|
py: string;
|
|
@@ -1,34 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createCodeExamples = createCodeExamples;
|
|
4
|
-
const
|
|
5
|
-
const stringifyTsSample = (data, pad = 4) => JSON.stringify(data, null, 2)
|
|
6
|
-
.replace(/"([A-Za-z_$][0-9A-Za-z_$]*)":/g, '$1:')
|
|
7
|
-
.split('\n')
|
|
8
|
-
.map((line, i, a) => (i === 0 ? line : i === a.length - 1 ? ' '.repeat(pad) + line : ' '.repeat(pad + 2) + line))
|
|
9
|
-
.join('\n');
|
|
10
|
-
const stringifyPySample = (data, pad = 4) => JSON.stringify(data, null, 2)
|
|
11
|
-
.split('\n')
|
|
12
|
-
.map((line, i, a) => (i === 0 ? line : i === a.length - 1 ? ' '.repeat(pad) + line : ' '.repeat(pad + 2) + line))
|
|
13
|
-
.join('\n');
|
|
4
|
+
const getJSONSchemaExample_1 = require("./getJSONSchemaExample");
|
|
14
5
|
const toSnakeCase = (str) => str
|
|
15
6
|
.replace(/-/g, '_') // Replace hyphens with underscores
|
|
16
7
|
.replace(/([a-z0-9])([A-Z])/g, '$1_$2') // Add underscore between lowercase/digit and uppercase
|
|
17
8
|
.replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1_$2') // Add underscore between uppercase letters if the second one is followed by a lowercase
|
|
18
9
|
.toLowerCase()
|
|
19
10
|
.replace(/^_/, ''); // Remove leading underscore
|
|
20
|
-
function createCodeExamples({ handlerName, handlerSchema, controllerSchema, package: packageJson,
|
|
11
|
+
function createCodeExamples({ handlerName, handlerSchema, controllerSchema, package: packageJson, }) {
|
|
21
12
|
const queryValidation = handlerSchema?.validation?.query;
|
|
22
13
|
const bodyValidation = handlerSchema?.validation?.body;
|
|
23
14
|
const paramsValidation = handlerSchema?.validation?.params;
|
|
24
15
|
const outputValidation = handlerSchema?.validation?.output;
|
|
25
16
|
const iterationValidation = handlerSchema?.validation?.iteration;
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const iterationFake = iterationValidation && sampler(iterationValidation);
|
|
31
|
-
const hasArg = !!queryFake || !!bodyFake || !!paramsFake;
|
|
17
|
+
const getTsSample = (schema, indent) => (0, getJSONSchemaExample_1.getJSONSchemaExample)(schema, { stripQuotes: true, indent: indent ?? 4 });
|
|
18
|
+
const getPySample = (schema, indent) => (0, getJSONSchemaExample_1.getJSONSchemaExample)(schema, { stripQuotes: false, indent: indent ?? 4, comment: '#' });
|
|
19
|
+
const getRsSample = (schema, indent) => (0, getJSONSchemaExample_1.getJSONSchemaExample)(schema, { stripQuotes: false, indent: indent ?? 4 });
|
|
20
|
+
const hasArg = !!queryValidation || !!bodyValidation || !!paramsValidation;
|
|
32
21
|
const rpcName = controllerSchema.rpcModuleName;
|
|
33
22
|
const handlerNameSnake = toSnakeCase(handlerName);
|
|
34
23
|
const rpcNameSnake = toSnakeCase(rpcName);
|
|
@@ -36,43 +25,50 @@ function createCodeExamples({ handlerName, handlerSchema, controllerSchema, pack
|
|
|
36
25
|
const packageNameSnake = toSnakeCase(packageName);
|
|
37
26
|
const tsArgs = hasArg
|
|
38
27
|
? `{
|
|
39
|
-
${
|
|
28
|
+
${[
|
|
29
|
+
bodyValidation ? ` body: ${getTsSample(bodyValidation)},` : null,
|
|
30
|
+
queryValidation ? ` query: ${getTsSample(queryValidation)},` : null,
|
|
31
|
+
paramsValidation ? ` params: ${getTsSample(paramsValidation)},` : null,
|
|
32
|
+
]
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.join('\n')}
|
|
35
|
+
}`
|
|
40
36
|
: '';
|
|
41
37
|
const TS_CODE = `import { ${rpcName} } from '${packageName}';
|
|
42
38
|
|
|
43
|
-
${
|
|
44
|
-
${
|
|
39
|
+
${iterationValidation ? 'using' : 'const'} response = await ${rpcName}.${handlerName}(${tsArgs});
|
|
40
|
+
${outputValidation
|
|
45
41
|
? `
|
|
46
42
|
console.log(response);
|
|
47
43
|
/*
|
|
48
|
-
${
|
|
44
|
+
${getTsSample(outputValidation, 0)}
|
|
49
45
|
*/`
|
|
50
|
-
: ''}${
|
|
46
|
+
: ''}${iterationValidation
|
|
51
47
|
? `
|
|
52
48
|
for await (const item of response) {
|
|
53
49
|
console.log(item);
|
|
54
50
|
/*
|
|
55
|
-
${
|
|
51
|
+
${getTsSample(iterationValidation, 2)}
|
|
56
52
|
*/
|
|
57
53
|
}`
|
|
58
54
|
: ''}`;
|
|
59
55
|
const PY_CODE = `from ${packageJson?.py_name ?? packageNameSnake} import ${rpcName}
|
|
60
56
|
|
|
61
|
-
response = ${rpcName}.${handlerNameSnake}(${hasArg
|
|
57
|
+
response = ${rpcName}.${handlerNameSnake}(${hasArg
|
|
58
|
+
? [
|
|
59
|
+
bodyValidation ? ` body=${getPySample(bodyValidation)},` : null,
|
|
60
|
+
queryValidation ? ` query=${getPySample(queryValidation)},` : null,
|
|
61
|
+
paramsValidation ? ` params=${getPySample(paramsValidation)},` : null,
|
|
62
|
+
]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join('\n')
|
|
65
|
+
: ''})
|
|
62
66
|
|
|
63
|
-
${
|
|
64
|
-
? `print(response)\n${JSON.stringify(outputFake, null, 2)
|
|
65
|
-
.split('\n')
|
|
66
|
-
.map((s) => `# ${s}`)
|
|
67
|
-
.join('\n')}`
|
|
68
|
-
: ''}${iterationFake
|
|
67
|
+
${outputValidation ? `print(response)\n${getPySample(outputValidation, 0)}` : ''}${iterationValidation
|
|
69
68
|
? `for i, item in enumerate(response):
|
|
70
|
-
print(f"iteration #{i}:\\n {item}")
|
|
71
|
-
# iteration #0:
|
|
72
|
-
${
|
|
73
|
-
.split('\n')
|
|
74
|
-
.map((s) => ` # ${s}`)
|
|
75
|
-
.join('\n')}`
|
|
69
|
+
print(f"iteration #{i}:\\n {item}")
|
|
70
|
+
# iteration #0:
|
|
71
|
+
${getPySample(iterationValidation, 2)}`
|
|
76
72
|
: ''}`;
|
|
77
73
|
const serdeUnwrap = (fake) => `from_value(json!(${fake})).unwrap()`;
|
|
78
74
|
const RS_CODE = `use ${packageJson?.rs_name ?? packageNameSnake}::${rpcNameSnake};
|
|
@@ -80,29 +76,29 @@ use serde_json::{ from_value, json };
|
|
|
80
76
|
|
|
81
77
|
pub fn main() {
|
|
82
78
|
let response = ${rpcNameSnake}::${handlerNameSnake}(
|
|
83
|
-
${
|
|
84
|
-
${
|
|
85
|
-
${
|
|
79
|
+
${bodyValidation ? serdeUnwrap(getRsSample(bodyValidation)) : '()'}, /* body */
|
|
80
|
+
${queryValidation ? serdeUnwrap(getRsSample(queryValidation)) : '()'}, /* query */
|
|
81
|
+
${paramsValidation ? serdeUnwrap(getRsSample(paramsValidation)) : '()'}, /* params */
|
|
86
82
|
None, /* headers (HashMap) */
|
|
87
83
|
None, /* api_root */
|
|
88
84
|
false, /* disable_client_validation */
|
|
89
85
|
);
|
|
90
86
|
|
|
91
|
-
${
|
|
87
|
+
${outputValidation
|
|
92
88
|
? `match response {
|
|
93
89
|
Ok(output) => println!("{:?}", output),
|
|
94
90
|
/*
|
|
95
|
-
output ${
|
|
91
|
+
output ${getTsSample(outputValidation, 2)}
|
|
96
92
|
*/
|
|
97
93
|
Err(e) => println!("error: {:?}", e),
|
|
98
94
|
}`
|
|
99
|
-
: ''}${
|
|
95
|
+
: ''}${iterationValidation
|
|
100
96
|
? `match response {
|
|
101
97
|
Ok(stream) => {
|
|
102
98
|
for (i, item) in stream.enumerate() {
|
|
103
99
|
println!("#{}: {:?}", i, item);
|
|
104
100
|
/*
|
|
105
|
-
#0: iteration ${
|
|
101
|
+
#0: iteration ${getTsSample(iterationValidation, 6)}
|
|
106
102
|
*/
|
|
107
103
|
}
|
|
108
104
|
},
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { KnownAny } from '../types';
|
|
2
|
+
interface SamplerOptions {
|
|
3
|
+
comment?: '//' | '#';
|
|
4
|
+
stripQuotes?: boolean;
|
|
5
|
+
indent?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function getJSONSchemaExample(schema: KnownAny, options: SamplerOptions, rootSchema?: KnownAny): string;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getJSONSchemaExample = getJSONSchemaExample;
|
|
4
|
+
function getJSONSchemaExample(schema, options, rootSchema) {
|
|
5
|
+
const { comment = '//', stripQuotes = false, indent = 0 } = options;
|
|
6
|
+
if (!schema || typeof schema !== 'object')
|
|
7
|
+
return 'null';
|
|
8
|
+
// Use the input schema as the root if not provided
|
|
9
|
+
rootSchema = rootSchema || schema;
|
|
10
|
+
// Get the sample value
|
|
11
|
+
const sampleValue = getSampleValue(schema, rootSchema);
|
|
12
|
+
// Format the output with descriptions
|
|
13
|
+
return formatWithDescriptions(sampleValue, schema, rootSchema, comment, stripQuotes, indent);
|
|
14
|
+
}
|
|
15
|
+
function getSampleValue(schema, rootSchema) {
|
|
16
|
+
if (!schema || typeof schema !== 'object')
|
|
17
|
+
return null;
|
|
18
|
+
// If there's an example, use it
|
|
19
|
+
if (schema.example !== undefined) {
|
|
20
|
+
return schema.example;
|
|
21
|
+
}
|
|
22
|
+
// If there are examples, use one of them
|
|
23
|
+
if (schema.examples && schema.examples.length > 0) {
|
|
24
|
+
return schema.examples[0];
|
|
25
|
+
}
|
|
26
|
+
// Handle const if present
|
|
27
|
+
if (schema.const !== undefined) {
|
|
28
|
+
return schema.const;
|
|
29
|
+
}
|
|
30
|
+
// Handle $ref if present
|
|
31
|
+
if (schema.$ref) {
|
|
32
|
+
return handleRef(schema.$ref, rootSchema);
|
|
33
|
+
}
|
|
34
|
+
// Handle enum if present
|
|
35
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
36
|
+
return schema.enum[0];
|
|
37
|
+
}
|
|
38
|
+
// Handle oneOf, anyOf, allOf
|
|
39
|
+
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
40
|
+
return getSampleValue(schema.oneOf[0], rootSchema);
|
|
41
|
+
}
|
|
42
|
+
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
43
|
+
return getSampleValue(schema.anyOf[0], rootSchema);
|
|
44
|
+
}
|
|
45
|
+
if (schema.allOf && schema.allOf.length > 0) {
|
|
46
|
+
// Merge all schemas in allOf
|
|
47
|
+
const mergedSchema = schema.allOf.reduce((acc, s) => ({ ...acc, ...s }), {});
|
|
48
|
+
return getSampleValue(mergedSchema, rootSchema);
|
|
49
|
+
}
|
|
50
|
+
// Handle different types
|
|
51
|
+
if (schema.type) {
|
|
52
|
+
switch (schema.type) {
|
|
53
|
+
case 'string':
|
|
54
|
+
return handleString(schema);
|
|
55
|
+
case 'number':
|
|
56
|
+
case 'integer':
|
|
57
|
+
return handleNumber(schema);
|
|
58
|
+
case 'boolean':
|
|
59
|
+
return handleBoolean();
|
|
60
|
+
case 'object':
|
|
61
|
+
return handleObject(schema, rootSchema);
|
|
62
|
+
case 'array':
|
|
63
|
+
return handleArray(schema, rootSchema);
|
|
64
|
+
case 'null':
|
|
65
|
+
return null;
|
|
66
|
+
default:
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// If type is not specified but properties are, treat it as an object
|
|
71
|
+
if (schema.properties) {
|
|
72
|
+
return handleObject(schema, rootSchema);
|
|
73
|
+
}
|
|
74
|
+
// Default fallback
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function formatWithDescriptions(value, schema, rootSchema, comment, stripQuotes, indent) {
|
|
78
|
+
const indentStr = ' '.repeat(indent);
|
|
79
|
+
// Handle null
|
|
80
|
+
if (value === null) {
|
|
81
|
+
return 'null';
|
|
82
|
+
}
|
|
83
|
+
// Handle primitives
|
|
84
|
+
if (typeof value !== 'object' || value instanceof Date) {
|
|
85
|
+
return JSON.stringify(value);
|
|
86
|
+
}
|
|
87
|
+
// Handle arrays
|
|
88
|
+
if (Array.isArray(value)) {
|
|
89
|
+
if (value.length === 0)
|
|
90
|
+
return '[]';
|
|
91
|
+
const items = value.map((item) => {
|
|
92
|
+
const itemSchema = schema.items || {};
|
|
93
|
+
const formattedItem = formatWithDescriptions(item, itemSchema, rootSchema, comment, stripQuotes, indent + 1);
|
|
94
|
+
return `${indentStr} ${formattedItem}`;
|
|
95
|
+
});
|
|
96
|
+
return `[\n${items.join(',\n')}\n${indentStr}]`;
|
|
97
|
+
}
|
|
98
|
+
// Handle objects
|
|
99
|
+
if (typeof value === 'object') {
|
|
100
|
+
const entries = Object.entries(value);
|
|
101
|
+
if (entries.length === 0)
|
|
102
|
+
return '{}';
|
|
103
|
+
const formattedEntries = [];
|
|
104
|
+
const isTopLevel = indent === 0;
|
|
105
|
+
// Add top-level description for objects
|
|
106
|
+
if (isTopLevel && schema.type === 'object' && schema.description) {
|
|
107
|
+
const descLines = schema.description.split('\n');
|
|
108
|
+
formattedEntries.push(`${indentStr}${comment} -----`);
|
|
109
|
+
descLines.forEach((line) => {
|
|
110
|
+
formattedEntries.push(`${indentStr}${comment} ${line.trim()}`);
|
|
111
|
+
});
|
|
112
|
+
formattedEntries.push(`${indentStr}${comment} -----`);
|
|
113
|
+
}
|
|
114
|
+
entries.forEach(([key, val], index) => {
|
|
115
|
+
const propSchema = schema.properties?.[key] || {};
|
|
116
|
+
// Handle $ref in property schema
|
|
117
|
+
let resolvedPropSchema = propSchema;
|
|
118
|
+
if (propSchema.$ref) {
|
|
119
|
+
resolvedPropSchema = resolveRef(propSchema.$ref, rootSchema);
|
|
120
|
+
}
|
|
121
|
+
// Add property description if it exists
|
|
122
|
+
if (resolvedPropSchema.description) {
|
|
123
|
+
const descLines = resolvedPropSchema.description.split('\n');
|
|
124
|
+
descLines.forEach((line) => {
|
|
125
|
+
formattedEntries.push(`${indentStr} ${comment} ${line.trim()}`);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// Format the key
|
|
129
|
+
const formattedKey = stripQuotes && /^[A-Za-z_$][0-9A-Za-z_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
130
|
+
// Format the value
|
|
131
|
+
const formattedValue = formatWithDescriptions(val, resolvedPropSchema, rootSchema, comment, stripQuotes, indent + 1);
|
|
132
|
+
formattedEntries.push(`${indentStr} ${formattedKey}: ${formattedValue}${index < entries.length - 1 ? ',' : ''}`);
|
|
133
|
+
});
|
|
134
|
+
return `{\n${formattedEntries.join('\n')}\n${indentStr}}`;
|
|
135
|
+
}
|
|
136
|
+
return JSON.stringify(value);
|
|
137
|
+
}
|
|
138
|
+
function resolveRef(ref, rootSchema) {
|
|
139
|
+
const path = ref.split('/').slice(1);
|
|
140
|
+
let current = rootSchema;
|
|
141
|
+
for (const segment of path) {
|
|
142
|
+
current = current[segment];
|
|
143
|
+
if (current === undefined) {
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return current;
|
|
148
|
+
}
|
|
149
|
+
function handleRef(ref, rootSchema) {
|
|
150
|
+
const resolved = resolveRef(ref, rootSchema);
|
|
151
|
+
return getSampleValue(resolved, rootSchema);
|
|
152
|
+
}
|
|
153
|
+
function handleString(schema) {
|
|
154
|
+
if (schema.format) {
|
|
155
|
+
switch (schema.format) {
|
|
156
|
+
case 'email':
|
|
157
|
+
case 'idn-email':
|
|
158
|
+
return 'user@example.com';
|
|
159
|
+
case 'uri':
|
|
160
|
+
case 'url':
|
|
161
|
+
case 'iri':
|
|
162
|
+
return 'https://example.com';
|
|
163
|
+
case 'date':
|
|
164
|
+
return '2023-01-01';
|
|
165
|
+
case 'date-time':
|
|
166
|
+
return '2023-01-01T00:00:00Z';
|
|
167
|
+
case 'time':
|
|
168
|
+
return '12:00:00Z';
|
|
169
|
+
case 'duration':
|
|
170
|
+
return 'PT1H';
|
|
171
|
+
case 'uuid':
|
|
172
|
+
return '00000000-0000-0000-0000-000000000000';
|
|
173
|
+
case 'regex':
|
|
174
|
+
return '^[a-zA-Z0-9]+$';
|
|
175
|
+
case 'relative-json-pointer':
|
|
176
|
+
return '/some/relative/path';
|
|
177
|
+
case 'color':
|
|
178
|
+
return '#000000';
|
|
179
|
+
case 'hostname':
|
|
180
|
+
return 'example.com';
|
|
181
|
+
case 'zipcode':
|
|
182
|
+
return '12345';
|
|
183
|
+
case 'phone':
|
|
184
|
+
return '+123-456-7890';
|
|
185
|
+
case 'password':
|
|
186
|
+
return '******';
|
|
187
|
+
default:
|
|
188
|
+
return 'string';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (schema.pattern) {
|
|
192
|
+
return 'pattern-string';
|
|
193
|
+
}
|
|
194
|
+
return 'string';
|
|
195
|
+
}
|
|
196
|
+
function handleNumber(schema) {
|
|
197
|
+
if (schema.minimum !== undefined && schema.maximum !== undefined) {
|
|
198
|
+
return schema.minimum;
|
|
199
|
+
}
|
|
200
|
+
else if (schema.minimum !== undefined) {
|
|
201
|
+
return schema.minimum;
|
|
202
|
+
}
|
|
203
|
+
else if (schema.maximum !== undefined) {
|
|
204
|
+
return schema.maximum;
|
|
205
|
+
}
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
function handleBoolean() {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
function handleObject(schema, rootSchema) {
|
|
212
|
+
const result = {};
|
|
213
|
+
if (schema.properties) {
|
|
214
|
+
const required = schema.required || [];
|
|
215
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
216
|
+
if (required.includes(key) || required.length === 0) {
|
|
217
|
+
result[key] = getSampleValue(propSchema, rootSchema);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
|
|
222
|
+
result['additionalProp'] = getSampleValue(schema.additionalProperties, rootSchema);
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
function handleArray(schema, rootSchema) {
|
|
227
|
+
if (schema.items) {
|
|
228
|
+
const itemSchema = schema.items;
|
|
229
|
+
const minItems = schema.minItems || 1;
|
|
230
|
+
const numItems = Math.min(minItems, 3);
|
|
231
|
+
return Array.from({ length: numItems }, () => getSampleValue(itemSchema, rootSchema));
|
|
232
|
+
}
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
function
|
|
3
|
+
exports.getJSONSchemaSample = getJSONSchemaSample;
|
|
4
|
+
function getJSONSchemaSample(schema, rootSchema) {
|
|
5
5
|
if (!schema || typeof schema !== 'object')
|
|
6
6
|
return null;
|
|
7
7
|
// Use the input schema as the root if not provided
|
|
@@ -28,15 +28,15 @@ function jsonSchemaSampler(schema, rootSchema) {
|
|
|
28
28
|
}
|
|
29
29
|
// Handle oneOf, anyOf, allOf
|
|
30
30
|
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
31
|
-
return
|
|
31
|
+
return getJSONSchemaSample(schema.oneOf[0], rootSchema);
|
|
32
32
|
}
|
|
33
33
|
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
34
|
-
return
|
|
34
|
+
return getJSONSchemaSample(schema.anyOf[0], rootSchema);
|
|
35
35
|
}
|
|
36
36
|
if (schema.allOf && schema.allOf.length > 0) {
|
|
37
37
|
// Merge all schemas in allOf
|
|
38
38
|
const mergedSchema = schema.allOf.reduce((acc, s) => ({ ...acc, ...s }), {});
|
|
39
|
-
return
|
|
39
|
+
return getJSONSchemaSample(mergedSchema, rootSchema);
|
|
40
40
|
}
|
|
41
41
|
// Handle different types
|
|
42
42
|
if (schema.type) {
|
|
@@ -77,7 +77,7 @@ function handleRef(ref, rootSchema) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
// Process the referenced schema
|
|
80
|
-
return
|
|
80
|
+
return getJSONSchemaSample(current, rootSchema);
|
|
81
81
|
}
|
|
82
82
|
function handleString(schema) {
|
|
83
83
|
if (schema.format) {
|
|
@@ -145,13 +145,13 @@ function handleObject(schema, rootSchema) {
|
|
|
145
145
|
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
146
146
|
// Only include required properties or as a basic example
|
|
147
147
|
if (required.includes(key) || required.length === 0) {
|
|
148
|
-
result[key] =
|
|
148
|
+
result[key] = getJSONSchemaSample(propSchema, rootSchema);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
// Handle additionalProperties
|
|
153
153
|
if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
|
|
154
|
-
result['additionalProp'] =
|
|
154
|
+
result['additionalProp'] = getJSONSchemaSample(schema.additionalProperties, rootSchema);
|
|
155
155
|
}
|
|
156
156
|
return result;
|
|
157
157
|
}
|
|
@@ -161,7 +161,7 @@ function handleArray(schema, rootSchema) {
|
|
|
161
161
|
const minItems = schema.minItems || 1;
|
|
162
162
|
// Create minimum number of items (capped at a reasonable max for examples)
|
|
163
163
|
const numItems = Math.min(minItems, 3);
|
|
164
|
-
return Array.from({ length: numItems }, () =>
|
|
164
|
+
return Array.from({ length: numItems }, () => getJSONSchemaSample(itemSchema, rootSchema));
|
|
165
165
|
}
|
|
166
166
|
return [];
|
|
167
167
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type VovkSchema, VovkStrictConfig } from '../../types';
|
|
2
|
-
export declare function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName, getMethodName, errorMessageKey, mixinName, }: VovkStrictConfig['openApiMixins'][string] & {
|
|
2
|
+
export declare function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName, getMethodName, errorMessageKey, package: packageJson, mixinName, }: VovkStrictConfig['openApiMixins'][string] & {
|
|
3
3
|
mixinName: string;
|
|
4
4
|
}): VovkSchema;
|
|
@@ -57,7 +57,7 @@ const normalizeGetMethodName = (getMethodName) => {
|
|
|
57
57
|
}
|
|
58
58
|
return getMethodName;
|
|
59
59
|
};
|
|
60
|
-
function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName = 'api', getMethodName = 'auto', errorMessageKey, mixinName, }) {
|
|
60
|
+
function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName = 'api', getMethodName = 'auto', errorMessageKey, package: packageJson, mixinName, }) {
|
|
61
61
|
const forceApiRoot = apiRoot ??
|
|
62
62
|
openAPIObject.servers?.[0]?.url ??
|
|
63
63
|
('host' in openAPIObject
|
|
@@ -77,6 +77,7 @@ function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getMo
|
|
|
77
77
|
controllers: {},
|
|
78
78
|
meta: {
|
|
79
79
|
components: openAPIObject.components,
|
|
80
|
+
package: packageJson,
|
|
80
81
|
},
|
|
81
82
|
},
|
|
82
83
|
},
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
2
2
|
import { type CodeSamplePackageJson } from '../utils/createCodeExamples';
|
|
3
|
-
import { type VovkSchema
|
|
4
|
-
export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson,
|
|
3
|
+
import { type VovkSchema } from '../types';
|
|
4
|
+
export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson, }: {
|
|
5
5
|
rootEntry: string;
|
|
6
6
|
schema: VovkSchema;
|
|
7
7
|
openAPIObject?: Partial<OpenAPIObject>;
|
|
8
8
|
package?: CodeSamplePackageJson;
|
|
9
|
-
sampler?: (schema: KnownAny) => KnownAny;
|
|
10
9
|
}): OpenAPIObject;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.vovkSchemaToOpenAPI = vovkSchemaToOpenAPI;
|
|
4
4
|
const createCodeExamples_1 = require("../utils/createCodeExamples");
|
|
5
|
-
const jsonSchemaSampler_1 = require("../utils/jsonSchemaSampler");
|
|
6
5
|
const types_1 = require("../types");
|
|
6
|
+
const getJSONSchemaSample_1 = require("../utils/getJSONSchemaSample");
|
|
7
7
|
function extractComponents(schema) {
|
|
8
8
|
if (!schema)
|
|
9
9
|
return [undefined, {}];
|
|
@@ -43,7 +43,7 @@ function extractComponents(schema) {
|
|
|
43
43
|
const processedSchema = process(schema);
|
|
44
44
|
return [processedSchema, components];
|
|
45
45
|
}
|
|
46
|
-
function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'my-rpc-client' },
|
|
46
|
+
function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'my-rpc-client' }, }) {
|
|
47
47
|
const paths = {};
|
|
48
48
|
const components = {};
|
|
49
49
|
for (const [segmentName, segmentSchema] of Object.entries(fullSchema.segments ?? {})) {
|
|
@@ -141,9 +141,9 @@ function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}
|
|
|
141
141
|
...iterationValidation,
|
|
142
142
|
examples: iterationValidation.examples ?? [
|
|
143
143
|
[
|
|
144
|
-
JSON.stringify(
|
|
145
|
-
JSON.stringify(
|
|
146
|
-
JSON.stringify(
|
|
144
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
145
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
146
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
147
147
|
].join('\n'),
|
|
148
148
|
],
|
|
149
149
|
},
|
package/mjs/types.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export type VovkSegmentSchema<T = KnownAny> = {
|
|
|
34
34
|
controllers: Record<string, VovkControllerSchema<T>>;
|
|
35
35
|
meta?: {
|
|
36
36
|
components?: OpenAPIObject['components'];
|
|
37
|
+
package?: PackageJson;
|
|
37
38
|
[key: string]: KnownAny;
|
|
38
39
|
};
|
|
39
40
|
};
|
|
@@ -241,6 +242,7 @@ export interface VovkLLMTool {
|
|
|
241
242
|
export type SimpleJSONSchema = {
|
|
242
243
|
type: 'object';
|
|
243
244
|
$ref?: string;
|
|
245
|
+
items?: SimpleJSONSchema;
|
|
244
246
|
description?: string;
|
|
245
247
|
properties: Record<string, SimpleJSONSchema>;
|
|
246
248
|
required?: string[];
|
|
@@ -7,12 +7,11 @@ export type CodeSamplePackageJson = {
|
|
|
7
7
|
py_name?: string;
|
|
8
8
|
[key: string]: KnownAny;
|
|
9
9
|
};
|
|
10
|
-
export declare function createCodeExamples({ handlerName, handlerSchema, controllerSchema, package: packageJson,
|
|
10
|
+
export declare function createCodeExamples({ handlerName, handlerSchema, controllerSchema, package: packageJson, }: {
|
|
11
11
|
handlerName: string;
|
|
12
12
|
handlerSchema: VovkHandlerSchema;
|
|
13
13
|
controllerSchema: VovkControllerSchema;
|
|
14
14
|
package?: CodeSamplePackageJson;
|
|
15
|
-
sampler?: (schema: KnownAny) => KnownAny;
|
|
16
15
|
}): {
|
|
17
16
|
ts: string;
|
|
18
17
|
py: string;
|
|
@@ -1,34 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createCodeExamples = createCodeExamples;
|
|
4
|
-
const
|
|
5
|
-
const stringifyTsSample = (data, pad = 4) => JSON.stringify(data, null, 2)
|
|
6
|
-
.replace(/"([A-Za-z_$][0-9A-Za-z_$]*)":/g, '$1:')
|
|
7
|
-
.split('\n')
|
|
8
|
-
.map((line, i, a) => (i === 0 ? line : i === a.length - 1 ? ' '.repeat(pad) + line : ' '.repeat(pad + 2) + line))
|
|
9
|
-
.join('\n');
|
|
10
|
-
const stringifyPySample = (data, pad = 4) => JSON.stringify(data, null, 2)
|
|
11
|
-
.split('\n')
|
|
12
|
-
.map((line, i, a) => (i === 0 ? line : i === a.length - 1 ? ' '.repeat(pad) + line : ' '.repeat(pad + 2) + line))
|
|
13
|
-
.join('\n');
|
|
4
|
+
const getJSONSchemaExample_1 = require("./getJSONSchemaExample");
|
|
14
5
|
const toSnakeCase = (str) => str
|
|
15
6
|
.replace(/-/g, '_') // Replace hyphens with underscores
|
|
16
7
|
.replace(/([a-z0-9])([A-Z])/g, '$1_$2') // Add underscore between lowercase/digit and uppercase
|
|
17
8
|
.replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1_$2') // Add underscore between uppercase letters if the second one is followed by a lowercase
|
|
18
9
|
.toLowerCase()
|
|
19
10
|
.replace(/^_/, ''); // Remove leading underscore
|
|
20
|
-
function createCodeExamples({ handlerName, handlerSchema, controllerSchema, package: packageJson,
|
|
11
|
+
function createCodeExamples({ handlerName, handlerSchema, controllerSchema, package: packageJson, }) {
|
|
21
12
|
const queryValidation = handlerSchema?.validation?.query;
|
|
22
13
|
const bodyValidation = handlerSchema?.validation?.body;
|
|
23
14
|
const paramsValidation = handlerSchema?.validation?.params;
|
|
24
15
|
const outputValidation = handlerSchema?.validation?.output;
|
|
25
16
|
const iterationValidation = handlerSchema?.validation?.iteration;
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const iterationFake = iterationValidation && sampler(iterationValidation);
|
|
31
|
-
const hasArg = !!queryFake || !!bodyFake || !!paramsFake;
|
|
17
|
+
const getTsSample = (schema, indent) => (0, getJSONSchemaExample_1.getJSONSchemaExample)(schema, { stripQuotes: true, indent: indent ?? 4 });
|
|
18
|
+
const getPySample = (schema, indent) => (0, getJSONSchemaExample_1.getJSONSchemaExample)(schema, { stripQuotes: false, indent: indent ?? 4, comment: '#' });
|
|
19
|
+
const getRsSample = (schema, indent) => (0, getJSONSchemaExample_1.getJSONSchemaExample)(schema, { stripQuotes: false, indent: indent ?? 4 });
|
|
20
|
+
const hasArg = !!queryValidation || !!bodyValidation || !!paramsValidation;
|
|
32
21
|
const rpcName = controllerSchema.rpcModuleName;
|
|
33
22
|
const handlerNameSnake = toSnakeCase(handlerName);
|
|
34
23
|
const rpcNameSnake = toSnakeCase(rpcName);
|
|
@@ -36,43 +25,50 @@ function createCodeExamples({ handlerName, handlerSchema, controllerSchema, pack
|
|
|
36
25
|
const packageNameSnake = toSnakeCase(packageName);
|
|
37
26
|
const tsArgs = hasArg
|
|
38
27
|
? `{
|
|
39
|
-
${
|
|
28
|
+
${[
|
|
29
|
+
bodyValidation ? ` body: ${getTsSample(bodyValidation)},` : null,
|
|
30
|
+
queryValidation ? ` query: ${getTsSample(queryValidation)},` : null,
|
|
31
|
+
paramsValidation ? ` params: ${getTsSample(paramsValidation)},` : null,
|
|
32
|
+
]
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.join('\n')}
|
|
35
|
+
}`
|
|
40
36
|
: '';
|
|
41
37
|
const TS_CODE = `import { ${rpcName} } from '${packageName}';
|
|
42
38
|
|
|
43
|
-
${
|
|
44
|
-
${
|
|
39
|
+
${iterationValidation ? 'using' : 'const'} response = await ${rpcName}.${handlerName}(${tsArgs});
|
|
40
|
+
${outputValidation
|
|
45
41
|
? `
|
|
46
42
|
console.log(response);
|
|
47
43
|
/*
|
|
48
|
-
${
|
|
44
|
+
${getTsSample(outputValidation, 0)}
|
|
49
45
|
*/`
|
|
50
|
-
: ''}${
|
|
46
|
+
: ''}${iterationValidation
|
|
51
47
|
? `
|
|
52
48
|
for await (const item of response) {
|
|
53
49
|
console.log(item);
|
|
54
50
|
/*
|
|
55
|
-
${
|
|
51
|
+
${getTsSample(iterationValidation, 2)}
|
|
56
52
|
*/
|
|
57
53
|
}`
|
|
58
54
|
: ''}`;
|
|
59
55
|
const PY_CODE = `from ${packageJson?.py_name ?? packageNameSnake} import ${rpcName}
|
|
60
56
|
|
|
61
|
-
response = ${rpcName}.${handlerNameSnake}(${hasArg
|
|
57
|
+
response = ${rpcName}.${handlerNameSnake}(${hasArg
|
|
58
|
+
? [
|
|
59
|
+
bodyValidation ? ` body=${getPySample(bodyValidation)},` : null,
|
|
60
|
+
queryValidation ? ` query=${getPySample(queryValidation)},` : null,
|
|
61
|
+
paramsValidation ? ` params=${getPySample(paramsValidation)},` : null,
|
|
62
|
+
]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join('\n')
|
|
65
|
+
: ''})
|
|
62
66
|
|
|
63
|
-
${
|
|
64
|
-
? `print(response)\n${JSON.stringify(outputFake, null, 2)
|
|
65
|
-
.split('\n')
|
|
66
|
-
.map((s) => `# ${s}`)
|
|
67
|
-
.join('\n')}`
|
|
68
|
-
: ''}${iterationFake
|
|
67
|
+
${outputValidation ? `print(response)\n${getPySample(outputValidation, 0)}` : ''}${iterationValidation
|
|
69
68
|
? `for i, item in enumerate(response):
|
|
70
|
-
print(f"iteration #{i}:\\n {item}")
|
|
71
|
-
# iteration #0:
|
|
72
|
-
${
|
|
73
|
-
.split('\n')
|
|
74
|
-
.map((s) => ` # ${s}`)
|
|
75
|
-
.join('\n')}`
|
|
69
|
+
print(f"iteration #{i}:\\n {item}")
|
|
70
|
+
# iteration #0:
|
|
71
|
+
${getPySample(iterationValidation, 2)}`
|
|
76
72
|
: ''}`;
|
|
77
73
|
const serdeUnwrap = (fake) => `from_value(json!(${fake})).unwrap()`;
|
|
78
74
|
const RS_CODE = `use ${packageJson?.rs_name ?? packageNameSnake}::${rpcNameSnake};
|
|
@@ -80,29 +76,29 @@ use serde_json::{ from_value, json };
|
|
|
80
76
|
|
|
81
77
|
pub fn main() {
|
|
82
78
|
let response = ${rpcNameSnake}::${handlerNameSnake}(
|
|
83
|
-
${
|
|
84
|
-
${
|
|
85
|
-
${
|
|
79
|
+
${bodyValidation ? serdeUnwrap(getRsSample(bodyValidation)) : '()'}, /* body */
|
|
80
|
+
${queryValidation ? serdeUnwrap(getRsSample(queryValidation)) : '()'}, /* query */
|
|
81
|
+
${paramsValidation ? serdeUnwrap(getRsSample(paramsValidation)) : '()'}, /* params */
|
|
86
82
|
None, /* headers (HashMap) */
|
|
87
83
|
None, /* api_root */
|
|
88
84
|
false, /* disable_client_validation */
|
|
89
85
|
);
|
|
90
86
|
|
|
91
|
-
${
|
|
87
|
+
${outputValidation
|
|
92
88
|
? `match response {
|
|
93
89
|
Ok(output) => println!("{:?}", output),
|
|
94
90
|
/*
|
|
95
|
-
output ${
|
|
91
|
+
output ${getTsSample(outputValidation, 2)}
|
|
96
92
|
*/
|
|
97
93
|
Err(e) => println!("error: {:?}", e),
|
|
98
94
|
}`
|
|
99
|
-
: ''}${
|
|
95
|
+
: ''}${iterationValidation
|
|
100
96
|
? `match response {
|
|
101
97
|
Ok(stream) => {
|
|
102
98
|
for (i, item) in stream.enumerate() {
|
|
103
99
|
println!("#{}: {:?}", i, item);
|
|
104
100
|
/*
|
|
105
|
-
#0: iteration ${
|
|
101
|
+
#0: iteration ${getTsSample(iterationValidation, 6)}
|
|
106
102
|
*/
|
|
107
103
|
}
|
|
108
104
|
},
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { KnownAny } from '../types';
|
|
2
|
+
interface SamplerOptions {
|
|
3
|
+
comment?: '//' | '#';
|
|
4
|
+
stripQuotes?: boolean;
|
|
5
|
+
indent?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function getJSONSchemaExample(schema: KnownAny, options: SamplerOptions, rootSchema?: KnownAny): string;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getJSONSchemaExample = getJSONSchemaExample;
|
|
4
|
+
function getJSONSchemaExample(schema, options, rootSchema) {
|
|
5
|
+
const { comment = '//', stripQuotes = false, indent = 0 } = options;
|
|
6
|
+
if (!schema || typeof schema !== 'object')
|
|
7
|
+
return 'null';
|
|
8
|
+
// Use the input schema as the root if not provided
|
|
9
|
+
rootSchema = rootSchema || schema;
|
|
10
|
+
// Get the sample value
|
|
11
|
+
const sampleValue = getSampleValue(schema, rootSchema);
|
|
12
|
+
// Format the output with descriptions
|
|
13
|
+
return formatWithDescriptions(sampleValue, schema, rootSchema, comment, stripQuotes, indent);
|
|
14
|
+
}
|
|
15
|
+
function getSampleValue(schema, rootSchema) {
|
|
16
|
+
if (!schema || typeof schema !== 'object')
|
|
17
|
+
return null;
|
|
18
|
+
// If there's an example, use it
|
|
19
|
+
if (schema.example !== undefined) {
|
|
20
|
+
return schema.example;
|
|
21
|
+
}
|
|
22
|
+
// If there are examples, use one of them
|
|
23
|
+
if (schema.examples && schema.examples.length > 0) {
|
|
24
|
+
return schema.examples[0];
|
|
25
|
+
}
|
|
26
|
+
// Handle const if present
|
|
27
|
+
if (schema.const !== undefined) {
|
|
28
|
+
return schema.const;
|
|
29
|
+
}
|
|
30
|
+
// Handle $ref if present
|
|
31
|
+
if (schema.$ref) {
|
|
32
|
+
return handleRef(schema.$ref, rootSchema);
|
|
33
|
+
}
|
|
34
|
+
// Handle enum if present
|
|
35
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
36
|
+
return schema.enum[0];
|
|
37
|
+
}
|
|
38
|
+
// Handle oneOf, anyOf, allOf
|
|
39
|
+
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
40
|
+
return getSampleValue(schema.oneOf[0], rootSchema);
|
|
41
|
+
}
|
|
42
|
+
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
43
|
+
return getSampleValue(schema.anyOf[0], rootSchema);
|
|
44
|
+
}
|
|
45
|
+
if (schema.allOf && schema.allOf.length > 0) {
|
|
46
|
+
// Merge all schemas in allOf
|
|
47
|
+
const mergedSchema = schema.allOf.reduce((acc, s) => ({ ...acc, ...s }), {});
|
|
48
|
+
return getSampleValue(mergedSchema, rootSchema);
|
|
49
|
+
}
|
|
50
|
+
// Handle different types
|
|
51
|
+
if (schema.type) {
|
|
52
|
+
switch (schema.type) {
|
|
53
|
+
case 'string':
|
|
54
|
+
return handleString(schema);
|
|
55
|
+
case 'number':
|
|
56
|
+
case 'integer':
|
|
57
|
+
return handleNumber(schema);
|
|
58
|
+
case 'boolean':
|
|
59
|
+
return handleBoolean();
|
|
60
|
+
case 'object':
|
|
61
|
+
return handleObject(schema, rootSchema);
|
|
62
|
+
case 'array':
|
|
63
|
+
return handleArray(schema, rootSchema);
|
|
64
|
+
case 'null':
|
|
65
|
+
return null;
|
|
66
|
+
default:
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// If type is not specified but properties are, treat it as an object
|
|
71
|
+
if (schema.properties) {
|
|
72
|
+
return handleObject(schema, rootSchema);
|
|
73
|
+
}
|
|
74
|
+
// Default fallback
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function formatWithDescriptions(value, schema, rootSchema, comment, stripQuotes, indent) {
|
|
78
|
+
const indentStr = ' '.repeat(indent);
|
|
79
|
+
// Handle null
|
|
80
|
+
if (value === null) {
|
|
81
|
+
return 'null';
|
|
82
|
+
}
|
|
83
|
+
// Handle primitives
|
|
84
|
+
if (typeof value !== 'object' || value instanceof Date) {
|
|
85
|
+
return JSON.stringify(value);
|
|
86
|
+
}
|
|
87
|
+
// Handle arrays
|
|
88
|
+
if (Array.isArray(value)) {
|
|
89
|
+
if (value.length === 0)
|
|
90
|
+
return '[]';
|
|
91
|
+
const items = value.map((item) => {
|
|
92
|
+
const itemSchema = schema.items || {};
|
|
93
|
+
const formattedItem = formatWithDescriptions(item, itemSchema, rootSchema, comment, stripQuotes, indent + 1);
|
|
94
|
+
return `${indentStr} ${formattedItem}`;
|
|
95
|
+
});
|
|
96
|
+
return `[\n${items.join(',\n')}\n${indentStr}]`;
|
|
97
|
+
}
|
|
98
|
+
// Handle objects
|
|
99
|
+
if (typeof value === 'object') {
|
|
100
|
+
const entries = Object.entries(value);
|
|
101
|
+
if (entries.length === 0)
|
|
102
|
+
return '{}';
|
|
103
|
+
const formattedEntries = [];
|
|
104
|
+
const isTopLevel = indent === 0;
|
|
105
|
+
// Add top-level description for objects
|
|
106
|
+
if (isTopLevel && schema.type === 'object' && schema.description) {
|
|
107
|
+
const descLines = schema.description.split('\n');
|
|
108
|
+
formattedEntries.push(`${indentStr}${comment} -----`);
|
|
109
|
+
descLines.forEach((line) => {
|
|
110
|
+
formattedEntries.push(`${indentStr}${comment} ${line.trim()}`);
|
|
111
|
+
});
|
|
112
|
+
formattedEntries.push(`${indentStr}${comment} -----`);
|
|
113
|
+
}
|
|
114
|
+
entries.forEach(([key, val], index) => {
|
|
115
|
+
const propSchema = schema.properties?.[key] || {};
|
|
116
|
+
// Handle $ref in property schema
|
|
117
|
+
let resolvedPropSchema = propSchema;
|
|
118
|
+
if (propSchema.$ref) {
|
|
119
|
+
resolvedPropSchema = resolveRef(propSchema.$ref, rootSchema);
|
|
120
|
+
}
|
|
121
|
+
// Add property description if it exists
|
|
122
|
+
if (resolvedPropSchema.description) {
|
|
123
|
+
const descLines = resolvedPropSchema.description.split('\n');
|
|
124
|
+
descLines.forEach((line) => {
|
|
125
|
+
formattedEntries.push(`${indentStr} ${comment} ${line.trim()}`);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// Format the key
|
|
129
|
+
const formattedKey = stripQuotes && /^[A-Za-z_$][0-9A-Za-z_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
130
|
+
// Format the value
|
|
131
|
+
const formattedValue = formatWithDescriptions(val, resolvedPropSchema, rootSchema, comment, stripQuotes, indent + 1);
|
|
132
|
+
formattedEntries.push(`${indentStr} ${formattedKey}: ${formattedValue}${index < entries.length - 1 ? ',' : ''}`);
|
|
133
|
+
});
|
|
134
|
+
return `{\n${formattedEntries.join('\n')}\n${indentStr}}`;
|
|
135
|
+
}
|
|
136
|
+
return JSON.stringify(value);
|
|
137
|
+
}
|
|
138
|
+
function resolveRef(ref, rootSchema) {
|
|
139
|
+
const path = ref.split('/').slice(1);
|
|
140
|
+
let current = rootSchema;
|
|
141
|
+
for (const segment of path) {
|
|
142
|
+
current = current[segment];
|
|
143
|
+
if (current === undefined) {
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return current;
|
|
148
|
+
}
|
|
149
|
+
function handleRef(ref, rootSchema) {
|
|
150
|
+
const resolved = resolveRef(ref, rootSchema);
|
|
151
|
+
return getSampleValue(resolved, rootSchema);
|
|
152
|
+
}
|
|
153
|
+
function handleString(schema) {
|
|
154
|
+
if (schema.format) {
|
|
155
|
+
switch (schema.format) {
|
|
156
|
+
case 'email':
|
|
157
|
+
case 'idn-email':
|
|
158
|
+
return 'user@example.com';
|
|
159
|
+
case 'uri':
|
|
160
|
+
case 'url':
|
|
161
|
+
case 'iri':
|
|
162
|
+
return 'https://example.com';
|
|
163
|
+
case 'date':
|
|
164
|
+
return '2023-01-01';
|
|
165
|
+
case 'date-time':
|
|
166
|
+
return '2023-01-01T00:00:00Z';
|
|
167
|
+
case 'time':
|
|
168
|
+
return '12:00:00Z';
|
|
169
|
+
case 'duration':
|
|
170
|
+
return 'PT1H';
|
|
171
|
+
case 'uuid':
|
|
172
|
+
return '00000000-0000-0000-0000-000000000000';
|
|
173
|
+
case 'regex':
|
|
174
|
+
return '^[a-zA-Z0-9]+$';
|
|
175
|
+
case 'relative-json-pointer':
|
|
176
|
+
return '/some/relative/path';
|
|
177
|
+
case 'color':
|
|
178
|
+
return '#000000';
|
|
179
|
+
case 'hostname':
|
|
180
|
+
return 'example.com';
|
|
181
|
+
case 'zipcode':
|
|
182
|
+
return '12345';
|
|
183
|
+
case 'phone':
|
|
184
|
+
return '+123-456-7890';
|
|
185
|
+
case 'password':
|
|
186
|
+
return '******';
|
|
187
|
+
default:
|
|
188
|
+
return 'string';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (schema.pattern) {
|
|
192
|
+
return 'pattern-string';
|
|
193
|
+
}
|
|
194
|
+
return 'string';
|
|
195
|
+
}
|
|
196
|
+
function handleNumber(schema) {
|
|
197
|
+
if (schema.minimum !== undefined && schema.maximum !== undefined) {
|
|
198
|
+
return schema.minimum;
|
|
199
|
+
}
|
|
200
|
+
else if (schema.minimum !== undefined) {
|
|
201
|
+
return schema.minimum;
|
|
202
|
+
}
|
|
203
|
+
else if (schema.maximum !== undefined) {
|
|
204
|
+
return schema.maximum;
|
|
205
|
+
}
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
208
|
+
function handleBoolean() {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
function handleObject(schema, rootSchema) {
|
|
212
|
+
const result = {};
|
|
213
|
+
if (schema.properties) {
|
|
214
|
+
const required = schema.required || [];
|
|
215
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
216
|
+
if (required.includes(key) || required.length === 0) {
|
|
217
|
+
result[key] = getSampleValue(propSchema, rootSchema);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
|
|
222
|
+
result['additionalProp'] = getSampleValue(schema.additionalProperties, rootSchema);
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
function handleArray(schema, rootSchema) {
|
|
227
|
+
if (schema.items) {
|
|
228
|
+
const itemSchema = schema.items;
|
|
229
|
+
const minItems = schema.minItems || 1;
|
|
230
|
+
const numItems = Math.min(minItems, 3);
|
|
231
|
+
return Array.from({ length: numItems }, () => getSampleValue(itemSchema, rootSchema));
|
|
232
|
+
}
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
function
|
|
3
|
+
exports.getJSONSchemaSample = getJSONSchemaSample;
|
|
4
|
+
function getJSONSchemaSample(schema, rootSchema) {
|
|
5
5
|
if (!schema || typeof schema !== 'object')
|
|
6
6
|
return null;
|
|
7
7
|
// Use the input schema as the root if not provided
|
|
@@ -28,15 +28,15 @@ function jsonSchemaSampler(schema, rootSchema) {
|
|
|
28
28
|
}
|
|
29
29
|
// Handle oneOf, anyOf, allOf
|
|
30
30
|
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
31
|
-
return
|
|
31
|
+
return getJSONSchemaSample(schema.oneOf[0], rootSchema);
|
|
32
32
|
}
|
|
33
33
|
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
34
|
-
return
|
|
34
|
+
return getJSONSchemaSample(schema.anyOf[0], rootSchema);
|
|
35
35
|
}
|
|
36
36
|
if (schema.allOf && schema.allOf.length > 0) {
|
|
37
37
|
// Merge all schemas in allOf
|
|
38
38
|
const mergedSchema = schema.allOf.reduce((acc, s) => ({ ...acc, ...s }), {});
|
|
39
|
-
return
|
|
39
|
+
return getJSONSchemaSample(mergedSchema, rootSchema);
|
|
40
40
|
}
|
|
41
41
|
// Handle different types
|
|
42
42
|
if (schema.type) {
|
|
@@ -77,7 +77,7 @@ function handleRef(ref, rootSchema) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
// Process the referenced schema
|
|
80
|
-
return
|
|
80
|
+
return getJSONSchemaSample(current, rootSchema);
|
|
81
81
|
}
|
|
82
82
|
function handleString(schema) {
|
|
83
83
|
if (schema.format) {
|
|
@@ -145,13 +145,13 @@ function handleObject(schema, rootSchema) {
|
|
|
145
145
|
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
146
146
|
// Only include required properties or as a basic example
|
|
147
147
|
if (required.includes(key) || required.length === 0) {
|
|
148
|
-
result[key] =
|
|
148
|
+
result[key] = getJSONSchemaSample(propSchema, rootSchema);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
// Handle additionalProperties
|
|
153
153
|
if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
|
|
154
|
-
result['additionalProp'] =
|
|
154
|
+
result['additionalProp'] = getJSONSchemaSample(schema.additionalProperties, rootSchema);
|
|
155
155
|
}
|
|
156
156
|
return result;
|
|
157
157
|
}
|
|
@@ -161,7 +161,7 @@ function handleArray(schema, rootSchema) {
|
|
|
161
161
|
const minItems = schema.minItems || 1;
|
|
162
162
|
// Create minimum number of items (capped at a reasonable max for examples)
|
|
163
163
|
const numItems = Math.min(minItems, 3);
|
|
164
|
-
return Array.from({ length: numItems }, () =>
|
|
164
|
+
return Array.from({ length: numItems }, () => getJSONSchemaSample(itemSchema, rootSchema));
|
|
165
165
|
}
|
|
166
166
|
return [];
|
|
167
167
|
}
|
package/package.json
CHANGED