github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/api/query_builder.go (about) 1 package api 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/ungtb10d/cli/v2/pkg/set" 8 ) 9 10 func squeeze(r rune) rune { 11 switch r { 12 case '\n', '\t': 13 return -1 14 default: 15 return r 16 } 17 } 18 19 func shortenQuery(q string) string { 20 return strings.Map(squeeze, q) 21 } 22 23 var issueComments = shortenQuery(` 24 comments(first: 100) { 25 nodes { 26 id, 27 author{login}, 28 authorAssociation, 29 body, 30 createdAt, 31 includesCreatedEdit, 32 isMinimized, 33 minimizedReason, 34 reactionGroups{content,users{totalCount}}, 35 url, 36 viewerDidAuthor 37 }, 38 pageInfo{hasNextPage,endCursor}, 39 totalCount 40 } 41 `) 42 43 var issueCommentLast = shortenQuery(` 44 comments(last: 1) { 45 nodes { 46 author{login}, 47 authorAssociation, 48 body, 49 createdAt, 50 includesCreatedEdit, 51 isMinimized, 52 minimizedReason, 53 reactionGroups{content,users{totalCount}} 54 }, 55 totalCount 56 } 57 `) 58 59 var prReviewRequests = shortenQuery(` 60 reviewRequests(first: 100) { 61 nodes { 62 requestedReviewer { 63 __typename, 64 ...on User{login}, 65 ...on Team{ 66 organization{login} 67 name, 68 slug 69 } 70 } 71 } 72 } 73 `) 74 75 var prReviews = shortenQuery(` 76 reviews(first: 100) { 77 nodes { 78 author{login}, 79 authorAssociation, 80 submittedAt, 81 body, 82 state, 83 reactionGroups{content,users{totalCount}} 84 } 85 pageInfo{hasNextPage,endCursor} 86 totalCount 87 } 88 `) 89 90 var prLatestReviews = shortenQuery(` 91 latestReviews(first: 100) { 92 nodes { 93 author{login}, 94 authorAssociation, 95 submittedAt, 96 body, 97 state 98 } 99 } 100 `) 101 102 var prFiles = shortenQuery(` 103 files(first: 100) { 104 nodes { 105 additions, 106 deletions, 107 path 108 } 109 } 110 `) 111 112 var prCommits = shortenQuery(` 113 commits(first: 100) { 114 nodes { 115 commit { 116 authors(first:100) { 117 nodes { 118 name, 119 email, 120 user{id,login} 121 } 122 }, 123 messageHeadline, 124 messageBody, 125 oid, 126 committedDate, 127 authoredDate 128 } 129 } 130 } 131 `) 132 133 func StatusCheckRollupGraphQL(after string) string { 134 var afterClause string 135 if after != "" { 136 afterClause = ",after:" + after 137 } 138 return fmt.Sprintf(shortenQuery(` 139 statusCheckRollup: commits(last: 1) { 140 nodes { 141 commit { 142 statusCheckRollup { 143 contexts(first:100%s) { 144 nodes { 145 __typename 146 ...on StatusContext { 147 context, 148 state, 149 targetUrl, 150 createdAt 151 }, 152 ...on CheckRun { 153 name, 154 checkSuite{workflowRun{workflow{name}}}, 155 status, 156 conclusion, 157 startedAt, 158 completedAt, 159 detailsUrl 160 } 161 }, 162 pageInfo{hasNextPage,endCursor} 163 } 164 } 165 } 166 } 167 }`), afterClause) 168 } 169 170 func RequiredStatusCheckRollupGraphQL(prID, after string) string { 171 var afterClause string 172 if after != "" { 173 afterClause = ",after:" + after 174 } 175 return fmt.Sprintf(shortenQuery(` 176 statusCheckRollup: commits(last: 1) { 177 nodes { 178 commit { 179 statusCheckRollup { 180 contexts(first:100%[1]s) { 181 nodes { 182 __typename 183 ...on StatusContext { 184 context, 185 state, 186 targetUrl, 187 createdAt, 188 isRequired(pullRequestId: %[2]s) 189 }, 190 ...on CheckRun { 191 name, 192 checkSuite{workflowRun{workflow{name}}}, 193 status, 194 conclusion, 195 startedAt, 196 completedAt, 197 detailsUrl, 198 isRequired(pullRequestId: %[2]s) 199 } 200 }, 201 pageInfo{hasNextPage,endCursor} 202 } 203 } 204 } 205 } 206 }`), afterClause, prID) 207 } 208 209 var IssueFields = []string{ 210 "assignees", 211 "author", 212 "body", 213 "closed", 214 "comments", 215 "createdAt", 216 "closedAt", 217 "id", 218 "labels", 219 "milestone", 220 "number", 221 "projectCards", 222 "reactionGroups", 223 "state", 224 "title", 225 "updatedAt", 226 "url", 227 } 228 229 var PullRequestFields = append(IssueFields, 230 "additions", 231 "baseRefName", 232 "changedFiles", 233 "commits", 234 "deletions", 235 "files", 236 "headRefName", 237 "headRefOid", 238 "headRepository", 239 "headRepositoryOwner", 240 "isCrossRepository", 241 "isDraft", 242 "latestReviews", 243 "maintainerCanModify", 244 "mergeable", 245 "mergeCommit", 246 "mergedAt", 247 "mergedBy", 248 "mergeStateStatus", 249 "potentialMergeCommit", 250 "reviewDecision", 251 "reviewRequests", 252 "reviews", 253 "statusCheckRollup", 254 ) 255 256 // IssueGraphQL constructs a GraphQL query fragment for a set of issue fields. 257 func IssueGraphQL(fields []string) string { 258 var q []string 259 for _, field := range fields { 260 switch field { 261 case "author": 262 q = append(q, `author{login}`) 263 case "mergedBy": 264 q = append(q, `mergedBy{login}`) 265 case "headRepositoryOwner": 266 q = append(q, `headRepositoryOwner{id,login,...on User{name}}`) 267 case "headRepository": 268 q = append(q, `headRepository{id,name}`) 269 case "assignees": 270 q = append(q, `assignees(first:100){nodes{id,login,name},totalCount}`) 271 case "labels": 272 q = append(q, `labels(first:100){nodes{id,name,description,color},totalCount}`) 273 case "projectCards": 274 q = append(q, `projectCards(first:100){nodes{project{name}column{name}},totalCount}`) 275 case "milestone": 276 q = append(q, `milestone{number,title,description,dueOn}`) 277 case "reactionGroups": 278 q = append(q, `reactionGroups{content,users{totalCount}}`) 279 case "mergeCommit": 280 q = append(q, `mergeCommit{oid}`) 281 case "potentialMergeCommit": 282 q = append(q, `potentialMergeCommit{oid}`) 283 case "comments": 284 q = append(q, issueComments) 285 case "lastComment": // pseudo-field 286 q = append(q, issueCommentLast) 287 case "reviewRequests": 288 q = append(q, prReviewRequests) 289 case "reviews": 290 q = append(q, prReviews) 291 case "latestReviews": 292 q = append(q, prLatestReviews) 293 case "files": 294 q = append(q, prFiles) 295 case "commits": 296 q = append(q, prCommits) 297 case "lastCommit": // pseudo-field 298 q = append(q, `commits(last:1){nodes{commit{oid}}}`) 299 case "commitsCount": // pseudo-field 300 q = append(q, `commits{totalCount}`) 301 case "requiresStrictStatusChecks": // pseudo-field 302 q = append(q, `baseRef{branchProtectionRule{requiresStrictStatusChecks}}`) 303 case "statusCheckRollup": 304 q = append(q, StatusCheckRollupGraphQL("")) 305 default: 306 q = append(q, field) 307 } 308 } 309 return strings.Join(q, ",") 310 } 311 312 // PullRequestGraphQL constructs a GraphQL query fragment for a set of pull request fields. 313 // It will try to sanitize the fields to just those available on pull request. 314 func PullRequestGraphQL(fields []string) string { 315 invalidFields := []string{"isPinned", "stateReason"} 316 s := set.NewStringSet() 317 s.AddValues(fields) 318 s.RemoveValues(invalidFields) 319 return IssueGraphQL(s.ToSlice()) 320 } 321 322 var RepositoryFields = []string{ 323 "id", 324 "name", 325 "nameWithOwner", 326 "owner", 327 "parent", 328 "templateRepository", 329 "description", 330 "homepageUrl", 331 "openGraphImageUrl", 332 "usesCustomOpenGraphImage", 333 "url", 334 "sshUrl", 335 "mirrorUrl", 336 "securityPolicyUrl", 337 338 "createdAt", 339 "pushedAt", 340 "updatedAt", 341 342 "isBlankIssuesEnabled", 343 "isSecurityPolicyEnabled", 344 "hasIssuesEnabled", 345 "hasProjectsEnabled", 346 "hasWikiEnabled", 347 "mergeCommitAllowed", 348 "squashMergeAllowed", 349 "rebaseMergeAllowed", 350 351 "forkCount", 352 "stargazerCount", 353 "watchers", 354 "issues", 355 "pullRequests", 356 357 "codeOfConduct", 358 "contactLinks", 359 "defaultBranchRef", 360 "deleteBranchOnMerge", 361 "diskUsage", 362 "fundingLinks", 363 "isArchived", 364 "isEmpty", 365 "isFork", 366 "isInOrganization", 367 "isMirror", 368 "isPrivate", 369 "isTemplate", 370 "isUserConfigurationRepository", 371 "licenseInfo", 372 "viewerCanAdminister", 373 "viewerDefaultCommitEmail", 374 "viewerDefaultMergeMethod", 375 "viewerHasStarred", 376 "viewerPermission", 377 "viewerPossibleCommitEmails", 378 "viewerSubscription", 379 380 "repositoryTopics", 381 "primaryLanguage", 382 "languages", 383 "issueTemplates", 384 "pullRequestTemplates", 385 "labels", 386 "milestones", 387 "latestRelease", 388 389 "assignableUsers", 390 "mentionableUsers", 391 "projects", 392 393 // "branchProtectionRules", // too complex to expose 394 // "collaborators", // does it make sense to expose without affiliation filter? 395 } 396 397 func RepositoryGraphQL(fields []string) string { 398 var q []string 399 for _, field := range fields { 400 switch field { 401 case "codeOfConduct": 402 q = append(q, "codeOfConduct{key,name,url}") 403 case "contactLinks": 404 q = append(q, "contactLinks{about,name,url}") 405 case "fundingLinks": 406 q = append(q, "fundingLinks{platform,url}") 407 case "licenseInfo": 408 q = append(q, "licenseInfo{key,name,nickname}") 409 case "owner": 410 q = append(q, "owner{id,login}") 411 case "parent": 412 q = append(q, "parent{id,name,owner{id,login}}") 413 case "templateRepository": 414 q = append(q, "templateRepository{id,name,owner{id,login}}") 415 case "repositoryTopics": 416 q = append(q, "repositoryTopics(first:100){nodes{topic{name}}}") 417 case "issueTemplates": 418 q = append(q, "issueTemplates{name,title,body,about}") 419 case "pullRequestTemplates": 420 q = append(q, "pullRequestTemplates{body,filename}") 421 case "labels": 422 q = append(q, "labels(first:100){nodes{id,color,name,description}}") 423 case "languages": 424 q = append(q, "languages(first:100){edges{size,node{name}}}") 425 case "primaryLanguage": 426 q = append(q, "primaryLanguage{name}") 427 case "latestRelease": 428 q = append(q, "latestRelease{publishedAt,tagName,name,url}") 429 case "milestones": 430 q = append(q, "milestones(first:100,states:OPEN){nodes{number,title,description,dueOn}}") 431 case "assignableUsers": 432 q = append(q, "assignableUsers(first:100){nodes{id,login,name}}") 433 case "mentionableUsers": 434 q = append(q, "mentionableUsers(first:100){nodes{id,login,name}}") 435 case "projects": 436 q = append(q, "projects(first:100,states:OPEN){nodes{id,name,number,body,resourcePath}}") 437 case "watchers": 438 q = append(q, "watchers{totalCount}") 439 case "issues": 440 q = append(q, "issues(states:OPEN){totalCount}") 441 case "pullRequests": 442 q = append(q, "pullRequests(states:OPEN){totalCount}") 443 case "defaultBranchRef": 444 q = append(q, "defaultBranchRef{name}") 445 default: 446 q = append(q, field) 447 } 448 } 449 return strings.Join(q, ",") 450 }