private function buildMethodProperties(ConduitAPIMethod $method) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView()); $view->addProperty(pht('Returns'), $method->getReturnType()); $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('<li><strong>%s:</strong> %s</li>', $error, $meaning); } $error_description = phutil_tag('ul', array(), $error_description); $view->addProperty(pht('Errors'), $error_description); $view->addSectionHeader(pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent(new PHUIRemarkupView($viewer, $method->getMethodDescription())); return $view; }
private function buildMethodProperties(ConduitAPIMethod $method) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView()); $view->addProperty(pht('Returns'), $method->getReturnType()); $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('<li><strong>%s:</strong> %s</li>', $error, $meaning); } $error_description = phutil_tag('ul', array(), $error_description); $view->addProperty(pht('Errors'), $error_description); $description = $method->getMethodDescription(); $description = PhabricatorMarkupEngine::renderOneObject(id(new PhabricatorMarkupOneOff())->setContent($description), 'default', $viewer); $view->addSectionHeader(pht('Description')); $view->addTextContent($description); return $view; }
private function getAllMethods() { $classes = $this->getAllMethodImplementationClasses(); $methods = array(); foreach ($classes as $class) { $name = ConduitAPIMethod::getAPIMethodNameFromClassName($class); $methods[$name] = $class; } return $methods; }
private function buildMethodProperties(ConduitAPIMethod $method) { $viewer = $this->getViewer(); $view = id(new PHUIPropertyListView()); $status = $method->getMethodStatus(); $reason = $method->getMethodStatusDescription(); switch ($status) { case ConduitAPIMethod::METHOD_STATUS_UNSTABLE: $stability_icon = 'fa-exclamation-triangle yellow'; $stability_label = pht('Unstable Method'); $stability_info = nonempty($reason, pht('This method is new and unstable. Its interface is subject ' . 'to change.')); break; case ConduitAPIMethod::METHOD_STATUS_DEPRECATED: $stability_icon = 'fa-exclamation-triangle red'; $stability_label = pht('Deprecated Method'); $stability_info = nonempty($reason, pht('This method is deprecated.')); break; default: $stability_label = null; break; } if ($stability_label) { $view->addProperty(pht('Stability'), array(id(new PHUIIconView())->setIcon($stability_icon), ' ', phutil_tag('strong', array(), $stability_label . ':'), ' ', $stability_info)); } $view->addProperty(pht('Returns'), $method->getReturnType()); $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('<li><strong>%s:</strong> %s</li>', $error, $meaning); } $error_description = phutil_tag('ul', array(), $error_description); $view->addProperty(pht('Errors'), $error_description); $scope = $method->getRequiredScope(); switch ($scope) { case ConduitAPIMethod::SCOPE_ALWAYS: $oauth_icon = 'fa-globe green'; $oauth_description = pht('OAuth clients may always call this method.'); break; case ConduitAPIMethod::SCOPE_NEVER: $oauth_icon = 'fa-ban red'; $oauth_description = pht('OAuth clients may never call this method.'); break; default: $oauth_icon = 'fa-unlock-alt blue'; $oauth_description = pht('OAuth clients may call this method after requesting access to ' . 'the "%s" scope.', $scope); break; } $view->addProperty(pht('OAuth Scope'), array(id(new PHUIIconView())->setIcon($oauth_icon), ' ', $oauth_description)); $view->addSectionHeader(pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent(new PHUIRemarkupView($viewer, $method->getMethodDescription())); return $view; }
protected function buildMethodHandler($method) { $method_class = ConduitAPIMethod::getClassNameFromAPIMethodName($method); // Test if the method exists. $ok = false; try { $ok = class_exists($method_class); } catch (Exception $ex) { // Discard, we provide a more specific exception below. } if (!$ok) { throw new Exception("Conduit method '{$method}' does not exist."); } $class_info = new ReflectionClass($method_class); if ($class_info->isAbstract()) { throw new Exception("Method '{$method}' is not valid; the implementation is an abstract " . "base class."); } return newv($method_class, array()); }
public static final function callConduitWithDiffusionRequest(PhabricatorUser $user, DiffusionRequest $drequest, $method, array $params = array()) { $repository = $drequest->getRepository(); $core_params = array('repository' => $repository->getPHID()); 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->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 buildConduitResponse(ConduitAPIRequest $request, ConduitAPIMethod $method) { $viewer = $this->requireViewer(); $query_key = $request->getValue('queryKey'); if (!strlen($query_key)) { $saved_query = new PhabricatorSavedQuery(); } else { if ($this->isBuiltinQuery($query_key)) { $saved_query = $this->buildSavedQueryFromBuiltin($query_key); } else { $saved_query = id(new PhabricatorSavedQueryQuery())->setViewer($viewer)->withQueryKeys(array($query_key))->executeOne(); if (!$saved_query) { throw new Exception(pht('Query key "%s" does not correspond to a valid query.', $query_key)); } } } $constraints = $request->getValue('constraints', array()); $fields = $this->getSearchFieldsForConduit(); foreach ($fields as $key => $field) { if (!$field->getConduitParameterType()) { unset($fields[$key]); } } $valid_constraints = array(); foreach ($fields as $field) { foreach ($field->getValidConstraintKeys() as $key) { $valid_constraints[$key] = true; } } foreach ($constraints as $key => $constraint) { if (empty($valid_constraints[$key])) { throw new Exception(pht('Constraint "%s" is not a valid constraint for this query.', $key)); } } foreach ($fields as $field) { if (!$field->getValueExistsInConduitRequest($constraints)) { continue; } $value = $field->readValueFromConduitRequest($constraints, $request->getIsStrictlyTyped()); $saved_query->setParameter($field->getKey(), $value); } // NOTE: Currently, when running an ad-hoc query we never persist it into // a saved query. We might want to add an option to do this in the future // (for example, to enable a CLI-to-Web workflow where user can view more // details about results by following a link), but have no use cases for // it today. If we do identify a use case, we could save the query here. $query = $this->buildQueryFromSavedQuery($saved_query); $pager = $this->newPagerForSavedQuery($saved_query); $attachments = $this->getConduitSearchAttachments(); // TODO: Validate this better. $attachment_specs = $request->getValue('attachments', array()); $attachments = array_select_keys($attachments, array_keys($attachment_specs)); foreach ($attachments as $key => $attachment) { $attachment->setViewer($viewer); } foreach ($attachments as $key => $attachment) { $attachment->willLoadAttachmentData($query, $attachment_specs[$key]); } $this->setQueryOrderForConduit($query, $request); $this->setPagerLimitForConduit($pager, $request); $this->setPagerOffsetsForConduit($pager, $request); $objects = $this->executeQuery($query, $pager); $data = array(); if ($objects) { $field_extensions = $this->getConduitFieldExtensions(); $extension_data = array(); foreach ($field_extensions as $key => $extension) { $extension_data[$key] = $extension->loadExtensionConduitData($objects); } $attachment_data = array(); foreach ($attachments as $key => $attachment) { $attachment_data[$key] = $attachment->loadAttachmentData($objects, $attachment_specs[$key]); } foreach ($objects as $object) { $field_map = $this->getObjectWireFieldsForConduit($object, $field_extensions, $extension_data); $attachment_map = array(); foreach ($attachments as $key => $attachment) { $attachment_map[$key] = $attachment->getAttachmentForObject($object, $attachment_data[$key], $attachment_specs[$key]); } // If this is empty, we still want to emit a JSON object, not a // JSON list. if (!$attachment_map) { $attachment_map = (object) $attachment_map; } $id = (int) $object->getID(); $phid = $object->getPHID(); $data[] = array('id' => $id, 'type' => phid_get_type($phid), 'phid' => $phid, 'fields' => $field_map, 'attachments' => $attachment_map); } } return array('data' => $data, 'maps' => $method->getQueryMaps($query), 'query' => array('queryKey' => $saved_query->getQueryKey()), 'cursor' => array('limit' => $pager->getPageSize(), 'after' => $pager->getNextPageID(), 'before' => $pager->getPrevPageID(), 'order' => $request->getValue('order'))); }
protected function getMethodFilters() { $classes = $this->getAllMethodImplementationClasses(); $method_names = array(); foreach ($classes as $method_class) { $method_name = ConduitAPIMethod::getAPIMethodNameFromClassName($method_class); $group_name = head(explode('.', $method_name)); $method_object = newv($method_class, array()); $status = $method_object->getMethodStatus(); $key = sprintf('%02d %s %s', $this->getOrderForMethodStatus($status), $group_name, $method_name); $method_names[$key] = array('full_name' => $method_name, 'group_name' => $group_name, 'status' => $status, 'description' => $method_object->getMethodDescription()); } ksort($method_names); $method_names = igroup($method_names, 'group_name'); ksort($method_names); return $method_names; }
public function processRequest() { $time_start = microtime(true); $request = $this->getRequest(); $method = $this->method; $method_class = ConduitAPIMethod::getClassNameFromAPIMethodName($method); $api_request = null; $log = new PhabricatorConduitMethodCallLog(); $log->setMethod($method); $metadata = array(); try { if (!class_exists($method_class)) { throw new Exception("Unable to load the implementation class for method '{$method}'. " . "You may have misspelled the method, need to define " . "'{$method_class}', or need to run 'arc build'."); } // Fake out checkModule, the class has already been autoloaded by the // class_exists() call above. $method_handler = newv($method_class, array()); if (isset($_REQUEST['params']) && is_array($_REQUEST['params'])) { $params_post = $request->getArr('params'); foreach ($params_post as $key => $value) { $params_post[$key] = json_decode($value, true); } $params = $params_post; } else { $params_json = $request->getStr('params'); if (!strlen($params_json)) { $params = array(); } else { $params = json_decode($params_json, true); if (!is_array($params)) { throw new Exception("Invalid parameter information was passed to method " . "'{$method}', could not decode JSON serialization."); } } } $metadata = idx($params, '__conduit__', array()); unset($params['__conduit__']); $result = null; $api_request = new ConduitAPIRequest($params); $auth_error = null; if ($method_handler->shouldRequireAuthentication()) { $auth_error = $this->authenticateUser($api_request, $metadata); } if ($auth_error === null) { try { $result = $method_handler->executeMethod($api_request); $error_code = null; $error_info = null; } catch (ConduitException $ex) { $result = null; $error_code = $ex->getMessage(); $error_info = $method_handler->getErrorDescription($error_code); } } else { list($error_code, $error_info) = $auth_error; } } catch (Exception $ex) { $result = null; $error_code = 'ERR-CONDUIT-CORE'; $error_info = $ex->getMessage(); } $time_end = microtime(true); $connection_id = null; if (idx($metadata, 'connectionID')) { $connection_id = $metadata['connectionID']; } else { if ($method == 'conduit.connect' && $result) { $connection_id = idx($result, 'connectionID'); } } $log->setConnectionID($connection_id); $log->setError((string) $error_code); $log->setDuration(1000000 * ($time_end - $time_start)); // TODO: This is a hack, but the insert is comparatively expensive and // we only really care about having these logs for real CLI clients, if // even that. if (empty($metadata['authToken'])) { $log->save(); } $result = array('result' => $result, 'error_code' => $error_code, 'error_info' => $error_info); switch ($request->getStr('output')) { case 'human': return $this->buildHumanReadableResponse($method, $api_request, $result); case 'json': default: return id(new AphrontFileResponse())->setMimeType('application/json')->setContent('for(;;);' . json_encode($result)); } }
private function decodeConduitParams(AphrontRequest $request, $method) { // Look for parameters from the Conduit API Console, which are encoded // as HTTP POST parameters in an array, e.g.: // // params[name]=value¶ms[name2]=value2 // // The fields are individually JSON encoded, since we require users to // enter JSON so that we avoid type ambiguity. $params = $request->getArr('params', null); if ($params !== null) { foreach ($params as $key => $value) { if ($value == '') { // Interpret empty string null (e.g., the user didn't type anything // into the box). $value = 'null'; } $decoded_value = json_decode($value, true); if ($decoded_value === null && strtolower($value) != 'null') { // When json_decode() fails, it returns null. This almost certainly // indicates that a user was using the web UI and didn't put quotes // around a string value. We can either do what we think they meant // (treat it as a string) or fail. For now, err on the side of // caution and fail. In the future, if we make the Conduit API // actually do type checking, it might be reasonable to treat it as // a string if the parameter type is string. throw new Exception(pht("The value for parameter '%s' is not valid JSON. All " . "parameters must be encoded as JSON values, including strings " . "(which means you need to surround them in double quotes). " . "Check your syntax. Value was: %s.", $key, $value)); } $params[$key] = $decoded_value; } $metadata = idx($params, '__conduit__', array()); unset($params['__conduit__']); return array($metadata, $params); } // Otherwise, look for a single parameter called 'params' which has the // entire param dictionary JSON encoded. $params_json = $request->getStr('params'); if (strlen($params_json)) { $params = null; try { $params = phutil_json_decode($params_json); } catch (PhutilJSONParserException $ex) { throw new PhutilProxyException(pht("Invalid parameter information was passed to method '%s'.", $method), $ex); } $metadata = idx($params, '__conduit__', array()); unset($params['__conduit__']); return array($metadata, $params); } // If we do not have `params`, assume this is a simple HTTP request with // HTTP key-value pairs. $params = array(); $metadata = array(); foreach ($request->getPassthroughRequestData() as $key => $value) { $meta_key = ConduitAPIMethod::getParameterMetadataKey($key); if ($meta_key !== null) { $metadata[$meta_key] = $value; } else { $params[$key] = $value; } } return array($metadata, $params); }
public function testLoadAllConduitMethods() { ConduitAPIMethod::loadAllConduitMethods(); $this->assertTrue(true); }
private function authorizeOAuthMethodAccess(PhabricatorOAuthClientAuthorization $authorization, $method_name) { $method = ConduitAPIMethod::getConduitMethod($method_name); if (!$method) { return false; } $required_scope = $method->getRequiredScope(); switch ($required_scope) { case ConduitAPIMethod::SCOPE_ALWAYS: return true; case ConduitAPIMethod::SCOPE_NEVER: return false; } $authorization_scope = $authorization->getScope(); if (!empty($authorization_scope[$required_scope])) { return true; } return false; }
protected function buildCustomSearchFields() { return array(id(new PhabricatorUsersSearchField())->setKey('callerPHIDs')->setLabel(pht('Methods'))->setAliases(array('caller', 'callers'))->setDescription(pht('Find calls by specific users.')), id(new PhabricatorSearchStringListField())->setKey('methods')->setLabel(pht('Methods'))->setDescription(pht('Find calls to specific methods.')), id(new PhabricatorSearchCheckboxesField())->setKey('statuses')->setLabel(pht('Method Status'))->setAliases(array('status'))->setDescription(pht('Find calls to stable, unstable, or deprecated methods.'))->setOptions(ConduitAPIMethod::getMethodStatusMap())); }
private function getMethodFilters() { $classes = $this->getAllMethodImplementationClasses(); $method_names = array(); foreach ($classes as $method_class) { $method_name = ConduitAPIMethod::getAPIMethodNameFromClassName($method_class); $parts = explode('.', $method_name); $method_names[] = array('full_name' => $method_name, 'group_name' => reset($parts)); } $method_names = igroup($method_names, 'group_name'); ksort($method_names); return $method_names; }
echo "usage: api.php <user_phid> <method>\n"; exit(1); } $user = null; $user_str = $argv[1]; try { $user = id(new PhabricatorUser())->loadOneWhere('phid = %s', $user_str); } catch (Exception $e) { // no op; we'll error in a line or two } if (empty($user)) { echo "usage: api.php <user_phid> <method>\n" . "user {$user_str} does not exist or failed to load\n"; exit(1); } $method = $argv[2]; $method_class_str = ConduitAPIMethod::getClassNameFromAPIMethodName($method); try { $method_class = newv($method_class_str, array()); } catch (Exception $e) { echo "usage: api.php <user_phid> <method>\n" . "method {$method_class_str} does not exist\n"; exit(1); } $log = new PhabricatorConduitMethodCallLog(); $log->setMethod($method); $params = @file_get_contents('php://stdin'); $params = json_decode($params, true); if (!is_array($params)) { echo "provide method parameters on stdin as a JSON blob"; exit(1); } // build a quick ConduitAPIRequest from stdin PLUS the authenticated user
public function processRequest() { $time_start = microtime(true); $request = $this->getRequest(); $method = $this->method; $method_class = ConduitAPIMethod::getClassNameFromAPIMethodName($method); $api_request = null; $log = new PhabricatorConduitMethodCallLog(); $log->setMethod($method); $metadata = array(); try { if (!class_exists($method_class)) { throw new Exception("Unable to load the implementation class for method '{$method}'. " . "You may have misspelled the method, need to define " . "'{$method_class}', or need to run 'arc build'."); } $class_info = new ReflectionClass($method_class); if ($class_info->isAbstract()) { throw new Exception("Method '{$method}' is not valid; the implementation is an abstract " . "base class."); } $method_handler = newv($method_class, array()); if (isset($_REQUEST['params']) && is_array($_REQUEST['params'])) { $params_post = $request->getArr('params'); foreach ($params_post as $key => $value) { if ($value == '') { // Interpret empty string null (e.g., the user didn't type anything // into the box). $value = 'null'; } $decoded_value = json_decode($value, true); if ($decoded_value === null && strtolower($value) != 'null') { // When json_decode() fails, it returns null. This almost certainly // indicates that a user was using the web UI and didn't put quotes // around a string value. We can either do what we think they meant // (treat it as a string) or fail. For now, err on the side of // caution and fail. In the future, if we make the Conduit API // actually do type checking, it might be reasonable to treat it as // a string if the parameter type is string. throw new Exception("The value for parameter '{$key}' is not valid JSON. All " . "parameters must be encoded as JSON values, including strings " . "(which means you need to surround them in double quotes). " . "Check your syntax. Value was: {$value}"); } $params_post[$key] = $decoded_value; } $params = $params_post; } else { $params_json = $request->getStr('params'); if (!strlen($params_json)) { $params = array(); } else { $params = json_decode($params_json, true); if (!is_array($params)) { throw new Exception("Invalid parameter information was passed to method " . "'{$method}', could not decode JSON serialization."); } } } $metadata = idx($params, '__conduit__', array()); unset($params['__conduit__']); $result = null; $api_request = new ConduitAPIRequest($params); $allow_unguarded_writes = false; $auth_error = null; if ($method_handler->shouldRequireAuthentication()) { $auth_error = $this->authenticateUser($api_request, $metadata); // If we've explicitly authenticated the user here and either done // CSRF validation or are using a non-web authentication mechanism. $allow_unguarded_writes = true; if (isset($metadata['actAsUser'])) { $this->actAsUser($api_request, $metadata['actAsUser']); } } if ($method_handler->shouldAllowUnguardedWrites()) { $allow_unguarded_writes = true; } if ($auth_error === null) { if ($allow_unguarded_writes) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); } try { $result = $method_handler->executeMethod($api_request); $error_code = null; $error_info = null; } catch (ConduitException $ex) { $result = null; $error_code = $ex->getMessage(); if ($ex->getErrorDescription()) { $error_info = $ex->getErrorDescription(); } else { $error_info = $method_handler->getErrorDescription($error_code); } } if ($allow_unguarded_writes) { unset($unguarded); } } else { list($error_code, $error_info) = $auth_error; } } catch (Exception $ex) { phlog($ex); $result = null; $error_code = 'ERR-CONDUIT-CORE'; $error_info = $ex->getMessage(); } $time_end = microtime(true); $connection_id = null; if (idx($metadata, 'connectionID')) { $connection_id = $metadata['connectionID']; } else { if ($method == 'conduit.connect' && $result) { $connection_id = idx($result, 'connectionID'); } } $log->setConnectionID($connection_id); $log->setError((string) $error_code); $log->setDuration(1000000 * ($time_end - $time_start)); // TODO: This is a hack, but the insert is comparatively expensive and // we only really care about having these logs for real CLI clients, if // even that. if (empty($metadata['authToken'])) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $log->save(); unset($unguarded); } $result = array('result' => $result, 'error_code' => $error_code, 'error_info' => $error_info); switch ($request->getStr('output')) { case 'human': return $this->buildHumanReadableResponse($method, $api_request, $result); case 'json': default: return id(new AphrontFileResponse())->setMimeType('application/json')->setContent('for(;;);' . json_encode($result)); } }
private function buildCURLExample(ConduitAPIMethod $method, $params) { $call_uri = '/api/' . $method->getAPIMethodName(); $parts = array(); $linebreak = array('\\', phutil_tag('br'), ' '); $parts[] = '$ curl '; $parts[] = phutil_tag('strong', array('class' => 'real'), csprintf('%R', PhabricatorEnv::getURI($call_uri))); $parts[] = ' '; $parts[] = $linebreak; $parts[] = '-d api.token='; $parts[] = phutil_tag('strong', array(), 'api-token'); $parts[] = ' '; $parts[] = $linebreak; if ($params === null) { $parts[] = '-d '; $parts[] = phutil_tag('strong', array(), 'param'); $parts[] = '='; $parts[] = phutil_tag('strong', array(), 'value'); $parts[] = ' '; $parts[] = $linebreak; $parts[] = phutil_tag('strong', array(), '...'); } else { $lines = array(); $params = $this->simplifyParams($params); foreach ($params as $key => $value) { $pieces = $this->getQueryStringParts(null, $key, $value); foreach ($pieces as $piece) { $lines[] = array('-d ', phutil_tag('strong', array('class' => 'real'), $piece)); } } $parts[] = phutil_implode_html(array(' ', $linebreak), $lines); } return $this->renderExampleCode($parts); }
public function buildConduitResponse(ConduitAPIRequest $request, ConduitAPIMethod $method) { $viewer = $this->requireViewer(); $query_key = $request->getValue('queryKey'); if (!strlen($query_key)) { $saved_query = new PhabricatorSavedQuery(); } else { if ($this->isBuiltinQuery($query_key)) { $saved_query = $this->buildSavedQueryFromBuiltin($query_key); } else { $saved_query = id(new PhabricatorSavedQueryQuery())->setViewer($viewer)->withQueryKeys(array($query_key))->executeOne(); if (!$saved_query) { throw new Exception(pht('Query key "%s" does not correspond to a valid query.', $query_key)); } } } $constraints = $request->getValue('constraints', array()); $fields = $this->getSearchFieldsForConduit(); foreach ($fields as $key => $field) { if (!$field->getConduitParameterType()) { unset($fields[$key]); } } foreach ($fields as $field) { if (!$field->getValueExistsInConduitRequest($constraints)) { continue; } $value = $field->readValueFromConduitRequest($constraints); $saved_query->setParameter($field->getKey(), $value); } $this->saveQuery($saved_query); $query = $this->buildQueryFromSavedQuery($saved_query); $pager = $this->newPagerForSavedQuery($saved_query); $attachments = $this->getConduitSearchAttachments(); // TODO: Validate this better. $attachment_specs = $request->getValue('attachments', array()); $attachments = array_select_keys($attachments, array_keys($attachment_specs)); foreach ($attachments as $key => $attachment) { $attachment->setViewer($viewer); } foreach ($attachments as $key => $attachment) { $attachment->willLoadAttachmentData($query, $attachment_specs[$key]); } $this->setQueryOrderForConduit($query, $request); $this->setPagerLimitForConduit($pager, $request); $this->setPagerOffsetsForConduit($pager, $request); $objects = $this->executeQuery($query, $pager); $data = array(); if ($objects) { $field_extensions = $this->getConduitFieldExtensions(); $attachment_data = array(); foreach ($attachments as $key => $attachment) { $attachment_data[$key] = $attachment->loadAttachmentData($objects, $attachment_specs[$key]); } foreach ($objects as $object) { $field_map = $this->getObjectWireFieldsForConduit($object, $field_extensions); $attachment_map = array(); foreach ($attachments as $key => $attachment) { $attachment_map[$key] = $attachment->getAttachmentForObject($object, $attachment_data[$key], $attachment_specs[$key]); } // If this is empty, we still want to emit a JSON object, not a // JSON list. if (!$attachment_map) { $attachment_map = (object) $attachment_map; } $id = (int) $object->getID(); $phid = $object->getPHID(); $data[] = array('id' => $id, 'type' => phid_get_type($phid), 'phid' => $phid, 'fields' => $field_map, 'attachments' => $attachment_map); } } return array('data' => $data, 'maps' => $method->getQueryMaps($query), 'query' => array('queryKey' => $saved_query->getQueryKey()), 'cursor' => array('limit' => $pager->getPageSize(), 'after' => $pager->getNextPageID(), 'before' => $pager->getPrevPageID(), 'order' => $request->getValue('order'))); }
public function __construct(ConduitAPIMethod $method, $application) { parent::__construct(pht("Method '%s' belongs to application '%s', which is not installed.", $method->getAPIMethodName(), $application)); }
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; }