SalsifyFetchExtension   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 22
eloc 87
c 2
b 0
f 0
dl 0
loc 203
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getImporterKey() 0 3 1
A canFetchSalsify() 0 14 5
A onBeforeInit() 0 3 1
A configContainsMapping() 0 11 3
A getClassMapping() 0 10 3
A fetchProduct() 0 27 2
A mapData() 0 37 1
A salsifyFetch() 0 31 6
1
<?php
2
3
namespace Dynamic\Salsify\ORM;
4
5
use Dynamic\Salsify\Model\Fetcher;
6
use Dynamic\Salsify\Model\Mapper;
7
use Dynamic\Salsify\Task\ImportTask;
8
use Dynamic\Salsify\Traits\InstanceCreator;
9
use GuzzleHttp\Client;
10
use SilverStripe\Admin\LeftAndMainExtension;
0 ignored issues
show
Bug introduced by
The type SilverStripe\Admin\LeftAndMainExtension was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use SilverStripe\Core\Config\Config;
12
use SilverStripe\Forms\Form;
13
use SilverStripe\ORM\DataObject;
14
use SilverStripe\Security\Security;
15
16
/**
17
 * Class LeftAndMainExtension
18
 * @package Dynamic\Salsify\ORM
19
 * @property-read \SilverStripe\Admin\LeftAndMain|\Dynamic\Salsify\ORM\SalsifyFetchExtension $owner
20
 */
