docusaurus-plugin-generate-schema-docs 1.8.3 → 1.8.4
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 +2 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-not-allof.json +11 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +6 -0
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +6 -0
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +15 -0
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +6 -0
- package/__tests__/components/PropertiesTable.test.js +66 -0
- package/__tests__/components/PropertyRow.test.js +85 -4
- package/__tests__/components/SchemaJsonViewer.test.js +118 -0
- package/__tests__/helpers/example-helper.test.js +12 -0
- package/__tests__/helpers/getConstraints.test.js +16 -0
- package/__tests__/helpers/processSchema.test.js +18 -0
- package/__tests__/helpers/schemaTraversal.test.js +110 -0
- package/__tests__/syncGtm.test.js +227 -3
- package/__tests__/validateSchemas.test.js +50 -0
- package/components/PropertiesTable.js +32 -2
- package/components/PropertyRow.js +29 -2
- package/components/SchemaJsonViewer.js +234 -131
- package/components/SchemaRows.css +40 -0
- package/components/SchemaViewer.js +11 -2
- package/helpers/example-helper.js +2 -2
- package/helpers/getConstraints.js +20 -0
- package/helpers/processSchema.js +32 -1
- package/helpers/schema-doc-template.js +4 -0
- package/helpers/schemaToExamples.js +29 -35
- package/helpers/schemaTraversal.cjs +148 -0
- package/package.json +1 -1
- package/scripts/sync-gtm.js +41 -28
- package/test-data/payloadContracts.js +35 -0
- package/validateSchemas.js +1 -1
|
@@ -12,6 +12,7 @@ export default function MdxTemplate(data) {
|
|
|
12
12
|
sourcePath,
|
|
13
13
|
schemaSources,
|
|
14
14
|
} = data;
|
|
15
|
+
const sourceSchema = schemaSources?.[sourcePath] || schema;
|
|
15
16
|
|
|
16
17
|
return `---
|
|
17
18
|
title: ${schema.title}
|
|
@@ -33,6 +34,9 @@ ${topPartialComponent}
|
|
|
33
34
|
|
|
34
35
|
<SchemaViewer
|
|
35
36
|
schema={${JSON.stringify(mergedSchema)}}
|
|
37
|
+
sourceSchema={${JSON.stringify(sourceSchema)}}
|
|
38
|
+
sourcePath={${JSON.stringify(sourcePath)}}
|
|
39
|
+
schemaSources={${JSON.stringify(schemaSources)}}
|
|
36
40
|
${dataLayerName ? ` dataLayerName={'${dataLayerName}'}` : ''}
|
|
37
41
|
/>
|
|
38
42
|
<SchemaJsonViewer
|
|
@@ -1,44 +1,31 @@
|
|
|
1
1
|
import buildExampleFromSchema from './buildExampleFromSchema';
|
|
2
2
|
import { mergeSchema } from './mergeSchema.js';
|
|
3
|
+
import traversalHelpers from './schemaTraversal.cjs';
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
if (!subSchema) {
|
|
6
|
-
return [];
|
|
7
|
-
}
|
|
5
|
+
const { visitSchemaNodes } = traversalHelpers;
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
: subSchema.anyOf
|
|
12
|
-
? 'anyOf'
|
|
13
|
-
: null;
|
|
14
|
-
const currentChoice = choiceType ? [{ path, schema: subSchema }] : [];
|
|
7
|
+
const findChoicePoints = (schema) => {
|
|
8
|
+
const choicePoints = [];
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
visitSchemaNodes(schema, (subSchema, context) => {
|
|
11
|
+
if (subSchema.oneOf || subSchema.anyOf) {
|
|
12
|
+
choicePoints.push({ path: context.path, schema: subSchema });
|
|
13
|
+
}
|
|
14
|
+
});
|
|
21
15
|
|
|
22
|
-
return
|
|
16
|
+
return choicePoints;
|
|
23
17
|
};
|
|
24
18
|
|
|
25
|
-
const findConditionalPoints = (
|
|
26
|
-
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const currentConditional =
|
|
31
|
-
subSchema.if && (subSchema.then || subSchema.else)
|
|
32
|
-
? [{ path, schema: subSchema }]
|
|
33
|
-
: [];
|
|
19
|
+
const findConditionalPoints = (schema) => {
|
|
20
|
+
const conditionalPoints = [];
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
visitSchemaNodes(schema, (subSchema, context) => {
|
|
23
|
+
if (subSchema.if && (subSchema.then || subSchema.else)) {
|
|
24
|
+
conditionalPoints.push({ path: context.path, schema: subSchema });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
40
27
|
|
|
41
|
-
return
|
|
28
|
+
return conditionalPoints;
|
|
42
29
|
};
|
|
43
30
|
|
|
44
31
|
const generateExampleForChoice = (rootSchema, path, option) => {
|
|
@@ -151,10 +138,17 @@ export function schemaToExamples(rootSchema) {
|
|
|
151
138
|
|
|
152
139
|
if (choicePoints.length === 0 && conditionalPoints.length === 0) {
|
|
153
140
|
const example = buildExampleFromSchema(rootSchema);
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
141
|
+
if (typeof example !== 'undefined') {
|
|
142
|
+
const shouldIncludeObjectExample =
|
|
143
|
+
typeof example !== 'object' ||
|
|
144
|
+
example === null ||
|
|
145
|
+
Object.keys(example).length > 0;
|
|
146
|
+
|
|
147
|
+
if (shouldIncludeObjectExample) {
|
|
148
|
+
return [
|
|
149
|
+
{ property: 'default', options: [{ title: 'Example', example }] },
|
|
150
|
+
];
|
|
151
|
+
}
|
|
158
152
|
}
|
|
159
153
|
return [];
|
|
160
154
|
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
function visitSchemaNodes(schema, visitor, path = []) {
|
|
2
|
+
if (!schema || typeof schema !== 'object') {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
visitor(schema, { path });
|
|
7
|
+
|
|
8
|
+
if (schema.properties && typeof schema.properties === 'object') {
|
|
9
|
+
Object.entries(schema.properties).forEach(([key, propertySchema]) => {
|
|
10
|
+
visitSchemaNodes(propertySchema, visitor, [...path, 'properties', key]);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (schema.items && typeof schema.items === 'object') {
|
|
15
|
+
visitSchemaNodes(schema.items, visitor, [...path, 'items']);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (Array.isArray(schema.oneOf)) {
|
|
19
|
+
schema.oneOf.forEach((optionSchema, index) => {
|
|
20
|
+
visitSchemaNodes(optionSchema, visitor, [...path, 'oneOf', index]);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (Array.isArray(schema.anyOf)) {
|
|
25
|
+
schema.anyOf.forEach((optionSchema, index) => {
|
|
26
|
+
visitSchemaNodes(optionSchema, visitor, [...path, 'anyOf', index]);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (schema.if && typeof schema.if === 'object') {
|
|
31
|
+
visitSchemaNodes(schema.if, visitor, [...path, 'if']);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (schema.then && typeof schema.then === 'object') {
|
|
35
|
+
visitSchemaNodes(schema.then, visitor, [...path, 'then']);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (schema.else && typeof schema.else === 'object') {
|
|
39
|
+
visitSchemaNodes(schema.else, visitor, [...path, 'else']);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function visitPropertyEntryBranches(
|
|
44
|
+
schema,
|
|
45
|
+
visitor,
|
|
46
|
+
{ prefix = '', path = [], skipArraySubProperties = false } = {},
|
|
47
|
+
) {
|
|
48
|
+
if (!schema || typeof schema !== 'object') {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (Array.isArray(schema.oneOf)) {
|
|
53
|
+
schema.oneOf.forEach((optionSchema, index) => {
|
|
54
|
+
visitSchemaPropertyEntries(optionSchema, visitor, {
|
|
55
|
+
prefix,
|
|
56
|
+
path: [...path, 'oneOf', index],
|
|
57
|
+
skipArraySubProperties,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (Array.isArray(schema.anyOf)) {
|
|
63
|
+
schema.anyOf.forEach((optionSchema, index) => {
|
|
64
|
+
visitSchemaPropertyEntries(optionSchema, visitor, {
|
|
65
|
+
prefix,
|
|
66
|
+
path: [...path, 'anyOf', index],
|
|
67
|
+
skipArraySubProperties,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (schema.then && typeof schema.then === 'object') {
|
|
73
|
+
visitSchemaPropertyEntries(schema.then, visitor, {
|
|
74
|
+
prefix,
|
|
75
|
+
path: [...path, 'then'],
|
|
76
|
+
skipArraySubProperties,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (schema.else && typeof schema.else === 'object') {
|
|
81
|
+
visitSchemaPropertyEntries(schema.else, visitor, {
|
|
82
|
+
prefix,
|
|
83
|
+
path: [...path, 'else'],
|
|
84
|
+
skipArraySubProperties,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function visitSchemaPropertyEntries(
|
|
90
|
+
schema,
|
|
91
|
+
visitor,
|
|
92
|
+
{ prefix = '', path = [], skipArraySubProperties = false } = {},
|
|
93
|
+
) {
|
|
94
|
+
if (!schema || typeof schema !== 'object') {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (schema.properties && typeof schema.properties === 'object') {
|
|
99
|
+
Object.entries(schema.properties).forEach(([key, propertySchema]) => {
|
|
100
|
+
const currentName = prefix ? `${prefix}.${key}` : key;
|
|
101
|
+
const currentPath = [...path, 'properties', key];
|
|
102
|
+
|
|
103
|
+
visitor(propertySchema, {
|
|
104
|
+
key,
|
|
105
|
+
name: currentName,
|
|
106
|
+
path: currentPath,
|
|
107
|
+
parentSchema: schema,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (propertySchema?.type === 'object' && propertySchema.properties) {
|
|
111
|
+
visitSchemaPropertyEntries(propertySchema, visitor, {
|
|
112
|
+
prefix: currentName,
|
|
113
|
+
path: currentPath,
|
|
114
|
+
skipArraySubProperties,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (
|
|
119
|
+
propertySchema?.type === 'array' &&
|
|
120
|
+
propertySchema.items?.properties &&
|
|
121
|
+
!skipArraySubProperties
|
|
122
|
+
) {
|
|
123
|
+
visitSchemaPropertyEntries(propertySchema.items, visitor, {
|
|
124
|
+
prefix: `${currentName}.0`,
|
|
125
|
+
path: [...currentPath, 'items'],
|
|
126
|
+
skipArraySubProperties,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
visitPropertyEntryBranches(propertySchema, visitor, {
|
|
131
|
+
prefix: currentName,
|
|
132
|
+
path: currentPath,
|
|
133
|
+
skipArraySubProperties,
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
visitPropertyEntryBranches(schema, visitor, {
|
|
139
|
+
prefix,
|
|
140
|
+
path,
|
|
141
|
+
skipArraySubProperties,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = {
|
|
146
|
+
visitSchemaNodes,
|
|
147
|
+
visitSchemaPropertyEntries,
|
|
148
|
+
};
|
package/package.json
CHANGED
package/scripts/sync-gtm.js
CHANGED
|
@@ -3,6 +3,9 @@ const path = require('path');
|
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const RefParser = require('@apidevtools/json-schema-ref-parser');
|
|
5
5
|
const mergeAllOf = require('json-schema-merge-allof');
|
|
6
|
+
const {
|
|
7
|
+
visitSchemaPropertyEntries,
|
|
8
|
+
} = require('../helpers/schemaTraversal.cjs');
|
|
6
9
|
|
|
7
10
|
const logger = {
|
|
8
11
|
_isQuiet: false,
|
|
@@ -86,49 +89,58 @@ function findJsonFiles(dir) {
|
|
|
86
89
|
return results;
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
function isRootTrackingSchema(schema) {
|
|
93
|
+
return Boolean(schema?.properties?.event || schema?.['x-tracking-targets']);
|
|
94
|
+
}
|
|
95
|
+
|
|
89
96
|
function parseSchema(schema, options, prefix = '') {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
const variables = [];
|
|
98
|
+
|
|
99
|
+
visitSchemaPropertyEntries(
|
|
100
|
+
schema,
|
|
101
|
+
(property, context) => {
|
|
102
|
+
variables.push({
|
|
103
|
+
name: context.name,
|
|
104
|
+
description: property.description,
|
|
105
|
+
type: property.type,
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
prefix,
|
|
110
|
+
skipArraySubProperties: options.skipArraySubProperties,
|
|
111
|
+
},
|
|
112
|
+
);
|
|
93
113
|
|
|
94
|
-
let variables = [];
|
|
95
|
-
for (const key in schema.properties) {
|
|
96
|
-
const property = schema.properties[key];
|
|
97
|
-
const currentPath = prefix ? `${prefix}.${key}` : key;
|
|
98
|
-
variables.push({
|
|
99
|
-
name: currentPath,
|
|
100
|
-
description: property.description,
|
|
101
|
-
type: property.type,
|
|
102
|
-
});
|
|
103
|
-
if (
|
|
104
|
-
property.type === 'array' &&
|
|
105
|
-
property.items &&
|
|
106
|
-
!options.skipArraySubProperties
|
|
107
|
-
) {
|
|
108
|
-
if (property.items.properties) {
|
|
109
|
-
variables.push(
|
|
110
|
-
...parseSchema(property.items, options, `${currentPath}.0`),
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
} else if (property.type === 'object' && property.properties) {
|
|
114
|
-
variables.push(...parseSchema(property, options, currentPath));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
114
|
return variables;
|
|
118
115
|
}
|
|
119
116
|
|
|
117
|
+
function shouldIncludeSchemaForGtm(schema) {
|
|
118
|
+
if (!isRootTrackingSchema(schema)) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const trackingTargets = schema?.['x-tracking-targets'];
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
Array.isArray(trackingTargets) &&
|
|
126
|
+
trackingTargets.includes('web-datalayer-js')
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
120
130
|
async function getVariablesFromSchemas(
|
|
121
131
|
schemaPath,
|
|
122
132
|
{ skipArraySubProperties = false },
|
|
123
133
|
) {
|
|
124
134
|
const allVariables = new Map();
|
|
125
135
|
const jsonFiles = findJsonFiles(schemaPath);
|
|
126
|
-
const eventFiles = jsonFiles.filter((f) => !f.includes('components'));
|
|
127
136
|
|
|
128
|
-
for (const file of
|
|
137
|
+
for (const file of jsonFiles) {
|
|
129
138
|
try {
|
|
130
139
|
let schema = await RefParser.bundle(file);
|
|
131
140
|
schema = mergeAllOf(schema);
|
|
141
|
+
if (!shouldIncludeSchemaForGtm(schema)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
132
144
|
const fileVariables = parseSchema(schema, { skipArraySubProperties });
|
|
133
145
|
for (const variable of fileVariables) {
|
|
134
146
|
if (!allVariables.has(variable.name)) {
|
|
@@ -388,6 +400,7 @@ module.exports = {
|
|
|
388
400
|
createGtmVariables,
|
|
389
401
|
deleteGtmVariables,
|
|
390
402
|
parseSchema,
|
|
403
|
+
shouldIncludeSchemaForGtm,
|
|
391
404
|
findJsonFiles,
|
|
392
405
|
safeJsonParse,
|
|
393
406
|
logger,
|
|
@@ -118,6 +118,41 @@ const PAYLOAD_CONTRACTS = [
|
|
|
118
118
|
},
|
|
119
119
|
},
|
|
120
120
|
},
|
|
121
|
+
{
|
|
122
|
+
id: 'login-with-user-properties',
|
|
123
|
+
class: 'user_properties',
|
|
124
|
+
example: {
|
|
125
|
+
event: 'login',
|
|
126
|
+
method: 'email',
|
|
127
|
+
user_properties: {
|
|
128
|
+
sign_up_method: 'email',
|
|
129
|
+
allow_ad_personalization_signals: 'false',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
expected: {
|
|
133
|
+
web: {
|
|
134
|
+
eventName: 'login',
|
|
135
|
+
payload: {
|
|
136
|
+
event: 'login',
|
|
137
|
+
method: 'email',
|
|
138
|
+
user_properties: {
|
|
139
|
+
sign_up_method: 'email',
|
|
140
|
+
allow_ad_personalization_signals: 'false',
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
firebase: {
|
|
145
|
+
eventName: 'login',
|
|
146
|
+
parameters: {
|
|
147
|
+
method: 'email',
|
|
148
|
+
},
|
|
149
|
+
userProperties: {
|
|
150
|
+
sign_up_method: 'email',
|
|
151
|
+
allow_personalized_ads: 'false',
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
121
156
|
{
|
|
122
157
|
id: 'web-fallback-json-serialization',
|
|
123
158
|
class: 'fallback_json_serialization',
|
package/validateSchemas.js
CHANGED
|
@@ -43,7 +43,7 @@ const validateSingleSchema = async (filePath, schemaPath) => {
|
|
|
43
43
|
fileHasAnyExample = true;
|
|
44
44
|
const { example, title } = option;
|
|
45
45
|
|
|
46
|
-
if (
|
|
46
|
+
if (typeof example === 'undefined') {
|
|
47
47
|
errors.push(
|
|
48
48
|
`x Schema ${file} (option: ${title}) does not produce a valid example.`,
|
|
49
49
|
);
|