From af97a109e18705a1c7f3c6de99dba3daddf9b0a8 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 3 Feb 2025 13:39:07 +0100 Subject: [PATCH] Delegate URL handling, namespacing and user information --- ChangeLog.md | 6 ++ src/main/php/web/frontend/HtmxFlow.class.php | 44 ++++++++++++- .../frontend/unittest/HtmxFlowTest.class.php | 62 ++++++++++++++----- 3 files changed, 94 insertions(+), 18 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index d5356b2..22f36ff 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,12 @@ HTMX for XP web frontends change log ## ?.?.? / ????-??-?? +## 1.0.0 / 2025-02-03 + +* Delegated URL handling, namespacing and user information to underyling + flow instances. + (@thekid) + ## 0.3.0 / 2024-03-29 * Dropped support for PHP 7.0 - 7.3, step 1 of xp-framework/rfc#343 diff --git a/src/main/php/web/frontend/HtmxFlow.class.php b/src/main/php/web/frontend/HtmxFlow.class.php index 7ee3987..e8d53bb 100755 --- a/src/main/php/web/frontend/HtmxFlow.class.php +++ b/src/main/php/web/frontend/HtmxFlow.class.php @@ -1,6 +1,6 @@ delegate= $delegate; } + /** + * Sets session namespace for this flow. Used to prevent conflicts + * in session state with multiple OAuth flows in place. + * + * @param string $namespace + * @return self + */ + public function namespaced($namespace) { + $this->delegate->namespaced($namespace); + return $this; + } + + /** + * Targets a given URL + * + * @param web.auth.URL $url + * @return self + */ + public function target(URL $url) { + $this->delegate->target($url); + return $this; + } + + /** + * Returns URL + * + * @param bool $default + * @return ?web.auth.URL + */ + public function url($default= false): URL { + return $this->delegate->url($default); + } + + /** + * Returns a user info instance + * + * @return web.auth.UserInfo + */ + public function userInfo(): UserInfo { + return $this->delegate->userInfo(); + } + /** * Refreshes access token given a refresh token if necessary. * diff --git a/src/test/php/web/frontend/unittest/HtmxFlowTest.class.php b/src/test/php/web/frontend/unittest/HtmxFlowTest.class.php index 9ffcd01..126bb74 100755 --- a/src/test/php/web/frontend/unittest/HtmxFlowTest.class.php +++ b/src/test/php/web/frontend/unittest/HtmxFlowTest.class.php @@ -2,7 +2,7 @@ use lang\IllegalStateException; use test\{Assert, Test}; -use web\auth\{Authorization, Flow}; +use web\auth\{Authorization, Flow, UseRequest, UseURL, UserInfo}; use web\frontend\HtmxFlow; use web\io\{TestInput, TestOutput}; use web\{Request, Response}; @@ -16,23 +16,53 @@ private function authenticate(array $headers, Flow $flow): Response { return $res; } + /** Returns a flow delegate */ + private function delegate(?callable $auth): Flow { + return newinstance(Flow::class, [], [ + 'userInfo' => function(): UserInfo { return new UserInfo('strtoupper'); }, + 'namespace' => function() { return $this->namespace; }, + 'authenticate' => $auth ?? function($request, $response, $session) { /* NOOP */ }, + ]); + } + #[Test] public function can_create() { - new HtmxFlow(new class() extends Flow { - public function authenticate($request, $response, $session) { - // NOOP - } - }); + new HtmxFlow($this->delegate(null)); + } + + #[Test] + public function returns_delegates_user_information() { + Assert::equals('TEST', (new HtmxFlow($this->delegate(null)))->userInfo()('test')); + } + + #[Test] + public function returns_delegates_default_url() { + Assert::instance(UseRequest::class, (new HtmxFlow($this->delegate(null)))->url(true)); + } + + #[Test] + public function delegates_namespacing() { + $delegate= $this->delegate(null); + (new HtmxFlow($delegate))->namespaced('test'); + + Assert::equals('test', $delegate->namespace()); + } + + #[Test] + public function delegates_target_url() { + $delegate= $this->delegate(null); + $url= new UseURL('https://example.com'); + (new HtmxFlow($delegate))->target($url); + + Assert::equals($url, $delegate->url()); } #[Test] public function delegates_authentication() { - $res= $this->authenticate([], new HtmxFlow(new class() extends Flow { - public function authenticate($request, $response, $session) { - $response->answer(302); - $response->header('Location', '/login'); - } - })); + $res= $this->authenticate([], new HtmxFlow($this->delegate(function($request, $response, $session) { + $response->answer(302); + $response->header('Location', '/login'); + }))); Assert::equals(302, $res->status()); Assert::equals('/login', $res->headers()['Location']); @@ -40,11 +70,9 @@ public function authenticate($request, $response, $session) { #[Test] public function returns_error_code_and_triggers_authenticationexpired_event() { - $res= $this->authenticate(['HX-Request' => 'true'], new HtmxFlow(new class() extends Flow { - public function authenticate($request, $response, $session) { - throw new IllegalStateException('Never called'); - } - })); + $res= $this->authenticate(['HX-Request' => 'true'], new HtmxFlow($this->delegate(function($request, $response, $session) { + throw new IllegalStateException('Never called'); + }))); Assert::equals(401, $res->status()); Assert::equals('authenticationexpired', $res->headers()['HX-Trigger']);