@loadstrike/loadstrike-sdk 1.0.30201 → 1.0.31001
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 +28 -0
- package/dist/cjs/cluster.js +2417 -7
- package/dist/cjs/index.js +13 -2
- package/dist/cjs/iteration-observation-diagnostics.js +513 -0
- package/dist/cjs/iteration-observations.js +884 -0
- package/dist/cjs/load-engine-v2.js +966 -0
- package/dist/cjs/local.js +128 -30
- package/dist/cjs/reporting.js +148 -19
- package/dist/cjs/runtime.js +2514 -196
- package/dist/cjs/sink-retry-policy.js +52 -0
- package/dist/cjs/sinks.js +580 -23
- package/dist/cjs/transports.js +154 -163
- package/dist/esm/cluster.js +2386 -7
- package/dist/esm/index.js +1 -0
- package/dist/esm/iteration-observation-diagnostics.js +508 -0
- package/dist/esm/iteration-observations.js +871 -0
- package/dist/esm/load-engine-v2.js +942 -0
- package/dist/esm/local.js +128 -30
- package/dist/esm/reporting.js +148 -19
- package/dist/esm/runtime.js +2515 -197
- package/dist/esm/sink-retry-policy.js +44 -0
- package/dist/esm/sinks.js +580 -23
- package/dist/esm/transports.js +154 -163
- package/dist/types/cluster.d.ts +379 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/iteration-observation-diagnostics.d.ts +21 -0
- package/dist/types/iteration-observations.d.ts +230 -0
- package/dist/types/load-engine-v2.d.ts +147 -0
- package/dist/types/runtime.d.ts +216 -8
- package/dist/types/sink-retry-policy.d.ts +9 -0
- package/dist/types/sinks.d.ts +73 -0
- package/dist/types/transports.d.ts +6 -8
- package/package.json +3 -4
package/dist/cjs/transports.js
CHANGED
|
@@ -638,10 +638,10 @@ class WebSocketEndpointDefinitionModel extends TrafficEndpointDefinitionModel {
|
|
|
638
638
|
parsed = new URL(url);
|
|
639
639
|
}
|
|
640
640
|
catch {
|
|
641
|
-
throw new Error("Url must be an absolute ws
|
|
641
|
+
throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
|
|
642
642
|
}
|
|
643
643
|
if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
|
|
644
|
-
throw new Error("Url must be an absolute ws
|
|
644
|
+
throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
|
|
645
645
|
}
|
|
646
646
|
if (this.ConnectTimeoutSeconds <= 0) {
|
|
647
647
|
throw new RangeError("ConnectTimeout must be greater than zero.");
|
|
@@ -1275,11 +1275,13 @@ function validateHttpAuthOptionsModel(target) {
|
|
|
1275
1275
|
}
|
|
1276
1276
|
function validateKafkaSaslOptionsModel(target) {
|
|
1277
1277
|
switch (normalizeToken(target.Mechanism)) {
|
|
1278
|
-
case "oauthbearer":
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1278
|
+
case "oauthbearer": {
|
|
1279
|
+
const additionalSettings = target.AdditionalSettings;
|
|
1280
|
+
validateKafkaOAuthBearerCredentials(String(target.AccessToken ?? "").trim() || String(target.OAuthBearerToken ?? "").trim(), String(target.OAuthBearerTokenEndpointUrl ?? "").trim()
|
|
1281
|
+
|| String(target.TokenEndpoint ?? "").trim()
|
|
1282
|
+
|| optionString(additionalSettings, "TokenEndpoint", "tokenEndpoint"), optionString(additionalSettings, "ClientId", "clientId"), optionString(additionalSettings, "ClientSecret", "clientSecret"));
|
|
1282
1283
|
return;
|
|
1284
|
+
}
|
|
1283
1285
|
default:
|
|
1284
1286
|
if (!String(target.Username ?? "").trim()) {
|
|
1285
1287
|
throw new Error("Username must be provided for SASL authentication.");
|
|
@@ -1828,21 +1830,12 @@ class KafkaEndpointAdapter extends CallbackAdapter {
|
|
|
1828
1830
|
if (!this.consumerStartPromise) {
|
|
1829
1831
|
this.consumerStartPromise = (async () => {
|
|
1830
1832
|
const options = this.endpoint.kafka ?? {};
|
|
1831
|
-
const useConfluentClient = shouldUseConfluentKafkaClient(this.endpoint);
|
|
1832
1833
|
const kafka = await createKafkaClient(this.endpoint);
|
|
1833
1834
|
const consumer = kafka.consumer(buildKafkaConsumerOptions(this.endpoint));
|
|
1834
1835
|
await consumer.connect();
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
});
|
|
1839
|
-
}
|
|
1840
|
-
else {
|
|
1841
|
-
await consumer.subscribe({
|
|
1842
|
-
topic: optionString(options, "Topic", "topic"),
|
|
1843
|
-
fromBeginning: optionBoolean(options, true, "StartFromEarliest", "startFromEarliest")
|
|
1844
|
-
});
|
|
1845
|
-
}
|
|
1836
|
+
await consumer.subscribe({
|
|
1837
|
+
topics: [optionString(options, "Topic", "topic")]
|
|
1838
|
+
});
|
|
1846
1839
|
await consumer.run({
|
|
1847
1840
|
eachMessage: async ({ message }) => {
|
|
1848
1841
|
this.queue.push(createBrokerPayload(fromKafkaHeaders(message.headers), bufferToUint8Array(message.value), this.endpoint, headerValue(message.headers, "content-type")));
|
|
@@ -3178,10 +3171,10 @@ function validateWebSocketEndpoint(endpoint, mode) {
|
|
|
3178
3171
|
parsed = new URL(url);
|
|
3179
3172
|
}
|
|
3180
3173
|
catch {
|
|
3181
|
-
throw new Error("Url must be an absolute ws
|
|
3174
|
+
throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
|
|
3182
3175
|
}
|
|
3183
3176
|
if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
|
|
3184
|
-
throw new Error("Url must be an absolute ws
|
|
3177
|
+
throw new Error("Url must be an absolute WebSocket URI using the ws or wss scheme.");
|
|
3185
3178
|
}
|
|
3186
3179
|
const connectMs = optionNumber(options, "ConnectTimeoutMs", "connectTimeoutMs");
|
|
3187
3180
|
const connectSeconds = optionNumber(options, "ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeout", "connectTimeout");
|
|
@@ -3256,7 +3249,8 @@ function validateKafkaEndpoint(endpoint, mode, hasModeDelegate) {
|
|
|
3256
3249
|
function validateKafkaSaslOptions(options) {
|
|
3257
3250
|
const mechanism = optionString(options, "Mechanism", "mechanism") || "Plain";
|
|
3258
3251
|
if (mechanism.toLowerCase() === "oauthbearer") {
|
|
3259
|
-
|
|
3252
|
+
const additionalSettings = asRecordOrEmpty(pickProtocolValue(options, "AdditionalSettings", "additionalSettings"));
|
|
3253
|
+
validateKafkaOAuthBearerCredentials(optionString(options, "AccessToken", "accessToken", "OAuthBearerToken", "oauthBearerToken"), optionString(options, "OAuthBearerTokenEndpointUrl", "oauthBearerTokenEndpointUrl", "TokenEndpoint", "tokenEndpoint") || optionString(additionalSettings, "TokenEndpoint", "tokenEndpoint"), optionString(additionalSettings, "ClientId", "clientId"), optionString(additionalSettings, "ClientSecret", "clientSecret"));
|
|
3260
3254
|
return;
|
|
3261
3255
|
}
|
|
3262
3256
|
requireNonEmptyString(optionString(options, "Username", "username"), "Username must be provided for SASL authentication.");
|
|
@@ -3610,7 +3604,13 @@ function extractJsonPath(value, arrayPath) {
|
|
|
3610
3604
|
if (!normalized) {
|
|
3611
3605
|
return value;
|
|
3612
3606
|
}
|
|
3613
|
-
|
|
3607
|
+
let tokens;
|
|
3608
|
+
try {
|
|
3609
|
+
tokens = safeJsonPathSegments(normalized.replace(/\[(\d+)\]/g, ".$1"));
|
|
3610
|
+
}
|
|
3611
|
+
catch {
|
|
3612
|
+
return null;
|
|
3613
|
+
}
|
|
3614
3614
|
for (const token of tokens) {
|
|
3615
3615
|
if (current == null) {
|
|
3616
3616
|
return null;
|
|
@@ -3620,13 +3620,17 @@ function extractJsonPath(value, arrayPath) {
|
|
|
3620
3620
|
if (!Number.isInteger(index) || index < 0 || index >= current.length) {
|
|
3621
3621
|
return null;
|
|
3622
3622
|
}
|
|
3623
|
-
current = current
|
|
3623
|
+
current = current.at(index);
|
|
3624
3624
|
continue;
|
|
3625
3625
|
}
|
|
3626
|
-
if (!isRecord(current)
|
|
3626
|
+
if (!isRecord(current)) {
|
|
3627
3627
|
return null;
|
|
3628
3628
|
}
|
|
3629
|
-
|
|
3629
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, token);
|
|
3630
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
3631
|
+
return null;
|
|
3632
|
+
}
|
|
3633
|
+
current = descriptor.value;
|
|
3630
3634
|
}
|
|
3631
3635
|
return current;
|
|
3632
3636
|
}
|
|
@@ -3643,7 +3647,7 @@ function parseMaybeJson(value) {
|
|
|
3643
3647
|
}
|
|
3644
3648
|
}
|
|
3645
3649
|
function headersToRecord(value) {
|
|
3646
|
-
const headers =
|
|
3650
|
+
const headers = Object.create(null);
|
|
3647
3651
|
value.forEach((headerValue, key) => {
|
|
3648
3652
|
headers[key] = headerValue;
|
|
3649
3653
|
});
|
|
@@ -3997,11 +4001,22 @@ function extractTrackingValue(payload, selector) {
|
|
|
3997
4001
|
return null;
|
|
3998
4002
|
}
|
|
3999
4003
|
let current = body;
|
|
4000
|
-
|
|
4004
|
+
const path = selector.slice("json:".length).trim().replace(/^\$\./, "");
|
|
4005
|
+
let segments;
|
|
4006
|
+
try {
|
|
4007
|
+
segments = safeJsonPathSegments(path);
|
|
4008
|
+
}
|
|
4009
|
+
catch {
|
|
4010
|
+
return null;
|
|
4011
|
+
}
|
|
4012
|
+
for (const segment of segments) {
|
|
4001
4013
|
if (!isRecord(current)) {
|
|
4002
4014
|
return null;
|
|
4003
4015
|
}
|
|
4004
|
-
current
|
|
4016
|
+
if (!Object.prototype.hasOwnProperty.call(current, segment)) {
|
|
4017
|
+
return null;
|
|
4018
|
+
}
|
|
4019
|
+
current = readOwnJsonProperty(current, segment);
|
|
4005
4020
|
}
|
|
4006
4021
|
return current == null ? null : String(current);
|
|
4007
4022
|
}
|
|
@@ -4032,23 +4047,61 @@ function injectTrackingValue(payload, selector, value) {
|
|
|
4032
4047
|
}
|
|
4033
4048
|
function setJsonBodyValue(body, path, value) {
|
|
4034
4049
|
const target = parseBodyObject(body) ?? {};
|
|
4035
|
-
const clone =
|
|
4036
|
-
const segments = path
|
|
4050
|
+
const clone = cloneJsonRecord(target);
|
|
4051
|
+
const segments = safeJsonPathSegments(path);
|
|
4037
4052
|
if (!segments.length) {
|
|
4038
4053
|
return clone;
|
|
4039
4054
|
}
|
|
4040
4055
|
let current = clone;
|
|
4041
4056
|
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
4042
4057
|
const segment = segments[i];
|
|
4043
|
-
|
|
4058
|
+
let next = readOwnJsonProperty(current, segment);
|
|
4044
4059
|
if (!isRecord(next)) {
|
|
4045
|
-
|
|
4060
|
+
next = {};
|
|
4061
|
+
defineJsonProperty(current, segment, next);
|
|
4046
4062
|
}
|
|
4047
|
-
current =
|
|
4063
|
+
current = next;
|
|
4048
4064
|
}
|
|
4049
|
-
current
|
|
4065
|
+
defineJsonProperty(current, segments[segments.length - 1], value);
|
|
4050
4066
|
return clone;
|
|
4051
4067
|
}
|
|
4068
|
+
const FORBIDDEN_JSON_PATH_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
|
|
4069
|
+
function safeJsonPathSegments(path) {
|
|
4070
|
+
const segments = path.split(".").filter(Boolean);
|
|
4071
|
+
const forbidden = segments.find((segment) => FORBIDDEN_JSON_PATH_SEGMENTS.has(segment));
|
|
4072
|
+
if (forbidden) {
|
|
4073
|
+
throw new Error(`Tracking selector contains forbidden JSON path segment '${forbidden}'.`);
|
|
4074
|
+
}
|
|
4075
|
+
return segments;
|
|
4076
|
+
}
|
|
4077
|
+
function defineJsonProperty(target, key, value) {
|
|
4078
|
+
Object.defineProperty(target, key, {
|
|
4079
|
+
configurable: true,
|
|
4080
|
+
enumerable: true,
|
|
4081
|
+
value,
|
|
4082
|
+
writable: true
|
|
4083
|
+
});
|
|
4084
|
+
}
|
|
4085
|
+
function readOwnJsonProperty(target, key) {
|
|
4086
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
4087
|
+
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
4088
|
+
}
|
|
4089
|
+
function cloneJsonRecord(source) {
|
|
4090
|
+
const target = {};
|
|
4091
|
+
for (const [key, value] of Object.entries(source)) {
|
|
4092
|
+
defineJsonProperty(target, key, cloneJsonValue(value));
|
|
4093
|
+
}
|
|
4094
|
+
return target;
|
|
4095
|
+
}
|
|
4096
|
+
function cloneJsonValue(value) {
|
|
4097
|
+
if (Array.isArray(value)) {
|
|
4098
|
+
return value.map((entry) => cloneJsonValue(entry));
|
|
4099
|
+
}
|
|
4100
|
+
if (isRecord(value)) {
|
|
4101
|
+
return cloneJsonRecord(value);
|
|
4102
|
+
}
|
|
4103
|
+
return value;
|
|
4104
|
+
}
|
|
4052
4105
|
function parseBodyObject(body) {
|
|
4053
4106
|
if (body instanceof Uint8Array) {
|
|
4054
4107
|
return parseBodyObject(new TextDecoder().decode(body));
|
|
@@ -4249,74 +4302,31 @@ function pickProtocolValue(options, ...keys) {
|
|
|
4249
4302
|
}
|
|
4250
4303
|
return undefined;
|
|
4251
4304
|
}
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
.split(",")
|
|
4257
|
-
.map((value) => value.trim())
|
|
4258
|
-
.filter((value) => value.length > 0);
|
|
4259
|
-
const clientOptions = {
|
|
4260
|
-
clientId: `${endpoint.name}-${(0, node_crypto_1.randomUUID)().slice(0, 8)}`,
|
|
4261
|
-
brokers: bootstrapServers,
|
|
4262
|
-
ssl: securityProtocol === "ssl" || securityProtocol === "saslssl"
|
|
4263
|
-
};
|
|
4264
|
-
if (securityProtocol === "saslplaintext" || securityProtocol === "saslssl") {
|
|
4265
|
-
const saslOptions = asRecordOrEmpty(pickProtocolValue(options, "Sasl", "sasl"));
|
|
4266
|
-
const mechanism = (optionString(saslOptions, "Mechanism", "mechanism") || "Plain").toLowerCase();
|
|
4267
|
-
if (mechanism === "oauthbearer") {
|
|
4268
|
-
clientOptions.sasl = {
|
|
4269
|
-
mechanism: "oauthbearer",
|
|
4270
|
-
oauthBearerProvider: async () => ({
|
|
4271
|
-
value: await resolveKafkaOAuthBearerToken(saslOptions)
|
|
4272
|
-
})
|
|
4273
|
-
};
|
|
4274
|
-
}
|
|
4275
|
-
else {
|
|
4276
|
-
clientOptions.sasl = {
|
|
4277
|
-
mechanism: mapKafkaSaslMechanism(mechanism),
|
|
4278
|
-
username: optionString(saslOptions, "Username", "username"),
|
|
4279
|
-
password: optionString(saslOptions, "Password", "password")
|
|
4280
|
-
};
|
|
4281
|
-
}
|
|
4305
|
+
let kafkaClientFactoryForTests = null;
|
|
4306
|
+
async function createKafkaClient(endpoint) {
|
|
4307
|
+
if (kafkaClientFactoryForTests) {
|
|
4308
|
+
return await kafkaClientFactoryForTests(endpoint);
|
|
4282
4309
|
}
|
|
4283
|
-
|
|
4310
|
+
const { KafkaJS } = await Promise.resolve().then(() => __importStar(require("@confluentinc/kafka-javascript")));
|
|
4311
|
+
return new KafkaJS.Kafka(buildConfluentKafkaClientOptions(endpoint));
|
|
4284
4312
|
}
|
|
4285
|
-
|
|
4286
|
-
if (
|
|
4287
|
-
|
|
4288
|
-
|
|
4313
|
+
function validateKafkaOAuthBearerCredentials(directToken, tokenEndpoint, clientId, clientSecret) {
|
|
4314
|
+
if (directToken.trim()) {
|
|
4315
|
+
return;
|
|
4316
|
+
}
|
|
4317
|
+
if (!tokenEndpoint.trim() || !clientId.trim() || !clientSecret.trim()) {
|
|
4318
|
+
throw new Error("Kafka OAuthBearer requires a direct access token or token endpoint with client credentials.");
|
|
4289
4319
|
}
|
|
4290
|
-
const { Kafka } = await Promise.resolve().then(() => __importStar(require("kafkajs")));
|
|
4291
|
-
return new Kafka(buildKafkaClientOptions(endpoint));
|
|
4292
4320
|
}
|
|
4293
4321
|
function buildKafkaConsumerOptions(endpoint) {
|
|
4294
4322
|
const options = endpoint.kafka ?? {};
|
|
4295
4323
|
const groupId = optionString(options, "ConsumerGroupId", "consumerGroupId");
|
|
4296
4324
|
const fromBeginning = optionBoolean(options, true, "StartFromEarliest", "startFromEarliest");
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
};
|
|
4303
|
-
}
|
|
4304
|
-
return { groupId };
|
|
4305
|
-
}
|
|
4306
|
-
function shouldUseConfluentKafkaClient(endpoint) {
|
|
4307
|
-
const options = endpoint.kafka ?? {};
|
|
4308
|
-
const securityProtocol = (optionString(options, "SecurityProtocol", "securityProtocol") || "Plaintext").toLowerCase();
|
|
4309
|
-
if (securityProtocol !== "saslplaintext" && securityProtocol !== "saslssl") {
|
|
4310
|
-
return Object.keys(toStringRecord(pickProtocolValue(options, "ConfluentSettings", "confluentSettings"))).length > 0;
|
|
4311
|
-
}
|
|
4312
|
-
const saslOptions = asRecordOrEmpty(pickProtocolValue(options, "Sasl", "sasl"));
|
|
4313
|
-
const mechanism = (optionString(saslOptions, "Mechanism", "mechanism") || "Plain").toLowerCase();
|
|
4314
|
-
const confluentSettings = toStringRecord(pickProtocolValue(options, "ConfluentSettings", "confluentSettings"));
|
|
4315
|
-
const additionalSettings = toStringRecord(pickProtocolValue(saslOptions, "AdditionalSettings", "additionalSettings"));
|
|
4316
|
-
const hasDirectOAuthToken = mechanism === "oauthbearer" && Boolean(optionString(saslOptions, "AccessToken", "accessToken", "OAuthBearerToken", "oauthBearerToken"));
|
|
4317
|
-
return mechanism === "gssapi"
|
|
4318
|
-
|| Object.keys(confluentSettings).length > 0
|
|
4319
|
-
|| (Object.keys(additionalSettings).length > 0 && !hasDirectOAuthToken);
|
|
4325
|
+
return {
|
|
4326
|
+
"group.id": groupId,
|
|
4327
|
+
"auto.offset.reset": fromBeginning ? "earliest" : "latest",
|
|
4328
|
+
"enable.auto.commit": true
|
|
4329
|
+
};
|
|
4320
4330
|
}
|
|
4321
4331
|
function buildConfluentKafkaClientOptions(endpoint) {
|
|
4322
4332
|
const options = endpoint.kafka ?? {};
|
|
@@ -4332,30 +4342,41 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4332
4342
|
const additionalSettings = toStringRecord(pickProtocolValue(saslOptions, "AdditionalSettings", "additionalSettings"));
|
|
4333
4343
|
base["sasl.mechanism"] = mapConfluentKafkaSaslMechanism(mechanism);
|
|
4334
4344
|
if (mechanism.toLowerCase() === "oauthbearer") {
|
|
4345
|
+
const directToken = optionString(saslOptions, "AccessToken", "accessToken", "OAuthBearerToken", "oauthBearerToken");
|
|
4335
4346
|
const tokenEndpoint = optionString(saslOptions, "OAuthBearerTokenEndpointUrl", "oauthBearerTokenEndpointUrl", "TokenEndpoint", "tokenEndpoint") || optionString(additionalSettings, "TokenEndpoint", "tokenEndpoint");
|
|
4336
|
-
if (tokenEndpoint) {
|
|
4337
|
-
base["sasl.oauthbearer.method"] = "oidc";
|
|
4338
|
-
base["sasl.oauthbearer.token.endpoint.url"] = tokenEndpoint;
|
|
4339
|
-
}
|
|
4340
4347
|
const clientId = optionString(additionalSettings, "ClientId", "clientId");
|
|
4341
|
-
if (clientId) {
|
|
4342
|
-
base["sasl.oauthbearer.client.id"] = clientId;
|
|
4343
|
-
}
|
|
4344
4348
|
const clientSecret = optionString(additionalSettings, "ClientSecret", "clientSecret");
|
|
4345
|
-
if (clientSecret) {
|
|
4346
|
-
base["sasl.oauthbearer.client.secret"] = clientSecret;
|
|
4347
|
-
}
|
|
4348
4349
|
const scope = optionString(additionalSettings, "Scope", "scope");
|
|
4349
|
-
if (scope) {
|
|
4350
|
-
base["sasl.oauthbearer.scope"] = scope;
|
|
4351
|
-
}
|
|
4352
4350
|
const grantType = optionString(additionalSettings, "GrantType", "grantType");
|
|
4353
|
-
if (grantType) {
|
|
4354
|
-
base["sasl.oauthbearer.grant.type"] = grantType;
|
|
4355
|
-
}
|
|
4356
4351
|
const extensions = optionString(additionalSettings, "Extensions", "extensions");
|
|
4357
|
-
if (
|
|
4358
|
-
|
|
4352
|
+
if (directToken) {
|
|
4353
|
+
const principal = optionString(additionalSettings, "Principal", "principal")
|
|
4354
|
+
|| optionString(saslOptions, "Username", "username")
|
|
4355
|
+
|| "loadstrike";
|
|
4356
|
+
base.oauthbearer_token_refresh_cb = async () => ({
|
|
4357
|
+
tokenValue: directToken,
|
|
4358
|
+
lifetime: Date.now() + 60 * 60 * 1000,
|
|
4359
|
+
principal,
|
|
4360
|
+
extensions: parseKafkaOAuthExtensions(extensions)
|
|
4361
|
+
});
|
|
4362
|
+
}
|
|
4363
|
+
else {
|
|
4364
|
+
if (!tokenEndpoint || !clientId || !clientSecret) {
|
|
4365
|
+
throw new Error("Kafka OAuthBearer requires a direct access token or token endpoint with client credentials.");
|
|
4366
|
+
}
|
|
4367
|
+
base["sasl.oauthbearer.method"] = "oidc";
|
|
4368
|
+
base["sasl.oauthbearer.token.endpoint.url"] = tokenEndpoint;
|
|
4369
|
+
base["sasl.oauthbearer.client.id"] = clientId;
|
|
4370
|
+
base["sasl.oauthbearer.client.secret"] = clientSecret;
|
|
4371
|
+
if (scope) {
|
|
4372
|
+
base["sasl.oauthbearer.scope"] = scope;
|
|
4373
|
+
}
|
|
4374
|
+
if (grantType) {
|
|
4375
|
+
base["sasl.oauthbearer.grant.type"] = grantType;
|
|
4376
|
+
}
|
|
4377
|
+
if (extensions) {
|
|
4378
|
+
base["sasl.oauthbearer.extensions"] = extensions;
|
|
4379
|
+
}
|
|
4359
4380
|
}
|
|
4360
4381
|
}
|
|
4361
4382
|
else {
|
|
@@ -4374,7 +4395,9 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4374
4395
|
|| key === "Extensions"
|
|
4375
4396
|
|| key === "extensions"
|
|
4376
4397
|
|| key === "TokenEndpoint"
|
|
4377
|
-
|| key === "tokenEndpoint"
|
|
4398
|
+
|| key === "tokenEndpoint"
|
|
4399
|
+
|| key === "Principal"
|
|
4400
|
+
|| key === "principal") {
|
|
4378
4401
|
continue;
|
|
4379
4402
|
}
|
|
4380
4403
|
base[key] = value;
|
|
@@ -4385,53 +4408,20 @@ function buildConfluentKafkaClientOptions(endpoint) {
|
|
|
4385
4408
|
}
|
|
4386
4409
|
return base;
|
|
4387
4410
|
}
|
|
4388
|
-
|
|
4389
|
-
const
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
const form = new URLSearchParams();
|
|
4401
|
-
form.set("grant_type", "client_credentials");
|
|
4402
|
-
form.set("client_id", clientId);
|
|
4403
|
-
form.set("client_secret", clientSecret);
|
|
4404
|
-
const scope = optionString(additional, "Scope", "scope");
|
|
4405
|
-
if (scope) {
|
|
4406
|
-
form.set("scope", scope);
|
|
4407
|
-
}
|
|
4408
|
-
const response = await fetch(tokenEndpoint, {
|
|
4409
|
-
method: "POST",
|
|
4410
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
4411
|
-
body: form.toString()
|
|
4412
|
-
});
|
|
4413
|
-
if (!response.ok) {
|
|
4414
|
-
throw new Error(`Kafka OAuthBearer token request failed with status ${response.status}.`);
|
|
4415
|
-
}
|
|
4416
|
-
const payload = parseMaybeJson(await response.text());
|
|
4417
|
-
if (!isRecord(payload) || !payload.access_token) {
|
|
4418
|
-
throw new Error("Kafka OAuthBearer token response did not contain access_token.");
|
|
4419
|
-
}
|
|
4420
|
-
return String(payload.access_token);
|
|
4421
|
-
}
|
|
4422
|
-
function mapKafkaSaslMechanism(mechanism) {
|
|
4423
|
-
switch (mechanism) {
|
|
4424
|
-
case "plain":
|
|
4425
|
-
return "plain";
|
|
4426
|
-
case "scramsha256":
|
|
4427
|
-
case "scram-sha-256":
|
|
4428
|
-
return "scram-sha-256";
|
|
4429
|
-
case "scramsha512":
|
|
4430
|
-
case "scram-sha-512":
|
|
4431
|
-
return "scram-sha-512";
|
|
4432
|
-
default:
|
|
4433
|
-
throw new Error(`Unsupported Kafka SASL mechanism: ${mechanism}.`);
|
|
4411
|
+
function parseKafkaOAuthExtensions(value) {
|
|
4412
|
+
const parsed = {};
|
|
4413
|
+
for (const entry of value.split(",")) {
|
|
4414
|
+
const separatorIndex = entry.indexOf("=");
|
|
4415
|
+
if (separatorIndex <= 0) {
|
|
4416
|
+
continue;
|
|
4417
|
+
}
|
|
4418
|
+
const key = entry.slice(0, separatorIndex).trim();
|
|
4419
|
+
if (!key) {
|
|
4420
|
+
continue;
|
|
4421
|
+
}
|
|
4422
|
+
parsed[key] = entry.slice(separatorIndex + 1).trim();
|
|
4434
4423
|
}
|
|
4424
|
+
return parsed;
|
|
4435
4425
|
}
|
|
4436
4426
|
function mapConfluentKafkaSecurityProtocol(protocol) {
|
|
4437
4427
|
switch (protocol.toLowerCase()) {
|
|
@@ -4715,14 +4705,15 @@ exports.__loadstrikeTestExports = {
|
|
|
4715
4705
|
applyHttpAuthHeaders,
|
|
4716
4706
|
buildHttpRequestBody,
|
|
4717
4707
|
buildConfluentKafkaClientOptions,
|
|
4718
|
-
buildKafkaClientOptions,
|
|
4719
4708
|
canonicalizeHttpResponseSource,
|
|
4720
4709
|
createDelegateRequestEndpointView,
|
|
4721
4710
|
createNatsHeaders,
|
|
4722
4711
|
createRedisStreamPayload,
|
|
4723
4712
|
deserializeBrokerPayloadBody,
|
|
4713
|
+
extractJsonPath,
|
|
4724
4714
|
extractTrackingValue,
|
|
4725
4715
|
fromKafkaHeaders,
|
|
4716
|
+
headersToRecord,
|
|
4726
4717
|
headerValue,
|
|
4727
4718
|
attachDotNetTrackingPayloadAliases,
|
|
4728
4719
|
attachPayloadHelpers,
|
|
@@ -4731,7 +4722,6 @@ exports.__loadstrikeTestExports = {
|
|
|
4731
4722
|
injectTrackingValue,
|
|
4732
4723
|
mapConfluentKafkaSaslMechanism,
|
|
4733
4724
|
mapConfluentKafkaSecurityProtocol,
|
|
4734
|
-
mapKafkaSaslMechanism,
|
|
4735
4725
|
parseBodyObject,
|
|
4736
4726
|
partitionFromKey,
|
|
4737
4727
|
payloadBodyAsUtf8,
|
|
@@ -4739,10 +4729,8 @@ exports.__loadstrikeTestExports = {
|
|
|
4739
4729
|
protocolBus: protocolBus,
|
|
4740
4730
|
readFirstRedisStreamEntry,
|
|
4741
4731
|
resolveConnectionMetadata,
|
|
4742
|
-
resolveKafkaOAuthBearerToken,
|
|
4743
4732
|
serializePayloadBody,
|
|
4744
4733
|
setJsonBodyValue,
|
|
4745
|
-
shouldUseConfluentKafkaClient,
|
|
4746
4734
|
toHeaderRecord,
|
|
4747
4735
|
toKafkaHeadersWithContentType,
|
|
4748
4736
|
toSqsMessageAttributes,
|
|
@@ -4750,6 +4738,9 @@ exports.__loadstrikeTestExports = {
|
|
|
4750
4738
|
setSqsClientFactoryForTests: (factory) => {
|
|
4751
4739
|
sqsClientFactoryForTests = factory;
|
|
4752
4740
|
},
|
|
4741
|
+
setKafkaClientFactoryForTests: (factory) => {
|
|
4742
|
+
kafkaClientFactoryForTests = factory;
|
|
4743
|
+
},
|
|
4753
4744
|
validateHttpEndpoint,
|
|
4754
4745
|
validateTrackingSelectorPath
|
|
4755
4746
|
};
|