Skip to content

Commit edae2a2

Browse files
committed
feat(graphql): add ModelTypeMap for GraphQL object type materialization
1 parent 5652caa commit edae2a2

3 files changed

Lines changed: 104 additions & 5 deletions

File tree

packages/graphql/src/registry.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { UsageFlags, type Enum } from "@typespec/compiler";
1+
import { UsageFlags, type Enum, type Model } from "@typespec/compiler";
22
import {
33
GraphQLBoolean,
44
GraphQLEnumType,
5+
GraphQLInputObjectType,
56
GraphQLObjectType,
67
type GraphQLNamedType,
78
type GraphQLSchemaConfig,
89
} from "graphql";
910
import { type TypeKey } from "./type-maps.js";
10-
import { EnumTypeMap } from "./type-maps/index.js";
11+
import { EnumTypeMap, ModelTypeMap } from "./type-maps/index.js";
1112
/**
1213
* GraphQLTypeRegistry manages the registration and materialization of TypeSpec (TSP)
1314
* types into their corresponding GraphQL type definitions.
@@ -32,8 +33,9 @@ import { EnumTypeMap } from "./type-maps/index.js";
3233
* by using thunks for fields/arguments.
3334
*/
3435
export class GraphQLTypeRegistry {
35-
// TypeMap for enum types
36+
// TypeMaps for each type kind
3637
private enumTypeMap = new EnumTypeMap();
38+
private modelTypeMap = new ModelTypeMap();
3739

3840
// Track all registered names to detect cross-TypeMap name collisions
3941
private allRegisteredNames = new Set<string>();
@@ -55,14 +57,36 @@ export class GraphQLTypeRegistry {
5557
this.allRegisteredNames.add(enumName);
5658
}
5759

58-
// Materializes a TSP Enum into a GraphQLEnumType.
60+
addModel(tspModel: Model, usageFlag: UsageFlags): void {
61+
const modelName = tspModel.name;
62+
63+
// Check for duplicate names across all type maps
64+
if (this.allRegisteredNames.has(modelName)) {
65+
// Already registered (could be same model or name collision)
66+
return;
67+
}
68+
69+
this.modelTypeMap.register({
70+
type: tspModel,
71+
usageFlag,
72+
});
73+
this.allRegisteredNames.add(modelName);
74+
}
75+
5976
materializeEnum(enumName: string): GraphQLEnumType | undefined {
6077
return this.enumTypeMap.get(enumName as TypeKey);
6178
}
6279

80+
materializeModel(modelName: string): GraphQLObjectType | GraphQLInputObjectType | undefined {
81+
return this.modelTypeMap.get(modelName as TypeKey);
82+
}
83+
6384
materializeSchemaConfig(): GraphQLSchemaConfig {
6485
// Collect all materialized types from all TypeMaps
65-
const allMaterializedGqlTypes: GraphQLNamedType[] = [...this.enumTypeMap.getAllMaterialized()];
86+
const allMaterializedGqlTypes: GraphQLNamedType[] = [
87+
...this.enumTypeMap.getAllMaterialized(),
88+
...this.modelTypeMap.getAllMaterialized(),
89+
];
6690
// TODO: Query type will come from operations
6791
let queryType: GraphQLObjectType | undefined = undefined;
6892
if (!queryType) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { TypeMap, type TSPContext, type TypeKey } from "../type-maps.js";
22
export { EnumTypeMap } from "./enum.js";
3+
export { ModelTypeMap } from "./model.js";
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { UsageFlags, type Model } from "@typespec/compiler";
2+
import {
3+
GraphQLInputObjectType,
4+
GraphQLObjectType,
5+
GraphQLString,
6+
type GraphQLFieldConfigMap,
7+
type GraphQLInputFieldConfigMap,
8+
} from "graphql";
9+
import { TypeMap, type TSPContext, type TypeKey } from "../type-maps.js";
10+
11+
/**
12+
* TypeMap for converting TypeSpec Models to GraphQL ObjectTypes or InputObjectTypes.
13+
*
14+
* Handles registration of TSP models and lazy materialization into
15+
* GraphQLObjectType (for output) or GraphQLInputObjectType (for input) instances.
16+
* The usageFlag in TSPContext determines which type to create.
17+
*/
18+
export class ModelTypeMap extends TypeMap<Model, GraphQLObjectType | GraphQLInputObjectType> {
19+
/**
20+
* Derives the type key from the mutated model's name.
21+
*/
22+
protected getNameFromContext(context: TSPContext<Model>): TypeKey {
23+
return context.type.name as TypeKey;
24+
}
25+
26+
/**
27+
* Materializes a TypeSpec Model into a GraphQL ObjectType or InputObjectType.
28+
*/
29+
protected materialize(context: TSPContext<Model>): GraphQLObjectType | GraphQLInputObjectType {
30+
const tspModel = context.type;
31+
const name = tspModel.name;
32+
33+
// Create InputObjectType for input usage, ObjectType for output
34+
if (context.usageFlag === UsageFlags.Input) {
35+
return this.materializeInputType(name, tspModel);
36+
}
37+
return this.materializeOutputType(name, tspModel);
38+
}
39+
40+
private materializeOutputType(name: string, tspModel: Model): GraphQLObjectType {
41+
const fields: GraphQLFieldConfigMap<unknown, unknown> = {};
42+
43+
for (const [propName, prop] of tspModel.properties) {
44+
fields[propName] = {
45+
type: this.mapPropertyType(prop.type),
46+
// TODO: Add description from doc comments
47+
};
48+
}
49+
50+
return new GraphQLObjectType({ name, fields });
51+
}
52+
53+
private materializeInputType(name: string, tspModel: Model): GraphQLInputObjectType {
54+
const fields: GraphQLInputFieldConfigMap = {};
55+
56+
for (const [propName, prop] of tspModel.properties) {
57+
fields[propName] = {
58+
type: this.mapPropertyType(prop.type),
59+
// TODO: Add description from doc comments
60+
};
61+
}
62+
63+
return new GraphQLInputObjectType({ name, fields });
64+
}
65+
66+
/**
67+
* Map a TypeSpec property type to a GraphQL String type.
68+
* TODO: Implement full type mapping with references to other registered types.
69+
*/
70+
private mapPropertyType(_type: unknown): typeof GraphQLString {
71+
// Placeholder - will need to resolve references to other types
72+
return GraphQLString;
73+
}
74+
}

0 commit comments

Comments
 (0)