operaton-bpm-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +134 -0
  3. data/lib/operaton/bpm/client/backoff/backoff_strategy.rb +20 -0
  4. data/lib/operaton/bpm/client/backoff/error_aware_backoff_strategy.rb +22 -0
  5. data/lib/operaton/bpm/client/backoff/exponential_backoff_strategy.rb +38 -0
  6. data/lib/operaton/bpm/client/backoff/exponential_error_backoff_strategy.rb +38 -0
  7. data/lib/operaton/bpm/client/exceptions.rb +74 -0
  8. data/lib/operaton/bpm/client/external_task_client.rb +20 -0
  9. data/lib/operaton/bpm/client/external_task_client_builder.rb +18 -0
  10. data/lib/operaton/bpm/client/impl/engine_client.rb +132 -0
  11. data/lib/operaton/bpm/client/impl/engine_client_exception.rb +25 -0
  12. data/lib/operaton/bpm/client/impl/engine_client_logger.rb +54 -0
  13. data/lib/operaton/bpm/client/impl/engine_rest_exception_dto.rb +28 -0
  14. data/lib/operaton/bpm/client/impl/external_task_client_builder_impl.rb +349 -0
  15. data/lib/operaton/bpm/client/impl/external_task_client_impl.rb +41 -0
  16. data/lib/operaton/bpm/client/impl/external_task_client_logger.rb +236 -0
  17. data/lib/operaton/bpm/client/impl/object_mapper.rb +44 -0
  18. data/lib/operaton/bpm/client/impl/permanent_url_resolver.rb +22 -0
  19. data/lib/operaton/bpm/client/impl/request_dto.rb +24 -0
  20. data/lib/operaton/bpm/client/impl/request_executor.rb +143 -0
  21. data/lib/operaton/bpm/client/interceptor/auth/basic_auth_provider.rb +42 -0
  22. data/lib/operaton/bpm/client/interceptor/client_request_context.rb +16 -0
  23. data/lib/operaton/bpm/client/interceptor/client_request_interceptor.rb +18 -0
  24. data/lib/operaton/bpm/client/interceptor/impl/client_request_context_impl.rb +28 -0
  25. data/lib/operaton/bpm/client/interceptor/impl/request_interceptor_handler.rb +43 -0
  26. data/lib/operaton/bpm/client/spi/data_format.rb +32 -0
  27. data/lib/operaton/bpm/client/spi/data_format_configurator.rb +31 -0
  28. data/lib/operaton/bpm/client/spi/data_format_provider.rb +31 -0
  29. data/lib/operaton/bpm/client/task/external_task.rb +22 -0
  30. data/lib/operaton/bpm/client/task/external_task_handler.rb +28 -0
  31. data/lib/operaton/bpm/client/task/external_task_service.rb +18 -0
  32. data/lib/operaton/bpm/client/task/impl/dto/bpmn_error_request_dto.rb +35 -0
  33. data/lib/operaton/bpm/client/task/impl/dto/complete_request_dto.rb +33 -0
  34. data/lib/operaton/bpm/client/task/impl/dto/extend_lock_request_dto.rb +29 -0
  35. data/lib/operaton/bpm/client/task/impl/dto/failure_request_dto.rb +43 -0
  36. data/lib/operaton/bpm/client/task/impl/dto/lock_request_dto.rb +29 -0
  37. data/lib/operaton/bpm/client/task/impl/dto/set_variables_request_dto.rb +29 -0
  38. data/lib/operaton/bpm/client/task/impl/external_task_impl.rb +128 -0
  39. data/lib/operaton/bpm/client/task/impl/external_task_service_impl.rb +90 -0
  40. data/lib/operaton/bpm/client/task/ordering_config.rb +78 -0
  41. data/lib/operaton/bpm/client/task/sorting_dto.rb +29 -0
  42. data/lib/operaton/bpm/client/topic/impl/dto/fetch_and_lock_request_dto.rb +47 -0
  43. data/lib/operaton/bpm/client/topic/impl/dto/fetch_and_lock_response_dto.rb +32 -0
  44. data/lib/operaton/bpm/client/topic/impl/dto/topic_request_dto.rb +70 -0
  45. data/lib/operaton/bpm/client/topic/impl/topic_subscription_builder_impl.rb +164 -0
  46. data/lib/operaton/bpm/client/topic/impl/topic_subscription_impl.rb +76 -0
  47. data/lib/operaton/bpm/client/topic/impl/topic_subscription_manager.rb +222 -0
  48. data/lib/operaton/bpm/client/topic/impl/topic_subscription_manager_logger.rb +52 -0
  49. data/lib/operaton/bpm/client/topic/topic_subscription.rb +20 -0
  50. data/lib/operaton/bpm/client/topic/topic_subscription_builder.rb +22 -0
  51. data/lib/operaton/bpm/client/url_resolver.rb +15 -0
  52. data/lib/operaton/bpm/client/variable/client_values.rb +33 -0
  53. data/lib/operaton/bpm/client/variable/impl/abstract_typed_value_mapper.rb +59 -0
  54. data/lib/operaton/bpm/client/variable/impl/default_value_mappers.rb +71 -0
  55. data/lib/operaton/bpm/client/variable/impl/format/json/json_data_format.rb +82 -0
  56. data/lib/operaton/bpm/client/variable/impl/format/json/json_data_format_provider.rb +34 -0
  57. data/lib/operaton/bpm/client/variable/impl/mapper/boolean_value_mapper.rb +35 -0
  58. data/lib/operaton/bpm/client/variable/impl/mapper/byte_array_value_mapper.rb +53 -0
  59. data/lib/operaton/bpm/client/variable/impl/mapper/date_value_mapper.rb +61 -0
  60. data/lib/operaton/bpm/client/variable/impl/mapper/double_value_mapper.rb +36 -0
  61. data/lib/operaton/bpm/client/variable/impl/mapper/file_value_mapper.rb +73 -0
  62. data/lib/operaton/bpm/client/variable/impl/mapper/integer_value_mapper.rb +36 -0
  63. data/lib/operaton/bpm/client/variable/impl/mapper/json_value_mapper.rb +35 -0
  64. data/lib/operaton/bpm/client/variable/impl/mapper/long_value_mapper.rb +36 -0
  65. data/lib/operaton/bpm/client/variable/impl/mapper/null_value_mapper.rb +45 -0
  66. data/lib/operaton/bpm/client/variable/impl/mapper/number_value_mapper.rb +25 -0
  67. data/lib/operaton/bpm/client/variable/impl/mapper/object_value_mapper.rb +151 -0
  68. data/lib/operaton/bpm/client/variable/impl/mapper/primitive_value_mapper.rb +44 -0
  69. data/lib/operaton/bpm/client/variable/impl/mapper/short_value_mapper.rb +36 -0
  70. data/lib/operaton/bpm/client/variable/impl/mapper/string_value_mapper.rb +35 -0
  71. data/lib/operaton/bpm/client/variable/impl/mapper/xml_value_mapper.rb +35 -0
  72. data/lib/operaton/bpm/client/variable/impl/type/json_type_impl.rb +24 -0
  73. data/lib/operaton/bpm/client/variable/impl/type/xml_type_impl.rb +24 -0
  74. data/lib/operaton/bpm/client/variable/impl/typed_value_field.rb +36 -0
  75. data/lib/operaton/bpm/client/variable/impl/typed_values.rb +100 -0
  76. data/lib/operaton/bpm/client/variable/impl/value/deferred_file_value_impl.rb +57 -0
  77. data/lib/operaton/bpm/client/variable/impl/value/json_value_impl.rb +26 -0
  78. data/lib/operaton/bpm/client/variable/impl/value/xml_value_impl.rb +26 -0
  79. data/lib/operaton/bpm/client/variable/impl/value_mapper.rb +42 -0
  80. data/lib/operaton/bpm/client/variable/impl/value_mappers.rb +26 -0
  81. data/lib/operaton/bpm/client/variable/impl/variable_value.rb +55 -0
  82. data/lib/operaton/bpm/client/variable/value/deferred_file_value.rb +19 -0
  83. data/lib/operaton/bpm/client/variable/value/json_value.rb +16 -0
  84. data/lib/operaton/bpm/client/variable/value/xml_value.rb +16 -0
  85. data/lib/operaton/bpm/client/version.rb +9 -0
  86. data/lib/operaton/bpm/client.rb +121 -0
  87. data/lib/operaton/bpm/engine/variable/typed_value.rb +153 -0
  88. data/lib/operaton/bpm/engine/variable/value_type.rb +126 -0
  89. data/lib/operaton/bpm/engine/variable/variable_map.rb +72 -0
  90. data/lib/operaton/bpm/engine/variable/variables.rb +197 -0
  91. data/lib/operaton-bpm-client.rb +3 -0
  92. metadata +188 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 74b372ece74dbf5d488be16d79062fd5dc13c3d513704a992b60e5e9e267ffd2
