text: "use strict";
  /**
   * Copyright 2018, OpenCensus Authors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  Object.defineProperty(exports, "__esModule", { value: true });
  const defaultLogger = require("../common/console-logger");
  const types_1 = require("../metrics/export/types");
  const bucket_boundaries_1 = require("./bucket-boundaries");
  const metric_utils_1 = require("./metric-utils");
  const recorder_1 = require("./recorder");
  const types_2 = require("./types");
  const RECORD_SEPARATOR = String.fromCharCode(30);
  const UNIT_SEPARATOR = String.fromCharCode(31);
  // String that has only printable characters
  const invalidString = /[^\u0020-\u007e]/;
  class BaseView {
      /**
       * Creates a new View instance. This constructor is used by Stats. User should
       * prefer using Stats.createView() instead.
       * @param name The view name
       * @param measure The view measure
       * @param aggregation The view aggregation type
       * @param tagsKeys The Tags' keys that view will have
       * @param description The view description
       * @param bucketBoundaries The view bucket boundaries for a distribution
       * aggregation type
       * @param logger
       */
      constructor(name, measure, aggregation, tagsKeys, description, bucketBoundaries, logger = defaultLogger) {
          /**
           * A map of stringified tags representing columns labels or tag keys, concept
           * similar to dimensions on multidimensional modeling, to AggregationData.
           * If no Tags are provided, then, all data is recorded in a single
           * aggregation.
           */
          this.rows = {};
          /** true if the view was registered */
          this.registered = false;
          if (aggregation === types_2.AggregationType.DISTRIBUTION && !bucketBoundaries) {
              throw new Error('No bucketBoundaries specified');
          }
          this.logger = logger.logger();
          this.name = name;
          this.description = description;
          this.measure = measure;
          this.columns = tagsKeys;
          this.aggregation = aggregation;
          this.startTime = Date.now();
          this.bucketBoundaries = new bucket_boundaries_1.BucketBoundaries(bucketBoundaries);
          this.metricDescriptor = metric_utils_1.MetricUtils.viewToMetricDescriptor(this);
      }
      /** Gets the view's tag keys */
      getColumns() {
          return this.columns;
      }
      /**
       * Records a measurement in the proper view's row. This method is used by
       * Stats. User should prefer using Stats.record() instead.
       *
       * Measurements with measurement type INT64 will have its value truncated.
       * @param measurement The measurement to record
       */
      recordMeasurement(measurement) {
          // Checks if measurement has valid tags
          if (this.invalidTags(measurement.tags)) {
              return;
          }
          // Checks if measurement has all tags in views
          for (const tagKey of this.columns) {
              if (!Object.keys(measurement.tags).some((key) => key === tagKey)) {
                  return;
              }
          }
          const encodedTags = this.encodeTags(measurement.tags);
          if (!this.rows[encodedTags]) {
              this.rows[encodedTags] = this.createAggregationData(measurement.tags);
          }
          recorder_1.Recorder.addMeasurement(this.rows[encodedTags], measurement);
      }
      /**
       * Encodes a Tags object into a key sorted string.
       * @param tags The tags to encode
       */
      encodeTags(tags) {
          return Object.keys(tags)
              .sort()
              .map(tagKey => {
              return tagKey + UNIT_SEPARATOR + tags[tagKey];
          })
              .join(RECORD_SEPARATOR);
      }
      /**
       * Checks if tag keys and values have only printable characters.
       * @param tags The tags to be checked
       */
      invalidTags(tags) {
          return Object.keys(tags).some(tagKey => {
              return invalidString.test(tagKey) || invalidString.test(tags[tagKey]);
          });
      }
      /**
       * Creates an empty aggregation data for a given tags.
       * @param tags The tags for that aggregation data
       */
      createAggregationData(tags) {
          const aggregationMetadata = { tags, timestamp: Date.now() };
          const { buckets, bucketCounts } = this.bucketBoundaries;
          const bucketsCopy = Object.assign([], buckets);
          const bucketCountsCopy = Object.assign([], bucketCounts);
          switch (this.aggregation) {
              case types_2.AggregationType.DISTRIBUTION:
                  return Object.assign({}, aggregationMetadata, { type: types_2.AggregationType.DISTRIBUTION, startTime: this.startTime, count: 0, sum: 0, mean: null, stdDeviation: null, sumOfSquaredDeviation: null, buckets: bucketsCopy, bucketCounts: bucketCountsCopy });
              case types_2.AggregationType.SUM:
                  return Object.assign({}, aggregationMetadata, { type: types_2.AggregationType.SUM, value: 0 });
              case types_2.AggregationType.COUNT:
                  return Object.assign({}, aggregationMetadata, { type: types_2.AggregationType.COUNT, value: 0 });
              default:
                  return Object.assign({}, aggregationMetadata, { type: types_2.AggregationType.LAST_VALUE, value: undefined });
          }
      }
      /**
       * Gets view`s metric
       * @returns {Metric}
       */
      getMetric() {
          const { type } = this.metricDescriptor;
          let startTimestamp;
          // The moment when this point was recorded.
          const [currentSeconds, currentNanos] = process.hrtime();
          const now = { seconds: currentSeconds, nanos: currentNanos };
          switch (type) {
              case types_1.MetricDescriptorType.GAUGE_INT64:
              case types_1.MetricDescriptorType.GAUGE_DOUBLE:
                  startTimestamp = null;
                  break;
              default:
                  const [seconds, nanos] = process.hrtime();
                  // TODO (mayurkale): This should be set when create Cumulative view.
                  startTimestamp = { seconds, nanos };
          }
          const timeseries = [];
          Object.keys(this.rows).forEach(key => {
              const { tags } = this.rows[key];
              const labelValues = metric_utils_1.MetricUtils.tagsToLabelValues(tags);
              const point = this.toPoint(now, this.getSnapshot(tags));
              if (startTimestamp) {
                  timeseries.push({ startTimestamp, labelValues, points: [point] });
              }
              else {
                  timeseries.push({ labelValues, points: [point] });
              }
          });
          return { descriptor: this.metricDescriptor, timeseries };
      }
      /**
       * Converts snapshot to point
       * @param timestamp The timestamp
       * @param data The aggregated data
       * @returns {Point}
       */
      toPoint(timestamp, data) {
          let value;
          if (data.type === types_2.AggregationType.DISTRIBUTION) {
              // TODO: Add examplar transition
              const { count, sum, sumOfSquaredDeviation } = data;
              value = {
                  count,
                  sum,
                  sumOfSquaredDeviation,
                  bucketOptions: { explicit: { bounds: data.buckets } },
                  buckets: data.bucketCounts
              };
          }
          else {
              value = data.value;
          }
          return { timestamp, value };
      }
      /**
       * Returns a snapshot of an AggregationData for that tags/labels values.
       * @param tags The desired data's tags
       * @returns {AggregationData}
       */
      getSnapshot(tags) {
          return this.rows[this.encodeTags(tags)];
      }
  }
  exports.BaseView = BaseView;
  //# sourceMappingURL=view.js.map