Skip to content

Commit bb202a5

Browse files
committed
Add a utility to detect description changes
We use this internally for pinging docs team for PR reviews.
1 parent 9b9c7ad commit bb202a5

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Copyright (c) 2016, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
import { expect } from 'chai';
11+
import { describe, it } from 'mocha';
12+
import { GraphQLObjectType, GraphQLSchema, GraphQLString } from '../../type';
13+
import { findDescriptionChanges } from '../findDescriptionChanges';
14+
15+
describe('findDescriptionChanges', () => {
16+
const queryType = new GraphQLObjectType({
17+
name: 'Query',
18+
fields: {
19+
field1: { type: GraphQLString },
20+
},
21+
});
22+
23+
it('should detect if a description was added to a type', () => {
24+
const typeOld = new GraphQLObjectType({
25+
name: 'Type',
26+
fields: {
27+
field1: { type: GraphQLString },
28+
},
29+
});
30+
const typeNew = new GraphQLObjectType({
31+
name: 'Type',
32+
description: 'Something rather',
33+
fields: {
34+
field1: { type: GraphQLString },
35+
},
36+
});
37+
38+
const oldSchema = new GraphQLSchema({
39+
query: queryType,
40+
types: [typeOld],
41+
});
42+
const newSchema = new GraphQLSchema({
43+
query: queryType,
44+
types: [typeNew],
45+
});
46+
expect(findDescriptionChanges(oldSchema, newSchema)).to.eql([
47+
'Description added on type Type.',
48+
]);
49+
expect(findDescriptionChanges(oldSchema, oldSchema)).to.eql([]);
50+
expect(findDescriptionChanges(newSchema, newSchema)).to.eql([]);
51+
});
52+
53+
it('should detect if a type with a description was added', () => {
54+
const type = new GraphQLObjectType({
55+
name: 'Type',
56+
description: 'Something rather',
57+
fields: {
58+
field1: { type: GraphQLString },
59+
},
60+
});
61+
62+
const oldSchema = new GraphQLSchema({
63+
query: queryType,
64+
types: [],
65+
});
66+
const newSchema = new GraphQLSchema({
67+
query: queryType,
68+
types: [type],
69+
});
70+
expect(findDescriptionChanges(oldSchema, newSchema)).to.eql([
71+
'Description added on new type Type.',
72+
]);
73+
expect(findDescriptionChanges(oldSchema, oldSchema)).to.eql([]);
74+
expect(findDescriptionChanges(newSchema, newSchema)).to.eql([]);
75+
});
76+
});
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/* eslint-disable no-restricted-syntax */
2+
// @flow
3+
4+
import {
5+
GraphQLInterfaceType,
6+
GraphQLObjectType,
7+
GraphQLEnumType,
8+
GraphQLInputObjectType,
9+
} from '../type/definition';
10+
import type { GraphQLFieldMap } from '../type/definition';
11+
import { GraphQLSchema } from '../type/schema';
12+
13+
/**
14+
* Given two schemas, returns an Array containing descriptions of any
15+
* descriptions that are new or changed and need review.
16+
*/
17+
export function findDescriptionChanges(
18+
oldSchema: GraphQLSchema,
19+
newSchema: GraphQLSchema,
20+
): Array<string> {
21+
const oldTypeMap = oldSchema.getTypeMap();
22+
const newTypeMap = newSchema.getTypeMap();
23+
24+
const descriptionChanges: Array<string> = [];
25+
26+
Object.keys(newTypeMap).forEach(typeName => {
27+
const oldType = oldTypeMap[typeName];
28+
const newType = newTypeMap[typeName];
29+
30+
if (newType.description) {
31+
if (!oldType) {
32+
descriptionChanges.push(
33+
`Description added on new type ${newType.name}.`,
34+
);
35+
} else if (!oldType.description) {
36+
descriptionChanges.push(`Description added on type ${newType.name}.`);
37+
} else if (oldType.description !== newType.description) {
38+
descriptionChanges.push(`Description changed on type ${newType.name}.`);
39+
}
40+
}
41+
42+
if (oldType && !(newType instanceof oldType.constructor)) {
43+
return;
44+
}
45+
46+
if (
47+
newType instanceof GraphQLObjectType ||
48+
newType instanceof GraphQLInterfaceType ||
49+
newType instanceof GraphQLInputObjectType
50+
) {
51+
const oldTypeFields: ?GraphQLFieldMap<*, *> = oldType
52+
? oldType.getFields()
53+
: null;
54+
const newTypeFields: GraphQLFieldMap<*, *> = newType.getFields();
55+
56+
Object.keys(newTypeFields).forEach(fieldName => {
57+
const oldField = oldTypeFields ? oldTypeFields[fieldName] : null;
58+
const newField = newTypeFields[fieldName];
59+
60+
if (newField.description) {
61+
if (!oldField) {
62+
descriptionChanges.push(
63+
`Description added on new field ${newType.name}.${newField.name}`,
64+
);
65+
} else if (!oldField.description) {
66+
descriptionChanges.push(
67+
`Description added on field ${newType.name}.${newField.name}.`,
68+
);
69+
} else if (oldField.description !== newField.description) {
70+
descriptionChanges.push(
71+
`Description changed on field ${newType.name}.${newField.name}.`,
72+
);
73+
}
74+
}
75+
76+
if (!newField.args) {
77+
return;
78+
}
79+
80+
newField.args.forEach(newArg => {
81+
const oldArg = oldField
82+
? oldField.args.find(arg => arg.name === newArg.name)
83+
: null;
84+
85+
if (newArg.description) {
86+
if (!oldArg) {
87+
descriptionChanges.push(
88+
`Description added on new arg ${newType.name}.${
89+
newField.name
90+
}.${newArg.name}.`,
91+
);
92+
} else if (!oldArg.description) {
93+
descriptionChanges.push(
94+
`Description added on arg ${newType.name}.${newField.name}.${
95+
newArg.name
96+
}.`,
97+
);
98+
} else if (oldArg.description !== newArg.description) {
99+
descriptionChanges.push(
100+
`Description changed on arg ${newType.name}.${newField.name}.${
101+
newArg.name
102+
}.`,
103+
);
104+
}
105+
}
106+
});
107+
});
108+
} else if (newType instanceof GraphQLEnumType) {
109+
const oldValues = oldType ? oldType.getValues() : null;
110+
const newValues = newType.getValues();
111+
newValues.forEach(newValue => {
112+
const oldValue = oldValues
113+
? oldValues.find(value => value.name === newValue.name)
114+
: null;
115+
116+
if (newValue.description) {
117+
if (!oldValue) {
118+
descriptionChanges.push(
119+
`Description added on enum value ${newType.name}.${
120+
newValue.name
121+
}.`,
122+
);
123+
} else if (!oldValue.description) {
124+
descriptionChanges.push(
125+
`Description added on enum value ${newType.name}.${
126+
newValue.name
127+
}.`,
128+
);
129+
} else if (oldValue.description !== newValue.description) {
130+
descriptionChanges.push(
131+
`Description changed on enum value ${newType.name}.${
132+
newValue.name
133+
}.`,
134+
);
135+
}
136+
}
137+
});
138+
}
139+
});
140+
141+
return descriptionChanges;
142+
}

0 commit comments

Comments
 (0)