@adobe/aio-cli-plugin-api-mesh 5.2.4-alpha.0 → 5.3.0-beta.1
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/oclif.manifest.json +1 -1
- package/package.json +13 -6
- package/src/commands/api-mesh/__tests__/cache-purge.test.js +1 -2
- package/src/commands/api-mesh/__tests__/create.test.js +19 -22
- package/src/commands/api-mesh/__tests__/delete-log-forwarding.test.js +106 -0
- package/src/commands/api-mesh/__tests__/delete.test.js +1 -2
- package/src/commands/api-mesh/__tests__/describe.test.js +1 -3
- package/src/commands/api-mesh/__tests__/get.test.js +2 -2
- package/src/commands/api-mesh/__tests__/log-get-bulk.test.js +19 -213
- package/src/commands/api-mesh/__tests__/run.test.js +191 -65
- package/src/commands/api-mesh/__tests__/update.test.js +8 -7
- package/src/commands/api-mesh/cache/purge.js +1 -3
- package/src/commands/api-mesh/config/delete/log-forwarding.js +80 -0
- package/src/commands/api-mesh/create.js +22 -6
- package/src/commands/api-mesh/delete.js +1 -3
- package/src/commands/api-mesh/describe.js +1 -3
- package/src/commands/api-mesh/get.js +1 -3
- package/src/commands/api-mesh/log-get-bulk.js +5 -26
- package/src/commands/api-mesh/log-get.js +1 -3
- package/src/commands/api-mesh/log-list.js +1 -3
- package/src/commands/api-mesh/run.js +207 -168
- package/src/commands/api-mesh/source/discover.js +2 -9
- package/src/commands/api-mesh/source/get.js +1 -8
- package/src/commands/api-mesh/source/install.js +1 -2
- package/src/commands/api-mesh/status.js +1 -2
- package/src/commands/api-mesh/update.js +21 -6
- package/src/commands/{PLUGINNAME/__tests__/index.test.js → api-mesh.js} +13 -15
- package/src/helpers.js +73 -15
- package/src/hooks/initMetadata.js +8 -0
- package/src/lib/smsClient.js +115 -1
- package/src/meshArtifact.js +231 -0
- package/src/project.js +56 -0
- package/src/server.js +74 -32
- package/src/utils.js +26 -24
- package/src/{index.js → worker.js} +9 -7
- package/src/wranglerCli.js +54 -0
- package/wrangler.toml +13 -0
- package/src/commands/PLUGINNAME/index.js +0 -32
- package/src/wranglerServer.js +0 -80
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { fixPlugins } = require('./fixPlugins');
|
|
4
|
+
const logger = require('../src/classes/logger');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Whether file is TypeScript
|
|
8
|
+
* @param filePath Filepath
|
|
9
|
+
*/
|
|
10
|
+
function isTypeScriptFile(filePath) {
|
|
11
|
+
const ext = path.extname(filePath);
|
|
12
|
+
return ext === '.ts' || ext === '.tsx';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Gets
|
|
17
|
+
* @param builtMeshTenantDir
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
function getBuiltMeshEntrypoint(builtMeshTenantDir) {
|
|
21
|
+
let builtMeshIndexPath = path.join(builtMeshTenantDir, 'index');
|
|
22
|
+
return fs.existsSync(`${builtMeshIndexPath}.ts`)
|
|
23
|
+
? `${builtMeshIndexPath}.ts`
|
|
24
|
+
: `${builtMeshIndexPath}.js`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Converts composer string to static imports compatible with bundling
|
|
29
|
+
* @param data {string} Data read from the built mesh
|
|
30
|
+
* @returns {string} Updated data
|
|
31
|
+
* @example Converts composer string
|
|
32
|
+
* ```
|
|
33
|
+
* "beforeAll": {
|
|
34
|
+
* "composer": "/Users/user/project/hooks.js#isAuth",
|
|
35
|
+
* "blocking": true
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
* To:
|
|
39
|
+
* ```
|
|
40
|
+
* "beforeAll": {
|
|
41
|
+
* "module": await import("/Users/user/project/hooks.js"), "fn": "isAuth",
|
|
42
|
+
* "blocking": true
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function resolveComposerAsTypeScriptModule(data) {
|
|
47
|
+
return data.replace(
|
|
48
|
+
/"composer":\s*"([^#]+)#([^"]+)"/,
|
|
49
|
+
`"module": await import("$1"), "fn": "$2"`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Converts composer string to static imports compatible with bundling
|
|
55
|
+
* @param data {string} Data read from the built mesh
|
|
56
|
+
* @returns {string} Updated data
|
|
57
|
+
* @example Converts composer string
|
|
58
|
+
* ```
|
|
59
|
+
* "beforeAll": {
|
|
60
|
+
* "composer": "/Users/user/project/hooks.js#isAuth",
|
|
61
|
+
* "blocking": true
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
* To:
|
|
65
|
+
* ```
|
|
66
|
+
* "beforeAll": {
|
|
67
|
+
* "module": __importStar(require("/Users/user/project/hooks.js")), "fn": "isAuth",
|
|
68
|
+
* "blocking": true
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
function resolveComposerAsJavaScriptModule(data) {
|
|
73
|
+
return data.replace(
|
|
74
|
+
/"composer":\s*"([^#]+)#([^"]+)"/,
|
|
75
|
+
`"module": __importStar(require("$1")), "fn": "$2"`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Takes a mesh artifact and converts composer configuration to static imports
|
|
81
|
+
* compatible with bundling
|
|
82
|
+
* @param meshArtifactPath Path to the mesh artifact used to determine extension
|
|
83
|
+
* @param data {string} Data read from the built mesh
|
|
84
|
+
* @returns {string} Updated data
|
|
85
|
+
* @see {@link resolveComposerAsJavaScriptModule}
|
|
86
|
+
* @see {@link resolveComposerAsTypeScriptModule}
|
|
87
|
+
*/
|
|
88
|
+
function resolveComposerAsStaticImport(meshArtifactPath, data) {
|
|
89
|
+
return isTypeScriptFile(meshArtifactPath)
|
|
90
|
+
? resolveComposerAsTypeScriptModule(data)
|
|
91
|
+
: resolveComposerAsJavaScriptModule(data);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// /**
|
|
95
|
+
// * Converts handler string to static imports compatible with bundling
|
|
96
|
+
// * @param data {string} Data read from the built mesh
|
|
97
|
+
// * @returns {string} Updated data
|
|
98
|
+
// * @example Converts composer string
|
|
99
|
+
// * ```
|
|
100
|
+
// * "onFetch": {
|
|
101
|
+
// * "handler": "/Users/user/project/fetch.js",
|
|
102
|
+
// * "blocking": true
|
|
103
|
+
// * }
|
|
104
|
+
// * ```
|
|
105
|
+
// * To:
|
|
106
|
+
// * ```
|
|
107
|
+
// * "onFetch": {
|
|
108
|
+
// * "handler": await import("/Users/user/project/fetch.js"),
|
|
109
|
+
// * "blocking": true
|
|
110
|
+
// * }
|
|
111
|
+
// * ```
|
|
112
|
+
// */
|
|
113
|
+
// function resolveHandlerAsTypeScriptModule(data) {
|
|
114
|
+
// return data.replace(/"handler":\s*"([^"]+)"/, `"handler": await import("$1")`);
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
// /**
|
|
118
|
+
// * Converts handler string to static imports compatible with bundling
|
|
119
|
+
// * @param data {string} Data read from the built mesh
|
|
120
|
+
// * @returns {string} Updated data
|
|
121
|
+
// * @example Converts composer string
|
|
122
|
+
// * ```
|
|
123
|
+
// * "onFetch": {
|
|
124
|
+
// * "handler": "/Users/user/project/fetch.js",
|
|
125
|
+
// * "blocking": true
|
|
126
|
+
// * }
|
|
127
|
+
// * ```
|
|
128
|
+
// * To:
|
|
129
|
+
// * ```
|
|
130
|
+
// * "onFetch": {
|
|
131
|
+
// * "module": __importStar(require("/Users/user/project/fetch.js")),
|
|
132
|
+
// * "blocking": true
|
|
133
|
+
// * }
|
|
134
|
+
// * ```
|
|
135
|
+
// */
|
|
136
|
+
// function resolveHandlerAsJavaScriptModule(data) {
|
|
137
|
+
// return data.replace(/"handler":\s*"([^"]+)"/, `"handler": __importStar(require("$1"))`);
|
|
138
|
+
// }
|
|
139
|
+
|
|
140
|
+
// /**
|
|
141
|
+
// * Takes a mesh artifact and converts handler configuration to static imports
|
|
142
|
+
// * compatible with bundling
|
|
143
|
+
// * @param meshArtifactPath Path to the mesh artifact used to determine extension
|
|
144
|
+
// * @param data {string} Data read from the built mesh
|
|
145
|
+
// * @returns {string} Updated data
|
|
146
|
+
// * @see {@link resolveHandlerAsJavaScriptModule}
|
|
147
|
+
// * @see {@link resolveHandlerAsTypeScriptModule}
|
|
148
|
+
// */
|
|
149
|
+
// TODO: onFetch support - requires plugin changes
|
|
150
|
+
// function resolveHandlerAsStaticImport(meshArtifactPath, data) {
|
|
151
|
+
// return isTypeScriptFile(meshArtifactPath)
|
|
152
|
+
// ? resolveHandlerAsTypeScriptModule(data)
|
|
153
|
+
// : resolveHandlerAsJavaScriptModule(data);
|
|
154
|
+
// }
|
|
155
|
+
|
|
156
|
+
const resolveRelativeSources = async builtMeshTenantDir => {
|
|
157
|
+
let builtMeshPath = getBuiltMeshEntrypoint(builtMeshTenantDir);
|
|
158
|
+
|
|
159
|
+
// Fix http details extensions plugin for edge compatibility
|
|
160
|
+
await fixPlugins(builtMeshPath);
|
|
161
|
+
|
|
162
|
+
// Read tenant files inventory
|
|
163
|
+
const artifactFilesPath = path.join(builtMeshTenantDir, 'files.json');
|
|
164
|
+
if (fs.existsSync(artifactFilesPath)) {
|
|
165
|
+
// Read mesh artifact
|
|
166
|
+
let builtMeshData = fs.readFileSync(builtMeshPath).toString();
|
|
167
|
+
|
|
168
|
+
const parentDirRegex = new RegExp('../tenantFiles'.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
169
|
+
builtMeshData = builtMeshData.replace(parentDirRegex, './tenantFiles');
|
|
170
|
+
|
|
171
|
+
// Write the modified mesh artifact
|
|
172
|
+
fs.writeFileSync(builtMeshPath, builtMeshData, 'utf8');
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Resolve original sources to materialized files in mesh artifact for local development and debugging
|
|
178
|
+
* @param builtMeshTenantDir Built mesh tenant directory
|
|
179
|
+
* @param localFileOverrides Local file overrides
|
|
180
|
+
*/
|
|
181
|
+
const resolveOriginalSources = async (builtMeshTenantDir, localFileOverrides) => {
|
|
182
|
+
let builtMeshPath = getBuiltMeshEntrypoint(builtMeshTenantDir);
|
|
183
|
+
|
|
184
|
+
// Read tenant files inventory
|
|
185
|
+
const artifactFilesPath = path.join(builtMeshTenantDir, 'files.json');
|
|
186
|
+
if (fs.existsSync(artifactFilesPath)) {
|
|
187
|
+
let files = {};
|
|
188
|
+
try {
|
|
189
|
+
files = JSON.parse(fs.readFileSync(artifactFilesPath).toString());
|
|
190
|
+
} catch (err) {
|
|
191
|
+
logger.error('Malformed "files.json" file. Skipping original source resolution.');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Read mesh artifact
|
|
195
|
+
let builtMeshData = fs.readFileSync(builtMeshPath).toString();
|
|
196
|
+
files.files.forEach(file => {
|
|
197
|
+
// Skip replacement of files for local development when the user was prompted
|
|
198
|
+
// to override and answered no
|
|
199
|
+
if (
|
|
200
|
+
Object.keys(localFileOverrides).includes(file.path) &&
|
|
201
|
+
localFileOverrides[file.path] === false
|
|
202
|
+
) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// When the source exists in project use it instead of the materialized file
|
|
207
|
+
const absoluteFilePath = path.resolve(file.path);
|
|
208
|
+
|
|
209
|
+
// Replace all occurrences of the materialized path with the fully qualified original path when it exists
|
|
210
|
+
if (fs.existsSync(absoluteFilePath)) {
|
|
211
|
+
const regex = new RegExp(file.materializedPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
212
|
+
builtMeshData = builtMeshData.replace(regex, absoluteFilePath);
|
|
213
|
+
builtMeshData = resolveComposerAsStaticImport(builtMeshPath, builtMeshData);
|
|
214
|
+
// TODO: onFetch support - requires plugin changes
|
|
215
|
+
// builtMeshData = resolveHandlerAsStaticImport(builtMeshPath, builtMeshData);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Write the modified mesh artifact
|
|
220
|
+
fs.writeFileSync(builtMeshPath, builtMeshData, 'utf8');
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
isTypeScriptFile,
|
|
226
|
+
resolveComposerAsTypeScriptModule,
|
|
227
|
+
resolveComposerAsJavaScriptModule,
|
|
228
|
+
resolveComposerAsStaticImport,
|
|
229
|
+
resolveRelativeSources,
|
|
230
|
+
resolveOriginalSources,
|
|
231
|
+
};
|
package/src/project.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const { cpSync, existsSync, renameSync, rmSync } = require('node:fs');
|
|
2
|
+
const { join } = require('node:path');
|
|
3
|
+
const { resolveOriginalSources } = require('./meshArtifact');
|
|
4
|
+
|
|
5
|
+
const BUILT_MESH_ARTIFACT_DIRECTORY = 'mesh-artifact';
|
|
6
|
+
const BUILT_MESH_TENANT_FILES_DIRECTORY = join(BUILT_MESH_ARTIFACT_DIRECTORY, 'tenantFiles');
|
|
7
|
+
const PACKAGED_MESH_DIR = '.mesh';
|
|
8
|
+
const PACKAGED_MESH_DIR_TENANT_FILES_DIRECTORY = join(PACKAGED_MESH_DIR, 'tenantFiles');
|
|
9
|
+
const PACKAGED_MESH_CLI_MIRROR_DIR = `${__dirname}/../${PACKAGED_MESH_DIR}`;
|
|
10
|
+
const TEMP_FILES_DIRECTORY = 'tempfiles';
|
|
11
|
+
const getBuiltMeshTenantDirectory = meshId => join(BUILT_MESH_ARTIFACT_DIRECTORY, meshId);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Copy built mesh artifact to packaged directory that runs local development.
|
|
15
|
+
* @param builtMeshTenantDir Built mesh directory path
|
|
16
|
+
* @returns {Promise<void>}
|
|
17
|
+
*/
|
|
18
|
+
const copyBuiltMeshToPackage = async builtMeshTenantDir => {
|
|
19
|
+
// Reset packaged directories
|
|
20
|
+
safeDelete(PACKAGED_MESH_DIR);
|
|
21
|
+
safeDelete(PACKAGED_MESH_CLI_MIRROR_DIR);
|
|
22
|
+
|
|
23
|
+
// Copy the built mesh to the packaged directory for in-project SDK
|
|
24
|
+
safeRename(builtMeshTenantDir, PACKAGED_MESH_DIR);
|
|
25
|
+
safeRename(BUILT_MESH_TENANT_FILES_DIRECTORY, PACKAGED_MESH_DIR_TENANT_FILES_DIRECTORY);
|
|
26
|
+
|
|
27
|
+
// Copy the packaged mesh to the CLI mirror directory for local server
|
|
28
|
+
safeCopy(PACKAGED_MESH_DIR, PACKAGED_MESH_CLI_MIRROR_DIR);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const safeDelete = path => {
|
|
32
|
+
if (existsSync(path)) {
|
|
33
|
+
rmSync(path, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const safeRename = (oldPath, newPath) => {
|
|
38
|
+
if (existsSync(oldPath)) {
|
|
39
|
+
renameSync(oldPath, newPath);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const safeCopy = (source, destination) => {
|
|
44
|
+
if (existsSync(source)) {
|
|
45
|
+
cpSync(source, destination, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
BUILT_MESH_ARTIFACT_DIRECTORY,
|
|
51
|
+
safeDelete,
|
|
52
|
+
copyBuiltMeshToPackage,
|
|
53
|
+
resolveOriginalSources,
|
|
54
|
+
TEMP_FILES_DIRECTORY,
|
|
55
|
+
getBuiltMeshTenantDirectory,
|
|
56
|
+
};
|
package/src/server.js
CHANGED
|
@@ -1,38 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
1
|
+
import { getMesh } from '@graphql-mesh/runtime';
|
|
2
|
+
|
|
3
|
+
const { getCorsOptions } = require('./cors');
|
|
4
|
+
const { createYoga } = require('graphql-yoga');
|
|
5
|
+
const { GraphQLError } = require('graphql/error');
|
|
6
|
+
|
|
7
|
+
const { loadMeshSecrets, getSecretsHandler } = require('./secrets');
|
|
8
|
+
const useComplianceHeaders = require('./plugins/complianceHeaders');
|
|
9
|
+
const UseHttpDetailsExtensions = require('./plugins/httpDetailsExtensions');
|
|
10
|
+
const useSourceHeaders = require('@adobe/plugin-source-headers');
|
|
11
|
+
const { useDisableIntrospection } = require('@envelop/disable-introspection');
|
|
12
|
+
|
|
13
|
+
let meshInstance$;
|
|
14
|
+
|
|
15
|
+
async function buildMeshInstance(meshArtifacts, meshConfig) {
|
|
16
|
+
const { getMeshOptions } = meshArtifacts;
|
|
17
|
+
const options = await getMeshOptions();
|
|
18
|
+
|
|
19
|
+
options.additionalEnvelopPlugins = (options.additionalEnvelopPlugins || []).concat(
|
|
20
|
+
useComplianceHeaders(),
|
|
21
|
+
UseHttpDetailsExtensions({
|
|
22
|
+
// Get the details of responseConfig.includeHTTPDetails and store in Cache
|
|
23
|
+
if: meshConfig.responseConfig?.includeHTTPDetails || false,
|
|
24
|
+
}),
|
|
25
|
+
useSourceHeaders(meshConfig),
|
|
26
|
+
);
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
if (meshConfig.disableIntrospection) {
|
|
29
|
+
options.additionalEnvelopPlugins.push(useDisableIntrospection());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return getMesh(options).then(mesh => {
|
|
33
|
+
const id = mesh.pubsub.subscribe('destroy', () => {
|
|
34
|
+
meshInstance$ = undefined;
|
|
35
|
+
mesh.pubsub.unsubscribe(id);
|
|
36
|
+
});
|
|
37
|
+
return mesh;
|
|
31
38
|
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function getBuiltMesh(meshArtifacts, meshConfig) {
|
|
42
|
+
if (meshInstance$ == null) {
|
|
43
|
+
meshInstance$ = buildMeshInstance(meshArtifacts, meshConfig);
|
|
44
|
+
}
|
|
45
|
+
return meshInstance$;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const buildServer = async (loggerInstance, env, meshArtifacts, meshConfig) => {
|
|
49
|
+
const { Secret: secret } = env;
|
|
50
|
+
const tenantMesh = await getBuiltMesh(meshArtifacts, meshConfig);
|
|
51
|
+
const meshSecrets = loadMeshSecrets(loggerInstance, secret);
|
|
52
|
+
return await buildYogaServer(env, tenantMesh, meshConfig, meshSecrets);
|
|
53
|
+
};
|
|
32
54
|
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
async function buildYogaServer(env, tenantMesh, meshConfig, meshSecrets) {
|
|
56
|
+
const secretsProxy = new Proxy(meshSecrets, getSecretsHandler);
|
|
57
|
+
return createYoga({
|
|
58
|
+
plugins: tenantMesh.plugins,
|
|
59
|
+
graphqlEndpoint: `/graphql`,
|
|
60
|
+
cors: getCorsOptions(env, meshConfig),
|
|
61
|
+
context: initialContext => ({
|
|
62
|
+
...initialContext,
|
|
63
|
+
secrets: secretsProxy,
|
|
64
|
+
}),
|
|
65
|
+
maskedErrors: {
|
|
66
|
+
maskError: maskError,
|
|
67
|
+
},
|
|
68
|
+
logging: 'debug',
|
|
35
69
|
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const maskError = error => {
|
|
73
|
+
if (error instanceof GraphQLError && error.extensions?.http?.headers) {
|
|
74
|
+
delete error.extensions.http.headers;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return error;
|
|
36
78
|
};
|
|
37
79
|
|
|
38
|
-
module.exports = {
|
|
80
|
+
module.exports = { buildServer };
|
package/src/utils.js
CHANGED
|
@@ -6,7 +6,6 @@ const { readFile } = require('fs/promises');
|
|
|
6
6
|
const { interpolateMesh } = require('./helpers');
|
|
7
7
|
const dotenv = require('dotenv');
|
|
8
8
|
const YAML = require('yaml');
|
|
9
|
-
const ms = require('ms');
|
|
10
9
|
const parseEnv = require('envsub/js/envsub-parser');
|
|
11
10
|
const os = require('os');
|
|
12
11
|
const chalk = require('chalk');
|
|
@@ -62,6 +61,13 @@ const secretsFlag = Flags.string({
|
|
|
62
61
|
const portNoFlag = Flags.integer({
|
|
63
62
|
char: 'p',
|
|
64
63
|
description: 'Port number for the local dev server',
|
|
64
|
+
default: 5000,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const inspectPortNoFlag = Flags.integer({
|
|
68
|
+
char: 'i',
|
|
69
|
+
description: 'Port number for the local dev server inspector',
|
|
70
|
+
default: 9229,
|
|
65
71
|
});
|
|
66
72
|
|
|
67
73
|
const debugFlag = Flags.boolean({
|
|
@@ -87,11 +93,7 @@ const endTimeFlag = Flags.string({
|
|
|
87
93
|
});
|
|
88
94
|
|
|
89
95
|
const pastFlag = Flags.string({
|
|
90
|
-
description: 'Past time window in
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const fromFlag = Flags.string({
|
|
94
|
-
description: `The from time in YYYY-MM-DD:HH:MM:SS format based on your system's time zone. It is used to fetch logs from the past and is the starting time for the past time duration.`,
|
|
96
|
+
description: 'Past time window in minutes',
|
|
95
97
|
});
|
|
96
98
|
|
|
97
99
|
const logFilenameFlag = Flags.string({
|
|
@@ -320,7 +322,7 @@ function checkPlaceholders(mesh) {
|
|
|
320
322
|
* @param {string} file
|
|
321
323
|
* @param {object} command
|
|
322
324
|
* @param {string} filetype
|
|
323
|
-
* @returns {string}
|
|
325
|
+
* @returns {Promise<string>}
|
|
324
326
|
*/
|
|
325
327
|
async function readFileContents(file, command, filetype) {
|
|
326
328
|
try {
|
|
@@ -388,7 +390,7 @@ function validateFileName(filesList) {
|
|
|
388
390
|
* @param {string} inputMeshData
|
|
389
391
|
* @param {string} envFilePath
|
|
390
392
|
* @param {object} command
|
|
391
|
-
* @returns {string}
|
|
393
|
+
* @returns {Promise<string>}
|
|
392
394
|
*/
|
|
393
395
|
async function validateAndInterpolateMesh(inputMeshData, envFilePath, command) {
|
|
394
396
|
//Read the environment file
|
|
@@ -658,23 +660,21 @@ function suggestCorrectedDateFormat(inputDate) {
|
|
|
658
660
|
/**
|
|
659
661
|
* Parses a duration string representing a past time window and converts it to milliseconds.
|
|
660
662
|
*
|
|
661
|
-
* @param {string} pastTimeWindow - The past time duration
|
|
663
|
+
* @param {string} pastTimeWindow - The past time duration in minutes, e.g., "20", "15".
|
|
662
664
|
* @returns {number} The duration in milliseconds.
|
|
663
665
|
*/
|
|
664
666
|
function parsePastDuration(pastTimeWindow) {
|
|
665
|
-
//
|
|
666
|
-
const
|
|
667
|
-
|
|
667
|
+
// Check if pastTimeWindow contains non-numeric characters
|
|
668
|
+
const match = pastTimeWindow.match(/^(\d+)$/);
|
|
669
|
+
|
|
670
|
+
const durationInMs = Number(pastTimeWindow) * 60 * 1000;
|
|
668
671
|
|
|
669
|
-
if (!match) {
|
|
672
|
+
if (isNaN(durationInMs) || !match) {
|
|
670
673
|
throw new Error(
|
|
671
|
-
'Invalid format. The
|
|
674
|
+
'Invalid format. The time window must be an integer, for example "20" or "15".',
|
|
672
675
|
);
|
|
673
676
|
}
|
|
674
677
|
|
|
675
|
-
// Convert the matched duration to milliseconds
|
|
676
|
-
const durationInMs = ms(pastTimeWindow);
|
|
677
|
-
|
|
678
678
|
return durationInMs;
|
|
679
679
|
}
|
|
680
680
|
|
|
@@ -714,14 +714,15 @@ function validateDateTimeRange(startTime, endTime) {
|
|
|
714
714
|
throw new Error('endTime cannot be in the future. Provide a valid endTime.');
|
|
715
715
|
}
|
|
716
716
|
|
|
717
|
-
if (start.getTime() === end.getTime()) {
|
|
718
|
-
throw new Error('The minimum duration is 1 minutes. The current duration is 0 minutes.');
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
// Check if the duration between start and end times is greater than 30 minutes (1800 seconds)
|
|
722
717
|
const timeDifferenceInSeconds = (end.getTime() - start.getTime()) / 1000;
|
|
723
718
|
|
|
724
|
-
if (timeDifferenceInSeconds
|
|
719
|
+
if (timeDifferenceInSeconds < 60) {
|
|
720
|
+
// Check if the duration between start and end times is less than 1 minute (60 seconds)
|
|
721
|
+
throw new Error(
|
|
722
|
+
`The minimum duration is 1 minute. The current duration is ${timeDifferenceInSeconds} seconds.`,
|
|
723
|
+
);
|
|
724
|
+
} else if (timeDifferenceInSeconds > 1800) {
|
|
725
|
+
// Check if the duration between start and end times is greater than 30 minutes (1800 seconds)
|
|
725
726
|
const hours = Math.floor(timeDifferenceInSeconds / 3600); // Hours calculation
|
|
726
727
|
const minutes = Math.floor((timeDifferenceInSeconds % 3600) / 60); // Minutes calculation
|
|
727
728
|
const seconds = timeDifferenceInSeconds % 60; // Seconds calculation
|
|
@@ -762,6 +763,7 @@ function validateDateTimeFormat(time) {
|
|
|
762
763
|
return timeString.replace(/-|:|Z/g, '').replace('T', 'T');
|
|
763
764
|
}
|
|
764
765
|
|
|
766
|
+
// can be used later if we want to take --startTime and --endTime in local time
|
|
765
767
|
/**
|
|
766
768
|
* Convert a given local time string to UTC time string
|
|
767
769
|
* @param {string} timeString - The time string in the format YYYY-MM-DD:HH:MM:SS
|
|
@@ -803,6 +805,7 @@ module.exports = {
|
|
|
803
805
|
validateAndInterpolateMesh,
|
|
804
806
|
getAppRootDir,
|
|
805
807
|
portNoFlag,
|
|
808
|
+
inspectPortNoFlag,
|
|
806
809
|
debugFlag,
|
|
807
810
|
selectFlag,
|
|
808
811
|
secretsFlag,
|
|
@@ -814,7 +817,6 @@ module.exports = {
|
|
|
814
817
|
endTimeFlag,
|
|
815
818
|
logFilenameFlag,
|
|
816
819
|
pastFlag,
|
|
817
|
-
fromFlag,
|
|
818
820
|
suggestCorrectedDateFormat,
|
|
819
821
|
parsePastDuration,
|
|
820
822
|
validateDateTimeRange,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ServedTier, addServedHeader } from './served';
|
|
2
|
+
import { buildServer } from './server';
|
|
2
3
|
import { bindedlogger as logger } from './utils/logger';
|
|
3
4
|
import { getRequestId } from './utils/requestId';
|
|
4
|
-
import { buildServer } from './wranglerServer';
|
|
5
5
|
|
|
6
6
|
let server;
|
|
7
7
|
|
|
@@ -21,10 +21,10 @@ export default {
|
|
|
21
21
|
const { MESH_ID: meshId, LOG_LEVEL: logLevel } = env;
|
|
22
22
|
const loggerInstance = logger({ logLevel, meshId, requestId });
|
|
23
23
|
const meshArtifacts = await import('../.mesh');
|
|
24
|
-
const
|
|
24
|
+
const meshConfig = await import('../.mesh/.meshrc.json');
|
|
25
25
|
|
|
26
26
|
if (!server) {
|
|
27
|
-
server = await this.buildAndCacheServer(env, loggerInstance, meshArtifacts,
|
|
27
|
+
server = await this.buildAndCacheServer(env, loggerInstance, meshArtifacts, meshConfig);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
loggerInstance.debug('WORKER HOT: Fetching via worker');
|
|
@@ -34,11 +34,13 @@ export default {
|
|
|
34
34
|
},
|
|
35
35
|
/**
|
|
36
36
|
* Build and cache mesh instance/server in global variable.
|
|
37
|
-
* @param env
|
|
38
|
-
* @param
|
|
37
|
+
* @param env Environment
|
|
38
|
+
* @param logger Logger
|
|
39
|
+
* @param meshArtifacts Mesh artifact
|
|
40
|
+
* @param meshConfig Mesh config
|
|
39
41
|
*/
|
|
40
|
-
async buildAndCacheServer(env,
|
|
41
|
-
server = await buildServer(
|
|
42
|
+
async buildAndCacheServer(env, logger, meshArtifacts, meshConfig) {
|
|
43
|
+
server = await buildServer(logger, env, meshArtifacts, meshConfig);
|
|
42
44
|
return server;
|
|
43
45
|
},
|
|
44
46
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const { readSecretsFile } = require('./serverUtils');
|
|
3
|
+
const packageData = require('../package.json');
|
|
4
|
+
const { join } = require('node:path');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Starts the wrangler dev server
|
|
8
|
+
* @param command {Command} CLI command
|
|
9
|
+
* @param port Port number
|
|
10
|
+
* @param debug Whether debug mode is enabled
|
|
11
|
+
* @param inspectPort Port number for local dev server inspector
|
|
12
|
+
*/
|
|
13
|
+
const start = (command, port, debug, inspectPort) => {
|
|
14
|
+
const wranglerPackageNumber = packageData.dependencies.wrangler;
|
|
15
|
+
const wranglerVersion = `wrangler@${wranglerPackageNumber.replace(/^[\^~]/, '')}`;
|
|
16
|
+
const wranglerToml = join(__dirname, '..', 'wrangler.toml');
|
|
17
|
+
const meshDir = '.mesh';
|
|
18
|
+
const secrets = readSecretsFile(meshDir);
|
|
19
|
+
const entrypoint = join(__dirname, 'worker.js');
|
|
20
|
+
|
|
21
|
+
const commandArgs = [
|
|
22
|
+
wranglerVersion,
|
|
23
|
+
'--config',
|
|
24
|
+
wranglerToml,
|
|
25
|
+
'--cwd',
|
|
26
|
+
process.cwd(),
|
|
27
|
+
'dev',
|
|
28
|
+
entrypoint,
|
|
29
|
+
'--show-interactive-dev-session',
|
|
30
|
+
'false',
|
|
31
|
+
'--var',
|
|
32
|
+
`Secret:${JSON.stringify(secrets)}`,
|
|
33
|
+
'--port',
|
|
34
|
+
port,
|
|
35
|
+
'--inspector-port',
|
|
36
|
+
debug ? inspectPort : 0,
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const wrangler = spawn('npx', commandArgs, {
|
|
40
|
+
stdio: 'inherit',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
wrangler.on('close', code => {
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.log(`wrangler dev process exited with code ${code}`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
wrangler.on('error', error => {
|
|
49
|
+
// eslint-disable-next-line no-console
|
|
50
|
+
console.error(`Failed to start wrangler dev: ${error.message}`);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
module.exports = { start };
|
package/wrangler.toml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Tenant worker core definition. For platform workers metadata configs are used to set bindings.
|
|
2
|
+
name = "tenant-worker-core"
|
|
3
|
+
compatibility_date = "2024-06-03"
|
|
4
|
+
|
|
5
|
+
find_additional_modules = true
|
|
6
|
+
base_dir = ".mesh"
|
|
7
|
+
rules = [
|
|
8
|
+
{ type = "CommonJS", globs = ["tenantFiles/**/*.js"] }
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
[[migrations]]
|
|
13
|
+
tag = "v1"
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020 Adobe. All rights reserved.
|
|
3
|
-
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
-
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
-
|
|
7
|
-
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
-
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
-
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
-
governing permissions and limitations under the License.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const { Command, flags } = require('@oclif/command');
|
|
14
|
-
const aioLogger = require('@adobe/aio-lib-core-logging')('PLUGINNAME', { provider: 'debug' });
|
|
15
|
-
|
|
16
|
-
class IndexCommand extends Command {
|
|
17
|
-
async run() {
|
|
18
|
-
// const { args, flags } = this.parse(IndexCommand)
|
|
19
|
-
aioLogger.debug('this is the index command.');
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
IndexCommand.flags = {
|
|
24
|
-
someflag: flags.string({ char: 'f', description: 'this is some flag' }),
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// this is set in package.json, see https://github.com/oclif/oclif/issues/120
|
|
28
|
-
// if not set it will get the first (alphabetical) topic's help description
|
|
29
|
-
IndexCommand.description = 'Your description here';
|
|
30
|
-
IndexCommand.examples = ['$ aio PLUGINNAME:some_command'];
|
|
31
|
-
|
|
32
|
-
module.exports = IndexCommand;
|