21
class SalsifyFetchExtension extends LeftAndMainExtension
22
{
23
    use InstanceCreator;
24
25
    /**
26
     * @var bool
27
     */
28
    private $noChannel = true;
0 ignored issues
show
introduced by
The private property $noChannel is not used, and could be removed.
Loading history...
29
30
    /**
31
     * @var array
32
     */
33
    private static $allowed_actions = [
0 ignored issues
show
introduced by
The private property $allowed_actions is not used, and could be removed.
Loading history...
34
        'salsifyFetch',
35
    ];
36
37
    /**
38
     * @return string
39
     */
40
    protected function getImporterKey()
41
    {
42
        return 'single';
43
    }
44
45
    /**
46
     *
47
     */
48
    public function onBeforeInit()
49
    {
50
        $this->createServices();
51
    }
52
53
    /**
54
     * @return boolean
55
     * @throws \Exception
56
     */
57
    public function canFetchSalsify()
58
    {
59
        $className = $this->owner->currentPage()->getClassName();
60
61
        // Only allow when product has a salsify id and has a single mapping config
62
        if (
63
            $this->owner->currentPage()->SalsifyID &&
64
            $this->hasService(Mapper::class) &&
65
            $this->configContainsMapping($className) &&
66
            $this->getFetcher()->config()->get('organizationID')
67
        ) {
68
            return true;
69
        }
70
        return false;
71
    }
72
73
    /**
74
     * @param string $className
75
     *
76
     * @return boolean
77
     * @throws \Exception
78
     */
79
    private function configContainsMapping($className)
80
    {
81
        if (!$this->getMapper()->config()->get('mapping')) {
82
            return false;
83
        }
84
85
        if (!$this->getClassMapping($className)) {
86
            return false;
87
        }
88
89
        return true;
90
    }
91
92
    /**
93
     * @param string $className
94
     * @return bool|array
95
     * @throws \Exception
96
     */
97
    private function getClassMapping($className)
98
    {
99
        $mapping = $this->getMapper()->config()->get('mapping');
100
        if (array_key_exists($className, $mapping)) {
101
            return $mapping[$className];
102
        }
103
        if (array_key_exists('\\' . $className, $mapping)) {
104
            return $mapping['\\' . $className];
105
        }
106
        return false;
107
    }
108
109
    /**
110
     * @param array $data
111
     * @param Form $form
112
     * @return \SilverStripe\Control\HTTPResponse
113
     * @throws \Exception
114
     */
115
    public function salsifyFetch($data, $form)
116
    {
117
        $className = $this->owner->currentPage()->getClassName();
118
119
        $id = $data['ID'];
120
        /** @var DataObject|\Dynamic\Salsify\ORM\SalsifyIDExtension $record */
121
        $record = DataObject::get_by_id($className, $id);
122
        if ($record && !$record->canEdit()) {
123
            return Security::permissionFailure();
124
        }
125
126
        if (!$record || !$record->SalsifyID) {
0 ignored issues
show
introduced by
$record is of type SilverStripe\ORM\DataObject, thus it always evaluated to true.
Loading history...
127
            $this->owner->httpError(404, "Bad salsify ID: $id");
128
        }
129
130
        ImportTask::config()->remove('output');
131
        $data = $this->fetchProduct($record->SalsifyID);
132
        if (array_key_exists('salsify:parent_id', $data)) {
133
            $parent = $this->fetchProduct($data['salsify:parent_id']);
134
            $data = array_merge($parent, $data);
0 ignored issues
show
Bug introduced by
It seems like $parent can also be of type null; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

134
            $data = array_merge(/** @scrutinizer ignore-type */ $parent, $data);
Loading history...
135
        }
136
137
        $this->changeToSalsifyUser();
138
        $this->mapData($record, $data);
139
        $this->changeToPreviousUser();
140
141
        $this->owner->getResponse()->addHeader(
142
            'X-Status',
143
            rawurlencode(_t(__CLASS__ . '.UPDATED', 'Updated.'))
144
        );
145
        return $this->owner->getResponseNegotiator()->respond($this->owner->getRequest());
146
    }
147
148
    /**
149
     * @param string $salsifyID
150
     * @return array|NULL
151
     * @throws \Exception
152
     */
153
    private function fetchProduct($salsifyID)
154
    {
155
156
        $apiKey = $this->getFetcher()->config()->get('apiKey');
157
        $timeout = $this->getFetcher()->config()->get('timeout');
158
        $orgID = $this->getFetcher()->config()->get('organizationID');
159
160
        $url = "v1/orgs/{$orgID}/products/{$salsifyID}";
161
162
        $client = new Client([
163
            'base_uri' => Fetcher::API_BASE_URL,
164
            'timeout' => $timeout,
165
            'http_errors' => false,
166
            'verify' => true,
167
            'headers' => [
168
                'Authorization' => 'Bearer ' . $apiKey,
169
                'Content-Type' => 'application/json',
170
            ],
171
        ]);
172
173
        $response = $client->get($url);
174
175
        if ($response->getStatusCode() == 404) {
176
            $this->owner->httpError(404, "Bad salsify ID: $salsifyID");
177
        }
178
179
        return json_decode($response->getBody(), true);
180
    }
181
182
    /**
183
     * @param DataObject $record
184
     * @param array $data
185
     * @throws \Exception
186
     */
187
    private function mapData($record, $data)
188
    {
189
        $forceUpdate = Config::inst()->get(
190
            $this->owner->currentPage()->getClassName(),
191
            'refetch_force_update'
192
        );
193
194
        $this->getFetcher()->config()->set('useLatest', true);
195
        $this->getFetcher()->startExportRun();
196
        $this->getFetcher()->waitForExportRunToComplete();
197
        $file = $this->getFetcher()->getExportUrl();
198
199
        $this->getMapper()->extend('onBeforeMap', $file, Mapper::$SINGLE);
200
201
        $this->getMapper()->mapToObject(
202
            $record->getClassName(),
203
            $this->getClassMapping($record->getClassName()),
0 ignored issues
show
Bug introduced by
It seems like $this->getClassMapping($record->getClassName()) can also be of type boolean; however, parameter $mappings of Dynamic\Salsify\Model\Mapper::mapToObject() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

203
            /** @scrutinizer ignore-type */ $this->getClassMapping($record->getClassName()),
Loading history...
204
            $data,
205
            $record,
206
            false,
207
            $forceUpdate
208
        );
209
210
        $forceUpdateRelations = Config::inst()->get(
211
            $this->owner->currentPage()->getClassName(),
212
            'refetch_force_update_relations'
213
        );
214
        $this->getMapper()->mapToObject(
215
            $record->getClassName(),
216
            $this->getClassMapping($record->getClassName()),
217
            $data,
218
            $record,
219
            true,
220
            $forceUpdateRelations
221
        );
222
223
        $this->getMapper()->extend('onAfterMap', $file, Mapper::$SINGLE);
224
    }
225
}
226