@temporalio/core-bridge 1.15.0 → 1.16.1
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.
- package/Cargo.lock +172 -70
- package/lib/native.d.ts +1 -1
- package/package.json +2 -2
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.github/workflows/per-pr.yml +6 -6
- package/sdk-core/AGENTS.md +41 -30
- package/sdk-core/Cargo.toml +3 -0
- package/sdk-core/README.md +15 -9
- package/sdk-core/crates/client/Cargo.toml +4 -0
- package/sdk-core/crates/client/README.md +139 -0
- package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
- package/sdk-core/crates/client/src/callback_based.rs +7 -0
- package/sdk-core/crates/client/src/errors.rs +294 -0
- package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +280 -159
- package/sdk-core/crates/client/src/lib.rs +920 -1326
- package/sdk-core/crates/client/src/metrics.rs +24 -33
- package/sdk-core/crates/client/src/options_structs.rs +457 -0
- package/sdk-core/crates/client/src/replaceable.rs +5 -4
- package/sdk-core/crates/client/src/request_extensions.rs +8 -9
- package/sdk-core/crates/client/src/retry.rs +99 -54
- package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +1 -1
- package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
- package/sdk-core/crates/common/Cargo.toml +61 -2
- package/sdk-core/crates/common/build.rs +742 -12
- package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
- package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
- package/sdk-core/crates/common/protos/api_upstream/buf.yaml +0 -3
- package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +1166 -770
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1243 -750
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +2 -2
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -3
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +16 -1
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -6
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +88 -33
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +4 -2
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +5 -5
- package/sdk-core/crates/common/src/activity_definition.rs +20 -0
- package/sdk-core/crates/common/src/data_converters.rs +770 -0
- package/sdk-core/crates/common/src/envconfig.rs +5 -0
- package/sdk-core/crates/common/src/lib.rs +15 -211
- package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
- package/sdk-core/crates/common/src/priority.rs +110 -0
- package/sdk-core/crates/common/src/protos/canned_histories.rs +3 -0
- package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
- package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
- package/sdk-core/crates/common/src/protos/mod.rs +122 -27
- package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
- package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +5 -7
- package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
- package/sdk-core/crates/common/src/telemetry/metrics.rs +268 -223
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
- package/sdk-core/crates/common/src/telemetry.rs +264 -4
- package/sdk-core/crates/common/src/worker.rs +68 -603
- package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
- package/sdk-core/crates/macros/Cargo.toml +5 -1
- package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
- package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
- package/sdk-core/crates/macros/src/lib.rs +138 -512
- package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
- package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
- package/sdk-core/crates/sdk/Cargo.toml +19 -6
- package/sdk-core/crates/sdk/README.md +415 -0
- package/sdk-core/crates/sdk/src/activities.rs +417 -0
- package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
- package/sdk-core/crates/sdk/src/lib.rs +757 -442
- package/sdk-core/crates/sdk/src/workflow_context/options.rs +45 -35
- package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
- package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
- package/sdk-core/crates/sdk/src/workflows.rs +711 -0
- package/sdk-core/crates/sdk-core/Cargo.toml +57 -64
- package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +41 -35
- package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
- package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
- package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
- package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +13 -15
- package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
- package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
- package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
- package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +493 -26
- package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +4 -8
- package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +7 -7
- package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
- package/sdk-core/crates/sdk-core/src/lib.rs +41 -111
- package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
- package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +118 -19
- package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
- package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
- package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +179 -196
- package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -280
- package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +6 -9
- package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
- package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
- package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
- package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +9 -5
- package/sdk-core/crates/sdk-core/src/worker/client.rs +103 -81
- package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +7 -11
- package/sdk-core/crates/sdk-core/src/worker/mod.rs +1124 -229
- package/sdk-core/crates/sdk-core/src/worker/nexus.rs +145 -23
- package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
- package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
- package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +13 -13
- package/sdk-core/crates/sdk-core/src/worker/tuner.rs +28 -8
- package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +19 -4
- package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +14 -18
- package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +4 -6
- package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
- package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
- package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
- package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
- package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
- package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
- package/sdk-core/crates/sdk-core/tests/common/mod.rs +241 -196
- package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
- package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +3 -5
- package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -64
- package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +298 -252
- package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
- package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +16 -12
- package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +48 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +327 -255
- package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
- package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +147 -126
- package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
- package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -453
- package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +360 -231
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +248 -185
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -43
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +428 -315
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -56
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -28
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -243
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +101 -42
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -147
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -28
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1036
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -41
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +397 -238
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +414 -189
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +154 -137
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -105
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -38
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -54
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +363 -226
- package/sdk-core/crates/sdk-core/tests/main.rs +17 -15
- package/sdk-core/crates/sdk-core/tests/manual_tests.rs +207 -152
- package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +65 -34
- package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
- package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +7 -1
- package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +14 -14
- package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +83 -74
- package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +9 -14
- package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +1 -2
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +13 -13
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +6 -6
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +3 -4
- package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +62 -75
- package/sdk-core/rustfmt.toml +2 -1
- package/src/client.rs +205 -318
- package/src/metrics.rs +22 -30
- package/src/runtime.rs +4 -5
- package/src/worker.rs +16 -19
- package/ts/native.ts +1 -1
- package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
- package/sdk-core/crates/common/src/errors.rs +0 -85
- package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
- package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
- package/sdk-core/crates/sdk/src/app_data.rs +0 -37
- package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
#[macro_use]
|
|
8
8
|
extern crate tracing;
|
|
9
9
|
|
|
10
|
+
mod async_activity_handle;
|
|
10
11
|
pub mod callback_based;
|
|
12
|
+
pub mod errors;
|
|
13
|
+
pub mod grpc;
|
|
11
14
|
mod metrics;
|
|
15
|
+
mod options_structs;
|
|
12
16
|
/// Visible only for tests
|
|
13
17
|
#[doc(hidden)]
|
|
14
18
|
pub mod proxy;
|
|
15
|
-
mod raw;
|
|
16
19
|
mod replaceable;
|
|
17
20
|
pub mod request_extensions;
|
|
18
21
|
mod retry;
|
|
@@ -21,60 +24,68 @@ mod workflow_handle;
|
|
|
21
24
|
|
|
22
25
|
pub use crate::{
|
|
23
26
|
proxy::HttpConnectProxyOptions,
|
|
24
|
-
retry::{CallType, RETRYABLE_ERROR_CODES
|
|
27
|
+
retry::{CallType, RETRYABLE_ERROR_CODES},
|
|
25
28
|
};
|
|
29
|
+
pub use async_activity_handle::{
|
|
30
|
+
ActivityHeartbeatResponse, ActivityIdentifier, AsyncActivityHandle,
|
|
31
|
+
};
|
|
32
|
+
|
|
26
33
|
pub use metrics::{LONG_REQUEST_LATENCY_HISTOGRAM_NAME, REQUEST_LATENCY_HISTOGRAM_NAME};
|
|
27
|
-
pub use
|
|
34
|
+
pub use options_structs::*;
|
|
28
35
|
pub use replaceable::SharedReplaceableClient;
|
|
36
|
+
pub use retry::RetryOptions;
|
|
29
37
|
pub use tonic;
|
|
30
38
|
pub use workflow_handle::{
|
|
31
|
-
|
|
39
|
+
UntypedQuery, UntypedSignal, UntypedUpdate, UntypedWorkflow, UntypedWorkflowHandle,
|
|
40
|
+
WorkflowExecutionDescription, WorkflowExecutionInfo, WorkflowExecutionResult, WorkflowHandle,
|
|
41
|
+
WorkflowHistory, WorkflowUpdateHandle,
|
|
32
42
|
};
|
|
33
43
|
|
|
34
44
|
use crate::{
|
|
45
|
+
grpc::{
|
|
46
|
+
AttachMetricLabels, CloudService, HealthService, OperatorService, TestService,
|
|
47
|
+
WorkflowService,
|
|
48
|
+
},
|
|
35
49
|
metrics::{ChannelOrGrpcOverride, GrpcMetricSvc, MetricsContext},
|
|
36
|
-
raw::AttachMetricLabels,
|
|
37
50
|
request_extensions::RequestExt,
|
|
38
|
-
sealed::WfHandleClient,
|
|
39
51
|
worker::ClientWorkerSet,
|
|
40
|
-
workflow_handle::UntypedWorkflowHandle,
|
|
41
52
|
};
|
|
42
|
-
use
|
|
43
|
-
use
|
|
53
|
+
use errors::*;
|
|
54
|
+
use futures_util::{stream, stream::Stream};
|
|
55
|
+
use http::Uri;
|
|
44
56
|
use parking_lot::RwLock;
|
|
45
57
|
use std::{
|
|
46
|
-
collections::HashMap,
|
|
47
|
-
fmt::
|
|
48
|
-
|
|
58
|
+
collections::{HashMap, VecDeque},
|
|
59
|
+
fmt::Debug,
|
|
60
|
+
pin::Pin,
|
|
49
61
|
str::FromStr,
|
|
50
62
|
sync::{Arc, OnceLock},
|
|
51
|
-
|
|
63
|
+
task::{Context, Poll},
|
|
64
|
+
time::{Duration, SystemTime},
|
|
52
65
|
};
|
|
53
66
|
use temporalio_common::{
|
|
67
|
+
WorkflowDefinition,
|
|
68
|
+
data_converters::{DataConverter, SerializationContextData},
|
|
54
69
|
protos::{
|
|
55
|
-
TaskToken,
|
|
56
70
|
coresdk::IntoPayloadsExt,
|
|
57
71
|
grpc::health::v1::health_client::HealthClient,
|
|
72
|
+
proto_ts_to_system_time,
|
|
58
73
|
temporal::api::{
|
|
59
74
|
cloud::cloudservice::v1::cloud_service_client::CloudServiceClient,
|
|
60
|
-
common::{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
},
|
|
64
|
-
enums::v1::{
|
|
65
|
-
ArchivalState, TaskQueueKind, WorkflowIdConflictPolicy, WorkflowIdReusePolicy,
|
|
66
|
-
},
|
|
67
|
-
filter::v1::StartTimeFilter,
|
|
75
|
+
common::v1::{Memo, Payload, SearchAttributes, WorkflowType},
|
|
76
|
+
enums::v1::{TaskQueueKind, WorkflowExecutionStatus},
|
|
77
|
+
errordetails::v1::WorkflowExecutionAlreadyStartedFailure,
|
|
68
78
|
operatorservice::v1::operator_service_client::OperatorServiceClient,
|
|
69
|
-
query::v1::WorkflowQuery,
|
|
70
|
-
replication::v1::ClusterReplicationConfig,
|
|
71
79
|
taskqueue::v1::TaskQueue,
|
|
72
80
|
testservice::v1::test_service_client::TestServiceClient,
|
|
73
|
-
|
|
74
|
-
workflowservice::v1::{
|
|
81
|
+
workflow::v1 as workflow,
|
|
82
|
+
workflowservice::v1::{
|
|
83
|
+
count_workflow_executions_response, workflow_service_client::WorkflowServiceClient,
|
|
84
|
+
*,
|
|
85
|
+
},
|
|
75
86
|
},
|
|
87
|
+
utilities::decode_status_detail,
|
|
76
88
|
},
|
|
77
|
-
telemetry::metrics::TemporalMeter,
|
|
78
89
|
};
|
|
79
90
|
use tonic::{
|
|
80
91
|
Code, IntoRequest,
|
|
@@ -89,308 +100,150 @@ use tonic::{
|
|
|
89
100
|
transport::{Certificate, Channel, Endpoint, Identity},
|
|
90
101
|
};
|
|
91
102
|
use tower::ServiceBuilder;
|
|
92
|
-
use url::Url;
|
|
93
103
|
use uuid::Uuid;
|
|
94
104
|
|
|
95
105
|
static CLIENT_NAME_HEADER_KEY: &str = "client-name";
|
|
96
106
|
static CLIENT_VERSION_HEADER_KEY: &str = "client-version";
|
|
97
107
|
static TEMPORAL_NAMESPACE_HEADER_KEY: &str = "temporal-namespace";
|
|
98
108
|
|
|
109
|
+
#[doc(hidden)]
|
|
99
110
|
/// Key used to communicate when a GRPC message is too large
|
|
100
111
|
pub static MESSAGE_TOO_LARGE_KEY: &str = "message-too-large";
|
|
112
|
+
#[doc(hidden)]
|
|
101
113
|
/// Key used to indicate a error was returned by the retryer because of the short-circuit predicate
|
|
102
114
|
pub static ERROR_RETURNED_DUE_TO_SHORT_CIRCUIT: &str = "short-circuit";
|
|
103
115
|
|
|
104
116
|
/// The server times out polls after 60 seconds. Set our timeout to be slightly beyond that.
|
|
105
117
|
const LONG_POLL_TIMEOUT: Duration = Duration::from_secs(70);
|
|
106
118
|
const OTHER_CALL_TIMEOUT: Duration = Duration::from_secs(30);
|
|
119
|
+
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
107
120
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
///
|
|
111
|
-
|
|
112
|
-
#[non_exhaustive]
|
|
113
|
-
#[builder(on(String, into), state_mod(vis = "pub"))]
|
|
114
|
-
pub struct ClientOptions {
|
|
115
|
-
/// The URL of the Temporal server to connect to
|
|
116
|
-
#[builder(into)]
|
|
117
|
-
pub target_url: Url,
|
|
118
|
-
|
|
119
|
-
/// The name of the SDK being implemented on top of core. Is set as `client-name` header in
|
|
120
|
-
/// all RPC calls
|
|
121
|
-
pub client_name: String,
|
|
122
|
-
|
|
123
|
-
/// The version of the SDK being implemented on top of core. Is set as `client-version` header
|
|
124
|
-
/// in all RPC calls. The server decides if the client is supported based on this.
|
|
125
|
-
pub client_version: String,
|
|
126
|
-
|
|
127
|
-
/// A human-readable string that can identify this process. Defaults to empty string.
|
|
128
|
-
#[builder(default)]
|
|
129
|
-
pub identity: String,
|
|
130
|
-
|
|
131
|
-
/// If specified, use TLS as configured by the [TlsOptions] struct. If this is set core will
|
|
132
|
-
/// attempt to use TLS when connecting to the Temporal server. Lang SDK is expected to pass any
|
|
133
|
-
/// certs or keys as bytes, loading them from disk itself if needed.
|
|
134
|
-
pub tls_options: Option<TlsOptions>,
|
|
135
|
-
|
|
136
|
-
/// Retry configuration for the server client. Default is [RetryOptions::default]
|
|
137
|
-
#[builder(default)]
|
|
138
|
-
pub retry_options: RetryOptions,
|
|
139
|
-
|
|
140
|
-
/// If set, override the origin used when connecting. May be useful in rare situations where tls
|
|
141
|
-
/// verification needs to use a different name from what should be set as the `:authority`
|
|
142
|
-
/// header. If [TlsOptions::domain] is set, and this is not, this will be set to
|
|
143
|
-
/// `https://<domain>`, effectively making the `:authority` header consistent with the domain
|
|
144
|
-
/// override.
|
|
145
|
-
pub override_origin: Option<Uri>,
|
|
146
|
-
|
|
147
|
-
/// If set, HTTP2 gRPC keep alive will be enabled.
|
|
148
|
-
/// To enable with default settings, use `.keep_alive(ClientKeepAliveConfig::default())`.
|
|
149
|
-
#[builder(required, default = Some(ClientKeepAliveOptions::default()))]
|
|
150
|
-
pub keep_alive: Option<ClientKeepAliveOptions>,
|
|
151
|
-
|
|
152
|
-
/// HTTP headers to include on every RPC call.
|
|
153
|
-
///
|
|
154
|
-
/// These must be valid gRPC metadata keys, and must not be binary metadata keys (ending in
|
|
155
|
-
/// `-bin). To set binary headers, use [ClientOptions::binary_headers]. Invalid header keys or
|
|
156
|
-
/// values will cause an error to be returned when connecting.
|
|
157
|
-
pub headers: Option<HashMap<String, String>>,
|
|
158
|
-
|
|
159
|
-
/// HTTP headers to include on every RPC call as binary gRPC metadata (encoded as base64).
|
|
160
|
-
///
|
|
161
|
-
/// These must be valid binary gRPC metadata keys (and end with a `-bin` suffix). Invalid
|
|
162
|
-
/// header keys will cause an error to be returned when connecting.
|
|
163
|
-
pub binary_headers: Option<HashMap<String, Vec<u8>>>,
|
|
164
|
-
|
|
165
|
-
/// API key which is set as the "Authorization" header with "Bearer " prepended. This will only
|
|
166
|
-
/// be applied if the headers don't already have an "Authorization" header.
|
|
167
|
-
pub api_key: Option<String>,
|
|
168
|
-
|
|
169
|
-
/// HTTP CONNECT proxy to use for this client.
|
|
170
|
-
pub http_connect_proxy: Option<HttpConnectProxyOptions>,
|
|
171
|
-
|
|
172
|
-
/// If set true, error code labels will not be included on request failure metrics.
|
|
173
|
-
#[builder(default)]
|
|
174
|
-
pub disable_error_code_metric_tags: bool,
|
|
175
|
-
|
|
176
|
-
/// If set true, get_system_info will not be called upon connection
|
|
177
|
-
#[builder(default)]
|
|
178
|
-
pub skip_get_system_info: bool,
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/// Configuration options for TLS
|
|
182
|
-
#[derive(Clone, Debug, Default)]
|
|
183
|
-
pub struct TlsOptions {
|
|
184
|
-
/// Bytes representing the root CA certificate used by the server. If not set, and the server's
|
|
185
|
-
/// cert is issued by someone the operating system trusts, verification will still work (ex:
|
|
186
|
-
/// Cloud offering).
|
|
187
|
-
pub server_root_ca_cert: Option<Vec<u8>>,
|
|
188
|
-
/// Sets the domain name against which to verify the server's TLS certificate. If not provided,
|
|
189
|
-
/// the domain name will be extracted from the URL used to connect.
|
|
190
|
-
pub domain: Option<String>,
|
|
191
|
-
/// TLS info for the client. If specified, core will attempt to use mTLS.
|
|
192
|
-
pub client_tls_options: Option<ClientTlsOptions>,
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/// If using mTLS, both the client cert and private key must be specified, this contains them.
|
|
121
|
+
/// A connection to the Temporal service.
|
|
122
|
+
///
|
|
123
|
+
/// Cloning a connection is cheap (single Arc increment). The underlying connection is shared
|
|
124
|
+
/// between clones.
|
|
196
125
|
#[derive(Clone)]
|
|
197
|
-
pub struct
|
|
198
|
-
|
|
199
|
-
pub client_cert: Vec<u8>,
|
|
200
|
-
/// The private key for this client, encoded as PEM
|
|
201
|
-
pub client_private_key: Vec<u8>,
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/// Client keep alive configuration.
|
|
205
|
-
#[derive(Clone, Debug)]
|
|
206
|
-
pub struct ClientKeepAliveOptions {
|
|
207
|
-
/// Interval to send HTTP2 keep alive pings.
|
|
208
|
-
pub interval: Duration,
|
|
209
|
-
/// Timeout that the keep alive must be responded to within or the connection will be closed.
|
|
210
|
-
pub timeout: Duration,
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
impl Default for ClientKeepAliveOptions {
|
|
214
|
-
fn default() -> Self {
|
|
215
|
-
Self {
|
|
216
|
-
interval: Duration::from_secs(30),
|
|
217
|
-
timeout: Duration::from_secs(15),
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/// Configuration for retrying requests to the server
|
|
223
|
-
#[derive(Clone, Debug, PartialEq)]
|
|
224
|
-
pub struct RetryOptions {
|
|
225
|
-
/// initial wait time before the first retry.
|
|
226
|
-
pub initial_interval: Duration,
|
|
227
|
-
/// randomization jitter that is used as a multiplier for the current retry interval
|
|
228
|
-
/// and is added or subtracted from the interval length.
|
|
229
|
-
pub randomization_factor: f64,
|
|
230
|
-
/// rate at which retry time should be increased, until it reaches max_interval.
|
|
231
|
-
pub multiplier: f64,
|
|
232
|
-
/// maximum amount of time to wait between retries.
|
|
233
|
-
pub max_interval: Duration,
|
|
234
|
-
/// maximum total amount of time requests should be retried for, if None is set then no limit
|
|
235
|
-
/// will be used.
|
|
236
|
-
pub max_elapsed_time: Option<Duration>,
|
|
237
|
-
/// maximum number of retry attempts.
|
|
238
|
-
pub max_retries: usize,
|
|
126
|
+
pub struct Connection {
|
|
127
|
+
inner: Arc<ConnectionInner>,
|
|
239
128
|
}
|
|
240
129
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
130
|
+
#[derive(Clone)]
|
|
131
|
+
struct ConnectionInner {
|
|
132
|
+
service: TemporalServiceClient,
|
|
133
|
+
retry_options: RetryOptions,
|
|
134
|
+
identity: String,
|
|
135
|
+
headers: Arc<RwLock<ClientHeaders>>,
|
|
136
|
+
client_name: String,
|
|
137
|
+
client_version: String,
|
|
138
|
+
/// Capabilities as read from the `get_system_info` RPC call made on client connection
|
|
139
|
+
capabilities: Option<get_system_info_response::Capabilities>,
|
|
140
|
+
workers: Arc<ClientWorkerSet>,
|
|
252
141
|
}
|
|
253
142
|
|
|
254
|
-
impl
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
143
|
+
impl Connection {
|
|
144
|
+
/// Connect to a Temporal service.
|
|
145
|
+
pub async fn connect(options: ConnectionOptions) -> Result<Self, ClientConnectError> {
|
|
146
|
+
let service = if let Some(service_override) = options.service_override {
|
|
147
|
+
GrpcMetricSvc {
|
|
148
|
+
inner: ChannelOrGrpcOverride::GrpcOverride(service_override),
|
|
149
|
+
metrics: options.metrics_meter.clone().map(MetricsContext::new),
|
|
150
|
+
disable_errcode_label: options.disable_error_code_metric_tags,
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
let channel = Channel::from_shared(options.target.to_string())?;
|
|
154
|
+
let channel = add_tls_to_channel(options.tls_options.as_ref(), channel).await?;
|
|
155
|
+
let channel = if let Some(keep_alive) = options.keep_alive.as_ref() {
|
|
156
|
+
channel
|
|
157
|
+
.keep_alive_while_idle(true)
|
|
158
|
+
.http2_keep_alive_interval(keep_alive.interval)
|
|
159
|
+
.keep_alive_timeout(keep_alive.timeout)
|
|
160
|
+
} else {
|
|
161
|
+
channel
|
|
162
|
+
};
|
|
163
|
+
let channel = if let Some(origin) = options.override_origin.clone() {
|
|
164
|
+
channel.origin(origin)
|
|
165
|
+
} else {
|
|
166
|
+
channel
|
|
167
|
+
};
|
|
168
|
+
// If there is a proxy, we have to connect that way
|
|
169
|
+
let channel = if let Some(proxy) = options.http_connect_proxy.as_ref() {
|
|
170
|
+
proxy.connect_endpoint(&channel).await?
|
|
171
|
+
} else {
|
|
172
|
+
channel.connect().await?
|
|
173
|
+
};
|
|
174
|
+
ServiceBuilder::new()
|
|
175
|
+
.layer_fn(move |channel| GrpcMetricSvc {
|
|
176
|
+
inner: ChannelOrGrpcOverride::Channel(channel),
|
|
177
|
+
metrics: options.metrics_meter.clone().map(MetricsContext::new),
|
|
178
|
+
disable_errcode_label: options.disable_error_code_metric_tags,
|
|
179
|
+
})
|
|
180
|
+
.service(channel)
|
|
181
|
+
};
|
|
288
182
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
183
|
+
let headers = Arc::new(RwLock::new(ClientHeaders {
|
|
184
|
+
user_headers: parse_ascii_headers(options.headers.clone().unwrap_or_default())?,
|
|
185
|
+
user_binary_headers: parse_binary_headers(
|
|
186
|
+
options.binary_headers.clone().unwrap_or_default(),
|
|
187
|
+
)?,
|
|
188
|
+
api_key: options.api_key.clone(),
|
|
189
|
+
}));
|
|
190
|
+
let interceptor = ServiceCallInterceptor {
|
|
191
|
+
client_name: options.client_name.clone(),
|
|
192
|
+
client_version: options.client_version.clone(),
|
|
193
|
+
headers: headers.clone(),
|
|
194
|
+
};
|
|
195
|
+
let svc = InterceptedService::new(service, interceptor);
|
|
196
|
+
let mut svc_client = TemporalServiceClient::new(svc);
|
|
302
197
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
198
|
+
let capabilities = if !options.skip_get_system_info {
|
|
199
|
+
match svc_client
|
|
200
|
+
.get_system_info(GetSystemInfoRequest::default().into_request())
|
|
201
|
+
.await
|
|
202
|
+
{
|
|
203
|
+
Ok(sysinfo) => sysinfo.into_inner().capabilities,
|
|
204
|
+
Err(status) => match status.code() {
|
|
205
|
+
Code::Unimplemented => None,
|
|
206
|
+
_ => return Err(ClientConnectError::SystemInfoCallError(status)),
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
None
|
|
211
|
+
};
|
|
212
|
+
Ok(Self {
|
|
213
|
+
inner: Arc::new(ConnectionInner {
|
|
214
|
+
service: svc_client,
|
|
215
|
+
retry_options: options.retry_options,
|
|
216
|
+
identity: options.identity,
|
|
217
|
+
headers,
|
|
218
|
+
client_name: options.client_name,
|
|
219
|
+
client_version: options.client_version,
|
|
220
|
+
capabilities,
|
|
221
|
+
workers: Arc::new(ClientWorkerSet::new()),
|
|
222
|
+
}),
|
|
223
|
+
})
|
|
306
224
|
}
|
|
307
|
-
}
|
|
308
225
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
write!(f, "ClientTlsOptions(..)")
|
|
226
|
+
/// Set API key, overwriting any previous one.
|
|
227
|
+
pub fn set_api_key(&self, api_key: Option<String>) {
|
|
228
|
+
self.inner.headers.write().api_key = api_key;
|
|
313
229
|
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/// Errors thrown while attempting to establish a connection to the server
|
|
317
|
-
#[derive(thiserror::Error, Debug)]
|
|
318
|
-
pub enum ClientInitError {
|
|
319
|
-
/// Invalid URI. Configuration error, fatal.
|
|
320
|
-
#[error("Invalid URI: {0:?}")]
|
|
321
|
-
InvalidUri(#[from] InvalidUri),
|
|
322
|
-
/// Invalid gRPC metadata headers. Configuration error.
|
|
323
|
-
#[error("Invalid headers: {0}")]
|
|
324
|
-
InvalidHeaders(#[from] InvalidHeaderError),
|
|
325
|
-
/// Server connection error. Crashing and restarting the worker is likely best.
|
|
326
|
-
#[error("Server connection error: {0:?}")]
|
|
327
|
-
TonicTransportError(#[from] tonic::transport::Error),
|
|
328
|
-
/// We couldn't successfully make the `get_system_info` call at connection time to establish
|
|
329
|
-
/// server capabilities / verify server is responding.
|
|
330
|
-
#[error("`get_system_info` call error after connection: {0:?}")]
|
|
331
|
-
SystemInfoCallError(tonic::Status),
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/// Errors thrown when a gRPC metadata header is invalid.
|
|
335
|
-
#[derive(thiserror::Error, Debug)]
|
|
336
|
-
pub enum InvalidHeaderError {
|
|
337
|
-
/// A binary header key was invalid
|
|
338
|
-
#[error("Invalid binary header key '{key}': {source}")]
|
|
339
|
-
InvalidBinaryHeaderKey {
|
|
340
|
-
/// The invalid key
|
|
341
|
-
key: String,
|
|
342
|
-
/// The source error from tonic
|
|
343
|
-
source: tonic::metadata::errors::InvalidMetadataKey,
|
|
344
|
-
},
|
|
345
|
-
/// An ASCII header key was invalid
|
|
346
|
-
#[error("Invalid ASCII header key '{key}': {source}")]
|
|
347
|
-
InvalidAsciiHeaderKey {
|
|
348
|
-
/// The invalid key
|
|
349
|
-
key: String,
|
|
350
|
-
/// The source error from tonic
|
|
351
|
-
source: tonic::metadata::errors::InvalidMetadataKey,
|
|
352
|
-
},
|
|
353
|
-
/// An ASCII header value was invalid
|
|
354
|
-
#[error("Invalid ASCII header value for key '{key}': {source}")]
|
|
355
|
-
InvalidAsciiHeaderValue {
|
|
356
|
-
/// The key
|
|
357
|
-
key: String,
|
|
358
|
-
/// The invalid value
|
|
359
|
-
value: String,
|
|
360
|
-
/// The source error from tonic
|
|
361
|
-
source: tonic::metadata::errors::InvalidMetadataValue,
|
|
362
|
-
},
|
|
363
|
-
}
|
|
364
230
|
|
|
365
|
-
/// A client with [ClientOptions] attached, which can be passed to initialize workers,
|
|
366
|
-
/// or can be used directly. Is cheap to clone.
|
|
367
|
-
#[derive(Clone, Debug)]
|
|
368
|
-
pub struct ConfiguredClient<C> {
|
|
369
|
-
client: C,
|
|
370
|
-
options: Arc<ClientOptions>,
|
|
371
|
-
headers: Arc<RwLock<ClientHeaders>>,
|
|
372
|
-
/// Capabilities as read from the `get_system_info` RPC call made on client connection
|
|
373
|
-
capabilities: Option<get_system_info_response::Capabilities>,
|
|
374
|
-
workers: Arc<ClientWorkerSet>,
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
impl<C> ConfiguredClient<C> {
|
|
378
231
|
/// Set HTTP request headers overwriting previous headers.
|
|
379
232
|
///
|
|
380
|
-
/// This will not affect headers set via [
|
|
233
|
+
/// This will not affect headers set via [ConnectionOptions::binary_headers].
|
|
381
234
|
///
|
|
382
235
|
/// # Errors
|
|
383
236
|
///
|
|
384
237
|
/// Will return an error if any of the provided keys or values are not valid gRPC metadata.
|
|
385
238
|
/// If an error is returned, the previous headers will remain unchanged.
|
|
386
239
|
pub fn set_headers(&self, headers: HashMap<String, String>) -> Result<(), InvalidHeaderError> {
|
|
387
|
-
self.headers.write().user_headers = parse_ascii_headers(headers)?;
|
|
240
|
+
self.inner.headers.write().user_headers = parse_ascii_headers(headers)?;
|
|
388
241
|
Ok(())
|
|
389
242
|
}
|
|
390
243
|
|
|
391
244
|
/// Set binary HTTP request headers overwriting previous headers.
|
|
392
245
|
///
|
|
393
|
-
/// This will not affect headers set via [
|
|
246
|
+
/// This will not affect headers set via [ConnectionOptions::headers].
|
|
394
247
|
///
|
|
395
248
|
/// # Errors
|
|
396
249
|
///
|
|
@@ -400,34 +253,80 @@ impl<C> ConfiguredClient<C> {
|
|
|
400
253
|
&self,
|
|
401
254
|
binary_headers: HashMap<String, Vec<u8>>,
|
|
402
255
|
) -> Result<(), InvalidHeaderError> {
|
|
403
|
-
self.headers.write().user_binary_headers = parse_binary_headers(binary_headers)?;
|
|
256
|
+
self.inner.headers.write().user_binary_headers = parse_binary_headers(binary_headers)?;
|
|
404
257
|
Ok(())
|
|
405
258
|
}
|
|
406
259
|
|
|
407
|
-
///
|
|
408
|
-
pub fn
|
|
409
|
-
self.
|
|
260
|
+
/// Returns the value used for the `client-name` header by this connection.
|
|
261
|
+
pub fn client_name(&self) -> &str {
|
|
262
|
+
&self.inner.client_name
|
|
410
263
|
}
|
|
411
264
|
|
|
412
|
-
/// Returns the
|
|
413
|
-
pub fn
|
|
414
|
-
&self.
|
|
265
|
+
/// Returns the value used for the `client-version` header by this connection.
|
|
266
|
+
pub fn client_version(&self) -> &str {
|
|
267
|
+
&self.inner.client_version
|
|
415
268
|
}
|
|
416
269
|
|
|
417
270
|
/// Returns the server capabilities we (may have) learned about when establishing an initial
|
|
418
271
|
/// connection
|
|
419
272
|
pub fn capabilities(&self) -> Option<&get_system_info_response::Capabilities> {
|
|
420
|
-
self.capabilities.as_ref()
|
|
273
|
+
self.inner.capabilities.as_ref()
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/// Get a mutable reference to the retry options.
|
|
277
|
+
///
|
|
278
|
+
/// Note: If this connection has been cloned, this will copy-on-write to avoid
|
|
279
|
+
/// affecting other clones.
|
|
280
|
+
pub fn retry_options_mut(&mut self) -> &mut RetryOptions {
|
|
281
|
+
&mut Arc::make_mut(&mut self.inner).retry_options
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/// Get a reference to the connection identity.
|
|
285
|
+
pub fn identity(&self) -> &str {
|
|
286
|
+
&self.inner.identity
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/// Get a mutable reference to the connection identity.
|
|
290
|
+
///
|
|
291
|
+
/// Note: If this connection has been cloned, this will copy-on-write to avoid
|
|
292
|
+
/// affecting other clones.
|
|
293
|
+
pub fn identity_mut(&mut self) -> &mut String {
|
|
294
|
+
&mut Arc::make_mut(&mut self.inner).identity
|
|
421
295
|
}
|
|
422
296
|
|
|
423
|
-
/// Returns a
|
|
297
|
+
/// Returns a reference to a registry with workers using this client instance.
|
|
424
298
|
pub fn workers(&self) -> Arc<ClientWorkerSet> {
|
|
425
|
-
self.workers.clone()
|
|
299
|
+
self.inner.workers.clone()
|
|
426
300
|
}
|
|
427
301
|
|
|
428
|
-
/// Returns the
|
|
302
|
+
/// Returns the client-wide key.
|
|
429
303
|
pub fn worker_grouping_key(&self) -> Uuid {
|
|
430
|
-
self.workers.worker_grouping_key()
|
|
304
|
+
self.inner.workers.worker_grouping_key()
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/// Get the underlying workflow service client for making raw gRPC calls.
|
|
308
|
+
pub fn workflow_service(&self) -> Box<dyn WorkflowService> {
|
|
309
|
+
self.inner.service.workflow_service()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/// Get the underlying operator service client for making raw gRPC calls.
|
|
313
|
+
pub fn operator_service(&self) -> Box<dyn OperatorService> {
|
|
314
|
+
self.inner.service.operator_service()
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/// Get the underlying cloud service client for making raw gRPC calls.
|
|
318
|
+
pub fn cloud_service(&self) -> Box<dyn CloudService> {
|
|
319
|
+
self.inner.service.cloud_service()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/// Get the underlying test service client for making raw gRPC calls.
|
|
323
|
+
pub fn test_service(&self) -> Box<dyn TestService> {
|
|
324
|
+
self.inner.service.test_service()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/// Get the underlying health service client for making raw gRPC calls.
|
|
328
|
+
pub fn health_service(&self) -> Box<dyn HealthService> {
|
|
329
|
+
self.inner.service.health_service()
|
|
431
330
|
}
|
|
432
331
|
}
|
|
433
332
|
|
|
@@ -463,166 +362,42 @@ impl ClientHeaders {
|
|
|
463
362
|
}
|
|
464
363
|
}
|
|
465
364
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
impl ClientOptions {
|
|
482
|
-
/// Attempt to establish a connection to the Temporal server in a specific namespace. The
|
|
483
|
-
/// returned client is bound to that namespace.
|
|
484
|
-
pub async fn connect(
|
|
485
|
-
&self,
|
|
486
|
-
namespace: impl Into<String>,
|
|
487
|
-
metrics_meter: Option<TemporalMeter>,
|
|
488
|
-
) -> Result<RetryClient<Client>, ClientInitError> {
|
|
489
|
-
let client = self.connect_no_namespace(metrics_meter).await?.into_inner();
|
|
490
|
-
let client = Client::new(client, namespace.into());
|
|
491
|
-
let retry_client = RetryClient::new(client, self.retry_options.clone());
|
|
492
|
-
Ok(retry_client)
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/// Attempt to establish a connection to the Temporal server and return a gRPC client which is
|
|
496
|
-
/// intercepted with retry, default headers functionality, and metrics if provided.
|
|
497
|
-
///
|
|
498
|
-
/// See [RetryClient] for more
|
|
499
|
-
pub async fn connect_no_namespace(
|
|
500
|
-
&self,
|
|
501
|
-
metrics_meter: Option<TemporalMeter>,
|
|
502
|
-
) -> Result<RetryClient<ConfiguredClient<TemporalServiceClient>>, ClientInitError> {
|
|
503
|
-
self.connect_no_namespace_with_service_override(metrics_meter, None)
|
|
504
|
-
.await
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/// Attempt to establish a connection to the Temporal server and return a gRPC client which is
|
|
508
|
-
/// intercepted with retry, default headers functionality, and metrics if provided. If a
|
|
509
|
-
/// service_override is present, network-specific options are ignored and the callback is
|
|
510
|
-
/// invoked for each gRPC call.
|
|
511
|
-
///
|
|
512
|
-
/// See [RetryClient] for more
|
|
513
|
-
pub async fn connect_no_namespace_with_service_override(
|
|
514
|
-
&self,
|
|
515
|
-
metrics_meter: Option<TemporalMeter>,
|
|
516
|
-
service_override: Option<callback_based::CallbackBasedGrpcService>,
|
|
517
|
-
) -> Result<RetryClient<ConfiguredClient<TemporalServiceClient>>, ClientInitError> {
|
|
518
|
-
let service = if let Some(service_override) = service_override {
|
|
519
|
-
GrpcMetricSvc {
|
|
520
|
-
inner: ChannelOrGrpcOverride::GrpcOverride(service_override),
|
|
521
|
-
metrics: metrics_meter.clone().map(MetricsContext::new),
|
|
522
|
-
disable_errcode_label: self.disable_error_code_metric_tags,
|
|
523
|
-
}
|
|
365
|
+
/// If TLS is configured, set the appropriate options on the provided channel and return it.
|
|
366
|
+
/// Passes it through if TLS options not set.
|
|
367
|
+
async fn add_tls_to_channel(
|
|
368
|
+
tls_options: Option<&TlsOptions>,
|
|
369
|
+
mut channel: Endpoint,
|
|
370
|
+
) -> Result<Endpoint, ClientConnectError> {
|
|
371
|
+
if let Some(tls_cfg) = tls_options {
|
|
372
|
+
let mut tls = tonic::transport::ClientTlsConfig::new();
|
|
373
|
+
|
|
374
|
+
if let Some(root_cert) = &tls_cfg.server_root_ca_cert {
|
|
375
|
+
let server_root_ca_cert = Certificate::from_pem(root_cert);
|
|
376
|
+
tls = tls.ca_certificate(server_root_ca_cert);
|
|
524
377
|
} else {
|
|
525
|
-
|
|
526
|
-
let channel = self.add_tls_to_channel(channel).await?;
|
|
527
|
-
let channel = if let Some(keep_alive) = self.keep_alive.as_ref() {
|
|
528
|
-
channel
|
|
529
|
-
.keep_alive_while_idle(true)
|
|
530
|
-
.http2_keep_alive_interval(keep_alive.interval)
|
|
531
|
-
.keep_alive_timeout(keep_alive.timeout)
|
|
532
|
-
} else {
|
|
533
|
-
channel
|
|
534
|
-
};
|
|
535
|
-
let channel = if let Some(origin) = self.override_origin.clone() {
|
|
536
|
-
channel.origin(origin)
|
|
537
|
-
} else {
|
|
538
|
-
channel
|
|
539
|
-
};
|
|
540
|
-
// If there is a proxy, we have to connect that way
|
|
541
|
-
let channel = if let Some(proxy) = self.http_connect_proxy.as_ref() {
|
|
542
|
-
proxy.connect_endpoint(&channel).await?
|
|
543
|
-
} else {
|
|
544
|
-
channel.connect().await?
|
|
545
|
-
};
|
|
546
|
-
ServiceBuilder::new()
|
|
547
|
-
.layer_fn(move |channel| GrpcMetricSvc {
|
|
548
|
-
inner: ChannelOrGrpcOverride::Channel(channel),
|
|
549
|
-
metrics: metrics_meter.clone().map(MetricsContext::new),
|
|
550
|
-
disable_errcode_label: self.disable_error_code_metric_tags,
|
|
551
|
-
})
|
|
552
|
-
.service(channel)
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
let headers = Arc::new(RwLock::new(ClientHeaders {
|
|
556
|
-
user_headers: parse_ascii_headers(self.headers.clone().unwrap_or_default())?,
|
|
557
|
-
user_binary_headers: parse_binary_headers(
|
|
558
|
-
self.binary_headers.clone().unwrap_or_default(),
|
|
559
|
-
)?,
|
|
560
|
-
api_key: self.api_key.clone(),
|
|
561
|
-
}));
|
|
562
|
-
let interceptor = ServiceCallInterceptor {
|
|
563
|
-
opts: self.clone(),
|
|
564
|
-
headers: headers.clone(),
|
|
565
|
-
};
|
|
566
|
-
let svc = InterceptedService::new(service, interceptor);
|
|
567
|
-
|
|
568
|
-
let mut client = ConfiguredClient {
|
|
569
|
-
headers,
|
|
570
|
-
client: TemporalServiceClient::new(svc),
|
|
571
|
-
options: Arc::new(self.clone()),
|
|
572
|
-
capabilities: None,
|
|
573
|
-
workers: Arc::new(ClientWorkerSet::new()),
|
|
574
|
-
};
|
|
575
|
-
if !self.skip_get_system_info {
|
|
576
|
-
match client
|
|
577
|
-
.get_system_info(GetSystemInfoRequest::default().into_request())
|
|
578
|
-
.await
|
|
579
|
-
{
|
|
580
|
-
Ok(sysinfo) => {
|
|
581
|
-
client.capabilities = sysinfo.into_inner().capabilities;
|
|
582
|
-
}
|
|
583
|
-
Err(status) => match status.code() {
|
|
584
|
-
Code::Unimplemented => {}
|
|
585
|
-
_ => return Err(ClientInitError::SystemInfoCallError(status)),
|
|
586
|
-
},
|
|
587
|
-
};
|
|
378
|
+
tls = tls.with_native_roots();
|
|
588
379
|
}
|
|
589
|
-
Ok(RetryClient::new(client, self.retry_options.clone()))
|
|
590
|
-
}
|
|
591
380
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
async fn add_tls_to_channel(&self, mut channel: Endpoint) -> Result<Endpoint, ClientInitError> {
|
|
595
|
-
if let Some(tls_cfg) = &self.tls_options {
|
|
596
|
-
let mut tls = tonic::transport::ClientTlsConfig::new();
|
|
381
|
+
if let Some(domain) = &tls_cfg.domain {
|
|
382
|
+
tls = tls.domain_name(domain);
|
|
597
383
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if let Some(domain) = &tls_cfg.domain {
|
|
606
|
-
tls = tls.domain_name(domain);
|
|
607
|
-
|
|
608
|
-
// This song and dance ultimately is just to make sure the `:authority` header ends
|
|
609
|
-
// up correct on requests while we use TLS. Setting the header directly in our
|
|
610
|
-
// interceptor doesn't work since seemingly it is overridden at some point by
|
|
611
|
-
// something lower level.
|
|
612
|
-
let uri: Uri = format!("https://{domain}").parse()?;
|
|
613
|
-
channel = channel.origin(uri);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if let Some(client_opts) = &tls_cfg.client_tls_options {
|
|
617
|
-
let client_identity =
|
|
618
|
-
Identity::from_pem(&client_opts.client_cert, &client_opts.client_private_key);
|
|
619
|
-
tls = tls.identity(client_identity);
|
|
620
|
-
}
|
|
384
|
+
// This song and dance ultimately is just to make sure the `:authority` header ends
|
|
385
|
+
// up correct on requests while we use TLS. Setting the header directly in our
|
|
386
|
+
// interceptor doesn't work since seemingly it is overridden at some point by
|
|
387
|
+
// something lower level.
|
|
388
|
+
let uri: Uri = format!("https://{domain}").parse()?;
|
|
389
|
+
channel = channel.origin(uri);
|
|
390
|
+
}
|
|
621
391
|
|
|
622
|
-
|
|
392
|
+
if let Some(client_opts) = &tls_cfg.client_tls_options {
|
|
393
|
+
let client_identity =
|
|
394
|
+
Identity::from_pem(&client_opts.client_cert, &client_opts.client_private_key);
|
|
395
|
+
tls = tls.identity(client_identity);
|
|
623
396
|
}
|
|
624
|
-
|
|
397
|
+
|
|
398
|
+
return channel.tls_config(tls).map_err(Into::into);
|
|
625
399
|
}
|
|
400
|
+
Ok(channel)
|
|
626
401
|
}
|
|
627
402
|
|
|
628
403
|
fn parse_ascii_headers(
|
|
@@ -679,7 +454,8 @@ fn parse_binary_headers(
|
|
|
679
454
|
/// Interceptor which attaches common metadata (like "client-name") to every outgoing call
|
|
680
455
|
#[derive(Clone)]
|
|
681
456
|
pub struct ServiceCallInterceptor {
|
|
682
|
-
|
|
457
|
+
client_name: String,
|
|
458
|
+
client_version: String,
|
|
683
459
|
/// Only accessed as a reader
|
|
684
460
|
headers: Arc<RwLock<ClientHeaders>>,
|
|
685
461
|
}
|
|
@@ -695,8 +471,7 @@ impl Interceptor for ServiceCallInterceptor {
|
|
|
695
471
|
if !metadata.contains_key(CLIENT_NAME_HEADER_KEY) {
|
|
696
472
|
metadata.insert(
|
|
697
473
|
CLIENT_NAME_HEADER_KEY,
|
|
698
|
-
self.
|
|
699
|
-
.client_name
|
|
474
|
+
self.client_name
|
|
700
475
|
.parse()
|
|
701
476
|
.unwrap_or_else(|_| MetadataValue::from_static("")),
|
|
702
477
|
);
|
|
@@ -704,8 +479,7 @@ impl Interceptor for ServiceCallInterceptor {
|
|
|
704
479
|
if !metadata.contains_key(CLIENT_VERSION_HEADER_KEY) {
|
|
705
480
|
metadata.insert(
|
|
706
481
|
CLIENT_VERSION_HEADER_KEY,
|
|
707
|
-
self.
|
|
708
|
-
.client_version
|
|
482
|
+
self.client_version
|
|
709
483
|
.parse()
|
|
710
484
|
.unwrap_or_else(|_| MetadataValue::from_static("")),
|
|
711
485
|
);
|
|
@@ -794,78 +568,145 @@ impl TemporalServiceClient {
|
|
|
794
568
|
}
|
|
795
569
|
|
|
796
570
|
/// Get the underlying workflow service client
|
|
797
|
-
pub fn
|
|
571
|
+
pub fn workflow_service(&self) -> Box<dyn WorkflowService> {
|
|
798
572
|
self.workflow_svc_client.clone()
|
|
799
573
|
}
|
|
800
574
|
/// Get the underlying operator service client
|
|
801
|
-
pub fn
|
|
575
|
+
pub fn operator_service(&self) -> Box<dyn OperatorService> {
|
|
802
576
|
self.operator_svc_client.clone()
|
|
803
577
|
}
|
|
804
578
|
/// Get the underlying cloud service client
|
|
805
|
-
pub fn
|
|
579
|
+
pub fn cloud_service(&self) -> Box<dyn CloudService> {
|
|
806
580
|
self.cloud_svc_client.clone()
|
|
807
581
|
}
|
|
808
582
|
/// Get the underlying test service client
|
|
809
|
-
pub fn
|
|
583
|
+
pub fn test_service(&self) -> Box<dyn TestService> {
|
|
810
584
|
self.test_svc_client.clone()
|
|
811
585
|
}
|
|
812
586
|
/// Get the underlying health service client
|
|
813
|
-
pub fn
|
|
587
|
+
pub fn health_service(&self) -> Box<dyn HealthService> {
|
|
814
588
|
self.health_svc_client.clone()
|
|
815
589
|
}
|
|
816
590
|
}
|
|
817
591
|
|
|
818
|
-
/// Contains an instance of a namespace-bound client for interacting with the Temporal server
|
|
592
|
+
/// Contains an instance of a namespace-bound client for interacting with the Temporal server.
|
|
593
|
+
/// Cheap to clone.
|
|
819
594
|
#[derive(Clone)]
|
|
820
595
|
pub struct Client {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
/// The namespace this client interacts with
|
|
824
|
-
namespace: String,
|
|
596
|
+
connection: Connection,
|
|
597
|
+
options: Arc<ClientOptions>,
|
|
825
598
|
}
|
|
826
599
|
|
|
827
600
|
impl Client {
|
|
828
|
-
/// Create a new client from
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
601
|
+
/// Create a new client from a connection and options.
|
|
602
|
+
///
|
|
603
|
+
/// Currently infallible, but returns a `Result` for future extensibility
|
|
604
|
+
/// (e.g., interceptor or plugin validation).
|
|
605
|
+
pub fn new(connection: Connection, options: ClientOptions) -> Result<Self, ClientNewError> {
|
|
606
|
+
Ok(Client {
|
|
607
|
+
connection,
|
|
608
|
+
options: Arc::new(options),
|
|
609
|
+
})
|
|
834
610
|
}
|
|
835
611
|
|
|
836
612
|
/// Return the options this client was initialized with
|
|
837
613
|
pub fn options(&self) -> &ClientOptions {
|
|
838
|
-
&self.
|
|
614
|
+
&self.options
|
|
839
615
|
}
|
|
840
616
|
|
|
841
|
-
/// Return
|
|
617
|
+
/// Return this client's options mutably.
|
|
618
|
+
///
|
|
619
|
+
/// Note: If this client has been cloned, this will copy-on-write to avoid affecting other
|
|
620
|
+
/// clones.
|
|
842
621
|
pub fn options_mut(&mut self) -> &mut ClientOptions {
|
|
843
|
-
Arc::make_mut(&mut self.
|
|
622
|
+
Arc::make_mut(&mut self.options)
|
|
844
623
|
}
|
|
845
624
|
|
|
846
|
-
/// Returns a reference to the underlying
|
|
847
|
-
pub fn
|
|
848
|
-
&self.
|
|
625
|
+
/// Returns a reference to the underlying connection
|
|
626
|
+
pub fn connection(&self) -> &Connection {
|
|
627
|
+
&self.connection
|
|
849
628
|
}
|
|
850
629
|
|
|
851
|
-
///
|
|
852
|
-
pub fn
|
|
853
|
-
self.
|
|
630
|
+
/// Returns a mutable reference to the underlying connection
|
|
631
|
+
pub fn connection_mut(&mut self) -> &mut Connection {
|
|
632
|
+
&mut self.connection
|
|
854
633
|
}
|
|
634
|
+
}
|
|
855
635
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
636
|
+
// High-level workflow operations on Client.
|
|
637
|
+
// These forward to the internal WorkflowClientTrait blanket impl which is
|
|
638
|
+
// available because Client implements WorkflowService + NamespacedClient + Clone.
|
|
639
|
+
impl Client {
|
|
640
|
+
/// Start a workflow execution.
|
|
641
|
+
///
|
|
642
|
+
/// Returns a [`WorkflowHandle`] that can be used to interact with the workflow
|
|
643
|
+
/// (e.g., get its result, send signals, query, etc.).
|
|
644
|
+
pub async fn start_workflow<W>(
|
|
645
|
+
&self,
|
|
646
|
+
workflow: W,
|
|
647
|
+
input: W::Input,
|
|
648
|
+
options: WorkflowStartOptions,
|
|
649
|
+
) -> Result<WorkflowHandle<Self, W>, WorkflowStartError>
|
|
650
|
+
where
|
|
651
|
+
W: WorkflowDefinition,
|
|
652
|
+
W::Input: Send,
|
|
653
|
+
{
|
|
654
|
+
WorkflowClientTrait::start_workflow(self, workflow, input, options).await
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/// Get a handle to an existing workflow.
|
|
658
|
+
///
|
|
659
|
+
/// For untyped access, use `get_workflow_handle::<UntypedWorkflow>(...)`.
|
|
660
|
+
pub fn get_workflow_handle<W: WorkflowDefinition>(
|
|
661
|
+
&self,
|
|
662
|
+
workflow_id: impl Into<String>,
|
|
663
|
+
) -> WorkflowHandle<Self, W> {
|
|
664
|
+
WorkflowClientTrait::get_workflow_handle(self, workflow_id)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/// List workflows matching a query.
|
|
668
|
+
///
|
|
669
|
+
/// Returns a stream that lazily paginates through results.
|
|
670
|
+
/// Use `limit` in options to cap the number of results returned.
|
|
671
|
+
pub fn list_workflows(
|
|
672
|
+
&self,
|
|
673
|
+
query: impl Into<String>,
|
|
674
|
+
opts: WorkflowListOptions,
|
|
675
|
+
) -> ListWorkflowsStream {
|
|
676
|
+
WorkflowClientTrait::list_workflows(self, query, opts)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/// Count workflows matching a query.
|
|
680
|
+
pub async fn count_workflows(
|
|
681
|
+
&self,
|
|
682
|
+
query: impl Into<String>,
|
|
683
|
+
opts: WorkflowCountOptions,
|
|
684
|
+
) -> Result<WorkflowExecutionCount, ClientError> {
|
|
685
|
+
WorkflowClientTrait::count_workflows(self, query, opts).await
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/// Get a handle to complete an activity asynchronously.
|
|
689
|
+
///
|
|
690
|
+
/// An activity returning `ActivityError::WillCompleteAsync` can be completed with this handle.
|
|
691
|
+
pub fn get_async_activity_handle(
|
|
692
|
+
&self,
|
|
693
|
+
identifier: ActivityIdentifier,
|
|
694
|
+
) -> AsyncActivityHandle<Self> {
|
|
695
|
+
WorkflowClientTrait::get_async_activity_handle(self, identifier)
|
|
859
696
|
}
|
|
860
697
|
}
|
|
861
698
|
|
|
862
699
|
impl NamespacedClient for Client {
|
|
863
700
|
fn namespace(&self) -> String {
|
|
864
|
-
self.namespace.clone()
|
|
701
|
+
self.options.namespace.clone()
|
|
865
702
|
}
|
|
866
703
|
|
|
867
704
|
fn identity(&self) -> String {
|
|
868
|
-
self.
|
|
705
|
+
self.connection.identity().to_owned()
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
fn data_converter(&self) -> &DataConverter {
|
|
709
|
+
&self.options.data_converter
|
|
869
710
|
}
|
|
870
711
|
}
|
|
871
712
|
|
|
@@ -889,268 +730,60 @@ impl Namespace {
|
|
|
889
730
|
}
|
|
890
731
|
}
|
|
891
732
|
|
|
892
|
-
/// Default workflow execution retention for a Namespace is 3 days
|
|
893
|
-
const DEFAULT_WORKFLOW_EXECUTION_RETENTION_PERIOD: Duration = Duration::from_secs(60 * 60 * 24 * 3);
|
|
894
|
-
|
|
895
|
-
/// Helper struct for `register_namespace`.
|
|
896
|
-
#[derive(Clone, bon::Builder)]
|
|
897
|
-
#[builder(on(String, into))]
|
|
898
|
-
pub struct RegisterNamespaceOptions {
|
|
899
|
-
/// Name (required)
|
|
900
|
-
pub namespace: String,
|
|
901
|
-
/// Description (required)
|
|
902
|
-
pub description: String,
|
|
903
|
-
/// Owner's email
|
|
904
|
-
#[builder(default)]
|
|
905
|
-
pub owner_email: String,
|
|
906
|
-
/// Workflow execution retention period
|
|
907
|
-
#[builder(default = DEFAULT_WORKFLOW_EXECUTION_RETENTION_PERIOD)]
|
|
908
|
-
pub workflow_execution_retention_period: Duration,
|
|
909
|
-
/// Cluster settings
|
|
910
|
-
#[builder(default)]
|
|
911
|
-
pub clusters: Vec<ClusterReplicationConfig>,
|
|
912
|
-
/// Active cluster name
|
|
913
|
-
#[builder(default)]
|
|
914
|
-
pub active_cluster_name: String,
|
|
915
|
-
/// Custom Data
|
|
916
|
-
#[builder(default)]
|
|
917
|
-
pub data: HashMap<String, String>,
|
|
918
|
-
/// Security Token
|
|
919
|
-
#[builder(default)]
|
|
920
|
-
pub security_token: String,
|
|
921
|
-
/// Global namespace
|
|
922
|
-
#[builder(default)]
|
|
923
|
-
pub is_global_namespace: bool,
|
|
924
|
-
/// History Archival setting
|
|
925
|
-
#[builder(default = ArchivalState::Unspecified)]
|
|
926
|
-
pub history_archival_state: ArchivalState,
|
|
927
|
-
/// History Archival uri
|
|
928
|
-
#[builder(default)]
|
|
929
|
-
pub history_archival_uri: String,
|
|
930
|
-
/// Visibility Archival setting
|
|
931
|
-
#[builder(default = ArchivalState::Unspecified)]
|
|
932
|
-
pub visibility_archival_state: ArchivalState,
|
|
933
|
-
/// Visibility Archival uri
|
|
934
|
-
#[builder(default)]
|
|
935
|
-
pub visibility_archival_uri: String,
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
impl From<RegisterNamespaceOptions> for RegisterNamespaceRequest {
|
|
939
|
-
fn from(val: RegisterNamespaceOptions) -> Self {
|
|
940
|
-
RegisterNamespaceRequest {
|
|
941
|
-
namespace: val.namespace,
|
|
942
|
-
description: val.description,
|
|
943
|
-
owner_email: val.owner_email,
|
|
944
|
-
workflow_execution_retention_period: val
|
|
945
|
-
.workflow_execution_retention_period
|
|
946
|
-
.try_into()
|
|
947
|
-
.ok(),
|
|
948
|
-
clusters: val.clusters,
|
|
949
|
-
active_cluster_name: val.active_cluster_name,
|
|
950
|
-
data: val.data,
|
|
951
|
-
security_token: val.security_token,
|
|
952
|
-
is_global_namespace: val.is_global_namespace,
|
|
953
|
-
history_archival_state: val.history_archival_state as i32,
|
|
954
|
-
history_archival_uri: val.history_archival_uri,
|
|
955
|
-
visibility_archival_state: val.visibility_archival_state as i32,
|
|
956
|
-
visibility_archival_uri: val.visibility_archival_uri,
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
// Note: The cluster_names custom setter from derive_builder is not supported in bon.
|
|
962
|
-
// Users should manually construct the clusters vector if needed.
|
|
963
|
-
|
|
964
|
-
/// Helper struct for `signal_with_start_workflow_execution`.
|
|
965
|
-
#[derive(Clone, bon::Builder)]
|
|
966
|
-
#[builder(on(String, into))]
|
|
967
|
-
pub struct SignalWithStartOptions {
|
|
968
|
-
/// Input payload for the workflow run
|
|
969
|
-
pub input: Option<Payloads>,
|
|
970
|
-
/// Task Queue to target (required)
|
|
971
|
-
pub task_queue: String,
|
|
972
|
-
/// Workflow id for the workflow run
|
|
973
|
-
pub workflow_id: String,
|
|
974
|
-
/// Workflow type for the workflow run
|
|
975
|
-
pub workflow_type: String,
|
|
976
|
-
/// Request id for idempotency/deduplication
|
|
977
|
-
pub request_id: Option<String>,
|
|
978
|
-
/// The signal name to send (required)
|
|
979
|
-
pub signal_name: String,
|
|
980
|
-
/// Payloads for the signal
|
|
981
|
-
pub signal_input: Option<Payloads>,
|
|
982
|
-
/// Headers for the signal
|
|
983
|
-
pub signal_header: Option<Header>,
|
|
984
|
-
}
|
|
985
|
-
|
|
986
733
|
/// This trait provides higher-level friendlier interaction with the server.
|
|
987
734
|
/// See the [WorkflowService] trait for a lower-level client.
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
async fn start_workflow(
|
|
992
|
-
&self,
|
|
993
|
-
input: Vec<Payload>,
|
|
994
|
-
task_queue: String,
|
|
995
|
-
workflow_id: String,
|
|
996
|
-
workflow_type: String,
|
|
997
|
-
request_id: Option<String>,
|
|
998
|
-
options: WorkflowOptions,
|
|
999
|
-
) -> Result<StartWorkflowExecutionResponse>;
|
|
1000
|
-
|
|
1001
|
-
/// Notifies the server that workflow tasks for a given workflow should be sent to the normal
|
|
1002
|
-
/// non-sticky task queue. This normally happens when workflow has been evicted from the cache.
|
|
1003
|
-
async fn reset_sticky_task_queue(
|
|
735
|
+
pub(crate) trait WorkflowClientTrait: NamespacedClient {
|
|
736
|
+
/// Start a workflow execution.
|
|
737
|
+
fn start_workflow<W>(
|
|
1004
738
|
&self,
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
&self,
|
|
1014
|
-
task_token: TaskToken,
|
|
1015
|
-
result: Option<Payloads>,
|
|
1016
|
-
) -> Result<RespondActivityTaskCompletedResponse>;
|
|
1017
|
-
|
|
1018
|
-
/// Report activity task heartbeat by sending details to the server. `task_token` contains
|
|
1019
|
-
/// activity identifier that would've been received from polling for an activity task. `result`
|
|
1020
|
-
/// contains `cancel_requested` flag, which if set to true indicates that activity has been
|
|
1021
|
-
/// cancelled.
|
|
1022
|
-
async fn record_activity_heartbeat(
|
|
1023
|
-
&self,
|
|
1024
|
-
task_token: TaskToken,
|
|
1025
|
-
details: Option<Payloads>,
|
|
1026
|
-
) -> Result<RecordActivityTaskHeartbeatResponse>;
|
|
1027
|
-
|
|
1028
|
-
/// Cancel activity task by sending response to the server. `task_token` contains activity
|
|
1029
|
-
/// identifier that would've been received from polling for an activity task. `details` is a
|
|
1030
|
-
/// blob that provides arbitrary user defined cancellation info.
|
|
1031
|
-
async fn cancel_activity_task(
|
|
1032
|
-
&self,
|
|
1033
|
-
task_token: TaskToken,
|
|
1034
|
-
details: Option<Payloads>,
|
|
1035
|
-
) -> Result<RespondActivityTaskCanceledResponse>;
|
|
1036
|
-
|
|
1037
|
-
/// Send a signal to a certain workflow instance
|
|
1038
|
-
async fn signal_workflow_execution(
|
|
1039
|
-
&self,
|
|
1040
|
-
workflow_id: String,
|
|
1041
|
-
run_id: String,
|
|
1042
|
-
signal_name: String,
|
|
1043
|
-
payloads: Option<Payloads>,
|
|
1044
|
-
request_id: Option<String>,
|
|
1045
|
-
) -> Result<SignalWorkflowExecutionResponse>;
|
|
1046
|
-
|
|
1047
|
-
/// Send signal and start workflow transcationally
|
|
1048
|
-
//#TODO maybe lift the Signal type from sdk::workflow_context::options
|
|
1049
|
-
#[allow(clippy::too_many_arguments)]
|
|
1050
|
-
async fn signal_with_start_workflow_execution(
|
|
1051
|
-
&self,
|
|
1052
|
-
options: SignalWithStartOptions,
|
|
1053
|
-
workflow_options: WorkflowOptions,
|
|
1054
|
-
) -> Result<SignalWithStartWorkflowExecutionResponse>;
|
|
739
|
+
workflow: W,
|
|
740
|
+
input: W::Input,
|
|
741
|
+
options: WorkflowStartOptions,
|
|
742
|
+
) -> impl Future<Output = Result<WorkflowHandle<Self, W>, WorkflowStartError>>
|
|
743
|
+
where
|
|
744
|
+
Self: Sized,
|
|
745
|
+
W: WorkflowDefinition,
|
|
746
|
+
W::Input: Send;
|
|
1055
747
|
|
|
1056
|
-
///
|
|
1057
|
-
|
|
748
|
+
/// Get a handle to an existing workflow. `run_id` may be left blank to specify the most recent
|
|
749
|
+
/// execution having the provided `workflow_id`.
|
|
750
|
+
///
|
|
751
|
+
/// For untyped access, use `get_workflow_handle::<UntypedWorkflow>(...)`.
|
|
752
|
+
///
|
|
753
|
+
/// See also [WorkflowHandle::new], for specifying namespace or first_execution_run_id.
|
|
754
|
+
fn get_workflow_handle<W: WorkflowDefinition>(
|
|
1058
755
|
&self,
|
|
1059
|
-
workflow_id: String
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
756
|
+
workflow_id: impl Into<String>,
|
|
757
|
+
) -> WorkflowHandle<Self, W>
|
|
758
|
+
where
|
|
759
|
+
Self: Sized;
|
|
1063
760
|
|
|
1064
|
-
///
|
|
1065
|
-
|
|
761
|
+
/// List workflows matching a query.
|
|
762
|
+
/// Returns a stream that lazily paginates through results.
|
|
763
|
+
/// Use `limit` in options to cap the number of results returned.
|
|
764
|
+
fn list_workflows(
|
|
1066
765
|
&self,
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
) ->
|
|
766
|
+
query: impl Into<String>,
|
|
767
|
+
opts: WorkflowListOptions,
|
|
768
|
+
) -> ListWorkflowsStream;
|
|
1070
769
|
|
|
1071
|
-
///
|
|
1072
|
-
|
|
770
|
+
/// Count workflows matching a query.
|
|
771
|
+
fn count_workflows(
|
|
1073
772
|
&self,
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
) -> Result<GetWorkflowExecutionHistoryResponse>;
|
|
773
|
+
query: impl Into<String>,
|
|
774
|
+
opts: WorkflowCountOptions,
|
|
775
|
+
) -> impl Future<Output = Result<WorkflowExecutionCount, ClientError>>;
|
|
1078
776
|
|
|
1079
|
-
///
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
run_id: Option<String>,
|
|
1084
|
-
reason: String,
|
|
1085
|
-
request_id: Option<String>,
|
|
1086
|
-
) -> Result<RequestCancelWorkflowExecutionResponse>;
|
|
1087
|
-
|
|
1088
|
-
/// Terminate a currently executing workflow
|
|
1089
|
-
async fn terminate_workflow_execution(
|
|
777
|
+
/// Get a handle to complete an activity asynchronously.
|
|
778
|
+
///
|
|
779
|
+
/// An activity returning `ActivityError::WillCompleteAsync` can be completed with this handle.
|
|
780
|
+
fn get_async_activity_handle(
|
|
1090
781
|
&self,
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
async fn register_namespace(
|
|
1097
|
-
&self,
|
|
1098
|
-
options: RegisterNamespaceOptions,
|
|
1099
|
-
) -> Result<RegisterNamespaceResponse>;
|
|
1100
|
-
|
|
1101
|
-
/// Lists all available namespaces
|
|
1102
|
-
async fn list_namespaces(&self) -> Result<ListNamespacesResponse>;
|
|
1103
|
-
|
|
1104
|
-
/// Query namespace details
|
|
1105
|
-
async fn describe_namespace(&self, namespace: Namespace) -> Result<DescribeNamespaceResponse>;
|
|
1106
|
-
|
|
1107
|
-
/// List open workflow executions with Standard Visibility filtering
|
|
1108
|
-
async fn list_open_workflow_executions(
|
|
1109
|
-
&self,
|
|
1110
|
-
max_page_size: i32,
|
|
1111
|
-
next_page_token: Vec<u8>,
|
|
1112
|
-
start_time_filter: Option<StartTimeFilter>,
|
|
1113
|
-
filters: Option<list_open_workflow_executions_request::Filters>,
|
|
1114
|
-
) -> Result<ListOpenWorkflowExecutionsResponse>;
|
|
1115
|
-
|
|
1116
|
-
/// List closed workflow executions Standard Visibility filtering
|
|
1117
|
-
async fn list_closed_workflow_executions(
|
|
1118
|
-
&self,
|
|
1119
|
-
max_page_size: i32,
|
|
1120
|
-
next_page_token: Vec<u8>,
|
|
1121
|
-
start_time_filter: Option<StartTimeFilter>,
|
|
1122
|
-
filters: Option<list_closed_workflow_executions_request::Filters>,
|
|
1123
|
-
) -> Result<ListClosedWorkflowExecutionsResponse>;
|
|
1124
|
-
|
|
1125
|
-
/// List workflow executions with Advanced Visibility filtering
|
|
1126
|
-
async fn list_workflow_executions(
|
|
1127
|
-
&self,
|
|
1128
|
-
page_size: i32,
|
|
1129
|
-
next_page_token: Vec<u8>,
|
|
1130
|
-
query: String,
|
|
1131
|
-
) -> Result<ListWorkflowExecutionsResponse>;
|
|
1132
|
-
|
|
1133
|
-
/// List archived workflow executions
|
|
1134
|
-
async fn list_archived_workflow_executions(
|
|
1135
|
-
&self,
|
|
1136
|
-
page_size: i32,
|
|
1137
|
-
next_page_token: Vec<u8>,
|
|
1138
|
-
query: String,
|
|
1139
|
-
) -> Result<ListArchivedWorkflowExecutionsResponse>;
|
|
1140
|
-
|
|
1141
|
-
/// Get Cluster Search Attributes
|
|
1142
|
-
async fn get_search_attributes(&self) -> Result<GetSearchAttributesResponse>;
|
|
1143
|
-
|
|
1144
|
-
/// Send an Update to a workflow execution
|
|
1145
|
-
async fn update_workflow_execution(
|
|
1146
|
-
&self,
|
|
1147
|
-
workflow_id: String,
|
|
1148
|
-
run_id: String,
|
|
1149
|
-
name: String,
|
|
1150
|
-
wait_policy: update::v1::WaitPolicy,
|
|
1151
|
-
args: Option<Payloads>,
|
|
1152
|
-
) -> Result<UpdateWorkflowExecutionResponse>;
|
|
1153
|
-
}
|
|
782
|
+
identifier: ActivityIdentifier,
|
|
783
|
+
) -> AsyncActivityHandle<Self>
|
|
784
|
+
where
|
|
785
|
+
Self: Sized;
|
|
786
|
+
}
|
|
1154
787
|
|
|
1155
788
|
/// A client that is bound to a namespace
|
|
1156
789
|
pub trait NamespacedClient {
|
|
@@ -1158,172 +791,260 @@ pub trait NamespacedClient {
|
|
|
1158
791
|
fn namespace(&self) -> String;
|
|
1159
792
|
/// Returns the client identity
|
|
1160
793
|
fn identity(&self) -> String;
|
|
794
|
+
/// Returns the data converter for serializing/deserializing payloads.
|
|
795
|
+
/// Default implementation returns a static default converter.
|
|
796
|
+
fn data_converter(&self) -> &DataConverter {
|
|
797
|
+
static DEFAULT: OnceLock<DataConverter> = OnceLock::new();
|
|
798
|
+
DEFAULT.get_or_init(DataConverter::default)
|
|
799
|
+
}
|
|
1161
800
|
}
|
|
1162
801
|
|
|
1163
|
-
///
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
802
|
+
/// A workflow execution returned from list operations.
|
|
803
|
+
/// This represents information about a workflow present in visibility.
|
|
804
|
+
#[derive(Debug, Clone)]
|
|
805
|
+
pub struct WorkflowExecution {
|
|
806
|
+
raw: workflow::WorkflowExecutionInfo,
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
impl WorkflowExecution {
|
|
810
|
+
/// Create a new WorkflowExecution from the raw proto.
|
|
811
|
+
pub fn new(raw: workflow::WorkflowExecutionInfo) -> Self {
|
|
812
|
+
Self { raw }
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/// The workflow ID.
|
|
816
|
+
pub fn id(&self) -> &str {
|
|
817
|
+
self.raw
|
|
818
|
+
.execution
|
|
819
|
+
.as_ref()
|
|
820
|
+
.map(|e| e.workflow_id.as_str())
|
|
821
|
+
.unwrap_or("")
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/// The run ID.
|
|
825
|
+
pub fn run_id(&self) -> &str {
|
|
826
|
+
self.raw
|
|
827
|
+
.execution
|
|
828
|
+
.as_ref()
|
|
829
|
+
.map(|e| e.run_id.as_str())
|
|
830
|
+
.unwrap_or("")
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/// The workflow type name.
|
|
834
|
+
pub fn workflow_type(&self) -> &str {
|
|
835
|
+
self.raw
|
|
836
|
+
.r#type
|
|
837
|
+
.as_ref()
|
|
838
|
+
.map(|t| t.name.as_str())
|
|
839
|
+
.unwrap_or("")
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/// The current status of the workflow execution.
|
|
843
|
+
pub fn status(&self) -> WorkflowExecutionStatus {
|
|
844
|
+
self.raw.status()
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/// When the workflow was created.
|
|
848
|
+
pub fn start_time(&self) -> Option<SystemTime> {
|
|
849
|
+
self.raw
|
|
850
|
+
.start_time
|
|
851
|
+
.as_ref()
|
|
852
|
+
.and_then(proto_ts_to_system_time)
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/// When the workflow run started or should start.
|
|
856
|
+
pub fn execution_time(&self) -> Option<SystemTime> {
|
|
857
|
+
self.raw
|
|
858
|
+
.execution_time
|
|
859
|
+
.as_ref()
|
|
860
|
+
.and_then(proto_ts_to_system_time)
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/// When the workflow was closed, if closed.
|
|
864
|
+
pub fn close_time(&self) -> Option<SystemTime> {
|
|
865
|
+
self.raw
|
|
866
|
+
.close_time
|
|
867
|
+
.as_ref()
|
|
868
|
+
.and_then(proto_ts_to_system_time)
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/// The task queue the workflow runs on.
|
|
872
|
+
pub fn task_queue(&self) -> &str {
|
|
873
|
+
&self.raw.task_queue
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/// Number of events in history.
|
|
877
|
+
pub fn history_length(&self) -> i64 {
|
|
878
|
+
self.raw.history_length
|
|
879
|
+
}
|
|
1168
880
|
|
|
1169
|
-
///
|
|
1170
|
-
|
|
1171
|
-
|
|
881
|
+
/// Workflow memo.
|
|
882
|
+
pub fn memo(&self) -> Option<&Memo> {
|
|
883
|
+
self.raw.memo.as_ref()
|
|
884
|
+
}
|
|
1172
885
|
|
|
1173
|
-
///
|
|
1174
|
-
|
|
1175
|
-
|
|
886
|
+
/// Parent workflow ID, if this is a child workflow.
|
|
887
|
+
pub fn parent_id(&self) -> Option<&str> {
|
|
888
|
+
self.raw
|
|
889
|
+
.parent_execution
|
|
890
|
+
.as_ref()
|
|
891
|
+
.map(|e| e.workflow_id.as_str())
|
|
892
|
+
}
|
|
1176
893
|
|
|
1177
|
-
///
|
|
1178
|
-
pub
|
|
894
|
+
/// Parent run ID, if this is a child workflow.
|
|
895
|
+
pub fn parent_run_id(&self) -> Option<&str> {
|
|
896
|
+
self.raw
|
|
897
|
+
.parent_execution
|
|
898
|
+
.as_ref()
|
|
899
|
+
.map(|e| e.run_id.as_str())
|
|
900
|
+
}
|
|
1179
901
|
|
|
1180
|
-
///
|
|
1181
|
-
pub
|
|
902
|
+
/// Search attributes on the workflow.
|
|
903
|
+
pub fn search_attributes(&self) -> Option<&SearchAttributes> {
|
|
904
|
+
self.raw.search_attributes.as_ref()
|
|
905
|
+
}
|
|
1182
906
|
|
|
1183
|
-
///
|
|
1184
|
-
pub
|
|
907
|
+
/// Access the raw proto for additional fields not exposed via accessors.
|
|
908
|
+
pub fn raw(&self) -> &workflow::WorkflowExecutionInfo {
|
|
909
|
+
&self.raw
|
|
910
|
+
}
|
|
1185
911
|
|
|
1186
|
-
///
|
|
1187
|
-
pub
|
|
912
|
+
/// Consume the wrapper and return the raw proto.
|
|
913
|
+
pub fn into_raw(self) -> workflow::WorkflowExecutionInfo {
|
|
914
|
+
self.raw
|
|
915
|
+
}
|
|
916
|
+
}
|
|
1188
917
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
918
|
+
impl From<workflow::WorkflowExecutionInfo> for WorkflowExecution {
|
|
919
|
+
fn from(raw: workflow::WorkflowExecutionInfo) -> Self {
|
|
920
|
+
Self::new(raw)
|
|
921
|
+
}
|
|
922
|
+
}
|
|
1192
923
|
|
|
1193
|
-
|
|
1194
|
-
|
|
924
|
+
/// A stream of workflow executions from a list query.
|
|
925
|
+
/// Internally paginates through results from the server.
|
|
926
|
+
pub struct ListWorkflowsStream {
|
|
927
|
+
inner: Pin<Box<dyn Stream<Item = Result<WorkflowExecution, ClientError>> + Send>>,
|
|
928
|
+
}
|
|
1195
929
|
|
|
1196
|
-
|
|
1197
|
-
|
|
930
|
+
impl ListWorkflowsStream {
|
|
931
|
+
fn new(
|
|
932
|
+
inner: Pin<Box<dyn Stream<Item = Result<WorkflowExecution, ClientError>> + Send>>,
|
|
933
|
+
) -> Self {
|
|
934
|
+
Self { inner }
|
|
935
|
+
}
|
|
936
|
+
}
|
|
1198
937
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
pub completion_callbacks: Vec<common::v1::Callback>,
|
|
938
|
+
impl Stream for ListWorkflowsStream {
|
|
939
|
+
type Item = Result<WorkflowExecution, ClientError>;
|
|
1202
940
|
|
|
1203
|
-
|
|
1204
|
-
|
|
941
|
+
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
942
|
+
self.inner.as_mut().poll_next(cx)
|
|
943
|
+
}
|
|
1205
944
|
}
|
|
1206
945
|
|
|
1207
|
-
///
|
|
1208
|
-
/// when tasks are backlogged in a queue. Initially, Priority will be used in
|
|
1209
|
-
/// activity and workflow task queues, which are typically where backlogs exist.
|
|
1210
|
-
/// Other queues in the server (such as transfer and timer queues) and rate
|
|
1211
|
-
/// limiting decisions do not use Priority, but may in the future.
|
|
946
|
+
/// Result of a workflow count operation.
|
|
1212
947
|
///
|
|
1213
|
-
///
|
|
1214
|
-
///
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
///
|
|
1220
|
-
/// Despite being named "Priority", this message will also contains fields that
|
|
1221
|
-
/// control "fairness" mechanisms.
|
|
1222
|
-
///
|
|
1223
|
-
/// The overall semantics of Priority are:
|
|
1224
|
-
/// (more will be added here later)
|
|
1225
|
-
/// 1. First, consider "priority_key": lower number goes first.
|
|
1226
|
-
#[derive(Debug, Clone, Default, PartialEq)]
|
|
1227
|
-
pub struct Priority {
|
|
1228
|
-
/// Priority key is a positive integer from 1 to n, where smaller integers
|
|
1229
|
-
/// correspond to higher priorities (tasks run sooner). In general, tasks in
|
|
1230
|
-
/// a queue should be processed in close to priority order, although small
|
|
1231
|
-
/// deviations are possible.
|
|
1232
|
-
///
|
|
1233
|
-
/// The maximum priority value (minimum priority) is determined by server
|
|
1234
|
-
/// configuration, and defaults to 5.
|
|
1235
|
-
///
|
|
1236
|
-
/// The default priority is (min+max)/2. With the default max of 5 and min of
|
|
1237
|
-
/// 1, that comes out to 3.
|
|
1238
|
-
pub priority_key: u32,
|
|
1239
|
-
|
|
1240
|
-
/// Fairness key is a short string that's used as a key for a fairness
|
|
1241
|
-
/// balancing mechanism. It may correspond to a tenant id, or to a fixed
|
|
1242
|
-
/// string like "high" or "low". The default is the empty string.
|
|
1243
|
-
///
|
|
1244
|
-
/// The fairness mechanism attempts to dispatch tasks for a given key in
|
|
1245
|
-
/// proportion to its weight. For example, using a thousand distinct tenant
|
|
1246
|
-
/// ids, each with a weight of 1.0 (the default) will result in each tenant
|
|
1247
|
-
/// getting a roughly equal share of task dispatch throughput.
|
|
1248
|
-
///
|
|
1249
|
-
/// (Note: this does not imply equal share of worker capacity! Fairness
|
|
1250
|
-
/// decisions are made based on queue statistics, not
|
|
1251
|
-
/// current worker load.)
|
|
1252
|
-
///
|
|
1253
|
-
/// As another example, using keys "high" and "low" with weight 9.0 and 1.0
|
|
1254
|
-
/// respectively will prefer dispatching "high" tasks over "low" tasks at a
|
|
1255
|
-
/// 9:1 ratio, while allowing either key to use all worker capacity if the
|
|
1256
|
-
/// other is not present.
|
|
1257
|
-
///
|
|
1258
|
-
/// All fairness mechanisms, including rate limits, are best-effort and
|
|
1259
|
-
/// probabilistic. The results may not match what a "perfect" algorithm with
|
|
1260
|
-
/// infinite resources would produce. The more unique keys are used, the less
|
|
1261
|
-
/// accurate the results will be.
|
|
1262
|
-
///
|
|
1263
|
-
/// Fairness keys are limited to 64 bytes.
|
|
1264
|
-
pub fairness_key: String,
|
|
1265
|
-
|
|
1266
|
-
/// Fairness weight for a task can come from multiple sources for
|
|
1267
|
-
/// flexibility. From highest to lowest precedence:
|
|
1268
|
-
/// 1. Weights for a small set of keys can be overridden in task queue
|
|
1269
|
-
/// configuration with an API.
|
|
1270
|
-
/// 2. It can be attached to the workflow/activity in this field.
|
|
1271
|
-
/// 3. The default weight of 1.0 will be used.
|
|
1272
|
-
///
|
|
1273
|
-
/// Weight values are clamped by the server to the range [0.001, 1000].
|
|
1274
|
-
pub fairness_weight: f32,
|
|
948
|
+
/// If the query includes a group-by clause, `groups` will contain the aggregated
|
|
949
|
+
/// counts and `count` will be the sum of all group counts.
|
|
950
|
+
#[derive(Debug, Clone)]
|
|
951
|
+
pub struct WorkflowExecutionCount {
|
|
952
|
+
count: usize,
|
|
953
|
+
groups: Vec<WorkflowCountAggregationGroup>,
|
|
1275
954
|
}
|
|
1276
955
|
|
|
1277
|
-
impl
|
|
1278
|
-
fn
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
956
|
+
impl WorkflowExecutionCount {
|
|
957
|
+
pub(crate) fn from_response(resp: CountWorkflowExecutionsResponse) -> Self {
|
|
958
|
+
Self {
|
|
959
|
+
count: resp.count as usize,
|
|
960
|
+
groups: resp
|
|
961
|
+
.groups
|
|
962
|
+
.into_iter()
|
|
963
|
+
.map(WorkflowCountAggregationGroup::from_proto)
|
|
964
|
+
.collect(),
|
|
1283
965
|
}
|
|
1284
966
|
}
|
|
967
|
+
|
|
968
|
+
/// The approximate number of workflows matching the query.
|
|
969
|
+
/// If grouping was applied, this is the sum of all group counts.
|
|
970
|
+
pub fn count(&self) -> usize {
|
|
971
|
+
self.count
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/// The groups if the query had a group-by clause, or empty if not.
|
|
975
|
+
pub fn groups(&self) -> &[WorkflowCountAggregationGroup] {
|
|
976
|
+
&self.groups
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/// Aggregation group from a workflow count query with a group-by clause.
|
|
981
|
+
#[derive(Debug, Clone)]
|
|
982
|
+
pub struct WorkflowCountAggregationGroup {
|
|
983
|
+
group_values: Vec<Payload>,
|
|
984
|
+
count: usize,
|
|
1285
985
|
}
|
|
1286
986
|
|
|
1287
|
-
impl
|
|
1288
|
-
fn
|
|
987
|
+
impl WorkflowCountAggregationGroup {
|
|
988
|
+
fn from_proto(proto: count_workflow_executions_response::AggregationGroup) -> Self {
|
|
1289
989
|
Self {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
fairness_weight: priority.fairness_weight,
|
|
990
|
+
group_values: proto.group_values,
|
|
991
|
+
count: proto.count as usize,
|
|
1293
992
|
}
|
|
1294
993
|
}
|
|
994
|
+
|
|
995
|
+
/// The search attribute values for this group.
|
|
996
|
+
pub fn group_values(&self) -> &[Payload] {
|
|
997
|
+
&self.group_values
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/// The approximate number of workflows matching for this group.
|
|
1001
|
+
pub fn count(&self) -> usize {
|
|
1002
|
+
self.count
|
|
1003
|
+
}
|
|
1295
1004
|
}
|
|
1296
1005
|
|
|
1297
|
-
#[async_trait::async_trait]
|
|
1298
1006
|
impl<T> WorkflowClientTrait for T
|
|
1299
1007
|
where
|
|
1300
1008
|
T: WorkflowService + NamespacedClient + Clone + Send + Sync + 'static,
|
|
1301
1009
|
{
|
|
1302
|
-
async fn start_workflow(
|
|
1010
|
+
async fn start_workflow<W>(
|
|
1303
1011
|
&self,
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
.
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1012
|
+
workflow: W,
|
|
1013
|
+
input: W::Input,
|
|
1014
|
+
options: WorkflowStartOptions,
|
|
1015
|
+
) -> Result<WorkflowHandle<Self, W>, WorkflowStartError>
|
|
1016
|
+
where
|
|
1017
|
+
W: WorkflowDefinition,
|
|
1018
|
+
W::Input: Send,
|
|
1019
|
+
{
|
|
1020
|
+
let payloads = self
|
|
1021
|
+
.data_converter()
|
|
1022
|
+
.to_payloads(&SerializationContextData::Workflow, &input)
|
|
1023
|
+
.await?;
|
|
1024
|
+
let namespace = self.namespace();
|
|
1025
|
+
let workflow_id = options.workflow_id.clone();
|
|
1026
|
+
let task_queue_name = options.task_queue.clone();
|
|
1027
|
+
|
|
1028
|
+
let run_id = if let Some(start_signal) = options.start_signal {
|
|
1029
|
+
// Use signal-with-start when a start_signal is provided
|
|
1030
|
+
let res = WorkflowService::signal_with_start_workflow_execution(
|
|
1031
|
+
&mut self.clone(),
|
|
1032
|
+
SignalWithStartWorkflowExecutionRequest {
|
|
1033
|
+
namespace: namespace.clone(),
|
|
1034
|
+
workflow_id: workflow_id.clone(),
|
|
1318
1035
|
workflow_type: Some(WorkflowType {
|
|
1319
|
-
name:
|
|
1036
|
+
name: workflow.name().to_string(),
|
|
1320
1037
|
}),
|
|
1321
1038
|
task_queue: Some(TaskQueue {
|
|
1322
|
-
name:
|
|
1323
|
-
kind: TaskQueueKind::
|
|
1039
|
+
name: task_queue_name,
|
|
1040
|
+
kind: TaskQueueKind::Normal as i32,
|
|
1324
1041
|
normal_name: "".to_string(),
|
|
1325
1042
|
}),
|
|
1326
|
-
|
|
1043
|
+
input: payloads.into_payloads(),
|
|
1044
|
+
signal_name: start_signal.signal_name,
|
|
1045
|
+
signal_input: start_signal.input,
|
|
1046
|
+
identity: self.identity(),
|
|
1047
|
+
request_id: Uuid::new_v4().to_string(),
|
|
1327
1048
|
workflow_id_reuse_policy: options.id_reuse_policy as i32,
|
|
1328
1049
|
workflow_id_conflict_policy: options.id_conflict_policy as i32,
|
|
1329
1050
|
workflow_execution_timeout: options
|
|
@@ -1333,482 +1054,203 @@ where
|
|
|
1333
1054
|
workflow_task_timeout: options.task_timeout.and_then(|d| d.try_into().ok()),
|
|
1334
1055
|
search_attributes: options.search_attributes.map(|d| d.into()),
|
|
1335
1056
|
cron_schedule: options.cron_schedule.unwrap_or_default(),
|
|
1336
|
-
|
|
1337
|
-
retry_policy: options.retry_policy,
|
|
1338
|
-
links: options.links,
|
|
1339
|
-
completion_callbacks: options.completion_callbacks,
|
|
1340
|
-
priority: options.priority.map(Into::into),
|
|
1057
|
+
header: options.header.or(start_signal.header),
|
|
1341
1058
|
..Default::default()
|
|
1342
1059
|
}
|
|
1343
1060
|
.into_request(),
|
|
1344
1061
|
)
|
|
1345
1062
|
.await?
|
|
1346
|
-
.into_inner()
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1063
|
+
.into_inner();
|
|
1064
|
+
res.run_id
|
|
1065
|
+
} else {
|
|
1066
|
+
// Normal start workflow
|
|
1067
|
+
let res = self
|
|
1068
|
+
.clone()
|
|
1069
|
+
.start_workflow_execution(
|
|
1070
|
+
StartWorkflowExecutionRequest {
|
|
1071
|
+
namespace: namespace.clone(),
|
|
1072
|
+
input: payloads.into_payloads(),
|
|
1073
|
+
workflow_id: workflow_id.clone(),
|
|
1074
|
+
workflow_type: Some(WorkflowType {
|
|
1075
|
+
name: workflow.name().to_string(),
|
|
1076
|
+
}),
|
|
1077
|
+
task_queue: Some(TaskQueue {
|
|
1078
|
+
name: task_queue_name,
|
|
1079
|
+
kind: TaskQueueKind::Unspecified as i32,
|
|
1080
|
+
normal_name: "".to_string(),
|
|
1081
|
+
}),
|
|
1082
|
+
request_id: Uuid::new_v4().to_string(),
|
|
1083
|
+
workflow_id_reuse_policy: options.id_reuse_policy as i32,
|
|
1084
|
+
workflow_id_conflict_policy: options.id_conflict_policy as i32,
|
|
1085
|
+
workflow_execution_timeout: options
|
|
1086
|
+
.execution_timeout
|
|
1087
|
+
.and_then(|d| d.try_into().ok()),
|
|
1088
|
+
workflow_run_timeout: options.run_timeout.and_then(|d| d.try_into().ok()),
|
|
1089
|
+
workflow_task_timeout: options.task_timeout.and_then(|d| d.try_into().ok()),
|
|
1090
|
+
search_attributes: options.search_attributes.map(|d| d.into()),
|
|
1091
|
+
cron_schedule: options.cron_schedule.unwrap_or_default(),
|
|
1092
|
+
request_eager_execution: options.enable_eager_workflow_start,
|
|
1093
|
+
retry_policy: options.retry_policy,
|
|
1094
|
+
links: options.links,
|
|
1095
|
+
completion_callbacks: options.completion_callbacks,
|
|
1096
|
+
priority: Some(options.priority.into()),
|
|
1097
|
+
header: options.header,
|
|
1098
|
+
..Default::default()
|
|
1099
|
+
}
|
|
1100
|
+
.into_request(),
|
|
1101
|
+
)
|
|
1102
|
+
.await
|
|
1103
|
+
.map_err(|status| {
|
|
1104
|
+
if status.code() == Code::AlreadyExists {
|
|
1105
|
+
let run_id =
|
|
1106
|
+
decode_status_detail::<WorkflowExecutionAlreadyStartedFailure>(
|
|
1107
|
+
status.details(),
|
|
1108
|
+
)
|
|
1109
|
+
.map(|f| f.run_id);
|
|
1110
|
+
WorkflowStartError::AlreadyStarted {
|
|
1111
|
+
run_id,
|
|
1112
|
+
source: status,
|
|
1113
|
+
}
|
|
1114
|
+
} else {
|
|
1115
|
+
WorkflowStartError::Rpc(status)
|
|
1116
|
+
}
|
|
1117
|
+
})?
|
|
1118
|
+
.into_inner();
|
|
1119
|
+
res.run_id
|
|
1360
1120
|
};
|
|
1361
|
-
Ok(
|
|
1362
|
-
WorkflowService::reset_sticky_task_queue(&mut self.clone(), request.into_request())
|
|
1363
|
-
.await?
|
|
1364
|
-
.into_inner(),
|
|
1365
|
-
)
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
async fn complete_activity_task(
|
|
1369
|
-
&self,
|
|
1370
|
-
task_token: TaskToken,
|
|
1371
|
-
result: Option<Payloads>,
|
|
1372
|
-
) -> Result<RespondActivityTaskCompletedResponse> {
|
|
1373
|
-
Ok(self
|
|
1374
|
-
.clone()
|
|
1375
|
-
.respond_activity_task_completed(
|
|
1376
|
-
RespondActivityTaskCompletedRequest {
|
|
1377
|
-
task_token: task_token.0,
|
|
1378
|
-
result,
|
|
1379
|
-
identity: self.identity(),
|
|
1380
|
-
namespace: self.namespace(),
|
|
1381
|
-
..Default::default()
|
|
1382
|
-
}
|
|
1383
|
-
.into_request(),
|
|
1384
|
-
)
|
|
1385
|
-
.await?
|
|
1386
|
-
.into_inner())
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
async fn record_activity_heartbeat(
|
|
1390
|
-
&self,
|
|
1391
|
-
task_token: TaskToken,
|
|
1392
|
-
details: Option<Payloads>,
|
|
1393
|
-
) -> Result<RecordActivityTaskHeartbeatResponse> {
|
|
1394
|
-
Ok(self
|
|
1395
|
-
.clone()
|
|
1396
|
-
.record_activity_task_heartbeat(
|
|
1397
|
-
RecordActivityTaskHeartbeatRequest {
|
|
1398
|
-
task_token: task_token.0,
|
|
1399
|
-
details,
|
|
1400
|
-
identity: self.identity(),
|
|
1401
|
-
namespace: self.namespace(),
|
|
1402
|
-
}
|
|
1403
|
-
.into_request(),
|
|
1404
|
-
)
|
|
1405
|
-
.await?
|
|
1406
|
-
.into_inner())
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
async fn cancel_activity_task(
|
|
1410
|
-
&self,
|
|
1411
|
-
task_token: TaskToken,
|
|
1412
|
-
details: Option<Payloads>,
|
|
1413
|
-
) -> Result<RespondActivityTaskCanceledResponse> {
|
|
1414
|
-
Ok(self
|
|
1415
|
-
.clone()
|
|
1416
|
-
.respond_activity_task_canceled(
|
|
1417
|
-
RespondActivityTaskCanceledRequest {
|
|
1418
|
-
task_token: task_token.0,
|
|
1419
|
-
details,
|
|
1420
|
-
identity: self.identity(),
|
|
1421
|
-
namespace: self.namespace(),
|
|
1422
|
-
..Default::default()
|
|
1423
|
-
}
|
|
1424
|
-
.into_request(),
|
|
1425
|
-
)
|
|
1426
|
-
.await?
|
|
1427
|
-
.into_inner())
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
async fn signal_workflow_execution(
|
|
1431
|
-
&self,
|
|
1432
|
-
workflow_id: String,
|
|
1433
|
-
run_id: String,
|
|
1434
|
-
signal_name: String,
|
|
1435
|
-
payloads: Option<Payloads>,
|
|
1436
|
-
request_id: Option<String>,
|
|
1437
|
-
) -> Result<SignalWorkflowExecutionResponse> {
|
|
1438
|
-
Ok(WorkflowService::signal_workflow_execution(
|
|
1439
|
-
&mut self.clone(),
|
|
1440
|
-
SignalWorkflowExecutionRequest {
|
|
1441
|
-
namespace: self.namespace(),
|
|
1442
|
-
workflow_execution: Some(WorkflowExecution {
|
|
1443
|
-
workflow_id,
|
|
1444
|
-
run_id,
|
|
1445
|
-
}),
|
|
1446
|
-
signal_name,
|
|
1447
|
-
input: payloads,
|
|
1448
|
-
identity: self.identity(),
|
|
1449
|
-
request_id: request_id.unwrap_or_else(|| Uuid::new_v4().to_string()),
|
|
1450
|
-
..Default::default()
|
|
1451
|
-
}
|
|
1452
|
-
.into_request(),
|
|
1453
|
-
)
|
|
1454
|
-
.await?
|
|
1455
|
-
.into_inner())
|
|
1456
|
-
}
|
|
1457
1121
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
workflow_id: options.workflow_id,
|
|
1468
|
-
workflow_type: Some(WorkflowType {
|
|
1469
|
-
name: options.workflow_type,
|
|
1470
|
-
}),
|
|
1471
|
-
task_queue: Some(TaskQueue {
|
|
1472
|
-
name: options.task_queue,
|
|
1473
|
-
kind: TaskQueueKind::Normal as i32,
|
|
1474
|
-
normal_name: "".to_string(),
|
|
1475
|
-
}),
|
|
1476
|
-
input: options.input,
|
|
1477
|
-
signal_name: options.signal_name,
|
|
1478
|
-
signal_input: options.signal_input,
|
|
1479
|
-
identity: self.identity(),
|
|
1480
|
-
request_id: options
|
|
1481
|
-
.request_id
|
|
1482
|
-
.unwrap_or_else(|| Uuid::new_v4().to_string()),
|
|
1483
|
-
workflow_id_reuse_policy: workflow_options.id_reuse_policy as i32,
|
|
1484
|
-
workflow_id_conflict_policy: workflow_options.id_conflict_policy as i32,
|
|
1485
|
-
workflow_execution_timeout: workflow_options
|
|
1486
|
-
.execution_timeout
|
|
1487
|
-
.and_then(|d| d.try_into().ok()),
|
|
1488
|
-
workflow_run_timeout: workflow_options.run_timeout.and_then(|d| d.try_into().ok()),
|
|
1489
|
-
workflow_task_timeout: workflow_options
|
|
1490
|
-
.task_timeout
|
|
1491
|
-
.and_then(|d| d.try_into().ok()),
|
|
1492
|
-
search_attributes: workflow_options.search_attributes.map(|d| d.into()),
|
|
1493
|
-
cron_schedule: workflow_options.cron_schedule.unwrap_or_default(),
|
|
1494
|
-
header: options.signal_header,
|
|
1495
|
-
..Default::default()
|
|
1496
|
-
}
|
|
1497
|
-
.into_request(),
|
|
1498
|
-
)
|
|
1499
|
-
.await?
|
|
1500
|
-
.into_inner())
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
async fn query_workflow_execution(
|
|
1504
|
-
&self,
|
|
1505
|
-
workflow_id: String,
|
|
1506
|
-
run_id: String,
|
|
1507
|
-
query: WorkflowQuery,
|
|
1508
|
-
) -> Result<QueryWorkflowResponse> {
|
|
1509
|
-
Ok(self
|
|
1510
|
-
.clone()
|
|
1511
|
-
.query_workflow(
|
|
1512
|
-
QueryWorkflowRequest {
|
|
1513
|
-
namespace: self.namespace(),
|
|
1514
|
-
execution: Some(WorkflowExecution {
|
|
1515
|
-
workflow_id,
|
|
1516
|
-
run_id,
|
|
1517
|
-
}),
|
|
1518
|
-
query: Some(query),
|
|
1519
|
-
query_reject_condition: 1,
|
|
1520
|
-
}
|
|
1521
|
-
.into_request(),
|
|
1522
|
-
)
|
|
1523
|
-
.await?
|
|
1524
|
-
.into_inner())
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
async fn describe_workflow_execution(
|
|
1528
|
-
&self,
|
|
1529
|
-
workflow_id: String,
|
|
1530
|
-
run_id: Option<String>,
|
|
1531
|
-
) -> Result<DescribeWorkflowExecutionResponse> {
|
|
1532
|
-
Ok(WorkflowService::describe_workflow_execution(
|
|
1533
|
-
&mut self.clone(),
|
|
1534
|
-
DescribeWorkflowExecutionRequest {
|
|
1535
|
-
namespace: self.namespace(),
|
|
1536
|
-
execution: Some(WorkflowExecution {
|
|
1537
|
-
workflow_id,
|
|
1538
|
-
run_id: run_id.unwrap_or_default(),
|
|
1539
|
-
}),
|
|
1540
|
-
}
|
|
1541
|
-
.into_request(),
|
|
1542
|
-
)
|
|
1543
|
-
.await?
|
|
1544
|
-
.into_inner())
|
|
1122
|
+
Ok(WorkflowHandle::new(
|
|
1123
|
+
self.clone(),
|
|
1124
|
+
WorkflowExecutionInfo {
|
|
1125
|
+
namespace,
|
|
1126
|
+
workflow_id,
|
|
1127
|
+
run_id: Some(run_id.clone()),
|
|
1128
|
+
first_execution_run_id: Some(run_id),
|
|
1129
|
+
},
|
|
1130
|
+
))
|
|
1545
1131
|
}
|
|
1546
1132
|
|
|
1547
|
-
|
|
1133
|
+
fn get_workflow_handle<W: WorkflowDefinition>(
|
|
1548
1134
|
&self,
|
|
1549
|
-
workflow_id: String
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1135
|
+
workflow_id: impl Into<String>,
|
|
1136
|
+
) -> WorkflowHandle<Self, W>
|
|
1137
|
+
where
|
|
1138
|
+
Self: Sized,
|
|
1139
|
+
{
|
|
1140
|
+
WorkflowHandle::new(
|
|
1141
|
+
self.clone(),
|
|
1142
|
+
WorkflowExecutionInfo {
|
|
1556
1143
|
namespace: self.namespace(),
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
next_page_token: page_token,
|
|
1562
|
-
..Default::default()
|
|
1563
|
-
}
|
|
1564
|
-
.into_request(),
|
|
1144
|
+
workflow_id: workflow_id.into(),
|
|
1145
|
+
run_id: None,
|
|
1146
|
+
first_execution_run_id: None,
|
|
1147
|
+
},
|
|
1565
1148
|
)
|
|
1566
|
-
.await?
|
|
1567
|
-
.into_inner())
|
|
1568
1149
|
}
|
|
1569
1150
|
|
|
1570
|
-
|
|
1151
|
+
fn list_workflows(
|
|
1571
1152
|
&self,
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1153
|
+
query: impl Into<String>,
|
|
1154
|
+
opts: WorkflowListOptions,
|
|
1155
|
+
) -> ListWorkflowsStream {
|
|
1156
|
+
let client = self.clone();
|
|
1157
|
+
let namespace = self.namespace();
|
|
1158
|
+
let query = query.into();
|
|
1159
|
+
let limit = opts.limit;
|
|
1160
|
+
|
|
1161
|
+
// State: (next_page_token, buffer, yielded_count, exhausted)
|
|
1162
|
+
let initial_state = (Vec::new(), VecDeque::new(), 0, false);
|
|
1163
|
+
|
|
1164
|
+
let stream = stream::unfold(
|
|
1165
|
+
initial_state,
|
|
1166
|
+
move |(next_page_token, mut buffer, mut yielded, exhausted)| {
|
|
1167
|
+
let mut client = client.clone();
|
|
1168
|
+
let namespace = namespace.clone();
|
|
1169
|
+
let query = query.clone();
|
|
1170
|
+
|
|
1171
|
+
async move {
|
|
1172
|
+
if let Some(l) = limit
|
|
1173
|
+
&& yielded >= l
|
|
1174
|
+
{
|
|
1175
|
+
return None;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
if let Some(exec) = buffer.pop_front() {
|
|
1179
|
+
yielded += 1;
|
|
1180
|
+
return Some((Ok(exec), (next_page_token, buffer, yielded, exhausted)));
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
if exhausted {
|
|
1184
|
+
return None;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
let response = WorkflowService::list_workflow_executions(
|
|
1188
|
+
&mut client,
|
|
1189
|
+
ListWorkflowExecutionsRequest {
|
|
1190
|
+
namespace,
|
|
1191
|
+
page_size: 0, // Use server default
|
|
1192
|
+
next_page_token: next_page_token.clone(),
|
|
1193
|
+
query,
|
|
1194
|
+
}
|
|
1195
|
+
.into_request(),
|
|
1196
|
+
)
|
|
1197
|
+
.await;
|
|
1198
|
+
|
|
1199
|
+
match response {
|
|
1200
|
+
Ok(resp) => {
|
|
1201
|
+
let resp = resp.into_inner();
|
|
1202
|
+
let new_exhausted = resp.next_page_token.is_empty();
|
|
1203
|
+
let new_token = resp.next_page_token;
|
|
1204
|
+
|
|
1205
|
+
buffer = resp
|
|
1206
|
+
.executions
|
|
1207
|
+
.into_iter()
|
|
1208
|
+
.map(WorkflowExecution::from)
|
|
1209
|
+
.collect();
|
|
1210
|
+
|
|
1211
|
+
if let Some(exec) = buffer.pop_front() {
|
|
1212
|
+
yielded += 1;
|
|
1213
|
+
Some((Ok(exec), (new_token, buffer, yielded, new_exhausted)))
|
|
1214
|
+
} else {
|
|
1215
|
+
None
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
Err(e) => Some((Err(e.into()), (next_page_token, buffer, yielded, true))),
|
|
1219
|
+
}
|
|
1591
1220
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
.await?
|
|
1595
|
-
.into_inner())
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
async fn terminate_workflow_execution(
|
|
1599
|
-
&self,
|
|
1600
|
-
workflow_id: String,
|
|
1601
|
-
run_id: Option<String>,
|
|
1602
|
-
) -> Result<TerminateWorkflowExecutionResponse> {
|
|
1603
|
-
Ok(WorkflowService::terminate_workflow_execution(
|
|
1604
|
-
&mut self.clone(),
|
|
1605
|
-
TerminateWorkflowExecutionRequest {
|
|
1606
|
-
namespace: self.namespace(),
|
|
1607
|
-
workflow_execution: Some(WorkflowExecution {
|
|
1608
|
-
workflow_id,
|
|
1609
|
-
run_id: run_id.unwrap_or_default(),
|
|
1610
|
-
}),
|
|
1611
|
-
reason: "".to_string(),
|
|
1612
|
-
details: None,
|
|
1613
|
-
identity: self.identity(),
|
|
1614
|
-
first_execution_run_id: "".to_string(),
|
|
1615
|
-
links: vec![],
|
|
1616
|
-
}
|
|
1617
|
-
.into_request(),
|
|
1618
|
-
)
|
|
1619
|
-
.await?
|
|
1620
|
-
.into_inner())
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
async fn register_namespace(
|
|
1624
|
-
&self,
|
|
1625
|
-
options: RegisterNamespaceOptions,
|
|
1626
|
-
) -> Result<RegisterNamespaceResponse> {
|
|
1627
|
-
let req = Into::<RegisterNamespaceRequest>::into(options);
|
|
1628
|
-
Ok(
|
|
1629
|
-
WorkflowService::register_namespace(&mut self.clone(), req.into_request())
|
|
1630
|
-
.await?
|
|
1631
|
-
.into_inner(),
|
|
1632
|
-
)
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
async fn list_namespaces(&self) -> Result<ListNamespacesResponse> {
|
|
1636
|
-
Ok(WorkflowService::list_namespaces(
|
|
1637
|
-
&mut self.clone(),
|
|
1638
|
-
ListNamespacesRequest::default().into_request(),
|
|
1639
|
-
)
|
|
1640
|
-
.await?
|
|
1641
|
-
.into_inner())
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
async fn describe_namespace(&self, namespace: Namespace) -> Result<DescribeNamespaceResponse> {
|
|
1645
|
-
Ok(WorkflowService::describe_namespace(
|
|
1646
|
-
&mut self.clone(),
|
|
1647
|
-
namespace.into_describe_namespace_request().into_request(),
|
|
1648
|
-
)
|
|
1649
|
-
.await?
|
|
1650
|
-
.into_inner())
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
async fn list_open_workflow_executions(
|
|
1654
|
-
&self,
|
|
1655
|
-
maximum_page_size: i32,
|
|
1656
|
-
next_page_token: Vec<u8>,
|
|
1657
|
-
start_time_filter: Option<StartTimeFilter>,
|
|
1658
|
-
filters: Option<list_open_workflow_executions_request::Filters>,
|
|
1659
|
-
) -> Result<ListOpenWorkflowExecutionsResponse> {
|
|
1660
|
-
Ok(WorkflowService::list_open_workflow_executions(
|
|
1661
|
-
&mut self.clone(),
|
|
1662
|
-
ListOpenWorkflowExecutionsRequest {
|
|
1663
|
-
namespace: self.namespace(),
|
|
1664
|
-
maximum_page_size,
|
|
1665
|
-
next_page_token,
|
|
1666
|
-
start_time_filter,
|
|
1667
|
-
filters,
|
|
1668
|
-
}
|
|
1669
|
-
.into_request(),
|
|
1670
|
-
)
|
|
1671
|
-
.await?
|
|
1672
|
-
.into_inner())
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
async fn list_closed_workflow_executions(
|
|
1676
|
-
&self,
|
|
1677
|
-
maximum_page_size: i32,
|
|
1678
|
-
next_page_token: Vec<u8>,
|
|
1679
|
-
start_time_filter: Option<StartTimeFilter>,
|
|
1680
|
-
filters: Option<list_closed_workflow_executions_request::Filters>,
|
|
1681
|
-
) -> Result<ListClosedWorkflowExecutionsResponse> {
|
|
1682
|
-
Ok(WorkflowService::list_closed_workflow_executions(
|
|
1683
|
-
&mut self.clone(),
|
|
1684
|
-
ListClosedWorkflowExecutionsRequest {
|
|
1685
|
-
namespace: self.namespace(),
|
|
1686
|
-
maximum_page_size,
|
|
1687
|
-
next_page_token,
|
|
1688
|
-
start_time_filter,
|
|
1689
|
-
filters,
|
|
1690
|
-
}
|
|
1691
|
-
.into_request(),
|
|
1692
|
-
)
|
|
1693
|
-
.await?
|
|
1694
|
-
.into_inner())
|
|
1695
|
-
}
|
|
1221
|
+
},
|
|
1222
|
+
);
|
|
1696
1223
|
|
|
1697
|
-
|
|
1698
|
-
&self,
|
|
1699
|
-
page_size: i32,
|
|
1700
|
-
next_page_token: Vec<u8>,
|
|
1701
|
-
query: String,
|
|
1702
|
-
) -> Result<ListWorkflowExecutionsResponse> {
|
|
1703
|
-
Ok(WorkflowService::list_workflow_executions(
|
|
1704
|
-
&mut self.clone(),
|
|
1705
|
-
ListWorkflowExecutionsRequest {
|
|
1706
|
-
namespace: self.namespace(),
|
|
1707
|
-
page_size,
|
|
1708
|
-
next_page_token,
|
|
1709
|
-
query,
|
|
1710
|
-
}
|
|
1711
|
-
.into_request(),
|
|
1712
|
-
)
|
|
1713
|
-
.await?
|
|
1714
|
-
.into_inner())
|
|
1224
|
+
ListWorkflowsStream::new(Box::pin(stream))
|
|
1715
1225
|
}
|
|
1716
1226
|
|
|
1717
|
-
async fn
|
|
1227
|
+
async fn count_workflows(
|
|
1718
1228
|
&self,
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
Ok(WorkflowService::list_archived_workflow_executions(
|
|
1229
|
+
query: impl Into<String>,
|
|
1230
|
+
_opts: WorkflowCountOptions,
|
|
1231
|
+
) -> Result<WorkflowExecutionCount, ClientError> {
|
|
1232
|
+
let resp = WorkflowService::count_workflow_executions(
|
|
1724
1233
|
&mut self.clone(),
|
|
1725
|
-
|
|
1234
|
+
CountWorkflowExecutionsRequest {
|
|
1726
1235
|
namespace: self.namespace(),
|
|
1727
|
-
|
|
1728
|
-
next_page_token,
|
|
1729
|
-
query,
|
|
1236
|
+
query: query.into(),
|
|
1730
1237
|
}
|
|
1731
1238
|
.into_request(),
|
|
1732
1239
|
)
|
|
1733
1240
|
.await?
|
|
1734
|
-
.into_inner()
|
|
1735
|
-
}
|
|
1241
|
+
.into_inner();
|
|
1736
1242
|
|
|
1737
|
-
|
|
1738
|
-
Ok(WorkflowService::get_search_attributes(
|
|
1739
|
-
&mut self.clone(),
|
|
1740
|
-
GetSearchAttributesRequest {}.into_request(),
|
|
1741
|
-
)
|
|
1742
|
-
.await?
|
|
1743
|
-
.into_inner())
|
|
1243
|
+
Ok(WorkflowExecutionCount::from_response(resp))
|
|
1744
1244
|
}
|
|
1745
1245
|
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
wait_policy: update::v1::WaitPolicy,
|
|
1752
|
-
args: Option<Payloads>,
|
|
1753
|
-
) -> Result<UpdateWorkflowExecutionResponse> {
|
|
1754
|
-
Ok(WorkflowService::update_workflow_execution(
|
|
1755
|
-
&mut self.clone(),
|
|
1756
|
-
UpdateWorkflowExecutionRequest {
|
|
1757
|
-
namespace: self.namespace(),
|
|
1758
|
-
workflow_execution: Some(WorkflowExecution {
|
|
1759
|
-
workflow_id,
|
|
1760
|
-
run_id,
|
|
1761
|
-
}),
|
|
1762
|
-
wait_policy: Some(wait_policy),
|
|
1763
|
-
request: Some(update::v1::Request {
|
|
1764
|
-
meta: Some(update::v1::Meta {
|
|
1765
|
-
update_id: "".into(),
|
|
1766
|
-
identity: self.identity(),
|
|
1767
|
-
}),
|
|
1768
|
-
input: Some(update::v1::Input {
|
|
1769
|
-
header: None,
|
|
1770
|
-
name,
|
|
1771
|
-
args,
|
|
1772
|
-
}),
|
|
1773
|
-
}),
|
|
1774
|
-
..Default::default()
|
|
1775
|
-
}
|
|
1776
|
-
.into_request(),
|
|
1777
|
-
)
|
|
1778
|
-
.await?
|
|
1779
|
-
.into_inner())
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
mod sealed {
|
|
1784
|
-
use crate::{WorkflowClientTrait, WorkflowService};
|
|
1785
|
-
pub trait WfHandleClient: WorkflowClientTrait + WorkflowService {}
|
|
1786
|
-
impl<T> WfHandleClient for T where T: WorkflowClientTrait + WorkflowService {}
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
/// Additional methods for workflow clients
|
|
1790
|
-
pub trait WfClientExt: WfHandleClient + Sized + Clone {
|
|
1791
|
-
/// Create an untyped handle for a workflow execution, which can be used to do things like
|
|
1792
|
-
/// wait for that workflow's result. `run_id` may be left blank to target the latest run.
|
|
1793
|
-
fn get_untyped_workflow_handle(
|
|
1794
|
-
&self,
|
|
1795
|
-
workflow_id: impl Into<String>,
|
|
1796
|
-
run_id: impl Into<String>,
|
|
1797
|
-
) -> UntypedWorkflowHandle<Self> {
|
|
1798
|
-
let rid = run_id.into();
|
|
1799
|
-
UntypedWorkflowHandle::new(
|
|
1800
|
-
self.clone(),
|
|
1801
|
-
WorkflowExecutionInfo {
|
|
1802
|
-
namespace: self.namespace(),
|
|
1803
|
-
workflow_id: workflow_id.into(),
|
|
1804
|
-
run_id: if rid.is_empty() { None } else { Some(rid) },
|
|
1805
|
-
},
|
|
1806
|
-
)
|
|
1246
|
+
fn get_async_activity_handle(&self, identifier: ActivityIdentifier) -> AsyncActivityHandle<Self>
|
|
1247
|
+
where
|
|
1248
|
+
Self: Sized,
|
|
1249
|
+
{
|
|
1250
|
+
AsyncActivityHandle::new(self.clone(), identifier)
|
|
1807
1251
|
}
|
|
1808
1252
|
}
|
|
1809
1253
|
|
|
1810
|
-
impl<T> WfClientExt for T where T: WfHandleClient + Clone + Sized {}
|
|
1811
|
-
|
|
1812
1254
|
macro_rules! dbg_panic {
|
|
1813
1255
|
($($arg:tt)*) => {
|
|
1814
1256
|
use tracing::error;
|
|
@@ -1822,16 +1264,10 @@ pub(crate) use dbg_panic;
|
|
|
1822
1264
|
mod tests {
|
|
1823
1265
|
use super::*;
|
|
1824
1266
|
use tonic::metadata::Ascii;
|
|
1267
|
+
use url::Url;
|
|
1825
1268
|
|
|
1826
1269
|
#[test]
|
|
1827
1270
|
fn applies_headers() {
|
|
1828
|
-
let opts = ClientOptions::builder()
|
|
1829
|
-
.identity("enchicat".to_string())
|
|
1830
|
-
.target_url(Url::parse("https://smolkitty").unwrap())
|
|
1831
|
-
.client_name("cute-kitty".to_string())
|
|
1832
|
-
.client_version("0.1.0".to_string())
|
|
1833
|
-
.build();
|
|
1834
|
-
|
|
1835
1271
|
// Initial header set
|
|
1836
1272
|
let headers = Arc::new(RwLock::new(ClientHeaders {
|
|
1837
1273
|
user_headers: HashMap::new(),
|
|
@@ -1847,7 +1283,8 @@ mod tests {
|
|
|
1847
1283
|
vec![1, 2, 3].try_into().unwrap(),
|
|
1848
1284
|
);
|
|
1849
1285
|
let mut interceptor = ServiceCallInterceptor {
|
|
1850
|
-
|
|
1286
|
+
client_name: "cute-kitty".to_string(),
|
|
1287
|
+
client_version: "0.1.0".to_string(),
|
|
1851
1288
|
headers: headers.clone(),
|
|
1852
1289
|
};
|
|
1853
1290
|
|
|
@@ -1959,9 +1396,8 @@ mod tests {
|
|
|
1959
1396
|
|
|
1960
1397
|
#[test]
|
|
1961
1398
|
fn keep_alive_defaults() {
|
|
1962
|
-
let opts =
|
|
1399
|
+
let opts = ConnectionOptions::new(Url::parse("https://smolkitty").unwrap())
|
|
1963
1400
|
.identity("enchicat".to_string())
|
|
1964
|
-
.target_url(Url::parse("https://smolkitty").unwrap())
|
|
1965
1401
|
.client_name("cute-kitty".to_string())
|
|
1966
1402
|
.client_version("0.1.0".to_string())
|
|
1967
1403
|
.build();
|
|
@@ -1975,9 +1411,8 @@ mod tests {
|
|
|
1975
1411
|
);
|
|
1976
1412
|
|
|
1977
1413
|
// Can be explicitly set to None
|
|
1978
|
-
let opts =
|
|
1414
|
+
let opts = ConnectionOptions::new(Url::parse("https://smolkitty").unwrap())
|
|
1979
1415
|
.identity("enchicat".to_string())
|
|
1980
|
-
.target_url(Url::parse("https://smolkitty").unwrap())
|
|
1981
1416
|
.client_name("cute-kitty".to_string())
|
|
1982
1417
|
.client_version("0.1.0".to_string())
|
|
1983
1418
|
.keep_alive(None)
|
|
@@ -1985,4 +1420,163 @@ mod tests {
|
|
|
1985
1420
|
dbg!(&opts.keep_alive);
|
|
1986
1421
|
assert!(opts.keep_alive.is_none());
|
|
1987
1422
|
}
|
|
1423
|
+
|
|
1424
|
+
mod list_workflows_tests {
|
|
1425
|
+
use super::*;
|
|
1426
|
+
use futures_util::{FutureExt, StreamExt};
|
|
1427
|
+
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
1428
|
+
use temporalio_common::protos::temporal::api::common::v1::WorkflowExecution as ProtoWorkflowExecution;
|
|
1429
|
+
use tonic::{Request, Response};
|
|
1430
|
+
|
|
1431
|
+
#[derive(Clone)]
|
|
1432
|
+
struct MockListWorkflowsClient {
|
|
1433
|
+
call_count: Arc<AtomicUsize>,
|
|
1434
|
+
// Returns this many workflows per page
|
|
1435
|
+
page_size: usize,
|
|
1436
|
+
// Total workflows available
|
|
1437
|
+
total_workflows: usize,
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
impl NamespacedClient for MockListWorkflowsClient {
|
|
1441
|
+
fn namespace(&self) -> String {
|
|
1442
|
+
"test-namespace".to_string()
|
|
1443
|
+
}
|
|
1444
|
+
fn identity(&self) -> String {
|
|
1445
|
+
"test-identity".to_string()
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
impl WorkflowService for MockListWorkflowsClient {
|
|
1450
|
+
fn list_workflow_executions(
|
|
1451
|
+
&mut self,
|
|
1452
|
+
request: Request<ListWorkflowExecutionsRequest>,
|
|
1453
|
+
) -> futures_util::future::BoxFuture<
|
|
1454
|
+
'_,
|
|
1455
|
+
Result<Response<ListWorkflowExecutionsResponse>, tonic::Status>,
|
|
1456
|
+
> {
|
|
1457
|
+
self.call_count.fetch_add(1, Ordering::SeqCst);
|
|
1458
|
+
let req = request.into_inner();
|
|
1459
|
+
|
|
1460
|
+
// Determine offset from page token
|
|
1461
|
+
let offset: usize = if req.next_page_token.is_empty() {
|
|
1462
|
+
0
|
|
1463
|
+
} else {
|
|
1464
|
+
String::from_utf8(req.next_page_token)
|
|
1465
|
+
.unwrap()
|
|
1466
|
+
.parse()
|
|
1467
|
+
.unwrap()
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1470
|
+
let remaining = self.total_workflows.saturating_sub(offset);
|
|
1471
|
+
let count = remaining.min(self.page_size);
|
|
1472
|
+
let new_offset = offset + count;
|
|
1473
|
+
|
|
1474
|
+
let executions: Vec<_> = (offset..offset + count)
|
|
1475
|
+
.map(|i| workflow::WorkflowExecutionInfo {
|
|
1476
|
+
execution: Some(ProtoWorkflowExecution {
|
|
1477
|
+
workflow_id: format!("wf-{i}"),
|
|
1478
|
+
run_id: format!("run-{i}"),
|
|
1479
|
+
}),
|
|
1480
|
+
r#type: Some(WorkflowType {
|
|
1481
|
+
name: "TestWorkflow".to_string(),
|
|
1482
|
+
}),
|
|
1483
|
+
task_queue: "test-queue".to_string(),
|
|
1484
|
+
..Default::default()
|
|
1485
|
+
})
|
|
1486
|
+
.collect();
|
|
1487
|
+
|
|
1488
|
+
let next_page_token = if new_offset < self.total_workflows {
|
|
1489
|
+
new_offset.to_string().into_bytes()
|
|
1490
|
+
} else {
|
|
1491
|
+
vec![]
|
|
1492
|
+
};
|
|
1493
|
+
|
|
1494
|
+
async move {
|
|
1495
|
+
Ok(Response::new(ListWorkflowExecutionsResponse {
|
|
1496
|
+
executions,
|
|
1497
|
+
next_page_token,
|
|
1498
|
+
}))
|
|
1499
|
+
}
|
|
1500
|
+
.boxed()
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
#[tokio::test]
|
|
1505
|
+
async fn list_workflows_paginates_through_all_results() {
|
|
1506
|
+
let call_count = Arc::new(AtomicUsize::new(0));
|
|
1507
|
+
let client = MockListWorkflowsClient {
|
|
1508
|
+
call_count: call_count.clone(),
|
|
1509
|
+
page_size: 3,
|
|
1510
|
+
total_workflows: 10,
|
|
1511
|
+
};
|
|
1512
|
+
|
|
1513
|
+
let stream = client.list_workflows("", WorkflowListOptions::default());
|
|
1514
|
+
let results: Vec<_> = stream.collect().await;
|
|
1515
|
+
|
|
1516
|
+
assert_eq!(results.len(), 10);
|
|
1517
|
+
for (i, result) in results.iter().enumerate() {
|
|
1518
|
+
let wf = result.as_ref().unwrap();
|
|
1519
|
+
assert_eq!(wf.id(), format!("wf-{i}"));
|
|
1520
|
+
assert_eq!(wf.run_id(), format!("run-{i}"));
|
|
1521
|
+
}
|
|
1522
|
+
// Should have made 4 calls: pages of 3, 3, 3, 1
|
|
1523
|
+
assert_eq!(call_count.load(Ordering::SeqCst), 4);
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
#[tokio::test]
|
|
1527
|
+
async fn list_workflows_respects_limit() {
|
|
1528
|
+
let call_count = Arc::new(AtomicUsize::new(0));
|
|
1529
|
+
let client = MockListWorkflowsClient {
|
|
1530
|
+
call_count: call_count.clone(),
|
|
1531
|
+
page_size: 3,
|
|
1532
|
+
total_workflows: 10,
|
|
1533
|
+
};
|
|
1534
|
+
|
|
1535
|
+
let opts = WorkflowListOptions::builder().limit(5).build();
|
|
1536
|
+
let stream = client.list_workflows("", opts);
|
|
1537
|
+
let results: Vec<_> = stream.collect().await;
|
|
1538
|
+
|
|
1539
|
+
assert_eq!(results.len(), 5);
|
|
1540
|
+
for (i, result) in results.iter().enumerate() {
|
|
1541
|
+
let wf = result.as_ref().unwrap();
|
|
1542
|
+
assert_eq!(wf.id(), format!("wf-{i}"));
|
|
1543
|
+
}
|
|
1544
|
+
// Should have made 2 calls: 1 page of 3, then 2 more from next page
|
|
1545
|
+
assert_eq!(call_count.load(Ordering::SeqCst), 2);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
#[tokio::test]
|
|
1549
|
+
async fn list_workflows_limit_less_than_page_size() {
|
|
1550
|
+
let call_count = Arc::new(AtomicUsize::new(0));
|
|
1551
|
+
let client = MockListWorkflowsClient {
|
|
1552
|
+
call_count: call_count.clone(),
|
|
1553
|
+
page_size: 10,
|
|
1554
|
+
total_workflows: 100,
|
|
1555
|
+
};
|
|
1556
|
+
|
|
1557
|
+
let opts = WorkflowListOptions::builder().limit(3).build();
|
|
1558
|
+
let stream = client.list_workflows("", opts);
|
|
1559
|
+
let results: Vec<_> = stream.collect().await;
|
|
1560
|
+
|
|
1561
|
+
assert_eq!(results.len(), 3);
|
|
1562
|
+
// Only 1 call needed since limit < page_size
|
|
1563
|
+
assert_eq!(call_count.load(Ordering::SeqCst), 1);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
#[tokio::test]
|
|
1567
|
+
async fn list_workflows_empty_results() {
|
|
1568
|
+
let call_count = Arc::new(AtomicUsize::new(0));
|
|
1569
|
+
let client = MockListWorkflowsClient {
|
|
1570
|
+
call_count: call_count.clone(),
|
|
1571
|
+
page_size: 10,
|
|
1572
|
+
total_workflows: 0,
|
|
1573
|
+
};
|
|
1574
|
+
|
|
1575
|
+
let stream = client.list_workflows("", WorkflowListOptions::default());
|
|
1576
|
+
let results: Vec<_> = stream.collect().await;
|
|
1577
|
+
|
|
1578
|
+
assert_eq!(results.len(), 0);
|
|
1579
|
+
assert_eq!(call_count.load(Ordering::SeqCst), 1);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1988
1582
|
}
|