Skip to content

Commit 3d93124

Browse files
authored
HCK-9614: adapt FE to work in the browser (#81)
* HCK-9614: adapt FE to work in the browser * HCK-9614: Small fixes * HCK-9614: Small fixes * HCK-9614: remove redundant imports
1 parent ffeddb2 commit 3d93124

27 files changed

+556
-369
lines changed

api/fe.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const { generateModelScript } = require('../forward_engineering/helpers/generateModelScript');
2+
const { validate } = require('../forward_engineering/helpers/validation/validate');
3+
4+
module.exports = {
5+
generateModelScript,
6+
validate,
7+
};

esbuild.package.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const fs = require('fs');
22
const path = require('path');
33
const esbuild = require('esbuild');
44
const { clean } = require('esbuild-plugin-clean');
5+
const { copy } = require('esbuild-plugin-copy');
56
const { copyFolderFiles, addReleaseFlag } = require('@hackolade/hck-esbuild-plugins-pack');
67
const { EXCLUDED_EXTENSIONS, EXCLUDED_FILES, DEFAULT_RELEASE_FOLDER_PATH } = require('./buildConstants');
78

@@ -11,21 +12,29 @@ const RELEASE_FOLDER_PATH = path.join(DEFAULT_RELEASE_FOLDER_PATH, `${packageDat
1112
esbuild
1213
.build({
1314
entryPoints: [
15+
path.resolve(__dirname, 'api', 'fe.js'),
1416
path.resolve(__dirname, 'api', 're.js'),
1517
path.resolve(__dirname, 'forward_engineering', 'api.js'),
1618
path.resolve(__dirname, 'reverse_engineering', 'api.js'),
1719
],
1820
bundle: true,
1921
keepNames: true,
2022
platform: 'node',
21-
target: 'node18',
23+
target: 'node16',
2224
outdir: RELEASE_FOLDER_PATH,
2325
minify: true,
2426
logLevel: 'info',
27+
external: ['lodash'],
2528
plugins: [
2629
clean({
2730
patterns: [DEFAULT_RELEASE_FOLDER_PATH],
2831
}),
32+
copy({
33+
assets: {
34+
from: [path.join('node_modules', 'lodash', '**', '*')],
35+
to: [path.join('node_modules', 'lodash')],
36+
},
37+
}),
2938
copyFolderFiles({
3039
fromPath: __dirname,
3140
targetFolderPath: RELEASE_FOLDER_PATH,

forward_engineering/api.js

Lines changed: 4 additions & 250 deletions
Original file line numberDiff line numberDiff line change
@@ -1,253 +1,7 @@
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');
143

154
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,
2537
};

forward_engineering/helpers/commentsHelper.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,65 @@ function commentDeactivatedItemOuter(item, isActivated, isParentActivated) {
3333
return commentDeactivatedItem(item, isActivated, isParentActivated, commentFlags.outer);
3434
}
3535

36-
module.exports = { commentFlags, commentDeactivatedItemInner, commentDeactivatedItemOuter };
36+
const addCommentsSigns = (string, format) => {
37+
const commentsStart = /hackoladeCommentStart\d+/i;
38+
const commentsEnd = /hackoladeCommentEnd\d+/i;
39+
const innerCommentStart = /hackoladeInnerCommentStart/i;
40+
const innerCommentEnd = /hackoladeInnerCommentEnd/i;
41+
const innerCommentStartYamlArrayItem = /- hackoladeInnerCommentStart/i;
42+
43+
const { result } = string.split('\n').reduce(
44+
({ isCommented, result }, line, index, array) => {
45+
if (commentsStart.test(line) || innerCommentStart.test(line)) {
46+
if (innerCommentStartYamlArrayItem.test(line)) {
47+
const lineBeginsAt = array[index + 1].search(/\S/);
48+
array[index + 1] =
49+
array[index + 1].slice(0, lineBeginsAt) + '- ' + array[index + 1].slice(lineBeginsAt);
50+
}
51+
return { isCommented: true, result: result };
52+
}
53+
if (commentsEnd.test(line)) {
54+
return { isCommented: false, result };
55+
}
56+
if (innerCommentEnd.test(line)) {
57+
if (format === 'json') {
58+
array[index + 1] = '# ' + array[index + 1];
59+
}
60+
return { isCommented: false, result };
61+
}
62+
63+
const isNextLineInnerCommentStart = index + 1 < array.length && innerCommentStart.test(array[index + 1]);
64+
if (
65+
(isCommented || isNextLineInnerCommentStart) &&
66+
!innerCommentStartYamlArrayItem.test(array[index + 1])
67+
) {
68+
result = result + '# ' + line + '\n';
69+
} else {
70+
result = result + line + '\n';
71+
}
72+
73+
return { isCommented, result };
74+
},
75+
{ isCommented: false, result: '' },
76+
);
77+
78+
return result;
79+
};
80+
81+
const removeCommentLines = scriptString => {
82+
const isCommentedLine = /^\s*#\s+/i;
83+
84+
return scriptString
85+
.split('\n')
86+
.filter(line => !isCommentedLine.test(line))
87+
.join('\n')
88+
.replace(/(.*?),\s*(\}|])/g, '$1$2');
89+
};
90+
91+
module.exports = {
92+
commentFlags,
93+
commentDeactivatedItemInner,
94+
commentDeactivatedItemOuter,
95+
addCommentsSigns,
96+
removeCommentLines,
97+
};

0 commit comments

Comments
 (0)