Skip to content

Commit

Permalink
Delegate URL handling, namespacing and user information
Browse files Browse the repository at this point in the history
  • Loading branch information
thekid committed Feb 3, 2025
1 parent 594c05e commit af97a10
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 18 deletions.
6 changes: 6 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 43 additions & 1 deletion src/main/php/web/frontend/HtmxFlow.class.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php namespace web\frontend;

use web\auth\Flow;
use web\auth\{Flow, URL, UserInfo};

/**
* Wraps around any authentication flow and ensures that if there is the
Expand All @@ -16,6 +16,48 @@ class HtmxFlow extends Flow {
/** Creates a new instance, wrapping around a given flow */
public function __construct(Flow $delegate) { $this->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.
*
Expand Down
62 changes: 45 additions & 17 deletions src/test/php/web/frontend/unittest/HtmxFlowTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -16,35 +16,63 @@ 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']);
}

#[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']);
Expand Down

0 comments on commit af97a10

Please sign in to comment.