@api-client/core 0.7.9 → 0.7.10

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 (40) hide show
  1. package/build/browser.d.ts +5 -1
  2. package/build/browser.js.map +1 -1
  3. package/build/index.d.ts +6 -1
  4. package/build/index.js +8 -0
  5. package/build/index.js.map +1 -1
  6. package/build/src/events/Events.d.ts +3 -3
  7. package/build/src/events/transport/TransportEvents.d.ts +10 -36
  8. package/build/src/events/transport/TransportEvents.js +9 -23
  9. package/build/src/events/transport/TransportEvents.js.map +1 -1
  10. package/build/src/proxy/AppProjectProxy.d.ts +27 -0
  11. package/build/src/proxy/AppProjectProxy.js +107 -0
  12. package/build/src/proxy/AppProjectProxy.js.map +1 -0
  13. package/build/src/proxy/HttpProjectProxy.d.ts +23 -0
  14. package/build/src/proxy/HttpProjectProxy.js +99 -0
  15. package/build/src/proxy/HttpProjectProxy.js.map +1 -0
  16. package/build/src/proxy/Proxy.d.ts +27 -0
  17. package/build/src/proxy/Proxy.js +7 -0
  18. package/build/src/proxy/Proxy.js.map +1 -0
  19. package/build/src/proxy/ProxyService.d.ts +42 -0
  20. package/build/src/proxy/ProxyService.js +66 -0
  21. package/build/src/proxy/ProxyService.js.map +1 -0
  22. package/build/src/proxy/RequestProxy.d.ts +42 -0
  23. package/build/src/proxy/RequestProxy.js +59 -0
  24. package/build/src/proxy/RequestProxy.js.map +1 -0
  25. package/build/src/runtime/http-runner/HttpRequestRunner.d.ts +5 -0
  26. package/build/src/runtime/http-runner/HttpRequestRunner.js +18 -0
  27. package/build/src/runtime/http-runner/HttpRequestRunner.js.map +1 -1
  28. package/build/src/runtime/node/ProjectRequestRunner.d.ts +0 -5
  29. package/build/src/runtime/node/ProjectRequestRunner.js +1 -18
  30. package/build/src/runtime/node/ProjectRequestRunner.js.map +1 -1
  31. package/package.json +4 -1
  32. package/src/events/transport/TransportEvents.ts +13 -53
  33. package/src/proxy/AppProjectProxy.ts +125 -0
  34. package/src/proxy/HttpProjectProxy.ts +113 -0
  35. package/src/proxy/Proxy.ts +30 -0
  36. package/src/proxy/ProxyService.ts +73 -0
  37. package/src/proxy/RequestProxy.ts +94 -0
  38. package/src/proxy/readme.md +14 -0
  39. package/src/runtime/http-runner/HttpRequestRunner.ts +19 -0
  40. package/src/runtime/node/ProjectRequestRunner.ts +2 -20
