pax_global_header00006660000000000000000000000064127377444220014526gustar00rootroot0000000000000052 comment=dac1219c54eaf1be48ea29ceab0ecad6d1032d9a python-phabricator-0.7.0/000077500000000000000000000000001273774442200153475ustar00rootroot00000000000000python-phabricator-0.7.0/.gitignore000066400000000000000000000000601273774442200173330ustar00rootroot00000000000000*.pyc *.swp *.egg *.egg-info build/ dist/ .idea python-phabricator-0.7.0/.travis.yml000066400000000000000000000006041273774442200174600ustar00rootroot00000000000000language: python python: - '2.7' - '3.5' install: pip install . script: python -m phabricator.tests.test_phabricator deploy: provider: pypi user: disqus password: secure: AJ7zSLd6BgI4W8Kp3KEx5O40bUJA91PkgLTZb5MnCx4/8nUPlkk+LqvodaiiJQEGzpP8COPvRlzJ/swd8d0P38+Se6V83wA43MylimzrgngO6t3c/lXa/aMnrRzSpSfK5QznEMP2zcSU1ReD+2dNr7ATKajbGqTzrCFk/hQPMZ0= on: tags: true python-phabricator-0.7.0/CHANGES000066400000000000000000000012661273774442200163470ustar00rootroot000000000000000.7.0 * Allow for nested methods to support conduit calls such as diffusion.repository.edit * Fix regular expressions that cause type parsing to fail 0.6.1 * Fix Python 3 related issues 0.6.0 * Python 3 support * Fix "not JSON serializable" error when not using a token * Better tests * Updated interfaces * Moved `conduit` to `_conduit` so `.conduit` API is available now * Updated interfaces to the latest Phabricator version 0.5.0 * Support new style token-based authentication 0.4.0 * Update interfaces.json to Phab @ e75b389 * Add Phabricator.update_interfaces to pull latest interfaces from conduit.query * Fix validation of string to be str or unicode 0.1.0 * Initial release python-phabricator-0.7.0/LICENSE000066400000000000000000000251161273774442200163610ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2012 DISQUS Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. python-phabricator-0.7.0/MANIFEST.in000066400000000000000000000001361273774442200171050ustar00rootroot00000000000000include setup.py README.rst MANIFEST.in LICENSE phabricator/interfaces.json global-exclude *~ python-phabricator-0.7.0/README.rst000066400000000000000000000017131273774442200170400ustar00rootroot00000000000000python-phabricator ================== .. image:: https://travis-ci.org/disqus/python-phabricator.png?branch=master :target: https://travis-ci.org/disqus/python-phabricator Installation ------------ :: $ pip install phabricator Usage ----- Use the API by instantiating it, and then calling the method through dotted notation chaining:: from phabricator import Phabricator phab = Phabricator() # This will use your ~/.arcrc file phab.user.whoami() Parameters are passed as keyword arguments to the resource call:: phab.user.find(aliases=["sugarc0de"]) Documentation on all methods is located at https://secure.phabricator.com/conduit/ Interface out-of-date --------------------- If Phabricator modifies Conduit and the included ``interfaces.json`` is out-of-date or to make sure to always have the latest interfaces:: from phabricator import Phabricator phab = Phabricator() phab.update_interfaces() phab.user.whoami() python-phabricator-0.7.0/phabricator/000077500000000000000000000000001273774442200176455ustar00rootroot00000000000000python-phabricator-0.7.0/phabricator/__init__.py000066400000000000000000000273021273774442200217620ustar00rootroot00000000000000""" python-phabricator ------------------ >>> api = phabricator.Phabricator() >>> api.user.whoami().userName 'example' For more endpoints, see https://secure.phabricator.com/conduit/ """ try: __version__ = __import__('pkg_resources') \ .get_distribution('phabricator').version except: __version__ = 'unknown' import collections import copy import hashlib import json import os.path import re import socket import time from ._compat import ( MutableMapping, iteritems, string_types, httplib, urlparse, urlencode, ) __all__ = ['Phabricator'] ON_WINDOWS = os.name == 'nt' CURRENT_DIR = os.getcwd() # Default Phabricator interfaces INTERFACES = {} with open(os.path.join(os.path.dirname(__file__), 'interfaces.json')) as fobj: INTERFACES = json.load(fobj) # Load arc config ARC_CONFIGS = ( # System config os.path.join( os.environ['ProgramData'], 'Phabricator', 'Arcanist', 'config' ) if ON_WINDOWS else os.path.join('/etc', 'arcconfig'), # User config os.path.join( os.environ['AppData'] if ON_WINDOWS else os.path.expanduser('~'), '.arcrc' ), # Project config os.path.join(CURRENT_DIR, '.arcconfig'), # Local project config os.path.join(CURRENT_DIR, '.git', 'arc', 'config'), ) ARCRC = {} for conf in ARC_CONFIGS: if os.path.exists(conf): with open(conf, 'r') as fobj: ARCRC.update(json.load(fobj)) # Map Phabricator types to Python types PARAM_TYPE_MAP = { # int types 'int': int, 'uint': int, 'revisionid': int, 'revision_id': int, 'diffid': int, 'diff_id': int, 'id': int, # bool types 'bool': bool, # dict types 'map': dict, 'dict': dict, # list types 'list': list, # tuple types 'pair': tuple, # str types 'str': string_types, 'string': string_types, 'phid': string_types, 'guids': string_types, 'type': string_types, } TYPE_INFO_COMMENT_RE = re.compile(r'\s*\([^)]+\)\s*$') TYPE_INFO_SPLITTER_RE = re.compile(r'(\w+(?:<.+>)?)(?:\s+|$)') TYPE_INFO_RE = re.compile(r']+>>?)?(?:.+|$)') def map_param_type(param_type): """ Perform param type mapping This requires a bit of logic since this isn't standardized. If a type doesn't map, assume str """ main_type, sub_type = TYPE_INFO_RE.match(param_type).groups() if main_type in ('list', 'array'): # Handle no sub-type: "required list" if sub_type is not None: sub_type = sub_type.strip() if not sub_type: sub_type = 'str' # Handle list of pairs: "optional list>" sub_match = TYPE_INFO_RE.match(sub_type) if sub_match: sub_type = sub_match.group(1).lower() return [PARAM_TYPE_MAP.setdefault(sub_type, string_types)] return PARAM_TYPE_MAP.setdefault(main_type, string_types) def parse_interfaces(interfaces): """ Parse the conduit.query json dict response This performs the logic of parsing the non-standard params dict and then returning a dict Resource can understand """ parsed_interfaces = collections.defaultdict(dict) for m, d in iteritems(interfaces): app, func = m.split('.', 1) method = parsed_interfaces[app][func] = {} # Make default assumptions since these aren't provided by Phab method['formats'] = ['json', 'human'] method['method'] = 'POST' method['optional'] = {} method['required'] = {} for name, type_info in iteritems(dict(d['params'])): # Set the defaults optionality = 'required' param_type = 'string' # Usually in the format: type_info = TYPE_INFO_COMMENT_RE.sub('', type_info) info_pieces = TYPE_INFO_SPLITTER_RE.findall(type_info) for info_piece in info_pieces: if info_piece in ('optional', 'required'): optionality = info_piece elif info_piece == 'ignored': optionality = 'optional' param_type = 'string' elif info_piece == 'nonempty': optionality = 'required' else: param_type = info_piece method[optionality][name] = map_param_type(param_type) return dict(parsed_interfaces) class ConfigurationError(Exception): pass class APIError(Exception): def __init__(self, code, message): self.code = code self.message = message def __str__(self): return '%s: %s' % (self.code, self.message) class Result(MutableMapping): def __init__(self, response): self.response = response def __getitem__(self, key): return self.response[key] __getattr__ = __getitem__ def __setitem__(self, key, value): self.response[key] = value def __delitem__(self, key): del self.response[key] def __iter__(self): return iter(self.response) def __len__(self): return len(self.response) def __repr__(self): return '<%s: %s>' % (type(self).__name__, repr(self.response)) class Resource(object): def __init__(self, api, interface=None, endpoint=None, method=None, nested=False): self.api = api self.interface = interface or copy.deepcopy(parse_interfaces(INTERFACES)) self.endpoint = endpoint self.method = method self.nested = nested def __getattr__(self, attr): if attr in getattr(self, '__dict__'): return getattr(self, attr) interface = self.interface if self.nested: attr = "%s.%s" % (self.endpoint, attr) submethod_exists = False submethod_match = attr + '.' for key in interface.keys(): if key.startswith(submethod_match): submethod_exists = True break if attr not in interface and submethod_exists: return Resource(self.api, interface, attr, self.endpoint, nested=True) elif attr not in interface: interface[attr] = {} if self.nested: return Resource(self.api, interface[attr], attr, self.method) return Resource(self.api, interface[attr], attr, self.endpoint) def __call__(self, **kwargs): return self._request(**kwargs) def _request(self, **kwargs): # Check for missing variables resource = self.interface def validate_kwarg(key, target): # Always allow list if isinstance(target, list): return ( isinstance(key, (list, tuple, set)) and all(validate_kwarg(x, target[0]) for x in key) ) return isinstance(key, tuple(target) if isinstance(target, list) else target) for key, val in resource.get('required', {}).items(): if key not in [x.split(':')[0] for x in kwargs.keys()]: raise ValueError('Missing required argument: %s' % key) if isinstance(kwargs.get(key), list) and not isinstance(val, list): raise ValueError('Wrong argument type: %s is not a list' % key) elif not validate_kwarg(kwargs.get(key), val): if isinstance(val, list): raise ValueError('Wrong argument type: %s is not a list of %ss' % (key, val[0])) raise ValueError('Wrong argument type: %s is not a %s' % (key, val)) conduit = self.api._conduit if conduit: # Already authenticated, add session key to json data kwargs['__conduit__'] = conduit elif self.method == 'conduit' and self.endpoint == 'connect': # Not authenticated, requesting new session key token = str(int(time.time())) kwargs['authToken'] = token kwargs['authSignature'] = self.api.generate_hash(token) else: # Authorization is required, silently auth the user self.api.connect() kwargs['__conduit__'] = self.api._conduit url = urlparse.urlparse(self.api.host) if url.scheme == 'https': conn = httplib.HTTPSConnection(url.netloc, timeout=self.api.timeout) else: conn = httplib.HTTPConnection(url.netloc, timeout=self.api.timeout) path = url.path + '%s.%s' % (self.method, self.endpoint) headers = { 'User-Agent': 'python-phabricator/%s' % str(self.api.clientVersion), 'Content-Type': 'application/x-www-form-urlencoded' } body = urlencode({ "params": json.dumps(kwargs), "output": self.api.response_format }) # TODO: Use HTTP "method" from interfaces.json conn.request('POST', path, body, headers) response = conn.getresponse() # Make sure we got a 2xx response indicating success if not response.status >= 200 or not response.status < 300: raise httplib.HTTPException( 'Bad response status: {0}'.format(response.status) ) response_data = response.read() if isinstance(response_data, str): response = response_data else: response = response_data.decode("utf-8") data = self._parse_response(response) return Result(data['result']) def _parse_response(self, data): # Process the response back to python parsed = self.api.formats[self.api.response_format](data) # Errors return 200, so check response content for exception if parsed['error_code']: raise APIError(parsed['error_code'], parsed['error_info']) return parsed class Phabricator(Resource): formats = { 'json': lambda x: json.loads(x), } def __init__(self, username=None, certificate=None, host=None, timeout=5, response_format='json', token=None, **kwargs): defined_hosts = ARCRC.get('hosts', {}) try: self.host = host if host else list(defined_hosts.keys())[0] except IndexError: raise ConfigurationError("No host found or provided.") current_host_config = defined_hosts.get(self.host, {}) self.token = token if token else current_host_config.get('token') if self.token is None: self.username = username if username else current_host_config.get('user') self.certificate = certificate if certificate else current_host_config.get('cert') self.timeout = timeout self.response_format = response_format self.client = 'python-phabricator' self.clientVersion = 1 self.clientDescription = socket.gethostname() + ':python-phabricator' self._conduit = None super(Phabricator, self).__init__(self) def _request(self, **kwargs): raise SyntaxError('You cannot call the Conduit API without a resource.') def connect(self): if self.token: self._conduit = { 'token': self.token } return auth = Resource(api=self, method='conduit', endpoint='connect') response = auth( user=self.username, host=self.host, client=self.client, clientVersion=self.clientVersion ) self._conduit = { 'sessionKey': response.sessionKey, 'connectionID': response.connectionID } def generate_hash(self, token): source_string = (token + self.api.certificate).encode('utf-8') return hashlib.sha1(source_string).hexdigest() def update_interfaces(self): query = Resource(api=self, method='conduit', endpoint='query') interfaces = query() self.interface = parse_interfaces(interfaces) python-phabricator-0.7.0/phabricator/_compat.py000066400000000000000000000012201273774442200216340ustar00rootroot00000000000000import sys PY3 = sys.version_info[0] >= 3 try: from collections.abc import MutableMapping except ImportError: from collections import MutableMapping try: import httplib except ImportError: import http.client as httplib try: import urlparse except ImportError: import urllib.parse as urlparse try: from urllib import urlencode except ImportError: from urllib.parse import urlencode if PY3: str_type = str string_types = str, def iteritems(d, **kw): return iter(d.items(**kw)) else: str_type = unicode string_types = basestring, def iteritems(d, **kw): return d.iteritems(**kw) python-phabricator-0.7.0/phabricator/interfaces.json000066400000000000000000001524131273774442200226710ustar00rootroot00000000000000{"almanac.device.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"almanac.service.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"audit.query":{"description":"Query audit requests.","params":{"auditorPHIDs":"optional list\u003cphid\u003e","commitPHIDs":"optional list\u003cphid\u003e","status":"optional string-constant\u003c\"audit-status-any\", \"audit-status-open\", \"audit-status-concern\", \"audit-status-accepted\", \"audit-status-partial\"\u003e (default = \"audit-status-any\")","offset":"optional int","limit":"optional int (default = 100)"},"return":"list\u003cdict\u003e"},"auth.logout":{"description":"Terminate all web login sessions. If called via OAuth, also terminate the current OAuth token.\n\nWARNING: This method does what it claims on the label. If you call this method via the test console in the web UI, it will log you out!","params":[],"return":"void"},"auth.querypublickeys":{"description":"Query public keys.","params":{"ids":"optional list\u003cid\u003e","phids":"optional list\u003cphid\u003e","objectPHIDs":"optional list\u003cphid\u003e","keys":"optional list\u003cstring\u003e","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"result-set"},"badges.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"badges.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"conduit.connect":{"description":"Connect a session-based client.","params":{"client":"required string","clientVersion":"required int","clientDescription":"optional string","user":"optional string","authToken":"optional int","authSignature":"optional string","host":"deprecated"},"return":"dict\u003cstring, any\u003e"},"conduit.getcapabilities":{"description":"List capabilities, wire formats, and authentication protocols available on this server.","params":[],"return":"dict\u003cstring, any\u003e"},"conduit.getcertificate":{"description":"Retrieve certificate information for a user.","params":{"token":"required string","host":"required string"},"return":"dict\u003cstring, any\u003e"},"conduit.ping":{"description":"Basic ping for monitoring or a health-check.","params":[],"return":"string"},"conduit.query":{"description":"Returns the parameters of the Conduit methods.","params":[],"return":"dict\u003cdict\u003e"},"conpherence.createthread":{"description":"Create a new conpherence thread.","params":{"title":"optional string","message":"required string","participantPHIDs":"required list\u003cphids\u003e"},"return":"nonempty dict"},"conpherence.querythread":{"description":"Query for Conpherence threads for the logged in user. You can query by IDs or PHIDs for specific Conpherence threads. Otherwise, specify limit and offset to query the most recently updated Conpherences for the logged in user.","params":{"ids":"optional array\u003cint\u003e","phids":"optional array\u003cphids\u003e","limit":"optional int","offset":"optional int"},"return":"nonempty dict"},"conpherence.querytransaction":{"description":"Query for transactions for the logged in user within a specific Conpherence room. You can specify the room by ID or PHID. Otherwise, specify limit and offset to query the most recent transactions within the Conpherence room for the logged in user.","params":{"roomID":"optional int","roomPHID":"optional phid","limit":"optional int","offset":"optional int"},"return":"nonempty dict"},"conpherence.updatethread":{"description":"Update an existing conpherence room.","params":{"id":"optional int","phid":"optional phid","title":"optional string","message":"optional string","addParticipantPHIDs":"optional list\u003cphids\u003e","removeParticipantPHID":"optional phid"},"return":"bool"},"dashboard.panel.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"differential.close":{"description":"Close a Differential revision.","params":{"revisionID":"required int"},"return":"void"},"differential.createcomment":{"description":"Add a comment to a Differential revision.","params":{"revision_id":"required revisionid","message":"optional string","action":"optional string","silent":"optional bool","attach_inlines":"optional bool"},"return":"nonempty dict"},"differential.creatediff":{"description":"Create a new Differential diff.","params":{"changes":"required list\u003cdict\u003e","sourceMachine":"required string","sourcePath":"required string","branch":"required string","bookmark":"optional string","sourceControlSystem":"required string-constant\u003c\"svn\", \"git\", \"hg\"\u003e","sourceControlPath":"required string","sourceControlBaseRevision":"required string","creationMethod":"optional string","lintStatus":"required string-constant\u003c\"none\", \"skip\", \"okay\", \"warn\", \"fail\"\u003e","unitStatus":"required string-constant\u003c\"none\", \"skip\", \"okay\", \"warn\", \"fail\"\u003e","repositoryPHID":"optional phid","parentRevisionID":"deprecated","authorPHID":"deprecated","repositoryUUID":"deprecated"},"return":"nonempty dict"},"differential.createinline":{"description":"Add an inline comment to a Differential revision.","params":{"revisionID":"optional revisionid","diffID":"optional diffid","filePath":"required string","isNewFile":"required bool","lineNumber":"required int","lineLength":"optional int","content":"required string"},"return":"nonempty dict"},"differential.createrawdiff":{"description":"Create a new Differential diff from a raw diff source.","params":{"diff":"required string","repositoryPHID":"optional string","viewPolicy":"optional string"},"return":"nonempty dict"},"differential.createrevision":{"description":"Create a new Differential revision.","params":{"user":"ignored","diffid":"required diffid","fields":"required dict"},"return":"nonempty dict"},"differential.getcommitmessage":{"description":"Retrieve Differential commit messages or message templates.","params":{"revision_id":"optional revision_id","fields":"optional dict\u003cstring, wild\u003e","edit":"optional string-constant\u003c\"edit\", \"create\"\u003e"},"return":"nonempty string"},"differential.getcommitpaths":{"description":"Query which paths should be included when committing a Differential revision.","params":{"revision_id":"required int"},"return":"nonempty list\u003cstring\u003e"},"differential.getrawdiff":{"description":"Retrieve a raw diff","params":{"diffID":"required diffID"},"return":"nonempty string"},"differential.parsecommitmessage":{"description":"Parse commit messages for Differential fields.","params":{"corpus":"required string","partial":"optional bool"},"return":"nonempty dict"},"differential.query":{"description":"Query Differential revisions which match certain criteria.","params":{"authors":"optional list\u003cphid\u003e","ccs":"optional list\u003cphid\u003e","reviewers":"optional list\u003cphid\u003e","paths":"optional list\u003cpair\u003ccallsign, path\u003e\u003e","commitHashes":"optional list\u003cpair\u003cstring-constant\u003c\"gtcm\", \"gttr\", \"hgcm\"\u003e, string\u003e\u003e","status":"optional string-constant\u003c\"status-any\", \"status-open\", \"status-accepted\", \"status-closed\"\u003e","order":"optional string-constant\u003c\"order-modified\", \"order-created\"\u003e","limit":"optional uint","offset":"optional uint","ids":"optional list\u003cuint\u003e","phids":"optional list\u003cphid\u003e","subscribers":"optional list\u003cphid\u003e","responsibleUsers":"optional list\u003cphid\u003e","branches":"optional list\u003cstring\u003e"},"return":"list\u003cdict\u003e"},"differential.querydiffs":{"description":"Query differential diffs which match certain criteria.","params":{"ids":"optional list\u003cuint\u003e","revisionIDs":"optional list\u003cuint\u003e"},"return":"list\u003cdict\u003e"},"differential.setdiffproperty":{"description":"Attach properties to Differential diffs.","params":{"diff_id":"required diff_id","name":"required string","data":"required string"},"return":"void"},"differential.updaterevision":{"description":"Update a Differential revision.","params":{"id":"required revisionid","diffid":"required diffid","fields":"required dict","message":"required string"},"return":"nonempty dict"},"differential.revision.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"differential.find":{"description":"Query Differential revisions which match certain criteria.","params":{"query":"required string-constant\u003c\"open\", \"committable\", \"revision-ids\", \"phids\"\u003e","guids":"required nonempty list\u003cguids\u003e"},"return":"nonempty list\u003cdict\u003e"},"differential.getalldiffs":{"description":"Load all diffs for given revisions from Differential.","params":{"revision_ids":"required list\u003cint\u003e"},"return":"dict"},"differential.getdiff":{"description":"Load the content of a diff from Differential by revision ID or diff ID.","params":{"revision_id":"optional id","diff_id":"optional id"},"return":"nonempty dict"},"differential.getrevision":{"description":"Load the content of a revision from Differential.","params":{"revision_id":"required id"},"return":"nonempty dict"},"differential.getrevisioncomments":{"description":"Retrieve Differential Revision Comments.","params":{"ids":"required list\u003cint\u003e","inlines":"optional bool (deprecated)"},"return":"nonempty list\u003cdict\u003cstring, wild\u003e\u003e"},"diffusion.findsymbols":{"description":"Retrieve Diffusion symbol information.","params":{"name":"optional string","namePrefix":"optional string","context":"optional string","language":"optional string","type":"optional string","repositoryPHID":"optional string"},"return":"nonempty list\u003cdict\u003e"},"diffusion.getrecentcommitsbypath":{"description":"Get commit identifiers for recent commits affecting a given path.","params":{"callsign":"required string","path":"required string","branch":"optional string","limit":"optional int"},"return":"nonempty list\u003cstring\u003e"},"diffusion.querycommits":{"description":"Retrieve information about commits.","params":{"ids":"optional list\u003cint\u003e","phids":"optional list\u003cphid\u003e","names":"optional list\u003cstring\u003e","repositoryPHID":"optional phid","needMessages":"optional bool","bypassCache":"optional bool","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, dict\u003e"},"diffusion.blame":{"description":"Get blame information for a list of paths.","params":{"paths":"required list\u003cstring\u003e","commit":"required string","timeout":"optional int","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"map\u003cstring, wild\u003e"},"diffusion.branchquery":{"description":"Determine what branches exist for a repository.","params":{"closed":"optional bool","limit":"optional int","offset":"optional int","contains":"optional string","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"list\u003cdict\u003e"},"diffusion.browsequery":{"description":"File(s) information for a repository at an (optional) path and (optional) commit.","params":{"path":"optional string","commit":"optional string","needValidityOnly":"optional bool","limit":"optional int","offset":"optional int","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"array"},"diffusion.commitparentsquery":{"description":"Get the commit identifiers for a commit's parent or parents.","params":{"commit":"required string","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"list\u003cstring\u003e"},"diffusion.diffquery":{"description":"Get diff information from a repository for a specific path at an (optional) commit.","params":{"path":"required string","commit":"optional string","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"array"},"diffusion.existsquery":{"description":"Determine if code exists in a version control system.","params":{"commit":"required string","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"bool"},"diffusion.filecontentquery":{"description":"Retrieve file content from a repository.","params":{"path":"required string","commit":"required string","timeout":"optional int","byteLimit":"optional int","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"array"},"diffusion.getlintmessages":{"description":"Get lint messages for existing code.","params":{"repositoryPHID":"required phid","branch":"required string","commit":"optional string","files":"required list\u003cstring\u003e"},"return":"list\u003cdict\u003e"},"diffusion.historyquery":{"description":"Returns history information for a repository at a specific commit and path.","params":{"commit":"required string","path":"required string","offset":"required int","limit":"required int","needDirectChanges":"optional bool","needChildChanges":"optional bool","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"array"},"diffusion.internal.gitrawdiffquery":{"description":"Internal method for getting raw diff information.","params":{"commit":"required string","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"string"},"diffusion.lastmodifiedquery":{"description":"Get the commits at which paths were last modified.","params":{"paths":"required map\u003cstring, string\u003e","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"map\u003cstring, string\u003e"},"diffusion.looksoon":{"description":"Advises Phabricator to look for new commits in a repository as soon as possible. This advice is most useful if you have just pushed new commits to that repository.","params":{"callsigns":"optional list\u003cstring\u003e (deprecated)","repositories":"optional list\u003cstring\u003e","urgency":"optional string"},"return":"void"},"diffusion.mergedcommitsquery":{"description":"Merged commit information for a specific commit in a repository.","params":{"commit":"required string","limit":"optional int","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"array"},"diffusion.querypaths":{"description":"Filename search on a repository.","params":{"path":"required string","commit":"required string","pattern":"optional string","limit":"optional int","offset":"optional int","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"list\u003cstring\u003e"},"diffusion.rawdiffquery":{"description":"Get raw diff information from a repository for a specific commit at an (optional) path.","params":{"commit":"required string","path":"optional string","timeout":"optional int","byteLimit":"optional int","linesOfContext":"optional int","againstCommit":"optional string","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"string"},"diffusion.refsquery":{"description":"Query a git repository for ref information at a specific commit.","params":{"commit":"required string","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"array"},"diffusion.repository.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"diffusion.repository.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"diffusion.resolverefs":{"description":"Resolve references into stable, canonical identifiers.","params":{"refs":"required list\u003cstring\u003e","types":"optional list\u003cstring\u003e","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"dict\u003cstring, list\u003cdict\u003cstring, wild\u003e\u003e\u003e"},"diffusion.searchquery":{"description":"Search (grep) a repository at a specific path and commit.","params":{"path":"required string","commit":"optional string","grep":"required string","limit":"optional int","offset":"optional int","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"array"},"diffusion.tagsquery":{"description":"Retrieve information about tags in a repository.","params":{"names":"optional list\u003cstring\u003e","commit":"optional string","needMessages":"optional bool","offset":"optional int","limit":"optional int","callsign":"optional string (deprecated)","repository":"optional string","branch":"optional string"},"return":"array"},"diffusion.updatecoverage":{"description":"Publish coverage information for a repository.","params":{"repositoryPHID":"required phid","branch":"required string","commit":"required string","coverage":"required map\u003cstring, string\u003e","mode":"optional string-constant\u003c\"overwrite\", \"update\"\u003e"},"return":"void"},"diffusion.uri.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"diffusion.createcomment":{"description":"Add a comment to a Diffusion commit. By specifying an action of \"concern\", \"accept\", \"resign\", or \"close\", auditing actions can be triggered. Defaults to \"comment\".","params":{"phid":"required string","action":"optional string","message":"required string","silent":"optional bool"},"return":"bool"},"feed.publish":{"description":"Publish a story to the feed.","params":{"type":"required string","data":"required dict","time":"optional int"},"return":"nonempty phid"},"feed.query":{"description":"Query the feed for stories","params":{"filterPHIDs":"optional list \u003cphid\u003e","limit":"optional int (default 100)","after":"optional int","before":"optional int","view":"optional string (data, html, html-summary, text)"},"return":"nonempty dict"},"file.allocate":{"description":"Prepare to upload a file.","params":{"name":"string","contentLength":"int","contentHash":"optional string","viewPolicy":"optional string","deleteAfterEpoch":"optional int"},"return":"map\u003cstring, wild\u003e"},"file.download":{"description":"Download a file from the server.","params":{"phid":"required phid"},"return":"nonempty base64-bytes"},"file.info":{"description":"Get information about a file.","params":{"phid":"optional phid","id":"optional id"},"return":"nonempty dict"},"file.querychunks":{"description":"Get information about file chunks.","params":{"filePHID":"phid"},"return":"list\u003cwild\u003e"},"file.upload":{"description":"Upload a file to the server.","params":{"data_base64":"required nonempty base64-bytes","name":"optional string","viewPolicy":"optional valid policy string or \u003cphid\u003e","canCDN":"optional bool"},"return":"nonempty guid"},"file.uploadchunk":{"description":"Upload a chunk of file data to the server.","params":{"filePHID":"phid","byteStart":"int","data":"string","dataEncoding":"string"},"return":"void"},"file.uploadhash":{"description":"Upload a file to the server using content hash.","params":{"hash":"required nonempty string","name":"required nonempty string"},"return":"phid or null"},"flag.delete":{"description":"Clear a flag.","params":{"id":"optional id","objectPHID":"optional phid"},"return":"dict | null"},"flag.edit":{"description":"Create or modify a flag.","params":{"objectPHID":"required phid","color":"optional int","note":"optional string"},"return":"dict"},"flag.query":{"description":"Query flag markers.","params":{"ownerPHIDs":"optional list\u003cphid\u003e","types":"optional list\u003ctype\u003e","objectPHIDs":"optional list\u003cphid\u003e","offset":"optional int","limit":"optional int (default = 100)"},"return":"list\u003cdict\u003e"},"harbormaster.createartifact":{"description":"Use this method to attach artifacts to build targets while running builds. Artifacts can be used to carry data through a complex build workflow, provide extra information to users, or store build results.\n\nWhen creating an artifact, you will choose an `artifactType` from this table. These types of artifacts are supported:\n| Artifact Type | Name | Summary |\n|-------------|--------------|--------------|\n| `host` | **Drydock Host** | References a host lease from Drydock. |\n| `working-copy` | **Drydock Working Copy** | References a working copy lease from Drydock. |\n| `file` | **File** | Stores a reference to file data which has been uploaded to Phabricator. |\n| `uri` | **URI** | Stores a URI. |\n\nEach artifact also needs an `artifactKey`, which names the artifact. Finally, you will provide some `artifactData` to fill in the content of the artifact. The data you provide depends on what type of artifact you are creating.\nDrydock Host\n--------------------------\n\nReferences a host lease from Drydock.\n\nCreate an artifact of this type by passing `host` as the `artifactType`. When creating an artifact of this type, provide these parameters as a dictionary to `artifactData`:\n| Key | Type | Description |\n|-------------|--------------|--------------|\n| `drydockLeasePHID` | \/\/string\/\/ | Drydock working copy lease to create an artifact from. |\nFor example:\n```lang=json\n{\n \"drydockLeasePHID\": \"PHID-DRYL-abcdefghijklmnopqrst\"\n}\n\n```\nDrydock Working Copy\n--------------------------\n\nReferences a working copy lease from Drydock.\n\nCreate an artifact of this type by passing `working-copy` as the `artifactType`. When creating an artifact of this type, provide these parameters as a dictionary to `artifactData`:\n| Key | Type | Description |\n|-------------|--------------|--------------|\n| `drydockLeasePHID` | \/\/string\/\/ | Drydock working copy lease to create an artifact from. |\nFor example:\n```lang=json\n{\n \"drydockLeasePHID\": \"PHID-DRYL-abcdefghijklmnopqrst\"\n}\n\n```\nFile\n--------------------------\n\nStores a reference to file data which has been uploaded to Phabricator.\n\nCreate an artifact of this type by passing `file` as the `artifactType`. When creating an artifact of this type, provide these parameters as a dictionary to `artifactData`:\n| Key | Type | Description |\n|-------------|--------------|--------------|\n| `filePHID` | \/\/string\/\/ | File to create an artifact from. |\nFor example:\n```lang=json\n{\n \"filePHID\": \"PHID-FILE-abcdefghijklmnopqrst\"\n}\n\n```\nURI\n--------------------------\n\nStores a URI.\n\nWith `ui.external`, you can use this artifact type to add links to build results in an external build system.\n\nCreate an artifact of this type by passing `uri` as the `artifactType`. When creating an artifact of this type, provide these parameters as a dictionary to `artifactData`:\n| Key | Type | Description |\n|-------------|--------------|--------------|\n| `uri` | \/\/string\/\/ | The URI to store. |\n| `name` | \/\/optional string\/\/ | Optional label for this URI. |\n| `ui.external` | \/\/optional bool\/\/ | If true, display this URI in the UI as an link to additional build details in an external build system. |\nFor example:\n```lang=json\n{\n \"uri\": \"https:\/\/buildserver.mycompany.com\/build\/123\/\",\n \"name\": \"View External Build Results\",\n \"ui.external\": true\n}\n\n```","params":{"buildTargetPHID":"phid","artifactKey":"string","artifactType":"string","artifactData":"map\u003cstring, wild\u003e"},"return":"wild"},"harbormaster.queryautotargets":{"description":"Load or create build autotargets.","params":{"objectPHID":"phid","targetKeys":"list\u003cstring\u003e"},"return":"map\u003cstring, phid\u003e"},"harbormaster.querybuildables":{"description":"Query Harbormaster buildables.","params":{"ids":"optional list\u003cid\u003e","phids":"optional list\u003cphid\u003e","buildablePHIDs":"optional list\u003cphid\u003e","containerPHIDs":"optional list\u003cphid\u003e","manualBuildables":"optional bool","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"wild"},"harbormaster.querybuilds":{"description":"Query Harbormaster builds.","params":{"ids":"optional list\u003cid\u003e","phids":"optional list\u003cphid\u003e","buildStatuses":"optional list\u003cstring\u003e","buildablePHIDs":"optional list\u003cphid\u003e","buildPlanPHIDs":"optional list\u003cphid\u003e","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"wild"},"harbormaster.sendmessage":{"description":"Send a message about the status of a build target to Harbormaster, notifying the application of build results in an external system.\n\nSending Messages\n================\nIf you run external builds, you can use this method to publish build results back into Harbormaster after the external system finishes work or as it makes progress.\n\nThe simplest way to use this method is to call it once after the build finishes with a `pass` or `fail` message. This will record the build result, and continue the next step in the build if the build was waiting for a result.\n\nWhen you send a status message about a build target, you can optionally include detailed `lint` or `unit` results alongside the message. See below for details.\n\nIf you want to report intermediate results but a build hasn't completed yet, you can use the `work` message. This message doesn't have any direct effects, but allows you to send additional data to update the progress of the build target. The target will continue waiting for a completion message, but the UI will update to show the progress which has been made.\n\nMessage Types\n=============\nWhen you send Harbormaster a message, you must include a `type`, which describes the overall state of the build. For example, use `pass` to tell Harbomaster that a build completed successfully.\n\nSupported message types are:\n\n| Type | Description |\n|--------------|--------------|\n| `pass` | Report that the target is complete, and the target has passed. |\n| `fail` | Report that the target is complete, and the target has failed. |\n| `work` | Report that work on the target is ongoing. This message can be used to report partial results during a build. |\n\nUnit Results\n============\nYou can report test results alongside a message. The simplest way to do this is to report all the results alongside a `pass` or `fail` message, but you can also send a `work` message to report intermediate results.\n\nTo provide unit test results, pass a list of results in the `unit` parameter. Each result shoud be a dictionary with these keys:\n\n| Key | Type | Description |\n|-------------|--------------|--------------|\n| `name` | \/\/string\/\/ | Short test name, like \"ExampleTest\". |\n| `result` | \/\/string\/\/ | Result of the test. |\n| `namespace` | \/\/optional string\/\/ | Optional namespace for this test. This is organizational and is often a class or module name, like \"ExampleTestCase\". |\n| `engine` | \/\/optional string\/\/ | Test engine running the test, like \"JavascriptTestEngine\". This primarily prevents collisions between tests with the same name in different test suites (for example, a Javascript test and a Python test). |\n| `duration` | \/\/optional float or int\/\/ | Runtime duration of the test, in seconds. |\n| `path` | \/\/optional string\/\/ | Path to the file where the test is declared, relative to the project root. |\n| `coverage` | \/\/optional map\u003cstring, wild\u003e\/\/ | Coverage information for this test. |\n| `details` | \/\/optional string\/\/ | Additional human-readable information about the failure. |\n\nThe `result` parameter recognizes these test results:\n\n| Key | Name | Description |\n|-------------|--------------|--------------|\n| `pass` | **Pass** | The test passed. |\n| `fail` | **Fail** | The test failed. |\n| `skip` | **Skip** | The test was not executed. |\n| `broken` | **Broken** | The test failed in an abnormal or severe way. For example, the harness crashed instead of reporting a failure. |\n| `unsound` | **Unsound** | The test failed, but this change is probably not what broke it. For example, it might have already been failing. |\n\nThis is a simple, valid value for the `unit` parameter. It reports one passing test and one failing test:\n\n\n\n```lang=json\n[\n {\n \"name\": \"PassingTest\",\n \"result\": \"pass\"\n },\n {\n \"name\": \"FailingTest\",\n \"result\": \"fail\"\n }\n]\n```\n\nLint Results\n============\nLike unit test results, you can report lint results alongside a message. The `lint` parameter should contain results as a list of dictionaries with these keys:\n\n| Key | Type | Description |\n|-------------|--------------|--------------|\n| `name` | \/\/string\/\/ | Short message name, like \"Syntax Error\". |\n| `code` | \/\/string\/\/ | Lint message code identifying the type of message, like \"ERR123\". |\n| `severity` | \/\/string\/\/ | Severity of the message. |\n| `path` | \/\/string\/\/ | Path to the file containing the lint message, from the project root. |\n| `line` | \/\/optional int\/\/ | Line number in the file where the text which triggered the message first appears. The first line of the file is line 1, not line 0. |\n| `char` | \/\/optional int\/\/ | Byte position on the line where the text which triggered the message starts. The first byte on the line is byte 1, not byte 0. This position is byte-based (not character-based) because not all lintable files have a valid character encoding. |\n| `description` | \/\/optional string\/\/ | Long explanation of the lint message. |\n\nThe `severity` parameter recognizes these severity levels:\n\n| Key | Name |\n|-------------|--------------|\n| `advice` | **Advice** |\n| `autofix` | **Auto-Fix** |\n| `warning` | **Warning** |\n| `error` | **Error** |\n| `disabled` | **Disabled** |\n\nThis is a simple, valid value for the `lint` parameter. It reports one error and one warning:\n\n```lang=json\n[\n {\n \"name\": \"Syntax Error\",\n \"code\": \"EXAMPLE1\",\n \"severity\": \"error\",\n \"path\": \"path\/to\/example.c\",\n \"line\": 17,\n \"char\": 3\n },\n {\n \"name\": \"Not A Haiku\",\n \"code\": \"EXAMPLE2\",\n \"severity\": \"error\",\n \"path\": \"path\/to\/source.cpp\",\n \"line\": 23,\n \"char\": 1,\n \"description\": \"This function definition is not a haiku.\"\n }\n]\n```\n\n","params":{"buildTargetPHID":"required phid","type":"required string-constant\u003c\"pass\", \"fail\", \"work\"\u003e","unit":"optional list\u003cwild\u003e","lint":"optional list\u003cwild\u003e"},"return":"void"},"macro.query":{"description":"Retrieve image macro information.","params":{"authorPHIDs":"optional list\u003cphid\u003e","phids":"optional list\u003cphid\u003e","ids":"optional list\u003cid\u003e","names":"optional list\u003cstring\u003e","nameLike":"optional string"},"return":"list\u003cdict\u003e"},"macro.creatememe":{"description":"Generate a meme.","params":{"macroName":"string","upperText":"optional string","lowerText":"optional string"},"return":"string"},"maniphest.createtask":{"description":"Create a new Maniphest task.","params":{"title":"required string","description":"optional string","ownerPHID":"optional phid","viewPolicy":"optional phid or policy string","editPolicy":"optional phid or policy string","ccPHIDs":"optional list\u003cphid\u003e","priority":"optional int","projectPHIDs":"optional list\u003cphid\u003e","auxiliary":"optional dict"},"return":"nonempty dict"},"maniphest.gettasktransactions":{"description":"Retrieve Maniphest task transactions.","params":{"ids":"required list\u003cint\u003e"},"return":"nonempty list\u003cdict\u003cstring, wild\u003e\u003e"},"maniphest.info":{"description":"Retrieve information about a Maniphest task, given its ID.","params":{"task_id":"required id"},"return":"nonempty dict"},"maniphest.query":{"description":"Execute complex searches for Maniphest tasks.","params":{"ids":"optional list\u003cuint\u003e","phids":"optional list\u003cphid\u003e","ownerPHIDs":"optional list\u003cphid\u003e","authorPHIDs":"optional list\u003cphid\u003e","projectPHIDs":"optional list\u003cphid\u003e","ccPHIDs":"optional list\u003cphid\u003e","fullText":"optional string","status":"optional string-constant\u003c\"status-any\", \"status-open\", \"status-closed\", \"status-resolved\", \"status-wontfix\", \"status-invalid\", \"status-spite\", \"status-duplicate\"\u003e","order":"optional string-constant\u003c\"order-priority\", \"order-created\", \"order-modified\"\u003e","limit":"optional int","offset":"optional int"},"return":"list"},"maniphest.querystatuses":{"description":"Retrieve information about possible Maniphest task status values.","params":[],"return":"nonempty dict\u003cstring, wild\u003e"},"maniphest.update":{"description":"Update an existing Maniphest task.","params":{"id":"optional int","phid":"optional int","title":"optional string","description":"optional string","ownerPHID":"optional phid","viewPolicy":"optional phid or policy string","editPolicy":"optional phid or policy string","ccPHIDs":"optional list\u003cphid\u003e","priority":"optional int","projectPHIDs":"optional list\u003cphid\u003e","auxiliary":"optional dict","status":"optional string","comments":"optional string"},"return":"nonempty dict"},"maniphest.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"maniphest.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"owners.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"owners.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"owners.query":{"description":"Query for Owners packages. Obsoleted by \"owners.search\".","params":{"userOwner":"optional string","projectOwner":"optional string","userAffiliated":"optional string","repositoryCallsign":"optional string","path":"optional string"},"return":"dict\u003cphid -\u003e dict of package info\u003e"},"passphrase.query":{"description":"Query credentials.","params":{"ids":"optional list\u003cint\u003e","phids":"optional list\u003cphid\u003e","needSecrets":"optional bool","needPublicKeys":"optional bool","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"list\u003cdict\u003e"},"paste.create":{"description":"Create a new paste.","params":{"content":"required string","title":"optional string","language":"optional string"},"return":"nonempty dict"},"paste.query":{"description":"Query Pastes.","params":{"ids":"optional list\u003cint\u003e","phids":"optional list\u003cphid\u003e","authorPHIDs":"optional list\u003cphid\u003e","after":"optional int","limit":"optional int, default = 100"},"return":"list\u003cdict\u003e"},"paste.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"paste.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"paste.info":{"description":"Retrieve an array of information about a paste.","params":{"paste_id":"required id"},"return":"nonempty dict"},"phame.blog.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"phame.blog.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"phame.post.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"phame.post.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"phid.lookup":{"description":"Look up objects by name.","params":{"names":"required list\u003cstring\u003e"},"return":"nonempty dict\u003cstring, wild\u003e"},"phid.query":{"description":"Retrieve information about arbitrary PHIDs.","params":{"phids":"required list\u003cphid\u003e"},"return":"nonempty dict\u003cstring, wild\u003e"},"phid.info":{"description":"Retrieve information about an arbitrary PHID.","params":{"phid":"required phid"},"return":"nonempty dict\u003cstring, wild\u003e"},"phragment.getpatch":{"description":"Retrieve the patches to apply for a given set of files.","params":{"path":"required string","state":"required dict\u003cstring, string\u003e"},"return":"nonempty dict"},"phragment.queryfragments":{"description":"Query fragments based on their paths.","params":{"paths":"required list\u003cstring\u003e"},"return":"nonempty dict"},"phriction.create":{"description":"Create a Phriction document.","params":{"slug":"required string","title":"required string","content":"required string","description":"optional string"},"return":"nonempty dict"},"phriction.edit":{"description":"Update a Phriction document.","params":{"slug":"required string","title":"optional string","content":"optional string","description":"optional string"},"return":"nonempty dict"},"phriction.history":{"description":"Retrieve history about a Phriction document.","params":{"slug":"required string"},"return":"nonempty list"},"phriction.info":{"description":"Retrieve information about a Phriction document.","params":{"slug":"required string"},"return":"nonempty dict"},"project.create":{"description":"Create a project.","params":{"name":"required string","members":"optional list\u003cphid\u003e","icon":"optional string","color":"optional string","tags":"optional list\u003cstring\u003e"},"return":"dict"},"project.query":{"description":"Execute searches for Projects.","params":{"ids":"optional list\u003cint\u003e","names":"optional list\u003cstring\u003e","phids":"optional list\u003cphid\u003e","slugs":"optional list\u003cstring\u003e","icons":"optional list\u003cstring\u003e","colors":"optional list\u003cstring\u003e","status":"optional string-constant\u003c\"status-any\", \"status-open\", \"status-closed\", \"status-active\", \"status-archived\"\u003e","members":"optional list\u003cphid\u003e","limit":"optional int","offset":"optional int"},"return":"list"},"project.edit":{"description":"This is a standard **ApplicationEditor** method which allows you to create and modify objects by applying transactions. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Edit+Endpoints&type=article&jump=1 | Conduit API: Using Edit Endpoints ]]**.","params":{"transactions":"list\u003cmap\u003cstring, wild\u003e\u003e","objectIdentifier":"optional id|phid|string"},"return":"map\u003cstring, wild\u003e"},"project.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"releeph.getbranches":{"description":"Return information about all active Releeph branches.","params":[],"return":"nonempty list\u003cdict\u003cstring, wild\u003e\u003e"},"releeph.querybranches":{"description":"Query information about Releeph branches.","params":{"ids":"optional list\u003cid\u003e","phids":"optional list\u003cphid\u003e","productPHIDs":"optional list\u003cphid\u003e","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"query-results"},"releeph.queryproducts":{"description":"Query information about Releeph products.","params":{"ids":"optional list\u003cid\u003e","phids":"optional list\u003cphid\u003e","repositoryPHIDs":"optional list\u003cphid\u003e","isActive":"optional bool","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"query-results"},"releeph.queryrequests":{"description":"Return information about all Releeph requests linked to the given ids.","params":{"revisionPHIDs":"optional list\u003cphid\u003e","requestedCommitPHIDs":"optional list\u003cphid\u003e"},"return":"dict\u003cstring, wild\u003e"},"releeph.request":{"description":"Request a commit or diff to be picked to a branch.","params":{"branchPHID":"required string","things":"required list\u003cstring\u003e","fields":"dict\u003cstring, string\u003e"},"return":"dict\u003cstring, wild\u003e"},"releephwork.canpush":{"description":"Return whether the conduit user is allowed to push.","params":{"projectPHID":"required string"},"return":"bool"},"releephwork.getauthorinfo":{"description":"Return a string to use as the VCS author.","params":{"userPHID":"required string","vcsType":"required string"},"return":"nonempty string"},"releephwork.getbranch":{"description":"Return information to help checkout \/ cut a Releeph branch.","params":{"branchPHID":"required string"},"return":"dict\u003cstring, wild\u003e"},"releephwork.getbranchcommitmessage":{"description":"Get a commit message for committing a Releeph branch.","params":{"branchPHID":"required string"},"return":"nonempty string"},"releephwork.getcommitmessage":{"description":"Get commit message components for building a ReleephRequest commit message.","params":{"requestPHID":"required string","action":"required string-constant\u003c\"pick\", \"revert\"\u003e"},"return":"dict\u003cstring, string\u003e"},"releephwork.nextrequest":{"description":"Return info required to cut a branch, and pick and revert ReleephRequests.","params":{"branchPHID":"required phid","seen":"required map\u003cstring, bool\u003e"},"return":""},"releephwork.record":{"description":"Record whether we committed a pick or revert to the upstream repository.","params":{"requestPHID":"required string","action":"required string-constant\u003c\"pick\", \"revert\"\u003e","commitIdentifier":"required string"},"return":"void"},"releephwork.recordpickstatus":{"description":"Record whether a pick or revert was successful or not.","params":{"requestPHID":"required string","action":"required string-constant\u003c\"pick\", \"revert\"\u003e","ok":"required bool","dryRun":"optional bool","details":"optional dict\u003cstring, wild\u003e"},"return":""},"remarkup.process":{"description":"Process text through remarkup in Phabricator context.","params":{"context":"required string-constant\u003c\"phriction\", \"maniphest\", \"differential\", \"phame\", \"feed\", \"diffusion\"\u003e","contents":"required list\u003cstring\u003e"},"return":"nonempty dict"},"repository.query":{"description":"Query repositories.","params":{"ids":"optional list\u003cint\u003e","phids":"optional list\u003cphid\u003e","callsigns":"optional list\u003cstring\u003e","vcsTypes":"optional list\u003cstring\u003e","remoteURIs":"optional list\u003cstring\u003e","uuids":"optional list\u003cstring\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"list\u003cdict\u003e"},"slowvote.info":{"description":"Retrieve an array of information about a poll.","params":{"poll_id":"required id"},"return":"nonempty dict"},"token.give":{"description":"Give or change a token.","params":{"tokenPHID":"phid|null","objectPHID":"phid"},"return":"void"},"token.given":{"description":"Query tokens given to objects.","params":{"authorPHIDs":"list\u003cphid\u003e","objectPHIDs":"list\u003cphid\u003e","tokenPHIDs":"list\u003cphid\u003e"},"return":"list\u003cdict\u003e"},"token.query":{"description":"Query tokens.","params":[],"return":"list\u003cdict\u003e"},"user.disable":{"description":"Permanently disable specified users (admin only).","params":{"phids":"required list\u003cphid\u003e"},"return":"void"},"user.enable":{"description":"Re-enable specified users (admin only).","params":{"phids":"required list\u003cphid\u003e"},"return":"void"},"user.query":{"description":"Query users.","params":{"usernames":"optional list\u003cstring\u003e","emails":"optional list\u003cstring\u003e","realnames":"optional list\u003cstring\u003e","phids":"optional list\u003cphid\u003e","ids":"optional list\u003cuint\u003e","offset":"optional int","limit":"optional int (default = 100)"},"return":"list\u003cdict\u003e"},"user.whoami":{"description":"Retrieve information about the logged-in user.","params":[],"return":"nonempty dict\u003cstring, wild\u003e"},"user.search":{"description":"This is a standard **ApplicationSearch** method which will let you list, query, or search for objects. For documentation on these endpoints, see **[[ https:\/\/secure.phabricator.com\/diviner\/find\/?name=Conduit+API%3A+Using+Search+Endpoints&type=article&jump=1 | Conduit API: Using Search Endpoints ]]**.","params":{"queryKey":"optional string","constraints":"optional map\u003cstring, wild\u003e","attachments":"optional map\u003cstring, bool\u003e","order":"optional order","before":"optional string","after":"optional string","limit":"optional int (default = 100)"},"return":"map\u003cstring, wild\u003e"},"user.find":{"description":"Lookup PHIDs by username. Obsoleted by \"user.query\".","params":{"aliases":"required list\u003cstring\u003e"},"return":"nonempty dict\u003cstring, phid\u003e"}}python-phabricator-0.7.0/phabricator/tests/000077500000000000000000000000001273774442200210075ustar00rootroot00000000000000python-phabricator-0.7.0/phabricator/tests/__init__.py000066400000000000000000000000001273774442200231060ustar00rootroot00000000000000python-phabricator-0.7.0/phabricator/tests/resources/000077500000000000000000000000001273774442200230215ustar00rootroot00000000000000python-phabricator-0.7.0/phabricator/tests/resources/__init__.py000066400000000000000000000000001273774442200251200ustar00rootroot00000000000000python-phabricator-0.7.0/phabricator/tests/resources/certificate.txt000066400000000000000000000004001273774442200260360ustar00rootroot00000000000000fdhcq3zsyijnm4h6gmh43zue5umsmng5t4dlwodvmiz4cnc6fl6fzrvjbfg2ftktrcddan7b3xtgmfge2afbrh4uwam6pfxpq5dbkhbl6mgaijdzpq5efw2ynlnjhoeqyh6dakl4yg346gbhabzkcxreu7hcjhw6vo6wwa7ky2sjdk742khlgsakwtme6sr2dfkhlxxkcqw3jngyrq5zj7m6m7hnscuzlzsviawnvg47pe7l4hxiexpbb5k456r python-phabricator-0.7.0/phabricator/tests/resources/responses.json000066400000000000000000000024251273774442200257400ustar00rootroot00000000000000{ "conduit.connect": "{\"result\":{\"connectionID\":1759,\"sessionKey\":\"lwvyv7f6hlzb2vawac6reix7ejvjty72svnir6zy\",\"userPHID\":\"PHID-USER-6ij4rnamb2gsfpdkgmny\"},\"error_code\":null,\"error_info\":null}", "user.whoami": "{\"result\":{\"phid\":\"PHID-USER-6ij4rnamz2gxfpbkamny\",\"userName\":\"testaccount\",\"realName\":\"Test Account\"},\"error_code\":null,\"error_info\":null}", "maniphest.find": "{\"result\":{\"PHID-TASK-4cgpskv6zzys6rp5rvrc\":{\"id\":\"722\",\"phid\":\"PHID-TASK-4cgpskv6zzys6rp5rvrc\",\"authorPHID\":\"PHID-USER-5022a9389121884ab9db\",\"ownerPHID\":\"PHID-USER-5022a9389121884ab9db\",\"ccPHIDs\":[\"PHID-USER-5022a9389121884ab9db\",\"PHID-USER-ba8aeea1b3fe2853d6bb\"],\"status\":\"3\",\"priority\":\"Needs Triage\",\"title\":\"Relations should be two-way\",\"description\":\"When adding a differential revision you can specify Maniphest Tickets to add the relation. However, this doesnt add the relation from the ticket -> the differently.(This was added via the commit message)\",\"projectPHIDs\":[\"PHID-PROJ-358dbc2e601f7e619232\",\"PHID-PROJ-f58a9ac58c333f106a69\"],\"uri\":\"https://secure.phabricator.com/T722\",\"auxiliary\":[],\"objectName\":\"T722\",\"dateCreated\":\"1325553508\",\"dateModified\":\"1325618490\"}},\"error_code\":null,\"error_info\":null}" } python-phabricator-0.7.0/phabricator/tests/test_phabricator.py000066400000000000000000000122751273774442200247250ustar00rootroot00000000000000try: import unittest2 as unittest except ImportError: import unittest try: from StringIO import StringIO except ImportError: from io import StringIO try: import unittest.mock as mock except ImportError: import mock from pkg_resources import resource_string import json import phabricator RESPONSES = json.loads( resource_string( 'phabricator.tests.resources', 'responses.json' ).decode('utf8') ) CERTIFICATE = resource_string( 'phabricator.tests.resources', 'certificate.txt' ).decode('utf8').strip() # Protect against local user's .arcrc interference. phabricator.ARCRC = {} class PhabricatorTest(unittest.TestCase): def setUp(self): self.api = phabricator.Phabricator( username='test', certificate='test', host='http://localhost' ) self.api.certificate = CERTIFICATE def test_generate_hash(self): token = '12345678' hashed = self.api.generate_hash(token) self.assertEqual(hashed, 'f8d3bea4e58a2b2967d93d5b307bfa7c693b2e7f') @mock.patch('phabricator.httplib.HTTPConnection') def test_connect(self, mock_connection): mock_obj = mock_connection.return_value = mock.Mock() mock_obj.getresponse.return_value = StringIO( RESPONSES['conduit.connect'] ) mock_obj.getresponse.return_value.status = 200 api = phabricator.Phabricator( username='test', certificate='test', host='http://localhost' ) api.connect() keys = api._conduit.keys() self.assertIn('sessionKey', keys) self.assertIn('connectionID', keys) @mock.patch('phabricator.httplib.HTTPConnection') def test_user_whoami(self, mock_connection): mock_obj = mock_connection.return_value = mock.Mock() mock_obj.getresponse.return_value = StringIO(RESPONSES['user.whoami']) mock_obj.getresponse.return_value.status = 200 api = phabricator.Phabricator( username='test', certificate='test', host='http://localhost' ) api._conduit = True self.assertEqual(api.user.whoami()['userName'], 'testaccount') def test_classic_resources(self): api = phabricator.Phabricator( username='test', certificate='test', host='http://localhost' ) self.assertEqual(api.user.whoami.method, 'user') self.assertEqual(api.user.whoami.endpoint, 'whoami') def test_nested_resources(self): api = phabricator.Phabricator( username='test', certificate='test', host='http://localhost' ) self.assertEqual(api.diffusion.repository.edit.method, 'diffusion') self.assertEqual(api.diffusion.repository.edit.endpoint, 'repository.edit') @mock.patch('phabricator.httplib.HTTPConnection') def test_bad_status(self, mock_connection): mock_obj = mock_connection.return_value = mock.Mock() mock_obj.getresponse.return_value = mock.Mock() mock_obj.getresponse.return_value.status = 400 api = phabricator.Phabricator( username='test', certificate='test', host='http://localhost' ) api._conduit = True with self.assertRaises(phabricator.httplib.HTTPException): api.user.whoami() @mock.patch('phabricator.httplib.HTTPConnection') def test_maniphest_find(self, mock_connection): mock_obj = mock_connection.return_value = mock.Mock() mock_obj.getresponse.return_value = StringIO( RESPONSES['maniphest.find'] ) mock_obj.getresponse.return_value.status = 200 api = phabricator.Phabricator( username='test', certificate='test', host='http://localhost' ) api._conduit = True result = api.maniphest.find( ownerphids=['PHID-USER-5022a9389121884ab9db'] ) self.assertEqual(len(result), 1) # Test iteration self.assertIsInstance([x for x in result], list) # Test getattr self.assertEqual( result['PHID-TASK-4cgpskv6zzys6rp5rvrc']['status'], '3' ) def test_validation(self): self.api._conduit = True self.assertRaises(ValueError, self.api.differential.find) with self.assertRaises(ValueError): self.api.differential.find(query=1) with self.assertRaises(ValueError): self.api.differential.find(query='1') with self.assertRaises(ValueError): self.api.differential.find(query='1', guids='1') def test_map_param_type(self): uint = 'uint' self.assertEqual(phabricator.map_param_type(uint), int) list_bool = 'list' self.assertEqual(phabricator.map_param_type(list_bool), [bool]) list_pair = 'list>' self.assertEqual(phabricator.map_param_type(list_pair), [tuple]) complex_list_pair = 'list, string>>' self.assertEqual(phabricator.map_param_type(complex_list_pair), [tuple]) if __name__ == '__main__': unittest.main() python-phabricator-0.7.0/setup.cfg000066400000000000000000000000321273774442200171630ustar00rootroot00000000000000[bdist_wheel] universal=1 python-phabricator-0.7.0/setup.py000066400000000000000000000020601273774442200170570ustar00rootroot00000000000000#!/usr/bin/env python import sys from setuptools import setup, find_packages tests_requires = [] if sys.version_info[:2] < (2, 7): tests_requires.append('unittest2') if sys.version_info[:2] <= (3, 3): tests_requires.append('mock') setup( name='phabricator', version='0.7.0', author='Disqus', author_email='opensource@disqus.com', url='http://github.com/disqus/python-phabricator', description='Phabricator API Bindings', packages=find_packages(), zip_safe=False, test_suite='phabricator.tests.test_phabricator', tests_require=tests_requires, include_package_data=True, classifiers=[ 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'Operating System :: OS Independent', 'Topic :: Software Development', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', ], )