Milestone uploader fetch github issues & pull requests on single milestone. fetched data can be uploaded to gist with upload button.
viewof url = text({placeholder: "milestone url", description: "https://github.com/:owner/:name/milestones/:number"})
viewof token = secret("github-oauth-token", { description: "github oauth token with 'gist' scope.", placeholder: "token", submit: "Set token" })
data = gql(statement, token) .then(({data}) => ({...data.repository.milestone, rateLimit: data.rateLimit})) .catch((err) => err.message)
valid && DOM.download( new Blob([JSON.stringify(data, null, 4)], {type: "application/json"}), "milestone.json" )
{ const button = html`<button>upload to gist`; button.onclick = () => mutable upload = !upload; return valid && button; }
{ if (valid && !!upload) { mutable upload = !upload return uploadGist(data) } }
gql = (statement, token) => { if (valid) { return d3.json('https://api.github.com/graphql', { method: 'POST', headers: { Authorization: `token ${token}` }, body: JSON.stringify(statement) }) } return new Promise((resolve, reject) => reject({message: html`<span style="color: red;">both <code>url</code> & <code>token</code> is required</span>`})) }
uploadGist = (data) => { return d3.json('https://api.github.com/gists', { method: 'POST', headers: { Authorization: `token ${token}` }, body: JSON.stringify({ description: `milestone report for ${data.title} (${url})`, files: { "data.json": { content: JSON.stringify(data, null, 4) } } }) }) // .then(res => res.json()) .then(res => { return html`milestone <code>${data.title}</code> <a href="${res.files['data.json'].raw_url}">uploaded</a>` }) .catch((err) => html`<span style="color: red;">upload failed : ${err.message}</span>`) }
info = { if (url.length > 0) { const parser = document.createElement('a') parser.href = url const [owner, name,, milestone] = parser.pathname.split('/').filter(d => d) return {owner, name, milestone: +milestone} } return {owner: '', name: '', milestone: null} }
d3 = require('d3')
statement = ({ query: `query($owner: String!, $name: String!, $milestone: Int!) { rateLimit { cost limit nodeCount remaining resetAt } repository(owner: $owner, name: $name) { milestone(number: $milestone) { title createdAt closedAt issues(first: 100) { totalCount nodes { title state number createdAt closedAt ...CommentFields ...ReactionFields comments(first: 100) { totalCount nodes { ...CommentFields ...ReactionFields } } } } pullRequests(first: 100) { totalCount nodes { title state number createdAt closedAt additions deletions ...CommentFields ...ReactionFields reviewRequests(first: 100) { totalCount nodes { requestedReviewer } } commits { totalCount } reviews(first: 100) { totalCount nodes { state author { ... UserField } createdAt comments { totalCount } } } comments(first: 100) { totalCount nodes { ...CommentFields ...ReactionFields } } } } } } } fragment UserField on User { name login } fragment ReactionFields on Reactable { reactions(first: 20) { totalCount nodes { content user { ...UserField } createdAt } } } fragment CommentFields on Comment { author { ...UserField } createdAt }`, variables: {...info} })
mutable upload = false
valid = url.length > 0 && info.owner.length > 0 && info.name.length > 0 && token.length > 0
import {text, button} from '@jashkenas/inputs'
import {secret} from "@tmcw/secret"