@@ -0,0 +1,113 @@
1
+ import { IProjectRunnerOptions } from "../runtime/node/InteropInterfaces.js";
2
+ import { Kind as HttpProjectKind, HttpProject, IHttpProject } from "../models/HttpProject.js";
3
+ import { ApiError } from "../runtime/store/Errors.js";
4
+ import { StoreSdk } from "../runtime/store/StoreSdkNode.js";
5
+ import { ProjectSerialRunner } from "../runtime/node/ProjectSerialRunner.js";
6
+ import { ProjectParallelRunner } from "../runtime/node/ProjectParallelRunner.js";
7
+ import Proxy, { IProxyResult } from "./Proxy.js";
8
+
9
+ export interface IHttpProjectProxyInit {
10
+ kind: typeof HttpProjectKind;
11
+ /**
12
+ * Project key
13
+ */
14
+ pid: string;
15
+ /**
16
+ * Runner options.
17
+ */
18
+ options: IProjectRunnerOptions;
19
+ }
20
+
21
+ /**
22
+ * Runs requests from an HTTP project read from the store.
23
+ */
24
+ export default class HttpProjectProxy extends Proxy {
25
+ project?: HttpProject;
26
+ options?: IProjectRunnerOptions;
27
+
28
+ async configure(init: IHttpProjectProxyInit, token: string, baseUri: string): Promise<void> {
29
+ const { pid, options } = init;
30
+ if (!pid) {
31
+ throw new ApiError({
32
+ error: true,
33
+ message: 'Invalid request',
34
+ detail: 'The "pid" parameter is required.',
35
+ code: 400,
36
+ });
37
+ }
38
+ if (!options) {
39
+ throw new ApiError({
40
+ error: true,
41
+ message: 'Invalid request',
42
+ detail: 'The "options" parameter is required.',
43
+ code: 400,
44
+ });
45
+ }
46
+ if (!token) {
47
+ throw new ApiError({
48
+ error: true,
49
+ message: 'Invalid request',
50
+ detail: 'Set the authentication credentials.',
51
+ code: 400,
52
+ });
53
+ }
54
+ if (!baseUri) {
55
+ throw new ApiError({
56
+ error: true,
57
+ message: 'Invalid request',
58
+ detail: 'The store uri is missing.',
59
+ code: 400,
60
+ });
61
+ }
62
+ if (!baseUri.startsWith('http')) {
63
+ throw new ApiError({
64
+ error: true,
65
+ message: 'Invalid request',
66
+ detail: 'The store uri is invalid.',
67
+ code: 400,
68
+ });
69
+ }
70
+ const sdk = new StoreSdk(baseUri);
71
+ sdk.token = token;
72
+ let project: IHttpProject;
73
+ try {
74
+ project = await sdk.file.read(pid, true) as IHttpProject;
75
+ } catch (cause) {
76
+ const e = cause as Error;
77
+ throw new ApiError(e.message, 400);
78
+ }
79
+ if (project.key !== pid) {
80
+ throw new ApiError(`Unable to read the project.`, 500);
81
+ }
82
+ this.options = options;
83
+ this.project = new HttpProject(project);
84
+ }
85
+
86
+ async execute(): Promise<IProxyResult> {
87
+ const project = this.project as HttpProject;
88
+ const opts = this.options as IProjectRunnerOptions;
89
+ let factory: ProjectParallelRunner | ProjectSerialRunner;
90
+ if (opts.parallel) {
91
+ factory = new ProjectParallelRunner(project, opts);
92
+ } else {
93
+ factory = new ProjectSerialRunner();
94
+ factory.configure(project, opts);
95
+ }
96
+
97
+ // eslint-disable-next-line no-inner-declarations
98
+ function unhandledRejection(): void {
99
+ //
100
+ }
101
+ // the executing library handles all related errors it need.
102
+ // However, when executing a request to an unknown host Node process may
103
+ // throw unhandled error event when the error is properly reported by the
104
+ // application. This suppresses these errors.
105
+ // Note, uncomment this line for debug.
106
+ process.on('unhandledRejection', unhandledRejection);
107
+ const report = await factory.execute();
108
+ process.off('unhandledRejection', unhandledRejection);
109
+ return {
110
+ result: report,
111
+ };
112
+ }
113
+ }
@@ -0,0 +1,30 @@
1
+ import { IRequestLog } from "../models/RequestLog.js";
2
+ import { IProjectExecutionLog } from "../runtime/reporters/Reporter.js";
3
+
4
+ export interface IProxyResult<T = IProjectExecutionLog | IRequestLog> {
5
+ /**
6
+ * The result of the proxy execution. It can be project execution log or a request log.
7
+ */
8
+ result: T;
9
+ /**
10
+ * Optional variables updated during the execution.
11
+ */
12
+ variables?: Record<string, string>;
13
+ }
14
+
15
+ export default abstract class Proxy {
16
+ /**
17
+ * The time when this class was initialized.
18
+ */
19
+ time = Date.now();
20
+
21
+ /**
22
+ * Configures the proxy before running it.
23
+ */
24
+ abstract configure(...args: unknown[]): Promise<unknown>;
25
+
26
+ /**
27
+ * Executes the proxy.
28
+ */
29
+ abstract execute(body?: Buffer): Promise<IProxyResult>;
30
+ }
@@ -0,0 +1,73 @@
1
+ import { ApiError } from "../runtime/store/Errors.js";
2
+ import v4 from "../lib/uuid.js";
3
+ import AppProjectProxy, { IAppProjectProxyInit } from "./AppProjectProxy.js";
4
+ import HttpProjectProxy, { IHttpProjectProxyInit } from "./HttpProjectProxy.js";
5
+ import { IProxyResult } from "./Proxy.js";
6
+ import RequestProxy, { IRequestProxyInit } from "./RequestProxy.js";
7
+
8
+ type Proxy = RequestProxy | HttpProjectProxy | AppProjectProxy;
9
+
10
+ export default class ProxyService {
11
+ state = new Map<string, Proxy>();
12
+
13
+ /**
14
+ * Executes a single HTTP request.
15
+ * @param init The request to execute with additional configuration.
16
+ * @returns The key under which the proxy is cached.
17
+ */
18
+ async addRequestProxy(init: IRequestProxyInit): Promise<string> {
19
+ const proxy = new RequestProxy();
20
+ await proxy.configure(init);
21
+ const id = v4();
22
+ this.state.set(id, proxy);
23
+ return id;
24
+ }
25
+
26
+ /**
27
+ * Executes an HttpProject.
28
+ *
29
+ * @param token The user access token to read data from the store.
30
+ * @param storeUri The store's base URI.
31
+ * @param init The project run configuration.
32
+ * @returns The key under which the proxy is cached.
33
+ */
34
+ async addHttpProjectProxy(token: string, storeUri: string, init: IHttpProjectProxyInit): Promise<string> {
35
+ const proxy = new HttpProjectProxy();
36
+ await proxy.configure(init, token, storeUri);
37
+ const id = v4();
38
+ this.state.set(id, proxy);
39
+ return id;
40
+ }
41
+
42
+ /**
43
+ * Executes an AppProject.
44
+ *
45
+ * @param token The user access token to read data from the store.
46
+ * @param storeUri The store's base URI.
47
+ * @param init The project run configuration.
48
+ * @returns The key under which the proxy is cached.
49
+ */
50
+ async addAppProjectProxy(token: string, storeUri: string, init: IAppProjectProxyInit): Promise<string> {
51
+ const proxy = new AppProjectProxy();
52
+ await proxy.configure(init, token, storeUri);
53
+ const id = v4();
54
+ this.state.set(id, proxy);
55
+ return id;
56
+ }
57
+
58
+ /**
59
+ * Executes previously configured proxy.
60
+ *
61
+ * @param key The key returned by the add proxy function.
62
+ * @param body Optional body to send with the `HttpRequest` proxy.
63
+ * @returns The execution log depending on the proxy type.
64
+ */
65
+ async proxy(key: string, body?: Buffer): Promise<IProxyResult> {
66
+ const proxy = this.state.get(key);
67
+ if (!proxy) {
68
+ throw new ApiError(`Unknown key: ${key}. The request may have been already executed.`, 400);
69
+ }
70
+ this.state.delete(key);
71
+ return proxy.execute(body);
72
+ }
73
+ }
@@ -0,0 +1,94 @@
1
+ import { IHttpRequest, Kind as HttpRequestKind } from '../models/HttpRequest.js';
2
+ import { IRequestAuthorization } from '../models/RequestAuthorization.js';
3
+ import { IRequestConfig } from '../models/RequestConfig.js';
4
+ import Proxy, { IProxyResult } from "./Proxy.js";
5
+ import { HttpCertificate } from '../models/ClientCertificate.js';
6
+ import { IHttpActionFlow } from '../models/http-actions/HttpActions.js';
7
+ import { ApiError } from '../runtime/store/Errors.js';
8
+ import { DummyLogger } from '../lib/logging/DummyLogger.js';
9
+ import { HttpRequestRunner } from '../runtime/http-runner/HttpRequestRunner.js';
10
+
11
+ export interface IRequestProxyInit {
12
+ kind: typeof HttpRequestKind;
13
+ /**
14
+ * The request to execute.
15
+ */
16
+ request: IHttpRequest;
17
+ /**
18
+ * The authorization data to apply.
19
+ */
20
+ authorization?: IRequestAuthorization[];
21
+ /**
22
+ * The request configuration.
23
+ */
24
+ config?: IRequestConfig;
25
+ /**
26
+ * The list of execution variables to use with the request.
27
+ */
28
+ variables?: Record<string, string>;
29
+ /**
30
+ * The certificate data to use with the request.
31
+ */
32
+ certificate?: HttpCertificate;
33
+ /**
34
+ * The request flows to execute with the request.
35
+ */
36
+ flows?: IHttpActionFlow[];
37
+ }
38
+
39
+ /**
40
+ * Proxies a single HTTP request
41
+ */
42
+ export default class RequestProxy extends Proxy {
43
+ init?: IRequestProxyInit;
44
+
45
+ async configure(init: IRequestProxyInit): Promise<void> {
46
+ const { request } = init;
47
+ if (!request) {
48
+ throw new ApiError({
49
+ error: true,
50
+ message: 'Invalid request',
51
+ detail: 'The "request" parameter is required.',
52
+ code: 400,
53
+ });
54
+ }
55
+ if (!request.url) {
56
+ throw new ApiError({
57
+ error: true,
58
+ message: 'Invalid request',
59
+ detail: 'The "request.url" parameter is required.',
60
+ code: 400,
61
+ });
62
+ }
63
+ this.init = init;
64
+ }
65
+
66
+ async execute(body?: Buffer): Promise<IProxyResult> {
67
+ const data = this.init as IRequestProxyInit;
68
+ const { request, authorization, certificate, config={}, variables={}, flows } = data;
69
+ if (body) {
70
+ request.payload = {
71
+ type: 'buffer',
72
+ data: [...body],
73
+ };
74
+ }
75
+ const factory = new HttpRequestRunner();
76
+ factory.variables = variables;
77
+ factory.logger = new DummyLogger();
78
+ factory.config = { ...config, enabled: true } as IRequestConfig;
79
+ if (Array.isArray(authorization) && authorization.length) {
80
+ factory.authorization = authorization;
81
+ }
82
+ if (Array.isArray(flows) && flows.length) {
83
+ factory.flows = flows;
84
+ }
85
+ if (certificate) {
86
+ factory.certificates = [certificate];
87
+ }
88
+ const result = await factory.run(request);
89
+ return {
90
+ result: result,
91
+ variables,
92
+ };
93
+ }
94
+ }
@@ -0,0 +1,14 @@
1
+ # HTTP Proxy
2
+
3
+ The libraries to create an HTTP/Socket proxy for API things (projects, requests).
4
+
5
+ ## Libraries
6
+
7
+ - `AppProjectProxy` - a class that runs request from an `AppProject`
8
+ - `HttpProjectProxy` - a class that runs request from an `HttpProject`
9
+ - `RequestProxy` - a class that runs a single request as `HttpRequest`
10
+
11
+ ## Architecture
12
+
13
+ The proxy is a 2-step process. In the first step the client sets-up a session (in a server that implements the proxy), processes the configuration, and validates the data.
14
+ In the second step the request is actually being executed. This 2nd step is very important when proxying `HttpRequest` as the `payload` read from the second server call is passed to the outgoing request (if any).
@@ -166,6 +166,7 @@ export class HttpRequestRunner {
166
166
  const { variables, variablesProcessor } = this;
167
167
  let copy: IHttpRequest = { ...request };
168
168
  if (variables) {
169
+ copy.url = this.prepareRequestUrl(copy.url, variables);
169
170
  copy = await variablesProcessor.evaluateVariablesWithContext(copy, variables);
170
171
  }
171
172
  return copy;
@@ -350,4 +351,22 @@ export class HttpRequestRunner {
350
351
  }
351
352
  return !config.ignoreSessionCookies;
352
353
  }
