temporal-flowrensic-python-sdk 0.6.63__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. temporal_flowrensic/__init__.py +2 -0
  2. temporal_flowrensic/_calls.py +185 -0
  3. temporal_flowrensic/_ci.py +56 -0
  4. temporal_flowrensic/_deploy.py +65 -0
  5. temporal_flowrensic/_gen/flowrensic/v1/activity_metrics_pb2.py +76 -0
  6. temporal_flowrensic/_gen/flowrensic/v1/activity_metrics_pb2.pyi +301 -0
  7. temporal_flowrensic/_gen/flowrensic/v1/catalog_pb2.py +60 -0
  8. temporal_flowrensic/_gen/flowrensic/v1/catalog_pb2.pyi +127 -0
  9. temporal_flowrensic/_gen/flowrensic/v1/catch_up_pb2.py +42 -0
  10. temporal_flowrensic/_gen/flowrensic/v1/catch_up_pb2.pyi +71 -0
  11. temporal_flowrensic/_gen/flowrensic/v1/client_calls_pb2.py +49 -0
  12. temporal_flowrensic/_gen/flowrensic/v1/client_calls_pb2.pyi +90 -0
  13. temporal_flowrensic/_gen/flowrensic/v1/cost_pb2.py +52 -0
  14. temporal_flowrensic/_gen/flowrensic/v1/cost_pb2.pyi +107 -0
  15. temporal_flowrensic/_gen/flowrensic/v1/deploy_pb2.py +51 -0
  16. temporal_flowrensic/_gen/flowrensic/v1/deploy_pb2.pyi +92 -0
  17. temporal_flowrensic/_gen/flowrensic/v1/ephemeral_run_pb2.py +107 -0
  18. temporal_flowrensic/_gen/flowrensic/v1/ephemeral_run_pb2.pyi +344 -0
  19. temporal_flowrensic/_gen/flowrensic/v1/finding_pb2.py +76 -0
  20. temporal_flowrensic/_gen/flowrensic/v1/finding_pb2.pyi +371 -0
  21. temporal_flowrensic/_gen/flowrensic/v1/job_pb2.py +46 -0
  22. temporal_flowrensic/_gen/flowrensic/v1/job_pb2.pyi +51 -0
  23. temporal_flowrensic/_gen/flowrensic/v1/load_pb2.py +51 -0
  24. temporal_flowrensic/_gen/flowrensic/v1/load_pb2.pyi +118 -0
  25. temporal_flowrensic/_gen/flowrensic/v1/page_pb2.py +38 -0
  26. temporal_flowrensic/_gen/flowrensic/v1/page_pb2.pyi +23 -0
  27. temporal_flowrensic/_gen/flowrensic/v1/remediation_pb2.py +60 -0
  28. temporal_flowrensic/_gen/flowrensic/v1/remediation_pb2.pyi +157 -0
  29. temporal_flowrensic/_gen/flowrensic/v1/replay_pb2.py +84 -0
  30. temporal_flowrensic/_gen/flowrensic/v1/replay_pb2.pyi +309 -0
  31. temporal_flowrensic/_gen/flowrensic/v1/server_config_pb2.py +48 -0
  32. temporal_flowrensic/_gen/flowrensic/v1/server_config_pb2.pyi +97 -0
  33. temporal_flowrensic/_gen/flowrensic/v1/services_connect.py +3621 -0
  34. temporal_flowrensic/_gen/flowrensic/v1/services_pb2.py +60 -0
  35. temporal_flowrensic/_gen/flowrensic/v1/services_pb2.pyi +20 -0
  36. temporal_flowrensic/_gen/flowrensic/v1/stored_data_pb2.py +43 -0
  37. temporal_flowrensic/_gen/flowrensic/v1/stored_data_pb2.pyi +50 -0
  38. temporal_flowrensic/_gen/flowrensic/v1/workflow_history_pb2.py +47 -0
  39. temporal_flowrensic/_gen/flowrensic/v1/workflow_history_pb2.pyi +102 -0
  40. temporal_flowrensic/_gen/flowrensic/v1/workflow_types_pb2.py +55 -0
  41. temporal_flowrensic/_gen/flowrensic/v1/workflow_types_pb2.pyi +197 -0
  42. temporal_flowrensic/_names.py +42 -0
  43. temporal_flowrensic/_queries.py +61 -0
  44. temporal_flowrensic/_replay.py +378 -0
  45. temporal_flowrensic/_sink.py +574 -0
  46. temporal_flowrensic/_telemetry.py +559 -0
  47. temporal_flowrensic/activity.py +136 -0
  48. temporal_flowrensic/client.py +166 -0
  49. temporal_flowrensic/py.typed +0 -0
  50. temporal_flowrensic/workflow.py +103 -0
  51. temporal_flowrensic_python_sdk-0.6.63.dist-info/METADATA +74 -0
  52. temporal_flowrensic_python_sdk-0.6.63.dist-info/RECORD +54 -0
  53. temporal_flowrensic_python_sdk-0.6.63.dist-info/WHEEL +4 -0
  54. temporal_flowrensic_python_sdk-0.6.63.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,2 @@
