Completed
Pull Request — 1.x (#124)
by Sosuke
01:58
created

JsonSchemaInterceptor::validateResponse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 2
crap 2
1
<?php
2
/**
3
 * This file is part of the BEAR.Resource package.
4
 *
5
 * @license http://opensource.org/licenses/MIT MIT
6
 */
7
namespace BEAR\Resource\Interceptor;
8
9
use BEAR\Resource\Annotation\JsonSchema;
10
use BEAR\Resource\Code;
11
use BEAR\Resource\Exception\JsonSchemaErrorException;
12
use BEAR\Resource\Exception\JsonSchemaException;
13
use BEAR\Resource\Exception\JsonSchemaNotFoundException;
14
use BEAR\Resource\ResourceObject;
15
use JsonSchema\Constraints\Constraint;
16
use JsonSchema\Validator;
17
use Ray\Aop\MethodInterceptor;
18
use Ray\Aop\MethodInvocation;
19
use Ray\Aop\WeavedInterface;
20
use Ray\Di\Di\Named;
21
22
final class JsonSchemaInterceptor implements MethodInterceptor
23
{
24
    /**
25
     * @var string
26
     */
27
    private $schemaDir;
28
29
    /**
30
     * @var string
31
     */
32
    private $validateDir;
33
34
    /**
35
     * @param string $schemaDir
36
     * @param string $validateDir
37
     *
38
     * @Named("schemaDir=json_schema_dir,validateDir=json_validate_dir")
39
     */
40 7
    public function __construct($schemaDir, $validateDir)
41
    {
42 7
        $this->schemaDir = $schemaDir;
43 7
        $this->validateDir = $validateDir;
44 7
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49 7
    public function invoke(MethodInvocation $invocation)
50
    {
51
        /* @var $jsonSchema JsonSchema */
52 7
        $jsonSchema = $invocation->getMethod()->getAnnotation(JsonSchema::class);
53 7
        if ($jsonSchema->params) {
54 3
            $arguments = $this->getNamedArguments($invocation);
55 3
            $this->validateRequest($jsonSchema, $arguments);
56
        }
57
58
        /* @var $ro ResourceObject */
59 6
        $ro = $invocation->proceed();
60 6
        if ($ro->code !== 200) {
61 1
            return $ro;
62
        }
63 5
        $this->validateResponse($jsonSchema, $ro);
64
65 2
        return $ro;
66
    }
67
68 3
    private function validateRequest(JsonSchema $jsonSchema, array $arguments)
69
    {
70 3
        $schemaFile = $this->validateDir . '/' . $jsonSchema->params;
71 3
        $this->validateFileExists($schemaFile);
72 3
        $this->validate($arguments, $schemaFile);
73 2
    }
74
75 5
    private function validateResponse(JsonSchema $jsonSchema, ResourceObject $ro)
76
    {
77 5
        $schemeFile = $this->getSchemaFile($jsonSchema, $ro);
78 4
        $body = isset($ro->body[$jsonSchema->key]) ? $ro->body[$jsonSchema->key] : $ro->body;
79 4
        $this->validate($body, $schemeFile);
80 2
    }
81
82 5
    private function validate($scanObject, $schemaFile)
83
    {
84 5
        $validator = new Validator;
85 5
        $schema = (object) ['$ref' => 'file://' . $schemaFile];
86 5
        $validator->validate($scanObject, $schema, Constraint::CHECK_MODE_TYPE_CAST);
87 5
        $isValid = $validator->isValid();
88 5
        if ($isValid) {
89 3
            return;
90
        }
91 3
        $e = null;
92 3
        foreach ($validator->getErrors() as $error) {
93 3
            $msg = sprintf('[%s] %s', $error['property'], $error['message']);
94 3
            $e = $e ? new JsonSchemaErrorException($msg, 0, $e) : new JsonSchemaErrorException($msg);
95
        }
96 3
        throw new JsonSchemaException($schemaFile, Code::ERROR, $e);
97
    }
98
99 5
    private function getSchemaFile(JsonSchema $jsonSchema, ResourceObject $ro) : string
100
    {
101 5
        if (! $jsonSchema->schema) {
102
            // for BC only
103 1
            $ref = new \ReflectionClass($ro);
104 1
            $roFileName = $ro instanceof WeavedInterface ? $roFileName = $ref->getParentClass()->getFileName() : $ref->getFileName();
105 1
            $bcFile = str_replace('.php', '.json', $roFileName);
106 1
            if (file_exists($bcFile)) {
107 1
                return $bcFile;
108
            }
109
        }
110 4
        $schemaFile = $this->schemaDir . '/' . $jsonSchema->schema;
111 4
        $this->validateFileExists($schemaFile);
112
113 3
        return $schemaFile;
114
    }
115
116 5
    private function validateFileExists(string $schemaFile)
117
    {
118 5
        if (! file_exists($schemaFile) || is_dir($schemaFile)) {
119 1
            throw new JsonSchemaNotFoundException($schemaFile);
120
        }
121 4
    }
122
123 3
    private function getNamedArguments(MethodInvocation $invocation)
124
    {
125 3
        $parameters = $invocation->getMethod()->getParameters();
126 3
        $values = $invocation->getArguments();
127
128 3
        $arguments = [];
129 3
        foreach ($parameters as $index => $parameter) {
130 3
            $arguments[$parameter->name] = $values[$index] ?? $parameter->getDefaultValue();
131
        }
132
133 3
        return $arguments;
134
    }
135
}
136