Skip to content

Instantly share code, notes, and snippets.

@monkut
Last active July 11, 2018 16:13
Show Gist options
  • Save monkut/c957e9c758be18804484134fa76b5a3f to your computer and use it in GitHub Desktop.
Save monkut/c957e9c758be18804484134fa76b5a3f to your computer and use it in GitHub Desktop.
"""
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