Passed
Push — master ( 993609...05c126 )
by y
01:36
created

Task::removeDependency()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 2
rs 10
c 1
b 0
f 1
1
<?php
2
3
namespace Helix\Asana;
4
5
use Helix\Asana\Base\AbstractEntity;
6
use Helix\Asana\Base\AbstractEntity\CrudTrait;
7
use Helix\Asana\Base\AbstractEntity\WorkspaceTrait;
8
use Helix\Asana\Event\StoryEvent;
9
use Helix\Asana\Event\TaskEvent;
10
use Helix\Asana\Project\Section;
11
use Helix\Asana\Task\Attachment;
12
use Helix\Asana\Task\CustomValues;
13
use Helix\Asana\Task\External;
14
use Helix\Asana\Task\Like;
15
use Helix\Asana\Task\Membership;
16
use Helix\Asana\Task\Story;
17
use Helix\Asana\Webhook\TaskWebhook;
18
19
/**
20
 * A task.
21
 *
22
 * @see https://developers.asana.com/docs/asana-tasks
23
 * @see https://developers.asana.com/docs/task
24
 *
25
 * @method null|User    getAssignee         ()
26
 * @method bool         hasAssignee         ()
27
 * @method $this        setAssignee         (null|User $user)
28
 * @method string       getAssigneeStatus   ()
29
 * @method $this        setAssigneeStatus   (string $status)
30
 * @method bool         isCompleted         ()
31
 * @method $this        setCompleted        (bool $completed)
32
 * @method string       getCompletedAt      ()
33
 * @method string       getCreatedAt        ()
34
 * @method null|CustomValues getCustomFields() Premium only.
35
 * @method bool         hasCustomFields     () Premium only.
36
 * @method string       getDueOn            ()
37
 * @method $this        setDueOn            (string $date)
38
 * @method User[]       getFollowers        ()
39
 * @method bool         hasFollowers        ()
40
 * @method string       getHtmlNotes        ()
41
 * @method $this        setHtmlNotes        (string $notes)
42
 * @method bool         isLiked             ()
43
 * @method $this        setLiked            (bool $liked)
44
 * @method Like[]       getLikes            ()
45
 * @method Membership[] getMemberships      ()
46
 * @method bool         hasMemberships      ()
47
 * @method string       getModifiedAt       ()
48
 * @method string       getName             ()
49
 * @method $this        setName             (string $name)
50
 * @method string       getNotes            ()
51
 * @method $this        setNotes            (string $notes)
52
 * @method int          getNumLikes         ()
53
 * @method int          getNumSubtasks      ()
54
 * @method null|Task    getParent           ()
55
 * @method bool         hasParent           ()
56
 * @method string       getResourceSubtype  ()
57
 * @method $this        setResourceSubtype  (string $type)
58
 * @method string       getStartOn          ()
59
 * @method $this        setStartOn          (string $date)
60
 * @method Tag[]        getTags             ()
61
 * @method bool         hasTags             ()
62
 */
