@foxtware/mineral 0.1.5 → 0.1.7
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/.creds.yml.sample +4 -1
- package/.env.sample +2 -0
- package/.gcloudignore +1 -4
- package/README.md +6 -1
- package/_build_scripts/createNewFunction.js +248 -38
- package/api/shopify/shopifyDecodeSessionToken.js +206 -0
- package/api/slack/slack.constants.js +5 -0
- package/api/slack/slack.utils.js +46 -0
- package/api/slack/slackMessagePost.js +85 -0
- package/api/utils.js +2 -4
- package/bin/mineral.js +1 -1
- package/{.hosting.yml.sample → hosting/.hosting.yml.sample} +4 -7
- package/hosting/copyCredsToEnv.js +48 -0
- package/{_deploy_scripts → hosting}/deployFromHostingYml.js +16 -37
- package/hosting/generateHosted.js +87 -0
- package/{handlerPaths.js → hosting/handlerPaths.js} +1 -1
- package/{hosting.utils.js → hosting/hosting.utils.js} +47 -52
- package/{wrappers.js → hosting/wrappers.js} +2 -2
- package/package.json +2 -2
- package/server.js +3 -6
- package/server.utils.js +41 -7
- package/_build_scripts/copyCredsToEnv.js +0 -45
- package/_deploy_scripts/generateHosted.js +0 -58
- /package/{_deploy_scripts → hosting}/execCommand.js +0 -0
- /package/{_deploy_scripts → hosting}/setEnvVarsGcloud.js +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const { SLACK_API_BASE_URL } = require('../slack/slack.constants');
|
|
2
|
+
const {
|
|
3
|
+
FetchClient,
|
|
4
|
+
Chain,
|
|
5
|
+
appendUrlToBase,
|
|
6
|
+
fetchClientCommonSteps,
|
|
7
|
+
} = require('../utils');
|
|
8
|
+
|
|
9
|
+
const addUrlAndAuthHeaders = async (state) => {
|
|
10
|
+
const { requestPayload, context } = state;
|
|
11
|
+
const { creds } = context;
|
|
12
|
+
const {
|
|
13
|
+
BOT_TOKEN,
|
|
14
|
+
} = creds;
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
requestPayload: {
|
|
18
|
+
...requestPayload,
|
|
19
|
+
method: requestPayload.method || 'post',
|
|
20
|
+
url: appendUrlToBase(SLACK_API_BASE_URL, requestPayload.url),
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
Authorization: `Bearer ${ BOT_TOKEN }`,
|
|
24
|
+
...requestPayload.headers,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
const slackClientRequestPreparer = new Chain([
|
|
32
|
+
addUrlAndAuthHeaders,
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const slackClientResponseInterpreter = new Chain([
|
|
36
|
+
fetchClientCommonSteps.exitEarlyOnNotOk,
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const slackClient = new FetchClient({
|
|
40
|
+
requestPreparer: slackClientRequestPreparer,
|
|
41
|
+
responseInterpreter: slackClientResponseInterpreter,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
slackClient,
|
|
46
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// https://docs.slack.dev/reference/methods/chat.postmessage
|
|
2
|
+
|
|
3
|
+
const { credsFromPayload, objHasAny, ArgsWarden } = require('../utils');
|
|
4
|
+
const { credsValidator } = require('../validators');
|
|
5
|
+
const { slackClient } = require('../slack/slack.utils');
|
|
6
|
+
|
|
7
|
+
const channelIdentifierValidator = (channelIdentifier) => {
|
|
8
|
+
return objHasAny(channelIdentifier, ['channelName', 'channelId']);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const messagePayloadValidator = (messagePayload) => {
|
|
12
|
+
return objHasAny(messagePayload, ['text', 'blocks', 'markdownText']);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const argsWarden = new ArgsWarden([
|
|
16
|
+
['credsPayload', credsValidator],
|
|
17
|
+
['channelIdentifier', channelIdentifierValidator],
|
|
18
|
+
['messagePayload', messagePayloadValidator],
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const slackMessagePost = async (
|
|
22
|
+
credsPayload,
|
|
23
|
+
channelIdentifier,
|
|
24
|
+
messagePayload,
|
|
25
|
+
{
|
|
26
|
+
inspect = false,
|
|
27
|
+
} = {},
|
|
28
|
+
) => {
|
|
29
|
+
|
|
30
|
+
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
31
|
+
credsPayload,
|
|
32
|
+
channelIdentifier,
|
|
33
|
+
messagePayload,
|
|
34
|
+
});
|
|
35
|
+
if (rejectResponse) {
|
|
36
|
+
return rejectResponse;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const creds = await credsFromPayload(credsPayload);
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
channelName,
|
|
43
|
+
channelId,
|
|
44
|
+
} = channelIdentifier;
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
text,
|
|
48
|
+
blocks,
|
|
49
|
+
markdownText,
|
|
50
|
+
} = messagePayload;
|
|
51
|
+
|
|
52
|
+
const response = await slackClient.fetch({
|
|
53
|
+
url: '/chat.postMessage',
|
|
54
|
+
method: 'post',
|
|
55
|
+
body: {
|
|
56
|
+
channel: channelId || channelName,
|
|
57
|
+
...text && { text },
|
|
58
|
+
...blocks && { blocks },
|
|
59
|
+
...markdownText && { markdown_text: markdownText },
|
|
60
|
+
},
|
|
61
|
+
context: { creds },
|
|
62
|
+
inspect,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return response;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const funcApiConfig = {
|
|
69
|
+
argsWarden,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
slackMessagePost,
|
|
74
|
+
funcApiConfig,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
curl -X POST "http://localhost:8000/slackMessagePost" \
|
|
79
|
+
-H "Content-Type: application/json" \
|
|
80
|
+
-d '{
|
|
81
|
+
"credsPayload": { "credsPath": "slack" },
|
|
82
|
+
"channelIdentifier": { "channelName": "#hidden_testing" },
|
|
83
|
+
"messagePayload": { "text": "new number, who dis?" }
|
|
84
|
+
}'
|
|
85
|
+
*/
|
package/api/utils.js
CHANGED
|
@@ -146,14 +146,14 @@ const customFetch = async (url, {
|
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
const responseContentType = response.headers.get('content-type');
|
|
149
|
-
console.log(responseContentType);
|
|
149
|
+
!HOSTED && console.log('responseContentType', responseContentType);
|
|
150
150
|
|
|
151
151
|
if (!responseParser) {
|
|
152
152
|
responseParser = getResponseParser(responseContentType);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
const parsedResponse = await responseParser(response);
|
|
156
|
-
logDeep({ parsedResponse });
|
|
156
|
+
!HOSTED && logDeep({ parsedResponse });
|
|
157
157
|
|
|
158
158
|
if (response.ok) {
|
|
159
159
|
return {
|
|
@@ -278,7 +278,6 @@ const pathAsArray = (path) => {
|
|
|
278
278
|
|
|
279
279
|
const objectDigNodeAtPath = (obj, path) => {
|
|
280
280
|
let nodes = pathAsArray(path);
|
|
281
|
-
console.log(nodes);
|
|
282
281
|
|
|
283
282
|
let output = obj;
|
|
284
283
|
for (const node of nodes) {
|
|
@@ -1161,7 +1160,6 @@ module.exports = {
|
|
|
1161
1160
|
actionSingleOrMultiple,
|
|
1162
1161
|
Processor,
|
|
1163
1162
|
Getter,
|
|
1164
|
-
capitaliseString,
|
|
1165
1163
|
sentenceCaseString,
|
|
1166
1164
|
ArgsWarden,
|
|
1167
1165
|
};
|
package/bin/mineral.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const command = process.argv[2];
|
|
4
4
|
|
|
5
5
|
if (command === 'host') {
|
|
6
|
-
const { deployFromHostingYml } = require('../
|
|
6
|
+
const { deployFromHostingYml } = require('../hosting/deployFromHostingYml');
|
|
7
7
|
deployFromHostingYml().catch((error) => {
|
|
8
8
|
console.error(error);
|
|
9
9
|
process.exit(1);
|
|
@@ -4,21 +4,18 @@ google_cloud_info:
|
|
|
4
4
|
|
|
5
5
|
# TODO: consider credsPayload in google_cloud_info instead of workspace .creds.yml
|
|
6
6
|
|
|
7
|
-
env:
|
|
8
|
-
- HOSTED_API_KEY
|
|
9
|
-
|
|
10
7
|
functions:
|
|
11
8
|
exampleFunction:
|
|
12
9
|
max_instances: 1
|
|
13
10
|
timeout: 300s
|
|
14
|
-
|
|
15
|
-
# entry_point: otherExportName
|
|
16
|
-
- requireHostedApiKey
|
|
11
|
+
before_wrappers:
|
|
17
12
|
- allowCrossOriginCallsAndHandleOptions
|
|
13
|
+
- requireHostedApiKey
|
|
14
|
+
# entry_point: otherHandlerName
|
|
18
15
|
|
|
19
16
|
pokemonPokeballThrow:
|
|
20
17
|
max_instances: 1
|
|
21
|
-
|
|
18
|
+
before_wrappers:
|
|
22
19
|
- checkTrainer
|
|
23
20
|
|
|
24
21
|
groups:
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const yaml = require('yaml');
|
|
3
|
+
|
|
4
|
+
const copyCredsToEnv = (workspace) => {
|
|
5
|
+
const normalizedWorkspace = workspace.replace(/\/$/, '');
|
|
6
|
+
const credsPath = `${ normalizedWorkspace }/.creds.yml`;
|
|
7
|
+
const envPath = `${ normalizedWorkspace }/.env`;
|
|
8
|
+
|
|
9
|
+
if (!fs.existsSync(credsPath)) {
|
|
10
|
+
throw new Error(`Missing .creds.yml in workspace: ${ workspace }`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const credsText = fs.readFileSync(credsPath, 'utf8');
|
|
14
|
+
const credsFromYml = yaml.parse(credsText);
|
|
15
|
+
const newCredsLine = `CREDS=${ JSON.stringify(credsFromYml) }`;
|
|
16
|
+
|
|
17
|
+
let envFileContents = '';
|
|
18
|
+
if (fs.existsSync(envPath)) {
|
|
19
|
+
envFileContents = fs.readFileSync(envPath, 'utf8');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!envFileContents) {
|
|
23
|
+
fs.writeFileSync(envPath, `${ newCredsLine }\n`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (/^CREDS=/m.test(envFileContents)) {
|
|
28
|
+
const updatedFileContents = envFileContents.replace(/^CREDS=.*$/m, newCredsLine);
|
|
29
|
+
|
|
30
|
+
if (updatedFileContents !== envFileContents) {
|
|
31
|
+
fs.writeFileSync(envPath, updatedFileContents);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fs.appendFileSync(envPath, `\n\n${ newCredsLine }\n`);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (require.main === module) {
|
|
41
|
+
const workspace = process.argv[2] || process.cwd();
|
|
42
|
+
copyCredsToEnv(workspace);
|
|
43
|
+
console.log('Copied CREDS from .creds.yml to .env');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
copyCredsToEnv,
|
|
48
|
+
};
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
const readline = require('readline');
|
|
2
|
-
const { toAbsolutePath, setWorkspace } = require('../api/workspace');
|
|
2
|
+
const { toAbsolutePath, setWorkspace, loadWorkspaceEnv } = require('../api/workspace');
|
|
3
3
|
const { getApiDirs, readCliFlag } = require('../cli');
|
|
4
4
|
const { loadHandlers } = require('../server');
|
|
5
5
|
const {
|
|
6
6
|
readHostingYml,
|
|
7
|
-
|
|
8
|
-
validateEnvVarsForDeploy,
|
|
7
|
+
ensureWorkspaceEnvForDeploy,
|
|
9
8
|
resolveHostedHandlersForDeploy,
|
|
10
9
|
functionUsesWrapper,
|
|
11
|
-
} = require('
|
|
10
|
+
} = require('./hosting.utils');
|
|
12
11
|
const { writeHostedJs } = require('./generateHosted');
|
|
13
12
|
const { execCommand } = require('./execCommand');
|
|
14
13
|
const { formatSetEnvVarsForGcloud, shellQuoteSingle } = require('./setEnvVarsGcloud');
|
|
@@ -91,8 +90,6 @@ const deployFunction = async ({
|
|
|
91
90
|
functionConfig,
|
|
92
91
|
googleCloudInfo,
|
|
93
92
|
workspace,
|
|
94
|
-
credsJson,
|
|
95
|
-
deployEnvVars = {},
|
|
96
93
|
}) => {
|
|
97
94
|
const config = {
|
|
98
95
|
...googleCloudInfo,
|
|
@@ -107,20 +104,17 @@ const deployFunction = async ({
|
|
|
107
104
|
allow_unauthenticated: allowUnauthenticated = true,
|
|
108
105
|
gen2 = true,
|
|
109
106
|
set_env_vars: extraSetEnvVars,
|
|
110
|
-
entry_point
|
|
107
|
+
entry_point,
|
|
111
108
|
schedules,
|
|
112
109
|
groups,
|
|
113
|
-
|
|
110
|
+
before_wrappers,
|
|
111
|
+
after_wrappers,
|
|
114
112
|
source,
|
|
113
|
+
env,
|
|
115
114
|
...gcloudArgs
|
|
116
115
|
} = config;
|
|
117
116
|
|
|
118
|
-
const
|
|
119
|
-
const envParts = [
|
|
120
|
-
'HOSTED=true',
|
|
121
|
-
`CREDS=${ credsJson }`,
|
|
122
|
-
...Object.entries(deployEnvVars).map(([envName, envValue]) => `${ envName }=${ envValue }`),
|
|
123
|
-
];
|
|
117
|
+
const envParts = ['HOSTED=true'];
|
|
124
118
|
|
|
125
119
|
if (extraSetEnvVars) {
|
|
126
120
|
envParts.push(extraSetEnvVars);
|
|
@@ -134,7 +128,7 @@ const deployFunction = async ({
|
|
|
134
128
|
`--project ${ project }`,
|
|
135
129
|
`--region ${ region }`,
|
|
136
130
|
`--source ${ shellQuoteSingle(workspace) }`,
|
|
137
|
-
`--entry-point ${
|
|
131
|
+
`--entry-point ${ functionName }`,
|
|
138
132
|
`--trigger-${ trigger }`,
|
|
139
133
|
`--runtime ${ runtime }`,
|
|
140
134
|
`--set-env-vars ${ shellQuoteSingle(setEnvVarsForGcloud) }`,
|
|
@@ -157,7 +151,7 @@ const deployFunction = async ({
|
|
|
157
151
|
}
|
|
158
152
|
|
|
159
153
|
const requiresHostedApiKey = functionUsesWrapper(functionConfig, 'requireHostedApiKey');
|
|
160
|
-
const hostedApiKey =
|
|
154
|
+
const hostedApiKey = process.env.HOSTED_API_KEY;
|
|
161
155
|
|
|
162
156
|
for (const schedule of schedules) {
|
|
163
157
|
const {
|
|
@@ -203,17 +197,18 @@ const deployFunction = async ({
|
|
|
203
197
|
const deployFromHostingYml = async (options = {}) => {
|
|
204
198
|
const config = getHostConfig(options);
|
|
205
199
|
setWorkspace(config.workspace);
|
|
200
|
+
ensureWorkspaceEnvForDeploy(config.workspace);
|
|
201
|
+
loadWorkspaceEnv();
|
|
206
202
|
|
|
207
203
|
const hostingConfig = readHostingYml(config.workspace);
|
|
208
204
|
const {
|
|
209
205
|
google_cloud_info: googleCloudInfo,
|
|
210
|
-
env: envNames = [],
|
|
211
206
|
functions = {},
|
|
212
207
|
groups = {},
|
|
213
208
|
} = hostingConfig;
|
|
214
209
|
|
|
215
210
|
if (!googleCloudInfo?.project || !googleCloudInfo?.region) {
|
|
216
|
-
throw new Error('
|
|
211
|
+
throw new Error('hosting/.hosting.yml requires google_cloud_info.project and google_cloud_info.region');
|
|
217
212
|
}
|
|
218
213
|
|
|
219
214
|
const handlers = loadHandlers({
|
|
@@ -227,19 +222,16 @@ const deployFromHostingYml = async (options = {}) => {
|
|
|
227
222
|
handlersByName,
|
|
228
223
|
});
|
|
229
224
|
|
|
230
|
-
const deployEnvVars = validateEnvVarsForDeploy(config.workspace, envNames);
|
|
231
|
-
|
|
232
225
|
writeHostedJs({
|
|
233
226
|
workspace: config.workspace,
|
|
234
227
|
hostedHandlers,
|
|
235
228
|
});
|
|
236
229
|
|
|
237
|
-
const credsJson = getCredsJsonForDeploy(config.workspace);
|
|
238
230
|
const deployArgs = getDeployArgs();
|
|
239
231
|
|
|
240
232
|
const deployOne = async (functionName) => {
|
|
241
233
|
if (!functions[functionName]) {
|
|
242
|
-
console.log(`Function ${ functionName } not found in
|
|
234
|
+
console.log(`Function ${ functionName } not found in hosting/.hosting.yml, skipping`);
|
|
243
235
|
return;
|
|
244
236
|
}
|
|
245
237
|
|
|
@@ -248,15 +240,13 @@ const deployFromHostingYml = async (options = {}) => {
|
|
|
248
240
|
functionConfig: functions[functionName],
|
|
249
241
|
googleCloudInfo,
|
|
250
242
|
workspace: config.workspace,
|
|
251
|
-
credsJson,
|
|
252
|
-
deployEnvVars,
|
|
253
243
|
});
|
|
254
244
|
};
|
|
255
245
|
|
|
256
246
|
if (deployArgs.includes('group')) {
|
|
257
247
|
const groupNames = Object.keys(groups);
|
|
258
248
|
if (!groupNames.length) {
|
|
259
|
-
console.log('No groups defined in
|
|
249
|
+
console.log('No groups defined in hosting/.hosting.yml');
|
|
260
250
|
return;
|
|
261
251
|
}
|
|
262
252
|
|
|
@@ -270,7 +260,7 @@ const deployFromHostingYml = async (options = {}) => {
|
|
|
270
260
|
if (deployArgs.includes('function')) {
|
|
271
261
|
const functionNames = Object.keys(functions);
|
|
272
262
|
if (!functionNames.length) {
|
|
273
|
-
console.log('No functions defined in
|
|
263
|
+
console.log('No functions defined in hosting/.hosting.yml');
|
|
274
264
|
return;
|
|
275
265
|
}
|
|
276
266
|
|
|
@@ -305,14 +295,3 @@ module.exports = {
|
|
|
305
295
|
deployFromHostingYml,
|
|
306
296
|
getHostConfig,
|
|
307
297
|
};
|
|
308
|
-
|
|
309
|
-
/*
|
|
310
|
-
Deploy everything:
|
|
311
|
-
mineral host all
|
|
312
|
-
|
|
313
|
-
Deploy a single function:
|
|
314
|
-
mineral host function
|
|
315
|
-
|
|
316
|
-
Deploy a group of functions:
|
|
317
|
-
mineral host group
|
|
318
|
-
*/
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { wrapHostedFunction } = require('./hosting.utils');
|
|
3
|
+
|
|
4
|
+
const formatResolvedWrapperForHostedJs = ({ modulePath, wrapperName }) => (
|
|
5
|
+
`require('${ modulePath }').${ wrapperName }`
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
const formatWrapperList = (resolvedWrappers = []) => {
|
|
9
|
+
if (!resolvedWrappers.length) {
|
|
10
|
+
return '';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const wrapperLines = resolvedWrappers.map((resolvedWrapper) => (
|
|
14
|
+
` ${ formatResolvedWrapperForHostedJs(resolvedWrapper) },`
|
|
15
|
+
));
|
|
16
|
+
|
|
17
|
+
return `\n${ wrapperLines.join('\n') }\n `;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const formatWrappersArg = ({
|
|
21
|
+
resolvedBeforeWrappers = [],
|
|
22
|
+
resolvedAfterWrappers = [],
|
|
23
|
+
} = {}) => {
|
|
24
|
+
if (!resolvedBeforeWrappers.length && !resolvedAfterWrappers.length) {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const beforeWrappersBlock = formatWrapperList(resolvedBeforeWrappers);
|
|
29
|
+
const afterWrappersBlock = formatWrapperList(resolvedAfterWrappers);
|
|
30
|
+
|
|
31
|
+
return `, {
|
|
32
|
+
beforeWrappers: [${ beforeWrappersBlock }],
|
|
33
|
+
afterWrappers: [${ afterWrappersBlock }],
|
|
34
|
+
}`;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const generateHostedJs = ({
|
|
38
|
+
hostedHandlers,
|
|
39
|
+
}) => {
|
|
40
|
+
const exportLines = [];
|
|
41
|
+
|
|
42
|
+
for (const hostedHandler of hostedHandlers) {
|
|
43
|
+
const {
|
|
44
|
+
hostedName,
|
|
45
|
+
handlerName,
|
|
46
|
+
resolvedBeforeWrappers = [],
|
|
47
|
+
resolvedAfterWrappers = [],
|
|
48
|
+
requirePath,
|
|
49
|
+
} = hostedHandler;
|
|
50
|
+
const wrappersArg = formatWrappersArg({
|
|
51
|
+
resolvedBeforeWrappers,
|
|
52
|
+
resolvedAfterWrappers,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
exportLines.push(
|
|
56
|
+
` ${ hostedName }: wrapHostedFunction(() => require('${ requirePath }'), '${ handlerName }'${ wrappersArg }),`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return `// Generated by mineral — do not edit
|
|
61
|
+
const { wrapHostedFunction } = require('@foxtware/mineral/hosting/hosting.utils');
|
|
62
|
+
|
|
63
|
+
module.exports = {
|
|
64
|
+
${ exportLines.join('\n') }
|
|
65
|
+
};
|
|
66
|
+
`;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const writeHostedJs = ({
|
|
70
|
+
workspace,
|
|
71
|
+
hostedHandlers,
|
|
72
|
+
}) => {
|
|
73
|
+
const { getHostingDir } = require('./hosting.utils');
|
|
74
|
+
const hostedPath = `${ getHostingDir(workspace) }/hosted.js`;
|
|
75
|
+
const content = generateHostedJs({
|
|
76
|
+
hostedHandlers,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
fs.mkdirSync(getHostingDir(workspace), { recursive: true });
|
|
80
|
+
fs.writeFileSync(hostedPath, content);
|
|
81
|
+
return hostedPath;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
generateHostedJs,
|
|
86
|
+
writeHostedJs,
|
|
87
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const dotenv = require('dotenv');
|
|
2
4
|
const { createRequire } = require('module');
|
|
3
5
|
const yaml = require('yaml');
|
|
4
6
|
const { getRequirePathForHandler } = require('./handlerPaths');
|
|
@@ -10,10 +12,16 @@ const {
|
|
|
10
12
|
funcApi,
|
|
11
13
|
wrapFunction,
|
|
12
14
|
statusCodeFromResult,
|
|
13
|
-
} = require('
|
|
15
|
+
} = require('../server.utils');
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
dotenv.config({
|
|
18
|
+
path: path.join(process.cwd(), '.env'),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const MINERAL_WRAPPERS_MODULE = '@foxtware/mineral/hosting/wrappers.js';
|
|
22
|
+
const WORKSPACE_WRAPPERS_MODULE = './hosting/wrappers.js';
|
|
23
|
+
|
|
24
|
+
const getHostingDir = (workspace) => `${ workspace.replace(/\/$/, '') }/hosting`;
|
|
17
25
|
|
|
18
26
|
const getMineralWrappers = (workspaceRequire) => {
|
|
19
27
|
try {
|
|
@@ -63,13 +71,16 @@ const getHostedEntries = (functions = {}) => (
|
|
|
63
71
|
Object.entries(functions).map(([hostedName, functionConfig = {}]) => ({
|
|
64
72
|
hostedName,
|
|
65
73
|
handlerName: functionConfig.entry_point || functionConfig.entryPoint || hostedName,
|
|
66
|
-
|
|
74
|
+
beforeWrappers: Array.isArray(functionConfig.before_wrappers) ? functionConfig.before_wrappers : [],
|
|
75
|
+
afterWrappers: Array.isArray(functionConfig.after_wrappers) ? functionConfig.after_wrappers : [],
|
|
67
76
|
}))
|
|
68
77
|
);
|
|
69
78
|
|
|
70
|
-
const functionUsesWrapper = (functionConfig = {}, wrapperName) =>
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
const functionUsesWrapper = (functionConfig = {}, wrapperName) => {
|
|
80
|
+
const { before_wrappers = [], after_wrappers = [] } = functionConfig;
|
|
81
|
+
|
|
82
|
+
return before_wrappers.includes(wrapperName) || after_wrappers.includes(wrapperName);
|
|
83
|
+
};
|
|
73
84
|
|
|
74
85
|
const getFuncApiConfig = ({
|
|
75
86
|
moduleExports,
|
|
@@ -92,7 +103,10 @@ const getFuncApiConfig = ({
|
|
|
92
103
|
}
|
|
93
104
|
};
|
|
94
105
|
|
|
95
|
-
const wrapHostedFunction = (loader, exportName,
|
|
106
|
+
const wrapHostedFunction = (loader, exportName, {
|
|
107
|
+
beforeWrappers = [],
|
|
108
|
+
afterWrappers = [],
|
|
109
|
+
} = {}) => {
|
|
96
110
|
let handler = null;
|
|
97
111
|
let usesFuncApi = false;
|
|
98
112
|
|
|
@@ -121,7 +135,10 @@ const wrapHostedFunction = (loader, exportName, wrappers = []) => {
|
|
|
121
135
|
: await handler(...args);
|
|
122
136
|
};
|
|
123
137
|
|
|
124
|
-
const wrappedHandler = wrapFunction(coreHandler,
|
|
138
|
+
const wrappedHandler = wrapFunction(coreHandler, {
|
|
139
|
+
beforeWrappers,
|
|
140
|
+
afterWrappers,
|
|
141
|
+
});
|
|
125
142
|
|
|
126
143
|
return async (req, res) => {
|
|
127
144
|
try {
|
|
@@ -156,60 +173,31 @@ const wrapHostedFunction = (loader, exportName, wrappers = []) => {
|
|
|
156
173
|
};
|
|
157
174
|
|
|
158
175
|
const readHostingYml = (workspace) => {
|
|
159
|
-
const hostingPath = `${ workspace }/.hosting.yml`;
|
|
176
|
+
const hostingPath = `${ getHostingDir(workspace) }/.hosting.yml`;
|
|
160
177
|
|
|
161
178
|
if (!fs.existsSync(hostingPath)) {
|
|
162
|
-
throw new Error(`Missing
|
|
179
|
+
throw new Error(`Missing hosting/.hosting.yml in workspace: ${ workspace }`);
|
|
163
180
|
}
|
|
164
181
|
|
|
165
182
|
const hostingText = fs.readFileSync(hostingPath, 'utf8');
|
|
166
183
|
const hostingConfig = yaml.parse(hostingText);
|
|
167
184
|
|
|
168
185
|
if (!hostingConfig || typeof hostingConfig !== 'object' || Array.isArray(hostingConfig)) {
|
|
169
|
-
throw new Error('Invalid
|
|
186
|
+
throw new Error('Invalid hosting/.hosting.yml');
|
|
170
187
|
}
|
|
171
188
|
|
|
172
189
|
return hostingConfig;
|
|
173
190
|
};
|
|
174
191
|
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
if (!fs.existsSync(credsPath)) {
|
|
179
|
-
throw new Error(`Missing .creds.yml in workspace: ${ workspace }`);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const credsText = fs.readFileSync(credsPath, 'utf8');
|
|
183
|
-
return JSON.stringify(yaml.parse(credsText));
|
|
184
|
-
};
|
|
192
|
+
const ensureWorkspaceEnvForDeploy = (workspace) => {
|
|
193
|
+
const { copyCredsToEnv } = require('./copyCredsToEnv');
|
|
194
|
+
const envPath = `${ workspace.replace(/\/$/, '') }/.env`;
|
|
185
195
|
|
|
186
|
-
|
|
187
|
-
const envPath = `${ workspace }/.env`;
|
|
196
|
+
copyCredsToEnv(workspace);
|
|
188
197
|
|
|
189
198
|
if (!fs.existsSync(envPath)) {
|
|
190
|
-
|
|
199
|
+
throw new Error(`Missing .env in workspace: ${ workspace }`);
|
|
191
200
|
}
|
|
192
|
-
|
|
193
|
-
const envText = fs.readFileSync(envPath, 'utf8');
|
|
194
|
-
const match = envText.match(new RegExp(`^${ envName }=(.*)$`, 'm'));
|
|
195
|
-
return match ? match[1].trim() : '';
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const getEnvVarsForDeploy = (workspace, envNames = []) => (
|
|
199
|
-
Object.fromEntries(
|
|
200
|
-
envNames.map((envName) => [envName, getEnvValueForDeploy(workspace, envName)]),
|
|
201
|
-
)
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
const validateEnvVarsForDeploy = (workspace, envNames = []) => {
|
|
205
|
-
const envVars = getEnvVarsForDeploy(workspace, envNames);
|
|
206
|
-
const missing = envNames.filter((envName) => !envVars[envName]);
|
|
207
|
-
|
|
208
|
-
if (missing.length) {
|
|
209
|
-
throw new Error(`Missing required .env values: ${ missing.join(', ') }`);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return envVars;
|
|
213
201
|
};
|
|
214
202
|
|
|
215
203
|
const resolveHostedHandlersForDeploy = ({
|
|
@@ -221,9 +209,16 @@ const resolveHostedHandlersForDeploy = ({
|
|
|
221
209
|
const hostedEntries = getHostedEntries(functions);
|
|
222
210
|
|
|
223
211
|
return hostedEntries.map((hostedEntry) => {
|
|
224
|
-
const {
|
|
212
|
+
const {
|
|
213
|
+
handlerName,
|
|
214
|
+
beforeWrappers = [],
|
|
215
|
+
afterWrappers = [],
|
|
216
|
+
} = hostedEntry;
|
|
225
217
|
|
|
226
|
-
const
|
|
218
|
+
const resolvedBeforeWrappers = beforeWrappers.map((wrapperName) => (
|
|
219
|
+
resolveWrapperName(wrapperName, workspaceRequire)
|
|
220
|
+
));
|
|
221
|
+
const resolvedAfterWrappers = afterWrappers.map((wrapperName) => (
|
|
227
222
|
resolveWrapperName(wrapperName, workspaceRequire)
|
|
228
223
|
));
|
|
229
224
|
|
|
@@ -236,7 +231,8 @@ const resolveHostedHandlersForDeploy = ({
|
|
|
236
231
|
|
|
237
232
|
return {
|
|
238
233
|
...hostedEntry,
|
|
239
|
-
|
|
234
|
+
resolvedBeforeWrappers,
|
|
235
|
+
resolvedAfterWrappers,
|
|
240
236
|
requirePath: getRequirePathForHandler(handler, workspace),
|
|
241
237
|
};
|
|
242
238
|
});
|
|
@@ -245,13 +241,12 @@ const resolveHostedHandlersForDeploy = ({
|
|
|
245
241
|
// TODO: support credsPayload in google_cloud_info instead of full workspace .creds.yml
|
|
246
242
|
|
|
247
243
|
module.exports = {
|
|
244
|
+
getHostingDir,
|
|
248
245
|
resolveWrapperName,
|
|
249
246
|
getFuncApiConfig,
|
|
250
247
|
wrapHostedFunction,
|
|
251
248
|
readHostingYml,
|
|
252
|
-
|
|
253
|
-
getEnvVarsForDeploy,
|
|
254
|
-
validateEnvVarsForDeploy,
|
|
249
|
+
ensureWorkspaceEnvForDeploy,
|
|
255
250
|
getHostedEntries,
|
|
256
251
|
resolveHostedHandlersForDeploy,
|
|
257
252
|
functionUsesWrapper,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { HOSTED } = require('
|
|
1
|
+
const { HOSTED } = require('../api/constants');
|
|
2
2
|
|
|
3
3
|
const requireHostedApiKey = async (req) => {
|
|
4
4
|
if (!HOSTED) {
|
|
@@ -22,7 +22,7 @@ const allowCrossOriginCallsAndHandleOptions = async (req, res) => {
|
|
|
22
22
|
|
|
23
23
|
res.setHeader('Access-Control-Allow-Origin', origin || '*');
|
|
24
24
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
25
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, x-api-key, x-wf-token, x-wf-value');
|
|
25
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-api-key, x-wf-token, x-wf-value');
|
|
26
26
|
|
|
27
27
|
if (req.method === 'OPTIONS') {
|
|
28
28
|
res.writeHead(204);
|