Completed
Push — master ( ed3593...2f770f )
by Boy
9s
created

CommandService::assertIsValidCommandName()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 3
eloc 6
nc 4
nop 1
1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
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 Surfnet\StepupMiddlewareClient\Service;
20
21
use GuzzleHttp\ClientInterface;
22
use RuntimeException;
23
use Surfnet\StepupMiddlewareClient\Exception\CommandExecutionFailedException;
24
use Surfnet\StepupMiddlewareClient\Exception\InvalidArgumentException;
25
26
class CommandService
27
{
28
    /**
29
     * @var ClientInterface
30
     */
31
    private $guzzleClient;
32
33
    /**
34
     * @var string
35
     */
36
    private $username;
37
38
    /**
39
     * @var string
40
     */
41
    private $password;
42
43
    /**
44
     * @param ClientInterface $guzzleClient A Guzzle client preconfigured with the command URL.
45
     * @param string $username
46
     * @param string $password
47
     */
48
    public function __construct(ClientInterface $guzzleClient, $username, $password)
49
    {
50
        if (!is_string($username)) {
51
            throw InvalidArgumentException::invalidType('string', 'username', $username);
52
        }
53
54
        if (!is_string($password)) {
55
            throw InvalidArgumentException::invalidType('string', 'password', $password);
56
        }
57
58
        $this->guzzleClient = $guzzleClient;
59
        $this->username = $username;
60
        $this->password = $password;
61
    }
62
63
    /**
64
     * @param string $commandName
65
     * @param string $uuid
66
     * @param array $payload
67
     * @param array $metadata
68
     * @return ExecutionResult
69
     * @throws CommandExecutionFailedException
70
     */
71
    public function execute($commandName, $uuid, array $payload, array $metadata = [])
72
    {
73
        $this->assertIsValidCommandName($commandName);
74
        if (!is_string($uuid)) {
75
            throw InvalidArgumentException::invalidType('string', 'uuid', $uuid);
76
        }
77
78
        $command = [
79
            'name' => $commandName,
80
            'uuid' => $uuid,
81
            'payload' => $payload,
82
        ];
83
84
        $body = ['command' => $command];
85
86
        if (count($metadata) > 0) {
87
            $body['meta'] = $metadata;
88
        }
89
90
        $requestOptions = [
91
            'json'       => $body,
92
            'exceptions' => false,
93
            'auth'       => [$this->username, $this->password, 'basic'],
94
            'headers'    => ['Accept' => 'application/json'],
95
        ];
96
        $httpResponse = $this->guzzleClient->post(null, $requestOptions);
97
98
        try {
99
            $response = $httpResponse->json();
100
        } catch (RuntimeException $e) {
101
            throw new CommandExecutionFailedException(
102
                'Server response could not be decoded as it isn\'t valid JSON.',
103
                0,
104
                $e
105
            );
106
        }
107
108
        return $httpResponse->getStatusCode() == 200
109
            ? $this->processOkResponse($uuid, $response)
110
            : $this->processErrorResponse($uuid, $response);
111
    }
112
113
    /**
114
     * @param mixed $commandName
115
     * @throws InvalidArgumentException
116
     */
117
    private function assertIsValidCommandName($commandName)
118
    {
119
        if (!is_string($commandName)) {
120
            InvalidArgumentException::invalidType('string', 'command', $commandName);
121
        }
122
123
        if (!preg_match('~^[a-z0-9_]+:([a-z0-9_].)*[a-z0-9_]+$~i', $commandName)) {
124
            throw new InvalidArgumentException(
125
                'Command must be formatted AggregateRoot:Command or AggregateRoot:Name.Space.Command'
126
            );
127
        }
128
    }
129
130
    /**
131
     * @param string $uuid
132
     * @param mixed $response
133
     * @return ExecutionResult
134
     */
135
    private function processOkResponse($uuid, $response)
136
    {
137
        if (!isset($response['command'])) {
138
            throw new CommandExecutionFailedException('Unexpected response format: command key missing.');
139
        }
140
141
        if ($response['command'] !== $uuid) {
142
            throw new CommandExecutionFailedException(sprintf(
143
                'Unexpected response: returned command UUID "%s" does not match sent UUID "%s".',
144
                $response['command'],
145
                $uuid
146
            ));
147
        }
148
149
        if (!isset($response['processed_by'])) {
150
            throw new CommandExecutionFailedException('Unexpected response format: processed_by key missing.');
151
        }
152
153
        if (!is_string($response['processed_by'])) {
154
            throw new CommandExecutionFailedException(sprintf(
155
                'Unexpected response format: processed_by should be a string, "%s" given.',
156
                gettype($response['processed_by'])
157
            ));
158
        }
159
160
        return new ExecutionResult($uuid, $response['processed_by']);
161
    }
162
163
    /**
164
     * @param string $uuid
165
     * @param mixed $response
166
     * @return ExecutionResult
167
     */
168
    private function processErrorResponse($uuid, $response)
169
    {
170
        if (!isset($response['errors'])) {
171
            throw new CommandExecutionFailedException('Unexpected response format: errors key missing.');
172
        }
173
174
        if (!is_array($response['errors'])) {
175
            throw new CommandExecutionFailedException(sprintf(
176
                'Unexpected response format: errors should be an array, "%s" given.',
177
                gettype($response['errors'])
178
            ));
179
        }
180
181
        return new ExecutionResult($uuid, null, $response['errors']);
182
    }
183
}
184