Skip to content
This repository was archived by the owner on Nov 9, 2022. It is now read-only.

Commit cbbf2d9

Browse files
committed
Fixes #21
1 parent ccd9fbc commit cbbf2d9

22 files changed

+413
-56
lines changed

data/restapi.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
{"name": "processinbox", "methods": ["GET"]},
77
{"name": "processqueue", "methods": ["GET"]},
88
{"name": "processmodel", "methods": ["GET","POST","PUT"]},
9-
{"name": "processsubscription", "methods": ["PUT"]}
9+
{"name": "processsubscription", "methods": ["PUT"]},
10+
{"name": "processsearch", "methods": ["PUT"]}
1011
],
1112
"transforms": [],
1213
"triggers": [

documentation/DEVELOPER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ Here is the developer documentation
22

33
- [Adding a new Activity type](DEV-NEWACTIVITY.md)
44
- [Creating a custom step processor](DEV-STEPPROC.md)
5-
- [Integrating your app to MarkLogic Workflow](./RESTAPI.md)
5+
- [Integrating your app to MarkLogic Workflow](RESTAPI.md)
66

77
Any issues contact me at [email protected]

documentation/RESTAPI.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22

3-
See also MODELLING.md for the REST API to install a MarkLogic workflow process model.
3+
See also the [Modelling Overview](MODELLING.md) for the REST API to install a MarkLogic workflow process model.
44

55

66
## How the workflow engine works

documentation/SPRINTS.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ steps if a duplicate is found. Requires search (by property hash AND not (same u
3434

3535
## Sprint 1 - Basic workflow
3636

37+
Completed Sat 18 Apr 2015 14:30 BST by Adam Fowler
38+
3739
- DONE BPMN2: generic blank task
3840
- DONE BPMN2: user task -> aka human step
3941
- DONE BPMN2: exclusive gateway -> Decision point with one outcome, multiple options
@@ -50,8 +52,8 @@ steps if a duplicate is found. Requires search (by property hash AND not (same u
5052
- DONE Tools: Process Data model XSD (for modeler import)
5153
- DONE Tools: Eclipse BPMN 2 Modeler Palette and Process diagram support, including new diagram creation for MarkLogic
5254
- DEFERRED UI: Ridiculously basic HTML widget in MLJS for rendering step and choosing action (for ease of testing)
53-
- TEST Start process using an Alert (content subscription)
54-
- TEST REST API: Basic process initiation, update and tracking methods
55+
- DONE Start process using an Alert (content subscription)
56+
- DONE REST API: Basic process initiation, update and tracking methods
5557
- DONE processmodel.xqy
5658
- DONE PUT create and publish process model, accepting BPMN2 content type .bpmn2, and to update process model without publishing
5759
- DONE GET to fetch process model
@@ -60,8 +62,8 @@ steps if a duplicate is found. Requires search (by property hash AND not (same u
6062
- DONE PUT create instance of a process (starts a process)
6163
- DONE POST complete a human task
6264
- DONE GET fetch the current state of a business process
63-
- TEST processsubscription.xqy
64-
- TEST PUT create a process subscription (alert) to create a new process instance (creating a content doc creates a process doc with an initiating attachment)
65+
- DONE processsubscription.xqy
66+
- DONE PUT create a process subscription (alert) to create a new process instance (creating a content doc creates a process doc with an initiating attachment)
6567
- DONE processinbox.xqy
6668
- DONE processqueue.xqy
6769
- TEST support for roles (processroleinbox.xqy) on user tasks (For BD)

documentation/USING-SUBSCRIPTIONS.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ The above is why I decided to have a process document that CPF runs against, rat
2121
## How it works
2222

2323
1. A user, or application, creates a new document in MarkLogic - say a Bank Account Opening E-form
24-
2. A 'Process Subscription' (basically a MarkLogic alert that uses the alert-action-process.xqy action) fires, creating a new Account Opening process document
25-
3. A CPF Domain (set up by enabling/publishing a MarkLogic Workflow BPMN2 model) fires for this document, invoking the Account Opening V1.2 CPF pipeline process
24+
2. A 'Process Subscription' (basically a MarkLogic alert that uses the alert-action-process.xqy action) fires, creating a new Account Opening process document, and attaching the account opening form as the InitiatingAttachment attachment
25+
3. A CPF Domain (set up by enabling/publishing a MarkLogic Workflow BPMN2 model) fires for this process document, invoking the Account Opening V1.2 CPF pipeline process
2626
4. CPF manages the state transitions and execution of BPMN2 CPF actions throughout the process lifecycle
2727
5. Process eventually completes (or fails), leaving the process document as an audit record of what happened, with metrics for performance analysis
2828

@@ -39,13 +39,19 @@ Several reasons.
3939
## Isn't it a bit convoluted?
4040

4141
If you had to create all these things by hand, yes. This is why the Workflow REST API provides a single endpoint
42-
(POST /v1/resource/process) to take a BPMN2 model, create a set of CPF pipelines, and create a
42+
(POST /v1/resources/process) to take a BPMN2 model, create a set of CPF pipelines, and create a
4343
domain configuration, for you.
4444

4545
All you then need to do then is separately configure one or more Alerts (aka Process Subscriptions) via calls to
46-
PUT /v1/resource/processsubscription to start the relevant workflow based on criteria about a new or updated document
46+
PUT /v1/resources/processsubscription to start the relevant workflow based on criteria about a new or updated document
4747
in marklogic.
4848

49-
Alternatively, manually start a process via PUT /v1/resource/process without needing a Process Subscription. This is useful
49+
Alternatively, manually start a process via PUT /v1/resources/process without needing a Process Subscription. This is useful
5050
if starting a process via an ESB or application. A good example is starting a process to tell a person to create a
5151
new document that doesn't exist yet.
52+
53+
## Examples
54+
55+
For examples of how to configure and call the REST extensions for alerting, see the /shtests/ folder, specifically:-
56+
- Automated start: 25-payload.xml and 25-processsubscription-create.sh
57+
- Manual start: 06-payload.xml 06-process-create.sh

documentation/USING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ This section of the documentation is very much a work in progress. See the [REST
77
details right now.
88

99
- [Creating a Process Subscription](USING-SUBSCRIPTIONS.md)
10-
- Starting a process without a content document
10+
- Starting a process without a content document - see the above link, last paragraph

modules/app/models/alert-action-process.xqy

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@ declare variable $alert:action as element(alert:action) external;
2121
(: Create a process instance for this document :)
2222

2323
(: Find appropriate process from alert action option :)
24-
let $procname := $alert-action/alert:options/wf:process-name/text()
25-
return
26-
wfu:create($procname,$alert:doc/element(),
27-
<attachment name="InitiatingAttachment" cardinality="1">
28-
<uri>{fn:base-uri($alert:doc)}</uri>
29-
</attachment>
24+
let $procname := xs:string($alert:action/alert:options/wf:process-name)
25+
let $pid := wfu:create($procname,$alert:doc/element(),
26+
(<wf:attachment name="InitiatingAttachment" uri="{fn:base-uri($alert:doc)}" cardinality="1"/>)
3027
)
28+
return ()
29+
3130

3231
(:)
3332
xdmp:document-insert($procname || "/" || sem:uuid-string() || ".xml",

modules/app/models/lib-search-subscribe.xqy

Lines changed: 122 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -252,20 +252,26 @@ declare function ss:create-rule-notify($alert-name as xs:string,$alert-detail as
252252

253253

254254

255-
declare function ss:add-alert($shortname as xs:string,$query as element(cts:query),
255+
declare function ss:add-alert($shortname as xs:string,$query as cts:query,
256256
$ruleoptions as element()*,$module as xs:string,$moduledbname as xs:string?,$actionoptions as element()*) as xs:string {
257257

258258
let $name := ss:do-create-config($shortname)
259-
return
260-
(
261-
ss:do-create-rule($name,$query,$ruleoptions),
262-
ss:do-create-action($name,$module,$moduledbname,$actionoptions),
263-
$name
264-
)
259+
let $_ := (
260+
ss:do-create-action($name,$module,$moduledbname,$actionoptions)
261+
,
262+
ss:do-create-rule($name,$query,$ruleoptions)
263+
)
264+
return $name
265+
};
266+
267+
declare function ss:get-alert($shortname as xs:string) as element(alert:config)? {
268+
alert:config-get($shortname)
265269
};
266270

267271

268272
declare function ss:do-create-config($shortname as xs:string) as xs:string {
273+
let $rem := ss:check-remove-config($shortname)
274+
return
269275
xdmp:eval(
270276
'xquery version "1.0-ml"; declare namespace my="http://marklogic.com/alerts"; ' ||
271277
'import module namespace ah = "http://marklogic.com/search/subscribe" at "/app/models/lib-search-subscribe.xqy";' ||
@@ -277,16 +283,16 @@ declare function ss:do-create-config($shortname as xs:string) as xs:string {
277283
)
278284
};
279285

280-
declare function ss:do-create-rule($alert-name as xs:string,$query as cts:query,$options as element()*) {
286+
declare function ss:do-create-rule($alert-name as xs:string,$query as cts:query,$options as element()*) as empty-sequence() {
281287
xdmp:eval(
282288
'xquery version "1.0-ml"; declare namespace my="http://marklogic.com/alerts"; ' ||
283289
'import module namespace ah = "http://marklogic.com/search/subscribe" at "/app/models/lib-search-subscribe.xqy";' ||
284290
'import module namespace alert="http://marklogic.com/xdmp/alert" at "/MarkLogic/alert.xqy";' ||
285291
'declare variable $my:alert-name as xs:string external;' ||
286292
'declare variable $my:query as cts:query external;' ||
287-
'declare variable $my:options as element* external;' ||
288-
'ah:create-rule($my:alert-name,$my:query,$my:options)',
289-
(xs:QName("my:alert-name"),$alert-name,xs:QName("my:query"),$query,xs:QName("my:options"),$options),
293+
(:)'declare variable $my:options as element()* external;' ||:)
294+
'ah:create-rule($my:alert-name,$my:query,())',
295+
(xs:QName("my:alert-name"),$alert-name,xs:QName("my:query"),$query (:,xs:QName("my:options"),($options) :) ),
290296
<options xmlns="xdmp:eval"><isolation>different-transaction</isolation></options>
291297
)
292298
};
@@ -299,13 +305,41 @@ declare function ss:do-create-action($alert-name as xs:string,$alert-module as x
299305
'declare variable $my:alert-name as xs:string external;' ||
300306
'declare variable $my:alert-module as xs:string external;' ||
301307
'declare variable $my:dbname as xs:string external;' ||
302-
'declare variable $my:options as element* external;' ||
308+
'declare variable $my:options as element()* external;' ||
303309
'ah:create-action($my:alert-name,$my:alert-module,$my:dbname,$my:options)',
304-
(xs:QName("my:alert-name"),$alert-name,xs:QName("my:alert-module"),$alert-module,xs:QName("my:dbname"),$dbname,xs:QName("my:options"),$options),
310+
(xs:QName("my:alert-name"),$alert-name,xs:QName("my:alert-module"),$alert-module,xs:QName("my:dbname"),$dbname,xs:QName("my:options"),($options)),
305311
<options xmlns="xdmp:eval"><isolation>different-transaction</isolation></options>
306312
)
307313
};
308314

315+
declare function ss:check-remove-config($alert-name as xs:string) {
316+
let $config := alert:config-get($alert-name)
317+
return
318+
if (fn:not(fn:empty($config))) then
319+
(: Check if config used in a cpf domain, if so remove it from that domain :)
320+
let $unreg := xdmp:eval(
321+
'xquery version "1.0-ml"; declare namespace my="http://marklogic.com/alerts"; ' ||
322+
'import module namespace alert="http://marklogic.com/xdmp/alert" at "/MarkLogic/alert.xqy";' ||
323+
'declare variable $my:alert-name as xs:string external;' ||
324+
'alert:config-insert(' ||
325+
' alert:config-set-cpf-domain-names(alert:config-get($my:alert-name),())' ||
326+
')',
327+
(xs:QName("my:alert-name"),$alert-name),
328+
<options xmlns="xdmp:eval"><isolation>different-transaction</isolation></options>
329+
)
330+
(: Do this for each domain - NA all done in one hit:)
331+
(: Now remove the alert config :)
332+
return xdmp:eval(
333+
'xquery version "1.0-ml"; declare namespace my="http://marklogic.com/alerts"; ' ||
334+
'import module namespace alert="http://marklogic.com/xdmp/alert" at "/MarkLogic/alert.xqy";' ||
335+
'declare variable $my:alert-name as xs:string external;' ||
336+
'alert:config-delete($my:alert-name)',
337+
(xs:QName("my:alert-name"),$alert-name),
338+
<options xmlns="xdmp:eval"><isolation>different-transaction</isolation></options>
339+
)
340+
else ()
341+
};
342+
309343
declare function ss:create-config($shortname as xs:string) as xs:string {
310344
(: add alert :)
311345
let $alert-name := "/config/alerts/" || $shortname
@@ -318,7 +352,7 @@ declare function ss:create-config($shortname as xs:string) as xs:string {
318352
return $alert-name
319353
};
320354

321-
declare function ss:create-rule($alert-name as xs:string,$query as cts:query,$options as element()*) {
355+
declare function ss:create-rule($alert-name as xs:string,$query as cts:query,$options as element()*) as empty-sequence() {
322356

323357
let $rule := alert:make-rule(
324358
fn:concat($alert-name,"-rule"),
@@ -347,7 +381,6 @@ declare function ss:create-action($alert-name as xs:string,$alert-module as xs:s
347381

348382

349383

350-
(: TODO add method for creating cpf domain, and enabling cpf on the database automatically - see 8001 admin code :)
351384

352385

353386
declare function ss:cpf-enable($alert-name as xs:string,$cpf-domain as xs:string) {
@@ -356,3 +389,77 @@ declare function ss:cpf-enable($alert-name as xs:string,$cpf-domain as xs:string
356389
alert:config-get($alert-name),
357390
($cpf-domain)))
358391
};
392+
393+
declare function ss:create-domain($domainname as xs:string,$domaintype as xs:string,$domainpath as xs:string,$domaindepth as xs:string,$pipeline-names as xs:string*,$modulesdb as xs:string) as xs:unsignedLong {
394+
395+
(: check if domain already exists and recreate :)
396+
let $remove :=
397+
try {
398+
if (fn:not(fn:empty(
399+
xdmp:eval(
400+
'xquery version "1.0-ml";declare namespace m="http://marklogic.com/alerts"; import module namespace dom = "http://marklogic.com/cpf/domains" at "/MarkLogic/cpf/domains.xqy";declare variable $m:processmodeluri as xs:string external; dom:get($m:processmodeluri)'
401+
,
402+
(xs:QName("my:processmodeluri"),$domainname),
403+
<options xmlns="xdmp:eval">
404+
<database>{xdmp:triggers-database()}</database>
405+
<isolation>different-transaction</isolation>
406+
</options>
407+
)
408+
))) then
409+
let $_ := xdmp:log(" GOT DOMAIN TO REMOVE")
410+
return
411+
xdmp:eval(
412+
'xquery version "1.0-ml";declare namespace m="http://marklogic.com/alerts"; import module namespace dom = "http://marklogic.com/cpf/domains" at "/MarkLogic/cpf/domains.xqy";declare variable $m:processmodeluri as xs:string external;'
413+
||
414+
'dom:remove($m:processmodeluri)'
415+
,
416+
(xs:QName("my:processmodeluri"),$domainname),
417+
<options xmlns="xdmp:eval">
418+
<database>{xdmp:triggers-database()}</database>
419+
<isolation>different-transaction</isolation>
420+
</options>
421+
)
422+
else
423+
()
424+
} catch ($e) { ( xdmp:log("Error trying to remove domain: " || $domainname),xdmp:log($e) ) } (: catching domain throwing error if it doesn't exist. We can safely ignore this :)
425+
426+
427+
(: Create domain :)
428+
429+
(: Configure domain :)
430+
return
431+
xdmp:eval(
432+
'xquery version "1.0-ml";declare namespace m="http://marklogic.com/alerts";' ||
433+
'import module namespace p="http://marklogic.com/cpf/pipelines" at "/MarkLogic/cpf/pipelines.xqy"; '||
434+
'import module namespace dom = "http://marklogic.com/cpf/domains" at "/MarkLogic/cpf/domains.xqy"; ' ||
435+
'declare variable $m:pname as xs:string external;declare variable $m:pnames as xs:string* external;' ||
436+
'declare variable $m:mdb as xs:unsignedLong external;'||
437+
'declare variable $m:type as xs:string external;' ||
438+
'declare variable $m:path as xs:string external;' ||
439+
'declare variable $m:otherpipeline as xs:string external;' ||
440+
'declare variable $m:depth as xs:string external;' ||
441+
'let $_ := xdmp:log("In eval") ' ||
442+
(:)'let $pids := for $pn in $m:pnames ' ||
443+
' return xs:unsignedLong(p:pipelines()[p:pipeline-name = $pn]/p:pipeline-id) ' || :)
444+
'let $pids := ' ||
445+
'(xs:unsignedLong(p:pipelines()[p:pipeline-name = "Status Change Handling"]/p:pipeline-id),xs:unsignedLong(p:pipelines()[p:pipeline-name = $m:otherpipeline]/p:pipeline-id))' ||
446+
'let $_ := xdmp:log("second point")' ||
447+
'let $ds := dom:domain-scope($m:type,$m:path,$m:depth) ' ||
448+
'let $_ := xdmp:log("third point")' ||
449+
'let $ec := dom:evaluation-context($m:mdb,"/")' ||
450+
'let $_ := xdmp:log("fourth point")' ||
451+
'let $dc := dom:create($m:pname,"Domain for "||$m:pname,' ||
452+
' $ds,$ec,$pids,())' ||
453+
'let $_ := xdmp:log("fifth point")' ||
454+
'return $dc'
455+
,
456+
(xs:QName("my:otherpipeline"),($pipeline-names[2]),xs:QName("my:mdb"),xdmp:database($modulesdb),xs:QName("my:pname"),$domainname,
457+
xs:QName("my:type"),$domaintype,xs:QName("my:path"),$domainpath,xs:QName("my:depth"),$domaindepth
458+
),
459+
<options xmlns="xdmp:eval">
460+
<database>{xdmp:triggers-database()}</database>
461+
<isolation>different-transaction</isolation>
462+
</options>
463+
) (: end eval :)
464+
465+
};

modules/app/models/workflow-util.xqy

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,28 @@ declare function m:create($pipelineName as xs:string,$data as element()*,$attach
3333
return $id
3434
};
3535

36+
(:
37+
: You must create one or more CPF domains for folders or collections that alerts can be evaluated within.
38+
:)
39+
declare function m:createAlertingDomain($name as xs:string,$type as xs:string,$path as xs:string,$depth as xs:string) as xs:unsignedLong {
40+
ss:create-domain($name,$type,$path,$depth,("Status Change Handling","Alerting"),xdmp:database-name(xdmp:modules-database()))
41+
};
42+
3643
(:
3744
: Create a new process subscription
3845
:)
39-
declare function m:createSubscription($pipelineName as xs:string,$name as xs:string,$query as element(cts:query)) as xs:string {
40-
ss:add-alert($name,$query,(),"/app/models/alert-action-process.xqy",(),(<wf:process-name>{$pipelineName}</wf:process-name>))
46+
declare function m:createSubscription($pipelineName as xs:string,$name as xs:string,$domainname as xs:string,$query as cts:query) as xs:string {
47+
let $alert-uri := ss:add-alert($name,$query,(),"/app/models/alert-action-process.xqy",xdmp:database-name(xdmp:modules-database()),
48+
(<wf:process-name>{$pipelineName}</wf:process-name>))
49+
let $alert-enabled := ss:cpf-enable($alert-uri,$domainname)
50+
return $alert-uri
51+
};
52+
53+
(:
54+
: Fetches a process subscription
55+
:)
56+
declare function m:getSubscription($name as xs:string) as element()? {
57+
ss:get-alert("/config/alerts/" || $name)
4158
};
4259

4360
(:
@@ -151,7 +168,27 @@ declare function m:roleinbox($role as xs:string) as element(wf:queue) {
151168
</wf:queue>
152169
};
153170

154-
171+
(:
172+
: Lists all processes, or all those with a specific PROCESS__MAJOR__MINOR name
173+
:)
174+
declare function m:list($processName as xs:string?) as element(wf:list) {
175+
<wf:list>
176+
{
177+
for $process in cts:search(fn:collection("http://marklogic.com/workflow/processes"),
178+
cts:and-query(
179+
if (fn:not(fn:empty($processName))) then
180+
cts:element-attribute-value-query(xs:QName("wf:process"),xs:QName("title"),$processName)
181+
else
182+
cts:not-query(())
183+
),("unfiltered") (: TODO ordering, prioritisation support, and so on :)
184+
)
185+
return
186+
<wf:listitem processid="{xs:string($process/wf:process/@id)}">
187+
{$process}
188+
</wf:listitem>
189+
}
190+
</wf:list>
191+
};
155192

156193

157194

0 commit comments

Comments
 (0)