-
-
Notifications
You must be signed in to change notification settings - Fork 676
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
Nested input types are not deserialized into class instances #133
Comments
Unfortunately, I can confirm that. I've added proper test cases for that on the branch. This bug applies also to arrays and nested inputs inside args type classes. It might also show up in nested At the beginning I've used But looks like even @ArgsType()
class SampleNestedArgs {
@Field()
factor: number;
@Field()
@Type(() => SampleInput) // without this, `input.constructor` is `Object`
input: SampleInput;
} As it applies to input type classes, the solution might be to load the input type class metadata during transformation and check for nested fields and then call it recursive. But using |
Thank you for such a quick reaction. I am beginning to use your library so I am not yet familiar with all it's internals. What exactly do you see as a problem when using |
Basically, after a dozen of new features current architecture is getting very messy. I've designed it with no problems like this in mind, so it's now the bottleneck. Generally, now decorators just collect simple metadata and put them in So after this refactor, I would have access to the info about the input type field in handler's params metadata, that are passed from the schema generator down to the convert type helper. No global access or finding in arrays for data 😉 So this may be related to #123 as it also rely on the change of the metadata info format. This would also simplify the schema generation step that now have to looks for super classes metadata and other things. |
I fall into this issue too. No metadata found. There is more than once class-validator version installed probably. You need to flatten your dependencies Is it related, and will it be fixed by your current work on this issue ? |
See #150 - if you don't use validation, just disable it 😉 |
@19majkel94 What is the status here? Do you need any help? |
It's generally blocked by #183 - I need to rewrite the args parsing phase to have more metadata info needed to deeply transform the nested object. |
Hey @19majkel94, what are the problems you ran into with |
If you can't wait for a proper fix, feel free to fork and replace this line of |
@19majkel94 this is precisely what I am doing, I guess this is my best option until a proper fix then 😄 |
The workaround does not work if using an abstract class ResourceResolver as in the example https://github.com/19majkel94/type-graphql/blob/d15d1317065da5256f2eb8b2fd9f2e51bb933497/examples/resolvers-inheritance/resource/resource.resolver.ts#L36 The Abstract Resolver class knowns by parameter the Class Type due the given parameter Did I miss something? |
Would it be possible to make the Class type available in the |
I got your point. Do you have any suggestion which way I should go? |
Why not combine decorators, for example: import { Type } from "class-transformer";
import { ValidateNested } from "class-validator";
import { ClassType, Field } from "type-graphql";
import {
AdvancedOptions,
MethodAndPropDecorator
} from "type-graphql/dist/decorators/types";
export type ReturnTypeFunc = (returns?: undefined) => Function | ClassType;
export function NestedField(
returnTypeFunction?: ReturnTypeFunc,
options?: AdvancedOptions
): MethodAndPropDecorator;
export function NestedField(
returnTypeFunction: ReturnTypeFunc,
options?: AdvancedOptions
): MethodDecorator | PropertyDecorator {
const fieldFn = Field(returnTypeFunction, options);
const typeFn = Type(returnTypeFunction);
const validateNestedFn = ValidateNested();
return (target, propertyKey, descriptor) => {
fieldFn(target, propertyKey, descriptor);
typeFn(target, propertyKey as string);
validateNestedFn(target, propertyKey as string);
};
} @InputType()
export class NestedTestInputType {
@Field()
foo: number
}
@InputType()
export class TestInputType {
@NestedField((type) => NestedTestInputType)
nested: NestedTestInputType
} |
Hi!
I've added Type decorator, but without expecting results (constructor = Object). |
Has there been any progress on this issue? One of my classes relies on a nested array of an object. I have tried the majority of the fixes here although I'm not sure what there is to do. Is there anything that we as the community can do to assist in the completion of this ticket? |
@bpofficial |
I didn't want to fork and maintain my own version. So I cooked a Here it is the code, hope it helps, hope @19majkel94 's eyes will not bleed. 😄 import { plainToClass } from "class-transformer";
import { Args, createParamDecorator, SymbolKeysNotSupportedError } from "type-graphql";
import { findType } from "type-graphql/dist/helpers/findType";
import { ValidateOptions, ReturnTypeFunc } from "type-graphql/dist/decorators/types";
import { getTypeDecoratorParams } from "type-graphql/dist/helpers/decorators";
import { validateArg } from "type-graphql/dist/resolvers/validate-arg";
export function TypedArgs(): ParameterDecorator;
export function TypedArgs(options: ValidateOptions): ParameterDecorator;
export function TypedArgs(
paramTypeFunction: ReturnTypeFunc,
options?: ValidateOptions,
): ParameterDecorator;
export function TypedArgs(
paramTypeFnOrOptions?: ReturnTypeFunc | ValidateOptions,
maybeOptions?: ValidateOptions,
): ParameterDecorator {
const { options, returnTypeFunc } = getTypeDecoratorParams(paramTypeFnOrOptions, maybeOptions);
return (prototype, propertyKey, parameterIndex) => {
if (typeof propertyKey === "symbol") {
throw new SymbolKeysNotSupportedError();
}
const { getType } = findType({
metadataKey: 'design:paramtypes',
prototype,
propertyKey,
parameterIndex,
returnTypeFunc,
})
const paramDecoratorFunc = createParamDecorator(async ({ args }) => {
const typedArgs = plainToClass(getType() as any, args)
return await validateArg(typedArgs, true, options.validate)
})
const argsOptions = { ...options, validate: false }
const argsFunc = returnTypeFunc ? Args(returnTypeFunc, argsOptions) : Args(argsOptions)
paramDecoratorFunc(prototype, propertyKey, parameterIndex)
argsFunc(prototype, propertyKey, parameterIndex)
};
} |
maybe this will help someone, i just created decorator import { plainToClass, ValidateNested } from 'class-validator';
import { Field } from 'type-graphql';
export function Nested() {
return (target: any, propertyName: string) => {
const Ctx = Reflect.getMetadata('design:type', target.constructor.prototype, propertyName);
Field(() => Ctx)(target, propertyName);
ValidateNested()(target, propertyName);
Object.defineProperty(target.constructor.prototype, propertyName, {
get() {
return this[`__${propertyName}`];
},
set(value: any) {
this[`__${propertyName}`] = plainToClass(Ctx, value);
}
});
};
} @InputType()
export class UserActivateDTO {
@Length(31, 32)
@Field()
public hash: string;
@Length(6, 256)
@Field()
public password: string;
@IsDateString()
@Field()
public bDay: string;
@Field()
@IsEmail()
public email: string;
@Nested()
public restore: UserRestoreDTO
} |
@MichalLytek this is kind of disappointing... so many people are probably using class validations with nested inputs expecting them to work. After a year of use in production, I just found out that a lot of internal apps don't ever validate nested inputs.... My typings are completely broken as well because nested inputs aren't actually what I would expect them to be. |
@j i know you might not intend it but your comment comes off as a little chippy. Keep in mind this is a free open source project @michaellytek has given us and i for one am very thankful for all he’s done. If we’re nice to him maybe he’ll keep it up. Try to be positive and contribute a fix if possible. |
@chrisdostert Not meaning to come off chippy. In regards to contributing a fix, I've submitted a few PR's to this library as we use it to to employee a handful of people and do quite a bit of revenue using this library. In fact, before your comment, I submitted a fix for this. (#452) We'd be the first to sponsor this project when profits start growing. I could have phrased things differently, but in the end of the day, it sucks giving trust to a project and realizing it doesn't work as you think it would. We use nested inputs for "patch" updates, and just realized that validations don't run. Users on our app can do any edit mutation with invalid data. We unit test validations separately have expected them to run correctly in Anyway, I'm willing to contribute to this feature. #452 Sorry @MichalLytek if I came off chippy to you. You come off chippy to me 99% of your comments to me ;) haha. But I appreciate this library 1000%. <3 |
@j I might be chippy because I'm irritated that I have so little time and so much things to do, so I am painfully honest and direct 😕 And I also appreciate your help with PRs and nice feature requests 😉
I hope so ❤️ I dream about being able to work half-time on TypeGraphQL without financial losses 😄 |
Fixed by #462 - you can check by installing |
Describe the bug
When having nested input types only the top-most class gets transformed into it's instance. Any nested objects remains plain javascript objects.
To Reproduce
Expected behavior
Nested input types should be deserialized into instances of their classes recursively.
Enviorment (please complete the following information):
The text was updated successfully, but these errors were encountered: