Setting up and using PeopleSoft REST Web Services - A Complete Example

REST Web Services in PeopleSoft 

1. Service Configuration

Navigation: PeopleTools > Integration Broker > Configuration > Service Configuration

Service Namespace: http://xmlns.oracle.com/Enterprise/Tools/Services

Schema Namespace: http://xmlns.oracle.com/Enterprise/Tools/Schemas


Click on Setup Target location link

Web Services Target Loacations : http://<machine>:<port>/PSIGW/PeopleSoftListeningConnector

REST Target Location: http://<machine>:<port>/PSIGW/RESTListeningConnector/PSFT_HR


2. Active the Any to Local routing on GETWADL service operation

Navigation: PeopleTools > Integration Broker > Integration Setup > Service Operation

Atleast 1 Routing Defination shoud be Active



3. Documents Builder

Navigation: Main Menu > PeopleTools > Documents > Document Builder

Page used to build the structure of a document.



Create Document from Schema:

Navigation: Main Menu > PeopleTools > Documents > Document Utilities > Create Document from Schema

Create documents from existing XML (XSD) schemas.

Create Document from Record:

Navigation: Main Menu > PeopleTools > Documents > Document Utilities > Create Document from Record

Create documents from existing PeopleSoft Records.

When created, a document will have one root element, which by default is the same name as the document definition. That root element could have one or more child elements, and the child elements could have none, one or more peer elements or child elements. The following 3 element types makeup the document hierarchy tree:

Primitive: Represents an element that will contain data. Primitives can have the following data types:

Binary, Boolean, Character, Date, DateTime, Decimal, Integer, String, Text and Time

Compound:

A compound represents a group of primitive values (elements). There are three options when adding a compound:

Add a document – this will retrieve another document from the package that has been defined with primitives

Add a record – this will retrieve the record definition and setup the primitives, data types and lengths automatically

Add a complex primitive – this will allow you to achieve the element attribute structure in XML. It could be used to represent child structures in JSON without the need to create a child-document, but it does have some drawbacks/limitations to be aware of – discussed below in compounds section.

Collection:

Represents a set of one or more compounds in the document. Adding a compound child to a collection object could represent a row set, with the child compound representing a row, and the collection being the row set.


Compounds
When working with documents, a compound represents an object consisting of a grouping of primitives. There are 3 ways to add a compound, as a complex primitive, a child/sub document, or created from a PeopleSoft record. This section will detail each method and highlight some positives/negatives of each. How you create and use your compound objects will largely come down to how you are returning your message format. JSON will tend to use child documents or records only, where XML will probably need some complex primitives to represent element/attribute pairs.
Complex Primitive
If you are using XML as your response format for the REST service, a complex primitive is how you can achieve the element/attribute pair with documents.

To be output as the following XML:

<?xml version="1.0" encoding="UTF-8"?> <student_phone_resp xmlns="http://xmlns.oracle.com/Enterprise/Tools/schemas/STUDENT_PHONE.student_pho ne_resp2.v1"> <phone country_code="" phone_type="" preferred_flag="" student_id="" /> </student_phone_resp>

And JSON:

{"student_phone_resp": { "phone": [

{"preferred_flag": "","country_code": "","phone_type": "","student_id": "","phone": ""}

]}

}

Some things to note with complex primitives:

·        When returning in XML, the elements (primitives) will be returned in alphabetical order, not what you have set on the document.

·   


When returning in XML, you will not get the last primitive returned e.g.



The two highlighted essentially represent the start and end tags of the element. So, in this instance if you needed to return their phone and you were using XML, you’d need to have another element to contain it.

·        When returning in JSON, you will get the last primitive returned as an element. Depending on the name of the complex primitive, this could be a good or bad thing. In the example, it works out OK, as we’d need to return a phone element anyway (phone number). If you had a student complex primitive, you would then be returning an element called student, which could be confusing.

·        When returning in JSON, the order of elements returned will match the document structure.

·        A complex primitive will not create a child document. Adding a compound as an existing document relies on the document having been already created, and adding one as a PeopleSoft record will create one on the fly. If you’re structure is simple, it may be worth

just using a complex primitive to represent the object, rather than creating many sub/child documents.


