
DO wwJsonSerializer
DO wwHttp

SET PROCEDURE TO wwJsonServiceClient ADDITIVE

*************************************************************
DEFINE CLASS wwJsonServiceClient AS Custom
*************************************************************
*: Author: Rick Strahl
*:         (c) West Wind Technologies, 2016
*:Contact: http://www.west-wind.com
*:Created: 01/11/2016
*************************************************************
#IF .F.
*:Help Documentation
*:Topic:
Class CustomerServiceClient

*:Description:
A small wrapper class that can make HTTP service calls, and
automatically serialize parameters and deserialize results
when calling JSON APIs.

You can subclass this class and use the CallService method
in your own service wrapper methods that call the service.

*:Example:

************************************************************************
*  GetCustomers
****************************************
FUNCTION Customers(loParms)
LOCAL loCustomers

loCustomers = THIS.CallService(this.cServiceBaseUrl + "customers.csvc", loParms)

IF THIS.lError
   RETURN null
ENDIF   

RETURN loCustomers
ENDFUNC
*   GetCustomers

*:Remarks:

*:SeeAlso:


*:ENDHELP
#ENDIF

*** Custom Properties

*** Stock Properties
cErrorMsg = ""
lError = .F.

cServiceBaseUrl = ""

*** The serializer used to serialize requests
*** Created for every request in `CreateSerializer()`
oSerializer = null
FUNCTION oSerializer_Access()
IF ISNULL(THIS.oSerializer)
   this.oSerializer = THIS.CreateSerializer()
ENDIF
RETURN THIS.oSerializer
ENDFUNC

*** The HTTP client used to serialize requests
*** Created for every request in `CreatewwHttp()`
oHttp = null
FUNCTION oHttp_Access()
IF ISNULL(THIS.oHttp)
   THIS.oHttp = THIS.CreatewwHttp()
ENDIF
RETURN THIS.oHttp
ENDFUNC

*** By default JSON responses are  always UTF-8 decoded. Set this .T. if the response *is not* utf-8 encoded
lNoUtf8Decoding = .F.

*** If .T. saves request data in cRequestData and cResponseData
lSaveRequestData = .F.

*** Saved Inbound JSON data if lSaveRequestData=.T.
cRequestData = ""

*** Outbound JSON data if lSaveRequestData=.T.
cResponseData = ""


************************************************************************
*  CreateSerializer
****************************************
***  Function: Sets or creates a Serializer instance
***            Use this method to customize the serializer settings
***            for things like overriding property values
***    Assume:
***      Pass:
***    Return:
************************************************************************
FUNCTION CreateSerializer(loSerializer)

IF VARTYPE(loSerializer) = "O"
   THIS.oSerializer = loSerializer
ENDIF
IF ISNULL(THIS.oSerializer)
   this.oSerializer = CREATEOBJECT("wwJsonSerializer")
ENDIF
   
RETURN this.oSerializer
ENDFUNC
*   CreateSerializer

************************************************************************
*  CreatewwHttp
****************************************
***  Function: Overrides the default wwHttp instance with a preset
***            custom instance. 
***    Assume: Set before making the actual service call
***            Metho
***      Pass: loHttp   -   (optional) Preconfigured wwHttp Instance
***    Return:
************************************************************************
FUNCTION CreatewwHttp(loHttp)

IF VARTYPE(loHttp) = "O"
    this.oHttp = loHttp
ELSE
	IF VARTYPE(this.oHttp) # "O"
		this.oHttp = CREATEOBJECT("wwHttp")
		this.oHttp.lDecodeUtf8 = .F.  && do it later optionally
	ENDIF
ENDIF	

RETURN this.oHttp
ENDFUNC
*   CreatewwHttp

************************************************************************
*  AddHeader
****************************************
***  Function: Helper function that lets you add HTTP headers
***            to the request. Same as .oHttp.AddHeader()
***            added to provide more obvious use case
***    Assume:
***      Pass: 
***    Return:
************************************************************************
FUNCTION AddHeader(lcHeader, lcValue)
this.CreatewwHttp()
this.oHttp.AddHeader(lcHeader, lcValue)
ENDFUNC
*   AddHeader

************************************************************************
*  CallService
****************************************
***  Function:
***    Assume:
***      Pass:
***    Return:
************************************************************************
FUNCTION CallService(lcUrl,lvPostData, lcVerb)
LOCAL lcResult, lcJson, loHttp, loSer, loResult

THIS.cRequestData = ""
THIS.cResponseData = ""

IF EMPTY(lcUrl)
  THIS.cErrorMsg = "Please provide a Url for calling the service."
  this.lError = .T.
  RETURN ""
ENDIF

IF (!STARTSWITH(lcUrl, "http://", .T.) AND !StartsWith(lcUrl, "https://", .T.))
   lcUrl = RTRIM(THIS.cServiceBaseUrl, "/") + "/" + LTRIM(lcUrl, "/")    
ENDIF

this.lError = .F.

IF EMPTY(lcVerb) 
	IF IsNullOrEmpty(lvPostData)
	   lcVerb = "GET"
	ELSE
	   lcVerb = "POST"
	ENDIF
ENDIF   

loHttp = this.CreatewwHttp()
loHttp.cHttpVerb = lcVerb

*** Serialize single value/object
IF !IsNullOrEmpty(lvPostData)
    loSer = THIS.CreateSerializer()
    lcJson = loSer.Serialize(lvPostData)
	loHttp.cContentType = "application/json"	
	loHttp.AddPostKey(STRCONV(lcJson,9))
	
	IF THIS.lSaveRequestData
	  this.cRequestData = lcJson
	ENDIF	
ENDIF

loHttp.lDecodeUtf8 = !this.lNoUtf8Decoding
lcResult = loHttp.HttpGet(lcUrl)

IF loHttp.nError != 0
   this.cErrorMsg = loHttp.cErrorMsg
   this.lError = .T.
ENDIF

IF THIS.lSaveRequestData
	this.cResponseData = lcResult
ENDIF

*** If the response has JSON in it
IF ATC("application/json",loHttp.cHttpHeaders) > 0
    loSer = THIS.CreateSerializer()
    loResult = loSer.DeserializeJson(lcResult)    
    RETURN loResult
ENDIF

RETURN lcResult
ENDFUNC
*   CallService

ENDDEFINE
*EOC wwJsonServiceClient