@adobe/aio-cli-plugin-api-mesh 5.2.3 → 5.2.4-alpha.0

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.
Files changed (42) hide show
  1. package/oclif.manifest.json +1 -1
  2. package/package.json +5 -10
  3. package/src/commands/{api-mesh.js → PLUGINNAME/__tests__/index.test.js} +15 -13
  4. package/src/commands/PLUGINNAME/index.js +32 -0
  5. package/src/commands/api-mesh/__tests__/cache-purge.test.js +2 -2
  6. package/src/commands/api-mesh/__tests__/create.test.js +9 -21
  7. package/src/commands/api-mesh/__tests__/delete.test.js +2 -2
  8. package/src/commands/api-mesh/__tests__/describe.test.js +2 -2
  9. package/src/commands/api-mesh/__tests__/get-log-forwarding.test.js +149 -0
  10. package/src/commands/api-mesh/__tests__/get.test.js +2 -2
  11. package/src/commands/api-mesh/__tests__/log-get-bulk.test.js +2 -2
  12. package/src/commands/api-mesh/__tests__/log-get.test.js +2 -2
  13. package/src/commands/api-mesh/__tests__/log-list.test.js +2 -2
  14. package/src/commands/api-mesh/__tests__/run.test.js +67 -193
  15. package/src/commands/api-mesh/__tests__/set-log-forwarding.test.js +246 -0
  16. package/src/commands/api-mesh/__tests__/status.test.js +2 -2
  17. package/src/commands/api-mesh/__tests__/update.test.js +4 -6
  18. package/src/commands/api-mesh/cache/purge.js +1 -1
  19. package/src/commands/api-mesh/config/get/log-forwarding.js +78 -0
  20. package/src/commands/api-mesh/config/set/log-forwarding.js +156 -0
  21. package/src/commands/api-mesh/create.js +4 -3
  22. package/src/commands/api-mesh/delete.js +1 -1
  23. package/src/commands/api-mesh/describe.js +1 -1
  24. package/src/commands/api-mesh/get.js +1 -1
  25. package/src/commands/api-mesh/log-get-bulk.js +1 -1
  26. package/src/commands/api-mesh/log-get.js +1 -1
  27. package/src/commands/api-mesh/log-list.js +1 -1
  28. package/src/commands/api-mesh/run.js +162 -208
  29. package/src/commands/api-mesh/source/__tests__/install.test.js +2 -2
  30. package/src/commands/api-mesh/source/install.js +1 -1
  31. package/src/commands/api-mesh/status.js +1 -1
  32. package/src/commands/api-mesh/update.js +4 -3
  33. package/src/helpers.js +29 -20
  34. package/src/{worker.js → index.js} +7 -9
  35. package/src/lib/{devConsole.js → smsClient.js} +186 -1
  36. package/src/server.js +32 -74
  37. package/src/utils.js +46 -10
  38. package/src/wranglerServer.js +80 -0
  39. package/src/meshArtifact.js +0 -231
  40. package/src/project.js +0 -56
  41. package/src/wranglerCli.js +0 -54
  42. package/wrangler.toml +0 -13
