Author Info
Chris Malek

Chris Malek is a PeopleTools® Technical Consultant with two decades of experience working on PeopleSoft enterprise software projects. He is available for consulting engagements.

About Chris Work with Chris
Looking for pain-free PeopleSoft web services? 😀
PeopleSoft Simple Web Services (SWS)

Introducing a small but powerful PeopleSoft bolt-on that makes web services very easy. If you have a SQL statement, you can turn that into a web service in PeopleSoft in a few minutes.

Contents

A Complete PeopleSoft REST POST Web Service Example

In this section, we will create a simple REST POST service that accepts data and inserts into a staging table for later processing. This is similar to the Simple REST GET Example which shows the HTTP GET Method that pulls information from PeopleSoft. Here we will explore sending data to PeopleSoft.

Staging Data

First we need to create a table that we will store data in. This simple web service example will take the raw data from the HTTP POST and perform a SQL Insert into a staging table.

We are going to create a simple table that accepts key/value pairs.

  • Record Name: C_KEY_VALUE
    • Field: IBTRANSACTIONID - Key value. The IB created a unique GUID of every transaction and we will use that in our table to create a unique value.
    • Field: KEY1 - This will be a “duplicate key” and is a string that the payload will pass in. We will allow the same key to be sent more than once.
    • Field: LONGVALUE - This is the “value” of the key and we accept a long value.
    • Field: ADD_DTTM - This will be the date time that the value was inserted.

Creating a Generic Message for Request Response

In order to have the most flexibility you need to use a NonRowset Message. This will allow us to accept any data encoding and return any data encoding. I generally have a generic message like this that is shared across many web services. In this case, the message is NOT going to provide any sort of functionality for us. We just need it as a required field in the Service Operation setup. We will handle all of the decoding and encoding of data in the PeopleCode handler below.

Creating The Service

Now we need to create a REST Service which will “group” or “own” our service operation. It really does not provide any functionality other than organization in the IB setup and security.

Creating the POST Service Operation

Now from the Service we created above, you can create the new POST Service Operation. You see here that we enter in C_KEYVALUE and then choose the “Rest Method” of “Post”. Choose your name wisely here as you will be stuck with this going forward and your clients will end up using this in the URLs for REST.

When you click the “Add” button, you will be transfered to the Service Operation Setup page.

We need to highlight some key points here that often cause much confusing with developers.

  • Service Operation Name: You will see that the service operation name turned into N_KEYVALUE_POST. PeopleTools automatically added the “POST” to the end which ties to the HTTP method chosen. So if you plan on supporting GET, DELETE, and POST methods you will end up being 3 different service operations for those named N_KEYVALUE_GET, N_KEYVALUE_DELETE, N_KEYVALUE_POST respectively. We will just be created the POST in this example.
  • Req Verification: This looks like there is no security tied to this. Ignore this for now.
  • URI Template: We are only entering a “/” here because we are NOT asking for any query string parameters. The Service operation name will be enough in the URL.
  • Document Template: Note that this is blank! Since we are NOT planning on using any query string parameters in the POST then we do NOT need to have a document template. That is only required for parsing query string parameters.
  • Message Request and Response: We are using the C_GENERIC message here in the request and response.

This is enough information to save the service operation. Since all of the “magic” of this web service will be handled in the PeopleCode handler, the service operation definition is just a shell in order from the Integration Broker to bootstrap our PeopleCode handler when a request comes in.

Routings

A Service Operation needs to have “routings” configured for it to be functional. This tells the IB engine what PeopleSoft nodes to send or accept messages from. In SOAP and HTTP based services, those are important and the Node Best Practices Section has some opinions documented.

In REST, PeopleSoft will handle all the Routing configurations for you and they are configured as “Any to default local node”. I have found that if you try to add routings manually it confuses the integration broker and the REST logic will NOT be triggered. This must be based on some internal PeopleTools logic. In our case, the routings look like this. There is no reason to add, remove or modify routings for REST based services. See REST Security for more information and best practices.

Right now we have a basic service operation without a handler configured and without any security applied. Let’s see what happens if we try to call this web service.

Here is the basic HTTP Request.

POST http://35.236.3.31:8000/PSIGW/RESTListeningConnector/PSFT_CS/C_KEYVALUE.v1/ HTTP/1.1

Here is the response sent back from the integration broker.

HTTP/1.1 200 OK
Connection: close
Date: Sun, 14 Mar 2021 18:23:56 GMT
Content-Length: 144
Content-Type: text/html; charset=UTF-8
Content-Encoding: gzip
Set-Cookie: JSESSIONID=onkx-wDsqoMNrCdQ2SuwK4RO4CsVprPqjSZR1J9WBvCAQTEyWoft!-1454137333; path=/; HttpOnly

<HTML><HEAD><TITLE>RESTListeningConnector</TITLE></HEAD><BODY>UserID NO_ACCESS not authorized for Service Operation C_KEYVALUE_POST</BODY></HTML>

