Skip to content

Commit 072371f

Browse files
authored
Merge pull request #54 from MacFJA/jsonpath-validation
Add JSONPath validation
2 parents b2cef72 + be07128 commit 072371f

File tree

6 files changed

+150
-3
lines changed

6 files changed

+150
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased v2.x]
88

9+
### Added
10+
11+
- Path validation for JSON fields
12+
913
### Changed
1014

1115
- (dev) Update PHP-CS-Fixer to version `3.12.0`

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"php": "^7.2 || ^8.0",
2424
"ext-intl": "*",
2525
"composer/semver": "^3.2",
26-
"respect/validation": "^2.0"
26+
"respect/validation": "^2.0",
27+
"softcreatr/jsonpath": "^0.7.6 || ^0.8.1"
2728
},
2829
"require-dev": {
2930
"ext-mbstring": "*",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* Copyright MacFJA
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
9+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
10+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
11+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
14+
* Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
namespace MacFJA\RediSearch\Exception;
23+
24+
use Exception;
25+
use InvalidArgumentException;
26+
27+
class InvalidJSONPathException extends InvalidArgumentException
28+
{
29+
public function __construct(string $path, Exception $previous = null)
30+
{
31+
parent::__construct(sprintf('The provided JSON Path is not valid. (path: "%s")', $path), 0, $previous);
32+
}
33+
}

src/Redis/Command/CreateCommand/JSONFieldOption.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121

2222
namespace MacFJA\RediSearch\Redis\Command\CreateCommand;
2323

24+
use function count;
25+
26+
use Flow\JSONPath\JSONPath;
27+
use Flow\JSONPath\JSONPathException;
28+
use MacFJA\RediSearch\Exception\InvalidJSONPathException;
2429
use MacFJA\RediSearch\Redis\Command\Option\AbstractCommandOption;
2530

2631
class JSONFieldOption extends AbstractCommandOption implements CreateCommandJSONFieldOption
@@ -36,6 +41,9 @@ public function __construct(string $path, CreateCommandFieldOption $decorated)
3641
parent::__construct('>=2.2.0');
3742
$this->path = $path;
3843
$this->decorated = $decorated;
44+
if (!self::isPathValid($path)) {
45+
throw new InvalidJSONPathException($path);
46+
}
3947
}
4048

4149
public function isValid(): bool
@@ -63,6 +71,26 @@ public function getJSONPath(): string
6371
return $this->path;
6472
}
6573

74+
/**
75+
* @throws InvalidJSONPathException
76+
*/
77+
public static function isPathValid(string $path): bool
78+
{
79+
if (0 === strpos($path, '$')) {
80+
$parser = new JSONPath();
81+
82+
try {
83+
$result = $parser->parseTokens($path);
84+
85+
return count($result) > 0;
86+
} catch (JSONPathException $e) {
87+
throw new InvalidJSONPathException($path, $e);
88+
}
89+
}
90+
91+
return false;
92+
}
93+
6694
protected function doRender(?string $version): array
6795
{
6896
return array_merge([$this->path, 'AS'], $this->decorated->render($version));
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* Copyright MacFJA
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
9+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
10+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
11+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
14+
* Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
namespace MacFJA\RediSearch\tests\Redis\Command\CreateCommand;
23+
24+
use MacFJA\RediSearch\Exception\InvalidJSONPathException;
25+
use MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption;
26+
use PHPUnit\Framework\TestCase;
27+
28+
/**
29+
* @internal
30+
*
31+
* @covers \MacFJA\RediSearch\Exception\InvalidJSONPathException
32+
* @covers \MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption
33+
*/
34+
class JSONFieldOptionTest extends TestCase
35+
{
36+
/**
37+
* @dataProvider dataProvider
38+
*/
39+
public function testIsPathValid(string $input, bool $expected): void
40+
{
41+
static::assertEquals($expected, JSONFieldOption::isPathValid($input));
42+
}
43+
44+
/**
45+
* @dataProvider dataProvider
46+
*/
47+
public function testIsPathValidException(string $input, bool $expected): void
48+
{
49+
$this->expectException(InvalidJSONPathException::class);
50+
static::assertEquals($expected, JSONFieldOption::isPathValid($input));
51+
}
52+
53+
/**
54+
* @return array<string, array<bool|string>>
55+
*/
56+
public function dataProvider(string $testName): array
57+
{
58+
if ('testIsPathValid' === $testName) {
59+
return [
60+
'2nd element of root' => ['$.[1]', true],
61+
'2nd element' => ['$[1]', true],
62+
'multilevel' => ['$.basic.dict.new_child_1', true],
63+
'empty 2' => [' ', false],
64+
'empty' => ['', false],
65+
'not close 2' => ['$[', false],
66+
'missing dollar' => ['.basic', false],
67+
];
68+
}
69+
70+
return [
71+
'not close 1' => ['$(', false],
72+
];
73+
}
74+
}

tests/Redis/Command/Option/JSONFieldOptionTest.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
namespace MacFJA\RediSearch\tests\Redis\Command\Option;
2323

24+
use MacFJA\RediSearch\Exception\InvalidJSONPathException;
2425
use MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption;
2526
use MacFJA\RediSearch\Redis\Command\CreateCommand\TextFieldOption;
2627
use PHPUnit\Framework\TestCase;
@@ -36,6 +37,7 @@
3637
* @uses \MacFJA\RediSearch\Redis\Command\Option\GroupedOption
3738
* @uses \MacFJA\RediSearch\Redis\Command\Option\NamedOption
3839
* @uses \MacFJA\RediSearch\Redis\Command\Option\NamelessOption
40+
* @uses \MacFJA\RediSearch\Exception\InvalidJSONPathException
3941
*
4042
* @internal
4143
*/
@@ -50,9 +52,14 @@ public function testIsValid(): void
5052
static::assertFalse($json->isValid());
5153

5254
$option->setField('data');
53-
$json = new JSONFieldOption('', $option);
5455
static::assertTrue($option->isValid());
55-
static::assertFalse($json->isValid());
56+
57+
try {
58+
new JSONFieldOption('', $option);
59+
static::fail();
60+
} catch (InvalidJSONPathException $e) {
61+
// Do nothing
62+
}
5663

5764
$json = new JSONFieldOption('$.data', $option);
5865
static::assertTrue($option->isValid());

0 commit comments

Comments
 (0)