Virtualize CRUD functionality
Wouldn't it be great if you had a virtualization solution that fully simulates Create, Read, Update and Delete functionality?
apiUi offers state-full behaviour in two flavors, a low-code way and a way with scripting.
This tutorial will demonstrate how to realize CRUD functionality with scripting.
CRUD functionality
The OpenApi specification (OAS) we use for this tutorial has one operation for Create (http POST), two for Read (http GET), one for Update (http PUT) and another one for Delete (http DELETE).
Start apiUi, copy this link and add it in the list of Wsdl and API files (Project->Maintain list of Wsdl and API files...).
After having done this, apiUi will look like below.
Make sure this instance of apiUi listens for HTTP requests on port 7777 (Extra->Options).
In apiUi scripts, there are a number of functions that deal with environment-variables (envvars). In this tutorial we will use the envvars as a kind of an in-memory-database. Therefor we will follow a simple convention in name-giving for the variables.
Each environment-name will consist of three parts
- an entity name (e.g. 'contact')
- a unique key
- an attribute name (e.g. 'emailaddress')
an example: contact[janId].emailaddress
.
Values will be set with the SetEnvVar function. Retrieving will be done with GetEnvVar and GetEnvVardef. In a more advanced operation we will retrieve a list of contacts using the MatchingEnvVar function. For updating we will also use ResetEnvVar to remove a property. Deletion of a contact will be done with ResetEnvVars to remove a set of envvars.
Create
The message for creation of contacts has contactId as optional field. If this field is omitted, the mock will generate an unique id. If the contactId is delivered, then the mock will first check for uniqueness of this value. If the delivered contactId is already in use, then an http-code 409 Conflict
will be returned, otherwise, the delivered contactId will be used.
If all is OK then we will create envvars that reflect the provided contact data. Since every field is optional, we will check for each individual field if a value was provided.
Open the script-editor by clicking on the script-button at the left of the createContact operation and enter below code there.
string ws.id;
string ws.varprfx;
with Req.createContact.body req do
{
if Assigned (req.contactId) then
{
ws.id := req.contactId;
if GetEnvVar ('contact[' + ws.id + '].contactId') <> '' then
{
Rpy.createContact.undefined.responseCode := '409';
Exit ();
}
}
else
ws.id := GenerateGUID ();
Rpy.createContact.rspns200.body.id := ws.id;
ws.varprfx := 'contact[' + ws.id + '].';
SetEnvVar (ws.varprfx + 'contactId', ws.id);
if Assigned (req.contactType) then SetEnvVar (ws.varprfx + 'contactType', req.contactType);
if Assigned (req.name) then SetEnvVar (ws.varprfx + 'name', req.name);
if Assigned (req.phonenumber) then SetEnvVar (ws.varprfx + 'phonenumber', req.phonenumber);
if Assigned (req.emailaddress) then SetEnvVar (ws.varprfx + 'emailaddress', req.emailaddress);
SetEnvVar (ws.varprfx + 'created', NowAsStr ());
}
The line of code with Req.createContact.body req do
sets req
as a shortcut for Req.createContact.body
.
The envvar for contactId intentionally gets the value of contactId self in this line of code:
SetEnvVar (ws.varprfx + 'contactId', ws.id);
.
This is done to make the script for queryContacts operation somewhat easier.
In this tutorial we will assume that you use another instance of apiUi to test our virtualized services. To do so, follow next steps:
- Activate apiUi by choosing Run->Start (F9) and then switch to a test-driver.
- Start another instance of apiUi.
- Copy this link again and add it to the list of Wsdl and API files
(Project->Maintain list of Wsdl and API files...). - Set the
createContact
operation to outbound with the drop-down below the list of operations - Populate the request with data you like.
Try the automatic population of data as shown below.
which will fill this createContact request with:
- Activate this instance of apiUi Run->Start (F9)
- Send this request twice to the service-virtualization Run->Send request (Ctrl-G)
- The log-panel will now show a success response and a conflict response.
- Uncheck the contactId in the request and send the request again. Note that a third log-line appears with a success response again and that the response contains a generated unique identifier.
- Switch to the instance of apiUi that acts as the service-virtualization and inspect the environment-variables
(Environment->Edit...)
Read
The getContact operation retrieves data for a single contact based on the given parameter id
.
When no contact can be found, the mock will respond with an http-code 404 not found
.
Default value for the response-field contactType is 'prospect'.
In the service-virtualization open the script-editor for the getContact operation and enter below code.
string ws.varprfx := 'contact[' + Req.getContact.id + '].';
string ws.value;
if GetEnvVar (ws.varprfx + 'contactId') = '' then
{
Rpy.getContact.undefined.responseCode := '404';
Exit ();
}
Rpy.getContact.rspns200 := nil;
with Rpy.getContact.rspns200.body rpy do
{
rpy.contactId := Req.getContact.id;
rpy.contactType := GetEnvVarDef (ws.varprfx + 'contactType', 'prospect');
ws.value := GetEnvVar (ws.varprfx + 'name');
if ws.value <> '' then rpy.name := ws.value;
ws.value := GetEnvVar (ws.varprfx + 'phonenumber');
if ws.value <> '' then rpy.phonenumber := ws.value;
ws.value := GetEnvVar (ws.varprfx + 'emailaddress');
if ws.value <> '' then rpy.emailaddress := ws.value;
ws.value := GetEnvVar (ws.varprfx + 'created');
if ws.value <> '' then rpy.created := ws.value;
}
To test this virtualization, switch to the other instance of apiUi again and follw next steps:
- Set the
getContact
operation tooutbound
. - Enter 'id' as parameter.
- Send the request Run->Send request (Ctrl-G)
- Note that the virtualization responses with an http-code
404 Not Found
. - Enter 'contactId' or any other value you have used and send the request again.
- Now the virtualization will respond with an http-code
200 OK
and with details of the requested contact in the body.
In the Update part of this tutorial we will also test the assignment of the default for contactType
.
Update
The updateContact operation has a mandatory path-parameter id
which is the key for updating contacts.
When no contact exists with a given id, the mock will respond with an http-code 404 not found
.
The body consists of optional properties for a contact. When the contact can be found on the id
parameter then delivered parameters will be updated.
In case such a body parameter has an empty string, the property will be removed.
Open the script-editor for the updateContact operation and enter below code.
string ws.varprfx := 'contact[' + Req.updateContact.id + '].';
if GetEnvVar (ws.varprfx + 'contactId') = '' then
{
Rpy.updateContact.undefined.responseCode := '404';
Exit ();
}
Rpy.updateContact.rspns200 := 'OK';
with Req.updateContact.body req do
{
if assigned (req.contactType) then
{
if req.contactType = '' then
ResetEnvVar (ws.varprfx + 'contactType')
else
SetEnvVar (ws.varprfx + 'contactType', req.contactType);
}
if assigned (req.name) then
{
if req.name = '' then
ResetEnvVar (ws.varprfx + 'name')
else
SetEnvVar (ws.varprfx + 'name', req.name);
}
if assigned (req.phonenumber) then
{
if req.phonenumber = '' then
ResetEnvVar (ws.varprfx + 'phonenumber')
else
SetEnvVar (ws.varprfx + 'phonenumber', req.phonenumber);
}
if assigned (req.emailaddress) then
{
if req.emailaddress = '' then
ResetEnvVar (ws.varprfx + 'emailaddress')
else
SetEnvVar (ws.varprfx + 'emailaddress', req.emailaddress);
}
}
To test above script, switch to the instance of apiUi that acts as test-driver and follow next steps:
- Select operation
updateContact
and set it toOutbound
. - Populate the request and enter an id that you did not use yet.
- Send the request Run->Send request (Ctrl-G)
- Note that the response http-code is
404 Not Found
- Enter an id that you did use with
createContact
, e.g. 'contactId' - Set the value for
contactType
to an empty string
(Ignore the validation message and make surecontactType
is checked to include it in the message) - Change the value for the
name
property to something like 'Jan'. - Also set the value for
phonenumber
to an empty string and make sure it is checked - Uncheck the property
emailaddress
. - The data as shown in the message-treeview should no look like this:
This request will update the contact with id 'contactId'.
It will remove the propertiescontactType
andphonenumber
, update propertyname
and leaveemailaddress
as is. - Before updating, select operation
getContact
, enter the same id and retrieve the data by pressingCtrl-G
. - Select
updateContact
again and send the update-requestCtrl-G
- Repeat retrieval with
getContact
- In the log panel select the last log-line which shows
getContact
by just clicking it. - Also select the previous log-line which shows the
getContact
just before the update.
(click on it two times while holding the Ctrl key down) - The log panel should look like this
- Use the context-menu option Compare (requires 2 selected rows) to compare the two log-lines
- The form that will pop-up will show data like below:
Note thecontactType
, it shows the default value and also note that thephonenumber
for the contact indeed has been removed.
Delete
The deleteContact operation of course has a mandatory path-parameter id
which is the key for deletion.
When no contact exists with the given id, the mock will respond with an http-code 404 not found
.
When the contact can be found on id
then the corresponding contact data will be deleted.
Enter below code in the script-editor for the deleteContact operation.
string ws.varprfx := 'contact[' + Req.deleteContact.id + '].';
if GetEnvVar (ws.varprfx + 'contactId') = '' then
{
Rpy.deleteContact.undefined.responseCode := '404';
Exit ();
}
ResetEnvVars (RegExprSafeStr (ws.varprfx) + '.*');
To remove all contact data an envvar function is used that has a regular expression as argument.
The function RegExprSafeStr is used to escape the meta-characters for regular-expression that are in the envvar-names.
To test above script, switch to the instance of apiUi that acts as test-driver and follow next steps:
- Select operation
deleteContact
and set it toOutbound
. - Populate the request and enter an id that you used earlier with
createContact
. - Send the request Run->Send request (Ctrl-G) twice
- Note that the first response returns http-code
200 OK
, second one404 Not Found
Query
The queryContacts operation just lists all the contacts currently existing in the service-virtualization.
(So indeed, it's main purpose is just this tutorial...)
Enter below code in the script-editor for the queryContacts
operation.
string ws.regexp := RegExprSafeStr ('contact[') + '.*' + RegExprSafeStr ('].contactId');
string ws.varname;
string ws.varprfx;
string ws.value;
Rpy.queryContacts := nil;
for each MatchingEnvVar (ws.regexp) as ws.varname do
{
with new Rpy.queryContacts.rspns200.body._ rpy do
{
rpy.contactId := GetEnvVar (ws.varname);
ws.varprfx := 'contact[' + rpy.contactId + '].';
rpy.contactType := GetEnvVarDef (ws.varprfx + 'contactType', 'prospect');
ws.value := GetEnvVar (ws.varprfx + 'name');
if ws.value <> '' then rpy.name := ws.value;
ws.value := GetEnvVar (ws.varprfx + 'phonenumber');
if ws.value <> '' then rpy.phonenumber := ws.value;
ws.value := GetEnvVar (ws.varprfx + 'emailaddress');
if ws.value <> '' then rpy.emailaddress := ws.value;
ws.value := GetEnvVar (ws.varprfx + 'created');
if ws.value <> '' then rpy.created := ws.value;
}
}
Since we will use a function that has a regular expression as argument, we have to deal with meta-characters for regular expressions.
As a result of the code
string ws.regexp := RegExprSafeStr ('contact[') + '.*' + RegExprSafeStr ('].contactId');
the string-variable ws.regexp
will contain contact\[.*\]\.contactId
, exactly what we need for the MatchingEnvVar function.
Then the code for each MatchingEnvVar (ws.regexp) as ws.varname do
will assign all the envvar-names that match this regular expression to the string field ws.varname
and execute the attached code-block for each value found.
An example value for ws.varname
is contact[janId].contactId
.
Because in the createContact
operation we assigned the id as value to this envvar, a simple GetEnvVar can assign the contactId to the response field contactId
.
The remaining code implements the same logic as getContact
.
To test above script, switch to the instance of apiUi that acts as test-driver and follow next steps:
- Select the 'createContact' operation and add some contacts
- Select the operation
queryContacts
and set it toOutbound
. - Send the request Run->Send request (Ctrl-G)
- Click on the log-line for this call on the grid icon
Browse reply
.
Below form will pop-up with the data you supplied withcreateContact
.