Completed
Push — develop ( a368ca...435c97 )
by
unknown
07:19 queued 04:10
created

JsonApiClient   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 120
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 7
dl 0
loc 120
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A read() 0 32 4
A buildResourcePath() 0 20 3
A parseJson() 0 26 3
1
<?php
2
3
/**
4
 * Copyright 2018 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\UserLifecycleApiClientBundle\Http;
20
21
use GuzzleHttp\ClientInterface;
22
use OpenConext\UserLifecycleApiClientBundle\Exception\InvalidArgumentException;
23
use OpenConext\UserLifecycleApiClientBundle\Exception\InvalidResponseException;
24
use OpenConext\UserLifecycleApiClientBundle\Exception\MalformedResponseException;
25
use OpenConext\UserLifecycleApiClientBundle\Exception\ResourceNotFoundException;
26
use OpenConext\UserLifecycleApiClientBundle\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 string $path
88
     * @param array $parameters
89
     * @return string
90
     * @throws RuntimeException
91
     */
92
    private function buildResourcePath($path, array $parameters)
93
    {
94
        if (count($parameters) > 0) {
95
            $resource = vsprintf($path, array_map('urlencode', $parameters));
96
        } else {
97
            $resource = $path;
98
        }
99
100
        if (empty($resource)) {
101
            throw new RuntimeException(
102
                sprintf(
103
                    'Could not construct resource path from path "%s", parameters "%s"',
104
                    $path,
105
                    implode('","', $parameters)
106
                )
107
            );
108
        }
109
110
        return $resource;
111
    }
112
113
    /**
114
     * Function to provide functionality common to Guzzle 5 Response's json method,
115
     * without config options as they are not needed.
116
     *
117
     * @param string $json
118
     * @return mixed
119
     * @throws InvalidArgumentException
120
     */
121
    private function parseJson($json)
122
    {
123
        static $jsonErrors = [
124
            JSON_ERROR_DEPTH          => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
125
            JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
126
            JSON_ERROR_CTRL_CHAR      => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
127
            JSON_ERROR_SYNTAX         => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
128
            JSON_ERROR_UTF8           => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded',
129
        ];
130
131
        $data = json_decode($json, true);
132
133
        if (JSON_ERROR_NONE !== json_last_error()) {
134
            $last = json_last_error();
135
136
            $errorMessage = $jsonErrors[$last];
137
138
            if (!isset($errorMessage)) {
139
                $errorMessage = 'Unknown error';
140
            }
141
142
            throw new InvalidArgumentException(sprintf('Unable to parse JSON data: %s', $errorMessage));
143
        }
144
145
        return $data;
146
    }
147
}
148