Skip to main content

Hello,

We need to implement a REST API that will call a REST WebService of our ERP (JD Edwards).
Can anyone share a simple REST API Call created with Uniface, so I don't need to perform too many tries and errors before I get it working correctly.

Thanks,

Stephane



------------------------------
Stephane Pfefer
Application Specialist
Darling
------------------------------

Hello,

We need to implement a REST API that will call a REST WebService of our ERP (JD Edwards).
Can anyone share a simple REST API Call created with Uniface, so I don't need to perform too many tries and errors before I get it working correctly.

Thanks,

Stephane



------------------------------
Stephane Pfefer
Application Specialist
Darling
------------------------------

I managed to create this example:

variables
  handle vUhttp                  ; Handle for UHTTP instance
  struct vMainStruct, vNestedStruct ; Structures for JSON data
  string vProfileName
  string vJsonPayload             ; JSON formatted data
  string vJsonPayload2            ; JSON formatted data
  string vUrl, vMethod            ; REST API URL and method
  string vResponseStatus          ; Status from the HTTP request
  string vHeaders, vContents, vMoreContent      ; Request headers and contents
  string vSendStatus              ; Send status for error checking
endvariables

; REST API Test 
; ********** Step 1: Create UHTTP instance and set profile **********
newinstance "UHTTP", vUhttp  ; Create a new UHTTP instance
vUhttp->SET_FLAGS(15)
 
; ********** Step 2: Define a Nested Structure **********
; Create the main structure with nested elements
vMainStruct = $newstruct
vMainStruct->name = "John Doe"
vMainStruct -> email = "john.doe@example.com"
vMainStruct->Orders = $newstruct
; Add a nested structure representing an array of objects
vNestedStruct = $newstruct
vNestedStruct->id = 1
vNestedStruct->item = "Laptop"
;vMainStruct->Orders->*{-1} = $newstruct
;vMainStruct->Orders->*{-1}->order = vNestedStruct
vMainStruct->Orders->*{-1} = vNestedStruct
vNestedStruct = $newstruct
vNestedStruct->id = 2
vNestedStruct->item = "Mouse"
;vMainStruct->Orders->*{-1} = $newstruct
;vMainStruct->Orders->*{-1}->order = vNestedStruct
vMainStruct->Orders->*{-1} = vNestedStruct

; ********** Step 3: Convert Struct to JSON **********
structToJson vJsonPayload, vMainStruct
; Log the JSON payload to verify the structure
putmess "JSON Payload to send: %%vJsonPayload"
 
; ********** Step 4: Send POST Request to Postman Echo **********
vUrl = "https://postman-echo.com/post"  ; Postman Echo API
vMethod = "POST"
vResponseStatus = ""
; Initialize headers
putitem/id vHeaders, "Content-Type", "application/soap+xml; charset=UTF-8"
putitem/id vheaders, "Accept-Charset", "ISO-8859-1 ; q = 0.4, UTF-16BE; q=0.9"
vContents = "" ; Initialize contents
; Prepare the headers for the POST request
putitem/id/case vHeaders, "Content-Type", "application/json"  ; Specify JSON format
; Send the POST request with the JSON payload
vUhttp->SEND(vUrl, vMethod, "", "", vHeaders, vJsonPayload, vResponseStatus)
; Capture the send status
vSendStatus = $status
; ********** Step 5: Handle and Log Response **********
if (vSendStatus >= 200 && vSendStatus < 300)
    putmess "Request successful! Response status: %%vSendStatus"
    putmess "Response contents: %%vResponseStatus"
else
    putmess "Request failed. Status: %%vSendStatus, Response: %%vContents"
endif

When I run it, I get this in the log:
Request successful! Response status: 200
Response contents: HTTP/1.1 200 OK

I was expecting to get back the JSON as echo.
Does anyone while the response contains only "HTTP/1.1 200 OK" ?

Thanks



------------------------------
Stephane Pfefer
Application Specialist
Darling
------------------------------

I managed to create this example:

variables
  handle vUhttp                  ; Handle for UHTTP instance
  struct vMainStruct, vNestedStruct ; Structures for JSON data
  string vProfileName
  string vJsonPayload             ; JSON formatted data
  string vJsonPayload2            ; JSON formatted data
  string vUrl, vMethod            ; REST API URL and method
  string vResponseStatus          ; Status from the HTTP request
  string vHeaders, vContents, vMoreContent      ; Request headers and contents
  string vSendStatus              ; Send status for error checking
endvariables

; REST API Test 
; ********** Step 1: Create UHTTP instance and set profile **********
newinstance "UHTTP", vUhttp  ; Create a new UHTTP instance
vUhttp->SET_FLAGS(15)
 
