Index: third_party/google-endpoints/google/api/control/operation.py |
diff --git a/third_party/google-endpoints/google/api/control/operation.py b/third_party/google-endpoints/google/api/control/operation.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a3c25fea0377e99f64e0b4eb71d3efeb7b55f549 |
--- /dev/null |
+++ b/third_party/google-endpoints/google/api/control/operation.py |
@@ -0,0 +1,200 @@ |
+# Copyright 2016 Google Inc. All Rights Reserved. |
+# |
+# 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. |
+ |
+"""operation provides support for working with `Operation` instances. |
+ |
+:class:`~google.api.gen.servicecontrol_v1_message.Operation` represents |
+information regarding an operation, and is a key constituent of |
+:class:`~google.api.gen.servicecontrol_v1_message.CheckRequest` and |
+:class:`~google.api.gen.servicecontrol_v1_message.ReportRequests. |
+ |
+The :class:`.Aggregator` support this. |
+ |
+""" |
+ |
+from __future__ import absolute_import |
+ |
+import collections |
+import logging |
+from datetime import datetime |
+ |
+from apitools.base.py import encoding |
+ |
+from . import messages, metric_value, timestamp, MetricKind |
+ |
+logger = logging.getLogger(__name__) |
+ |
+ |
+class Info( |
+ collections.namedtuple( |
+ 'Info', [ |
+ 'api_key', |
+ 'api_key_valid', |
+ 'consumer_project_id', |
+ 'operation_id', |
+ 'operation_name', |
+ 'referer', |
+ 'service_name', |
+ ])): |
+ """Holds basic information about an api call. |
+ |
+ This class is one of several used to mediate between the raw service |
+ control api surface and python frameworks. Client code can construct |
+ operations using this surface |
+ |
+ Attributes: |
+ api_key (string): the api key |
+ api_key_valid (bool): it the request has a valid api key. By default |
+ it is true, it will only be set to false if the api key cannot |
+ be validated by the service controller |
+ consumer_project_id (string): the project id of the api consumer |
+ operation_id (string): identity of the operation, which must be unique |
+ within the scope of the service. Calls to report and check on the |
+ same operation should carry the same operation id |
+ operation_name (string): the fully-qualified name of the operation |
+ referer (string): the referer header, or if not present the origin |
+ service_name(string): the name of service |
+ |
+ """ |
+ # pylint: disable=too-many-arguments |
+ |
+ def __new__(cls, |
+ api_key='', |
+ api_key_valid=False, |
+ consumer_project_id='', |
+ operation_id='', |
+ operation_name='', |
+ referer='', |
+ service_name=''): |
+ """Invokes the base constructor with default values.""" |
+ return super(cls, Info).__new__( |
+ cls, |
+ api_key, |
+ api_key_valid, |
+ consumer_project_id, |
+ operation_id, |
+ operation_name, |
+ referer, |
+ service_name) |
+ |
+ def as_operation(self, timer=datetime.utcnow): |
+ """Makes an ``Operation`` from this instance. |
+ |
+ Returns: |
+ an ``Operation`` |
+ |
+ """ |
+ now = timer() |
+ op = messages.Operation( |
+ endTime=timestamp.to_rfc3339(now), |
+ startTime=timestamp.to_rfc3339(now), |
+ importance=messages.Operation.ImportanceValueValuesEnum.LOW) |
+ if self.operation_id: |
+ op.operationId = self.operation_id |
+ if self.operation_name: |
+ op.operationName = self.operation_name |
+ if self.api_key and self.api_key_valid: |
+ op.consumerId = 'api_key:' + self.api_key |
+ elif self.consumer_project_id: |
+ op.consumerId = 'project:' + self.consumer_project_id |
+ return op |
+ |
+ |
+class Aggregator(object): |
+ """Container that implements operation aggregation. |
+ |
+ Thread compatible. |
+ """ |
+ DEFAULT_KIND = MetricKind.DELTA |
+ """Used when kinds are not specified, or are missing a metric name""" |
+ |
+ def __init__(self, initial_op, kinds=None): |
+ """Constructor. |
+ |
+ If kinds is not specifed, all operations will be merged assuming |
+ they are of Kind ``DEFAULT_KIND`` |
+ |
+ Args: |
+ initial_op ( |
+ :class:`google.api.gen.servicecontrol_v1_messages.Operation`): the |
+ initial version of the operation |
+ kinds (dict[string,[string]]): specifies the metric kind for |
+ each metric name |
+ |
+ """ |
+ assert isinstance(initial_op, messages.Operation) |
+ if kinds is None: |
+ kinds = {} |
+ self._kinds = kinds |
+ self._metric_values_by_name_then_sign = collections.defaultdict(dict) |
+ our_op = encoding.CopyProtoMessage(initial_op) |
+ self._merge_metric_values(our_op) |
+ our_op.metricValueSets = [] |
+ self._op = our_op |
+ |
+ def as_operation(self): |
+ """Obtains a single `Operation` representing this instances contents. |
+ |
+ Returns: |
+ :class:`google.api.gen.servicecontrol_v1_messages.Operation` |
+ """ |
+ result = encoding.CopyProtoMessage(self._op) |
+ names = sorted(self._metric_values_by_name_then_sign.keys()) |
+ for name in names: |
+ mvs = self._metric_values_by_name_then_sign[name] |
+ result.metricValueSets.append( |
+ messages.MetricValueSet( |
+ metricName=name, metricValues=mvs.values())) |
+ return result |
+ |
+ def add(self, other_op): |
+ """Combines `other_op` with the operation held by this aggregator. |
+ |
+ N.B. It merges the operations log entries and metric values, but makes |
+ the assumption the operation is consistent. It's the callers |
+ responsibility to ensure consistency |
+ |
+ Args: |
+ other_op ( |
+ class:`google.api.gen.servicecontrol_v1_messages.Operation`): |
+ an operation merge into this one |
+ |
+ """ |
+ self._op.logEntries.extend(other_op.logEntries) |
+ self._merge_timestamps(other_op) |
+ self._merge_metric_values(other_op) |
+ |
+ def _merge_metric_values(self, other_op): |
+ for value_set in other_op.metricValueSets: |
+ name = value_set.metricName |
+ kind = self._kinds.get(name, self.DEFAULT_KIND) |
+ by_signature = self._metric_values_by_name_then_sign[name] |
+ for mv in value_set.metricValues: |
+ signature = metric_value.sign(mv) |
+ prior = by_signature.get(signature) |
+ if prior is not None: |
+ metric_value.merge(kind, prior, mv) |
+ by_signature[signature] = mv |
+ |
+ def _merge_timestamps(self, other_op): |
+ # Update the start time and end time in self._op as needed |
+ if (other_op.startTime and |
+ (self._op.startTime is None or |
+ timestamp.compare(other_op.startTime, self._op.startTime) == -1)): |
+ self._op.startTime = other_op.startTime |
+ |
+ if (other_op.endTime and |
+ (self._op.endTime is None or timestamp.compare( |
+ self._op.endTime, other_op.endTime) == -1)): |
+ self._op.endTime = other_op.endTime |