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

Arguments for fields of interfaces #284

Closed
Closed
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
afe2b94
docs(readme): add opencollective backers section
MichalLytek Mar 18, 2019
e2d7f95
create test for intended behaviour
Mar 21, 2019
0f4077b
handle Args decorator for fields of graphql interfaces
Mar 21, 2019
dacfff6
Merge branch 'pr/1'
Mar 24, 2019
22f2b97
Merge branch 'master' into feature/arguments-for-interface-fields
Mar 24, 2019
3d0a2b1
fix incorrect merge
Mar 26, 2019
d394eb2
choose more meaningfull testing names for the tested fields
Mar 26, 2019
cfa66f5
add functional tests for field argument resolving
Mar 26, 2019
bb8a918
split into separate test cases
Mar 28, 2019
ce810a1
remove console.log from test
Mar 28, 2019
536647c
fix missing resolvers for interface fields
Mar 28, 2019
bb05d53
update changelog
Mar 28, 2019
837f660
add simple documentation for fields with arguments on GraphQL interfa…
Mar 28, 2019
1e66138
extend interface example
Mar 28, 2019
ef10058
Merge remote-tracking branch 'upstream/master'
Mar 28, 2019
4451bf1
Merge branch 'master' into feature/arguments-for-interface-fields
Mar 28, 2019
9e65edd
Merge branch 'master' into feature/arguments-for-interface-fields
MichalLytek Mar 30, 2019
5e5f68e
update naming of variables and types
Apr 4, 2019
c3095bb
Merge remote-tracking branch 'upstream/master'
Apr 20, 2019
3c0b20f
Merge branch 'master' into feature/arguments-for-interface-fields
Apr 20, 2019
8c5bd05
implement graphql interfaces instead of extending them
Apr 20, 2019
5c7a39a
add tests for nullable arguments and default values
Apr 27, 2019
cf7e232
add tests for field resolvers
Apr 27, 2019
18a66f4
remove force flag for new test cases
Apr 27, 2019
5e3bc44
Merge branch 'master' into feature/arguments-for-interface-fields
MichalLytek Apr 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased
<!-- here goes all the unreleased changes descriptions -->
### Features
- add support for arguments in fields of interfaces (#284)
- add postinstall script for printing info on console about supporting the project
- add support for setting default nullability for fields and return types (#297)
- add `skipCheck` option in `buildSchema` to disable checking the correctness of a schema
Expand Down
16 changes: 15 additions & 1 deletion docs/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,27 @@ We can then we use this "interface" in the object type class definition:

```typescript
@ObjectType({ implements: IPerson })
class Person implements IPerson {
class Person extends IPerson {
MichalLytek marked this conversation as resolved.
Show resolved Hide resolved
id: string;
name: string;
age: number;
}
```

We can also define dynamic fields for an interface using the same syntax you would use when defining one for your object types:

```typescript
@InterfaceType()
abstract class IPerson {
// ...

@Field()
avatar(@Arg("size") size: number): string {
return `http://i.pravatar.cc/${size}`;
kl4n4 marked this conversation as resolved.
Show resolved Hide resolved
}
}
```

The only difference is that we have to let TypeGraphQL know that this `ObjectType` is implementing the `InterfaceType`. We do this by passing the param `({ implements: IPerson })` to the decorator. If we implement multiple interfaces, we pass an array of interfaces like so: `({ implements: [IPerson, IAnimal, IMachine] })`.

We can also omit the decorators since the GraphQL types will be copied from the interface definition - this way we won't have to maintain two definitions and solely rely on TypeScript type checking for correct interface implementation.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InterfaceType, Field, Int, ID } from "../../../src";
import { InterfaceType, Field, Int, ID, Arg } from "../../../src";

import { IResource } from "../resource/resource.interface";

Expand All @@ -12,4 +12,9 @@ export abstract class IPerson implements IResource {

@Field(type => Int)
age: number;

@Field()
avatar(@Arg("size") size: number): string {
throw new Error("Method not implemented.");
}
}
7 changes: 6 additions & 1 deletion examples/interfaces-inheritance/person/person.type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ObjectType } from "../../../src";
import { ObjectType, Field, Arg } from "../../../src";

import { IPerson } from "./person.interface";

Expand All @@ -7,4 +7,9 @@ export class Person implements IPerson {
id: string;
name: string;
age: number;

@Field()
MichalLytek marked this conversation as resolved.
Show resolved Hide resolved
avatar(@Arg("size") size: number): string {
return `http://i.pravatar.cc/${size}`;
}
}
15 changes: 14 additions & 1 deletion src/schema/schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,22 @@ export abstract class SchemaGenerator {
fields: () => {
let fields = interfaceType.fields!.reduce<GraphQLFieldConfigMap<any, any>>(
(fieldsMap, field) => {
const fieldResolverMetadata = getMetadataStorage().fieldResolvers.find(
resolver =>
resolver.getObjectType!() === interfaceType.target &&
resolver.methodName === field.name &&
(resolver.resolverClassMetadata === undefined ||
resolver.resolverClassMetadata.isAbstract === false),
);
fieldsMap[field.schemaName] = {
description: field.description,
type: this.getGraphQLOutputType(field.name, field.getType(), field.typeOptions),
complexity: field.complexity,
args: this.generateHandlerArgs(field.params!),
resolve: fieldResolverMetadata
? createAdvancedFieldResolver(fieldResolverMetadata)
: createSimpleFieldResolver(field),
description: field.description,
deprecationReason: field.deprecationReason,
};
return fieldsMap;
},
Expand Down
Loading