@@ -1064,7 +1064,7 @@ const getMeshDeployments = async (organizationCode, projectId, workspaceId, mesh
1064
1064
  * As a result, we provide the publicKey used for secrets encryption.
1065
1065
  * The near-term goal is to stop using Dev Console as a proxy for all routes.
1066
1066
  * @param organizationCode
1067
- * @returns Promise<string>
1067
+ * @returns string
1068
1068
  */
1069
1069
  const getPublicEncryptionKey = async organizationCode => {
1070
1070
  const { accessToken } = await getDevConsoleConfig();
@@ -1201,6 +1201,189 @@ const getLogsByRayId = async (organizationCode, projectId, workspaceId, meshId,
1201
1201
  }
1202
1202
  };
1203
1203
 
1204
+ /**
1205
+ * @param {string} organizationCode - The IMS org code
1206
+ * @param {string} projectId - The project ID
1207
+ * @param {string} workspaceId - The workspace ID
1208
+ * @param {string} meshId - The mesh ID
1209
+ * @param {Object} logConfig - The log forwarding configuration
1210
+ */
1211
+ const setLogForwarding = async (organizationCode, projectId, workspaceId, meshId, logConfig) => {
1212
+ const { accessToken } = await getDevConsoleConfig();
1213
+ const config = {
1214
+ method: 'POST',
1215
+ url: `${SMS_BASE_URL}/organizations/${organizationCode}/projects/${projectId}/workspaces/${workspaceId}/meshes/${meshId}/log/forwarding`,
1216
+ headers: {
1217
+ 'Authorization': `Bearer ${accessToken}`,
1218
+ 'Content-Type': 'application/json',
1219
+ 'x-request-id': global.requestId,
1220
+ 'x-api-key': SMS_API_KEY,
1221
+ },
1222
+ data: JSON.stringify(logConfig),
1223
+ };
1224
+
1225
+ logger.info(
1226
+ 'Initiating POST %s',
1227
+ `${SMS_BASE_URL}/organizations/${organizationCode}/projects/${projectId}/workspaces/${workspaceId}/meshes/${meshId}/log/forwarding`,
1228
+ );
1229
+
1230
+ try {
1231
+ const response = await axios(config);
1232
+
1233
+ logger.info('Response from POST %s', response.status);
1234
+
1235
+ if (response?.status === 200) {
1236
+ logger.info(`Log forwarding configuration: ${objToString(response, ['data'])}`);
1237
+ return {
1238
+ result: response.data.result,
1239
+ message: response.data.message,
1240
+ };
1241
+ } else {
1242
+ // not 200 response
1243
+ logger.error(
1244
+ `Something went wrong: ${objToString(
1245
+ response,
1246
+ ['data'],
1247
+ 'Unable to set log forwarding details.',
1248
+ )}. Received ${response.status}, expected 200`,
1249
+ );
1250
+ throw new Error(response.data.message);
1251
+ }
1252
+ } catch (error) {
1253
+ if (error.response && error.response.status === 400) {
1254
+ // The request was made and the server responded with a 400 status code
1255
+ logger.error('Error setting log forwarding configuration: %j', error.response.data);
1256
+
1257
+ throw new Error('Invalid input parameters.');
1258
+ }
1259
+ // request made but no response received
1260
+ else if (error.request && !error.response) {
1261
+ logger.error('No response received from server when setting log forwarding configuration');
1262
+ throw new Error('Unable to set log forwarding details. Check the details and try again.');
1263
+ }
1264
+ // response received with error
1265
+ else if (error.response && error.response.data) {
1266
+ logger.error(
1267
+ 'Error setting log forwarding configuration: %s',
1268
+ objToString(error, ['response', 'data'], 'Unable to set log forwarding'),
1269
+ );
1270
+
1271
+ // response a message or messages field
1272
+
1273
+ if (error.response.data.message || error.response.data.messages) {
1274
+ const message = objToString(
1275
+ error,
1276
+ ['response', 'data', 'message' || 'messages'],
1277
+ 'Unable to set log forwarding',
1278
+ );
1279
+ throw new Error(message);
1280
+ }
1281
+ // response contains error but no specific message field
1282
+ else {
1283
+ const message = objToString(error, ['response', 'data'], 'Unable to set log forwarding');
1284
+ throw new Error(message);
1285
+ }
1286
+ } else {
1287
+ // Something else happened while setting up the request
1288
+ logger.error('Error setting log forwarding configuration: %s', error.message);
1289
+ throw new Error(`Something went wrong while setting log forwarding. ${error.message}`);
1290
+ }
1291
+ }
1292
+ };
1293
+
1294
+ /**
1295
+ * @param {string} organizationCode - The IMS org code
1296
+ * @param {string} projectId - The project ID
1297
+ * @param {string} workspaceId - The workspace ID
1298
+ * @param {string} meshId - The mesh ID
1299
+ */
1300
+ const getLogForwarding = async (organizationCode, projectId, workspaceId, meshId) => {
1301
+ const { accessToken } = await getDevConsoleConfig();
1302
+ const config = {
1303
+ method: 'GET',
1304
+ url: `${SMS_BASE_URL}/organizations/${organizationCode}/projects/${projectId}/workspaces/${workspaceId}/meshes/${meshId}/log/forwarding`,
1305
+ headers: {
1306
+ 'Authorization': `Bearer ${accessToken}`,
1307
+ 'Content-Type': 'application/json',
1308
+ 'x-request-id': global.requestId,
1309
+ 'x-api-key': SMS_API_KEY,
1310
+ },
1311
+ };
1312
+
1313
+ logger.info(
1314
+ 'Initiating POST %s',
1315
+ `${SMS_BASE_URL}/organizations/${organizationCode}/projects/${projectId}/workspaces/${workspaceId}/meshes/${meshId}/log/forwarding`,
1316
+ );
1317
+
1318
+ try {
1319
+ const response = await axios(config);
1320
+
1321
+ logger.info('Response from GET %s', response.status);
1322
+
1323
+ if (response?.status === 200) {
1324
+ logger.info(`Get log forwarding configuration: ${objToString(response, ['data'])}`);
1325
+ return {
1326
+ data: response.data,
1327
+ };
1328
+ } else {
1329
+ // not 200 response
1330
+ logger.error(
1331
+ `Something went wrong: ${objToString(
1332
+ response,
1333
+ ['data'],
1334
+ 'Unable to get log forwarding details.',
1335
+ )}. Received ${response.status}, expected 200`,
1336
+ );
1337
+ throw new Error(response.data.message);
1338
+ }
1339
+ } catch (error) {
1340
+ if (error.response && error.response.status === 400) {
1341
+ // The request was made and the server responded with a 400 status code
1342
+ logger.error('Error getting the log forwarding configuration: %j', error.response.data);
1343
+
1344
+ throw new Error('Invalid input parameters.');
1345
+ } else if (error.response && error.response.status === 404) {
1346
+ logger.error('Log forwarding details not found');
1347
+
1348
+ return null;
1349
+ }
1350
+ // request made but no response received
1351
+ else if (error.request && !error.response) {
1352
+ logger.error('No response from server when getting the log forwarding configuration');
1353
+ throw new Error('Unable to get log forwarding details. Check the details and try again.');
1354
+ }
1355
+ // response received with error
1356
+ else if (error.response && error.response.data) {
1357
+ logger.error(
1358
+ 'Error getting the log forwarding configuration: %s',
1359
+ objToString(error, ['response', 'data'], 'Unable to get log forwarding'),
1360
+ );
1361
+
1362
+ // response a message or messages field
1363
+
1364
+ if (error.response.data.message || error.response.data.messages) {
1365
+ const message = objToString(
1366
+ error,
1367
+ ['response', 'data', 'message' || 'messages'],
1368
+ 'Unable to get log forwarding',
1369
+ );
1370
+ throw new Error(message);
1371
+ }
1372
+ // response contains error but no specific message field
1373
+ else {
1374
+ const message = objToString(error, ['response', 'data'], 'Unable to get log forwarding');
1375
+ throw new Error(message);
1376
+ }
1377
+ } else {
1378
+ // Something else happened while setting up the request
1379
+ logger.error('Error getting the log forwarding configuration: %s', error.message);
1380
+ throw new Error(
1381
+ `Something went wrong while getting the log forwarding configuration. ${error.message}`,
1382
+ );
1383
+ }
1384
+ }
1385
+ };
1386
+
1204
1387
  module.exports = {
1205
1388
  getApiKeyCredential,
1206
1389
  describeMesh,
@@ -1222,4 +1405,6 @@ module.exports = {
1222
1405
  getPresignedUrls,
1223
1406
  getLogsByRayId,
1224
1407
  cachePurge,
1408
+ setLogForwarding,
1409
+ getLogForwarding,
1225
1410
  };
package/src/server.js CHANGED
@@ -1,80 +1,38 @@
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
- );
27
-
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;
1
+ const { spawn } = require('child_process');
2
+ const { readSecretsFile } = require('./serverUtils');
3
+ const packageData = require('../package.json');
4
+
5
+ const runServer = portNo => {
6
+ const wranglerPackageNumber = packageData.dependencies.wrangler;
7
+ const wranglerVersion = `wrangler@${wranglerPackageNumber.replace(/^[\^~]/, '')}`;
8
+ const indexFilePath = `${__dirname}/index.js`;
9
+ const filePath = '.mesh';
10
+ const secrets = readSecretsFile(filePath);
11
+ const commandArgs = [
12
+ wranglerVersion,
13
+ 'dev',
14
+ indexFilePath,
15
+ '--show-interactive-dev-session',
16
+ 'false',
17
+ '--var',
18
+ `Secret:${JSON.stringify(secrets)}`,
19
+ '--port',
20
+ portNo,
21
+ '--inspector-port',
22
+ '9229',
23
+ ];
24
+
25
+ const wrangler = spawn('npx', commandArgs, {
26
+ stdio: 'inherit',
38
27
  });
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
- };
54
28
 
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',
29
+ wrangler.on('close', code => {
30
+ console.log(`wrangler dev process exited with code ${code}`);
69
31
  });
70
- }
71
-
72
- const maskError = error => {
73
- if (error instanceof GraphQLError && error.extensions?.http?.headers) {
74
- delete error.extensions.http.headers;
75
- }
76
32
 
77
- return error;
33
+ wrangler.on('error', error => {
34
+ console.error(`Failed to start wrangler dev: ${error.message}`);
35
+ });
78
36
  };