; ********** Step 2: Define a Nested Structure **********
; Create the main structure with nested elements
vMainStruct = $newstruct
vMainStruct->name = "John Doe"
vMainStruct -> email = "john.doe@example.com"
vMainStruct->Orders = $newstruct
; Add a nested structure representing an array of objects
vNestedStruct = $newstruct
vNestedStruct->id = 1
vNestedStruct->item = "Laptop"
;vMainStruct->Orders->*{-1} = $newstruct
;vMainStruct->Orders->*{-1}->order = vNestedStruct
vMainStruct->Orders->*{-1} = vNestedStruct
vNestedStruct = $newstruct
vNestedStruct->id = 2
vNestedStruct->item = "Mouse"
;vMainStruct->Orders->*{-1} = $newstruct
;vMainStruct->Orders->*{-1}->order = vNestedStruct
vMainStruct->Orders->*{-1} = vNestedStruct

; ********** Step 3: Convert Struct to JSON **********
structToJson vJsonPayload, vMainStruct
; Log the JSON payload to verify the structure
putmess "JSON Payload to send: %%vJsonPayload"
 
; ********** Step 4: Send POST Request to Postman Echo **********
vUrl = "https://postman-echo.com/post"  ; Postman Echo API
vMethod = "POST"
vResponseStatus = ""
; Initialize headers
putitem/id vHeaders, "Content-Type", "application/soap+xml; charset=UTF-8"
putitem/id vheaders, "Accept-Charset", "ISO-8859-1 ; q = 0.4, UTF-16BE; q=0.9"
vContents = "" ; Initialize contents
; Prepare the headers for the POST request
putitem/id/case vHeaders, "Content-Type", "application/json"  ; Specify JSON format
; Send the POST request with the JSON payload
vUhttp->SEND(vUrl, vMethod, "", "", vHeaders, vJsonPayload, vResponseStatus)
; Capture the send status
vSendStatus = $status
; ********** Step 5: Handle and Log Response **********
if (vSendStatus >= 200 && vSendStatus < 300)
    putmess "Request successful! Response status: %%vSendStatus"
    putmess "Response contents: %%vResponseStatus"
else
    putmess "Request failed. Status: %%vSendStatus, Response: %%vContents"
endif

When I run it, I get this in the log:
Request successful! Response status: 200
Response contents: HTTP/1.1 200 OK

I was expecting to get back the JSON as echo.
Does anyone while the response contains only "HTTP/1.1 200 OK" ?

Thanks



------------------------------
Stephane Pfefer
Application Specialist
Darling
------------------------------

Hi Stéphane.

Have you tried to read content after the request ?

Example in the doc :

entry uHttpCallOut
params
  string pUrl         : IN
  string pUsername    : IN
  string pPassword    : IN
  string pHttpHeader  : INOUT
  string pHttpContent : INOUT
endparams
variables
  string vMoreContent, vResponse
endvariables

  activate "UHTTP".send(pUrl, "GET", pUsername, pPassword, pHttpHeader, pHttpContent, vResponse)
  while ($status = 1)
    activate "UHTTP".read_Content(vMoreContent)      ; get more data from UHTTP buffer
    pHttpContent = $concat(pHttpContent, vMoreContent)
  endwhile
  if ($status < 0)
    message/error $concat("UHTTP error:%%^", $status)
  else
    message/info $concat("UHTTP success:%%^", $status)  ; 200
  endif
  return 0
end


------------------------------
Jean-Marc SALIS
Mp Services
Montauban Cedex FR
------------------------------

Hello,

We need to implement a REST API that will call a REST WebService of our ERP (JD Edwards).
Can anyone share a simple REST API Call created with Uniface, so I don't need to perform too many tries and errors before I get it working correctly.

Thanks,

Stephane



------------------------------
Stephane Pfefer
Application Specialist
Darling
------------------------------

We get the answer in a changed vContent (json in)

