CapsuleService::configureClient()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 16
rs 9.9332
1
<?php
2
/**
3
 * SaaS Link plugin for Craft CMS 3.x
4
 *
5
 * @link      https://workingconcept.com
6
 * @copyright Copyright (c) 2018 Working Concept Inc.
7
 */
8
9
namespace workingconcept\saaslink\services;
10
11
use workingconcept\saaslink\models\capsule\CapsuleOpportunity;
12
use workingconcept\saaslink\SaasLink;
13
use workingconcept\saaslink\models\capsule\CapsuleParty;
14
use GuzzleHttp\Psr7;
15
use Craft;
16
17
class CapsuleService extends SaasLinkService
18
{
19
    // Constants
20
    // =========================================================================
21
22
    const CACHE_ENABLED = true;
23
    const CACHE_SECONDS = 60;
24
25
26
    // Properties
27
    // =========================================================================
28
29
    /**
30
     * @var string
31
     */
32
    protected $apiBaseUrl = 'https://api.capsulecrm.com/api/v2/';
33
34
    /**
35
     * @var string
36
     */
37
    public $serviceName = 'Capsule';
38
39
    /**
40
     * @var string
41
     */
42
    public $serviceSlug = 'capsule';
43
44
45
    // Public Methods
46
    // =========================================================================
47
48
    /**
49
     * @inheritdoc
50
     */
51
    public function isConfigured(): bool
52
    {
53
        return ! empty($this->settings->capsuleToken);
54
    }
55
56
    /**
57
     * @inheritdoc
58
     */
59
    public function configureClient()
60
    {
61
        $this->client = new \GuzzleHttp\Client([
62
            'base_uri' => $this->apiBaseUrl,
63
            'headers' => [
64
                'Authorization' => 'Bearer ' . $this->settings->capsuleToken,
65
                'Accept'        => 'application/json',
66
                'User-Agent'    => 'Craft CMS',
67
            ],
68
            'verify' => false,
69
            'debug' => false
70
        ]);
71
72
        if (empty($this->settings->capsuleBaseUrl))
73
        {
74
            $this->setCapsuleBaseUrlSetting();
75
        }
76
    }
77
78
    /**
79
     * Save a reference to the customer-facing base Capsule URL so we don't have to keep looking it up.
80
     */
81
    public function setCapsuleBaseUrlSetting()
82
    {
83
        $this->settings->capsuleBaseUrl = $this->getSite()->url;
84
85
        // let the base plugin class worry about *saving* the settings model
86
        // Craft::$app->plugins->savePluginSettings(SaasLink::$plugin, $this->settings->toArray());
87
    }
88
89
    /**
90
     * @inheritdoc
91
     */
92
    public function getAvailableRelationshipTypes(): array
93
    {
94
        return [
95
            [
96
                'label' => Craft::t('saas-link', 'Opportunity'),
97
                'value' => 'opportunity'
98
            ],
99
            [
100
                'label' => Craft::t('saas-link', 'Organization'),
101
                'value' => 'organization'
102
            ],
103
            [
104
                'label' => Craft::t('saas-link', 'Person'),
105
                'value' => 'person'
106
            ],
107
        ];
108
    }
109
110
    /**
111
     * @inheritdoc
112
     */
113
    public function getOptions($relationshipType): array
114
    {
115
        $options = [];
116
117
        if ($relationshipType === 'opportunity')
118
        {
119
            $opportunities = $this->getOpportunities();
120
121
            // first sort by updatedAt
122
            usort($opportunities, function($a, $b) {
123
                return $b->updatedAt <=> $a->updatedAt;
124
            });
125
126
            foreach ($opportunities as $opportunity)
127
            {
128
                $label = $opportunity->name;
129
130
                if (isset($opportunity->party->name))
131
                {
132
                    $label = $opportunity->name . ' (' . $opportunity->party->name . ')';
133
                }
134
135
                $options[] = [
136
                    'label'   => $label,
137
                    'value'   => (string)$opportunity->id,
138
                    'link'    => $this->settings->capsuleBaseUrl . '/opportunity/' . $opportunity->id,
139
                    'default' => null
140
                ];
141
            }
142
        }
143
        elseif ($relationshipType === 'organization')
144
        {
145
            foreach ($this->getOrganizations() as $organization)
146
            {
147
                $options[] = [
148
                    'label'   => $organization->name,
149
                    'value'   => (string)$organization->id,
150
                    'link'    => $this->settings->capsuleBaseUrl . '/party/' . $organization->id,
151
                    'default' => null
152
                ];
153
            }
154
155
            // alphabetize
156
            usort($options, function($a, $b) {
157
                return strtolower($a['label']) <=> strtolower($b['label']);
158
            });
159
        }
160
        elseif ($relationshipType === 'person')
161
        {
162
            foreach (SaasLink::$plugin->capsule->getPeople() as $person)
163
            {
164
                $options[] = [
165
                    'label'   => $person->firstName . ' ' . $person->lastName,
166
                    'value'   => (string)$person->id,
167
                    'link'    => $this->settings->capsuleBaseUrl . '/party/' . $person->id,
168
                    'default' => null
169
                ];
170
            }
171
172
            // alphabetize
173
            usort($options, function($a, $b) {
174
                return strtolower($a['label']) <=> strtolower($b['label']);
175
            });
176
        }
177
178
        return $options;
179
    }
180
181
    /**
182
     * Get available Capsule parties.
183
     *
184
     * @return CapsuleParty[]
185
     */
186
    public function getParties(): array
187
    {
188
        $parties = [];
189
        $result  = $this->collectPaginatedResults('parties');
190
191
        foreach ($result as $partyData)
192
        {
193
            $parties[] = new CapsuleParty($partyData);
194
        }
195
196
        return $parties;
197
    }
198
199
    /**
200
     * Get available Capsule humans.
201
     *
202
     * @return CapsuleParty[]
203
     */
204
    public function getPeople(): array
205
    {
206
        $people = [];
207
208
        foreach ($this->getParties() as $party)
209
        {
210
            if ($party->type === CapsuleParty::TYPE_PERSON)
211
            {
212
                $people[] = $party;
213
            }
214
        }
215
216
        return $people;
217
    }
218
219
    /**
220
     * Get available Capsule organizations.
221
     *
222
     * @return CapsuleParty[]
223
     */
224
    public function getOrganizations(): array
225
    {
226
        $organizations = [];
227
228
        foreach ($this->getParties() as $party)
229
        {
230
            if ($party->type === CapsuleParty::TYPE_ORGANISATION)
231
            {
232
                $organizations[] = $party;
233
            }
234
        }
235
236
        return $organizations;
237
    }
238
239
    /**
240
     * Get available Capsule opportunities.
241
     *
242
     * @return CapsuleOpportunity[]
243
     */
244
    public function getOpportunities(): array
245
    {
246
        $opportunities = [];
247
        $result = $this->collectPaginatedResults('opportunities');
248
249
        foreach ($result as $opportunityData)
250
        {
251
            $opportunities[] = new CapsuleOpportunity($opportunityData);
252
        }
253
254
        return $opportunities;
255
    }
256
257
    /**
258
     * Get Capsule site details.
259
     *
260
     * @return object
261
     */
262
    public function getSite()
263
    {
264
        if ($cachedResponse = Craft::$app->cache->get('capsule_site'))
265
        {
266
            return $cachedResponse;
267
        }
268
269
        $response = $this->client->get('site');
270
        $responseData = json_decode($response->getBody(true))->site;
0 ignored issues
show
Unused Code introduced by
The call to Psr\Http\Message\MessageInterface::getBody() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

270
        $responseData = json_decode($response->/** @scrutinizer ignore-call */ getBody(true))->site;

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
271
272
        Craft::$app->cache->set('capsule_site', $responseData, 3600);
273
274
        return $responseData;
275
    }
276
277
278
    // Private Methods
279
    // =========================================================================
280
281
    private function collectPaginatedResults($endpoint, $property = '')
282
    {
283
        $cacheKey = 'capsule_' . $endpoint;
284
285
        if (strpos($endpoint, '?') !== false)
286
        {
287
            $endpointPieces = explode('?', $endpoint);
288
            $endpointWithoutParameters = $endpointPieces[0];
289
            $endpoint .= '&page=';
290
        }
291
        else
292
        {
293
            $endpointWithoutParameters = $endpoint;
294
            $endpoint .= '?page=';
295
        }
296
297
        if ($property === '')
298
        {
299
            $property = $endpointWithoutParameters;
300
        }
301
302
        if (self::CACHE_ENABLED)
303
        {
304
            if ($cachedRecords = Craft::$app->cache->get($cacheKey))
305
            {
306
                return $cachedRecords;
307
            }
308
        }
309
310
        $fetchedAllRecords = false;
311
        $records           = [];
312
        $page              = 1;
313
314
        while ( ! $fetchedAllRecords)
315
        {
316
            $response = $this->client->get($endpoint . $page);
317
            $responseData = json_decode($response->getBody(true));
0 ignored issues
show
Unused Code introduced by
The call to Psr\Http\Message\MessageInterface::getBody() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

317
            $responseData = json_decode($response->/** @scrutinizer ignore-call */ getBody(true));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
318
319
            $records = array_merge($records, $responseData->{$property});
320
321
            if ($response->hasHeader('Link'))
322
            {
323
                $parsed = /** @scrutinizer ignore-call */ Psr7\parse_header($response->getHeader('Link'));
324
325
                if ( ! empty($parsed[0]['rel']) && $parsed[0]['rel'] === 'next')
326
                {
327
                    $page++;
328
                    continue;
329
                }
330
            }
331
332
            $fetchedAllRecords = true;
333
        }
334
335
        if (self::CACHE_ENABLED)
336
        {
337
            Craft::$app->cache->set($cacheKey, $records, self::CACHE_SECONDS);
338
        }
339
340
        return $records;
341
    }
342
343
}
344