Tuesday, April 21, 2020

Creating JSON request for REST web services

PeopleSoft provides Document technology to be used to generate JSON request messages but in my experience they are very restrictive especially when working on integrating with 3rd party web services. So following is what I did to generate a JSON request message to post to a 3rd party REST web service. 

The request that I have to generate is in the following form.
[
   {
      "attrib1":"value1",
      "attrib2":"value2",
      "attrib3":{
         "attrib3_1":"values3_1",
         "attrib3_2":"values3_2",
         "attrib3_3":"values3_3",
         "attrib3_4":"values3_4"
      }
   }
]

I am running PT 8.57.x and at this time its not possible to build a document with the root node as an array as shown in the example below. Also I have nested compounds which is also a challenge, the parent compound does not have a label where as the child does. 
So to build something like above I am using the CreateJsonBuilder API provided by PeopleSoft.

Local JsonBuilder &jbldr = CreateJsonBuilder();
Local JsonArray &jArray;
Local string &json;
Local message &request, &response;
Local boolean &bRet;

&jbldr.StartArray(""); /* no label */
 &jbldr.StartObject(""); /* no label */
  &jbldr.AddProperty("attrib1", "value1");
  &jbldr.AddProperty("attrib2", "value2");
   &jbldr.StartObject("attrib3"); /* need a label */
    &jbldr.AddProperty("attrib3_1", "value3_1");
    &jbldr.AddProperty("attrib3_2", "value3_2");
    &jbldr.AddProperty("attrib3_3", "value3_3");
    &jbldr.AddProperty("attrib3_4", "value3_4");
   &jbldr.EndObject("attrib3"); /* closing out the compound or JSONObject */
 &jbldr.EndObject("");
&jbldr.EndArray("");


/* this will return the array just like what I want */
&jArray = &jbldr.GetRootNode().GetJsonObject().GetJsonArray("");
&json = &jArray.ToString();

Created a basic non-rowset based message and assigned that as the request message in my service operation. Use this method to set the content for the message segment for a non-rowset-based message only.

&bRet = &request.SetContentString(&json);
&response = %IntBroker.SyncRequest(&request);

That's it, works like a charm.

Sunday, March 8, 2020

Event Mapping (managing customizations)

Event Mapping is a PeopleSoft delivered framework that helps applications and customers adapt PeopleSoft to meet their business needs with minimum upgrade impact. It introduces business logic as a configuration instead of customization. Event mapping allows the user to develop reusable code and artifacts and ensures that the customer’s code will not be overwritten by PeopleSoft delivered code during an upgrade.

Following is what I have tried in PeopleTools 8.56.07 and 8.57.08 on HCM 9.2 (PUM 31 and beyond). For this POC I am going to call my custom application package peoplecode on SavePostChange event on the DEPARTMENT_TBL component to perform something like sending out a notification. In the absence of the event mapping functionality I would have written this code in SavePostChange event of the DEPARTMENT_TBL component thus customizing the delivered object.

So, step one is to build the application package peoplecode. Import of PT_RCF:ServiceInterface is the key. The class name can be anything but the method name has to be "Execute".

import PT_RCF:ServiceInterface;

class FoundationDataNotify extends PT_RCF:ServiceInterface
   method Execute();
end-class;

method Execute
   /+ Extends/implements PT_RCF:ServiceInterface.execute +/

   /* custom logic to send out notification goes here */
end-method;
  
Once the package is developed navigate to PeopleTools > Portal > Related Content Service > Define Related Content Service. Create a new definition and select "URL Type" as application class and then provide the package details. For this POC I selected "Public Access" under security options. Save the service definition.

Then navigate to PeopleTools > Portal > Related Content Service > Manage Related Content Service, Event Mapping tab. Click on the "Map Application Classes to Component Events" link and then navigate the portal tree to find the component on which this event would be based on or associated with. In my case it would be the department table component located under Set Up HCM > Foundation Tables > Organization > Departments.

Under "Component Level Event Mapping" section, select "enable" check-box, event name would be SavePostChange and select the Service ID created in the earlier step. Set sequence number to 1 and processing sequence as "Post Process". Save the page and that is it. 