operation GETBYSOSSECNO
	params
		string  P_URI       : IN
		string  P_SIGNUM    : IN
		string  P_MUNICIPALITY : IN
		string  P_TOKEN        : IN
		struct  P_STRUCTASW : OUT
		numeric P_HTTPSTAT  : OUT
	endparams
	variables
		string  vJson
		string  vContent, vHeaders, vResponse
		string  strStatBeg
		string  vUri
		handle  vUHTTP
		struct  vStruct
	endvariables
	
	
	call MakeJsonSignum(P_SIGNUM, P_MUNICIPALITY, vJson)    ;Define nested struct and convert to Json
	newinstance "UHTTP", vUHTTP
	vUHTTP->SET_FLAGS(10)   
	vURI = P_URI
	;Create list of headers 
	putitem/id vHeaders, "Content-Type", "application/json"
	putitem/id vHeaders, "Authorization", "Bearer %%P_TOKEN%%%" 
	
	vContent=vJson
	; Call the UHTTP.SEND operation
	P_HTTPSTAT = 0
	P_HTTPSTAT = vUHTTP->SEND(vURI, "POST", "", "", vHeaders, vContent, vResponse)
	if(P_HTTPSTAT<0)
		exit(P_HTTPSTAT)
	else
		strStatBeg = "%%P_HTTPSTAT%%%"
		strStatBeg = strStatBeg[1,1]
		if(P_HTTPSTAT == 404 | P_HTTPSTAT == 200)
			jsonToStruct vStruct, vContent
			P_STRUCTASW = vStruct->result
			deleteinstance vUHTTP
		elseif(strStatBeg="1" | strStatBeg="3" | strStatBeg="4" | strStatBeg="5") ;Tex 503 "HTTP/1.1 503 Service Unavailable"
			exit(P_HTTPSTAT)
		else
			jsonToStruct vStruct, vContent
			P_STRUCTASW = vStruct->result
			deleteinstance vUHTTP
			
		endif
	endif
end; operation GETBYSOSSECNO



------------------------------
Roger Wallin
Abilita Oy
------------------------------

We get the answer in a changed vContent (json in)

operation GETBYSOSSECNO
	params
		string  P_URI       : IN
		string  P_SIGNUM    : IN
		string  P_MUNICIPALITY : IN
		string  P_TOKEN        : IN
		struct  P_STRUCTASW : OUT
		numeric P_HTTPSTAT  : OUT
	endparams
	variables
		string  vJson
		string  vContent, vHeaders, vResponse
		string  strStatBeg
		string  vUri
		handle  vUHTTP
		struct  vStruct
	endvariables
	
	
	call MakeJsonSignum(P_SIGNUM, P_MUNICIPALITY, vJson)    ;Define nested struct and convert to Json
	newinstance "UHTTP", vUHTTP
	vUHTTP->SET_FLAGS(10)   
	vURI = P_URI
	;Create list of headers 
	putitem/id vHeaders, "Content-Type", "application/json"
	putitem/id vHeaders, "Authorization", "Bearer %%P_TOKEN%%%" 
	
	vContent=vJson
	; Call the UHTTP.SEND operation
	P_HTTPSTAT = 0
	P_HTTPSTAT = vUHTTP->SEND(vURI, "POST", "", "", vHeaders, vContent, vResponse)
	if(P_HTTPSTAT<0)
		exit(P_HTTPSTAT)
	else
		strStatBeg = "%%P_HTTPSTAT%%%"
		strStatBeg = strStatBeg[1,1]
		if(P_HTTPSTAT == 404 | P_HTTPSTAT == 200)
			jsonToStruct vStruct, vContent
			P_STRUCTASW = vStruct->result
			deleteinstance vUHTTP
		elseif(strStatBeg="1" | strStatBeg="3" | strStatBeg="4" | strStatBeg="5") ;Tex 503 "HTTP/1.1 503 Service Unavailable"
			exit(P_HTTPSTAT)
		else
			jsonToStruct vStruct, vContent
			P_STRUCTASW = vStruct->result
			deleteinstance vUHTTP
			
		endif
	endif
end; operation GETBYSOSSECNO



------------------------------
Roger Wallin
Abilita Oy
------------------------------

Merci Jean-Marc and Thank you Roger.
Indeed I completely missed that Content is a INOUT parameter and is changed in the operation.
Now it works very well.
Thanks again both of you.



------------------------------
Stephane Pfefer
Application Specialist
Darling
------------------------------

We get the answer in a changed vContent (json in)

