From 4e6953b861e6abd271f90352c5125ca1d8912f59 Mon Sep 17 00:00:00 2001 From: "Rubens F. N. da Silva" Date: Wed, 7 Jan 2026 17:10:49 -0300 Subject: [PATCH] fix naming desync and change some API terms --- cls/Forgery/Agent.cls | 6 ++-- cls/Forgery/AgentBuilder.cls | 6 ++++ ...Handler.cls => DefaultDispatchHandler.cls} | 2 +- cls/Forgery/IAgent.cls | 6 ++-- cls/Forgery/Internal/BaseAgent.cls | 18 ++++++++--- cls/Forgery/Internal/RESTClassResolver.cls | 3 +- cls/Forgery/Internal/RequestDispatcher.cls | 20 ++++++++++-- .../integration/AgentRequestDispatchTest.cls | 31 +++++++++++++++++++ 8 files changed, 77 insertions(+), 15 deletions(-) rename cls/Forgery/CSP/{BasicDispatchHandler.cls => DefaultDispatchHandler.cls} (89%) create mode 100644 tests/integration/AgentRequestDispatchTest.cls diff --git a/cls/Forgery/Agent.cls b/cls/Forgery/Agent.cls index abdf9a5..d3ae679 100644 --- a/cls/Forgery/Agent.cls +++ b/cls/Forgery/Agent.cls @@ -6,7 +6,7 @@ Class Forgery.Agent Extends (%RegisteredObject, Forgery.Internal.BaseAgent) { /// Sends a HTTP POST request to a web application resource. -Method Post(resource As %String, data As %DynamicAbstractObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status +Method Post(resource As %String, data As %RegisteredObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status { return ..DoRequest(..#HTTPPOSTMETHOD, resource, data, overrides) } @@ -18,7 +18,7 @@ Method Get(resource As %String, queryParams As %DynamicObject = {$$$NULLOREF}, o } /// Sends a HTTP PUT request to a web application resource. -Method Put(resource As %String, data As %DynamicAbstractObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status +Method Put(resource As %String, data As %RegisteredObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status { return ..DoRequest(..#HTTPPUTMETHOD, resource, data, overrides) } @@ -36,7 +36,7 @@ Method Head(resource As %String, queryParams As %DynamicObject = {$$$NULLOREF}, } /// Sends a HTTP Patch request to a web application resource. -Method Patch(resource As %String, data As %DynamicAbstractObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status +Method Patch(resource As %String, data As %RegisteredObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status { return ..DoRequest(..#HTTPPATCHMETHOD, resource, data, overrides) } diff --git a/cls/Forgery/AgentBuilder.cls b/cls/Forgery/AgentBuilder.cls index 8b6bbf8..55eab73 100644 --- a/cls/Forgery/AgentBuilder.cls +++ b/cls/Forgery/AgentBuilder.cls @@ -21,6 +21,12 @@ Method UseDispatchHandler(handler As Forgery.IDispatchHandler) As Forgery.AgentB return $this } +Method UseDefaultDispatchHandler(handler As Forgery.IDispatchHandler) As Forgery.AgentBuilder +{ + do ..Configuration.SetDispatchHandler(##class(Forgery.CSP.DefaultDispatchHandler).%New()) + return $this +} + Method WithHeaders(headers As %DynamicObject = {{}}) As Forgery.AgentBuilder { do ..Configuration.SetRequestDefaultHeaders(headers) diff --git a/cls/Forgery/CSP/BasicDispatchHandler.cls b/cls/Forgery/CSP/DefaultDispatchHandler.cls similarity index 89% rename from cls/Forgery/CSP/BasicDispatchHandler.cls rename to cls/Forgery/CSP/DefaultDispatchHandler.cls index 366eba3..00d665e 100644 --- a/cls/Forgery/CSP/BasicDispatchHandler.cls +++ b/cls/Forgery/CSP/DefaultDispatchHandler.cls @@ -1,4 +1,4 @@ -Class Forgery.CSP.BasicDispatchHandler Extends (%RegisteredObject, Forgery.IDispatchHandler) +Class Forgery.CSP.DefaultDispatchHandler Extends (%RegisteredObject, Forgery.IDispatchHandler) { Method OnDispatch(resource As %String, httpMethod As %String, restDispatchClass As %String, cspContext As Forgery.CSP.Context, interceptor As Forgery.IO.DeviceInterceptor) As %Status diff --git a/cls/Forgery/IAgent.cls b/cls/Forgery/IAgent.cls index b2701d6..d9c92cf 100644 --- a/cls/Forgery/IAgent.cls +++ b/cls/Forgery/IAgent.cls @@ -2,7 +2,7 @@ Class Forgery.IAgent [ Abstract ] { /// Sends a HTTP POST request to a web application resource. -Method Post(resource As %String, data As %DynamicAbstractObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status +Method Post(resource As %String, data As %RegisteredObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status { $$$ThrowOnError($$$ERROR($$$MethodNotImplemented, "Post")) } @@ -14,7 +14,7 @@ Method Get(resource As %String, queryParams As %DynamicObject = {$$$NULLOREF}, o } /// Sends a HTTP PUT request to a web application resource. -Method Put(resource As %String, data As %DynamicAbstractObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status +Method Put(resource As %String, data As %RegisteredObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status { $$$ThrowOnError($$$ERROR($$$MethodNotImplemented, "Put")) } @@ -32,7 +32,7 @@ Method Head(resource As %String, queryParams As %DynamicObject = {$$$NULLOREF}, } /// Sends a HTTP PATCH request to a web application resource. -Method Patch(resource As %String, data As %DynamicAbstractObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) +Method Patch(resource As %String, data As %RegisteredObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) { $$$ThrowOnError($$$ERROR($$$MethodNotImplemented, "Patch")) } diff --git a/cls/Forgery/Internal/BaseAgent.cls b/cls/Forgery/Internal/BaseAgent.cls index 0ca0158..e62749c 100644 --- a/cls/Forgery/Internal/BaseAgent.cls +++ b/cls/Forgery/Internal/BaseAgent.cls @@ -31,19 +31,29 @@ Method %OnNew(configuration As Forgery.Configuration, requestDispatcherFactory A } /// Shortcut method for concrete agents to dispatch requests without exposing the underlaying dispatcher helper. -Method DoRequest(resource As %String, httpMethod As %String, data As %DynamicAbstractObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status [ Private ] +Method DoRequest(resource As %String, httpMethod As %String, data As %RegisteredObject = {$$$NULLOREF}, overrides As %DynamicObject = {$$$NULLOREF}) As %Status [ Private ] { return ..RequestDispatcher.Dispatch(resource, httpMethod, data, overrides) } -/// Shortcut method for retrieving the last operation context, on which operation stands for three CSP objects: request, response and session. -/// -/// Please note that the behavior of these objects may slightly differ from their real implementations. +/// Method for retrieving the last operation context, on which operation stands for three CSP objects: request, response and session. Method GetLastContext() As Forgery.CSP.Context { return ..RequestDispatcher.GetLastContext() } +/// Retrieves the %CSP.Response generated with the last dispatch. +Method GetLastResponse() As %CSP.Response +{ + return ..RequestDispatcher.GetLastContext().Response +} + +/// Retrieves the %CSP.Request generated with the last dispatch. +Method GetLastRequest() As Forgery.CSP.Request +{ + return ..RequestDispatcher.GetLastContext().Request +} + /// Retrieves the last reply sent back by the dispatch class. /// /// This is what you'd expect to receive in the browser and truthfully so, should be used for test assertions. diff --git a/cls/Forgery/Internal/RESTClassResolver.cls b/cls/Forgery/Internal/RESTClassResolver.cls index 9d60009..6f5a437 100644 --- a/cls/Forgery/Internal/RESTClassResolver.cls +++ b/cls/Forgery/Internal/RESTClassResolver.cls @@ -33,13 +33,14 @@ Method Resolve(url As %String, Output info As %DynamicObject = "") As %Status set prefixedResource = baseUrl_$select($extract(url) '= "/" : "/"_url, 1: url) // Reverts the ordering to match longer names first. - set rows = ##class(%SQL.Statement).%ExecDirect(, "SELECT TOP 1 Name, DispatchClass, Path FROM SECURITY.APPLICATIONS WHERE ? %STARTSWITH Name AND DispatchClass IS NOT NULL ORDER BY LEN(Name) DESC", prefixedResource) + set rows = ##class(%SQL.Statement).%ExecDirect(, "SELECT TOP 1 Name, DispatchClass, Path, Namespace FROM SECURITY.APPLICATIONS WHERE ? %STARTSWITH Name AND DispatchClass IS NOT NULL ORDER BY LEN(Name) DESC", prefixedResource) if rows.%Next() { set name = rows.%Get("Name") set ..WebApplicationInfoCache.Name = rows.%Get("Name") set ..WebApplicationInfoCache.DispatchClass = rows.%Get("DispatchClass") set ..WebApplicationInfoCache.Path = rows.%Get("Path") set ..WebApplicationInfoCache.AppUrl = name_$select($extract(name, *) '= "/" : "/", 1: "") + set ..WebApplicationInfoCache.Namespace = rows.%Get("NameSpace") } if ..WebApplicationInfoCache.%Size() = 0 { diff --git a/cls/Forgery/Internal/RequestDispatcher.cls b/cls/Forgery/Internal/RequestDispatcher.cls index 97a42cd..e05023d 100644 --- a/cls/Forgery/Internal/RequestDispatcher.cls +++ b/cls/Forgery/Internal/RequestDispatcher.cls @@ -62,7 +62,7 @@ Method Dispatch(httpMethod As %String, resource As %String, data As %DynamicAbst try { do ..LastContext.Request.PickFromJar(..CookieJar) do ..DeviceInterceptor.StartInterception() - $$$ThrowOnError(dispatchHandler.OnDispatch(..LastContext.Request.Resource, httpMethod, appInfo.DispatchClass, ..LastContext, ..DeviceInterceptor)) + do ..DispatchRequest(dispatchHandler, httpMethod, appInfo) do ..CookieJar.PutCookiesFromResponse(..LastContext.Response) } catch ex { set status = ex.AsStatus() @@ -71,11 +71,25 @@ Method Dispatch(httpMethod As %String, resource As %String, data As %DynamicAbst do ..DeviceInterceptor.EndInterception() $$$QuitOnError(dispatchHandler.OnDispose()) - // Ensure we are back to the original namespace after the dispatch. - set $namespace = ..InitialNamespace return status } +Method DispatchRequest(dispatchHandler As Forgery.IDispatchHandler, httpMethod As %String, appInfo As %DynamicObject) +{ + set sc = $$$OK + + try { + set $namespace = appInfo.Namespace + $$$ThrowOnError(dispatchHandler.OnDispatch(..LastContext.Request.Resource, httpMethod, appInfo.DispatchClass, ..LastContext, ..DeviceInterceptor)) + } catch ex { + set sc = ex.AsStatus() + } + + set $namespace = ..InitialNamespace + $$$ThrowOnError(sc) + return sc +} + /// Exposes the last context generated for the last request made. Method GetLastContext() As Forgery.CSP.Context { diff --git a/tests/integration/AgentRequestDispatchTest.cls b/tests/integration/AgentRequestDispatchTest.cls new file mode 100644 index 0000000..d645e57 --- /dev/null +++ b/tests/integration/AgentRequestDispatchTest.cls @@ -0,0 +1,31 @@ +Class Test.Integration.Forgery.AgentRequestDispatchTest Extends %UnitTest.TestCase +{ + +Property Agent As Forgery.Agent; + +Method OnBeforeOneTest() As %Status +{ + + set builder = ##class(Forgery.AgentBuilder).%New() + + do builder.SetWebApplication("/api/atelier") + do builder.UseDefaultDispatchHandler() + + $$$QuitOnError(builder.Build(.agent)) + + set ..Agent = agent + return $$$OK +} + +Method WithPrefix(resource As %String) [ CodeMode = expression, Private ] +{ +$$$FormatText("/v1/%1%2", $namespace, resource) +} + +Method TestGETForgerySources() +{ + set sc = ..Agent.Get(..WithPrefix("/doc/Forgery.AgentBuilder.cls")) + do $$$AssertEquals(..Agent.GetLastResponse().Status, "200 OK") +} + +}