Issues (1)

src/Segment/BeesWaxSegmentManager.php (1 issue)

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 FILE_UPLOAD_STATUS_UNEXPECTED_STATUS_CODE = -1;
17
    public const FILE_UPLOAD_STATUS_NO_FILE_UPLOADED = 0;
18
    public const FILE_UPLOAD_STATUS_PENDING = 1;
19
    public const FILE_UPLOAD_STATUS_PROCESSED = 2;
20
    public const FILE_UPLOAD_STATUS_ERROR = 3;
21
    public const FILE_UPLOAD_STATUS_IN_PROGRESS = 4;
22
23
    public const USERS_UPLOAD_CONTINENT_NAM  = 'NAM';
24
    public const USERS_UPLOAD_CONTINENT_EMEA = 'EMEA';
25
    public const USERS_UPLOAD_CONTINENT_APEC = 'APEC';
26
27
    protected const API_CRUD_PATH = '/rest/segment';
28
    protected const API_UPLOAD_METADATA_PATH = '/rest/segment_upload';
29
    protected const API_UPLOAD_FILE_PATH = '/rest/segment_upload/upload/%d';
30
31
    /** @var BeesWaxSession */
32
    protected $session;
33
34
    public function __construct(BeesWaxSession $session)
35
    {
36
        $this->session = $session;
37
    }
38
39
    /**
40
     * Returns a BeesWax segment.
41
     * The argument segment will be modified adding the identifier to it
42
     *
43
     * @param BeesWaxSegment $segment
44
     *
45
     * @return BeesWaxSegment
46
     * @throws \Audiens\BeesWax\Exception\BeesWaxGenericException (CODE_SEGMENT_ALREADY_CREATED)
47
     * @throws \Audiens\BeesWax\Exception\BeesWaxResponseException
48
     */
49
    public function create(BeesWaxSegment $segment): BeesWaxSegment
50
    {
51
        if ($segment->getId()) {
52
            throw new BeesWaxGenericException(
53
                sprintf('The segment %s has already been created', $segment->getName()),
54
                BeesWaxGenericException::CODE_SEGMENT_ALREADY_CREATED
55
            );
56
        }
57
58
        $payloadData = [
59
            'segment_name' => $segment->getName(),
60
            'cpm_cost' => $segment->getCpmCost(),
61
            'ttl_days' => $segment->getTtlDays(),
62
            'aggregate_excludes' => $segment->isAggregateExcludes(),
63
        ];
64
65
        if ($advertiserId = $segment->getAdvertiserId()) {
66
            $payloadData['advertiser_id'] = $advertiserId;
67
        }
68
69
        if ($description = $segment->getDescription()) {
70
            $payloadData['segment_description'] = $description;
71
        }
72
73
        if ($alternativeId = $segment->getAlternativeId()) {
74
            $payloadData['alternative_id'] = $alternativeId;
75
        }
76
77
        $payload = json_encode($payloadData);
78
79
        $request = $this
80
            ->session
81
            ->getRequestBuilder()
82
            ->build(static::API_CRUD_PATH, [], BeesWaxRequest::METHOD_POST, $payload);
83
84
        $response = $request->doRequest();
85
        $this->manageSuccess($response, 'Error creating segment: %s');
86
87
        $responseData = json_decode($response->getPayload());
88
        $segmentId = $responseData->payload->id ?? null;
89
90
        if ($segmentId === null) {
91
            throw new BeesWaxResponseException(
92
                $response->getCurlHandler(),
93
                'Error creating segment: id not found'
94
            );
95
        }
96
97
        $segment->setId((string) $segmentId);
98
99
        return $segment;
100
    }
101
102
    /**
103
     * @param string $id
104
     *
105
     * @return BeesWaxSegment
106
     * @throws BeesWaxGenericException (CODE_SEGMENT_NOT_FOUND)
107
     * @throws BeesWaxResponseException
108
     */
