diff --git a/src/applications/almanac/conduit/AlmanacQueryDevicesConduitAPIMethod.php b/src/applications/almanac/conduit/AlmanacQueryDevicesConduitAPIMethod.php index 8225ddd1b0..b0a593a381 100644 --- a/src/applications/almanac/conduit/AlmanacQueryDevicesConduitAPIMethod.php +++ b/src/applications/almanac/conduit/AlmanacQueryDevicesConduitAPIMethod.php @@ -1,67 +1,63 @@ 'optional list', 'phids' => 'optional list', 'names' => 'optional list', ) + self::getPagerParamTypes(); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $query = id(new AlmanacDeviceQuery()) ->setViewer($viewer); $ids = $request->getValue('ids'); if ($ids !== null) { $query->withIDs($ids); } $phids = $request->getValue('phids'); if ($phids !== null) { $query->withPHIDs($phids); } $names = $request->getValue('names'); if ($names !== null) { $query->withNames($names); } $pager = $this->newPager($request); $devices = $query->executeWithCursorPager($pager); $data = array(); foreach ($devices as $device) { $data[] = $this->getDeviceDictionary($device); } $results = array( 'data' => $data, ); return $this->addPagerResults($results, $pager); } } diff --git a/src/applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php b/src/applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php index 4cd019fed3..81e59d072f 100644 --- a/src/applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php +++ b/src/applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php @@ -1,90 +1,86 @@ 'optional list', 'phids' => 'optional list', 'names' => 'optional list', 'devicePHIDs' => 'optional list', 'serviceClasses' => 'optional list', ) + self::getPagerParamTypes(); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $query = id(new AlmanacServiceQuery()) ->setViewer($viewer) ->needBindings(true); $ids = $request->getValue('ids'); if ($ids !== null) { $query->withIDs($ids); } $phids = $request->getValue('phids'); if ($phids !== null) { $query->withPHIDs($phids); } $names = $request->getValue('names'); if ($names !== null) { $query->withNames($names); } $classes = $request->getValue('serviceClasses'); if ($classes !== null) { $query->withServiceClasses($classes); } $device_phids = $request->getValue('devicePHIDs'); if ($device_phids !== null) { $query->withDevicePHIDs($device_phids); } $pager = $this->newPager($request); $services = $query->executeWithCursorPager($pager); $data = array(); foreach ($services as $service) { $phid = $service->getPHID(); $service_bindings = $service->getBindings(); $service_bindings = array_values($service_bindings); foreach ($service_bindings as $key => $service_binding) { $service_bindings[$key] = $this->getBindingDictionary($service_binding); } $data[] = $this->getServiceDictionary($service) + array( 'bindings' => $service_bindings, ); } $results = array( 'data' => $data, ); return $this->addPagerResults($results, $pager); } } diff --git a/src/applications/arcanist/conduit/ArcanistProjectInfoConduitAPIMethod.php b/src/applications/arcanist/conduit/ArcanistProjectInfoConduitAPIMethod.php index aad4650ce6..677c4985aa 100644 --- a/src/applications/arcanist/conduit/ArcanistProjectInfoConduitAPIMethod.php +++ b/src/applications/arcanist/conduit/ArcanistProjectInfoConduitAPIMethod.php @@ -1,70 +1,70 @@ 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-ARCANIST-PROJECT' => 'No such project exists.', ); } protected function execute(ConduitAPIRequest $request) { $name = $request->getValue('name'); $project = id(new PhabricatorRepositoryArcanistProject())->loadOneWhere( 'name = %s', $name); if (!$project) { throw new ConduitException('ERR-BAD-ARCANIST-PROJECT'); } $repository = null; if ($project->getRepositoryID()) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->withIDs(array($project->getRepositoryID())) ->executeOne(); } $repository_phid = null; $tracked = false; $encoding = null; $dictionary = array(); if ($repository) { $repository_phid = $repository->getPHID(); $tracked = $repository->isTracked(); $encoding = $repository->getDetail('encoding'); $dictionary = $repository->toDictionary(); } return array( 'name' => $project->getName(), 'phid' => $project->getPHID(), 'repositoryPHID' => $repository_phid, 'tracked' => $tracked, 'encoding' => $encoding, 'repository' => $dictionary, ); } } diff --git a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php index 9b24b6866b..d5a02810f8 100644 --- a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php +++ b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php @@ -1,96 +1,91 @@ formatStringConstants($statuses); return array( 'auditorPHIDs' => 'optional list', 'commitPHIDs' => 'optional list', 'status' => ('optional '.$status_const. ' (default = "audit-status-any")'), 'offset' => 'optional int', 'limit' => 'optional int (default = 100)', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $query = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()); $auditor_phids = $request->getValue('auditorPHIDs', array()); if ($auditor_phids) { $query->withAuditorPHIDs($auditor_phids); } $commit_phids = $request->getValue('commitPHIDs', array()); if ($commit_phids) { $query->withPHIDs($commit_phids); } $status = $request->getValue( 'status', DiffusionCommitQuery::AUDIT_STATUS_ANY); $query->withAuditStatus($status); // NOTE: These affect the number of commits identified, which is sort of // reasonable but means the method may return an arbitrary number of // actual audit requests. $query->setOffset($request->getValue('offset', 0)); $query->setLimit($request->getValue('limit', 100)); $commits = $query->execute(); $auditor_map = array_fuse($auditor_phids); $results = array(); foreach ($commits as $commit) { $requests = $commit->getAudits(); foreach ($requests as $request) { // If this audit isn't triggered for one of the requested PHIDs, // skip it. if ($auditor_map && empty($auditor_map[$request->getAuditorPHID()])) { continue; } $results[] = array( 'id' => $request->getID(), 'commitPHID' => $request->getCommitPHID(), 'auditorPHID' => $request->getAuditorPHID(), 'reasons' => $request->getAuditReasons(), 'status' => $request->getAuditStatus(), ); } } return $results; } } diff --git a/src/applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php b/src/applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php index a2e3d55d21..e7d036aee4 100644 --- a/src/applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php +++ b/src/applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php @@ -1,77 +1,73 @@ 'optional list', 'objectPHIDs' => 'optional list', 'keys' => 'optional list', ) + self::getPagerParamTypes(); } - public function defineReturnType() { + protected function defineReturnType() { return 'result-set'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $query = id(new PhabricatorAuthSSHKeyQuery()) ->setViewer($viewer); $ids = $request->getValue('ids'); if ($ids !== null) { $query->withIDs($ids); } $object_phids = $request->getValue('objectPHIDs'); if ($object_phids !== null) { $query->withObjectPHIDs($object_phids); } $keys = $request->getValue('keys'); if ($keys !== null) { $key_objects = array(); foreach ($keys as $key) { $key_objects[] = PhabricatorAuthSSHPublicKey::newFromRawKey($key); } $query->withKeys($key_objects); } $pager = $this->newPager($request); $public_keys = $query->executeWithCursorPager($pager); $data = array(); foreach ($public_keys as $public_key) { $data[] = array( 'id' => $public_key->getID(), 'name' => $public_key->getName(), 'objectPHID' => $public_key->getObjectPHID(), 'isTrusted' => (bool)$public_key->getIsTrusted(), 'key' => $public_key->getEntireKey(), ); } $results = array( 'data' => $data, ); return $this->addPagerResults($results, $pager); } } diff --git a/src/applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php b/src/applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php index 2259d6c4db..8270ced921 100644 --- a/src/applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php +++ b/src/applications/chatlog/conduit/ChatLogQueryConduitAPIMethod.php @@ -1,63 +1,59 @@ 'optional list', 'limit' => 'optional int (default = 100)', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $query = new PhabricatorChatLogQuery(); $channel_ids = $request->getValue('channelIDs'); if ($channel_ids) { $query->withChannelIDs($channel_ids); } $limit = $request->getValue('limit'); if (!$limit) { $limit = 100; } $query->setLimit($limit); $logs = $query->execute(); $results = array(); foreach ($logs as $log) { $results[] = array( 'channelID' => $log->getChannelID(), 'epoch' => $log->getEpoch(), 'author' => $log->getAuthor(), 'type' => $log->getType(), 'message' => $log->getMessage(), 'loggedByPHID' => $log->getLoggedByPHID(), ); } return $results; } } diff --git a/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php b/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php index 4c7a8d5cff..ca94b91dcc 100644 --- a/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php +++ b/src/applications/chatlog/conduit/ChatLogRecordConduitAPIMethod.php @@ -1,76 +1,72 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $logs = $request->getValue('logs'); if (!is_array($logs)) { $logs = array(); } $template = new PhabricatorChatLogEvent(); $template->setLoggedByPHID($request->getUser()->getPHID()); $objs = array(); foreach ($logs as $log) { $channel_name = idx($log, 'channel'); $service_name = idx($log, 'serviceName'); $service_type = idx($log, 'serviceType'); $channel = id(new PhabricatorChatLogChannel())->loadOneWhere( 'channelName = %s AND serviceName = %s AND serviceType = %s', $channel_name, $service_name, $service_type); if (!$channel) { $channel = id(new PhabricatorChatLogChannel()) ->setChannelName($channel_name) ->setserviceName($service_name) ->setServiceType($service_type) ->setViewPolicy(PhabricatorPolicies::POLICY_USER) ->setEditPolicy(PhabricatorPolicies::POLICY_USER) ->save(); } $obj = clone $template; $obj->setChannelID($channel->getID()); $obj->setType(idx($log, 'type')); $obj->setAuthor(idx($log, 'author')); $obj->setEpoch(idx($log, 'epoch')); $obj->setMessage(idx($log, 'message')); $obj->save(); $objs[] = $obj; } return array_values(mpull($objs, 'getID')); } } diff --git a/src/applications/conduit/call/ConduitCall.php b/src/applications/conduit/call/ConduitCall.php index 9215e4bc15..aa55d1b895 100644 --- a/src/applications/conduit/call/ConduitCall.php +++ b/src/applications/conduit/call/ConduitCall.php @@ -1,154 +1,154 @@ 'value')); * $call->setUser($user); * $result = $call->execute(); * */ final class ConduitCall { private $method; private $request; private $user; public function __construct($method, array $params) { - $this->method = $method; - $this->handler = $this->buildMethodHandler($method); + $this->method = $method; + $this->handler = $this->buildMethodHandler($method); - $param_types = $this->handler->defineParamTypes(); + $param_types = $this->handler->getParamTypes(); foreach ($param_types as $key => $spec) { if (ConduitAPIMethod::getParameterMetadataKey($key) !== null) { throw new ConduitException( pht( 'API Method "%s" defines a disallowed parameter, "%s". This '. 'parameter name is reserved.', $method, $key)); } } $invalid_params = array_diff_key($params, $param_types); if ($invalid_params) { throw new ConduitException( pht( 'API Method "%s" does not define these parameters: %s.', $method, "'".implode("', '", array_keys($invalid_params))."'")); } $this->request = new ConduitAPIRequest($params); } public function getAPIRequest() { return $this->request; } public function setUser(PhabricatorUser $user) { $this->user = $user; return $this; } public function getUser() { return $this->user; } public function shouldRequireAuthentication() { return $this->handler->shouldRequireAuthentication(); } public function shouldAllowUnguardedWrites() { return $this->handler->shouldAllowUnguardedWrites(); } public function getRequiredScope() { return $this->handler->getRequiredScope(); } public function getErrorDescription($code) { return $this->handler->getErrorDescription($code); } public function execute() { $profiler = PhutilServiceProfiler::getInstance(); $call_id = $profiler->beginServiceCall( array( 'type' => 'conduit', 'method' => $this->method, )); try { $result = $this->executeMethod(); } catch (Exception $ex) { $profiler->endServiceCall($call_id, array()); throw $ex; } $profiler->endServiceCall($call_id, array()); return $result; } private function executeMethod() { $user = $this->getUser(); if (!$user) { $user = new PhabricatorUser(); } $this->request->setUser($user); if (!$this->shouldRequireAuthentication()) { // No auth requirement here. } else { $allow_public = $this->handler->shouldAllowPublic() && PhabricatorEnv::getEnvConfig('policy.allow-public'); if (!$allow_public) { if (!$user->isLoggedIn() && !$user->isOmnipotent()) { // TODO: As per below, this should get centralized and cleaned up. throw new ConduitException('ERR-INVALID-AUTH'); } } // TODO: This would be slightly cleaner by just using a Query, but the // Conduit auth workflow requires the Call and User be built separately. // Just do it this way for the moment. $application = $this->handler->getApplication(); if ($application) { $can_view = PhabricatorPolicyFilter::hasCapability( $user, $application, PhabricatorPolicyCapability::CAN_VIEW); if (!$can_view) { throw new ConduitException( pht( 'You do not have access to the application which provides this '. 'API method.')); } } } return $this->handler->executeMethod($this->request); } protected function buildMethodHandler($method_name) { $method = ConduitAPIMethod::getConduitMethod($method_name); if (!$method) { throw new ConduitMethodDoesNotExistException($method_name); } $application = $method->getApplication(); if ($application && !$application->isInstalled()) { $app_name = $application->getName(); throw new ConduitApplicationNotInstalledException($method, $app_name); } return $method; } } diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index aa541bdd06..1e0c7eb276 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -1,137 +1,137 @@ method = $data['method']; } public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $method = id(new PhabricatorConduitMethodQuery()) ->setViewer($viewer) ->withMethods(array($this->method)) ->executeOne(); if (!$method) { return new Aphront404Response(); } $can_call_method = false; $status = $method->getMethodStatus(); $reason = $method->getMethodStatusDescription(); $errors = array(); switch ($status) { case ConduitAPIMethod::METHOD_STATUS_DEPRECATED: $reason = nonempty($reason, pht('This method is deprecated.')); $errors[] = pht('Deprecated Method: %s', $reason); break; case ConduitAPIMethod::METHOD_STATUS_UNSTABLE: $reason = nonempty( $reason, pht( 'This method is new and unstable. Its interface is subject '. 'to change.')); $errors[] = pht('Unstable Method: %s', $reason); break; } - $error_types = $method->defineErrorTypes(); + $error_types = $method->getErrorTypes(); $error_types['ERR-CONDUIT-CORE'] = pht('See error message for details.'); $error_description = array(); foreach ($error_types as $error => $meaning) { $error_description[] = hsprintf( '
  • %s: %s
  • ', $error, $meaning); } $error_description = phutil_tag('ul', array(), $error_description); $form = new AphrontFormView(); $form ->setUser($request->getUser()) ->setAction('/api/'.$this->method) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Description') ->setValue($method->getMethodDescription())) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Returns') - ->setValue($method->defineReturnType())) + ->setValue($method->getReturnType())) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel('Errors') ->setValue($error_description)) ->appendChild(hsprintf( '

    Enter parameters using '. 'JSON. For instance, to enter a list, type: '. '["apple", "banana", "cherry"]')); - $params = $method->defineParamTypes(); + $params = $method->getParamTypes(); foreach ($params as $param => $desc) { $form->appendChild( id(new AphrontFormTextControl()) ->setLabel($param) ->setName("params[{$param}]") ->setCaption($desc)); } $must_login = !$viewer->isLoggedIn() && $method->shouldRequireAuthentication(); if ($must_login) { $errors[] = pht( 'Login Required: This method requires authentication. You must '. 'log in before you can make calls to it.'); } else { $form ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Output Format') ->setName('output') ->setOptions( array( 'human' => 'Human Readable', 'json' => 'JSON', ))) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($this->getApplicationURI()) ->setValue(pht('Call Method'))); } $header = id(new PHUIHeaderView()) ->setUser($viewer) ->setHeader($method->getAPIMethodName()); $form_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->setFormErrors($errors) ->setForm($form); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($method->getAPIMethodName()); return $this->buildApplicationPage( array( $crumbs, $form_box, ), array( 'title' => $method->getAPIMethodName(), )); } } diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php index 3e28d6899e..f964684d71 100644 --- a/src/applications/conduit/method/ConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitAPIMethod.php @@ -1,308 +1,325 @@ defineParamTypes(); + } + + public function getReturnType() { + return $this->defineReturnType(); + } + + public function getErrorTypes() { + return $this->defineErrorTypes(); + } + /** * This is mostly for compatibility with * @{class:PhabricatorCursorPagedPolicyAwareQuery}. */ public function getID() { return $this->getAPIMethodName(); } /** * Get the status for this method (e.g., stable, unstable or deprecated). * Should return a METHOD_STATUS_* constant. By default, methods are * "stable". * * @return const METHOD_STATUS_* constant. * @task status */ public function getMethodStatus() { return self::METHOD_STATUS_STABLE; } /** * Optional description to supplement the method status. In particular, if * a method is deprecated, you can return a string here describing the reason * for deprecation and stable alternatives. * * @return string|null Description of the method status, if available. * @task status */ public function getMethodStatusDescription() { return null; } public function getErrorDescription($error_code) { - return idx($this->defineErrorTypes(), $error_code, 'Unknown Error'); + return idx($this->getErrorTypes(), $error_code, 'Unknown Error'); } public function getRequiredScope() { // by default, conduit methods are not accessible via OAuth return PhabricatorOAuthServerScope::SCOPE_NOT_ACCESSIBLE; } public function executeMethod(ConduitAPIRequest $request) { return $this->execute($request); } public abstract function getAPIMethodName(); /** * Return a key which sorts methods by application name, then method status, * then method name. */ public function getSortOrder() { $name = $this->getAPIMethodName(); $map = array( ConduitAPIMethod::METHOD_STATUS_STABLE => 0, ConduitAPIMethod::METHOD_STATUS_UNSTABLE => 1, ConduitAPIMethod::METHOD_STATUS_DEPRECATED => 2, ); $ord = idx($map, $this->getMethodStatus(), 0); list($head, $tail) = explode('.', $name, 2); return "{$head}.{$ord}.{$tail}"; } public function getApplicationName() { return head(explode('.', $this->getAPIMethodName(), 2)); } public static function getConduitMethod($method_name) { static $method_map = null; if ($method_map === null) { $methods = id(new PhutilSymbolLoader()) ->setAncestorClass(__CLASS__) ->loadObjects(); foreach ($methods as $method) { $name = $method->getAPIMethodName(); if (empty($method_map[$name])) { $method_map[$name] = $method; continue; } $orig_class = get_class($method_map[$name]); $this_class = get_class($method); throw new Exception( "Two Conduit API method classes ({$orig_class}, {$this_class}) ". "both have the same method name ({$name}). API methods ". "must have unique method names."); } } return idx($method_map, $method_name); } public function shouldRequireAuthentication() { return true; } public function shouldAllowPublic() { return false; } public function shouldAllowUnguardedWrites() { return false; } /** * Optionally, return a @{class:PhabricatorApplication} which this call is * part of. The call will be disabled when the application is uninstalled. * * @return PhabricatorApplication|null Related application. */ public function getApplication() { return null; } protected function formatStringConstants($constants) { foreach ($constants as $key => $value) { $constants[$key] = '"'.$value.'"'; } $constants = implode(', ', $constants); return 'string-constant<'.$constants.'>'; } public static function getParameterMetadataKey($key) { if (strncmp($key, 'api.', 4) === 0) { // All keys passed beginning with "api." are always metadata keys. return substr($key, 4); } else { switch ($key) { // These are real keys which always belong to request metadata. case 'access_token': case 'scope': case 'output': // This is not a real metadata key; it is included here only to // prevent Conduit methods from defining it. case '__conduit__': // This is prevented globally as a blanket defense against OAuth // redirection attacks. It is included here to stop Conduit methods // from defining it. case 'code': // This is not a real metadata key, but the presence of this // parameter triggers an alternate request decoding pathway. case 'params': return $key; } } return null; } /* -( Paging Results )----------------------------------------------------- */ /** * @task pager */ protected function getPagerParamTypes() { return array( 'before' => 'optional string', 'after' => 'optional string', 'limit' => 'optional int (default = 100)', ); } /** * @task pager */ protected function newPager(ConduitAPIRequest $request) { $limit = $request->getValue('limit', 100); $limit = min(1000, $limit); $limit = max(1, $limit); $pager = id(new AphrontCursorPagerView()) ->setPageSize($limit); $before_id = $request->getValue('before'); if ($before_id !== null) { $pager->setBeforeID($before_id); } $after_id = $request->getValue('after'); if ($after_id !== null) { $pager->setAfterID($after_id); } return $pager; } /** * @task pager */ protected function addPagerResults( array $results, AphrontCursorPagerView $pager) { $results['cursor'] = array( 'limit' => $pager->getPageSize(), 'after' => $pager->getNextPageID(), 'before' => $pager->getPrevPageID(), ); return $results; } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getPHID() { return null; } public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, ); } public function getPolicy($capability) { // Application methods get application visibility; other methods get open // visibility. $application = $this->getApplication(); if ($application) { return $application->getPolicy($capability); } return PhabricatorPolicies::getMostOpenPolicy(); } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { if (!$this->shouldRequireAuthentication()) { // Make unauthenticated methods universally visible. return true; } return false; } public function describeAutomaticCapability($capability) { return null; } protected function hasApplicationCapability( $capability, PhabricatorUser $viewer) { $application = $this->getApplication(); if (!$application) { return false; } return PhabricatorPolicyFilter::hasCapability( $viewer, $application, $capability); } protected function requireApplicationCapability( $capability, PhabricatorUser $viewer) { $application = $this->getApplication(); if (!$application) { return; } PhabricatorPolicyFilter::requireCapability( $viewer, $this->getApplication(), $capability); } } diff --git a/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php b/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php index 1654214cb7..12f08f8636 100644 --- a/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php @@ -1,159 +1,159 @@ 'required string', 'clientVersion' => 'required int', 'clientDescription' => 'optional string', 'user' => 'optional string', 'authToken' => 'optional int', 'authSignature' => 'optional string', 'host' => 'deprecated', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-VERSION' => 'Client/server version mismatch. Upgrade your server or downgrade '. 'your client.', 'NEW-ARC-VERSION' => 'Client/server version mismatch. Upgrade your client.', 'ERR-UNKNOWN-CLIENT' => 'Client is unknown.', 'ERR-INVALID-USER' => 'The username you are attempting to authenticate with is not valid.', 'ERR-INVALID-CERTIFICATE' => 'Your authentication certificate for this server is invalid.', 'ERR-INVALID-TOKEN' => "The challenge token you are authenticating with is outside of the ". "allowed time range. Either your system clock is out of whack or ". "you're executing a replay attack.", 'ERR-NO-CERTIFICATE' => 'This server requires authentication.', ); } protected function execute(ConduitAPIRequest $request) { $client = $request->getValue('client'); $client_version = (int)$request->getValue('clientVersion'); $client_description = (string)$request->getValue('clientDescription'); $client_description = id(new PhutilUTF8StringTruncator()) ->setMaximumBytes(255) ->truncateString($client_description); $username = (string)$request->getValue('user'); // Log the connection, regardless of the outcome of checks below. $connection = new PhabricatorConduitConnectionLog(); $connection->setClient($client); $connection->setClientVersion($client_version); $connection->setClientDescription($client_description); $connection->setUsername($username); $connection->save(); switch ($client) { case 'arc': $server_version = 6; $supported_versions = array( $server_version => true, // Client version 5 introduced "user.query" call 4 => true, // Client version 6 introduced "diffusion.getlintmessages" call 5 => true, ); if (empty($supported_versions[$client_version])) { if ($server_version < $client_version) { $ex = new ConduitException('ERR-BAD-VERSION'); $ex->setErrorDescription( "Your 'arc' client version is '{$client_version}', which ". "is newer than the server version, '{$server_version}'. ". "Upgrade your Phabricator install."); } else { $ex = new ConduitException('NEW-ARC-VERSION'); $ex->setErrorDescription( "A new version of arc is available! You need to upgrade ". "to connect to this server (you are running version ". "{$client_version}, the server is running version ". "{$server_version})."); } throw $ex; } break; default: // Allow new clients by default. break; } $token = $request->getValue('authToken'); $signature = $request->getValue('authSignature'); $user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username); if (!$user) { throw new ConduitException('ERR-INVALID-USER'); } $session_key = null; if ($token && $signature) { $threshold = 60 * 15; $now = time(); if (abs($token - $now) > $threshold) { throw id(new ConduitException('ERR-INVALID-TOKEN')) ->setErrorDescription( pht( 'The request you submitted is signed with a timestamp, but that '. 'timestamp is not within %s of the current time. The '. 'signed timestamp is %s (%s), and the current server time is '. '%s (%s). This is a difference of %s seconds, but the '. 'timestamp must differ from the server time by no more than '. '%s seconds. Your client or server clock may not be set '. 'correctly.', phutil_format_relative_time($threshold), $token, date('r', $token), $now, date('r', $now), ($token - $now), $threshold)); } $valid = sha1($token.$user->getConduitCertificate()); if ($valid != $signature) { throw new ConduitException('ERR-INVALID-CERTIFICATE'); } $session_key = id(new PhabricatorAuthSessionEngine())->establishSession( PhabricatorAuthSession::TYPE_CONDUIT, $user->getPHID(), $partial = false); } else { throw new ConduitException('ERR-NO-CERTIFICATE'); } return array( 'connectionID' => $connection->getID(), 'sessionKey' => $session_key, 'userPHID' => $user->getPHID(), ); } } diff --git a/src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php b/src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php index ddbca233df..44acf3e0d3 100644 --- a/src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php @@ -1,60 +1,56 @@ '; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $authentication = array( 'token', 'asymmetric', 'session', 'sessionless', ); $oauth_app = 'PhabricatorOAuthServerApplication'; if (PhabricatorApplication::isClassInstalled($oauth_app)) { $authentication[] = 'oauth'; } return array( 'authentication' => $authentication, 'signatures' => array( 'consign', ), 'input' => array( 'json', 'urlencoded', ), 'output' => array( 'json', 'human', ), ); } } diff --git a/src/applications/conduit/method/ConduitGetCertificateConduitAPIMethod.php b/src/applications/conduit/method/ConduitGetCertificateConduitAPIMethod.php index d57c1030cc..be14610796 100644 --- a/src/applications/conduit/method/ConduitGetCertificateConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitGetCertificateConduitAPIMethod.php @@ -1,92 +1,92 @@ 'required string', 'host' => 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-TOKEN' => 'Token does not exist or has expired.', 'ERR-RATE-LIMIT' => 'You have made too many invalid token requests recently. Wait before '. 'making more.', ); } protected function execute(ConduitAPIRequest $request) { $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP( PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE_FAILURE, 60 * 5); if (count($failed_attempts) > 5) { $this->logFailure($request); throw new ConduitException('ERR-RATE-LIMIT'); } $token = $request->getValue('token'); $info = id(new PhabricatorConduitCertificateToken())->loadOneWhere( 'token = %s', trim($token)); if (!$info || $info->getDateCreated() < time() - (60 * 15)) { $this->logFailure($request, $info); throw new ConduitException('ERR-BAD-TOKEN'); } else { $log = PhabricatorUserLog::initializeNewLog( $request->getUser(), $info->getUserPHID(), PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE) ->save(); } $user = id(new PhabricatorUser())->loadOneWhere( 'phid = %s', $info->getUserPHID()); if (!$user) { throw new Exception('Certificate token points to an invalid user!'); } return array( 'username' => $user->getUserName(), 'certificate' => $user->getConduitCertificate(), ); } private function logFailure( ConduitAPIRequest $request, PhabricatorConduitCertificateToken $info = null) { $log = PhabricatorUserLog::initializeNewLog( $request->getUser(), $info ? $info->getUserPHID() : '-', PhabricatorUserLog::ACTION_CONDUIT_CERTIFICATE_FAILURE) ->save(); } } diff --git a/src/applications/conduit/method/ConduitPingConduitAPIMethod.php b/src/applications/conduit/method/ConduitPingConduitAPIMethod.php index 7e1ece57eb..f3c502defa 100644 --- a/src/applications/conduit/method/ConduitPingConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitPingConduitAPIMethod.php @@ -1,33 +1,29 @@ '; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $classes = id(new PhutilSymbolLoader()) ->setAncestorClass('ConduitAPIMethod') ->setType('class') ->loadObjects(); $names_to_params = array(); foreach ($classes as $class) { $names_to_params[$class->getAPIMethodName()] = array( 'description' => $class->getMethodDescription(), - 'params' => $class->defineParamTypes(), - 'return' => $class->defineReturnType(), + 'params' => $class->getParamTypes(), + 'return' => $class->getReturnType(), ); } return $names_to_params; } } diff --git a/src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php b/src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php index 81138831d0..d58b11ffa5 100644 --- a/src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php +++ b/src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php @@ -1,67 +1,67 @@ 'optional string', 'message' => 'required string', 'participantPHIDs' => 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_EMPTY_PARTICIPANT_PHIDS' => pht( 'You must specify participant phids.'), 'ERR_EMPTY_MESSAGE' => pht( 'You must specify a message.'), ); } protected function execute(ConduitAPIRequest $request) { $participant_phids = $request->getValue('participantPHIDs', array()); $message = $request->getValue('message'); $title = $request->getValue('title'); list($errors, $conpherence) = ConpherenceEditor::createThread( $request->getUser(), $participant_phids, $title, $message, PhabricatorContentSource::newFromConduitRequest($request)); if ($errors) { foreach ($errors as $error_code) { switch ($error_code) { case ConpherenceEditor::ERROR_EMPTY_MESSAGE: throw new ConduitException('ERR_EMPTY_MESSAGE'); break; case ConpherenceEditor::ERROR_EMPTY_PARTICIPANTS: throw new ConduitException('ERR_EMPTY_PARTICIPANT_PHIDS'); break; } } } return array( 'conpherenceID' => $conpherence->getID(), 'conpherencePHID' => $conpherence->getPHID(), 'conpherenceURI' => $this->getConpherenceURI($conpherence), ); } } diff --git a/src/applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php b/src/applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php index 2fa3dc6f27..986e8e520c 100644 --- a/src/applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php +++ b/src/applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php @@ -1,87 +1,83 @@ 'optional array', 'phids' => 'optional array', 'limit' => 'optional int', 'offset' => 'optional int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $ids = $request->getValue('ids', array()); $phids = $request->getValue('phids', array()); $limit = $request->getValue('limit'); $offset = $request->getValue('offset'); $query = id(new ConpherenceThreadQuery()) ->setViewer($user) ->needParticipantCache(true) ->needFilePHIDs(true); if ($ids) { $conpherences = $query ->withIDs($ids) ->setLimit($limit) ->setOffset($offset) ->execute(); } else if ($phids) { $conpherences = $query ->withPHIDs($phids) ->setLimit($limit) ->setOffset($offset) ->execute(); } else { $participation = id(new ConpherenceParticipantQuery()) ->withParticipantPHIDs(array($user->getPHID())) ->setLimit($limit) ->setOffset($offset) ->execute(); $conpherence_phids = array_keys($participation); $query->withPHIDs($conpherence_phids); $conpherences = $query->execute(); $conpherences = array_select_keys($conpherences, $conpherence_phids); } $data = array(); foreach ($conpherences as $conpherence) { $id = $conpherence->getID(); $data[$id] = array( 'conpherenceID' => $id, 'conpherencePHID' => $conpherence->getPHID(), 'conpherenceTitle' => $conpherence->getTitle(), 'messageCount' => $conpherence->getMessageCount(), 'recentParticipantPHIDs' => $conpherence->getRecentParticipantPHIDs(), 'filePHIDs' => $conpherence->getFilePHIDs(), 'conpherenceURI' => $this->getConpherenceURI($conpherence), ); } return $data; } } diff --git a/src/applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php b/src/applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php index a7d30c5a45..958748a68e 100644 --- a/src/applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php +++ b/src/applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php @@ -1,97 +1,97 @@ 'optional int', 'threadPHID' => 'optional phid', 'limit' => 'optional int', 'offset' => 'optional int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_USAGE_NO_THREAD_ID' => pht( 'You must specify a thread id or thread phid to query transactions '. 'from.'), ); } protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $thread_id = $request->getValue('threadID'); $thread_phid = $request->getValue('threadPHID'); $limit = $request->getValue('limit'); $offset = $request->getValue('offset'); $query = id(new ConpherenceThreadQuery()) ->setViewer($user); if ($thread_id) { $query->withIDs(array($thread_id)); } else if ($thread_phid) { $query->withPHIDs(array($thread_phid)); } else { throw new ConduitException('ERR_USAGE_NO_THREAD_ID'); } $conpherence = $query->executeOne(); $query = id(new ConpherenceTransactionQuery()) ->setViewer($user) ->withObjectPHIDs(array($conpherence->getPHID())) ->setLimit($limit) ->setOffset($offset); $transactions = $query->execute(); $data = array(); foreach ($transactions as $transaction) { $comment = null; $comment_obj = $transaction->getComment(); if ($comment_obj) { $comment = $comment_obj->getContent(); } $title = null; $title_obj = $transaction->getTitle(); if ($title_obj) { $title = $title_obj->getHTMLContent(); } $id = $transaction->getID(); $data[$id] = array( 'transactionID' => $id, 'transactionType' => $transaction->getTransactionType(), 'transactionTitle' => $title, 'transactionComment' => $comment, 'transactionOldValue' => $transaction->getOldValue(), 'transactionNewValue' => $transaction->getNewValue(), 'transactionMetadata' => $transaction->getMetadata(), 'authorPHID' => $transaction->getAuthorPHID(), 'dateCreated' => $transaction->getDateCreated(), 'conpherenceID' => $conpherence->getID(), 'conpherencePHID' => $conpherence->getPHID(), ); } return $data; } } diff --git a/src/applications/conpherence/conduit/ConpherenceUpdateThreadConduitAPIMethod.php b/src/applications/conpherence/conduit/ConpherenceUpdateThreadConduitAPIMethod.php index 1e98011b83..e183d44699 100644 --- a/src/applications/conpherence/conduit/ConpherenceUpdateThreadConduitAPIMethod.php +++ b/src/applications/conpherence/conduit/ConpherenceUpdateThreadConduitAPIMethod.php @@ -1,109 +1,109 @@ 'optional int', 'phid' => 'optional phid', 'title' => 'optional string', 'message' => 'optional string', 'addParticipantPHIDs' => 'optional list', 'removeParticipantPHID' => 'optional phid', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'bool'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_USAGE_NO_THREAD_ID' => pht( 'You must specify a thread id or thread phid to query transactions '. 'from.'), 'ERR_USAGE_THREAD_NOT_FOUND' => pht( 'Thread does not exist or logged in user can not see it.'), 'ERR_USAGE_ONLY_SELF_REMOVE' => pht( 'Only a user can remove themselves from a thread.'), 'ERR_USAGE_NO_UPDATES' => pht( 'You must specify data that actually updates the conpherence.'), ); } protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $id = $request->getValue('id'); $phid = $request->getValue('phid'); $query = id(new ConpherenceThreadQuery()) ->setViewer($user) ->needFilePHIDs(true); if ($id) { $query->withIDs(array($id)); } else if ($phid) { $query->withPHIDs(array($phid)); } else { throw new ConduitException('ERR_USAGE_NO_THREAD_ID'); } $conpherence = $query->executeOne(); if (!$conpherence) { throw new ConduitException('ERR_USAGE_THREAD_NOT_FOUND'); } $source = PhabricatorContentSource::newFromConduitRequest($request); $editor = id(new ConpherenceEditor()) ->setContentSource($source) ->setActor($user); $xactions = array(); $add_participant_phids = $request->getValue('addParticipantPHIDs', array()); $remove_participant_phid = $request->getValue('removeParticipantPHID'); $message = $request->getValue('message'); $title = $request->getValue('title'); if ($add_participant_phids) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceTransactionType::TYPE_PARTICIPANTS) ->setNewValue(array('+' => $add_participant_phids)); } if ($remove_participant_phid) { if ($remove_participant_phid != $user->getPHID()) { throw new ConduitException('ERR_USAGE_ONLY_SELF_REMOVE'); } $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceTransactionType::TYPE_PARTICIPANTS) ->setNewValue(array('-' => array($remove_participant_phid))); } if ($title) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransactionType::TYPE_TITLE) ->setNewValue($title); } if ($message) { $xactions = array_merge( $xactions, $editor->generateTransactionsFromText( $user, $conpherence, $message)); } try { $xactions = $editor->applyTransactions($conpherence, $xactions); } catch (PhabricatorApplicationTransactionNoEffectException $ex) { throw new ConduitException('ERR_USAGE_NO_UPDATES'); } return true; } } diff --git a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php index 8d1ffd90a3..58fed6cf6e 100644 --- a/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCloseConduitAPIMethod.php @@ -1,63 +1,63 @@ 'required int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_NOT_FOUND' => 'Revision was not found.', ); } protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $id = $request->getValue('revisionID'); $revision = id(new DifferentialRevisionQuery()) ->withIDs(array($id)) ->setViewer($viewer) ->needReviewerStatus(true) ->executeOne(); if (!$revision) { throw new ConduitException('ERR_NOT_FOUND'); } $xactions = array(); $xactions[] = id(new DifferentialTransaction()) ->setTransactionType(DifferentialTransaction::TYPE_ACTION) ->setNewValue(DifferentialAction::ACTION_CLOSE); $content_source = PhabricatorContentSource::newForSource( PhabricatorContentSource::SOURCE_CONDUIT, array()); $editor = id(new DifferentialTransactionEditor()) ->setActor($viewer) ->setContentSourceFromConduitRequest($request) ->setContinueOnMissingFields(true) ->setContinueOnNoEffect(true); $editor->applyTransactions($revision, $xactions); return; } } diff --git a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php index b1c145b809..fe1d57857d 100644 --- a/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php @@ -1,92 +1,92 @@ 'required revisionid', 'message' => 'optional string', 'action' => 'optional string', 'silent' => 'optional bool', 'attach_inlines' => 'optional bool', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_REVISION' => 'Bad revision ID.', ); } protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $revision = id(new DifferentialRevisionQuery()) ->setViewer($viewer) ->withIDs(array($request->getValue('revision_id'))) ->needReviewerStatus(true) ->needReviewerAuthority(true) ->executeOne(); if (!$revision) { throw new ConduitException('ERR_BAD_REVISION'); } $xactions = array(); $action = $request->getValue('action'); if ($action && ($action != 'comment') && ($action != 'none')) { $xactions[] = id(new DifferentialTransaction()) ->setTransactionType(DifferentialTransaction::TYPE_ACTION) ->setNewValue($action); } $content = $request->getValue('message'); if (strlen($content)) { $xactions[] = id(new DifferentialTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new DifferentialTransactionComment()) ->setContent($content)); } if ($request->getValue('attach_inlines')) { $type_inline = DifferentialTransaction::TYPE_INLINE; $inlines = DifferentialTransactionQuery::loadUnsubmittedInlineComments( $viewer, $revision); foreach ($inlines as $inline) { $xactions[] = id(new DifferentialTransaction()) ->setTransactionType($type_inline) ->attachComment($inline); } } $editor = id(new DifferentialTransactionEditor()) ->setActor($viewer) ->setDisableEmail($request->getValue('silent')) ->setContentSourceFromConduitRequest($request) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true); $editor->applyTransactions($revision, $xactions); return array( 'revisionid' => $revision->getID(), 'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()), ); } } diff --git a/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php index 232fd71d59..6db5bc6cb4 100644 --- a/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php @@ -1,189 +1,184 @@ formatStringConstants( array( 'svn', 'git', 'hg', )); $status_const = $this->formatStringConstants( array( 'none', 'skip', 'okay', 'warn', 'fail', 'postponed', )); return array( 'changes' => 'required list', 'sourceMachine' => 'required string', 'sourcePath' => 'required string', 'branch' => 'required string', 'bookmark' => 'optional string', 'sourceControlSystem' => 'required '.$vcs_const, 'sourceControlPath' => 'required string', 'sourceControlBaseRevision' => 'required string', 'creationMethod' => 'optional string', 'arcanistProject' => 'optional string', 'lintStatus' => 'required '.$status_const, 'unitStatus' => 'required '.$status_const, 'repositoryPHID' => 'optional phid', 'parentRevisionID' => 'deprecated', 'authorPHID' => 'deprecated', 'repositoryUUID' => 'deprecated', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $change_data = $request->getValue('changes'); $changes = array(); foreach ($change_data as $dict) { $changes[] = ArcanistDiffChange::newFromDictionary($dict); } $diff = DifferentialDiff::newFromRawChanges($viewer, $changes); // TODO: Remove repository UUID eventually; for now continue writing // the UUID. Note that we'll overwrite it below if we identify a // repository, and `arc` no longer sends it. This stuff is retained for // backward compatibility. $repository_uuid = $request->getValue('repositoryUUID'); $repository_phid = $request->getValue('repositoryPHID'); if ($repository_phid) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->withPHIDs(array($repository_phid)) ->executeOne(); if ($repository) { $repository_phid = $repository->getPHID(); $repository_uuid = $repository->getUUID(); } } $project_name = $request->getValue('arcanistProject'); $project_phid = null; if ($project_name) { $arcanist_project = id(new PhabricatorRepositoryArcanistProject()) ->loadOneWhere( 'name = %s', $project_name); if (!$arcanist_project) { $arcanist_project = new PhabricatorRepositoryArcanistProject(); $arcanist_project->setName($project_name); $arcanist_project->save(); } $project_phid = $arcanist_project->getPHID(); } switch ($request->getValue('lintStatus')) { case 'skip': $lint_status = DifferentialLintStatus::LINT_SKIP; break; case 'okay': $lint_status = DifferentialLintStatus::LINT_OKAY; break; case 'warn': $lint_status = DifferentialLintStatus::LINT_WARN; break; case 'fail': $lint_status = DifferentialLintStatus::LINT_FAIL; break; case 'postponed': $lint_status = DifferentialLintStatus::LINT_POSTPONED; break; case 'none': default: $lint_status = DifferentialLintStatus::LINT_NONE; break; } switch ($request->getValue('unitStatus')) { case 'skip': $unit_status = DifferentialUnitStatus::UNIT_SKIP; break; case 'okay': $unit_status = DifferentialUnitStatus::UNIT_OKAY; break; case 'warn': $unit_status = DifferentialUnitStatus::UNIT_WARN; break; case 'fail': $unit_status = DifferentialUnitStatus::UNIT_FAIL; break; case 'postponed': $unit_status = DifferentialUnitStatus::UNIT_POSTPONED; break; case 'none': default: $unit_status = DifferentialUnitStatus::UNIT_NONE; break; } $diff_data_dict = array( 'sourcePath' => $request->getValue('sourcePath'), 'sourceMachine' => $request->getValue('sourceMachine'), 'branch' => $request->getValue('branch'), 'creationMethod' => $request->getValue('creationMethod'), 'authorPHID' => $viewer->getPHID(), 'bookmark' => $request->getValue('bookmark'), 'repositoryUUID' => $repository_uuid, 'repositoryPHID' => $repository_phid, 'sourceControlSystem' => $request->getValue('sourceControlSystem'), 'sourceControlPath' => $request->getValue('sourceControlPath'), 'sourceControlBaseRevision' => $request->getValue('sourceControlBaseRevision'), 'arcanistProjectPHID' => $project_phid, 'lintStatus' => $lint_status, 'unitStatus' => $unit_status, ); $xactions = array(id(new DifferentialTransaction()) ->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE) ->setNewValue($diff_data_dict), ); id(new DifferentialDiffEditor()) ->setActor($viewer) ->setContentSourceFromConduitRequest($request) ->setContinueOnNoEffect(true) ->applyTransactions($diff, $xactions); $path = '/differential/diff/'.$diff->getID().'/'; $uri = PhabricatorEnv::getURI($path); return array( 'diffid' => $diff->getID(), 'uri' => $uri, ); } } diff --git a/src/applications/differential/conduit/DifferentialCreateInlineConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateInlineConduitAPIMethod.php index 67e4c68a60..98a567b0b8 100644 --- a/src/applications/differential/conduit/DifferentialCreateInlineConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateInlineConduitAPIMethod.php @@ -1,109 +1,109 @@ 'optional revisionid', 'diffID' => 'optional diffid', 'filePath' => 'required string', 'isNewFile' => 'required bool', 'lineNumber' => 'required int', 'lineLength' => 'optional int', 'content' => 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-REVISION' => 'Bad revision ID.', 'ERR-BAD-DIFF' => 'Bad diff ID, or diff does not belong to revision.', 'ERR-NEED-DIFF' => 'Neither revision ID nor diff ID was provided.', 'ERR-NEED-FILE' => 'A file path was not provided.', 'ERR-BAD-FILE' => "Requested file doesn't exist in this revision.", ); } protected function execute(ConduitAPIRequest $request) { $rid = $request->getValue('revisionID'); $did = $request->getValue('diffID'); if ($rid) { // Given both a revision and a diff, check that they match. // Given only a revision, find the active diff. $revision = id(new DifferentialRevisionQuery()) ->setViewer($request->getUser()) ->withIDs(array($rid)) ->executeOne(); if (!$revision) { throw new ConduitException('ERR-BAD-REVISION'); } if (!$did) { // did not! $diff = $revision->loadActiveDiff(); $did = $diff->getID(); } else { // did too! $diff = id(new DifferentialDiff())->load($did); if (!$diff || $diff->getRevisionID() != $rid) { throw new ConduitException('ERR-BAD-DIFF'); } } } else if ($did) { // Given only a diff, find the parent revision. $diff = id(new DifferentialDiff())->load($did); if (!$diff) { throw new ConduitException('ERR-BAD-DIFF'); } $rid = $diff->getRevisionID(); } else { // Given neither, bail. throw new ConduitException('ERR-NEED-DIFF'); } $file = $request->getValue('filePath'); if (!$file) { throw new ConduitException('ERR-NEED-FILE'); } $changes = id(new DifferentialChangeset())->loadAllWhere( 'diffID = %d', $did); $cid = null; foreach ($changes as $id => $change) { if ($file == $change->getFilename()) { $cid = $id; } } if ($cid == null) { throw new ConduitException('ERR-BAD-FILE'); } $inline = id(new DifferentialInlineComment()) ->setRevisionID($rid) ->setChangesetID($cid) ->setAuthorPHID($request->getUser()->getPHID()) ->setContent($request->getValue('content')) ->setIsNewFile($request->getValue('isNewFile')) ->setLineNumber($request->getValue('lineNumber')) ->setLineLength($request->getValue('lineLength', 0)) ->save(); // Load everything again, just to be safe. $changeset = id(new DifferentialChangeset()) ->load($inline->getChangesetID()); return $this->buildInlineInfoDictionary($inline, $changeset); } } diff --git a/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php index c7596fa04b..e8dd29f86a 100644 --- a/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php @@ -1,80 +1,75 @@ 'required string', 'repositoryPHID' => 'optional string', 'viewPolicy' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $raw_diff = $request->getValue('diff'); $repository_phid = $request->getValue('repositoryPHID'); if ($repository_phid) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->withPHIDs(array($repository_phid)) ->executeOne(); if (!$repository) { throw new Exception( pht('No such repository "%s"!', $repository_phid)); } } $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($raw_diff); $diff = DifferentialDiff::newFromRawChanges($viewer, $changes); $diff_data_dict = array( 'creationMethod' => 'web', 'authorPHID' => $viewer->getPHID(), 'repositoryPHID' => $repository_phid, 'lintStatus' => DifferentialLintStatus::LINT_SKIP, 'unitStatus' => DifferentialUnitStatus::UNIT_SKIP, ); $xactions = array(id(new DifferentialTransaction()) ->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE) ->setNewValue($diff_data_dict), ); if ($request->getValue('viewPolicy')) { $xactions[] = id(new DifferentialTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($request->getValue('viewPolicy')); } id(new DifferentialDiffEditor()) ->setActor($viewer) ->setContentSourceFromConduitRequest($request) ->setContinueOnNoEffect(true) ->setLookupRepository(false) // respect user choice ->applyTransactions($diff, $xactions); return $this->buildDiffInfoDictionary($diff); } } diff --git a/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php index 786b0694d3..de0c3e02d5 100644 --- a/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php @@ -1,61 +1,61 @@ 'ignored', 'diffid' => 'required diffid', 'fields' => 'required dict', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_DIFF' => 'Bad diff ID.', ); } protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $diff = id(new DifferentialDiffQuery()) ->setViewer($viewer) ->withIDs(array($request->getValue('diffid'))) ->executeOne(); if (!$diff) { throw new ConduitException('ERR_BAD_DIFF'); } $revision = DifferentialRevision::initializeNewRevision($viewer); $revision->attachReviewerStatus(array()); $this->applyFieldEdit( $request, $revision, $diff, $request->getValue('fields', array()), $message = null); return array( 'revisionid' => $revision->getID(), 'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()), ); } } diff --git a/src/applications/differential/conduit/DifferentialFindConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialFindConduitAPIMethod.php index 585d60df37..56cb0f08da 100644 --- a/src/applications/differential/conduit/DifferentialFindConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialFindConduitAPIMethod.php @@ -1,106 +1,101 @@ 'required '.$this->formatStringConstants($types), 'guids' => 'required nonempty list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $type = $request->getValue('query'); $guids = $request->getValue('guids'); $results = array(); if (!$guids) { return $results; } $query = id(new DifferentialRevisionQuery()) ->setViewer($request->getUser()); switch ($type) { case 'open': $query ->withStatus(DifferentialRevisionQuery::STATUS_OPEN) ->withAuthors($guids); break; case 'committable': $query ->withStatus(DifferentialRevisionQuery::STATUS_ACCEPTED) ->withAuthors($guids); break; case 'revision-ids': $query ->withIDs($guids); break; case 'owned': $query->withAuthors($guids); break; case 'phids': $query ->withPHIDs($guids); break; } $revisions = $query->execute(); foreach ($revisions as $revision) { $diff = $revision->loadActiveDiff(); if (!$diff) { continue; } $id = $revision->getID(); $results[] = array( 'id' => $id, 'phid' => $revision->getPHID(), 'name' => $revision->getTitle(), 'uri' => PhabricatorEnv::getProductionURI('/D'.$id), 'dateCreated' => $revision->getDateCreated(), 'authorPHID' => $revision->getAuthorPHID(), 'statusName' => ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( $revision->getStatus()), 'sourcePath' => $diff->getSourcePath(), ); } return $results; } } diff --git a/src/applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php index 8cd4f87ea3..093f06e117 100644 --- a/src/applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php @@ -1,118 +1,118 @@ 'required diffID', 'linters' => 'required dict', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-DIFF' => 'Bad diff ID.', 'ERR-BAD-LINTER' => 'No postponed linter by the given name', 'ERR-NO-LINT' => 'No postponed lint field available in diff', ); } protected function execute(ConduitAPIRequest $request) { $diff_id = $request->getValue('diffID'); $linter_map = $request->getValue('linters'); $diff = id(new DifferentialDiffQuery()) ->setViewer($request->getUser()) ->withIDs(array($diff_id)) ->executeOne(); if (!$diff) { throw new ConduitException('ERR-BAD-DIFF'); } // Extract the finished linters and messages from the linter map. $finished_linters = array_keys($linter_map); $new_messages = array(); foreach ($linter_map as $linter => $messages) { $new_messages = array_merge($new_messages, $messages); } // Load the postponed linters attached to this diff. $postponed_linters_property = id( new DifferentialDiffProperty())->loadOneWhere( 'diffID = %d AND name = %s', $diff_id, 'arc:lint-postponed'); if ($postponed_linters_property) { $postponed_linters = $postponed_linters_property->getData(); } else { $postponed_linters = array(); } foreach ($finished_linters as $linter) { if (!in_array($linter, $postponed_linters)) { throw new ConduitException('ERR-BAD-LINTER'); } } foreach ($postponed_linters as $idx => $linter) { if (in_array($linter, $finished_linters)) { unset($postponed_linters[$idx]); } } // Load the lint messages currenty attached to the diff. If this // diff property doesn't exist, create it. $messages_property = id(new DifferentialDiffProperty())->loadOneWhere( 'diffID = %d AND name = %s', $diff_id, 'arc:lint'); if ($messages_property) { $messages = $messages_property->getData(); } else { $messages = array(); } // Add new lint messages, removing duplicates. foreach ($new_messages as $new_message) { if (!in_array($new_message, $messages)) { $messages[] = $new_message; } } // Use setdiffproperty to update the postponed linters and messages, // as these will also update the lint status correctly. $call = new ConduitCall( 'differential.setdiffproperty', array( 'diff_id' => $diff_id, 'name' => 'arc:lint', 'data' => json_encode($messages), )); $call->setUser($request->getUser()); $call->execute(); $call = new ConduitCall( 'differential.setdiffproperty', array( 'diff_id' => $diff_id, 'name' => 'arc:lint-postponed', 'data' => json_encode($postponed_linters), )); $call->setUser($request->getUser()); $call->execute(); } } diff --git a/src/applications/differential/conduit/DifferentialGetAllDiffsConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetAllDiffsConduitAPIMethod.php index 0f2e091d4c..c4e2d35215 100644 --- a/src/applications/differential/conduit/DifferentialGetAllDiffsConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetAllDiffsConduitAPIMethod.php @@ -1,60 +1,56 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $results = array(); $revision_ids = $request->getValue('revision_ids'); if (!$revision_ids) { return $results; } $diffs = id(new DifferentialDiffQuery()) ->setViewer($request->getUser()) ->withRevisionIDs($revision_ids) ->execute(); foreach ($diffs as $diff) { $results[] = array( 'revision_id' => $diff->getRevisionID(), 'diff_id' => $diff->getID(), ); } return $results; } } diff --git a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php index afadce3397..8d7e71c030 100644 --- a/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php @@ -1,198 +1,198 @@ 'optional revision_id', 'fields' => 'optional dict', 'edit' => 'optional '.$this->formatStringConstants($edit_types), ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty string'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_NOT_FOUND' => 'Revision was not found.', ); } protected function execute(ConduitAPIRequest $request) { $id = $request->getValue('revision_id'); $viewer = $request->getUser(); if ($id) { $revision = id(new DifferentialRevisionQuery()) ->withIDs(array($id)) ->setViewer($viewer) ->needReviewerStatus(true) ->needActiveDiffs(true) ->executeOne(); if (!$revision) { throw new ConduitException('ERR_NOT_FOUND'); } } else { $revision = DifferentialRevision::initializeNewRevision($viewer); $revision->attachReviewerStatus(array()); $revision->attachActiveDiff(null); } $is_edit = $request->getValue('edit'); $is_create = ($is_edit == 'create'); $field_list = PhabricatorCustomField::getObjectFields( $revision, ($is_edit ? DifferentialCustomField::ROLE_COMMITMESSAGEEDIT : DifferentialCustomField::ROLE_COMMITMESSAGE)); $field_list ->setViewer($viewer) ->readFieldsFromStorage($revision); $field_map = mpull($field_list->getFields(), null, 'getFieldKeyForConduit'); if ($is_edit) { $fields = $request->getValue('fields', array()); foreach ($fields as $field => $value) { $custom_field = idx($field_map, $field); if (!$custom_field) { // Just ignore this, these workflows don't make strong distictions // about field editability on the client side. continue; } if ($is_create || $custom_field->shouldOverwriteWhenCommitMessageIsEdited()) { $custom_field->readValueFromCommitMessage($value); } } } $phids = array(); foreach ($field_list->getFields() as $key => $field) { $field_phids = $field->getRequiredHandlePHIDsForCommitMessage(); if (!is_array($field_phids)) { throw new Exception( pht( 'Custom field "%s" was expected to return an array of handle '. 'PHIDs required for commit message rendering, but returned "%s" '. 'instead.', $field->getFieldKey(), gettype($field_phids))); } $phids[$key] = $field_phids; } $all_phids = array_mergev($phids); if ($all_phids) { $all_handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs($all_phids) ->execute(); } else { $all_handles = array(); } $key_title = id(new DifferentialTitleField())->getFieldKey(); $default_title = DifferentialTitleField::getDefaultTitle(); $commit_message = array(); foreach ($field_list->getFields() as $key => $field) { $handles = array_select_keys($all_handles, $phids[$key]); $label = $field->renderCommitMessageLabel(); $value = $field->renderCommitMessageValue($handles); if (!is_string($value) && !is_null($value)) { throw new Exception( pht( 'Custom field "%s" was expected to render a string or null value, '. 'but rendered a "%s" instead.', $field->getFieldKey(), gettype($value))); } $is_title = ($key == $key_title); if (!strlen($value)) { if ($is_title) { $commit_message[] = $default_title; } else { if ($is_edit && $field->shouldAppearInCommitMessageTemplate()) { $commit_message[] = $label.': '; } } } else { if ($is_title) { $commit_message[] = $value; } else { $value = str_replace( array("\r\n", "\r"), array("\n", "\n"), $value); if (strpos($value, "\n") !== false || substr($value, 0, 2) === ' ') { $commit_message[] = "{$label}:\n{$value}"; } else { $commit_message[] = "{$label}: {$value}"; } } } } if ($is_edit) { $tip = $this->getProTip($field_list); if ($tip !== null) { $commit_message[] = "\n".$tip; } } $commit_message = implode("\n\n", $commit_message); return $commit_message; } private function getProTip() { // Any field can provide tips, whether it normally appears on commit // messages or not. $field_list = PhabricatorCustomField::getObjectFields( new DifferentialRevision(), PhabricatorCustomField::ROLE_DEFAULT); $tips = array(); foreach ($field_list->getFields() as $key => $field) { $tips[] = $field->getProTips(); } $tips = array_mergev($tips); if (!$tips) { return null; } shuffle($tips); $tip = pht('Tip: %s', head($tips)); $tip = wordwrap($tip, 78, "\n", true); $lines = explode("\n", $tip); foreach ($lines as $key => $line) { $lines[$key] = '# '.$line; } return implode("\n", $lines); } } diff --git a/src/applications/differential/conduit/DifferentialGetCommitPathsConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetCommitPathsConduitAPIMethod.php index 0a1a644888..a025cd4983 100644 --- a/src/applications/differential/conduit/DifferentialGetCommitPathsConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetCommitPathsConduitAPIMethod.php @@ -1,56 +1,56 @@ 'required int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_NOT_FOUND' => 'No such revision exists.', ); } protected function execute(ConduitAPIRequest $request) { $id = $request->getValue('revision_id'); $revision = id(new DifferentialRevisionQuery()) ->setViewer($request->getUser()) ->withIDs(array($id)) ->executeOne(); if (!$revision) { throw new ConduitException('ERR_NOT_FOUND'); } $paths = array(); $diff = id(new DifferentialDiff())->loadOneWhere( 'revisionID = %d ORDER BY id DESC limit 1', $revision->getID()); $diff->attachChangesets($diff->loadChangesets()); foreach ($diff->getChangesets() as $changeset) { $paths[] = $changeset->getFilename(); } return $paths; } } diff --git a/src/applications/differential/conduit/DifferentialGetDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetDiffConduitAPIMethod.php index 65611e8c5b..4770821f78 100644 --- a/src/applications/differential/conduit/DifferentialGetDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetDiffConduitAPIMethod.php @@ -1,81 +1,81 @@ 'optional id', 'diff_id' => 'optional id', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_DIFF' => 'No such diff exists.', ); } protected function execute(ConduitAPIRequest $request) { $diff_id = $request->getValue('diff_id'); // If we have a revision ID, we need the most recent diff. Figure that out // without loading all the attached data. $revision_id = $request->getValue('revision_id'); if ($revision_id) { $diffs = id(new DifferentialDiffQuery()) ->setViewer($request->getUser()) ->withRevisionIDs(array($revision_id)) ->execute(); if ($diffs) { $diff_id = head($diffs)->getID(); } else { throw new ConduitException('ERR_BAD_DIFF'); } } $diff = null; if ($diff_id) { $diff = id(new DifferentialDiffQuery()) ->setViewer($request->getUser()) ->withIDs(array($diff_id)) ->needChangesets(true) ->needArcanistProjects(true) ->executeOne(); } if (!$diff) { throw new ConduitException('ERR_BAD_DIFF'); } return $diff->getDiffDict(); } } diff --git a/src/applications/differential/conduit/DifferentialGetRawDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetRawDiffConduitAPIMethod.php index 0181d1eb08..e5b6f25a82 100644 --- a/src/applications/differential/conduit/DifferentialGetRawDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetRawDiffConduitAPIMethod.php @@ -1,53 +1,53 @@ 'required diffID', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty string'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_NOT_FOUND' => pht('Diff not found.'), ); } protected function execute(ConduitAPIRequest $request) { $diff_id = $request->getValue('diffID'); $viewer = $request->getUser(); $diff = id(new DifferentialDiffQuery()) ->withIDs(array($diff_id)) ->setViewer($viewer) ->needChangesets(true) ->executeOne(); if (!$diff) { throw new ConduitException('ERR_NOT_FOUND'); } $renderer = id(new DifferentialRawDiffRenderer()) ->setChangesets($diff->getChangesets()) ->setViewer($viewer) ->setFormat('git'); return $renderer->buildPatch(); } } diff --git a/src/applications/differential/conduit/DifferentialGetRevisionCommentsConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetRevisionCommentsConduitAPIMethod.php index c470da0681..6a6770d3e8 100644 --- a/src/applications/differential/conduit/DifferentialGetRevisionCommentsConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetRevisionCommentsConduitAPIMethod.php @@ -1,94 +1,89 @@ 'required list', 'inlines' => 'optional bool (deprecated)', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list>'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $results = array(); $revision_ids = $request->getValue('ids'); if (!$revision_ids) { return $results; } $revisions = id(new DifferentialRevisionQuery()) ->setViewer($viewer) ->withIDs($revision_ids) ->execute(); if (!$revisions) { return $results; } $xactions = id(new DifferentialTransactionQuery()) ->setViewer($viewer) ->withObjectPHIDs(mpull($revisions, 'getPHID')) ->execute(); $revisions = mpull($revisions, null, 'getPHID'); foreach ($xactions as $xaction) { $revision = idx($revisions, $xaction->getObjectPHID()); if (!$revision) { continue; } $type = $xaction->getTransactionType(); if ($type == DifferentialTransaction::TYPE_ACTION) { $action = $xaction->getNewValue(); } else if ($type == PhabricatorTransactions::TYPE_COMMENT) { $action = 'comment'; } else { $action = 'none'; } $result = array( 'revisionID' => $revision->getID(), 'action' => $action, 'authorPHID' => $xaction->getAuthorPHID(), 'dateCreated' => $xaction->getDateCreated(), 'content' => ($xaction->hasComment() ? $xaction->getComment()->getContent() : null), ); $results[$revision->getID()][] = $result; } return $results; } } diff --git a/src/applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php index df0dfa5c9d..db0aeae83f 100644 --- a/src/applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php @@ -1,103 +1,103 @@ 'required id', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_REVISION' => 'No such revision exists.', ); } protected function execute(ConduitAPIRequest $request) { $diff = null; $revision_id = $request->getValue('revision_id'); $revision = id(new DifferentialRevisionQuery()) ->withIDs(array($revision_id)) ->setViewer($request->getUser()) ->needRelationships(true) ->needReviewerStatus(true) ->executeOne(); if (!$revision) { throw new ConduitException('ERR_BAD_REVISION'); } $reviewer_phids = array_values($revision->getReviewers()); $diffs = id(new DifferentialDiffQuery()) ->setViewer($request->getUser()) ->withRevisionIDs(array($revision_id)) ->needChangesets(true) ->needArcanistProjects(true) ->execute(); $diff_dicts = mpull($diffs, 'getDiffDict'); $commit_dicts = array(); $commit_phids = $revision->loadCommitPHIDs(); $handles = id(new PhabricatorHandleQuery()) ->setViewer($request->getUser()) ->withPHIDs($commit_phids) ->execute(); foreach ($commit_phids as $commit_phid) { $commit_dicts[] = array( 'fullname' => $handles[$commit_phid]->getFullName(), 'dateCommitted' => $handles[$commit_phid]->getTimestamp(), ); } $field_data = $this->loadCustomFieldsForRevisions( $request->getUser(), array($revision)); $dict = array( 'id' => $revision->getID(), 'phid' => $revision->getPHID(), 'authorPHID' => $revision->getAuthorPHID(), 'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()), 'title' => $revision->getTitle(), 'status' => $revision->getStatus(), 'statusName' => ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( $revision->getStatus()), 'summary' => $revision->getSummary(), 'testPlan' => $revision->getTestPlan(), 'lineCount' => $revision->getLineCount(), 'reviewerPHIDs' => $reviewer_phids, 'diffs' => $diff_dicts, 'commits' => $commit_dicts, 'auxiliary' => idx($field_data, $revision->getPHID(), array()), ); return $dict; } } diff --git a/src/applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php index 388fa364e0..107d22d2a9 100644 --- a/src/applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialParseCommitMessageConduitAPIMethod.php @@ -1,146 +1,142 @@ 'required string', 'partial' => 'optional bool', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $corpus = $request->getValue('corpus'); $is_partial = $request->getValue('partial'); $revision = new DifferentialRevision(); $field_list = PhabricatorCustomField::getObjectFields( $revision, DifferentialCustomField::ROLE_COMMITMESSAGE); $field_list->setViewer($viewer); $field_map = mpull($field_list->getFields(), null, 'getFieldKeyForConduit'); $this->errors = array(); $label_map = $this->buildLabelMap($field_list); $corpus_map = $this->parseCommitMessage($corpus, $label_map); $values = array(); foreach ($corpus_map as $field_key => $text_value) { $field = idx($field_map, $field_key); if (!$field) { throw new Exception( pht( 'Parser emitted text value for field key "%s", but no such '. 'field exists.', $field_key)); } try { $values[$field_key] = $field->parseValueFromCommitMessage($text_value); } catch (DifferentialFieldParseException $ex) { $this->errors[] = pht( 'Error parsing field "%s": %s', $field->renderCommitMessageLabel(), $ex->getMessage()); } } if (!$is_partial) { foreach ($field_map as $key => $field) { try { $field->validateCommitMessageValue(idx($values, $key)); } catch (DifferentialFieldValidationException $ex) { $this->errors[] = pht( 'Invalid or missing field "%s": %s', $field->renderCommitMessageLabel(), $ex->getMessage()); } } } // grab some extra information about the Differential Revision: field... $revision_id_field = new DifferentialRevisionIDField(); $revision_id_value = idx( $corpus_map, $revision_id_field->getFieldKeyForConduit()); $revision_id_valid_domain = PhabricatorEnv::getProductionURI(''); return array( 'errors' => $this->errors, 'fields' => $values, 'revisionIDFieldInfo' => array( 'value' => $revision_id_value, 'validDomain' => $revision_id_valid_domain, ), ); } private function buildLabelMap(PhabricatorCustomFieldList $field_list) { $label_map = array(); foreach ($field_list->getFields() as $key => $field) { $labels = $field->getCommitMessageLabels(); $key = $field->getFieldKeyForConduit(); foreach ($labels as $label) { $normal_label = DifferentialCommitMessageParser::normalizeFieldLabel( $label); if (!empty($label_map[$normal_label])) { throw new Exception( pht( 'Field label "%s" is parsed by two custom fields: "%s" and '. '"%s". Each label must be parsed by only one field.', $label, $key, $label_map[$normal_label])); } $label_map[$normal_label] = $key; } } return $label_map; } private function parseCommitMessage($corpus, array $label_map) { $key_title = id(new DifferentialTitleField())->getFieldKeyForConduit(); $key_summary = id(new DifferentialSummaryField())->getFieldKeyForConduit(); $parser = id(new DifferentialCommitMessageParser()) ->setLabelMap($label_map) ->setTitleKey($key_title) ->setSummaryKey($key_summary); $result = $parser->parseCorpus($corpus); foreach ($parser->getErrors() as $error) { $this->errors[] = $error; } return $result; } } diff --git a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php index 1d4882447d..04af9ff234 100644 --- a/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialQueryConduitAPIMethod.php @@ -1,247 +1,247 @@ formatStringConstants($hash_types); $status_types = array( DifferentialRevisionQuery::STATUS_ANY, DifferentialRevisionQuery::STATUS_OPEN, DifferentialRevisionQuery::STATUS_ACCEPTED, DifferentialRevisionQuery::STATUS_CLOSED, ); $status_const = $this->formatStringConstants($status_types); $order_types = array( DifferentialRevisionQuery::ORDER_MODIFIED, DifferentialRevisionQuery::ORDER_CREATED, ); $order_const = $this->formatStringConstants($order_types); return array( 'authors' => 'optional list', 'ccs' => 'optional list', 'reviewers' => 'optional list', 'paths' => 'optional list>', 'commitHashes' => 'optional list>', 'status' => 'optional '.$status_const, 'order' => 'optional '.$order_const, 'limit' => 'optional uint', 'offset' => 'optional uint', 'ids' => 'optional list', 'phids' => 'optional list', 'subscribers' => 'optional list', 'responsibleUsers' => 'optional list', 'branches' => 'optional list', 'arcanistProjects' => 'optional list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-INVALID-PARAMETER' => 'Missing or malformed parameter.', ); } protected function execute(ConduitAPIRequest $request) { $authors = $request->getValue('authors'); $ccs = $request->getValue('ccs'); $reviewers = $request->getValue('reviewers'); $status = $request->getValue('status'); $order = $request->getValue('order'); $path_pairs = $request->getValue('paths'); $commit_hashes = $request->getValue('commitHashes'); $limit = $request->getValue('limit'); $offset = $request->getValue('offset'); $ids = $request->getValue('ids'); $phids = $request->getValue('phids'); $subscribers = $request->getValue('subscribers'); $responsible_users = $request->getValue('responsibleUsers'); $branches = $request->getValue('branches'); $arc_projects = $request->getValue('arcanistProjects'); $query = id(new DifferentialRevisionQuery()) ->setViewer($request->getUser()); if ($authors) { $query->withAuthors($authors); } if ($ccs) { $query->withCCs($ccs); } if ($reviewers) { $query->withReviewers($reviewers); } if ($path_pairs) { $paths = array(); foreach ($path_pairs as $pair) { list($callsign, $path) = $pair; $paths[] = $path; } $path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs(); if (count($path_map) != count($paths)) { $unknown_paths = array(); foreach ($paths as $p) { if (!idx($path_map, $p)) { $unknown_paths[] = $p; } } throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription( 'Unknown paths: '.implode(', ', $unknown_paths)); } $repos = array(); foreach ($path_pairs as $pair) { list($callsign, $path) = $pair; if (!idx($repos, $callsign)) { $repos[$callsign] = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->withCallsigns(array($callsign)) ->executeOne(); if (!$repos[$callsign]) { throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription( 'Unknown repo callsign: '.$callsign); } } $repo = $repos[$callsign]; $query->withPath($repo->getID(), idx($path_map, $path)); } } if ($commit_hashes) { $hash_types = ArcanistDifferentialRevisionHash::getTypes(); foreach ($commit_hashes as $info) { list($type, $hash) = $info; if (empty($type) || !in_array($type, $hash_types) || empty($hash)) { throw new ConduitException('ERR-INVALID-PARAMETER'); } } $query->withCommitHashes($commit_hashes); } if ($status) { $query->withStatus($status); } if ($order) { $query->setOrder($order); } if ($limit) { $query->setLimit($limit); } if ($offset) { $query->setOffset($offset); } if ($ids) { $query->withIDs($ids); } if ($phids) { $query->withPHIDs($phids); } if ($responsible_users) { $query->withResponsibleUsers($responsible_users); } if ($subscribers) { $query->withCCs($subscribers); } if ($branches) { $query->withBranches($branches); } if ($arc_projects) { // This is sort of special-cased, but don't make arc do an extra round // trip. $projects = id(new PhabricatorRepositoryArcanistProject()) ->loadAllWhere( 'name in (%Ls)', $arc_projects); if (!$projects) { return array(); } $query->withArcanistProjectPHIDs(mpull($projects, 'getPHID')); } $query->needRelationships(true); $query->needCommitPHIDs(true); $query->needDiffIDs(true); $query->needActiveDiffs(true); $query->needHashes(true); $revisions = $query->execute(); $field_data = $this->loadCustomFieldsForRevisions( $request->getUser(), $revisions); $results = array(); foreach ($revisions as $revision) { $diff = $revision->getActiveDiff(); if (!$diff) { continue; } $id = $revision->getID(); $phid = $revision->getPHID(); $result = array( 'id' => $id, 'phid' => $phid, 'title' => $revision->getTitle(), 'uri' => PhabricatorEnv::getProductionURI('/D'.$id), 'dateCreated' => $revision->getDateCreated(), 'dateModified' => $revision->getDateModified(), 'authorPHID' => $revision->getAuthorPHID(), 'status' => $revision->getStatus(), 'statusName' => ArcanistDifferentialRevisionStatus::getNameForRevisionStatus( $revision->getStatus()), 'branch' => $diff->getBranch(), 'summary' => $revision->getSummary(), 'testPlan' => $revision->getTestPlan(), 'lineCount' => $revision->getLineCount(), 'activeDiffPHID' => $diff->getPHID(), 'diffs' => $revision->getDiffIDs(), 'commits' => $revision->getCommitPHIDs(), 'reviewers' => array_values($revision->getReviewers()), 'ccs' => array_values($revision->getCCPHIDs()), 'hashes' => $revision->getHashes(), 'auxiliary' => idx($field_data, $phid, array()), 'arcanistProjectPHID' => $diff->getArcanistProjectPHID(), 'repositoryPHID' => $diff->getRepositoryPHID(), ); // TODO: This is a hacky way to put permissions on this field until we // have first-class support, see T838. if ($revision->getAuthorPHID() == $request->getUser()->getPHID()) { $result['sourcePath'] = $diff->getSourcePath(); } $results[] = $result; } return $results; } } diff --git a/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php index 398a3176a2..630f07da2b 100644 --- a/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialQueryDiffsConduitAPIMethod.php @@ -1,47 +1,43 @@ 'optional list', 'revisionIDs' => 'optional list', ); } - public function defineErrorTypes() { - return array(); - } - - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } protected function execute(ConduitAPIRequest $request) { $ids = $request->getValue('ids', array()); $revision_ids = $request->getValue('revisionIDs', array()); $diffs = array(); if ($ids || $revision_ids) { $diffs = id(new DifferentialDiffQuery()) ->setViewer($request->getUser()) ->withIDs($ids) ->withRevisionIDs($revision_ids) ->needChangesets(true) ->needArcanistProjects(true) ->execute(); } return mpull($diffs, 'getDiffDict', 'getID'); } } diff --git a/src/applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php index d392ce374d..d79be1363d 100644 --- a/src/applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php @@ -1,115 +1,115 @@ 'required diff_id', 'name' => 'required string', 'data' => 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_NOT_FOUND' => 'Diff was not found.', ); } private static function updateLintStatus($diff_id) { $diff = id(new DifferentialDiff())->load($diff_id); if (!$diff) { throw new ConduitException('ERR_NOT_FOUND'); } // Load the postponed linters attached to this diff. $postponed_linters_property = id( new DifferentialDiffProperty())->loadOneWhere( 'diffID = %d AND name = %s', $diff_id, 'arc:lint-postponed'); if ($postponed_linters_property) { $postponed_linters = $postponed_linters_property->getData(); } else { $postponed_linters = array(); } // Load the lint messages currenty attached to the diff $messages_property = id(new DifferentialDiffProperty())->loadOneWhere( 'diffID = %d AND name = %s', $diff_id, 'arc:lint'); if ($messages_property) { $results = $messages_property->getData(); } else { $results = array(); } $has_error = false; $has_warning = false; foreach ($results as $result) { if ($result['severity'] === ArcanistLintSeverity::SEVERITY_ERROR) { $has_error = true; break; } else if ($result['severity'] === ArcanistLintSeverity::SEVERITY_WARNING) { $has_warning = true; } } if ($has_error) { $diff->setLintStatus(DifferentialLintStatus::LINT_FAIL); } else if ($has_warning) { $diff->setLintStatus(DifferentialLintStatus::LINT_WARN); } else if (!empty($postponed_linters)) { $diff->setLintStatus(DifferentialLintStatus::LINT_POSTPONED); } else if ($diff->getLintStatus() != DifferentialLintStatus::LINT_SKIP) { $diff->setLintStatus(DifferentialLintStatus::LINT_OKAY); } $diff->save(); } protected function execute(ConduitAPIRequest $request) { $diff_id = $request->getValue('diff_id'); $name = $request->getValue('name'); $data = json_decode($request->getValue('data'), true); self::updateDiffProperty($diff_id, $name, $data); if ($name === 'arc:lint' || $name == 'arc:lint-postponed') { self::updateLintStatus($diff_id); } return; } private static function updateDiffProperty($diff_id, $name, $data) { $property = id(new DifferentialDiffProperty())->loadOneWhere( 'diffID = %d AND name = %s', $diff_id, $name); if (!$property) { $property = new DifferentialDiffProperty(); $property->setDiffID($diff_id); $property->setName($name); } $property->setData($data); $property->save(); return $property; } } diff --git a/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php index 6a328814fb..e8a34fd133 100644 --- a/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php @@ -1,79 +1,79 @@ 'required revisionid', 'diffid' => 'required diffid', 'fields' => 'required dict', 'message' => 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_DIFF' => 'Bad diff ID.', 'ERR_BAD_REVISION' => 'Bad revision ID.', 'ERR_WRONG_USER' => 'You are not the author of this revision.', 'ERR_CLOSED' => 'This revision has already been closed.', ); } protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $diff = id(new DifferentialDiffQuery()) ->setViewer($viewer) ->withIDs(array($request->getValue('diffid'))) ->executeOne(); if (!$diff) { throw new ConduitException('ERR_BAD_DIFF'); } $revision = id(new DifferentialRevisionQuery()) ->setViewer($request->getUser()) ->withIDs(array($request->getValue('id'))) ->needReviewerStatus(true) ->needActiveDiffs(true) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$revision) { throw new ConduitException('ERR_BAD_REVISION'); } if ($revision->getStatus() == ArcanistDifferentialRevisionStatus::CLOSED) { throw new ConduitException('ERR_CLOSED'); } $this->applyFieldEdit( $request, $revision, $diff, $request->getValue('fields', array()), $request->getValue('message')); return array( 'revisionid' => $revision->getID(), 'uri' => PhabricatorEnv::getURI('/D'.$revision->getID()), ); } } diff --git a/src/applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php index 90dc59761a..0f0c14a6b8 100644 --- a/src/applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php @@ -1,155 +1,155 @@ 'required diff_id', 'file' => 'required string', 'name' => 'required string', 'link' => 'optional string', 'result' => 'required string', 'message' => 'required string', 'coverage' => 'optional map', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_DIFF' => 'Bad diff ID.', 'ERR_NO_RESULTS' => 'Could not find the postponed test', ); } protected function execute(ConduitAPIRequest $request) { $diff_id = $request->getValue('diff_id'); if (!$diff_id) { throw new ConduitException('ERR_BAD_DIFF'); } $file = $request->getValue('file'); $name = $request->getValue('name'); $link = $request->getValue('link'); $message = $request->getValue('message'); $result = $request->getValue('result'); $coverage = $request->getValue('coverage', array()); $diff_property = id(new DifferentialDiffProperty())->loadOneWhere( 'diffID = %d AND name = %s', $diff_id, 'arc:unit'); if (!$diff_property) { throw new ConduitException('ERR_NO_RESULTS'); } $diff = id(new DifferentialDiffQuery()) ->setViewer($request->getUser()) ->withIDs(array($diff_id)) ->executeOne(); $unit_results = $diff_property->getData(); $postponed_count = 0; $unit_status = null; // If the test result already exists, then update it with // the new info. foreach ($unit_results as &$unit_result) { if ($unit_result['name'] === $name || $unit_result['name'] === $file || $unit_result['name'] === $diff->getSourcePath().$file) { $unit_result['name'] = $name; $unit_result['link'] = $link; $unit_result['file'] = $file; $unit_result['result'] = $result; $unit_result['userdata'] = $message; $unit_result['coverage'] = $coverage; $unit_status = $result; break; } } unset($unit_result); // If the test result doesn't exist, just add it. if (!$unit_status) { $unit_result = array(); $unit_result['file'] = $file; $unit_result['name'] = $name; $unit_result['link'] = $link; $unit_result['result'] = $result; $unit_result['userdata'] = $message; $unit_result['coverage'] = $coverage; $unit_status = $result; $unit_results[] = $unit_result; } unset($unit_result); $diff_property->setData($unit_results); $diff_property->save(); // Map external unit test status to internal overall diff status $status_codes = array( DifferentialUnitTestResult::RESULT_PASS => DifferentialUnitStatus::UNIT_OKAY, DifferentialUnitTestResult::RESULT_UNSOUND => DifferentialUnitStatus::UNIT_WARN, DifferentialUnitTestResult::RESULT_FAIL => DifferentialUnitStatus::UNIT_FAIL, DifferentialUnitTestResult::RESULT_BROKEN => DifferentialUnitStatus::UNIT_FAIL, DifferentialUnitTestResult::RESULT_SKIP => DifferentialUnitStatus::UNIT_OKAY, DifferentialUnitTestResult::RESULT_POSTPONED => DifferentialUnitStatus::UNIT_POSTPONED, ); // These are the relative priorities for the unit test results $status_codes_priority = array( DifferentialUnitStatus::UNIT_OKAY => 1, DifferentialUnitStatus::UNIT_WARN => 2, DifferentialUnitStatus::UNIT_POSTPONED => 3, DifferentialUnitStatus::UNIT_FAIL => 4, ); // Walk the now-current list of status codes to find the overall diff // status $final_diff_status = DifferentialUnitStatus::UNIT_NONE; foreach ($unit_results as $unit_result) { // Convert the text result into a diff unit status value $status_code = idx($status_codes, $unit_result['result'], DifferentialUnitStatus::UNIT_NONE); // Convert the unit status into a relative value $diff_status_priority = idx($status_codes_priority, $status_code, 0); // If the relative value of this result is "more bad" than previous // results, use it as the new final diff status if ($diff_status_priority > idx($status_codes_priority, $final_diff_status, 0)) { $final_diff_status = $status_code; } } // Update our unit test result status with the final value $diff->setUnitStatus($final_diff_status); $diff->save(); } } diff --git a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php index d0c4d9c484..1271088b1b 100644 --- a/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBranchQueryConduitAPIMethod.php @@ -1,114 +1,114 @@ '; } protected function defineCustomParamTypes() { return array( 'limit' => 'optional int', 'offset' => 'optional int', 'contains' => 'optional string', ); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $contains = $request->getValue('contains'); if (strlen($contains)) { // NOTE: We can't use DiffusionLowLevelGitRefQuery here because // `git for-each-ref` does not support `--contains`. if ($repository->isWorkingCopyBare()) { list($stdout) = $repository->execxLocalCommand( 'branch --verbose --no-abbrev --contains %s --', $contains); $ref_map = DiffusionGitBranch::parseLocalBranchOutput( $stdout); } else { list($stdout) = $repository->execxLocalCommand( 'branch -r --verbose --no-abbrev --contains %s --', $contains); $ref_map = DiffusionGitBranch::parseRemoteBranchOutput( $stdout, DiffusionGitBranch::DEFAULT_GIT_REMOTE); } $refs = array(); foreach ($ref_map as $ref => $commit) { $refs[] = id(new DiffusionRepositoryRef()) ->setShortName($ref) ->setCommitIdentifier($commit); } } else { $refs = id(new DiffusionLowLevelGitRefQuery()) ->setRepository($repository) ->withIsOriginBranch(true) ->execute(); } return $this->processBranchRefs($request, $refs); } protected function getMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $query = id(new DiffusionLowLevelMercurialBranchesQuery()) ->setRepository($repository); $contains = $request->getValue('contains'); if (strlen($contains)) { $query->withContainsCommit($contains); } $refs = $query->execute(); return $this->processBranchRefs($request, $refs); } protected function getSVNResult(ConduitAPIRequest $request) { // Since SVN doesn't have meaningful branches, just return nothing for all // queries. return array(); } private function processBranchRefs(ConduitAPIRequest $request, array $refs) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); foreach ($refs as $key => $ref) { if (!$repository->shouldTrackBranch($ref->getShortName())) { unset($refs[$key]); } } // NOTE: We can't apply the offset or limit until here, because we may have // filtered untrackable branches out of the result set. if ($offset) { $refs = array_slice($refs, $offset); } if ($limit) { $refs = array_slice($refs, 0, $limit); } return mpull($refs, 'toDictionary'); } } diff --git a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php index ab20f13d02..f8854ac0f2 100644 --- a/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionBrowseQueryConduitAPIMethod.php @@ -1,469 +1,469 @@ 'optional string', 'commit' => 'optional string', 'needValidityOnly' => 'optional bool', ); } protected function getResult(ConduitAPIRequest $request) { $result = parent::getResult($request); return $result->toDictionary(); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $path = $request->getValue('path'); $commit = $request->getValue('commit'); $result = $this->getEmptyResultSet(); if ($path == '') { // Fast path to improve the performance of the repository view; we know // the root is always a tree at any commit and always exists. $stdout = 'tree'; } else { try { list($stdout) = $repository->execxLocalCommand( 'cat-file -t %s:%s', $commit, $path); } catch (CommandException $e) { $stderr = $e->getStdErr(); if (preg_match('/^fatal: Not a valid object name/', $stderr)) { // Grab two logs, since the first one is when the object was deleted. list($stdout) = $repository->execxLocalCommand( 'log -n2 --format="%%H" %s -- %s', $commit, $path); $stdout = trim($stdout); if ($stdout) { $commits = explode("\n", $stdout); $result ->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_DELETED) ->setDeletedAtCommit(idx($commits, 0)) ->setExistedAtCommit(idx($commits, 1)); return $result; } $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_NONEXISTENT); return $result; } else { throw $e; } } } if (trim($stdout) == 'blob') { $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_FILE); return $result; } $result->setIsValidResults(true); if ($this->shouldOnlyTestValidity($request)) { return $result; } list($stdout) = $repository->execxLocalCommand( 'ls-tree -z -l %s:%s', $commit, $path); $submodules = array(); if (strlen($path)) { $prefix = rtrim($path, '/').'/'; } else { $prefix = ''; } $results = array(); foreach (explode("\0", rtrim($stdout)) as $line) { // NOTE: Limit to 5 components so we parse filenames with spaces in them // correctly. // NOTE: The output uses a mixture of tabs and one-or-more spaces to // delimit fields. $parts = preg_split('/\s+/', $line, 5); if (count($parts) < 5) { throw new Exception( pht( 'Expected " \t", for ls-tree of '. '"%s:%s", got: %s', $commit, $path, $line)); } list($mode, $type, $hash, $size, $name) = $parts; $path_result = new DiffusionRepositoryPath(); if ($type == 'tree') { $file_type = DifferentialChangeType::FILE_DIRECTORY; } else if ($type == 'commit') { $file_type = DifferentialChangeType::FILE_SUBMODULE; $submodules[] = $path_result; } else { $mode = intval($mode, 8); if (($mode & 0120000) == 0120000) { $file_type = DifferentialChangeType::FILE_SYMLINK; } else { $file_type = DifferentialChangeType::FILE_NORMAL; } } $path_result->setFullPath($prefix.$name); $path_result->setPath($name); $path_result->setHash($hash); $path_result->setFileType($file_type); $path_result->setFileSize($size); $results[] = $path_result; } // If we identified submodules, lookup the module info at this commit to // find their source URIs. if ($submodules) { // NOTE: We need to read the file out of git and write it to a temporary // location because "git config -f" doesn't accept a "commit:path"-style // argument. // NOTE: This file may not exist, e.g. because the commit author removed // it when they added the submodule. See T1448. If it's not present, just // show the submodule without enriching it. If ".gitmodules" was removed // it seems to partially break submodules, but the repository as a whole // continues to work fine and we've seen at least two cases of this in // the wild. list($err, $contents) = $repository->execLocalCommand( 'cat-file blob %s:.gitmodules', $commit); if (!$err) { $tmp = new TempFile(); Filesystem::writeFile($tmp, $contents); list($module_info) = $repository->execxLocalCommand( 'config -l -f %s', $tmp); $dict = array(); $lines = explode("\n", trim($module_info)); foreach ($lines as $line) { list($key, $value) = explode('=', $line, 2); $parts = explode('.', $key); $dict[$key] = $value; } foreach ($submodules as $path) { $full_path = $path->getFullPath(); $key = 'submodule.'.$full_path.'.url'; if (isset($dict[$key])) { $path->setExternalURI($dict[$key]); } } } } return $result->setPaths($results); } protected function getMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $path = $request->getValue('path'); $commit = $request->getValue('commit'); $result = $this->getEmptyResultSet(); $entire_manifest = id(new DiffusionLowLevelMercurialPathsQuery()) ->setRepository($repository) ->withCommit($commit) ->withPath($path) ->execute(); $results = array(); $match_against = trim($path, '/'); $match_len = strlen($match_against); // For the root, don't trim. For other paths, trim the "/" after we match. // We need this because Mercurial's canonical paths have no leading "/", // but ours do. $trim_len = $match_len ? $match_len + 1 : 0; foreach ($entire_manifest as $path) { if (strncmp($path, $match_against, $match_len)) { continue; } if (!strlen($path)) { continue; } $remainder = substr($path, $trim_len); if (!strlen($remainder)) { // There is a file with this exact name in the manifest, so clearly // it's a file. $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_FILE); return $result; } $parts = explode('/', $remainder); if (count($parts) == 1) { $type = DifferentialChangeType::FILE_NORMAL; } else { $type = DifferentialChangeType::FILE_DIRECTORY; } $results[reset($parts)] = $type; } foreach ($results as $key => $type) { $path_result = new DiffusionRepositoryPath(); $path_result->setPath($key); $path_result->setFileType($type); $path_result->setFullPath(ltrim($match_against.'/', '/').$key); $results[$key] = $path_result; } $valid_results = true; if (empty($results)) { // TODO: Detect "deleted" by issuing "hg log"? $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_NONEXISTENT); $valid_results = false; } return $result ->setPaths($results) ->setIsValidResults($valid_results); } protected function getSVNResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $path = $request->getValue('path'); $commit = $request->getValue('commit'); $result = $this->getEmptyResultSet(); $subpath = $repository->getDetail('svn-subpath'); if ($subpath && strncmp($subpath, $path, strlen($subpath))) { // If we have a subpath and the path isn't a child of it, it (almost // certainly) won't exist since we don't track commits which affect // it. (Even if it exists, return a consistent result.) $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_UNTRACKED_PARENT); return $result; } $conn_r = $repository->establishConnection('r'); $parent_path = DiffusionPathIDQuery::getParentPath($path); $path_query = new DiffusionPathIDQuery( array( $path, $parent_path, )); $path_map = $path_query->loadPathIDs(); $path_id = $path_map[$path]; $parent_path_id = $path_map[$parent_path]; if (empty($path_id)) { $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_NONEXISTENT); return $result; } if ($commit) { $slice_clause = 'AND svnCommit <= '.(int)$commit; } else { $slice_clause = ''; } $index = queryfx_all( $conn_r, 'SELECT pathID, max(svnCommit) maxCommit FROM %T WHERE repositoryID = %d AND parentID = %d %Q GROUP BY pathID', PhabricatorRepository::TABLE_FILESYSTEM, $repository->getID(), $path_id, $slice_clause); if (!$index) { if ($path == '/') { $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_EMPTY); } else { // NOTE: The parent path ID is included so this query can take // advantage of the table's primary key; it is uniquely determined by // the pathID but if we don't do the lookup ourselves MySQL doesn't have // the information it needs to avoid a table scan. $reasons = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE repositoryID = %d AND parentID = %d AND pathID = %d %Q ORDER BY svnCommit DESC LIMIT 2', PhabricatorRepository::TABLE_FILESYSTEM, $repository->getID(), $parent_path_id, $path_id, $slice_clause); $reason = reset($reasons); if (!$reason) { $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_NONEXISTENT); } else { $file_type = $reason['fileType']; if (empty($reason['existed'])) { $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_DELETED); $result->setDeletedAtCommit($reason['svnCommit']); if (!empty($reasons[1])) { $result->setExistedAtCommit($reasons[1]['svnCommit']); } } else if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_EMPTY); } else { $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_FILE); } } } return $result; } $result->setIsValidResults(true); if ($this->shouldOnlyTestValidity($request)) { return $result; } $sql = array(); foreach ($index as $row) { $sql[] = '(pathID = '.(int)$row['pathID'].' AND '. 'svnCommit = '.(int)$row['maxCommit'].')'; } $browse = queryfx_all( $conn_r, 'SELECT *, p.path pathName FROM %T f JOIN %T p ON f.pathID = p.id WHERE repositoryID = %d AND parentID = %d AND existed = 1 AND (%Q) ORDER BY pathName', PhabricatorRepository::TABLE_FILESYSTEM, PhabricatorRepository::TABLE_PATH, $repository->getID(), $path_id, implode(' OR ', $sql)); $loadable_commits = array(); foreach ($browse as $key => $file) { // We need to strip out directories because we don't store last-modified // in the filesystem table. if ($file['fileType'] != DifferentialChangeType::FILE_DIRECTORY) { $loadable_commits[] = $file['svnCommit']; $browse[$key]['hasCommit'] = true; } } $commits = array(); $commit_data = array(); if ($loadable_commits) { // NOTE: Even though these are integers, use '%Ls' because MySQL doesn't // use the second part of the key otherwise! $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( 'repositoryID = %d AND commitIdentifier IN (%Ls)', $repository->getID(), $loadable_commits); $commits = mpull($commits, null, 'getCommitIdentifier'); if ($commits) { $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 'commitID in (%Ld)', mpull($commits, 'getID')); $commit_data = mpull($commit_data, null, 'getCommitID'); } else { $commit_data = array(); } } $path_normal = DiffusionPathIDQuery::normalizePath($path); $results = array(); foreach ($browse as $file) { $full_path = $file['pathName']; $file_path = ltrim(substr($full_path, strlen($path_normal)), '/'); $full_path = ltrim($full_path, '/'); $result_path = new DiffusionRepositoryPath(); $result_path->setPath($file_path); $result_path->setFullPath($full_path); // $result_path->setHash($hash); $result_path->setFileType($file['fileType']); // $result_path->setFileSize($size); if (!empty($file['hasCommit'])) { $commit = idx($commits, $file['svnCommit']); if ($commit) { $data = idx($commit_data, $commit->getID()); $result_path->setLastModifiedCommit($commit); $result_path->setLastCommitData($data); } } $results[] = $result_path; } if (empty($results)) { $result->setReasonForEmptyResultSet( DiffusionBrowseResultSet::REASON_IS_EMPTY); } return $result->setPaths($results); } private function getEmptyResultSet() { return id(new DiffusionBrowseResultSet()) ->setPaths(array()) ->setReasonForEmptyResultSet(null) ->setIsValidResults(false); } private function shouldOnlyTestValidity(ConduitAPIRequest $request) { return $request->getValue('needValidityOnly', false); } } diff --git a/src/applications/diffusion/conduit/DiffusionCommitParentsQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionCommitParentsQueryConduitAPIMethod.php index c6a3220747..28b08a2c6d 100644 --- a/src/applications/diffusion/conduit/DiffusionCommitParentsQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionCommitParentsQueryConduitAPIMethod.php @@ -1,34 +1,34 @@ '; } protected function defineCustomParamTypes() { return array( 'commit' => 'required string', ); } protected function getResult(ConduitAPIRequest $request) { $repository = $this->getRepository($request); return id(new DiffusionLowLevelParentsQuery()) ->setRepository($repository) ->withIdentifier($request->getValue('commit')) ->execute(); } } diff --git a/src/applications/diffusion/conduit/DiffusionCreateCommentConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionCreateCommentConduitAPIMethod.php index e0e3927f93..8d978e3f25 100644 --- a/src/applications/diffusion/conduit/DiffusionCreateCommentConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionCreateCommentConduitAPIMethod.php @@ -1,100 +1,100 @@ 'required string', 'action' => 'optional string', 'message' => 'required string', 'silent' => 'optional bool', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'bool'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_COMMIT' => 'No commit found with that PHID', 'ERR_BAD_ACTION' => 'Invalid action type', 'ERR_MISSING_MESSAGE' => 'Message is required', ); } protected function execute(ConduitAPIRequest $request) { $commit_phid = $request->getValue('phid'); $commit = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()) ->withPHIDs(array($commit_phid)) ->needAuditRequests(true) ->executeOne(); if (!$commit) { throw new ConduitException('ERR_BAD_COMMIT'); } $message = trim($request->getValue('message')); if (!$message) { throw new ConduitException('ERR_MISSING_MESSAGE'); } $action = $request->getValue('action'); if (!$action) { $action = PhabricatorAuditActionConstants::COMMENT; } // Disallow ADD_CCS, ADD_AUDITORS forever. if (!in_array($action, array( PhabricatorAuditActionConstants::CONCERN, PhabricatorAuditActionConstants::ACCEPT, PhabricatorAuditActionConstants::COMMENT, PhabricatorAuditActionConstants::RESIGN, PhabricatorAuditActionConstants::CLOSE, ))) { throw new ConduitException('ERR_BAD_ACTION'); } $xactions = array(); if ($action != PhabricatorAuditActionConstants::COMMENT) { $xactions[] = id(new PhabricatorAuditTransaction()) ->setTransactionType(PhabricatorAuditActionConstants::ACTION) ->setNewValue($action); } if (strlen($message)) { $xactions[] = id(new PhabricatorAuditTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new PhabricatorAuditTransactionComment()) ->setCommitPHID($commit->getPHID()) ->setContent($message)); } id(new PhabricatorAuditEditor()) ->setActor($request->getUser()) ->setContentSourceFromConduitRequest($request) ->setDisableEmail($request->getValue('silent')) ->setContinueOnMissingFields(true) ->applyTransactions($commit, $xactions); return true; } } diff --git a/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php index 4dc2a2bac9..0ec00bc762 100644 --- a/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionDiffQueryConduitAPIMethod.php @@ -1,239 +1,239 @@ 'required string', 'commit' => 'optional string', ); } protected function getResult(ConduitAPIRequest $request) { $result = parent::getResult($request); return array( 'changes' => mpull($result, 'toDictionary'), 'effectiveCommit' => $this->getEffectiveCommit($request), ); } protected function getGitResult(ConduitAPIRequest $request) { return $this->getGitOrMercurialResult($request); } protected function getMercurialResult(ConduitAPIRequest $request) { return $this->getGitOrMercurialResult($request); } /** * NOTE: We have to work particularly hard for SVN as compared to other VCS. * That's okay but means this shares little code with the other VCS. */ protected function getSVNResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $effective_commit = $this->getEffectiveCommit($request); if (!$effective_commit) { return $this->getEmptyResult(); } $drequest = clone $drequest; $drequest->updateSymbolicCommit($effective_commit); $path_change_query = DiffusionPathChangeQuery::newFromDiffusionRequest( $drequest); $path_changes = $path_change_query->loadChanges(); $path = null; foreach ($path_changes as $change) { if ($change->getPath() == $drequest->getPath()) { $path = $change; } } if (!$path) { return $this->getEmptyResult(); } $change_type = $path->getChangeType(); switch ($change_type) { case DifferentialChangeType::TYPE_MULTICOPY: case DifferentialChangeType::TYPE_DELETE: if ($path->getTargetPath()) { $old = array( $path->getTargetPath(), $path->getTargetCommitIdentifier(), ); } else { $old = array($path->getPath(), $path->getCommitIdentifier() - 1); } $old_name = $path->getPath(); $new_name = ''; $new = null; break; case DifferentialChangeType::TYPE_ADD: $old = null; $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = ''; $new_name = $path->getPath(); break; case DifferentialChangeType::TYPE_MOVE_HERE: case DifferentialChangeType::TYPE_COPY_HERE: $old = array( $path->getTargetPath(), $path->getTargetCommitIdentifier(), ); $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = $path->getTargetPath(); $new_name = $path->getPath(); break; case DifferentialChangeType::TYPE_MOVE_AWAY: $old = array( $path->getPath(), $path->getCommitIdentifier() - 1, ); $old_name = $path->getPath(); $new_name = null; $new = null; break; default: $old = array($path->getPath(), $path->getCommitIdentifier() - 1); $new = array($path->getPath(), $path->getCommitIdentifier()); $old_name = $path->getPath(); $new_name = $path->getPath(); break; } $futures = array( 'old' => $this->buildSVNContentFuture($old), 'new' => $this->buildSVNContentFuture($new), ); $futures = array_filter($futures); foreach (new FutureIterator($futures) as $key => $future) { $stdout = ''; try { list($stdout) = $future->resolvex(); } catch (CommandException $e) { if ($path->getFileType() != DifferentialChangeType::FILE_DIRECTORY) { throw $e; } } $futures[$key] = $stdout; } $old_data = idx($futures, 'old', ''); $new_data = idx($futures, 'new', ''); $engine = new PhabricatorDifferenceEngine(); $engine->setOldName($old_name); $engine->setNewName($new_name); $raw_diff = $engine->generateRawDiffFromFileContent($old_data, $new_data); $arcanist_changes = DiffusionPathChange::convertToArcanistChanges( $path_changes); $parser = $this->getDefaultParser(); $parser->setChanges($arcanist_changes); $parser->forcePath($path->getPath()); $changes = $parser->parseDiff($raw_diff); $change = $changes[$path->getPath()]; return array($change); } private function getEffectiveCommit(ConduitAPIRequest $request) { if ($this->effectiveCommit === null) { $drequest = $this->getDiffusionRequest(); $path = $drequest->getPath(); $result = DiffusionQuery::callConduitWithDiffusionRequest( $request->getUser(), $drequest, 'diffusion.lastmodifiedquery', array( 'paths' => array($path => $drequest->getStableCommit()), )); $this->effectiveCommit = idx($result, $path); } return $this->effectiveCommit; } private function buildSVNContentFuture($spec) { if (!$spec) { return null; } $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); list($ref, $rev) = $spec; return $repository->getRemoteCommandFuture( 'cat %s', $repository->getSubversionPathURI($ref, $rev)); } private function getGitOrMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $effective_commit = $this->getEffectiveCommit($request); if (!$effective_commit) { return $this->getEmptyResult(1); } $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest) ->setAnchorCommit($effective_commit); $raw_diff = $raw_query->loadRawDiff(); if (!$raw_diff) { return $this->getEmptyResult(2); } $parser = $this->getDefaultParser(); $changes = $parser->parseDiff($raw_diff); return $changes; } private function getDefaultParser() { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $parser = new ArcanistDiffParser(); $try_encoding = $repository->getDetail('encoding'); if ($try_encoding) { $parser->setTryEncoding($try_encoding); } $parser->setDetectBinaryFiles(true); return $parser; } private function getEmptyResult() { return array(); } } diff --git a/src/applications/diffusion/conduit/DiffusionExistsQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionExistsQueryConduitAPIMethod.php index 9728c2f1f5..d0d5453a34 100644 --- a/src/applications/diffusion/conduit/DiffusionExistsQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionExistsQueryConduitAPIMethod.php @@ -1,57 +1,57 @@ 'required string', ); } protected function getGitResult(ConduitAPIRequest $request) { $repository = $this->getDiffusionRequest()->getRepository(); $commit = $request->getValue('commit'); list($err, $merge_base) = $repository->execLocalCommand( 'cat-file -t %s', $commit); return !$err; } protected function getSVNResult(ConduitAPIRequest $request) { $repository = $this->getDiffusionRequest()->getRepository(); $commit = $request->getValue('commit'); list($info) = $repository->execxRemoteCommand( 'info %s', $repository->getRemoteURI()); $exists = false; $matches = null; if (preg_match('/^Revision: (\d+)$/m', $info, $matches)) { $base_revision = $matches[1]; $exists = $base_revision >= $commit; } return $exists; } protected function getMercurialResult(ConduitAPIRequest $request) { $repository = $this->getDiffusionRequest()->getRepository(); $commit = $request->getValue('commit'); list($err, $stdout) = $repository->execLocalCommand( 'id --rev %s', $commit); return !$err; } } diff --git a/src/applications/diffusion/conduit/DiffusionFileContentQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionFileContentQueryConduitAPIMethod.php index 0afd3bcb25..9204a84bbc 100644 --- a/src/applications/diffusion/conduit/DiffusionFileContentQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionFileContentQueryConduitAPIMethod.php @@ -1,47 +1,47 @@ 'required string', 'commit' => 'required string', 'needsBlame' => 'optional bool', ); } protected function getResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $needs_blame = $request->getValue('needsBlame'); $file_query = DiffusionFileContentQuery::newFromDiffusionRequest( $drequest); $file_query ->setViewer($request->getUser()) ->setNeedsBlame($needs_blame); $file_content = $file_query->loadFileContent(); if ($needs_blame) { list($text_list, $rev_list, $blame_dict) = $file_query->getBlameData(); } else { $text_list = $rev_list = $blame_dict = array(); } $file_content ->setBlameDict($blame_dict) ->setRevList($rev_list) ->setTextList($text_list); return $file_content->toDictionary(); } } diff --git a/src/applications/diffusion/conduit/DiffusionFindSymbolsConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionFindSymbolsConduitAPIMethod.php index 34575ae99d..da4025bc05 100644 --- a/src/applications/diffusion/conduit/DiffusionFindSymbolsConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionFindSymbolsConduitAPIMethod.php @@ -1,86 +1,81 @@ 'optional string', 'namePrefix' => 'optional string', 'context' => 'optional string', 'language' => 'optional string', 'type' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $name = $request->getValue('name'); $name_prefix = $request->getValue('namePrefix'); $context = $request->getValue('context'); $language = $request->getValue('language'); $type = $request->getValue('type'); $query = id(new DiffusionSymbolQuery()) ->setViewer($request->getUser()); if ($name !== null) { $query->setName($name); } if ($name_prefix !== null) { $query->setNamePrefix($name_prefix); } if ($context !== null) { $query->setContext($context); } if ($language !== null) { $query->setLanguage($language); } if ($type !== null) { $query->setType($type); } $query->needPaths(true); $query->needArcanistProjects(true); $query->needRepositories(true); $results = $query->execute(); $response = array(); foreach ($results as $result) { $uri = $result->getURI(); if ($uri) { $uri = PhabricatorEnv::getProductionURI($uri); } $response[] = array( 'name' => $result->getSymbolName(), 'context' => $result->getSymbolContext(), 'type' => $result->getSymbolType(), 'language' => $result->getSymbolLanguage(), 'path' => $result->getPath(), 'line' => $result->getLineNumber(), 'uri' => $uri, ); } return $response; } } diff --git a/src/applications/diffusion/conduit/DiffusionGetCommitsConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionGetCommitsConduitAPIMethod.php index e6cfd9730d..cdc89807cd 100644 --- a/src/applications/diffusion/conduit/DiffusionGetCommitsConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionGetCommitsConduitAPIMethod.php @@ -1,297 +1,293 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list>'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $results = array(); $commits = $request->getValue('commits'); $commits = array_fill_keys($commits, array()); foreach ($commits as $name => $info) { $matches = null; if (!preg_match('/^r([A-Z]+)([0-9a-f]+)\z/', $name, $matches)) { $results[$name] = array( 'error' => 'ERR-UNPARSEABLE', ); unset($commits[$name]); continue; } $commits[$name] = array( 'callsign' => $matches[1], 'commitIdentifier' => $matches[2], ); } if (!$commits) { return $results; } $callsigns = ipull($commits, 'callsign'); $callsigns = array_unique($callsigns); $repos = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->withCallsigns($callsigns) ->execute(); $repos = mpull($repos, null, 'getCallsign'); foreach ($commits as $name => $info) { $repo = idx($repos, $info['callsign']); if (!$repo) { $results[$name] = $info + array( 'error' => 'ERR-UNKNOWN-REPOSITORY', ); unset($commits[$name]); continue; } $commits[$name] += array( 'repositoryPHID' => $repo->getPHID(), 'repositoryID' => $repo->getID(), ); } if (!$commits) { return $results; } // Execute a complicated query to figure out the primary commit information // for each referenced commit. $cdata = $this->queryCommitInformation($commits, $repos); // We've built the queries so that each row also has the identifier we used // to select it, which might be a git prefix rather than a full identifier. $ref_map = ipull($cdata, 'commitIdentifier', 'commitRef'); $cobjs = id(new PhabricatorRepositoryCommit())->loadAllFromArray($cdata); $cobjs = mgroup($cobjs, 'getRepositoryID', 'getCommitIdentifier'); foreach ($commits as $name => $commit) { // Expand short git names into full identifiers. For SVN this map is just // the identity. $full_identifier = idx($ref_map, $commit['commitIdentifier']); $repo_id = $commit['repositoryID']; unset($commits[$name]['repositoryID']); if (empty($full_identifier) || empty($cobjs[$commit['repositoryID']][$full_identifier])) { $results[$name] = $commit + array( 'error' => 'ERR-UNKNOWN-COMMIT', ); unset($commits[$name]); continue; } $cobj_arr = $cobjs[$commit['repositoryID']][$full_identifier]; $cobj = head($cobj_arr); $commits[$name] += array( 'epoch' => $cobj->getEpoch(), 'commitPHID' => $cobj->getPHID(), 'commitID' => $cobj->getID(), ); // Upgrade git short references into full commit identifiers. $identifier = $cobj->getCommitIdentifier(); $commits[$name]['commitIdentifier'] = $identifier; $callsign = $commits[$name]['callsign']; $uri = "/r{$callsign}{$identifier}"; $commits[$name]['uri'] = PhabricatorEnv::getProductionURI($uri); } if (!$commits) { return $results; } $commits = $this->addRepositoryCommitDataInformation($commits); $commits = $this->addDifferentialInformation($commits, $request); $commits = $this->addManiphestInformation($commits); foreach ($commits as $name => $commit) { $results[$name] = $commit; } return $results; } /** * Retrieve primary commit information for all referenced commits. */ private function queryCommitInformation(array $commits, array $repos) { assert_instances_of($repos, 'PhabricatorRepository'); $conn_r = id(new PhabricatorRepositoryCommit())->establishConnection('r'); $repos = mpull($repos, null, 'getID'); $groups = array(); foreach ($commits as $name => $commit) { $groups[$commit['repositoryID']][] = $commit['commitIdentifier']; } // NOTE: MySQL goes crazy and does a massive table scan if we build a more // sensible version of this query. Make sure the query plan is OK if you // attempt to reduce the craziness here. METANOTE: The addition of prefix // selection for Git further complicates matters. $query = array(); $commit_table = id(new PhabricatorRepositoryCommit())->getTableName(); foreach ($groups as $repository_id => $identifiers) { $vcs = $repos[$repository_id]->getVersionControlSystem(); $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT); if ($is_git) { foreach ($identifiers as $identifier) { if (strlen($identifier) < 7) { // Don't bother with silly stuff like 'rX2', which will select // 1/16th of all commits. Note that with length 7 we'll still get // collisions in repositories at the tens-of-thousands-of-commits // scale. continue; } $query[] = qsprintf( $conn_r, 'SELECT %T.*, %s commitRef FROM %T WHERE repositoryID = %d AND commitIdentifier LIKE %>', $commit_table, $identifier, $commit_table, $repository_id, $identifier); } } else { $query[] = qsprintf( $conn_r, 'SELECT %T.*, commitIdentifier commitRef FROM %T WHERE repositoryID = %d AND commitIdentifier IN (%Ls)', $commit_table, $commit_table, $repository_id, $identifiers); } } return queryfx_all( $conn_r, '%Q', implode(' UNION ALL ', $query)); } /** * Enhance the commit list with RepositoryCommitData information. */ private function addRepositoryCommitDataInformation(array $commits) { $commit_ids = ipull($commits, 'commitID'); $data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 'commitID in (%Ld)', $commit_ids); $data = mpull($data, null, 'getCommitID'); foreach ($commits as $name => $commit) { if (isset($data[$commit['commitID']])) { $dobj = $data[$commit['commitID']]; $commits[$name] += array( 'commitMessage' => $dobj->getCommitMessage(), 'commitDetails' => $dobj->getCommitDetails(), ); } // Remove this information so we don't expose it via the API since // external services shouldn't be storing internal Commit IDs. unset($commits[$name]['commitID']); } return $commits; } /** * Enhance the commit list with Differential information. */ private function addDifferentialInformation( array $commits, ConduitAPIRequest $request) { $commit_phids = ipull($commits, 'commitPHID'); $revisions = id(new DifferentialRevisionQuery()) ->setViewer($request->getUser()) ->withCommitPHIDs($commit_phids) ->needCommitPHIDs(true) ->execute(); $rev_phid_commit_phids_map = mpull($revisions, 'getCommitPHIDs', 'getPHID'); $revisions = mpull($revisions, null, 'getPHID'); foreach ($rev_phid_commit_phids_map as $rev_phid => $commit_phids) { foreach ($commits as $name => $commit) { $commit_phid = $commit['commitPHID']; if (in_array($commit_phid, $commit_phids)) { $revision = $revisions[$rev_phid]; $commits[$name] += array( 'differentialRevisionID' => 'D'.$revision->getID(), 'differentialRevisionPHID' => $revision->getPHID(), ); } } } return $commits; } /** * Enhances the commits list with Maniphest information. */ private function addManiphestInformation(array $commits) { $task_type = DiffusionCommitHasTaskEdgeType::EDGECONST; $commit_phids = ipull($commits, 'commitPHID'); $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs($commit_phids) ->withEdgeTypes(array($task_type)); $edges = $edge_query->execute(); foreach ($commits as $name => $commit) { $task_phids = $edge_query->getDestinationPHIDs( array($commit['commitPHID']), array($task_type)); $commits[$name] += array( 'taskPHIDs' => $task_phids, ); } return $commits; } } diff --git a/src/applications/diffusion/conduit/DiffusionGetLintMessagesConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionGetLintMessagesConduitAPIMethod.php index ba129557b3..d73213b148 100644 --- a/src/applications/diffusion/conduit/DiffusionGetLintMessagesConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionGetLintMessagesConduitAPIMethod.php @@ -1,73 +1,69 @@ 'required string', 'branch' => 'optional string', 'commit' => 'optional string', 'files' => 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $project = id(new PhabricatorRepositoryArcanistProject())->loadOneWhere( 'name = %s', $request->getValue('arcanistProject')); if (!$project || !$project->getRepositoryID()) { return array(); } $branch_name = $request->getValue('branch'); if ($branch_name == '') { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->withIDs(array($project->getRepositoryID())) ->executeOne(); $branch_name = $repository->getDefaultArcanistBranch(); } $branch = id(new PhabricatorRepositoryBranch())->loadOneWhere( 'repositoryID = %d AND name = %s', $project->getRepositoryID(), $branch_name); if (!$branch || !$branch->getLintCommit()) { return array(); } $lint_messages = queryfx_all( $branch->establishConnection('r'), 'SELECT path, line, code FROM %T WHERE branchID = %d AND path IN (%Ls)', PhabricatorRepository::TABLE_LINTMESSAGE, $branch->getID(), $request->getValue('files')); // TODO: Compare commit identifiers of individual files like in // DiffusionBrowseFileController::loadLintMessages(). return $lint_messages; } } diff --git a/src/applications/diffusion/conduit/DiffusionGetRecentCommitsByPathConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionGetRecentCommitsByPathConduitAPIMethod.php index 4ef28ddbad..89e40c69ba 100644 --- a/src/applications/diffusion/conduit/DiffusionGetRecentCommitsByPathConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionGetRecentCommitsByPathConduitAPIMethod.php @@ -1,70 +1,65 @@ 'required string', 'path' => 'required string', 'branch' => 'optional string', 'limit' => 'optional int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $drequest = DiffusionRequest::newFromDictionary( array( 'user' => $request->getUser(), 'callsign' => $request->getValue('callsign'), 'path' => $request->getValue('path'), 'branch' => $request->getValue('branch'), )); $limit = nonempty( $request->getValue('limit'), self::DEFAULT_LIMIT); $history_result = DiffusionQuery::callConduitWithDiffusionRequest( $request->getUser(), $drequest, 'diffusion.historyquery', array( 'commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => 0, 'limit' => $limit, 'needDirectChanges' => true, 'needChildChanges' => true, )); $history = DiffusionPathChange::newFromConduit( $history_result['pathChanges']); $raw_commit_identifiers = mpull($history, 'getCommitIdentifier'); $result = array(); foreach ($raw_commit_identifiers as $id) { $result[] = 'r'.$request->getValue('callsign').$id; } return $result; } } diff --git a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php index 05c98a5b5b..939a47ab64 100644 --- a/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php @@ -1,269 +1,269 @@ 'required string', 'path' => 'required string', 'offset' => 'required int', 'limit' => 'required int', 'needDirectChanges' => 'optional bool', 'needChildChanges' => 'optional bool', ); } protected function getResult(ConduitAPIRequest $request) { $path_changes = parent::getResult($request); return array( 'pathChanges' => mpull($path_changes, 'toDictionary'), 'parents' => $this->parents, ); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $commit_hash = $request->getValue('commit'); $path = $request->getValue('path'); $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); list($stdout) = $repository->execxLocalCommand( 'log '. '--skip=%d '. '-n %d '. '--pretty=format:%s '. '%s -- %C', $offset, $limit, '%H:%P', $commit_hash, // Git omits merge commits if the path is provided, even if it is empty. (strlen($path) ? csprintf('%s', $path) : '')); $lines = explode("\n", trim($stdout)); $lines = array_filter($lines); if (!$lines) { return array(); } $hash_list = array(); $parent_map = array(); foreach ($lines as $line) { list($hash, $parents) = explode(':', $line); $hash_list[] = $hash; $parent_map[$hash] = preg_split('/\s+/', $parents); } $this->parents = $parent_map; return DiffusionQuery::loadHistoryForCommitIdentifiers( $hash_list, $drequest); } protected function getMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $commit_hash = $request->getValue('commit'); $path = $request->getValue('path'); $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); $path = DiffusionPathIDQuery::normalizePath($path); $path = ltrim($path, '/'); // NOTE: Older versions of Mercurial give different results for these // commands (see T1268): // // $ hg log -- '' // $ hg log // // All versions of Mercurial give different results for these commands // (merge commits are excluded with the "." version): // // $ hg log -- . // $ hg log // // If we don't have a path component in the query, omit it from the command // entirely to avoid these inconsistencies. // NOTE: When viewing the history of a file, we don't use "-b", because // Mercurial stops history at the branchpoint but we're interested in all // ancestors. When viewing history of a branch, we do use "-b", and thus // stop history (this is more consistent with the Mercurial worldview of // branches). if (strlen($path)) { $path_arg = csprintf('-- %s', $path); $branch_arg = ''; } else { $path_arg = ''; // NOTE: --branch used to be called --only-branch; use -b for // compatibility. $branch_arg = csprintf('-b %s', $drequest->getBranch()); } list($stdout) = $repository->execxLocalCommand( 'log --debug --template %s --limit %d %C --rev %s %C', '{node};{parents}\\n', ($offset + $limit), // No '--skip' in Mercurial. $branch_arg, hgsprintf('reverse(ancestors(%s))', $commit_hash), $path_arg); $stdout = PhabricatorRepository::filterMercurialDebugOutput($stdout); $lines = explode("\n", trim($stdout)); $lines = array_slice($lines, $offset); $hash_list = array(); $parent_map = array(); $last = null; foreach (array_reverse($lines) as $line) { list($hash, $parents) = explode(';', $line); $parents = trim($parents); if (!$parents) { if ($last === null) { $parent_map[$hash] = array('...'); } else { $parent_map[$hash] = array($last); } } else { $parents = preg_split('/\s+/', $parents); foreach ($parents as $parent) { list($plocal, $phash) = explode(':', $parent); if (!preg_match('/^0+$/', $phash)) { $parent_map[$hash][] = $phash; } } // This may happen for the zeroth commit in repository, both hashes // are "000000000...". if (empty($parent_map[$hash])) { $parent_map[$hash] = array('...'); } } // The rendering code expects the first commit to be "mainline", like // Git. Flip the order so it does the right thing. $parent_map[$hash] = array_reverse($parent_map[$hash]); $hash_list[] = $hash; $last = $hash; } $hash_list = array_reverse($hash_list); $this->parents = $parent_map; return DiffusionQuery::loadHistoryForCommitIdentifiers( $hash_list, $drequest); } protected function getSVNResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $commit = $request->getValue('commit'); $path = $request->getValue('path'); $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); $need_direct_changes = $request->getValue('needDirectChanges'); $need_child_changes = $request->getValue('needChildChanges'); $conn_r = $repository->establishConnection('r'); $paths = queryfx_all( $conn_r, 'SELECT id, path FROM %T WHERE pathHash IN (%Ls)', PhabricatorRepository::TABLE_PATH, array(md5('/'.trim($path, '/')))); $paths = ipull($paths, 'id', 'path'); $path_id = idx($paths, '/'.trim($path, '/')); if (!$path_id) { return array(); } $filter_query = ''; if ($need_direct_changes) { if ($need_child_changes) { $type = DifferentialChangeType::TYPE_CHILD; $filter_query = 'AND (isDirect = 1 OR changeType = '.$type.')'; } else { $filter_query = 'AND (isDirect = 1)'; } } $history_data = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d AND commitSequence <= %d %Q ORDER BY commitSequence DESC LIMIT %d, %d', PhabricatorRepository::TABLE_PATHCHANGE, $repository->getID(), $path_id, $commit ? $commit : 0x7FFFFFFF, $filter_query, $offset, $limit); $commits = array(); $commit_data = array(); $commit_ids = ipull($history_data, 'commitID'); if ($commit_ids) { $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( 'id IN (%Ld)', $commit_ids); if ($commits) { $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 'commitID in (%Ld)', $commit_ids); $commit_data = mpull($commit_data, null, 'getCommitID'); } } $history = array(); foreach ($history_data as $row) { $item = new DiffusionPathChange(); $commit = idx($commits, $row['commitID']); if ($commit) { $item->setCommit($commit); $item->setCommitIdentifier($commit->getCommitIdentifier()); $data = idx($commit_data, $commit->getID()); if ($data) { $item->setCommitData($data); } } $item->setChangeType($row['changeType']); $item->setFileType($row['fileType']); $history[] = $item; } return $history; } } diff --git a/src/applications/diffusion/conduit/DiffusionLastModifiedQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionLastModifiedQueryConduitAPIMethod.php index c5a256a307..30b436cfff 100644 --- a/src/applications/diffusion/conduit/DiffusionLastModifiedQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionLastModifiedQueryConduitAPIMethod.php @@ -1,156 +1,156 @@ '; } protected function defineCustomParamTypes() { return array( 'paths' => 'required map', ); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $paths = $request->getValue('paths'); $results = $this->loadCommitsFromCache($paths); foreach ($paths as $path => $commit) { if (array_key_exists($path, $results)) { continue; } list($hash) = $repository->execxLocalCommand( 'log -n1 --format=%%H %s -- %s', $commit, $path); $results[$path] = trim($hash); } return $results; } protected function getSVNResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $results = array(); foreach ($request->getValue('paths') as $path => $commit) { $history_result = DiffusionQuery::callConduitWithDiffusionRequest( $request->getUser(), $drequest, 'diffusion.historyquery', array( 'commit' => $commit, 'path' => $path, 'limit' => 1, 'offset' => 0, 'needDirectChanges' => true, 'needChildChanges' => true, )); $history_array = DiffusionPathChange::newFromConduit( $history_result['pathChanges']); if ($history_array) { $results[$path] = head($history_array) ->getCommit() ->getCommitIdentifier(); } } return $results; } protected function getMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $paths = $request->getValue('paths'); $results = $this->loadCommitsFromCache($paths); foreach ($paths as $path => $commit) { if (array_key_exists($path, $results)) { continue; } list($hash) = $repository->execxLocalCommand( 'log --template %s --limit 1 --removed --rev %s -- %s', '{node}', hgsprintf('reverse(ancestors(%s))', $commit), nonempty(ltrim($path, '/'), '.')); $results[$path] = trim($hash); } return $results; } private function loadCommitsFromCache(array $map) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $path_map = id(new DiffusionPathIDQuery(array_keys($map))) ->loadPathIDs(); $commit_query = id(new DiffusionCommitQuery()) ->setViewer($drequest->getUser()) ->withRepository($repository) ->withIdentifiers(array_values($map)); $commit_query->execute(); $commit_map = $commit_query->getIdentifierMap(); $commit_map = mpull($commit_map, 'getID'); $graph_cache = new PhabricatorRepositoryGraphCache(); $results = array(); foreach ($map as $path => $commit) { $path_id = idx($path_map, $path); if (!$path_id) { continue; } $commit_id = idx($commit_map, $commit); if (!$commit_id) { continue; } $cache_result = $graph_cache->loadLastModifiedCommitID( $commit_id, $path_id); if ($cache_result !== false) { $results[$path] = $cache_result; } } if ($results) { $commits = id(new DiffusionCommitQuery()) ->setViewer($drequest->getUser()) ->withRepository($repository) ->withIDs($results) ->execute(); foreach ($results as $path => $id) { $commit = idx($commits, $id); if ($commit) { $results[$path] = $commit->getCommitIdentifier(); } else { unset($results[$path]); } } } return $results; } } diff --git a/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php index 01a60406e8..49dd5dc117 100644 --- a/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php @@ -1,59 +1,55 @@ 'required list', 'urgency' => 'optional string', ); } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { // NOTE: The "urgency" parameter does nothing, it is just a hilarious joke // which exemplifies the boundless clever wit of this project. $callsigns = $request->getValue('callsigns'); if (!$callsigns) { return null; } $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->withCallsigns($callsigns) ->execute(); foreach ($repositories as $repository) { $repository->writeStatusMessage( PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY); } return null; } } diff --git a/src/applications/diffusion/conduit/DiffusionMergedCommitsQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionMergedCommitsQueryConduitAPIMethod.php index 867da02331..71c8014de5 100644 --- a/src/applications/diffusion/conduit/DiffusionMergedCommitsQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionMergedCommitsQueryConduitAPIMethod.php @@ -1,110 +1,110 @@ 'required string', 'limit' => 'optional int', ); } private function getLimit(ConduitAPIRequest $request) { return $request->getValue('limit', PHP_INT_MAX); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $commit = $request->getValue('commit'); $limit = $this->getLimit($request); list($parents) = $repository->execxLocalCommand( 'log -n 1 --format=%s %s', '%P', $commit); $parents = preg_split('/\s+/', trim($parents)); if (count($parents) < 2) { // This is not a merge commit, so it doesn't merge anything. return array(); } // Get all of the commits which are not reachable from the first parent. // These are the commits this change merges. $first_parent = head($parents); list($logs) = $repository->execxLocalCommand( 'log -n %d --format=%s %s %s --', // NOTE: "+ 1" accounts for the merge commit itself. $limit + 1, '%H', $commit, '^'.$first_parent); $hashes = explode("\n", trim($logs)); // Remove the merge commit. $hashes = array_diff($hashes, array($commit)); $history = DiffusionQuery::loadHistoryForCommitIdentifiers( $hashes, $drequest); return mpull($history, 'toDictionary'); } protected function getMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $commit = $request->getValue('commit'); $limit = $this->getLimit($request); list($parents) = $repository->execxLocalCommand( 'parents --template=%s --rev %s', '{node}\\n', $commit); $parents = explode("\n", trim($parents)); if (count($parents) < 2) { // Not a merge commit. return array(); } // NOTE: In Git, the first parent is the "mainline". In Mercurial, the // second parent is the "mainline" (the way 'git merge' and 'hg merge' // work is also reversed). $last_parent = last($parents); list($logs) = $repository->execxLocalCommand( 'log --template=%s --follow --limit %d --rev %s:0 --prune %s --', '{node}\\n', $limit + 1, $commit, $last_parent); $hashes = explode("\n", trim($logs)); // Remove the merge commit. $hashes = array_diff($hashes, array($commit)); $history = DiffusionQuery::loadHistoryForCommitIdentifiers( $hashes, $drequest); return mpull($history, 'toDictionary'); } } diff --git a/src/applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php index 490ce03985..c2e1a3de50 100644 --- a/src/applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionQueryCommitsConduitAPIMethod.php @@ -1,142 +1,138 @@ '; } - public function defineParamTypes() { + protected function defineParamTypes() { return array( 'ids' => 'optional list', 'phids' => 'optional list', 'names' => 'optional list', 'repositoryPHID' => 'optional phid', 'needMessages' => 'optional bool', 'bypassCache' => 'optional bool', ) + $this->getPagerParamTypes(); } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $need_messages = $request->getValue('needMessages'); $bypass_cache = $request->getValue('bypassCache'); $query = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()) ->needCommitData(true); $repository_phid = $request->getValue('repositoryPHID'); if ($repository_phid) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->withPHIDs(array($repository_phid)) ->executeOne(); if ($repository) { $query->withRepository($repository); } } $names = $request->getValue('names'); if ($names) { $query->withIdentifiers($names); } $ids = $request->getValue('ids'); if ($ids) { $query->withIDs($ids); } $phids = $request->getValue('phids'); if ($phids) { $query->withPHIDs($phids); } $pager = $this->newPager($request); $commits = $query->executeWithCursorPager($pager); $map = $query->getIdentifierMap(); $map = mpull($map, 'getPHID'); $data = array(); foreach ($commits as $commit) { $commit_data = $commit->getCommitData(); $callsign = $commit->getRepository()->getCallsign(); $identifier = $commit->getCommitIdentifier(); $uri = '/r'.$callsign.$identifier; $uri = PhabricatorEnv::getProductionURI($uri); $dict = array( 'id' => $commit->getID(), 'phid' => $commit->getPHID(), 'repositoryPHID' => $commit->getRepository()->getPHID(), 'identifier' => $identifier, 'epoch' => $commit->getEpoch(), 'uri' => $uri, 'isImporting' => !$commit->isImported(), 'summary' => $commit->getSummary(), 'authorPHID' => $commit->getAuthorPHID(), 'committerPHID' => $commit_data->getCommitDetail('committerPHID'), 'author' => $commit_data->getAuthorName(), 'authorName' => $commit_data->getCommitDetail('authorName'), 'authorEmail' => $commit_data->getCommitDetail('authorEmail'), 'committer' => $commit_data->getCommitDetail('committer'), 'committerName' => $commit_data->getCommitDetail('committerName'), 'committerEmail' => $commit_data->getCommitDetail('committerEmail'), 'hashes' => array(), ); if ($bypass_cache) { $lowlevel_commitref = id(new DiffusionLowLevelCommitQuery()) ->setRepository($commit->getRepository()) ->withIdentifier($commit->getCommitIdentifier()) ->execute(); $dict['author'] = $lowlevel_commitref->getAuthor(); $dict['authorName'] = $lowlevel_commitref->getAuthorName(); $dict['authorEmail'] = $lowlevel_commitref->getAuthorEmail(); $dict['committer'] = $lowlevel_commitref->getCommitter(); $dict['committerName'] = $lowlevel_commitref->getCommitterName(); $dict['committerEmail'] = $lowlevel_commitref->getCommitterEmail(); if ($need_messages) { $dict['message'] = $lowlevel_commitref->getMessage(); } foreach ($lowlevel_commitref->getHashes() as $hash) { $dict['hashes'][] = array( 'type' => $hash->getHashType(), 'value' => $hash->getHashValue(), ); } } if ($need_messages && !$bypass_cache) { $dict['message'] = $commit_data->getCommitMessage(); } $data[$commit->getPHID()] = $dict; } $result = array( 'data' => $data, 'identifierMap' => nonempty($map, (object)array()), ); return $this->addPagerResults($result, $pager); } } diff --git a/src/applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php index dd58cd09ab..22c4adad45 100644 --- a/src/applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionQueryConduitAPIMethod.php @@ -1,159 +1,159 @@ diffusionRequest = $request; return $this; } protected function getDiffusionRequest() { return $this->diffusionRequest; } protected function getRepository(ConduitAPIRequest $request) { return $this->getDiffusionRequest()->getRepository(); } - final public function defineErrorTypes() { + final protected function defineErrorTypes() { return $this->defineCustomErrorTypes() + array( 'ERR-UNKNOWN-REPOSITORY' => pht('There is no repository with that callsign.'), 'ERR-UNKNOWN-VCS-TYPE' => pht('Unknown repository VCS type.'), 'ERR-UNSUPPORTED-VCS' => pht('VCS is not supported for this method.'), ); } /** * Subclasses should override this to specify custom error types. */ protected function defineCustomErrorTypes() { return array(); } - final public function defineParamTypes() { + final protected function defineParamTypes() { return $this->defineCustomParamTypes() + array( 'callsign' => 'required string', 'branch' => 'optional string', ); } /** * Subclasses should override this to specify custom param types. */ protected function defineCustomParamTypes() { return array(); } /** * Subclasses should override these methods with the proper result for the * pertinent version control system, e.g. getGitResult for Git. * * If the result is not supported for that VCS, do not implement it. e.g. * Subversion (SVN) does not support branches. */ protected function getGitResult(ConduitAPIRequest $request) { throw new ConduitException('ERR-UNSUPPORTED-VCS'); } protected function getSVNResult(ConduitAPIRequest $request) { throw new ConduitException('ERR-UNSUPPORTED-VCS'); } protected function getMercurialResult(ConduitAPIRequest $request) { throw new ConduitException('ERR-UNSUPPORTED-VCS'); } /** * This method is final because most queries will need to construct a * @{class:DiffusionRequest} and use it. Consolidating this codepath and * enforcing @{method:getDiffusionRequest} works when we need it is good. * * @{method:getResult} should be overridden by subclasses as necessary, e.g. * there is a common operation across all version control systems that * should occur after @{method:getResult}, like formatting a timestamp. */ final protected function execute(ConduitAPIRequest $request) { $drequest = DiffusionRequest::newFromDictionary( array( 'user' => $request->getUser(), 'callsign' => $request->getValue('callsign'), 'branch' => $request->getValue('branch'), 'path' => $request->getValue('path'), 'commit' => $request->getValue('commit'), )); // Figure out whether we're going to handle this request on this device, // or proxy it to another node in the cluster. // If this is a cluster request and we need to proxy, we'll explode here // to prevent infinite recursion. $is_cluster_request = $request->getIsClusterRequest(); $repository = $drequest->getRepository(); $client = $repository->newConduitClient( $request->getUser(), $is_cluster_request); if ($client) { // We're proxying, so just make an intracluster call. return $client->callMethodSynchronous( $this->getAPIMethodName(), $request->getAllParameters()); } else { // We pass this flag on to prevent proxying of any other Conduit calls // which we need to make in order to respond to this one. Although we // could safely proxy them, we take a big performance hit in the common // case, and doing more proxying wouldn't exercise any additional code so // we wouldn't gain a testability/predictability benefit. $drequest->setIsClusterRequest($is_cluster_request); $this->setDiffusionRequest($drequest); return $this->getResult($request); } } protected function getResult(ConduitAPIRequest $request) { $repository = $this->getRepository($request); $result = null; switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $result = $this->getGitResult($request); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $result = $this->getMercurialResult($request); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $result = $this->getSVNResult($request); break; default: throw new ConduitException('ERR-UNKNOWN-VCS-TYPE'); break; } return $result; } } diff --git a/src/applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php index c25d71aeb4..6061825444 100644 --- a/src/applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php @@ -1,105 +1,105 @@ '; } protected function defineCustomParamTypes() { return array( 'path' => 'required string', 'commit' => 'required string', 'pattern' => 'optional string', 'limit' => 'optional int', 'offset' => 'optional int', ); } protected function getResult(ConduitAPIRequest $request) { $results = parent::getResult($request); $offset = $request->getValue('offset'); return array_slice($results, $offset); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $path = $drequest->getPath(); $commit = $request->getValue('commit'); $repository = $drequest->getRepository(); // http://comments.gmane.org/gmane.comp.version-control.git/197735 $future = $repository->getLocalCommandFuture( 'ls-tree --name-only -r -z %s -- %s', $commit, $path); $lines = id(new LinesOfALargeExecFuture($future))->setDelimiter("\0"); return $this->filterResults($lines, $request); } protected function getMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $path = $request->getValue('path'); $commit = $request->getValue('commit'); $entire_manifest = id(new DiffusionLowLevelMercurialPathsQuery()) ->setRepository($repository) ->withCommit($commit) ->withPath($path) ->execute(); $match_against = trim($path, '/'); $match_len = strlen($match_against); $lines = array(); foreach ($entire_manifest as $path) { if (strlen($path) && !strncmp($path, $match_against, $match_len)) { $lines[] = $path; } } return $this->filterResults($lines, $request); } protected function filterResults($lines, ConduitAPIRequest $request) { $pattern = $request->getValue('pattern'); $limit = (int)$request->getValue('limit'); $offset = (int)$request->getValue('offset'); if (strlen($pattern)) { $pattern = '/'.preg_quote($pattern, '/').'/'; } $results = array(); $count = 0; foreach ($lines as $line) { if (!$pattern || preg_match($pattern, $line)) { if ($count >= $offset) { $results[] = $line; } $count++; if ($limit && ($count >= ($offset + $limit))) { break; } } } return $results; } } diff --git a/src/applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php index 0cc3d93b3a..a7e217d2aa 100644 --- a/src/applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionRawDiffQueryConduitAPIMethod.php @@ -1,59 +1,59 @@ 'required string', 'path' => 'optional string', 'timeout' => 'optional int', 'byteLimit' => 'optional int', 'linesOfContext' => 'optional int', 'againstCommit' => 'optional string', ); } protected function getResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest); $timeout = $request->getValue('timeout'); if ($timeout !== null) { $raw_query->setTimeout($timeout); } $lines_of_context = $request->getValue('linesOfContext'); if ($lines_of_context !== null) { $raw_query->setLinesOfContext($lines_of_context); } $against_commit = $request->getValue('againstCommit'); if ($against_commit !== null) { $raw_query->setAgainstCommit($against_commit); } $byte_limit = $request->getValue('byteLimit'); if ($byte_limit !== null) { $raw_query->setByteLimit($byte_limit); } return $raw_query->loadRawDiff(); } } diff --git a/src/applications/diffusion/conduit/DiffusionRefsQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionRefsQueryConduitAPIMethod.php index 38a44de6b5..2d025e9d4a 100644 --- a/src/applications/diffusion/conduit/DiffusionRefsQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionRefsQueryConduitAPIMethod.php @@ -1,59 +1,59 @@ 'required string', ); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $commit = $request->getValue('commit'); list($stdout) = $repository->execxLocalCommand( 'log --format=%s -n 1 %s --', '%d', $commit); // %d, gives a weird output format // similar to (remote/one, remote/two, remote/three) $refs = trim($stdout, "() \n"); if (!$refs) { return array(); } $refs = explode(',', $refs); $refs = array_map('trim', $refs); $ref_links = array(); foreach ($refs as $ref) { $ref_links[] = array( 'ref' => $ref, 'href' => $drequest->generateURI( array( 'action' => 'browse', 'branch' => $ref, )), ); } return $ref_links; } } diff --git a/src/applications/diffusion/conduit/DiffusionResolveRefsConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionResolveRefsConduitAPIMethod.php index 36a2256bdb..0cfb2bf565 100644 --- a/src/applications/diffusion/conduit/DiffusionResolveRefsConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionResolveRefsConduitAPIMethod.php @@ -1,33 +1,33 @@ >>'; } protected function defineCustomParamTypes() { return array( 'refs' => 'required list', ); } protected function getResult(ConduitAPIRequest $request) { $refs = $request->getValue('refs'); return id(new DiffusionLowLevelResolveRefsQuery()) ->setRepository($this->getDiffusionRequest()->getRepository()) ->withRefs($refs) ->execute(); } } diff --git a/src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php index db73f6b79d..5cf91a10f0 100644 --- a/src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php @@ -1,118 +1,118 @@ 'required string', 'commit' => 'optional string', 'grep' => 'required string', 'limit' => 'optional int', 'offset' => 'optional int', ); } protected function defineCustomErrorTypes() { return array( 'ERR-GREP-COMMAND' => 'Grep command failed.', ); } protected function getResult(ConduitAPIRequest $request) { try { $results = parent::getResult($request); } catch (CommandException $ex) { throw id(new ConduitException('ERR-GREP-COMMAND')) ->setErrorDescription($ex->getStderr()); } $offset = $request->getValue('offset'); $results = array_slice($results, $offset); return $results; } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $path = $drequest->getPath(); $grep = $request->getValue('grep'); $repository = $drequest->getRepository(); $limit = $request->getValue('limit'); $offset = $request->getValue('offset'); $results = array(); $future = $repository->getLocalCommandFuture( // NOTE: --perl-regexp is available only with libpcre compiled in. 'grep --extended-regexp --null -n --no-color -e %s %s -- %s', $grep, $drequest->getStableCommit(), $path); $binary_pattern = '/Binary file [^:]*:(.+) matches/'; $lines = new LinesOfALargeExecFuture($future); foreach ($lines as $line) { $result = null; if (preg_match('/[^:]*:(.+)\0(.+)\0(.*)/', $line, $result)) { $results[] = array_slice($result, 1); } else if (preg_match($binary_pattern, $line, $result)) { list(, $path) = $result; $results[] = array($path, null, pht('Binary file')); } else { $results[] = array(null, null, $line); } if (count($results) >= $offset + $limit) { break; } } unset($lines); return $results; } protected function getMercurialResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $path = $drequest->getPath(); $grep = $request->getValue('grep'); $repository = $drequest->getRepository(); $limit = $request->getValue('limit'); $offset = $request->getValue('offset'); $results = array(); $future = $repository->getLocalCommandFuture( 'grep --rev %s --print0 --line-number %s %s', hgsprintf('ancestors(%s)', $drequest->getStableCommit()), $grep, $path); $lines = id(new LinesOfALargeExecFuture($future))->setDelimiter("\0"); $parts = array(); foreach ($lines as $line) { $parts[] = $line; if (count($parts) == 4) { list($path, $char_offset, $line, $string) = $parts; $results[] = array($path, $line, $string); if (count($results) >= $offset + $limit) { break; } $parts = array(); } } unset($lines); return $results; } } diff --git a/src/applications/diffusion/conduit/DiffusionTagsQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionTagsQueryConduitAPIMethod.php index 1ece4dd85b..ddf3a2152b 100644 --- a/src/applications/diffusion/conduit/DiffusionTagsQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionTagsQueryConduitAPIMethod.php @@ -1,168 +1,168 @@ 'optional list', 'commit' => 'optional string', 'needMessages' => 'optional bool', 'offset' => 'optional int', 'limit' => 'optional int', ); } protected function getGitResult(ConduitAPIRequest $request) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $commit = $drequest->getSymbolicCommit(); $commit_filter = null; if ($commit) { $commit_filter = $this->loadTagNamesForCommit($commit); } $name_filter = $request->getValue('names', null); $all_tags = $this->loadGitTagList(); $all_tags = mpull($all_tags, null, 'getName'); if ($name_filter !== null) { $all_tags = array_intersect_key($all_tags, array_fuse($name_filter)); } if ($commit_filter !== null) { $all_tags = array_intersect_key($all_tags, $commit_filter); } $tags = array_values($all_tags); $offset = $request->getValue('offset'); $limit = $request->getValue('limit'); if ($offset) { $tags = array_slice($tags, $offset); } if ($limit) { $tags = array_slice($tags, 0, $limit); } if ($request->getValue('needMessages')) { $this->loadMessagesForTags($all_tags); } return mpull($tags, 'toDictionary'); } private function loadGitTagList() { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $refs = id(new DiffusionLowLevelGitRefQuery()) ->setRepository($repository) ->withIsTag(true) ->execute(); $tags = array(); foreach ($refs as $ref) { $fields = $ref->getRawFields(); $tag = id(new DiffusionRepositoryTag()) ->setAuthor($fields['author']) ->setEpoch($fields['epoch']) ->setCommitIdentifier($ref->getCommitIdentifier()) ->setName($ref->getShortName()) ->setDescription($fields['subject']) ->setType('git/'.$fields['objecttype']); $tags[] = $tag; } return $tags; } private function loadTagNamesForCommit($commit) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); list($err, $stdout) = $repository->execLocalCommand( 'tag -l --contains %s', $commit); if ($err) { // Git exits with an error code if the commit is bogus. return array(); } $stdout = rtrim($stdout, "\n"); if (!strlen($stdout)) { return array(); } $tag_names = explode("\n", $stdout); $tag_names = array_fill_keys($tag_names, true); return $tag_names; } private function loadMessagesForTags(array $tags) { assert_instances_of($tags, 'DiffusionRepositoryTag'); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $futures = array(); foreach ($tags as $key => $tag) { $futures[$key] = $repository->getLocalCommandFuture( 'cat-file tag %s', $tag->getName()); } id(new FutureIterator($futures)) ->resolveAll(); foreach ($tags as $key => $tag) { $future = $futures[$key]; list($err, $stdout) = $future->resolve(); $message = null; if ($err) { // Not all tags are actually "tag" objects: a "tag" object is only // created if you provide a message or sign the tag. Tags created with // `git tag x [commit]` are "lightweight tags" and `git cat-file tag` // will fail on them. This is fine: they don't have messages. } else { $parts = explode("\n\n", $stdout, 2); if (count($parts) == 2) { $message = last($parts); } } $tag->attachMessage($message); } return $tags; } protected function getMercurialResult(ConduitAPIRequest $request) { // For now, we don't support Mercurial tags via API. return array(); } protected function getSVNResult(ConduitAPIRequest $request) { // Subversion has no meaningful concept of tags. return array(); } } diff --git a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php index bcadbd9f00..f356da3a10 100644 --- a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php @@ -1,100 +1,96 @@ 'required phid', 'branch' => 'required string', 'commit' => 'required string', 'coverage' => 'required map', ); } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $repository_phid = $request->getValue('repositoryPHID'); $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->withPHIDs(array($repository_phid)) ->executeOne(); if (!$repository) { throw new Exception( pht('No repository exists with PHID "%s".', $repository_phid)); } $commit_name = $request->getValue('commit'); $commit = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->withRepository($repository) ->withIdentifiers(array($commit_name)) ->executeOne(); if (!$commit) { throw new Exception( pht('No commit exists with identifier "%s".', $commit_name)); } $branch = PhabricatorRepositoryBranch::loadOrCreateBranch( $repository->getID(), $request->getValue('branch')); $coverage = $request->getValue('coverage'); $path_map = id(new DiffusionPathIDQuery(array_keys($coverage))) ->loadPathIDs(); $conn = $repository->establishConnection('w'); $sql = array(); foreach ($coverage as $path => $coverage_info) { $sql[] = qsprintf( $conn, '(%d, %d, %d, %s)', $branch->getID(), $path_map[$path], $commit->getID(), $coverage_info); } $table_name = 'repository_coverage'; $conn->openTransaction(); queryfx( $conn, 'DELETE FROM %T WHERE branchID = %d', $table_name, $branch->getID()); foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn, 'INSERT INTO %T (branchID, pathID, commitID, coverage) VALUES %Q', $table_name, $chunk); } $conn->saveTransaction(); } } diff --git a/src/applications/diffusion/query/DiffusionQuery.php b/src/applications/diffusion/query/DiffusionQuery.php index 5a1755084f..24f58583bf 100644 --- a/src/applications/diffusion/query/DiffusionQuery.php +++ b/src/applications/diffusion/query/DiffusionQuery.php @@ -1,217 +1,217 @@ } protected static function newQueryObject( $base_class, DiffusionRequest $request) { $repository = $request->getRepository(); $obj = self::initQueryObject($base_class, $repository); $obj->request = $request; return $obj; } final protected static function initQueryObject( $base_class, PhabricatorRepository $repository) { $map = array( PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => 'Git', PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => 'Mercurial', PhabricatorRepositoryType::REPOSITORY_TYPE_SVN => 'Svn', ); $name = idx($map, $repository->getVersionControlSystem()); if (!$name) { throw new Exception('Unsupported VCS!'); } $class = str_replace('Diffusion', 'Diffusion'.$name, $base_class); $obj = new $class(); return $obj; } final protected function getRequest() { return $this->request; } final public static function callConduitWithDiffusionRequest( PhabricatorUser $user, DiffusionRequest $drequest, $method, array $params = array()) { $repository = $drequest->getRepository(); $core_params = array( 'callsign' => $repository->getCallsign(), ); if ($drequest->getBranch() !== null) { $core_params['branch'] = $drequest->getBranch(); } // If the method we're calling doesn't actually take some of the implicit // parameters we derive from the DiffusionRequest, omit them. $method_object = ConduitAPIMethod::getConduitMethod($method); - $method_params = $method_object->defineParamTypes(); + $method_params = $method_object->getParamTypes(); foreach ($core_params as $key => $value) { if (empty($method_params[$key])) { unset($core_params[$key]); } } $params = $params + $core_params; $client = $repository->newConduitClient( $user, $drequest->getIsClusterRequest()); if (!$client) { return id(new ConduitCall($method, $params)) ->setUser($user) ->execute(); } else { return $client->callMethodSynchronous($method, $params); } } public function execute() { return $this->executeQuery(); } abstract protected function executeQuery(); /* -( Query Utilities )---------------------------------------------------- */ final public static function loadCommitsByIdentifiers( array $identifiers, DiffusionRequest $drequest) { if (!$identifiers) { return array(); } $commits = array(); $commit_data = array(); $repository = $drequest->getRepository(); $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( 'repositoryID = %d AND commitIdentifier IN (%Ls)', $repository->getID(), $identifiers); $commits = mpull($commits, null, 'getCommitIdentifier'); // Build empty commit objects for every commit, so we can show unparsed // commits in history views (as "Importing") instead of not showing them. // This makes the process of importing and parsing commits clearer to the // user. $commit_list = array(); foreach ($identifiers as $identifier) { $commit_obj = idx($commits, $identifier); if (!$commit_obj) { $commit_obj = new PhabricatorRepositoryCommit(); $commit_obj->setRepositoryID($repository->getID()); $commit_obj->setCommitIdentifier($identifier); $commit_obj->makeEphemeral(); } $commit_list[$identifier] = $commit_obj; } $commits = $commit_list; $commit_ids = array_filter(mpull($commits, 'getID')); if ($commit_ids) { $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere( 'commitID in (%Ld)', $commit_ids); $commit_data = mpull($commit_data, null, 'getCommitID'); } foreach ($commits as $commit) { if (!$commit->getID()) { continue; } if (idx($commit_data, $commit->getID())) { $commit->attachCommitData($commit_data[$commit->getID()]); } } return $commits; } final public static function loadHistoryForCommitIdentifiers( array $identifiers, DiffusionRequest $drequest) { if (!$identifiers) { return array(); } $repository = $drequest->getRepository(); $commits = self::loadCommitsByIdentifiers($identifiers, $drequest); if (!$commits) { return array(); } $path = $drequest->getPath(); $conn_r = $repository->establishConnection('r'); $path_normal = DiffusionPathIDQuery::normalizePath($path); $paths = queryfx_all( $conn_r, 'SELECT id, path FROM %T WHERE pathHash IN (%Ls)', PhabricatorRepository::TABLE_PATH, array(md5($path_normal))); $paths = ipull($paths, 'id', 'path'); $path_id = idx($paths, $path_normal); $commit_ids = array_filter(mpull($commits, 'getID')); $path_changes = array(); if ($path_id && $commit_ids) { $path_changes = queryfx_all( $conn_r, 'SELECT * FROM %T WHERE commitID IN (%Ld) AND pathID = %d', PhabricatorRepository::TABLE_PATHCHANGE, $commit_ids, $path_id); $path_changes = ipull($path_changes, null, 'commitID'); } $history = array(); foreach ($identifiers as $identifier) { $item = new DiffusionPathChange(); $item->setCommitIdentifier($identifier); $commit = idx($commits, $identifier); if ($commit) { $item->setCommit($commit); try { $item->setCommitData($commit->getCommitData()); } catch (Exception $ex) { // Ignore, commit just doesn't have data. } $change = idx($path_changes, $commit->getID()); if ($change) { $item->setChangeType($change['changeType']); $item->setFileType($change['fileType']); } } $history[] = $item; } return $history; } } diff --git a/src/applications/feed/conduit/FeedPublishConduitAPIMethod.php b/src/applications/feed/conduit/FeedPublishConduitAPIMethod.php index 7cb741e151..a082e36fea 100644 --- a/src/applications/feed/conduit/FeedPublishConduitAPIMethod.php +++ b/src/applications/feed/conduit/FeedPublishConduitAPIMethod.php @@ -1,54 +1,49 @@ 'required string', 'data' => 'required dict', 'time' => 'optional int', ); } - public function defineErrorTypes() { - return array( - ); - } - - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty phid'; } protected function execute(ConduitAPIRequest $request) { $type = $request->getValue('type'); $data = $request->getValue('data'); $time = $request->getValue('time'); $author_phid = $request->getUser()->getPHID(); $phids = array($author_phid); $publisher = new PhabricatorFeedStoryPublisher(); $publisher->setStoryType($type); $publisher->setStoryData($data); $publisher->setStoryTime($time); $publisher->setRelatedPHIDs($phids); $publisher->setStoryAuthorPHID($author_phid); $data = $publisher->publish(); return $data->getPHID(); } } diff --git a/src/applications/feed/conduit/FeedQueryConduitAPIMethod.php b/src/applications/feed/conduit/FeedQueryConduitAPIMethod.php index 9612122b40..02efb83f73 100644 --- a/src/applications/feed/conduit/FeedQueryConduitAPIMethod.php +++ b/src/applications/feed/conduit/FeedQueryConduitAPIMethod.php @@ -1,145 +1,145 @@ 'optional list ', 'limit' => 'optional int (default '.$this->getDefaultLimit().')', 'after' => 'optional int', 'before' => 'optional int', 'view' => 'optional string (data, html, html-summary, text)', ); } private function getSupportedViewTypes() { return array( 'html' => 'Full HTML presentation of story', 'data' => 'Dictionary with various data of the story', 'html-summary' => 'Story contains only the title of the story', 'text' => 'Simple one-line plain text representation of story', ); } - public function defineErrorTypes() { + protected function defineErrorTypes() { $view_types = array_keys($this->getSupportedViewTypes()); $view_types = implode(', ', $view_types); return array( 'ERR-UNKNOWN-TYPE' => 'Unsupported view type, possibles are: '.$view_types, ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } protected function execute(ConduitAPIRequest $request) { $results = array(); $user = $request->getUser(); $view_type = $request->getValue('view'); if (!$view_type) { $view_type = 'data'; } $limit = $request->getValue('limit'); if (!$limit) { $limit = $this->getDefaultLimit(); } $filter_phids = $request->getValue('filterPHIDs'); if (!$filter_phids) { $filter_phids = array(); } $query = id(new PhabricatorFeedQuery()) ->setLimit($limit) ->setFilterPHIDs($filter_phids) ->setViewer($user); $after = $request->getValue('after'); if (strlen($after)) { $query->setAfterID($after); } $before = $request->getValue('before'); if (strlen($before)) { $query->setBeforeID($before); } $stories = $query->execute(); if ($stories) { foreach ($stories as $story) { $story_data = $story->getStoryData(); $data = null; try { $view = $story->renderView(); } catch (Exception $ex) { // When stories fail to render, just fail that story. phlog($ex); continue; } $view->setEpoch($story->getEpoch()); $view->setUser($user); switch ($view_type) { case 'html': $data = $view->render(); break; case 'html-summary': $data = $view->render(); break; case 'data': $data = array( 'class' => $story_data->getStoryType(), 'epoch' => $story_data->getEpoch(), 'authorPHID' => $story_data->getAuthorPHID(), 'chronologicalKey' => $story_data->getChronologicalKey(), 'data' => $story_data->getStoryData(), ); break; case 'text': $data = array( 'class' => $story_data->getStoryType(), 'epoch' => $story_data->getEpoch(), 'authorPHID' => $story_data->getAuthorPHID(), 'chronologicalKey' => $story_data->getChronologicalKey(), 'objectPHID' => $story->getPrimaryObjectPHID(), 'text' => $story->renderText(), ); break; default: throw new ConduitException('ERR-UNKNOWN-TYPE'); } $results[$story_data->getPHID()] = $data; } } return $results; } } diff --git a/src/applications/files/conduit/FileAllocateConduitAPIMethod.php b/src/applications/files/conduit/FileAllocateConduitAPIMethod.php index 80f1cad7e1..d21c26fe11 100644 --- a/src/applications/files/conduit/FileAllocateConduitAPIMethod.php +++ b/src/applications/files/conduit/FileAllocateConduitAPIMethod.php @@ -1,137 +1,133 @@ 'string', 'contentLength' => 'int', 'contentHash' => 'optional string', 'viewPolicy' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'map'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $hash = $request->getValue('contentHash'); $name = $request->getValue('name'); $view_policy = $request->getValue('viewPolicy'); $length = $request->getValue('contentLength'); $properties = array( 'name' => $name, 'authorPHID' => $viewer->getPHID(), 'viewPolicy' => $view_policy, 'isExplicitUpload' => true, ); $file = null; if ($hash) { $file = PhabricatorFile::newFileFromContentHash( $hash, $properties); } if ($hash && !$file) { $chunked_hash = PhabricatorChunkedFileStorageEngine::getChunkedHash( $viewer, $hash); $file = id(new PhabricatorFileQuery()) ->setViewer($viewer) ->withContentHashes(array($chunked_hash)) ->executeOne(); } if (strlen($name) && !$hash && !$file) { if ($length > PhabricatorFileStorageEngine::getChunkThreshold()) { // If we don't have a hash, but this file is large enough to store in // chunks and thus may be resumable, try to find a partially uploaded // file by the same author with the same name and same length. This // allows us to resume uploads in Javascript where we can't efficiently // compute file hashes. $file = id(new PhabricatorFileQuery()) ->setViewer($viewer) ->withAuthorPHIDs(array($viewer->getPHID())) ->withNames(array($name)) ->withLengthBetween($length, $length) ->withIsPartial(true) ->setLimit(1) ->executeOne(); } } if ($file) { return array( 'upload' => (bool)$file->getIsPartial(), 'filePHID' => $file->getPHID(), ); } // If there are any non-chunk engines which this file can fit into, // just tell the client to upload the file. $engines = PhabricatorFileStorageEngine::loadStorageEngines($length); if ($engines) { return array( 'upload' => true, 'filePHID' => null, ); } // Otherwise, this is a large file and we want to perform a chunked // upload if we have a chunk engine available. $chunk_engines = PhabricatorFileStorageEngine::loadWritableChunkEngines(); if ($chunk_engines) { $chunk_properties = $properties; if ($hash) { $chunk_properties += array( 'chunkedHash' => $chunked_hash, ); } $chunk_engine = head($chunk_engines); $file = $chunk_engine->allocateChunks($length, $chunk_properties); return array( 'upload' => true, 'filePHID' => $file->getPHID(), ); } // None of the storage engines can accept this file. if (PhabricatorFileStorageEngine::loadWritableEngines()) { $error = pht( 'Unable to upload file: this file is too large for any '. 'configured storage engine.'); } else { $error = pht( 'Unable to upload file: the server is not configured with any '. 'writable storage engines.'); } return array( 'upload' => false, 'filePHID' => null, 'error' => $error, ); } } diff --git a/src/applications/files/conduit/FileDownloadConduitAPIMethod.php b/src/applications/files/conduit/FileDownloadConduitAPIMethod.php index ede4a07317..67e9d1f25c 100644 --- a/src/applications/files/conduit/FileDownloadConduitAPIMethod.php +++ b/src/applications/files/conduit/FileDownloadConduitAPIMethod.php @@ -1,43 +1,43 @@ 'required phid', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty base64-bytes'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-PHID' => 'No such file exists.', ); } protected function execute(ConduitAPIRequest $request) { $phid = $request->getValue('phid'); $file = id(new PhabricatorFileQuery()) ->setViewer($request->getUser()) ->withPHIDs(array($phid)) ->executeOne(); if (!$file) { throw new ConduitException('ERR-BAD-PHID'); } return base64_encode($file->loadFileData()); } } diff --git a/src/applications/files/conduit/FileInfoConduitAPIMethod.php b/src/applications/files/conduit/FileInfoConduitAPIMethod.php index d95b002b9a..7b8cecbdf1 100644 --- a/src/applications/files/conduit/FileInfoConduitAPIMethod.php +++ b/src/applications/files/conduit/FileInfoConduitAPIMethod.php @@ -1,64 +1,64 @@ 'optional phid', 'id' => 'optional id', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-NOT-FOUND' => 'No such file exists.', ); } protected function execute(ConduitAPIRequest $request) { $phid = $request->getValue('phid'); $id = $request->getValue('id'); $query = id(new PhabricatorFileQuery()) ->setViewer($request->getUser()); if ($id) { $query->withIDs(array($id)); } else { $query->withPHIDs(array($phid)); } $file = $query->executeOne(); if (!$file) { throw new ConduitException('ERR-NOT-FOUND'); } $uri = $file->getInfoURI(); return array( 'id' => $file->getID(), 'phid' => $file->getPHID(), 'objectName' => 'F'.$file->getID(), 'name' => $file->getName(), 'mimeType' => $file->getMimeType(), 'byteSize' => $file->getByteSize(), 'authorPHID' => $file->getAuthorPHID(), 'dateCreated' => $file->getDateCreated(), 'dateModified' => $file->getDateModified(), 'uri' => PhabricatorEnv::getProductionURI($uri), ); } } diff --git a/src/applications/files/conduit/FileQueryChunksConduitAPIMethod.php b/src/applications/files/conduit/FileQueryChunksConduitAPIMethod.php index 6f5db0733c..e0929ef8c4 100644 --- a/src/applications/files/conduit/FileQueryChunksConduitAPIMethod.php +++ b/src/applications/files/conduit/FileQueryChunksConduitAPIMethod.php @@ -1,47 +1,43 @@ 'phid', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $file_phid = $request->getValue('filePHID'); $file = $this->loadFileByPHID($viewer, $file_phid); $chunks = $this->loadFileChunks($viewer, $file); $results = array(); foreach ($chunks as $chunk) { $results[] = array( 'byteStart' => $chunk->getByteStart(), 'byteEnd' => $chunk->getByteEnd(), 'complete' => (bool)$chunk->getDataFilePHID(), ); } return $results; } } diff --git a/src/applications/files/conduit/FileUploadChunkConduitAPIMethod.php b/src/applications/files/conduit/FileUploadChunkConduitAPIMethod.php index c26ab42124..e3444d05e2 100644 --- a/src/applications/files/conduit/FileUploadChunkConduitAPIMethod.php +++ b/src/applications/files/conduit/FileUploadChunkConduitAPIMethod.php @@ -1,77 +1,73 @@ 'phid', 'byteStart' => 'int', 'data' => 'string', 'dataEncoding' => 'string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $file_phid = $request->getValue('filePHID'); $file = $this->loadFileByPHID($viewer, $file_phid); $start = $request->getValue('byteStart'); $data = $request->getValue('data'); $encoding = $request->getValue('dataEncoding'); switch ($encoding) { case 'base64': $data = $this->decodeBase64($data); break; case null: break; default: throw new Exception(pht('Unsupported data encoding.')); } $length = strlen($data); $chunk = $this->loadFileChunkForUpload( $viewer, $file, $start, $start + $length); // NOTE: These files have a view policy which prevents normal access. They // are only accessed through the storage engine. $chunk_data = PhabricatorFile::newFromFileData( $data, array( 'name' => $file->getMonogram().'.chunk-'.$chunk->getID(), 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, )); $chunk->setDataFilePHID($chunk_data->getPHID())->save(); $missing = $this->loadAnyMissingChunk($viewer, $file); if (!$missing) { $file->setIsPartial(0)->save(); } return null; } } diff --git a/src/applications/files/conduit/FileUploadConduitAPIMethod.php b/src/applications/files/conduit/FileUploadConduitAPIMethod.php index 3601594dad..594b53bcce 100644 --- a/src/applications/files/conduit/FileUploadConduitAPIMethod.php +++ b/src/applications/files/conduit/FileUploadConduitAPIMethod.php @@ -1,54 +1,49 @@ 'required nonempty base64-bytes', 'name' => 'optional string', 'viewPolicy' => 'optional valid policy string or ', 'canCDN' => 'optional bool', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty guid'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $name = $request->getValue('name'); $can_cdn = $request->getValue('canCDN'); $view_policy = $request->getValue('viewPolicy'); $data = $request->getValue('data_base64'); $data = $this->decodeBase64($data); $file = PhabricatorFile::newFromFileData( $data, array( 'name' => $name, 'authorPHID' => $viewer->getPHID(), 'viewPolicy' => $view_policy, 'canCDN' => $can_cdn, 'isExplicitUpload' => true, )); return $file->getPHID(); } } diff --git a/src/applications/files/conduit/FileUploadHashConduitAPIMethod.php b/src/applications/files/conduit/FileUploadHashConduitAPIMethod.php index 8d1132c022..2ab13dc350 100644 --- a/src/applications/files/conduit/FileUploadHashConduitAPIMethod.php +++ b/src/applications/files/conduit/FileUploadHashConduitAPIMethod.php @@ -1,48 +1,43 @@ 'required nonempty string', 'name' => 'required nonempty string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'phid or null'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $hash = $request->getValue('hash'); $name = $request->getValue('name'); $user = $request->getUser(); $file = PhabricatorFile::newFileFromContentHash( $hash, array( 'name' => $name, 'authorPHID' => $user->getPHID(), )); if ($file) { return $file->getPHID(); } return $file; } } diff --git a/src/applications/flag/conduit/FlagDeleteConduitAPIMethod.php b/src/applications/flag/conduit/FlagDeleteConduitAPIMethod.php index fe10f35467..6634fa7769 100644 --- a/src/applications/flag/conduit/FlagDeleteConduitAPIMethod.php +++ b/src/applications/flag/conduit/FlagDeleteConduitAPIMethod.php @@ -1,60 +1,60 @@ 'optional id', 'objectPHID' => 'optional phid', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict | null'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_NOT_FOUND' => 'Bad flag ID.', 'ERR_WRONG_USER' => 'You are not the creator of this flag.', 'ERR_NEED_PARAM' => 'Must pass an id or an objectPHID.', ); } protected function execute(ConduitAPIRequest $request) { $id = $request->getValue('id'); $object = $request->getValue('objectPHID'); if ($id) { $flag = id(new PhabricatorFlag())->load($id); if (!$flag) { throw new ConduitException('ERR_NOT_FOUND'); } if ($flag->getOwnerPHID() != $request->getUser()->getPHID()) { throw new ConduitException('ERR_WRONG_USER'); } } else if ($object) { $flag = id(new PhabricatorFlag())->loadOneWhere( 'objectPHID = %s AND ownerPHID = %s', $object, $request->getUser()->getPHID()); if (!$flag) { return null; } } else { throw new ConduitException('ERR_NEED_PARAM'); } $this->attachHandleToFlag($flag, $request->getUser()); $ret = $this->buildFlagInfoDictionary($flag); $flag->delete(); return $ret; } } diff --git a/src/applications/flag/conduit/FlagEditConduitAPIMethod.php b/src/applications/flag/conduit/FlagEditConduitAPIMethod.php index 4629ae942a..5a07cdeb9d 100644 --- a/src/applications/flag/conduit/FlagEditConduitAPIMethod.php +++ b/src/applications/flag/conduit/FlagEditConduitAPIMethod.php @@ -1,65 +1,60 @@ 'required phid', 'color' => 'optional int', 'note' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $user = $request->getUser()->getPHID(); $phid = $request->getValue('objectPHID'); $new = false; $flag = id(new PhabricatorFlag())->loadOneWhere( 'objectPHID = %s AND ownerPHID = %s', $phid, $user); if ($flag) { $params = $request->getAllParameters(); if (isset($params['color'])) { $flag->setColor($params['color']); } if (isset($params['note'])) { $flag->setNote($params['note']); } } else { $default_color = PhabricatorFlagColor::COLOR_BLUE; $flag = id(new PhabricatorFlag()) ->setOwnerPHID($user) ->setType(phid_get_type($phid)) ->setObjectPHID($phid) ->setReasonPHID($user) ->setColor($request->getValue('color', $default_color)) ->setNote($request->getValue('note', '')); $new = true; } $this->attachHandleToFlag($flag, $request->getUser()); $flag->save(); $ret = $this->buildFlagInfoDictionary($flag); $ret['new'] = $new; return $ret; } } diff --git a/src/applications/flag/conduit/FlagQueryConduitAPIMethod.php b/src/applications/flag/conduit/FlagQueryConduitAPIMethod.php index fd4c48c3c4..486a5afa9c 100644 --- a/src/applications/flag/conduit/FlagQueryConduitAPIMethod.php +++ b/src/applications/flag/conduit/FlagQueryConduitAPIMethod.php @@ -1,67 +1,62 @@ 'optional list', 'types' => 'optional list', 'objectPHIDs' => 'optional list', 'offset' => 'optional int', 'limit' => 'optional int (default = 100)', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $query = new PhabricatorFlagQuery(); $query->setViewer($request->getUser()); $owner_phids = $request->getValue('ownerPHIDs', array()); if ($owner_phids) { $query->withOwnerPHIDs($owner_phids); } $object_phids = $request->getValue('objectPHIDs', array()); if ($object_phids) { $query->withObjectPHIDs($object_phids); } $types = $request->getValue('types', array()); if ($types) { $query->withTypes($types); } $query->needHandles(true); $query->setOffset($request->getValue('offset', 0)); $query->setLimit($request->getValue('limit', 100)); $flags = $query->execute(); $results = array(); foreach ($flags as $flag) { $results[] = $this->buildFlagInfoDictionary($flag); } return $results; } } diff --git a/src/applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php index e0cd69d8be..818b983538 100644 --- a/src/applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php +++ b/src/applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php @@ -1,95 +1,91 @@ 'optional list', 'phids' => 'optional list', 'buildablePHIDs' => 'optional list', 'containerPHIDs' => 'optional list', 'manualBuildables' => 'optional bool', ) + self::getPagerParamTypes(); } - public function defineReturnType() { + protected function defineReturnType() { return 'wild'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $query = id(new HarbormasterBuildableQuery()) ->setViewer($viewer); $ids = $request->getValue('ids'); if ($ids !== null) { $query->withIDs($ids); } $phids = $request->getValue('phids'); if ($phids !== null) { $query->withPHIDs($phids); } $buildable_phids = $request->getValue('buildablePHIDs'); if ($buildable_phids !== null) { $query->withBuildablePHIDs($buildable_phids); } $container_phids = $request->getValue('containerPHIDs'); if ($container_phids !== null) { $query->withContainerPHIDs($container_phids); } $manual = $request->getValue('manualBuildables'); if ($manual !== null) { $query->withManualBuildables($manual); } $pager = $this->newPager($request); $buildables = $query->executeWithCursorPager($pager); $data = array(); foreach ($buildables as $buildable) { $monogram = $buildable->getMonogram(); $status = $buildable->getBuildableStatus(); $status_name = HarbormasterBuildable::getBuildableStatusName($status); $data[] = array( 'id' => $buildable->getID(), 'phid' => $buildable->getPHID(), 'monogram' => $monogram, 'uri' => PhabricatorEnv::getProductionURI('/'.$monogram), 'buildableStatus' => $status, 'buildableStatusName' => $status_name, 'buildablePHID' => $buildable->getBuildablePHID(), 'containerPHID' => $buildable->getContainerPHID(), 'isManualBuildable' => (bool)$buildable->getIsManualBuildable(), ); } $results = array( 'data' => $data, ); $results = $this->addPagerResults($results, $pager); return $results; } } diff --git a/src/applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php index dfb8a7109d..95628500c1 100644 --- a/src/applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php +++ b/src/applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php @@ -1,94 +1,90 @@ 'optional list', 'phids' => 'optional list', 'buildStatuses' => 'optional list', 'buildablePHIDs' => 'optional list', 'buildPlanPHIDs' => 'optional list', ) + self::getPagerParamTypes(); } - public function defineReturnType() { + protected function defineReturnType() { return 'wild'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $query = id(new HarbormasterBuildQuery()) ->setViewer($viewer); $ids = $request->getValue('ids'); if ($ids !== null) { $query->withIDs($ids); } $phids = $request->getValue('phids'); if ($phids !== null) { $query->withPHIDs($phids); } $statuses = $request->getValue('buildStatuses'); if ($statuses !== null) { $query->withBuildStatuses($statuses); } $buildable_phids = $request->getValue('buildablePHIDs'); if ($buildable_phids !== null) { $query->withBuildablePHIDs($buildable_phids); } $build_plan_phids = $request->getValue('buildPlanPHIDs'); if ($build_plan_phids !== null) { $query->withBuildPlanPHIDs($build_plan_phids); } $pager = $this->newPager($request); $builds = $query->executeWithCursorPager($pager); $data = array(); foreach ($builds as $build) { $id = $build->getID(); $uri = '/harbormaster/build/'.$id.'/'; $status = $build->getBuildStatus(); $data[] = array( 'id' => $id, 'phid' => $build->getPHID(), 'uri' => PhabricatorEnv::getProductionURI($uri), 'name' => $build->getBuildPlan()->getName(), 'buildablePHID' => $build->getBuildablePHID(), 'buildPlanPHID' => $build->getBuildPlanPHID(), 'buildStatus' => $status, 'buildStatusName' => HarbormasterBuild::getBuildStatusName($status), ); } $results = array( 'data' => $data, ); $results = $this->addPagerResults($results, $pager); return $results; } } diff --git a/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php index 3eafe55259..f6752f08fb 100644 --- a/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php +++ b/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php @@ -1,63 +1,59 @@ formatStringConstants(array('pass', 'fail')); return array( 'buildTargetPHID' => 'required phid', 'type' => 'required '.$type_const, ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $build_target_phid = $request->getValue('buildTargetPHID'); $message_type = $request->getValue('type'); $build_target = id(new HarbormasterBuildTargetQuery()) ->setViewer($viewer) ->withPHIDs(array($build_target_phid)) ->executeOne(); if (!$build_target) { throw new Exception(pht('No such build target!')); } $message = HarbormasterBuildMessage::initializeNewMessage($viewer) ->setBuildTargetPHID($build_target->getPHID()) ->setType($message_type) ->save(); // If the build has completely paused because all steps are blocked on // waiting targets, this will resume it. PhabricatorWorker::scheduleTask( 'HarbormasterBuildWorker', array( 'buildID' => $build_target->getBuild()->getID(), )); return null; } } diff --git a/src/applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php b/src/applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php index 05f14b1677..308f1c3b3a 100644 --- a/src/applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php +++ b/src/applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php @@ -1,57 +1,57 @@ 'string', 'upperText' => 'optional string', 'lowerText' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'string'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-NOT-FOUND' => 'Macro was not found.', ); } protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $macro_name = $request->getValue('macroName'); $upper_text = $request->getValue('upperText'); $lower_text = $request->getValue('lowerText'); $uri = PhabricatorMacroMemeController::generateMacro( $user, $macro_name, $upper_text, $lower_text); if (!$uri) { throw new ConduitException('ERR-NOT-FOUND'); } return array( 'uri' => $uri, ); } } diff --git a/src/applications/macro/conduit/MacroQueryConduitAPIMethod.php b/src/applications/macro/conduit/MacroQueryConduitAPIMethod.php index 29702c8dc1..66cdedeebc 100644 --- a/src/applications/macro/conduit/MacroQueryConduitAPIMethod.php +++ b/src/applications/macro/conduit/MacroQueryConduitAPIMethod.php @@ -1,84 +1,79 @@ 'optional list', 'phids' => 'optional list', 'ids' => 'optional list', 'names' => 'optional list', 'nameLike' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $query = id(new PhabricatorMacroQuery()) ->setViewer($request->getUser()) ->needFiles(true); $author_phids = $request->getValue('authorPHIDs'); $phids = $request->getValue('phids'); $ids = $request->getValue('ids'); $name_like = $request->getValue('nameLike'); $names = $request->getValue('names'); if ($author_phids) { $query->withAuthorPHIDs($author_phids); } if ($phids) { $query->withPHIDs($phids); } if ($ids) { $query->withIDs($ids); } if ($name_like) { $query->withNameLike($name_like); } if ($names) { $query->withNames($names); } $macros = $query->execute(); if (!$macros) { return array(); } $results = array(); foreach ($macros as $macro) { $file = $macro->getFile(); $results[$macro->getName()] = array( 'uri' => $file->getBestURI(), 'phid' => $macro->getPHID(), 'authorPHID' => $file->getAuthorPHID(), 'dateCreated' => $file->getDateCreated(), 'filePHID' => $file->getPHID(), ); } return $results; } } diff --git a/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php index b89369f692..39067f7cbd 100644 --- a/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php @@ -1,322 +1,322 @@ 'Missing or malformed parameter.', ); } protected function buildTaskInfoDictionary(ManiphestTask $task) { $results = $this->buildTaskInfoDictionaries(array($task)); return idx($results, $task->getPHID()); } protected function getTaskFields($is_new) { $fields = array(); if (!$is_new) { $fields += array( 'id' => 'optional int', 'phid' => 'optional int', ); } $fields += array( 'title' => $is_new ? 'required string' : 'optional string', 'description' => 'optional string', 'ownerPHID' => 'optional phid', 'viewPolicy' => 'optional phid or policy string', 'editPolicy' => 'optional phid or policy string', 'ccPHIDs' => 'optional list', 'priority' => 'optional int', 'projectPHIDs' => 'optional list', 'auxiliary' => 'optional dict', ); if (!$is_new) { $fields += array( 'status' => 'optional string', 'comments' => 'optional string', ); } return $fields; } protected function applyRequest( ManiphestTask $task, ConduitAPIRequest $request, $is_new) { $changes = array(); if ($is_new) { $task->setTitle((string)$request->getValue('title')); $task->setDescription((string)$request->getValue('description')); $changes[ManiphestTransaction::TYPE_STATUS] = ManiphestTaskStatus::getDefaultStatus(); $changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] = array('+' => array($request->getUser()->getPHID())); } else { $comments = $request->getValue('comments'); if (!$is_new && $comments !== null) { $changes[PhabricatorTransactions::TYPE_COMMENT] = null; } $title = $request->getValue('title'); if ($title !== null) { $changes[ManiphestTransaction::TYPE_TITLE] = $title; } $desc = $request->getValue('description'); if ($desc !== null) { $changes[ManiphestTransaction::TYPE_DESCRIPTION] = $desc; } $status = $request->getValue('status'); if ($status !== null) { $valid_statuses = ManiphestTaskStatus::getTaskStatusMap(); if (!isset($valid_statuses[$status])) { throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription('Status set to invalid value.'); } $changes[ManiphestTransaction::TYPE_STATUS] = $status; } } $priority = $request->getValue('priority'); if ($priority !== null) { $valid_priorities = ManiphestTaskPriority::getTaskPriorityMap(); if (!isset($valid_priorities[$priority])) { throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription('Priority set to invalid value.'); } $changes[ManiphestTransaction::TYPE_PRIORITY] = $priority; } $owner_phid = $request->getValue('ownerPHID'); if ($owner_phid !== null) { $this->validatePHIDList( array($owner_phid), PhabricatorPeopleUserPHIDType::TYPECONST, 'ownerPHID'); $changes[ManiphestTransaction::TYPE_OWNER] = $owner_phid; } $ccs = $request->getValue('ccPHIDs'); if ($ccs !== null) { $changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] = array('=' => array_fuse($ccs)); } $transactions = array(); $view_policy = $request->getValue('viewPolicy'); if ($view_policy !== null) { $transactions[] = id(new ManiphestTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($view_policy); } $edit_policy = $request->getValue('editPolicy'); if ($edit_policy !== null) { $transactions[] = id(new ManiphestTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) ->setNewValue($edit_policy); } $project_phids = $request->getValue('projectPHIDs'); if ($project_phids !== null) { $this->validatePHIDList( $project_phids, PhabricatorProjectProjectPHIDType::TYPECONST, 'projectPHIDS'); $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $transactions[] = id(new ManiphestTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue('edge:type', $project_type) ->setNewValue( array( '=' => array_fuse($project_phids), )); } $template = new ManiphestTransaction(); foreach ($changes as $type => $value) { $transaction = clone $template; $transaction->setTransactionType($type); if ($type == PhabricatorTransactions::TYPE_COMMENT) { $transaction->attachComment( id(new ManiphestTransactionComment()) ->setContent($comments)); } else { $transaction->setNewValue($value); } $transactions[] = $transaction; } $field_list = PhabricatorCustomField::getObjectFields( $task, PhabricatorCustomField::ROLE_EDIT); $field_list->readFieldsFromStorage($task); $auxiliary = $request->getValue('auxiliary'); if ($auxiliary) { foreach ($field_list->getFields() as $key => $field) { if (!array_key_exists($key, $auxiliary)) { continue; } $transaction = clone $template; $transaction->setTransactionType( PhabricatorTransactions::TYPE_CUSTOMFIELD); $transaction->setMetadataValue('customfield:key', $key); $transaction->setOldValue( $field->getOldValueForApplicationTransactions()); $transaction->setNewValue($auxiliary[$key]); $transactions[] = $transaction; } } if (!$transactions) { return; } $event = new PhabricatorEvent( PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK, array( 'task' => $task, 'new' => $is_new, 'transactions' => $transactions, )); $event->setUser($request->getUser()); $event->setConduitRequest($request); PhutilEventEngine::dispatchEvent($event); $task = $event->getValue('task'); $transactions = $event->getValue('transactions'); $content_source = PhabricatorContentSource::newForSource( PhabricatorContentSource::SOURCE_CONDUIT, array()); $editor = id(new ManiphestTransactionEditor()) ->setActor($request->getUser()) ->setContentSource($content_source) ->setContinueOnNoEffect(true); if (!$is_new) { $editor->setContinueOnMissingFields(true); } $editor->applyTransactions($task, $transactions); $event = new PhabricatorEvent( PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK, array( 'task' => $task, 'new' => $is_new, 'transactions' => $transactions, )); $event->setUser($request->getUser()); $event->setConduitRequest($request); PhutilEventEngine::dispatchEvent($event); // reload the task now that we've done all the fun stuff return id(new ManiphestTaskQuery()) ->setViewer($request->getUser()) ->withPHIDs(array($task->getPHID())) ->needSubscriberPHIDs(true) ->needProjectPHIDs(true) ->executeOne(); } protected function buildTaskInfoDictionaries(array $tasks) { assert_instances_of($tasks, 'ManiphestTask'); if (!$tasks) { return array(); } $task_phids = mpull($tasks, 'getPHID'); $all_deps = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs($task_phids) ->withEdgeTypes(array(ManiphestTaskDependsOnTaskEdgeType::EDGECONST)); $all_deps->execute(); $result = array(); foreach ($tasks as $task) { // TODO: Batch this get as CustomField gets cleaned up. $field_list = PhabricatorCustomField::getObjectFields( $task, PhabricatorCustomField::ROLE_EDIT); $field_list->readFieldsFromStorage($task); $auxiliary = mpull( $field_list->getFields(), 'getValueForStorage', 'getFieldKey'); $task_deps = $all_deps->getDestinationPHIDs( array($task->getPHID()), array(ManiphestTaskDependsOnTaskEdgeType::EDGECONST)); $result[$task->getPHID()] = array( 'id' => $task->getID(), 'phid' => $task->getPHID(), 'authorPHID' => $task->getAuthorPHID(), 'ownerPHID' => $task->getOwnerPHID(), 'ccPHIDs' => $task->getSubscriberPHIDs(), 'status' => $task->getStatus(), 'statusName' => ManiphestTaskStatus::getTaskStatusName( $task->getStatus()), 'isClosed' => $task->isClosed(), 'priority' => ManiphestTaskPriority::getTaskPriorityName( $task->getPriority()), 'priorityColor' => ManiphestTaskPriority::getTaskPriorityColor( $task->getPriority()), 'title' => $task->getTitle(), 'description' => $task->getDescription(), 'projectPHIDs' => $task->getProjectPHIDs(), 'uri' => PhabricatorEnv::getProductionURI('/T'.$task->getID()), 'auxiliary' => $auxiliary, 'objectName' => 'T'.$task->getID(), 'dateCreated' => $task->getDateCreated(), 'dateModified' => $task->getDateModified(), 'dependsOnTaskPHIDs' => $task_deps, ); } return $result; } /** * NOTE: This is a temporary stop gap since its easy to make malformed tasks. * Long-term, the values set in @{method:defineParamTypes} will be used to * validate data implicitly within the larger Conduit application. * * TODO: Remove this in favor of generalized Conduit hotness. */ private function validatePHIDList(array $phid_list, $phid_type, $field) { $phid_groups = phid_group_by_type($phid_list); unset($phid_groups[$phid_type]); if (!empty($phid_groups)) { throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription('One or more PHIDs were invalid for '.$field.'.'); } return true; } } diff --git a/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php index 4abcaecb6a..ed16f77495 100644 --- a/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestCreateTaskConduitAPIMethod.php @@ -1,36 +1,36 @@ getTaskFields($is_new = true); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-INVALID-PARAMETER' => 'Missing or malformed parameter.', ); } protected function execute(ConduitAPIRequest $request) { $task = ManiphestTask::initializeNewTask($request->getUser()); $task = $this->applyRequest($task, $request, $is_new = true); return $this->buildTaskInfoDictionary($task); } } diff --git a/src/applications/maniphest/conduit/ManiphestGetTaskTransactionsConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestGetTaskTransactionsConduitAPIMethod.php index cf3f755314..ff51dafd7d 100644 --- a/src/applications/maniphest/conduit/ManiphestGetTaskTransactionsConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestGetTaskTransactionsConduitAPIMethod.php @@ -1,80 +1,75 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list>'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $results = array(); $task_ids = $request->getValue('ids'); if (!$task_ids) { return $results; } $tasks = id(new ManiphestTaskQuery()) ->setViewer($request->getUser()) ->withIDs($task_ids) ->execute(); $tasks = mpull($tasks, null, 'getPHID'); $transactions = array(); if ($tasks) { $transactions = id(new ManiphestTransactionQuery()) ->setViewer($request->getUser()) ->withObjectPHIDs(mpull($tasks, 'getPHID')) ->needComments(true) ->execute(); } foreach ($transactions as $transaction) { $task_phid = $transaction->getObjectPHID(); if (empty($tasks[$task_phid])) { continue; } $task_id = $tasks[$task_phid]->getID(); $comments = null; if ($transaction->hasComment()) { $comments = $transaction->getComment()->getContent(); } $results[$task_id][] = array( 'taskID' => $task_id, 'transactionPHID' => $transaction->getPHID(), 'transactionType' => $transaction->getTransactionType(), 'oldValue' => $transaction->getOldValue(), 'newValue' => $transaction->getNewValue(), 'comments' => $comments, 'authorPHID' => $transaction->getAuthorPHID(), 'dateCreated' => $transaction->getDateCreated(), ); } return $results; } } diff --git a/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php index b998bb627c..5915e74fb5 100644 --- a/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php @@ -1,45 +1,45 @@ 'required id', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_TASK' => 'No such maniphest task exists', ); } protected function execute(ConduitAPIRequest $request) { $task_id = $request->getValue('task_id'); $task = id(new ManiphestTaskQuery()) ->setViewer($request->getUser()) ->withIDs(array($task_id)) ->needSubscriberPHIDs(true) ->needProjectPHIDs(true) ->executeOne(); if (!$task) { throw new ConduitException('ERR_BAD_TASK'); } return $this->buildTaskInfoDictionary($task); } } diff --git a/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php index 6c850b8630..a8c687b36e 100644 --- a/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php @@ -1,123 +1,119 @@ formatStringConstants($statuses); $orders = array( ManiphestTaskQuery::ORDER_PRIORITY, ManiphestTaskQuery::ORDER_CREATED, ManiphestTaskQuery::ORDER_MODIFIED, ); $order_const = $this->formatStringConstants($orders); return array( 'ids' => 'optional list', 'phids' => 'optional list', 'ownerPHIDs' => 'optional list', 'authorPHIDs' => 'optional list', 'projectPHIDs' => 'optional list', 'ccPHIDs' => 'optional list', 'fullText' => 'optional string', 'status' => 'optional '.$status_const, 'order' => 'optional '.$order_const, 'limit' => 'optional int', 'offset' => 'optional int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $query = id(new ManiphestTaskQuery()) ->setViewer($request->getUser()) ->needProjectPHIDs(true) ->needSubscriberPHIDs(true); $task_ids = $request->getValue('ids'); if ($task_ids) { $query->withIDs($task_ids); } $task_phids = $request->getValue('phids'); if ($task_phids) { $query->withPHIDs($task_phids); } $owners = $request->getValue('ownerPHIDs'); if ($owners) { $query->withOwners($owners); } $authors = $request->getValue('authorPHIDs'); if ($authors) { $query->withAuthors($authors); } $projects = $request->getValue('projectPHIDs'); if ($projects) { $query->withAllProjects($projects); } $ccs = $request->getValue('ccPHIDs'); if ($ccs) { $query->withSubscribers($ccs); } $full_text = $request->getValue('fullText'); if ($full_text) { $query->withFullTextSearch($full_text); } $status = $request->getValue('status'); if ($status) { $query->withStatus($status); } $order = $request->getValue('order'); if ($order) { $query->setOrderBy($order); } $limit = $request->getValue('limit'); if ($limit) { $query->setLimit($limit); } $offset = $request->getValue('offset'); if ($offset) { $query->setOffset($offset); } $results = $query->execute(); return $this->buildTaskInfoDictionaries($results); } } diff --git a/src/applications/maniphest/conduit/ManiphestQueryStatusesConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestQueryStatusesConduitAPIMethod.php index b6d933ae1e..8255b3474c 100644 --- a/src/applications/maniphest/conduit/ManiphestQueryStatusesConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestQueryStatusesConduitAPIMethod.php @@ -1,39 +1,35 @@ '; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $results = array( 'defaultStatus' => ManiphestTaskStatus::getDefaultStatus(), 'defaultClosedStatus' => ManiphestTaskStatus::getDefaultClosedStatus(), 'duplicateStatus' => ManiphestTaskStatus::getDuplicateStatus(), 'openStatuses' => ManiphestTaskStatus::getOpenStatusConstants(), 'closedStatuses' => ManiphestTaskStatus::getClosedStatusConstants(), 'allStatuses' => array_keys(ManiphestTaskStatus::getTaskStatusMap()), 'statusMap' => ManiphestTaskStatus::getTaskStatusMap(), ); return $results; } } diff --git a/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php index 667857a189..a4a6232679 100644 --- a/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php +++ b/src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php @@ -1,65 +1,65 @@ 'No such maniphest task exists.', 'ERR-INVALID-PARAMETER' => 'Missing or malformed parameter.', 'ERR-NO-EFFECT' => 'Update has no effect.', ); } - public function defineParamTypes() { + protected function defineParamTypes() { return $this->getTaskFields($is_new = false); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } protected function execute(ConduitAPIRequest $request) { $id = $request->getValue('id'); $phid = $request->getValue('phid'); if (($id && $phid) || (!$id && !$phid)) { throw new Exception("Specify exactly one of 'id' and 'phid'."); } $query = id (new ManiphestTaskQuery()) ->setViewer($request->getUser()) ->needSubscriberPHIDs(true) ->needProjectPHIDs(true); if ($id) { $query->withIDs(array($id)); } else { $query->withPHIDs(array($phid)); } $task = $query->executeOne(); $params = $request->getAllParameters(); unset($params['id']); unset($params['phid']); if (call_user_func_array('coalesce', $params) === null) { throw new ConduitException('ERR-NO-EFFECT'); } if (!$task) { throw new ConduitException('ERR-BAD-TASK'); } $task = $this->applyRequest($task, $request, $is_new = false); return $this->buildTaskInfoDictionary($task); } } diff --git a/src/applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php b/src/applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php index ae2220834c..2bc5e9151a 100644 --- a/src/applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php +++ b/src/applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php @@ -1,73 +1,73 @@ 'required string', 'sourcePHID' => 'required string', 'ownerPHID' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-NO-REQUESTOR-PHID' => pht('Items must have a requestor.'), 'ERR-NO-SOURCE-PHID' => pht('Items must have a source.'), ); } protected function execute(ConduitAPIRequest $request) { $source_phid = $request->getValue('sourcePHID'); $owner_phid = $request->getValue('ownerPHID'); $requestor_phid = $request->getValue('requestorPHID'); $user = $request->getUser(); $item = NuanceItem::initializeNewItem($user); $xactions = array(); if ($source_phid) { $xactions[] = id(new NuanceItemTransaction()) ->setTransactionType(NuanceItemTransaction::TYPE_SOURCE) ->setNewValue($source_phid); } else { throw new ConduitException('ERR-NO-SOURCE-PHID'); } if ($owner_phid) { $xactions[] = id(new NuanceItemTransaction()) ->setTransactionType(NuanceItemTransaction::TYPE_OWNER) ->setNewValue($owner_phid); } if ($requestor_phid) { $xactions[] = id(new NuanceItemTransaction()) ->setTransactionType(NuanceItemTransaction::TYPE_REQUESTOR) ->setNewValue($requestor_phid); } else { throw new ConduitException('ERR-NO-REQUESTOR-PHID'); } $source = PhabricatorContentSource::newFromConduitRequest($request); $editor = id(new NuanceItemEditor()) ->setActor($user) ->setContentSource($source) ->applyTransactions($item, $xactions); return $item->toDictionary(); } } diff --git a/src/applications/owners/conduit/OwnersQueryConduitAPIMethod.php b/src/applications/owners/conduit/OwnersQueryConduitAPIMethod.php index f0908f83e7..f62c456283 100644 --- a/src/applications/owners/conduit/OwnersQueryConduitAPIMethod.php +++ b/src/applications/owners/conduit/OwnersQueryConduitAPIMethod.php @@ -1,157 +1,157 @@ 'optional string', 'projectOwner' => 'optional string', 'userAffiliated' => 'optional string', 'repositoryCallsign' => 'optional string', 'path' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict dict of package info>'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-INVALID-USAGE' => 'Provide one of a single owner phid (user/project), a single '. 'affiliated user phid (user), or a repository/path.', 'ERR-INVALID-PARAMETER' => 'parameter should be a phid', 'ERR_REP_NOT_FOUND' => 'The repository callsign is not recognized', ); } protected static function queryAll() { return id(new PhabricatorOwnersPackage())->loadAll(); } protected static function queryByOwner($owner) { $is_valid_phid = phid_get_type($owner) == PhabricatorPeopleUserPHIDType::TYPECONST || phid_get_type($owner) == PhabricatorProjectProjectPHIDType::TYPECONST; if (!$is_valid_phid) { throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription( 'Expected user/project PHID for owner, got '.$owner); } $owners = id(new PhabricatorOwnersOwner())->loadAllWhere( 'userPHID = %s', $owner); $package_ids = mpull($owners, 'getPackageID'); $packages = array(); foreach ($package_ids as $id) { $packages[] = id(new PhabricatorOwnersPackage())->load($id); } return $packages; } private static function queryByPath( PhabricatorUser $viewer, $repo_callsign, $path) { $repository = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->withCallsigns(array($repo_callsign)) ->executeOne(); if (!$repository) { throw id(new ConduitException('ERR_REP_NOT_FOUND')) ->setErrorDescription( 'Repository callsign '.$repo_callsign.' not recognized'); } if ($path == null) { return PhabricatorOwnersPackage::loadPackagesForRepository($repository); } else { return PhabricatorOwnersPackage::loadOwningPackages( $repository, $path); } } public static function buildPackageInformationDictionaries($packages) { assert_instances_of($packages, 'PhabricatorOwnersPackage'); $result = array(); foreach ($packages as $package) { $p_owners = $package->loadOwners(); $p_paths = $package->loadPaths(); $owners = array_values(mpull($p_owners, 'getUserPHID')); $paths = array(); foreach ($p_paths as $p) { $paths[] = array($p->getRepositoryPHID(), $p->getPath()); } $result[$package->getPHID()] = array( 'phid' => $package->getPHID(), 'name' => $package->getName(), 'description' => $package->getDescription(), 'primaryOwner' => $package->getPrimaryOwnerPHID(), 'owners' => $owners, 'paths' => $paths, ); } return $result; } protected function execute(ConduitAPIRequest $request) { $is_owner_query = ($request->getValue('userOwner') || $request->getValue('projectOwner')) ? 1 : 0; $is_affiliated_query = $request->getValue('userAffiliated') ? 1 : 0; $repo = $request->getValue('repositoryCallsign'); $path = $request->getValue('path'); $is_path_query = $repo ? 1 : 0; if ($is_owner_query + $is_path_query + $is_affiliated_query === 0) { // if no search terms are provided, return everything $packages = self::queryAll(); } else if ($is_owner_query + $is_path_query + $is_affiliated_query > 1) { // otherwise, exactly one of these should be provided throw new ConduitException('ERR-INVALID-USAGE'); } if ($is_affiliated_query) { $query = id(new PhabricatorOwnersPackageQuery()) ->setViewer($request->getUser()); $query->withOwnerPHIDs(array($request->getValue('userAffiliated'))); $packages = $query->execute(); } else if ($is_owner_query) { $owner = nonempty( $request->getValue('userOwner'), $request->getValue('projectOwner')); $packages = self::queryByOwner($owner); } else if ($is_path_query) { $packages = self::queryByPath($request->getUser(), $repo, $path); } return self::buildPackageInformationDictionaries($packages); } } diff --git a/src/applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php b/src/applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php index c0ec34acf5..07a8b0226e 100644 --- a/src/applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php +++ b/src/applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php @@ -1,124 +1,120 @@ 'optional list', 'phids' => 'optional list', 'needSecrets' => 'optional bool', 'needPublicKeys' => 'optional bool', ) + $this->getPagerParamTypes(); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $query = id(new PassphraseCredentialQuery()) ->setViewer($request->getUser()); if ($request->getValue('ids')) { $query->withIDs($request->getValue('ids')); } if ($request->getValue('phids')) { $query->withPHIDs($request->getValue('phids')); } if ($request->getValue('needSecrets')) { $query->needSecrets(true); } $pager = $this->newPager($request); $credentials = $query->executeWithCursorPager($pager); $results = array(); foreach ($credentials as $credential) { $type = PassphraseCredentialType::getTypeByConstant( $credential->getCredentialType()); if (!$type) { continue; } $public_key = null; if ($request->getValue('needPublicKeys') && $type->hasPublicKey()) { $public_key = $type->getPublicKey( $request->getUser(), $credential); } $secret = null; if ($request->getValue('needSecrets')) { if ($credential->getAllowConduit()) { $secret = $credential->getSecret()->openEnvelope(); } } $material = array(); switch ($credential->getCredentialType()) { case PassphraseCredentialTypeSSHPrivateKeyFile::CREDENTIAL_TYPE: if ($secret) { $material['file'] = $secret; } if ($public_key) { $material['publicKey'] = $public_key; } break; case PassphraseCredentialTypeSSHGeneratedKey::CREDENTIAL_TYPE: case PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE: if ($secret) { $material['privateKey'] = $secret; } if ($public_key) { $material['publicKey'] = $public_key; } break; case PassphraseCredentialTypePassword::CREDENTIAL_TYPE: if ($secret) { $material['password'] = $secret; } break; } if (!$credential->getAllowConduit()) { $material['noAPIAccess'] = pht( 'This credential\'s private material '. 'is not accessible via API calls.'); } $results[$credential->getPHID()] = array( 'id' => $credential->getID(), 'phid' => $credential->getPHID(), 'type' => $credential->getCredentialType(), 'name' => $credential->getName(), 'uri' => PhabricatorEnv::getProductionURI('/'.$credential->getMonogram()), 'monogram' => $credential->getMonogram(), 'username' => $credential->getUsername(), 'material' => $material, ); } $result = array( 'data' => $results, ); return $this->addPagerResults($result, $pager); } } diff --git a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php index f1172f6915..0c09109a2c 100644 --- a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php @@ -1,76 +1,76 @@ 'required string', 'title' => 'optional string', 'language' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-NO-PASTE' => 'Paste may not be empty.', ); } protected function execute(ConduitAPIRequest $request) { $content = $request->getValue('content'); $title = $request->getValue('title'); $language = $request->getValue('language'); if (!strlen($content)) { throw new ConduitException('ERR-NO-PASTE'); } $title = nonempty($title, 'Masterwork From Distant Lands'); $language = nonempty($language, ''); $viewer = $request->getUser(); $paste = PhabricatorPaste::initializeNewPaste($viewer); $file = PhabricatorPasteEditor::initializeFileForPaste( $viewer, $title, $content); $xactions = array(); $xactions[] = id(new PhabricatorPasteTransaction()) ->setTransactionType(PhabricatorPasteTransaction::TYPE_CONTENT) ->setNewValue($file->getPHID()); $xactions[] = id(new PhabricatorPasteTransaction()) ->setTransactionType(PhabricatorPasteTransaction::TYPE_TITLE) ->setNewValue($title); $xactions[] = id(new PhabricatorPasteTransaction()) ->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE) ->setNewValue($language); $editor = id(new PhabricatorPasteEditor()) ->setActor($viewer) ->setContentSourceFromConduitRequest($request); $xactions = $editor->applyTransactions($paste, $xactions); $paste->attachRawContent($content); return $this->buildPasteInfoDictionary($paste); } } diff --git a/src/applications/paste/conduit/PasteInfoConduitAPIMethod.php b/src/applications/paste/conduit/PasteInfoConduitAPIMethod.php index 574391461e..ea39b9c18c 100644 --- a/src/applications/paste/conduit/PasteInfoConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteInfoConduitAPIMethod.php @@ -1,50 +1,50 @@ 'required id', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_PASTE' => 'No such paste exists', ); } protected function execute(ConduitAPIRequest $request) { $paste_id = $request->getValue('paste_id'); $paste = id(new PhabricatorPasteQuery()) ->setViewer($request->getUser()) ->withIDs(array($paste_id)) ->needRawContent(true) ->executeOne(); if (!$paste) { throw new ConduitException('ERR_BAD_PASTE'); } return $this->buildPasteInfoDictionary($paste); } } diff --git a/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php b/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php index a8bcf9c574..b03079245a 100644 --- a/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteQueryConduitAPIMethod.php @@ -1,67 +1,63 @@ 'optional list', 'phids' => 'optional list', 'authorPHIDs' => 'optional list', 'after' => 'optional int', 'limit' => 'optional int, default = 100', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $query = id(new PhabricatorPasteQuery()) ->setViewer($request->getUser()) ->needRawContent(true); if ($request->getValue('ids')) { $query->withIDs($request->getValue('ids')); } if ($request->getValue('phids')) { $query->withPHIDs($request->getValue('phids')); } if ($request->getValue('authorPHIDs')) { $query->withAuthorPHIDs($request->getValue('authorPHIDs')); } if ($request->getValue('after')) { $query->setAfterID($request->getValue('after')); } $limit = $request->getValue('limit', 100); if ($limit) { $query->setLimit($limit); } $pastes = $query->execute(); $results = array(); foreach ($pastes as $paste) { $results[$paste->getPHID()] = $this->buildPasteInfoDictionary($paste); } return $results; } } diff --git a/src/applications/people/conduit/UserAddStatusConduitAPIMethod.php b/src/applications/people/conduit/UserAddStatusConduitAPIMethod.php index d3d9a6c8cf..71ddd26997 100644 --- a/src/applications/people/conduit/UserAddStatusConduitAPIMethod.php +++ b/src/applications/people/conduit/UserAddStatusConduitAPIMethod.php @@ -1,66 +1,66 @@ formatStringConstants(array('away', 'sporadic')); return array( 'fromEpoch' => 'required int', 'toEpoch' => 'required int', 'status' => 'required '.$status_const, 'description' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-EPOCH' => "'toEpoch' must be bigger than 'fromEpoch'.", 'ERR-OVERLAP' => 'There must be no status in any part of the specified epoch.', ); } protected function execute(ConduitAPIRequest $request) { $user_phid = $request->getUser()->getPHID(); $from = $request->getValue('fromEpoch'); $to = $request->getValue('toEpoch'); $status = $request->getValue('status'); $description = $request->getValue('description', ''); try { id(new PhabricatorCalendarEvent()) ->setUserPHID($user_phid) ->setDateFrom($from) ->setDateTo($to) ->setTextStatus($status) ->setDescription($description) ->save(); } catch (PhabricatorCalendarEventInvalidEpochException $e) { throw new ConduitException('ERR-BAD-EPOCH'); } } } diff --git a/src/applications/people/conduit/UserDisableConduitAPIMethod.php b/src/applications/people/conduit/UserDisableConduitAPIMethod.php index 22925b16c6..6d9850a8ab 100644 --- a/src/applications/people/conduit/UserDisableConduitAPIMethod.php +++ b/src/applications/people/conduit/UserDisableConduitAPIMethod.php @@ -1,53 +1,53 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-PERMISSIONS' => 'Only admins can call this method.', 'ERR-BAD-PHID' => 'Non existent user PHID.', ); } protected function execute(ConduitAPIRequest $request) { $actor = $request->getUser(); if (!$actor->getIsAdmin()) { throw new ConduitException('ERR-PERMISSIONS'); } $phids = $request->getValue('phids'); $users = id(new PhabricatorUser())->loadAllWhere( 'phid IN (%Ls)', $phids); if (count($phids) != count($users)) { throw new ConduitException('ERR-BAD-PHID'); } foreach ($users as $user) { id(new PhabricatorUserEditor()) ->setActor($actor) ->disableUser($user, true); } } } diff --git a/src/applications/people/conduit/UserEnableConduitAPIMethod.php b/src/applications/people/conduit/UserEnableConduitAPIMethod.php index 2f785e91d0..e29ce3ca61 100644 --- a/src/applications/people/conduit/UserEnableConduitAPIMethod.php +++ b/src/applications/people/conduit/UserEnableConduitAPIMethod.php @@ -1,53 +1,53 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-PERMISSIONS' => 'Only admins can call this method.', 'ERR-BAD-PHID' => 'Non existent user PHID.', ); } protected function execute(ConduitAPIRequest $request) { $actor = $request->getUser(); if (!$actor->getIsAdmin()) { throw new ConduitException('ERR-PERMISSIONS'); } $phids = $request->getValue('phids'); $users = id(new PhabricatorUser())->loadAllWhere( 'phid IN (%Ls)', $phids); if (count($phids) != count($users)) { throw new ConduitException('ERR-BAD-PHID'); } foreach ($users as $user) { id(new PhabricatorUserEditor()) ->setActor($actor) ->disableUser($user, false); } } } diff --git a/src/applications/people/conduit/UserFindConduitAPIMethod.php b/src/applications/people/conduit/UserFindConduitAPIMethod.php index afb6d0c1c8..06f124d3a3 100644 --- a/src/applications/people/conduit/UserFindConduitAPIMethod.php +++ b/src/applications/people/conduit/UserFindConduitAPIMethod.php @@ -1,45 +1,40 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $users = id(new PhabricatorPeopleQuery()) ->setViewer($request->getUser()) ->withUsernames($request->getValue('aliases', array())) ->execute(); return mpull($users, 'getPHID', 'getUsername'); } } diff --git a/src/applications/people/conduit/UserQueryConduitAPIMethod.php b/src/applications/people/conduit/UserQueryConduitAPIMethod.php index 1678c88246..5c81fd5d4f 100644 --- a/src/applications/people/conduit/UserQueryConduitAPIMethod.php +++ b/src/applications/people/conduit/UserQueryConduitAPIMethod.php @@ -1,83 +1,83 @@ 'optional list', 'emails' => 'optional list', 'realnames' => 'optional list', 'phids' => 'optional list', 'ids' => 'optional list', 'offset' => 'optional int', 'limit' => 'optional int (default = 100)', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-INVALID-PARAMETER' => 'Missing or malformed parameter.', ); } protected function execute(ConduitAPIRequest $request) { $usernames = $request->getValue('usernames', array()); $emails = $request->getValue('emails', array()); $realnames = $request->getValue('realnames', array()); $phids = $request->getValue('phids', array()); $ids = $request->getValue('ids', array()); $offset = $request->getValue('offset', 0); $limit = $request->getValue('limit', 100); $query = id(new PhabricatorPeopleQuery()) ->setViewer($request->getUser()) ->needProfileImage(true); if ($usernames) { $query->withUsernames($usernames); } if ($emails) { $query->withEmails($emails); } if ($realnames) { $query->withRealnames($realnames); } if ($phids) { $query->withPHIDs($phids); } if ($ids) { $query->withIDs($ids); } if ($limit) { $query->setLimit($limit); } if ($offset) { $query->setOffset($offset); } $users = $query->execute(); $statuses = id(new PhabricatorCalendarEvent())->loadCurrentStatuses( mpull($users, 'getPHID')); $results = array(); foreach ($users as $user) { $results[] = $this->buildUserInformationDictionary( $user, idx($statuses, $user->getPHID())); } return $results; } } diff --git a/src/applications/people/conduit/UserRemoveStatusConduitAPIMethod.php b/src/applications/people/conduit/UserRemoveStatusConduitAPIMethod.php index 3acabd0916..9cb83db1c1 100644 --- a/src/applications/people/conduit/UserRemoveStatusConduitAPIMethod.php +++ b/src/applications/people/conduit/UserRemoveStatusConduitAPIMethod.php @@ -1,85 +1,85 @@ 'required int', 'toEpoch' => 'required int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'int'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-EPOCH' => "'toEpoch' must be bigger than 'fromEpoch'.", ); } protected function execute(ConduitAPIRequest $request) { $user_phid = $request->getUser()->getPHID(); $from = $request->getValue('fromEpoch'); $to = $request->getValue('toEpoch'); if ($to <= $from) { throw new ConduitException('ERR-BAD-EPOCH'); } $table = new PhabricatorCalendarEvent(); $table->openTransaction(); $table->beginReadLocking(); $overlap = $table->loadAllWhere( 'userPHID = %s AND dateFrom < %d AND dateTo > %d', $user_phid, $to, $from); foreach ($overlap as $status) { if ($status->getDateFrom() < $from) { if ($status->getDateTo() > $to) { // Split the interval. id(new PhabricatorCalendarEvent()) ->setUserPHID($user_phid) ->setDateFrom($to) ->setDateTo($status->getDateTo()) ->setStatus($status->getStatus()) ->setDescription($status->getDescription()) ->save(); } $status->setDateTo($from); $status->save(); } else if ($status->getDateTo() > $to) { $status->setDateFrom($to); $status->save(); } else { $status->delete(); } } $table->endReadLocking(); $table->saveTransaction(); return count($overlap); } } diff --git a/src/applications/people/conduit/UserWhoAmIConduitAPIMethod.php b/src/applications/people/conduit/UserWhoAmIConduitAPIMethod.php index 97023bebd4..aa7e3e7f26 100644 --- a/src/applications/people/conduit/UserWhoAmIConduitAPIMethod.php +++ b/src/applications/people/conduit/UserWhoAmIConduitAPIMethod.php @@ -1,39 +1,35 @@ '; } - public function defineErrorTypes() { - return array(); - } - public function getRequiredScope() { return PhabricatorOAuthServerScope::SCOPE_WHOAMI; } protected function execute(ConduitAPIRequest $request) { $person = id(new PhabricatorPeopleQuery()) ->setViewer($request->getUser()) ->needProfileImage(true) ->withPHIDs(array($request->getUser()->getPHID())) ->executeOne(); return $this->buildUserInformationDictionary($person); } } diff --git a/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php b/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php index 2515b185cc..bdee6e2829 100644 --- a/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php +++ b/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php @@ -1,103 +1,103 @@ 'required phid', 'title' => 'required string', 'body' => 'required string', 'phameTitle' => 'optional string', 'bloggerPHID' => 'optional phid', 'isDraft' => 'optional bool', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'), 'ERR-INVALID-BLOG' => pht('Invalid blog PHID or user can not post to blog.'), ); } protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $blog_phid = $request->getValue('blogPHID'); $title = $request->getValue('title'); $body = $request->getValue('body'); $exception_description = array(); if (!$blog_phid) { $exception_description[] = pht('No blog phid.'); } if (!strlen($title)) { $exception_description[] = pht('No post title.'); } if (!strlen($body)) { $exception_description[] = pht('No post body.'); } if ($exception_description) { throw id(new ConduitException('ERR-INVALID-PARAMETER')) ->setErrorDescription(implode("\n", $exception_description)); } $blogger_phid = $request->getValue('bloggerPHID'); if ($blogger_phid) { $blogger = id(new PhabricatorPeopleQuery()) ->setViewer($user) ->withPHIDs(array($blogger_phid)) ->executeOne(); } else { $blogger = $user; } $blog = id(new PhameBlogQuery()) ->setViewer($blogger) ->withPHIDs(array($blog_phid)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_JOIN, )) ->executeOne(); if (!$blog) { throw new ConduitException('ERR-INVALID-BLOG'); } $post = PhamePost::initializePost($blogger, $blog); $is_draft = $request->getValue('isDraft', false); if (!$is_draft) { $post->setDatePublished(time()); $post->setVisibility(PhamePost::VISIBILITY_PUBLISHED); } $post->setTitle($title); $phame_title = $request->getValue( 'phameTitle', id(new PhutilUTF8StringTruncator()) ->setMaximumBytes(64) ->truncateString($title)); $post->setPhameTitle(PhabricatorSlug::normalize($phame_title)); $post->setBody($body); $post->save(); return $post->toDictionary(); } } diff --git a/src/applications/phame/conduit/PhameQueryConduitAPIMethod.php b/src/applications/phame/conduit/PhameQueryConduitAPIMethod.php index a1a4d196e9..8b882674a3 100644 --- a/src/applications/phame/conduit/PhameQueryConduitAPIMethod.php +++ b/src/applications/phame/conduit/PhameQueryConduitAPIMethod.php @@ -1,82 +1,78 @@ 'optional list', 'phids' => 'optional list', 'after' => 'optional int', 'before' => 'optional int', 'limit' => 'optional int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $query = new PhameBlogQuery(); $query->setViewer($request->getUser()); $ids = $request->getValue('ids', array()); if ($ids) { $query->withIDs($ids); } $phids = $request->getValue('phids', array()); if ($phids) { $query->withPHIDs($phids); } $after = $request->getValue('after', null); if ($after !== null) { $query->setAfterID($after); } $before = $request->getValue('before', null); if ($before !== null) { $query->setBeforeID($before); } $limit = $request->getValue('limit', null); if ($limit !== null) { $query->setLimit($limit); } $blogs = $query->execute(); $results = array(); foreach ($blogs as $blog) { $results[] = array( 'id' => $blog->getID(), 'phid' => $blog->getPHID(), 'name' => $blog->getName(), 'description' => $blog->getDescription(), 'domain' => $blog->getDomain(), 'creatorPHID' => $blog->getCreatorPHID(), ); } return $results; } } diff --git a/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php b/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php index 2fd95ec3db..822c32a8da 100644 --- a/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php +++ b/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php @@ -1,108 +1,103 @@ 'optional list', 'phids' => 'optional list', 'blogPHIDs' => 'optional list', 'bloggerPHIDs' => 'optional list', 'phameTitles' => 'optional list', 'published' => 'optional bool', 'publishedAfter' => 'optional date', 'before' => 'optional int', 'after' => 'optional int', 'limit' => 'optional int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $query = new PhamePostQuery(); $query->setViewer($request->getUser()); $ids = $request->getValue('ids', array()); if ($ids) { $query->withIDs($ids); } $phids = $request->getValue('phids', array()); if ($phids) { $query->withPHIDs($phids); } $blog_phids = $request->getValue('blogPHIDs', array()); if ($blog_phids) { $query->withBlogPHIDs($blog_phids); } $blogger_phids = $request->getValue('bloggerPHIDs', array()); if ($blogger_phids) { $query->withBloggerPHIDs($blogger_phids); } $phame_titles = $request->getValue('phameTitles', array()); if ($phame_titles) { $query->withPhameTitles($phame_titles); } $published = $request->getValue('published', null); if ($published === true) { $query->withVisibility(PhamePost::VISIBILITY_PUBLISHED); } else if ($published === false) { $query->withVisibility(PhamePost::VISIBILITY_DRAFT); } $published_after = $request->getValue('publishedAfter', null); if ($published_after !== null) { $query->withPublishedAfter($published_after); } $after = $request->getValue('after', null); if ($after !== null) { $query->setAfterID($after); } $before = $request->getValue('before', null); if ($before !== null) { $query->setBeforeID($before); } $limit = $request->getValue('limit', null); if ($limit !== null) { $query->setLimit($limit); } $posts = $query->execute(); $results = array(); foreach ($posts as $post) { $results[] = $post->toDictionary(); } return $results; } } diff --git a/src/applications/phid/conduit/PHIDInfoConduitAPIMethod.php b/src/applications/phid/conduit/PHIDInfoConduitAPIMethod.php index 6c6282a4e7..465697c60e 100644 --- a/src/applications/phid/conduit/PHIDInfoConduitAPIMethod.php +++ b/src/applications/phid/conduit/PHIDInfoConduitAPIMethod.php @@ -1,52 +1,52 @@ 'required phid', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-PHID' => 'No such object exists.', ); } protected function execute(ConduitAPIRequest $request) { $phid = $request->getValue('phid'); $handle = id(new PhabricatorHandleQuery()) ->setViewer($request->getUser()) ->withPHIDs(array($phid)) ->executeOne(); if (!$handle->isComplete()) { throw new ConduitException('ERR-BAD-PHID'); } return $this->buildHandleInformationDictionary($handle); } } diff --git a/src/applications/phid/conduit/PHIDLookupConduitAPIMethod.php b/src/applications/phid/conduit/PHIDLookupConduitAPIMethod.php index ecf3f12db2..73196daaff 100644 --- a/src/applications/phid/conduit/PHIDLookupConduitAPIMethod.php +++ b/src/applications/phid/conduit/PHIDLookupConduitAPIMethod.php @@ -1,51 +1,47 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $names = $request->getValue('names'); $query = id(new PhabricatorObjectQuery()) ->setViewer($request->getUser()) ->withNames($names); $query->execute(); $name_map = $query->getNamedResults(); $handles = id(new PhabricatorHandleQuery()) ->setViewer($request->getUser()) ->withPHIDs(mpull($name_map, 'getPHID')) ->execute(); $result = array(); foreach ($name_map as $name => $object) { $phid = $object->getPHID(); $handle = $handles[$phid]; $result[$name] = $this->buildHandleInformationDictionary($handle); } return $result; } } diff --git a/src/applications/phid/conduit/PHIDQueryConduitAPIMethod.php b/src/applications/phid/conduit/PHIDQueryConduitAPIMethod.php index e90f605985..69b6fe0acb 100644 --- a/src/applications/phid/conduit/PHIDQueryConduitAPIMethod.php +++ b/src/applications/phid/conduit/PHIDQueryConduitAPIMethod.php @@ -1,45 +1,41 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $phids = $request->getValue('phids'); $handles = id(new PhabricatorHandleQuery()) ->setViewer($request->getUser()) ->withPHIDs($phids) ->execute(); $result = array(); foreach ($handles as $phid => $handle) { if ($handle->isComplete()) { $result[$phid] = $this->buildHandleInformationDictionary($handle); } } return $result; } } diff --git a/src/applications/phragment/conduit/PhragmentGetPatchConduitAPIMethod.php b/src/applications/phragment/conduit/PhragmentGetPatchConduitAPIMethod.php index 186b8ed4c8..dedf00e1eb 100644 --- a/src/applications/phragment/conduit/PhragmentGetPatchConduitAPIMethod.php +++ b/src/applications/phragment/conduit/PhragmentGetPatchConduitAPIMethod.php @@ -1,189 +1,189 @@ 'required string', 'state' => 'required dict', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_FRAGMENT' => 'No such fragment exists', ); } protected function execute(ConduitAPIRequest $request) { $path = $request->getValue('path'); $state = $request->getValue('state'); // The state is an array mapping file paths to hashes. $patches = array(); // We need to get all of the mappings (like phragment.getstate) first // so that we can detect deletions and creations of files. $fragment = id(new PhragmentFragmentQuery()) ->setViewer($request->getUser()) ->withPaths(array($path)) ->executeOne(); if ($fragment === null) { throw new ConduitException('ERR_BAD_FRAGMENT'); } $mappings = $fragment->getFragmentMappings( $request->getUser(), $fragment->getPath()); $file_phids = mpull(mpull($mappings, 'getLatestVersion'), 'getFilePHID'); $files = id(new PhabricatorFileQuery()) ->setViewer($request->getUser()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); // Scan all of the files that the caller currently has and iterate // over that. foreach ($state as $path => $hash) { // If $mappings[$path] exists, then the user has the file and it's // also a fragment. if (array_key_exists($path, $mappings)) { $file_phid = $mappings[$path]->getLatestVersion()->getFilePHID(); if ($file_phid !== null) { // If the file PHID is present, then we need to check the // hashes to see if they are the same. $hash_caller = strtolower($state[$path]); $hash_current = $files[$file_phid]->getContentHash(); if ($hash_caller === $hash_current) { // The user's version is identical to our version, so // there is no update needed. } else { // The hash differs, and the user needs to update. $patches[] = array( 'path' => $path, 'fileOld' => null, 'fileNew' => $files[$file_phid], 'hashOld' => $hash_caller, 'hashNew' => $hash_current, 'patchURI' => null, ); } } else { // We have a record of this as a file, but there is no file // attached to the latest version, so we consider this to be // a deletion. $patches[] = array( 'path' => $path, 'fileOld' => null, 'fileNew' => null, 'hashOld' => $hash_caller, 'hashNew' => PhragmentPatchUtil::EMPTY_HASH, 'patchURI' => null, ); } } else { // If $mappings[$path] does not exist, then the user has a file, // and we have absolutely no record of it what-so-ever (we haven't // even recorded a deletion). Assuming most applications will store // some form of data near their own files, this is probably a data // file relevant for the application that is not versioned, so we // don't tell the client to do anything with it. } } // Check the remaining files that we know about but the caller has // not reported. foreach ($mappings as $path => $child) { if (array_key_exists($path, $state)) { // We have already evaluated this above. } else { $file_phid = $mappings[$path]->getLatestVersion()->getFilePHID(); if ($file_phid !== null) { // If the file PHID is present, then this is a new file that // we know about, but the caller does not. We need to tell // the caller to create the file. $hash_current = $files[$file_phid]->getContentHash(); $patches[] = array( 'path' => $path, 'fileOld' => null, 'fileNew' => $files[$file_phid], 'hashOld' => PhragmentPatchUtil::EMPTY_HASH, 'hashNew' => $hash_current, 'patchURI' => null, ); } else { // We have a record of deleting this file, and the caller hasn't // reported it, so they've probably deleted it in a previous // update. } } } // Before we can calculate patches, we need to resolve the old versions // of files so we can draw diffs on them. $hashes = array(); foreach ($patches as $patch) { if ($patch['hashOld'] !== PhragmentPatchUtil::EMPTY_HASH) { $hashes[] = $patch['hashOld']; } } $old_files = array(); if (count($hashes) !== 0) { $old_files = id(new PhabricatorFileQuery()) ->setViewer($request->getUser()) ->withContentHashes($hashes) ->execute(); } $old_files = mpull($old_files, null, 'getContentHash'); foreach ($patches as $key => $patch) { if ($patch['hashOld'] !== PhragmentPatchUtil::EMPTY_HASH) { if (array_key_exists($patch['hashOld'], $old_files)) { $patches[$key]['fileOld'] = $old_files[$patch['hashOld']]; } else { // We either can't see or can't read the old file. $patches[$key]['hashOld'] = PhragmentPatchUtil::EMPTY_HASH; $patches[$key]['fileOld'] = null; } } } // Now run through all of the patch entries, calculate the patches // and return the results. foreach ($patches as $key => $patch) { $data = PhragmentPatchUtil::calculatePatch( $patches[$key]['fileOld'], $patches[$key]['fileNew']); unset($patches[$key]['fileOld']); unset($patches[$key]['fileNew']); $file = PhabricatorFile::buildFromFileDataOrHash( $data, array( 'name' => 'patch.dmp', 'ttl' => time() + 60 * 60 * 24, )); $patches[$key]['patchURI'] = $file->getDownloadURI(); } return $patches; } } diff --git a/src/applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php b/src/applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php index f4aafd3105..27ddac1d36 100644 --- a/src/applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php +++ b/src/applications/phragment/conduit/PhragmentQueryFragmentsConduitAPIMethod.php @@ -1,85 +1,85 @@ 'required list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_FRAGMENT' => 'No such fragment exists', ); } protected function execute(ConduitAPIRequest $request) { $paths = $request->getValue('paths'); $fragments = id(new PhragmentFragmentQuery()) ->setViewer($request->getUser()) ->withPaths($paths) ->execute(); $fragments = mpull($fragments, null, 'getPath'); foreach ($paths as $path) { if (!array_key_exists($path, $fragments)) { throw new ConduitException('ERR_BAD_FRAGMENT'); } } $results = array(); foreach ($fragments as $path => $fragment) { $mappings = $fragment->getFragmentMappings( $request->getUser(), $fragment->getPath()); $file_phids = mpull(mpull($mappings, 'getLatestVersion'), 'getFilePHID'); $files = id(new PhabricatorFileQuery()) ->setViewer($request->getUser()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); $result = array(); foreach ($mappings as $cpath => $child) { $file_phid = $child->getLatestVersion()->getFilePHID(); if (!isset($files[$file_phid])) { // Skip any files we don't have permission to access. continue; } $file = $files[$file_phid]; $cpath = substr($child->getPath(), strlen($fragment->getPath()) + 1); $result[] = array( 'phid' => $child->getPHID(), 'phidVersion' => $child->getLatestVersionPHID(), 'path' => $cpath, 'hash' => $file->getContentHash(), 'version' => $child->getLatestVersion()->getSequence(), 'uri' => $file->getViewURI(), ); } $results[$path] = $result; } return $results; } } diff --git a/src/applications/phrequent/conduit/PhrequentPopConduitAPIMethod.php b/src/applications/phrequent/conduit/PhrequentPopConduitAPIMethod.php index 467d822d9f..59b11bbbdc 100644 --- a/src/applications/phrequent/conduit/PhrequentPopConduitAPIMethod.php +++ b/src/applications/phrequent/conduit/PhrequentPopConduitAPIMethod.php @@ -1,52 +1,47 @@ 'phid', 'stopTime' => 'int', 'note' => 'string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'phid'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $object_phid = $request->getValue('objectPHID'); $timestamp = $request->getValue('stopTime'); $note = $request->getValue('note'); if ($timestamp === null) { $timestamp = time(); } $editor = new PhrequentTrackingEditor(); if (!$object_phid) { return $editor->stopTrackingTop($user, $timestamp, $note); } else { return $editor->stopTracking($user, $object_phid, $timestamp, $note); } } } diff --git a/src/applications/phrequent/conduit/PhrequentPushConduitAPIMethod.php b/src/applications/phrequent/conduit/PhrequentPushConduitAPIMethod.php index d890f8e87c..1095aeab0d 100644 --- a/src/applications/phrequent/conduit/PhrequentPushConduitAPIMethod.php +++ b/src/applications/phrequent/conduit/PhrequentPushConduitAPIMethod.php @@ -1,47 +1,42 @@ 'required phid', 'startTime' => 'int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'phid'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $object_phid = $request->getValue('objectPHID'); $timestamp = $request->getValue('startTime'); if ($timestamp === null) { $timestamp = time(); } $editor = new PhrequentTrackingEditor(); return $editor->startTracking($user, $object_phid, $timestamp); } } diff --git a/src/applications/phrequent/conduit/PhrequentTrackingConduitAPIMethod.php b/src/applications/phrequent/conduit/PhrequentTrackingConduitAPIMethod.php index 07f1e6a43e..f5391a56fb 100644 --- a/src/applications/phrequent/conduit/PhrequentTrackingConduitAPIMethod.php +++ b/src/applications/phrequent/conduit/PhrequentTrackingConduitAPIMethod.php @@ -1,50 +1,45 @@ getUser(); $times = id(new PhrequentUserTimeQuery()) ->setViewer($user) ->needPreemptingEvents(true) ->withEnded(PhrequentUserTimeQuery::ENDED_NO) ->withUserPHIDs(array($user->getPHID())) ->execute(); $now = time(); $results = id(new PhrequentTimeBlock($times)) ->getCurrentWorkStack($now); return array('data' => $results); } } diff --git a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php index c0aaedf15f..e3d28941bb 100644 --- a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php @@ -1,70 +1,71 @@ 'required string', 'title' => 'required string', 'content' => 'required string', 'description' => 'optional string', ); } - public function defineReturnType() { + + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array( - ); - } + protected function execute(ConduitAPIRequest $request) { $slug = $request->getValue('slug'); if (!strlen($slug)) { throw new Exception(pht('No such document.')); } $doc = id(new PhrictionDocumentQuery()) ->setViewer($request->getUser()) ->withSlugs(array(PhabricatorSlug::normalize($slug))) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if ($doc) { throw new Exception(pht('Document already exists!')); } $doc = PhrictionDocument::initializeNewDocument( $request->getUser(), $slug); $xactions = array(); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_TITLE) ->setNewValue($request->getValue('title')); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) ->setNewValue($request->getValue('content')); $editor = id(new PhrictionTransactionEditor()) ->setActor($request->getUser()) ->setContentSourceFromConduitRequest($request) ->setContinueOnNoEffect(true) ->setDescription($request->getValue('description')); try { $editor->applyTransactions($doc, $xactions); } catch (PhabricatorApplicationTransactionValidationException $ex) { // TODO - some magical hotness via T5873 throw $ex; } return $this->buildDocumentInfoDictionary($doc); } } diff --git a/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php index 1bf6b53395..f9c8c26a3e 100644 --- a/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php @@ -1,72 +1,67 @@ 'required string', 'title' => 'optional string', 'content' => 'optional string', 'description' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $slug = $request->getValue('slug'); $doc = id(new PhrictionDocumentQuery()) ->setViewer($request->getUser()) ->withSlugs(array(PhabricatorSlug::normalize($slug))) ->needContent(true) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$doc) { throw new Exception(pht('No such document.')); } $xactions = array(); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_TITLE) ->setNewValue($request->getValue('title')); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) ->setNewValue($request->getValue('content')); $editor = id(new PhrictionTransactionEditor()) ->setActor($request->getUser()) ->setContentSourceFromConduitRequest($request) ->setContinueOnNoEffect(true) ->setDescription($request->getValue('description')); try { $editor->applyTransactions($doc, $xactions); } catch (PhabricatorApplicationTransactionValidationException $ex) { // TODO - some magical hotness via T5873 throw $ex; } return $this->buildDocumentInfoDictionary($doc); } } diff --git a/src/applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php index 372ff4a97c..9a4098b5bd 100644 --- a/src/applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php @@ -1,53 +1,53 @@ 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty list'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-DOCUMENT' => 'No such document exists.', ); } protected function execute(ConduitAPIRequest $request) { $slug = $request->getValue('slug'); $doc = id(new PhrictionDocumentQuery()) ->setViewer($request->getUser()) ->withSlugs(array(PhabricatorSlug::normalize($slug))) ->executeOne(); if (!$doc) { throw new ConduitException('ERR-BAD-DOCUMENT'); } $content = id(new PhrictionContent())->loadAllWhere( 'documentID = %d ORDER BY version DESC', $doc->getID()); $results = array(); foreach ($content as $version) { $results[] = $this->buildDocumentContentDictionary( $doc, $version); } return $results; } } diff --git a/src/applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php index 33eb51dffe..14e424e83c 100644 --- a/src/applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php @@ -1,46 +1,46 @@ 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-BAD-DOCUMENT' => 'No such document exists.', ); } protected function execute(ConduitAPIRequest $request) { $slug = $request->getValue('slug'); $document = id(new PhrictionDocumentQuery()) ->setViewer($request->getUser()) ->withSlugs(array(PhabricatorSlug::normalize($slug))) ->needContent(true) ->executeOne(); if (!$document) { throw new ConduitException('ERR-BAD-DOCUMENT'); } return $this->buildDocumentInfoDictionary( $document, $document->getContent()); } } diff --git a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php index fef3eb7ec3..47be6aa3f9 100644 --- a/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectCreateConduitAPIMethod.php @@ -1,64 +1,60 @@ 'required string', 'members' => 'optional list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $this->requireApplicationCapability( ProjectCreateProjectsCapability::CAPABILITY, $user); $project = PhabricatorProject::initializeNewProject($user); $type_name = PhabricatorProjectTransaction::TYPE_NAME; $members = $request->getValue('members'); $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType($type_name) ->setNewValue($request->getValue('name')); $xactions[] = id(new PhabricatorProjectTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue( 'edge:type', PhabricatorProjectProjectHasMemberEdgeType::EDGECONST) ->setNewValue( array( '+' => array_fuse($members), )); $editor = id(new PhabricatorProjectTransactionEditor()) ->setActor($user) ->setContinueOnNoEffect(true) ->setContentSourceFromConduitRequest($request); $editor->applyTransactions($project, $xactions); return $this->buildProjectInfoDictionary($project); } } diff --git a/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php b/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php index 8818d96a1e..1f2078ff67 100644 --- a/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php +++ b/src/applications/project/conduit/ProjectQueryConduitAPIMethod.php @@ -1,118 +1,114 @@ formatStringConstants($statuses); return array( 'ids' => 'optional list', 'names' => 'optional list', 'phids' => 'optional list', 'slugs' => 'optional list', 'status' => 'optional '.$status_const, 'members' => 'optional list', 'limit' => 'optional int', 'offset' => 'optional int', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $query = new PhabricatorProjectQuery(); $query->setViewer($request->getUser()); $query->needMembers(true); $query->needSlugs(true); $ids = $request->getValue('ids'); if ($ids) { $query->withIDs($ids); } $names = $request->getValue('names'); if ($names) { $query->withNames($names); } $status = $request->getValue('status'); if ($status) { $query->withStatus($status); } $phids = $request->getValue('phids'); if ($phids) { $query->withPHIDs($phids); } $slugs = $request->getValue('slugs'); if ($slugs) { $query->withSlugs($slugs); } $members = $request->getValue('members'); if ($members) { $query->withMemberPHIDs($members); } $limit = $request->getValue('limit'); if ($limit) { $query->setLimit($limit); } $offset = $request->getValue('offset'); if ($offset) { $query->setOffset($offset); } $pager = $this->newPager($request); $results = $query->executeWithCursorPager($pager); $projects = $this->buildProjectInfoDictionaries($results); // TODO: This is pretty hideous. $slug_map = array(); if ($slugs) { foreach ($slugs as $slug) { $normal = rtrim(PhabricatorSlug::normalize($slug), '/'); foreach ($projects as $project) { if (in_array($normal, $project['slugs'])) { $slug_map[$slug] = $project['phid']; } } } } $result = array( 'data' => $projects, 'slugMap' => $slug_map, ); return $this->addPagerResults($result, $pager); } } diff --git a/src/applications/releeph/conduit/ReleephGetBranchesConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephGetBranchesConduitAPIMethod.php index 14c1c59fe6..59e8900108 100644 --- a/src/applications/releeph/conduit/ReleephGetBranchesConduitAPIMethod.php +++ b/src/applications/releeph/conduit/ReleephGetBranchesConduitAPIMethod.php @@ -1,66 +1,61 @@ >'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $results = array(); $projects = id(new ReleephProductQuery()) ->setViewer($request->getUser()) ->withActive(1) ->execute(); foreach ($projects as $project) { $repository = $project->getRepository(); $branches = $project->loadRelatives( id(new ReleephBranch()), 'releephProjectID', 'getID', 'isActive = 1'); foreach ($branches as $branch) { $full_branch_name = $branch->getName(); $cut_point_commit = $branch->loadOneRelative( id(new PhabricatorRepositoryCommit()), 'phid', 'getCutPointCommitPHID'); $results[] = array( 'project' => $project->getName(), 'repository' => $repository->getCallsign(), 'branch' => $branch->getBasename(), 'fullBranchName' => $full_branch_name, 'symbolicName' => $branch->getSymbolicName(), 'cutPoint' => $cut_point_commit->getCommitIdentifier(), ); } } return $results; } } diff --git a/src/applications/releeph/conduit/ReleephProjectInfoConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephProjectInfoConduitAPIMethod.php index 821bcf00f4..f30d1070c4 100644 --- a/src/applications/releeph/conduit/ReleephProjectInfoConduitAPIMethod.php +++ b/src/applications/releeph/conduit/ReleephProjectInfoConduitAPIMethod.php @@ -1,98 +1,98 @@ 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_UNKNOWN_ARC' => "The given Arcanist project name doesn't exist in the ". "installation of Phabricator you are accessing.", ); } protected function execute(ConduitAPIRequest $request) { $arc_project_name = $request->getValue('arcProjectName'); if ($arc_project_name) { $arc_project = id(new PhabricatorRepositoryArcanistProject()) ->loadOneWhere('name = %s', $arc_project_name); if (!$arc_project) { throw id(new ConduitException('ERR_UNKNOWN_ARC')) ->setErrorDescription( "Unknown Arcanist project '{$arc_project_name}': ". "are you using the correct Conduit URI?"); } $releeph_projects = id(new ReleephProject()) ->loadAllWhere('arcanistProjectID = %d', $arc_project->getID()); } else { $releeph_projects = id(new ReleephProject())->loadAll(); } $releeph_projects = mfilter($releeph_projects, 'getIsActive'); $result = array(); foreach ($releeph_projects as $releeph_project) { $selector = $releeph_project->getReleephFieldSelector(); $fields = $selector->getFieldSpecifications(); $fields_info = array(); foreach ($fields as $field) { $field->setReleephProject($releeph_project); if ($field->isEditable()) { $key = $field->getKeyForConduit(); $fields_info[$key] = array( 'class' => get_class($field), 'name' => $field->getName(), 'key' => $key, 'arcHelp' => $field->renderHelpForArcanist(), ); } } $releeph_branches = mfilter( id(new ReleephBranch()) ->loadAllWhere('releephProjectID = %d', $releeph_project->getID()), 'getIsActive'); $releeph_branches_struct = array(); foreach ($releeph_branches as $branch) { $releeph_branches_struct[] = array( 'branchName' => $branch->getName(), 'projectName' => $releeph_project->getName(), 'projectPHID' => $releeph_project->getPHID(), 'branchPHID' => $branch->getPHID(), ); } $result[] = array( 'projectName' => $releeph_project->getName(), 'projectPHID' => $releeph_project->getPHID(), 'branches' => $releeph_branches_struct, 'fields' => $fields_info, ); } return $result; } } diff --git a/src/applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php index 2785f06507..400bbee604 100644 --- a/src/applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php +++ b/src/applications/releeph/conduit/ReleephQueryBranchesConduitAPIMethod.php @@ -1,77 +1,73 @@ 'optional list', 'phids' => 'optional list', 'productPHIDs' => 'optional list', ) + $this->getPagerParamTypes(); } - public function defineReturnType() { + protected function defineReturnType() { return 'query-results'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $query = id(new ReleephBranchQuery()) ->setViewer($viewer); $ids = $request->getValue('ids'); if ($ids !== null) { $query->withIDs($ids); } $phids = $request->getValue('phids'); if ($phids !== null) { $query->withPHIDs($phids); } $product_phids = $request->getValue('productPHIDs'); if ($product_phids !== null) { $query->withProductPHIDs($product_phids); } $pager = $this->newPager($request); $branches = $query->executeWithCursorPager($pager); $data = array(); foreach ($branches as $branch) { $id = $branch->getID(); $uri = '/releeph/branch/'.$id.'/'; $uri = PhabricatorEnv::getProductionURI($uri); $data[] = array( 'id' => $id, 'phid' => $branch->getPHID(), 'uri' => $uri, 'name' => $branch->getName(), 'productPHID' => $branch->getProduct()->getPHID(), ); } return $this->addPagerResults( array( 'data' => $data, ), $pager); } } diff --git a/src/applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php index 8a38a732f1..fe4d1d1df9 100644 --- a/src/applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php +++ b/src/applications/releeph/conduit/ReleephQueryProductsConduitAPIMethod.php @@ -1,84 +1,80 @@ 'optional list', 'phids' => 'optional list', 'repositoryPHIDs' => 'optional list', 'isActive' => 'optional bool', ) + $this->getPagerParamTypes(); } - public function defineReturnType() { + protected function defineReturnType() { return 'query-results'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $query = id(new ReleephProductQuery()) ->setViewer($viewer); $ids = $request->getValue('ids'); if ($ids !== null) { $query->withIDs($ids); } $phids = $request->getValue('phids'); if ($phids !== null) { $query->withPHIDs($phids); } $repository_phids = $request->getValue('repositoryPHIDs'); if ($repository_phids !== null) { $query->withRepositoryPHIDs($repository_phids); } $is_active = $request->getValue('isActive'); if ($is_active !== null) { $query->withActive($is_active); } $pager = $this->newPager($request); $products = $query->executeWithCursorPager($pager); $data = array(); foreach ($products as $product) { $id = $product->getID(); $uri = '/releeph/product/'.$id.'/'; $uri = PhabricatorEnv::getProductionURI($uri); $data[] = array( 'id' => $id, 'phid' => $product->getPHID(), 'uri' => $uri, 'name' => $product->getName(), 'isActive' => (bool)$product->getIsActive(), 'repositoryPHID' => $product->getRepositoryPHID(), ); } return $this->addPagerResults( array( 'data' => $data, ), $pager); } } diff --git a/src/applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php index e59f4dd9f8..5ccc8a967d 100644 --- a/src/applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php +++ b/src/applications/releeph/conduit/ReleephQueryRequestsConduitAPIMethod.php @@ -1,81 +1,77 @@ 'optional list', 'requestedCommitPHIDs' => 'optional list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $conduit_request) { $revision_phids = $conduit_request->getValue('revisionPHIDs'); $requested_commit_phids = $conduit_request->getValue('requestedCommitPHIDs'); $result = array(); if (!$revision_phids && !$requested_commit_phids) { return $result; } $query = new ReleephRequestQuery(); $query->setViewer($conduit_request->getUser()); if ($revision_phids) { $query->withRequestedObjectPHIDs($revision_phids); } else if ($requested_commit_phids) { $query->withRequestedCommitPHIDs($requested_commit_phids); } $releeph_requests = $query->execute(); foreach ($releeph_requests as $releeph_request) { $branch = $releeph_request->getBranch(); $request_commit_phid = $releeph_request->getRequestCommitPHID(); $object = $releeph_request->getRequestedObject(); if ($object instanceof DifferentialRevision) { $object_phid = $object->getPHID(); } else { $object_phid = null; } $status = $releeph_request->getStatus(); $status_name = ReleephRequestStatus::getStatusDescriptionFor($status); $url = PhabricatorEnv::getProductionURI('/RQ'.$releeph_request->getID()); $result[] = array( 'branchBasename' => $branch->getBasename(), 'branchSymbolic' => $branch->getSymbolicName(), 'requestID' => $releeph_request->getID(), 'revisionPHID' => $object_phid, 'status' => $status, 'status_name' => $status_name, 'url' => $url, ); } return $result; } } diff --git a/src/applications/releeph/conduit/ReleephRequestConduitAPIMethod.php b/src/applications/releeph/conduit/ReleephRequestConduitAPIMethod.php index bc084b206d..f60e40894b 100644 --- a/src/applications/releeph/conduit/ReleephRequestConduitAPIMethod.php +++ b/src/applications/releeph/conduit/ReleephRequestConduitAPIMethod.php @@ -1,169 +1,169 @@ 'required string', 'things' => 'required list', 'fields' => 'dict', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BRANCH' => 'Unknown Releeph branch.', 'ERR_FIELD_PARSE' => 'Unable to parse a Releeph field.', ); } protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); $viewer_handle = id(new PhabricatorHandleQuery()) ->setViewer($user) ->withPHIDs(array($user->getPHID())) ->executeOne(); $branch_phid = $request->getValue('branchPHID'); $releeph_branch = id(new ReleephBranchQuery()) ->setViewer($user) ->withPHIDs(array($branch_phid)) ->executeOne(); if (!$releeph_branch) { throw id(new ConduitException('ERR_BRANCH'))->setErrorDescription( "No ReleephBranch found with PHID {$branch_phid}!"); } $releeph_project = $releeph_branch->getProduct(); // Find the requested commit identifiers $requested_commits = array(); $requested_object_phids = array(); $things = $request->getValue('things'); $finder = id(new ReleephCommitFinder()) ->setUser($user) ->setReleephProject($releeph_project); foreach ($things as $thing) { try { $requested_commits[$thing] = $finder->fromPartial($thing); $object_phid = $finder->getRequestedObjectPHID(); if (!$object_phid) { $object_phid = $requested_commits[$thing]->getPHID(); } $requested_object_phids[$thing] = $object_phid; } catch (ReleephCommitFinderException $ex) { throw id(new ConduitException('ERR_NO_MATCHES')) ->setErrorDescription($ex->getMessage()); } } $requested_commit_phids = mpull($requested_commits, 'getPHID'); // Find any existing requests that clash on the commit id, for this branch $existing_releeph_requests = id(new ReleephRequest())->loadAllWhere( 'requestCommitPHID IN (%Ls) AND branchID = %d', $requested_commit_phids, $releeph_branch->getID()); $existing_releeph_requests = mpull( $existing_releeph_requests, null, 'getRequestCommitPHID'); $selector = $releeph_project->getReleephFieldSelector(); $fields = $selector->getFieldSpecifications(); foreach ($fields as $field) { $field ->setReleephProject($releeph_project) ->setReleephBranch($releeph_branch); } $results = array(); $handles = id(new PhabricatorHandleQuery()) ->setViewer($user) ->withPHIDs($requested_commit_phids) ->execute(); foreach ($requested_commits as $thing => $commit) { $phid = $commit->getPHID(); $name = id($handles[$phid])->getName(); $releeph_request = null; $existing_releeph_request = idx($existing_releeph_requests, $phid); if ($existing_releeph_request) { $releeph_request = $existing_releeph_request; } else { $releeph_request = id(new ReleephRequest()) ->setRequestUserPHID($user->getPHID()) ->setBranchID($releeph_branch->getID()) ->setInBranch(0) ->setRequestedObjectPHID($requested_object_phids[$thing]); $xactions = array(); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_REQUEST) ->setNewValue($commit->getPHID()); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) ->setMetadataValue('userPHID', $user->getPHID()) ->setMetadataValue( 'isAuthoritative', $releeph_project->isAuthoritative($user)) ->setNewValue(ReleephRequest::INTENT_WANT); foreach ($fields as $field) { if (!$field->isEditable()) { continue; } $field->setReleephRequest($releeph_request); try { $field->setValueFromConduitAPIRequest($request); } catch (ReleephFieldParseException $ex) { throw id(new ConduitException('ERR_FIELD_PARSE')) ->setErrorDescription($ex->getMessage()); } } $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($user) ->setContinueOnNoEffect(true) ->setContentSource( PhabricatorContentSource::newForSource( PhabricatorContentSource::SOURCE_CONDUIT, array())); $editor->applyTransactions($releeph_request, $xactions); } $url = PhabricatorEnv::getProductionURI('/Y'.$releeph_request->getID()); $results[$thing] = array( 'thing' => $thing, 'branch' => $releeph_branch->getDisplayNameWithDetail(), 'commitName' => $name, 'commitID' => $commit->getCommitIdentifier(), 'url' => $url, 'requestID' => $releeph_request->getID(), 'requestor' => $viewer_handle->getName(), 'requestTime' => $releeph_request->getDateCreated(), 'existing' => $existing_releeph_request !== null, ); } return $results; } } diff --git a/src/applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php index 19d3d95acf..339f0ac5eb 100644 --- a/src/applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php +++ b/src/applications/releeph/conduit/work/ReleephWorkCanPushConduitAPIMethod.php @@ -1,38 +1,34 @@ 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'bool'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $releeph_project = id(new ReleephProject()) ->loadOneWhere('phid = %s', $request->getValue('projectPHID')); $user = $request->getUser(); return $releeph_project->isAuthoritative($user); } } diff --git a/src/applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php index 28986d72a6..08381befb7 100644 --- a/src/applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php +++ b/src/applications/releeph/conduit/work/ReleephWorkGetAuthorInfoConduitAPIMethod.php @@ -1,48 +1,44 @@ 'required string', 'vcsType' => 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty string'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $user = id(new PhabricatorUser()) ->loadOneWhere('phid = %s', $request->getValue('userPHID')); $email = $user->loadPrimaryEmailAddress(); if (is_numeric($email)) { $email = $user->getUserName().'@fb.com'; } return sprintf( '%s <%s>', $user->getRealName(), $email); } } diff --git a/src/applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php index 9ba11b85b7..20d86a003a 100644 --- a/src/applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php +++ b/src/applications/releeph/conduit/work/ReleephWorkGetBranchCommitMessageConduitAPIMethod.php @@ -1,104 +1,100 @@ 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty string'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $branch = id(new ReleephBranchQuery()) ->setViewer($viewer) ->withPHIDs(array($request->getValue('branchPHID'))) ->executeOne(); $project = $branch->getProduct(); $creator_phid = $branch->getCreatedByUserPHID(); $cut_phid = $branch->getCutPointCommitPHID(); $phids = array( $branch->getPHID(), $project->getPHID(), $creator_phid, $cut_phid, ); $handles = id(new PhabricatorHandleQuery()) ->setViewer($request->getUser()) ->withPHIDs($phids) ->execute(); $h_branch = $handles[$branch->getPHID()]; $h_project = $handles[$project->getPHID()]; // Not as customizable as a ReleephRequest's commit message. It doesn't // really need to be. // TODO: Yes it does, see FB-specific stuff below. $commit_message = array(); $commit_message[] = $h_branch->getFullName(); $commit_message[] = $h_branch->getURI(); $commit_message[] = 'Cut Point: '.$handles[$cut_phid]->getName(); $cut_point_pr_commit = id(new PhabricatorRepositoryCommit()) ->loadOneWhere('phid = %s', $cut_phid); $cut_point_commit_date = strftime( '%Y-%m-%d %H:%M:%S%z', $cut_point_pr_commit->getEpoch()); $commit_message[] = "Cut Point Date: {$cut_point_commit_date}"; $commit_message[] = 'Created By: '.$handles[$creator_phid]->getName(); $project_uri = $project->getURI(); $commit_message[] = 'Project: '.$h_project->getName().' '.$project_uri; /** * Required for 090-limit_new_branch_creations.sh in * admin/scripts/git/hosting/hooks/update.d (in the E repo): * * http://fburl.com/2372545 * * The commit message must have a line saying: * * @new-branch: * */ $repo = $project->getRepository(); switch ($repo->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $commit_message[] = sprintf( '@new-branch: %s', $branch->getName()); break; } return implode("\n\n", $commit_message); } } diff --git a/src/applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php index c687205aef..5f6f82b17d 100644 --- a/src/applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php +++ b/src/applications/releeph/conduit/work/ReleephWorkGetBranchConduitAPIMethod.php @@ -1,61 +1,57 @@ 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $branch = id(new ReleephBranchQuery()) ->setViewer($request->getUser()) ->withPHIDs(array($request->getValue('branchPHID'))) ->needCutPointCommits(true) ->executeOne(); $cut_phid = $branch->getCutPointCommitPHID(); $phids = array($cut_phid); $handles = id(new PhabricatorHandleQuery()) ->setViewer($request->getUser()) ->withPHIDs($phids) ->execute(); $project = $branch->getProject(); $repo = $project->getRepository(); $commit = $branch->getCutPointCommit(); return array( 'branchName' => $branch->getName(), 'branchPHID' => $branch->getPHID(), 'vcsType' => $repo->getVersionControlSystem(), 'cutCommitID' => $commit->getCommitIdentifier(), 'cutCommitName' => $handles[$cut_phid]->getName(), 'creatorPHID' => $branch->getCreatedByUserPHID(), 'trunk' => $project->getTrunkBranch(), ); } } diff --git a/src/applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php index e0539fb690..398f37e7a0 100644 --- a/src/applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php +++ b/src/applications/releeph/conduit/work/ReleephWorkGetCommitMessageConduitAPIMethod.php @@ -1,100 +1,96 @@ formatStringConstants(array('pick', 'revert')); return array( 'requestPHID' => 'required string', 'action' => 'required '.$action_const, ); } - public function defineReturnType() { + protected function defineReturnType() { return 'dict'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $releeph_request = id(new ReleephRequestQuery()) ->setViewer($viewer) ->withPHIDs(array($request->getValue('requestPHID'))) ->executeOne(); $action = $request->getValue('action'); $title = $releeph_request->getSummaryForDisplay(); $commit_message = array(); $branch = $releeph_request->getBranch(); $project = $branch->getProduct(); $selector = $project->getReleephFieldSelector(); $fields = $selector->getFieldSpecifications(); $fields = $selector->sortFieldsForCommitMessage($fields); foreach ($fields as $field) { $field ->setUser($request->getUser()) ->setReleephProject($project) ->setReleephBranch($branch) ->setReleephRequest($releeph_request); $label = null; $value = null; switch ($action) { case 'pick': if ($field->shouldAppearOnCommitMessage()) { $label = $field->renderLabelForCommitMessage(); $value = $field->renderValueForCommitMessage(); } break; case 'revert': if ($field->shouldAppearOnRevertMessage()) { $label = $field->renderLabelForRevertMessage(); $value = $field->renderValueForRevertMessage(); } break; } if ($label && $value) { if (strpos($value, "\n") !== false || substr($value, 0, 2) === ' ') { $commit_message[] = "{$label}:\n{$value}"; } else { $commit_message[] = "{$label}: {$value}"; } } } return array( 'title' => $title, 'body' => implode("\n\n", $commit_message), ); } } diff --git a/src/applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php index cb6d7646fe..5e3d004ce6 100644 --- a/src/applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php +++ b/src/applications/releeph/conduit/work/ReleephWorkNextRequestConduitAPIMethod.php @@ -1,228 +1,228 @@ 'required phid', 'seen' => 'required map', ); } - public function defineReturnType() { + protected function defineReturnType() { return ''; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-NOT-PUSHER' => 'You are not listed as a pusher for thie Releeph project!', ); } protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $seen = $request->getValue('seen'); $branch = id(new ReleephBranchQuery()) ->setViewer($viewer) ->withPHIDs(array($request->getValue('branchPHID'))) ->executeOne(); $project = $branch->getProduct(); $needs_pick = array(); $needs_revert = array(); // Load every request ever made for this branch...?!!! $releeph_requests = id(new ReleephRequestQuery()) ->setViewer($viewer) ->withBranchIDs(array($branch->getID())) ->execute(); foreach ($releeph_requests as $candidate) { $phid = $candidate->getPHID(); if (idx($seen, $phid)) { continue; } $should = $candidate->shouldBeInBranch(); $in = $candidate->getInBranch(); if ($should && !$in) { $needs_pick[] = $candidate; } if (!$should && $in) { $needs_revert[] = $candidate; } } /** * Sort both needs_pick and needs_revert in ascending commit order, as * discovered by Phabricator (using the `id` column to perform that * ordering). * * This is easy for $needs_pick as the ordinal is stored. It is hard for * reverts, as we have to look that information up. */ $needs_pick = $this->sortPicks($needs_pick); $needs_revert = $this->sortReverts($needs_revert); /** * Do reverts first in reverse order, then the picks in original-commit * order. * * This seems like the correct thing to do, but there may be a better * algorithm for the releephwork.nextrequest Conduit call that orders * things better. * * We could also button-mash our way through everything that failed (at the * end of the run) to try failed things again. */ $releeph_request = null; $action = null; if ($needs_revert) { $releeph_request = last($needs_revert); $action = 'revert'; $commit_id = $releeph_request->getCommitIdentifier(); $commit_phid = $releeph_request->getCommitPHID(); } else if ($needs_pick) { $releeph_request = head($needs_pick); $action = 'pick'; $commit = $releeph_request->loadPhabricatorRepositoryCommit(); $commit_id = $commit->getCommitIdentifier(); $commit_phid = $commit->getPHID(); } else { // Return early if there's nothing to do! return array(); } // Build the response $phids = array(); $phids[] = $commit_phid; $diff_phid = null; $diff_rev_id = null; $requested_object = $releeph_request->getRequestedObject(); if ($requested_object instanceof DifferentialRevision) { $diff_rev = $requested_object; } else { $diff_rev = null; } if ($diff_rev) { $diff_phid = $diff_rev->getPHID(); $phids[] = $diff_phid; $diff_rev_id = $diff_rev->getID(); } $phids[] = $releeph_request->getPHID(); $handles = id(new PhabricatorHandleQuery()) ->setViewer($request->getUser()) ->withPHIDs($phids) ->execute(); $diff_name = null; if ($diff_rev) { $diff_name = $handles[$diff_phid]->getName(); } $new_author_phid = null; if ($diff_rev) { $new_author_phid = $diff_rev->getAuthorPHID(); } else { $pr_commit = $releeph_request->loadPhabricatorRepositoryCommit(); if ($pr_commit) { $new_author_phid = $pr_commit->getAuthorPHID(); } } return array( 'requestID' => $releeph_request->getID(), 'requestPHID' => $releeph_request->getPHID(), 'requestName' => $handles[$releeph_request->getPHID()]->getName(), 'requestorPHID' => $releeph_request->getRequestUserPHID(), 'action' => $action, 'diffRevID' => $diff_rev_id, 'diffName' => $diff_name, 'commitIdentifier' => $commit_id, 'commitPHID' => $commit_phid, 'commitName' => $handles[$commit_phid]->getName(), 'needsRevert' => mpull($needs_revert, 'getID'), 'needsPick' => mpull($needs_pick, 'getID'), 'newAuthorPHID' => $new_author_phid, ); } private function sortPicks(array $releeph_requests) { $surrogate = array(); foreach ($releeph_requests as $rq) { // TODO: it's likely that relying on the `id` column to provide // trunk-commit-order is thoroughly broken. $ordinal = (int) $rq->loadPhabricatorRepositoryCommit()->getID(); $surrogate[$ordinal] = $rq; } ksort($surrogate); return $surrogate; } /** * Sort an array of ReleephRequests, that have been picked into a branch, in * the order in which they were picked to the branch. */ private function sortReverts(array $releeph_requests) { if (!$releeph_requests) { return array(); } // ReleephRequests, keyed by $releeph_requests = mpull($releeph_requests, null, 'getCommitIdentifier'); $commits = id(new PhabricatorRepositoryCommit()) ->loadAllWhere( 'commitIdentifier IN (%Ls)', mpull($releeph_requests, 'getCommitIdentifier')); // A map of => $surrogate = mpull($commits, 'getID', 'getCommitIdentifier'); $unparsed = array(); $result = array(); foreach ($releeph_requests as $commit_id => $releeph_request) { $ordinal = idx($surrogate, $commit_id); if ($ordinal) { $result[$ordinal] = $releeph_request; } else { $unparsed[] = $releeph_request; } } // Sort $result in ascending order ksort($result); // Unparsed commits we'll just have to guess, based on time $unparsed = msort($unparsed, 'getDateModified'); return array_merge($result, $unparsed); } } diff --git a/src/applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php index 8eea8d1a28..43e26f2f4d 100644 --- a/src/applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php +++ b/src/applications/releeph/conduit/work/ReleephWorkRecordConduitAPIMethod.php @@ -1,79 +1,75 @@ formatStringConstants( array( 'pick', 'revert', )); return array( 'requestPHID' => 'required string', 'action' => 'required '.$action_const, 'commitIdentifier' => 'required string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $action = $request->getValue('action'); $new_commit_id = $request->getValue('commitIdentifier'); $releeph_request = id(new ReleephRequest()) ->loadOneWhere('phid = %s', $request->getValue('requestPHID')); $xactions = array(); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_COMMIT) ->setMetadataValue('action', $action) ->setNewValue($new_commit_id); $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($request->getUser()) ->setContinueOnNoEffect(true) ->setContentSource( PhabricatorContentSource::newForSource( PhabricatorContentSource::SOURCE_CONDUIT, array())); $editor->applyTransactions($releeph_request, $xactions); } } diff --git a/src/applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php b/src/applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php index dd4cb7a3bc..f9434ab48c 100644 --- a/src/applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php +++ b/src/applications/releeph/conduit/work/ReleephWorkRecordPickStatusConduitAPIMethod.php @@ -1,87 +1,83 @@ formatStringConstants( array( 'pick', 'revert', )); return array( 'requestPHID' => 'required string', 'action' => 'required '.$action_const, 'ok' => 'required bool', 'dryRun' => 'optional bool', 'details' => 'optional dict', ); } - public function defineReturnType() { + protected function defineReturnType() { return ''; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $action = $request->getValue('action'); $ok = $request->getValue('ok'); $dry_run = $request->getValue('dryRun'); $details = $request->getValue('details', array()); switch ($request->getValue('action')) { case 'pick': $pick_status = $ok ? ReleephRequest::PICK_OK : ReleephRequest::PICK_FAILED; break; case 'revert': $pick_status = $ok ? ReleephRequest::REVERT_OK : ReleephRequest::REVERT_FAILED; break; default: throw new Exception("Unknown action {$action}!"); } $releeph_request = id(new ReleephRequest()) ->loadOneWhere('phid = %s', $request->getValue('requestPHID')); $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($request->getUser()) ->setContinueOnNoEffect(true) ->setContentSource( PhabricatorContentSource::newForSource( PhabricatorContentSource::SOURCE_CONDUIT, array())); $xactions = array(); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_PICK_STATUS) ->setMetadataValue('dryRun', $dry_run) ->setMetadataValue('details', $details) ->setNewValue($pick_status); $editor->applyTransactions($releeph_request, $xactions); } } diff --git a/src/applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php b/src/applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php index 64f29405af..d6c0554234 100644 --- a/src/applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php +++ b/src/applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php @@ -1,76 +1,76 @@ 'Content may not be empty.', 'ERR-INVALID-ENGINE' => 'Invalid markup engine.', ); } - public function defineParamTypes() { + protected function defineParamTypes() { $available_contexts = array_keys($this->getEngineContexts()); $available_const = $this->formatStringConstants($available_contexts); return array( 'context' => 'required '.$available_const, 'contents' => 'required list', ); } protected function execute(ConduitAPIRequest $request) { $contents = $request->getValue('contents'); $context = $request->getValue('context'); $engine_class = idx($this->getEngineContexts(), $context); if (!$engine_class) { throw new ConduitException('ERR-INVALID_ENGINE'); } $engine = PhabricatorMarkupEngine::$engine_class(); $engine->setConfig('viewer', $request->getUser()); $results = array(); foreach ($contents as $content) { $text = $engine->markupText($content); if ($text) { $content = hsprintf('%s', $text)->getHTMLContent(); } else { $content = ''; } $results[] = array( 'content' => $content, ); } return $results; } private function getEngineContexts() { return array( 'phriction' => 'newPhrictionMarkupEngine', 'maniphest' => 'newManiphestMarkupEngine', 'differential' => 'newDifferentialMarkupEngine', 'phame' => 'newPhameMarkupEngine', 'feed' => 'newFeedMarkupEngine', 'diffusion' => 'newDiffusionMarkupEngine', ); } } diff --git a/src/applications/repository/conduit/RepositoryCreateConduitAPIMethod.php b/src/applications/repository/conduit/RepositoryCreateConduitAPIMethod.php index b8a898921f..42df1b6d46 100644 --- a/src/applications/repository/conduit/RepositoryCreateConduitAPIMethod.php +++ b/src/applications/repository/conduit/RepositoryCreateConduitAPIMethod.php @@ -1,142 +1,142 @@ formatStringConstants(array('git', 'hg', 'svn')); return array( 'name' => 'required string', 'vcs' => 'required '.$vcs_const, 'callsign' => 'required string', 'description' => 'optional string', 'encoding' => 'optional string', 'tracking' => 'optional bool', 'uri' => 'required string', 'credentialPHID' => 'optional string', 'svnSubpath' => 'optional string', 'branchFilter' => 'optional list', 'closeCommitsFilter' => 'optional list', 'pullFrequency' => 'optional int', 'defaultBranch' => 'optional string', 'heraldEnabled' => 'optional bool, default = true', 'autocloseEnabled' => 'optional bool, default = true', 'svnUUID' => 'optional string', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR-DUPLICATE' => 'Duplicate repository callsign.', 'ERR-BAD-CALLSIGN' => 'Callsign is required and must be ALL UPPERCASE LETTERS.', 'ERR-UNKNOWN-REPOSITORY-VCS' => 'Unknown repository VCS type.', ); } protected function execute(ConduitAPIRequest $request) { $application = id(new PhabricatorApplicationQuery()) ->setViewer($request->getUser()) ->withClasses(array('PhabricatorDiffusionApplication')) ->executeOne(); PhabricatorPolicyFilter::requireCapability( $request->getUser(), $application, DiffusionCreateRepositoriesCapability::CAPABILITY); // TODO: This has some duplication with (and lacks some of the validation // of) the web workflow; refactor things so they can share more code as this // stabilizes. Specifically, this should move to transactions since they // work properly now. $repository = PhabricatorRepository::initializeNewRepository( $request->getUser()); $repository->setName($request->getValue('name')); $callsign = $request->getValue('callsign'); if (!preg_match('/^[A-Z]+\z/', $callsign)) { throw new ConduitException('ERR-BAD-CALLSIGN'); } $repository->setCallsign($callsign); $local_path = PhabricatorEnv::getEnvConfig( 'repository.default-local-path'); $local_path = rtrim($local_path, '/'); $local_path = $local_path.'/'.$callsign.'/'; $vcs = $request->getValue('vcs'); $map = array( 'git' => PhabricatorRepositoryType::REPOSITORY_TYPE_GIT, 'hg' => PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL, 'svn' => PhabricatorRepositoryType::REPOSITORY_TYPE_SVN, ); if (empty($map[$vcs])) { throw new ConduitException('ERR-UNKNOWN-REPOSITORY-VCS'); } $repository->setVersionControlSystem($map[$vcs]); $repository->setCredentialPHID($request->getValue('credentialPHID')); $remote_uri = $request->getValue('uri'); PhabricatorRepository::assertValidRemoteURI($remote_uri); $details = array( 'encoding' => $request->getValue('encoding'), 'description' => $request->getValue('description'), 'tracking-enabled' => (bool)$request->getValue('tracking', true), 'remote-uri' => $remote_uri, 'local-path' => $local_path, 'branch-filter' => array_fill_keys( $request->getValue('branchFilter', array()), true), 'close-commits-filter' => array_fill_keys( $request->getValue('closeCommitsFilter', array()), true), 'pull-frequency' => $request->getValue('pullFrequency'), 'default-branch' => $request->getValue('defaultBranch'), 'herald-disabled' => !$request->getValue('heraldEnabled', true), 'svn-subpath' => $request->getValue('svnSubpath'), 'disable-autoclose' => !$request->getValue('autocloseEnabled', true), ); foreach ($details as $key => $value) { $repository->setDetail($key, $value); } try { $repository->save(); } catch (AphrontDuplicateKeyQueryException $ex) { throw new ConduitException('ERR-DUPLICATE'); } return $repository->toDictionary(); } } diff --git a/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php b/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php index 0323446fb1..92fe84120b 100644 --- a/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php +++ b/src/applications/repository/conduit/RepositoryQueryConduitAPIMethod.php @@ -1,86 +1,81 @@ 'optional list', 'phids' => 'optional list', 'callsigns' => 'optional list', 'vcsTypes' => 'optional list', 'remoteURIs' => 'optional list', 'uuids' => 'optional list', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } - public function defineErrorTypes() { - return array( - ); - } - protected function execute(ConduitAPIRequest $request) { $query = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()); $ids = $request->getValue('ids', array()); if ($ids) { $query->withIDs($ids); } $phids = $request->getValue('phids', array()); if ($phids) { $query->withPHIDs($phids); } $callsigns = $request->getValue('callsigns', array()); if ($callsigns) { $query->withCallsigns($callsigns); } $vcs_types = $request->getValue('vcsTypes', array()); if ($vcs_types) { $query->withTypes($vcs_types); } $remote_uris = $request->getValue('remoteURIs', array()); if ($remote_uris) { $query->withRemoteURIs($remote_uris); } $uuids = $request->getValue('uuids', array()); if ($uuids) { $query->withUUIDs($uuids); } $repositories = $query->execute(); $results = array(); foreach ($repositories as $repository) { $results[] = $repository->toDictionary(); } return $results; } } diff --git a/src/applications/slowvote/conduit/SlowvoteInfoConduitAPIMethod.php b/src/applications/slowvote/conduit/SlowvoteInfoConduitAPIMethod.php index 42200ec28f..50478abc0c 100644 --- a/src/applications/slowvote/conduit/SlowvoteInfoConduitAPIMethod.php +++ b/src/applications/slowvote/conduit/SlowvoteInfoConduitAPIMethod.php @@ -1,47 +1,47 @@ 'required id', ); } - public function defineReturnType() { + protected function defineReturnType() { return 'nonempty dict'; } - public function defineErrorTypes() { + protected function defineErrorTypes() { return array( 'ERR_BAD_POLL' => 'No such poll exists', ); } protected function execute(ConduitAPIRequest $request) { $poll_id = $request->getValue('poll_id'); $poll = id(new PhabricatorSlowvotePoll())->load($poll_id); if (!$poll) { throw new ConduitException('ERR_BAD_POLL'); } $result = array( 'id' => $poll->getID(), 'phid' => $poll->getPHID(), 'authorPHID' => $poll->getAuthorPHID(), 'question' => $poll->getQuestion(), 'uri' => PhabricatorEnv::getProductionURI('/V'.$poll->getID()), ); return $result; } } diff --git a/src/applications/tokens/conduit/TokenGiveConduitAPIMethod.php b/src/applications/tokens/conduit/TokenGiveConduitAPIMethod.php index 099f93de27..4e0a463a74 100644 --- a/src/applications/tokens/conduit/TokenGiveConduitAPIMethod.php +++ b/src/applications/tokens/conduit/TokenGiveConduitAPIMethod.php @@ -1,44 +1,40 @@ 'phid|null', 'objectPHID' => 'phid', ); } - public function defineErrorTypes() { - return array(); - } - - public function defineReturnType() { + protected function defineReturnType() { return 'void'; } protected function execute(ConduitAPIRequest $request) { $content_source = PhabricatorContentSource::newFromConduitRequest($request); $editor = id(new PhabricatorTokenGivenEditor()) ->setActor($request->getUser()) ->setContentSource($content_source); if ($request->getValue('tokenPHID')) { $editor->addToken( $request->getValue('objectPHID'), $request->getValue('tokenPHID')); } else { $editor->deleteToken($request->getValue('objectPHID')); } } } diff --git a/src/applications/tokens/conduit/TokenGivenConduitAPIMethod.php b/src/applications/tokens/conduit/TokenGivenConduitAPIMethod.php index 86f666b68c..dfa887fc76 100644 --- a/src/applications/tokens/conduit/TokenGivenConduitAPIMethod.php +++ b/src/applications/tokens/conduit/TokenGivenConduitAPIMethod.php @@ -1,53 +1,49 @@ 'list', 'objectPHIDs' => 'list', 'tokenPHIDs' => 'list', ); } - public function defineErrorTypes() { - return array(); - } - - public function defineReturnType() { + protected function defineReturnType() { return 'list'; } protected function execute(ConduitAPIRequest $request) { $query = id(new PhabricatorTokenGivenQuery()) ->setViewer($request->getUser()); $author_phids = $request->getValue('authorPHIDs'); if ($author_phids) { $query->withAuthorPHIDs($author_phids); } $object_phids = $request->getValue('objectPHIDs'); if ($object_phids) { $query->withObjectPHIDs($object_phids); } $token_phids = $request->getValue('tokenPHIDs'); if ($token_phids) { $query->withTokenPHIDs($token_phids); } $given = $query->execute(); return $this->buildTokenGivenDicts($given); } } diff --git a/src/applications/tokens/conduit/TokenQueryConduitAPIMethod.php b/src/applications/tokens/conduit/TokenQueryConduitAPIMethod.php index 3168feab87..ec0dcfb873 100644 --- a/src/applications/tokens/conduit/TokenQueryConduitAPIMethod.php +++ b/src/applications/tokens/conduit/TokenQueryConduitAPIMethod.php @@ -1,34 +1,30 @@ '; } - public function defineErrorTypes() { - return array(); - } - protected function execute(ConduitAPIRequest $request) { $query = id(new PhabricatorTokenQuery()) ->setViewer($request->getUser()); $tokens = $query->execute(); return $this->buildTokenDicts($tokens); } }