You will see from the response that it never got past the security validation. We did not send any authentication information in the request and we have NOT add this service operations to any sort of permission lists. This is important to note because the service operation configuration shown above has a Req Verification flag unchecked. That could be falsely interpreted as “this is open to the world” but that is untrue. One funky think on the tools release I am testing on this returned a HTTP 200 OK status which is a bug.

Setting Up Service Operation Security

We need a valid PeopleSoft user to be able to invoke this service operation. We are going to add this service operation to a permission list and valid user. That is covered in both the REST Security and Simple REST GET Example sections and I will assume you have read those sections and I will not cover them here again.

Implementing the Handler

Up to this point we have not configured one of the most important pieces of the web service. That would be the handler which will be an Application Class that is implemented in a certain way. Let’s establish a base working handler that we will build upon as we work through this example.

The PeopleCode handler below:

  • Is not useful other than a basic starting point.
  • Responds back with a hard coded “hello world” xml document for now.
  • Does not process any input yet.
import PS_PT:Integration:IRequestHandler;

class RestPostV1 implements PS_PT:Integration:IRequestHandler
   method OnRequest(&msgRequest As Message) Returns Message;
   
end-class;

method OnRequest
   /+ &msgRequest as Message +/
   /+ Returns Message +/
   /+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/
   
   Local Message &response = CreateMessage(@("Operation." | &msgRequest.OperationName), %IntBroker_Response);
   
   Local XmlDoc &xmlout;
   Local XmlNode &childNode;
   
   &xmlout = CreateXmlDoc("<?xml version='1.0'?><response/>");
   
   &childNode = &xmlout.DocumentElement.AddElement("hello").AddText("world");
   
   &response.SetXmlDoc(&xmlout);
   
   Return &response;
end-method;

Now we need to configure the service operation to run this PeopleCode when a request is received. That is done on the “Handler” tab of the service operation.

Testing Round 1

Now we security setup (not shown) and a handler let’s call the web service over HTTP. We will send in a request body payload but the handler is not code to do anything with it and it will be ignored.

Here is the request:

POST http://35.236.3.31:8000/PSIGW/RESTListeningConnector/PSFT_CS/C_KEYVALUE.v1/ HTTP/1.1
Authorization: Basic UkVTVF9URVNUX1VTRVI6QmFkI1Bhc3N3b3JkIzEyMw==
Content-Type: text/xml; encoding="UTF-8"

<?xml version="1.0"?>
<request>
    <row>
        <key>EMPLID</key>
        <value>111111</value>
    </row>
    <row>
        <key>FAVORITE_FOOD</key>
        <value>Pizza</value>
    </row>
    <row>
        <key>FAVORITE_DRINK</key>
        <value>Lemo ade</value>
    </row>
</request>

Here is the response:

HTTP/1.1 200 OK
Connection: close
Date: Sun, 14 Mar 2021 18:41:47 GMT
Content-Length: 72
Content-Type: text/xml; encoding="UTF-8"
Content-Encoding: gzip
Set-Cookie: JSESSIONID=dWsyC1Y0wyok2-ErWgvfiDGN3C2IetXZ0AVI43z1FQjKvZcD5sDi!-1454137333; path=/; HttpOnly,psmap=; domain=.c.peoplesoftdemo-1470578694381.internal; path=/

<?xml version="1.0"?>
<response>
  <hello>world</hello>
</response>

What would happen if we tried to call this same service operation with a GET method? If you recall from above the “GET” method would actually be a separate service operation called C_KEYVALUE_GET but we did not create that. Let’s try to call it.

GET http://35.236.3.31:8000/PSIGW/RESTListeningConnector/PSFT_CS/C_KEYVALUE.v1/ HTTP/1.1
Authorization: Basic UkVTVF9URVNUX1VTRVI6QmFkI1Bhc3N3b3JkIzEyMw==

Here is the response, the HTTP Response code is 200 (Success) which is NOT the correct response code. This is an example of one of those rough edges.

HTTP/1.1 200 OK
Connection: close
Date: Sun, 14 Mar 2021 20:52:05 GMT
Content-Length: 140
Content-Type: text/html; charset=UTF-8
X-ORACLE-DMS-ECID: c8d9efad-cb10-4ec8-bc1c-a59fc852e83a-00000014
Content-Encoding: gzip
Set-Cookie: JSESSIONID=JFgygqLYRnO1h9hPakWpaFAXQ2ObIOdveA1RUkT3g1C5YrGLAuff!1814625920; path=/; HttpOnly
X-ORACLE-DMS-RID: 0

<HTML><HEAD><TITLE>RESTListeningConnector</TITLE></HEAD><BODY>Service Version Method  GET for Service C_KEYVALUE.v1 not found in DB.</BODY></HTML>

Expanding the Handler

Now let’s go deeper into the handler and parse the XML sent by the client and insert it into our table. Please be sure to read the Best Practices with PeopleSoft Table Updates and Inserts regarding updates.

We are going to expect XML that resembles this:

<?xml version="1.0"?>
<request>
    <row>
        <key>EMPLID</key>
        <value>111111</value>
    </row>
    <row>
        <key>FAVORITE_FOOD</key>
        <value>Pizza</value>
    </row>
    <row>
        <key>FAVORITE_DRINK</key>
        <value>Lemo ade</value>
    </row>
