go to post Gordon Hodgkinson · Feb 20, 2018 Hi George, Robert, Sean,Thanks all for responding so promptly.All comments have combined into what I needed...Cheers,GordonCode follows: Class RestApi.Ris Extends %CSP.REST{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.Responseif ((debugCspRequest '= "") && ($IsObject(debugCspRequest)) && ($ClassName(debugCspRequest) = "%CSP.Request")){set %request = debugCspRequestset %response = ##class(%CSP.Response).%New()}Method Implementation continues... Class UnitTests.RestApi.Ris.RisUnitTests Extends Atomic.UnitTest.TestHelper{/*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 Methodset requestMethod = $zConvert(requestMethod, "U")set cspRequest.CgiEnvs("REQUEST_METHOD") = requestMethodset 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") = protocolset cspRequest.CgiEnvs("REQUEST_URI") = requestUriset cspRequest.URL = locationif (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 KvpElementset 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_dqset 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 / Assertset resultActual = ##class(RestApi.Ris).ProcessUrl(capturedUrlData, cspRequest)Method Implementation continues...
go to post Gordon Hodgkinson · Feb 18, 2018 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.StreamMy 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
go to post Gordon Hodgkinson · Apr 10, 2017 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