79
37
 
80
- module.exports = { buildServer };
38
+ module.exports = { runServer };
package/src/utils.js CHANGED
@@ -62,13 +62,6 @@ const secretsFlag = Flags.string({
62
62
  const portNoFlag = Flags.integer({
63
63
  char: 'p',
64
64
  description: 'Port number for the local dev server',
65
- default: 5000,
66
- });
67
-
68
- const inspectPortNoFlag = Flags.integer({
69
- char: 'i',
70
- description: 'Port number for the local dev server inspector',
71
- default: 9229,
72
65
  });
73
66
 
74
67
  const debugFlag = Flags.boolean({
@@ -106,6 +99,49 @@ const logFilenameFlag = Flags.string({
106
99
  required: true,
107
100
  });
108
101
 
102
+ // The `destinations` object to hold the configuration for log forwarding destinations.
103
+ // It prompts for the required inputs for the destination.
104
+ // Each destination can have different key/value pairs of configuration credentials.
105
+ // and applies the validation logic accordingly.
106
+ const destinations = {
107
+ // Configuration for the 'New Relic' destination
108
+ 'New Relic': {
109
+ name: 'newrelic', // internal value that will be used
110
+ // Required inputs for the 'New Relic' destination
111
+ inputs: [
112
+ {
113
+ name: 'baseUri',
114
+ promptMessage: 'Enter base URI:',
115
+ isSecret: false,
116
+ validate: value => {
117
+ if (!value) {
118
+ throw new Error('Base URI is required');
119
+ }
120
+ if (!value.startsWith('https://')) {
121
+ throw new Error('The URI value must include the protocol (https://)');
122
+ }
123
+ },
124
+ },
125
+ {
126
+ name: 'licenseKey',
127
+ promptMessage: 'Enter license key:',
128
+ isSecret: true,
129
+ validate: value => {
130
+ if (!value) {
131
+ throw new Error('License key is required');
132
+ }
133
+ if (value.length !== 40) {
134
+ throw new Error(
135
+ `The license key is in the wrong format. Expected: 40 characters (received: ${value.length})`,
136
+ );
137
+ }
138
+ },
139
+ },
140
+ ],
141
+ },
142
+ // Additional destinations can be added here
143
+ };
144
+
109
145
  /**
110
146
  * Parse the meshConfig and get the list of (local) files to be imported
111
147
  *
@@ -284,7 +320,7 @@ function checkPlaceholders(mesh) {
284
320
  * @param {string} file
285
321
  * @param {object} command
286
322
  * @param {string} filetype
287
- * @returns {Promise<string>}
323
+ * @returns {string}
288
324
  */
289
325
  async function readFileContents(file, command, filetype) {
290
326
  try {
@@ -352,7 +388,7 @@ function validateFileName(filesList) {
352
388
  * @param {string} inputMeshData
353
389
  * @param {string} envFilePath
354
390
  * @param {object} command
355
- * @returns {Promise<string>}
391
+ * @returns {string}
356
392
  */
357
393
  async function validateAndInterpolateMesh(inputMeshData, envFilePath, command) {
358
394
  //Read the environment file
@@ -767,7 +803,6 @@ module.exports = {
767
803
  validateAndInterpolateMesh,
768
804
  getAppRootDir,
769
805
  portNoFlag,
770
- inspectPortNoFlag,
771
806
  debugFlag,
772
807
  selectFlag,
773
808
  secretsFlag,
@@ -786,4 +821,5 @@ module.exports = {
786
821
  validateDateTimeFormat,
787
822
  localToUTCTime,
788
823
  cachePurgeAllActionFlag,
824
+ destinations,
789
825
  };
@@ -0,0 +1,80 @@
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
+ );
27
+
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;
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
+ };
54
+
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',
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;
78
+ };
79
+
80
+ module.exports = { buildServer };