Skip to content

Commit

Permalink
fix(loader): correct interface loader inherit
Browse files Browse the repository at this point in the history
  • Loading branch information
Davide-Gheri committed Mar 23, 2021
1 parent 7911127 commit 6e6357f
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 46 deletions.
83 changes: 38 additions & 45 deletions lib/services/loaders-explorer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ interface LoaderMetadata {
interface ObjectTypeLoaders {
objectTypeMetadata: ObjectTypeMetadata;
isInterface: boolean;
interfaces: string[];
concretes?: Set<string>;
loaders: Record<
string,
{
Expand Down Expand Up @@ -86,6 +86,8 @@ export class LoadersExplorerService extends BaseExplorerService {
this.filterLoaders(instance, moduleRef),
);

const objectTypesByInterface = this.getInterfacesImplementations();

/**
* From the retrieved loaders, create a Loader tree and add useful metadata
* ObjectType: {
Expand All @@ -106,10 +108,13 @@ export class LoadersExplorerService extends BaseExplorerService {
const objectTypeMetadata = this.getObjectTypeMetadataByName(
loader.type,
);
const isInterface = !objectTypeMetadata;
acc[loader.type] = {
isInterface: !objectTypeMetadata,
isInterface,
concretes: isInterface
? objectTypesByInterface[loader.type] || new Set<string>()
: undefined,
objectTypeMetadata,
interfaces: this.getObjectTypeInterfaces(objectTypeMetadata),
loaders: {},
};
}
Expand All @@ -124,12 +129,22 @@ export class LoadersExplorerService extends BaseExplorerService {
/**
* For each entity in the Loader tree, merge its loader functions with the ones defined on its interfaces
*/
return Object.entries(typeLoaders)
.filter(([, metadata]) => !metadata.isInterface)
.reduce((acc, [type, metadata]: any) => {
acc[type] = this.mergeInterfaceLoaders(metadata, typeLoaders);
return acc;
}, {} as MercuriusLoaders);
return Object.entries(typeLoaders).reduce((acc, [type, metadata]) => {
if (metadata.isInterface) {
metadata.concretes.forEach((value) => {
acc[value] = {
...acc[value],
...metadata.loaders,
};
});
} else {
acc[type] = {
...acc[type],
...metadata.loaders,
};
}
return acc;
}, {} as MercuriusLoaders);
}

/**
Expand Down Expand Up @@ -181,44 +196,22 @@ export class LoadersExplorerService extends BaseExplorerService {
}

/**
* From a given ObjectType metadata, get the interfaces names that implements
* @param objectTypeMetadata
* Get a map of all Interfaces and their ObjectType implementations
* @private
*/
private getObjectTypeInterfaces(objectTypeMetadata: ObjectTypeMetadata) {
return objectTypeMetadata?.interfaces
? getInterfacesArray(objectTypeMetadata.interfaces)
.map((type) =>
TypeMetadataStorage.getInterfaceMetadataByTarget(
type as Type<unknown>,
),
)
.filter(Boolean)
.map((meta) => meta.name)
: [];
}

/**
* Add interface defined loaders to current ObjectType loaders
* @param metadata
* @param typeLoaders
* @private
*/
private mergeInterfaceLoaders(
metadata: ObjectTypeLoaders,
typeLoaders: Record<string, ObjectTypeLoaders>,
) {
const { interfaces } = metadata;
interfaces.forEach((interfaceName) => {
const interfaceMetadata = typeLoaders[interfaceName];
if (interfaceMetadata) {
metadata.loaders = {
...interfaceMetadata.loaders,
...metadata.loaders,
};
}
});
return metadata.loaders;
private getInterfacesImplementations(): Record<string, Set<string>> {
return TypeMetadataStorage.getObjectTypesMetadata().reduce((acc, curr) => {
getInterfacesArray(curr.interfaces).forEach((interfaceTarget) => {
const int = TypeMetadataStorage.getInterfaceMetadataByTarget(
interfaceTarget as Type<unknown>,
);
if (!acc[int.name]) {
acc[int.name] = new Set();
}
acc[int.name].add(curr.name);
});
return acc;
}, {} as Record<string, Set<string>>);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/code-first/services/dog.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class DogService {
return dogs;
}

god(id: number) {
dog(id: number) {
dogs.find((c) => c.id === id);
}

Expand Down
13 changes: 13 additions & 0 deletions tests/example/modules/user/resolvers/customer.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Query, Resolver } from '@nestjs/graphql';
import { CustomerType } from '../../../types/customer.type';
import { CustomerService } from '../services/customer.service';

@Resolver(() => CustomerType)
export class CustomerResolver {
constructor(private readonly customerService: CustomerService) {}

@Query(() => [CustomerType])
customers() {
return this.customerService.customers();
}
}
20 changes: 20 additions & 0 deletions tests/example/modules/user/services/customer.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Injectable } from '@nestjs/common';
import { CustomerType } from '../../../types/customer.type';

const customers: CustomerType[] = [
{
id: '1',
name: 'C1',
},
{
id: '2',
name: 'C2',
},
];

@Injectable()
export class CustomerService {
customers() {
return customers;
}
}
4 changes: 4 additions & 0 deletions tests/example/modules/user/user.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import { UserResolver } from './resolvers/user.resolver';
import { SearchResolver } from './resolvers/search.resolver';
import { PostService } from './services/post.service';
import { UserService } from './services/user.service';
import { CustomerService } from './services/customer.service';
import { CustomerResolver } from './resolvers/customer.resolver';

@Module({
providers: [
PostService,
UserService,
CustomerService,
PersonResolver,
UserResolver,
SearchResolver,
CustomerResolver,
],
})
export class UserModule {}
14 changes: 14 additions & 0 deletions tests/example/types/customer.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Extensions, Field, ID, ObjectType } from '@nestjs/graphql';
import { Person } from './person.interface';

@ObjectType('Customer', {
implements: Person,
})
export class CustomerType implements Person {
@Field(() => ID)
id: string;

@Extensions({ role: 'ADMIN' })
@Field(() => String, { nullable: true })
name?: string;
}
8 changes: 8 additions & 0 deletions tests/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,18 @@ enum Status {
DRAFT
}

type Customer implements IPerson {
id: ID!
name: String
uniqueName: String!
uniqueId: String!
}

type Query {
users: [User!]!
user(id: ID!): User
search: [SearchUnion!]!
customers: [Customer!]!
}

union SearchUnion = PostType | User
Expand Down

0 comments on commit 6e6357f

Please sign in to comment.