/
RenameTypes.ts
121 lines (109 loc) · 3.46 KB
/
RenameTypes.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import {
GraphQLNamedType,
GraphQLSchema,
Kind,
NamedTypeNode,
isScalarType,
isSpecifiedScalarType,
visit,
} from 'graphql';
import {
ExecutionRequest,
ExecutionResult,
MapperKind,
RenameTypesOptions,
mapSchema,
visitData,
renameType,
} from '@graphql-tools/utils';
import { Transform, DelegationContext, SubschemaConfig } from '@graphql-tools/delegate';
interface RenameTypesTransformationContext extends Record<string, any> {}
export default class RenameTypes<TContext = Record<string, any>>
implements Transform<RenameTypesTransformationContext, TContext>
{
private readonly renamer: (name: string) => string | undefined;
private map: Record<string, string>;
private reverseMap: Record<string, string>;
private readonly renameBuiltins: boolean;
private readonly renameScalars: boolean;
constructor(renamer: (name: string) => string | undefined, options?: RenameTypesOptions) {
this.renamer = renamer;
this.map = Object.create(null);
this.reverseMap = Object.create(null);
const { renameBuiltins = false, renameScalars = true } = options != null ? options : {};
this.renameBuiltins = renameBuiltins;
this.renameScalars = renameScalars;
}
public transformSchema(
originalWrappingSchema: GraphQLSchema,
_subschemaConfig: SubschemaConfig<any, any, any, TContext>
): GraphQLSchema {
const typeNames = new Set<string>(Object.keys(originalWrappingSchema.getTypeMap()));
return mapSchema(originalWrappingSchema, {
[MapperKind.TYPE]: (type: GraphQLNamedType) => {
if (isSpecifiedScalarType(type) && !this.renameBuiltins) {
return undefined;
}
if (isScalarType(type) && !this.renameScalars) {
return undefined;
}
const oldName = type.name;
const newName = this.renamer(oldName);
if (newName !== undefined && newName !== oldName) {
if (typeNames.has(newName)) {
console.warn(`New type name ${newName} for ${oldName} already exists in the schema. Skip renaming.`);
return;
}
this.map[oldName] = newName;
this.reverseMap[newName] = oldName;
typeNames.delete(oldName);
typeNames.add(newName);
return renameType(type, newName);
}
},
[MapperKind.ROOT_OBJECT]() {
return undefined;
},
});
}
public transformRequest(
originalRequest: ExecutionRequest,
_delegationContext: DelegationContext<TContext>,
_transformationContext: RenameTypesTransformationContext
): ExecutionRequest {
const document = visit(originalRequest.document, {
[Kind.NAMED_TYPE]: (node: NamedTypeNode) => {
const name = node.name.value;
if (name in this.reverseMap) {
return {
...node,
name: {
kind: Kind.NAME,
value: this.reverseMap[name],
},
};
}
},
});
return {
...originalRequest,
document,
};
}
public transformResult(
originalResult: ExecutionResult,
_delegationContext: DelegationContext<TContext>,
_transformationContext?: RenameTypesTransformationContext
): ExecutionResult {
return {
...originalResult,
data: visitData(originalResult.data, object => {
const typeName = object?.__typename;
if (typeName != null && typeName in this.map) {
object.__typename = this.map[typeName];
}
return object;
}),
};
}
}