Client::getAttachmentsAsArrays()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Nexylan packages.
7
 *
8
 * (c) Nexylan SAS <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Nexy\Slack;
15
16
use Nexy\Slack\Exception\SlackApiException;
17
use Psr\Http\Client\ClientInterface as HttpClientInterface;
18
use Psr\Http\Message\RequestFactoryInterface;
19
use Psr\Http\Message\StreamFactoryInterface;
20
use Symfony\Component\OptionsResolver\OptionsResolver;
21
22
/**
23
 * @author Sullivan Senechal <[email protected]>
24
 */
25
final class Client implements ClientInterface
26
{
27
    /**
28
     * @var ErrorResponseHandler
29
     */
30
    private $errorResponseHandler;
31
32
    /**
33
     * @var string
34
     */
35
    private $endpoint;
36
37
    /**
38
     * @var array
39
     */
40
    private $options;
41
42
    /**
43
     * @var HttpClientInterface
44
     */
45
    private $httpClient;
46
47
    /**
48
     * @var RequestFactoryInterface
49
     */
50
    private $requestFactory;
51
52
    /**
53
     * @var StreamFactoryInterface
54
     */
55
    private $streamFactory;
56
57
    /**
58
     * @param mixed[] $options
59
     */
60
    public function __construct(
61
        HttpClientInterface $httpClient,
62
        RequestFactoryInterface $requestFactory,
63
        StreamFactoryInterface $streamFactory,
64
        string $endpoint,
65
        array $options = []
66
    ) {
67
        $this->httpClient = $httpClient;
68
        $this->requestFactory = $requestFactory;
69
        $this->streamFactory = $streamFactory;
70
        $this->endpoint = $endpoint;
71
72
        $resolver = (new OptionsResolver())
73
            ->setDefaults([
74
                'channel' => null,
75
                'sticky_channel' => false,
76
                'username' => null,
77
                'icon' => null,
78
                'link_names' => false,
79
                'unfurl_links' => false,
80
                'unfurl_media' => true,
81
                'allow_markdown' => true,
82
                'markdown_in_attachments' => [],
83
            ])
84
            ->setAllowedTypes('channel', ['string', 'null'])
85
            ->setAllowedTypes('sticky_channel', ['bool'])
86
            ->setAllowedTypes('username', ['string', 'null'])
87
            ->setAllowedTypes('icon', ['string', 'null'])
88
            ->setAllowedTypes('link_names', 'bool')
89
            ->setAllowedTypes('unfurl_links', 'bool')
90
            ->setAllowedTypes('unfurl_media', 'bool')
91
            ->setAllowedTypes('allow_markdown', 'bool')
92
            ->setAllowedTypes('markdown_in_attachments', 'array')
93
        ;
94
        $this->options = $resolver->resolve($options);
95
96
        $this->errorResponseHandler = new ErrorResponseHandler();
97
    }
98
99
    /**
100
     * Pass any unhandled methods through to a new Message
101
     * instance.
102
     *
103
     * @param string $name      The name of the method
104
     * @param array  $arguments The method arguments
105
     *
106
     * @return \Nexy\Slack\MessageInterface
107
     */
108
    public function __call(string $name, array $arguments): MessageInterface
109
    {
110
        return \call_user_func_array([$this->createMessage(), $name], $arguments);
111
    }
112
113
    public function getOptions(): array
114
    {
115
        return $this->options;
116
    }
117
118
    /**
119
     * Create a new message with defaults.
120
     *
121
     * @return \Nexy\Slack\MessageInterface
122
     */
123
    public function createMessage(): MessageInterface
124
    {
125
        return (new Message($this))
126
            ->setChannel($this->options['channel'])
127
            ->setUsername($this->options['username'])
128
            ->setIcon($this->options['icon'])
129
            ->setAllowMarkdown($this->options['allow_markdown'])
130
            ->setMarkdownInAttachments($this->options['markdown_in_attachments'])
131
        ;
132
    }
133
134
    /**
135
     * Send a message.
136
     *
137
     * @param \Nexy\Slack\MessageInterface $message
138
     *
139
     * @throws \RuntimeException
140
     * @throws \Psr\Http\Client\Exception
141
     * @throws SlackApiException
142
     * @throws \Http\Client\Exception
143
     */
144
    public function sendMessage(MessageInterface $message): void
145
    {
146
        // Ensure the message will always be sent to the default channel if asked for.
147
        if ($this->options['sticky_channel']) {
148
            $message->setChannel($this->options['channel']);
149
        }
150
151
        $payload = $this->preparePayload($message);
152
153
        $encoded = \json_encode($payload, JSON_UNESCAPED_UNICODE);
154
155
        if (false === $encoded) {
156
            throw new \RuntimeException(\sprintf('JSON encoding error %s: %s', \json_last_error(), \json_last_error_msg()));
157
        }
158
159
        $response = $this->httpClient->sendRequest(
160
            $this->requestFactory->createRequest('POST', $this->endpoint)->withBody(
161
                $this->streamFactory->createStream($encoded)
162
            )
163
        );
164
165
        $this->errorResponseHandler->handleResponse($response);
166
    }
167
168
    /**
169
     * Prepares the payload to be sent to the webhook.
170
     *
171
     * @param \Nexy\Slack\MessageInterface $message The message to send
172
     */
173
    private function preparePayload(MessageInterface $message): array
174
    {
175
        $payload = [
176
            'text' => $message->getText(),
177
            'channel' => $message->getChannel(),
178
            'username' => $message->getUsername(),
179
            'link_names' => $this->options['link_names'] ? 1 : 0,
180
            'unfurl_links' => $this->options['unfurl_links'],
181
            'unfurl_media' => $this->options['unfurl_media'],
182
            'mrkdwn' => $this->options['allow_markdown'],
183
        ];
184
185
        if ($icon = $message->getIcon()) {
186
            $payload[$message->getIconType()] = $icon;
187
        }
188
189
        $payload['attachments'] = $this->getAttachmentsAsArrays($message);
190
191
        return $payload;
192
    }
193
194
    /**
195
     * Get the attachments in array form.
196
     *
197
     * @param \Nexy\Slack\MessageInterface $message
198
     */
199
    private function getAttachmentsAsArrays(MessageInterface $message): array
200
    {
201
        $attachments = [];
202
203
        foreach ($message->getAttachments() as $attachment) {
204
            $attachments[] = $attachment->toArray();
205
        }
206
207
        return $attachments;
208
    }
209
}
210