Laying the Foundation for OutSystems CRUD Wrappers
Before you can write your CRUD wrappers, you will need some supporting Actions and Entities. These will provide us with the ability to always get a valid User Id to represent “who did this?” even if it is a situation like a Timer where changes are happening without a user being logged in, and to handle our messaging.
System User
For the first need, I like to make an Action called GetNormalizedSessionUserId (or something along those lines, rename it to fit your needs). This Action is very simple, it will be a Function returning a User Identifier and implements this logic:
- Set return value to GetUserId()
- If return value is NullIdentifier(), go to your configuration and retrieve the User Id for a system user, and set the return value to that User Id
- If return value is still NullIdentifier(), throw an exception making it clear that a system user needs to be configured
Obviously you will need a system to create this system user and store its User Id to configuration. The easy way out is to just make a User manually and then store the Id in a Site Property. I am not a big fan of this because it’s a good way to open security holes. Instead, I make a Bootstrap_SystemUser Action, and run it from a Timer that runs on “When Published”. This Action:
- Check to see if the configuration has a User Id for the system user.
- If it does not, create this user with an “obvious” Username and Name and a random password (the function GeneratePassword(15, True) will do a good job here… don’t forget to run it through EncryptPassword from Users too), and Is_Active set to False. Create the User record using CreateUser in System (do not use “User_Create” from Users it will not work). Save the resulting User Id to your configuration.
- Otherwise, retrieve that User record, and set Is_Active to False and save with UpdateUser from System if Is_Active was set to True.
Why do this? Because now you can make “CreatedByUserId” and “UpdatedOnUserId” mandatory Attributes in your Entities and save them in your Create and Update CRUD wrappers, and call GetUser on those Attributes, and never see an error, and users will see a sensible value for these fields at runtime even if the data was changed by a Timer, BPT, etc.
Messaging
Consistent messaging that allows for consumers to get either fine or coarse messages is one of the big wins of this system. Let’s make some Structures (don’t forget to set them to be Public!). You will want a reference to the “MessageType” Static Entity in RichWidgets for this, by the way.
- EntityActionMessage: MessageValue (Text), MessageTypeId (MessageType Identifier)
- EntityActionResult: IsSuccess (Boolean), CombinedEntityActionMessageValues (Text), CombinedEntityActionMessageTypeId (MessageType Identifier), EntityActionMessages (List of EntityActionMessage)
The way these Structures work, is they allow us to return 0..n messages, each one with its own separate message text and severity of the message, as well as something that represents the combined text of all messages and the “worst” severity, as well as a simple “did this work or not?” flag. In the overwhelming majority of cases, you will only have 0 or 1 message to return, but in some cases (such as providing a list of fields that fail validation, or performing a batch update where some records may fail and others pass), having the ability to see individual messages will come in handy.
Next, let’s make two helper Actions. Each one will be a function, and accept a single input (“MessageValue” of type Text), and output an EntityActionResult. One will be called EntityActionResult_BuildFromSuccess and the other will be EntityActionResult_BuildFromFailure. These helper Actions will make your life much easier. The logic to implement is:
- Have a Variable of type EntityActionMessage, set its MessageValue to the MessageValue parameter and its MessageTypeId to Entities.MessageType.Success (for the Success)or Entities.MessageType.Error (for the Failure).
- Use ListAppend to push that Variable into the output’s EntityActionMessages list.
- Set the IsSuccess of the output to either True (for Success) or False (for Failure).
- Set the CombinedEntityActionMessageValues field of the output to your MessageValue parameter, and set the CombinedEntityActionMessageTypeId to Entities.MessageType.Success (for the Success)or Entities.MessageType.Error (for the Failure).
There is a third helper Action you need to build, which will get used less often but it still important, called EntityActionMessages_Combine. It will accept a list of EntityActionMessage Structures as its input and return a Text output called CombinedEntityActionMessageValues and a MessageTypeId called CombinedEntityActionMessageTypeId. Inside this Action, your logic is:
- ForEach over the input parameter.
- Inside the cycle, take the existing CombinedEntityActionMessages value and append the current EntityActionMessage.MessageValue to it, preferably separated with something like a NewLine(), a space, or whatever makes sense for you (I prefer NewLine()).
- Also, within the cycle, set CombinedEntityActionMessageTypeId equal to the current EntityActionMessageTypeId if the value of CombinedEntityActionMessageTypeId is NullIdentifier(), or if the current EntityActionMessageTypeId is more “severe”.
Whew! That’s a lot. Like I said at the beginning… just go download the Application Framework and save yourself all of this effort.
J.Ja