</request>
  • The client can pass in any number of rows.
  • For each row, we are expecting a key and value node which we will use to insert into out key value table C_KEY_VALUE.

Here is the completed handler that will:

  • Parse the XML
  • Insert into C_KEY_VALUE
  • Return some meta-data about the request.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import PS_PT:Integration:IRequestHandler;


class RestPostV1 implements PS_PT:Integration:IRequestHandler
   method OnRequest(&msgRequest As Message) Returns Message;
   
end-class;


method OnRequest
   /+ &msgRequest as Message +/
   /+ Returns Message +/
   /+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/
   
   Local Message &response = CreateMessage(@("Operation." | &msgRequest.OperationName), %IntBroker_Response);
   
   /* Parse Input */
   
   Local XmlDoc &xmlDocRequest = &msgRequest.GetXmlDoc();
   Local XmlNode &requestRootNode = &xmlDocRequest.DocumentElement;
   Local string &key, &value;
   
   
   Local array of XmlNode &rowNodes;
   &rowNodes = &requestRootNode.GetElementsByTagName("row");
   
   Local integer &i, &j;
   
   /* loop over each "row" found */
   For &i = 1 To &rowNodes.Len
      
      Local Record &recKV;
      &recKV = CreateRecord(Record.C_KEY_VALUE);
      &recKV.IBTRANSACTIONID.Value = &msgRequest.TransactionId;
      &recKV.ADD_DTTM.Value = %Datetime;
      
      /* Loop over each XML child node pulling out only the key & value nodes */
      For &j = 1 To &rowNodes [&i].ChildNodeCount
         
         Local XmlNode &nodeInspect;
         &nodeInspect = &rowNodes [&i].GetChildNode(&j);
         
         Evaluate Lower(&nodeInspect.NodeName)
         When = "key"
            &recKV.KEY1.Value = &nodeInspect.NodeValue;
            Break;
         When = "value"
            &recKV.LONGVALUE.Value = &nodeInspect.NodeValue;
            Break;
         End-Evaluate;
         
      End-For;
      
      
      &recKV.Insert();
      
      
   End-For;
   
   
   /* Setup Output */
   
   Local XmlDoc &xmlDocResponse;
   Local XmlNode &childNode;
   
   &xmlDocResponse = CreateXmlDoc("<?xml version='1.0'?><RESPONSE/>");
   
   Local XmlNode &nxMetaNode, &xnTemp;
   &nxMetaNode = &xmlDocResponse.DocumentElement.AddElement("META");
   &xnTemp = &nxMetaNode.AddElement("OPRID").AddText(%OperatorId);
   &xnTemp = &nxMetaNode.AddElement("CURRENT_TIME").AddText(String(%Datetime));
   &xnTemp = &nxMetaNode.AddElement("TRANSACTION_ID").AddText(&msgRequest.TransactionId);
   &xnTemp = &nxMetaNode.AddElement("DBNAME").AddText(%DbName);
   &response.SetXmlDoc(&xmlDocResponse);
   
   Return &response;
   
end-method;

Testing Full Handler

Here is a full request.

POST http://35.236.3.31:8000/PSIGW/RESTListeningConnector/PSFT_CS/C_KEYVALUE.v1/ HTTP/1.1
Authorization: Basic UkVTVF9URVNUX1VTRVI6QmFkI1Bhc3N3b3JkIzEyMw==
Content-Type: text/xml; encoding="UTF-8"

<?xml version="1.0"?>
<request>
  <row>
    <key>EMPLID</key>
    <value>111111</value>
  </row>
  <row>
    <key>FAVORITE_FOOD</key>
    <value>Pizza</value>
  </row>
  <row>
    <key>FAVORITE_DRINK</key>
    <value>Lemonade</value>
  </row>
</request>

The response that came back from our handler is:

HTTP/1.1 200 OK
Connection: close
Date: Sun, 14 Mar 2021 21:24:15 GMT
Content-Length: 203
Content-Type: text/xml; encoding="UTF-8"
Content-Encoding: gzip
Set-Cookie: JSESSIONID=R8kyoBYAGiP-cljZYIDnd2gWkm3P56EG4x1mW8LCLHFiY_rzJP1R!1814625920; path=/; HttpOnly,psmap=; domain=.c.peoplesoftdemo-1470578694381.internal; path=/

<?xml version="1.0"?>
<response>
  <META isMETA="True">
    <OPRID>REST_TEST_USER</OPRID>
    <CURRENT_TIME>2021-03-14-21.24.15.000000</CURRENT_TIME>
    <TRANSACTION_ID>a0e158cd-850b-11eb-a700-cd5c40da7841</TRANSACTION_ID>
    <DBNAME>CS92U009</DBNAME>
  </META>
</response>

Here we can see the data was inserted into our table.

Conclusion

  • We created a POST operation and handled inbound XML and inserting data into a table.
  • In future sections we will look how we can support different encoding types on run time.