1 | |
2 |
|
3 | async function graphql(auth: RT.Github, query: string, variables: object) { |
4 | const response = await fetch("https://api.github.com/graphql", { |
5 | method: "POST", |
6 | headers: { |
7 | Authorization: `Bearer ${auth.token}`, |
8 | "Content-Type": "application/json", |
9 | Accept: "application/json", |
10 | }, |
11 | body: JSON.stringify({ query, variables }), |
12 | }) |
13 | if (!response.ok) { |
14 | throw new Error(`${response.status} ${await response.text()}`) |
15 | } |
16 | const { data, errors } = (await response.json()) as { |
17 | data: any |
18 | errors?: unknown[] |
19 | } |
20 | if (errors?.length) throw new Error(JSON.stringify(errors)) |
21 | return data |
22 | } |
23 |
|
24 | export type DynSelect_project_number = string |
25 |
|
26 | |
27 | export async function project_number(auth: RT.Github, owner: string) { |
28 | if (!auth?.token || !owner) return [] |
29 | const data = await graphql( |
30 | auth, |
31 | ` |
32 | query ($owner: String!) { |
33 | repositoryOwner(login: $owner) { |
34 | ... on ProjectV2Owner { |
35 | projectsV2(first: 100) { |
36 | nodes { |
37 | number |
38 | title |
39 | } |
40 | } |
41 | } |
42 | } |
43 | } |
44 | `, |
45 | { owner } |
46 | ) |
47 | return (data.repositoryOwner?.projectsV2?.nodes ?? []).map((p: any) => ({ |
48 | value: String(p.number), |
49 | label: `#${p.number} ${p.title}`, |
50 | })) |
51 | } |
52 |
|
53 | export type DynSelect_item_id = string |
54 |
|
55 | |
56 | export async function item_id( |
57 | auth: RT.Github, |
58 | owner: string, |
59 | project_number: string |
60 | ) { |
61 | if (!auth?.token || !owner || !project_number) return [] |
62 | const data = await graphql( |
63 | auth, |
64 | ` |
65 | query ($owner: String!, $number: Int!) { |
66 | repositoryOwner(login: $owner) { |
67 | ... on ProjectV2Owner { |
68 | projectV2(number: $number) { |
69 | items(first: 100) { |
70 | nodes { |
71 | id |
72 | content { |
73 | ... on Issue { |
74 | number |
75 | title |
76 | } |
77 | ... on PullRequest { |
78 | number |
79 | title |
80 | } |
81 | ... on DraftIssue { |
82 | title |
83 | } |
84 | } |
85 | } |
86 | } |
87 | } |
88 | } |
89 | } |
90 | } |
91 | `, |
92 | { owner, number: Number(project_number) } |
93 | ) |
94 | return (data.repositoryOwner?.projectV2?.items.nodes ?? []).map((i: any) => ({ |
95 | value: i.id, |
96 | label: i.content?.number |
97 | ? `#${i.content.number} ${i.content.title}` |
98 | : (i.content?.title ?? i.id), |
99 | })) |
100 | } |
101 |
|
102 | export type DynSelect_status = string |
103 |
|
104 | |
105 | export async function status( |
106 | auth: RT.Github, |
107 | owner: string, |
108 | project_number: string |
109 | ) { |
110 | if (!auth?.token || !owner || !project_number) return [] |
111 | const data = await graphql( |
112 | auth, |
113 | ` |
114 | query ($owner: String!, $number: Int!) { |
115 | repositoryOwner(login: $owner) { |
116 | ... on ProjectV2Owner { |
117 | projectV2(number: $number) { |
118 | field(name: "Status") { |
119 | ... on ProjectV2SingleSelectField { |
120 | options { |
121 | id |
122 | name |
123 | } |
124 | } |
125 | } |
126 | } |
127 | } |
128 | } |
129 | } |
130 | `, |
131 | { owner, number: Number(project_number) } |
132 | ) |
133 | return (data.repositoryOwner?.projectV2?.field?.options ?? []).map( |
134 | (o: any) => ({ |
135 | value: o.id, |
136 | label: o.name, |
137 | }) |
138 | ) |
139 | } |
140 |
|
141 | |
142 | * Update Project Item Status |
143 | * Set the Status of a project item (issue, pull request or draft issue). status is the Status option id. Classic tokens and OAuth connections need the project scope. |
144 | */ |
145 | export async function main( |
146 | auth: RT.Github, |
147 | owner: string, |
148 | project_number: DynSelect_project_number, |
149 | item_id: DynSelect_item_id, |
150 | status: DynSelect_status |
151 | ) { |
152 | const data = await graphql( |
153 | auth, |
154 | ` |
155 | query ($owner: String!, $number: Int!) { |
156 | repositoryOwner(login: $owner) { |
157 | ... on ProjectV2Owner { |
158 | projectV2(number: $number) { |
159 | id |
160 | field(name: "Status") { |
161 | ... on ProjectV2SingleSelectField { |
162 | id |
163 | } |
164 | } |
165 | } |
166 | } |
167 | } |
168 | } |
169 | `, |
170 | { owner, number: Number(project_number) } |
171 | ) |
172 | const project = data.repositoryOwner?.projectV2 |
173 | if (!project?.field) |
174 | throw new Error(`Project ${project_number} has no Status field`) |
175 |
|
176 | const result = await graphql( |
177 | auth, |
178 | ` |
179 | mutation ( |
180 | $projectId: ID! |
181 | $itemId: ID! |
182 | $fieldId: ID! |
183 | $optionId: String! |
184 | ) { |
185 | updateProjectV2ItemFieldValue( |
186 | input: { |
187 | projectId: $projectId |
188 | itemId: $itemId |
189 | fieldId: $fieldId |
190 | value: { singleSelectOptionId: $optionId } |
191 | } |
192 | ) { |
193 | projectV2Item { |
194 | id |
195 | } |
196 | } |
197 | } |
198 | `, |
199 | { |
200 | projectId: project.id, |
201 | itemId: item_id, |
202 | fieldId: project.field.id, |
203 | optionId: status, |
204 | } |
205 | ) |
206 | return result.updateProjectV2ItemFieldValue.projectV2Item |
207 | } |
208 |
|