354
+
355
+ /**
356
+ * When defined it applies the serve's base URI to relative URLs.
357
+ * @param currentUrl The URL to process.
358
+ */
359
+ prepareRequestUrl(currentUrl: string, variables: Record<string, string>): string {
360
+ const { baseUri } = variables;
361
+ if (!baseUri) {
362
+ return currentUrl;
363
+ }
364
+ if (currentUrl.startsWith('http:') || currentUrl.startsWith('https:')) {
365
+ return currentUrl;
366
+ }
367
+ if (currentUrl.startsWith('/')) {
368
+ return `${baseUri}${currentUrl}`;
369
+ }
370
+ return `${baseUri}/${currentUrl}`;
371
+ }
353
372
  }
@@ -252,11 +252,11 @@ export class ProjectRequestRunner extends EventEmitter {
252
252
  key: request.key,
253
253
  };
254
254
  const requestData = request.expects.toJSON();
255
- requestData.url = this.prepareRequestUrl(requestData.url, variables);
256
-
255
+
257
256
  try {
258
257
  // Below replaces the single call to the `run()` function of the factory to
259
258
  // report via the events a request object that has evaluated with the Jexl library.
259
+ requestData.url = factory.prepareRequestUrl(requestData.url, variables);
260
260
  const requestCopy = await factory.applyVariables(requestData);
261
261
  await factory.applyAuthorization(requestCopy);
262
262
  await factory.applyCookies(requestCopy);
@@ -333,22 +333,4 @@ export class ProjectRequestRunner extends EventEmitter {
333
333
  ctx.baseUri = baseUri || '';
334
334
  return this.variablesProcessor.buildContext(ctx);
335
335
  }
336
-
337
- /**
338
- * When defined it applies the serve's base URI to relative URLs.
339
- * @param currentUrl The URL to process.
340
- */
341
- protected prepareRequestUrl(currentUrl: string, variables: Record<string, string>): string {
342
- const { baseUri } = variables;
343
- if (!baseUri) {
344
- return currentUrl;
345
- }
346
- if (currentUrl.startsWith('http:') || currentUrl.startsWith('https:')) {
347
- return currentUrl;
348
- }
349
- if (currentUrl.startsWith('/')) {
350
- return `${baseUri}${currentUrl}`;
351
- }
352
- return `${baseUri}/${currentUrl}`;
353
- }
354
336
  }