-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a utility to detect description changes
We use this internally for pinging docs team for PR reviews.
- Loading branch information
Showing
2 changed files
with
218 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/** | ||
* Copyright (c) 2016, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
import { expect } from 'chai'; | ||
import { describe, it } from 'mocha'; | ||
import { GraphQLObjectType, GraphQLSchema, GraphQLString } from '../../type'; | ||
import { findDescriptionChanges } from '../findDescriptionChanges'; | ||
|
||
describe('findDescriptionChanges', () => { | ||
const queryType = new GraphQLObjectType({ | ||
name: 'Query', | ||
fields: { | ||
field1: { type: GraphQLString }, | ||
}, | ||
}); | ||
|
||
it('should detect if a description was added to a type', () => { | ||
const typeOld = new GraphQLObjectType({ | ||
name: 'Type', | ||
fields: { | ||
field1: { type: GraphQLString }, | ||
}, | ||
}); | ||
const typeNew = new GraphQLObjectType({ | ||
name: 'Type', | ||
description: 'Something rather', | ||
fields: { | ||
field1: { type: GraphQLString }, | ||
}, | ||
}); | ||
|
||
const oldSchema = new GraphQLSchema({ | ||
query: queryType, | ||
types: [typeOld], | ||
}); | ||
const newSchema = new GraphQLSchema({ | ||
query: queryType, | ||
types: [typeNew], | ||
}); | ||
expect(findDescriptionChanges(oldSchema, newSchema)).to.eql([ | ||
'Description added on type Type.', | ||
]); | ||
expect(findDescriptionChanges(oldSchema, oldSchema)).to.eql([]); | ||
expect(findDescriptionChanges(newSchema, newSchema)).to.eql([]); | ||
}); | ||
|
||
it('should detect if a type with a description was added', () => { | ||
const type = new GraphQLObjectType({ | ||
name: 'Type', | ||
description: 'Something rather', | ||
fields: { | ||
field1: { type: GraphQLString }, | ||
}, | ||
}); | ||
|
||
const oldSchema = new GraphQLSchema({ | ||
query: queryType, | ||
types: [], | ||
}); | ||
const newSchema = new GraphQLSchema({ | ||
query: queryType, | ||
types: [type], | ||
}); | ||
expect(findDescriptionChanges(oldSchema, newSchema)).to.eql([ | ||
'Description added on new type Type.', | ||
]); | ||
expect(findDescriptionChanges(oldSchema, oldSchema)).to.eql([]); | ||
expect(findDescriptionChanges(newSchema, newSchema)).to.eql([]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* eslint-disable no-restricted-syntax */ | ||
// @flow | ||
|
||
import { | ||
GraphQLInterfaceType, | ||
GraphQLObjectType, | ||
GraphQLEnumType, | ||
GraphQLInputObjectType, | ||
} from '../type/definition'; | ||
import type { GraphQLFieldMap } from '../type/definition'; | ||
import { GraphQLSchema } from '../type/schema'; | ||
|
||
/** | ||
* Given two schemas, returns an Array containing descriptions of any | ||
* descriptions that are new or changed and need review. | ||
*/ | ||
export function findDescriptionChanges( | ||
oldSchema: GraphQLSchema, | ||
newSchema: GraphQLSchema, | ||
): Array<string> { | ||
const oldTypeMap = oldSchema.getTypeMap(); | ||
const newTypeMap = newSchema.getTypeMap(); | ||
|
||
const descriptionChanges: Array<string> = []; | ||
|
||
Object.keys(newTypeMap).forEach(typeName => { | ||
const oldType = oldTypeMap[typeName]; | ||
const newType = newTypeMap[typeName]; | ||
|
||
if (newType.description) { | ||
if (!oldType) { | ||
descriptionChanges.push( | ||
`Description added on new type ${newType.name}.`, | ||
); | ||
} else if (!oldType.description) { | ||
descriptionChanges.push(`Description added on type ${newType.name}.`); | ||
} else if (oldType.description !== newType.description) { | ||
descriptionChanges.push(`Description changed on type ${newType.name}.`); | ||
} | ||
} | ||
|
||
if (oldType && !(newType instanceof oldType.constructor)) { | ||
return; | ||
} | ||
|
||
if ( | ||
newType instanceof GraphQLObjectType || | ||
newType instanceof GraphQLInterfaceType || | ||
newType instanceof GraphQLInputObjectType | ||
) { | ||
const oldTypeFields: ?GraphQLFieldMap<*, *> = oldType | ||
? oldType.getFields() | ||
: null; | ||
const newTypeFields: GraphQLFieldMap<*, *> = newType.getFields(); | ||
|
||
Object.keys(newTypeFields).forEach(fieldName => { | ||
const oldField = oldTypeFields ? oldTypeFields[fieldName] : null; | ||
const newField = newTypeFields[fieldName]; | ||
|
||
if (newField.description) { | ||
if (!oldField) { | ||
descriptionChanges.push( | ||
`Description added on new field ${newType.name}.${newField.name}`, | ||
); | ||
} else if (!oldField.description) { | ||
descriptionChanges.push( | ||
`Description added on field ${newType.name}.${newField.name}.`, | ||
); | ||
} else if (oldField.description !== newField.description) { | ||
descriptionChanges.push( | ||
`Description changed on field ${newType.name}.${newField.name}.`, | ||
); | ||
} | ||
} | ||
|
||
if (!newField.args) { | ||
return; | ||
} | ||
|
||
newField.args.forEach(newArg => { | ||
const oldArg = oldField | ||
? oldField.args.find(arg => arg.name === newArg.name) | ||
: null; | ||
|
||
if (newArg.description) { | ||
if (!oldArg) { | ||
descriptionChanges.push( | ||
`Description added on new arg ${newType.name}.${ | ||
newField.name | ||
}.${newArg.name}.`, | ||
); | ||
} else if (!oldArg.description) { | ||
descriptionChanges.push( | ||
`Description added on arg ${newType.name}.${newField.name}.${ | ||
newArg.name | ||
}.`, | ||
); | ||
} else if (oldArg.description !== newArg.description) { | ||
descriptionChanges.push( | ||
`Description changed on arg ${newType.name}.${newField.name}.${ | ||
newArg.name | ||
}.`, | ||
); | ||
} | ||
} | ||
}); | ||
}); | ||
} else if (newType instanceof GraphQLEnumType) { | ||
const oldValues = oldType ? oldType.getValues() : null; | ||
const newValues = newType.getValues(); | ||
newValues.forEach(newValue => { | ||
const oldValue = oldValues | ||
? oldValues.find(value => value.name === newValue.name) | ||
: null; | ||
|
||
if (newValue.description) { | ||
if (!oldValue) { | ||
descriptionChanges.push( | ||
`Description added on enum value ${newType.name}.${ | ||
newValue.name | ||
}.`, | ||
); | ||
} else if (!oldValue.description) { | ||
descriptionChanges.push( | ||
`Description added on enum value ${newType.name}.${ | ||
newValue.name | ||
}.`, | ||
); | ||
} else if (oldValue.description !== newValue.description) { | ||
descriptionChanges.push( | ||
`Description changed on enum value ${newType.name}.${ | ||
newValue.name | ||
}.`, | ||
); | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
|
||
return descriptionChanges; | ||
} |