Files
phabricator/src/applications/diffusion/query/DiffusionFileFutureQuery.php
epriestley 771579496f Make logic for streaming VCS stuff directly to Files more reusable
Summary:
Ref T11524. Ref T10423. Earlier, I converted `diffusion.filecontentquery` to put the actual file content in Files, then return a PHID for the file, instead of trying to send the content over Conduit.

In T11524, we have a similar set of problems with diffs that contain non-UTF8 data (and, in T10423, diffs that are simply enormous).

I want to provide an API method to do the same sort of thing with diff output (like from `git diff`), so we call the method, it shoves the data in Files, and then we go pull it out of Files.

To support this, take the "shove the output of a Future into Files" logic and put it in a new base `FileFuture` query. This will let me make `RawDiffQuery` share the logic more easily.

Test Plan: Browsed Diffusion, ran `diffusion.filecontentquery` to fetch file content.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10423, T11524

Differential Revision: https://secure.phabricator.com/D16458
2016-08-27 09:10:20 -07:00

154 lines
3.5 KiB
PHP

<?php
abstract class DiffusionFileFutureQuery
extends DiffusionQuery {
private $timeout;
private $byteLimit;
private $didHitByteLimit = false;
private $didHitTimeLimit = false;
public static function getConduitParameters() {
return array(
'timeout' => 'optional int',
'byteLimit' => 'optional int',
);
}
public function setTimeout($timeout) {
$this->timeout = $timeout;
return $this;
}
public function getTimeout() {
return $this->timeout;
}
public function setByteLimit($byte_limit) {
$this->byteLimit = $byte_limit;
return $this;
}
public function getByteLimit() {
return $this->byteLimit;
}
final public function getExceededByteLimit() {
return $this->didHitByteLimit;
}
final public function getExceededTimeLimit() {
return $this->didHitTimeLimit;
}
abstract protected function getFileContentFuture();
final public function respondToConduitRequest(ConduitAPIRequest $request) {
$drequest = $this->getRequest();
$timeout = $request->getValue('timeout');
if ($timeout) {
$this->setTimeout($timeout);
}
$byte_limit = $request->getValue('byteLimit');
if ($byte_limit) {
$this->setByteLimit($byte_limit);
}
$file = $this->execute();
$too_slow = (bool)$this->getExceededTimeLimit();
$too_huge = (bool)$this->getExceededByteLimit();
$file_phid = null;
if (!$too_slow && !$too_huge) {
$repository = $drequest->getRepository();
$repository_phid = $repository->getPHID();
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file->attachToObject($repository_phid);
unset($unguarded);
$file_phid = $file->getPHID();
}
return array(
'tooSlow' => $too_slow,
'tooHuge' => $too_huge,
'filePHID' => $file_phid,
);
}
final public function executeInline() {
$future = $this->getFileContentFuture();
list($stdout) = $future->resolvex();
return $future;
}
final protected function executeQuery() {
$future = $this->newConfiguredFileContentFuture();
$drequest = $this->getRequest();
$name = basename($drequest->getPath());
$ttl = PhabricatorTime::getNow() + phutil_units('48 hours in seconds');
try {
$threshold = PhabricatorFileStorageEngine::getChunkThreshold();
$future->setReadBufferSize($threshold);
$source = id(new PhabricatorExecFutureFileUploadSource())
->setName($name)
->setTTL($ttl)
->setViewPolicy(PhabricatorPolicies::POLICY_NOONE)
->setExecFuture($future);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file = $source->uploadFile();
unset($unguarded);
} catch (CommandException $ex) {
if (!$future->getWasKilledByTimeout()) {
throw $ex;
}
$this->didHitTimeLimit = true;
$file = null;
}
$byte_limit = $this->getByteLimit();
if ($byte_limit && ($file->getByteSize() > $byte_limit)) {
$this->didHitByteLimit = true;
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
id(new PhabricatorDestructionEngine())
->destroyObject($file);
unset($unguarded);
$file = null;
}
return $file;
}
private function newConfiguredFileContentFuture() {
$future = $this->getFileContentFuture();
if ($this->getTimeout()) {
$future->setTimeout($this->getTimeout());
}
$byte_limit = $this->getByteLimit();
if ($byte_limit) {
$future->setStdoutSizeLimit($byte_limit + 1);
}
return $future;
}
}