text: import { Validate, Guid } from '@microsoft/sp-core-library';
  import { _EngagementLogger, _LogEntry, _LogType } from '@microsoft/sp-diagnostics';
  import { _DynamicDataUtilities } from '@microsoft/sp-dynamic-data';
  import * as lodash from '@microsoft/sp-lodash-subset';
  /**
   * The Dynamic Data Provider allows components to consume Dynamic Data.
   * It allows components to request Dynamic Data sources and register/unregister to those.
   *
   * @public
   */
  var DynamicDataProvider = /** @class */ (function () {
      function DynamicDataProvider() {
          this._isDisposed = false;
          this._isInitialized = false;
          this._isSourcesChangedEventListenerAdded = false;
      }
      /**
       * Initializes the Dynamic Data Provider for a specific component.
       *
       * @param component - Component that is going to use the Dynamic Data Provider.
       *
       * @internal
       */
      DynamicDataProvider.prototype._initialize = function (component, serviceScope) {
          Validate.isNotNullOrUndefined(component, 'component');
          Validate.isNotNullOrUndefined(serviceScope, 'serviceScope');
          this._component = component;
          // Initialize all the required datastructures
          this._pendingRegistrations = new Map();
          this._dynamicDataManager = serviceScope.consume(_DynamicDataUtilities.IDynamicDataManagerServiceKey);
          this._isInitialized = true;
          // Method bindings
          this._onSourcesChanged = this._onSourcesChanged.bind(this);
      };
      /**
       * Disposes the Dynamic Data Provider.
       */
      DynamicDataProvider.prototype.dispose = function () {
          if (!this._isDisposed) {
              delete this._pendingRegistrations;
              this._dynamicDataManager.sourcesChangedEvent.remove(this._component, this._onSourcesChanged);
              delete this._dynamicDataManager;
              delete this._component;
              this._isInitialized = false;
              this._isDisposed = true;
          }
      };
      Object.defineProperty(DynamicDataProvider.prototype, "isDisposed", {
          /**
           * Returns true if the Dynamic Data Provider is disposed.
           */
          get: function () {
              return this._isDisposed;
          },
          enumerable: false,
          configurable: true
      });
      Object.defineProperty(DynamicDataProvider.prototype, "_isProviderInitialized", {
          /**
           * Returns true if the Dynamic Data Provider is intialized.
           * @internal
           */
          get: function () {
              return this._isInitialized;
          },
          enumerable: false,
          configurable: true
      });
      Object.defineProperty(DynamicDataProvider.prototype, "_dynamicDataSourceId", {
          /**
           * Returns the dynamic data source id for the current instance of the component.
           * @internal
           */
          get: function () {
              return this._component._dynamicDataSourceId;
          },
          enumerable: false,
          configurable: true
      });
      /**
       * Registers a callback for updates when a Dynamic Data source is changed.
       * This happens when any property is updated within the source.
       *
       * @privateRemarks
       * We register the source, only if the source is available else defer the registration
       * to when the availableSources change.
       *
       * @param sourceId - Id of the Dynamic Data Source.
       * @param callback - Function to execute when the source updates its data.
       */
      DynamicDataProvider.prototype.registerSourceChanged = function (sourceId, callback) {
          this._assertNotDisposed();
          this._assertInitialized();
          Validate.isNonemptyString(sourceId, 'sourceId');
          Validate.isNotNullOrUndefined(callback, 'callback');
          var availableSourceIds = this.getAvailableSources().map(function (source) { return source.id; });
          if (availableSourceIds.indexOf(sourceId) !== -1) {
              // requested source is available
              this._dynamicDataManager.registerSourceChanged(sourceId, this._component, callback);
              this._logFeatureEntry('RegisterSourceChanged', { sourceId: sourceId });
          }
          else {
              // requested source is NOT available
              this._addPendingRegistration({ id: sourceId, callback: callback });
          }
      };
      /**
       * Unregisters a callback for updates when a Dynamic Data source is changed.
       *
       * @param sourceId - Id of the Dynamic Data Source.
       * @param callback - Function to remove from the registration. Must be the same it was registered with.
       */
      DynamicDataProvider.prototype.unregisterSourceChanged = function (sourceId, callback) {
          this._assertNotDisposed();
          this._assertInitialized();
          Validate.isNonemptyString(sourceId, 'sourceId');
          Validate.isNotNullOrUndefined(callback, 'callback');
          var registration = { id: sourceId, callback: callback };
          if (!this.isRegistrationPending(registration)) {
              // Source is already registered.
              this._dynamicDataManager.unregisterSourceChanged(sourceId, this._component, callback);
              this._logFeatureEntry('UnregisterSourceChanged', { sourceId: sourceId });
          }
          else {
              // Source is in pending state
              this._removePendingRegistration(registration);
          }
      };
      /**
       * Registers a callback for updates when a property is changed in a Dynamic Data source.
       *
       * @privateRemarks
       * We register the source, only if the source is available else defer the registration
       * to when the availableSources change.
       *
       * @param sourceId - Id of the Dynamic Data Source.
       * @param propertyId - Id of the property of the source.
       * @param callback - Function to execute when the source updates its data.
       */
      DynamicDataProvider.prototype.registerPropertyChanged = function (sourceId, propertyId, callback) {
          this._assertNotDisposed();
          this._assertInitialized();
          Validate.isNonemptyString(sourceId, 'sourceId');
          Validate.isNonemptyString(propertyId, 'propertyId');
          Validate.isNotNullOrUndefined(callback, 'callback');
          var availableSourceIds = this.getAvailableSources().map(function (source) { return source.id; });
          if (availableSourceIds.indexOf(sourceId) !== -1) {
              // requested source is available
              this._dynamicDataManager.registerPropertyChanged(sourceId, propertyId, this._component, callback);
              this._logFeatureEntry('RegisterPropertyChanged', { sourceId: sourceId, propertyId: propertyId });
          }
          else {
              // requested source is NOT available
              this._addPendingRegistration({ id: sourceId, propertyId: propertyId, callback: callback });
          }
      };
      /**
       * Unregisters a callback for updates when a property is changed in a Dynamic Data source.
       *
       * @privateRemarks
       * If the sourceId is present in _registeredSourcesForPropertyChanged then
       *    it removes from sourceID from the set _registeredSourcesForPropertyChanged and
       *    invokes unregisterPropertyChanged event on the DynamicDataManager
       *
       * else if the sourceId is present in _pendingSourcesForPropertyChanged then
       *    it just removes the sourceId from the set _pendingSourcesForPropertyChanged
       *
       * @param sourceId - Id of the Dynamic Data Source.
       * @param propertyId - Id of the property of the source.
       * @param callback - Function to remove from the registration. Must be the same it was registered with.
       */
      DynamicDataProvider.prototype.unregisterPropertyChanged = function (sourceId, propertyId, callback) {
          this._assertNotDisposed();
          this._assertInitialized();
          Validate.isNonemptyString(sourceId, 'sourceId');
          Validate.isNonemptyString(propertyId, 'propertyId');
          Validate.isNotNullOrUndefined(callback, 'callback');
          var registration = {
              id: sourceId,
              callback: callback,
              propertyId: propertyId
          };
          // registration is in pending state.
          if (!this.isRegistrationPending(registration)) {
              this._dynamicDataManager.unregisterPropertyChanged(sourceId, propertyId, this._component, callback);
              this._logFeatureEntry('UnregisterPropertyChanged', { sourceId: sourceId, propertyId: propertyId });
          }
          else {
              // Already registered.
              this._removePendingRegistration(registration);
          }
      };
      /**
       * Registers a callback to an event that raises when the list of available Dynamic Data Sources is updated.
       *
       * @param callback - Function to execute when the sources are updated.
       */
      DynamicDataProvider.prototype.registerAvailableSourcesChanged = function (callback) {
          Validate.isNotNullOrUndefined(callback, 'callback');
          this._assertNotDisposed();
          this._assertInitialized();
          this._dynamicDataManager.sourcesChangedEvent.add(this._component, callback);
      };
      /**
       * Unregisters a callback to an event that raises when the list of available Dynamic Data Sources is updated.
       *
       * @privateRemarks
       * This api is used by the consumer of the dynamic data.
       *
       * @param callback - Function to remove from the registration. Must be the same it was registered with.
       */
      DynamicDataProvider.prototype.unregisterAvailableSourcesChanged = function (callback) {
          Validate.isNotNullOrUndefined(callback, 'callback');
          this._assertNotDisposed();
          this._assertInitialized();
          this._dynamicDataManager.sourcesChangedEvent.remove(this._component, callback);
      };
      /**
       * Returns a list with all available Dynamic Data Sources.
       *
       * @returns Read-only array with all available sources.
       */
      DynamicDataProvider.prototype.getAvailableSources = function () {
          this._assertNotDisposed();
          this._assertInitialized();
          return this._dynamicDataManager.getSources();
      };
      /**
       * Returns a list of all the available Dynamic Data Sources which have same component id.
       *
       * @returns Read-only array of available sources with same component id.
       * @alpha
       */
      DynamicDataProvider.prototype.getAvailableSourcesByComponentId = function (componentId) {
          this._assertNotDisposed();
          this._assertInitialized();
          var sources = [];
          this._dynamicDataManager.getSources().forEach(function (source) {
              if (componentId.equals(Guid.tryParse(source.metadata.componentId) || Guid.empty)) {
                  sources.push(source);
              }
          });
          return sources;
      };
      /**
       * Returns a Dynamic Data Source, by its id.
       * If the source is not present, it returns undefined.
       *
       * @param id - Id of the Dynamic Data Source.
       */
      DynamicDataProvider.prototype.tryGetSource = function (id) {
          this._assertNotDisposed();
          this._assertInitialized();
          Validate.isNotNullOrUndefined(id, 'id');
          return this._dynamicDataManager.tryGetSource(id);
      };
      /**
       * Returns specific data from a source based on a Dynamic Data reference.
       *
       * @param dataReference - Dynamic Data Reference.
       * @internal
       */
      DynamicDataProvider.prototype._getData = function (dataReference) {
          this._assertNotDisposed();
          this._assertInitialized();
          Validate.isNotNullOrUndefined(dataReference, 'dataReference');
          var dataSource = this._dynamicDataManager.tryGetSource(dataReference.sourceId);
          if (!dataSource) {
              return undefined;
          }
          var data = dataSource.getPropertyValue(dataReference.property); // tslint:disable-line:no-any
          if (data && dataReference.propertyPath) {
              // multiple levels of sub-properties are separated by '.' in the property path.
              var subpaths = dataReference.propertyPath.split('.');
              subpaths.forEach(function (subpath) {
                  // If sub-property yields to an array value, then the path to the sub-property
                  // will have [*] appended after the name of the sub-property.
                  // Hence separating out to get the subPropertyName.
                  var subpathElements = subpath.split(DynamicDataProvider._jsonPathArrayRegex);
                  var subProperty = subpathElements[0];
                  // If the supplied reference has an array([*]) notation, then we extract the data
                  // accordingly. [*] means all the entries and if there is a number instead of the '*'
                  // then extract that specific entry.
                  if (data) {
                      if (Array.isArray(data)) {
                          var dataItems_1 = []; /* tslint:disable-line:no-any */
                          data.forEach(function (item) {
                              if (item.hasOwnProperty(subProperty)) {
                                  dataItems_1.push(item[subProperty]);
                              }
                          });
                          /*
                           * If a specific entry is asked, we return it else we return all the entries,
                           * essentially it is treated as [*].
                           */
                          var index = subpathElements[1];
                          if (subpathElements.length > 1) {
                              /**
                               * This means that the current subpath element is an array type, an array inside an array.
                               * Since the subpath element is an array, we are normalizing the existing dataItems object
                               * i.e., dataItems will have an array of array objects.
                               */
                              var tempDataItems_1 = []; /* tslint:disable-line:no-any */
                              dataItems_1.forEach(function (dataItem) {
                                  dataItem.forEach(function (internalItem) { return tempDataItems_1.push(internalItem); });
                              });
                              dataItems_1 = tempDataItems_1;
                          }
                          if (!isNaN(parseInt(index, 10))) {
                              data = dataItems_1[index];
                          }
                          else {
                              data = dataItems_1;
                          }
                      }
                      else if (data.hasOwnProperty(subProperty)) {
                          // data is an object type
                          data = data[subProperty];
                      }
                      else {
                          // If the control is here, it means that the subProperty was not found in data.
                          // Hence the next iteration would get data as undefined.
                          data = undefined;
                      }
                  }
              });
          }
          return data;
      };
      DynamicDataProvider.prototype._onSourcesChanged = function () {
          var _this = this;
          var availableSourceIds = this.getAvailableSources().map(function (source) { return source.id; });
          // Registering to sourceChanged event
          this._pendingRegistrations.forEach(function (src) {
              // register to sourceChanged event, if the source is available.
              if (availableSourceIds.indexOf(src.id) !== -1) {
                  // Source is available
                  if (!src.propertyId) {
                      // No propertyId, means register to sourceChanged event.
                      _this._dynamicDataManager.registerSourceChanged(src.id, _this._component, src.callback);
                      _this._logFeatureEntry('RegisterSourceChanged', { sourceId: src.id });
                  }
                  else {
                      // With propertyId, means register to propertyChanged event.
                      _this._dynamicDataManager.registerPropertyChanged(src.id, src.propertyId, _this._component, src.callback);
                      _this._logFeatureEntry('RegisterPropertyChanged', { sourceId: src.id, propertyId: src.propertyId });
                  }
                  _this._removePendingRegistration(src);
              }
          });
      };
      DynamicDataProvider.prototype._assertNotDisposed = function () {
          if (this.isDisposed) {
              throw new Error('Dynamic Data Provider has been disposed');
          }
      };
      DynamicDataProvider.prototype._assertInitialized = function () {
          if (!this._isInitialized) {
              throw new Error('Dynamic Data Provider has not been initialized.');
          }
      };
      /**
       * Adds the registration to the pending registrations if not already added.
       * Also adds a listener to the _DynamicDataManager's sourcesChangedEvent.
       */
      DynamicDataProvider.prototype._addPendingRegistration = function (registration) {
          if (!this.isRegistrationPending(registration)) {
              this._pendingRegistrations.set(registration.id + (registration.propertyId || ''), {
                  id: registration.id,
                  propertyId: registration.propertyId,
                  callback: registration.callback
              });
          }
          if (!this._isSourcesChangedEventListenerAdded) {
              this._dynamicDataManager.sourcesChangedEvent.add(this._component, this._onSourcesChanged);
              this._isSourcesChangedEventListenerAdded = true;
          }
      };
      /**
       * Removes the pending registration and then, if there are no more pending registrations
       * remove the 'sourcesChangedEvent' listener.
       */
      DynamicDataProvider.prototype._removePendingRegistration = function (registration) {
          this._pendingRegistrations.delete(registration.id + (registration.propertyId || ''));
          if (this._pendingRegistrations.size === 0) {
              this._dynamicDataManager.sourcesChangedEvent.remove(this._component, this._onSourcesChanged);
              this._isSourcesChangedEventListenerAdded = false;
          }
      };
      /**
       * Returns true if the incoming source is already added to the _pendingRegistrations Map.
       *
       * @param incomingRegistration - registration to be checked.
       */
      DynamicDataProvider.prototype.isRegistrationPending = function (incomingRegistration) {
          return this._pendingRegistrations.has(incomingRegistration.id + (incomingRegistration.propertyId || ''));
      };
      /**
       * Logs a feature event with any extra data from the producer of the event.
       * @param logFeature - Name of the feature to log.
       * @param contextualProps - Contextual log properties of the feature event.
       */
      DynamicDataProvider.prototype._logFeatureEntry = function (logFeature, contextualProps) {
          var isInternal = this._component.manifest.isInternal || false;
          var logProperties = {
              alias: this._component.manifest.alias,
              isInternal: isInternal.toString()
          };
          var logEntry = new _LogEntry('DynamicDataProvider', logFeature, _LogType.Event, lodash.merge(logProperties, contextualProps));
          _EngagementLogger.logEventWithLogEntry(logEntry);
      };
      /**
       * Regular expression to evaluate arrays in a json path expression.
       * @internal
       */
      DynamicDataProvider._jsonPathArrayRegex = new RegExp(/\[|\]/);
      return DynamicDataProvider;
  }());
  export default DynamicDataProvider;
  //# sourceMappingURL=DynamicDataProvider.js.map