@whook/gcp-functions 18.1.0 → 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +16 -54
  2. package/dist/commands/testGCPFunctionRoute.d.ts +50 -0
  3. package/dist/commands/{testHTTPFunction.js → testGCPFunctionRoute.js} +43 -44
  4. package/dist/commands/testGCPFunctionRoute.js.map +1 -0
  5. package/dist/index.d.ts +7 -6
  6. package/dist/index.js +49 -52
  7. package/dist/index.js.map +1 -1
  8. package/dist/services/_autoload.d.ts +14 -9
  9. package/dist/services/_autoload.js +49 -62
  10. package/dist/services/_autoload.js.map +1 -1
  11. package/dist/wrappers/wrapRouteHandlerForGoogleHTTPFunction.d.ts +19 -0
  12. package/dist/wrappers/wrapRouteHandlerForGoogleHTTPFunction.js +307 -0
  13. package/dist/wrappers/wrapRouteHandlerForGoogleHTTPFunction.js.map +1 -0
  14. package/package.json +10 -8
  15. package/src/commands/{testHTTPFunction.ts → testGCPFunctionRoute.ts} +68 -69
  16. package/src/index.ts +85 -87
  17. package/src/services/_autoload.ts +79 -106
  18. package/src/types.d.ts +8 -0
  19. package/src/wrappers/wrapRouteHandlerForGoogleHTTPFunction.ts +571 -0
  20. package/dist/commands/testHTTPFunction.d.ts +0 -13
  21. package/dist/commands/testHTTPFunction.js.map +0 -1
  22. package/dist/services/HANDLER.d.ts +0 -10
  23. package/dist/services/HANDLER.js +0 -21
  24. package/dist/services/HANDLER.js.map +0 -1
  25. package/dist/services/log.d.ts +0 -5
  26. package/dist/services/log.js +0 -4
  27. package/dist/services/log.js.map +0 -1
  28. package/dist/wrappers/wrapHandlerForGoogleHTTPFunction.d.ts +0 -21
  29. package/dist/wrappers/wrapHandlerForGoogleHTTPFunction.js +0 -270
  30. package/dist/wrappers/wrapHandlerForGoogleHTTPFunction.js.map +0 -1
  31. package/src/services/HANDLER.ts +0 -47
  32. package/src/services/log.ts +0 -7
  33. package/src/wrappers/wrapHandlerForGoogleHTTPFunction.ts +0 -480
@@ -1,81 +1,84 @@
1
1
  import { loadFunction } from '../libs/utils.js';
2
- import { extra, autoService } from 'knifecycle';
2
+ import { location, autoService } from 'knifecycle';
3
3
  import { YError } from 'yerror';
4
4
  import stream from 'node:stream';
5
- import camelCase from 'camelcase';
6
5
  import {
7
- dereferenceOpenAPIOperations,
8
- getOpenAPIOperations,
9
6
  DEFAULT_COMPILER_OPTIONS,
10
- readArgs,
11
- type WhookCommandArgs,
7
+ PATH_SEPARATOR,
8
+ SEARCH_SEPARATOR,
9
+ type WhookRouteHandlerParameters,
10
+ type WhookCommandHandler,
12
11
  type WhookCommandDefinition,
13
12
  type WhookCompilerOptions,
13
+ type WhookRoutesDefinitionsService,
14
14
  } from '@whook/whook';
15
15
  import { type LogService } from 'common-services';
16
- import { type OpenAPIV3_1 } from 'openapi-types';
17
16
 
