Skip to content

Commit 8ddec68

Browse files
committed
- Add search and hash processing to URL resolver
- Add additional validation to URL resolver - Update indicator of Service resolver per spec change - Re-factor Abstract Resolver and Factory to support deprecated indicators - Update tests
1 parent cd67526 commit 8ddec68

File tree

5 files changed

+94
-18
lines changed

5 files changed

+94
-18
lines changed

src/Resolver/AbstractResolver.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ abstract class AbstractResolver implements ResolverInterface
1818
*/
1919
protected $iterator;
2020

21+
/**
22+
* Return list of previous indicators.
23+
*
24+
* Given that the UPWARD specification is a living document, it's possible that indicators may change, but
25+
* to maintain backward compatibility we should still support those past indicators.
26+
*
27+
* @return string[]
28+
*/
29+
public function getDeprecatedIndicators()
30+
{
31+
return [];
32+
}
33+
2134
/**
2235
* {@inheritdoc}
2336
*/

src/Resolver/Service.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,22 @@
1313

1414
class Service extends AbstractResolver
1515
{
16+
public const DEPRECATED_URL_INDICATOR = 'url';
17+
18+
/**
19+
* {@inheritdoc}
20+
*/
21+
public function getDeprecatedIndicators()
22+
{
23+
return [self::DEPRECATED_URL_INDICATOR];
24+
}
25+
1626
/**
1727
* {@inheritdoc}
1828
*/
1929
public function getIndicator(): string
2030
{
21-
return 'url';
31+
return 'endpoint';
2232
}
2333

2434
/**
@@ -37,7 +47,7 @@ public function isValid(Definition $definition): bool
3747
}
3848
}
3949

40-
return parent::isValid($definition);
50+
return $definition->has($this->getIndicator()) xor $definition->has(self::DEPRECATED_URL_INDICATOR);
4151
}
4252

4353
/**
@@ -49,7 +59,11 @@ public function resolve($definition)
4959
throw new \InvalidArgumentException('$definition must be an instance of ' . Definition::class);
5060
}
5161

52-
$url = $this->getIterator()->get('url', $definition);
62+
$urlParameter = $definition->has($this->getIndicator())
63+
? $this->getIndicator()
64+
: self::DEPRECATED_URL_INDICATOR;
65+
66+
$url = $this->getIterator()->get($urlParameter, $definition);
5367
$query = $this->getIterator()->get('query', $definition);
5468
$method = $definition->has('method') ? $this->getIterator()->get('method', $definition) : 'POST';
5569
$variables = $definition->has('variables') ? $this->getIterator()->get('variables', $definition) : [];

src/Resolver/Url.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313

1414
class Url extends AbstractResolver
1515
{
16-
public const FAKE_BASE_HOST = 'upward-fake.localhost';
17-
public const FAKE_BASE_URL = 'https://' . self::FAKE_BASE_HOST;
16+
public const FAKE_BASE_HOST = 'upward-fake.localhost';
17+
public const FAKE_BASE_URL = 'https://' . self::FAKE_BASE_HOST;
18+
public const NON_RELATIVE_PARTS = ['protocol', 'port', 'username', 'password'];
1819

1920
/**
2021
* {@inheritdoc}
@@ -36,6 +37,18 @@ public function isValid(Definition $definition): bool
3637
}
3738
}
3839

40+
if ($definition->has('password') && !$definition->has('username')) {
41+
return false;
42+
}
43+
44+
if ($this->getIterator()->get('baseUrl', $definition) === false && !$definition->has('hostname')) {
45+
foreach (self::NON_RELATIVE_PARTS as $nonRelativePart) {
46+
if ($definition->has($nonRelativePart)) {
47+
return false;
48+
}
49+
}
50+
}
51+
3952
return parent::isValid($definition);
4053
}
4154

@@ -78,6 +91,11 @@ public function resolve($definition)
7891
$uri->setPath($pathname);
7992
}
8093

94+
if ($definition->has('search')) {
95+
parse_str($this->getIterator()->get('search', $definition), $searchArray);
96+
$uri->setQuery(array_merge($uri->getQueryAsArray(), $searchArray));
97+
}
98+
8199
if ($definition->has('query')) {
82100
$mergedQuery = array_merge($uri->getQueryAsArray(), $this->getIterator()->get('query', $definition));
83101
$uri->setQuery($mergedQuery);
@@ -95,6 +113,10 @@ public function resolve($definition)
95113
$uri->setUserInfo($userInfo);
96114
}
97115

116+
if ($definition->has('hash')) {
117+
$uri->setFragment(str_replace('#', '', $this->getIterator()->get('hash', $definition)));
118+
}
119+
98120
$returnUrl = $uri->toString();
99121

100122
return $uri->getHost() === self::FAKE_BASE_HOST

src/ResolverFactory.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static function get($definition): ?Resolver\ResolverInterface
6868
* Return a resolver from instance cache by its type,
6969
* otherwise instantiate one based on its type and cache it.
7070
*
71-
* @throws RuntimeException if there is no cached instance or configured class for $resolverType
71+
* @throws \RuntimeException if there is no cached instance or configured class for $resolverType
7272
*/
7373
private static function build(string $resolverType): Resolver\ResolverInterface
7474
{
@@ -86,7 +86,7 @@ private static function build(string $resolverType): Resolver\ResolverInterface
8686
/**
8787
* Get a Resolver for a Definition.
8888
*
89-
* @throws RuntimeException if $definition is not valid for the Resolver
89+
* @throws \RuntimeException if $definition is not valid for the Resolver
9090
*/
9191
private static function getForDefinition(Definition $definition): Resolver\ResolverInterface
9292
{
@@ -114,7 +114,7 @@ private static function getForScalar(string $lookup): ?Resolver\ResolverInterfac
114114
/**
115115
* Return a resolver for a Definition based on if that Definition has the resolver's indicator.
116116
*
117-
* @throw RuntimeException if no resolver can be inferred.
117+
* @throws \RuntimeException if no resolver can be inferred
118118
*/
119119
private static function inferResolver(Definition $definition): Resolver\ResolverInterface
120120
{
@@ -124,6 +124,14 @@ private static function inferResolver(Definition $definition): Resolver\Resolver
124124
if ($definition->has($resolver->getIndicator())) {
125125
return $resolver;
126126
}
127+
128+
if ($resolver instanceof Resolver\AbstractResolver) {
129+
foreach ($resolver->getDeprecatedIndicators() as $deprecatedIndicator) {
130+
if ($definition->has($deprecatedIndicator)) {
131+
return $resolver;
132+
}
133+
}
134+
}
127135
}
128136

129137
throw new \RuntimeException('No resolver found for definition: ' . json_encode($definition));

test/Resolver/ServiceTest.php

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,31 @@ protected function setUp(): void
4242
$this->resolver->setIterator($this->definitionIteratorMock);
4343
}
4444

45+
/**
46+
* Data provider to verify backwards compatibility with previous indicator.
47+
*
48+
* @return string[]
49+
*/
50+
public function indicatorDataProvider()
51+
{
52+
return [['url'], ['endpoint']];
53+
}
54+
4555
public function testIndicator(): void
4656
{
47-
verify($this->resolver->getIndicator())->is()->sameAs('url');
57+
verify($this->resolver->getIndicator())->is()->sameAs('endpoint');
4858
}
4959

50-
public function testIsValid(): void
60+
/**
61+
* @dataProvider indicatorDataProvider
62+
*/
63+
public function testIsValid(string $indicator): void
5164
{
52-
$validDefinition = new Definition(['url' => '/graphql', 'query' => 'gql']);
53-
$validWithMethodDefinition = new Definition(['url' => '/graphql', 'query' => 'gql', 'method' => 'GET']);
65+
$validDefinition = new Definition([$indicator => '/graphql', 'query' => 'gql']);
66+
$validWithMethodDefinition = new Definition([$indicator => '/graphql', 'query' => 'gql', 'method' => 'GET']);
5467
$invalidNoURL = new Definition(['query' => 'gql']);
55-
$invalidNoQuery = new Definition(['url' => '/graphql']);
56-
$invalidUnsupportedMethod = new Definition(['url' => '/graphql', 'query' => 'gql', 'method' => 'PUT']);
68+
$invalidNoQuery = new Definition([$indicator => '/graphql']);
69+
$invalidUnsupportedMethod = new Definition([$indicator => '/graphql', 'query' => 'gql', 'method' => 'PUT']);
5770

5871
$this->definitionIteratorMock->shouldReceive('get')
5972
->twice()
@@ -69,9 +82,12 @@ public function testIsValid(): void
6982
verify($this->resolver->isValid($invalidUnsupportedMethod))->is()->false();
7083
}
7184

72-
public function testResolve(): void
85+
/**
86+
* @dataProvider indicatorDataProvider
87+
*/
88+
public function testResolve(string $indicator): void
7389
{
74-
$definition = new Definition(['url' => '/graphql', 'query' => 'gql']);
90+
$definition = new Definition([$indicator => '/graphql', 'query' => 'gql']);
7591
$expectedRequestBody = json_encode(['query' => 'gql', 'variables' => []]);
7692
$expectedResponseArray = ['data' => ['key' => 'value']];
7793

@@ -102,10 +118,13 @@ public function testResolveThrowsException(): void
102118
$this->resolver->resolve('Not a Definition');
103119
}
104120

105-
public function testResolveWithConfiguration(): void
121+
/**
122+
* @dataProvider indicatorDataProvider
123+
*/
124+
public function testResolveWithConfiguration(string $indicator): void
106125
{
107126
$definition = new Definition([
108-
'url' => '/graphql',
127+
$indicator => '/graphql',
109128
'query' => 'gql',
110129
'variables' => [
111130
'var1' => 'var1Value',

0 commit comments

Comments
 (0)