@vtex/api 7.1.0-beta.0 → 7.2.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.
@@ -58,3 +58,6 @@ export declare const PRODUCTION: boolean;
58
58
  export declare const INSPECT_DEBUGGER_PORT = 5858;
59
59
  export declare const cancellableMethods: Set<string>;
60
60
  export declare const LOG_CLIENT_INIT_TIMEOUT_MS = 5000;
61
+ export declare const OTEL_EXPORTER_OTLP_ENDPOINT: string;
62
+ export declare const DK_APP_ID: string;
63
+ export declare const DIAGNOSTICS_TELEMETRY_ENABLED: boolean;
package/lib/constants.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LOG_CLIENT_INIT_TIMEOUT_MS = exports.cancellableMethods = exports.INSPECT_DEBUGGER_PORT = exports.PRODUCTION = exports.WORKSPACE = exports.ACCOUNT = exports.NODE_ENV = exports.APP = exports.PUBLIC_ENDPOINT = exports.REGION = exports.LINKED = exports.MAX_WORKERS = exports.HTTP_SERVER_PORT = exports.MAX_AGE = exports.UP_SIGNAL = exports.BODY_HASH = exports.PROVIDER_HEADER = exports.TRACE_ID_HEADER = exports.COLOSSUS_PARAMS_HEADER = exports.COLOSSUS_ROUTE_ID_HEADER = exports.COLOSSUS_ROUTE_DECLARER_HEADER = exports.EVENT_HANDLER_ID_HEADER = exports.EVENT_SUBJECT_HEADER = exports.EVENT_SENDER_HEADER = exports.EVENT_KEY_HEADER = exports.WORKSPACE_HEADER = exports.WORKSPACE_IS_PRODUCTION_HEADER = exports.PLATFORM_HEADER = exports.OPERATION_ID_HEADER = exports.ROUTER_CACHE_HEADER = exports.REQUEST_ID_HEADER = exports.CREDENTIAL_HEADER = exports.ACCOUNT_HEADER = exports.ETAG_HEADER = exports.META_HEADER_BUCKET = exports.META_HEADER = exports.BINDING_HEADER = exports.TENANT_HEADER = exports.FORWARDED_HOST_HEADER = exports.LOCALE_HEADER = exports.PRODUCT_HEADER = exports.SESSION_HEADER = exports.SEGMENT_HEADER = exports.CACHE_CONTROL_HEADER = exports.PID = exports.IS_IO = exports.DEFAULT_WORKSPACE = exports.NODE_VTEX_API_VERSION = void 0;
3
+ exports.DK_APP_ID = exports.OTEL_EXPORTER_OTLP_ENDPOINT = exports.LOG_CLIENT_INIT_TIMEOUT_MS = exports.cancellableMethods = exports.INSPECT_DEBUGGER_PORT = exports.PRODUCTION = exports.WORKSPACE = exports.ACCOUNT = exports.NODE_ENV = exports.APP = exports.PUBLIC_ENDPOINT = exports.REGION = exports.LINKED = exports.MAX_WORKERS = exports.HTTP_SERVER_PORT = exports.MAX_AGE = exports.UP_SIGNAL = exports.BODY_HASH = exports.PROVIDER_HEADER = exports.TRACE_ID_HEADER = exports.COLOSSUS_PARAMS_HEADER = exports.COLOSSUS_ROUTE_ID_HEADER = exports.COLOSSUS_ROUTE_DECLARER_HEADER = exports.EVENT_HANDLER_ID_HEADER = exports.EVENT_SUBJECT_HEADER = exports.EVENT_SENDER_HEADER = exports.EVENT_KEY_HEADER = exports.WORKSPACE_HEADER = exports.WORKSPACE_IS_PRODUCTION_HEADER = exports.PLATFORM_HEADER = exports.OPERATION_ID_HEADER = exports.ROUTER_CACHE_HEADER = exports.REQUEST_ID_HEADER = exports.CREDENTIAL_HEADER = exports.ACCOUNT_HEADER = exports.ETAG_HEADER = exports.META_HEADER_BUCKET = exports.META_HEADER = exports.BINDING_HEADER = exports.TENANT_HEADER = exports.FORWARDED_HOST_HEADER = exports.LOCALE_HEADER = exports.PRODUCT_HEADER = exports.SESSION_HEADER = exports.SEGMENT_HEADER = exports.CACHE_CONTROL_HEADER = exports.PID = exports.IS_IO = exports.DEFAULT_WORKSPACE = exports.NODE_VTEX_API_VERSION = void 0;
4
+ exports.DIAGNOSTICS_TELEMETRY_ENABLED = void 0;
4
5
  const app_1 = require("./utils/app");
