Skip to content

Commit a63054f

Browse files
committed
Enable passing values to enum as a thunk
1 parent 9c90a23 commit a63054f

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

src/type/__tests__/enumType-test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ const ComplexEnum = new GraphQLEnumType({
3131
},
3232
});
3333

34+
const ThunkValuesEnum = new GraphQLEnumType({
35+
name: 'ThunkValues',
36+
values: () => ({
37+
A: { value: 'a' },
38+
B: { value: 'b' },
39+
}),
40+
});
41+
3442
const QueryType = new GraphQLObjectType({
3543
name: 'Query',
3644
fields: {
@@ -84,6 +92,15 @@ const QueryType = new GraphQLObjectType({
8492
return fromEnum;
8593
},
8694
},
95+
thunkValuesString: {
96+
type: GraphQLString,
97+
args: {
98+
fromEnum: { type: ThunkValuesEnum },
99+
},
100+
resolve(_source, { fromEnum }) {
101+
return fromEnum;
102+
},
103+
},
87104
},
88105
});
89106

@@ -400,6 +417,14 @@ describe('Type System: Enum Values', () => {
400417
});
401418
});
402419

420+
it('may have values specified via a callback', () => {
421+
const result = executeQuery('{ thunkValuesString(fromEnum: B) }');
422+
423+
expect(result).to.deep.equal({
424+
data: { thunkValuesString: 'b' },
425+
});
426+
});
427+
403428
it('can be introspected without error', () => {
404429
expect(() => introspectionFromSchema(schema)).to.not.throw();
405430
});

src/type/definition.ts

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,17 @@ export interface GraphQLEnumTypeExtensions {
12371237
[attributeName: string]: unknown;
12381238
}
12391239

1240+
function enumValuesFromConfig(values: GraphQLEnumValueConfigMap) {
1241+
return Object.entries(values).map(([valueName, valueConfig]) => ({
1242+
name: assertEnumValueName(valueName),
1243+
description: valueConfig.description,
1244+
value: valueConfig.value !== undefined ? valueConfig.value : valueName,
1245+
deprecationReason: valueConfig.deprecationReason,
1246+
extensions: toObjMap(valueConfig.extensions),
1247+
astNode: valueConfig.astNode,
1248+
}));
1249+
}
1250+
12401251
/**
12411252
* Enum Type Definition
12421253
*
@@ -1267,9 +1278,12 @@ export class GraphQLEnumType /* <T> */ {
12671278
astNode: Maybe<EnumTypeDefinitionNode>;
12681279
extensionASTNodes: ReadonlyArray<EnumTypeExtensionNode>;
12691280

1270-
private _values: ReadonlyArray<GraphQLEnumValue /* <T> */>;
1271-
private _valueLookup: ReadonlyMap<any /* T */, GraphQLEnumValue>;
1272-
private _nameLookup: ObjMap<GraphQLEnumValue>;
1281+
private _values:
1282+
| ReadonlyArray<GraphQLEnumValue /* <T> */>
1283+
| (() => GraphQLEnumValueConfigMap);
1284+
1285+
private _valueLookup: ReadonlyMap<any /* T */, GraphQLEnumValue> | null;
1286+
private _nameLookup: ObjMap<GraphQLEnumValue> | null;
12731287

12741288
constructor(config: Readonly<GraphQLEnumTypeConfig /* <T> */>) {
12751289
this.name = assertName(config.name);
@@ -1278,35 +1292,38 @@ export class GraphQLEnumType /* <T> */ {
12781292
this.astNode = config.astNode;
12791293
this.extensionASTNodes = config.extensionASTNodes ?? [];
12801294

1281-
this._values = Object.entries(config.values).map(
1282-
([valueName, valueConfig]) => ({
1283-
name: assertEnumValueName(valueName),
1284-
description: valueConfig.description,
1285-
value: valueConfig.value !== undefined ? valueConfig.value : valueName,
1286-
deprecationReason: valueConfig.deprecationReason,
1287-
extensions: toObjMap(valueConfig.extensions),
1288-
astNode: valueConfig.astNode,
1289-
}),
1290-
);
1291-
this._valueLookup = new Map(
1292-
this._values.map((enumValue) => [enumValue.value, enumValue]),
1293-
);
1294-
this._nameLookup = keyMap(this._values, (value) => value.name);
1295+
this._values =
1296+
typeof config.values === 'function'
1297+
? config.values
1298+
: enumValuesFromConfig(config.values);
1299+
this._valueLookup = null;
1300+
this._nameLookup = null;
12951301
}
12961302

12971303
get [Symbol.toStringTag]() {
12981304
return 'GraphQLEnumType';
12991305
}
13001306

13011307
getValues(): ReadonlyArray<GraphQLEnumValue /* <T> */> {
1308+
if (typeof this._values === 'function') {
1309+
this._values = enumValuesFromConfig(this._values());
1310+
}
13021311
return this._values;
13031312
}
13041313

13051314
getValue(name: string): Maybe<GraphQLEnumValue> {
1315+
if (this._nameLookup === null) {
1316+
this._nameLookup = keyMap(this.getValues(), (value) => value.name);
1317+
}
13061318
return this._nameLookup[name];
13071319
}
13081320

13091321
serialize(outputValue: unknown /* T */): Maybe<string> {
1322+
if (this._valueLookup === null) {
1323+
this._valueLookup = new Map(
1324+
this.getValues().map((enumValue) => [enumValue.value, enumValue]),
1325+
);
1326+
}
13101327
const enumValue = this._valueLookup.get(outputValue);
13111328
if (enumValue === undefined) {
13121329
throw new GraphQLError(
@@ -1406,7 +1423,7 @@ function didYouMeanEnumValue(
14061423
export interface GraphQLEnumTypeConfig {
14071424
name: string;
14081425
description?: Maybe<string>;
1409-
values: GraphQLEnumValueConfigMap /* <T> */;
1426+
values: ThunkObjMap<GraphQLEnumValueConfig /* <T> */>;
14101427
extensions?: Maybe<Readonly<GraphQLEnumTypeExtensions>>;
14111428
astNode?: Maybe<EnumTypeDefinitionNode>;
14121429
extensionASTNodes?: Maybe<ReadonlyArray<EnumTypeExtensionNode>>;

0 commit comments

Comments
 (0)