constructor()

in provisioning/device/src/polling_state_machine.ts [25:251]


  constructor(transport: PollingTransport) {
    super();

    this._transport = transport;

    this._fsm = new machina.Fsm({
      namespace: 'provisioning-client-polling',
      initialState: 'disconnected',
      states: {
        disconnected: {
          _onEnter: (err, result, response, callback) => {
            if (callback) {
              callback(err, result, response);
            }
          },
          register: (request, callback) => this._fsm.transition('sendingRegistrationRequest', request, callback),
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_025: [ If `cancel` is called while disconnected, it shall immediately call its `callback`. ] */
          cancel: (_cancelledOpErr, callback) => callback(),
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_031: [ If `disconnect` is called while disconnected, it shall immediately call its `callback`. ] */
          disconnect: (callback) => callback()
        },
        idle: {
          _onEnter: (err, result, response, callback) => callback(err, result, response),
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_030: [ If `cancel` is called while the transport is connected but idle, it shall immediately call its `callback`. ] */
          cancel: (_cancelledOpErr, callback) => callback(),
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_032: [ If `disconnect` is called while while the transport is connected but idle, it shall call `PollingTransport.disconnect` and call it's `callback` passing the results of the transport operation. ] */
          disconnect: (callback) => this._fsm.transition('disconnecting', callback),
          register: (request, callback) => this._fsm.transition('sendingRegistrationRequest', request, callback)
        },
        sendingRegistrationRequest: {
          _onEnter: (request, callback) => {
            this._queryTimer = setTimeout(() => {
              /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_036: [ If `PollingTransport.registrationRequest` does not call its callback within `ProvisioningDeviceConstants.defaultTimeoutInterval` ms, register shall with with a `TimeoutError` error. ] */
              if (this._currentOperationCallback === callback) {
                debugErrors('timeout while sending request');
                /* tslint:disable:no-empty */
                this._fsm.handle('cancel', new errors.TimeoutError(), () => { });
              }
            }, ProvisioningDeviceConstants.defaultTimeoutInterval);
            /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_012: [ `register` shall call `PollingTransport.registrationRequest`. ] */
            this._currentOperationCallback = callback;
            this._transport.registrationRequest(request, (err, result, response, pollingInterval) => {
              clearTimeout(this._queryTimer);
              // Check if the operation is still pending before transitioning.  We might be in a different state now and we don't want to mess that up.
              if (this._currentOperationCallback === callback) {
                this._fsm.transition('responseReceived', err, request, result, response, pollingInterval, callback);
              } else if (this._currentOperationCallback) {
                debugErrors('Unexpected: received unexpected response for cancelled operation');
              }
            });
          },
          cancel: (cancelledOpErr, callback) => this._fsm.transition('cancelling', cancelledOpErr, callback),
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_033: [ If `disconnect` is called while in the middle of a `registrationRequest` operation, the operation shall be cancelled and the transport shall be disconnected. ] */
          disconnect: (callback) => {
            this._fsm.handle('cancel', new errors.OperationCancelledError(''), (_err) => {
              this._fsm.handle('disconnect', callback);
            });
          },
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_024: [ If `register` is called while a different request is in progress, it shall fail with an `InvalidOperationError`. ] */
          register: (_request, callback) => callback(new errors.InvalidOperationError('another operation is in progress'))
        },
        responseReceived: {
          _onEnter: (err, request, result, response, pollingInterval, callback) => {
            if (err) {
              /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_013: [ If `PollingTransport.registrationRequest` fails, `register` shall fail. ] */
              /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_019: [ If `PollingTransport.queryOperationStatus` fails, `register` shall fail. ] */
              this._fsm.transition('responseError', err, result, response, callback);
            } else {
              debug('received response from service:' + JSON.stringify(result));
              switch (result.status.toLowerCase()) {
                case 'registering': {
                  /*Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_06_001: [If `PollingTransport.registrationRequest` succeeds with status==registering, `register` shall wait, then attempt `PollingTransport.registrationRequest` again.] */
                  this._fsm.transition('waitingToPoll', request, null, pollingInterval, callback);
                  break;
                }
                case 'assigned': {
                  this._fsm.transition('responseComplete', result, response, callback);
                  break;
                }
                case 'assigning': {
                  /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_015: [ If `PollingTransport.registrationRequest` succeeds with status==Assigning, it shall begin polling for operation status requests. ] */
                  /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_021: [ If `PollingTransport.queryOperationStatus` succeeds with status==Assigning, `register` shall begin polling for operation status requests. ] */
                  this._fsm.transition('waitingToPoll', request, result.operationId, pollingInterval, callback);
                  break;
                }
                case 'failed': {
                  /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_028: [ If `TransportHandlers.registrationRequest` succeeds with status==Failed, it shall fail with a `DeviceRegistrationFailedError` error ] */
                  /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_029: [ If `TransportHandlers.queryOperationStatus` succeeds with status==Failed, it shall fail with a `DeviceRegistrationFailedError` error ] */
                  let err = new errors.DeviceRegistrationFailedError('registration failed');
                  (err as any).result = result;
                  (err as any).response = response;
                  this._fsm.transition('responseError', err, result, response, callback);
                  break;
                }
                default: {
                  /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_016: [ If `PollingTransport.registrationRequest` succeeds returns with an unknown status, `register` shall fail with a `SyntaxError` and pass the response body and the protocol-specific result to the `callback`. ] */
                  /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_022: [ If `PollingTransport.queryOperationStatus` succeeds with an unknown status, `register` shall fail with a `SyntaxError` and pass the response body and the protocol-specific result to the `callback`. ] */
                  let err = new SyntaxError('status is ' + result.status);
                  (err as any).result = result;
                  (err as any).response = response;
                  this._fsm.transition('responseError', err, result, response, callback);
                  break;
                }
              }
            }
          },
          '*': () => this._fsm.deferUntilTransition()
        },
        responseComplete: {
          _onEnter: (result, response, callback) => {
            this._currentOperationCallback = null;
            /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_014: [ If `PollingTransport.registrationRequest` succeeds with status==Assigned, it shall call `callback` with null, the response body, and the protocol-specific result. ] */
            /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_020: [ If `PollingTransport.queryOperationStatus` succeeds with status==Assigned, `register` shall complete and pass the body of the response and the protocol-specific result to the `callback`. ] */
           this._fsm.transition('idle', null, result, response, callback);
          },
          '*': () => this._fsm.deferUntilTransition()
        },
        responseError: {
          _onEnter: (err, result, response, callback) => {
            debugErrors('Response error: ' + err);
            this._currentOperationCallback = null;
            this._fsm.transition('idle', err, result, response, callback);
          },
          '*': () => this._fsm.deferUntilTransition()
        },
        waitingToPoll: {
          _onEnter: (request, operationId, pollingInterval, callback) => {
            debug('waiting for ' + pollingInterval + ' ms');
            this._pollingTimer = setTimeout(() => {
              if (operationId) {
                this._fsm.transition('polling', request, operationId, pollingInterval, callback);
              } else {
                //
                // retrying registration must necessarily have a falsy operation id.  If they had
                // an operation id they wouldn't need to retry!
                //
                this._fsm.transition('sendingRegistrationRequest', request, callback);
              }
            }, pollingInterval);
          },
          cancel: (cancelledOpErr, callback) => {
            /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_027: [ If a registration is in progress, `cancel` shall cause that registration to fail with an `OperationCancelledError`. ] */
            clearTimeout(this._pollingTimer);
            this._pollingTimer = null;
            this._fsm.transition('cancelling', cancelledOpErr, callback);
          },
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_034: [ If `disconnect` is called while the state machine is waiting to poll, the current operation shall be cancelled and the transport shall be disconnected. ] */
          disconnect: (callback) => {
            this._fsm.handle('cancel', new errors.OperationCancelledError(''), (_err) => {
              this._fsm.handle('disconnect', callback);
            });
          },
          register: (_request, callback) => callback(new errors.InvalidOperationError('another operation is in progress'))
        },
        polling: {
          _onEnter: (request, operationId, _pollingInterval, callback) => {
            /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_037: [ If `PollingTransport.queryOperationStatus` does not call its callback within `ProvisioningDeviceConstants.defaultTimeoutInterval` ms, register shall with with a `TimeoutError` error. ] */
            this._queryTimer = setTimeout(() => {
              debug('timeout while query');
              if (this._currentOperationCallback === callback) {
                /* tslint:disable:no-empty */
                this._fsm.handle('cancel', new errors.TimeoutError(), () => { });
              }
            }, ProvisioningDeviceConstants.defaultTimeoutInterval);
            /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_018: [ When the polling interval elapses, `register` shall call `PollingTransport.queryOperationStatus`. ] */
            this._transport.queryOperationStatus(request, operationId, (err, result, response, pollingInterval) => {
              clearTimeout(this._queryTimer);
              // Check if the operation is still pending before transitioning.  We might be in a different state now and we don't want to mess that up.
              if (this._currentOperationCallback === callback) {
                this._fsm.transition('responseReceived', err, request, result, response, pollingInterval, callback);
              } else if (this._currentOperationCallback) {
                debugErrors('Unexpected: received unexpected response for cancelled operation');
              }
            });
          },
          cancel: (cancelledOpErr, callback) => this._fsm.transition('cancelling', cancelledOpErr, callback),
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_035: [ If `disconnect` is called while in the middle of a `queryOperationStatus` operation, the operation shall be cancelled and the transport shall be disconnected. ] */
          disconnect: (callback) => {
            this._fsm.handle('cancel', new errors.OperationCancelledError(''), (_err) => {
              this._fsm.handle('disconnect', callback);
            });
          },
          /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_024: [ If `register` is called while a different request is in progress, it shall fail with an `InvalidOperationError`. ] */
          register: (_request, callback) => callback(new errors.InvalidOperationError('another operation is in progress'))
        },
        cancelling: {
          _onEnter: (cancelledOpErr, callback) => {
            /* Codes_SRS_NODE_PROVISIONING_TRANSPORT_STATE_MACHINE_18_027: [ If a registration is in progress, `cancel` shall cause that registration to fail with an `OperationCancelledError`. ] */
            if (this._currentOperationCallback) {
              let _callback = this._currentOperationCallback;
              this._currentOperationCallback = null;
              process.nextTick(() => _callback(cancelledOpErr));
            }
            this._transport.cancel((cancelErr) => {
              if (cancelErr) {
                debugErrors('error received from transport during cancel: ' + cancelErr);
              }
              this._fsm.transition('idle', cancelErr, null, null, callback);
            });
          },
          '*': () => this._fsm.deferUntilTransition()
        },
        disconnecting: {
          _onEnter: (callback) => {
            if (this._pollingTimer) {
              debug('cancelling polling timer');
              clearTimeout(this._pollingTimer);
            }

            if (this._queryTimer) {
              debug('cancelling query timer');
              clearTimeout(this._queryTimer);
            }

            this._transport.disconnect((err) => {
              this._fsm.transition('disconnected', err, null, null, callback);
            });
          },
          '*': () => this._fsm.deferUntilTransition()
        }
      }
    });

    this._fsm.on('transition', (data) => {
      debug('completed transition from ' + data.fromState + ' to ' + data.toState);
    });
  }