Hi George, Robert, Sean,
Thanks all for responding so promptly.
All comments have combined into what I needed...
Cheers,
Gordon
Code follows:
{
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/(.*)" Method="GET" Call="RestApi.Ris:ProcessUrl"/>
<Route Url="/(.*)" Method="POST" Call="RestApi.Ris:ProcessUrl"/>
<Route Url="/(.*)" Method="PUT" Call="RestApi.Ris:ProcessUrl"/>
<Route Url="/(.*)" Method="DELETE" Call="RestApi.Ris:ProcessUrl"/>
</Routes>
}
ClassMethod ProcessUrl(capturedUrlData As %String = "", debugCspRequest As %CSP.Request = "") As %Status
{
#dim %request As %CSP.Request
#dim %response As %CSP.Response
if ((debugCspRequest '= "") && ($IsObject(debugCspRequest)) && ($ClassName(debugCspRequest) = "%CSP.Request"))
{
set %request = debugCspRequest
set %response = ##class(%CSP.Response).%New()
}
Method Implementation continues...
{
/*
Method OnBeforeOneTest() As %Status
{
/// Code to run Before Test/s
///
/// Quit $$$OK
}
*/
ClassMethod MockCspRequest(requestMethod As %String = "", requestUri As %String = "", jsonPayload As %String = "", authorizationToken As %String = "") As %Net.HttpRequest
{
#dim status As %Status
#dim cspRequest As %CSP.Request = ##class(%CSP.Request).%New()
set cspRequest.CharSet = "UTF-8"
if (('$Data(requestMethod)) || ($IsObject(requestMethod)) || (requestMethod = "")) return ""
if (('$Data(requestUri)) || ($IsObject(requestUri)) || (requestUri = "")) return ""
if (('$Data(jsonPayload)) || ($IsObject(jsonPayload)) || ($zStrip(jsonPayload, "<>", " ") = "")) set jsonPayload = ""
if (('$Data(authorizationToken)) || ($IsObject(authorizationToken)) || ($zStrip(authorizationToken, "<>", " ") = "")) set authorizationToken = ""
if ((authorizationToken '= "") && ('##class(RestApi.Helpers.String).IsValidGuid(authorizationToken))) return ""
#dim patternRequestMethod as %String = "\A(?i)Get|Post|Put|Delete\z"
#dim regex as %Regex.Matcher = ##class(%Regex.Matcher).%New(patternRequestMethod, requestMethod)
#dim isMatch as %Boolean = regex.Locate()
if ('isMatch) return ""
// Request Method
set requestMethod = $zConvert(requestMethod, "U")
set cspRequest.CgiEnvs("REQUEST_METHOD") = requestMethod
set cspRequest.Method = requestMethod
// URI Endpoint validation
#dim patternEndpoint as %String = "(?i)\A(https?|ftp)://([-A-Z0-9.]+)(?::(\d{1,5}))?(/[-A-Z0-9+&@#/%=~_|!:,.;]*)?(\?[A-Z0-9+&@#/%=~_|!:,.;]*)?\z"
// Standard Naming (G1 Protocol) (G2 Domain ) (G3 Port) (G4 File ) (G5 QueryParameters )
// Caché Doco Naming (G1 Protocol) (G2 Server ) (G3 Port) (G4 Location ) (G5 QueryParameters )
set regex = ##class(%Regex.Matcher).%New(patternEndpoint, requestUri)
set isMatch = regex.Locate()
if ('isMatch) return ""
#dim protocol as %String = $zConvert(regex.GroupGet(1),"L")
#dim server as %String = regex.GroupGet(2)
#dim port as %String = regex.GroupGet(3)
#dim location as %String = regex.GroupGet(4)
#dim queryParams as %String = regex.GroupGet(5)
if (protocol = "ftp") return ""
if (port > 65535) return ""
set cspRequest.CgiEnvs("REQUEST_SCHEME") = protocol
set cspRequest.CgiEnvs("REQUEST_URI") = requestUri
set cspRequest.URL = location
if (authorizationToken '= "")
{
set cspRequest.CgiEnvs("HTTP_AUTHORIZATION") = authorizationToken
}
if ((jsonPayload '= "") && (requestMethod '= "GET"))
{
#dim cspStream As %CSP.Stream = ##class(%CSP.CharacterStream).%New()
set cspStream.CharSet = "utf-8"
set cspStream.ContentType = "application/json"
set status = cspStream.MimeSectionSet(jsonPayload)
set cspRequest.Content = cspStream
}
set queryParams = $zStrip(queryParams, "<>", " ")
if (queryParams = "") return cspRequest
#dim kvpData as %String = queryParams
#dim kvpElement as %String
#dim key as %String
#dim value as %String
#dim patternKvpElement as %String = "(?i)(\??(?:[a-z0-9]*?=[a-z0-9]*?)(?:&|\z))"
#dim patternKvp as %String = "(?i)(?:\??([a-z0-9]*?)=([a-z0-9]*?)(?:&|\z))"
set regex = ##class(%Regex.Matcher).%New(patternKvpElement, kvpData)
#dim isMatchKvpElement As %Boolean = regex.Locate()
while (isMatchKvpElement)
{
set kvpElement = regex.GroupGet(1)
set regex = ##class(%Regex.Matcher).%New(patternKvp, kvpElement)
set isMatch = regex.Locate()
if (isMatch)
{
set key = regex.GroupGet(1)
set value = regex.GroupGet(2)
Set cspRequest.Data("Name",$Order(cspRequest.Data(key, ""), -1) +1) = value
}
// Get Next KvpElement
set kvpData = $Extract(kvpData, ($Length(kvpElement) + 1), *-0)
if (kvpData '= "")
{
set regex = ##class(%Regex.Matcher).%New(patternKvpElement, kvpData)
set isMatchKvpElement = regex.Locate()
}
else
{
set isMatchKvpElement = 0
}
}
return cspRequest
}
Method TestProcessUrl()
{
// -------------------------------------------------------------------------------------------------------------------
// Arrange
#dim resultActual As %Status
#dim resultExpected As %Status
#dim status As %Status
#dim queryString As %String = "?qParam01=123456&qParam02=ABCDEF"
#dim mockUrl As %String = "http://1.2.3.4:80/SomeLocation"_queryString
#dim dq As %String = $Char(34)
#dim message As %String = "Unit Test REST API method without Web Server and complete line by line debug"
#dim jsonPayload As %String = "{"
set jsonPayload = jsonPayload_dq_"source"_dq_":"_dq_"Gordo"_dq _","
set jsonPayload = jsonPayload_dq_"urlMethod"_dq_":"_dq_"UnitTest"_dq _","
set jsonPayload = jsonPayload_dq_"text"_dq_":"_dq_message_dq
set jsonPayload = jsonPayload_"}"
#dim cspRequest As %CSP.Request = ..MockCspRequest("Post", mockUrl, jsonPayload)
Do $$$AssertTrue($IsObject(cspRequest), "cspRequest instance created")
#dim cspUrl As %String = cspRequest.URL
#dim cspMethod As %String = cspRequest.Method
#dim cspContentType As %String = cspRequest.ContentType
#dim patternRouteCaptue as %String = "/(.*)" // <Route Url="/(.*)"
#dim regex as %Regex.Matcher = ##class(%Regex.Matcher).%New(patternRouteCaptue, cspUrl)
#dim isMatch as %Boolean = regex.Locate()
Do $$$AssertTrue(isMatch, "Route has capturedUrlData")
#dim capturedUrlData As %String = regex.GroupGet(1)
// -------------------------------------------------------------------------------------------------------------------
// Act / Assert
set resultActual = ##class(RestApi.Ris).ProcessUrl(capturedUrlData, cspRequest)
Method Implementation continues...
Hi George,
I'm most interested in your Question on Unit Testing a REST service.
The approach I've taken seems similar to yours. Unfortunately I've not yet cracked a working Unit Test implementation for this scenario.
Equally I had already studied the documentation that 'Robert Cemper' suggested in his answer links. Also I note in the following link:
http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=...
that a bunch of the %CSP.Stream methods are being deprecated from 2016.1 on. I have been unable to locate their replacement or alternative approach.
If you, Robert or anyone in the community can supply a working code sample of how to set the parameters of a %CSP.Request object would be much appreciated.
The following code compiles but fails on execution.
#dim status As %Status
#dim cspRequest As %CSP.Request = ##class(%CSP.Request).%New()
set status = cspRequest.Content.ContentTypeSet("application/json") => Fails => OREF Failure => I assume to do with %CSP.Stream
My aim with the Unit Test is to somehow have the class that Extends %CSP.REST be instansiated or ingest the cspRequest As %CSP.Request. This would allow for directly debugging the ClassMethods of the %CSP.REST class.
Much appreciate your assistance.
Cheers,
Gordon
Hi Eduard,
Much appreciate your fast response.
OK... That makes sense... so I assume the Tutorials are assuming you have a server instance on a local machine and hence the local files would then work...
Cheers,
Gordon