Completed
Push — master ( 548654...ca6abf )
by Dmytro
24:35
created

WebhookController::validate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
declare(strict_types=1);
3
4
5
namespace Webhook\Bundle\Controller;
6
7
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
8
use Symfony\Component\HttpFoundation\JsonResponse;
9
use Symfony\Component\HttpFoundation\Request;
10
use Symfony\Component\HttpFoundation\Response;
11
use Symfony\Component\PropertyAccess\PropertyAccessor;
12
use Symfony\Component\Validator\Constraints\All;
13
use Symfony\Component\Validator\Constraints\Choice;
14
use Symfony\Component\Validator\Constraints\Collection;
15
use Symfony\Component\Validator\Constraints\Length;
16
use Symfony\Component\Validator\Constraints\Optional;
17
use Symfony\Component\Validator\Constraints\Range;
18
use Symfony\Component\Validator\Constraints\Required;
19
use Symfony\Component\Validator\Constraints\Url;
20
use Symfony\Component\Validator\Validation;
21
use Webhook\Bundle\Repository\WebhookRepository;
22
use Webhook\Domain\Infrastructure\Strategy\StrategyFactory;
23
use Webhook\Domain\Infrastructure\Strategy\StrategyRegistry;
24
use Webhook\Domain\Model\Webhook;
25
26
27
/**
28
 * Class WebhookController
29
 *
30
 * @package Webhook\Bundle\Controller
31
 */
32
class WebhookController extends Controller
33
{
34
    /**
35
     * @param Request $request
36
     *
37
     * @return Response
38
     */
39
    public function createAction(Request $request): Response
40
    {
41
        $data = json_decode((string)$request->getContent(), true);
42
43
        if (empty($data) || json_last_error() !== JSON_ERROR_NONE) {
44
            return new JsonResponse(['error' => 'Malformed json provided.'], 400);
45
        }
46
47
        if (true !== $response = $this->validate($data)) {
48
            return $response;
49
        }
50
51
        $message = $this->buildMessage($data);
52
53
        $this->get('webhook.repository')->save($message);
54
        $this->get('amqp.producer')->publish($message);
55
56
        return new JsonResponse($message, Response::HTTP_CREATED);
57
    }
58
59
    /**
60
     * @param array $data
61
     *
62
     * @return bool|JsonResponse
63
     */
64
    private function validate(array $data)
65
    {
66
        $validator = Validation::createValidator();
67
        $violations = $validator->validate($data, $this->getConstraints());
68
69
        if (0 !== $violations->count()) {
70
            $errors = [];
71
            foreach ($violations as $violation) {
72
                $field = preg_replace('/\[|\]/', '', $violation->getPropertyPath());
73
                $error = $violation->getMessage();
74
                $errors[$field] = $error;
75
            }
76
77
            return new JsonResponse(['errors' => $errors], 400);
78
        }
79
80
        return true;
81
    }
82
83
    /**
84
     * @return Collection
85
     */
86
    private function getConstraints(): Collection
87
    {
88
        $strategiesMap = StrategyRegistry::getMap();
89
        $strategies = array_keys($strategiesMap);
90
91
        return new Collection([
92
            'fields' => [
93
                'body'            => new Required(),
94
                'url'             => new Url(),
95
                'strategy'        => new Optional(
96
                    new Collection(
97
                        [
98
                            'fields' => [
99
                                'name'    => new Required(new Choice(['choices' => $strategies, 'strict' => true])),
100
                                'options' => new Optional(new All(new Length(['min' => 1]))),
101
                            ]
102
                        ])
103
                ),
104
                'raw'             => new Optional(new Choice(['choices' => [true, false], 'strict' => true])),
105
                'maxAttempts'     => new Optional(new Range(['min' => 1, 'max' => 100])),
106
                'expectedCode'    => new Optional(new Range(['min' => 200, 'max' => 515])),
107
                'expectedContent' => new Optional(new Length(['min' => 1, 'max' => 128])),
108
                'userAgent'       => new Optional(new Length(['min' => 1, 'max' => 128])),
109
                'metadata'        => new Optional(new All(new Length(['min' => 1, 'max' => 128]))),
110
                'callbackUrl'     => new Optional(new Url()),
111
                'reference'       => new Optional(new Length(['min' => 1, 'max' => 255])),
112
            ]
113
        ]);
114
    }
115
116
    /**
117
     * @param $id
118
     *
119
     * @return JsonResponse
120
     */
121
    public function getAction($id): JsonResponse
122
    {
123
        $message = $this->get('webhook.repository')->get($id);
124
125
        if (null === $message) {
126
            return new JsonResponse(['error' => 'Message not found'], 404);
127
        }
128
129
        return new JsonResponse($message);
130
    }
131
132
    /**
133
     * @param $id
134
     *
135
     * @return JsonResponse
136
     */
137
    public function getByReferenceAction($id): JsonResponse
138
    {
139
        $message = $this->get('webhook.repository')->getByReference($id);
140
141
        return new JsonResponse($message);
142
    }
143
144
    /**
145
     * @return JsonResponse
146
     */
147
    public function getLastAction(): JsonResponse
148
    {
149
        /** @var WebhookRepository $message */
150
        $repo = $this->get('webhook.repository');
151
152
        return new JsonResponse($repo->getLastWebhooks(50));
153
    }
154
155
    /**
156
     * @param $data
157
     *
158
     * @return Webhook
159
     */
160
    private function buildMessage($data): Webhook
161
    {
162
        $body = is_array($data['body']) ? json_encode($data['body']) : $data['body'];
163
        $message = new Webhook($data['url'], $body);
164
165
        unset($data['url'], $data['body']);
166
167
        if (isset($data['strategy'])) {
168
            $name = $data['strategy']['name'];  
169
            $options = $data['strategy']['options'] ?? [];
170
171
            $data['strategy'] = StrategyFactory::create($name, $options);
172
        }
173
174
        $accessor = new PropertyAccessor();
175
        foreach ($data as $k => $v) {
176
            if (null === $v || '' === $v) {
177
                continue;
178
            }
179
180
            if ($accessor->isWritable($message, $k)) {
181
                $accessor->setValue($message, $k, $v);
182
            }
183
        }
184
        return $message;
185
    }
186
}