18
- const SEARCH_SEPARATOR = '?';
19
- const PATH_SEPARATOR = '/';
20
-
21
- export const definition: WhookCommandDefinition = {
22
- description: 'A command for testing GCP HTTP function',
23
- example: `whook testHTTPFunction --name getPing`,
24
- arguments: {
25
- type: 'object',
26
- additionalProperties: false,
27
- required: ['name'],
28
- properties: {
29
- name: {
30
- description: 'Name of the function to run',
17
+ export const definition = {
18
+ name: 'testGCPFunctionRoute',
19
+ description: 'A command for testing a GCP function route',
20
+ example: `whook testGCPFunctionRoute --name getPing`,
21
+ arguments: [
22
+ {
23
+ description: 'Name of the function to run',
24
+ name: 'name',
25
+ required: true,
26
+ schema: {
31
27
  type: 'string',
32
28
  },
33
- type: {
34
- description: 'Type of function to test',
29
+ },
30
+ {
31
+ name: 'type',
32
+ description: 'Type of function to test',
33
+ schema: {
35
34
  type: 'string',
36
35
  enum: ['main', 'index'],
37
36
  default: 'index',
38
37
  },
39
- contentType: {
40
- description: 'Content type of the payload',
38
+ },
39
+ {
40
+ name: 'contentType',
41
+ description: 'Content type of the payload',
42
+ schema: {
41
43
  type: 'string',
42
44
  default: 'application/json',
43
45
  },
44
- parameters: {
45
- description: 'The HTTP call parameters',
46
+ },
47
+ {
48
+ name: 'parameters',
49
+ description: 'The HTTP call parameters',
50
+ schema: {
46
51
  type: 'string',
47
52
  default: '{}',
48
53
  },
49
54
  },
50
- },
51
- };
52
-
53
- export default extra(definition, autoService(initTestHTTPFunctionCommand));
55
+ ],
56
+ } as const satisfies WhookCommandDefinition;
54
57
 
55
- async function initTestHTTPFunctionCommand({
58
+ async function initTestGCPFunctionRouteCommand({
56
59
  APP_ENV,
57
60
  PROJECT_DIR,
58
61
  COMPILER_OPTIONS = DEFAULT_COMPILER_OPTIONS,
59
- API,
62
+ ROUTES_DEFINITIONS,
60
63
  log,
61
- args,
62
64
  }: {
63
65
  APP_ENV: string;
64
66
  PROJECT_DIR: string;
65
67
  COMPILER_OPTIONS?: WhookCompilerOptions;
66
- API: OpenAPIV3_1.Document;
68
+ ROUTES_DEFINITIONS: WhookRoutesDefinitionsService;
67
69
  log: LogService;
68
- args: WhookCommandArgs;
69
- }) {
70
- return async () => {
70
+ }): Promise<
71
+ WhookCommandHandler<{
72
+ name: string;
73
+ type: string;
74
+ contentType: string;
75
+ parameters: string;
76
+ }>
77
+ > {
78
+ return async (args) => {
71
79
  const {
72
80
  namedArguments: { name, type, contentType, parameters: rawParameters },
73
- } = readArgs<{
74
- name: string;
75
- type: string;
76
- contentType: string;
77
- parameters: string;
78
- }>(definition.arguments, args);
81
+ } = args;
79
82
  const extension = COMPILER_OPTIONS.format === 'cjs' ? '.cjs' : '.mjs';
80
83
  const handler = await loadFunction(
81
84
  { APP_ENV, PROJECT_DIR, log },
@@ -83,61 +86,52 @@ async function initTestHTTPFunctionCommand({
83
86
  type,
84
87
  extension,
85
88
  );
86
- const OPERATION = (
87
- await dereferenceOpenAPIOperations(API, getOpenAPIOperations(API))
88
- ).find(({ operationId }) => operationId === name);
89
+ const handlerDefinition = ROUTES_DEFINITIONS[name]?.module?.definition;
89
90
 
90
- if (!OPERATION) {
91
+ if (!handlerDefinition) {
91
92
  throw new YError('E_OPERATION_NOT_FOUND');
92
93
  }
93
94
 
94
- const hasBody = !!OPERATION.requestBody;
95
- const parameters = JSON.parse(rawParameters);
96
- const search = (
97
- (OPERATION.parameters || []) as OpenAPIV3_1.ParameterObject[]
98
- )
99
- .filter((p) => p.in === 'query')
100
- .reduce((accSearch, p) => {
101
- if (null != parameters[p.name]) {
95
+ const hasBody = !!handlerDefinition.operation.requestBody;
96
+ const parameters = JSON.parse(rawParameters) as WhookRouteHandlerParameters;
97
+ const search = Object.keys(parameters.query || {}).reduce(
98
+ (accSearch, name) => {
99
+ if (null != parameters.query[name]) {
102
100
  return (
103
101
  accSearch +
104
102
  (accSearch ? '&' : '') +
105
- p.name +
103
+ name +
106
104
  '=' +
107
- parameters[p.name]
105
+ parameters.query[name]
108
106
  );
109
107
  }
110
108
  return accSearch;
111
- }, '');
109
+ },
110
+ '',
111
+ );
112
112
 
113
- const path = OPERATION.path
113
+ const path = handlerDefinition.path
114
114
  .split(PATH_SEPARATOR)
115
-
116
115
  .map((part) => {
117
116
  const matches = /^\{([\d\w]+)\}$/i.exec(part);
118
117
 
119
118
  if (matches) {
120
- return parameters[matches[1]];
119
+ return parameters.path?.[matches[1]];
121
120
  }
122
121
  return part;
123
122
  })
124
123
  .join(PATH_SEPARATOR);
125
124
  const gcpfRequest = {
126
- method: OPERATION.method,
125
+ method: handlerDefinition.method,
127
126
  originalUrl: path + (search ? SEARCH_SEPARATOR + search : ''),
128
- headers: ((OPERATION.parameters || []) as OpenAPIV3_1.ParameterObject[])
129
- .filter((p) => p.in === 'header')
130
- .reduce((headerParameters, p) => {
131
- headerParameters[p.name] = parameters[camelCase(p.name)];
132
- return headerParameters;
133
- }, {}),
127
+ headers: parameters.header || {},
134
128
  rawBody: Buffer.from(
135
129
  hasBody
136
130
  ? contentType === 'application/json'
137
131
  ? parameters.body
138
132
  ? JSON.stringify(parameters.body)
139
133
  : ''
140
- : parameters.body || ''
134
+ : (parameters.body as string) || ''
141
135
  : '',
142
136
  ),
143
137
  };
@@ -182,3 +176,8 @@ async function initTestHTTPFunctionCommand({
182
176
  log('info', 'SUCCESS:', response);
183
177
  };
184
178
  }
179
+
180
+ export default location(
181
+ autoService(initTestGCPFunctionRouteCommand),
182
+ import.meta.url,
183
+ );
package/src/index.ts CHANGED
@@ -20,33 +20,32 @@ import initBuildAutoloader from './services/_autoload.js';
20
20
  import {
21
21
  DEFAULT_BUILD_INITIALIZER_PATH_MAP as BASE_DEFAULT_BUILD_INITIALIZER_PATH_MAP,
22
22
  initCompiler,
23
- dereferenceOpenAPIOperations,
24
- getOpenAPIOperations,
25
- type WhookOperation,
23
+ type WhookDefinitions,
26
24
  type WhookCompilerOptions,
27
25
  type WhookCompilerService,
28
26
  } from '@whook/whook';
29
- import { type OpenAPIV3_1 } from 'openapi-types';
30
27
  import { type LogService } from 'common-services';
31
28
  import { type CprOptions } from 'cpr';
32
29
  import { parseArgs } from '@whook/whook/dist/libs/args.js';
30
+ import initWrapRouteHandlerForGoogleHTTPFunction from './wrappers/wrapRouteHandlerForGoogleHTTPFunction.js';
33
31
 
34
32
  export const DEFAULT_BUILD_PARALLELISM = 10;
35
33
  export const DEFAULT_BUILD_INITIALIZER_PATH_MAP = {
36
34
  ...BASE_DEFAULT_BUILD_INITIALIZER_PATH_MAP,
37
- log: '@whook/gcp-functions/dist/services/log.js',
35
+ log: '@whook/whook/dist/services/rawLog.js',
38
36
  };
39
37
 
40
- export type WhookGCPBuildConfig = {
38
+ export type * from './wrappers/wrapRouteHandlerForGoogleHTTPFunction.js';
39
+ export { initWrapRouteHandlerForGoogleHTTPFunction };
40
+
41
+ export type WhookGCPFunctionBuildConfig = {
41
42
  BUILD_PARALLELISM?: number;
42
43
  };
43
- export type WhookAPIOperationGCPFunctionConfig = {
44
- type?: 'http';
45
- sourceOperationId?: string;
44
+ export type WhookGCPFunctionBaseConfig = {
46
45
  staticFiles?: string[];
47
46
  compilerOptions?: WhookCompilerOptions;
48
- suffix?: string;
49
47
  };
48
+ export type WhookGCPFunctionRouteConfig = WhookGCPFunctionBaseConfig;
50
49
 
51
50
  const cprAsync = promisify(cpr) as (
52
51
  source: string,
@@ -83,15 +82,15 @@ export async function runBuild(
83
82
  compiler,
84
83
  log,
85
84
  $autoload,
86
- API,
85
+ DEFINITIONS,
87
86
  buildInitializer,
88
- }: WhookGCPBuildConfig & {
87
+ }: WhookGCPFunctionBuildConfig & {
89
88
  APP_ENV: string;
90
89
  PROJECT_DIR: string;
91
90
  compiler: WhookCompilerService;
92
91
  log: LogService;
93
92
  $autoload: Autoloader<Initializer<Dependencies, Service>>;
94
- API: OpenAPIV3_1.Document;
93
+ DEFINITIONS: WhookDefinitions;
95
94
  buildInitializer: BuildInitializer;
96
95
  } = await $.run([
97
96
  'APP_ENV',
@@ -101,43 +100,30 @@ export async function runBuild(
101
100
  'compiler',
102
101
  'log',
103
102
  '$autoload',
104
- 'API',
103
+ 'DEFINITIONS',
105
104
  'buildInitializer',
106
105
  ]);
107
106
 
108
107
  log('info', 'GCP Functions build Environment initialized 🚀🌕');
109
108
 
110
- const operations = (
111
- await dereferenceOpenAPIOperations(
112
- API,
113
- getOpenAPIOperations<WhookAPIOperationGCPFunctionConfig>(API),
114
- )
115
- ).filter((operation) => {
116
- if (handlerName) {
117
- const sourceOperationId =
118
- operation['x-whook'] && operation['x-whook'].sourceOperationId;
119
-
120
- return (
121
- handlerName === operation.operationId ||
122
- handlerName === sourceOperationId
123
- );
124
- }
125
- return true;
126
- });
109
+ const handlerNames = Object.keys(DEFINITIONS.configs).filter(
110
+ (aHandlerName) => handlerName === aHandlerName || !handlerName,
111
+ );
127
112
 
128
- log('warning', `📃 - ${operations.length} operations to process.`);
113
+ log('warning', `📃 - ${handlerNames.length} handlerNames to process.`);
129
114
 
130
- await processOperations(
115
+ await processHandlers(
131
116
  {
132
117
  APP_ENV,
133
118
  BUILD_PARALLELISM: BUILD_PARALLELISM || DEFAULT_BUILD_PARALLELISM,
134
119
  PROJECT_DIR,
120
+ DEFINITIONS,
135
121
  compiler,
136
122
  log,
137
123
  $autoload,
138
124
  buildInitializer,
139
125
  },
140
- operations,
126
+ handlerNames,
141
127
  );
142
128
  await $.destroy();
143
129
  } catch (err) {
@@ -148,124 +134,128 @@ export async function runBuild(
148
134
  }
149
135
  }
150
136
 
151
- async function processOperations(
137
+ async function processHandlers(
152
138
  {
153
139
  APP_ENV,
154
140
  BUILD_PARALLELISM,
155
141
  PROJECT_DIR,
142
+ DEFINITIONS,
156
143
  compiler,
157
144
  log,
158
145
  $autoload,
159
146
  buildInitializer,
160
- }: WhookGCPBuildConfig & {
147
+ }: WhookGCPFunctionBuildConfig & {
161
148
  APP_ENV: string;
162
149
  PROJECT_DIR: string;
150
+ DEFINITIONS: WhookDefinitions;
163
151
  compiler: WhookCompilerService;
164
152
  log: LogService;
165
153
  $autoload: Autoloader<Initializer<Dependencies, Service>>;
166
154
  buildInitializer: BuildInitializer;
167
155
  },
168
- operations: WhookOperation<WhookAPIOperationGCPFunctionConfig>[],
156
+ handlerNames: string[],
169
157
  ): Promise<void> {
170
- const operationsLeft = operations.slice(BUILD_PARALLELISM);
158
+ const handlerNamesLeft = handlerNames.slice(BUILD_PARALLELISM);
171
159
 
172
160
  await Promise.all(
173
- operations.slice(0, BUILD_PARALLELISM).map((operation) =>
174
- buildAnyLambda(
161
+ handlerNames.slice(0, BUILD_PARALLELISM).map((handlerName) =>
162
+ buildHandler(
175
163
  {
176
164
  APP_ENV,
177
165
  PROJECT_DIR,
166
+ DEFINITIONS,
178
167
  compiler,
179
168
  log,
180
169
  buildInitializer,
181
170
  },
182
- operation,
171
+ handlerName,
183
172
  ),
184
173
  ),
185
174
  );
186
175
 
187
- if (operationsLeft.length) {
188
- log('info', `📃 - ${operationsLeft.length} operations left.`);
189
- return processOperations(
176
+ if (handlerNamesLeft.length) {
177
+ log('info', `📃 - ${handlerNamesLeft.length} handlerNames left.`);
178
+ return processHandlers(
190
179
  {
191
180
  APP_ENV,
192
181
  BUILD_PARALLELISM,
193
182
  PROJECT_DIR,
183
+ DEFINITIONS,
194
184
  compiler,
195
185
  log,
196
186
  $autoload,
197
187
  buildInitializer,
198
188
  },
199
- operationsLeft,
189
+ handlerNamesLeft,
200
190
  );
201
191
  }
202
- log('info', '🤷 - No more operations.');
192
+ log('info', '🤷 - No more handlerNames.');
203
193
  }
204
194
 
205
- async function buildAnyLambda(
195
+ async function buildHandler(
206
196
  {
207
197
  APP_ENV,
208
198
  PROJECT_DIR,
199
+ DEFINITIONS,
209
200
  compiler,
210
201
  log,
211
202
  buildInitializer,
212
203
  }: {
213
204
  APP_ENV: string;
214
205
  PROJECT_DIR: string;
206
+ DEFINITIONS: WhookDefinitions;
215
207
  compiler: WhookCompilerService;
216
208
  log: LogService;
217
209
  buildInitializer: BuildInitializer;
218
210
  },
219
- operation: WhookOperation<WhookAPIOperationGCPFunctionConfig>,
211
+ handlerName: string,
220
212
  ): Promise<void> {
221
- const { operationId } = operation;
222
-
223
213
  try {
224
- const whookConfig: WhookAPIOperationGCPFunctionConfig = operation[
225
- 'x-whook'
226
- ] || { type: 'http' };
227
- const operationType = whookConfig.type || 'http';
228
- const sourceOperationId = whookConfig.sourceOperationId;
229
- const finalEntryPoint =
230
- (sourceOperationId ? sourceOperationId : operationId) +
231
- ((operation['x-whook'] || {}).suffix || '');
232
-
233
- log('warning', `🏗 - Building ${operationType} "${finalEntryPoint}"...`);
234
-
235
- const lambdaPath = join(PROJECT_DIR, 'builds', APP_ENV, finalEntryPoint);
214
+ const definition = DEFINITIONS.configs[handlerName];
215
+
216
+ if (definition.type !== 'route') {
217
+ log('warning', `🚮 - Skipping "${handlerName}"...`);
218
+ return;
219
+ }
220
+
221
+ log('warning', `🏗 - Building "${handlerName}"...`);
222
+
223
+ const handlerPath = join(PROJECT_DIR, 'builds', APP_ENV, handlerName);
236
224
  const srcPath = join(PROJECT_DIR, 'src');
237
- const srcRelativePath = relative(lambdaPath, srcPath);
225
+ const srcRelativePath = relative(handlerPath, srcPath);
238
226
 
239
227
  const initializerContent = (
240
- await buildInitializer([`OPERATION_HANDLER_${finalEntryPoint}`])
228
+ await buildInitializer([`MAIN_HANDLER_${handlerName}`])
241
229
  ).replaceAll(pathToFileURL(srcPath).toString(), srcRelativePath);
242
- const indexContent = await buildLambdaIndex(
243
- `OPERATION_HANDLER_${finalEntryPoint}`,
244
- );
230
+ const indexContent = await buildHandlerIndex(`MAIN_HANDLER_${handlerName}`);
245
231
 
246
- await mkdirp(lambdaPath);
232
+ await mkdirp(handlerPath);
247
233
  await Promise.all([
248
234
  copyStaticFiles(
249
235
  { PROJECT_DIR, log },
250
- lambdaPath,
251
- whookConfig.staticFiles || [],
236
+ handlerPath,
237
+ definition.config?.staticFiles || [],
252
238
  ),
253
239
  ensureFile(
254
240
  { log },
255
- join(lambdaPath, 'initialize.js'),
241
+ join(handlerPath, 'initialize.js'),
256
242
  initializerContent,
257
243
  ),
258
- ensureFile({ log }, join(lambdaPath, 'main.js'), indexContent),
244
+ ensureFile({ log }, join(handlerPath, 'main.js'), indexContent),
259
245
  ]);
260
- await buildFinalLambda({ compiler, log }, lambdaPath, whookConfig);
246
+ await buildFinalHandler(
247
+ { DEFINITIONS, compiler, log },
248
+ handlerPath,
249
+ handlerName,
250
+ );
261
251
  } catch (err) {
262
- log('error', `Error building "${operationId}"...`);
252
+ log('error', `💥 - Error building "${handlerName}"...`);
263
253
  log('error-stack', printStackTrace(err as Error));
264
- throw YError.wrap(err as Error, 'E_LAMBDA_BUILD', operationId);
254
+ throw YError.wrap(err as Error, 'E_HANDLER_BUILD', handlerName);
265
255
  }
266
256
  }
267
257
 
268
- async function buildLambdaIndex(name: string): Promise<string> {
258
+ async function buildHandlerIndex(handlerName: string): Promise<string> {
269
259
  return `// Automatically generated by \`@whook/gcp-functions\`
270
260
  import { initialize } from './initialize.js';
271
261
 
@@ -273,28 +263,36 @@ const initializationPromise = initialize();
273
263
 
274
264
  export default function handler (req, res) {
275
265
  return initializationPromise
276
- .then(services => services['${name}'](req, res));
266
+ .then(services => services['${handlerName}'](req, res));
277
267
  };
278
268
  `;
279
269
  }
280
270
 
281
- async function buildFinalLambda(
282
- { compiler, log }: { compiler: WhookCompilerService; log: LogService },
283
- lambdaPath: string,
284
- whookConfig: WhookAPIOperationGCPFunctionConfig,
271
+ async function buildFinalHandler(
272
+ {
273
+ DEFINITIONS,
274
+ compiler,
275
+ log,
276
+ }: {
277
+ DEFINITIONS: WhookDefinitions;
278
+ compiler: WhookCompilerService;
279
+ log: LogService;
280
+ },
281
+ handlerPath: string,
282
+ handlerName: string,
285
283
  ): Promise<void> {
286
- const entryPoint = `${lambdaPath}/main.js`;
284
+ const entryPoint = `${handlerPath}/main.js`;
287
285
  const { extension, contents, mappings } = await compiler(
288
286
  entryPoint,
289
- whookConfig.compilerOptions,
287
+ DEFINITIONS[handlerName]?.config?.compilerOptions,
290
288
  );
291
289
 
292
290
  await Promise.all([
293
- ensureFile({ log }, join(lambdaPath, `/index${extension}`), contents),
291
+ ensureFile({ log }, join(handlerPath, `/index${extension}`), contents),
294
292
  mappings
295
293
  ? ensureFile(
296
294
  { log },
297
- join(lambdaPath, `/index${extension}.map`),
295
+ join(handlerPath, `/index${extension}.map`),
298
296
  mappings,
299
297
  )
300
298
  : Promise.resolve(),
@@ -303,7 +301,7 @@ async function buildFinalLambda(
303
301
 
304
302
  async function copyStaticFiles(
305
303
  { PROJECT_DIR, log }: { PROJECT_DIR: string; log: LogService },
306
- lambdaPath: string,
304
+ handlerPath: string,
307
305
  staticFiles: string[] = [],
308
306
  ): Promise<void> {
309
307
  await Promise.all(
@@ -312,7 +310,7 @@ async function copyStaticFiles(
312
310
  await copyFiles(
313
311
  { log },
314
312
  join(PROJECT_DIR, 'node_modules', staticFile),
315
- join(lambdaPath, 'node_modules', staticFile),
313
+ join(handlerPath, 'node_modules', staticFile),
316
314
  ),
317
315
  ),
318
316
  );