@moreapp/common-nodejs 0.5.0 → 0.7.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.
package/README.md CHANGED
@@ -5,3 +5,17 @@ A collection of shared NodeJS code.
5
5
  # Pre-commit hooks
6
6
 
7
7
  Run `yarn prepare` once to install Git hooks, doing lints and prettier formatting
8
+
9
+ ## Usage
10
+
11
+ ### Tracing
12
+
13
+ Tracing code should always be loaded first, before any other libraries. This is because the OpenTelemetry
14
+ instrumentations monkey patch libraries to add tracing. This will break if the library has been loaded (import/require),
15
+ because the unpatched library will be cached.
16
+
17
+ Put the following snippet on the first line of the entrypoint file.
18
+
19
+ ```
20
+ require("@moreapp/common-nodejs/dist/tracer").tracer(<service_name>, {});
21
+ ```
@@ -15,8 +15,8 @@ export default class MoreAppClient {
15
15
  private readonly client;
16
16
  constructor(options: Options);
17
17
  getBinary(path: string): Promise<BinaryFile>;
18
- get(path: string): Promise<any>;
19
- post(path: string, payload: Record<string, any>): Promise<any>;
18
+ get<T>(path: string): Promise<T>;
19
+ post<T>(path: string, payload: Record<string, any>): Promise<T>;
20
20
  delete(path: string): Promise<any>;
21
21
  }