109
    public function read(string $id): BeesWaxSegment
110
    {
111
        $request = $this
112
            ->session
113
            ->getRequestBuilder()
114
            ->build(static::API_CRUD_PATH, ['segment_id' => $id], BeesWaxRequest::METHOD_GET, null);
115
116
        $response = $request->doRequest();
117
        $this->manageSuccess($response, 'Error reading segment: %s');
118
119
        $responseData = json_decode($response->getPayload());
120
        if (
121
            !isset($responseData->payload)
122
            || !\is_array($responseData->payload)
123
            || \count($responseData->payload) !== 1
124
        ) {
125
            throw new BeesWaxGenericException(
126
                sprintf('Segment #%s not found', $id),
127
                BeesWaxGenericException::CODE_SEGMENT_NOT_FOUND
128
            );
129
        }
130
        $responseData = $responseData->payload[0];
131
132
        $segment = new BeesWaxSegment(
133
            $responseData->segment_name,
134
            $responseData->segment_description,
135
            $responseData->alternative_id,
136
            $responseData->advertiser_id,
137
            $responseData->cpm_cost,
138
            $responseData->ttl_days,
139
            $responseData->aggregate_excludes
140
        );
141
        $segment->setId((string)$responseData->segment_id);
142
143
        return $segment;
144
    }
145
146
    /**
147
     * @param BeesWaxSegment $segment
148
     *
149
     * @return BeesWaxSegment
150
     * @throws BeesWaxGenericException (CODE_NON_EXISTING_SEGMENT)
151
     */
152
    public function update(BeesWaxSegment $segment): BeesWaxSegment
153
    {
154
        if ($segment->getId() === null) {
155
            throw new BeesWaxGenericException(
156
                "Can't update a non-existing segment!",
157
                BeesWaxGenericException::CODE_NON_EXISTING_SEGMENT
158
            );
159
        }
160
161
        $payloadData = [
162
            'segment_id' => (int)$segment->getId(),
163
            'segment_name' => $segment->getName(),
164
            'alternative_id' => $segment->getAlternativeId(),
165
            'advertiser_id' => $segment->getAdvertiserId(),
166
            'segment_description' => $segment->getDescription(),
167
            'cpm_cost' => $segment->getCpmCost(),
168
            'aggregate_excludes' => $segment->isAggregateExcludes(),
169
        ];
170
171
        $payload = json_encode($payloadData);
172
173
        $request = $this
174
            ->session
175
            ->getRequestBuilder()
176
            ->build(static::API_CRUD_PATH, [], BeesWaxRequest::METHOD_PUT, $payload);
177
178
        $response = $request->doRequest();
179
        $this->manageSuccess($response, 'Error updating segment: %s');
180
181
        return $segment;
182
    }
183
184
    /**
185
     * @param BeesWaxSegment           $segment
186
     * @param BeesWaxSegmentUserData[] $userData
187
     * @param string                   $segmentKeyType BeesWaxSegment::SEGMENT_KEY_TYPE_*
188
     * @param string                   $userIdType BeesWaxSegmentUserData::USER_ID_TYPE_*
189
     * @param string                   $operationType BeesWaxSegmentManager::USERS_UPLOAD_OPERATION_TYPE_*
190
     * @param string                   $continent BeesWaxSegmentManager::USERS_UPLOAD_CONTINENT_*
191
     *
192
     * @return int
193
     *
194
     * @throws BeesWaxGenericException
195
     *
196
     * @see BeesWaxSegment::SEGMENT_KEY_TYPE_*
197
     * @see BeesWaxSegmentUserData::USER_ID_TYPE_*
198
     * @see BeesWaxSegmentManager::USERS_UPLOAD_OPERATION_TYPE_*
199
     * @see BeesWaxSegmentManager::USERS_UPLOAD_CONTINENT_*
200
     */
