OneGraph 和 Graphql Codegen 输出带有数值的枚举

OneGraph and Graphql Codegen outputs enums with numeric values

因此,我只是将项目的整个代码库从使用直接的 Headless Wordpress GraphQL 端点迁移到使用 OneGraph 以获得 Google+Facebook 业务支持。 OneGraph 是一个了不起的工具,实际上我打算在无头 WordPress 上编写的技术课程中使用它。无论如何,我收到这个 apollo 错误,它引用了输出代码生成器。这是错误:

  graphQLErrors: [
    {
      message: 'Variable "$idTypeFoot" got invalid value 2; Expected type MenuNodeIdTypeEnum.',
      path: [Array],
      extensions: [Object]
    },
    {
      message: 'Variable "$idTypeFoot" got invalid value 2; Expected type MenuNodeIdTypeEnum.',
      path: [Array],
      extensions: [Object]
    }
  ],

这是我的 generated/graphql.tsx 文件中的输出代码生成定义:


export enum WordpressMenuNodeIdTypeEnum {
    /** Identify a menu node by the Database ID. */
    DATABASE_ID = 0,
    /** Identify a menu node by the (hashed) Global ID. */
    ID = 1,
    /** Identify a menu node by it's name */
    NAME = 2
}

这是在将我的代码库迁移到 onegraph 之前相同枚举的先前定义:


/** The Type of Identifier used to fetch a single node. Default is "ID". To be used along with the "id" field. */
export enum MenuNodeIdTypeEnum {
    /** Identify a menu node by the Database ID. */
    DatabaseId = 'DATABASE_ID',
    /** Identify a menu node by the (hashed) Global ID. */
    Id = 'ID',
    /** Identify a menu node by it's name */
    Name = 'NAME'
}

这是父查询中使用的 dynamic-nav-fields.graphql 部分:

fragment DynamicNavFragment on WordpressMenuItem {
    id
    label
    path
    parentId
}

这里是 dynamic-nav.graphql 父查询:

# import DynamicNavFragment from './Partials/dynamic-nav-fields.graphql'