Navigate to the department component make a change and save the page and verify that the custom application package peoplecode fires.


The PeopleCode Editor provides an Event Mapping button in the dynamic Application Designer toolbar. For an application developer, the Event Mapping button is a visual indicator of custom PeopleCode programs mapped to events of a component, component page, component record, or component record field. This button is not available for page level event mapping.







So opened component peoplecode of component DEPARTMENT_TBL and clicked on the button to produce the following output.
















Right-clicking on the application package code and selecting "Event Mapping References" in the pop-up menu as shown below should display the component but that did not work.













"Event Mapping References" tab is empty as shown below.












So I guess this might work in future releases or maybe I don't have something configured correctly for this to work.

Thursday, March 5, 2020

Drop Zones

Configurable drop zones allow us to add fields to delivered fluid pages without customizing the component or fluid page definition. Our custom fields are displayed and processed along with fields from the main fluid page definition and any nested fluid subpages.

Following is what I tried in PeopleTools 8.56.07, HCM database on PUM 31.

The goal was to display Job Family information on the ESS additional info page.

1. Created a new fluid page based on the PSL_APPS_CONTENT template

2. The fluid page type is set as sub-page

3. Inserted a group-box, and on the group box properties, fluid tab, set group box type as "Container" and added style class  psc_padding-0em. Took default values for rest of the properties of the group box.

4. In this group box added two fields, first one is job family which is display control field and invisible and the second one is a related display field as shown below



















That is it as far as development in application designer is concerned.

5. Via PIA navigate to PeopleTools > Portal > Configure Drop Zones

6. Pull up delivered ESS Additional Information page and select which drop zone to be used. So in my example the job family information will be displayed after highest education level info.










7. Save the configuration and verify the results by navigating to the Fluid ESS page. 

Sunday, December 23, 2018

JSON Parser

PeopleSoft has undocumented JSON related API and this post covers some of the routines that I have tried to dynamically parse a json response. I think this API calls were made available in PT 8.56.x as part of PeopleSoft's inbuilt integration with ElasticSearch. 

I am making a RESTful web service call to a service hosted by a 3rd party vendor from PeopleSoft; so PeopleSoft is a consumer of the service. Focus here is to parse the response from the service so I am not covering how the service is setup and the request part of the service. 

So I have two types of responses. Response 1 as shown below 


{

    "error": {
        "message": "Some message text",
        "detail": "detail text about the error"
    },
    "status": "failure"
}

and Response 2 as follows.

{
   "import_set":"Import set value",
   "staging_table":"tablename",
   "result":[
      {
         "status":"updated",
         "error_message":"Unable to format 01-01-2019 using format string yyyyMMdd  for field hire_dt"
      }
   ]
}

In Response 1, I has 2 children, viz "error" and "status", whereas Response 2 has 3 children, import_set, staging_table and result.

In Response 1, "error" is a JSonObject which has 2 more children, message and detail. In Response 2, "result" is a JSonArray which has 2 children status and error_message.


Local string &content, &propName, &propValue;
Local JsonParser &parser;
Local JsonObject &jsonRoot, &jsonDetails;
Local JsonArray &jArray;
Local boolean &ret; 
Local number &i, &j, &k, &l;


&parser = CreateJsonParser(); /* this is the undocumented API */
&ret = &parser.Parse(&content); /* &content is the json response as a string */
&jsonRoot = &parser.GetRootObject();

For &i = 1 To &jsonRoot.GetChildCount()
/* for Response 1, following will get status tag and its value */
/* for Response 2, following will get import_set, staging_table and its values */
   &propName = &jsonRoot.GetPropertyNameAt(&i);
   &propValue = &jsonRoot.GetProperty(&propName);
   /* if there is a nested value then its either JsonArray or JsonObject */
   Evaluate &propValue
   When = "JsonArray"
/* this will return status and error_message which are in Response 2 */
      &jArray = &jsonRoot.GetJsonArray(&propName);
      For &j = 1 To &jArray.Length()
         &jsonDetails = &jArray.GetJsonObject(&j);
         For &k = 1 To &jsonDetails.GetChildCount()
            &propName = &jsonDetails.GetPropertyNameAt(&k);
            &propValue = &jsonDetails.GetProperty(&propName);
         End-For;
      End-For;
      Break;
   When = "JsonObject"
