GitHub GraphQL APIでProjectにIssuesを追加してみました。 そのあと、EstimateとStatusとを設定してみます。
おすすめの方
- GitHub GraphQL APIについて知りたい方
- GitHub GraphQL APIでProjectにIssuesを追加したい方
- GitHub GraphQL APIでProjectのItemのフィールドを取得・更新したい方
- GitHub GraphQL APIで変数を使いたい方
GitHub Projectのフィールド
デフォルトのフィールドを利用します。
GitHub GraphQL APIでProjectにIssuesを追加するスクリプト
プロジェクト番号は、URLに含まれています。たとえば、「projects/4/views/1」なら、プロジェクト番号は「4」です。
また、プロジェクトが「User or Organization」のどちらにあるかによって、GraphQLのクエリ内容が異なります。 次のコードでは両方記載しています。
app.py
import requests
import json
from enum import StrEnum
ENDPOINT = "https://api.github.com/graphql"
GITHUB_TOKEN = "xxx"
GITHUB_REPOSITORY_OWNER = "yyy"
GITHUB_REPOSITORY_NAME = "zzz"
GITHUB_PROJECT_NUMBER = 4
class StatusName(StrEnum):
TODO = "Todo"
IN_PROGRESS = "In Progress"
DONE = "Done"
def main():
# リポジトリIDを取得する
repository_id = get_repository_id(GITHUB_REPOSITORY_OWNER, GITHUB_REPOSITORY_NAME)
# プロジェクト情報を取得する(User)
project_id, project_fields = get_project_v2_user(
GITHUB_REPOSITORY_OWNER, GITHUB_PROJECT_NUMBER
)
# プロジェクト情報を取得する(Organization)
# project_id, project_fields = get_project_v2_organization(GITHUB_REPOSITORY_OWNER, GITHUB_PROJECT_NUMBER)
# プロジェクトのフィールドIDとオプションIDを取得する
for item in project_fields:
if item.get("name", "") == "Estimate":
estimate_id = item["id"]
if item.get("name", "") == "Status":
status_id = item["id"]
status_options = array_to_dict(item["options"], "name")
# Issueを作成する
issue_id = create_issue(
repository_id,
"これがタイトルです",
"これが本文です",
)
# Issueをプロジェクトに追加する
project_item_id = add_issue_to_project(project_id, issue_id)
# Estimateを更新する
update_project_v2_item_field_number(project_id, project_item_id, estimate_id, 3)
# Statusを更新する
update_project_v2_item_field_single_select_option_id(
project_id, project_item_id, status_id, status_options[StatusName.TODO]["id"]
)
def get_repository_id(
repository_owner,
repository_name,
):
query = """
query getProjectLabels($repositoryOwner:String!, $repositoryName:String!) {
repository(owner: $repositoryOwner, name: $repositoryName) {
id
}
}
"""
variables = {
"repositoryOwner": repository_owner,
"repositoryName": repository_name,
}
resp = requests.post(
ENDPOINT,
headers=get_headers(),
data=json.dumps({"query": query, "variables": variables}),
)
body = resp.json()
return body["data"]["repository"]["id"]
def get_project_v2_user(
repository_owner,
number,
):
query = """
query getProjectV2($repositoryOwner:String!, $number:Int!) {
user(login: $repositoryOwner){
projectV2(number: $number) {
id,
fields(first: 20) {
nodes {
... on ProjectV2Field {
id
name
}
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
}
}
}
}
}
"""
variables = {
"repositoryOwner": repository_owner,
"number": number,
}
resp = requests.post(
ENDPOINT,
headers=get_headers(),
data=json.dumps({"query": query, "variables": variables}),
)
body = resp.json()
return (
body["data"]["user"]["projectV2"]["id"],
body["data"]["user"]["projectV2"]["fields"]["nodes"],
)
def get_project_v2_organization(
repository_owner,
number,
):
query = """
query getProjectV2($repositoryOwner:String!, $number:Int!) {
organization(login: $repositoryOwner){
projectV2(number: $number) {
id,
fields(first: 20) {
nodes {
... on ProjectV2Field {
id
name
}
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
}
}
}
}
}
"""
variables = {
"repositoryOwner": repository_owner,
"number": number,
}
resp = requests.post(
ENDPOINT,
headers=get_headers(),
data=json.dumps({"query": query, "variables": variables}),
)
body = resp.json()
return (
body["data"]["organization"]["projectV2"]["id"],
body["data"]["organization"]["projectV2"]["fields"]["nodes"],
)
def create_issue(repository_id, title, body, labels=[]):
mutation = """
mutation CreateIssue($repositoryId: ID!, $title: String!, $body: String, $labelIds: [ID!]) {
createIssue(input: {repositoryId: $repositoryId, title: $title, body: $body, labelIds: $labelIds}) {
issue {
id
number
title
bodyText
}
}
}
"""
variables = {
"repositoryId": repository_id,
"title": title,
"body": body,
"labelIds": labels,
}
resp = requests.post(
ENDPOINT,
headers=get_headers(),
data=json.dumps({"query": mutation, "variables": variables}),
)
body = resp.json()
return body["data"]["createIssue"]["issue"]["id"]
def add_issue_to_project(project_id, issue_id):
mutation = """
mutation AddIssueToProject($projectId: ID!, $contentId: ID!) {
addProjectV2ItemById(input: {projectId: $projectId, contentId: $contentId}) {
item {
id
}
}
}"""
variables = {
"projectId": project_id,
"contentId": issue_id,
}
resp = requests.post(
ENDPOINT,
headers=get_headers(),
data=json.dumps({"query": mutation, "variables": variables}),
)
body = resp.json()
return body["data"]["addProjectV2ItemById"]["item"]["id"]
def update_project_v2_item_field_single_select_option_id(
project_id, item_id, field_id, value
):
mutation = """
mutation updateProjectV2ItemFieldValue($projectId: ID!, $itemId: ID!, $fieldId: ID!, $value: String) {
updateProjectV2ItemFieldValue(input: {projectId: $projectId, itemId: $itemId, fieldId: $fieldId, value: {singleSelectOptionId: $value}}) {
projectV2Item {
id
}
}
}"""
variables = {
"projectId": project_id,
"itemId": item_id,
"fieldId": field_id,
"value": value,
}
resp = requests.post(
ENDPOINT,
headers=get_headers(),
data=json.dumps({"query": mutation, "variables": variables}),
)
body = resp.json()
def update_project_v2_item_field_number(project_id, item_id, field_id, value):
mutation = """
mutation updateProjectV2ItemFieldValue($projectId: ID!, $itemId: ID!, $fieldId: ID!, $value: Float) {
updateProjectV2ItemFieldValue(input: {projectId: $projectId, itemId: $itemId, fieldId: $fieldId, value: {number: $value}}) {
projectV2Item {
id
}
}
}"""
variables = {
"projectId": project_id,
"itemId": item_id,
"fieldId": field_id,
"value": value,
}
resp = requests.post(
ENDPOINT,
headers=get_headers(),
data=json.dumps({"query": mutation, "variables": variables}),
)
body = resp.json()
def get_headers():
return {
"Authorization": f"bearer {GITHUB_TOKEN}",
"Accept": "application/vnd.github.bane-preview+json",
}
def array_to_dict(array: list, key_name: str):
return {x[key_name]: x for x in array}
if __name__ == "__main__":
main()
実行結果
python app.py
GitHub ProjectにIssuesが追加されました。 StatusとEstimateも設定されています。
Issues側でも確認できます。
さいごに
GitHub GraphQL APIでProjectにIssuesを追加して、フィールドを更新してみました。 フィールド情報を取得してフィールドIDを特定してから更新するのが少し手間かもしれません。 参考になれば幸いです。