operation GETBYSOSSECNO
	params
		string  P_URI       : IN
		string  P_SIGNUM    : IN
		string  P_MUNICIPALITY : IN
		string  P_TOKEN        : IN
		struct  P_STRUCTASW : OUT
		numeric P_HTTPSTAT  : OUT
	endparams
	variables
		string  vJson
		string  vContent, vHeaders, vResponse
		string  strStatBeg
		string  vUri
		handle  vUHTTP
		struct  vStruct
	endvariables
	
	
	call MakeJsonSignum(P_SIGNUM, P_MUNICIPALITY, vJson)    ;Define nested struct and convert to Json
	newinstance "UHTTP", vUHTTP
	vUHTTP->SET_FLAGS(10)   
	vURI = P_URI
	;Create list of headers 
	putitem/id vHeaders, "Content-Type", "application/json"
	putitem/id vHeaders, "Authorization", "Bearer %%P_TOKEN%%%" 
	
	vContent=vJson
	; Call the UHTTP.SEND operation
	P_HTTPSTAT = 0
	P_HTTPSTAT = vUHTTP->SEND(vURI, "POST", "", "", vHeaders, vContent, vResponse)
	if(P_HTTPSTAT<0)
		exit(P_HTTPSTAT)
	else
		strStatBeg = "%%P_HTTPSTAT%%%"
		strStatBeg = strStatBeg[1,1]
		if(P_HTTPSTAT == 404 | P_HTTPSTAT == 200)
			jsonToStruct vStruct, vContent
			P_STRUCTASW = vStruct->result
			deleteinstance vUHTTP
		elseif(strStatBeg="1" | strStatBeg="3" | strStatBeg="4" | strStatBeg="5") ;Tex 503 "HTTP/1.1 503 Service Unavailable"
			exit(P_HTTPSTAT)
		else
			jsonToStruct vStruct, vContent
			P_STRUCTASW = vStruct->result
			deleteinstance vUHTTP
			
		endif
	endif
end; operation GETBYSOSSECNO



------------------------------
Roger Wallin
Abilita Oy
------------------------------

Hi Roger
I just saw that you used "EXIT" in an operation. that's not a good idea

if(P_HTTPSTAT<0)
		exit(P_HTTPSTAT)

This exit kills your instance of the component, all further calls will fail.
Use RETURN(P_HTTPSTAT) instead :-)
Ingo



------------------------------
Ingo Stiller
Aareon Deutschland GmbH
------------------------------

Hi Roger
I just saw that you used "EXIT" in an operation. that's not a good idea

if(P_HTTPSTAT<0)
		exit(P_HTTPSTAT)

This exit kills your instance of the component, all further calls will fail.
Use RETURN(P_HTTPSTAT) instead :-)
Ingo



------------------------------
Ingo Stiller
Aareon Deutschland GmbH
------------------------------

Hi Ingo,
I suppose that you are right, it would be more efficient  not to "kill" the instance. Actually I was a bit surprised myself as I saw the Exit.
However what about "all further calls will fail", I suppose using "activate" will just start a new instance of the component.
Isn't it just about how you want the handle the instance of the component.



------------------------------
Roger Wallin
Abilita Oy
------------------------------

Hi Ingo,
I suppose that you are right, it would be more efficient  not to "kill" the instance. Actually I was a bit surprised myself as I saw the Exit.
However what about "all further calls will fail", I suppose using "activate" will just start a new instance of the component.
Isn't it just about how you want the handle the instance of the component.



------------------------------
Roger Wallin
Abilita Oy
------------------------------

Hi Ingo,
It seem that it's not possible to edit a posted message anymore (perhaps that's how it should be). I'll just send another one here.

I just started to think about this clever code :-) that I posted.
Isn't it a good solution that as you get a very strange Uniface status  or http status back, that the component is killed and the next call will activate a fresh instance of the component. 



------------------------------
Roger Wallin
Abilita Oy
------------------------------

Hi Ingo,
It seem that it's not possible to edit a posted message anymore (perhaps that's how it should be). I'll just send another one here.

I just started to think about this clever code :-) that I posted.
Isn't it a good solution that as you get a very strange Uniface status  or http status back, that the component is killed and the next call will activate a fresh instance of the component. 



------------------------------
Roger Wallin
Abilita Oy
------------------------------

Back in the early days, we discovered that if you ran an operation on a particular service, using shared uservers, and someone else ran a long operation which happened to use the same userver, you were in a queue behind them to run your operation again on the same service. 

Therefore we start (almost) all services activate/stateless, and end (almost) all operations "exit XXX". This way you are never in contention for the same userver with someone else. 

Future calls to the server will only fail if the previous program used newinstance to start a named instance, and tries to reference that instance by name again. 

If this is the way you do it Ingo, I am curious how you avoid the contention for uservers and the unintentional delays this causes? 

Regards, 

Iain



------------------------------
Iain Sharp
Head of Technical Services
Pci Systems Ltd
Sheffield GB
------------------------------

Hi Ingo,
I suppose that you are right, it would be more efficient  not to "kill" the instance. Actually I was a bit surprised myself as I saw the Exit.
However what about "all further calls will fail", I suppose using "activate" will just start a new instance of the component.
Isn't it just about how you want the handle the instance of the component.



------------------------------
Roger Wallin
Abilita Oy
------------------------------