63
class Task extends AbstractEntity {
64
65
    use CrudTrait {
66
        create as private _create;
67
        update as private _update;
68
    }
69
70
    use WorkspaceTrait;
71
72
    const TYPE = 'task';
73
    const TYPE_DEFAULT = 'default_task';
74
    const TYPE_MILESTONE = 'milestone';
75
76
    const ASSIGN_INBOX = 'inbox';
77
    const ASSIGN_LATER = 'later';
78
    const ASSIGN_NEW = 'new';
79
    const ASSIGN_TODAY = 'today';
80
    const ASSIGN_UPCOMING = 'upcoming';
81
82
    protected static $map = [
83
        'assignee' => User::class,
84
        'custom_fields' => CustomValues::class,
85
        'external' => External::class, // not included by expanding "this". always lazy.
86
        'followers' => [User::class],
87
        'likes' => [Like::class],
88
        'memberships' => [Membership::class],
89
        'parent' => self::class,
90
        'tags' => [Tag::class],
91
        'workspace' => Workspace::class
92
    ];
93
94
    protected static $optFields = [
95
        'memberships' => 'memberships.(project|section)'
96
    ];
97
98
    final public function __toString (): string {
99
        return "tasks/{$this->getGid()}";
100
    }
101
102
    /**
103
     * Clears the diffs for `custom_fields` and `external` sub-objects after saving.
104
     */
105
    protected function _clearSubDiffs (): void {
106
        // use isset() to avoid has() fetch.
107
        if (isset($this->data['custom_fields'])) {
108
            $this->getCustomFields()->diff = [];
109
        }
110
        if (isset($this->data['external'])) {
111
            $this->getExternal()->diff = [];
112
        }
113
    }
114
115
    final protected function _getDir (): string {
116
        return 'tasks';
117
    }
118
119
    /**
120
     * Uploads a file attachment.
121
     *
122
     * @depends after-create
123
     * @param string $file
124
     * @return Attachment
125
     */
126
    public function addAttachment (string $file) {
127
        /** @var Attachment $attachment */
128
        $attachment = $this->factory(Attachment::class, ['parent' => $this]);
129
        return $attachment->upload($file);
130
    }
131
132
    /**
133
     * Posts a comment on an existing task and returns it.
134
     *
135
     * @param string $text
136
     * @return Story
137
     */
138
    public function addComment (string $text) {
139
        return $this->newComment()->setText($text)->create();
1 ignored issue
show
Unused Code introduced by
The call to Helix\Asana\Task\Story::setText() has too many arguments starting with $text. ( Ignorable by Annotation )

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

139
        return $this->newComment()->/** @scrutinizer ignore-call */ setText($text)->create();

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...
140
    }
141
142
    /**
143
     * Premium feature.
144
     *
145
     * @depends after-create
146
     * @param Task[] $tasks
147
     * @return $this
148
     */
149
    public function addDependencies (array $tasks) {
150
        $this->api->post("{$this}/addDependencies", ['dependents' => array_column($tasks, 'gid')]);
151
        return $this;
152
    }
153
154
    /**
155
     * Premium feature.
156
     *
157
     * @depends after-create
158
     * @param Task $task
159
     * @return $this
160
     */
161
    public function addDependency (Task $task) {
162
        return $this->addDependencies([$task]);
163
    }
164
165
    /**
166
     * Premium feature.
167
     *
168
     * @depends after-create
169
     * @param Task $task
170
     * @return $this
171
     */
172
    public function addDependent (Task $task) {
173
        return $this->addDependents([$task]);
174
    }
175
176
    /**
177
     * Premium feature.
178
     *
179
     * @depends after-create
180
     * @param Task[] $tasks
181
     * @return $this
182
     */
183
    public function addDependents (array $tasks) {
184
        $this->api->post("{$this}/addDependents", ['dependents' => array_column($tasks, 'gid')]);
185
        return $this;
186
    }
187
188
    /**
189
     * Adds a follower.
190
     *
191
     * @param User $user
192
     * @return $this
193
     */
194
    public function addFollower (User $user) {
195
        return $this->addFollowers([$user]);
196
    }
197
198
    /**
199
     * Adds followers.
200
     *
201
     * @param User[] $users
202
     * @return $this
203
     */
204
    public function addFollowers (array $users) {
205
        if ($this->hasGid()) {
206
            $this->api->post("{$this}/addFollowers", ['followers' => array_column($users, 'gid')]);
207
            $this->_merge('followers', $users);
208
        }
209
        else {
210
            $this->_merge('followers', $users, true);
211
        }
212
        return $this;
213
    }
214
215
    /**
216
     * Adds a tag.
217
     *
218
     * @param Tag $tag
219
     * @return $this
220
     */
221
    public function addTag (Tag $tag) {
222
        if ($this->hasGid()) {
223
            $this->api->post("{$this}/addTag", ['tag' => $tag->getGid()]);
224
            $this->_merge('tags', [$tag]);
225
        }
226
        else {
227
            $this->_merge('tags', [$tag], true);
228
        }
229
        return $this;
230
    }
231
232
    /**
233
     * Adds the task to a project / section.
234
     *
235
     * @param Project|Section $target
236
     * @return $this
237
     */
238
    public function addToProject ($target) {
239
        if ($target instanceof Project) {
240
            $project = $target;
241
            $section = null;
242
        }
243
        else {
244
            $project = $target->getProject();
245
            $section = $target;
246
        }
247
        if ($this->hasGid()) {
248
            $this->api->post("{$this}/addProject", [
249
                'project' => $project->getGid(),
250
                'section' => $section ? $section->getGid() : null
251
            ]);
252
            $this->_merge('memberships', [
253
                $this->factory(Membership::class, [
254
                    'project' => $project,
255
                    'section' => $section
256
                ])
257
            ]);
258
        }
259
        else {
260
            if (!$this->hasWorkspace() and $workspace = $project->getWorkspace()) {
261
                $this->setWorkspace($workspace);
262
            }
263
            $this->_merge('memberships', [
264
                $this->factory(Membership::class)
265
                    ->_set('project', $project)
266
                    ->_set('section', $section)
267
            ], true);
268
        }
269
        return $this;
270
    }
271
272
    /**
273
     * Creates and returns a webhook.
274
     *
275
     * @depends after-create
276
     * @param string $target
277
     * @return TaskWebhook
278
     */
279
    public function addWebhook (string $target) {
280
        /** @var TaskWebhook $webhook */
281
        $webhook = $this->factory(TaskWebhook::class);
282
        return $webhook->create($this, $target);
283
    }
284
285
    /**
286
     * @return $this
287
     */
288
    public function create () {
289
        $this->_create();
290
        $this->_clearSubDiffs();
291
        return $this;
292
    }
293
294
    /**
295
     * Creates and returns job to duplicate the task.
296
     *
297
     * @see https://developers.asana.com/docs/duplicate-a-task
298
     *
299
     * @depends after-create
300
     * @param string $name
301
     * @param string[] $include
302
     * @return Job
303
     */
304
    public function duplicate (string $name, array $include) {
305
        $remote = $this->api->post("{$this}/duplicate", [
306
            'name' => $name,
307
            'include' => array_values($include)
308
        ]);
309
        return $this->factory(Job::class, $remote ?? []);
310
    }
311
312
    /**
313
     * Returns the task's files.
314
     *
315
     * @depends after-create
316
     * @return Attachment[]
317
     */
318
    public function getAttachments () {
319
        return $this->loadAll(Attachment::class, "{$this}/attachments");
320
    }
321
322
    /**
323
     * Returns the task's comments.
324
     *
325
     * @depends after-create
326
     * @return Story[]
327
     */
328
    public function getComments () {
329
        return array_values(array_filter($this->getStories(), function(Story $story) {
330
            return $story->isComment();
331
        }));
332
    }
333
334
    /**
335
     * Premium feature.
336
     *
337
     * @depends after-create
338
     * @return Task[]
339
     */
340
    public function getDependencies () {
341
        return $this->loadAll(self::class, "{$this}/dependencies");
342
    }
343
344
    /**
345
     * Premium feature.
346
     *
347
     * @depends after-create
348
     * @return Task[]
349
     */
350
    public function getDependents () {
351
        return $this->loadAll(self::class, "{$this}/dependents");
352
    }
353
354
    /**
355
     * Returns events since the last sync.
356
     *
357
     * @depends after-create
358
     * @param null|string $token
359
     * @return TaskEvent[]|StoryEvent[]
360
     */
361
    public function getEvents (&$token) {
362
        return $this->api->sync($this, $token);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->api->sync($this, $token) returns an array which contains values of type Helix\Asana\Event\ProjectEvent which are incompatible with the documented value type Helix\Asana\Event\StoryE...x\Asana\Event\TaskEvent.
Loading history...
363
    }
364
365
    /**
366
     * @return External
367
     */
368
    public function getExternal () {
369
        return $this->_get('external') ?? $this->data['external'] = $this->factory(static::$map['external']);
370
    }
371
372
    /**
373
     * @return Project[]
374
     */
375
    public function getProjects () {
376
        return array_map(function(Membership $membership) {
377
            return $membership->getProject();
378
        }, $this->getMemberships());
379
    }
380
381
    /**
382
     * Returns the task's activity.
383
     *
384
     * @depends after-create
385
     * @return Story[]
386
     */
387
    public function getStories () {
388
        return $this->loadAll(Story::class, "{$this}/stories");
389
    }
390
391
    /**
392
     * Returns the task's subtasks.
393
     *
394
     * @depends after-create
395
     * @return Task[]
396
     */
397
    public function getSubTasks () {
398
        return $this->loadAll(self::class, "{$this}/subtasks");
399
    }
400
401
    /**
402
     * Returns the task's URL.
403
     *
404
     * @depends after-create
405
     * @return string
406
     */
407
    public function getUrl (): string {
408
        return "https://app.asana.com/0/0/{$this->getGid()}";
409
    }
410
411
    /**
412
     * Returns the task's webhooks.
413
     *
414
     * @depends after-create
415
     * @return TaskWebhook[]
416
     */
417
    public function getWebhooks () {
418
        return $this->loadAll(TaskWebhook::class, 'webhooks', [
419
            'workspace' => $this->getWorkspace()->getGid(),
420
            'resource' => $this->getGid()
421
        ]);
422
    }
423
424
    /**
425
     * @return bool
426
     */
427
    public function isRenderedAsSeparator (): bool {
428
        return $this->_is('is_rendered_as_separator');
429
    }
430
431
    /**
432
     * Instantiates and returns a new comment.
433
     *
434
     * @depends after-create
435
     * @return Story
436
     */
437
    public function newComment () {
438
        /** @var Story $comment */
439
        $comment = $this->factory(Story::class, [
440
            'resource_subtype' => Story::TYPE_COMMENT_ADDED
441
        ]);
442
        return $comment->_set('task', $this);
443
    }
444
445
    /**
446
     * Instantiates and returns a new subtask.
447
     *
448
     * @depends after-create
449
     * @return Task
450
     */
451
    public function newSubTask () {
452
        /** @var Task $sub */
453
        $sub = $this->factory(self::class);
454
        return $sub->setParent($this);
455
    }
456
457
    /**
458
     * Premium feature.
459
     *
460
     * @depends after-create
461
     * @param Task[] $tasks
462
     * @return $this
463
     */
464
    public function removeDependencies (array $tasks) {
465
        $this->api->post("{$this}/removeDependencies", ['dependencies' => array_column($tasks, 'gid')]);
466
        return $this;
467
    }
468
469
    /**
470
     * Premium feature.
471
     *
472
     * @depends after-create
473
     * @param Task $task
474
     * @return $this
475
     */
476
    public function removeDependency (Task $task) {
477
        return $this->removeDependencies([$task]);
478
    }
479
480
    /**
481
     * Premium feature.
482
     *
483
     * @depends after-create
484
     * @param Task $task
485
     * @return $this
486
     */
487
    public function removeDependent (Task $task) {
488
        return $this->removeDependents([$task]);
489
    }
490
491
    /**
492
     * Premium feature.
493
     *
494
     * @depends after-create
495
     * @param Task[] $tasks
496
     * @return $this
497
     */
498
    public function removeDependents (array $tasks) {
499
        $this->api->post("{$this}/removeDependents", ['dependents' => array_column($tasks, 'gid')]);
500
        return $this;
501
    }
502
503
    /**
504
     * Removes a follower.
505
     *
506
     * @param User $user
507
     * @return $this
508
     */
509
    public function removeFollower (User $user) {
510
        return $this->removeFollowers([$user]);
511
    }
512
513
    /**
514
     * Removes followers.
515
     *
516
     * @param User[] $users
517
     * @return $this
518
     */
519
    public function removeFollowers (array $users) {
520
        if ($this->hasGid()) {
521
            $this->api->post("{$this}/removeFollowers", ['followers' => array_column($users, 'gid')]);
522
        }
523
        $this->_remove('followers', $users);
524
        return $this;
525
    }
526
527
    /**
528
     * Removes the task from a project.
529
     *
530
     * @param Project $project
531
     * @return $this
532
     */
533
    public function removeFromProject (Project $project) {
534
        $gid = $project->getGid();
535
        if ($this->hasGid()) {
536
            $this->api->post("{$this}/removeProject", ['project' => $gid]);
537
        }
538
        if (isset($this->data['memberships'])) {
539
            $this->data['memberships'] = array_values(array_filter($this->data['memberships'], function(Membership $membership) use ($gid) {
540
                return $membership->getProject()->getGid() !== $gid;
541
            }));
542
        }
543
        return $this;
544
    }
545
546
    /**
547
     * Removes a tag.
548
     *
549
     * @param Tag $tag
550
     * @return $this
551
     */
552
    public function removeTag (Tag $tag) {
553
        if ($this->hasGid()) {
554
            $this->api->post("{$this}/removeTag", ['tag' => $tag->getGid()]);
555
        }
556
        $this->_remove('tags', [$tag]);
557
        return $this;
558
    }
559
560
    /**
561
     * Makes the task a subtask of another.
562
     *
563
     * @param null|Task $parent
564
     * @return $this
565
     */
566
    public function setParent (?self $parent) {
567
        if ($this->hasGid()) {
568
            $this->api->post("{$this}/setParent", ['parent' => $parent ? $parent->getGid() : null]);
569
            $this->data['parent'] = $parent;
570
        }
571
        else {
572
            $this->_set('parent', $parent);
573
        }
574
        return $this;
575
    }
576
577
    /**
578
     * @param bool $flag
579
     * @return $this
580
     */
581
    public function setRenderedAsSeparator (bool $flag) {
582
        return $this->_set('is_rendered_as_separator', $flag);
583
    }
584
585
    /**
586
     * @return $this
587
     */
588
    public function update () {
589
        $this->_update();
590
        $this->_clearSubDiffs();
591
        return $this;
592
    }
593
594
}