|
1 | | -const yaml = require('js-yaml'); |
2 | | -const get = require('lodash.get'); |
3 | | -const validationHelper = require('./helpers/validationHelper'); |
4 | | -const getInfo = require('./helpers/infoHelper'); |
5 | | -const { getPaths } = require('./helpers/pathHelper'); |
6 | | -const getComponents = require('./helpers/componentsHelpers'); |
7 | | -const commonHelper = require('./helpers/commonHelper'); |
8 | | -const { getServers } = require('./helpers/serversHelper'); |
9 | | -const getExtensions = require('./helpers/extensionsHelper'); |
10 | | -const handleReferencePath = require('./helpers/handleReferencePath'); |
11 | | -const mapJsonSchema = require('../reverse_engineering/helpers/adaptJsonSchema/mapJsonSchema'); |
12 | | -const path = require('path'); |
13 | | -const versions = require('../package.json').contributes.target.versions; |
| 1 | +const { generateModelScript } = require('./helpers/generateModelScript'); |
| 2 | +const { validate } = require('./helpers/validation/validate'); |
14 | 3 |
|
15 | 4 | module.exports = { |
16 | | - generateModelScript(data, logger, cb) { |
17 | | - try { |
18 | | - const { |
19 | | - dbVersion, |
20 | | - externalDocs: modelExternalDocs, |
21 | | - tags: modelTags, |
22 | | - security: modelSecurity, |
23 | | - servers: modelServers, |
24 | | - jsonSchemaDialect, |
25 | | - } = data.modelData[0]; |
26 | | - const apiTargetVersion = data?.options?.apiTargetVersion; |
27 | | - const specVersion = apiTargetVersion && versions.includes(apiTargetVersion) ? apiTargetVersion : dbVersion; |
28 | | - |
29 | | - const containersIdsFromCallbacks = commonHelper.getContainersIdsForCallbacks(data); |
30 | | - |
31 | | - const resolveApiExternalRefs = data.options?.additionalOptions?.find( |
32 | | - option => option.id === 'resolveApiExternalRefs', |
33 | | - )?.value; |
34 | | - |
35 | | - const info = getInfo(data.modelData[0]); |
36 | | - const servers = getServers(modelServers); |
37 | | - const externalDefinitions = JSON.parse(data.externalDefinitions || '{}').properties || {}; |
38 | | - const containers = handleRefInContainers(data.containers, externalDefinitions, resolveApiExternalRefs); |
39 | | - const { pathContainers, webhookContainers } = separatePathAndWebhooks(containers); |
40 | | - const paths = getPaths(pathContainers, containersIdsFromCallbacks, specVersion); |
41 | | - const webhooks = getPaths(webhookContainers, containersIdsFromCallbacks, specVersion); |
42 | | - const definitions = JSON.parse(data.modelDefinitions) || {}; |
43 | | - const definitionsWithHandledReferences = mapJsonSchema( |
44 | | - definitions, |
45 | | - handleRef(externalDefinitions, resolveApiExternalRefs), |
46 | | - ); |
47 | | - const components = getComponents({ |
48 | | - definitions: definitionsWithHandledReferences, |
49 | | - containers: data.containers, |
50 | | - specVersion, |
51 | | - }); |
52 | | - const security = commonHelper.mapSecurity(modelSecurity); |
53 | | - const tags = commonHelper.mapTags(modelTags); |
54 | | - const externalDocs = commonHelper.mapExternalDocs(modelExternalDocs); |
55 | | - |
56 | | - const openApiSchema = { |
57 | | - openapi: specVersion, |
58 | | - info, |
59 | | - ...(jsonSchemaDialect && { jsonSchemaDialect }), |
60 | | - servers, |
61 | | - paths, |
62 | | - ...(webhooks && Object.keys(webhooks).length ? { webhooks } : {}), |
63 | | - components, |
64 | | - security, |
65 | | - tags, |
66 | | - externalDocs, |
67 | | - }; |
68 | | - const extensions = getExtensions(data.modelData[0].scopesExtensions); |
69 | | - |
70 | | - const resultSchema = Object.assign({}, openApiSchema, extensions); |
71 | | - |
72 | | - switch (data.targetScriptOptions.format) { |
73 | | - case 'yaml': { |
74 | | - const schema = yaml.safeDump(resultSchema, { skipInvalid: true }); |
75 | | - const schemaWithComments = addCommentsSigns(schema, 'yaml'); |
76 | | - cb(null, schemaWithComments); |
77 | | - break; |
78 | | - } |
79 | | - case 'json': |
80 | | - default: { |
81 | | - const schemaString = JSON.stringify(resultSchema, null, 2); |
82 | | - let schema = addCommentsSigns(schemaString, 'json'); |
83 | | - if (!get(data, 'options.isCalledFromFETab')) { |
84 | | - schema = removeCommentLines(schema); |
85 | | - } |
86 | | - cb(null, schema); |
87 | | - } |
88 | | - } |
89 | | - } catch (err) { |
90 | | - logger.log('error', { error: err }, 'OpenAPI FE Error'); |
91 | | - cb(err); |
92 | | - } |
93 | | - }, |
94 | | - |
95 | | - validate(data, logger, cb) { |
96 | | - const { script, targetScriptOptions } = data; |
97 | | - try { |
98 | | - const filteredScript = removeCommentLines(script); |
99 | | - let parsedScript = {}; |
100 | | - |
101 | | - switch (targetScriptOptions.format) { |
102 | | - case 'yaml': |
103 | | - parsedScript = yaml.safeLoad(filteredScript); |
104 | | - break; |
105 | | - case 'json': |
106 | | - default: |
107 | | - parsedScript = JSON.parse(filteredScript); |
108 | | - } |
109 | | - |
110 | | - validationHelper |
111 | | - .validate(replaceRelativePathByAbsolute(parsedScript, targetScriptOptions.modelDirectory)) |
112 | | - .then(messages => { |
113 | | - cb(null, messages); |
114 | | - }) |
115 | | - .catch(err => { |
116 | | - cb(err.message); |
117 | | - }); |
118 | | - } catch (e) { |
119 | | - logger.log('error', { error: e }, 'OpenAPI Validation Error'); |
120 | | - |
121 | | - cb(e.message); |
122 | | - } |
123 | | - }, |
124 | | -}; |
125 | | - |
126 | | -const addCommentsSigns = (string, format) => { |
127 | | - const commentsStart = /hackoladeCommentStart\d+/i; |
128 | | - const commentsEnd = /hackoladeCommentEnd\d+/i; |
129 | | - const innerCommentStart = /hackoladeInnerCommentStart/i; |
130 | | - const innerCommentEnd = /hackoladeInnerCommentEnd/i; |
131 | | - const innerCommentStartYamlArrayItem = /- hackoladeInnerCommentStart/i; |
132 | | - |
133 | | - const { result } = string.split('\n').reduce( |
134 | | - ({ isCommented, result }, line, index, array) => { |
135 | | - if (commentsStart.test(line) || innerCommentStart.test(line)) { |
136 | | - if (innerCommentStartYamlArrayItem.test(line)) { |
137 | | - const lineBeginsAt = array[index + 1].search(/\S/); |
138 | | - array[index + 1] = |
139 | | - array[index + 1].slice(0, lineBeginsAt) + '- ' + array[index + 1].slice(lineBeginsAt); |
140 | | - } |
141 | | - return { isCommented: true, result: result }; |
142 | | - } |
143 | | - if (commentsEnd.test(line)) { |
144 | | - return { isCommented: false, result }; |
145 | | - } |
146 | | - if (innerCommentEnd.test(line)) { |
147 | | - if (format === 'json') { |
148 | | - array[index + 1] = '# ' + array[index + 1]; |
149 | | - } |
150 | | - return { isCommented: false, result }; |
151 | | - } |
152 | | - |
153 | | - const isNextLineInnerCommentStart = index + 1 < array.length && innerCommentStart.test(array[index + 1]); |
154 | | - if ( |
155 | | - (isCommented || isNextLineInnerCommentStart) && |
156 | | - !innerCommentStartYamlArrayItem.test(array[index + 1]) |
157 | | - ) { |
158 | | - result = result + '# ' + line + '\n'; |
159 | | - } else { |
160 | | - result = result + line + '\n'; |
161 | | - } |
162 | | - |
163 | | - return { isCommented, result }; |
164 | | - }, |
165 | | - { isCommented: false, result: '' }, |
166 | | - ); |
167 | | - |
168 | | - return result; |
169 | | -}; |
170 | | - |
171 | | -const removeCommentLines = scriptString => { |
172 | | - const isCommentedLine = /^\s*#\s+/i; |
173 | | - |
174 | | - return scriptString |
175 | | - .split('\n') |
176 | | - .filter(line => !isCommentedLine.test(line)) |
177 | | - .join('\n') |
178 | | - .replace(/(.*?),\s*(\}|])/g, '$1$2'); |
179 | | -}; |
180 | | - |
181 | | -const replaceRelativePathByAbsolute = (script, modelDirectory) => { |
182 | | - if (!modelDirectory || typeof modelDirectory !== 'string') { |
183 | | - return script; |
184 | | - } |
185 | | - const stringifiedScript = JSON.stringify(script); |
186 | | - const fixedScript = stringifiedScript.replace(/("\$ref":\s*)"(.*?(?<!\\))"/g, (match, refGroup, relativePath) => { |
187 | | - const isAbsolutePath = relativePath.startsWith('file:'); |
188 | | - const isInternetLink = relativePath.startsWith('http:') || relativePath.startsWith('https:'); |
189 | | - const isModelRef = relativePath.startsWith('#'); |
190 | | - if (isAbsolutePath || isInternetLink || isModelRef) { |
191 | | - return match; |
192 | | - } |
193 | | - const absolutePath = path.join(path.dirname(modelDirectory), relativePath).replace(/\\/g, '/'); |
194 | | - return `${refGroup}"file://${absolutePath}"`; |
195 | | - }); |
196 | | - return JSON.parse(fixedScript); |
197 | | -}; |
198 | | - |
199 | | -const handleRefInContainers = (containers, externalDefinitions, resolveApiExternalRefs) => { |
200 | | - return containers.map(container => { |
201 | | - try { |
202 | | - const updatedSchemas = Object.keys(container.jsonSchema).reduce((schemas, id) => { |
203 | | - const json = container.jsonSchema[id]; |
204 | | - try { |
205 | | - const updatedSchema = mapJsonSchema( |
206 | | - JSON.parse(json), |
207 | | - handleRef(externalDefinitions, resolveApiExternalRefs), |
208 | | - ); |
209 | | - |
210 | | - return { |
211 | | - ...schemas, |
212 | | - [id]: JSON.stringify(updatedSchema), |
213 | | - }; |
214 | | - } catch (err) { |
215 | | - return { ...schemas, [id]: json }; |
216 | | - } |
217 | | - }, {}); |
218 | | - |
219 | | - return { |
220 | | - ...container, |
221 | | - jsonSchema: updatedSchemas, |
222 | | - }; |
223 | | - } catch (err) { |
224 | | - return container; |
225 | | - } |
226 | | - }); |
227 | | -}; |
228 | | - |
229 | | -const handleRef = (externalDefinitions, resolveApiExternalRefs) => field => { |
230 | | - if (!field.$ref) { |
231 | | - return field; |
232 | | - } |
233 | | - const ref = handleReferencePath(externalDefinitions, field, resolveApiExternalRefs); |
234 | | - if (!ref.$ref) { |
235 | | - return ref; |
236 | | - } |
237 | | - |
238 | | - return { ...field, ...ref }; |
239 | | -}; |
240 | | - |
241 | | -const separatePathAndWebhooks = containers => { |
242 | | - const pathContainers = []; |
243 | | - const webhookContainers = []; |
244 | | - containers.forEach(container => { |
245 | | - if (container.containerData?.[0]?.webhook) { |
246 | | - webhookContainers.push(container); |
247 | | - } else { |
248 | | - pathContainers.push(container); |
249 | | - } |
250 | | - }); |
251 | | - |
252 | | - return { pathContainers, webhookContainers }; |
| 5 | + generateModelScript, |
| 6 | + validate, |
253 | 7 | }; |
0 commit comments