Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Cannot accept/send array using input type / input plugin #1269

Open
SpeedySH opened this issue Aug 4, 2024 · 4 comments
Open

[BUG] Cannot accept/send array using input type / input plugin #1269

SpeedySH opened this issue Aug 4, 2024 · 4 comments

Comments

@SpeedySH
Copy link

SpeedySH commented Aug 4, 2024

I don't know why, but if you make an inputType that is an array (list) the input plugin just ignores it, expecting a keyless object :)

Even if you specify array literals directly in the mutation, it still, converts that to an object, for some unknown reason

Even when trying to do a validation scheme, it doesn't like it. Below is the full code.

And yes, I can choose not to send an array, and the input plugin is fine with that, although it's very strange.

Mutation:

mutation MyMutation {
  createProject(
    input: {name: "", rootDirectory: "", url: "", environmentVariables: {createMany: {skipDuplicates: false, data: [{key: "ss", value: "ss", environments: [PRODUCTION]}]}}, preset: {connect: {id: 1}}}
  ) {
    ... on ValidationError {
      __typename
      code
      errors {
        codeDescription
        code
        isFatal
        path
        systemCode
      }
      message
      timestamp
    }
  }
}
const EnvironmentVariable = builder.prismaObject("EnvironmentVariable", {
  fields: (t) => ({
    id: t.exposeID("id"),
    key: t.exposeString("key"),
    value: t.exposeString("value"),
    isSensitive: t.exposeBoolean("isSensitive"),
    environments: t.exposeStringList("environments"),
  }),
});

const EnvironmentManyVariableCreateInput = builder.prismaCreateMany("EnvironmentVariable", {
  name: "EnvironmentManyVariableCreateInput",
  fields: (t) => ({
    key: t.string({ required: true }),
    value: t.string({ required: true }),
    isSensitive: t.boolean(),
    environments: t.field({
      type: [EnvironmentEnum],
    }),
  }),
});

const EnvironmentManyVariableCreateInputData = builder.inputType("EnvironmentManyVariableCreateInputData", {
  fields: (t) => ({
    data: t.field({ type: [EnvironmentManyVariableCreateInput], required: true }),
    skipDuplicates: t.boolean(),
  }),
});

const EnvironmentManyVariableCreate = builder.inputType("EnvironmentManyVariableCreate", {
  fields: (t) => ({
    createMany: t.field({ type: EnvironmentManyVariableCreateInputData }),
  }),
});
Invalid `prisma.project.create()` invocation in
E:\project\Project.initialization.ts:356:44

  353 
  354 console.log(environmentVariables);
  355 
→ 356 const project = await prisma.project.create({
        data: {
          preset: {
            connect: {
              id: 1
            }
          },
          environmentVariables: {
            createMany: {
              data: {
                0: {
                  key: "ss",
                  value: "ss",
                  environments: {
                    0: "PRODUCTION"
                  }
                },
      +         key: String
              }
            }
          },
          name: "",
          rootDirectory: "",
          url: "",
          customPreset: undefined,
          ownerTeam: undefined,
          ownerUser: {
            connect: {
              id: 1
            }
          }
        }
      })

