Skip to content

Commit 6cd747f

Browse files
author
epriestley
committed
Kinda start bridging data in from GitHub via Nuance
Summary: Ref T10538. Very sloppy, but starting to sort of work. This sort of gets a piece of framework into a reasonable spot, next couple of diffs are going to be "extract comment text" and "show stuff in the UI" sorts of things. Test Plan: {F1186726} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10538 Differential Revision: https://secure.phabricator.com/D15511
1 parent 4a65895 commit 6cd747f

File tree

4 files changed

+196
-26
lines changed

4 files changed

+196
-26
lines changed

src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubIssue.php

+13-3
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,6 @@ public function pullRefs(array $refs) {
7676
$ref->setAttribute('name', $body['title']);
7777

7878
$obj = $ref->getExternalObject();
79-
if ($obj->getID()) {
80-
continue;
81-
}
8279

8380
$this->fillObjectFromData($obj, $result);
8481

@@ -92,6 +89,19 @@ public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) {
9289
$body = $result->getBody();
9390
$uri = $body['html_url'];
9491
$obj->setObjectURI($uri);
92+
93+
$title = idx($body, 'title');
94+
$description = idx($body, 'body');
95+
96+
$created = idx($body, 'created_at');
97+
$created = strtotime($created);
98+
99+
$state = idx($body, 'state');
100+
101+
$obj->setProperty('task.title', $title);
102+
$obj->setProperty('task.description', $description);
103+
$obj->setProperty('task.created', $created);
104+
$obj->setProperty('task.state', $state);
95105
}
96106

97107
}

src/applications/nuance/github/NuanceGitHubRawEvent.php

+4
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ public function getID() {
9090
return null;
9191
}
9292