22
22
  export {};
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const axios_1 = __importDefault(require("axios"));
7
+ const content_disposition_parser_1 = __importDefault(require("content-disposition-parser"));
7
8
  class MoreAppClient {
8
9
  constructor(options) {
9
10
  this.client = axios_1.default.create({
@@ -48,11 +49,5 @@ class MoreAppClient {
48
49
  exports.default = MoreAppClient;
49
50
  function getFilename(res) {
50
51
  const contentDispositionHeader = res.headers["content-disposition"];
51
- if (contentDispositionHeader) {
52
- const execParts = /filename="(.*)"/gi.exec(contentDispositionHeader);
53
- if (execParts && execParts.length > 1) {
54
- return execParts[1];
55
- }
56
- }
57
- return undefined;
52
+ return (0, content_disposition_parser_1.default)(contentDispositionHeader)?.filename;
58
53
  }
@@ -13,7 +13,7 @@ test("download binary file", async () => {
13
13
  .matchHeader("x-more-seal", "my-seal")
14
14
  .reply(200, file, {
15
15
  "Content-Type": "image/jpg",
16
- "content-disposition": 'filename="my-photo.jpg"',
16
+ "content-disposition": 'attachment; filename="my-photo.jpg"',
17
17
  });
18
18
  const client = new MoreAppClient_1.default({
19
19
  serviceName: "Common Test",
@@ -44,3 +44,36 @@ test("unable to connect", async () => {
44
44
  });
45
45
  await expect(client.getBinary("/download")).rejects.toThrowError("getaddrinfo ENOTFOUND non-existing.moreapp.com");
46
46
  });
47
+ const client = new MoreAppClient_1.default({
48
+ serviceName: "Common Test",
49
+ prefix: "https://api.moreapp.com",
50
+ seal: "my-seal",
51
+ });
52
+ test("Handle non existing file", async () => {
53
+ const apiMock = (0, nock_1.default)("https://api.moreapp.com").get("/download").reply(404);
54
+ await expect(client.getBinary("/download")).rejects.toThrow("Request failed with status code 404");
55
+ apiMock.done();
56
+ });
57
+ test("Handle missing 'content-type' header", async () => {
58
+ const apiMock = (0, nock_1.default)("https://api.moreapp.com").get("/download").reply(200);
59
+ const binaryFile = await client.getBinary("/download");
60
+ expect(binaryFile).not.toBeNull();
61
+ expect(binaryFile.contentType).toBe("unknown");
62
+ apiMock.done();
63
+ });
64
+ test("Handle missing 'content-disposition' header", async () => {
65
+ const apiMock = (0, nock_1.default)("https://api.moreapp.com").get("/download").reply(200);
66
+ const binaryFile = await client.getBinary("/download");
67
+ expect(binaryFile).not.toBeNull();
68
+ expect(binaryFile.filename).toBeUndefined();
69
+ apiMock.done();
70
+ });
71
+ test("Handle invalid 'content-disposition' header", async () => {
72
+ const apiMock = (0, nock_1.default)("https://api.moreapp.com").get("/download").reply(200, {}, {
73
+ "content-disposition": "filename=my-photo.png",
74
+ });
75
+ const binaryFile = await client.getBinary("/download");
76
+ expect(binaryFile).not.toBeNull();
77
+ expect(binaryFile.filename).toBeUndefined();
78
+ apiMock.done();
79
+ });
package/dist/tracer.d.ts CHANGED
@@ -1,2 +1,8 @@
1
1
  import * as types from "@opentelemetry/instrumentation/build/src/types";
2
- export declare const tracer: (serviceName: string, extraInstrumentations: types.Instrumentation[]) => void;
2
+ export declare const tracer: (serviceName: string, { http, extraInstrumentations, debug, }: {
3
+ http: {
4
+ portsToInstrument: number[];
5
+ };
6
+ extraInstrumentations: types.Instrumentation[];
7
+ debug: boolean;
8
+ }) => void;
package/dist/tracer.js CHANGED
@@ -1,19 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.tracer = void 0;
4
+ const api_1 = require("@opentelemetry/api");
5
+ const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
4
6
  const opentelemetry_cloud_trace_exporter_1 = require("@google-cloud/opentelemetry-cloud-trace-exporter");
5
7
  const instrumentation_1 = require("@opentelemetry/instrumentation");
6
8
  const instrumentation_dns_1 = require("@opentelemetry/instrumentation-dns");
7
9
  const instrumentation_http_1 = require("@opentelemetry/instrumentation-http");
8
10
  const instrumentation_express_1 = require("@opentelemetry/instrumentation-express");
9
11
  const instrumentation_winston_1 = require("@opentelemetry/instrumentation-winston");
10
- const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
11
12
  const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
12
13
  const resources_1 = require("@opentelemetry/resources");
13
14
  const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
14
15
  const propagator_b3_1 = require("@opentelemetry/propagator-b3");
15
16
  const utils_1 = require("./utils");
16
- const tracer = (serviceName, extraInstrumentations) => {
17
+ const tracer = (serviceName, { http = {
18
+ portsToInstrument: [3000],
19
+ }, extraInstrumentations = [], debug = false, }) => {
20
+ (0, instrumentation_1.registerInstrumentations)({
21
+ instrumentations: [
22
+ new instrumentation_dns_1.DnsInstrumentation(),
23
+ new instrumentation_http_1.HttpInstrumentation({
24
+ ignoreIncomingRequestHook: (req) => {
25
+ return !http.portsToInstrument.includes(req.socket.localPort || 0);
26
+ },
27
+ }),
28
+ new instrumentation_express_1.ExpressInstrumentation(),
29
+ new instrumentation_winston_1.WinstonInstrumentation(),
30
+ ...(extraInstrumentations || []),
31
+ ],
32
+ });
17
33
  const provider = new sdk_trace_node_1.NodeTracerProvider({
18
34
  resource: new resources_1.Resource({
19
35
  [semantic_conventions_1.SemanticResourceAttributes.SERVICE_NAME]: serviceName,
@@ -25,25 +41,15 @@ const tracer = (serviceName, extraInstrumentations) => {
25
41
  injectEncoding: propagator_b3_1.B3InjectEncoding.MULTI_HEADER,
26
42
  }),
27
43
  });
28
- (0, instrumentation_1.registerInstrumentations)({
29
- instrumentations: [
30
- new instrumentation_dns_1.DnsInstrumentation(),
31
- new instrumentation_http_1.HttpInstrumentation({
32
- ignoreIncomingRequestHook: (_req) => {
33
- // Disable instrumentation for incoming requests, handled by ExpressInstrumentation below
34
- return true;
35
- },
36
- }),
37
- new instrumentation_express_1.ExpressInstrumentation(),
38
- new instrumentation_winston_1.WinstonInstrumentation(),
39
- ...extraInstrumentations,
40
- ],
41
- });
42
44
  if (process.env["STACKDRIVER_TRACING_ENABLED"] === "true") {
43
45
  const exporter = new opentelemetry_cloud_trace_exporter_1.TraceExporter({
44
46
  resourceFilter: /^service\./,
45
47
  });
46
48
  provider.addSpanProcessor(new sdk_trace_base_1.BatchSpanProcessor(exporter));
47
49
  }
50
+ if (debug) {
51
+ api_1.diag.setLogger(new api_1.DiagConsoleLogger(), api_1.DiagLogLevel.DEBUG);
52
+ provider.addSpanProcessor(new sdk_trace_base_1.BatchSpanProcessor(new sdk_trace_node_1.ConsoleSpanExporter()));
53
+ }
48
54
  };
49
55
  exports.tracer = tracer;
package/dist/types.d.ts CHANGED
@@ -3,20 +3,21 @@ import { FormVersionFieldSchema, SubmissionSchema } from "./schema";
3
3
  export declare type Submission = z.infer<typeof SubmissionSchema>;
4
4
  export declare type FormVersionField = z.infer<typeof FormVersionFieldSchema>;
5
5
  export declare type IntegrationTestResponse = {
6
- status: "VALID" | "SUCCESS";
6
+ status: "VALID";
7
7
  } | {
8
8
  status: "INVALID";
9
9
  globalError: string;
10
10
  fieldErrors?: Record<string, any>;
11
11
  };
12
12
  export declare type IntegrationHandleResponse = {
13
- status: "VALID" | "SUCCESS";
13
+ status: "SUCCESS";
14
14
  files?: {
15
15
  type: string;
16
16
  name: string;
17
17
  data: string;
18
18
  }[];
19
+ data: Record<string, any>;
19
20
  } | {
20
- status: "INVALID";
21
+ status: "ERROR";
21
22
  message: string;
22
23
  };
package/dist/utils.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export declare function environmentVariable(key: string, fallback?: string | number): string;
2
2
  export declare function currentTraceId(): string | undefined;
3
+ export declare function isValidEmail(email: string): boolean;
package/dist/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.currentTraceId = exports.environmentVariable = void 0;
3
+ exports.isValidEmail = exports.currentTraceId = exports.environmentVariable = void 0;
4
4
  const context_utils_1 = require("@opentelemetry/api/build/src/trace/context-utils");
5
5
  const api_1 = require("@opentelemetry/api");
6
6
  function environmentVariable(key, fallback) {
@@ -15,3 +15,8 @@ function currentTraceId() {
15
15
  return (0, context_utils_1.getSpanContext)(api_1.context.active())?.traceId;
16
16
  }
17
17
  exports.currentTraceId = currentTraceId;
18
+ const validEmailPattern = /^[A-Z0-9._&%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
19
+ function isValidEmail(email) {
20
+ return validEmailPattern.test(email);
21
+ }
22
+ exports.isValidEmail = isValidEmail;
@@ -13,3 +13,13 @@ describe("environmentVariable", () => {
13
13
  expect(() => (0, utils_1.environmentVariable)("COMMONS_NODEJS_TEST_NON_EXISTING")).toThrow("Missing environment variable 'COMMONS_NODEJS_TEST_NON_EXISTING'");
14
14
  });
15
15
  });
16
+ describe("isValidEmail", () => {
17
+ test("Should return true on valid email addresses", () => {
18
+ expect((0, utils_1.isValidEmail)("john@example.com")).toBe(true);
19
+ expect((0, utils_1.isValidEmail)("sbe-vsv.ch@schindler.com")).toEqual(true);
20
+ });
21
+ test("Should return false on invalid email addresses", () => {
22
+ expect((0, utils_1.isValidEmail)("john")).toBe(false);
23
+ expect((0, utils_1.isValidEmail)("john@example")).toBe(false);
24
+ });
25
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moreapp/common-nodejs",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "license": "UNLICENSED",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,6 +29,7 @@
29
29
  "@opentelemetry/sdk-node": "0.36.1",
30
30
  "@opentelemetry/sdk-trace-node": "1.10.1",
31
31
  "axios": "1.3.4",
32
+ "content-disposition-parser": "1.0.2",
32
33
  "date-and-time": "2.4.3",
33
34
  "winston": "3.8.2",
34
35
  "zod": "3.21.4"