query DynamicNav(
  $idHead: ID!
  $idTypeHead: WordpressMenuNodeIdTypeEnum!
  $idFoot: ID!
  $idTypeFoot: WordpressMenuNodeIdTypeEnum!
    ) {
    Header: wordpress {
        menu(id: $idHead, idType: $idTypeHead) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                    childItems {
                                        edges {
                                            node {
                                                ...DynamicNavFragment
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    Footer: wordpress {
        menu(id: $idFoot, idType: idTypeFoot) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

这是我的 codegen.yml 配置文件:

overwrite: true
schema:
  ${WORDPRESS_API_URL_YML}:
    headers:
      Authorization: Bearer ${WORDPRESS_AUTH_REFRESH_TOKEN_YML}
documents: 'graphql/**/*.graphql'
generates:
  graphql/generated/graphql.tsx:
    plugins:
      - typescript:
          constEnums: false
          enumsAsTypes: false          
          numericEnums: true
          futureProofEnums: false
          enumsAsConst: false
          onlyOperationTypes: false
          maybeValue: T | null | undefined
          noExport: false
          enumPrefix: true
          fieldWrapperValue: T
          wrapFieldDefinitions: true
          skipTypename: false
          nonOptionalTypename: false
          useTypeImports: false
          avoidOptionals: true
          declarationKind: 
            input: interface
            type: interface
      - typescript-operations:
          declarationKind:
            input: interface
            type: interface
          avoidOptionals: true
          exportFragmentSpreadSubTypes: true
      - typescript-react-apollo:
          addDocBlocks: true
          reactApolloVersion: 3
          documentMode: documentNodeImportFragments
    config:
      maybeValue: T | null | undefined
      declarationKind:
        input: interface
        type: interface
      documentNodeImportFragments: true
      reactApolloVersion: 3
      withHooks: true
      withHOC: false
      avoidOptionals: true
      withComponent: false
      exportFragmentSpreadSubTypes: true
      addDocBlocks: true
  graphql/graphql.schema.graphql:
    plugins:
      - schema-ast
    config:
      commentDescriptions: true
  graphql/graphql.schema.json:
    plugins:
      - introspection
    config:
      commentDescriptions: true
      
hooks:
  afterAllFileWrite: 
    - prettier --write

为什么 OneGraph 将生成的 Enum 值替换为 apollo 客户端无法读取的数字?如果有人知道如何解决这个问题,请告诉我

更新 — 查看@sgrove 的回答,他很棒,帮我解决了问题!

好的,我有两个选择:(1) 覆盖每个枚举,该枚举是从 __type 之前 的代表字符串值转换而来的 (2) 对这些进行硬编码值。要获得自定义枚举值,我必须将以下内容添加到我的 codegen.yml:

config:
    enumValues:
       WordpressMenuNodeIdTypeEnum: '@/types/global-enums#WordpressMenuNodeIdTypeEnum'

指示生成的代码从@/types/global-enums.ts

导入这个枚举定义
/** The Type of Identifier used to fetch a single node. Default is "ID". To be used along with the "id" field. */
export enum WordpressMenuNodeIdTypeEnum {
    /** Identify a menu node by the Database ID. */
    DatabaseId = 'DATABASE_ID',
    /** Identify a menu node by the (hashed) Global ID. */
    Id = 'ID',
    /** Identify a menu node by it's name */
    Name = 'NAME'
}

然而,我随后 运行 进入另一个问题,并决定直接在我的 graphql 模式中硬编码 WordpressMenuNodeIdTypeEnum 的 NAME 值:

# import DynamicNavFragment from './Partials/dynamic-nav-fields.graphql'

query DynamicNav($idHead: ID!, $idFoot: ID!) {
    Header: wordpress {
        menu(id: $idHead, idType: NAME) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                    childItems {
                                        edges {
                                            node {
                                                ...DynamicNavFragment
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    Footer: wordpress {
        menu(id: $idFoot, idType: NAME) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

然后我 运行 遇到了 apollo 缓存的另一个问题,因为顶级 ServicesTopQuery 和 DynamicNavQuery 都有一个共享的顶级 wordpress 字段和相应的 __typename " WordPressRootQuery..

ServicesTopQueryServicesTopQueryVariables 的代码生成定义:

export type ServicesTopQueryVariables = Exact<{
    popular: Scalars['String'];
    other: Scalars['String'];
}>;

export type ServicesTopQuery = { __typename?: 'Query' } & {
    popular: Maybe<
        { __typename?: 'WordpressRootQuery' } & {
            services: Maybe<
                { __typename?: 'WordpressRootQueryToServiceConnection' } & {
                    edges: Maybe<
                        Array<
                            Maybe<
                                {
                                    __typename?: 'WordpressRootQueryToServiceConnectionEdge';
                                } & {
                                    node: Maybe<
                                        {
                                            __typename?: 'WordpressService';
                                        } & ServiceFieldsTopFragment
                                    >;
                                }
                            >
                        >
                    >;
                }
            >;
        }
    >;
    other: Maybe<
        { __typename?: 'WordpressRootQuery' } & {
            services: Maybe<
                { __typename?: 'WordpressRootQueryToServiceConnection' } & {
                    edges: Maybe<
                        Array<
                            Maybe<
                                {
                                    __typename?: 'WordpressRootQueryToServiceConnectionEdge';
                                } & {
                                    node: Maybe<
                                        {
                                            __typename?: 'WordpressService';
                                        } & ServiceFieldsTopFragment
                                    >;
                                }
                            >
                        >
                    >;
                }
            >;
        }
    >;
};

DynamicNavQueryDynamicNavQueryVariables 的代码生成定义:


export type DynamicNavQueryVariables = Exact<{
    idHead: Scalars['ID'];
    idFoot: Scalars['ID'];
}>;

export type DynamicNavQuery = { __typename?: 'Query' } & {
    Header: Maybe<
        { __typename?: 'WordpressRootQuery' } & {
            menu: Maybe<
                { __typename?: 'WordpressMenu' } & {
                    menuItems: Maybe<
                        { __typename?: 'WordpressMenuToMenuItemConnection' } & {
                            edges: Maybe<
                                Array<
                                    Maybe<
                                        {
                                            __typename?: 'WordpressMenuToMenuItemConnectionEdge';
                                        } & {
                                            node: Maybe<
                                                { __typename?: 'WordpressMenuItem' } & {
                                                    childItems: Maybe<
                                                        {
                                                            __typename?: 'WordpressMenuItemToMenuItemConnection';
                                                        } & {
                                                            edges: Maybe<
                                                                Array<
                                                                    Maybe<
                                                                        {
                                                                            __typename?: 'WordpressMenuItemToMenuItemConnectionEdge';
                                                                        } & {
                                                                            node: Maybe<
                                                                                { __typename?: 'WordpressMenuItem' } & {
                                                                                    childItems: Maybe<
                                                                                        {
                                                                                            __typename?: 'WordpressMenuItemToMenuItemConnection';
                                                                                        } & {
                                                                                            edges: Maybe<
                                                                                                Array<
                                                                                                    Maybe<
                                                                                                        {
                                                                                                            __typename?: 'WordpressMenuItemToMenuItemConnectionEdge';
                                                                                                        } & {
                                                                                                            node: Maybe<
                                                                                                                {
                                                                                                                    __typename?: 'WordpressMenuItem';
                                                                                                                } & DynamicNavFragmentFragment
                                                                                                            >;
                                                                                                        }
                                                                                                    >
                                                                                                >
                                                                                            >;
                                                                                        }
                                                                                    >;
                                                                                } & DynamicNavFragmentFragment
                                                                            >;
                                                                        }
                                                                    >
                                                                >
                                                            >;
                                                        }
                                                    >;
                                                } & DynamicNavFragmentFragment
                                            >;
                                        }
                                    >
                                >
                            >;
                        }
                    >;
                }
            >;
        }
    >;
    Footer: Maybe<
        { __typename?: 'WordpressRootQuery' } & {
            menu: Maybe<
                { __typename?: 'WordpressMenu' } & {
                    menuItems: Maybe<
                        { __typename?: 'WordpressMenuToMenuItemConnection' } & {
                            edges: Maybe<
                                Array<
                                    Maybe<
                                        {
                                            __typename?: 'WordpressMenuToMenuItemConnectionEdge';
                                        } & {
                                            node: Maybe<
                                                { __typename?: 'WordpressMenuItem' } & {
                                                    childItems: Maybe<
                                                        {
                                                            __typename?: 'WordpressMenuItemToMenuItemConnection';
                                                        } & {
                                                            edges: Maybe<
                                                                Array<
                                                                    Maybe<
                                                                        {
                                                                            __typename?: 'WordpressMenuItemToMenuItemConnectionEdge';
                                                                        } & {
                                                                            node: Maybe<
                                                                                {
                                                                                    __typename?: 'WordpressMenuItem';
                                                                                } & DynamicNavFragmentFragment
                                                                            >;
                                                                        }
                                                                    >
                                                                >
                                                            >;
                                                        }
                                                    >;
                                                } & DynamicNavFragmentFragment
                                            >;
                                        }
                                    >
                                >
                            >;
                        }
                    >;
                }
            >;
        }
    >;
};

来自 pages/index.tsx 的代码,其中最初填充了 DynamicNavQuery 数据,然后迅速被 ServicesTopQuery 数据覆盖

import { Container } from '@/components/UI';
import { initializeApollo, addApolloState } from '@/lib/apollo';
import { LandingHero } from '@/components/Landing';
import ServiceTopCoalesced from '@/components/ServicesTop/services-top-coalesced';
import AppLayout from '@/components/Layout/layout';
import {
    GetStaticPropsContext,
    GetStaticPropsResult,
    InferGetStaticPropsType
} from 'next';
import {
    ServicesTopQuery,
    ServicesTopQueryVariables,
    ServicesTopDocument,
    DynamicNavDocument,
    DynamicNavQueryVariables,
    DynamicNavQuery
} from '@/graphql/generated/graphql';

export function Index({
    other,
    popular,
    Header,
    Footer
}: InferGetStaticPropsType<typeof getStaticProps>) {
    return (
        <>
            <AppLayout
                title={'✂ The Fade Room Inc. ✂'}
                Header={Header}
                Footer={Footer}
                hero={<LandingHero />}
            >
                <Container clean className='fit'>
                    <ServiceTopCoalesced other={other} popular={popular} />
                </Container>
            </AppLayout>
        </>
    );
}

export async function getStaticProps(
    ctx: GetStaticPropsContext
): Promise<
    GetStaticPropsResult<{
        other: ServicesTopQuery['other'];
        popular: ServicesTopQuery['popular'];
        Header: DynamicNavQuery['Header'];
        Footer: DynamicNavQuery['Footer'];
    }>
> {
    const apolloClient = initializeApollo();
    await apolloClient.query<
        DynamicNavQuery,
        DynamicNavQueryVariables
    >({
        query: DynamicNavDocument,
        variables: {
            idHead: 'Header',
            idFoot: 'Footer'
        }
    });

    await apolloClient.query<
        ServicesTopQuery,
        ServicesTopQueryVariables
    >({
        query: ServicesTopDocument,
        variables: {
            other: 'all',
            popular: 'popular-service'
        }
    });
    return addApolloState(apolloClient, {
        props: {},
        revalidate: 10
    });
}
export default Index;

DynamicNavQuery 数据已按预期填充,然后我的后备加载组件在初始 DOM 渲染后渲染了四分之一秒左右,然后 DynamicNavQuery 数据消失并填充了 ServicesTopQuery 数据。因此,现有的缓存数据被传入数据覆盖,在 apollo 客户端缓存中共享相同的顶级字段 wordpress 和 __typename WordPressRootQuery。为了纠正这种行为,我查阅了 apollo 文档并使用以下内容更新了 new InMemoryCache class 的实例化:

cache: new InMemoryCache({
            typePolicies: {
                Query: {
                    fields: {
                        wordpress: {
                            merge(existing, incoming, { mergeObjects }) {
                                // invoking nested merge functions
                                return mergeObjects(existing, incoming);
                            }
                        }
                    }
                }
            }
        })
    });

以上内容阻止了现有的 wordpress 字段数据被传入的 wordpress 字段数据覆盖,从而解决了该问题。从无头 WordPress 端点迁移到 OneGraph 端点的过程证明比最初预期的要麻烦,所以我想我会跟进一个详细的响应,以防有人在路上遇到类似的问题。

tldr;将 codegen.yml 更改为使用 numericEnums: false:

generates:
  graphql/generated/graphql.tsx:
    plugins:
      - typescript:
          numericEnums: false

为了追踪这一点,我采用了 WordpressMenuNodeIdTypeEnum 的 GraphQL SDL 定义:

enum WordpressMenuNodeIdTypeEnum {
    # Identify a menu node by the Database ID.
    DATABASE_ID
    # Identify a menu node by the (hashed) Global ID.
    ID
    # Identify a menu node by it's name
    NAME
}

连同之前在 codegen.yml 中的 numericEnums: true 设置,并在 https://www.graphql-code-generator.com 的操场上使用了它们

如您在屏幕截图中所见,SDL 定义被编译为数字枚举(运行时 typescript 枚举的标准)

然后我用 numericEnums: false 进行了测试:

并且该输出看起来像您期望的那样,其中 WordpressMenuNodeIdTypeEnum.Name 等于 "NAME",因此 Apollo 获得预期值而不是运行时打字稿枚举值(2).