Argument `key` is missing.
builder.mutationField("createProject", (t) =>
  t.prismaFieldWithInput({
    type: Project,
    errors: {},
    authScopes: {
      isAuthorised: true,
    },
    input: {
      name: t.input.string({ required: true }),
      url: t.input.string({ required: true }),
      rootDirectory: t.input.string({ required: true }),
      preset: t.input.field({
        type: PresetConnect,
        required: true,
      }),
      customPreset: t.input.field({ type: CustomPresetCreate }),
      ownerTeam: t.input.field({ type: TeamConnect }),
      environmentVariables: t.input.field({
        type: EnvironmentManyVariableCreate,
        validate: {
          schema: z.object({
            createMany: z.object({
              data: z.array(
                z.object({
                  key: z.string(),
                  value: z.string(),
                  isSensitive: z.boolean().optional(),
                  environments: z.array(z.nativeEnum(Environment)), // ERROR, see below
                }),
              ),
              removeDuplicates: z.boolean().optional(),
            }),
          }),
        },
      }),
    },
Type "{ schema: z.ZodObject<{ createMany: z.ZodObject<{ data: z.ZodArray<z.ZodObject<{ key: z.ZodString; value: z.ZodString; isSensitive: z.ZodOptional<z.ZodBoolean>; environments: z.ZodArray<z. ZodNativeEnum<{ DEVELOPMENT: "DEVELOPMENT"; PRODUCTION: "PRODUCTION"; PREVIEW: "PREVIEW"; }>, "many">; }, "strip", z.ZodTypeAny, ..." cannot be assigned to type "ValidationOptions<{ createMany? { skipDuplicates?: boolean | null | undefined; data: { value: string; id: number; teamId: number | null; projectId: number | null; projectId: number | null; key: string; isSensitive: boolean; environments: environment[]; }[]; } | null | undefined; }> | undefined".
  The types "schema._type.createMany.data" are incompatible between these types.
    The type "{ value: string; key: string; environments: ("DEVELOPMENT" | "PRODUCTION" | "PREVIEW")[]; isSensitive?: boolean | undefined; }[]" cannot be assigned to the type "{ value: string; id: number; teamId: number | null; projectId: number | null; key: string; isSensitive: boolean; environments: Environment[]; }[]".
      The following properties from the type "{ value: string; key: string; environments: ("DEVELOPMENT" | "PRODUCTION" | "PREVIEW")[]; isSensitive?: boolean | undefined; }" are missing from the type "{ value: string; id: number; teamId: number | null; projectId: number | null; key: string; isSensitive: boolean; environments: Environment[]; }": id, teamId, projectIdts(2322).
global-types.d.ts(27, 13): The expected type comes from the "validate" property declared here in type "InputObjectFieldOptions<ExtendDefaultTypes<SchemaBuilderTypes>, InputObjectRef<ExtendDefaultTypes<SchemaBuilderTypes>, { ...; }>, FieldRequiredness<...>>"
@SpeedySH SpeedySH changed the title [BUG] [BUG] Cannot accept/send array using input type / input plugin Aug 4, 2024
@hayes
Copy link
Owner

hayes commented Aug 4, 2024

Not sure whats going on here, can you share the generated GraphQL schema.

Part of the issue with types is that the prisma utils plugin claims to return the full create input with the required relation properties, but the validation schema can't validate that (since they aren't actually part of the input). Working around this would require significantly better type inference for the prisma input fields, which isn't likely to be added soon.

I am not clear on whats happening with the runtime error.

Is the issue that the input is being converted from an array to an object, or is the prisma error just rendering it that way?

the input plugin just ignores it, expecting a keyless object :)

I don't know what this means, what is the input plugin, and where is it "expecting" and keyless object?

having the GraphQL schema output, and the JSON version of the input args received in the resolver might help clarify what the issue is here

@SpeedySH
Copy link
Author

SpeedySH commented Aug 4, 2024

UP

The problem is partially solved, and it was my mistake.

  1. Changing type from array to object [SOLVED]
  2. You can send an object instead of an array and it will not be considered an error. Although it is specifically described in inputType that only an array is expected.
  3. It is impossible to validate such an input without an error using zod, because it writes that wrong fields/incorrect types .

As I understand point 3 is difficult to fix now, because of the complexity of type casting.

@hayes
Copy link
Owner

hayes commented Aug 7, 2024

Changing type from array to object [SOLVED]

Glad you got this figured out

You can send an object instead of an array and it will not be considered an error. Although it is specifically described in inputType that only an array is expected.

If I am understanding this correctly, I think it might be a GraphQL issue, GraphQL automatically allows list inputs to be passed as an object, which it will treat as an list with 1 item, but it also shouldn't cause any issues

It is impossible to validate such an input without an error using zod, because it writes that wrong fields/incorrect types .

I think impossible might be an exaggeration here, I think if you do validate: { schema: { ... } as {} } it will avoid the type errors, but getting complete type-safety would be challenging.

@hayes
Copy link
Owner

hayes commented Aug 7, 2024

createMany is tricky to use correctly. I think you are better of using create which can create multiple relations:

const CreateUserInput = builder.prismaCreate('User', {
  fields: () => ({
    email: 'String',
    name: 'String',
    posts: CreateUserPostsInput,
  }),
});

const CreateUserPostsInput = builder.prismaCreateRelation('User', 'posts', {
  fields: () => ({
    create: CreateUserPostInput,
  }),
});

const CreateUserPostInput = builder.prismaCreate('Post', {
  name: 'CreateUserPostsInput',
  fields: () => ({
    title: 'String',
  }),
});

builder.mutationType({
  fields: (t) => ({
    createUser: t.prismaField({
      type: 'User',
      args: {
        data: t.arg({ type: CreateUserInput, required: true }),
      },
      resolve: (query, _, args) => prisma.user.create({ ...query, data: args.data }),
    }),
  }
})
mutation {
  createUser(data: { 
    name: "test"
    email: "test@test.com"
    posts: {
      create: [{
        title: "post 1"
      }, {
        title: "post 2"
      }]
    }
  }) {
    id
    posts {
      title
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants