@adobe/aio-cli-plugin-api-mesh 3.4.0 → 3.5.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.
@@ -18,6 +18,7 @@ const {
18
18
  promptConfirm,
19
19
  setUpTenantFiles,
20
20
  initSdk,
21
+ writeSecretsFile,
21
22
  } = require('../../../helpers');
22
23
  const { getMeshId, getMeshArtifact } = require('../../../lib/devConsole');
23
24
  require('@adobe-apimesh/mesh-builder');
@@ -35,12 +36,16 @@ jest.mock('../../../helpers', () => ({
35
36
  importFiles: jest.fn().mockResolvedValue(),
36
37
  promptConfirm: jest.fn().mockResolvedValue(true),
37
38
  setUpTenantFiles: jest.fn().mockResolvedValue(),
39
+ writeSecretsFile: jest.fn().mockResolvedValue(),
38
40
  }));
39
41
 
40
42
  jest.mock('../../../lib/devConsole', () => ({
41
43
  getMeshId: jest.fn().mockResolvedValue('mockMeshId'),
42
44
  getMeshArtifact: jest.fn().mockResolvedValue(),
43
45
  }));
46
+ jest.mock('chalk', () => ({
47
+ red: jest.fn(text => text), // Return the input text without any color formatting
48
+ }));
44
49
 
