Set up tests, start formatting messages

This commit is contained in:
Robert Mosolgo 2020-11-20 17:02:21 -05:00
Родитель 7e07c1c831
Коммит 96e4ea9499
3 изменённых файлов: 180 добавлений и 29 удалений

Просмотреть файл

@ -1,4 +1,4 @@
const { diff } = require('@graphql-inspector/core')
const { diff, ChangeType } = require('@graphql-inspector/core')
const { loadSchema } = require('@graphql-tools/load')
const git = require('../../lib/git-utils')
const fs = require('fs')
@ -8,9 +8,9 @@ if (!process.env.GITHUB_TOKEN) {
process.exit(1)
}
main()
// main()
async function main () {
async function main() {
// Load the previous schema from this repo
// TODO -- how to make sure that this script runs _before_ this artifact is updated?
// Maybe hook into the existing `update-files` script instead of being a stand-alone script.
@ -20,26 +20,59 @@ async function main () {
const tree = await git.getTree('github', 'github', 'heads/master')
const schemaFileBlob = tree.find(entry => entry.path.includes('config/schema.docs.graphql') && entry.type === 'blob')
const newSchemaBuffer = await git.getContentsForBlob('github', 'github', schemaFileBlob)
const changelogEntry = createChangelogEntry(oldSchemaString, newSchemaBuffer.toString())
if (changelogEntry) {
const previousChangelogString = fs.readFileSync('lib/graphql/static/changelog.json')
const previousChangelog = JSON.parse(previousChangelogString)
// add a new entry to the changelog data
previousChangelog.unshift(changelogEntry)
// rewrite the updated changelog
fs.writeFileSync('lib/graphql/static/changelog.json', JSON.stringify(previousChangelog, null, 2))
}
}
// Compare `oldSchemaString` to `newSchemaString`, and if there are any
// changes that warrant a changelog entry, return a changelog entry.
// Otherwise, return null.
async function createChangelogEntry(oldSchemaString, newSchemaString) {
// Create schema objects out of the strings
const oldSchema = await loadSchema(oldSchemaString)
const newSchema = await loadSchema(newSchemaBuffer.toString())
const newSchema = await loadSchema(newSchemaString)
// Generate changes between the two schemas
const changes = diff(oldSchema, newSchema)
console.log(changes)
const changesToReport = []
changes.forEach(function (change) {
if (CHANGES_TO_REPORT.includes(change.type)) {
changesToReport.push(change)
} else if (CHANGES_TO_IGNORE.includes(change.type)) {
// Do nothing
} else {
throw "This change type should be added to CHANGES_TO_REPORT or CHANGES_TO_IGNORE: " + change.type
}
})
const { schemaChangesToReport, previewChangesToReport } = segmentPreviewChanges(changesToReport)
// If there were any changes, create a changelog entry
if (changes.length > 0) {
const previousChangelogString = fs.readFileSync('lib/graphql/static/changelog.json')
const previousChangelog = JSON.parse(previousChangelogString)
if (schemaChangesToReport.length > 0 || previewChangesToReport.length > 0) {
// Build a `yyyy-mm-dd`-formatted date string
const today = new Date()
const todayString = String(today.getFullYear()) + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0')
// TODO format `changes` into strings
const formattedChanges = []
const changelogEntry = {
date: todayString,
schemaChanges: [],
previewChanges: [],
upcomingChanges: [],
}
const schemaChange = {
title: 'The GraphQL schema includes these changes:',
// Replace single quotes which wrap field/argument/type names with backticks
changes: changesToReport.map(function (change) { return change.message.replace(/'([a-zA-Z\. :!]+)'/g, '`$1`') })
}
changelogEntry.schemaChanges.push(schemaChange)
// TODO how are these populated?
// {
@ -62,23 +95,78 @@ async function main () {
// }
// ]
const upcomingChanges = []
// add a new entry to the changelog data
previousChangelog.unshift(
{
date: todayString,
schemaChanges: [
{
title: 'The GraphQL schema includes these changes:',
changes: formattedChanges
}
],
previewChanges: previewChanges,
upcomingChanges: upcomingChanges
}
)
// rewrite the updated changelog
fs.writeFileSync('lib/graphql/static/changelog.json', JSON.stringify(previousChangelog, null, 2))
return changelogEntry
} else {
return null
}
}
function segmentPreviewChanges(changesToReport) {
// TODO: read the previews yaml file and
// split the list of changes based on whether the change's path
// (or any "parents" in the change's path) are in a preview.
// See: https://github.com/github/graphql-docs/blob/master/lib/graphql_docs/update_internal_developer/change_log.rb#L230
return { schemaChangesToReport: changesToReport, previewChangesToReport: [] }
}
const CHANGES_TO_REPORT = [
ChangeType.FieldArgumentDefaultChanged,
ChangeType.FieldArgumentTypeChanged,
ChangeType.EnumValueRemoved,
ChangeType.EnumValueAdded,
ChangeType.FieldRemoved,
ChangeType.FieldAdded,
ChangeType.FieldTypeChanged,
ChangeType.FieldArgumentAdded,
ChangeType.FieldArgumentRemoved,
ChangeType.ObjectTypeInterfaceAdded,
ChangeType.ObjectTypeInterfaceRemoved,
ChangeType.InputFieldRemoved,
ChangeType.InputFieldAdded,
ChangeType.InputFieldDefaultValueChanged,
ChangeType.InputFieldTypeChanged,
ChangeType.TypeRemoved,
ChangeType.TypeAdded,
ChangeType.TypeKindChanged,
ChangeType.UnionMemberRemoved,
ChangeType.UnionMemberAdded,
ChangeType.SchemaQueryTypeChanged,
ChangeType.SchemaMutationTypeChanged,
ChangeType.SchemaSubscriptionTypeChanged,
]
const CHANGES_TO_IGNORE = [
ChangeType.FieldArgumentDescriptionChanged,
ChangeType.DirectiveRemoved,
ChangeType.DirectiveAdded,
ChangeType.DirectiveDescriptionChanged,
ChangeType.DirectiveLocationAdded,
ChangeType.DirectiveLocationRemoved,
ChangeType.DirectiveArgumentAdded,
ChangeType.DirectiveArgumentRemoved,
ChangeType.DirectiveArgumentDescriptionChanged,
ChangeType.DirectiveArgumentDefaultValueChanged,
ChangeType.DirectiveArgumentTypeChanged,
ChangeType.EnumValueDescriptionChanged,
ChangeType.EnumValueDeprecationReasonChanged,
ChangeType.EnumValueDeprecationReasonAdded,
ChangeType.EnumValueDeprecationReasonRemoved,
ChangeType.FieldDescriptionChanged,
ChangeType.FieldDescriptionAdded,
ChangeType.FieldDescriptionRemoved,
ChangeType.FieldDeprecationAdded,
ChangeType.FieldDeprecationRemoved,
ChangeType.FieldDeprecationReasonChanged,
ChangeType.FieldDeprecationReasonAdded,
ChangeType.FieldDeprecationReasonRemoved,
ChangeType.InputFieldDescriptionAdded,
ChangeType.InputFieldDescriptionRemoved,
ChangeType.InputFieldDescriptionChanged,
ChangeType.TypeDescriptionChanged,
ChangeType.TypeDescriptionRemoved,
ChangeType.TypeDescriptionAdded,
]
module.exports = { createChangelogEntry }

Просмотреть файл

@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`creating a changelog from old schema and new schema finds a diff of schema changes, upcoming changes, and preview changes 1`] = `
Object {
"date": "2020-11-20",
"previewChanges": Array [],
"schemaChanges": Array [
Object {
"changes": Array [
"Field \`removedField\` was removed from object type \`Query\`",
"Type for argument \`argumentMadeRequired\` on field \`Query.argumentsField\` changed from \`Int\` to \`Int!\`",
"Type for argument \`argumentMadeOptional\` on field \`Query.argumentsField\` changed from \`Int!\` to \`Int\`",
"Argument \`removedRequiredArgument: Int!\` was removed from field \`Query.argumentsField\`",
"Argument \`removedOptionalArgument: Int!\` was removed from field \`Query.argumentsField\`",
],
"title": "The GraphQL schema includes these changes:",
},
],
"upcomingChanges": Array [],
}
`;

Просмотреть файл

@ -0,0 +1,42 @@
const { createChangelogEntry } = require('../../script/graphql/build-changelog')
describe('creating a changelog from old schema and new schema', () => {
it('finds a diff of schema changes, upcoming changes, and preview changes', async () => {
const oldSchemaString = `
type Query {
stableField: String
removedField: Boolean
argumentsField(
removedRequiredArgument: Int!
removedOptionalArgument: Int!
argumentMadeRequired: Int
argumentMadeOptional: Int!
): String
}
`
const newSchemaString = `
type Query {
stableField: String
argumentsField(
argumentMadeRequired: Int!
argumentMadeOptional: Int
): String
}
`
const entry = await createChangelogEntry(oldSchemaString, newSchemaString)
expect(entry).toMatchSnapshot()
})
it('returns null when there isnt any difference', async () => {
const schemaString = `
type Query {
i: Int!
}`
const nullEntry = await createChangelogEntry(schemaString, schemaString)
expect(nullEntry).toBeNull()
})
})