/ Development  

Danger; the hidden menace lurking in your "Case Management System"

Hi there AppWorks fans,

Welcome to a new installment of AppWorks tips.

A recurring requirement in every case management project built with the low-code OpenText platform AppWorks; How to find back your cases? When it’s all small you’ll be fine with the standard lists and the filter options, but when it starts to grow you want to have a more mature search possibility. How? Well, again with same entity power where you crafted your case management solution with.


Let get right into it…

Time to build a search functionality for your solution. So, boot it all up, make sure to login and create that first ‘Case’ entity in your favorite workspace and corresponding project (in the ‘entities’ folder). You can use this input for the properties:

Name Label Type Note
case_budget Budget Currency EUR
case_description Description Long text
case_duration Duration Duration Default 1 month
case_end_date End date Date
case_is_active Is active Boolean Only true (as default) or false; as checkbox
case_logo Logo Image
case_name Name Text
case_start_date Start date Date
case_type Type Enumerated text Static: Type1 (t1), Type2 (t2), Type3 (t3)

After this first entity initialization, we do some extra steps:

  1. Follow your naming convention on the building blocks (like the lists, layouts, etc.)
  2. Update the properties of the entity itself (the name and display name!)

search_001

  1. Fine-tune the properties that require extra attention from the notes (in the table above); This makes the entity publishable!
  2. Fine-tune the ‘Create’ form, so it suits your end-user thinking; like this:

search_002

  1. Add the ‘Web service’ BB where you enable the ‘Read’ operation and create a new find_all operation:

search_003

  1. Add the ‘Assignee’ BB and select the current user as assignee during creation (that’s your case owner!)

search_004

  1. Add the ‘Tracking’ BB…Just because we can.
  2. Add a second form frm_view (as copy of the ‘Create’ form) to show some logic information AFTER the creation (like the tracking data and assignee data for example)…Don’t forget to select this one in the layout as well!
  3. Add the ‘Discussions’ BB…Also, because we can; Add the discussions panel to the default layout as well
  4. Same for the ‘Contents’ BB…AND add panel to the layout (as tab next to the discussions panel…see below)
  5. Add a ‘History’ BB; Including a history log saved in the ‘configs’ folder of your project. You can (optionally) add the ‘History’ panel to the layout as well (as third tab…not in the screenshot below!)
  6. Finally, add a basic ‘Lifecycle’ BB with states like ‘Draft’, ‘In review’, and ‘Approved’…Don’t forget to set the primary transition for each flow to define the “happy flow” AND add the progress bar as panel into the layout as well

search_005

FYI: This is the lifecycle with the primary transition checkmarks on the flows:

search_006

I know, it’s a lot of descriptive text with quick screenshots…Comment me if you don’t have a clue on these extra steps!
Why all the BBs? Well, that’s a spoiler question! In the end we’ll do a regular expression match over the data for each case…AHA!

Now, do a first publication, and create some ‘Case’ instances in runtime. This is my view on the first created case:

search_007

Don’t we need an ‘Application Server’ service container? Yes, my friend…Don’t rush things; Always make sure your team can follow you…Lesson #1 of being a great consultant! 😉


The ‘Saved search’ entity implementation

Our second entity is the ‘saved_search’ entity. You can use this table input as a start (saved in the ‘entities’ folder):

Name Label Type
ss_reg_ex Regular Expression Text

We’ll misuse the ‘Title’ BB as the “Name” for our entity; Make sure to set it to manual input with the type option: ‘Provided by the user’!

Also, here some extra steps on the implementation part, but this time a bit more guidance…

  1. Follow your naming convention on the building blocks (like the lists, layouts, etc.)
  2. Update the properties of the entity itself (the name and display name!)
  3. Enhance the ‘Create’ screen and add a hyperlink component with a reference to: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

search_008

You can also add a ‘Text’ component, make it ‘Rich Text’ and add the Hyperlink from there!

  1. Create a new relation ‘toMany’ case

search_009

  1. Add the ‘Web service’ BB and enable the ‘Read’ and ‘AddTo’ case (injected by the previous relationship!)

search_010

  1. Add a new view form frm_view to show the results (the ‘toMany’ relation as grid); AND (like I did), don’t forget to select this form (in the form panel) of the lyt_default layout!

search_011

Tweak the ‘ToManyCase’ grid a little, so you can’t do any actions on it (maybe only the ‘Open’ action)…It’s just a result list (for viewing!)

  1. Finally, create a new ‘onCreate’ event ‘Rule’ BB (named e_oncreate_start_bpm_relate_results) that starts a new BPM with the name bpm_relate_results (saved in the ‘bpms’ folder of our project!); We do the real implementation of the BPM further down the post, but you can already create a dummy BPM with just the start-, one-activity-, and end-construct!

search_012

Now, do a second publication, and create your first ‘Saved search’ instance in runtime. After creation do a check in the Process Instance Manager artifact on the BPM instance…A task to do on your own (or else, comment me)!


The missing service container

Before we move to the BPM implementation, we first create a new service container of type ‘Application Server Connector’. You can use this input during the “Create new service group” wizard from the ‘System Resource Manager’ artifact point of view:

  • Connector type: Application Server Connector
  • Group name: sg_appserver
    • Select the method sets of the ‘Case’ and ‘Saved search’ entities
  • Service name: sc_appserver
    • Startup: automatically
    • Assign to OS (recommended by OT by default for all service containers!)

With this service container supporting our webservice operations of the ‘Case’ and ‘Saved search’ entities, we’re now ready to implement our BPM which will use our exposed entity web service operations!

