JsonApiClient::parseJson()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 9.504
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
3
/**
4
 * Copyright 2015 SURFnet B.V.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace OpenConext\EngineBlockApiClientBundle\Http;
20
21
use GuzzleHttp\ClientInterface;
22
use OpenConext\EngineBlockApiClientBundle\Exception\InvalidArgumentException;
23
use OpenConext\EngineBlockApiClientBundle\Exception\InvalidResponseException;
24
use OpenConext\EngineBlockApiClientBundle\Exception\MalformedResponseException;
25
use OpenConext\EngineBlockApiClientBundle\Exception\ResourceNotFoundException;
26
use OpenConext\EngineBlockApiClientBundle\Exception\RuntimeException;
27
28
class JsonApiClient
29
{
30
    /**
31
     * @var ClientInterface
32
     */
33
    private $httpClient;
34
35
    /**
36
     * @param ClientInterface $httpClient
37
     */
38
    public function __construct(ClientInterface $httpClient)
39
    {
40
        $this->httpClient = $httpClient;
41
    }
42
43
    /**
44
     * @param string $path A URL path, optionally containing printf parameters. The parameters
45
     *               will be URL encoded and formatted into the path string.
46
     *               Example: "connections/%d.json"
47
     * @param array  $parameters
48
     * @return mixed $data
49
     * @throws InvalidResponseException
50
     * @throws MalformedResponseException
51
     * @throws ResourceNotFoundException
52
     */
53
    public function read($path, array $parameters = [])
54
    {
55
        $resource = $this->buildResourcePath($path, $parameters);
56
57
        $response = $this->httpClient->request('GET', $resource, ['exceptions' => false]);
58
59
        $statusCode = $response->getStatusCode();
60
61
        if ($statusCode === 404) {
62
            throw new ResourceNotFoundException(sprintf('Resource "%s" not found', $resource));
63
        }
64
65
        if ($statusCode !== 200) {
66
            throw new InvalidResponseException(
67
                sprintf(
68
                    'Request to resource "%s" returned an invalid response with status code %s',
69
                    $resource,
70
                    $statusCode
71
                )
72
            );
73
        }
74
75
        try {
76
            $data = $this->parseJson((string) $response->getBody());
77
        } catch (InvalidArgumentException $e) {
78
            throw new MalformedResponseException(
79
                sprintf('Cannot read resource "%s": malformed JSON returned', $resource)
80
            );
81
        }
82
83
        return $data;
84
    }
85
86
    /**
87
     * @param mixed $data
88
     * @param string $path
89
     * @param array $parameters
90
     * @return mixed
91
     */
92
    public function post($data, $path, $parameters = [])
93
    {
94
        $resource = $this->buildResourcePath($path, $parameters);
95
96
        $response = $this->httpClient->request(
97
            'POST',
98
            $resource,
99
            [
100
                'exceptions' => false,
101
                'body' => json_encode($data)
102
            ]
103
        );
104
105
        $statusCode = $response->getStatusCode();
106
107
        if ($statusCode === 404) {
108
            throw new ResourceNotFoundException(sprintf('Resource "%s" not found', $resource));
109
        }
110
111
        if ($statusCode !== 200) {
112
            throw new InvalidResponseException(
113
                sprintf(
114
                    'Request to resource "%s" returned an invalid response with status code %s',
115
                    $resource,
116
                    $statusCode,
117
                    $response->getBody()
118
                )
119
            );
120
        }
121
122
        try {
123
            $data = $this->parseJson((string) $response->getBody());
124
        } catch (InvalidArgumentException $e) {
125
            throw new MalformedResponseException(
126
                sprintf('Cannot read resource "%s": malformed JSON returned', $resource)
127
            );
128
        }
129
130
        return $data;
131
    }
132
133
    /**
134
     * @param string $path
135
     * @param array $parameters
136
     * @return string
137
     * @throws RuntimeException
138
     */
139
    private function buildResourcePath($path, array $parameters)
140
    {
141
        if (count($parameters) > 0) {
142
            $resource = vsprintf($path, array_map('urlencode', $parameters));
143
        } else {
144
            $resource = $path;
145
        }
146
147
        if (empty($resource)) {
148
            throw new RuntimeException(
149
                sprintf(
150
                    'Could not construct resource path from path "%s", parameters "%s"',
151
                    $path,
152
                    implode('","', $parameters)
153
                )
154
            );
155
        }
156
157
        return $resource;
158
    }
159
160
    /**
161
     * Function to provide functionality common to Guzzle 5 Response's json method,
162
     * without config options as they are not needed.
163
     *
164
     * @param string $json
165
     * @return mixed
166
     * @throws InvalidArgumentException
167
     */
168
    private function parseJson($json)
169
    {
170
        static $jsonErrors = [
171
            JSON_ERROR_DEPTH          => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
172
            JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
173
            JSON_ERROR_CTRL_CHAR      => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
174
            JSON_ERROR_SYNTAX         => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
175
            JSON_ERROR_UTF8           => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded',
176
        ];
177
178
        $data = json_decode($json, true);
179
180
        if (JSON_ERROR_NONE !== json_last_error()) {
181
            $last = json_last_error();
182
183
            $errorMessage = $jsonErrors[$last];
184
185
            if (!isset($errorMessage)) {
186
                $errorMessage = 'Unknown error';
187
            }
188
189
            throw new InvalidArgumentException(sprintf('Unable to parse JSON data: %s', $errorMessage));
190
        }
191
192
        return $data;
193
    }
194
}
195