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 = `
|
const defaultMedusaSchema = `
|
||||||
scalar DateTime
|
scalar DateTime
|
||||||
scalar JSON
|
scalar JSON
|
||||||
|
directive @enumValue(value: String) on ENUM_VALUE
|
||||||
`
|
`
|
||||||
const { schema: cleanedSchema, notFound } = GraphQLUtils.cleanGraphQLSchema(
|
const { schema: cleanedSchema, notFound } = GraphQLUtils.cleanGraphQLSchema(
|
||||||
defaultMedusaSchema + loadedSchema
|
defaultMedusaSchema + loadedSchema
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ describe("GraphQL builder", () => {
|
|||||||
isVerified: model.boolean(),
|
isVerified: model.boolean(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
enum DepartmentEnum {
|
||||||
|
FinanceDept = "finance",
|
||||||
|
MarketingDept = "marketing",
|
||||||
|
}
|
||||||
|
|
||||||
const user = model.define("user", {
|
const user = model.define("user", {
|
||||||
id: model.id(),
|
id: model.id(),
|
||||||
username: model.text(),
|
username: model.text(),
|
||||||
@@ -21,8 +26,9 @@ describe("GraphQL builder", () => {
|
|||||||
phones: model.array(),
|
phones: model.array(),
|
||||||
group: model.belongsTo(() => group, { mappedBy: "users" }),
|
group: model.belongsTo(() => group, { mappedBy: "users" }),
|
||||||
role: model
|
role: model
|
||||||
.enum(["moderator", "admin", "guest", "new_user"])
|
.enum(["moderator", "admin", "guest", "new-user"])
|
||||||
.default("guest"),
|
.default("guest"),
|
||||||
|
department: model.enum(DepartmentEnum),
|
||||||
tags: model.manyToMany(() => tag, {
|
tags: model.manyToMany(() => tag, {
|
||||||
pivotTable: "custom_user_tags",
|
pivotTable: "custom_user_tags",
|
||||||
}),
|
}),
|
||||||
@@ -38,6 +44,9 @@ describe("GraphQL builder", () => {
|
|||||||
const toGql = toGraphQLSchema([tag, email, user, group])
|
const toGql = toGraphQLSchema([tag, email, user, group])
|
||||||
|
|
||||||
const expected = `
|
const expected = `
|
||||||
|
scalar DateTime
|
||||||
|
scalar JSON
|
||||||
|
directive @enumValue(value: String) on ENUM_VALUE
|
||||||
type Tag {
|
type Tag {
|
||||||
id: ID!
|
id: ID!
|
||||||
value: String!
|
value: String!
|
||||||
@@ -59,10 +68,15 @@ describe("GraphQL builder", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum UserRoleEnum {
|
enum UserRoleEnum {
|
||||||
MODERATOR
|
MODERATOR @enumValue(value: "moderator")
|
||||||
ADMIN
|
ADMIN @enumValue(value: "admin")
|
||||||
GUEST
|
GUEST @enumValue(value: "guest")
|
||||||
NEW_USER
|
NEW_USER @enumValue(value: "new-user")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UserDepartmentEnum {
|
||||||
|
FINANCE @enumValue(value: "finance")
|
||||||
|
MARKETING @enumValue(value: "marketing")
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
@@ -74,6 +88,7 @@ describe("GraphQL builder", () => {
|
|||||||
group_id:String!
|
group_id:String!
|
||||||
group: Group!
|
group: Group!
|
||||||
role: UserRoleEnum!
|
role: UserRoleEnum!
|
||||||
|
department: UserDepartmentEnum!
|
||||||
tags: [Tag]!
|
tags: [Tag]!
|
||||||
raw_spend_limit: JSON!
|
raw_spend_limit: JSON!
|
||||||
created_at: DateTime!
|
created_at: DateTime!
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { PropertyType } from "@medusajs/types"
|
import type { PropertyType } from "@medusajs/types"
|
||||||
import { DmlEntity } from "../entity"
|
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 { 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>>(
|
export function generateGraphQLFromEntity<T extends DmlEntity<any, any>>(
|
||||||
entity: T
|
entity: T
|
||||||
@@ -82,5 +82,14 @@ export const toGraphQLSchema = <T extends any[]>(entities: T): string => {
|
|||||||
return entity
|
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
|
const enumValues = field.dataType
|
||||||
.options!.choices.map((value) => {
|
.options!.choices.map((value) => {
|
||||||
const enumValue = value.replace(/[^a-z0-9_]/gi, "_").toUpperCase()
|
const enumValue = value.replace(/[^a-z0-9_]/gi, "_").toUpperCase()
|
||||||
return ` ${enumValue}`
|
return ` ${enumValue} @enumValue(value: "${value}")`
|
||||||
})
|
})
|
||||||
.join("\n")
|
.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 { codegen } from "@graphql-codegen/core"
|
||||||
import * as typescriptPlugin from "@graphql-codegen/typescript"
|
import * as typescriptPlugin from "@graphql-codegen/typescript"
|
||||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
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"
|
import { FileSystem } from "../common"
|
||||||
|
|
||||||
function buildEntryPointsTypeMap({
|
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
|
// TODO: rename from gqlSchemaToTypes to grapthqlToTsTypes
|
||||||
export async function gqlSchemaToTypes({
|
export async function gqlSchemaToTypes({
|
||||||
schema,
|
schema,
|
||||||
@@ -118,9 +154,11 @@ export async function gqlSchemaToTypes({
|
|||||||
filename: "",
|
filename: "",
|
||||||
schema: parse(printSchema(schema as any)),
|
schema: parse(printSchema(schema as any)),
|
||||||
plugins: [
|
plugins: [
|
||||||
// Each plugin should be an object
|
|
||||||
{
|
{
|
||||||
typescript: {}, // Here you can pass configuration to the plugin
|
typescript: {
|
||||||
|
enumValues: getEnumValues(schema),
|
||||||
|
enumsAsTypes: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
pluginMap: {
|
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!
|
id: ID!
|
||||||
created_at: DateTime!
|
created_at: DateTime!
|
||||||
updated_at: DateTime!
|
updated_at: DateTime!
|
||||||
|
|||||||
@@ -1253,7 +1253,10 @@ export function buildSchemaObjectRepresentation(schema: string): {
|
|||||||
} as IndexTypes.SchemaObjectRepresentation
|
} as IndexTypes.SchemaObjectRepresentation
|
||||||
|
|
||||||
Object.entries(entitiesMap).forEach(([entityName, entityMapValue]) => {
|
Object.entries(entitiesMap).forEach(([entityName, entityMapValue]) => {
|
||||||
if (!entityMapValue.astNode) {
|
if (
|
||||||
|
!entityMapValue.astNode ||
|
||||||
|
entityMapValue.astNode.kind === GraphQLUtils.Kind.SCALAR_TYPE_DEFINITION
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user