weflayr 0.24.0 → 0.26.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 +2 -2
- package/dist/{api → src/api}/generated/facade.gen.d.ts +86 -12
- package/dist/{api → src/api}/generated/facade.gen.js +4 -2
- package/dist/src/api/generated/index.d.ts +2 -0
- package/dist/{api → src/api}/generated/index.js +3 -1
- package/dist/{api → src/api}/generated/sdk.gen.d.ts +11 -3
- package/dist/{api → src/api}/generated/sdk.gen.js +34 -4
- package/{src/api/generated/types.gen.ts → dist/src/api/generated/types.gen.d.ts} +436 -153
- package/dist/{api → src/api}/makeEndpoint.js +1 -3
- package/dist/src/auto-instrument.d.ts +42 -0
- package/dist/src/auto-instrument.js +156 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +49 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.d.ts +20 -0
- package/dist/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +114 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/anthropic/get_cache_config_telemetry.js +92 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/anthropic/get_streaming_accumulation_telemetry.js +141 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/anthropic/get_structured_output_schema_telemetry.js +17 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +50 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.d.ts +15 -0
- package/dist/src/instrumentation/anthropic/set_telemetry_attributes.js +39 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_config_telemetry.js +97 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/bedrock/get_cache_telemetry.js +102 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_input_message_content_telemetry.js +91 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.d.ts +21 -0
- package/dist/src/instrumentation/bedrock/get_model_id_telemetry.js +54 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.d.ts +19 -0
- package/dist/src/instrumentation/bedrock/get_reasoning_output_telemetry.js +81 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +126 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.d.ts +17 -0
- package/dist/src/instrumentation/bedrock/get_tool_call_output_telemetry.js +85 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.d.ts +15 -0
- package/dist/src/instrumentation/bedrock/get_tool_definitions_telemetry.js +68 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/bedrock/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/cohere/get_chat_telemetry.js +77 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/cohere/get_embeddings_telemetry.js +88 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/cohere/get_structured_output_schema_telemetry.js +18 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.d.ts +31 -0
- package/dist/src/instrumentation/cohere/get_tool_call_output_telemetry.js +106 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/cohere/get_tool_definitions_telemetry.js +75 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/cohere/set_telemetry_attributes.js +44 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.d.ts +28 -0
- package/dist/src/instrumentation/elevenlabs/full_instrumentation.js +209 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.d.ts +9 -0
- package/dist/src/instrumentation/google/get_audio_telemetry.js +28 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.d.ts +23 -0
- package/dist/src/instrumentation/google/get_cache_operations_telemetry.js +154 -0
- package/dist/src/instrumentation/google/get_cache_telemetry.d.ts +16 -0
- package/{src → dist/src}/instrumentation/google/get_cache_telemetry.js +18 -19
- package/dist/src/instrumentation/google/get_embeddings_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_embeddings_telemetry.js +85 -0
- package/dist/src/instrumentation/google/get_provider_name.d.ts +11 -0
- package/{src → dist/src}/instrumentation/google/get_provider_name.js +11 -12
- package/{src/instrumentation/google/get_reasoning_telemetry.js → dist/src/instrumentation/google/get_reasoning_telemetry.d.ts} +1 -11
- package/dist/src/instrumentation/google/get_reasoning_telemetry.js +21 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/google/get_structured_output_schema_telemetry.js +19 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.d.ts +18 -0
- package/dist/src/instrumentation/google/set_telemetry_attributes.js +98 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.d.ts +25 -0
- package/dist/src/instrumentation/openai/get_audio_telemetry.js +123 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/openai/get_bedrock_mantle_telemetry.js +148 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.d.ts +27 -0
- package/dist/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +72 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.d.ts +28 -0
- package/dist/src/instrumentation/openai/get_cache_config_telemetry.js +91 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.d.ts +18 -0
- package/dist/src/instrumentation/openai/get_embeddings_telemetry.js +86 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.d.ts +21 -0
- package/dist/src/instrumentation/openai/get_streaming_usage.js +74 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.d.ts +12 -0
- package/dist/src/instrumentation/openai/get_structured_output_schema_telemetry.js +20 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.d.ts +17 -0
- package/dist/src/instrumentation/openai/set_telemetry_attributes.js +47 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.d.ts +35 -0
- package/dist/src/instrumentation/shared/get_structured_output_schema_telemetry.js +76 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.d.ts +3 -0
- package/dist/src/instrumentation/vercel/full_instrumentation.js +54 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.d.ts +22 -0
- package/dist/src/instrumentation/vercel/get_cache_creation_telemetry.js +66 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.d.ts +24 -0
- package/dist/src/instrumentation/vercel/get_structured_output_schema_telemetry.js +84 -0
- package/dist/src/logger.d.ts +1 -0
- package/dist/src/logger.js +6 -0
- package/dist/src/otel/exporter.d.ts +19 -0
- package/dist/src/otel/exporter.js +94 -0
- package/dist/src/otel/propagation.d.ts +46 -0
- package/dist/src/otel/propagation.js +110 -0
- package/dist/src/otel/span-filter.d.ts +3 -0
- package/dist/src/otel/span-filter.js +61 -0
- package/dist/src/otel/span-processor.d.ts +55 -0
- package/dist/src/otel/span-processor.js +169 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.d.ts +12 -0
- package/dist/src/otel/vercel-ai-sdk-span-filter.js +88 -0
- package/dist/src/setup.d.ts +50 -0
- package/dist/src/setup.js +155 -0
- package/package.json +11 -5
- package/.prettierignore +0 -2
- package/.prettierrc.json +0 -7
- package/LICENSE-MIT +0 -21
- package/NOTICE +0 -12
- package/dist/api/generated/index.d.ts +0 -2
- package/dist/api/generated/types.gen.d.ts +0 -1005
- package/eslint.config.mjs +0 -36
- package/index.d.ts +0 -170
- package/openapi-ts.config.mjs +0 -9
- package/scripts/generate_facade.mjs +0 -86
- package/src/api/caseConversion.ts +0 -58
- package/src/api/generated/client/client.gen.ts +0 -277
- package/src/api/generated/client/index.ts +0 -27
- package/src/api/generated/client/types.gen.ts +0 -218
- package/src/api/generated/client/utils.gen.ts +0 -316
- package/src/api/generated/client.gen.ts +0 -16
- package/src/api/generated/core/auth.gen.ts +0 -48
- package/src/api/generated/core/bodySerializer.gen.ts +0 -82
- package/src/api/generated/core/params.gen.ts +0 -178
- package/src/api/generated/core/pathSerializer.gen.ts +0 -171
- package/src/api/generated/core/queryKeySerializer.gen.ts +0 -117
- package/src/api/generated/core/serverSentEvents.gen.ts +0 -242
- package/src/api/generated/core/types.gen.ts +0 -110
- package/src/api/generated/core/utils.gen.ts +0 -140
- package/src/api/generated/facade.gen.ts +0 -104
- package/src/api/generated/index.ts +0 -4
- package/src/api/generated/sdk.gen.ts +0 -175
- package/src/api/index.ts +0 -45
- package/src/api/makeEndpoint.ts +0 -54
- package/src/auto-instrument.js +0 -172
- package/src/index.js +0 -19
- package/src/instrumentation/anthropic/get_cache_and_reasoning_telemetry.js +0 -118
- package/src/instrumentation/anthropic/get_cache_config_telemetry.js +0 -90
- package/src/instrumentation/anthropic/get_streaming_stop_reason_telemetry.js +0 -121
- package/src/instrumentation/anthropic/get_tool_definitions_telemetry.js +0 -55
- package/src/instrumentation/anthropic/set_telemetry_attributes.js +0 -39
- package/src/instrumentation/bedrock/get_cache_config_telemetry.js +0 -106
- package/src/instrumentation/bedrock/get_cache_telemetry.js +0 -109
- package/src/instrumentation/bedrock/get_input_message_content_telemetry.js +0 -94
- package/src/instrumentation/bedrock/get_streaming_output_message_telemetry.js +0 -87
- package/src/instrumentation/bedrock/set_telemetry_attributes.js +0 -40
- package/src/instrumentation/cohere/get_chat_telemetry.js +0 -88
- package/src/instrumentation/cohere/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/cohere/set_telemetry_attributes.js +0 -32
- package/src/instrumentation/elevenlabs/full_instrumentation.js +0 -236
- package/src/instrumentation/google/get_audio_telemetry.js +0 -30
- package/src/instrumentation/google/get_cache_operations_telemetry.js +0 -171
- package/src/instrumentation/google/get_embeddings_telemetry.js +0 -99
- package/src/instrumentation/google/set_telemetry_attributes.js +0 -104
- package/src/instrumentation/openai/get_audio_telemetry.js +0 -149
- package/src/instrumentation/openai/get_cache_and_reasoning_telemetry.js +0 -71
- package/src/instrumentation/openai/get_cache_config_telemetry.js +0 -63
- package/src/instrumentation/openai/get_embeddings_telemetry.js +0 -107
- package/src/instrumentation/openai/get_streaming_usage.js +0 -81
- package/src/instrumentation/openai/set_telemetry_attributes.js +0 -41
- package/src/instrumentation/vercel/full_instrumentation.js +0 -52
- package/src/instrumentation/vercel/get_cache_creation_telemetry.js +0 -70
- package/src/logger.js +0 -8
- package/src/otel/exporter.js +0 -108
- package/src/otel/propagation.js +0 -146
- package/src/otel/span-filter.js +0 -69
- package/src/otel/span-processor.js +0 -170
- package/src/otel/vercel-ai-sdk-span-filter.js +0 -79
- package/src/setup.js +0 -167
- package/tests/api/index.test.js +0 -437
- package/tests/index.test.js +0 -884
- package/tests/instrumentation/anthropic/get_cache_and_reasoning_telemetry.test.js +0 -181
- package/tests/instrumentation/anthropic/get_cache_config_telemetry.test.js +0 -92
- package/tests/instrumentation/anthropic/get_streaming_stop_reason_telemetry.test.js +0 -117
- package/tests/instrumentation/anthropic/get_tool_definitions_telemetry.test.js +0 -97
- package/tests/instrumentation/bedrock/get_cache_config_telemetry.test.js +0 -107
- package/tests/instrumentation/bedrock/get_cache_telemetry.test.js +0 -118
- package/tests/instrumentation/bedrock/get_input_message_content_telemetry.test.js +0 -89
- package/tests/instrumentation/bedrock/get_streaming_output_message_telemetry.test.js +0 -87
- package/tests/instrumentation/cohere/get_chat_telemetry.test.js +0 -148
- package/tests/instrumentation/cohere/get_embeddings_telemetry.test.js +0 -132
- package/tests/instrumentation/elevenlabs/full_instrumentation.test.js +0 -258
- package/tests/instrumentation/google/get_cache_operations_telemetry.test.js +0 -130
- package/tests/instrumentation/google/get_embeddings_telemetry.test.js +0 -128
- package/tests/instrumentation/google/get_reasoning_telemetry.test.js +0 -41
- package/tests/instrumentation/google/set_telemetry_attributes.test.js +0 -219
- package/tests/instrumentation/openai/get_audio_telemetry.test.js +0 -170
- package/tests/instrumentation/openai/get_cache_and_reasoning_telemetry.test.js +0 -83
- package/tests/instrumentation/openai/get_cache_config_telemetry.test.js +0 -83
- package/tests/instrumentation/openai/get_embeddings_telemetry.test.js +0 -145
- package/tests/instrumentation/openai/get_streaming_usage.test.js +0 -105
- package/tests/instrumentation/vercel/full_instrumentation.test.js +0 -16
- package/tests/instrumentation/vercel/get_cache_creation_telemetry.test.js +0 -96
- package/tests/otel/vercel-ai-sdk-span-filter.test.js +0 -159
- package/tsconfig.json +0 -18
- /package/dist/{api → src/api}/caseConversion.d.ts +0 -0
- /package/dist/{api → src/api}/caseConversion.js +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/index.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/index.js +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/client.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/client.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/auth.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/bodySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/params.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/pathSerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/queryKeySerializer.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/serverSentEvents.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/types.gen.js +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.d.ts +0 -0
- /package/dist/{api → src/api}/generated/core/utils.gen.js +0 -0
- /package/dist/{api → src/api}/generated/types.gen.js +0 -0
- /package/dist/{api → src/api}/index.d.ts +0 -0
- /package/dist/{api → src/api}/index.js +0 -0
- /package/dist/{api → src/api}/makeEndpoint.d.ts +0 -0
package/src/setup.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { trace } = require('@opentelemetry/api');
|
|
4
|
-
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
|
|
5
|
-
const { resourceFromAttributes } = require('@opentelemetry/resources');
|
|
6
|
-
|
|
7
|
-
const { buildExporter } = require('./otel/exporter');
|
|
8
|
-
const { CONTENT_CAPTURE_ENV, WeflayrSpanProcessor } = require('./otel/span-processor');
|
|
9
|
-
const { debug } = require('./logger');
|
|
10
|
-
const { version: SDK_VERSION } = require('../package.json');
|
|
11
|
-
|
|
12
|
-
const SDK_LANGUAGE = 'javascript';
|
|
13
|
-
const DEFAULT_BASE_URL = 'https://api.weflayr.com';
|
|
14
|
-
|
|
15
|
-
const _state = {
|
|
16
|
-
tracerProvider: null,
|
|
17
|
-
processor: null,
|
|
18
|
-
beforeExitListener: null,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// `trace.getTracerProvider()` returns a ProxyTracerProvider that forwards to
|
|
22
|
-
// whoever called `.register()` last. Unwrap it to reach the real provider.
|
|
23
|
-
function _unwrapTracerProvider(p) {
|
|
24
|
-
if (p && typeof p.getDelegate === 'function') {
|
|
25
|
-
return p.getDelegate();
|
|
26
|
-
}
|
|
27
|
-
return p;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// `NodeTracerProvider` 2.x dropped the public `addSpanProcessor` API —
|
|
31
|
-
// processors are construction-time only. To attach to a provider somebody
|
|
32
|
-
// else owns, we splice into the internal MultiSpanProcessor. This matches
|
|
33
|
-
// the actual public OTel JS guidance for composing post-construction.
|
|
34
|
-
function _attachProcessorToExistingProvider(provider, processor) {
|
|
35
|
-
if (!provider) return false;
|
|
36
|
-
if (typeof provider.addSpanProcessor === 'function') {
|
|
37
|
-
provider.addSpanProcessor(processor);
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
const multi = provider._activeSpanProcessor;
|
|
41
|
-
if (multi && Array.isArray(multi._spanProcessors)) {
|
|
42
|
-
multi._spanProcessors.push(processor);
|
|
43
|
-
return true;
|
|
44
|
-
}
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Configure the Weflayr SDK.
|
|
50
|
-
*
|
|
51
|
-
* Call once per process, before `autoInstrument`. For per-request or
|
|
52
|
-
* per-tenant data in a long-running server use `propagateMetadata(...)` —
|
|
53
|
-
* it stores metadata in OTel's `Context` (built on AsyncLocalStorage) and
|
|
54
|
-
* isolates correctly across concurrent requests.
|
|
55
|
-
*
|
|
56
|
-
* Calling `setup` again is supported but replaces the previous
|
|
57
|
-
* configuration: the prior processor is shut down and detached so we never
|
|
58
|
-
* stack exporters (which would otherwise double-export every span and leak
|
|
59
|
-
* HTTP pools).
|
|
60
|
-
*/
|
|
61
|
-
function setup(options) {
|
|
62
|
-
const opts = options || {};
|
|
63
|
-
const apiKey = opts.apiKey || process.env.WEFLAYR_API_KEY;
|
|
64
|
-
if (typeof apiKey !== 'string' || !apiKey) {
|
|
65
|
-
throw new Error(
|
|
66
|
-
'weflayr.setup: `apiKey` is required (pass it explicitly or set WEFLAYR_API_KEY)'
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
options = { ...opts, apiKey };
|
|
70
|
-
|
|
71
|
-
if (options.captureMessageContent === false) {
|
|
72
|
-
process.env[CONTENT_CAPTURE_ENV] = 'false';
|
|
73
|
-
} else if (options.captureMessageContent === true) {
|
|
74
|
-
process.env[CONTENT_CAPTURE_ENV] = 'true';
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
_detachPreviousWeflayrProcessor();
|
|
78
|
-
|
|
79
|
-
const baseUrl = options.baseUrl || process.env.WEFLAYR_BASE_URL || DEFAULT_BASE_URL;
|
|
80
|
-
|
|
81
|
-
const exporter = buildExporter({
|
|
82
|
-
baseUrl,
|
|
83
|
-
apiKey: options.apiKey,
|
|
84
|
-
warnOnExportError: options.warnOnExportError !== false,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const processor = new WeflayrSpanProcessor({
|
|
88
|
-
exporter,
|
|
89
|
-
exportMode: options.exportMode || 'batch',
|
|
90
|
-
defaultTags: options.defaultTags || {},
|
|
91
|
-
mask: options.mask,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Adopt an existing TracerProvider when there is one — e.g. the user already
|
|
95
|
-
// configured Datadog/Honeycomb/OTel, or called weflayr.setup() before.
|
|
96
|
-
// provider.register() silently refuses to override the proxy if another
|
|
97
|
-
// provider is already wired in, so naively creating a new provider would
|
|
98
|
-
// leave our processor attached to a dead one and we'd drop every LLM span.
|
|
99
|
-
const currentTracerProvider = _unwrapTracerProvider(trace.getTracerProvider());
|
|
100
|
-
let tracerProvider;
|
|
101
|
-
if (_attachProcessorToExistingProvider(currentTracerProvider, processor)) {
|
|
102
|
-
tracerProvider = currentTracerProvider;
|
|
103
|
-
} else {
|
|
104
|
-
tracerProvider = new NodeTracerProvider({
|
|
105
|
-
resource: resourceFromAttributes({
|
|
106
|
-
sdk_version: SDK_VERSION,
|
|
107
|
-
sdk_language: SDK_LANGUAGE,
|
|
108
|
-
}),
|
|
109
|
-
spanProcessors: [processor],
|
|
110
|
-
});
|
|
111
|
-
tracerProvider.register();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// beforeExit fires when the event loop empties on a clean exit, giving us a
|
|
115
|
-
// last async tick to drain buffered spans. It does NOT fire on process.exit()
|
|
116
|
-
// or uncaught fatals — callers handling those paths still need to call flush
|
|
117
|
-
// explicitly (e.g. in a finally) for the OTLP HTTP request to complete.
|
|
118
|
-
if (!_state.beforeExitListener) {
|
|
119
|
-
_state.beforeExitListener = async () => {
|
|
120
|
-
try {
|
|
121
|
-
await flush();
|
|
122
|
-
} catch {
|
|
123
|
-
// best-effort: process is already winding down.
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
process.on('beforeExit', _state.beforeExitListener);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
_state.tracerProvider = tracerProvider;
|
|
130
|
-
_state.processor = processor;
|
|
131
|
-
debug(
|
|
132
|
-
'setup ready: baseUrl=%s exportMode=%s defaultTags=%d',
|
|
133
|
-
baseUrl,
|
|
134
|
-
options.exportMode || 'batch',
|
|
135
|
-
Object.keys(options.defaultTags || {}).length
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async function flush() {
|
|
140
|
-
if (!_state.processor) return;
|
|
141
|
-
debug('flush: forcing exporter flush');
|
|
142
|
-
await _state.processor.forceFlush();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Shut down + detach the prior weflayr processor — idempotency for setup().
|
|
146
|
-
// Without this, calling setup() twice (key rotation, hot reload, tests that
|
|
147
|
-
// reconfigure) would leave the first processor wired in alongside the new
|
|
148
|
-
// one, double-exporting every span and leaking exporter HTTP pools.
|
|
149
|
-
function _detachPreviousWeflayrProcessor() {
|
|
150
|
-
const previousProcessor = _state.processor;
|
|
151
|
-
if (!previousProcessor) return;
|
|
152
|
-
const tp = _state.tracerProvider;
|
|
153
|
-
const multi = tp && tp._activeSpanProcessor;
|
|
154
|
-
if (multi && Array.isArray(multi._spanProcessors)) {
|
|
155
|
-
multi._spanProcessors = multi._spanProcessors.filter(
|
|
156
|
-
(processor) => processor !== previousProcessor
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
try {
|
|
160
|
-
previousProcessor.shutdown();
|
|
161
|
-
} catch {
|
|
162
|
-
// best-effort cleanup.
|
|
163
|
-
}
|
|
164
|
-
_state.processor = null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
module.exports = { setup, flush, _state };
|
package/tests/api/index.test.js
DELETED
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Tests for the public-API client: auth and base-URL wiring of the
|
|
4
|
-
// client facade, plus the request/response wiring of every generated
|
|
5
|
-
// endpoint (server behavior is covered by the dashboard's app_api tests).
|
|
6
|
-
|
|
7
|
-
const { test } = require('node:test');
|
|
8
|
-
const assert = require('node:assert/strict');
|
|
9
|
-
const http = require('node:http');
|
|
10
|
-
|
|
11
|
-
const weflayr = require('../../src/index');
|
|
12
|
-
const { api } = weflayr;
|
|
13
|
-
|
|
14
|
-
// A client whose server records requests and returns a canned response.
|
|
15
|
-
async function withClientReturning(responseBody, statusCode, run, clientOptions = {}) {
|
|
16
|
-
const seenRequests = [];
|
|
17
|
-
const server = http.createServer((request, response) => {
|
|
18
|
-
let requestBody = '';
|
|
19
|
-
request.on('data', (chunk) => {
|
|
20
|
-
requestBody += chunk;
|
|
21
|
-
});
|
|
22
|
-
request.on('end', () => {
|
|
23
|
-
seenRequests.push({
|
|
24
|
-
method: request.method,
|
|
25
|
-
url: request.url,
|
|
26
|
-
authorization: request.headers.authorization,
|
|
27
|
-
body: requestBody === '' ? null : JSON.parse(requestBody),
|
|
28
|
-
});
|
|
29
|
-
response.statusCode = statusCode;
|
|
30
|
-
response.setHeader('content-type', 'application/json');
|
|
31
|
-
response.end(JSON.stringify(responseBody));
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
await new Promise((resolve) => server.listen(0, resolve));
|
|
35
|
-
const client = api.client({
|
|
36
|
-
apiKey: 'wf-test-key',
|
|
37
|
-
...clientOptions,
|
|
38
|
-
baseUrl: `http://127.0.0.1:${server.address().port}/api`,
|
|
39
|
-
});
|
|
40
|
-
try {
|
|
41
|
-
await run(client, seenRequests);
|
|
42
|
-
} finally {
|
|
43
|
-
server.close();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ── client facade ─────────────────────────────────────────────────────────
|
|
48
|
-
|
|
49
|
-
test('client sends the bearer key to the configured host', async () => {
|
|
50
|
-
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
51
|
-
const { data } = await api.getRevenue({ client, throwOnError: true });
|
|
52
|
-
assert.deepEqual(data, { rows: [] });
|
|
53
|
-
assert.deepEqual(seenRequests, [
|
|
54
|
-
{ method: 'GET', url: '/api/revenue/', authorization: 'Bearer wf-test-key', body: null },
|
|
55
|
-
]);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('weflayr.client is the same function as api.client', () => {
|
|
60
|
-
assert.equal(weflayr.client, api.client);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('client defaults to the production API host', () => {
|
|
64
|
-
const client = api.client({ apiKey: 'wf-test-key' });
|
|
65
|
-
assert.equal(client.getConfig().baseUrl, 'https://app.weflayr.com/api');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('client throws without apiKey', () => {
|
|
69
|
-
const saved = process.env.WEFLAYR_API_KEY;
|
|
70
|
-
delete process.env.WEFLAYR_API_KEY;
|
|
71
|
-
try {
|
|
72
|
-
assert.throws(() => api.client(), /apiKey/);
|
|
73
|
-
} finally {
|
|
74
|
-
if (saved !== undefined) process.env.WEFLAYR_API_KEY = saved;
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test('client picks up apiKey from WEFLAYR_API_KEY env var', async () => {
|
|
79
|
-
process.env.WEFLAYR_API_KEY = 'env-key';
|
|
80
|
-
try {
|
|
81
|
-
await withClientReturning(
|
|
82
|
-
{ rows: [] },
|
|
83
|
-
200,
|
|
84
|
-
async (client, seenRequests) => {
|
|
85
|
-
await api.getRevenue({ client, throwOnError: true });
|
|
86
|
-
assert.equal(seenRequests[0].authorization, 'Bearer env-key');
|
|
87
|
-
},
|
|
88
|
-
{ apiKey: undefined }
|
|
89
|
-
);
|
|
90
|
-
} finally {
|
|
91
|
-
delete process.env.WEFLAYR_API_KEY;
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// ── Revenue ───────────────────────────────────────────────────────────────────
|
|
96
|
-
|
|
97
|
-
test('setRevenue posts the rows in snake_case and parses the booked count', async () => {
|
|
98
|
-
await withClientReturning({ ok: true, rows_inserted: 2 }, 200, async (client, seenRequests) => {
|
|
99
|
-
const rows = [
|
|
100
|
-
{ customerName: 'c_1', amount: 1200.5, month: '2026-06' },
|
|
101
|
-
{ customerName: 'c_2', amount: 800, month: '2026-06' },
|
|
102
|
-
];
|
|
103
|
-
const { data } = await api.setRevenue({ client, throwOnError: true, body: { rows } });
|
|
104
|
-
assert.deepEqual(data, { ok: true, rowsInserted: 2 });
|
|
105
|
-
const request = seenRequests[0];
|
|
106
|
-
assert.equal(request.method, 'POST');
|
|
107
|
-
assert.equal(request.url, '/api/revenue/');
|
|
108
|
-
assert.deepEqual(request.body, {
|
|
109
|
-
rows: [
|
|
110
|
-
{ customer_name: 'c_1', amount: 1200.5, month: '2026-06' },
|
|
111
|
-
{ customer_name: 'c_2', amount: 800, month: '2026-06' },
|
|
112
|
-
],
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test('setRevenue posts the revenueId in snake_case and the upsert flag', async () => {
|
|
118
|
-
await withClientReturning({ ok: true, rows_inserted: 1 }, 200, async (client, seenRequests) => {
|
|
119
|
-
const rows = [
|
|
120
|
-
{
|
|
121
|
-
revenueId: 'b3f2b160-6e12-4f1a-9c3d-0a2e7c9b5d41',
|
|
122
|
-
customerName: 'c_1',
|
|
123
|
-
amount: 1300,
|
|
124
|
-
month: '2026-06',
|
|
125
|
-
},
|
|
126
|
-
];
|
|
127
|
-
const { data } = await api.setRevenue({
|
|
128
|
-
client,
|
|
129
|
-
throwOnError: true,
|
|
130
|
-
body: { upsert: true, rows },
|
|
131
|
-
});
|
|
132
|
-
assert.deepEqual(data, { ok: true, rowsInserted: 1 });
|
|
133
|
-
assert.deepEqual(seenRequests[0].body, {
|
|
134
|
-
upsert: true,
|
|
135
|
-
rows: [
|
|
136
|
-
{
|
|
137
|
-
revenue_id: 'b3f2b160-6e12-4f1a-9c3d-0a2e7c9b5d41',
|
|
138
|
-
customer_name: 'c_1',
|
|
139
|
-
amount: 1300,
|
|
140
|
-
month: '2026-06',
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('getRevenue parses the returned rows into camelCase', async () => {
|
|
148
|
-
const wireRow = {
|
|
149
|
-
customer_name: 'c_1',
|
|
150
|
-
metric_name: 'revenue',
|
|
151
|
-
month: '2026-06',
|
|
152
|
-
amount: 1200.5,
|
|
153
|
-
source: 'api',
|
|
154
|
-
};
|
|
155
|
-
await withClientReturning({ rows: [wireRow] }, 200, async (client) => {
|
|
156
|
-
const { data } = await api.getRevenue({ client, throwOnError: true });
|
|
157
|
-
assert.deepEqual(data.rows, [
|
|
158
|
-
{
|
|
159
|
-
customerName: 'c_1',
|
|
160
|
-
metricName: 'revenue',
|
|
161
|
-
month: '2026-06',
|
|
162
|
-
amount: 1200.5,
|
|
163
|
-
source: 'api',
|
|
164
|
-
},
|
|
165
|
-
]);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test('getRevenue sends the customerNames filter as repeated params', async () => {
|
|
170
|
-
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
171
|
-
const { data } = await api.getRevenue({
|
|
172
|
-
client,
|
|
173
|
-
throwOnError: true,
|
|
174
|
-
query: { customerNames: ['c_1', 'c_2'] },
|
|
175
|
-
});
|
|
176
|
-
assert.deepEqual(data, { rows: [] });
|
|
177
|
-
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
178
|
-
assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test('deleteRevenue sends the customerNames filter and parses the deleted count', async () => {
|
|
183
|
-
await withClientReturning({ ok: true, rows_deleted: 2 }, 200, async (client, seenRequests) => {
|
|
184
|
-
const { data } = await api.deleteRevenue({
|
|
185
|
-
client,
|
|
186
|
-
throwOnError: true,
|
|
187
|
-
query: { customerNames: ['c_1', 'c_2'] },
|
|
188
|
-
});
|
|
189
|
-
assert.deepEqual(data, { ok: true, rowsDeleted: 2 });
|
|
190
|
-
const request = seenRequests[0];
|
|
191
|
-
assert.equal(request.method, 'DELETE');
|
|
192
|
-
const url = new URL(request.url, 'http://x');
|
|
193
|
-
assert.equal(url.pathname, '/api/revenue/');
|
|
194
|
-
assert.deepEqual(url.searchParams.getAll('customer_names'), ['c_1', 'c_2']);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// ── Key metrics ───────────────────────────────────────────────────────────────
|
|
199
|
-
|
|
200
|
-
test('setKeyMetrics posts the rows in snake_case and parses the booked count', async () => {
|
|
201
|
-
await withClientReturning({ ok: true, rows_inserted: 1 }, 200, async (client, seenRequests) => {
|
|
202
|
-
const rows = [{ userName: 'u_1', metricName: 'Meetings booked', amount: 3, month: '2026-06' }];
|
|
203
|
-
const { data } = await api.setKeyMetrics({ client, throwOnError: true, body: { rows } });
|
|
204
|
-
assert.deepEqual(data, { ok: true, rowsInserted: 1 });
|
|
205
|
-
const request = seenRequests[0];
|
|
206
|
-
assert.equal(request.method, 'POST');
|
|
207
|
-
assert.equal(request.url, '/api/key-metrics/');
|
|
208
|
-
assert.deepEqual(request.body, {
|
|
209
|
-
rows: [{ user_name: 'u_1', metric_name: 'Meetings booked', amount: 3, month: '2026-06' }],
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test('getKeyMetrics parses the returned rows into camelCase', async () => {
|
|
215
|
-
const wireRow = {
|
|
216
|
-
user_name: 'u_1',
|
|
217
|
-
metric_name: 'Meetings booked',
|
|
218
|
-
month: '2026-06',
|
|
219
|
-
amount: 3.0,
|
|
220
|
-
source: 'api',
|
|
221
|
-
};
|
|
222
|
-
await withClientReturning({ rows: [wireRow] }, 200, async (client) => {
|
|
223
|
-
const { data } = await api.getKeyMetrics({ client, throwOnError: true });
|
|
224
|
-
assert.deepEqual(data.rows, [
|
|
225
|
-
{
|
|
226
|
-
userName: 'u_1',
|
|
227
|
-
metricName: 'Meetings booked',
|
|
228
|
-
month: '2026-06',
|
|
229
|
-
amount: 3.0,
|
|
230
|
-
source: 'api',
|
|
231
|
-
},
|
|
232
|
-
]);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
test('getKeyMetrics sends the userNames filter as repeated params', async () => {
|
|
237
|
-
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
238
|
-
const { data } = await api.getKeyMetrics({
|
|
239
|
-
client,
|
|
240
|
-
throwOnError: true,
|
|
241
|
-
query: { userNames: ['u_1', 'u_2'] },
|
|
242
|
-
});
|
|
243
|
-
assert.deepEqual(data, { rows: [] });
|
|
244
|
-
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
245
|
-
assert.deepEqual(query.getAll('user_names'), ['u_1', 'u_2']);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test('setKeyMetrics surfaces a 422 and its details', async () => {
|
|
250
|
-
const rejection = {
|
|
251
|
-
error: 'Invalid payload.',
|
|
252
|
-
detail: [{ field: 'rows.0.metric_name', error: 'Unknown metric.' }],
|
|
253
|
-
};
|
|
254
|
-
await withClientReturning(rejection, 422, async (client) => {
|
|
255
|
-
const { error, response } = await api.setKeyMetrics({ client, body: { rows: [] } });
|
|
256
|
-
assert.equal(response.status, 422);
|
|
257
|
-
assert.deepEqual(error, rejection);
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
test('deleteKeyMetrics sends the userNames filter and parses the deleted count', async () => {
|
|
262
|
-
await withClientReturning({ ok: true, rows_deleted: 2 }, 200, async (client, seenRequests) => {
|
|
263
|
-
const { data } = await api.deleteKeyMetrics({
|
|
264
|
-
client,
|
|
265
|
-
throwOnError: true,
|
|
266
|
-
query: { userNames: ['u_1', 'u_2'] },
|
|
267
|
-
});
|
|
268
|
-
assert.deepEqual(data, { ok: true, rowsDeleted: 2 });
|
|
269
|
-
const request = seenRequests[0];
|
|
270
|
-
assert.equal(request.method, 'DELETE');
|
|
271
|
-
const url = new URL(request.url, 'http://x');
|
|
272
|
-
assert.equal(url.pathname, '/api/key-metrics/');
|
|
273
|
-
assert.deepEqual(url.searchParams.getAll('user_names'), ['u_1', 'u_2']);
|
|
274
|
-
});
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// ── Margin ────────────────────────────────────────────────────────────────────
|
|
278
|
-
|
|
279
|
-
test('getMargin parses the returned rows into camelCase', async () => {
|
|
280
|
-
const wireRow = {
|
|
281
|
-
customer_name: 'c_1',
|
|
282
|
-
revenue: 1000.0,
|
|
283
|
-
cost: 200.0,
|
|
284
|
-
margin: 800.0,
|
|
285
|
-
margin_pct: 80.0,
|
|
286
|
-
profitability_status: 'healthy',
|
|
287
|
-
};
|
|
288
|
-
await withClientReturning(
|
|
289
|
-
{ rows: [wireRow], next_cursor: null, has_more: false },
|
|
290
|
-
200,
|
|
291
|
-
async (client) => {
|
|
292
|
-
const { data } = await api.getMargin({ client, throwOnError: true });
|
|
293
|
-
assert.deepEqual(data.rows, [
|
|
294
|
-
{
|
|
295
|
-
customerName: 'c_1',
|
|
296
|
-
revenue: 1000.0,
|
|
297
|
-
cost: 200.0,
|
|
298
|
-
margin: 800.0,
|
|
299
|
-
marginPct: 80.0,
|
|
300
|
-
profitabilityStatus: 'healthy',
|
|
301
|
-
},
|
|
302
|
-
]);
|
|
303
|
-
}
|
|
304
|
-
);
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
test('getMargin sends the cursor and limit query params', async () => {
|
|
308
|
-
await withClientReturning(
|
|
309
|
-
{ rows: [], next_cursor: 'Y19h', has_more: true },
|
|
310
|
-
200,
|
|
311
|
-
async (client, seenRequests) => {
|
|
312
|
-
const { data } = await api.getMargin({
|
|
313
|
-
client,
|
|
314
|
-
throwOnError: true,
|
|
315
|
-
query: { cursor: 'abc123', limit: 2 },
|
|
316
|
-
});
|
|
317
|
-
assert.equal(data.nextCursor, 'Y19h');
|
|
318
|
-
assert.equal(data.hasMore, true);
|
|
319
|
-
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
320
|
-
assert.equal(query.get('cursor'), 'abc123');
|
|
321
|
-
assert.equal(query.get('limit'), '2');
|
|
322
|
-
}
|
|
323
|
-
);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
test('getMargin sends the customerNames filter as repeated params', async () => {
|
|
327
|
-
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
328
|
-
const { data } = await api.getMargin({
|
|
329
|
-
client,
|
|
330
|
-
throwOnError: true,
|
|
331
|
-
query: { customerNames: ['c_1', 'c_2'] },
|
|
332
|
-
});
|
|
333
|
-
assert.deepEqual(data, { rows: [] });
|
|
334
|
-
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
335
|
-
assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
test('getMargin sends the dateFrom/dateTo/applyCredits filters in snake_case', async () => {
|
|
340
|
-
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
341
|
-
await api.getMargin({
|
|
342
|
-
client,
|
|
343
|
-
throwOnError: true,
|
|
344
|
-
query: { dateFrom: '2026-06-01', dateTo: '2026-06-30', applyCredits: true },
|
|
345
|
-
});
|
|
346
|
-
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
347
|
-
assert.equal(query.get('date_from'), '2026-06-01');
|
|
348
|
-
assert.equal(query.get('date_to'), '2026-06-30');
|
|
349
|
-
assert.equal(query.get('apply_credits'), 'true');
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
// ── Customer tags ─────────────────────────────────────────────────────────────
|
|
354
|
-
|
|
355
|
-
test('setCustomersTags posts the customers in snake_case and parses the updated count', async () => {
|
|
356
|
-
await withClientReturning(
|
|
357
|
-
{ ok: true, customers_updated: 1 },
|
|
358
|
-
200,
|
|
359
|
-
async (client, seenRequests) => {
|
|
360
|
-
const customers = [{ customerName: 'c_1', tags: { plan: 'pro' } }];
|
|
361
|
-
const { data } = await api.setCustomersTags({
|
|
362
|
-
client,
|
|
363
|
-
throwOnError: true,
|
|
364
|
-
body: { customers },
|
|
365
|
-
});
|
|
366
|
-
assert.deepEqual(data, { ok: true, customersUpdated: 1 });
|
|
367
|
-
const request = seenRequests[0];
|
|
368
|
-
assert.equal(request.method, 'POST');
|
|
369
|
-
assert.equal(request.url, '/api/customer-tags/');
|
|
370
|
-
assert.deepEqual(request.body, {
|
|
371
|
-
customers: [{ customer_name: 'c_1', tags: { plan: 'pro' } }],
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
test('setCustomersTags leaves tag keys untouched, even when they contain underscores', async () => {
|
|
378
|
-
await withClientReturning(
|
|
379
|
-
{ ok: true, customers_updated: 1 },
|
|
380
|
-
200,
|
|
381
|
-
async (client, seenRequests) => {
|
|
382
|
-
const customers = [{ customerName: 'c_1', tags: { utm_source: 'ads', plan_tier: 'pro' } }];
|
|
383
|
-
await api.setCustomersTags({ client, throwOnError: true, body: { customers } });
|
|
384
|
-
assert.deepEqual(seenRequests[0].body, {
|
|
385
|
-
customers: [{ customer_name: 'c_1', tags: { utm_source: 'ads', plan_tier: 'pro' } }],
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
);
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
test('getCustomersTags parses the returned customers into camelCase', async () => {
|
|
392
|
-
const wireCustomer = { customer_name: 'c_1', tags: { plan: 'pro' } };
|
|
393
|
-
await withClientReturning(
|
|
394
|
-
{ customers: [wireCustomer], next_cursor: null, has_more: false },
|
|
395
|
-
200,
|
|
396
|
-
async (client) => {
|
|
397
|
-
const { data } = await api.getCustomersTags({ client, throwOnError: true });
|
|
398
|
-
assert.deepEqual(data.customers, [{ customerName: 'c_1', tags: { plan: 'pro' } }]);
|
|
399
|
-
}
|
|
400
|
-
);
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
test('getCustomersTags sends the customerNames filter, leaving tag keys untouched', async () => {
|
|
404
|
-
const wireCustomer = { customer_name: 'c_1', tags: { plan: 'pro', utm_source: 'ads' } };
|
|
405
|
-
await withClientReturning(
|
|
406
|
-
{ customers: [wireCustomer], next_cursor: null, has_more: false },
|
|
407
|
-
200,
|
|
408
|
-
async (client, seenRequests) => {
|
|
409
|
-
const { data } = await api.getCustomersTags({
|
|
410
|
-
client,
|
|
411
|
-
throwOnError: true,
|
|
412
|
-
query: { customerNames: ['c_1', 'c_2'] },
|
|
413
|
-
});
|
|
414
|
-
assert.deepEqual(data.customers, [
|
|
415
|
-
{ customerName: 'c_1', tags: { plan: 'pro', utm_source: 'ads' } },
|
|
416
|
-
]);
|
|
417
|
-
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
418
|
-
assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
|
|
419
|
-
}
|
|
420
|
-
);
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
test('deleteCustomersTags sends the customerNames filter and parses the deleted count', async () => {
|
|
424
|
-
await withClientReturning({ ok: true, tags_deleted: 3 }, 200, async (client, seenRequests) => {
|
|
425
|
-
const { data } = await api.deleteCustomersTags({
|
|
426
|
-
client,
|
|
427
|
-
throwOnError: true,
|
|
428
|
-
query: { customerNames: ['c_1', 'c_2'] },
|
|
429
|
-
});
|
|
430
|
-
assert.deepEqual(data, { ok: true, tagsDeleted: 3 });
|
|
431
|
-
const request = seenRequests[0];
|
|
432
|
-
assert.equal(request.method, 'DELETE');
|
|
433
|
-
const url = new URL(request.url, 'http://x');
|
|
434
|
-
assert.equal(url.pathname, '/api/customer-tags/');
|
|
435
|
-
assert.deepEqual(url.searchParams.getAll('customer_names'), ['c_1', 'c_2']);
|
|
436
|
-
});
|
|
437
|
-
});
|