Hi Roger
You are right, "ACTIVATE" creates a new instance if none already exists under the name.
We also have a REST interface and everything is hidden in "real" instances. Even for UHTTP, a separate instance is created via NEWINSTANCE :-)
Ingo

here is a detail from the lowest level

IF($AAK_HND_UHTTP$=="")
    newinstance "UHTTP", $AAK_HND_UHTTP$
    $AAK_HND_UHTTP$->"SET_FLAGS"(8)  ; Send headers with all methods. By default, headers are sent only on the POST and PUT methods.
  ENDIF

  IF($AAK_TOKEN$!="")
    putitem/id/case v_HEADER, "access_token",$AAK_TOKEN$
  ENDIF
  IF(0) putitem/id/case v_HEADER, "Content-Type", "text/xml; charset=UTF-8"
  IF(0)
    IF($USE_JSON$)
      putitem/id/case v_HEADER, "Content-Type", "application/json; charset=UTF-8"

    ELSE
      putitem/id/case v_HEADER, "Content-Type", "application/xml; charset=UTF-8"
    ENDIF
  ENDIF

  v_HEADER_IN   = v_HEADER
  v_CONTENT_OUT = v_CONTENT_IN

  IF(0)
    IF(v_SEC_LVL_U)
      call LP_MELD("HTTP/XML %%v_METHOD%%% '%%v_URL_PART2%%%")
    ELSE
      call LP_MELD("HTTP/XML %%v_METHOD%%% URL=***")
    ENDIF
  ENDIF

  REPEAT
    $AAK_HND_UHTTP$->"READ_CONTENT"($99)
  UNTIL($status!=1)

  $AAK_HND_UHTTP$->"SEND"("%%$AAK_URL_BASE$%%v_URL_PART2%%%",v_METHOD, "", "",v_HEADER, v_CONTENT_OUT, v_STS_RSP)
  v_STS_SND = $status
  WHILE(v_STS_SND == 1 || (v_STS_SND>=200 && v_STS_SND<=299 ) )
    $AAK_HND_UHTTP$->"READ_CONTENT"($99)
    IF($status<0) BREAK
    v_STS_SND = $status
    IF(v_STS_SND!=1 && (v_STS_SND<200 || v_STS_SND>299)  ) BREAK
    v_CONTENT_OUT = $concat(v_CONTENT_OUT,$99)
  ENDWHILE

  IF(0)
    IF(v_SEC_LVL_U)
      call LP_MELD("HTTP/XML %%v_METHOD%%% '%%v_URL_PART2%%%Ä  : STS=%%v_STS_SND%%%/%%v_STS_RSP%%%")
    ELSE
      call LP_MELD("HTTP/XML %%v_METHOD%%% URL=***  : STS=%%v_STS_SND%%%/%%v_STS_RSP%%%")
    ENDIF
  ENDIF

  IF(v_STS_SND<0)
    IF(v_SEC_LVL_U)
      call LP_ERR("UHTTP error %%v_STS_SND%%% : %%v_METHOD%%% '%%v_URL_PART2%%%'")
    ELSE
      call LP_ERR("UHTTP error %%v_STS_SND%%% : %%v_METHOD%%% URL=***")
    ENDIF
    RETURN(v_STS_SND)
  ENDIF

  IF(v_STS_SND==404)
    SELECTCASE $uppercase(v_METHOD)
    CASE "DELETE"
      RETURN(<UIOSERR_OCC_NOT_FOUND>)
    ENDSELECTCASE
    WHILE($scan(" %%"",v_CONTENT_OUT[1:1])>0) v_CONTENT_OUT = v_CONTENT_OUT[2]
    IF($uppercase(v_CONTENT_OUT[1:16])=="RECORD NOT FOUND")
      RETURN(<UIOSERR_OCC_NOT_FOUND>)
    ENDIF
  ELSEIF(v_STS_SND==403) ; 28.11.2018,IST
    RETURN(-9999)
  ENDIF

  IF(v_STS_SND>=400 || v_STS_RSP>=400)
    call LP_DBG_REST_ERR1(v_METHOD,v_URL_PART2,v_STS_SND,v_HEADER_IN,v_CONTENT_IN,v_STS_RSP,v_HEADER,v_CONTENT_OUT,v_SEC_LVL)
    RETURN($status)
  ENDIF

  IF(0) ; DBG
    call LP_DBG_REST_ERR1(v_METHOD,v_URL_PART2,v_STS_SND,v_HEADER_IN,v_CONTENT_IN,v_STS_RSP,v_HEADER,v_CONTENT_OUT,v_SEC_LVL)
  ENDIF

  RETURN(v_STS_RSP)



------------------------------
Ingo Stiller
Aareon Deutschland GmbH
------------------------------