You can add a complex primitive to either a collection (0…n), or the document root (0…1



From here, you can add the elements within the complex primitive. You will need to make sure the compound object is selected on the document to add a primitive:


One thing to note, you will still need to set a data type (and length if required) for the closing phone

– even though it won’t get returned in XML, it will in JSON so it still needs to be set.


Child Document

Adding a child document compound is the best way to ensure you have full control of the format, structure and data types. This relies on you creating a document that represents the object you want e.g.

As with complex primitive, you could add this child document against the root or a collection. Once you add the compound, select the document option and it will allow you to search for the document.

Select the document and it will add compound of the child document by reference, that is any changes made on the child document will be reflected on the parent.

PeopleSoft Record

The last option is to use an existing PeopleSoft record as the basis of a compound object.




After selecting the record, you will be shown a list of all the fields available on the record, along with the option of providing an alias. Here you will need to provide a document name, and based on the fields chosen a new child document will be created.

Things to note with record compounds:

·        The alias you provide will not end up being the element name. It will still have the PeopleSoft field name as the element name (EMPLID), but for XML it will use the alias as the tag name and JSON will use it as the JSON tag name. You will need to use EMPLID when referring to the elements in code, but they will appear different in the output. This may end up being confusing, depending on how many differences there are.



·        The type and length of the elements will be mapped from the field definition so you will not need to set them manually.

There are several methods that can be used to create documents via the PIA:

Creating a document by hand (manual)

The first is by using the document builder to manually build up a document with the primitive, compound and collection element types. This gives you the most flexibility in setting the names, tags, data types (and lengths) and the full structure.

Creating a document via XSD Schema

If you are looking to convert existing messages or you already have the xml, you can create a document by uploading the file via the PIA:

After providing a package, uploading the XSD source and building, you can then click on the link to view the newly created document. Any further changes/tweaks can be done manually on the document builder page as required.

Creating a document via PeopleSoft Record

If there is an existing PeopleSoft record that you are basing your messages off, you can use the following page to create a document structure:

After searching for the record, click the record link and you will be taken to another page that will display the record fields and allow you to select which fields to be imported, along with any alias you wish to use. Any record field will be created as a primitive on the new document, and the data type and lengths will be taken from the PeopleSoft field definition, so you will not need to set them yourself.

 

This is the method used when adding a compound based off a PeopleSoft Record.

Document Tester

The document tester is a handy utility page in the PIA that will give you XML, JSON and PeopleCode representations of any document that you have built. This is good if you need to provide the output format to another developer to work with, test the hierarchy and formatting of your document, and the PeopleCode option will provide you with a code base to start working with your document.



Document tester page showing the supported output format types






Required Documents for REST Web Services

The following table details the documents that should be created for the 4 main REST web service types:

 

HTTP Method

Template

Request (Body)

Response

Fault

GET

Yes, required to create URI

template

No

Yes

Optional

DELETE

Yes, required to create URI

template

No

Optional

Optional

POST

Yes, required to create URI

template

Yes

Optional

Optional

PUT

Yes, required to create URI

template

Yes

Optional

Optional

 

While the fault document is optional, I would strongly recommend you create one and set a fault code for each REST web service. It is the only way to return a meaningful error and error status code to the receiving party. The fault document/message can be generic and re-used across services as well, there is no need to create one per service/service operation.

Request & Response Messages

After you have created the document messages, for them to appear on the service operations page, you will need to create a message based off the document. The messages end up being copies of the document, but you will only need to create messages for those at the top level i.e. request, response and fault documents. Child documents, or those that appear as compounds, do not need to be re- created.

All you need to do is navigate to the Messages page under:


There is a Document type available when adding a new message, selecting that will allow you to select the package and document you want to add. It will only display documents that do not have an existing message reference, so you cannot create 2 messages based of the same document.


After clicking add, it will take you to a page that looks identical to the document builder, and should hopefully display a clone of your document. Simply save the page and you now can reference your document as a message on services pages.



Creating a REST Service

Service Operation Types (HTTP Methods)

PeopleSoft supports the following HTTP Methods:

 

HTTP Method

Description

GET

Retrieve a representation of a specific resource

DELETE

Delete a specific resource

POST

Create a new resource to an existing URL

PUT

Create a new resource to a new URL, or modify an existing resource against an existing URL.

HEAD

Identical to a GET request, but no message body is returned

An example of when to use a POST vs. PUT:

You need to create a new application for a student, but do no already know the application ID, only the details. You would submit that as a POST request, and the POST response would include the application ID so that subsequent requests can refer to that application specifically.

You want to create an application for a student, and already have the application ID ahead of time. You submit a PUT request at CreateApplication/{application_id} and the server will either create the resource at CreateApplication/{application_id} if it does not exist, or it will update/replace whatever is at that URL.

To put it simply, POST should be used to create a resource where you may not know specific identifying information about the resource (ID), and a PUT should be used where you know that identifying information (regardless of whether it exists).

Creating a Service Operation

To create a REST service operation, you need to navigate to the existing services page:



Where there is an option to filter for REST services, or when creating a new service.

Once you have created the REST service and saved the page, you can start adding individual service operations:

When creating a service operation, be aware that PeopleSoft will append the REST Method to the end. So if you entered STUDENT_PHONE with a method of GET, the resulting name will be STUDENT_PHONE_GET. PeopleSoft supports having multiple service operations with different HTTP methods (GET, DELETE, POST…). In example REST service, all of the operations have the STUDENT_PHONE operation alias, and based on the HTTP method on the inbound request, PeopleSoft will route it to the correct service operation.

What you end up with, is all 4 of the service operations above with the same service operation alias, but a different REST method:




Setting up the Uniform Resource Indicators (URI)

The URI is how you interact with your resource (service operation) and will consist of 2 parts:

1.      The base URL, which is taken from:


2.      The URI that is setup on the service operation pag



The URI should contain a resource name, to indicate the resource that the service is interacting with, along with any parameters or filters that you want to use to retrieve specific representations of the resource. Ideally they should be simple enough to understand what would be returned just by looking at them:


·        The first URI will return a list of phones for a student, based on a student ID

·        The second URI will return a phone for a student, based on a student ID and a specific phone type

There are no strict rules with the URI templates in PeopleSoft. Your service will not stop working if you had a non-RESTful URI there, you could choose to remove the StudentPhone resource identifier and it would work perfectly fine. There’s a lot of information available online depending on how strict you want to follow REST practices, some recommended reading on good URI design:

http://www.restapitutorial.com/lessons/restfulresourcenaming.html

 

http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

 

In PeopleSoft, you have 2 choices for creating the URI’s:

Manually

If you’re comfortable with the concepts, you can create your URI templates manually on the service operations page. To do this, add your template message and type out the URI, making sure you surround any parameters with { } and ensure the names map exactly. When you save the page, it should try and validate the URI, which will pick up any incorrectly named parameters.



Template Builder

The template builder page is available by clicking the build button on the grid row:


From here you can build up the URI by selecting primitives on the template document and adding separators:






REST Fault Messages

Adding a fault message to your service operation is the only method of returning a meaningful error, along with a relevant 4xx HTTP status code. Technically you could try/catch your response and return an in-line error message, but this would then return a 200-status code indicating the request was processed OK, but it failed.

To add a fault message, you select the Add Fault Type button:

You then have the option to provide a fault message and set the status code.

HTTP Status Codes for Messages

PeopleSoft supports several HTTP status codes foGenerally, 200 is accepted as an OK response, even for something like a delete where you could either return 200 (OK) or 204 (no content). The same goes for a POST or PUT, where you could return 200 (OK) or 201 (Created) if a new resource was created for a request.

The fault codes are where things get more interesting, as there could be several different fault statuses you want to return, where currently only 1 can be set.

For example, 404 is commonly known as resource not found, and could be returned should a GET request be performed where the resource does not exist. You could also need to account for a bad request, where the URI parameters are invalid, which should return 400 (bad request). 401 (unauthorized) is not an option to select in the list as it is reserved by PeopleSoft and returned if any authorization failures occur.

We tend to just use 400 to indicate some generic error, and in the instance of a GET request on something that does not exist, we return an empty response rowset.

Adding a Service Operation Handler

Service operation handler setup works the same as SOAP based services, on the Handlers tab you set the type to be On Request, and then select Application Class as the Implementation method:r both responses and faults. For a full description of each, read https://en.wikipedia.org/wiki/List_of_HTTP_status_codes or Google around.

You can then set the Application class path to the OnRequest method, or the code that is invoked when your service operation is called:


PeopleCode with REST/Documents

Creating the Application Package

REST Services don’t have a lot of dependencies when it comes to application package setup, only

within the application package code. You can structure the application package code however you’d like, even creating a single application class for multiple service operations. In that instance, you can use the following code snippet to work out which code to run based on the HTTP method on the inbound message:

I would suggest separating the service operations out, so that your application package has a structure of:

This way you can keep the logic for each separate, and reduce the risk of any changes made. If done that way, you can exclude the HTTPMethod code check, as the service operation should point to the right code.

Application Class Structure

A few things to note when creating an application class for a REST service operation

1.     Import and implement the IRequestHandler class. This is required by the Integration Broker to pass the message to your code, and can also be used to invoke an error that will return the correct HTTP status code.

1.      Have at the following 3 methods

a.      Constructor

b.      OnRequest method invoked when your service operation is called

c.      OnError – assuming you’ve implemented IRequestHandler, this is the method 


invoked when an exception is thrown at any point in the OnRequest.


Creating Messages from Service Operation Setup

When working with REST web services, you may need to create a message based off the config that has been setup against the service operation. This can be done with the following code, depending on what message you need to work with (request, response or fault):


You can then read the document object off the message by using:






PeopleCode Objects for Documents

The following object classes can be created in PeopleCode and used to work with the document API:

 

Class

Purpose

Documentation

Document

Document class allows you to interact with and modify the contents of a PeopleSoft document

as a PeopleCode object.

PeopleBooks

 



Compound

Compound class allows you to interact with and modify the contents of a document, either by the DocumentElement, or a specific

compound on the document.

PeopleBooks

 




 

Collection

Collection class allows you to retrieve and

populate a specific collection object off a document.

PeopleBooks

 



Primitive

The primitive class allows you to work with a primitive document element directly.

Generally, not a necessary object to use, as you can always interact with it from the Document or Compound level.

PeopleBooks

 





Reading the URI

To read in the URI template, and retrieve any parameters passed in, you need to do the following:

1.      Get the URI document from the inbound message – passed into the OnRequest method by the integration broker.

2.      Read the URI document into a local compound object

3.      Use either GetPropertyByName or GetPropertyByIndex to retrieve primitive values off the compound object.

Reading a request message body

In the instance of a POST or PUT, you could have both a URI indicating the resource path and a message body containing the data to update or delete. In addition to reading and processing the URI document, you also need to retrieve the message content into a local document to work on. This can be done with the following steps:

1.      Create a request message based off the service operation request

2.      Read the request document object into a local document object

3.      Parse the message content string (either JSON or XML) onto the document

Read the document element into a local compound and work with it as you would any other document/compound.

This will read the following string into a document object you can work with through the document API:

Collections

Anytime you need to either read in a group of objects (to create or update) or return a group of objects, you will need to work with a collection object in PeopleCode. The collection object itself works a bit like an array, where you can use the following methods:

 

Method

Description

CreateItem()

Create an object of the collection

AppendItem(&element)

Appends the item at the end of the collection.

DeleteItem(&index)

Deletes the collection object at the specified index

GetItem(&index)

Retrieves the collection object at the specified index

InsertItem(&element, &index)

Inserts the object at the specified index

 

The general approach to collections is:

1.      Retrieve the collection off your document object. This can be done with either GetPropertyByName (where the name must match the collection name on the document), or with GetPropertyByIndex (where the index must match the position of the collection on the document).

2.      Create a local object (compound, primitive) from the collection with Collection.CreateItem()

3.      Set the values on that object with the standard GetPropertyByName/GetPropertyByIndex

4.      Append the item to the end of the collection, or insert at a specific position using either Collection.AppendItem() or Collection.InsertItem()

5.      Either of the above return a Boolean, so we generally have a separate class method to evaluate the return and throw an error if false




Handling Faults

Assuming you have a fault document setup on your service operation, and have implemented the IRequestHandler interface class, you can return a fault message by throwing an exception at any point in your OnRequest method. You could have a try catch block to pick up unexpected errors and return them, and then check for specific conditions and use throw CreateException to send back specific messages. To do so, the OnError method should look something like:





Where a message is created, and you then set the error property to be the value of the IBException text. The OnError message can only return a string, not a message, so you can use either &Document.GenJsonString() or &Document.GetXMLString() to return a string representation of your message.

REST Web Service Security

By Default, REST services will use the anonymous node, so if no authentication is set on your service operation, the user set there will need to have access.


Authentication Protocols

The following authentication methods are available on REST service operations:

Anything with SSL as part of the name will result in IB only accepting requests to that service operation over HTTPS. Any requests made over HTTP will result in the following:

With a 403 (forbidden) status code:

We’ve only used Basic Authentication methods, so I won’t be covering PeopleSoft Token.

Basic Authentication works with REST by passing in a user account and password as a HTTP header, where both are concatenated with a semi-colon, and then converted to a base 64 string.

For example, having a PeopleSoft user account of TestAccount, with a password of password1, would result in the authentication being passed in as:

TestAccount:password1 => Base64Encode => VGVzdEFjY291bnQ6cGFzc3dvcmQx

Any REST testing tool or development language will allow you to set a HTTP header on the request, and it can then be passed.

As REST relies on the transport protocol to define its security, and does not have any message-level security that SOAP offers, sending REST requests over HTTP does leave the username and password exposed.

Service Operation Security

For basic authentication to work, you will need an existing PeopleSoft account, and that account will need to have access to your REST service operation. At UniSA we use the following methods for service operation security:

1.      A user account is created per application or consumer – this allows us to segment off certain services and ensures that we can control who has access to what within the system

2.      Each account will have a role, or set of roles that will provide access to some service operations.

3.      The role will have a permission list, of the same name, that will list the service operations, along with any component interface access required.

4.      Each service operation will then have a set of permission lists that provide access to it, which can then be set on the Service Operation page

You could then use some SQL to quickly identify what service operations a specific user has access to, what users have access to a specific permission list or who has access to a specific service operation:

SELECT *

FROM PSAUTHWS

WHERE CLASSID = 'WS_STUDENT_PHONES';

SELECT IB_OPERATIONNAME

FROM PSAUTHWS A, PSROLEUSER B

WHERE A.CLASSID = B.ROLENAME AND B.ROLEUSER = 'INT_APPLICATION';

 

SELECT *

FROM PSROLEUSER

WHERE ROLENAME = 'WS_STUDENT_PHONES';

 

SELECT ROLEUSER

FROM PSROLEUSER A, PSAUTHWS B

WHERE B.CLASSID = A.ROLENAME AND B.IB_OPERATIONNAME = 'STUDENT_PHONE_GET';

Testing Tools for REST Web Services

Browser Based

Browser

Chrome/Chrome Application

Name

Postman

Website/Download Link

http://www.getpostman.com/

Postman is a Chrome application that can be run separate to the browser. Postman is my preferred method of testing REST services; it provides a lot of great features along with a nice-looking UI. For full documentation on Postman and how to use it, please refer to their documentation available at https://www.getpostman.com/docs/. Some feature highlights include:

 

Import

You can import an existing Postman collection (provided by another developer), a curl command, a RAML or WADL file. The important one is WADL, which is how PeopleSoft can provide its REST services via the PIA. You can paste either the raw text of your WADL or save it as a file and import.

Environment Import/Export

Postman provides an option to import/export your environment and settings to a file, which could then be imported by another person to get them setup. This could be useful if you had new developers coming on board and wanted to get them setup with some testing services in Postman.

Collections

Postman allows you to create collections of services, so you could group them logically based off how they are used/consumed. Requests can also store sample responses when saved in a collection, so you can quickly see the full request/response structure for a given service.


History

Postman will track services that are called and display a full history. This relies on your Chrome browser cache, so if that is cleared you will potentially lose anything that has not been saved as a collection or to the library.

 

Browser

Firefox

Name

RESTClient

Website/Download Link

http://restclient.net/

RESTClient is a simple REST testing app available in Firefox that is used in browser. It does not provide the full range of features that Postman does, but is useful for running quick tests or if you’re just getting started with REST - It was the first testing client/app I used for a while until Postman.

Some features of RESTClient:

Favourites

RESTClient allows you to save requests as a favourite, so you can recall it to run it in the future. It does not allow you to group these requests, so they all appear under the same drop down list.

Applications

Name

SoapUI

OS

Linux, Windows, OSX

Website/Download Link

https://www.soapui.org/ https://www.soapui.org/downloads/


SoapUI is a testing tool that can be utilised for both SOAP and REST, which means there is a good chance most developers had already had some exposure to it in the past. It has a lot of features, and depending on what you’re after for a testing tool, is probably the best option available. Some feature highlights for SoapUI:

Performance/Functional Testing

SoapUI allows you to setup functional and performance tests that can be run against your REST services/API. This is a good way to perform some basic load testing to see how the service handles higher load.

Import

SoapUI can import your services via a WADL document, or you can setup individual services via the REST URL.

Environment

SoapUI can save both SOAP and REST requests in the same working area and swap between either with ease. Depending on how extensively SoapUI has been used, it may make sense to keep all of your web service/integration tests in the one spot.

Documentation

The SoapUI website has a lot of documentation that will step through how to use each of the main features, such as Setting up a REST service, Running a Load Test, Creating a Mock REST Service.


Example REST Web Services

A sample project is available from the following URL: https://drive.google.com/open?id=0B0I9OF8NZ7kGU0VnZWlickRsVm8

The sample project contains examples of how you could setup and use GET, POST, PUT and DELETE service operations, and should contain all necessary objects and code to get you on the right track.

Import the project into whatever environment suits. Assuming it is at least 8.53 PeopleTools or above all objects should import, otherwise JSON and HTML documents may not if 8.52. If you’re below 8.52 you will need to hold off. I’d recommending importing it into a demo environment if you have one available to view the project initially and verify it. Once you’re happy you can either use the services out of your demo environment, if IB is setup, or move it to an environment that has IB running.

Below are the documents, messages, service operations and raw PeopleCode for each of the service operations – please note that it’s mostly a quick example of how to do it, the actual logic of retrieving and managing phone data will not account for all scenarios. The PUT service will technically allow a collection to be passed in, but will only process the first one, you can tweak it around to process the full collection if required.



Documents and Messages




GET Service Operation



import PS_PT:Integration:IRequestHandler;

class StudentPhoneGet_v1 implements PS_PT:Integration:IRequestHandler method StudentPhoneGet_v1();
method OnRequest(&msg As Message) Returns Message; method OnError(&request As Message) Returns string; method EvaluateAppend(&inRet As boolean);
end-class;

rem Constructor;
method StudentPhoneGet_v1 end-method;

method OnRequest
/+ &msg as Message +/
/+ Returns Message +/
/+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/ Local Document &reqDocument, &respDocument;
Local Compound &reqCompound, &respCompound, &phoneCompound; Local Collection &phoneCollection;
Local Message &response; Local boolean &ret;
Local string &studentID, &phoneType, &validPhoneType; Local Rowset &personalPhone;
Local integer &fillCount, &i;

rem Setup request document/compound; &reqDocument = &msg.GetURIDocument(); &reqCompound = &reqDocument.DocumentElement;

rem Setup response document/compound;
&response = CreateMessage(Operation.STUDENT_PHONE_GET, %IntBroker_Response);
 
&respDocument = &response.GetDocument(); &respCompound = &respDocument.DocumentElement;

rem Retrieve data from the URI passed in - property should map to values on the request document;
If All(&reqCompound.GetPropertyByName("student_id").Value) Then &studentID = &reqCompound.GetPropertyByName("student_id").value;
End-If;

If All(&reqCompound.GetPropertyByName("phone_type").Value) Then &phoneType = &reqCompound.GetPropertyByName("phone_type").value;
End-If;

If All(&phoneType) Then
rem Check for a valid phone type;
SQLExec("SELECT 1 FROM PSXLATITEM XLAT WHERE XLAT.FIELDNAME = 'PHONE_TYPE' AND
XLAT.FIELDVALUE = :1 AND %EffDtCheck(PSXLATITEM XLAT_ED, XLAT, %CurrentDateIn)", &phoneType, &validPhoneType);
If &validPhoneType <> "1" Then
rem Throw an exception - this will invoke the OnError method and return the exception message, along with the error status code set on the service operation;
throw CreateException(0, 0, &phoneType | " is an invalid phone type."); End-If;
End-If;

rem Determine what parameters came in, and what to filter on;
If All(&studentID, &phoneType) Then
&personalPhone = CreateRowset(Record.PERSONAL_PHONE);
&fillCount = &personalPhone.Fill("WHERE EMPLID = :1 AND PHONE_TYPE = :2", &studentID, &phoneType);
Else
If All(&studentID) And None(&phoneType) Then
&personalPhone = CreateRowset(Record.PERSONAL_PHONE);
&fillCount = &personalPhone.Fill("WHERE EMPLID = :1", &studentID); Else
If None(&studentID, &phoneType) Then
rem Throw an exception - this will invoke the OnError method and return the exception message, along with the error status code set on the service operation;
throw CreateException(0, 0, "Student ID required."); End-If;
End-If; End-If;


If &fillCount > 0 Then
rem Create a collection object - the property name should match the name on the response document;
&phoneCollection = &respCompound.GetPropertyByName("phoneCollection");
For &i = 1 To &personalPhone.ActiveRowCount
rem Create a collection compound object;
&phoneCompound = &phoneCollection.CreateItem();

rem Add the property values to the compound;

&phoneCompound.GetPropertyByName("student_id").Value = &personalPhone(&i).PERSONAL_PHONE.EMPLID.Value;
&phoneCompound.GetPropertyByName("phone_type").Value = &personalPhone(&i).PERSONAL_PHONE.PHONE_TYPE.Value;
&phoneCompound.GetPropertyByName("country_code").Value = &personalPhone(&i).PERSONAL_PHONE.COUNTRY_CODE.Value;
 
POST Service Operation
import PS_PT:Integration:IRequestHandler;

class StudentPhonePost_v1 implements PS_PT:Integration:IRequestHandler method OnRequest(&Msg As Message) Returns Message;
method OnError(&request As Message) Returns string; method evaluateAppend(&inRet As boolean);
end-class;

method OnRequest
/+ &Msg as Message +/
/+ Returns Message +/
/+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/ Local Document &reqURIDocument, &reqDocument, &respDocument;
Local Compound &reqURICompound, &reqCompound, &respCompound, &phoneReqCompound, &phoneRespCompound;
Local Collection &phonesCollection, &phonesResponseCollection; Local Message &response, &request;
Local boolean &ret;
Local string &studentID, &phoneType, &prefPhoneFlag, &reqJSON, &lsErrorString, &typeExists;
Local Record &phone;
Local integer &fillCount, &i;

Local ApiObject &oSession;
Local ApiObject &oPhonesTypeVwCollection; Local ApiObject &oPersonalPhonesCi;
Local ApiObject &oPersonalPhone, &oPersonalPhoneCollection, &oPSMessageCollection, &oPSMessage;

If &Msg.HTTPMethod = %IntBroker_HTTP_POST Then &reqURIDocument = &Msg.GetURIDocument(); &reqURICompound = &reqURIDocument.DocumentElement;
 
rem Retrieve data from the URI passed in - property should map to values on the request document;
If All(&reqURICompound.GetPropertyByName("student_id").Value) Then &studentID = &reqURICompound.GetPropertyByName("student_id").value;
End-If;

If All(&studentID) Then

rem Post/Put can have both a URI request document, and a body request document; &request = CreateMessage(Operation.STUDENT_PHONE_POST, %IntBroker_Request); &reqDocument = &request.GetDocument();
&ret = &reqDocument.ParseJsonString(&Msg.GetContentString()); &reqCompound = &reqDocument.DocumentElement;

rem Collections may not work with GetPropertyByName when creating a document this way - GetPropertyByIndex should work;
&phonesCollection = &reqCompound.GetPropertyByIndex(1);

rem Setup response document/compound;
&response = CreateMessage(Operation.STUDENT_PHONE_POST, %IntBroker_Response); &respDocument = &response.GetDocument();
&respCompound = &respDocument.DocumentElement;
&phonesResponseCollection = &respCompound.GetPropertyByName("phoneCollection"); &oSession = %Session;
&oSession.PSMessagesMode = 1;
&oPersonalPhonesCi = &oSession.GetCompIntfc(CompIntfc.PERSONAL_PHONES_CI);

If &oPersonalPhonesCi = Null Then
throw CreateException(0, 0, "GetCompIntfc failed"); End-If;

&oPersonalPhonesCi.InteractiveMode = False; &oPersonalPhonesCi.GetHistoryItems = True; &oPersonalPhonesCi.EditHistoryItems = True; &oPersonalPhonesCi.EMPLID = &studentID;

If Not &oPersonalPhonesCi.Get() Then
throw CreateException(1, 1, "Get failed"); End-If;

&oPersonalPhoneCollection = &oPersonalPhonesCi.PERSONAL_PHONE;

For &i = 1 To &phonesCollection.Count &phoneReqCompound = &phonesCollection.GetItem(&i);
&phoneRespCompound = &phonesResponseCollection.CreateItem();

&studentID = &phoneReqCompound.GetPropertyByName("student_id").Value; &phoneType = &phoneReqCompound.GetPropertyByName("phone_type").Value;
SQLExec("SELECT 1 FROM PS_PERSONAL_PHONE WHERE EMPLID = :1 AND PHONE_TYPE = :2",
&studentID, &phoneType, &typeExists);

rem Phone type doesn't exist, add;
If &typeExists <> "1" Then
&oPersonalPhone = &oPersonalPhoneCollection.InsertItem(1);

&oPersonalPhone.EMPLID = &phoneReqCompound.GetPropertyByName("student_id").Value;
&oPersonalPhone.PHONE_TYPE = &phoneReqCompound.GetPropertyByName("phone_type").Value;
 
&oPersonalPhone.PHONE = &phoneReqCompound.GetPropertyByName("phone").Value; &oPersonalPhone.PREF_PHONE_FLAG =
&phoneReqCompound.GetPropertyByName("preferred_flag").Value;

&phoneRespCompound.GetPropertyByName("status").Value = "OK";
Else
&phoneRespCompound.GetPropertyByName("status").Value = "Phone Type exists already, nothing new added.";
End-If;

&phoneRespCompound.GetPropertyByName("student_id").Value = &phoneReqCompound.GetPropertyByName("student_id").Value;
&phoneRespCompound.GetPropertyByName("phone_type").Value = &phoneReqCompound.GetPropertyByName("phone_type").Value;
&phoneRespCompound.GetPropertyByName("country_code").Value = &phoneReqCompound.GetPropertyByName("country_code").Value;
&phoneRespCompound.GetPropertyByName("phone").Value = &phoneReqCompound.GetPropertyByName("phone").Value;
&phoneRespCompound.GetPropertyByName("preferred_flag").Value = &phoneReqCompound.GetPropertyByName("preferred_flag").Value;

&ret = &phonesResponseCollection.AppendItem(&phoneRespCompound);

%This.evaluateAppend(&ret); End-For;

If Not &oPersonalPhonesCi.Save() Then
throw CreateException(0, 0, "Unable to create Student Phone record(s)."); End-If;
Else
throw CreateException(0, 0, "Student ID required to create record."); End-If;
End-If;

Return &response; end-method;

method OnError
/+ &request as Message +/
/+ Returns String +/
/+ Extends/implements PS_PT:Integration:IRequestHandler.OnError +/ Local Message &response;
Local Document &errorDocument; Local Compound &errorCompound;

&response = CreateMessage(Operation.STUDENT_PHONE_POST, %IntBroker_Fault); &errorDocument = &response.GetDocument();
&errorCompound = &errorDocument.DocumentElement; &errorCompound.GetPropertyByName("error").Value = &request.IBException.DefaultText;
Return &errorDocument.GenJsonString(); end-method;

method evaluateAppend
/+ &inRet as Boolean +/ If Not (&inRet) Then
throw CreateException(0, 0, "Error building JSON response document"); End-If;
end-method;

PUT Service Operation


import PS_PT:Integration:IRequestHandler;

class StudentPhonePut_v1 implements PS_PT:Integration:IRequestHandler method OnRequest(&Msg As Message) Returns Message;
method OnError(&request As Message) Returns string; method evaluateAppend(&inRet As boolean);
end-class;

method OnRequest
/+ &Msg as Message +/
/+ Returns Message +/
/+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/ Local Document &reqURIDocument, &reqDocument, &respDocument;
Local Compound &reqURICompound, &reqCompound, &respCompound, &phoneReqCompound, &phoneRespCompound;
Local Collection &phonesCollection, &phonesResponseCollection; Local Message &response, &request;
Local boolean &ret;
Local string &studentID, &phoneType, &prefPhoneFlag, &reqJSON, &lsErrorString; Local Record &phone;
Local integer &fillCount, &i;

Local ApiObject &oSession;
Local ApiObject &oPhonesTypeVwCollection; Local ApiObject &oPersonalPhonesCi;
Local ApiObject &oPersonalPhone, &oPersonalPhoneCollection, &oPSMessageCollection, &oPSMessage;

rem Setup request document/compound;
&reqURIDocument = &Msg.GetURIDocument();
 
&reqURICompound = &reqURIDocument.DocumentElement;


rem Retrieve data from the URI passed in - property should map to values on the request document;
If All(&reqURICompound.GetPropertyByName("student_id").Value) Then &studentID = &reqURICompound.GetPropertyByName("student_id").value;
End-If;

If All(&reqURICompound.GetPropertyByName("phone_type").Value) Then &phoneType = &reqURICompound.GetPropertyByName("phone_type").value;
End-If;

rem Determine what parameters came in, and what to filter on;
If All(&studentID, &phoneType) Then

rem Post/Put can have both a URI request document, and a body request document; &request = CreateMessage(Operation.STUDENT_PHONE_PUT, %IntBroker_Request); &reqDocument = &request.GetDocument();
&ret = &reqDocument.ParseJsonString(&Msg.GetContentString()); &reqCompound = &reqDocument.DocumentElement;

rem Collections may not work with GetPropertyByName when creating a document this way
- GetPropertyByIndex should work;
&phonesCollection = &reqCompound.GetPropertyByIndex(1); &phoneReqCompound = &phonesCollection.GetItem(1);

rem Setup response document/compound;
&response = CreateMessage(Operation.STUDENT_PHONE_PUT, %IntBroker_Response); &respDocument = &response.GetDocument();
&respCompound = &respDocument.DocumentElement;

&phonesResponseCollection = &respCompound.GetPropertyByName("phoneCollection");

&prefPhoneFlag = &phoneReqCompound.GetPropertyByName("preferred_flag").Value; &oSession = %Session;
&oSession.PSMessagesMode = 1;
&oPersonalPhonesCi = &oSession.GetCompIntfc(CompIntfc.PERSONAL_PHONES_CI);

If &oPersonalPhonesCi = Null Then
throw CreateException(0, 0, "GetCompIntfc failed"); End-If;

&oPersonalPhonesCi.InteractiveMode = False; &oPersonalPhonesCi.GetHistoryItems = True; &oPersonalPhonesCi.EditHistoryItems = True; &oPersonalPhonesCi.EMPLID = &studentID;

If Not &oPersonalPhonesCi.Get() Then
throw CreateException(1, 1, "Get failed"); End-If;

&oPersonalPhoneCollection = &oPersonalPhonesCi.PERSONAL_PHONE;

For &i = 1 To &oPersonalPhoneCollection.Count &oPersonalPhone = &oPersonalPhoneCollection.Item(&i);

If &oPersonalPhone.PHONE_TYPE = &phoneType Then
&oPersonalPhone.PHONE = &phoneReqCompound.GetPropertyByName("phone").Value;
 
&oPersonalPhone.PREF_PHONE_FLAG = &phoneReqCompound.GetPropertyByName("preferred_flag").Value;
Else
rem Set preferences on all phone records;
If &oPersonalPhone.PHONE_TYPE <> &phoneType And &phoneReqCompound.GetPropertyByName("preferred_flag").Value = "Y" Then
&oPersonalPhone.PREF_PHONE_FLAG = "N"; End-If;
End-If; End-For;

If Not &oPersonalPhonesCi.Save() Then
throw CreateException(0, 0, "Unable to update Student Phone record.");
Else
&phoneRespCompound = &phonesResponseCollection.CreateItem(); &phoneRespCompound.GetPropertyByName("student_id").Value =
&phoneReqCompound.GetPropertyByName("student_id").Value; &phoneRespCompound.GetPropertyByName("phone_type").Value =
&phoneReqCompound.GetPropertyByName("phone_type").Value; &phoneRespCompound.GetPropertyByName("country_code").Value =
&phoneReqCompound.GetPropertyByName("country_code").Value; &phoneRespCompound.GetPropertyByName("phone").Value =
&phoneReqCompound.GetPropertyByName("phone").Value; &phoneRespCompound.GetPropertyByName("preferred_flag").Value =
&phoneReqCompound.GetPropertyByName("preferred_flag").Value; &phoneRespCompound.GetPropertyByName("status").Value = "OK";

&ret = &phonesResponseCollection.AppendItem(&phoneRespCompound); End-If;
Else
throw CreateException(0, 0, "Both Student ID and Phone Type are required to update a phone record.");
End-If;

Return &response; end-method;

method OnError
/+ &request as Message +/
/+ Returns String +/
/+ Extends/implements PS_PT:Integration:IRequestHandler.OnError +/ Local Message &response;
Local Document &errorDocument; Local Compound &errorCompound;

&response = CreateMessage(Operation.STUDENT_PHONE_PUT, %IntBroker_Fault); &errorDocument = &response.GetDocument();
&errorCompound = &errorDocument.DocumentElement;
&errorCompound.GetPropertyByName("error").Value = &request.IBException.DefaultText; Return &errorDocument.GenJsonString();
end-method;

method evaluateAppend
/+ &inRet as Boolean +/ If Not (&inRet) Then
throw CreateException(0, 0, "Error building JSON response document"); End-If;
 

 
DELETE Service Operation





import PS_PT:Integration:IRequestHandler;

class StudentPhoneDelete_v1 implements PS_PT:Integration:IRequestHandler method OnRequest(&Msg As Message) Returns Message;
method OnError(&request As Message) Returns string; method evaluateAppend(&inRet As boolean);
end-class;

method OnRequest
/+ &Msg as Message +/
/+ Returns Message +/
/+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/ Local Document &reqDocument, &respDocument;
Local Compound &reqCompound, &respCompound; Local Message &response;
Local boolean &ret;
Local string &studentID, &phoneType, &prefPhoneFlag; Local Record &phone;
Local integer &fillCount, &i;

Local ApiObject &oSession;
Local ApiObject &oPhonesTypeVwCollection; Local ApiObject &oPersonalPhonesCi;
Local ApiObject &oPersonalPhone, &oPersonalPhoneCollection;

rem Setup request document/compound; &reqDocument = &Msg.GetURIDocument(); &reqCompound = &reqDocument.DocumentElement;

rem Setup response document/compound;
&response = CreateMessage(Operation.STUDENT_PHONE_DELETE, %IntBroker_Response); &respDocument = &response.GetDocument();
&respCompound = &respDocument.DocumentElement;
 

rem Retrieve data from the URI passed in - property should map to values on the request document;
If All(&reqCompound.GetPropertyByName("student_id").Value) Then &studentID = &reqCompound.GetPropertyByName("student_id").value;
End-If;

If All(&reqCompound.GetPropertyByName("phone_type").Value) Then &phoneType = &reqCompound.GetPropertyByName("phone_type").value;
End-If;

rem Determine what parameters came in, and what to filter on;
If All(&studentID, &phoneType) Then &oSession = %Session; &oSession.PSMessagesMode = 1;
&oPersonalPhonesCi = &oSession.GetCompIntfc(CompIntfc.PERSONAL_PHONES_CI);

If &oPersonalPhonesCi = Null Then
throw CreateException(0, 0, "GetCompIntfc failed"); End-If;

&oPersonalPhonesCi.InteractiveMode = False; &oPersonalPhonesCi.GetHistoryItems = True; &oPersonalPhonesCi.EditHistoryItems = True; &oPersonalPhonesCi.EMPLID = &studentID;

If Not &oPersonalPhonesCi.Get() Then
throw CreateException(1, 1, "Get failed"); End-If;

&oPersonalPhoneCollection = &oPersonalPhonesCi.PERSONAL_PHONE;

For &i = 1 To &oPersonalPhoneCollection.Count &oPersonalPhone = &oPersonalPhoneCollection.Item(&i);
If &oPersonalPhone.PHONE_TYPE = &phoneType Then If &oPersonalPhone.PREF_PHONE_FLAG = "Y" Then
throw CreateException(0, 0, "Unable to delete the preferred phone method.
Please ensure it is updated to another phone type first."); End-If;

&oPersonalPhoneCollection.DeleteItem(&i); End-If;
End-For;

If Not &oPersonalPhonesCi.Save() Then
throw CreateException(0, 0, "Unable to delete Student Phone record.");
Else
&respCompound.GetPropertyByName("student_id").Value = &studentID; &respCompound.GetPropertyByName("phone_type").Value = &phoneType; &respCompound.GetPropertyByName("status").Value = "OK";
End-If;
Else
throw CreateException(0, 0, "Both Student ID and Phone Type are required to delete a phone record.");
End-If;

Return &response; end-method;
 

 

Testing the Examples

If you navigate to the service operation page for the operation you’d like to test, and click on the validate button on any grid row for the URIs, you should be taken to a page that looks like:

Click the Generate URL button, and it should provide you with a URL that you can use to test the service.

Using Postman as an example, you can take that URL and paste it in the URL window:

Change the HTTP method to match what you’re testing:

If you’ve got Basic Auth setup, add the relevant account details and click Update Request:


You should see the authorization added as a HTTP header:

If you’re doing a POST or PUT and want to add the body, you can use the following to add a single or multiple:

{"student_phone_rqst": { "phone": [

{"student_id": "111111111","phone_type": "MOB3","country_code": "","phone": "0400 222 333","preferred_flag": "N"}

]}

}

{"student_phone_rqst": { "phone": [

{"student_id": "111111111","phone_type": "MOB2","country_code": "","phone": "0400 111 222","preferred_flag": "N"},

{"student_id": "111111111","phone_type": "MOB3","country_code": "","phone": "0400 222 333","preferred_flag": "N"}

]}

}


Replace the data as required and paste it into the body (raw) text body:Replace the data as required and paste it into the body (raw) text body:

Note: Body will only become available for POST and PUT
































No comments:

Post a Comment

PeopleCode to retrieve Google map between two addresses

  PeopleCode Example: /* Define constants for the API request */ Local string &origin = "123 Main St, Anytown, USA";   /* ...