flowable 1.0.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.
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flowable
4
+ module Resources
5
+ class ProcessInstances < Base
6
+ BASE_PATH = 'service/runtime/process-instances'
7
+
8
+ # List all process instances
9
+ # @param options [Hash] Query parameters
10
+ # @option options [String] :id Filter by instance ID
11
+ # @option options [String] :processDefinitionKey Filter by definition key
12
+ # @option options [String] :processDefinitionId Filter by definition ID
13
+ # @option options [String] :businessKey Filter by business key
14
+ # @option options [String] :involvedUser Filter by involved user
15
+ # @option options [Boolean] :suspended Filter suspended instances
16
+ # @option options [String] :tenantId Filter by tenant
17
+ # @return [Hash] Paginated list of process instances
18
+ def list(**options)
19
+ params = paginate_params(options)
20
+ %i[id processDefinitionKey processDefinitionId businessKey
21
+ involvedUser superProcessInstanceId tenantId tenantIdLike].each do |key|
22
+ params[key] = options[key] if options[key]
23
+ end
24
+ params[:suspended] = options[:suspended] if options.key?(:suspended)
25
+ params[:withoutTenantId] = options[:withoutTenantId] if options.key?(:withoutTenantId)
26
+ params[:includeProcessVariables] = options[:includeProcessVariables] if options.key?(:includeProcessVariables)
27
+
28
+ client.get(BASE_PATH, params)
29
+ end
30
+
31
+ # Get a specific process instance
32
+ # @param process_instance_id [String] The process instance ID
33
+ # @return [Hash] Process instance details
34
+ def get(process_instance_id)
35
+ client.get("#{BASE_PATH}/#{process_instance_id}")
36
+ end
37
+
38
+ # Start a new process instance by process definition ID
39
+ # @param process_definition_id [String] The process definition ID
40
+ # @param variables [Hash] Optional variables (name => value)
41
+ # @param business_key [String] Optional business key
42
+ # @param return_variables [Boolean] Return variables in response
43
+ # @return [Hash] Created process instance
44
+ def start_by_id(process_definition_id, variables: {}, business_key: nil, return_variables: false)
45
+ body = { processDefinitionId: process_definition_id }
46
+ body[:businessKey] = business_key if business_key
47
+ body[:variables] = build_variables_array(variables) unless variables.empty?
48
+ body[:returnVariables] = return_variables if return_variables
49
+
50
+ client.post(BASE_PATH, body)
51
+ end
52
+
53
+ # Start a new process instance by process definition key
54
+ # @param process_definition_key [String] The process definition key
55
+ # @param variables [Hash] Optional variables (name => value)
56
+ # @param business_key [String] Optional business key
57
+ # @param tenant_id [String] Optional tenant ID
58
+ # @param return_variables [Boolean] Return variables in response
59
+ # @return [Hash] Created process instance
60
+ def start_by_key(process_definition_key, variables: {}, business_key: nil, tenant_id: nil,
61
+ return_variables: false)
62
+ body = { processDefinitionKey: process_definition_key }
63
+ body[:businessKey] = business_key if business_key
64
+ body[:tenantId] = tenant_id if tenant_id
65
+ body[:variables] = build_variables_array(variables) unless variables.empty?
66
+ body[:returnVariables] = return_variables if return_variables
67
+
68
+ client.post(BASE_PATH, body)
69
+ end
70
+
71
+ # Delete a process instance
72
+ # @param process_instance_id [String] The process instance ID
73
+ # @param delete_reason [String] Reason for deletion
74
+ # @return [Boolean] true if successful
75
+ def delete(process_instance_id, delete_reason: nil)
76
+ params = {}
77
+ params[:deleteReason] = delete_reason if delete_reason
78
+ client.delete("#{BASE_PATH}/#{process_instance_id}", params)
79
+ end
80
+
81
+ # Suspend a process instance
82
+ # @param process_instance_id [String] The process instance ID
83
+ # @return [Hash] Updated process instance
84
+ def suspend(process_instance_id)
85
+ client.put("#{BASE_PATH}/#{process_instance_id}", { action: 'suspend' })
86
+ end
87
+
88
+ # Activate a suspended process instance
89
+ # @param process_instance_id [String] The process instance ID
90
+ # @return [Hash] Updated process instance
91
+ def activate(process_instance_id)
92
+ client.put("#{BASE_PATH}/#{process_instance_id}", { action: 'activate' })
93
+ end
94
+
95
+ # Query process instances with complex filters
96
+ # @param query [Hash] Query body with filters and variable conditions
97
+ # @return [Hash] Paginated list of process instances
98
+ def query(query)
99
+ client.post('service/query/process-instances', query)
100
+ end
101
+
102
+ # Get the diagram/image for a process instance
103
+ # @param process_instance_id [String] The process instance ID
104
+ # @return [String] Binary image data
105
+ def diagram(process_instance_id)
106
+ client.get("#{BASE_PATH}/#{process_instance_id}/diagram")
107
+ end
108
+
109
+ # --- Identity Links ---
110
+
111
+ # Get involved people for a process instance
112
+ # @param process_instance_id [String] The process instance ID
113
+ # @return [Array<Hash>] List of identity links
114
+ def identity_links(process_instance_id)
115
+ client.get("#{BASE_PATH}/#{process_instance_id}/identitylinks")
116
+ end
117
+
118
+ # Add an involved user to a process instance
119
+ # @param process_instance_id [String] The process instance ID
120
+ # @param user_id [String] The user ID
121
+ # @param type [String] Type of involvement (e.g., 'participant')
122
+ # @return [Hash] Created identity link
123
+ def add_involved_user(process_instance_id, user_id, type: 'participant')
124
+ client.post(
125
+ "#{BASE_PATH}/#{process_instance_id}/identitylinks",
126
+ { userId: user_id, type: type }
127
+ )
128
+ end
129
+
130
+ # Remove an involved user from a process instance
131
+ # @param process_instance_id [String] The process instance ID
132
+ # @param user_id [String] The user ID
133
+ # @param type [String] Type of involvement
134
+ # @return [Boolean] true if successful
135
+ def remove_involved_user(process_instance_id, user_id, type)
136
+ client.delete("#{BASE_PATH}/#{process_instance_id}/identitylinks/users/#{user_id}/#{type}")
137
+ end
138
+
139
+ # --- Variables ---
140
+
141
+ # Get all variables for a process instance
142
+ # @param process_instance_id [String] The process instance ID
143
+ # @return [Array<Hash>] List of variables
144
+ def variables(process_instance_id)
145
+ client.get("#{BASE_PATH}/#{process_instance_id}/variables")
146
+ end
147
+
148
+ # Get a specific variable from a process instance
149
+ # @param process_instance_id [String] The process instance ID
150
+ # @param variable_name [String] The variable name
151
+ # @return [Hash] Variable details
152
+ def variable(process_instance_id, variable_name)
153
+ client.get("#{BASE_PATH}/#{process_instance_id}/variables/#{variable_name}")
154
+ end
155
+
156
+ # Create variables on a process instance (fails if exists)
157
+ # @param process_instance_id [String] The process instance ID
158
+ # @param variables [Hash] Variables to create (name => value)
159
+ # @return [Array<Hash>] Created variables
160
+ def create_variables(process_instance_id, variables)
161
+ client.post(
162
+ "#{BASE_PATH}/#{process_instance_id}/variables",
163
+ build_variables_array(variables)
164
+ )
165
+ end
166
+
167
+ # Create or update variables on a process instance
168
+ # @param process_instance_id [String] The process instance ID
169
+ # @param variables [Hash] Variables to set (name => value)
170
+ # @return [Array<Hash>] Updated variables
171
+ def set_variables(process_instance_id, variables)
172
+ client.put(
173
+ "#{BASE_PATH}/#{process_instance_id}/variables",
174
+ build_variables_array(variables)
175
+ )
176
+ end
177
+
178
+ # Update a single variable on a process instance
179
+ # @param process_instance_id [String] The process instance ID
180
+ # @param name [String] Variable name
181
+ # @param value [Object] Variable value
182
+ # @param type [String] Optional explicit type
183
+ # @return [Hash] Updated variable
184
+ def update_variable(process_instance_id, name, value, type: nil)
185
+ body = { name: name, value: value }
186
+ body[:type] = type || infer_type(value)
187
+
188
+ client.put("#{BASE_PATH}/#{process_instance_id}/variables/#{name}", body)
189
+ end
190
+
191
+ # Delete a variable from a process instance
192
+ # @param process_instance_id [String] The process instance ID
193
+ # @param variable_name [String] The variable name
194
+ # @return [Boolean] true if successful
195
+ def delete_variable(process_instance_id, variable_name)
196
+ client.delete("#{BASE_PATH}/#{process_instance_id}/variables/#{variable_name}")
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flowable
4
+ module Resources
5
+ class Tasks < Base
6
+ BASE_PATH = 'cmmn-runtime/tasks'
7
+
8
+ # List all tasks
9
+ # @param options [Hash] Query parameters
10
+ # @option options [String] :name Filter by exact name
11
+ # @option options [String] :nameLike Filter by name pattern
12
+ # @option options [String] :assignee Filter by assignee
13
+ # @option options [String] :owner Filter by owner
14
+ # @option options [String] :candidateUser Tasks claimable by user
15
+ # @option options [String] :candidateGroup Tasks claimable by group
16
+ # @option options [String] :caseInstanceId Filter by case instance
17
+ # @option options [String] :caseDefinitionId Filter by case definition
18
+ # @option options [Boolean] :unassigned Only unassigned tasks
19
+ # @option options [Boolean] :active Only active tasks
20
+ # @option options [String] :tenantId Filter by tenant
21
+ # @return [Hash] Paginated list of tasks
22
+ def list(**options)
23
+ params = paginate_params(options)
24
+ %i[name nameLike description assignee assigneeLike owner ownerLike
25
+ candidateUser candidateGroup candidateGroups involvedUser
26
+ taskDefinitionKey taskDefinitionKeyLike caseInstanceId
27
+ caseDefinitionId tenantId tenantIdLike category].each do |key|
28
+ params[key] = options[key] if options[key]
29
+ end
30
+
31
+ # Integer/Date filters
32
+ params[:priority] = options[:priority] if options[:priority]
33
+ params[:minimumPriority] = options[:minimumPriority] if options[:minimumPriority]
34
+ params[:maximumPriority] = options[:maximumPriority] if options[:maximumPriority]
35
+
36
+ # Boolean filters
37
+ %i[unassigned active excludeSubTasks withoutDueDate
38
+ includeTaskLocalVariables withoutTenantId].each do |key|
39
+ params[key] = options[key] if options.key?(key)
40
+ end
41
+
42
+ # Date filters
43
+ %i[createdOn createdBefore createdAfter dueOn dueBefore dueAfter].each do |key|
44
+ params[key] = format_date(options[key]) if options[key]
45
+ end
46
+
47
+ client.get(BASE_PATH, params)
48
+ end
49
+
50
+ # Get a specific task
51
+ # @param task_id [String] The task ID
52
+ # @return [Hash] Task details
53
+ def get(task_id)
54
+ client.get("#{BASE_PATH}/#{task_id}")
55
+ end
56
+
57
+ # Update a task
58
+ # @param task_id [String] The task ID
59
+ # @param attributes [Hash] Attributes to update
60
+ # @option attributes [String] :name Task name
61
+ # @option attributes [String] :description Task description
62
+ # @option attributes [String] :assignee Assignee user ID
63
+ # @option attributes [String] :owner Owner user ID
64
+ # @option attributes [Integer] :priority Priority (default: 50)
65
+ # @option attributes [String] :dueDate Due date (ISO-8601)
66
+ # @option attributes [String] :category Task category
67
+ # @return [Hash] Updated task
68
+ def update(task_id, **attributes)
69
+ body = {}
70
+ %i[name description assignee owner parentTaskId category
71
+ formKey delegationState tenantId].each do |key|
72
+ body[key] = attributes[key] if attributes.key?(key)
73
+ end
74
+ body[:priority] = attributes[:priority] if attributes[:priority]
75
+ body[:dueDate] = format_date(attributes[:dueDate]) if attributes[:dueDate]
76
+
77
+ client.put("#{BASE_PATH}/#{task_id}", body)
78
+ end
79
+
80
+ # Delete a task
81
+ # @param task_id [String] The task ID
82
+ # @param cascade_history [Boolean] Also delete historic task
83
+ # @param delete_reason [String] Reason for deletion
84
+ # @return [Boolean] true if successful
85
+ def delete(task_id, cascade_history: false, delete_reason: nil)
86
+ params = {}
87
+ params[:cascadeHistory] = cascade_history if cascade_history
88
+ params[:deleteReason] = delete_reason if delete_reason
89
+
90
+ client.delete("#{BASE_PATH}/#{task_id}", params)
91
+ end
92
+
93
+ # --- Task Actions ---
94
+
95
+ # Complete a task
96
+ # @param task_id [String] The task ID
97
+ # @param variables [Hash] Optional variables to set (name => value)
98
+ # @param outcome [String] Optional outcome
99
+ # @return [Hash] Response
100
+ def complete(task_id, variables: {}, outcome: nil)
101
+ body = { action: 'complete' }
102
+ body[:variables] = build_variables_array(variables) unless variables.empty?
103
+ body[:outcome] = outcome if outcome
104
+
105
+ client.post("#{BASE_PATH}/#{task_id}", body)
106
+ end
107
+
108
+ # Claim a task
109
+ # @param task_id [String] The task ID
110
+ # @param assignee [String] User to assign the task to
111
+ # @return [Hash] Response
112
+ def claim(task_id, assignee)
113
+ client.post("#{BASE_PATH}/#{task_id}", { action: 'claim', assignee: assignee })
114
+ end
115
+
116
+ # Unclaim a task (set assignee to null)
117
+ # @param task_id [String] The task ID
118
+ # @return [Hash] Response
119
+ def unclaim(task_id)
120
+ client.post("#{BASE_PATH}/#{task_id}", { action: 'claim', assignee: nil })
121
+ end
122
+
123
+ # Delegate a task to another user
124
+ # @param task_id [String] The task ID
125
+ # @param assignee [String] User to delegate to
126
+ # @return [Hash] Response
127
+ def delegate(task_id, assignee)
128
+ client.post("#{BASE_PATH}/#{task_id}", { action: 'delegate', assignee: assignee })
129
+ end
130
+
131
+ # Resolve a delegated task
132
+ # @param task_id [String] The task ID
133
+ # @return [Hash] Response
134
+ def resolve(task_id)
135
+ client.post("#{BASE_PATH}/#{task_id}", { action: 'resolve' })
136
+ end
137
+
138
+ # --- Variables ---
139
+
140
+ # Get all variables for a task
141
+ # @param task_id [String] The task ID
142
+ # @param scope [String] 'local', 'global', or nil for both
143
+ # @return [Array<Hash>] List of variables
144
+ def variables(task_id, scope: nil)
145
+ params = {}
146
+ params[:scope] = scope if scope
147
+ client.get("#{BASE_PATH}/#{task_id}/variables", params)
148
+ end
149
+
150
+ # Get a specific variable from a task
151
+ # @param task_id [String] The task ID
152
+ # @param variable_name [String] The variable name
153
+ # @param scope [String] 'local' or 'global'
154
+ # @return [Hash] Variable details
155
+ def variable(task_id, variable_name, scope: nil)
156
+ params = {}
157
+ params[:scope] = scope if scope
158
+ client.get("#{BASE_PATH}/#{task_id}/variables/#{variable_name}", params)
159
+ end
160
+
161
+ # Create variables on a task
162
+ # @param task_id [String] The task ID
163
+ # @param variables [Hash] Variables to create (name => value)
164
+ # @param scope [String] 'local' or 'global' (default: local)
165
+ # @return [Array<Hash>] Created variables
166
+ def create_variables(task_id, variables, scope: 'local')
167
+ vars = build_variables_array(variables).map { |v| v.merge(scope: scope) }
168
+ client.post("#{BASE_PATH}/#{task_id}/variables", vars)
169
+ end
170
+
171
+ # Set (update or create) variables on a task
172
+ # Updates each variable individually using PUT to /variables/{name}
173
+ # Creates variables that don't exist using POST
174
+ # @param task_id [String] The task ID
175
+ # @param variables [Hash] Variables to set (name => value)
176
+ # @param scope [String] 'local' or 'global' (default: local)
177
+ # @return [Array<Hash>] Updated/created variables
178
+ def set_variables(task_id, variables, scope: 'local')
179
+ results = []
180
+ variables.each do |name, value|
181
+ # Try to update existing variable
182
+ result = update_variable(task_id, name.to_s, value, scope: scope)
183
+ results << result
184
+ rescue Flowable::NotFoundError
185
+ # Variable doesn't exist, create it
186
+ vars = [{ name: name.to_s, value: value, scope: scope, type: infer_type(value) }]
187
+ created = client.post("#{BASE_PATH}/#{task_id}/variables", vars)
188
+ results.concat(created.is_a?(Array) ? created : [created])
189
+ end
190
+ results
191
+ end
192
+
193
+ # Update a variable on a task
194
+ # @param task_id [String] The task ID
195
+ # @param name [String] Variable name
196
+ # @param value [Object] Variable value
197
+ # @param scope [String] 'local' or 'global'
198
+ # @return [Hash] Updated variable
199
+ def update_variable(task_id, name, value, scope: 'local')
200
+ body = { name: name, value: value, scope: scope, type: infer_type(value) }
201
+ client.put("#{BASE_PATH}/#{task_id}/variables/#{name}", body)
202
+ end
203
+
204
+ # Delete a variable from a task
205
+ # @param task_id [String] The task ID
206
+ # @param variable_name [String] The variable name
207
+ # @param scope [String] 'local' or 'global'
208
+ # @return [Boolean] true if successful
209
+ def delete_variable(task_id, variable_name, scope: 'local')
210
+ client.delete("#{BASE_PATH}/#{task_id}/variables/#{variable_name}", { scope: scope })
211
+ end
212
+
213
+ # Delete all local variables from a task
214
+ # @param task_id [String] The task ID
215
+ # @return [Boolean] true if successful
216
+ def delete_all_local_variables(task_id)
217
+ client.delete("#{BASE_PATH}/#{task_id}/variables")
218
+ end
219
+
220
+ # --- Identity Links ---
221
+
222
+ # Get all identity links for a task
223
+ # @param task_id [String] The task ID
224
+ # @return [Array<Hash>] List of identity links
225
+ def identity_links(task_id)
226
+ client.get("#{BASE_PATH}/#{task_id}/identitylinks")
227
+ end
228
+
229
+ # Get identity links for users only
230
+ # @param task_id [String] The task ID
231
+ # @return [Array<Hash>] List of user identity links
232
+ def user_identity_links(task_id)
233
+ client.get("#{BASE_PATH}/#{task_id}/identitylinks/users")
234
+ end
235
+
236
+ # Get identity links for groups only
237
+ # @param task_id [String] The task ID
238
+ # @return [Array<Hash>] List of group identity links
239
+ def group_identity_links(task_id)
240
+ client.get("#{BASE_PATH}/#{task_id}/identitylinks/groups")
241
+ end
242
+
243
+ # Create an identity link (user) on a task
244
+ # @param task_id [String] The task ID
245
+ # @param user_id [String] The user ID
246
+ # @param type [String] Link type (e.g., 'candidate')
247
+ # @return [Hash] Created identity link
248
+ def add_user_identity_link(task_id, user_id, type: 'candidate')
249
+ client.post("#{BASE_PATH}/#{task_id}/identitylinks", { userId: user_id, type: type })
250
+ end
251
+
252
+ # Create an identity link (group) on a task
253
+ # @param task_id [String] The task ID
254
+ # @param group_id [String] The group ID
255
+ # @param type [String] Link type (e.g., 'candidate')
256
+ # @return [Hash] Created identity link
257
+ def add_group_identity_link(task_id, group_id, type: 'candidate')
258
+ client.post("#{BASE_PATH}/#{task_id}/identitylinks", { groupId: group_id, type: type })
259
+ end
260
+
261
+ # Delete an identity link from a task
262
+ # @param task_id [String] The task ID
263
+ # @param family [String] 'users' or 'groups'
264
+ # @param identity_id [String] The user or group ID
265
+ # @param type [String] Link type
266
+ # @return [Boolean] true if successful
267
+ def delete_identity_link(task_id, family, identity_id, type)
268
+ client.delete("#{BASE_PATH}/#{task_id}/identitylinks/#{family}/#{identity_id}/#{type}")
269
+ end
270
+
271
+ private
272
+
273
+ def format_date(date)
274
+ return date if date.is_a?(String)
275
+ return date.iso8601 if date.respond_to?(:iso8601)
276
+
277
+ date.to_s
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flowable
4
+ # Gem version
5
+ VERSION = '1.0.0'
6
+
7
+ # Minimum supported Flowable REST API version
8
+ FLOWABLE_API_VERSION = '7.1.0'
9
+
10
+ # Version information hash
11
+ VERSION_INFO = {
12
+ major: 1,
13
+ minor: 0,
14
+ patch: 0,
15
+ pre: nil
16
+ }.freeze
17
+
18
+ class << self
19
+ # Returns the gem version string
20
+ # @return [String] version string (e.g., "1.0.0")
21
+ def version
22
+ VERSION
23
+ end
24
+
25
+ # Returns full version information
26
+ # @return [Hash] version info hash
27
+ def version_info
28
+ VERSION_INFO
29
+ end
30
+
31
+ # Returns the supported Flowable API version
32
+ # @return [String] Flowable API version
33
+ def flowable_api_version
34
+ FLOWABLE_API_VERSION
35
+ end
36
+ end
37
+ end