201
    public function usersUpload(
202
        BeesWaxSegment $segment,
203
        array $userData,
204
        string $segmentKeyType = BeesWaxSegment::SEGMENT_KEY_TYPE_DEFAULT,
205
        string $userIdType = BeesWaxSegmentUserData::USER_ID_TYPE_BEESWAX_COOKIE,
206
        string $operationType = BeesWaxSegmentManager::USERS_UPLOAD_OPERATION_TYPE_ADD_SEGMENTS,
207
        string $continent = BeesWaxSegmentManager::USERS_UPLOAD_CONTINENT_NAM
208
    ): int {
209
        if ($segment->getId() === null) {
210
            throw new BeesWaxGenericException(
211
                "Can't add users to a non-existing segment!",
212
                BeesWaxGenericException::CODE_NON_EXISTING_SEGMENT
213
            );
214
        }
215
216
        $fh = tmpfile();
217
        if ($fh === false) {
218
            throw new BeesWaxGenericException(
219
                'Error attempting to create the temporary file',
220
                BeesWaxGenericException::CODE_CANT_CREATE_TEMP_FILE
221
            );
222
        }
223
224
        $tmpFilePath = realpath(stream_get_meta_data($fh)['uri']);
225
        $rows = 0;
226
        $exceptionToThrow = null;
227
        $fileId = 0;
228
229
        $buzzKey = $this->session->getBuzzKey();
230
231
        try {
232
            foreach ($userData as $datum) {
233
                $segments = $datum->getSegments();
234
                if ($segmentKeyType === BeesWaxSegment::SEGMENT_KEY_TYPE_DEFAULT) {
235
                    $segments = \array_map(function (string $segment) use ($buzzKey) {
236
                        return sprintf('%s-%s', $buzzKey, $segment);
237
                    }, $segments);
238
                }
239
240
                $rowData = array_merge([$datum->getUserId()], $segments);
241
                fputcsv($fh, $rowData, '|');
242
                $rows++;
243
            }
244
245
            $fileSize = filesize($tmpFilePath);
246
            if ($fileSize === false || $fileSize === 0) {
247
                fclose($fh);
248
249
                if ($rows > 0) {
250
                    throw new BeesWaxGenericException(
251
                        'An error occurred creating the file to upload',
252
                        BeesWaxGenericException::CODE_ERROR_UPLOADING_SEGMENTS_USERS
253
                    );
254
                }
255
256
                return $fileId;
257
            }
258
259
            $fileId = $this->sendUsersUploadMetadata(
260
                $tmpFilePath,
261
                $segmentKeyType,
262
                $continent,
263
                $userIdType,
264
                $operationType
265
            );
266
267
            $this->uploadSegmentUsersFile($fileId, $tmpFilePath);
268
        } catch (BeesWaxGenericException $exception) {
269
            $exceptionToThrow = $exception;
270
        }
271
272
        fclose($fh);
273
274
        if ($exceptionToThrow !== null) {
275
            throw $exceptionToThrow;
276
        }
277
278
        return $fileId;
279
    }
280
281
    /**
282
     * @param int    $fileId
283
     *
284
     * @return int BeesWaxSegmentManager::FILE_UPLOAD_STATUS_*
285
     *
286
     * @throws BeesWaxGenericException
287
     * @throws BeesWaxResponseException
288
     */
289
    public function getUploadSegmentUsersFileStatus(int $fileId): int
290
    {
291
        $request = $this->session->getRequestBuilder()->build(
292
            static::API_UPLOAD_METADATA_PATH,
293
            ['segment_upload_id' => $fileId],
294
            BeesWaxRequest::METHOD_GET
295
        );
296
297
        $response = $request->doRequest();
298
        $this->manageSuccess($response, "Error retrieving segment's users file upload status: %s");
299
300
        $data = \json_decode($response->getPayload());
301
302
        $condition = isset($data->payload)
303
            && \is_array($data->payload)
304
            && !empty($data->payload)
305
            && isset($data->payload[0]->upload_status);
306
307
        return $condition ? $data->payload[0]->upload_status : static::FILE_UPLOAD_STATUS_UNEXPECTED_STATUS_CODE;
308
    }