1
+ """Flowrensic for Temporal Python workers. The parts follow temporalio:
2
+ temporal_flowrensic.activity, temporal_flowrensic.client and temporal_flowrensic.workflow."""
@@ -0,0 +1,185 @@
1
+ """The client interceptor: every query and signal of a temporalio Client that
2
+ raises is sent to Flowrensic, through the record buffer."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import time
7
+ from typing import Any, Optional
8
+
9
+ import temporalio.client
10
+ import temporalio.service
11
+ from temporalio.service import RPCError, RPCStatusCode
12
+
13
+ from temporal_flowrensic._gen.flowrensic.v1.client_calls_pb2 import (
14
+ CLIENT_CALL_OUTCOME_FAILED,
15
+ CLIENT_CALL_OUTCOME_TIMED_OUT,
16
+ ClientCall,
17
+ IngestClientCallsRequest,
18
+ )
19
+ from temporal_flowrensic._sink import (
20
+ DEFAULT_BATCH_SIZE,
21
+ DEFAULT_FLUSH_INTERVAL,
22
+ DEFAULT_MAX_BATCH_SIZE,
23
+ DEFAULT_MAX_WAITING,
24
+ DEFAULT_MAX_WAITING_BYTES,
25
+ DEFAULT_TIMEOUT_MS,
26
+ BufferTarget,
27
+ RecordSink,
28
+ SendId,
29
+ SinkCounts,
30
+ ingest_client_sync,
31
+ client_headers,
32
+ )
33
+
34
+ __all__ = ["ClientInterceptor"]
35
+
36
+
37
+ # The error type Flowrensic knows a query by that a worker answered with an
38
+ # error, the name the TypeScript client gives it. The Python client raises
39
+ # WorkflowQueryFailedError for the same case.
40
+ _ANSWERED_QUERY_ERROR = "QueryNotRegisteredError"
41
+
42
+
43
+ def _error_type(err: BaseException) -> str:
44
+ if isinstance(err, temporalio.client.WorkflowQueryFailedError):
45
+ return _ANSWERED_QUERY_ERROR
46
+ return type(err).__name__
47
+
48
+
49
+ def _timed_out(err: BaseException) -> bool:
50
+ """Whether the call ended on its deadline: deadline exceeded, or cancelled by the server."""
51
+ return isinstance(err, RPCError) and err.status in (RPCStatusCode.DEADLINE_EXCEEDED, RPCStatusCode.CANCELLED)
52
+
53
+
54
+ class _CallTarget(BufferTarget[ClientCall]):
55
+ def __init__(self, server_base_url: str, headers: dict[str, str]) -> None:
56
+ self._ingest = ingest_client_sync(server_base_url)
57
+ self._headers = headers
58
+
59
+ def send(self, records: list[ClientCall], timeout_ms: int, id: SendId) -> None:
60
+ self._ingest.ingest_client_calls(
61
+ IngestClientCallsRequest(calls=records, sender=id.sender, send_number=id.send_number),
62
+ headers=self._headers,
63
+ timeout_ms=max(1, timeout_ms),
64
+ )
65
+
66
+
67
+ # The namespace a temporalio Client talks to when it is given none.
68
+ _DEFAULT_NAMESPACE = "default"
69
+
70
+
71
+ class ClientInterceptor(temporalio.client.Interceptor, temporalio.client.Plugin):
72
+ """Put in Client.connect(plugins=[...]). Every query and signal that
73
+ raises is sent to Flowrensic with its name, the workflow it was for and
74
+ whether it ended on its deadline. A call that works is not sent. The calls
75
+ wait in a buffer and leave in batches, as the attempts of the worker do,
76
+ so sending never changes what the caller gets or how long it waits. Call
77
+ close before the process ends, so the calls still waiting are sent.
78
+
79
+ As a client plugin it reads the namespace of the client and puts itself in
80
+ the interceptors of the client, so each call is sent under the namespace
81
+ the client talks to. current_env, env_access_key and current_ephemeral_run
82
+ go in their headers on every send. Times are in seconds."""
83
+
84
+ def __init__(
85
+ self,
86
+ server_base_url: str,
87
+ *,
88
+ current_env: str = "",
89
+ env_access_key: str = "",
90
+ current_ephemeral_run: str = "",
91
+ batch_size: int = DEFAULT_BATCH_SIZE,
92
+ flush_interval: float = DEFAULT_FLUSH_INTERVAL,
93
+ timeout_ms: int = DEFAULT_TIMEOUT_MS,
94
+ max_waiting: int = DEFAULT_MAX_WAITING,
95
+ max_batch_size: int = DEFAULT_MAX_BATCH_SIZE,
96
+ max_waiting_bytes: int = DEFAULT_MAX_WAITING_BYTES,
97
+ ) -> None:
98
+ if not server_base_url:
99
+ raise ValueError("flowrensic: server_base_url is required")
100
+ self._namespace = _DEFAULT_NAMESPACE
101
+ self._next_plugin: Optional[temporalio.client.Plugin] = None
102
+ self._buffer: RecordSink[ClientCall] = RecordSink(
103
+ _CallTarget(server_base_url, client_headers(current_env, env_access_key, current_ephemeral_run)),
104
+ batch_size=batch_size,
105
+ flush_interval=flush_interval,
106
+ timeout_ms=timeout_ms,
107
+ max_waiting=max_waiting,
108
+ max_batch_size=max_batch_size,
109
+ max_waiting_bytes=max_waiting_bytes,
110
+ )
111
+
112
+ def intercept_client(self, next: temporalio.client.OutboundInterceptor) -> temporalio.client.OutboundInterceptor:
113
+ return _ClientOutbound(next, self)
114
+
115
+ def init_client_plugin(self, next: temporalio.client.Plugin) -> None:
116
+ """Keeps the next plugin, for the temporalio versions that chain them."""
117
+ self._next_plugin = next
118
+
119
+ def configure_client(self, config: Any) -> Any:
120
+ """Reads the namespace of the client and adds this interceptor to its interceptors."""
121
+ self._namespace = config.get("namespace") or _DEFAULT_NAMESPACE
122
+ interceptors = list(config.get("interceptors") or [])
123
+ if not any(one is self for one in interceptors):
124
+ interceptors.append(self)
125
+ config["interceptors"] = interceptors
126
+ if self._next_plugin is not None:
127
+ return self._next_plugin.configure_client(config)
128
+ return config
129
+
130
+ async def connect_service_client(self, config: Any, next: Any = None) -> Any:
131
+ """Connects as the next plugin or temporalio would, unchanged."""
132
+ if next is not None:
133
+ return await next(config)
134
+ if self._next_plugin is not None:
135
+ return await self._next_plugin.connect_service_client(config)
136
+ return await temporalio.service.ServiceClient.connect(config)
137
+
138
+ async def flush(self) -> None:
139
+ """Sends what waits and waits for it to land."""
140
+ await self._buffer.flush()
141
+
142
+ async def close(self) -> None:
143
+ """Sends what waits and stops."""
144
+ await self._buffer.close()
145
+
146
+ def counts(self) -> SinkCounts:
147
+ """What the buffer lost and holds."""
148
+ return self._buffer.counts()
149
+
150
+ def _record(self, kind: str, name: str, workflow_id: str, run_id: Optional[str], err: BaseException) -> None:
151
+ """Records a call that raised, and never raises itself."""
152
+ try:
153
+ call = ClientCall(
154
+ namespace=self._namespace,
155
+ workflow_id=workflow_id,
156
+ run_id=run_id or "",
157
+ kind=kind,
158
+ name=name,
159
+ outcome=CLIENT_CALL_OUTCOME_TIMED_OUT if _timed_out(err) else CLIENT_CALL_OUTCOME_FAILED,
160
+ error_type=_error_type(err),
161
+ )
162
+ call.ended_at.FromNanoseconds(int(time.time() * 1_000_000_000))
163
+ self._buffer.record(call)
164
+ except Exception: # noqa: BLE001
165
+ return
166
+
167
+
168
+ class _ClientOutbound(temporalio.client.OutboundInterceptor):
169
+ def __init__(self, next: temporalio.client.OutboundInterceptor, owner: ClientInterceptor) -> None:
170
+ super().__init__(next)
171
+ self._owner = owner
172
+
173
+ async def query_workflow(self, input: temporalio.client.QueryWorkflowInput) -> Any:
174
+ try:
175
+ return await super().query_workflow(input)
176
+ except Exception as err:
177
+ self._owner._record("query", input.query, input.id, input.run_id, err)
178
+ raise
179
+
180
+ async def signal_workflow(self, input: temporalio.client.SignalWorkflowInput) -> None:
181
+ try:
182
+ await super().signal_workflow(input)
183
+ except Exception as err:
184
+ self._owner._record("signal", input.signal, input.id, input.run_id, err)
185
+ raise
@@ -0,0 +1,56 @@
1
+ """What the CI side of the SDK shares: the error a call that has to fail
2
+ raises, and the message that says Flowrensic could not be reached or what it
3
+ answered."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Optional
8
+
9
+ from connectrpc.code import Code
10
+ from connectrpc.errors import ConnectError
11
+
12
+ __all__ = ["CIError", "ci_error", "ci_failure", "not_reached"]
13
+
14
+
15
+ class CIError(Exception):
16
+ """A CI call to Flowrensic that failed. Its text says that Flowrensic
17
+ could not be reached in time, or what it answered. code is the Connect code
18
+ Flowrensic answered with, None when it was not reached."""
19
+
20
+ def __init__(self, message: str, code: Optional[Code] = None) -> None:
21
+ super().__init__(message)
22
+ self.message = message
23
+ self.code = code
24
+
25
+
26
+ def not_reached(error: BaseException) -> bool:
27
+ """Whether a call failed because nothing answered in time or no connection
28
+ could be made. The client raises a transport error, such as a refused
29
+ connection, as a ConnectError raised from that error, while an answer of
30
+ Flowrensic carries no error under it."""
31
+ if not isinstance(error, ConnectError):
32
+ return True
33
+ if error.code in (Code.DEADLINE_EXCEEDED, Code.CANCELED):
34
+ return True
35
+ return error.__cause__ is not None
36
+
37
+
38
+ def _seconds(timeout_ms: int) -> str:
39
+ return f"{timeout_ms / 1000:g}"
40
+
41
+
42
+ def ci_failure(what: str, server_base_url: str, timeout_ms: int, error: BaseException) -> str:
43
+ """The message a CI call stops with: that Flowrensic could not be reached
44
+ in time, or what it answered, the code in brackets as Connect writes it and
45
+ the message. what names the call, such as deploy report."""
46
+ if not_reached(error):
47
+ cause = error.message if isinstance(error, ConnectError) else str(error)
48
+ return f"{what}: Flowrensic at {server_base_url} could not be reached within {_seconds(timeout_ms)} seconds: {cause}"
49
+ assert isinstance(error, ConnectError)
50
+ return f"{what}: Flowrensic at {server_base_url} answered [{error.code.value}] {error.message}"
51
+
52
+
53
+ def ci_error(what: str, server_base_url: str, timeout_ms: int, error: BaseException) -> CIError:
54
+ """The error a CI call that has to fail raises, with the message of ci_failure."""
55
+ code = None if not_reached(error) or not isinstance(error, ConnectError) else error.code
56
+ return CIError(ci_failure(what, server_base_url, timeout_ms, error), code)
@@ -0,0 +1,65 @@
1
+ """The deploy stage report a CI job sends, with a time limit, so it never hangs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Optional, Sequence
7
+
8
+ from temporal_flowrensic._ci import ci_error, ci_failure
9
+ from temporal_flowrensic._gen.flowrensic.v1.deploy_pb2 import IngestDeployStageRequest
10
+ from temporal_flowrensic._gen.flowrensic.v1.services_connect import IngestServiceClient
11
+ from temporal_flowrensic._sink import client_headers
12
+
13
+ __all__ = ["DeployStageResult", "report_deploy_stage"]
14
+
15
+ _DEPLOY_REPORT_TIMEOUT_MS = 10_000
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class DeployStageResult:
20
+ """What a deploy report did."""
21
+
22
+ sent: bool
23
+ """Whether Flowrensic took the write."""
24
+ message: str
25
+ """What happened, in one line, for the CI log."""
26
+
27
+
28
+ async def report_deploy_stage(
29
+ *,
30
+ server_base_url: str,
31
+ env_access_key: str,
32
+ deploy_id: str,
33
+ current_env: str,
34
+ stage: str,
35
+ current_ephemeral_run: str = "",
36
+ declared_stages: Optional[Sequence[str]] = None,
37
+ rolling_out_version: str = "",
38
+ outcome: str = "",
39
+ timeout_ms: int = _DEPLOY_REPORT_TIMEOUT_MS,
40
+ fail_on_error: bool = False,
41
+ ) -> DeployStageResult:
42
+ """Sends one stage of a deploy to Flowrensic: initiated, started, replay,
43
+ testsStarted, testsDone or finished. The report has a time limit of
44
+ timeout_ms milliseconds, 10000 by default. A report that fails returns
45
+ sent False and a message that says Flowrensic could not be reached or what
46
+ it answered, so the deploy goes on; with fail_on_error it raises CIError instead.
47
+ current_env, env_access_key and current_ephemeral_run go in their headers."""
48
+ try:
49
+ async with IngestServiceClient(server_base_url.rstrip("/"), send_compression=None) as ingest:
50
+ await ingest.ingest_deploy_stage(
51
+ IngestDeployStageRequest(
52
+ deploy_id=deploy_id,
53
+ stage=stage,
54
+ declared_stages=list(declared_stages or []),
55
+ rolling_out_version=rolling_out_version,
56
+ outcome=outcome,
57
+ ),
58
+ headers=client_headers(current_env, env_access_key, current_ephemeral_run),
59
+ timeout_ms=max(1, timeout_ms),
60
+ )
61
+ except Exception as error: # noqa: BLE001
62
+ if fail_on_error:
63
+ raise ci_error("deploy report", server_base_url, timeout_ms, error) from error
64
+ return DeployStageResult(False, ci_failure("deploy report", server_base_url, timeout_ms, error))
65
+ return DeployStageResult(True, f"deploy report: Flowrensic at {server_base_url} took the stage {stage} of {deploy_id}")
@@ -0,0 +1,76 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: flowrensic/v1/activity_metrics.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'flowrensic/v1/activity_metrics.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+ from temporal_flowrensic._gen.flowrensic.v1 import page_pb2 as flowrensic_dot_v1_dot_page__pb2
26
+ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
27
+
28
+
29
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$flowrensic/v1/activity_metrics.proto\x12\rflowrensic.v1\x1a\x18\x66lowrensic/v1/page.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xbf\x06\n\x0f\x41\x63tivityAttempt\x12\x1c\n\tnamespace\x18\x15 \x01(\tR\tnamespace\x12#\n\rworkflow_type\x18\x02 \x01(\tR\x0cworkflowType\x12\x32\n\x15workflow_execution_id\x18\x03 \x01(\tR\x13workflowExecutionId\x12\x17\n\x07run_key\x18\x04 \x01(\tR\x06runKey\x12#\n\ractivity_type\x18\x05 \x01(\tR\x0c\x61\x63tivityType\x12\x1f\n\x0b\x61\x63tivity_id\x18\x06 \x01(\tR\nactivityId\x12\x18\n\x07\x61ttempt\x18\x07 \x01(\x05R\x07\x61ttempt\x12\x1d\n\ntask_queue\x18\x08 \x01(\tR\ttaskQueue\x12\x30\n\x07outcome\x18\t \x01(\x0e\x32\x16.flowrensic.v1.OutcomeR\x07outcome\x12\x1d\n\nerror_type\x18\n \x01(\tR\terrorType\x12\x39\n\nstarted_at\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tstartedAt\x12\x35\n\x08\x65nded_at\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x07\x65ndedAt\x12\x1f\n\x0b\x64uration_ms\x18\r \x01(\x03R\ndurationMs\x12\x10\n\x03\x65nv\x18\x0e \x01(\tR\x03\x65nv\x12#\n\rephemeral_run\x18\x0f \x01(\tR\x0c\x65phemeralRun\x12\x18\n\x07version\x18\x10 \x01(\tR\x07version\x12\x38\n\x19start_to_close_timeout_ms\x18\x11 \x01(\x03R\x15startToCloseTimeoutMs\x12)\n\x10maximum_attempts\x18\x12 \x01(\x05R\x0fmaximumAttempts\x12\x30\n\x14heartbeat_timeout_ms\x18\x13 \x01(\x03R\x12heartbeatTimeoutMs\x12=\n\x0cscheduled_at\x18\x14 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x0bscheduledAtJ\x04\x08\x01\x10\x02R\x0cnamespace_id\"\x96\x06\n\rActivityStats\x12!\n\x0cnamespace_id\x18\x01 \x01(\tR\x0bnamespaceId\x12\x10\n\x03\x65nv\x18\x02 \x01(\tR\x03\x65nv\x12#\n\rworkflow_type\x18\x03 \x01(\tR\x0cworkflowType\x12#\n\ractivity_type\x18\x04 \x01(\tR\x0c\x61\x63tivityType\x12\x38\n\x19start_to_close_timeout_ms\x18\x13 \x01(\x03R\x15startToCloseTimeoutMs\x12)\n\x10maximum_attempts\x18\x14 \x01(\x05R\x0fmaximumAttempts\x12\x1e\n\nexecutions\x18\x05 \x01(\x05R\nexecutions\x12\x1a\n\x08\x61ttempts\x18\x06 \x01(\x05R\x08\x61ttempts\x12\x18\n\x07retries\x18\x07 \x01(\x05R\x07retries\x12\x1c\n\tcompleted\x18\x08 \x01(\x05R\tcompleted\x12\x16\n\x06\x66\x61iled\x18\t \x01(\x05R\x06\x66\x61iled\x12\x1b\n\ttimed_out\x18\n \x01(\x05R\x08timedOut\x12\x1a\n\x08\x63\x61nceled\x18\x0b \x01(\x05R\x08\x63\x61nceled\x12M\n\x0b\x65rror_types\x18\x0c \x03(\x0b\x32,.flowrensic.v1.ActivityStats.ErrorTypesEntryR\nerrorTypes\x12\x15\n\x06p50_ms\x18\r \x01(\x03R\x05p50Ms\x12\x15\n\x06p95_ms\x18\x0e \x01(\x03R\x05p95Ms\x12\x15\n\x06p99_ms\x18\x0f \x01(\x03R\x05p99Ms\x12\x15\n\x06max_ms\x18\x10 \x01(\x03R\x05maxMs\x12\x39\n\nfirst_seen\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tfirstSeen\x12\x37\n\tlast_seen\x18\x12 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x08lastSeen\x1a=\n\x0f\x45rrorTypesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x05R\x05value:\x02\x38\x01\"\x89\x02\n\x1bIngestActivityEventsRequest\x12\x36\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1e.flowrensic.v1.ActivityAttemptR\x06\x65vents\x12\x1b\n\tworker_id\x18\x02 \x01(\tR\x08workerId\x12;\n\x07reports\x18\x03 \x03(\x0b\x32!.flowrensic.v1.WorkerBufferReportR\x07reports\x12\x16\n\x06sender\x18\x04 \x01(\tR\x06sender\x12\x1f\n\x0bsend_number\x18\x05 \x01(\x03R\nsendNumber\x12\x1f\n\x0bworker_name\x18\x06 \x01(\tR\nworkerName\"\xa0\x01\n\x12WorkerBufferReport\x12\x10\n\x03\x65nv\x18\x01 \x01(\tR\x03\x65nv\x12\'\n\x0frecords_dropped\x18\x02 \x01(\x03R\x0erecordsDropped\x12&\n\x0fsends_timed_out\x18\x03 \x01(\x03R\rsendsTimedOut\x12\'\n\x0frecords_waiting\x18\x04 \x01(\x03R\x0erecordsWaiting\":\n\x1cIngestActivityEventsResponse\x12\x1a\n\x08\x61\x63\x63\x65pted\x18\x01 \x01(\x05R\x08\x61\x63\x63\x65pted\"\xc3\x05\n\x0bWorkflowRun\x12\x32\n\x15workflow_execution_id\x18\x01 \x01(\tR\x13workflowExecutionId\x12\x17\n\x07run_key\x18\x02 \x01(\tR\x06runKey\x12#\n\rworkflow_type\x18\x03 \x01(\tR\x0cworkflowType\x12!\n\x0cnamespace_id\x18\x04 \x01(\tR\x0bnamespaceId\x12\x10\n\x03\x65nv\x18\x05 \x01(\tR\x03\x65nv\x12\x1e\n\nactivities\x18\x06 \x01(\x05R\nactivities\x12\x1a\n\x08\x61ttempts\x18\x07 \x01(\x05R\x08\x61ttempts\x12\x18\n\x07retries\x18\x08 \x01(\x05R\x07retries\x12\x16\n\x06\x66\x61iled\x18\t \x01(\x05R\x06\x66\x61iled\x12\x39\n\nstarted_at\x18\n \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tstartedAt\x12\x37\n\tlast_seen\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x08lastSeen\x12#\n\rephemeral_run\x18\x0c \x01(\tR\x0c\x65phemeralRun\x12\x1b\n\tdeploy_id\x18\r \x01(\tR\x08\x64\x65ployId\x12!\n\x0c\x64\x65ploy_stage\x18\x0e \x01(\tR\x0b\x64\x65ployStage\x12\x18\n\x07version\x18\x0f \x01(\tR\x07version\x12%\n\x0ehistory_length\x18\x10 \x01(\x05R\rhistoryLength\x12)\n\x10updates_accepted\x18\x11 \x01(\x05R\x0fupdatesAccepted\x12\'\n\x0fupdates_refused\x18\x12 \x01(\x05R\x0eupdatesRefused\x12\x31\n\x14\x63onsecutive_refusals\x18\x13 \x01(\x05R\x13\x63onsecutiveRefusals\"\xc0\x01\n\x0fListRunsRequest\x12#\n\rnamespace_ids\x18\x01 \x03(\tR\x0cnamespaceIds\x12\x10\n\x03\x65nv\x18\x02 \x01(\tR\x03\x65nv\x12#\n\rworkflow_type\x18\x03 \x01(\tR\x0cworkflowType\x12.\n\x04page\x18\x06 \x01(\x0b\x32\x1a.flowrensic.v1.PageRequestR\x04pageJ\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06R\tpage_sizeR\npage_token\"\x8a\x01\n\x10ListRunsResponse\x12.\n\x04runs\x18\x01 \x03(\x0b\x32\x1a.flowrensic.v1.WorkflowRunR\x04runs\x12/\n\x04page\x18\x03 \x01(\x0b\x32\x1b.flowrensic.v1.PageResponseR\x04pageJ\x04\x08\x02\x10\x03R\x0fnext_page_token\"\xbf\x02\n\x0c\x45ngineMetric\x12\x1b\n\tserver_id\x18\x01 \x01(\tR\x08serverId\x12\x10\n\x03\x65nv\x18\x02 \x01(\tR\x03\x65nv\x12\x12\n\x04name\x18\x03 \x01(\tR\x04name\x12?\n\x06labels\x18\x04 \x03(\x0b\x32\'.flowrensic.v1.EngineMetric.LabelsEntryR\x06labels\x12\x14\n\x05value\x18\x05 \x01(\x01R\x05value\x12\x39\n\nscraped_at\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tscrapedAt\x12\x1f\n\x0bmetric_type\x18\x07 \x01(\tR\nmetricType\x1a\x39\n\x0bLabelsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\x86\x01\n\x14\x45ngineMetricsRequest\x12\x1d\n\nserver_ids\x18\x01 \x03(\tR\tserverIds\x12\x1f\n\x0bname_prefix\x18\x02 \x01(\tR\nnamePrefix\x12.\n\x04page\x18\x03 \x01(\x0b\x32\x1a.flowrensic.v1.PageRequestR\x04page\"\x7f\n\x15\x45ngineMetricsResponse\x12\x35\n\x07metrics\x18\x01 \x03(\x0b\x32\x1b.flowrensic.v1.EngineMetricR\x07metrics\x12/\n\x04page\x18\x02 \x01(\x0b\x32\x1b.flowrensic.v1.PageResponseR\x04page\"<\n\x17UpdateRejectionsRequest\x12!\n\x0cnamespace_id\x18\x01 \x01(\tR\x0bnamespaceId\"\x98\x01\n\x18UpdateRejectionsResponse\x12!\n\x0cnamespace_id\x18\x01 \x01(\tR\x0bnamespaceId\x12\x1e\n\nrejections\x18\x02 \x01(\x01R\nrejections\x12\x39\n\nscraped_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tscrapedAt\"\xab\x02\n\x14\x41\x63tivityStatsRequest\x12#\n\rnamespace_ids\x18\x01 \x03(\tR\x0cnamespaceIds\x12\x10\n\x03\x65nv\x18\x02 \x01(\tR\x03\x65nv\x12#\n\rworkflow_type\x18\x03 \x01(\tR\x0cworkflowType\x12#\n\ractivity_type\x18\x04 \x01(\tR\x0c\x61\x63tivityType\x12\x30\n\x05since\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x05since\x12\x30\n\x05until\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x05until\x12.\n\x04page\x18\x07 \x01(\x0b\x32\x1a.flowrensic.v1.PageRequestR\x04page\"|\n\x15\x41\x63tivityStatsResponse\x12\x32\n\x05stats\x18\x01 \x03(\x0b\x32\x1c.flowrensic.v1.ActivityStatsR\x05stats\x12/\n\x04page\x18\x02 \x01(\x0b\x32\x1b.flowrensic.v1.PageResponseR\x04page*z\n\x07Outcome\x12\x17\n\x13OUTCOME_UNSPECIFIED\x10\x00\x12\x15\n\x11OUTCOME_COMPLETED\x10\x01\x12\x12\n\x0eOUTCOME_FAILED\x10\x02\x12\x15\n\x11OUTCOME_TIMED_OUT\x10\x03\x12\x14\n\x10OUTCOME_CANCELED\x10\x04\x62\x06proto3')
30
+
31
+ _globals = globals()
32
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
33
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flowrensic.v1.activity_metrics_pb2', _globals)
34
+ if not _descriptor._USE_C_DESCRIPTORS:
35
+ DESCRIPTOR._loaded_options = None
36
+ _globals['_ACTIVITYSTATS_ERRORTYPESENTRY']._loaded_options = None
37
+ _globals['_ACTIVITYSTATS_ERRORTYPESENTRY']._serialized_options = b'8\001'
38
+ _globals['_ENGINEMETRIC_LABELSENTRY']._loaded_options = None
39
+ _globals['_ENGINEMETRIC_LABELSENTRY']._serialized_options = b'8\001'
40
+ _globals['_OUTCOME']._serialized_start=4511
41
+ _globals['_OUTCOME']._serialized_end=4633
42
+ _globals['_ACTIVITYATTEMPT']._serialized_start=115
43
+ _globals['_ACTIVITYATTEMPT']._serialized_end=946
44
+ _globals['_ACTIVITYSTATS']._serialized_start=949
45
+ _globals['_ACTIVITYSTATS']._serialized_end=1739
46
+ _globals['_ACTIVITYSTATS_ERRORTYPESENTRY']._serialized_start=1678
47
+ _globals['_ACTIVITYSTATS_ERRORTYPESENTRY']._serialized_end=1739
48
+ _globals['_INGESTACTIVITYEVENTSREQUEST']._serialized_start=1742
49
+ _globals['_INGESTACTIVITYEVENTSREQUEST']._serialized_end=2007
50
+ _globals['_WORKERBUFFERREPORT']._serialized_start=2010
51
+ _globals['_WORKERBUFFERREPORT']._serialized_end=2170
52
+ _globals['_INGESTACTIVITYEVENTSRESPONSE']._serialized_start=2172
53
+ _globals['_INGESTACTIVITYEVENTSRESPONSE']._serialized_end=2230
54
+ _globals['_WORKFLOWRUN']._serialized_start=2233
55
+ _globals['_WORKFLOWRUN']._serialized_end=2940
56
+ _globals['_LISTRUNSREQUEST']._serialized_start=2943
57
+ _globals['_LISTRUNSREQUEST']._serialized_end=3135
58
+ _globals['_LISTRUNSRESPONSE']._serialized_start=3138
59
+ _globals['_LISTRUNSRESPONSE']._serialized_end=3276
60
+ _globals['_ENGINEMETRIC']._serialized_start=3279
61
+ _globals['_ENGINEMETRIC']._serialized_end=3598
62
+ _globals['_ENGINEMETRIC_LABELSENTRY']._serialized_start=3541
63
+ _globals['_ENGINEMETRIC_LABELSENTRY']._serialized_end=3598
64
+ _globals['_ENGINEMETRICSREQUEST']._serialized_start=3601
65
+ _globals['_ENGINEMETRICSREQUEST']._serialized_end=3735
66
+ _globals['_ENGINEMETRICSRESPONSE']._serialized_start=3737
67
+ _globals['_ENGINEMETRICSRESPONSE']._serialized_end=3864
68
+ _globals['_UPDATEREJECTIONSREQUEST']._serialized_start=3866
69
+ _globals['_UPDATEREJECTIONSREQUEST']._serialized_end=3926
70
+ _globals['_UPDATEREJECTIONSRESPONSE']._serialized_start=3929
71
+ _globals['_UPDATEREJECTIONSRESPONSE']._serialized_end=4081
72
+ _globals['_ACTIVITYSTATSREQUEST']._serialized_start=4084
73
+ _globals['_ACTIVITYSTATSREQUEST']._serialized_end=4383
74
+ _globals['_ACTIVITYSTATSRESPONSE']._serialized_start=4385
75
+ _globals['_ACTIVITYSTATSRESPONSE']._serialized_end=4509
76
+ # @@protoc_insertion_point(module_scope)