Skip to content

Commit

Permalink
Enable passing values configuration to GraphQLEnumType as a thunk (#4018
Browse files Browse the repository at this point in the history
)
  • Loading branch information
benjie committed Jun 21, 2024
1 parent c1aeb92 commit 9e2e751
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 18 deletions.
25 changes: 25 additions & 0 deletions src/type/__tests__/enumType-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ const ComplexEnum = new GraphQLEnumType({
},
});

const ThunkValuesEnum = new GraphQLEnumType({
name: 'ThunkValues',
values: () => ({
A: { value: 'a' },
B: { value: 'b' },
}),
});

const QueryType = new GraphQLObjectType({
name: 'Query',
fields: {
Expand Down Expand Up @@ -84,6 +92,15 @@ const QueryType = new GraphQLObjectType({
return fromEnum;
},
},
thunkValuesString: {
type: GraphQLString,
args: {
fromEnum: { type: ThunkValuesEnum },
},
resolve(_source, { fromEnum }) {
return fromEnum;
},
},
},
});

Expand Down Expand Up @@ -400,6 +417,14 @@ describe('Type System: Enum Values', () => {
});
});

it('may have values specified via a callback', () => {
const result = executeQuery('{ thunkValuesString(fromEnum: B) }');

expect(result).to.deep.equal({
data: { thunkValuesString: 'b' },
});
});

it('can be introspected without error', () => {
expect(() => introspectionFromSchema(schema)).to.not.throw();
});
Expand Down
54 changes: 36 additions & 18 deletions src/type/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,17 @@ export interface GraphQLEnumTypeExtensions {
[attributeName: string]: unknown;
}

function enumValuesFromConfig(values: GraphQLEnumValueConfigMap) {
return Object.entries(values).map(([valueName, valueConfig]) => ({
name: assertEnumValueName(valueName),
description: valueConfig.description,
value: valueConfig.value !== undefined ? valueConfig.value : valueName,
deprecationReason: valueConfig.deprecationReason,
extensions: toObjMap(valueConfig.extensions),
astNode: valueConfig.astNode,
}));
}

/**
* Enum Type Definition
*
Expand Down Expand Up @@ -1267,9 +1278,12 @@ export class GraphQLEnumType /* <T> */ {
astNode: Maybe<EnumTypeDefinitionNode>;
extensionASTNodes: ReadonlyArray<EnumTypeExtensionNode>;

private _values: ReadonlyArray<GraphQLEnumValue /* <T> */>;
private _valueLookup: ReadonlyMap<any /* T */, GraphQLEnumValue>;
private _nameLookup: ObjMap<GraphQLEnumValue>;
private _values:
| ReadonlyArray<GraphQLEnumValue /* <T> */>
| (() => GraphQLEnumValueConfigMap);

private _valueLookup: ReadonlyMap<any /* T */, GraphQLEnumValue> | null;
private _nameLookup: ObjMap<GraphQLEnumValue> | null;

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

this._values = Object.entries(config.values).map(
([valueName, valueConfig]) => ({
name: assertEnumValueName(valueName),
description: valueConfig.description,
value: valueConfig.value !== undefined ? valueConfig.value : valueName,
deprecationReason: valueConfig.deprecationReason,
extensions: toObjMap(valueConfig.extensions),
astNode: valueConfig.astNode,
}),
);
this._valueLookup = new Map(
this._values.map((enumValue) => [enumValue.value, enumValue]),
);
this._nameLookup = keyMap(this._values, (value) => value.name);
this._values =
typeof config.values === 'function'
? config.values
: enumValuesFromConfig(config.values);
this._valueLookup = null;
this._nameLookup = null;
}

get [Symbol.toStringTag]() {
return 'GraphQLEnumType';
}

getValues(): ReadonlyArray<GraphQLEnumValue /* <T> */> {
if (typeof this._values === 'function') {
this._values = enumValuesFromConfig(this._values());
}
return this._values;
}

getValue(name: string): Maybe<GraphQLEnumValue> {
if (this._nameLookup === null) {
this._nameLookup = keyMap(this.getValues(), (value) => value.name);
}
return this._nameLookup[name];
}

serialize(outputValue: unknown /* T */): Maybe<string> {
if (this._valueLookup === null) {
this._valueLookup = new Map(
this.getValues().map((enumValue) => [enumValue.value, enumValue]),
);
}
const enumValue = this._valueLookup.get(outputValue);
if (enumValue === undefined) {
throw new GraphQLError(
Expand Down Expand Up @@ -1406,13 +1423,14 @@ function didYouMeanEnumValue(
export interface GraphQLEnumTypeConfig {
name: string;
description?: Maybe<string>;
values: GraphQLEnumValueConfigMap /* <T> */;
values: ThunkObjMap<GraphQLEnumValueConfig /* <T> */>;
extensions?: Maybe<Readonly<GraphQLEnumTypeExtensions>>;
astNode?: Maybe<EnumTypeDefinitionNode>;
extensionASTNodes?: Maybe<ReadonlyArray<EnumTypeExtensionNode>>;
}

interface GraphQLEnumTypeNormalizedConfig extends GraphQLEnumTypeConfig {
values: ObjMap<GraphQLEnumValueConfig /* <T> */>;
extensions: Readonly<GraphQLEnumTypeExtensions>;
extensionASTNodes: ReadonlyArray<EnumTypeExtensionNode>;
}
Expand Down

0 comments on commit 9e2e751

Please sign in to comment.