From 017e1efc329bd4e511662e4da4616601f1871b77 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Tue, 11 Feb 2025 09:48:13 -0600 Subject: [PATCH] Search attributes and UI port on dev server Fixes #205 Fixes #206 --- temporalio/ext/src/testing.rs | 3 +- .../lib/temporalio/internal/bridge/testing.rb | 1 + .../lib/temporalio/search_attributes.rb | 13 +++++ .../testing/workflow_environment.rb | 17 +++++- .../temporalio/internal/bridge/testing.rbs | 2 + .../sig/temporalio/search_attributes.rbs | 2 + .../testing/workflow_environment.rbs | 4 ++ .../test/testing/workflow_environment_test.rb | 58 +++++++++++++++++++ 8 files changed, 98 insertions(+), 2 deletions(-) diff --git a/temporalio/ext/src/testing.rs b/temporalio/ext/src/testing.rs index 1e5fcb82..d0e93b0f 100644 --- a/temporalio/ext/src/testing.rs +++ b/temporalio/ext/src/testing.rs @@ -61,7 +61,8 @@ impl EphemeralServer { .ip(options.member::(id!("ip"))?) .port(options.member::>(id!("port"))?) .db_filename(options.member::>(id!("database_filename"))?) - .ui(options.member(id!("namespace"))?) + .ui(options.member(id!("ui"))?) + .ui_port(options.member::>(id!("ui_port"))?) .log(( options.member::(id!("log_format"))?, options.member::(id!("log_level"))?, diff --git a/temporalio/lib/temporalio/internal/bridge/testing.rb b/temporalio/lib/temporalio/internal/bridge/testing.rb index 846c4fb3..883daab5 100644 --- a/temporalio/lib/temporalio/internal/bridge/testing.rb +++ b/temporalio/lib/temporalio/internal/bridge/testing.rb @@ -18,6 +18,7 @@ class EphemeralServer :port, # Optional :database_filename, # Optional :ui, + :ui_port, # Optional, should be nil if ui is false :log_format, :log_level, :extra_args, diff --git a/temporalio/lib/temporalio/search_attributes.rb b/temporalio/lib/temporalio/search_attributes.rb index bb3a9458..e8a1d283 100644 --- a/temporalio/lib/temporalio/search_attributes.rb +++ b/temporalio/lib/temporalio/search_attributes.rb @@ -251,6 +251,14 @@ def length @raw_hash.length end + # Check equality. + # + # @param other [SearchAttributes] To compare. + # @return [Boolean] Whether equal. + def ==(other) + other.is_a?(SearchAttributes) && @raw_hash == other._raw_hash + end + alias size length # Return a new search attributes collection with updates applied. @@ -280,6 +288,11 @@ def update!(*updates) end end + # @!visibility private + def _raw_hash + @raw_hash + end + # @!visibility private def _to_proto Api::Common::V1::SearchAttributes.new(indexed_fields: _to_proto_hash) diff --git a/temporalio/lib/temporalio/testing/workflow_environment.rb b/temporalio/lib/temporalio/testing/workflow_environment.rb index 668d9d81..a2138673 100644 --- a/temporalio/lib/temporalio/testing/workflow_environment.rb +++ b/temporalio/lib/temporalio/testing/workflow_environment.rb @@ -10,6 +10,7 @@ require 'temporalio/internal/bridge/testing' require 'temporalio/internal/proto_utils' require 'temporalio/runtime' +require 'temporalio/search_attributes' require 'temporalio/version' module Temporalio @@ -34,8 +35,10 @@ class WorkflowEnvironment # @param default_workflow_query_reject_condition [WorkflowQueryRejectCondition, nil] Default rejection condition # for the client. # @param ip [String] IP to bind to. - # @param port [Integer, nil] Port to bind on, or +nil+ for random. + # @param port [Integer, nil] Port to bind on, or `nil` for random. # @param ui [Boolean] If +true+, also starts the UI. + # @param ui_port [Integer, nil] Port to bind on if `ui` is true, or `nil` for random. + # @param search_attributes [Array] Search attributes to make available on start. # @param runtime [Runtime] Runtime for the server and client. # @param dev_server_existing_path [String, nil] Existing CLI path to use instead of downloading and caching to # tmp. @@ -62,6 +65,8 @@ def self.start_local( ip: '127.0.0.1', port: nil, ui: false, # rubocop:disable Naming/MethodParameterName + ui_port: nil, + search_attributes: [], runtime: Runtime.default, dev_server_existing_path: nil, dev_server_database_filename: nil, @@ -72,6 +77,15 @@ def self.start_local( dev_server_extra_args: [], & ) + # Add search attribute args + unless search_attributes.empty? + dev_server_extra_args += search_attributes.flat_map do |key| + raise 'Search attribute must be Key' unless key.is_a?(SearchAttributes::Key) + + ['--search-attribute', "#{key.name}=#{SearchAttributes::IndexedValueType::PROTO_NAMES[key.type]}"] + end + end + server_options = Internal::Bridge::Testing::EphemeralServer::StartDevServerOptions.new( existing_path: dev_server_existing_path, sdk_name: 'sdk-ruby', @@ -83,6 +97,7 @@ def self.start_local( port:, database_filename: dev_server_database_filename, ui:, + ui_port: ui ? ui_port : nil, log_format: dev_server_log_format, log_level: dev_server_log_level, extra_args: dev_server_extra_args diff --git a/temporalio/sig/temporalio/internal/bridge/testing.rbs b/temporalio/sig/temporalio/internal/bridge/testing.rbs index 56a36c77..310585c8 100644 --- a/temporalio/sig/temporalio/internal/bridge/testing.rbs +++ b/temporalio/sig/temporalio/internal/bridge/testing.rbs @@ -14,6 +14,7 @@ module Temporalio attr_accessor port: Integer? attr_accessor database_filename: String? attr_accessor ui: bool + attr_accessor ui_port: Integer? attr_accessor log_format: String attr_accessor log_level: String attr_accessor extra_args: Array[String] @@ -29,6 +30,7 @@ module Temporalio port: Integer?, database_filename: String?, ui: bool, + ui_port: Integer?, log_format: String, log_level: String, extra_args: Array[String] diff --git a/temporalio/sig/temporalio/search_attributes.rbs b/temporalio/sig/temporalio/search_attributes.rbs index aeeb15fb..77982759 100644 --- a/temporalio/sig/temporalio/search_attributes.rbs +++ b/temporalio/sig/temporalio/search_attributes.rbs @@ -56,6 +56,8 @@ module Temporalio def update!: (*Update updates) -> void + def _raw_hash: -> Hash[Key, Object] + def _to_proto: -> untyped def _to_proto_hash: -> Hash[String, untyped] diff --git a/temporalio/sig/temporalio/testing/workflow_environment.rbs b/temporalio/sig/temporalio/testing/workflow_environment.rbs index 779bb1de..79e85f0d 100644 --- a/temporalio/sig/temporalio/testing/workflow_environment.rbs +++ b/temporalio/sig/temporalio/testing/workflow_environment.rbs @@ -12,6 +12,8 @@ module Temporalio ?ip: String, ?port: Integer?, ?ui: bool, + ?ui_port: Integer?, + ?search_attributes: Array[SearchAttributes::Key], ?runtime: Runtime, ?dev_server_existing_path: String?, ?dev_server_database_filename: String?, @@ -30,6 +32,8 @@ module Temporalio ?ip: String, ?port: Integer?, ?ui: bool, + ?ui_port: Integer?, + ?search_attributes: Array[SearchAttributes::Key], ?runtime: Runtime, ?dev_server_existing_path: String?, ?dev_server_database_filename: String?, diff --git a/temporalio/test/testing/workflow_environment_test.rb b/temporalio/test/testing/workflow_environment_test.rb index ec21238b..aebc231d 100644 --- a/temporalio/test/testing/workflow_environment_test.rb +++ b/temporalio/test/testing/workflow_environment_test.rb @@ -131,5 +131,63 @@ def test_time_skipping_heartbeat_timeout end end end + + def test_start_local_search_attributes + pre = 'ruby-temporal-test-' + attr_boolean = Temporalio::SearchAttributes::Key.new( + "#{pre}boolean", Temporalio::SearchAttributes::IndexedValueType::BOOLEAN + ) + attr_float = Temporalio::SearchAttributes::Key.new( + "#{pre}float", Temporalio::SearchAttributes::IndexedValueType::FLOAT + ) + attr_integer = Temporalio::SearchAttributes::Key.new( + "#{pre}integer", Temporalio::SearchAttributes::IndexedValueType::INTEGER + ) + attr_keyword = Temporalio::SearchAttributes::Key.new( + "#{pre}keyword", Temporalio::SearchAttributes::IndexedValueType::KEYWORD + ) + attr_keyword_list = Temporalio::SearchAttributes::Key.new( + "#{pre}keyword-list", Temporalio::SearchAttributes::IndexedValueType::KEYWORD_LIST + ) + attr_text = Temporalio::SearchAttributes::Key.new( + "#{pre}text", Temporalio::SearchAttributes::IndexedValueType::TEXT + ) + attr_time = Temporalio::SearchAttributes::Key.new( + "#{pre}time", Temporalio::SearchAttributes::IndexedValueType::TIME + ) + attrs = Temporalio::SearchAttributes.new( + { + attr_boolean => true, + attr_float => 1.23, + attr_integer => 456, + attr_keyword => 'some keyword', + attr_keyword_list => ['some keyword list 1', 'some keyword list 2'], + attr_text => 'some text', + attr_time => Time.at(Time.now.to_i) + } + ) + + # Confirm when used in env without SAs it fails + Temporalio::Testing::WorkflowEnvironment.start_local do |env| + err = assert_raises(Temporalio::Error::RPCError) do + env.client.start_workflow( + :some_workflow, + id: "wf-#{SecureRandom.uuid}", task_queue: "tq-#{SecureRandom.uuid}", + search_attributes: attrs + ) + end + assert_includes err.message, 'no mapping defined' + end + + # Confirm when used in env with SAs it succeeds + Temporalio::Testing::WorkflowEnvironment.start_local(search_attributes: attrs.to_h.keys) do |env| + handle = env.client.start_workflow( + :some_workflow, + id: "wf-#{SecureRandom.uuid}", task_queue: "tq-#{SecureRandom.uuid}", + search_attributes: attrs + ) + assert_equal attrs, handle.describe.search_attributes + end + end end end