/* this will return message and detail which are in Response 1 */
      &jsonDetails = &jsonRoot.GetJsonObject(&propName);
      &numCnt = &jsonDetails.GetChildCount();
      For &l = 1 To &jsonDetails.GetChildCount()
         &propName = &jsonDetails.GetPropertyNameAt(&l);
         &propValue = &jsonDetails.GetProperty(&propName);
      End-For;
      Break;
   When-Other;
      /* when not JsonArray or JsonObject, get prop name and value which is at root level */
      Break;
   End-Evaluate;
End-For;

Hope this helps.

Friday, November 30, 2018

Query Transformations

I rarely do much work in PS Query, but while doing some other troubleshooting for PS Query related issues, I noticed the "Transformations" tab in Query Manager. I am running PT 8.56.x in HCM 9.2, but looks like this has been around since 8.53 or 8.54. 

So decided to do a quick test to see what this does and how it works. So the benefit here is that the output can be transformed to create reports right out off the output delivered by PS Query.

So as part of this POC used any existing query. Transforming the output to HTML and using a XSL transformation to highlight the row when a certain condition is matched.

On the Transformation tab within Query Manager, click on Add XSLT which opens a window to key in free-form XSLT text. It would have been nice if there is a wizard of some sort to build the XSLT. 

Gave it a Name and in the "Output File Type" lookup selected HTML. Following are the available choices - 001, A01, ABA, AET, APC, CSS, CSV, D01, DAT, DTD, DTL, ERR, GIF, GIR, HTM, HTML, IDX, JCD, JS, LIS, LOG, MDL, MT9, N01, NVT, NXT, OUT, PS, SQL, STDOUT, SYS, TMP, TRC, TRN, TXT, XLSM, XLSX, XML, XSD, ZIP

Not sure what is the difference between HTM and HTML, but I selected HTML. My sample XSLT is designed as follows.

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <html>
<!-- defined a style for the row that is going to be highlighted -->
<style type="text/css">
tr.redRow td {
font-size: 100%;
font-family: calibri;
font-weight:bold;
background-color: #FF2626;
padding-right: 10px;
color:#000;
}
</style>
<body>
<!-- in the html body defined a html table --> <table border="1">
<!-- Added a heading --> <font color="blue"> <h2>Sample Transformation test</h2> </font>
<!-- defined column headers --> <tr> <th>Employee ID</th> <th>Last Name</th> <th>First Name</th>
<th>Location</th> </tr>
<!-- Here I am selecting the data from PS Query --> <xsl:for-each select="query/row"> <tr>
<!-- defined a if condition to check if -->
<!-- value is McLean then highlight the row --> <xsl:if test="L.DESCR='McLean'"> <xsl:attribute name="class">redRow</xsl:attribute> </xsl:if>
<!-- Output the PS Query results --> <td> <xsl:value-of select="A.EMPLID" /> </td> <td> <xsl:value-of select="C.LAST_NAME" /> </td> <td> <xsl:value-of select="C.FIRST_NAME" /> </td> <td> <xsl:value-of select="L.DESCR" /> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>

So saved this and hit the preview button to preview the results. My query uses runtime prompts so those pop'ed up first, provided values for them and results are displayed in a new window (disable pop-up blocker or allow pop-up for this site).


Sample Transformation test


Employee IDLast NameFirst NameLocation
12345DoeJohnMadrid
13244DoeJaneEgypt
43423HuftonAbcdeLondon
42343FlemingJillMcLean
34423TiboldJozsefLondon
67554FarrellWilliamJohannesburg
43556MyersTonyIreland
75757KramerNicoleMcLean
27341WadaMarkNew York
27045RogerWaiMcLean
27098WayneJessicaNew York
27125GarciaGuillermoNew York
75688Deepak ShirguppiMaryland

If this query is scheduled using Query Scheduler option then a new format of XFORM is available and then I can pick any transformations (can define n number of transformations) that I have defined on the query.