Merge pull request #12684 from medusajs/pedro/keep-enum-values-in-types-generation
fix: keep enum values in types generation fix: generate union types instead of enums
This commit is contained in:
@@ -234,6 +234,7 @@ function cleanAndMergeSchema(loadedSchema) {
|
||||
const defaultMedusaSchema = `
|
||||
scalar DateTime
|
||||
scalar JSON
|
||||
directive @enumValue(value: String) on ENUM_VALUE
|
||||
`
|
||||
const { schema: cleanedSchema, notFound } = GraphQLUtils.cleanGraphQLSchema(
|
||||
defaultMedusaSchema + loadedSchema
|
||||
|
||||
@@ -13,6 +13,11 @@ describe("GraphQL builder", () => {
|
||||
isVerified: model.boolean(),
|
||||
})
|
||||
|
||||
enum DepartmentEnum {
|
||||
FinanceDept = "finance",
|
||||
MarketingDept = "marketing",
|
||||
}
|
||||
|
||||
const user = model.define("user", {
|
||||
id: model.id(),
|
||||
username: model.text(),
|
||||
@@ -21,8 +26,9 @@ describe("GraphQL builder", () => {
|
||||
phones: model.array(),
|
||||
group: model.belongsTo(() => group, { mappedBy: "users" }),
|
||||
role: model
|
||||
.enum(["moderator", "admin", "guest", "new_user"])
|
||||
.enum(["moderator", "admin", "guest", "new-user"])
|
||||
.default("guest"),
|
||||
department: model.enum(DepartmentEnum),
|
||||
tags: model.manyToMany(() => tag, {
|
||||
pivotTable: "custom_user_tags",
|
||||
}),
|
||||
@@ -38,6 +44,9 @@ describe("GraphQL builder", () => {
|
||||
const toGql = toGraphQLSchema([tag, email, user, group])
|
||||
|
||||
const expected = `
|
||||
scalar DateTime
|
||||
scalar JSON
|
||||
directive @enumValue(value: String) on ENUM_VALUE
|
||||
type Tag {
|
||||
id: ID!
|
||||
value: String!
|
||||
@@ -59,10 +68,15 @@ describe("GraphQL builder", () => {
|
||||
}
|
||||
|
||||
enum UserRoleEnum {
|
||||
MODERATOR
|
||||
ADMIN
|
||||
GUEST
|
||||
NEW_USER
|
||||
MODERATOR @enumValue(value: "moderator")
|
||||
ADMIN @enumValue(value: "admin")
|
||||
GUEST @enumValue(value: "guest")
|
||||
NEW_USER @enumValue(value: "new-user")
|
||||
}
|
||||
|
||||
enum UserDepartmentEnum {
|
||||
FINANCE @enumValue(value: "finance")
|
||||
MARKETING @enumValue(value: "marketing")
|
||||
}
|
||||
|
||||
type User {
|
||||
@@ -74,6 +88,7 @@ describe("GraphQL builder", () => {
|
||||
group_id:String!
|
||||
group: Group!
|
||||
role: UserRoleEnum!
|
||||
department: UserDepartmentEnum!
|
||||
tags: [Tag]!
|
||||
raw_spend_limit: JSON!
|
||||
created_at: DateTime!
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { PropertyType } from "@medusajs/types"
|
||||
import { DmlEntity } from "../entity"
|
||||
import { parseEntityName } from "./entity-builder/parse-entity-name"
|
||||
import { setGraphQLRelationship } from "./graphql-builder/set-relationship"
|
||||
import { getGraphQLAttributeFromDMLPropety } from "./graphql-builder/get-attribute"
|
||||
import { getForeignKey } from "./entity-builder"
|
||||
import { parseEntityName } from "./entity-builder/parse-entity-name"
|
||||
import { getGraphQLAttributeFromDMLPropety } from "./graphql-builder/get-attribute"
|
||||
import { setGraphQLRelationship } from "./graphql-builder/set-relationship"
|
||||
|
||||
export function generateGraphQLFromEntity<T extends DmlEntity<any, any>>(
|
||||
entity: T
|
||||
@@ -82,5 +82,14 @@ export const toGraphQLSchema = <T extends any[]>(entities: T): string => {
|
||||
return entity
|
||||
})
|
||||
|
||||
return gqlSchemas.join("\n")
|
||||
const defaultMedusaSchema =
|
||||
gqlSchemas.length > 0
|
||||
? `
|
||||
scalar DateTime
|
||||
scalar JSON
|
||||
directive @enumValue(value: String) on ENUM_VALUE
|
||||
`
|
||||
: ""
|
||||
|
||||
return defaultMedusaSchema + gqlSchemas.join("\n")
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export function getGraphQLAttributeFromDMLPropety(
|
||||
const enumValues = field.dataType
|
||||
.options!.choices.map((value) => {
|
||||
const enumValue = value.replace(/[^a-z0-9_]/gi, "_").toUpperCase()
|
||||
return ` ${enumValue}`
|
||||
return ` ${enumValue} @enumValue(value: "${value}")`
|
||||
})
|
||||
.join("\n")
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.test-output
|
||||
@@ -0,0 +1,65 @@
|
||||
import { makeExecutableSchema } from "@graphql-tools/schema"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import { gqlSchemaToTypes } from "../graphql-to-ts-types"
|
||||
|
||||
describe("gqlSchemaToTypes", () => {
|
||||
it("should use enumValue directive for enum values", async () => {
|
||||
const schema = `
|
||||
directive @enumValue(value: String) on ENUM_VALUE
|
||||
|
||||
enum Test {
|
||||
Test_A @enumValue(value: "test-a")
|
||||
Test_B
|
||||
}
|
||||
`
|
||||
|
||||
const executableSchema = makeExecutableSchema({
|
||||
typeDefs: schema,
|
||||
})
|
||||
|
||||
await gqlSchemaToTypes({
|
||||
schema: executableSchema,
|
||||
outputDir: path.resolve(__dirname, ".test-output/enum-values"),
|
||||
filename: "query-entry-points",
|
||||
joinerConfigs: [],
|
||||
interfaceName: "RemoteQueryEntryPoints",
|
||||
})
|
||||
|
||||
const expectedTypes = `
|
||||
import "@medusajs/framework/types"
|
||||
export type Maybe<T> = T | null;
|
||||
export type InputMaybe<T> = Maybe<T>;
|
||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
||||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
||||
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
|
||||
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
|
||||
/** All built-in and custom scalars, mapped to their actual values */
|
||||
export type Scalars = {
|
||||
ID: { input: string; output: string; }
|
||||
String: { input: string; output: string; }
|
||||
Boolean: { input: boolean; output: boolean; }
|
||||
Int: { input: number; output: number; }
|
||||
Float: { input: number; output: number; }
|
||||
};
|
||||
|
||||
export type Test = | 'test-a' | 'Test_B';
|
||||
|
||||
declare module '@medusajs/framework/types' {
|
||||
interface RemoteQueryEntryPoints {
|
||||
|
||||
}
|
||||
}`
|
||||
|
||||
const fileName = ".test-output/enum-values/query-entry-points.d.ts"
|
||||
const generatedTypes = fs
|
||||
.readFileSync(path.resolve(__dirname, fileName))
|
||||
.toString()
|
||||
expect(normalizeFile(generatedTypes)).toEqual(normalizeFile(expectedTypes))
|
||||
})
|
||||
})
|
||||
|
||||
const normalizeFile = (file: string) => {
|
||||
return file.replace(/^\s+/g, "").replace(/\s+/g, " ").trim()
|
||||
}
|
||||
@@ -1,7 +1,14 @@
|
||||
import { codegen } from "@graphql-codegen/core"
|
||||
import * as typescriptPlugin from "@graphql-codegen/typescript"
|
||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||
import { type GraphQLSchema, parse, printSchema } from "graphql"
|
||||
import {
|
||||
EnumTypeDefinitionNode,
|
||||
EnumValueDefinitionNode,
|
||||
type GraphQLSchema,
|
||||
Kind,
|
||||
parse,
|
||||
printSchema,
|
||||
} from "graphql"
|
||||
import { FileSystem } from "../common"
|
||||
|
||||
function buildEntryPointsTypeMap({
|
||||
@@ -87,6 +94,35 @@ ${entryPoints
|
||||
}
|
||||
}
|
||||
|
||||
const getEnumValues = (schema: GraphQLSchema) => {
|
||||
const enumTypes = Object.values(schema.getTypeMap()).filter(
|
||||
(type) => type.astNode?.kind === Kind.ENUM_TYPE_DEFINITION
|
||||
)
|
||||
|
||||
const enumValues = {}
|
||||
enumTypes.forEach((type) => {
|
||||
const enumName = type.name
|
||||
enumValues[enumName] = {}
|
||||
|
||||
const nodes = (type.astNode as EnumTypeDefinitionNode).values || []
|
||||
nodes.forEach((node: EnumValueDefinitionNode) => {
|
||||
const directive = node.directives?.find(
|
||||
(d) => d.name.value === "enumValue"
|
||||
)
|
||||
if (directive) {
|
||||
const valueArg = directive.arguments?.find(
|
||||
(a) => a.name.value === "value"
|
||||
)
|
||||
if (valueArg && valueArg.value.kind === Kind.STRING) {
|
||||
enumValues[enumName][node.name.value] = valueArg.value.value
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return enumValues
|
||||
}
|
||||
|
||||
// TODO: rename from gqlSchemaToTypes to grapthqlToTsTypes
|
||||
export async function gqlSchemaToTypes({
|
||||
schema,
|
||||
@@ -118,9 +154,11 @@ export async function gqlSchemaToTypes({
|
||||
filename: "",
|
||||
schema: parse(printSchema(schema as any)),
|
||||
plugins: [
|
||||
// Each plugin should be an object
|
||||
{
|
||||
typescript: {}, // Here you can pass configuration to the plugin
|
||||
typescript: {
|
||||
enumValues: getEnumValues(schema),
|
||||
enumsAsTypes: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
pluginMap: {
|
||||
|
||||
@@ -405,7 +405,10 @@ describe("joiner-config-builder", () => {
|
||||
],
|
||||
})
|
||||
|
||||
const schemaExpected = `type FulfillmentSet {
|
||||
const schemaExpected = `scalar DateTime
|
||||
scalar JSON
|
||||
directive @enumValue(value: String) on ENUM_VALUE
|
||||
type FulfillmentSet {
|
||||
id: ID!
|
||||
created_at: DateTime!
|
||||
updated_at: DateTime!
|
||||
|
||||
@@ -1253,7 +1253,10 @@ export function buildSchemaObjectRepresentation(schema: string): {
|
||||
} as IndexTypes.SchemaObjectRepresentation
|
||||
|
||||
Object.entries(entitiesMap).forEach(([entityName, entityMapValue]) => {
|
||||
if (!entityMapValue.astNode) {
|
||||
if (
|
||||
!entityMapValue.astNode ||
|
||||
entityMapValue.astNode.kind === GraphQLUtils.Kind.SCALAR_TYPE_DEFINITION
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user