Completed
Push — master ( 91a0f5...aaf4d8 )
by Giacomo "Mr. Wolf"
01:50
created

BeesWaxSegmentManager::uploadSegmentUsersFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 2
dl 0
loc 15
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace Audiens\BeesWax\Segment;
4
5
use Audiens\BeesWax\BeesWaxRequest;
6
use Audiens\BeesWax\BeesWaxResponse;
7
use Audiens\BeesWax\BeesWaxSession;
8
use Audiens\BeesWax\Exception\BeesWaxGenericException;
9
use Audiens\BeesWax\Exception\BeesWaxResponseException;
10
11
class BeesWaxSegmentManager
12
{
13
    public const USERS_UPLOAD_OPERATION_TYPE_ADD_SEGMENTS         = 'ADD_SEGMENTS';
14
    public const USERS_UPLOAD_OPERATION_TYPE_REPLACE_ALL_SEGMENTS = 'REPLACE_ALL_SEGMENTS';
15
16
    public const USERS_UPLOAD_CONTINENT_NAM  = 'NAM';
17
    public const USERS_UPLOAD_CONTINENT_EMEA = 'EMEA';
18
    public const USERS_UPLOAD_CONTINENT_APEC = 'APEC';
19
20
    protected const API_CRUD_PATH = '/rest/segment';
21
    protected const API_UPLOAD_METADATA_PATH = '/rest/segment_upload';
22
    protected const API_UPLOAD_FILE_PATH = '/rest/segment_upload/upload/%d';
23
24
    /** @var BeesWaxSession */
25
    protected $session;
26
27
    public function __construct(BeesWaxSession $session)
28
    {
29
        $this->session = $session;
30
    }
31
32
    /**
33
     * Returns a BeesWax segment.
34
     * The argument segment will be modified adding the identifier to it
35
     *
36
     * @param BeesWaxSegment $segment
37
     *
38
     * @return BeesWaxSegment
39
     * @throws \Audiens\BeesWax\Exception\BeesWaxGenericException (CODE_SEGMENT_ALREADY_CREATED)
40
     * @throws \Audiens\BeesWax\Exception\BeesWaxResponseException
41
     */
42
    public function create(BeesWaxSegment $segment): BeesWaxSegment
43
    {
44
        if ($segment->getId()) {
45
            throw new BeesWaxGenericException(
46
                sprintf('The segment %s has already been created', $segment->getName()),
47
                BeesWaxGenericException::CODE_SEGMENT_ALREADY_CREATED
48
            );
49
        }
50
51
        $payloadData = [
52
            'segment_name' => $segment->getName(),
53
            'cpm_cost' => $segment->getCpmCost(),
54
            'ttl_days' => $segment->getTtlDays(),
55
            'aggregate_excludes' => $segment->isAggregateExcludes(),
56
        ];
57
58
        if ($advertiserId = $segment->getAdvertiserId()) {
59
            $payloadData['advertiser_id'] = $advertiserId;
60
        }
61
62
        if ($description = $segment->getDescription()) {
63
            $payloadData['segment_description'] = $description;
64
        }
65
66
        if ($alternativeId = $segment->getAlternativeId()) {
67
            $payloadData['alternative_id'] = $alternativeId;
68
        }
69
70
        $payload = json_encode($payloadData);
71
72
        $request = $this->session->getRequestBuilder()->build(static::API_CRUD_PATH, [], BeesWaxRequest::METHOD_POST, $payload);
73
74
        $response = $request->doRequest();
75
        $this->manageSuccess($response, 'Error creating segment: %s');
76
77
        $responseData = json_decode($response->getPayload());
78
        $segmentId = $responseData->payload->id ?? null;
79
80
        if ($segmentId === null) {
81
            throw new BeesWaxResponseException(
82
                $response->getCurlHandler(),
83
                'Error creating segment: id not found'
84
            );
85
        }
86
87
        $segment->setId((string) $segmentId);
88
89
        return $segment;
90
    }
91
92
    /**
93
     * @param string $id
94
     *
95
     * @return BeesWaxSegment
96
     * @throws BeesWaxGenericException (CODE_SEGMENT_NOT_FOUND)
97
     * @throws BeesWaxResponseException
98
     */
99
    public function read(string $id): BeesWaxSegment
100
    {
101
        $request = $this->session->getRequestBuilder()->build(static::API_CRUD_PATH, ['segment_id' => $id], BeesWaxRequest::METHOD_GET, null);
102
103
        $response = $request->doRequest();
104
        $this->manageSuccess($response, 'Error reading segment: %s');
105
106
        $responseData = json_decode($response->getPayload());
107
        if (
108
            !isset($responseData->payload)
109
            || !\is_array($responseData->payload)
110
            || \count($responseData->payload) !== 1
111
        ) {
112
            throw new BeesWaxGenericException(
113
                sprintf('Segment #%s not found', $id),
114
                BeesWaxGenericException::CODE_SEGMENT_NOT_FOUND
115
            );
116
        }
117
        $responseData = $responseData->payload[0];
118
119
        $segment = new BeesWaxSegment(
120
            $responseData->segment_name,
121
            $responseData->segment_description,
122
            $responseData->alternative_id,
123
            $responseData->advertiser_id,
124
            $responseData->cpm_cost,
125
            $responseData->ttl_days,
126
            $responseData->aggregate_excludes
127
        );
128
        $segment->setId((string)$responseData->segment_id);
129
130
        return $segment;
131
    }
132
133
    /**
134
     * @param BeesWaxSegment $segment
135
     *
136
     * @return BeesWaxSegment
137
     * @throws BeesWaxGenericException (CODE_NON_EXISTING_SEGMENT)
138
     */
139
    public function update(BeesWaxSegment $segment): BeesWaxSegment
140
    {
141
        if ($segment->getId() === null) {
142
            throw new BeesWaxGenericException(
143
                "Can't update a non-existing segment!",
144
                BeesWaxGenericException::CODE_NON_EXISTING_SEGMENT
145
            );
146
        }
147
148
        $payloadData = [
149
            'segment_id' => (int)$segment->getId(),
150
            'segment_name' => $segment->getName(),
151
            'alternative_id' => $segment->getAlternativeId(),
152
            'advertiser_id' => $segment->getAdvertiserId(),
153
            'segment_description' => $segment->getDescription(),
154
            'cpm_cost' => $segment->getCpmCost(),
155
            'aggregate_excludes' => $segment->isAggregateExcludes(),
156
        ];
157
158
        $payload = json_encode($payloadData);
159
160
        $request = $this->session->getRequestBuilder()->build(static::API_CRUD_PATH, [], BeesWaxRequest::METHOD_PUT, $payload);
161
162
        $response = $request->doRequest();
163
        $this->manageSuccess($response, 'Error updating segment: %s');
164
165
        return $segment;
166
    }
167
168
    /**
169
     * @param BeesWaxSegment           $segment
170
     * @param BeesWaxSegmentUserData[] $userData
171
     * @param string                   $segmentKeyType BeesWaxSegment::SEGMENT_KEY_TYPE_*
172
     * @param string                   $userIdType BeesWaxSegmentUserData::USER_ID_TYPE_*
173
     * @param string                   $operationType BeesWaxSegmentManager::USERS_UPLOAD_OPERATION_TYPE_*
174
     * @param string                   $continent BeesWaxSegmentManager::USERS_UPLOAD_CONTINENT_*
175
     *
176
     * @throws BeesWaxGenericException
177
     *
178
     * @see BeesWaxSegment::SEGMENT_KEY_TYPE_*
179
     * @see BeesWaxSegmentUserData::USER_ID_TYPE_*
180
     * @see BeesWaxSegmentManager::USERS_UPLOAD_OPERATION_TYPE_*
181
     * @see BeesWaxSegmentManager::USERS_UPLOAD_CONTINENT_*
182
     */
183
    public function usersUpload(
184
        BeesWaxSegment $segment,
185
        array $userData,
186
        string $segmentKeyType = BeesWaxSegment::SEGMENT_KEY_TYPE_DEFAULT,
187
        string $userIdType = BeesWaxSegmentUserData::USER_ID_TYPE_BEESWAX_COOKIE,
188
        string $operationType = BeesWaxSegmentManager::USERS_UPLOAD_OPERATION_TYPE_ADD_SEGMENTS,
189
        string $continent = BeesWaxSegmentManager::USERS_UPLOAD_CONTINENT_NAM
190
    ): void {
191
        if ($segment->getId() === null) {
192
            throw new BeesWaxGenericException(
193
                "Can't add users to a non-existing segment!",
194
                BeesWaxGenericException::CODE_NON_EXISTING_SEGMENT
195
            );
196
        }
197
198
        $fh = tmpfile();
199
        $tmpFilePath = realpath(stream_get_meta_data($fh)['uri']);
0 ignored issues
show
Bug introduced by
It seems like $fh can also be of type false; however, parameter $stream of stream_get_meta_data() does only seem to accept resource, 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

199
        $tmpFilePath = realpath(stream_get_meta_data(/** @scrutinizer ignore-type */ $fh)['uri']);
Loading history...
200
        $rows = 0;
201
        $exceptionToThrow = null;
202
203
        try {
204
            foreach ($userData as $datum) {
205
                $rowData = array_merge([$datum->getUserId()], $datum->getSegments());
206
                fputcsv($fh, $rowData, '|');
0 ignored issues
show
Bug introduced by
It seems like $fh can also be of type false; however, parameter $handle of fputcsv() does only seem to accept resource, 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

206
                fputcsv(/** @scrutinizer ignore-type */ $fh, $rowData, '|');
Loading history...
207
                $rows++;
208
            }
209
210
            $fileSize = filesize($tmpFilePath);
211
            if ($fileSize === false || $fileSize === 0) {
212
                fclose($fh);
0 ignored issues
show
Bug introduced by
It seems like $fh can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, 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

212
                fclose(/** @scrutinizer ignore-type */ $fh);
Loading history...
213
214
                if ($rows > 0) {
215
                    throw new BeesWaxGenericException(
216
                        'An error occurred creating the file to upload',
217
                        BeesWaxGenericException::CODE_ERROR_UPLOADING_SEGMENTS_USERS
218
                    );
219
                }
220
221
                return;
222
            }
223
224
            $fileId = $this->sendUsersUploadMetadata(
225
                $tmpFilePath,
226
                $segmentKeyType,
227
                $continent,
228
                $userIdType,
229
                $operationType
230
            );
231
232
            $this->uploadSegmentUsersFile($fileId, $tmpFilePath);
233
        } catch (BeesWaxGenericException $exception) {
234
            $exceptionToThrow = $exception;
235
        }
236
237
        fclose($fh);
238
239
        if ($exceptionToThrow !== null) {
240
            throw $exceptionToThrow;
241
        }
242
    }
243
244
    public function delete(BeesWaxSegment $segment): bool
0 ignored issues
show
Unused Code introduced by
The parameter $segment is not used and could be removed. ( Ignorable by Annotation )

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

244
    public function delete(/** @scrutinizer ignore-unused */ BeesWaxSegment $segment): bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
245
    {
246
        throw new \RuntimeException('Not implemented yet!');
247
    }
248
249
    /**
250
     * @param BeesWaxResponse $response
251
     * @param string          $errorFormat with one string parameter
252
     *
253
     * @throws BeesWaxResponseException
254
     */
255
    private function manageSuccess(BeesWaxResponse $response, string $errorFormat): void
256
    {
257
        $responseData = json_decode($response->getPayload());
258
        $responseErrors = $responseData->errors ?? [];
259
        $statusCode = $response->getStatusCode();
260
261
        if (
262
            !empty($responseErrors)
263
            || \is_bool($responseData)
264
            || (isset($responseData->success) && !$responseData->success)
265
            || $statusCode !== 200
266
        ) {
267
            $message = \count($responseErrors) ? $responseErrors[0] : ($responseData->message ?? (string) $statusCode);
268
            throw new BeesWaxResponseException(
269
                $response->getCurlHandler(),
270
                sprintf($errorFormat, $message)
271
            );
272
        }
273
    }
274
275
    /**
276
     * @param        $filePath
277
     * @param string $segmentKeyType
278
     * @param string $continent
279
     * @param string $userIdType
280
     * @param string $operationType
281
     *
282
     * @return int
283
     *
284
     * @throws BeesWaxGenericException
285
     * @throws BeesWaxResponseException
286
     */
287
    private function sendUsersUploadMetadata(
288
        $filePath,
289
        string $segmentKeyType,
290
        string $continent,
291
        string $userIdType,
292
        string $operationType
293
    ): int {
294
        $fileSize = filesize($filePath);
295
296
        if ($fileSize === false) {
297
            throw new BeesWaxGenericException(
298
                'Impossible to determine the size of the file to upload',
299
                BeesWaxGenericException::CODE_ERROR_UPLOADING_SEGMENTS_USERS
300
            );
301
        }
302
        $payload = \json_encode([
303
            'file_name' => basename($filePath),
304
            'size_in_bytes' => $fileSize,
305
            'file_format' => 'DELIMITED',
306
            'segment_key_type' => $segmentKeyType,
307
            'continent' => $continent,
308
            'user_id_type' => $userIdType,
309
            'operation_type' => $operationType,
310
        ]);
311
312
        $request = $this
313
            ->session
314
            ->getRequestBuilder()
315
            ->build(static::API_UPLOAD_METADATA_PATH, [], BeesWaxRequest::METHOD_POST, $payload);
316
317
        $response = $request->doRequest();
318
        $this->manageSuccess($response, "Error sending segment's users meta data: %s");
319
320
        $data = \json_decode($response->getPayload());
321
322
        return $data->payload->id;
323
    }
324
325
    /**
326
     * @param int    $fileId
327
     * @param string $filePath
328
     *
329
     * @throws BeesWaxGenericException
330
     * @throws BeesWaxResponseException
331
     */
332
    private function uploadSegmentUsersFile(int $fileId, string $filePath): void
333
    {
334
        $cFile = new \CURLFile($filePath, 'text/csv', 'segment_file');
335
        $cFile->setPostFilename(basename($filePath));
336
        $payload = ['segment_file' => $cFile];
337
338
        $request = $this->session->getRequestBuilder()->build(
339
            sprintf(static::API_UPLOAD_FILE_PATH, $fileId),
340
            [],
341
            BeesWaxRequest::METHOD_POST,
342
            $payload
0 ignored issues
show
Bug introduced by
$payload of type array<string,CURLFile> is incompatible with the type null|string|string[] expected by parameter $payload of Audiens\BeesWax\BeesWaxRequestBuilder::build(). ( Ignorable by Annotation )

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

342
            /** @scrutinizer ignore-type */ $payload
Loading history...
343
        );
344
345
        $response = $request->doRequest();
346
        $this->manageSuccess($response, "Error uploading segment's users CSV file: %s");
347
    }
348
}
349