Hi there AppWorks fans,
Welcome to a new installment of AppWorks tips.
Versioning an entity instance? Hmmm…Where is this thought coming from? Well, I’m also a Documentum consultant and with an Enterprise Content Management system you have this functionality out of the box (when you check-out/check-in content). An entity has of course content related building blocks (like ‘Content’ and ‘File’) that support such functionality (when connected with the appropriate content storage provider like Document or Content Server!). All great and interesting, but how about making a version or the entity instance itself!? Why would we want to do this? Well, I just want to see if it’s possible to low-code such functionality and what bumps in the road will we face during implementation…
Let get right into it…
Where to start with this version implementation? A plan would be nice, so let’s produce a list of features we would like for a version implementation on an entity instance:
- A version (for me) is just a copy of the current entity with a new selectable major/minor version
- My first question here would be: How about related entities and other BBs data?
- We don’t need the implementation of a “Same version” feature like we know from content (which overrides the current version content!)
- We create a new version via an action rule with an option to select what our next version (major/minor) will be
- The list of entities as well as the entity itself will have an option to “Show all versions”
- It should be possible to delete the last version so the version before that one will be the new current version
- We can create a current version out of a previous version
- We can delete including all previous versions
…
Building time; Spin up your VM, dive into your organization and workspace, and create your first entity with the name document
. You can use this table of properties as input for the entity creation task:
Label | Name | Type | Length |
---|---|---|---|
Name | doc_name | Text | 64 |
Is current version | doc_version_is_current | Boolean | |
Major version number | doc_version_major_nr | Integer | |
Minor version number | doc_version_minor_nr | Integer | |
Version label | doc_version_label | Text | 32 |
Version | doc_version | Text | 64 |
Next version | doc_next_version | EnumText (static) | 8 |
Next version label | doc_next_version_label | Text | 16 |
Next on the list would be making it all nice and publishable:
- Make default generated BBs bright and shiny with interesting new names (from your naming convention!)
- I extend the ‘List’ BB
lst_all_documents
with theIdentity.Id
column to have a clear view on what’s happening during my copy actions (keep reading…) - Remove the ‘none’ option from the ‘Boolean’ property and set the default value to
true
- Add
Major
andMinor
as selectable Enum values;Minor
is default (remove the default ‘none’)! - Major starts with minimal
0
; set default value to0
- Minor starts with minimal
0
; set default value to1
- Add the ‘Web service’ building block and expose all CRUD operations
- Update the lengths of all the ‘Text’ properties (check that table again…)
After these quick updates we create an ‘onInit’ rule BB setting the version text to 0.1,CURRENT
. I name the rule e_oninit_set_doc_version
and let it set the property doc_version
with this expression: item.Properties.doc_version_major_nr + '.' + item.Properties.doc_version_minor_nr + ',CURRENT'
Now, do a first publication, and create that first document in runtime:
Ohw, I also updated the ‘Create’ form! 😁
With that first initialization step ready, we move to the next step!
Create a copy of an entity
Now, we’re getting into advanced logic as there is no default action available making this feature available in runtime!? So, now what? Well, when we’re talking advanced logic, we talk BPMs…Let’s create one! Real simple with 3 activities…Just like this:
Set the execution mode the ‘Short Lived’ as we would like to get direct feedback in runtime; Name it bpm_create_version
and save it in the bpms
folder of the project.
Back on the ‘Document’ entity, we need a modal screen/form which we’ll show just before we trigger our create-version BPM! This makes it possible to provide some settings for the next version; Like the Major/Minor selection, and an additional version label. The form (of type ‘Cancellable’) is named frm_version
and will look like this:
With this new form in place, we can now create a second rule; Not an ‘event’, but this time of type ‘action’; I name it a_create_version
with a proper label. This action will trigger our BPM managing the advanced logic, but before the action is performed we show that ‘Cancellable’ form! Simple and efficient like this:
Notes:
- The condition is wisely chosen; So, we can only create a new version on the latest version.
- After review of this post, the form name would be nicer with a name
frm_create_version
; We’ll create a new formfrm_show_versions
later on…
Publish all that challenging work; From runtime we can now trigger the action from a “current”, ‘Document’ entity instance:
Double-check the PIM artifact for that BPM instance!
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 wizard from the ‘System Resource Manager’ artifact point of view:
- Connector type:
Application Server Connector
- Group name:
sg_appserver
- Select the method set of our ‘Document’ entity
- 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 CRUD operations of the ‘Document’ entity, we’re now ready to implement this pseudocode for our BPM:
1 | var document = ReadDocument(ItemId); |
Time to open the BPM document bpm_create_version
; The one (short-lived) with those three activities in place. You can inject the first activity with the webservice ‘Readdocument’ (from the right-click-context-menu of the activity!). The second will have an injection of ‘Createdocument’, and finally we’ll inject ‘Updatedocument’ in the third activity. We keep it simple for now with a first messagemap implementation like this:
I see a mismatch in the screenshot (after reviewing this post!) for the ‘Createdocument’ service call. The target
doc_version_label
should have a match with the sourcedoc_next_version_label
…An extra task for you! 😁
Publish it, and try it out. A new instance is created in runtime, the old instance will be removed from the current state; You can easily filter it out from the filter option (which you can also save since version 21.4
):
NICEEEE…Let’s enhance this with an (exclusive!) decision split…WAIT, let’s do something smart here; without that split; Have a look:
Don’t forget to update the usage option; These are the expressions behind it:
1 | //MAJOR property |
Publish, and try out…
Works smoothly! The only thing not working is the ‘Version label’ update itself; It’s still 0.1,CURRENT
! For this we just add a new ‘onChange’, ‘Rule’ BB with the nice name e_onchange_set_doc_version
doing a fancy ternary expression like this:
This is for you to copy from:
1 | item.Properties.doc_version_is_current ? item.Properties.doc_version_major_nr + '.' + item.Properties.doc_version_minor_nr + (item.Properties.doc_version_label is null ? '' : ',' + item.Properties.doc_version_label) + ',CURRENT' : item.Properties.doc_version_major_nr + '.' + item.Properties.doc_version_minor_nr |
Note the nested ternary operator!! GREAT stuff, once you experience it! Watch the between-brackets-part to make it work! 😉
Now the great question…Is this smart doing it like this; Via an event rule while we’re already in BPM-land? My honest answer would be a “NO”! Does it work? For sure, but it separates two dependent logics into 2 levels of complexity! Keep things together if possible, so we’ll remove the rule again and implement this logic into the ‘Createdocument’ activity of the BPM with a new consolidated BPM view like this:
This is the expression setting the doc_version
value:
1 | concat( |
It’s a bit hard to read, but it’s a concatenation of string values where the if-else statements make sure we optionally apply the correct labels we pass in from the modal version form selections! I learned this xPath language from the BPM-guru…You know who you are when reading this! 😉
Does it work? See for yourself (from a clean test):
If you watch closely it’s not working all that smoothly as I tell you! Two things that will have an improvement in the final downloadable sources at the end of this post:
- When you create a new major version, the minor version will start at
0
…See that latest major version!- The
doc_version
property is not correctly updated with theCURRENT
tag…See those previous versions!
Great…what else?
Other features
All the basics for creating a new version are in place, let’s now have a look on the additional features:
🔥 The list of entities as well as the entity itself will have an option to “Show all versions”
Our default ‘All’ list already has the ability to filter out the current versions of the ‘Document’ entity instances. Let’s make an action rule available on a selected current version showing the other versions in a modal popup! This will behave the same as the previous action rule to create a new version. So, let’s make a copy from a_create_verions
and give it a new name a_show_versions
and low code it like this:
The frm_show_versions
needs special attention! Why? Well, we would like to show a toMany-relation-grid to itself!? 😅 Is this possible? Sure…Just create a new relationship BB with name to_many_document
and place this relation on that new ‘Autosave’ form!? ‘Autosave’?? Yes, ‘Autosave’! Why not ‘Cancellable’? Because it’s not possible to add a ‘toMany’ relation on a ‘Cancellable’ form! Yeah…Please, don’t shoot the messenger!
I also unchecked all the available actions (falling outside the screenshot) like ‘Open’, ‘Create’, ‘Browse’, etc. for this grid!
With this relation created and the form updated, we now only need to make sure the relations are put in place when our new version is created! This will be a BPM task which retrieves the previous versions and relates then to the new current version…Great, we have a solution! Now for the implementation…
First we move to the webservice BB where we enable the extra operations:
We also add that extra service operation to get a list of ‘Document’ entity instances by their ‘doc_name’; This is what’s behind the low-code service operation:
Notes:
- We use the ‘doc_name’ as constant to get a grip over “seamless” versions, but a better solution would be to have a revision ID in place for this. A task for you!
- We filter already the non-current versions here which makes it easier in the BPM!
- In the ‘Results’ tab you can also add a “Sorting by”; If you sort descending by ‘Identity.Id’, you can add the last non-current version at the top!
Next is the BPM…I add 2 extra activities; The first one injects the ‘GetByName’ service; The other one will be a for-loop injecting the ‘AddToto_many_document’ service call:
I see there is a wrong defined arrow-flow in the above screenshot! Can you detect it? Yes, it’s the one directly connected as input to ‘AddToto_many_document’…You can remove it yourself! It’s a common mistake…That’s why I left it in the screenshot! 😇
‘GetByName’ requires the ‘doc_name’ as input from a messagemap point of view. Like this:
‘AddToto_many_document’ requires 2 mappings:
- A mapping between the new created document ItemId to the webservice ‘document-id.ItemId’
- A mapping between the current document for the loop to the webservice ‘to_many_document.document-id.ItemId’
This is the consolidated view for the two extra activities:
Time for a publication and a test in runtime! Is it working? Yesssss…It is!
The only “too bad” thing here is that we have a ‘Close’ button instead of a ‘Cancel’ button…But YOLO, who cares? Who will notice? 🙈
Next!
🔥 It should be possible to delete the last version so the version before that one will be the new current version
Now we’re talking business logic! Sounds like an extended ‘Delete version’ action (as a ‘Rule’ BB) to me. The only thing to do here is finding out if there is a previous version; If so, we need to figure out which (of all previous versions) is the last one before the current one. A simple task if we had set the previous version in a “helper” property, but as we have not designed it like this we just go by the latest ordering approach on ItemId; This is not the safest way to do it correctly, but it works. We could also add the ‘Tracking’ BB to the entity which exposed the ‘creation_date’ of the entity which make it more solid…It’s all up to you; I go for the order by ItemId approach for our post.
So, let’s create a new short-lived BPM with name bpm_delete_current_version
. The best way to do this is by opening the previous BPM bpm_create_version
and save it as new BPM. This way it’s already short-lived, and it also copies the runtime security (if applied!). You can remove all activities (except ‘Readdocument’ and ‘GetByName’) between the start/end construct for now and save it. With this BPM in place, we can now create a new action ‘Rule’ BB for our entity; The same exercise as before, but an implementation like this (I name that new rule a_delete_current_version
):
After publication, we have an extra action available on the current selected versions for our ‘Document’ entity instances!
Next is that BPM implementation…It’s not that hard! This is my BPM with an exclusive check on the number of versions available:
This is the corresponding messagemap consolidated view to get a better understanding:
Publish and test…Is it working!? Hell yeah, it’s working! 🤠
I was thinking on how to remove the default ‘Delete’ action from the UI! First I thought of having an ‘Actionbar’ BB where I leave the ‘Delete’ action from possible options. Having the ‘Actionbar’ selected for my default layout does indeed the trick. Only, the ‘List’ BB still shows the action. The ‘List’ BB does not have a low-code feature to tell that it must use a specific actionbar…#FeatureRequest?? The only way removing the ‘Delete’ action would be the ‘Security’ BB and scope it from the list via a role. An easy task I leave with you as it’s out of scope for my post!
🔥 We can delete; including all previous versions
We can do this off-course already (selecting all entity version entities and hitting the ‘Delete’ action), but we also want to make this possible by selecting only the current version! Simple and effective with some BPM power (a ‘save as’ on the other BPM and naming it bpm_delete_all_versions
) and a new action ‘Rule’ with name a_delete_all_versions
triggering the BPM. I configure the action Rule with an extra “Confirmation” cancellable form which just shows the text “Are you sure?”:
This is my BPM:
With the consolidated messagemap view to have yourself a great implementation:
We could also update the
bpm_delete_current_version
with this functionality (with an interesting switch flag), but it’s always an excellent choice to make sure your BPMs do only one thing and that they also do it very, very well!…Just like in programming. See Single Responsibility Principle; Is the above BPM the only way forward? After a peer review, you can make it better; It’s my minimal viable product ready for improvements! 😁
🔥 We can create a current version out of a previous version
Owh yeah…More advanced stuff to build! I’m not writing it all out…You can have a look yourself in the sources downloadable here! After assessing my own low-code stuff, I implemented some fixes to make it all a bit smoother…That’s agile!
Answer to my own question
Question? Yes, this one: How about related entities and other BBs data? So, when our ‘Document’ entity has related entities, has a ‘Content’ or ‘File’ BB in place, has a lifecycle applied, has an assignee, deadline, discussion, or tracking BB data; Did I already mention the history, the new ‘Notification’ BB, or what about an applied and synchronized business workspace BB!?
Now we’re talking 2.0 stuff to further build a nice implementation around our versioning feature! Just some thoughts per building block…I leave the craftsmanship to you. 🤔
- Relationship: This one depends on how you look at it! Should it be a copy so the old versions are still related to them, or should those relations be moved to the current version!? Ask you analyst guy/gal! Both implementations require advanced business logic via updates in our BPMs!
- Assignee: In my opinion the assignee person/role must be retained on the current version. So, that’s an un-assign and re-assign via the assignee webservices (‘Create|Delete|Update Assignment’) in our BPMs
- Deadline: We can discuss this one, but I would try to push your analyst guy/gal to have a new deadline for the new version! It is eventually a new entity instance with its own deadline conditions which can be different.
- Lifecycle: Well, we create a new version instance meaning we also start a new lifecycle with it! We can move the lifecycle to a specific state based on the previous version, but it all depends…as always! By the way, this is the same for activity flows and dynamic workflows.
- Tracking: A new entity instance will have its own tracking with modify-date, creation-date, creator-, and modifier-name. Leave it as is, would be my first thought.
- Content/File: A new version can also have new content-blob…right? Now we’re getting close at the CMS check-out/check-in principles where versioning is already possible! I would copy the content to the new version, so you have it directly available, but requires a BPM deep dive again with content related webservices!
- Business workspace: A new entity instance will have its own business workspace (via xECM) applied; I don’t see how we can/want to manipulate this from a versioning principle…comment me on your thoughts!?
- Discussions: Well, a new version with a new discussion! You can always have a look at the old discussion in the previous versions.
- History: Same here…New version, new history! Old versions will have their own history in place!
- Notification: This is an interesting one! I think you still want to have the notification for the old entity version in place. You can’t decide if users applied for notification want to keep their notification settings in place for that new version…correct? Maybe work with a modal popup screen where you make it optional!? 🤔
…
Let’s finalize the post with a conclusion as we can discuss for ages about implementation choices and directions…
Nice; Nailed it…DONE! An interesting low-code implementation on versioning an entity instance in runtime. Again, we see also the power of BPMs when it comes to advanced logic! Not all things are solvable via a simple ‘Rule’ BB. Give it a go, try the implementation yourself and share any thoughts on improvements from your experience. Have a great weekend and CU in the next post; next week!
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”?