Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
128 lines (106 sloc) 5.35 KB
import { danger, peril } from "danger"
// This extends the schema objects generated by graphql-js
import "graphql-schema-utils"
import { GraphQLObjectType } from "graphql"
import { makeExecutableSchema } from "graphql-tools"
import { IncomingWebhook, MessageAttachment } from "@slack/client"
import { GraphQLDiff } from "../ambient"
// Some potential options for other folks looking to map schema changes
const org = "artsy"
const graphQLFile = "_schema.graphql"
export default async () => {
const api = danger.github.api
//
// First up, use the github search API To grab all files that match the original RFC
// https://github.com/search?q=org%3Aartsy+filename%3A_schema.graphql+path%3A%2F+language%3AGraphQL&type=Code
// so that we don't need to have a hard-coded list, you support the RFC, then you get this for free.
const queryForGraphQLFiles = `org:${org} filename:${graphQLFile} path:/ language:GraphQL`
const searchResponse = await api.search.code({ q: queryForGraphQLFiles })
console.log(`Found ${searchResponse.data.items.length} repos with a ${graphQLFile}`)
const reposWithSchema: string[] = searchResponse.data.items.map((i: any) => i.repository.name)
reposWithSchema.forEach(async repoName => {
const now = new Date()
now.setDate(now.getDate() - 7)
const weekAgoISO = now.toISOString()
// Grab the commits that are on the graphql schema in the last week, so we can get the last
// if there's over 100 commits _to the schema_ in a week, I'd be impressed.
const commitsSinceLastWeek = await api.repos.listCommits({
owner: org,
repo: repoName,
per_page: 100,
path: graphQLFile,
since: weekAgoISO,
})
const commitLength = commitsSinceLastWeek.data.length
if (!commitLength) {
console.log(`Skipping GraphQL diff for ${org}/${repoName}, due to no activity.`)
return
}
console.log(`Found ${commitLength} commits for ${org}/${repoName}`)
const lastCommit = commitsSinceLastWeek.data[commitLength - 1]
console.log(`Looking at the difference between master and ${lastCommit.sha} for ${org}/${repoName}`)
// Grab the SDL files
const masterSDL = await danger.github.utils.fileContents("_schema.graphql", `${org}/${repoName}`, "master")
const oldSDL = await danger.github.utils.fileContents("_schema.graphql", `${org}/${repoName}`, lastCommit.sha)
// It could be new, and the old ref might not have it yet
if (!oldSDL) {
console.log(`No _schema.graphql found back in ${lastCommit.sha}`)
return
}
// We want to make a GraphQL schema but we don't care about all the resolvers at all
// as we'll never run queries.
const makeSchema = (sdl: string) =>
makeExecutableSchema({
typeDefs: [sdl],
resolverValidationOptions: {
requireResolversForResolveType: false,
requireResolversForAllFields: false,
requireResolversForNonScalar: false,
requireResolversForArgs: false,
},
})
// SDL -> Schema
const masterSchema = makeSchema(masterSDL)
const oldSchema = makeSchema(oldSDL)
// Get the diff between these two schemas
const diffs: GraphQLDiff[] = (oldSchema as any).diff(masterSchema)
// Note: When looking at diff objects,
// thisType = old schema
// otherType = new schema
const codeJoin = (arr: string[]) => arr.map(a => "`" + a + "`").join(", ")
const messages: MessageAttachment[] = []
// What got added, this is the majority of our messages
const addedTypeMessages = diffs.filter(d => d.diffType == "TypeMissing" && d.otherType).map(d => d.otherType.name)
if (addedTypeMessages.length) {
messages.push({ color: "good", text: "Added:" + codeJoin(addedTypeMessages) })
}
// What was removed, highlighted in red as this stuff can break things
const removedTypesMessages = diffs.filter(d => d.diffType == "TypeMissing" && d.thisType).map(d => d.thisType.name)
if (removedTypesMessages.length) {
messages.push({ color: "danger", text: "Removed: " + codeJoin(removedTypesMessages) })
}
// Grab root query field changes, as they tend to be really useful to know about
const newRootQueriesTypesMessages = diffs.filter(d => d.diffType == "FieldMissing" && d.thisType.name === "Query")
if (newRootQueriesTypesMessages.length) {
const oldQuery = newRootQueriesTypesMessages[0].thisType as GraphQLObjectType
const newQuery = newRootQueriesTypesMessages[0].otherType as GraphQLObjectType
const newFields = Object.keys(newQuery.getFields())
const oldFields = Object.keys(oldQuery.getFields())
const diff = newFields.filter(f => !oldFields.includes(f))
messages.push({ color: "good", text: "New root query fields: " + codeJoin(diff) })
}
// TODO: There are probably more things we can show in here
// If there are any messages to send, wrap them up in a slack message with a link to the full compare url.
if (messages.length) {
var url = peril.env.SLACK_RFC_WEBHOOK_URL || ""
var webhook = new IncomingWebhook(url)
const compareURL = `https://github.com/${org}/${repoName}/compare/${lastCommit.sha}...master`
await webhook.send({
channel: "C1HH3KNJG",
unfurl_links: false,
text: `GraphQL Schema changes on \`${repoName}\``,
attachments: [...messages, { title: "Diff for last week", title_link: compareURL }],
})
}
})
}