Did you know that when you create new entities after this service container creation, they will automagically add to this service group as method set (after publication of the entity)!

Do we need a BPM service container as well? Nope, this is only required when you generate a webservice out of a BPM…WHAT? Yes, have a read here.


The advanced BPM logic

The final part of the post where we glue it all together with advanced business logic via a BPM. Building one equals basic programming skills and that’s why I always like to start with pseudocode:

1
2
3
4
5
6
7
SavedSearch savedSearch = readSavedSearch(rootEntityInstanceId);
List<Case> cases = findAll();
for(Case case : cases) {
if(matches(case.text, savedSearch.regEx)) {
addToToManyCase(rootEntityInstanceId, case.itemId);
}
}

Next is the implementation part…Open that already crafted BPM bpm_relate_results and extend it like this:

search_013

Notes:

  • Make sure you attach the flow-arrows correctly to the for-loop.
  • You can create such a for-loop with a right-click on the activity.
  • The webservices on the activities are injected via the right-click menu on an activity.
  • Finally, give a name to the for-loop iterator…A common forgotten thing! AND set the ‘Select Condition’ (see the red arrows from above)

Next is the message mapping (at the bottom of the BPM editor); This is where we connect the dots together for input on the service calls and the output of the service call response. Have a look at the consolidated view for my implementation:

search_014

Notes:

  • Read the screenshot from right (source) to left (target)…Don’t shoot the messenger here; I feel you! 🙄
  • Use your iterator with a replacement of ns5:find_allOutput/ns5:find_allResponse/ns7:case/ns7:case-id/ns7:ItemId/text() with instance:iterator_case/ns7:case-id/ns7:ItemId/text()
  • Also, the last “source” could be instance:instanceProperties/instance:rootEntityInstanceId/text(); It’s the same…Whatever you find convenient.

Almost ready, we miss one special if-statement! We currently attach/relate each case to our search result, but we only want the cases matching our search input (which is our regular expression!). So, we need an ‘Execute Condition’ on the activity within our loop:

search_015

Additional/important notes:

  • Note the renaming of the activities…A good habit for your fellow low-code teammates.
  • For you to copy:
1
2
3
4
nl.bos.appworks.utilities.Utilities.matches (
string(ns2:Readsaved_searchOutput/ns2:Readsaved_searchResponse/ns3:saved_search/ns3:ss_reg_ex/text()),
string(instance:iterator_case)
)
  • You see this is a custom method; You learn about this implementation in this post
  • The code behind this function is simple, and you find it on my GitHub
  • Why not something like this java.util.regex.Pattern.matches('a*b', string('aaaaab'))? Well, this one gives an error as it requires a “CharSequence” object as input and AppWorks is a bit picky on the type of parameters. That’s also why you see me adding the extra string(...) functions in the above xPath! Try it without the function yourself, and make your conclusion…

I also played with the out of the box ‘matches’ String-function like this: matches(instance:iterator_case, ns2:Readsaved_searchOutput/ns2:Readsaved_searchResponse/ns3:saved_search/ns3:ss_reg_ex/text()). Using this method almost cancelled this post as after a tooooo long evening of remote debugging (and learning a lot!), I conclude this is not a RegEx matcher, but more some kind of xPath matcher where I (and others) have no clue how to validly use it!? So, give me a comment when you are the lucky one using this in your project…I love to get a demo to share some knowledge.

Save it, do a publication, and move yourself into runtime to do the ‘Saved search’ entity test with RegEx inputs like:

  • EMPTY input for matching nothing!
  • .* for matching all!
  • .*Draft.* for matching the lifecycle state ‘Draft’.
  • .*Hello world.* matching the description value
  • .*test001.* which matches my ‘case_name’ value
  • .*test00[1-9].* matching all case names starting with ‘test00’ and an extra digit!
  • .*true.* checking the Boolean property.
  • .*t\d.* for the ‘t1’ value behind the Enum value ‘Type1’.
  • .*P\dM.* checking the duration of x months. See here about the format.
  • .*2024(-02)?-17Z.* for a date check
  • .*2024-(\d{2}-)?17Z.* for a more flexible date check

Make sure you have cases available that match your input…dûh!

A final note as our input data is removed from all the metadata XML tags because of the stringification(?); Not a problem for my post, but at the customer I would add the property names as well to further fine-tune the input. As with AI…Crap in, is crap out!

By the way, those smart RegEx statements were off course generated by my ChatGPT friend! Get familiar with prompting AI…It’s the future, although a site like regex101 is also extremely helpful.

And how about deleting a “Saved search” instance? Well, you can just delete it; We only create a one-way relation to the cases which will break on instance removal! And deleting a case that was part of a search result? Well, what do you expect? It will not magically be there…The relation is nicely removed as well and removed from the result list of your search entity instance.

…AND 😃 you will see that each case (in the loop) is not exposing all the applied building block information. That’s a task for you to complete; simply add additional activities in the BPM to grab the information via related service calls and combine it together in one input stream to the custom if-condition. Hard? comment me below…


Ohhhh yeah…That’s a RegEx searchable “DONE”! How nice; You see again the power of BPM and an interesting RegEx matcher that can help you greatly on these search type of implementation. Spread the word and reuse these simple implementation thoughts in your own project. Have a great weekend and I see you next week, same time, in another post about AppWorks Tips.

Don’t forget to subscribe to get updates on the activities happening on this site. Have you noticed the quiz where you find out if you are also “The AppWorks guy”?