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.
- checksums.yaml +7 -0
- data/README.md +134 -0
- data/lib/operaton/bpm/client/backoff/backoff_strategy.rb +20 -0
- data/lib/operaton/bpm/client/backoff/error_aware_backoff_strategy.rb +22 -0
- data/lib/operaton/bpm/client/backoff/exponential_backoff_strategy.rb +38 -0
- data/lib/operaton/bpm/client/backoff/exponential_error_backoff_strategy.rb +38 -0
- data/lib/operaton/bpm/client/exceptions.rb +74 -0
- data/lib/operaton/bpm/client/external_task_client.rb +20 -0
- data/lib/operaton/bpm/client/external_task_client_builder.rb +18 -0
- data/lib/operaton/bpm/client/impl/engine_client.rb +132 -0
- data/lib/operaton/bpm/client/impl/engine_client_exception.rb +25 -0
- data/lib/operaton/bpm/client/impl/engine_client_logger.rb +54 -0
- data/lib/operaton/bpm/client/impl/engine_rest_exception_dto.rb +28 -0
- data/lib/operaton/bpm/client/impl/external_task_client_builder_impl.rb +349 -0
- data/lib/operaton/bpm/client/impl/external_task_client_impl.rb +41 -0
- data/lib/operaton/bpm/client/impl/external_task_client_logger.rb +236 -0
- data/lib/operaton/bpm/client/impl/object_mapper.rb +44 -0
- data/lib/operaton/bpm/client/impl/permanent_url_resolver.rb +22 -0
- data/lib/operaton/bpm/client/impl/request_dto.rb +24 -0
- data/lib/operaton/bpm/client/impl/request_executor.rb +143 -0
- data/lib/operaton/bpm/client/interceptor/auth/basic_auth_provider.rb +42 -0
- data/lib/operaton/bpm/client/interceptor/client_request_context.rb +16 -0
- data/lib/operaton/bpm/client/interceptor/client_request_interceptor.rb +18 -0
- data/lib/operaton/bpm/client/interceptor/impl/client_request_context_impl.rb +28 -0
- data/lib/operaton/bpm/client/interceptor/impl/request_interceptor_handler.rb +43 -0
- data/lib/operaton/bpm/client/spi/data_format.rb +32 -0
- data/lib/operaton/bpm/client/spi/data_format_configurator.rb +31 -0
- data/lib/operaton/bpm/client/spi/data_format_provider.rb +31 -0
- data/lib/operaton/bpm/client/task/external_task.rb +22 -0
- data/lib/operaton/bpm/client/task/external_task_handler.rb +28 -0
- data/lib/operaton/bpm/client/task/external_task_service.rb +18 -0
- data/lib/operaton/bpm/client/task/impl/dto/bpmn_error_request_dto.rb +35 -0
- data/lib/operaton/bpm/client/task/impl/dto/complete_request_dto.rb +33 -0
- data/lib/operaton/bpm/client/task/impl/dto/extend_lock_request_dto.rb +29 -0
- data/lib/operaton/bpm/client/task/impl/dto/failure_request_dto.rb +43 -0
- data/lib/operaton/bpm/client/task/impl/dto/lock_request_dto.rb +29 -0
- data/lib/operaton/bpm/client/task/impl/dto/set_variables_request_dto.rb +29 -0
- data/lib/operaton/bpm/client/task/impl/external_task_impl.rb +128 -0
- data/lib/operaton/bpm/client/task/impl/external_task_service_impl.rb +90 -0
- data/lib/operaton/bpm/client/task/ordering_config.rb +78 -0
- data/lib/operaton/bpm/client/task/sorting_dto.rb +29 -0
- data/lib/operaton/bpm/client/topic/impl/dto/fetch_and_lock_request_dto.rb +47 -0
- data/lib/operaton/bpm/client/topic/impl/dto/fetch_and_lock_response_dto.rb +32 -0
- data/lib/operaton/bpm/client/topic/impl/dto/topic_request_dto.rb +70 -0
- data/lib/operaton/bpm/client/topic/impl/topic_subscription_builder_impl.rb +164 -0
- data/lib/operaton/bpm/client/topic/impl/topic_subscription_impl.rb +76 -0
- data/lib/operaton/bpm/client/topic/impl/topic_subscription_manager.rb +222 -0
- data/lib/operaton/bpm/client/topic/impl/topic_subscription_manager_logger.rb +52 -0
- data/lib/operaton/bpm/client/topic/topic_subscription.rb +20 -0
- data/lib/operaton/bpm/client/topic/topic_subscription_builder.rb +22 -0
- data/lib/operaton/bpm/client/url_resolver.rb +15 -0
- data/lib/operaton/bpm/client/variable/client_values.rb +33 -0
- data/lib/operaton/bpm/client/variable/impl/abstract_typed_value_mapper.rb +59 -0
- data/lib/operaton/bpm/client/variable/impl/default_value_mappers.rb +71 -0
- data/lib/operaton/bpm/client/variable/impl/format/json/json_data_format.rb +82 -0
- data/lib/operaton/bpm/client/variable/impl/format/json/json_data_format_provider.rb +34 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/boolean_value_mapper.rb +35 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/byte_array_value_mapper.rb +53 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/date_value_mapper.rb +61 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/double_value_mapper.rb +36 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/file_value_mapper.rb +73 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/integer_value_mapper.rb +36 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/json_value_mapper.rb +35 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/long_value_mapper.rb +36 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/null_value_mapper.rb +45 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/number_value_mapper.rb +25 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/object_value_mapper.rb +151 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/primitive_value_mapper.rb +44 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/short_value_mapper.rb +36 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/string_value_mapper.rb +35 -0
- data/lib/operaton/bpm/client/variable/impl/mapper/xml_value_mapper.rb +35 -0
- data/lib/operaton/bpm/client/variable/impl/type/json_type_impl.rb +24 -0
- data/lib/operaton/bpm/client/variable/impl/type/xml_type_impl.rb +24 -0
- data/lib/operaton/bpm/client/variable/impl/typed_value_field.rb +36 -0
- data/lib/operaton/bpm/client/variable/impl/typed_values.rb +100 -0
- data/lib/operaton/bpm/client/variable/impl/value/deferred_file_value_impl.rb +57 -0
- data/lib/operaton/bpm/client/variable/impl/value/json_value_impl.rb +26 -0
- data/lib/operaton/bpm/client/variable/impl/value/xml_value_impl.rb +26 -0
- data/lib/operaton/bpm/client/variable/impl/value_mapper.rb +42 -0
- data/lib/operaton/bpm/client/variable/impl/value_mappers.rb +26 -0
- data/lib/operaton/bpm/client/variable/impl/variable_value.rb +55 -0
- data/lib/operaton/bpm/client/variable/value/deferred_file_value.rb +19 -0
- data/lib/operaton/bpm/client/variable/value/json_value.rb +16 -0
- data/lib/operaton/bpm/client/variable/value/xml_value.rb +16 -0
- data/lib/operaton/bpm/client/version.rb +9 -0
- data/lib/operaton/bpm/client.rb +121 -0
- data/lib/operaton/bpm/engine/variable/typed_value.rb +153 -0
- data/lib/operaton/bpm/engine/variable/value_type.rb +126 -0
- data/lib/operaton/bpm/engine/variable/variable_map.rb +72 -0
- data/lib/operaton/bpm/engine/variable/variables.rb +197 -0
- data/lib/operaton-bpm-client.rb +3 -0
- 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
|