forked from ToolJet/ToolJet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdataSourceSchemaManager.js
More file actions
121 lines (103 loc) · 3.33 KB
/
dataSourceSchemaManager.js
File metadata and controls
121 lines (103 loc) · 3.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { datasourceService } from '@/_services';
import Ajv2020 from 'ajv';
const ajvOptions = {
strict: false,
allErrors: true,
removeAdditional: false,
// We disable meta-schema validation to avoid the schema being validated
// against the official 2020-12 standard in ways that conflict with custom keywords.
validateSchema: false,
coerceTypes: true,
errorDataPath: 'property',
};
export default class DataSourceSchemaManager {
constructor(schema) {
this.schema = schema;
this.ajv = new Ajv2020(ajvOptions);
this.validate = this.ajv.compile(this.schema);
}
async validateData(options) {
const decryptedOptions = await datasourceService.getDecryptedOptions(options);
const data = this._convertDataSourceOptionsToData(decryptedOptions);
try {
const valid = this.validate(data);
if (!valid) {
return { valid: false, errors: this.validate.errors };
}
return { valid: true, errors: [] };
} catch (error) {
console.log('Validtion error: ', error);
}
}
getDefaults(options = {}) {
const dataWithDefaults = { ...this._convertDataSourceOptionsToData(options) };
// AJV does not support defaults with conditional schemas
// https://ajv.js.org/guide/modifying-data.html#assigning-defaults
// Create a schema without conditional properties for default value assignment
const schemaWithoutConditionals = {
type: this.schema.type,
properties: { ...this.schema.properties },
};
// Compile the schema without conditionals to fill in default values
const ajvForDefaults = new Ajv2020({
...ajvOptions,
useDefaults: true,
});
const applyDefaults = ajvForDefaults.compile(schemaWithoutConditionals);
applyDefaults(dataWithDefaults);
const encryptedProperties = this.getEncryptedProperties();
// Combine the data with defaults and set encrypted fields to null
const combinedData = {
...dataWithDefaults,
...Object.fromEntries(encryptedProperties.map((key) => [key, null])),
};
return Object.entries(combinedData).reduce((result, [key, value]) => {
result[key] = {
value: value,
encrypted: encryptedProperties.includes(key),
};
return result;
}, {});
}
getEncryptedProperties() {
return this.schema['tj:encrypted'] || [];
}
getSourceMetadata() {
const { name, kind, type } = this.schema['tj:source'];
if (!name || !kind || !type) {
throw new Error('Schema is missing required source metadata');
}
return {
name,
kind,
type,
options: this._getOptionsMetadata(),
// Can remove exposed variables?
exposedVariables: {
isLoading: false,
data: {},
rawData: {},
},
};
}
_convertDataSourceOptionsToData(options) {
return Object.entries(options).reduce((result, [key, { value }]) => {
// Skip empty string values
if (value !== '' && value !== null && value !== undefined) {
result[key] = value;
}
return result;
}, {});
}
_getOptionsMetadata() {
const options = {};
const properties = this.schema.properties || {};
for (const [key, value] of Object.entries(properties)) {
options[key] = { type: value.type };
if (value.encrypted) {
options[key].encrypted = true;
}
}
return options;
}
}