Last active
July 11, 2018 16:13
-
-
Save monkut/c957e9c758be18804484134fa76b5a3f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Creates a github organization project with columns from a defined template | |
using the Github graphql API | |
""" | |
import os | |
import uuid | |
import requests | |
GITHUB_API_TOKEN = os.environ.get('GITHUB_API_TOKEN', None) | |
GITHUB_GRAPHQL_API_URL = 'https://api.github.com/graphql' | |
class GithubGraphQLError(Exception): | |
pass | |
class UnexpectedResponse(Exception): | |
pass | |
def run_graphql_request(query, token=GITHUB_API_TOKEN, raise_on_error=False): | |
"""A simple function to use requests.post to make the graphql API call.""" | |
headers = {"Authorization": f"token {token}"} | |
response = requests.post(GITHUB_GRAPHQL_API_URL, json={"query": query}, headers=headers) | |
if response.status_code == 200: | |
result = response.json() | |
if raise_on_error: | |
if not result['data'] and result['errors']: | |
raise GithubGraphQLError(result['errors'] | |
else: | |
raise GithubGraphQLError("Query failed to run by returning code of {}. {}".format(response.status_code, query)) | |
def _get_owner_id(name): | |
query = """ | |
{ | |
viewer { | |
login, | |
id, | |
organization (login:"%(name)s"){ | |
id, | |
name | |
} | |
} | |
} | |
""" % {'name': name} | |
response = run_graphql_request(query, raise_on_error=True) | |
# {'data': {'viewer': {'login': 'monkut', 'id': 'Node id', 'organization': {'id': 'ORG_ID', 'name': 'ORG_NAME'}}}} | |
try: | |
org_id = response['data']['viewer']['organization']['id'] | |
except KeyError as e: | |
raise UnexpectedResponse(e.args) | |
return org_id | |
def create_github_organizational_project(organization: str, name: str, description: str, columns: list=None): | |
""" | |
Create an Organizational Project in github | |
:param organization: Github organiation name | |
:param name: Project Name of project to create | |
:param description: description of project | |
:param columns: | |
Columns Definition | |
If included the columns will be added on project creation. | |
Added in the order given. | |
.. code:: python | |
[ | |
{'name': COLUMN_NAME}, | |
{'name': COLUMN_NAME}, | |
] | |
:return: | |
""" | |
owner_id = _get_owner_id(organization) | |
mutation_id = str(uuid.uuid4()) # get a random id | |
# escape description content, may be json | |
encoded_description = json.dumps(description) | |
if encoded_description.startswith('"') and encoded_description.endswith('"'): | |
# remove unnecessary quotes | |
encoded_description = encoded_description[1:-1] | |
graphql_mutation = """ | |
mutation { | |
createProject(input:{name:"%(name)s", body:"%(body)s", ownerId:"%(owner_id)s", clientMutationId:"%(mutation_id)s"}) { | |
project{ | |
id, | |
name, | |
url, | |
number | |
} | |
} | |
} | |
""" % {'name': name, | |
'body': encoded_description, | |
'owner_id': owner_id, | |
'mutation_id': mutation_id} | |
responses = [] | |
response = run_graphql_request(graphql_mutation, raise_on_error=True) | |
# {'data': {'createProject': {'project': {'id': '{NODE ID}', 'name': 'project name', 'url': | |
project_url = response['data']['createProject']['project']['url'] | |
responses.append(response) | |
try: | |
project_id = response['data']['createProject']['project']['id'] | |
except KeyError as e: | |
raise UnexpectedResponse(e.args) | |
if columns: | |
add_columns_response = add_columns(project_id, columns) | |
responses.append(add_columns_response) | |
return project_url, responses | |
def add_columns(project_id, columns): | |
addcolumns_responses = [] | |
for column_definition in columns: | |
mutation_id = str(uuid.uuid4()) # get a random id | |
graphql_addprojectcolumn = """ | |
mutation { | |
addProjectColumn(input:{name:"%(name)s", projectId:"%(project_id)s", clientMutationId:"%(mutation_id)s"}) { | |
columnEdge { | |
node { | |
id, | |
name, | |
resourcePath | |
} | |
} | |
} | |
}""" % {'name': column_definition['name'], | |
'project_id': project_id, | |
'mutation_id': mutation_id} | |
response = run_graphql_request(graphql_addprojectcolumn) | |
addcolumns_responses.append(response) | |
return addcolumns_responses | |
if __name__ == '__main__': | |
import json | |
import argparse | |
parser = argparse.ArgumentParser(description=__doc__) | |
parser.add_argument('-o', '--organization', | |
required=True, | |
help='The organization name to create the project in') | |
parser.add_argument('-n', '--name', | |
required=True, | |
help='Project Name') | |
parser.add_argument('-d', '--description', | |
default=None, | |
type=str, | |
help='(Optional) Project Description') | |
parser.add_argument('-t', '--token', | |
default=None, | |
help='GITHUB API Token [DEFAULT=os.environ("GITHUB_API_TOKEN")') | |
parser.add_argument('-c', '--column-definitions', | |
dest='column_definitions', | |
default=None, | |
help='Path to JSON file of column definitions (If given, columns will be added to project)') | |
args = parser.parse_args() | |
column_definitions = None | |
if args.column_definitions: | |
column_defintions_filepath = os.path.abspath(os.path.expanduser(args.column_definitions)) | |
if os.path.exists(column_defintions_filepath): | |
with open(column_defintions_filepath, 'r', encoding='utf8') as f: | |
column_definitions = json.load(f) | |
else: | |
raise parser.error(f'--column-definitions PATH Error: {column_defintions_filepath}') | |
result = create_project(args.organization, | |
args.name, | |
args.description, | |
column_definitions) | |
print(result) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment