@adobe/aio-cli-plugin-api-mesh 5.2.2 → 5.2.3-alpha

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.
@@ -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
- 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',
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
- wrangler.on('close', code => {
30
- console.log(`wrangler dev process exited with code ${code}`);
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
- wrangler.on('error', error => {
34
- console.error(`Failed to start wrangler dev: ${error.message}`);
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 = { runServer };
80
+ module.exports = { buildServer };
package/src/utils.js CHANGED
@@ -62,6 +62,13 @@ 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,
65
72
  });
66
73
 
67
74
  const debugFlag = Flags.boolean({
@@ -277,7 +284,7 @@ function checkPlaceholders(mesh) {
277
284
  * @param {string} file
278
285
  * @param {object} command
279
286
  * @param {string} filetype
280
- * @returns {string}
287
+ * @returns {Promise<string>}
281
288
  */
282
289
  async function readFileContents(file, command, filetype) {
283
290
  try {
@@ -345,7 +352,7 @@ function validateFileName(filesList) {
345
352
  * @param {string} inputMeshData
346
353
  * @param {string} envFilePath
347
354
  * @param {object} command
348
- * @returns {string}
355
+ * @returns {Promise<string>}
349
356
  */
350
357
  async function validateAndInterpolateMesh(inputMeshData, envFilePath, command) {
351
358
  //Read the environment file
@@ -760,6 +767,7 @@ module.exports = {
760
767
  validateAndInterpolateMesh,
761
768
  getAppRootDir,
762
769
  portNoFlag,
770
+ inspectPortNoFlag,
763
771
  debugFlag,
764
772
  selectFlag,
765
773
  secretsFlag,
@@ -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 rawMesh = await import('../.mesh/.meshrc.json');
24
+ const meshConfig = await import('../.mesh/.meshrc.json');
25
25
 
26
26
  if (!server) {
27
- server = await this.buildAndCacheServer(env, loggerInstance, meshArtifacts, rawMesh);
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 loggerInstance
37
+ * @param env Environment
38
+ * @param logger Logger
39
+ * @param meshArtifacts Mesh artifact
40
+ * @param meshConfig Mesh config
39
41
  */
40
- async buildAndCacheServer(env, loggerInstance, meshArtifacts, meshConfig) {
41
- server = await buildServer(loggerInstance, env, meshArtifacts, meshConfig);
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 };
@@ -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;
@@ -1,80 +0,0 @@
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 };