1135 lines
55 KiB
JavaScript
1135 lines
55 KiB
JavaScript
"use strict";
|
|
/*
|
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
* SPDX-License-Identifier: Apache-2.0.
|
|
*/
|
|
var __extends = (this && this.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
};
|
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
});
|
|
};
|
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
function step(op) {
|
|
if (f) throw new TypeError("Generator is already executing.");
|
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
switch (op[0]) {
|
|
case 0: case 1: t = op; break;
|
|
case 4: _.label++; return { value: op[1], done: false };
|
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
default:
|
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
if (t[2]) _.ops.pop();
|
|
_.trys.pop(); continue;
|
|
}
|
|
op = body.call(thisArg, _);
|
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
}
|
|
};
|
|
var __values = (this && this.__values) || function(o) {
|
|
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
if (m) return m.call(o);
|
|
if (o && typeof o.length === "number") return {
|
|
next: function () {
|
|
if (o && i >= o.length) o = void 0;
|
|
return { value: o && o[i++], done: !o };
|
|
}
|
|
};
|
|
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
};
|
|
var __read = (this && this.__read) || function (o, n) {
|
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
if (!m) return o;
|
|
var i = m.call(o), r, ar = [], e;
|
|
try {
|
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
}
|
|
catch (error) { e = { error: error }; }
|
|
finally {
|
|
try {
|
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
}
|
|
finally { if (e) throw e.error; }
|
|
}
|
|
return ar;
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.RequestResponseClient = exports.StreamingOperationBase = void 0;
|
|
/**
|
|
*
|
|
* @packageDocumentation
|
|
* @module mqtt_request_response
|
|
* @mergeTarget
|
|
*
|
|
*/
|
|
var protocol_client_adapter = __importStar(require("./mqtt_request_response/protocol_adapter"));
|
|
var subscription_manager = __importStar(require("./mqtt_request_response/subscription_manager"));
|
|
var mqtt_request_response = __importStar(require("../common/mqtt_request_response"));
|
|
var mqtt_request_response_internal = __importStar(require("../common/mqtt_request_response_internal"));
|
|
var event_1 = require("../common/event");
|
|
var error_1 = require("./error");
|
|
var promise_1 = require("../common/promise");
|
|
var io = __importStar(require("../common/io"));
|
|
var mqtt_shared = __importStar(require("../common/mqtt_shared"));
|
|
__exportStar(require("../common/mqtt_request_response"), exports);
|
|
var OperationState;
|
|
(function (OperationState) {
|
|
/* creation -> in event loop enqueue */
|
|
OperationState[OperationState["None"] = 0] = "None";
|
|
/* in event loop queue -> non blocked response from subscription manager */
|
|
OperationState[OperationState["Queued"] = 1] = "Queued";
|
|
/* subscribing response from sub manager -> subscription success/failure event */
|
|
OperationState[OperationState["PendingSubscription"] = 2] = "PendingSubscription";
|
|
/* (request only) subscription success -> (publish failure OR correlated response received) */
|
|
OperationState[OperationState["PendingResponse"] = 3] = "PendingResponse";
|
|
/* (streaming only) subscription success -> (operation finished OR subscription ended event) */
|
|
OperationState[OperationState["Subscribed"] = 4] = "Subscribed";
|
|
/* (streaming only) (subscription failure OR subscription ended) -> operation close/terminate */
|
|
OperationState[OperationState["Terminal"] = 5] = "Terminal";
|
|
})(OperationState || (OperationState = {}));
|
|
function operationStateToString(state) {
|
|
switch (state) {
|
|
case OperationState.None:
|
|
return "None";
|
|
case OperationState.Queued:
|
|
return "Queued";
|
|
case OperationState.PendingSubscription:
|
|
return "PendingSubscription";
|
|
case OperationState.PendingResponse:
|
|
return "PendingResponse";
|
|
case OperationState.Subscribed:
|
|
return "Subscribed";
|
|
case OperationState.Terminal:
|
|
return "Terminal";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
var OperationType;
|
|
(function (OperationType) {
|
|
OperationType[OperationType["RequestResponse"] = 0] = "RequestResponse";
|
|
OperationType[OperationType["Streaming"] = 1] = "Streaming";
|
|
})(OperationType || (OperationType = {}));
|
|
function areClientOptionsValid(options) {
|
|
if (!options) {
|
|
return false;
|
|
}
|
|
if (!options.maxRequestResponseSubscriptions) {
|
|
return false;
|
|
}
|
|
if (!Number.isInteger(options.maxRequestResponseSubscriptions)) {
|
|
return false;
|
|
}
|
|
if (options.maxRequestResponseSubscriptions < 2) {
|
|
return false;
|
|
}
|
|
if (!options.maxStreamingSubscriptions) {
|
|
return false;
|
|
}
|
|
if (!Number.isInteger(options.maxStreamingSubscriptions)) {
|
|
return false;
|
|
}
|
|
if (options.operationTimeoutInSeconds) {
|
|
if (!Number.isInteger(options.operationTimeoutInSeconds)) {
|
|
return false;
|
|
}
|
|
if (options.operationTimeoutInSeconds <= 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
/**
|
|
* An AWS MQTT service streaming operation. A streaming operation listens to messages on
|
|
* a particular topic, deserializes them using a service model, and emits the modeled data as Javascript events.
|
|
*/
|
|
var StreamingOperationBase = /** @class */ (function (_super) {
|
|
__extends(StreamingOperationBase, _super);
|
|
function StreamingOperationBase(options) {
|
|
var _this = _super.call(this) || this;
|
|
_this.state = mqtt_request_response_internal.StreamingOperationState.None;
|
|
_this.internalOptions = options;
|
|
return _this;
|
|
}
|
|
/**
|
|
* Triggers the streaming operation to start listening to the configured stream of events. Has no effect on an
|
|
* already-open operation. It is an error to attempt to re-open a closed streaming operation.
|
|
*/
|
|
StreamingOperationBase.prototype.open = function () {
|
|
if (this.state == mqtt_request_response_internal.StreamingOperationState.None) {
|
|
this.internalOptions.open();
|
|
this.state = mqtt_request_response_internal.StreamingOperationState.Open;
|
|
}
|
|
else if (this.state == mqtt_request_response_internal.StreamingOperationState.Closed) {
|
|
throw new error_1.CrtError("MQTT streaming operation already closed");
|
|
}
|
|
};
|
|
/**
|
|
* Stops a streaming operation from listening to the configured stream of events
|
|
*/
|
|
StreamingOperationBase.prototype.close = function () {
|
|
if (this.state != mqtt_request_response_internal.StreamingOperationState.Closed) {
|
|
this.state = mqtt_request_response_internal.StreamingOperationState.Closed;
|
|
this.internalOptions.close();
|
|
}
|
|
};
|
|
StreamingOperationBase.prototype.on = function (event, listener) {
|
|
_super.prototype.on.call(this, event, listener);
|
|
return this;
|
|
};
|
|
/**
|
|
* Event emitted when the stream's subscription status changes.
|
|
*
|
|
* Listener type: {@link SubscriptionStatusListener}
|
|
*
|
|
* @event
|
|
*/
|
|
StreamingOperationBase.SUBSCRIPTION_STATUS = 'subscriptionStatus';
|
|
/**
|
|
* Event emitted when a stream message is received
|
|
*
|
|
* Listener type: {@link IncomingPublishListener}
|
|
*
|
|
* @event
|
|
*/
|
|
StreamingOperationBase.INCOMING_PUBLISH = 'incomingPublish';
|
|
return StreamingOperationBase;
|
|
}(event_1.BufferedEventEmitter));
|
|
exports.StreamingOperationBase = StreamingOperationBase;
|
|
var StreamingOperationInternal = /** @class */ (function (_super) {
|
|
__extends(StreamingOperationInternal, _super);
|
|
function StreamingOperationInternal(options) {
|
|
return _super.call(this, options) || this;
|
|
}
|
|
StreamingOperationInternal.newInternal = function (options) {
|
|
var operation = new StreamingOperationInternal(options);
|
|
return operation;
|
|
};
|
|
StreamingOperationInternal.prototype.triggerIncomingPublishEvent = function (publishEvent) {
|
|
var _this = this;
|
|
process.nextTick(function () {
|
|
_this.emit(StreamingOperationBase.INCOMING_PUBLISH, publishEvent);
|
|
});
|
|
};
|
|
StreamingOperationInternal.prototype.triggerSubscriptionStatusUpdateEvent = function (statusEvent) {
|
|
var _this = this;
|
|
process.nextTick(function () {
|
|
_this.emit(StreamingOperationBase.SUBSCRIPTION_STATUS, statusEvent);
|
|
});
|
|
};
|
|
return StreamingOperationInternal;
|
|
}(StreamingOperationBase));
|
|
/**
|
|
* Native implementation of an MQTT-based request-response client tuned for AWS MQTT services.
|
|
*
|
|
* Supports streaming operations (listen to a stream of modeled events from an MQTT topic) and request-response
|
|
* operations (performs the subscribes, publish, and incoming publish correlation and error checking needed to
|
|
* perform simple request-response operations over MQTT).
|
|
*/
|
|
var RequestResponseClient = /** @class */ (function (_super) {
|
|
__extends(RequestResponseClient, _super);
|
|
function RequestResponseClient(protocolClientAdapter, options) {
|
|
var _this = this;
|
|
var _a;
|
|
if (!areClientOptionsValid(options)) {
|
|
throw new error_1.CrtError("Invalid client options passed to RequestResponseClient constructor");
|
|
}
|
|
_this = _super.call(this) || this;
|
|
_this.nextOperationId = 1;
|
|
_this.state = mqtt_request_response_internal.RequestResponseClientState.Ready;
|
|
_this.operations = new Map();
|
|
_this.streamingOperationsByTopicFilter = new Map(); // topic filter -> set of operation ids
|
|
_this.correlationTokenPathsByResponsePaths = new Map(); // response topic -> response path entry
|
|
_this.operationsByCorrelationToken = new Map(); // correlation token -> operation id
|
|
_this.operationQueue = new Array;
|
|
_this.operationTimeoutInSeconds = (_a = options.operationTimeoutInSeconds) !== null && _a !== void 0 ? _a : 60;
|
|
_this.protocolClientAdapter = protocolClientAdapter;
|
|
_this.protocolClientAdapter.addListener(protocol_client_adapter.ProtocolClientAdapter.PUBLISH_COMPLETION, _this.handlePublishCompletionEvent.bind(_this));
|
|
_this.protocolClientAdapter.addListener(protocol_client_adapter.ProtocolClientAdapter.CONNECTION_STATUS, _this.handleConnectionStatusEvent.bind(_this));
|
|
_this.protocolClientAdapter.addListener(protocol_client_adapter.ProtocolClientAdapter.INCOMING_PUBLISH, _this.handleIncomingPublishEvent.bind(_this));
|
|
var config = {
|
|
maxRequestResponseSubscriptions: options.maxRequestResponseSubscriptions,
|
|
maxStreamingSubscriptions: options.maxStreamingSubscriptions,
|
|
operationTimeoutInSeconds: _this.operationTimeoutInSeconds,
|
|
};
|
|
_this.subscriptionManager = new subscription_manager.SubscriptionManager(protocolClientAdapter, config);
|
|
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.SUBSCRIBE_SUCCESS, _this.handleSubscribeSuccessEvent.bind(_this));
|
|
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.SUBSCRIBE_FAILURE, _this.handleSubscribeFailureEvent.bind(_this));
|
|
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.SUBSCRIPTION_ENDED, _this.handleSubscriptionEndedEvent.bind(_this));
|
|
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.STREAMING_SUBSCRIPTION_ESTABLISHED, _this.handleStreamingSubscriptionEstablishedEvent.bind(_this));
|
|
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.STREAMING_SUBSCRIPTION_LOST, _this.handleStreamingSubscriptionLostEvent.bind(_this));
|
|
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.STREAMING_SUBSCRIPTION_HALTED, _this.handleStreamingSubscriptionHaltedEvent.bind(_this));
|
|
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.SUBSCRIPTION_ORPHANED, _this.handleSubscriptionOrphanedEvent.bind(_this));
|
|
_this.subscriptionManager.addListener(subscription_manager.SubscriptionManager.UNSUBSCRIBE_COMPLETE, _this.handleUnsubscribeCompleteEvent.bind(_this));
|
|
return _this;
|
|
}
|
|
/**
|
|
* Creates a new MQTT service request-response client that uses an MQTT5 client as the protocol implementation.
|
|
*
|
|
* @param protocolClient protocol client to use for all operations
|
|
* @param options configuration options for the desired request-response client
|
|
*/
|
|
RequestResponseClient.newFromMqtt5 = function (protocolClient, options) {
|
|
if (!protocolClient) {
|
|
throw new error_1.CrtError("protocol client is null");
|
|
}
|
|
var adapter = protocol_client_adapter.ProtocolClientAdapter.newFrom5(protocolClient);
|
|
var client = new RequestResponseClient(adapter, options);
|
|
return client;
|
|
};
|
|
/**
|
|
* Creates a new MQTT service request-response client that uses an MQTT311 client as the protocol implementation.
|
|
*
|
|
* @param protocolClient protocol client to use for all operations
|
|
* @param options configuration options for the desired request-response client
|
|
*/
|
|
RequestResponseClient.newFromMqtt311 = function (protocolClient, options) {
|
|
if (!protocolClient) {
|
|
throw new error_1.CrtError("protocol client is null");
|
|
}
|
|
var adapter = protocol_client_adapter.ProtocolClientAdapter.newFrom311(protocolClient);
|
|
var client = new RequestResponseClient(adapter, options);
|
|
return client;
|
|
};
|
|
/**
|
|
* Triggers cleanup of native resources associated with the request-response client. Closing a client will fail
|
|
* all incomplete requests and close all outstanding streaming operations.
|
|
*
|
|
* This must be called when finished with a client; otherwise, native resources will leak.
|
|
*/
|
|
RequestResponseClient.prototype.close = function () {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Closed) {
|
|
io.logInfo(RequestResponseClient.logSubject, "closing MQTT RequestResponseClient");
|
|
this.state = mqtt_request_response_internal.RequestResponseClientState.Closed;
|
|
this.closeAllOperations();
|
|
this.protocolClientAdapter.close();
|
|
this.subscriptionManager.close();
|
|
}
|
|
};
|
|
/**
|
|
* Submits a request to the request-response client.
|
|
*
|
|
* @param requestOptions description of the request to perform
|
|
*
|
|
* Returns a promise that resolves to a response to the request or an error describing how the request attempt
|
|
* failed.
|
|
*
|
|
* A "successful" request-response execution flow is defined as "the service sent a response payload that
|
|
* correlates with the request payload." Upon deserialization (which is the responsibility of the service model
|
|
* client, one layer up), such a payload may actually indicate a failure.
|
|
*/
|
|
RequestResponseClient.prototype.submitRequest = function (requestOptions) {
|
|
return __awaiter(this, void 0, void 0, function () {
|
|
var resultPromise, id, operation;
|
|
var _this = this;
|
|
return __generator(this, function (_a) {
|
|
resultPromise = (0, promise_1.newLiftedPromise)();
|
|
if (this.state == mqtt_request_response_internal.RequestResponseClientState.Closed) {
|
|
resultPromise.reject(new error_1.CrtError("MQTT request-response client has already been closed"));
|
|
return [2 /*return*/, resultPromise.promise];
|
|
}
|
|
try {
|
|
validateRequestOptions(requestOptions);
|
|
}
|
|
catch (err) {
|
|
resultPromise.reject(err);
|
|
return [2 /*return*/, resultPromise.promise];
|
|
}
|
|
id = this.nextOperationId;
|
|
this.nextOperationId++;
|
|
operation = {
|
|
id: id,
|
|
type: OperationType.RequestResponse,
|
|
state: OperationState.Queued,
|
|
pendingSubscriptionCount: requestOptions.subscriptionTopicFilters.length,
|
|
inClientTables: false,
|
|
options: requestOptions,
|
|
resultPromise: resultPromise,
|
|
};
|
|
this.operations.set(id, operation);
|
|
this.operationQueue.push(id);
|
|
setTimeout(function () {
|
|
try {
|
|
_this.completeRequestResponseOperationWithError(id, new error_1.CrtError("Operation timeout"));
|
|
}
|
|
catch (err) {
|
|
;
|
|
}
|
|
}, this.operationTimeoutInSeconds * 1000);
|
|
this.wakeServiceTask();
|
|
io.logInfo(RequestResponseClient.logSubject, "request-response operation with id \"".concat(id, "\" submitted to operation queue"));
|
|
return [2 /*return*/, resultPromise.promise];
|
|
});
|
|
});
|
|
};
|
|
/**
|
|
* Creates a new streaming operation from a set of configuration options. A streaming operation provides a
|
|
* mechanism for listening to a specific event stream from an AWS MQTT-based service.
|
|
*
|
|
* @param streamOptions configuration options for the streaming operation
|
|
*
|
|
* browser/node implementers are covariant by returning an implementation of IStreamingOperation. This split
|
|
* is necessary because event listening (which streaming operations need) cannot be modeled on an interface.
|
|
*/
|
|
RequestResponseClient.prototype.createStream = function (streamOptions) {
|
|
var _this = this;
|
|
if (this.state == mqtt_request_response_internal.RequestResponseClientState.Closed) {
|
|
throw new error_1.CrtError("MQTT request-response client has already been closed");
|
|
}
|
|
validateStreamingOptions(streamOptions);
|
|
var id = this.nextOperationId;
|
|
this.nextOperationId++;
|
|
var internalOptions = {
|
|
open: function () { _this.openStreamingOperation(id); },
|
|
close: function () { _this.closeStreamingOperation(id); },
|
|
};
|
|
var internalOperation = StreamingOperationInternal.newInternal(internalOptions);
|
|
var operation = {
|
|
id: id,
|
|
type: OperationType.Streaming,
|
|
state: OperationState.None,
|
|
pendingSubscriptionCount: 1,
|
|
inClientTables: false,
|
|
options: streamOptions,
|
|
operation: internalOperation
|
|
};
|
|
this.operations.set(id, operation);
|
|
return internalOperation;
|
|
};
|
|
RequestResponseClient.prototype.canOperationDequeue = function (operation) {
|
|
var _a;
|
|
if (operation.type != OperationType.RequestResponse) {
|
|
return true;
|
|
}
|
|
var rrOperation = operation;
|
|
var correlationToken = (_a = rrOperation.options.correlationToken) !== null && _a !== void 0 ? _a : "";
|
|
return !this.operationsByCorrelationToken.has(correlationToken);
|
|
};
|
|
RequestResponseClient.buildSuscriptionListFromOperation = function (operation) {
|
|
if (operation.type == OperationType.RequestResponse) {
|
|
var rrOperation = operation;
|
|
return rrOperation.options.subscriptionTopicFilters;
|
|
}
|
|
else {
|
|
var streamingOperation = operation;
|
|
return new Array(streamingOperation.options.subscriptionTopicFilter);
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.addOperationToInProgressTables = function (operation) {
|
|
var e_1, _a;
|
|
var _b;
|
|
if (operation.type == OperationType.Streaming) {
|
|
var streamingOperation = operation;
|
|
var filter = streamingOperation.options.subscriptionTopicFilter;
|
|
var existingSet = this.streamingOperationsByTopicFilter.get(filter);
|
|
if (!existingSet) {
|
|
existingSet = new Set();
|
|
this.streamingOperationsByTopicFilter.set(filter, existingSet);
|
|
io.logDebug(RequestResponseClient.logSubject, "adding topic filter \"".concat(filter, "\" to streaming subscriptions table"));
|
|
}
|
|
existingSet.add(operation.id);
|
|
io.logDebug(RequestResponseClient.logSubject, "adding operation ".concat(operation.id, " to streaming subscriptions table under topic filter \"").concat(filter, "\""));
|
|
}
|
|
else {
|
|
var rrOperation = operation;
|
|
var correlationToken = (_b = rrOperation.options.correlationToken) !== null && _b !== void 0 ? _b : "";
|
|
this.operationsByCorrelationToken.set(correlationToken, operation.id);
|
|
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(operation.id, " registered with correlation token \"").concat(correlationToken, "\""));
|
|
try {
|
|
for (var _c = __values(rrOperation.options.responsePaths), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
var path = _d.value;
|
|
var existingEntry = this.correlationTokenPathsByResponsePaths.get(path.topic);
|
|
if (!existingEntry) {
|
|
existingEntry = {
|
|
refCount: 0
|
|
};
|
|
if (path.correlationTokenJsonPath) {
|
|
existingEntry.correlationTokenPath = path.correlationTokenJsonPath.split('.');
|
|
}
|
|
this.correlationTokenPathsByResponsePaths.set(path.topic, existingEntry);
|
|
io.logDebug(RequestResponseClient.logSubject, "adding response path \"".concat(path.topic, "\" to response path table"));
|
|
}
|
|
existingEntry.refCount++;
|
|
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(operation.id, " adding reference to response path \"").concat(path.topic, "\""));
|
|
}
|
|
}
|
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
finally {
|
|
try {
|
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
}
|
|
finally { if (e_1) throw e_1.error; }
|
|
}
|
|
}
|
|
operation.inClientTables = true;
|
|
};
|
|
RequestResponseClient.prototype.handleAcquireSubscriptionResult = function (operation, result) {
|
|
if (result == subscription_manager.AcquireSubscriptionResult.Failure || result == subscription_manager.AcquireSubscriptionResult.NoCapacity) {
|
|
this.completeOperationWithError(operation.id, new error_1.CrtError("Acquire subscription error: ".concat(subscription_manager.acquireSubscriptionResultToString(result))));
|
|
return;
|
|
}
|
|
this.addOperationToInProgressTables(operation);
|
|
if (result == subscription_manager.AcquireSubscriptionResult.Subscribing) {
|
|
this.changeOperationState(operation, OperationState.PendingSubscription);
|
|
return;
|
|
}
|
|
if (operation.type == OperationType.Streaming) {
|
|
this.changeOperationState(operation, OperationState.Subscribed);
|
|
var streamingOperation = operation;
|
|
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
|
|
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionEstablished
|
|
});
|
|
}
|
|
else {
|
|
this.applyRequestResponsePublish(operation);
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.service = function () {
|
|
this.serviceTask = undefined;
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
this.subscriptionManager.purge();
|
|
io.logDebug(RequestResponseClient.logSubject, "servicing operation queue with ".concat(this.operationQueue.length, " entries"));
|
|
while (this.operationQueue.length > 0) {
|
|
var headId = this.operationQueue[0];
|
|
var operation = this.operations.get(headId);
|
|
if (!operation) {
|
|
this.operationQueue.shift();
|
|
continue;
|
|
}
|
|
if (!this.canOperationDequeue(operation)) {
|
|
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(headId, " cannot be dequeued"));
|
|
break;
|
|
}
|
|
var acquireOptions = {
|
|
topicFilters: RequestResponseClient.buildSuscriptionListFromOperation(operation),
|
|
operationId: headId,
|
|
type: (operation.type == OperationType.RequestResponse) ? subscription_manager.SubscriptionType.RequestResponse : subscription_manager.SubscriptionType.EventStream,
|
|
};
|
|
var acquireResult = this.subscriptionManager.acquireSubscription(acquireOptions);
|
|
io.logDebug(RequestResponseClient.logSubject, "servicing queued operation ".concat(operation.id, " yielded acquire subscription result of \"").concat(subscription_manager.acquireSubscriptionResultToString(acquireResult), "\""));
|
|
if (acquireResult == subscription_manager.AcquireSubscriptionResult.Blocked) {
|
|
break;
|
|
}
|
|
this.operationQueue.shift();
|
|
this.handleAcquireSubscriptionResult(operation, acquireResult);
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.clearServiceTask = function () {
|
|
if (this.serviceTask) {
|
|
clearTimeout(this.serviceTask.serviceTask);
|
|
this.serviceTask = undefined;
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.tryScheduleServiceTask = function (serviceTime) {
|
|
var _this = this;
|
|
if (this.serviceTask) {
|
|
if (serviceTime >= this.serviceTask.nextServiceTime) {
|
|
return;
|
|
}
|
|
this.clearServiceTask();
|
|
}
|
|
var futureMs = Math.max(0, Date.now() - serviceTime);
|
|
this.serviceTask = {
|
|
serviceTask: setTimeout(function () { _this.service(); }, futureMs),
|
|
nextServiceTime: serviceTime,
|
|
};
|
|
io.logDebug(RequestResponseClient.logSubject, "service task scheduled for execution in ".concat(futureMs, " MS"));
|
|
};
|
|
RequestResponseClient.prototype.wakeServiceTask = function () {
|
|
this.tryScheduleServiceTask(Date.now());
|
|
};
|
|
RequestResponseClient.prototype.closeAllOperations = function () {
|
|
var e_2, _a;
|
|
var operations = Array.from(this.operations).map(function (_a) {
|
|
var _b = __read(_a, 2), key = _b[0], value = _b[1];
|
|
return key;
|
|
});
|
|
try {
|
|
for (var operations_1 = __values(operations), operations_1_1 = operations_1.next(); !operations_1_1.done; operations_1_1 = operations_1.next()) {
|
|
var id = operations_1_1.value;
|
|
this.completeOperationWithError(id, new error_1.CrtError("Request-response client closed"));
|
|
}
|
|
}
|
|
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
finally {
|
|
try {
|
|
if (operations_1_1 && !operations_1_1.done && (_a = operations_1.return)) _a.call(operations_1);
|
|
}
|
|
finally { if (e_2) throw e_2.error; }
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.removeStreamingOperationFromTopicFilterSet = function (topicFilter, id) {
|
|
var operationSet = this.streamingOperationsByTopicFilter.get(topicFilter);
|
|
if (!operationSet) {
|
|
return;
|
|
}
|
|
operationSet.delete(id);
|
|
io.logDebug(RequestResponseClient.logSubject, "removed operation ".concat(id, " from streaming topic filter table entry for \"").concat(topicFilter, "\""));
|
|
if (operationSet.size > 0) {
|
|
return;
|
|
}
|
|
this.streamingOperationsByTopicFilter.delete(topicFilter);
|
|
io.logDebug(RequestResponseClient.logSubject, "removed streaming topic filter table entry for \"".concat(topicFilter, "\""));
|
|
};
|
|
RequestResponseClient.prototype.decRefResponsePaths = function (topic) {
|
|
var pathEntry = this.correlationTokenPathsByResponsePaths.get(topic);
|
|
if (!pathEntry) {
|
|
return;
|
|
}
|
|
pathEntry.refCount--;
|
|
io.logDebug(RequestResponseClient.logSubject, "dec-refing response path entry for \"".concat(topic, "\", ").concat(pathEntry.refCount, " references left"));
|
|
if (pathEntry.refCount < 1) {
|
|
io.logDebug(RequestResponseClient.logSubject, "removing response path entry for \"".concat(topic, "\""));
|
|
this.correlationTokenPathsByResponsePaths.delete(topic);
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.removeRequestResponseOperation = function (operation) {
|
|
var e_3, _a;
|
|
var _b;
|
|
io.logDebug(RequestResponseClient.logSubject, "removing request-response operation ".concat(operation.id, " from client state"));
|
|
this.operations.delete(operation.id);
|
|
if (operation.inClientTables) {
|
|
try {
|
|
for (var _c = __values(operation.options.responsePaths), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
var responsePath = _d.value;
|
|
this.decRefResponsePaths(responsePath.topic);
|
|
}
|
|
}
|
|
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
finally {
|
|
try {
|
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
}
|
|
finally { if (e_3) throw e_3.error; }
|
|
}
|
|
var correlationToken = (_b = operation.options.correlationToken) !== null && _b !== void 0 ? _b : "";
|
|
this.operationsByCorrelationToken.delete(correlationToken);
|
|
}
|
|
var releaseOptions = {
|
|
topicFilters: operation.options.subscriptionTopicFilters,
|
|
operationId: operation.id,
|
|
};
|
|
this.subscriptionManager.releaseSubscription(releaseOptions);
|
|
};
|
|
RequestResponseClient.prototype.removeStreamingOperation = function (operation) {
|
|
io.logDebug(RequestResponseClient.logSubject, "removing streaming operation ".concat(operation.id, " from client state"));
|
|
this.operations.delete(operation.id);
|
|
if (operation.inClientTables) {
|
|
this.removeStreamingOperationFromTopicFilterSet(operation.options.subscriptionTopicFilter, operation.id);
|
|
}
|
|
var releaseOptions = {
|
|
topicFilters: new Array(operation.options.subscriptionTopicFilter),
|
|
operationId: operation.id,
|
|
};
|
|
this.subscriptionManager.releaseSubscription(releaseOptions);
|
|
};
|
|
RequestResponseClient.prototype.removeOperation = function (id) {
|
|
var operation = this.operations.get(id);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
if (operation.type == OperationType.RequestResponse) {
|
|
this.removeRequestResponseOperation(operation);
|
|
}
|
|
else {
|
|
this.removeStreamingOperation(operation);
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.completeRequestResponseOperationWithError = function (id, err) {
|
|
var operation = this.operations.get(id);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
io.logInfo(RequestResponseClient.logSubject, "request-response operation ".concat(id, " completed with error: \"").concat(JSON.stringify(err), "\""));
|
|
this.removeOperation(id);
|
|
if (operation.type != OperationType.RequestResponse) {
|
|
return;
|
|
}
|
|
var rrOperation = operation;
|
|
var promise = rrOperation.resultPromise;
|
|
promise.reject(err);
|
|
};
|
|
RequestResponseClient.prototype.haltStreamingOperationWithError = function (id, err) {
|
|
var operation = this.operations.get(id);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
io.logInfo(RequestResponseClient.logSubject, "streaming operation ".concat(id, " halted with error: \"").concat(JSON.stringify(err), "\""));
|
|
this.removeOperation(id);
|
|
if (operation.type != OperationType.Streaming) {
|
|
return;
|
|
}
|
|
var streamingOperation = operation;
|
|
if (operation.state != OperationState.Terminal && operation.state != OperationState.None) {
|
|
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
|
|
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionHalted,
|
|
error: err
|
|
});
|
|
}
|
|
this.changeOperationState(operation, OperationState.Terminal);
|
|
// this is mostly a no-op except it's the only way we can guarantee that the streaming operation state also gets
|
|
// flipped to closed
|
|
streamingOperation.operation.close();
|
|
};
|
|
RequestResponseClient.prototype.completeOperationWithError = function (id, err) {
|
|
var operation = this.operations.get(id);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
if (operation.type == OperationType.RequestResponse) {
|
|
this.completeRequestResponseOperationWithError(id, err);
|
|
}
|
|
else {
|
|
this.haltStreamingOperationWithError(id, err);
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.completeRequestResponseOperationWithResponse = function (id, responseTopic, payload) {
|
|
var operation = this.operations.get(id);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
io.logInfo(RequestResponseClient.logSubject, "request-response operation ".concat(id, " successfully completed with response\""));
|
|
this.removeOperation(id);
|
|
if (operation.type != OperationType.RequestResponse) {
|
|
return;
|
|
}
|
|
var rrOperation = operation;
|
|
var promise = rrOperation.resultPromise;
|
|
promise.resolve({
|
|
topic: responseTopic,
|
|
payload: payload
|
|
});
|
|
};
|
|
RequestResponseClient.prototype.handlePublishCompletionEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
var id = event.completionData;
|
|
if (event.err) {
|
|
this.completeRequestResponseOperationWithError(id, event.err);
|
|
}
|
|
else {
|
|
io.logDebug(RequestResponseClient.logSubject, "request-response operation ".concat(id, " successfully published request payload\""));
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.handleConnectionStatusEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
if (event.status == protocol_client_adapter.ConnectionState.Connected && this.operationQueue.length > 0) {
|
|
this.wakeServiceTask();
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.handleIncomingPublishEventStreaming = function (event, operations) {
|
|
var e_4, _a;
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
try {
|
|
for (var operations_2 = __values(operations), operations_2_1 = operations_2.next(); !operations_2_1.done; operations_2_1 = operations_2.next()) {
|
|
var id = operations_2_1.value;
|
|
var operation = this.operations.get(id);
|
|
if (!operation) {
|
|
continue;
|
|
}
|
|
if (operation.type != OperationType.Streaming) {
|
|
continue;
|
|
}
|
|
var streamingOperation = operation;
|
|
streamingOperation.operation.triggerIncomingPublishEvent({
|
|
topic: event.topic,
|
|
payload: event.payload
|
|
});
|
|
}
|
|
}
|
|
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
finally {
|
|
try {
|
|
if (operations_2_1 && !operations_2_1.done && (_a = operations_2.return)) _a.call(operations_2);
|
|
}
|
|
finally { if (e_4) throw e_4.error; }
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.handleIncomingPublishEventRequestResponse = function (event, responsePathEntry) {
|
|
var e_5, _a;
|
|
io.logDebug(RequestResponseClient.logSubject, "processing incoming publish event on response path topic \"".concat(event.topic, "\""));
|
|
if (!event.payload) {
|
|
io.logError(RequestResponseClient.logSubject, "incoming publish on response path topic \"".concat(event.topic, "\" has no payload"));
|
|
return;
|
|
}
|
|
try {
|
|
var correlationToken = undefined;
|
|
if (!responsePathEntry.correlationTokenPath) {
|
|
correlationToken = "";
|
|
}
|
|
else {
|
|
var payloadAsString = new TextDecoder().decode(new Uint8Array(event.payload));
|
|
var payloadAsJson = JSON.parse(payloadAsString);
|
|
var segmentValue = payloadAsJson;
|
|
try {
|
|
for (var _b = __values(responsePathEntry.correlationTokenPath), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
var segment = _c.value;
|
|
var segmentPropertyValue = segmentValue[segment];
|
|
if (!segmentPropertyValue) {
|
|
io.logError(RequestResponseClient.logSubject, "incoming publish on response path topic \"".concat(event.topic, "\" does not have a correlation token at the expected JSON path"));
|
|
break;
|
|
}
|
|
segmentValue = segmentValue[segment];
|
|
}
|
|
}
|
|
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
finally {
|
|
try {
|
|
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
}
|
|
finally { if (e_5) throw e_5.error; }
|
|
}
|
|
if (segmentValue && typeof (segmentValue) === "string") {
|
|
correlationToken = segmentValue;
|
|
}
|
|
}
|
|
if (correlationToken === undefined) {
|
|
io.logError(RequestResponseClient.logSubject, "A valid correlation token could not be inferred for incoming publish on response path topic \"".concat(event.topic, "\""));
|
|
return;
|
|
}
|
|
var id = this.operationsByCorrelationToken.get(correlationToken);
|
|
if (!id) {
|
|
io.logDebug(RequestResponseClient.logSubject, "incoming publish on response path topic \"".concat(event.topic, "\" with correlation token \"").concat(correlationToken, "\" does not have an originating request entry"));
|
|
return;
|
|
}
|
|
this.completeRequestResponseOperationWithResponse(id, event.topic, event.payload);
|
|
}
|
|
catch (err) {
|
|
io.logError(RequestResponseClient.logSubject, "incoming publish on response path topic \"".concat(event.topic, "\" triggered exception: ").concat(JSON.stringify(err)));
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.handleIncomingPublishEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
var responsePathEntry = this.correlationTokenPathsByResponsePaths.get(event.topic);
|
|
if (responsePathEntry) {
|
|
this.handleIncomingPublishEventRequestResponse(event, responsePathEntry);
|
|
}
|
|
var streamingOperationSet = this.streamingOperationsByTopicFilter.get(event.topic);
|
|
if (streamingOperationSet) {
|
|
this.handleIncomingPublishEventStreaming(event, streamingOperationSet);
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.handleSubscribeSuccessEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
io.logDebug(RequestResponseClient.logSubject, "subscribe success event received for operation ".concat(event.operationId, " using topic filter \"").concat(event.topicFilter, "\""));
|
|
var operation = this.operations.get(event.operationId);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
var rrOperation = operation;
|
|
rrOperation.pendingSubscriptionCount--;
|
|
if (rrOperation.pendingSubscriptionCount === 0) {
|
|
this.applyRequestResponsePublish(rrOperation);
|
|
}
|
|
else {
|
|
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(event.operationId, " has ").concat(rrOperation.pendingSubscriptionCount, " pending subscriptions left"));
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.handleSubscribeFailureEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
io.logDebug(RequestResponseClient.logSubject, "subscribe failure event received for operation ".concat(event.operationId, " using topic filter \"").concat(event.topicFilter, "\""));
|
|
this.completeRequestResponseOperationWithError(event.operationId, new error_1.CrtError("Subscribe failure"));
|
|
};
|
|
RequestResponseClient.prototype.handleSubscriptionEndedEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
io.logDebug(RequestResponseClient.logSubject, "subscription ended event received for operation ".concat(event.operationId, " using topic filter \"").concat(event.topicFilter, "\""));
|
|
this.completeRequestResponseOperationWithError(event.operationId, new error_1.CrtError("Subscription Ended Early"));
|
|
};
|
|
RequestResponseClient.prototype.handleStreamingSubscriptionEstablishedEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
var operation = this.operations.get(event.operationId);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
if (operation.state == OperationState.Terminal) {
|
|
return;
|
|
}
|
|
if (operation.type != OperationType.Streaming) {
|
|
return;
|
|
}
|
|
var streamingOperation = operation;
|
|
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
|
|
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionEstablished
|
|
});
|
|
this.changeOperationState(operation, OperationState.Subscribed);
|
|
};
|
|
RequestResponseClient.prototype.handleStreamingSubscriptionLostEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
var operation = this.operations.get(event.operationId);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
if (operation.state == OperationState.Terminal) {
|
|
return;
|
|
}
|
|
if (operation.type != OperationType.Streaming) {
|
|
return;
|
|
}
|
|
var streamingOperation = operation;
|
|
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
|
|
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionLost,
|
|
});
|
|
};
|
|
RequestResponseClient.prototype.handleStreamingSubscriptionHaltedEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
var operation = this.operations.get(event.operationId);
|
|
if (!operation) {
|
|
return;
|
|
}
|
|
if (operation.state == OperationState.Terminal) {
|
|
return;
|
|
}
|
|
if (operation.type != OperationType.Streaming) {
|
|
return;
|
|
}
|
|
var streamingOperation = operation;
|
|
streamingOperation.operation.triggerSubscriptionStatusUpdateEvent({
|
|
type: mqtt_request_response.SubscriptionStatusEventType.SubscriptionHalted,
|
|
error: new error_1.CrtError("Subscription Failure for topic filter \"".concat(event.topicFilter, "\""))
|
|
});
|
|
this.changeOperationState(operation, OperationState.Terminal);
|
|
};
|
|
RequestResponseClient.prototype.handleSubscriptionOrphanedEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
io.logDebug(RequestResponseClient.logSubject, "subscription orphaned event received for topic filter \"".concat(event.topicFilter, "\""));
|
|
this.wakeServiceTask();
|
|
};
|
|
RequestResponseClient.prototype.handleUnsubscribeCompleteEvent = function (event) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
return;
|
|
}
|
|
io.logDebug(RequestResponseClient.logSubject, "unsubscribe completion event received for topic filter \"".concat(event.topicFilter, "\""));
|
|
this.wakeServiceTask();
|
|
};
|
|
RequestResponseClient.prototype.changeOperationState = function (operation, state) {
|
|
if (state == operation.state) {
|
|
return;
|
|
}
|
|
io.logDebug(RequestResponseClient.logSubject, "operation ".concat(operation.id, " changing state from \"").concat(operationStateToString(operation.state), "\" to \"").concat(operationStateToString(state), "\""));
|
|
operation.state = state;
|
|
};
|
|
RequestResponseClient.prototype.applyRequestResponsePublish = function (operation) {
|
|
var publishOptions = {
|
|
topic: operation.options.publishTopic,
|
|
payload: operation.options.payload,
|
|
timeoutInSeconds: this.operationTimeoutInSeconds,
|
|
completionData: operation.id
|
|
};
|
|
try {
|
|
io.logDebug(RequestResponseClient.logSubject, "submitting publish for request-response operation ".concat(operation.id));
|
|
this.protocolClientAdapter.publish(publishOptions);
|
|
this.changeOperationState(operation, OperationState.PendingResponse);
|
|
}
|
|
catch (err) {
|
|
var errorStringified = JSON.stringify(err);
|
|
this.completeRequestResponseOperationWithError(operation.id, new error_1.CrtError("Publish error: \"".concat(errorStringified, "\"")));
|
|
io.logError(RequestResponseClient.logSubject, "request-response operation ".concat(operation.id, " synchronously failed publish step due to error: ").concat(errorStringified));
|
|
}
|
|
};
|
|
RequestResponseClient.prototype.openStreamingOperation = function (id) {
|
|
if (this.state != mqtt_request_response_internal.RequestResponseClientState.Ready) {
|
|
throw new error_1.CrtError("Attempt to open streaming operation with id \"".concat(id, "\" after client closed"));
|
|
}
|
|
var operation = this.operations.get(id);
|
|
if (!operation) {
|
|
throw new error_1.CrtError("Attempt to open untracked streaming operation with id \"".concat(id, "\""));
|
|
}
|
|
if (operation.state != OperationState.None) {
|
|
throw new error_1.CrtError("Attempt to open already-opened streaming operation with id \"".concat(id, "\""));
|
|
}
|
|
operation.state = OperationState.Queued;
|
|
this.operationQueue.push(id);
|
|
this.wakeServiceTask();
|
|
io.logInfo(RequestResponseClient.logSubject, "streaming operation with id \"".concat(id, "\" submitted to operation queue"));
|
|
};
|
|
RequestResponseClient.prototype.closeStreamingOperation = function (id) {
|
|
var operation = this.operations.get(id);
|
|
if (!operation) {
|
|
// don't throw here intentionally; there's a bit of a recursive tangle with closing streaming operations
|
|
return;
|
|
}
|
|
this.haltStreamingOperationWithError(id, new error_1.CrtError("Streaming operation closed"));
|
|
};
|
|
RequestResponseClient.logSubject = "RequestResponseClient";
|
|
return RequestResponseClient;
|
|
}(event_1.BufferedEventEmitter));
|
|
exports.RequestResponseClient = RequestResponseClient;
|
|
function validateResponsePath(responsePath) {
|
|
if (!mqtt_shared.isValidTopic(responsePath.topic)) {
|
|
throw new error_1.CrtError("\"".concat(JSON.stringify(responsePath.topic), ")\" is not a valid topic"));
|
|
}
|
|
if (responsePath.correlationTokenJsonPath) {
|
|
if (typeof (responsePath.correlationTokenJsonPath) !== 'string') {
|
|
throw new error_1.CrtError("\"".concat(JSON.stringify(responsePath.correlationTokenJsonPath), ")\" is not a valid correlation token path"));
|
|
}
|
|
}
|
|
}
|
|
function validateRequestOptions(requestOptions) {
|
|
var e_6, _a, e_7, _b;
|
|
if (!requestOptions) {
|
|
throw new error_1.CrtError("Invalid request options - null options");
|
|
}
|
|
if (!requestOptions.subscriptionTopicFilters) {
|
|
throw new error_1.CrtError("Invalid request options - null subscriptionTopicFilters");
|
|
}
|
|
if (!Array.isArray(requestOptions.subscriptionTopicFilters)) {
|
|
throw new error_1.CrtError("Invalid request options - subscriptionTopicFilters is not an array");
|
|
}
|
|
if (requestOptions.subscriptionTopicFilters.length === 0) {
|
|
throw new error_1.CrtError("Invalid request options - subscriptionTopicFilters is empty");
|
|
}
|
|
try {
|
|
for (var _c = __values(requestOptions.subscriptionTopicFilters), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
var topicFilter = _d.value;
|
|
if (!mqtt_shared.isValidTopicFilter(topicFilter)) {
|
|
throw new error_1.CrtError("Invalid request options - \"".concat(JSON.stringify(topicFilter), "\" is not a valid topic filter"));
|
|
}
|
|
}
|
|
}
|
|
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
finally {
|
|
try {
|
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
}
|
|
finally { if (e_6) throw e_6.error; }
|
|
}
|
|
if (!requestOptions.responsePaths) {
|
|
throw new error_1.CrtError("Invalid request options - null responsePaths");
|
|
}
|
|
if (!Array.isArray(requestOptions.responsePaths)) {
|
|
throw new error_1.CrtError("Invalid request options - responsePaths is not an array");
|
|
}
|
|
if (requestOptions.responsePaths.length === 0) {
|
|
throw new error_1.CrtError("Invalid request options - responsePaths is empty");
|
|
}
|
|
try {
|
|
for (var _e = __values(requestOptions.responsePaths), _f = _e.next(); !_f.done; _f = _e.next()) {
|
|
var responsePath = _f.value;
|
|
try {
|
|
validateResponsePath(responsePath);
|
|
}
|
|
catch (err) {
|
|
throw new error_1.CrtError("Invalid request options - invalid response path: ".concat(JSON.stringify(err)));
|
|
}
|
|
}
|
|
}
|
|
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
finally {
|
|
try {
|
|
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
|
}
|
|
finally { if (e_7) throw e_7.error; }
|
|
}
|
|
if (!requestOptions.publishTopic) {
|
|
throw new error_1.CrtError("Invalid request options - null publishTopic");
|
|
}
|
|
if (!mqtt_shared.isValidTopic(requestOptions.publishTopic)) {
|
|
throw new error_1.CrtError("Invalid request options - \"".concat(JSON.stringify(requestOptions.publishTopic), "\" is not a valid topic"));
|
|
}
|
|
if (!requestOptions.payload) {
|
|
throw new error_1.CrtError("Invalid request options - null payload");
|
|
}
|
|
if (requestOptions.payload.byteLength == 0) {
|
|
throw new error_1.CrtError("Invalid request options - empty payload");
|
|
}
|
|
if (requestOptions.correlationToken) {
|
|
if (typeof (requestOptions.correlationToken) !== 'string') {
|
|
throw new error_1.CrtError("Invalid request options - correlationToken is not a string");
|
|
}
|
|
}
|
|
else if (requestOptions.correlationToken === null) {
|
|
throw new error_1.CrtError("Invalid request options - correlationToken null");
|
|
}
|
|
}
|
|
function validateStreamingOptions(streamOptions) {
|
|
if (!streamOptions) {
|
|
throw new error_1.CrtError("Invalid streaming options - null options");
|
|
}
|
|
if (!streamOptions.subscriptionTopicFilter) {
|
|
throw new error_1.CrtError("Invalid streaming options - null subscriptionTopicFilter");
|
|
}
|
|
if (typeof (streamOptions.subscriptionTopicFilter) !== 'string') {
|
|
throw new error_1.CrtError("Invalid streaming options - subscriptionTopicFilter not a string");
|
|
}
|
|
if (!mqtt_shared.isValidTopicFilter(streamOptions.subscriptionTopicFilter)) {
|
|
throw new error_1.CrtError("Invalid streaming options - subscriptionTopicFilter not a valid topic filter");
|
|
}
|
|
}
|
|
//# sourceMappingURL=mqtt_request_response.js.map
|