Skip to content

Commit 6b75dd8

Browse files
committed
Add first draft GetStorageUrlTrait based on existing code.
1 parent fd20e41 commit 6b75dd8

2 files changed

Lines changed: 236 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace OCA\Solid\Controller;
4+
5+
use OCA\Solid\BaseServerConfig;
6+
use OCP\IURLGenerator;
7+
8+
trait GetStorageUrlTrait
9+
{
10+
//////////////////////////// GETTERS AND SETTERS \\\\\\\\\\\\\\\\\\\\\\\\\\\
11+
12+
final public function setConfig(BaseServerConfig $config): void
13+
{
14+
$this->config = $config;
15+
}
16+
17+
final public function setUrlGenerator(IURLGenerator $urlGenerator): void
18+
{
19+
$this->urlGenerator = $urlGenerator;
20+
}
21+
22+
////////////////////////////// CLASS PROPERTIES \\\\\\\\\\\\\\\\\\\\\\\\\\\\
23+
24+
protected BaseServerConfig $config;
25+
protected IURLGenerator $urlGenerator;
26+
27+
/////////////////////////////// PROTECTED API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
28+
29+
/**
30+
* @FIXME: Deduplicate multiple declarations of getStorageUrl()
31+
* @FIXME: Add check for bob.nextcloud.local/solid/alice to throw 404
32+
* @TODO: Use route without `@alice` in /apps/solid/@alice/profile/card#me when user-domains are enabled
33+
*/
34+
protected function getStorageUrl($userId) {
35+
$routeUrl = $this->urlGenerator->linkToRoute(
36+
'solid.storage.handleHead',
37+
['userId' => $userId, 'path' => 'foo']
38+
);
39+
40+
$storageUrl = $this->urlGenerator->getAbsoluteURL($routeUrl);
41+
42+
// (?) $storageUrl = preg_replace('/foo$/', '', $storageUrl);
43+
$storageUrl = preg_replace('/foo$/', '/', $storageUrl);
44+
45+
// @FIXME: $this->getUserSubDomainsEnabled should contain true/false from (?) somewhere
46+
if ($this->config->getUserSubDomainsEnabled()) {
47+
// @FIXME: Check whether we are already on a domain that starts with $userId,
48+
// in which case it should not be prepended again
49+
$url = parse_url($storageUrl);
50+
$url['host'] = $userId . '.' . $url['host']; // $storageUrl = $userId . '.' . $storageUrl;
51+
$storageUrl = $this->build_url($url);
52+
}
53+
54+
return $storageUrl;
55+
}
56+
57+
////////////////////////////// UTILITY METHODS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\
58+
59+
private function build_url(array $parts) {
60+
// @FIXME: Replace with existing more robust URL builder
61+
return (isset($parts['scheme']) ? "{$parts['scheme']}:" : '') .
62+
(isset($parts['host']) ? "//{$parts['host']}" : '') .
63+
(isset($parts['port']) ? ":{$parts['port']}" : '') .
64+
(isset($parts['path']) ? "{$parts['path']}" : '') .
65+
(isset($parts['query']) ? "?{$parts['query']}" : '') .
66+
(isset($parts['fragment']) ? "#{$parts['fragment']}" : '');
67+
}
68+
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
3+
namespace OCA\Solid\Controller;
4+
5+
use Error;
6+
use OCA\Solid\BaseServerConfig;
7+
use OCP\IURLGenerator;
8+
use PHPUnit\Framework\MockObject\MockObject;
9+
use PHPUnit\Framework\TestCase;
10+
use ReflectionObject;
11+
12+
/**
13+
* @coversDefaultClass \OCA\Solid\Controller\GetStorageUrlTrait
14+
* @covers ::setConfig
15+
* @covers ::setUrlGenerator
16+
*/
17+
class GetStorageUrlTraitTest extends TestCase
18+
{
19+
////////////////////////////////// FIXTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
20+
21+
const MOCK_URL = 'mock url';
22+
const MOCK_USER_ID = 'mock user id';
23+
24+
private $trait;
25+
26+
protected function setUp(): void
27+
{
28+
$this->trait = new class {
29+
use GetStorageUrlTrait;
30+
31+
public function _getStorageUrl($userId)
32+
{
33+
$class = new ReflectionObject($this);
34+
$method = $class->getMethod('getStorageUrl');
35+
// Only needed for PHP 8.1 and lower
36+
$method->setAccessible(true);
37+
38+
return $method->invokeArgs($this, [$userId]);
39+
}
40+
};
41+
}
42+
43+
/////////////////////////////////// TESTS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
44+
45+
/**
46+
* @testdox GetStorageUrlTrait should complain when called before given a UrlGenerator
47+
* @covers ::getStorageUrl
48+
*/
49+
public function testGetStorageUrlWithoutUrlGenerator()
50+
{
51+
$this->expectException(Error::class);
52+
$this->expectExceptionMessage('urlGenerator must not be accessed before initialization');
53+
54+
$this->trait->_getStorageUrl(self::MOCK_USER_ID);
55+
}
56+
57+
/**
58+
* @testdox GetStorageUrlTrait should complain when called before given a Configuration
59+
* @covers ::getStorageUrl
60+
*/
61+
public function testGetStorageUrlWithoutConfig()
62+
{
63+
$mockUrlGenerator = $this->getMockUrlGenerator(self::MOCK_URL);
64+
65+
$this->expectException(Error::class);
66+
$this->expectExceptionMessage('config must not be accessed before initialization');
67+
68+
$this->trait->setUrlGenerator($mockUrlGenerator);
69+
70+
$this->trait->_getStorageUrl(self::MOCK_USER_ID);
71+
}
72+
73+
/**
74+
* @testdox GetStorageUrlTrait should return a string when called with a UrlGenerator and Configuration
75+
* @covers ::getStorageUrl
76+
* @dataProvider provideSubDomainsDisabledUrls
77+
*/
78+
public function testGetStorageUrlWithUserSubDomainsDisabled($url, $userId, $expected)
79+
{
80+
$mockConfig = $this->getMockConfig();
81+
$mockUrlGenerator = $this->getMockUrlGenerator($url);
82+
83+
$this->trait->setUrlGenerator($mockUrlGenerator);
84+
$this->trait->setConfig($mockConfig);
85+
86+
$actual = $this->trait->_getStorageUrl($userId);
87+
88+
$this->assertEquals($expected, $actual);
89+
}
90+
91+
/**
92+
* @testdox GetStorageUrlTrait should return a string when called with a UrlGenerator and Configuration
93+
* @covers ::getStorageUrl
94+
* @covers ::build_url
95+
*
96+
* @dataProvider provideSubDomainsEnabledUrls
97+
*/
98+
public function testGetStorageUrlWithUserSubDomainsEnabled($url, $userId, $expected)
99+
{
100+
$mockUrlGenerator = $this->getMockUrlGenerator($url);
101+
$mockConfig = $this->getMockConfig(true);
102+
103+
$this->trait->setUrlGenerator($mockUrlGenerator);
104+
$this->trait->setConfig($mockConfig);
105+
106+
$actual = $this->trait->_getStorageUrl($userId);
107+
108+
$this->assertEquals($expected, $actual);
109+
}
110+
111+
////////////////////////////// MOCKS AND STUBS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\
112+
113+
public function getMockConfig($enabled = false): MockObject|BaseServerConfig
114+
{
115+
$mockConfig = $this->getMockBuilder(BaseServerConfig::class)
116+
->disableOriginalConstructor()
117+
->getMock();
118+
119+
$mockConfig->expects($this->any())
120+
->method('getUserSubDomainsEnabled')
121+
->willReturn($enabled);
122+
123+
return $mockConfig;
124+
}
125+
126+
public function getMockUrlGenerator($url): MockObject|IURLGenerator
127+
{
128+
$mockUrlGenerator = $this
129+
->getMockBuilder(IURLGenerator::class)
130+
->getMock();
131+
132+
$mockUrlGenerator->expects($this->atLeast(1))
133+
->method('getAbsoluteURL')
134+
->willReturn($url);
135+
136+
return $mockUrlGenerator;
137+
}
138+
139+
/////////////////////////////// DATAPROVIDERS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
140+
141+
public function provideSubDomainsDisabledUrls()
142+
{
143+
return [
144+
['url' => 'example.com/foo', 'userId' => 'alice', 'expected' => 'example.com//'],
145+
['url' => 'https://example.com/foo', 'userId' => 'alice', 'expected' => 'https://example.com//'],
146+
['url' => 'http://example.com/foo', 'userId' => 'alice', 'expected' => 'http://example.com//'],
147+
['url' => 'https://bob.example.com/foo', 'userId' => 'alice', 'expected' => 'https://bob.example.com//'],
148+
['url' => 'http://bob.example.com/foo', 'userId' => 'alice', 'expected' => 'http://bob.example.com//'],
149+
['url' => 'https://bob.example.com/foo', 'userId' => 'bob', 'expected' => 'https://bob.example.com//'],
150+
['url' => 'http://bob.example.com/foo', 'userId' => 'bob', 'expected' => 'http://bob.example.com//'],
151+
];
152+
}
153+
154+
public function provideSubDomainsEnabledUrls()
155+
{
156+
return [
157+
// @FIXME: "Undefined array key 'host'" caused by the use of `parse_url`
158+
// ['url' => 'example.com/foo', 'userId' => 'alice', 'expected' => 'example.com//'],
159+
160+
['url' => 'https://example.com/foo', 'userId' => 'alice', 'expected' => 'https://alice.example.com//'],
161+
['url' => 'http://example.com/foo', 'userId' => 'alice', 'expected' => 'http://alice.example.com//'],
162+
['url' => 'https://bob.example.com/foo', 'userId' => 'alice', 'expected' => 'https://alice.bob.example.com//'],
163+
['url' => 'http://bob.example.com/foo', 'userId' => 'alice', 'expected' => 'http://alice.bob.example.com//'],
164+
['url' => 'https://bob.example.com/foo', 'userId' => 'bob', 'expected' => 'https://bob.bob.example.com//'],
165+
['url' => 'http://bob.example.com/foo', 'userId' => 'bob', 'expected' => 'http://bob.bob.example.com//'],
166+
];
167+
}
168+
}

0 commit comments

Comments
 (0)