5
6
  // tslint:disable-next-line
6
7
  const pkg = require('../package.json');
@@ -65,3 +66,6 @@ exports.PRODUCTION = process.env.VTEX_PRODUCTION === 'true';
65
66
  exports.INSPECT_DEBUGGER_PORT = 5858;
66
67
  exports.cancellableMethods = new Set(['GET', 'OPTIONS', 'HEAD']);
67
68
  exports.LOG_CLIENT_INIT_TIMEOUT_MS = 5000;
69
+ exports.OTEL_EXPORTER_OTLP_ENDPOINT = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
70
+ exports.DK_APP_ID = process.env.NODE_VTEX_API_DK_APP_ID || "apps-team";
71
+ exports.DIAGNOSTICS_TELEMETRY_ENABLED = process.env.VTEX_DIAGNOSTICS_TELEMETRY_ENABLED === 'true';
@@ -77,7 +77,7 @@ class Logger {
77
77
  traceId: ctx.tracer.traceId,
78
78
  };
79
79
  }
80
- // this.initLogClient();
80
+ this.initLogClient();
81
81
  }
82
82
  initLogClient() {
83
83
  if (this.clientInitPromise) {
@@ -1,2 +1,2 @@
1
1
  import { Types } from "@vtex/diagnostics-nodejs";
2
- export declare function getMetricClient(): Promise<Types.MetricClient>;
2
+ export declare const getMetricClient: () => Promise<Types.MetricClient>;
@@ -2,34 +2,37 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getMetricClient = void 0;
4
4
  const telemetry_1 = require("../telemetry");
5
- let client;
6
- let isInitializing = false;
7
- let initPromise = undefined;
8
- async function getMetricClient() {
9
- if (client) {
10
- return client;
5
+ class MetricClientSingleton {
6
+ constructor() { }
7
+ static getInstance() {
8
+ if (!MetricClientSingleton.instance) {
9
+ MetricClientSingleton.instance = new MetricClientSingleton();
10
+ }
11
+ return MetricClientSingleton.instance;
11
12
  }
12
- if (initPromise) {
13
- return initPromise;
13
+ async getClient() {
14
+ if (this.client) {
15
+ return this.client;
16
+ }
17
+ if (this.initPromise) {
18
+ return this.initPromise;
19
+ }
20
+ this.initPromise = this.initializeClient();
21
+ return this.initPromise;
14
22
  }
15
- isInitializing = true;
16
- initPromise = initializeClient();
17
- return initPromise;
18
- }
19
- exports.getMetricClient = getMetricClient;
20
- async function initializeClient() {
21
- try {
22
- const { metricsClient } = await (0, telemetry_1.initializeTelemetry)();
23
- client = metricsClient;
24
- initPromise = undefined;
25
- return metricsClient;
26
- }
27
- catch (error) {
28
- console.error('Failed to initialize metrics client:', error);
29
- initPromise = undefined;
30
- throw error;
31
- }
32
- finally {
33
- isInitializing = false;
23
+ async initializeClient() {
24
+ try {
25
+ const { metricsClient } = await (0, telemetry_1.initializeTelemetry)();
26
+ this.client = metricsClient;
27
+ this.initPromise = undefined;
28
+ return metricsClient;
29
+ }
30
+ catch (error) {
31
+ console.error('Failed to initialize metrics client:', error);
32
+ this.initPromise = undefined;
33
+ throw error;
34
+ }
34
35
  }
35
36
  }
37
+ const getMetricClient = () => MetricClientSingleton.getInstance().getClient();
38
+ exports.getMetricClient = getMetricClient;
@@ -2,8 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getOtelInstruments = void 0;
4
4
  const client_1 = require("./client");
5
- let instruments;
6
- let initializingPromise;
7
5
  const createOtelConcurrentRequestsInstrument = async () => {
8
6
  const metricsClient = await (0, client_1.getMetricClient)();
9
7
  return metricsClient.createGauge('io_http_requests_current', {
@@ -39,36 +37,51 @@ const createOtelTotalAbortedRequestsInstrument = async () => {
39
37
  unit: '1'
40
38
  });
41
39
  };
42
- const getOtelInstruments = async () => {
43
- if (instruments) {
44
- return instruments;
40
+ class OtelInstrumentsSingleton {
41
+ constructor() { }
42
+ static getInstance() {
43
+ if (!OtelInstrumentsSingleton.instance) {
44
+ OtelInstrumentsSingleton.instance = new OtelInstrumentsSingleton();
45
+ }
46
+ return OtelInstrumentsSingleton.instance;
45
47
  }
46
- if (initializingPromise) {
47
- return initializingPromise;
48
+ async getInstruments() {
49
+ if (this.instruments) {
50
+ return this.instruments;
51
+ }
52
+ if (this.initializingPromise) {
53
+ return this.initializingPromise;
54
+ }
55
+ this.initializingPromise = this.initializeInstruments();
56
+ try {
57
+ this.instruments = await this.initializingPromise;
58
+ return this.instruments;
59
+ }
60
+ catch (error) {
61
+ console.error('Failed to initialize OTel instruments:', error);
62
+ this.initializingPromise = undefined;
63
+ throw error;
64
+ }
65
+ finally {
66
+ this.initializingPromise = undefined;
67
+ }
48
68
  }
49
- initializingPromise = initializeOtelInstruments();
50
- try {
51
- instruments = await initializingPromise;
52
- return instruments;
69
+ async initializeInstruments() {
70
+ const [concurrentRequests, requestTimings, totalRequests, responseSizes, abortedRequests] = await Promise.all([
71
+ createOtelConcurrentRequestsInstrument(),
72
+ createOtelRequestsTimingsInstrument(),
73
+ createOtelTotalRequestsInstrument(),
74
+ createOtelRequestsResponseSizesInstrument(),
75
+ createOtelTotalAbortedRequestsInstrument()
76
+ ]);
77
+ return {
78
+ concurrentRequests,
79
+ requestTimings,
80
+ totalRequests,
81
+ responseSizes,
82
+ abortedRequests
83
+ };
53
84
  }
54
- finally {
55
- initializingPromise = undefined;
56
- }
57
- };
85
+ }
86
+ const getOtelInstruments = () => OtelInstrumentsSingleton.getInstance().getInstruments();
58
87
  exports.getOtelInstruments = getOtelInstruments;
59
- const initializeOtelInstruments = async () => {
60
- const [concurrentRequests, requestTimings, totalRequests, responseSizes, abortedRequests] = await Promise.all([
61
- createOtelConcurrentRequestsInstrument(),
62
- createOtelRequestsTimingsInstrument(),
63
- createOtelTotalRequestsInstrument(),
64
- createOtelRequestsResponseSizesInstrument(),
65
- createOtelTotalAbortedRequestsInstrument()
66
- ]);
67
- return {
68
- concurrentRequests,
69
- requestTimings,
70
- totalRequests,
71
- responseSizes,
72
- abortedRequests
73
- };
74
- };
@@ -7,20 +7,24 @@ const metrics_1 = require("./metrics");
7
7
  const INSTRUMENTS_INITIALIZATION_TIMEOUT = 500;
8
8
  const addOtelRequestMetricsMiddleware = () => {
9
9
  let instruments;
10
+ const tryGetInstruments = async (ctx) => {
11
+ try {
12
+ return await Promise.race([
13
+ (0, metrics_1.getOtelInstruments)(),
14
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout waiting for OpenTelemetry instruments initialization')), INSTRUMENTS_INITIALIZATION_TIMEOUT))
15
+ ]);
16
+ }
17
+ catch (error) {
18
+ const errorMessage = error instanceof Error ? error.message : String(error);
19
+ console.warn(`OpenTelemetry instruments not ready for request ${ctx.requestHandlerName}: ${errorMessage}`);
20
+ return undefined;
21
+ }
22
+ };
10
23
  return async function addOtelRequestMetrics(ctx, next) {
24
+ instruments = instruments ? instruments : await tryGetInstruments(ctx);
11
25
  if (!instruments) {
12
- try {
13
- instruments = await Promise.race([
14
- (0, metrics_1.getOtelInstruments)(),
15
- new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout waiting for OpenTelemetry instruments initialization')), INSTRUMENTS_INITIALIZATION_TIMEOUT))
16
- ]);
17
- }
18
- catch (error) {
19
- const errorMessage = error instanceof Error ? error.message : String(error);
20
- console.warn(`OpenTelemetry instruments not ready for request ${ctx.requestHandlerName}: ${errorMessage}`);
21
- await next();
22
- return;
23
- }
26
+ await next();
27
+ return;
24
28
  }
25
29
  const start = process.hrtime();
26
30
  instruments.concurrentRequests.add(1);
@@ -5,27 +5,25 @@ const diagnostics_nodejs_1 = require("@vtex/diagnostics-nodejs");
5
5
  const constants_1 = require("../../constants");
6
6
  const instrumentation_koa_1 = require("@opentelemetry/instrumentation-koa");
7
7
  const hostMetrics_1 = require("../metrics/instruments/hostMetrics");
8
- const CLIENT_NAME = constants_1.APP.NAME || 'node-vtex-api';
9
8
  const APPLICATION_ID = constants_1.APP.ID || 'vtex-io-app';
10
- const EXPORTER_OTLP_ENDPOINT = process.env.EXPORTER_OTLP_ENDPOINT;
11
9
  class TelemetryClientSingleton {
12
10
  constructor() {
13
11
  this.initializationPromise = undefined;
14
12
  this.initializeTracesClient = async (telemetryClient) => await telemetryClient.newTracesClient({
15
13
  exporter: diagnostics_nodejs_1.Exporters.CreateExporter(diagnostics_nodejs_1.Exporters.CreateTracesExporterConfig({
16
- endpoint: EXPORTER_OTLP_ENDPOINT,
14
+ endpoint: constants_1.OTEL_EXPORTER_OTLP_ENDPOINT,
17
15
  }), 'otlp'),
18
16
  });
19
17
  this.initializeMetricsClient = async (telemetryClient) => await telemetryClient.newMetricsClient({
20
18
  exporter: diagnostics_nodejs_1.Exporters.CreateExporter(diagnostics_nodejs_1.Exporters.CreateMetricsExporterConfig({
21
- endpoint: EXPORTER_OTLP_ENDPOINT,
19
+ endpoint: constants_1.OTEL_EXPORTER_OTLP_ENDPOINT,
22
20
  interval: 5,
23
21
  timeoutSeconds: 5,
24
22
  }), 'otlp'),
25
23
  });
26
24
  this.initializeLogsClient = async (telemetryClient) => await telemetryClient.newLogsClient({
27
25
  exporter: diagnostics_nodejs_1.Exporters.CreateExporter(diagnostics_nodejs_1.Exporters.CreateLogsExporterConfig({
28
- endpoint: EXPORTER_OTLP_ENDPOINT,
26
+ endpoint: constants_1.OTEL_EXPORTER_OTLP_ENDPOINT,
29
27
  }), 'otlp'),
30
28
  loggerName: `node-vtex-api-${APPLICATION_ID}`,
31
29
  });
@@ -38,10 +36,15 @@ class TelemetryClientSingleton {
38
36
  }
39
37
  async initializeTelemetryClients() {
40
38
  try {
41
- const telemetryClient = await (0, diagnostics_nodejs_1.NewTelemetryClient)(APPLICATION_ID, CLIENT_NAME, 'node-vtex-api', {
39
+ const telemetryClient = await (0, diagnostics_nodejs_1.NewTelemetryClient)(constants_1.DK_APP_ID, 'node-vtex-api', APPLICATION_ID, {
40
+ // Use built-in no-op functionality when telemetry is disabled
41
+ noop: !constants_1.DIAGNOSTICS_TELEMETRY_ENABLED,
42
42
  additionalAttrs: {
43
+ 'app.id': APPLICATION_ID,
44
+ 'vendor': constants_1.APP.VENDOR,
43
45
  'version': constants_1.APP.VERSION || '',
44
- 'environment': process.env.VTEX_WORKSPACE || 'development',
46
+ 'workspace': constants_1.WORKSPACE,
47
+ 'production': constants_1.PRODUCTION.toString(),
45
48
  },
46
49
  });
47
50
  const [tracesClient, metricsClient, logsClient] = await Promise.all([
@@ -49,15 +52,18 @@ class TelemetryClientSingleton {
49
52
  this.initializeMetricsClient(telemetryClient),
50
53
  this.initializeLogsClient(telemetryClient),
51
54
  ]);
52
- const instrumentations = [
53
- ...diagnostics_nodejs_1.Instrumentation.CommonInstrumentations.minimal(),
54
- new instrumentation_koa_1.KoaInstrumentation(),
55
- new hostMetrics_1.HostMetricsInstrumentation({
56
- name: 'host-metrics-instrumentation',
57
- meterProvider: metricsClient.provider(),
58
- }),
59
- ];
60
- telemetryClient.registerInstrumentations(instrumentations);
55
+ if (constants_1.DIAGNOSTICS_TELEMETRY_ENABLED) {
56
+ console.log(`Telemetry enabled for app: ${constants_1.APP.ID} (vendor: ${constants_1.APP.VENDOR})`);
57
+ const instrumentations = [
58
+ ...diagnostics_nodejs_1.Instrumentation.CommonInstrumentations.minimal(),
59
+ new instrumentation_koa_1.KoaInstrumentation(),
60
+ new hostMetrics_1.HostMetricsInstrumentation({
61
+ name: 'host-metrics-instrumentation',
62
+ meterProvider: metricsClient.provider(),
63
+ }),
64
+ ];
65
+ telemetryClient.registerInstrumentations(instrumentations);
66
+ }
61
67
  const clients = {
62
68
  logsClient,
63
69
  metricsClient,
@@ -3,4 +3,4 @@ import { SchemaDirectiveVisitor } from 'graphql-tools';
3
3
  export declare class Auth extends SchemaDirectiveVisitor {
4
4
  visitFieldDefinition(field: GraphQLField<any, any>): void;
5
5
  }
6
- export declare const authDirectiveTypeDefs = "\ndirective @auth(\n productCode: String\n resourceCode: String\n) on FIELD_DEFINITION\n";
6
+ export declare const authDirectiveTypeDefs = "\n\nenum IOAuthScope {\n PUBLIC\n PRIVATE\n}\n\ndirective @auth(\n scope: IOAuthScope\n productCode: String\n resourceCode: String\n) on FIELD_DEFINITION\n";
@@ -50,8 +50,11 @@ async function auth(ctx, authArgs) {
50
50
  }
51
51
  }
52
52
  function parseArgs(authArgs) {
53
+ if (authArgs.scope == 'PUBLIC') {
54
+ return authArgs;
55
+ }
53
56
  if (!authArgs.productCode || !authArgs.resourceCode) {
54
- throw new apollo_server_errors_1.UserInputError('Invalid auth schema directive args. Usage: @auth(productCode: String, resourceCode: String).');
57
+ throw new apollo_server_errors_1.UserInputError('Invalid auth schema directive args. Usage: @auth(scope: IOAuthScope, productCode: String, resourceCode: String).');
55
58
  }
56
59
  return authArgs;
57
60
  }
@@ -60,14 +63,23 @@ class Auth extends graphql_tools_1.SchemaDirectiveVisitor {
60
63
  const { resolve = graphql_1.defaultFieldResolver } = field;
61
64
  field.resolve = async (root, args, ctx, info) => {
62
65
  const authArgs = parseArgs(this.args);
63
- await auth(ctx, authArgs);
66
+ if (!authArgs.scope || authArgs.scope == 'PRIVATE') {
67
+ await auth(ctx, authArgs);
68
+ }
64
69
  return resolve(root, args, ctx, info);
65
70
  };
66
71
  }
67
72
  }
68
73
  exports.Auth = Auth;
69
74
  exports.authDirectiveTypeDefs = `
75
+
76
+ enum IOAuthScope {
77
+ PUBLIC
78
+ PRIVATE
79
+ }
80
+
70
81
  directive @auth(
82
+ scope: IOAuthScope
71
83
  productCode: String
72
84
  resourceCode: String
73
85
  ) on FIELD_DEFINITION
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/api",
3
- "version": "7.1.0-beta.0",
3
+ "version": "7.2.0",
4
4
  "description": "VTEX I/O API client",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -53,7 +53,7 @@
53
53
  "@opentelemetry/instrumentation-koa": "0.47.1",
54
54
  "@types/koa": "^2.11.0",
55
55
  "@types/koa-compose": "^3.2.3",
56
- "@vtex/diagnostics-nodejs": "0.1.0-io-beta.19",
56
+ "@vtex/diagnostics-nodejs": "0.1.0-io-beta.20",
57
57
  "@vtex/node-error-report": "^0.0.3",
58
58
  "@wry/equality": "^0.1.9",
59
59
  "agentkeepalive": "^4.0.2",