93+
public function getComment() {
94+
return 'TODO: Actually extract comment text.';
95+
}
96+
9397
public function getURI() {
9498
$raw = $this->raw;
9599

src/applications/nuance/item/NuanceGitHubEventItemType.php

+176-22
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ final class NuanceGitHubEventItemType
55

66
const ITEMTYPE = 'github.event';
77

8+
private $externalObject;
9+
810
public function getItemTypeDisplayName() {
911
return pht('GitHub Event');
1012
}
@@ -79,29 +81,13 @@ protected function updateItemFromSource(NuanceItem $item) {
7981

8082
// TODO: Link up the requestor, etc.
8183

82-
$source = $item->getSource();
83-
$token = $source->getSourceProperty('github.token');
84-
$token = new PhutilOpaqueEnvelope($token);
84+
$is_dirty = false;
8585

86-
$ref = $this->getDoorkeeperRef($item);
87-
if ($ref) {
88-
$ref = id(new DoorkeeperImportEngine())
89-
->setViewer($viewer)
90-
->setRefs(array($ref))
91-
->setThrowOnMissingLink(true)
92-
->setContextProperty('github.token', $token)
93-
->executeOne();
94-
95-
if ($ref->getSyncFailed()) {
96-
$xobj = null;
97-
} else {
98-
$xobj = $ref->getExternalObject();
99-
}
86+
$xobj = $this->reloadExternalObject($item);
10087

101-
if ($xobj) {
102-
$item->setItemProperty('doorkeeper.xobj.phid', $xobj->getPHID());
103-
$is_dirty = true;
104-
}
88+
if ($xobj) {
89+
$item->setItemProperty('doorkeeper.xobj.phid', $xobj->getPHID());
90+
$is_dirty = true;
10591
}
10692

10793
if ($item->getStatus() == NuanceItem::STATUS_IMPORTING) {
@@ -137,6 +123,56 @@ private function getDoorkeeperRef(NuanceItem $item) {
137123
->setObjectID($full_ref);
138124
}
139125

126+
private function reloadExternalObject(NuanceItem $item, $local = false) {
127+
$ref = $this->getDoorkeeperRef($item);
128+
if (!$ref) {
129+
return null;
130+
}
131+
132+
$source = $item->getSource();
133+
$token = $source->getSourceProperty('github.token');
134+
$token = new PhutilOpaqueEnvelope($token);
135+
136+
$viewer = $this->getViewer();
137+
138+
$ref = id(new DoorkeeperImportEngine())
139+
->setViewer($viewer)
140+
->setRefs(array($ref))
141+
->setThrowOnMissingLink(true)
142+
->setContextProperty('github.token', $token)
143+
->needLocalOnly($local)
144+
->executeOne();
145+
146+
if ($ref->getSyncFailed()) {
147+
$xobj = null;
148+
} else {
149+
$xobj = $ref->getExternalObject();
150+
}
151+
152+
if ($xobj) {
153+
$this->externalObject = $xobj;
154+
}
155+
156+
return $xobj;
157+
}
158+
159+
private function getExternalObject(NuanceItem $item) {
160+
if ($this->externalObject === null) {
161+
$xobj = $this->reloadExternalObject($item, $local = true);
162+
if ($xobj) {
163+
$this->externalObject = $xobj;
164+
} else {
165+
$this->externalObject = false;
166+
}
167+
}
168+
169+
if ($this->externalObject) {
170+
return $this->externalObject;
171+
}
172+
173+
return null;
174+
}
175+
140176
private function newRawEvent(NuanceItem $item) {
141177
$type = $item->getItemProperty('api.type');
142178
$raw = $item->getItemProperty('api.raw', array());
@@ -147,6 +183,15 @@ private function newRawEvent(NuanceItem $item) {
147183
public function getItemActions(NuanceItem $item) {
148184
$actions = array();
149185

186+
$xobj = $this->getExternalObject($item);
187+
if ($xobj) {
188+
$actions[] = $this->newItemAction($item, 'reload')
189+
->setName(pht('Reload from GitHub'))
190+
->setIcon('fa-refresh')
191+
->setWorkflow(true)
192+
->setRenderAsForm(true);
193+
}
194+
150195
$actions[] = $this->newItemAction($item, 'sync')
151196
->setName(pht('Import to Maniphest'))
152197
->setIcon('fa-anchor')
@@ -189,6 +234,7 @@ protected function handleAction(NuanceItem $item, $action) {
189234
->appendForm($form)
190235
->addCancelButton($item->getURI(), pht('Done'));
191236
case 'sync':
237+
case 'reload':
192238
$item->issueCommand($viewer->getPHID(), $action);
193239
return id(new AphrontRedirectResponse())->setURI($item->getURI());
194240
}
@@ -238,9 +284,117 @@ private function newGitHubEventItemPropertyBox($item) {
238284
->appendChild($property_list);
239285
}
240286

241-
protected function handleCommand(NuanceItem $item, $action) {
287+
protected function handleCommand(
288+
NuanceItem $item,
289+
NuanceItemCommand $command) {
290+
291+
$action = $command->getCommand();
292+
switch ($action) {
293+
case 'sync':
294+
return $this->syncItem($item, $command);
295+
case 'reload':
296+
$this->reloadExternalObject($item);
297+
return true;
298+
}
299+
242300
return null;
243301
}
244302

303+
private function syncItem(
304+
NuanceItem $item,
305+
NuanceItemCommand $command) {
306+
307+
$xobj_phid = $item->getItemProperty('doorkeeper.xobj.phid');
308+
if (!$xobj_phid) {
309+
throw new Exception(
310+
pht(
311+
'Unable to sync: no external object PHID.'));
312+
}
313+
314+
// TODO: Write some kind of marker to prevent double-synchronization.
315+
316+
$viewer = $this->getViewer();
317+
318+
$xobj = id(new DoorkeeperExternalObjectQuery())
319+
->setViewer($viewer)
320+
->withPHIDs(array($xobj_phid))
321+
->executeOne();
322+
if (!$xobj) {
323+
throw new Exception(
324+
pht(
325+
'Unable to sync: failed to load object "%s".',
326+
$xobj_phid));
327+
}
328+
329+
$nuance_phid = id(new PhabricatorNuanceApplication())->getPHID();
330+
331+
$xactions = array();
332+
333+
$task = id(new ManiphestTaskQuery())
334+
->setViewer($viewer)
335+
->withBridgedObjectPHIDs(array($xobj_phid))
336+
->executeOne();
337+
if (!$task) {
338+
$task = ManiphestTask::initializeNewTask($viewer)
339+
->setAuthorPHID($nuance_phid)
340+
->setBridgedObjectPHID($xobj_phid);
341+
342+
$title = $xobj->getProperty('task.title');
343+
if (!strlen($title)) {
344+
$title = pht('Nuance Item %d Task', $item->getID());
345+
}
346+
347+
$description = $xobj->getProperty('task.description');
348+
$created = $xobj->getProperty('task.created');
349+
$state = $xobj->getProperty('task.state');
350+
351+
$xactions[] = id(new ManiphestTransaction())
352+
->setTransactionType(ManiphestTransaction::TYPE_TITLE)
353+
->setNewValue($title)
354+
->setDateCreated($created);
355+
356+
$xactions[] = id(new ManiphestTransaction())
357+
->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION)
358+
->setNewValue($description)
359+
->setDateCreated($created);
360+
361+
$task->setDateCreated($created);
362+
363+
// TODO: Synchronize state.
364+
}
365+
366+
$event = $this->newRawEvent($item);
367+
$comment = $event->getComment();
368+
if (strlen($comment)) {
369+
$xactions[] = id(new ManiphestTransaction())
370+
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
371+
->attachComment(
372+
id(new ManiphestTransactionComment())
373+
->setContent($comment));
374+
}
375+
376+
// TODO: Preserve the item's original source.
377+
$source = PhabricatorContentSource::newForSource(
378+
PhabricatorContentSource::SOURCE_DAEMON,
379+
array());
380+
381+
// TOOD: This should really be the external source.
382+
$acting_phid = $nuance_phid;
383+
384+
$editor = id(new ManiphestTransactionEditor())
385+
->setActor($viewer)
386+
->setActingAsPHID($acting_phid)
387+
->setContentSource($source)
388+
->setContinueOnNoEffect(true)
389+
->setContinueOnMissingFields(true);
390+
391+
$xactions = $editor->applyTransactions($task, $xactions);
392+
393+
return array(
394+
'objectPHID' => $task->getPHID(),
395+
'xactionPHIDs' => mpull($xactions, 'getPHID'),
396+
);
397+
}
398+
245399

246400
}

src/applications/nuance/item/NuanceItemType.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ final public function applyCommand(
131131
->applyTransactions($item, array($xaction));
132132
}
133133

134-
protected function handleCommand(NuanceItem $item, $action) {
134+
protected function handleCommand(
135+
NuanceItem $item,
136+
NuanceItemCommand $command) {
135137
return null;
136138
}
137139

0 commit comments

Comments
 (0)