@adobe/aio-cli-plugin-api-mesh 4.1.0 → 5.0.0-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.
- package/oclif.manifest.json +1 -1
- package/package.json +3 -7
- package/src/commands/api-mesh/__tests__/create.test.js +217 -293
- package/src/commands/api-mesh/__tests__/delete.test.js +6 -6
- package/src/commands/api-mesh/__tests__/describe.test.js +2 -17
- package/src/commands/api-mesh/__tests__/get.test.js +9 -78
- package/src/commands/api-mesh/__tests__/log-get-bulk.test.js +1 -1
- package/src/commands/api-mesh/__tests__/run.test.js +22 -25
- package/src/commands/api-mesh/__tests__/status.test.js +1 -1
- package/src/commands/api-mesh/__tests__/update.test.js +8 -9
- package/src/commands/api-mesh/create.js +6 -9
- package/src/commands/api-mesh/delete.js +5 -5
- package/src/commands/api-mesh/describe.js +3 -3
- package/src/commands/api-mesh/get.js +8 -11
- package/src/commands/api-mesh/init.js +0 -2
- package/src/commands/api-mesh/log-get-bulk.js +2 -2
- package/src/commands/api-mesh/log-get.js +2 -2
- package/src/commands/api-mesh/log-list.js +1 -1
- package/src/commands/api-mesh/run.js +9 -35
- package/src/commands/api-mesh/source/install.js +3 -2
- package/src/commands/api-mesh/status.js +4 -6
- package/src/commands/api-mesh/update.js +7 -9
- package/src/constants.js +2 -0
- package/src/lib/devConsole.js +38 -32
- package/src/server.js +198 -36
- package/src/serverUtils.js +3 -3
- package/src/cors.js +0 -28
- package/src/fixPlugins.js +0 -28
- package/src/index.js +0 -44
- package/src/plugins/complianceHeaders/complianceHeaders.js +0 -55
- package/src/plugins/complianceHeaders/index.js +0 -2
- package/src/plugins/httpDetailsExtensions/LICENSE +0 -21
- package/src/plugins/httpDetailsExtensions/index.js +0 -81
- package/src/secrets.js +0 -34
- package/src/served.js +0 -22
- package/src/templates/wrangler.toml +0 -14
- package/src/utils/logger.js +0 -42
- package/src/utils/requestId.js +0 -26
- package/src/wranglerServer.js +0 -80
package/src/server.js
CHANGED
|
@@ -1,38 +1,200 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
// Resolve the path to 'fastify' and 'graphql-yoga' within the local 'node_modules'
|
|
2
|
+
const fastifyPath = require.resolve('fastify', { paths: [process.cwd()] });
|
|
3
|
+
const yogaPath = require.resolve('graphql-yoga', { paths: [process.cwd()] });
|
|
4
|
+
|
|
5
|
+
// Load 'fastify' and 'graphql-yoga' using the resolved paths
|
|
6
|
+
const fastify = require(fastifyPath);
|
|
7
|
+
const { createYoga } = require(yogaPath);
|
|
8
|
+
const logger = require('./classes/logger');
|
|
9
|
+
|
|
10
|
+
//Load the functions from serverUtils.js
|
|
11
|
+
const {
|
|
12
|
+
readMeshConfig,
|
|
13
|
+
removeRequestHeaders,
|
|
14
|
+
prepSourceResponseHeaders,
|
|
15
|
+
processResponseHeaders,
|
|
16
|
+
readSecretsFile,
|
|
17
|
+
} = require('./serverUtils');
|
|
18
|
+
|
|
19
|
+
let yogaServer = null;
|
|
20
|
+
let meshConfig;
|
|
21
|
+
|
|
22
|
+
// catch unhandled promise rejections
|
|
23
|
+
process.on('unhandledRejection', reason => {
|
|
24
|
+
logger.error('Unhandled Rejection at:', reason.stack || reason);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// catch uncaught exceptions
|
|
28
|
+
process.on('uncaughtException', err => {
|
|
29
|
+
logger.error('Uncaught Exception thrown');
|
|
30
|
+
logger.error(err.stack);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// get meshId from command line arguments
|
|
35
|
+
const meshId = process.argv[2];
|
|
36
|
+
|
|
37
|
+
// get PORT number from command line arguments
|
|
38
|
+
const portNo = parseInt(process.argv[3]);
|
|
39
|
+
|
|
40
|
+
const getCORSOptions = () => {
|
|
41
|
+
try {
|
|
42
|
+
const currentWorkingDirectory = process.cwd();
|
|
43
|
+
const meshConfigPath = `${currentWorkingDirectory}/mesh-artifact/${meshId}/.meshrc.json`;
|
|
44
|
+
|
|
45
|
+
const meshConfig = require(meshConfigPath);
|
|
46
|
+
const { responseConfig } = meshConfig;
|
|
47
|
+
const { CORS } = responseConfig;
|
|
48
|
+
|
|
49
|
+
return CORS;
|
|
50
|
+
} catch (e) {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Custom get secrets handler
|
|
56
|
+
const getSecretsHandler = {
|
|
57
|
+
get: function (target, prop, receiver) {
|
|
58
|
+
if (prop === 'toJSON') {
|
|
59
|
+
// Handle the toJSON case
|
|
60
|
+
return () => target;
|
|
61
|
+
}
|
|
62
|
+
if (prop in target) {
|
|
63
|
+
return Reflect.get(target, prop, receiver);
|
|
64
|
+
} else {
|
|
65
|
+
throw new Error(`The secret ${String(prop)} is not available.`);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
set: function () {
|
|
69
|
+
throw new Error('Setting secrets is not allowed');
|
|
70
|
+
},
|
|
36
71
|
};
|
|
37
72
|
|
|
38
|
-
|
|
73
|
+
const getYogaServer = async () => {
|
|
74
|
+
if (yogaServer) {
|
|
75
|
+
return yogaServer;
|
|
76
|
+
} else {
|
|
77
|
+
const currentWorkingDirectory = process.cwd();
|
|
78
|
+
const meshArtifactsPath = `${currentWorkingDirectory}/mesh-artifact/${meshId}`;
|
|
79
|
+
|
|
80
|
+
const meshArtifacts = require(meshArtifactsPath);
|
|
81
|
+
const { getBuiltMesh } = meshArtifacts;
|
|
82
|
+
|
|
83
|
+
const tenantMesh = await getBuiltMesh();
|
|
84
|
+
const corsOptions = getCORSOptions();
|
|
85
|
+
|
|
86
|
+
const secrets = readSecretsFile(meshId);
|
|
87
|
+
|
|
88
|
+
const secretsProxy = new Proxy(secrets, getSecretsHandler);
|
|
89
|
+
|
|
90
|
+
logger.info('Creating graphQL server');
|
|
91
|
+
|
|
92
|
+
meshConfig = readMeshConfig(meshId);
|
|
93
|
+
|
|
94
|
+
yogaServer = createYoga({
|
|
95
|
+
plugins: tenantMesh.plugins,
|
|
96
|
+
graphqlEndpoint: `/graphql`,
|
|
97
|
+
graphiql: true,
|
|
98
|
+
maskedErrors: false,
|
|
99
|
+
cors: corsOptions,
|
|
100
|
+
context: initialContext => ({
|
|
101
|
+
...initialContext,
|
|
102
|
+
secrets: secretsProxy,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return yogaServer;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const app = fastify();
|
|
111
|
+
|
|
112
|
+
app.route({
|
|
113
|
+
method: ['GET', 'POST'],
|
|
114
|
+
url: '/graphql',
|
|
115
|
+
handler: async (req, res) => {
|
|
116
|
+
logger.info('Request received: ', req.body);
|
|
117
|
+
|
|
118
|
+
let body = null;
|
|
119
|
+
let responseBody = null;
|
|
120
|
+
let includeMetaData = false;
|
|
121
|
+
|
|
122
|
+
if (req.headers['x-include-metadata'] && req.headers['x-include-metadata'].length > 0) {
|
|
123
|
+
if (req.headers['x-include-metadata'].toLowerCase() === 'true') {
|
|
124
|
+
includeMetaData = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const response = await yogaServer.handleNodeRequest(req, {
|
|
129
|
+
req,
|
|
130
|
+
reply: res,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
try {
|
|
135
|
+
body = await response.text();
|
|
136
|
+
if (body) {
|
|
137
|
+
responseBody = JSON.parse(body);
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
logger.error(`Error parsing response body: ${err}`);
|
|
141
|
+
logger.error(response);
|
|
142
|
+
throw new Error(`Error parsing response body: ${err}`);
|
|
143
|
+
}
|
|
144
|
+
//Set the value of includeHTTPDetails flag
|
|
145
|
+
|
|
146
|
+
const includeHTTPDetails = !!meshConfig?.responseConfig?.includeHTTPDetails;
|
|
147
|
+
const meshHTTPDetails = responseBody?.extensions?.httpDetails;
|
|
148
|
+
logger.info('Mesh HTTP Details: ', meshHTTPDetails);
|
|
149
|
+
|
|
150
|
+
/* the logic for handling mesh response headers using includeMetaData */
|
|
151
|
+
prepSourceResponseHeaders(meshHTTPDetails, req.id);
|
|
152
|
+
const responseHeaders = processResponseHeaders(meshId, req.id, includeMetaData, req.method);
|
|
153
|
+
|
|
154
|
+
/** Adding the yoga response headers to the response */
|
|
155
|
+
response.headers?.forEach((value, key) => {
|
|
156
|
+
res.header(key, value);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Delete the httpDetails extensions details if mesh owner has disabled those details in the config
|
|
160
|
+
if (includeHTTPDetails !== true) {
|
|
161
|
+
delete responseBody?.extensions?.httpDetails;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//make sure to remove the request headers from cache after the request is complete
|
|
165
|
+
removeRequestHeaders(req.id);
|
|
166
|
+
const fastifyResponseBody = JSON.stringify(responseBody);
|
|
167
|
+
res.status(response.status).headers(responseHeaders).send(fastifyResponseBody);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
logger.error(`Error parsing response body: ${err}`);
|
|
170
|
+
//we have this fallback catch clause if someone wants to load the graphiql engine. This returns the default headers back
|
|
171
|
+
response.headers?.forEach((value, key) => {
|
|
172
|
+
res.header(key, value);
|
|
173
|
+
});
|
|
174
|
+
res.status(response.status);
|
|
175
|
+
res.send(response.body);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return res;
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
app.listen(
|
|
183
|
+
{
|
|
184
|
+
//set the port no of the server based on the input value
|
|
185
|
+
port: portNo,
|
|
186
|
+
},
|
|
187
|
+
async err => {
|
|
188
|
+
try {
|
|
189
|
+
if (err) {
|
|
190
|
+
throw new Error(`Server setup error: ${err.message}`);
|
|
191
|
+
}
|
|
192
|
+
yogaServer = await getYogaServer();
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error(error);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(`Server is running on http://localhost:${portNo}/graphql`);
|
|
199
|
+
},
|
|
200
|
+
);
|
package/src/serverUtils.js
CHANGED
|
@@ -318,13 +318,13 @@ function ccDirectivesToString(directives) {
|
|
|
318
318
|
|
|
319
319
|
/**
|
|
320
320
|
* Returns secrets content from artifacts
|
|
321
|
-
* @param
|
|
321
|
+
* @param meshId
|
|
322
322
|
* @returns
|
|
323
323
|
*/
|
|
324
|
-
function readSecretsFile(
|
|
324
|
+
function readSecretsFile(meshId) {
|
|
325
325
|
let secrets = {};
|
|
326
326
|
try {
|
|
327
|
-
const filePath = path.resolve(process.cwd(), `${
|
|
327
|
+
const filePath = path.resolve(process.cwd(), 'mesh-artifact', `${meshId}`, 'secrets.yaml');
|
|
328
328
|
if (fs.existsSync(filePath)) {
|
|
329
329
|
secrets = YAML.parse(fs.readFileSync(filePath, 'utf8'));
|
|
330
330
|
}
|
package/src/cors.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get default CORS options.
|
|
3
|
-
* @param env Environment.
|
|
4
|
-
*/
|
|
5
|
-
const getDefaultCorsOptions = env => {
|
|
6
|
-
return env.CORS_DEFAULT_URL
|
|
7
|
-
? {
|
|
8
|
-
origin: env.CORS_DEFAULT_URL,
|
|
9
|
-
}
|
|
10
|
-
: {};
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Get CORS options. Merges default CORS options with custom specific options if present in the mesh configuration.
|
|
15
|
-
* Custom CORS options overwrite the default options to ensure that specific requirements can be met without altering
|
|
16
|
-
* the default configuration.
|
|
17
|
-
* @param env Environment.
|
|
18
|
-
* @param meshConfig Mesh configuration.
|
|
19
|
-
*/
|
|
20
|
-
const getCorsOptions = (env, meshConfig) => {
|
|
21
|
-
const defaultCorsOptions = getDefaultCorsOptions(env);
|
|
22
|
-
return {
|
|
23
|
-
...defaultCorsOptions,
|
|
24
|
-
...meshConfig.responseConfig?.CORS,
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
module.exports = { getCorsOptions };
|
package/src/fixPlugins.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
/**
|
|
4
|
-
* Modifies `index.js` of a mesh artifact to mutate plugin references for edge compatibility. Current forces
|
|
5
|
-
* `@graphql-mesh/plugin-http-details-extensions` to resolve to local fork instead.
|
|
6
|
-
*/
|
|
7
|
-
async function fixPlugins(meshArtifactPath) {
|
|
8
|
-
try {
|
|
9
|
-
console.log('Modifying mesh artifact to fix plugins for edge compatibility.');
|
|
10
|
-
const data = fs.readFileSync(meshArtifactPath, 'utf8');
|
|
11
|
-
const updatedData = data.replace(
|
|
12
|
-
/@graphql-mesh\/plugin-http-details-extensions/g,
|
|
13
|
-
'../src/plugins/httpDetailsExtensions',
|
|
14
|
-
);
|
|
15
|
-
fs.writeFileSync(meshArtifactPath, updatedData, 'utf8');
|
|
16
|
-
} catch (err) {
|
|
17
|
-
console.error(err);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Execute fixPlugins if run directly from CLI
|
|
22
|
-
if (require.main === module) {
|
|
23
|
-
fixPlugins();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = {
|
|
27
|
-
fixPlugins,
|
|
28
|
-
};
|
package/src/index.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { ServedTier, addServedHeader } from './served';
|
|
2
|
-
import { bindedlogger as logger } from './utils/logger';
|
|
3
|
-
import { getRequestId } from './utils/requestId';
|
|
4
|
-
import { buildServer } from './wranglerServer';
|
|
5
|
-
|
|
6
|
-
let server;
|
|
7
|
-
|
|
8
|
-
export default {
|
|
9
|
-
setServer(newServer) {
|
|
10
|
-
server = newServer;
|
|
11
|
-
},
|
|
12
|
-
/**
|
|
13
|
-
* Fetch.
|
|
14
|
-
* @param request Request.
|
|
15
|
-
* @param env Environment.
|
|
16
|
-
* @param ctx Event context.
|
|
17
|
-
*/
|
|
18
|
-
async fetch(request, env, ctx) {
|
|
19
|
-
const requestId = getRequestId(request);
|
|
20
|
-
// Retrieve environment variables
|
|
21
|
-
const { MESH_ID: meshId, LOG_LEVEL: logLevel } = env;
|
|
22
|
-
const loggerInstance = logger({ logLevel, meshId, requestId });
|
|
23
|
-
const meshArtifacts = await import('../.mesh');
|
|
24
|
-
const rawMesh = await import('../.mesh/.meshrc.json');
|
|
25
|
-
|
|
26
|
-
if (!server) {
|
|
27
|
-
server = await this.buildAndCacheServer(env, loggerInstance, meshArtifacts, rawMesh);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
loggerInstance.debug('WORKER HOT: Fetching via worker');
|
|
31
|
-
const response = await server.fetch(request, ctx);
|
|
32
|
-
addServedHeader(response, ServedTier.WORKER_HOT);
|
|
33
|
-
return response;
|
|
34
|
-
},
|
|
35
|
-
/**
|
|
36
|
-
* Build and cache mesh instance/server in global variable.
|
|
37
|
-
* @param env
|
|
38
|
-
* @param loggerInstance
|
|
39
|
-
*/
|
|
40
|
-
async buildAndCacheServer(env, loggerInstance, meshArtifacts, meshConfig) {
|
|
41
|
-
server = await buildServer(loggerInstance, env, meshArtifacts, meshConfig);
|
|
42
|
-
return server;
|
|
43
|
-
},
|
|
44
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CF-Connecting-IP provides the client IP address connecting to Cloudflare to the origin web server. This header will only be sent on the
|
|
3
|
-
* traffic from Cloudflare’s edge to your origin web server. Upstream requests will the Cloudflare Worker client IP address of
|
|
4
|
-
* `2a06:98c0:3600::103`.
|
|
5
|
-
* @see https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ip
|
|
6
|
-
*/
|
|
7
|
-
const CF_CONNECTING_IP_HEADER = 'cf-connecting-ip';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The X-Forwarded-For (XFF) request header is a de-facto standard header for identifying the originating IP address of a client connecting
|
|
11
|
-
* to a web server through a proxy server.
|
|
12
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
|
|
13
|
-
*/
|
|
14
|
-
const X_FORWARDED_FOR_HEADER = 'x-forwarded-for';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Add `x-forwarded-for` header from request context to each fetch.
|
|
18
|
-
* @param context Request context.
|
|
19
|
-
* @param headers Fetch headers.
|
|
20
|
-
*/
|
|
21
|
-
const addXForwardedForHeader = (context, headers) => {
|
|
22
|
-
// `cf-connecting-ip` header contains the original visitor's IP address
|
|
23
|
-
const connectingIp = context?.request.headers.get(CF_CONNECTING_IP_HEADER);
|
|
24
|
-
const xForwardedFor = context?.request.headers.get(X_FORWARDED_FOR_HEADER);
|
|
25
|
-
if (connectingIp) {
|
|
26
|
-
if (!xForwardedFor) {
|
|
27
|
-
// Construct new `x-forwarded-for` header if not present in original request
|
|
28
|
-
headers.set(X_FORWARDED_FOR_HEADER, connectingIp);
|
|
29
|
-
} else {
|
|
30
|
-
// Construct `x-forwarded-for` header using original header
|
|
31
|
-
headers.set(X_FORWARDED_FOR_HEADER, `${xForwardedFor}, ${connectingIp}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Adds compliance headers to source fetch requests.
|
|
38
|
-
*/
|
|
39
|
-
function useComplianceHeaders() {
|
|
40
|
-
return {
|
|
41
|
-
onFetch({ context, options }) {
|
|
42
|
-
// Construct mutable headers from options passed to each fetch
|
|
43
|
-
const headers = new Headers(options.headers);
|
|
44
|
-
addXForwardedForHeader(context, headers);
|
|
45
|
-
options.headers = headers;
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
module.exports = {
|
|
51
|
-
useComplianceHeaders,
|
|
52
|
-
CF_CONNECTING_IP_HEADER,
|
|
53
|
-
X_FORWARDED_FOR_HEADER,
|
|
54
|
-
addXForwardedForHeader,
|
|
55
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2020 Uri Goldshtein
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fork of https://github.com/ardatan/graphql-mesh/blob/%40graphql-mesh/plugin-http-details-extensions%400.1.21/packages/plugins/http-details-extensions/src/index.ts
|
|
3
|
-
* Version: 0.1.21
|
|
4
|
-
* TODO: Extract to a separate repository/artifact after approach has been validated.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/* eslint-disable */
|
|
8
|
-
|
|
9
|
-
const { isAsyncIterable } = require('@envelop/core');
|
|
10
|
-
const { getHeadersObj } = require('@graphql-mesh/utils');
|
|
11
|
-
|
|
12
|
-
function useIncludeHttpDetailsInExtensions(opts) {
|
|
13
|
-
if (!opts.if) {
|
|
14
|
-
return {};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const httpDetailsByContext = new WeakMap();
|
|
18
|
-
|
|
19
|
-
function getHttpDetailsByContext(context) {
|
|
20
|
-
let httpDetails = httpDetailsByContext.get(context);
|
|
21
|
-
if (!httpDetails) {
|
|
22
|
-
httpDetails = [];
|
|
23
|
-
httpDetailsByContext.set(context, httpDetails);
|
|
24
|
-
}
|
|
25
|
-
return httpDetails;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
onFetch({ url, context, info, options }) {
|
|
30
|
-
if (context != null) {
|
|
31
|
-
const requestTimestamp = Date.now();
|
|
32
|
-
return ({ response }) => {
|
|
33
|
-
const responseTimestamp = Date.now();
|
|
34
|
-
const responseTime = responseTimestamp - requestTimestamp;
|
|
35
|
-
const httpDetailsList = getHttpDetailsByContext(context);
|
|
36
|
-
const httpDetails = {
|
|
37
|
-
sourceName: (info)?.sourceName,
|
|
38
|
-
path: info?.path,
|
|
39
|
-
request: {
|
|
40
|
-
timestamp: requestTimestamp,
|
|
41
|
-
url,
|
|
42
|
-
method: options.method || 'GET',
|
|
43
|
-
headers: getHeadersObj(options.headers),
|
|
44
|
-
},
|
|
45
|
-
response: {
|
|
46
|
-
timestamp: responseTimestamp,
|
|
47
|
-
status: response.status,
|
|
48
|
-
statusText: response.statusText,
|
|
49
|
-
headers: getHeadersObj(response.headers),
|
|
50
|
-
// Added to interface to account for edge fetch implementation/behavior
|
|
51
|
-
cookies: response.headers.getSetCookie(),
|
|
52
|
-
},
|
|
53
|
-
responseTime,
|
|
54
|
-
};
|
|
55
|
-
httpDetailsList.push(httpDetails);
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return undefined;
|
|
59
|
-
},
|
|
60
|
-
onExecute({ args: { contextValue } }) {
|
|
61
|
-
return {
|
|
62
|
-
onExecuteDone({ result, setResult }) {
|
|
63
|
-
if (!isAsyncIterable(result)) {
|
|
64
|
-
const httpDetailsList = httpDetailsByContext.get(contextValue);
|
|
65
|
-
if (httpDetailsList != null) {
|
|
66
|
-
setResult({
|
|
67
|
-
...result,
|
|
68
|
-
extensions: {
|
|
69
|
-
...result.extensions,
|
|
70
|
-
httpDetails: httpDetailsList,
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
module.exports = useIncludeHttpDetailsInExtensions;
|
package/src/secrets.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Parse the yaml secrets string from env to json object
|
|
2
|
-
function loadMeshSecrets(logger, secret) {
|
|
3
|
-
let parsedSecrets = {};
|
|
4
|
-
|
|
5
|
-
try {
|
|
6
|
-
// Replace escaped backslashes with a single backslash
|
|
7
|
-
secret = secret.replace(/\\"/g, '"');
|
|
8
|
-
parsedSecrets = JSON.parse(secret);
|
|
9
|
-
} catch (err) {
|
|
10
|
-
logger.error('Error parsing secrets.');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return parsedSecrets;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Custom get secrets handler
|
|
17
|
-
const getSecretsHandler = {
|
|
18
|
-
get: function (target, prop, receiver) {
|
|
19
|
-
if (prop === 'toJSON') {
|
|
20
|
-
// Handle the toJSON case
|
|
21
|
-
return () => target;
|
|
22
|
-
}
|
|
23
|
-
if (prop in target) {
|
|
24
|
-
return Reflect.get(target, prop, receiver);
|
|
25
|
-
} else {
|
|
26
|
-
throw new Error(`The secret ${String(prop)} is not available.`);
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
set: function () {
|
|
30
|
-
throw new Error('Setting secrets is not allowed');
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
module.exports = { loadMeshSecrets, getSecretsHandler };
|
package/src/served.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Header to indicate which tier served the request.
|
|
3
|
-
*/
|
|
4
|
-
const SERVE_TIER_HEADER = 'x-api-mesh-served';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Tiers that served the request.
|
|
8
|
-
*/
|
|
9
|
-
const ServedTier = {
|
|
10
|
-
WORKER_HOT: 0,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Add the served header to the response. Requires mutable headers on the response object.
|
|
15
|
-
* @param response Response.
|
|
16
|
-
* @param servedTier Tier that served the request.
|
|
17
|
-
*/
|
|
18
|
-
const addServedHeader = (response, servedTier) => {
|
|
19
|
-
response.headers.set(SERVE_TIER_HEADER, servedTier.toString());
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
module.exports = { ServedTier, addServedHeader };
|
|
@@ -1,14 +0,0 @@
|
|
|
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
|
-
node_compat = false
|
|
5
|
-
|
|
6
|
-
find_additional_modules = true
|
|
7
|
-
base_dir = ".mesh"
|
|
8
|
-
rules = [
|
|
9
|
-
{ type = "CommonJS", globs = ["tenantFiles/**/*.js"] }
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
[[migrations]]
|
|
14
|
-
tag = "v1"
|
package/src/utils/logger.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const pino = require('pino');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Get a new instance of a Pino logger with default configuration.
|
|
5
|
-
* @param level Log level.
|
|
6
|
-
*/
|
|
7
|
-
const pinoLogger = level =>
|
|
8
|
-
pino({
|
|
9
|
-
level: level || 'info',
|
|
10
|
-
formatters: {
|
|
11
|
-
level(label) {
|
|
12
|
-
return {
|
|
13
|
-
level: label,
|
|
14
|
-
};
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Get a new instance of a Pino logger with default configuration and child bindings.
|
|
21
|
-
* @param bindings Logger bindings.
|
|
22
|
-
*/
|
|
23
|
-
const logger = bindings => {
|
|
24
|
-
return pinoLogger(bindings?.logLevel).child({
|
|
25
|
-
meshId: bindings?.meshId,
|
|
26
|
-
requestId: bindings?.requestId,
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Create a logger from environment/request.
|
|
32
|
-
* @param bindings Logger bindings.
|
|
33
|
-
*/
|
|
34
|
-
const bindedlogger = bindings => {
|
|
35
|
-
return logger({
|
|
36
|
-
logLevel: bindings?.logLevel,
|
|
37
|
-
meshId: bindings?.meshId,
|
|
38
|
-
requestId: bindings?.requestId,
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
module.exports = { logger, pinoLogger, bindedlogger };
|
package/src/utils/requestId.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const UUID = require('../uuid');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Default request identifier header name.
|
|
5
|
-
*/
|
|
6
|
-
const DEFAULT_REQUEST_ID_HEADER_NAME = 'x-request-id';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get request identifier from headers. Will attempt to set request identifier if not present.
|
|
10
|
-
* @param request Request.
|
|
11
|
-
* @param headerName Request identifier header name.
|
|
12
|
-
*/
|
|
13
|
-
function getRequestId(request, headerName = DEFAULT_REQUEST_ID_HEADER_NAME) {
|
|
14
|
-
let requestId = request.headers.get(headerName);
|
|
15
|
-
if (!requestId) {
|
|
16
|
-
requestId = UUID.newUuid().toString();
|
|
17
|
-
try {
|
|
18
|
-
request.headers.set(headerName, requestId);
|
|
19
|
-
} catch (err) {
|
|
20
|
-
// Unable to set request headers
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return requestId;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = { DEFAULT_REQUEST_ID_HEADER_NAME, getRequestId };
|