Skip to content

Commit 6d6f1be

Browse files
author
Thorsten Suckow-Homberg
committed
refactor: add draft for ResourceResolver
refs #8
1 parent 14d5cb4 commit 6d6f1be

File tree

6 files changed

+533
-0
lines changed

6 files changed

+533
-0
lines changed

src/JsonApi/Resource/Resource.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/**
4+
* conjoon
5+
* php-lib-conjoon
6+
* Copyright (C) 2022 Thorsten Suckow-Homberg https://github.com/conjoon/php-lib-conjoon
7+
*
8+
* Permission is hereby granted, free of charge, to any person
9+
* obtaining a copy of this software and associated documentation
10+
* files (the "Software"), to deal in the Software without restriction,
11+
* including without limitation the rights to use, copy, modify, merge,
12+
* publish, distribute, sublicense, and/or sell copies of the Software,
13+
* and to permit persons to whom the Software is furnished to do so,
14+
* subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included
17+
* in all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25+
* USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
*/
27+
28+
declare(strict_types=1);
29+
30+
namespace Conjoon\JsonApi\Resource;
31+
32+
use Conjoon\Net\Uri\Component\Path\Template;
33+
34+
/**
35+
* Interface for Resources that serve as conceptually representatives for discoverable entities.
36+
*/
37+
interface Resource
38+
{
39+
/**
40+
* Returns the URI template where this resource is located found.
41+
*
42+
* @return Template
43+
*/
44+
public function getUri(): Template;
45+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* conjoon
5+
* php-lib-conjoon
6+
* Copyright (C) 2022 Thorsten Suckow-Homberg https://github.com/conjoon/php-lib-conjoon
7+
*
8+
* Permission is hereby granted, free of charge, to any person
9+
* obtaining a copy of this software and associated documentation
10+
* files (the "Software"), to deal in the Software without restriction,
11+
* including without limitation the rights to use, copy, modify, merge,
12+
* publish, distribute, sublicense, and/or sell copies of the Software,
13+
* and to permit persons to whom the Software is furnished to do so,
14+
* subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included
17+
* in all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25+
* USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
*/
27+
28+
declare(strict_types=1);
29+
30+
namespace Conjoon\JsonApi\Resource;
31+
32+
/**
33+
* Tagging interface for Resources that represent resource collections.
34+
*/
35+
interface ResourceCollection extends Resource
36+
{
37+
}

src/JsonApi/Resource/ResourceList.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
/**
4+
* conjoon
5+
* php-lib-conjoon
6+
* Copyright (C) 2022 Thorsten Suckow-Homberg https://github.com/conjoon/php-lib-conjoon
7+
*
8+
* Permission is hereby granted, free of charge, to any person
9+
* obtaining a copy of this software and associated documentation
10+
* files (the "Software"), to deal in the Software without restriction,
11+
* including without limitation the rights to use, copy, modify, merge,
12+
* publish, distribute, sublicense, and/or sell copies of the Software,
13+
* and to permit persons to whom the Software is furnished to do so,
14+
* subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included
17+
* in all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25+
* USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
*/
27+
28+
declare(strict_types=1);
29+
30+
namespace Conjoon\JsonApi\Resource;
31+
32+
use Conjoon\Core\AbstractList;
33+
use InvalidArgumentException;
34+
use OutOfBoundsException;
35+
use TypeError;
36+
37+
/**
38+
* An abstract list maintaining entities of the type Resource.
39+
* This implementation makes sure that no URI duplicates may exist in a ResourceList-object.
40+
*
41+
* @extends AbstractList<Resource>
42+
*/
43+
class ResourceList extends AbstractList
44+
{
45+
/**
46+
* @inheritdoc
47+
*
48+
* @throws InvalidArgumentException|OutOfBoundsException|TypeError
49+
*/
50+
public function offsetSet(mixed $offset, mixed $value): void
51+
{
52+
$this->assertTypeFor($value);
53+
54+
/**
55+
* @var Resource $value
56+
*/
57+
$value = $this->uriExists($value);
58+
59+
$this->doInsert($offset, $value);
60+
}
61+
62+
/**
63+
* Returns the Resource itself if its URI does not already exist in *this* ResourceList.
64+
*
65+
* @param Resource $resource
66+
* @return Resource
67+
*
68+
* @throws InvalidArgumentException if the URI of the resource was already registered with
69+
* this ResourceList.
70+
*/
71+
private function uriExists(Resource $resource): Resource
72+
{
73+
$uri = $resource->getUri();
74+
75+
if (count(array_filter($this->data, fn ($res) => $res->getUri()->equals($uri))) > 0) {
76+
throw new InvalidArgumentException(
77+
"A resource for the URI \"" . $uri->toString() . "\" already exists"
78+
);
79+
}
80+
81+
return $resource;
82+
}
83+
84+
85+
/**
86+
* @return string
87+
*/
88+
public function getEntityType(): string
89+
{
90+
return Resource::class;
91+
}
92+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
/**
4+
* conjoon
5+
* php-lib-conjoon
6+
* Copyright (C) 2022 Thorsten Suckow-Homberg https://github.com/conjoon/php-lib-conjoon
7+
*
8+
* Permission is hereby granted, free of charge, to any person
9+
* obtaining a copy of this software and associated documentation
10+
* files (the "Software"), to deal in the Software without restriction,
11+
* including without limitation the rights to use, copy, modify, merge,
12+
* publish, distribute, sublicense, and/or sell copies of the Software,
13+
* and to permit persons to whom the Software is furnished to do so,
14+
* subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included
17+
* in all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25+
* USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
*/
27+
28+
declare(strict_types=1);
29+
30+
namespace Conjoon\JsonApi\Resource;
31+
32+
use Conjoon\Net\Uri;
33+
34+
/**
35+
* Provides functionality for resolving URIs to an actual resource.
36+
*
37+
* @example
38+
*
39+
* class EmployeeResource implements Resource {
40+
* public function getUri(): Template
41+
* {
42+
* return new Template("/directory/employees/{id}");
43+
* }
44+
* }
45+
*
46+
* class EmployeeResourceCollection extends EmployeeResource implements ResourceCollection
47+
* {
48+
* public function getUri(): Template
49+
* {
50+
* return new Template("/directory/employees");
51+
* }
52+
* }
53+
*
54+
* $resources = [
55+
* new EmployeeResource(),
56+
* new EmployeeResourceCollection()
57+
* ];
58+
*
59+
* $resolver = new ResourceResolver(ResourceList::make(...$resources));
60+
* $resolver->resolve(Uri::make("https://localhost:8080/directory/employees/1")); // EmployeeResource
61+
* $resolver->resolve(Uri::make("https://localhost:8080/directory/employees")); // EmployeeResourceCollection
62+
* $resolver->resolve(Uri::make("https://localhost:8080/directory/shipments")); // null
63+
*
64+
*/
65+
class ResourceResolver
66+
{
67+
/**
68+
* @var ResourceList
69+
*/
70+
private ResourceList $locations;
71+
72+
73+
/**
74+
* @param ResourceList $locations
75+
*/
76+
public function __construct(ResourceList $locations)
77+
{
78+
$this->locations = $locations;
79+
}
80+
81+
82+
/**
83+
* Resolves the specified URI and returns the Resource that is located there.
84+
* Returns null if the URI did not target a registered resource with this ResourceResolver.
85+
*
86+
* @param Uri $uri
87+
* @return Resource|null
88+
*/
89+
public function resolve(Uri $uri): ?Resource
90+
{
91+
foreach ($this->locations as $location) {
92+
if ($location->getUri()->match($uri) !== null) {
93+
return $location;
94+
}
95+
}
96+
return null;
97+
}
98+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/**
4+
* conjoon
5+
* php-lib-conjoon
6+
* Copyright (C) 2022 Thorsten Suckow-Homberg https://github.com/conjoon/php-lib-conjoon
7+
*
8+
* Permission is hereby granted, free of charge, to any person
9+
* obtaining a copy of this software and associated documentation
10+
* files (the "Software"), to deal in the Software without restriction,
11+
* including without limitation the rights to use, copy, modify, merge,
12+
* publish, distribute, sublicense, and/or sell copies of the Software,
13+
* and to permit persons to whom the Software is furnished to do so,
14+
* subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included
17+
* in all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25+
* USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
*/
27+
28+
declare(strict_types=1);
29+
30+
namespace Tests\Conjoon\JsonApi\Resource;
31+
32+
use Conjoon\Core\AbstractList;
33+
use Conjoon\JsonApi\Resource\Resource;
34+
use Conjoon\JsonApi\Resource\ResourceList;
35+
use Conjoon\Net\Uri\Component\Path\Template;
36+
use InvalidArgumentException;
37+
use PHPUnit\Framework\MockObject\MockObject;
38+
use Tests\TestCase;
39+
40+
/**
41+
* tests ResourceList
42+
*/
43+
class ResourceListTest extends TestCase
44+
{
45+
/**
46+
* Tests constructor
47+
*/
48+
public function testClass(): void
49+
{
50+
$list = $this->createList();
51+
$this->assertInstanceOf(AbstractList::class, $list);
52+
53+
$this->assertSame(Resource::class, $list->getEntityType());
54+
}
55+
56+
57+
/**
58+
* tests offsetSet(), makes sure no URI duplicates may occur
59+
* @return void
60+
*/
61+
public function testOffsetSet(): void
62+
{
63+
$resource = function (string $uri): MockObject&Resource {
64+
/**
65+
* @var MockObject&Resource $res
66+
*/
67+
$res = $this->createMockForAbstract(Resource::class, ["getUri"]);
68+
$res->expects($this->any())->method("getUri")->willReturn(new Template($uri));
69+
return $res;
70+
};
71+
72+
$resourceList = new ResourceList();
73+
$resourceList[] = $resource("/uri1");
74+
$resourceList[] = $resource("/uri2");
75+
$resourceList[] = $resource("/uri3");
76+
77+
try {
78+
$resourceList[] = $resource("/uri2");
79+
$this->fail();
80+
} catch (InvalidArgumentException $e) {
81+
$this->assertStringContainsString(
82+
strtolower("a resource for the URI \"/uri2\" already exists"),
83+
strtolower($e->getMessage())
84+
);
85+
}
86+
}
87+
88+
89+
/**
90+
* @return ResourceList
91+
*/
92+
protected function createList(): ResourceList
93+
{
94+
$list = new ResourceList();
95+
96+
97+
return $list;
98+
}
99+
}

0 commit comments

Comments
 (0)