Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php
12256 views
1
<?php
2
3
final class DoorkeeperBridgeAsana extends DoorkeeperBridge {
4
5
const APPTYPE_ASANA = 'asana';
6
const APPDOMAIN_ASANA = 'asana.com';
7
const OBJTYPE_TASK = 'asana:task';
8
9
public function canPullRef(DoorkeeperObjectRef $ref) {
10
if ($ref->getApplicationType() != self::APPTYPE_ASANA) {
11
return false;
12
}
13
14
if ($ref->getApplicationDomain() != self::APPDOMAIN_ASANA) {
15
return false;
16
}
17
18
$types = array(
19
self::OBJTYPE_TASK => true,
20
);
21
22
return isset($types[$ref->getObjectType()]);
23
}
24
25
public function pullRefs(array $refs) {
26
27
$id_map = mpull($refs, 'getObjectID', 'getObjectKey');
28
$viewer = $this->getViewer();
29
30
$provider = PhabricatorAsanaAuthProvider::getAsanaProvider();
31
if (!$provider) {
32
return;
33
}
34
35
$accounts = id(new PhabricatorExternalAccountQuery())
36
->setViewer($viewer)
37
->withUserPHIDs(array($viewer->getPHID()))
38
->withProviderConfigPHIDs(
39
array(
40
$provider->getProviderConfigPHID(),
41
))
42
->requireCapabilities(
43
array(
44
PhabricatorPolicyCapability::CAN_VIEW,
45
PhabricatorPolicyCapability::CAN_EDIT,
46
))
47
->execute();
48
49
if (!$accounts) {
50
return $this->didFailOnMissingLink();
51
}
52
53
// TODO: If the user has several linked Asana accounts, we just pick the
54
// first one arbitrarily. We might want to try using all of them or do
55
// something with more finesse. There's no UI way to link multiple accounts
56
// right now so this is currently moot.
57
$account = head($accounts);
58
59
$token = $provider->getOAuthAccessToken($account);
60
if (!$token) {
61
return;
62
}
63
64
$template = id(new PhutilAsanaFuture())
65
->setAccessToken($token);
66
67
$timeout = $this->getTimeout();
68
if ($timeout !== null) {
69
$template->setTimeout($timeout);
70
}
71
72
$futures = array();
73
foreach ($id_map as $key => $id) {
74
$futures[$key] = id(clone $template)
75
->setRawAsanaQuery("tasks/{$id}");
76
}
77
78
$results = array();
79
$failed = array();
80
foreach (new FutureIterator($futures) as $key => $future) {
81
try {
82
$results[$key] = $future->resolve();
83
} catch (Exception $ex) {
84
if (($ex instanceof HTTPFutureResponseStatus) &&
85
($ex->getStatusCode() == 404)) {
86
// This indicates that the object has been deleted (or never existed,
87
// or isn't visible to the current user) but it's a successful sync of
88
// an object which isn't visible.
89
} else {
90
// This is something else, so consider it a synchronization failure.
91
phlog($ex);
92
$failed[$key] = $ex;
93
}
94
}
95
}
96
97
foreach ($refs as $ref) {
98
$ref->setAttribute('name', pht('Asana Task %s', $ref->getObjectID()));
99
100
$did_fail = idx($failed, $ref->getObjectKey());
101
if ($did_fail) {
102
$ref->setSyncFailed(true);
103
continue;
104
}
105
106
$result = idx($results, $ref->getObjectKey());
107
if (!$result) {
108
continue;
109
}
110
111
$ref->setIsVisible(true);
112
$ref->setAttribute('asana.data', $result);
113
$ref->setAttribute('fullname', pht('Asana: %s', $result['name']));
114
$ref->setAttribute('title', $result['name']);
115
$ref->setAttribute('description', $result['notes']);
116
117
$obj = $ref->getExternalObject();
118
if ($obj->getID()) {
119
continue;
120
}
121
122
$this->fillObjectFromData($obj, $result);
123
$this->saveExternalObject($ref, $obj);
124
}
125
}
126
127
public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) {
128
$gid = $result['gid'];
129
$uri = urisprintf(
130
'https://app.asana.com/0/%s/%s',
131
$gid,
132
$gid);
133
$obj->setObjectURI($uri);
134
}
135
136
}
137
138