4
+ data.tar.gz: bf39b3e8e4dc77fb7feb41445e4745945f9894f4de58e466a5f03831d0bd0290
5
+ SHA512:
6
+ metadata.gz: 159410691b053c0ad712df52d59151fc85bb6b6c4f6d0c22b854451a63b145bb46b997f6f5cc3c9193c39dc1ee8e8800ea2c9ee9c1a225301289bc66f0b743d4
7
+ data.tar.gz: 930f1a568192d5eae7e895703b62abfe823d19d2b990efbde7feec89eb66c9639841da9393d3d084cfd9823d6452b7cf829ec18070fe0d924d6ee10350f95072
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # Operaton External Task Client for Ruby
2
+
3
+ A faithful Ruby recreation of the [Operaton](https://github.com/operaton/operaton)
4
+ Java external task client (`org.operaton.bpm.client`). The class structure,
5
+ method contract, defaults, validation rules, REST payloads, and error handling
6
+ mirror the Java implementation one-to-one; names are translated from Java
7
+ camelCase to Ruby snake_case.
8
+
9
+ ```
10
+ org.operaton.bpm.client -> Operaton::Bpm::Client
11
+ org.operaton.bpm.client.impl -> Operaton::Bpm::Client::Impl
12
+ org.operaton.bpm.client.task -> Operaton::Bpm::Client::Task
13
+ org.operaton.bpm.client.topic -> Operaton::Bpm::Client::Topic
14
+ org.operaton.bpm.client.backoff -> Operaton::Bpm::Client::Backoff
15
+ org.operaton.bpm.client.interceptor -> Operaton::Bpm::Client::Interceptor
16
+ org.operaton.bpm.client.variable -> Operaton::Bpm::Client::Variable
17
+ org.operaton.bpm.engine.variable -> Operaton::Bpm::Engine::Variable (shim)
18
+ ```
19
+
20
+ Exception classes from `org.operaton.bpm.client.exception` live directly under
21
+ `Operaton::Bpm::Client` (`BadRequestException`, `NotFoundException`,
22
+ `EngineException`, `ConnectionLostException`, `UnknownHttpErrorException`,
23
+ `ValueMapperException`, ...).
24
+
25
+ ## Usage
26
+
27
+ ```ruby
28
+ require "operaton-bpm-client"
29
+
30
+ client = Operaton::Bpm::Client::ExternalTaskClient.create
31
+ .base_url("http://localhost:8080/engine-rest")
32
+ .worker_id("my-worker") # optional; defaults to hostname + UUID
33
+ .max_tasks(10) # default 10
34
+ .lock_duration(20_000) # default 20s (milliseconds)
35
+ .async_response_timeout(30_000) # optional long polling
36
+ .add_interceptor(
37
+ Operaton::Bpm::Client::Interceptor::Auth::BasicAuthProvider.new("demo", "demo")
38
+ )
39
+ .build # starts fetching immediately
40
+
41
+ subscription = client.subscribe("invoice-topic")
42
+ .lock_duration(10_000)
43
+ .process_definition_key("invoice")
44
+ .handler do |task, service|
45
+ amount = task.variable("amount")
46
+
47
+ if amount.nil?
48
+ service.handle_bpmn_error(task, "MISSING_AMOUNT")
49
+ elsif amount.negative?
50
+ service.handle_failure(task, "negative amount", nil, (task.retries || 3) - 1, 5_000)
51
+ else
52
+ service.complete(task, { "approved" => true })
53
+ end
54
+ end
55
+ .open
56
+
57
+ sleep
58
+
59
+ # later:
60
+ subscription.close
61
+ client.stop
62
+ ```
63
+
64
+ Like the Java client, `create` returns a fluent `ExternalTaskClientBuilder`,
65
+ `build` validates the configuration and (unless `disable_auto_fetching` was
66
+ called) starts the `TopicSubscriptionManager` polling thread, and `subscribe`
67
+ returns a fluent `TopicSubscriptionBuilder` whose `open` activates the
68
+ subscription. Handlers may be blocks, lambdas, or objects responding to
69
+ `execute(task, service)`.
70
+
71
+ ## Typed variables
72
+
73
+ The engine's typed value API is recreated under `Operaton::Bpm::Engine::Variable`
74
+ and extended by `ClientValues`:
75
+
76
+ ```ruby
77
+ Variables = Operaton::Bpm::Engine::Variable::Variables
78
+ ClientValues = Operaton::Bpm::Client::Variable::ClientValues
79
+
80
+ service.complete(task, {
81
+ "count" => 3, # -> Integer
82
+ "big" => 2**40, # -> Long
83
+ "rate" => 0.19, # -> Double
84
+ "ok" => true, # -> Boolean
85
+ "when" => Time.now, # -> Date
86
+ "payload" => { "items" => [1, 2, 3] }, # -> Object (application/json)
87
+ "explicit" => Variables.long_value(5), # force Long
88
+ "raw" => Variables.byte_array_value("\x00\x01"),# -> Bytes (base64)
89
+ "doc" => ClientValues.json_value('{"a":1}'), # -> Json
90
+ "xml" => ClientValues.xml_value("<a/>"), # -> Xml
91
+ })
92
+
93
+ task.variable("payload") # deserialized value
94
+ task.variable_typed("payload") # ObjectValue (lazily deserialized)
95
+ task.all_variables # Hash of plain values
96
+ task.all_variables_typed # VariableMap of typed values
97
+ ```
98
+
99
+ File variables are received as deferred values whose content is fetched from
100
+ the engine on first access, mirroring `DeferredFileValue`.
101
+
102
+ ## Deviations from the Java client
103
+
104
+ Necessary adaptations to the Ruby ecosystem; everything else follows the Java
105
+ source:
106
+
107
+ - **HTTP**: `Net::HTTP` replaces Apache HttpClient. `customize_http_client`
108
+ accepts a block invoked with the `Net::HTTP` instance of each request.
109
+ - **JSON**: Ruby's `json` replaces Jackson. Object values serialize any
110
+ JSON-mappable Ruby value; `objectTypeName` records the Ruby class name.
111
+ - **Date format**: configured as a Ruby `strftime` pattern; the default
112
+ `"%Y-%m-%dT%H:%M:%S.%L%z"` matches the Java default
113
+ `"yyyy-MM-dd'T'HH:mm:ss.SSSZ"`.
114
+ - **SPI**: `java.util.ServiceLoader` is replaced by explicit registries
115
+ (`Spi::DataFormatProvider.register`, `Spi::DataFormatConfigurator.register`).
116
+ The JSON data format is registered by default; the Java-serialization and
117
+ DOM/XML object data formats have no Ruby equivalent (string-typed `xml`
118
+ values are fully supported).
119
+ - **Overloads**: Java method overloads collapse into single Ruby methods that
120
+ accept either an `ExternalTask` or an id, with optional trailing arguments.
121
+ - **`useCreateTime(true)`**: the Java implementation appends the same ordering
122
+ property twice (producing a duplicated sorting entry in the request); the
123
+ Ruby port configures it once.
124
+
125
+ ## Development
126
+
127
+ ```sh
128
+ bundle install
129
+ bundle exec rspec
130
+ ```
131
+
132
+ The spec suite covers builder validation, variable serialization round-trips,
133
+ every REST endpoint payload, HTTP error translation, interceptors, backoff
134
+ strategies, and the polling/dispatch loop (84 examples).
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Operaton
4
+ module Bpm
5
+ module Client
6
+ module Backoff
7
+ # Mirrors org.operaton.bpm.client.backoff.BackoffStrategy
8
+ module BackoffStrategy
9
+ def reconfigure(external_tasks)
10
+ raise NotImplementedError, "#{self.class} must implement #reconfigure"
11
+ end
12
+
13
+ def calculate_backoff_time
14
+ raise NotImplementedError, "#{self.class} must implement #calculate_backoff_time"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "backoff_strategy"
4
+
5
+ module Operaton
6
+ module Bpm
7
+ module Client
8
+ module Backoff
9
+ # Mirrors org.operaton.bpm.client.backoff.ErrorAwareBackoffStrategy.
10
+ # Implementations receive the exception (or nil) of the last
11
+ # fetch-and-lock attempt in addition to the fetched tasks.
12
+ module ErrorAwareBackoffStrategy
13
+ include BackoffStrategy
14
+
15
+ def reconfigure(external_tasks, exception = nil)
16
+ raise NotImplementedError, "#{self.class} must implement #reconfigure"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "backoff_strategy"
4
+
5
+ module Operaton
6
+ module Bpm
7
+ module Client
8
+ module Backoff
9
+ # Mirrors org.operaton.bpm.client.backoff.ExponentialBackoffStrategy
10
+ class ExponentialBackoffStrategy
11
+ include BackoffStrategy
12
+
13
+ def initialize(init_time = 500, factor = 2, max_time = 60_000)
14
+ @init_time = init_time
15
+ @factor = factor
16
+ @level = 0
17
+ @max_time = max_time
18
+ end
19
+
20
+ def reconfigure(external_tasks)
21
+ if external_tasks.empty?
22
+ @level += 1
23
+ else
24
+ @level = 0
25
+ end
26
+ end
27
+
28
+ def calculate_backoff_time
29
+ return 0 if @level.zero?
30
+
31
+ backoff_time = (@init_time * (@factor**(@level - 1))).to_i
32
+ [backoff_time, @max_time].min
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "error_aware_backoff_strategy"
4
+
5
+ module Operaton
6
+ module Bpm
7
+ module Client
8
+ module Backoff
9
+ # Mirrors org.operaton.bpm.client.backoff.ExponentialErrorBackoffStrategy
10
+ class ExponentialErrorBackoffStrategy
11
+ include ErrorAwareBackoffStrategy
12
+
13
+ def initialize(init_time = 500, factor = 2, max_time = 60_000)
14
+ @init_time = init_time
15
+ @factor = factor
16
+ @level = 0
17
+ @max_time = max_time
18
+ end
19
+
20
+ def reconfigure(_external_tasks, error = nil)
21
+ if error
22
+ @level += 1
23
+ else
24
+ @level = 0
25
+ end
26
+ end
27
+
28
+ def calculate_backoff_time
29
+ return 0 if @level.zero?
30
+
31
+ backoff_time = (@init_time * (@factor**(@level - 1))).to_i
32
+ [backoff_time, @max_time].min
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Mirrors the org.operaton.bpm.client.exception package. The classes live
4
+ # directly under Operaton::Bpm::Client to avoid shadowing Ruby's ::Exception.
5
+ module Operaton
6
+ module Bpm
7
+ module Client
8
+ # Mirrors org.operaton.bpm.client.exception.ExternalTaskClientException
9
+ class ExternalTaskClientException < StandardError
10
+ def initialize(message = nil, cause = nil)
11
+ super(message)
12
+ @wrapped_cause = cause
13
+ end
14
+
15
+ def cause
16
+ @wrapped_cause || super
17
+ end
18
+ end
19
+
20
+ # Mirrors org.operaton.bpm.client.exception.RestException
21
+ class RestException < ExternalTaskClientException
22
+ attr_reader :type, :code
23
+
24
+ def initialize(message, type_or_cause = nil, code = nil)
25
+ if type_or_cause.is_a?(::Exception)
26
+ super(message, type_or_cause)
27
+ @type = nil
28
+ @code = nil
29
+ else
30
+ super(message)
31
+ @type = type_or_cause
32
+ @code = code
33
+ end
34
+ @http_status_code = nil
35
+ end
36
+
37
+ def http_status_code
38
+ cause.is_a?(RestException) ? cause.http_status_code : @http_status_code
39
+ end
40
+
41
+ attr_writer :http_status_code
42
+
43
+ def type
44
+ cause.is_a?(RestException) ? cause.type : @type
45
+ end
46
+
47
+ def code
48
+ cause.is_a?(RestException) ? cause.code : @code
49
+ end
50
+ end
51
+
52
+ # Thrown when the engine responds with HTTP 400
53
+ class BadRequestException < RestException; end
54
+
55
+ # Thrown when the engine responds with HTTP 500
56
+ class EngineException < RestException; end
57
+
58
+ # Thrown when the engine responds with HTTP 404
59
+ class NotFoundException < RestException; end
60
+
61
+ # Thrown when the engine responds with an HTTP status code not known by the client
62
+ class UnknownHttpErrorException < RestException; end
63
+
64
+ # Thrown when the connection to the engine could not be established
65
+ class ConnectionLostException < ExternalTaskClientException; end
66
+
67
+ # Thrown when a data format error occurs
68
+ class DataFormatException < ExternalTaskClientException; end
69
+
70
+ # Thrown when a variable value cannot be mapped
71
+ class ValueMapperException < ExternalTaskClientException; end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Operaton
4
+ module Bpm
5
+ module Client
6
+ # Mirrors org.operaton.bpm.client.ExternalTaskClient. The static create()
7
+ # factory returns a fluent builder; the built client is an
8
+ # Impl::ExternalTaskClientImpl responding to #subscribe, #start, #stop
9
+ # and #active?.
10
+ module ExternalTaskClient
11
+ # Creates a fluent builder to configure the Operaton client.
12
+ def self.create
13
+ Impl::ExternalTaskClientBuilderImpl.new
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ require_relative "impl/external_task_client_builder_impl"
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Operaton
4
+ module Bpm
5
+ module Client
6
+ # Mirrors org.operaton.bpm.client.ExternalTaskClientBuilder. Documents
7
+ # the fluent contract implemented by Impl::ExternalTaskClientBuilderImpl.
8
+ module ExternalTaskClientBuilder
9
+ INTERFACE_METHODS = %i[
10
+ base_url url_resolver worker_id add_interceptor max_tasks use_priority
11
+ use_create_time order_by_create_time asc desc default_serialization_format
12
+ date_format async_response_timeout lock_duration disable_auto_fetching
13
+ backoff_strategy disable_backoff_strategy customize_http_client build
14
+ ].freeze
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "permanent_url_resolver"
4
+ require_relative "request_executor"
5
+ require_relative "../task/impl/external_task_impl"
6
+ require_relative "../task/impl/dto/bpmn_error_request_dto"
7
+ require_relative "../task/impl/dto/complete_request_dto"
8
+ require_relative "../task/impl/dto/extend_lock_request_dto"
9
+ require_relative "../task/impl/dto/failure_request_dto"
10
+ require_relative "../task/impl/dto/lock_request_dto"
11
+ require_relative "../task/impl/dto/set_variables_request_dto"
12
+ require_relative "../task/ordering_config"
13
+ require_relative "../topic/impl/dto/fetch_and_lock_request_dto"
14
+
15
+ module Operaton
16
+ module Bpm
17
+ module Client
18
+ module Impl
19
+ # Mirrors org.operaton.bpm.client.impl.EngineClient
20
+ class EngineClient
21
+ EXTERNAL_TASK_RESOURCE_PATH = "/external-task"
22
+ EXTERNAL_TASK_PROCESS_RESOURCE_PATH = "/process-instance"
23
+ FETCH_AND_LOCK_RESOURCE_PATH = "#{EXTERNAL_TASK_RESOURCE_PATH}/fetchAndLock"
24
+ ID_PATH_PARAM = "{id}"
25
+ ID_RESOURCE_PATH = "#{EXTERNAL_TASK_RESOURCE_PATH}/#{ID_PATH_PARAM}"
26
+ LOCK_RESOURCE_PATH = "#{ID_RESOURCE_PATH}/lock"
27
+ EXTEND_LOCK_RESOURCE_PATH = "#{ID_RESOURCE_PATH}/extendLock"
28
+ SET_VARIABLES_RESOURCE_PATH = "#{EXTERNAL_TASK_PROCESS_RESOURCE_PATH}/#{ID_PATH_PARAM}/variables"
29
+ UNLOCK_RESOURCE_PATH = "#{ID_RESOURCE_PATH}/unlock"
30
+ COMPLETE_RESOURCE_PATH = "#{ID_RESOURCE_PATH}/complete"
31
+ FAILURE_RESOURCE_PATH = "#{ID_RESOURCE_PATH}/failure"
32
+ BPMN_ERROR_RESOURCE_PATH = "#{ID_RESOURCE_PATH}/bpmnError"
33
+ NAME_PATH_PARAM = "{name}"
34
+ PROCESS_INSTANCE_RESOURCE_PATH = "/process-instance"
35
+ PROCESS_INSTANCE_ID_RESOURCE_PATH = "#{PROCESS_INSTANCE_RESOURCE_PATH}/#{ID_PATH_PARAM}"
36
+ GET_BINARY_VARIABLE = "#{PROCESS_INSTANCE_ID_RESOURCE_PATH}/variables/#{NAME_PATH_PARAM}/data"
37
+
38
+ attr_reader :worker_id, :max_tasks, :async_response_timeout, :ordering_config
39
+ attr_accessor :typed_values
40
+
41
+ def initialize(worker_id, max_tasks, async_response_timeout, url_resolver, engine_interaction,
42
+ use_priority = true, ordering_config = Task::OrderingConfig.empty)
43
+ @worker_id = worker_id
44
+ @max_tasks = max_tasks
45
+ @async_response_timeout = async_response_timeout
46
+ @url_resolver = url_resolver.respond_to?(:base_url) ? url_resolver : PermanentUrlResolver.new(url_resolver)
47
+ @engine_interaction = engine_interaction
48
+ @use_priority = use_priority
49
+ @ordering_config = ordering_config
50
+ end
51
+
52
+ def fetch_and_lock(topics)
53
+ payload = Topic::Impl::Dto::FetchAndLockRequestDto.new(
54
+ worker_id, max_tasks, async_response_timeout, topics, @use_priority, ordering_config
55
+ )
56
+ resource_url = base_url + FETCH_AND_LOCK_RESOURCE_PATH
57
+ @engine_interaction.post_request(resource_url, payload, [Task::Impl::ExternalTaskImpl])
58
+ end
59
+
60
+ def lock(task_id, lock_duration)
61
+ payload = Task::Impl::Dto::LockRequestDto.new(worker_id, lock_duration)
62
+ post_task_request(LOCK_RESOURCE_PATH, task_id, payload)
63
+ end
64
+
65
+ def unlock(task_id)
66
+ post_task_request(UNLOCK_RESOURCE_PATH, task_id, nil)
67
+ end
68
+
69
+ def complete(task_id, variables, local_variables)
70
+ payload = Task::Impl::Dto::CompleteRequestDto.new(
71
+ worker_id,
72
+ typed_values.serialize_variables(variables),
73
+ typed_values.serialize_variables(local_variables)
74
+ )
75
+ post_task_request(COMPLETE_RESOURCE_PATH, task_id, payload)
76
+ end
77
+
78
+ def set_variables(process_id, variables)
79
+ payload = Task::Impl::Dto::SetVariablesRequestDto.new(
80
+ worker_id, typed_values.serialize_variables(variables)
81
+ )
82
+ resource_url = base_url + SET_VARIABLES_RESOURCE_PATH.sub(ID_PATH_PARAM, process_id)
83
+ @engine_interaction.post_request(resource_url, payload, RequestExecutor::VOID)
84
+ end
85
+
86
+ def failure(task_id, error_message, error_details, retries, retry_timeout, variables, local_variables)
87
+ payload = Task::Impl::Dto::FailureRequestDto.new(
88
+ worker_id, error_message, error_details, retries, retry_timeout,
89
+ typed_values.serialize_variables(variables),
90
+ typed_values.serialize_variables(local_variables)
91
+ )
92
+ post_task_request(FAILURE_RESOURCE_PATH, task_id, payload)
93
+ end
94
+
95
+ def bpmn_error(task_id, error_code, error_message, variables)
96
+ payload = Task::Impl::Dto::BpmnErrorRequestDto.new(
97
+ worker_id, error_code, error_message, typed_values.serialize_variables(variables)
98
+ )
99
+ post_task_request(BPMN_ERROR_RESOURCE_PATH, task_id, payload)
100
+ end
101
+
102
+ def extend_lock(task_id, new_duration)
103
+ payload = Task::Impl::Dto::ExtendLockRequestDto.new(worker_id, new_duration)
104
+ post_task_request(EXTEND_LOCK_RESOURCE_PATH, task_id, payload)
105
+ end
106
+
107
+ def get_local_binary_variable(variable_name, execution_id)
108
+ resource_url = base_url + GET_BINARY_VARIABLE
109
+ .sub(ID_PATH_PARAM, execution_id)
110
+ .sub(NAME_PATH_PARAM, variable_name)
111
+ @engine_interaction.get_request(resource_url)
112
+ end
113
+
114
+ def base_url
115
+ @url_resolver.base_url
116
+ end
117
+
118
+ def use_priority?
119
+ @use_priority
120
+ end
121
+
122
+ private
123
+
124
+ def post_task_request(resource_path, task_id, payload)
125
+ resource_url = base_url + resource_path.sub(ID_PATH_PARAM, task_id)
126
+ @engine_interaction.post_request(resource_url, payload, RequestExecutor::VOID)
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../exceptions"
4
+
5
+ module Operaton
6
+ module Bpm
7
+ module Client
8
+ module Impl
9
+ # Mirrors org.operaton.bpm.client.impl.EngineClientException — the
10
+ # internal exception wrapping transport/REST failures before they are
11
+ # translated into the public exception hierarchy.
12
+ class EngineClientException < StandardError
13
+ def initialize(message = nil, cause = nil)
14
+ super(message)
15
+ @wrapped_cause = cause
16
+ end
17
+
18
+ def cause
19
+ @wrapped_cause || super
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "external_task_client_logger"
4
+ require_relative "engine_client_exception"
5
+
6
+ module Operaton
7
+ module Bpm
8
+ module Client
9
+ module Impl
10
+ # Mirrors org.operaton.bpm.client.impl.EngineClientLogger
11
+ class EngineClientLogger < ExternalTaskClientLogger
12
+ def exception_while_receiving_response(http_request, error)
13
+ EngineClientException.new(exception_message(
14
+ "001", "Request '{}' returned error: status code '{}' - message: {}",
15
+ http_request, error.http_status_code, error.message), error)
16
+ end
17
+
18
+ def exception_while_establishing_connection(http_request, error)
19
+ EngineClientException.new(exception_message(
20
+ "002", "Exception while establishing connection for request '{}'", http_request), error)
21
+ end
22
+
23
+ def exception_while_closing_resource_stream(response, error)
24
+ log_error("003", "Exception while closing resource stream of response '{}': ", error, response)
25
+ end
26
+
27
+ def exception_while_parsing_json_object(response_dto_class, error)
28
+ EngineClientException.new(exception_message(
29
+ "004", "Exception while parsing json object to response dto class '{}'", response_dto_class), error)
30
+ end
31
+
32
+ def exception_while_mapping_json_object(response_dto_class, error)
33
+ EngineClientException.new(exception_message(
34
+ "005", "Exception while mapping json object to response dto class '{}'", response_dto_class), error)
35
+ end
36
+
37
+ def exception_while_deserializing_json_object(response_dto_class, error)
38
+ EngineClientException.new(exception_message(
39
+ "006", "Exception while deserializing json object to response dto class '{}'", response_dto_class), error)
40
+ end
41
+
42
+ def exception_while_serializing_json_object(dto, error)
43
+ EngineClientException.new(exception_message(
44
+ "007", "Exception while serializing json object to '{}'", dto), error)
45
+ end
46
+
47
+ def request_interceptor_exception(error)
48
+ log_error("008", "Exception while executing request interceptor: {}", error)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../exceptions"
4
+
5
+ module Operaton
6
+ module Bpm
7
+ module Client
8
+ module Impl
9
+ # Mirrors org.operaton.bpm.client.impl.EngineRestExceptionDto
10
+ class EngineRestExceptionDto
11
+ attr_accessor :message, :type, :code
12
+
13
+ def self.from_json(hash)
14
+ dto = new
15
+ dto.message = hash["message"]
16
+ dto.type = hash["type"]
17
+ dto.code = hash["code"]
18
+ dto
19
+ end
20
+
21
+ def to_rest_exception
22
+ RestException.new(message, type, code)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end