45
50
  jest.mock('@adobe-apimesh/mesh-builder', () => {
46
51
  return {
@@ -55,12 +60,15 @@ jest.mock('@adobe-apimesh/mesh-builder', () => {
55
60
  let logSpy = null;
56
61
  let errorLogSpy = null;
57
62
  let parseSpy = null;
63
+ let platformSpy = null;
58
64
 
59
65
  const originalEnv = {
60
66
  API_MESH_TIER: 'NON-TI',
61
67
  };
62
68
 
63
69
  const defaultPort = 5000;
70
+ const os = require('os');
71
+
64
72
  describe('run command tests', () => {
65
73
  beforeEach(() => {
66
74
  global.requestId = 'dummy_request_id';
@@ -68,10 +76,14 @@ describe('run command tests', () => {
68
76
  logSpy = jest.spyOn(RunCommand.prototype, 'log');
69
77
  errorLogSpy = jest.spyOn(RunCommand.prototype, 'error');
70
78
  parseSpy = jest.spyOn(RunCommand.prototype, 'parse');
79
+ platformSpy = jest.spyOn(os, 'platform');
71
80
  process.env = {
72
81
  ...originalEnv,
73
82
  };
74
83
  });
84
+ afterEach(() => {
85
+ platformSpy.mockRestore();
86
+ });
75
87
 
76
88
  test('snapshot run command description', () => {
77
89
  expect(RunCommand.description).toMatchInlineSnapshot(
@@ -121,6 +133,15 @@ describe('run command tests', () => {
121
133
  "parse": [Function],
122
134
  "type": "option",
123
135
  },
136
+ "secrets": {
137
+ "char": "s",
138
+ "default": false,
139
+ "description": "Path to secrets file",
140
+ "input": [],
141
+ "multiple": false,
142
+ "parse": [Function],
143
+ "type": "option",
144
+ },
124
145
  "select": {
125
146
  "allowNo": false,
126
147
  "default": false,
@@ -251,17 +272,20 @@ describe('run command tests', () => {
251
272
 
252
273
  await expect(runResult).rejects.toEqual(
253
274
  new Error(
254
- "Issue in src/commands/__fixtures__/env_invalid file - Duplicate key << key1 >> on line 3,Invalid format for key/value << key2=='value3' >> on line 5,Invalid format << key3 >> on line 6,Invalid format for key/value << key4='value4 >> on line 7",
275
+ 'Issue in src/commands/__fixtures__/env_invalid file - Interpolated mesh is not a valid JSON. Please check the generated json file.',
255
276
  ),
256
277
  );
257
278
 
258
279
  expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
259
280
  [
260
281
  [
261
- "Issue in src/commands/__fixtures__/env_invalid file - Duplicate key << key1 >> on line 3,Invalid format for key/value << key2=='value3' >> on line 5,Invalid format << key3 >> on line 6,Invalid format for key/value << key4='value4 >> on line 7",
282
+ "Interpolated mesh is not a valid JSON. Please check the generated json file.",
283
+ ],
284
+ [
285
+ "Issue in src/commands/__fixtures__/env_invalid file - Interpolated mesh is not a valid JSON. Please check the generated json file.",
262
286
  ],
263
287
  [
264
- "Issue in src/commands/__fixtures__/env_invalid file - Duplicate key << key1 >> on line 3,Invalid format for key/value << key2=='value3' >> on line 5,Invalid format << key3 >> on line 6,Invalid format for key/value << key4='value4 >> on line 7",
288
+ "Issue in src/commands/__fixtures__/env_invalid file - Interpolated mesh is not a valid JSON. Please check the generated json file.",
265
289
  ],
266
290
  ]
267
291
  `);
@@ -283,7 +307,9 @@ describe('run command tests', () => {
283
307
 
284
308
  const runResult = RunCommand.run();
285
309
  await expect(runResult).rejects.toEqual(
286
- new Error('The mesh file cannot be interpolated due to missing keys : newKey1 , newKey2'),
310
+ new Error(
311
+ 'Issue in src/commands/__fixtures__/env_valid file - The mesh file cannot be interpolated due to missing keys : newKey1 , newKey2',
312
+ ),
287
313
  );
288
314
 
289
315
  await expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
@@ -292,7 +318,10 @@ describe('run command tests', () => {
292
318
  "The mesh file cannot be interpolated due to missing keys : newKey1 , newKey2",
293
319
  ],
294
320
  [
295
- "The mesh file cannot be interpolated due to missing keys : newKey1 , newKey2",
321
+ "Issue in src/commands/__fixtures__/env_valid file - The mesh file cannot be interpolated due to missing keys : newKey1 , newKey2",
322
+ ],
323
+ [
324
+ "Issue in src/commands/__fixtures__/env_valid file - The mesh file cannot be interpolated due to missing keys : newKey1 , newKey2",
296
325
  ],
297
326
  ]
298
327
  `);
@@ -318,7 +347,9 @@ describe('run command tests', () => {
318
347
 
319
348
  const runResult = RunCommand.run();
320
349
  await expect(runResult).rejects.toEqual(
321
- new Error('Interpolated mesh is not a valid JSON. Please check the generated json file.'),
350
+ new Error(
351
+ 'Issue in src/commands/__fixtures__/env_valid file - Interpolated mesh is not a valid JSON. Please check the generated json file.',
352
+ ),
322
353
  );
323
354
 
324
355
  await expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
@@ -327,7 +358,10 @@ describe('run command tests', () => {
327
358
  "Interpolated mesh is not a valid JSON. Please check the generated json file.",
328
359
  ],
329
360
  [
330
- "Interpolated mesh is not a valid JSON. Please check the generated json file.",
361
+ "Issue in src/commands/__fixtures__/env_valid file - Interpolated mesh is not a valid JSON. Please check the generated json file.",
362
+ ],
363
+ [
364
+ "Issue in src/commands/__fixtures__/env_valid file - Interpolated mesh is not a valid JSON. Please check the generated json file.",
331
365
  ],
332
366
  ]
333
367
  `);
@@ -836,4 +870,109 @@ describe('run command tests', () => {
836
870
  );
837
871
  expect(setUpTenantFiles).toHaveBeenCalled();
838
872
  });
873
+
874
+ test('should return error for run command if mesh has placeholders and the provided secrets file is invalid', async () => {
875
+ parseSpy.mockResolvedValueOnce({
876
+ args: { file: 'src/commands/__fixtures__/sample_secrets_mesh.json' },
877
+ flags: {
878
+ secrets: 'src/commands/__fixtures__/secrets_invalid.yaml',
879
+ },
880
+ });
881
+
882
+ const runResult = RunCommand.run();
883
+
884
+ await expect(runResult).rejects.toEqual(
885
+ new Error('Unable to import secrets. Please check the file and try again.'),
886
+ );
887
+ });
888
+
889
+ test('should return error for run command if mesh has placeholders and the provided secrets file is not yaml or yml', async () => {
890
+ parseSpy.mockResolvedValueOnce({
891
+ args: { file: 'src/commands/__fixtures__/sample_secrets_mesh.json' },
892
+ flags: {
893
+ secrets: 'src/commands/__fixtures__/.secrets_file.env',
894
+ },
895
+ });
896
+
897
+ const runResult = RunCommand.run();
898
+
899
+ await expect(runResult).rejects.toEqual(
900
+ new Error('Unable to import secrets. Please check the file and try again.'),
901
+ );
902
+
903
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
904
+ [
905
+ [
906
+ "Invalid file format. Please provide a YAML file (.yaml or .yml).",
907
+ ],
908
+ ]
909
+ `);
910
+ });
911
+
912
+ test('should successfully run the mesh if provided secrets file is valid', async () => {
913
+ parseSpy.mockResolvedValueOnce({
914
+ args: { file: 'src/commands/__fixtures__/sample_secrets_mesh.json' },
915
+ flags: {
916
+ secrets: 'src/commands/__fixtures__/secrets_valid.yaml',
917
+ debug: false,
918
+ },
919
+ });
920
+
921
+ await RunCommand.run();
922
+ expect(writeSecretsFile).toHaveBeenCalled();
923
+ expect(startGraphqlServer).toHaveBeenCalledWith(expect.anything(), defaultPort, false);
924
+ });
925
+
926
+ test('should return error if ran with secrets against windows platform with batch variables', async () => {
927
+ platformSpy.mockReturnValue('win32');
928
+ parseSpy.mockResolvedValueOnce({
929
+ args: { file: 'src/commands/__fixtures__/sample_secrets_mesh.json' },
930
+ flags: {
931
+ secrets: 'src/commands/__fixtures__/secrets_with_batch_variables.yaml',
932
+ },
933
+ });
934
+
935
+ const runResult = RunCommand.run();
936
+ await expect(runResult).rejects.toEqual(
937
+ new Error('Unable to import secrets. Please check the file and try again.'),
938
+ );
939
+
940
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
941
+ [
942
+ [
943
+ "Batch variables are not supported in YAML files on Windows.",
944
+ ],
945
+ ]
946
+ `);
947
+ });
948
+
949
+ test('should pass if ran with secrets against linux platform with batch variables', async () => {
950
+ platformSpy.mockReturnValue('linux');
951
+ parseSpy.mockResolvedValueOnce({
952
+ args: { file: 'src/commands/__fixtures__/sample_secrets_mesh.json' },
953
+ flags: {
954
+ secrets: 'src/commands/__fixtures__/secrets_with_batch_variables.yaml',
955
+ debug: false,
956
+ },
957
+ });
958
+
959
+ await RunCommand.run();
960
+ expect(writeSecretsFile).toHaveBeenCalled();
961
+ expect(startGraphqlServer).toHaveBeenCalledWith(expect.anything(), defaultPort, false);
962
+ });
963
+
964
+ test('should pass if ran with secrets against darwin(macOS) platform with batch variables', async () => {
965
+ platformSpy.mockReturnValue('darwin');
966
+ parseSpy.mockResolvedValueOnce({
967
+ args: { file: 'src/commands/__fixtures__/sample_secrets_mesh.json' },
968
+ flags: {
969
+ secrets: 'src/commands/__fixtures__/secrets_with_batch_variables.yaml',
970
+ debug: false,
971
+ },
972
+ });
973
+
974
+ await RunCommand.run();
975
+ expect(writeSecretsFile).toHaveBeenCalled();
976
+ expect(startGraphqlServer).toHaveBeenCalledWith(expect.anything(), defaultPort, false);
977
+ });
839
978
  });
@@ -19,11 +19,15 @@ const {
19
19
  jsonFlag,
20
20
  getFilesInMeshConfig,
21
21
  envFileFlag,
22
+ secretsFlag,
22
23
  checkPlaceholders,
23
24
  readFileContents,
24
25
  validateAndInterpolateMesh,
26
+ interpolateSecrets,
27
+ validateSecretsFile,
28
+ encryptSecrets,
25
29
  } = require('../../utils');
26
- const { createMesh, getTenantFeatures } = require('../../lib/devConsole');
30
+ const { createMesh, getTenantFeatures, getPublicEncryptionKey } = require('../../lib/devConsole');
27
31
  const { buildEdgeMeshUrl, buildMeshUrl } = require('../../urlBuilder');
28
32
 
29
33
  class CreateCommand extends Command {
@@ -33,6 +37,7 @@ class CreateCommand extends Command {
33
37
  autoConfirmAction: autoConfirmActionFlag,
34
38
  json: jsonFlag,
35
39
  env: envFileFlag,
40
+ secrets: secretsFlag,
36
41
  };
37
42
 
38
43
  static enableJsonFlag = true;
@@ -53,6 +58,7 @@ class CreateCommand extends Command {
53
58
  const ignoreCache = await flags.ignoreCache;
54
59
  const autoConfirmAction = await flags.autoConfirmAction;
55
60
  const envFilePath = await flags.env;
61
+ const secretsFilePath = await flags.secrets;
56
62
  const {
57
63
  imsOrgId,
58
64
  imsOrgCode,
@@ -103,6 +109,20 @@ class CreateCommand extends Command {
103
109
  }
104
110
  }
105
111
 
112
+ // if secrets is present, include that in data.secrets
113
+ if (secretsFilePath) {
114
+ try {
115
+ await validateSecretsFile(secretsFilePath);
116
+ const secretsData = await interpolateSecrets(secretsFilePath, this);
117
+ const publicKey = await getPublicEncryptionKey(imsOrgCode);
118
+ const encryptedSecrets = await encryptSecrets(publicKey, secretsData);
119
+ data.secrets = encryptedSecrets;
120
+ } catch (err) {
121
+ this.log(err.message);
122
+ this.error('Unable to import secrets. Please check the file and try again.');
123
+ }
124
+ }
125
+
106
126
  let shouldContinue = true;
107
127
 
108
128
  if (!autoConfirmAction) {
@@ -15,11 +15,14 @@ const {
15
15
  debugFlag,
16
16
  selectFlag,
17
17
  envFileFlag,
18
+ secretsFlag,
18
19
  autoConfirmActionFlag,
19
20
  readFileContents,
20
21
  validateAndInterpolateMesh,
21
22
  checkPlaceholders,
22
23
  getFilesInMeshConfig,
24
+ validateSecretsFile,
25
+ interpolateSecrets,
23
26
  } = require('../../utils');
24
27
  const meshBuilder = require('@adobe-apimesh/mesh-builder');
25
28
  const fs = require('fs');
@@ -31,6 +34,7 @@ const {
31
34
  startGraphqlServer,
32
35
  importFiles,
33
36
  setUpTenantFiles,
37
+ writeSecretsFile,
34
38
  } = require('../../helpers');
35
39
  const logger = require('../../classes/logger');
36
40
  const { getMeshId, getMeshArtifact } = require('../../lib/devConsole');
@@ -55,6 +59,7 @@ class RunCommand extends Command {
55
59
  env: envFileFlag,
56
60
  autoConfirmAction: autoConfirmActionFlag,
57
61
  select: selectFlag,
62
+ secrets: secretsFlag,
58
63
  };
59
64
 
60
65
  static enableJsonFlag = true;
@@ -67,6 +72,7 @@ class RunCommand extends Command {
67
72
  logger.info(`RequestId: ${global.requestId}`);
68
73
 
69
74
  const { args, flags } = await this.parse(RunCommand);
75
+ const secretsFilePath = await flags.secrets;
70
76
 
71
77
  //Initialize the meshId based on
72
78
  let meshId = null;
@@ -165,6 +171,17 @@ class RunCommand extends Command {
165
171
  }
166
172
 
167
173
  let portNo;
174
+ //secrets management
175
+ if (secretsFilePath) {
176
+ try {
177
+ await validateSecretsFile(secretsFilePath);
178
+ const stringifiedSecrets = await interpolateSecrets(secretsFilePath, this);
179
+ await writeSecretsFile(stringifiedSecrets, meshId);
180
+ } catch (error) {
181
+ this.log(error.message);
182
+ this.error('Unable to import secrets. Please check the file and try again.');
183
+ }
184
+ }
168
185
 
169
186
  //To set the port number using the environment file
170
187
  if (process.env.PORT !== undefined) {
@@ -17,12 +17,16 @@ const {
17
17
  ignoreCacheFlag,
18
18
  autoConfirmActionFlag,
19
19
  envFileFlag,
20
+ secretsFlag,
20
21
  checkPlaceholders,
21
22
  readFileContents,
22
23
  validateAndInterpolateMesh,
23
24
  getFilesInMeshConfig,
25
+ interpolateSecrets,
26
+ validateSecretsFile,
27
+ encryptSecrets,
24
28
  } = require('../../utils');
25
- const { getMeshId, updateMesh } = require('../../lib/devConsole');
29
+ const { getMeshId, updateMesh, getPublicEncryptionKey } = require('../../lib/devConsole');
26
30
 
27
31
  class UpdateCommand extends Command {
28
32
  static args = [{ name: 'file' }];
@@ -30,6 +34,7 @@ class UpdateCommand extends Command {
30
34
  ignoreCache: ignoreCacheFlag,
31
35
  autoConfirmAction: autoConfirmActionFlag,
32
36
  env: envFileFlag,
37
+ secrets: secretsFlag,
33
38
  };
34
39
 
35
40
  async run() {
@@ -48,12 +53,19 @@ class UpdateCommand extends Command {
48
53
  const ignoreCache = await flags.ignoreCache;
49
54
  const autoConfirmAction = await flags.autoConfirmAction;
50
55
  const envFilePath = await flags.env;
51
-
52
- const { imsOrgId, projectId, workspaceId, orgName, projectName, workspaceName } = await initSdk(
53
- {
54
- ignoreCache,
55
- },
56
- );
56
+ const secretsFilePath = await flags.secrets;
57
+
58
+ const {
59
+ imsOrgId,
60
+ imsOrgCode,
61
+ projectId,
62
+ workspaceId,
63
+ orgName,
64
+ projectName,
65
+ workspaceName,
66
+ } = await initSdk({
67
+ ignoreCache,
68
+ });
57
69
 
58
70
  //Input the mesh data from the input file
59
71
  let inputMeshData = await readFileContents(args.file, this, 'mesh');
@@ -103,6 +115,20 @@ class UpdateCommand extends Command {
103
115
  }
104
116
  }
105
117
 
118
+ // if secrets is present, include that in data.secrets
119
+ if (secretsFilePath) {
120
+ try {
121
+ await validateSecretsFile(secretsFilePath);
122
+ const secretsData = await interpolateSecrets(secretsFilePath, this);
123
+ const publicKey = await getPublicEncryptionKey(imsOrgCode);
124
+ const encryptedSecrets = await encryptSecrets(publicKey, secretsData);
125
+ data.secrets = encryptedSecrets;
126
+ } catch (err) {
127
+ this.log(err.message);
128
+ this.error('Unable to import secrets. Please check the file and try again.');
129
+ }
130
+ }
131
+
106
132
  if (meshId) {
107
133
  let shouldContinue = true;
108
134
 
package/src/helpers.js CHANGED
@@ -860,6 +860,26 @@ async function setUpTenantFiles(meshId) {
860
860
  }
861
861
  }
862
862
 
863
+ /**
864
+ * This function is to create secrets.yaml in mesh-artifacts for respective meshId. Used for local development run command
865
+ *
866
+ * @secretsData secretsData
867
+ * @meshId meshId
868
+ */
869
+ async function writeSecretsFile(secretsData, meshId) {
870
+ if (!fs.existsSync(path.resolve(process.cwd(), 'mesh-artifact', meshId))) {
871
+ throw new Error(`Unexpected Error: issue creating secrets file.`);
872
+ }
873
+ try {
874
+ const secretsFileName = 'secrets.yaml';
875
+ const folderPath = path.join(process.cwd(), 'mesh-artifact', meshId);
876
+ const filePath = path.join(folderPath, secretsFileName);
877
+ fs.writeFileSync(filePath, secretsData);
878
+ } catch (error) {
879
+ throw new Error(error.message);
880
+ }
881
+ }
882
+
863
883
  module.exports = {
864
884
  objToString,
865
885
  promptInput,
@@ -876,4 +896,5 @@ module.exports = {
876
896
  updateFilesArray,
877
897
  startGraphqlServer,
878
898
  setUpTenantFiles,
899
+ writeSecretsFile,
879
900
  };
@@ -10,6 +10,7 @@ const fs = require('fs');
10
10
  const util = require('util');
11
11
  const exec = util.promisify(require('child_process').exec);
12
12
  const contentDisposition = require('content-disposition');
13
+ const chalk = require('chalk');
13
14
 
14
15
  const { DEV_CONSOLE_TRANSPORTER_API_KEY, SMS_BASE_URL } = CONSTANTS;
15
16
 
@@ -968,6 +969,52 @@ const getMeshDeployments = async (organizationCode, projectId, workspaceId, mesh
968
969
  }
969
970
  };
970
971
 
972
+ /**
973
+ * Gets the public key to encrypt secrets.
974
+ *
975
+ * This request bypasses the Dev Console and is sent directly to the Schema Management Service.
976
+ * As a result, we provide the publicKey used for secrets encryption.
977
+ * The near-term goal is to stop using Dev Console as a proxy for all routes.
978
+ * @param organizationCode
979
+ * @returns string
980
+ */
981
+ const getPublicEncryptionKey = async organizationCode => {
982
+ const { accessToken, apiKey } = await getDevConsoleConfig();
983
+ const config = {
984
+ method: 'get',
985
+ url: `${SMS_BASE_URL}/organizations/${organizationCode}/getPublicKey?API_KEY=${apiKey}`,
986
+ headers: {
987
+ 'Authorization': `Bearer ${accessToken}`,
988
+ 'x-request-id': global.requestId,
989
+ },
990
+ };
991
+ logger.info(
992
+ 'Initiating GET %s',
993
+ `${SMS_BASE_URL}/organizations/${organizationCode}/getPublicKey?API_KEY=${apiKey}`,
994
+ );
995
+ try {
996
+ const response = await axios(config);
997
+
998
+ logger.info('Response from GET %s', response.status);
999
+ if (response.status == 200) {
1000
+ let publicKey = '';
1001
+ logger.info(`Public key for encryption: ${objToString(response, ['data'])}`);
1002
+ if (response.data.publicKey) {
1003
+ publicKey = response.data.publicKey.replace(/\\n/g, '\n'); //correcting public key format
1004
+ }
1005
+ return publicKey;
1006
+ } else {
1007
+ let errorMessage = `Failed to load encryption keys. Please contact support.`;
1008
+ logger.error(`${errorMessage}. Received ${response.status}, expected 200`);
1009
+ throw new Error(chalk.red(errorMessage));
1010
+ }
1011
+ } catch (error) {
1012
+ let errorMessage = `Something went wrong while encrypting secrets. Please try again.`;
1013
+ logger.error(errorMessage);
1014
+ throw new Error(chalk.red(errorMessage));
1015
+ }
1016
+ };
1017
+
971
1018
  module.exports = {
972
1019
  getApiKeyCredential,
973
1020
  describeMesh,
@@ -984,4 +1031,5 @@ module.exports = {
984
1031
  getMeshArtifact,
985
1032
  getTenantFeatures,
986
1033
  getMeshDeployments,
1034
+ getPublicEncryptionKey,
987
1035
  };
package/src/server.js CHANGED
@@ -13,6 +13,7 @@ const {
13
13
  removeRequestHeaders,
14
14
  prepSourceResponseHeaders,
15
15
  processResponseHeaders,
16
+ readSecretsFile,
16
17
  } = require('./serverUtils');
17
18
 
18
19
  let yogaServer = null;
@@ -64,6 +65,8 @@ const getYogaServer = async () => {
64
65
  const tenantMesh = await getBuiltMesh();
65
66
  const corsOptions = getCORSOptions();
66
67
 
68
+ const secrets = readSecretsFile(meshId);
69
+
67
70
  logger.info('Creating graphQL server');
68
71
 
69
72
  meshConfig = readMeshConfig(meshId);
@@ -73,6 +76,10 @@ const getYogaServer = async () => {
73
76
  graphqlEndpoint: `/graphql`,
74
77
  graphiql: true,
75
78
  cors: corsOptions,
79
+ context: initialContext => ({
80
+ ...initialContext,
81
+ secrets,
82
+ }),
76
83
  });
77
84
 
78
85
  return yogaServer;
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const LRUCache = require('lru-cache');
4
4
  const logger = require('./classes/logger');
5
+ const YAML = require('yaml');
5
6
 
6
7
  const headersCache = new LRUCache({
7
8
  max: parseInt(process.env.CACHE_OPT_MAX || '500', 10),
@@ -315,9 +316,29 @@ function ccDirectivesToString(directives) {
315
316
  return chStr.toString();
316
317
  }
317
318
 
319
+ /**
320
+ * Returns secrets content from artifacts
321
+ * @param meshId
322
+ * @returns
323
+ */
324
+ function readSecretsFile(meshId) {
325
+ let secrets = {};
326
+ try {
327
+ const filePath = path.resolve(process.cwd(), 'mesh-artifact', `${meshId}`, 'secrets.yaml');
328
+ if (fs.existsSync(filePath)) {
329
+ secrets = YAML.parse(fs.readFileSync(filePath, 'utf8'));
330
+ }
331
+ } catch (error) {
332
+ logger.error('Unexpected error: unable to locate secrets file in mesh artifacts.');
333
+ throw new Error(error.message);
334
+ }
335
+ return secrets;
336
+ }
337
+
318
338
  module.exports = {
319
339
  readMeshConfig,
320
340
  removeRequestHeaders,
321
341
  prepSourceResponseHeaders,
322
342
  processResponseHeaders,
343
+ readSecretsFile,
323
344
  };