309
310
    public function delete(BeesWaxSegment $segment): bool
0 ignored issues
show
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

310
    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...
311
    {
312
        throw new \RuntimeException('Not implemented yet!');
313
    }
314
315
    /**
316
     * @param BeesWaxResponse $response
317
     * @param string          $errorFormat with one string parameter
318
     *
319
     * @throws BeesWaxResponseException
320
     */
321
    private function manageSuccess(BeesWaxResponse $response, string $errorFormat): void
322
    {
323
        $responseData = json_decode($response->getPayload());
324
        $responseErrors = $responseData->errors ?? [];
325
        $statusCode = $response->getStatusCode();
326
327
        if (
328
            !empty($responseErrors)
329
            || \is_bool($responseData)
330
            || (isset($responseData->success) && !$responseData->success)
331
            || $statusCode !== 200
332
        ) {
333
            $message = \count($responseErrors) ? $responseErrors[0] : ($responseData->message ?? (string) $statusCode);
334
            throw new BeesWaxResponseException(
335
                $response->getCurlHandler(),
336
                sprintf($errorFormat, $message)
337
            );
338
        }
339
    }
340
341
    /**
342
     * @param        $filePath
343
     * @param string $segmentKeyType
344
     * @param string $continent
345
     * @param string $userIdType
346
     * @param string $operationType
347
     *
348
     * @return int
349
     *
350
     * @throws BeesWaxGenericException
351
     * @throws BeesWaxResponseException
352
     */
353
    private function sendUsersUploadMetadata(
354
        $filePath,
355
        string $segmentKeyType,
356
        string $continent,
357
        string $userIdType,
358
        string $operationType
359
    ): int {
360
        $fileSize = filesize($filePath);
361
362
        if ($fileSize === false) {
363
            throw new BeesWaxGenericException(
364
                'Impossible to determine the size of the file to upload',
365
                BeesWaxGenericException::CODE_ERROR_UPLOADING_SEGMENTS_USERS
366
            );
367
        }
368
        $payload = \json_encode([
369
            'file_name' => basename($filePath),
370
            'size_in_bytes' => $fileSize,
371
            'file_format' => 'DELIMITED',
372
            'segment_key_type' => $segmentKeyType,
373
            'continent' => $continent,
374
            'user_id_type' => $userIdType,
375
            'operation_type' => $operationType,
376
        ]);
377
378
        $request = $this
379
            ->session
380
            ->getRequestBuilder()
381
            ->build(static::API_UPLOAD_METADATA_PATH, [], BeesWaxRequest::METHOD_POST, $payload);
382
383
        $response = $request->doRequest();
384
        $this->manageSuccess($response, "Error sending segment's users meta data: %s");
385
386
        $data = \json_decode($response->getPayload());
387
388
        return $data->payload->id;
389
    }
390
391
    /**
392
     * @param int    $fileId
393
     * @param string $filePath
394
     *
395
     * @throws BeesWaxGenericException
396
     * @throws BeesWaxResponseException
397
     */
398
    private function uploadSegmentUsersFile(int $fileId, string $filePath): void
399
    {
400
        $cFile = new \CURLFile($filePath, 'text/csv', 'segment_file');
401
        $cFile->setPostFilename(basename($filePath));
402
        $payload = ['segment_file' => $cFile];
403
404
        $request = $this->session->getRequestBuilder()->build(
405
            sprintf(static::API_UPLOAD_FILE_PATH, $fileId),
406
            [],
407
            BeesWaxRequest::METHOD_POST,
408
            $payload
409
        );
410
411
        $response = $request->doRequest();
412
        $this->manageSuccess($response, "Error uploading segment's users CSV file: %s");
413
    }
414
}
415