-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathresolver.provider.ts
189 lines (162 loc) · 6.45 KB
/
resolver.provider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import { Inject, Provider } from '@nestjs/common'
import { Resolver } from '@nestjs/graphql'
import {
Assembler,
AssemblerFactory,
AssemblerQueryService,
Class,
DeepPartial,
InjectAssemblerQueryService,
InjectQueryService,
QueryService
} from '@ptc-org/nestjs-query-core'
import { PubSub } from 'graphql-subscriptions'
import { InjectPubSub } from '../decorators'
import { CRUDResolver, CRUDResolverOpts, FederationResolver } from '../resolvers'
import { PagingStrategies } from '../types/query/paging'
export type CRUDAutoResolverOpts<DTO, C, U, R, PS extends PagingStrategies> = CRUDResolverOpts<DTO, C, U, R, PS> & {
DTOClass: Class<DTO>
}
export type EntityCRUDAutoResolverOpts<DTO, Entity, C, U, R, PS extends PagingStrategies> = CRUDAutoResolverOpts<
DTO,
C,
U,
R,
PS
> & {
EntityClass: Class<Entity>
}
export type AssemblerCRUDAutoResolverOpts<DTO, Assembler, C, U, R, PS extends PagingStrategies> = CRUDAutoResolverOpts<
DTO,
C,
U,
R,
PS
> & {
AssemblerClass: Class<Assembler>
}
export type ServiceCRUDAutoResolverOpts<DTO, QueryService, C, U, R, PS extends PagingStrategies> = CRUDAutoResolverOpts<
DTO,
C,
U,
R,
PS
> & {
ServiceClass: Class<QueryService>
}
export type FederatedAutoResolverOpts<DTO, Service> = {
type: 'federated'
DTOClass: Class<DTO>
Service: Class<Service>
}
export type AutoResolverOpts<DTO, EntityServiceOrAssembler, C, U, R, PS extends PagingStrategies> =
| EntityCRUDAutoResolverOpts<DTO, EntityServiceOrAssembler, C, U, R, PS>
| AssemblerCRUDAutoResolverOpts<DTO, EntityServiceOrAssembler, C, U, R, PS>
| ServiceCRUDAutoResolverOpts<DTO, EntityServiceOrAssembler, C, U, R, PS>
| FederatedAutoResolverOpts<DTO, EntityServiceOrAssembler>
export const isFederatedResolverOpts = <DTO, MaybeService, C, U, R, PS extends PagingStrategies>(
opts: AutoResolverOpts<DTO, MaybeService, C, U, R, PS>
): opts is FederatedAutoResolverOpts<DTO, MaybeService> => 'type' in opts && opts.type === 'federated'
export const isAssemblerCRUDAutoResolverOpts = <DTO, MaybeAssembler, C, U, R, PS extends PagingStrategies>(
opts: AutoResolverOpts<DTO, MaybeAssembler, C, U, R, PS>
): opts is AssemblerCRUDAutoResolverOpts<DTO, MaybeAssembler, C, U, R, PS> => 'DTOClass' in opts && 'AssemblerClass' in opts
export const isServiceCRUDAutoResolverOpts = <DTO, MaybeService, C, U, R, PS extends PagingStrategies>(
opts: AutoResolverOpts<DTO, MaybeService, C, U, R, PS>
): opts is ServiceCRUDAutoResolverOpts<DTO, MaybeService, C, U, R, PS> => 'DTOClass' in opts && 'ServiceClass' in opts
const getResolverToken = <DTO>(DTOClass: Class<DTO>): string => `${DTOClass.name}AutoResolver`
const getFederatedResolverToken = <DTO>(DTOClass: Class<DTO>): string => `${DTOClass.name}FederatedAutoResolver`
function createFederatedResolver<DTO, Service>(resolverOpts: FederatedAutoResolverOpts<DTO, Service>): Provider {
const { DTOClass } = resolverOpts
@Resolver(() => DTOClass)
class AutoResolver extends FederationResolver(DTOClass) {
constructor(
@Inject(resolverOpts.Service) readonly service: QueryService<DTO, unknown, unknown>,
@InjectPubSub() readonly pubSub: PubSub
) {
super(service)
}
}
// need to set class name so DI works properly
Object.defineProperty(AutoResolver, 'name', { value: getFederatedResolverToken(DTOClass), writable: false })
return AutoResolver
}
function createEntityAutoResolver<DTO, Entity extends DeepPartial<Entity>, C, U, R, PS extends PagingStrategies>(
resolverOpts: EntityCRUDAutoResolverOpts<DTO, Entity, C, U, R, PS>
): Provider {
const { DTOClass, EntityClass } = resolverOpts
class Service extends AssemblerQueryService<DTO, Entity, C, C, U, U> {
constructor(service: QueryService<Entity, C, U>) {
const assembler = AssemblerFactory.getAssembler<DTO, Entity, C, C, U, U>(DTOClass, EntityClass)
super(assembler, service)
}
}
@Resolver(() => DTOClass)
class AutoResolver extends CRUDResolver(DTOClass, resolverOpts) {
constructor(
@InjectQueryService(EntityClass) service: QueryService<Entity, C, U>,
@InjectPubSub() readonly pubSub: PubSub
) {
super(new Service(service))
}
}
// need to set class name so DI works properly
Object.defineProperty(AutoResolver, 'name', { value: getResolverToken(DTOClass), writable: false })
return AutoResolver
}
function createAssemblerAutoResolver<DTO, Asmblr, C, U, R, PS extends PagingStrategies>(
resolverOpts: AssemblerCRUDAutoResolverOpts<DTO, Asmblr, C, U, R, PS>
): Provider {
const { DTOClass, AssemblerClass } = resolverOpts
@Resolver(() => DTOClass)
class AutoResolver extends CRUDResolver(DTOClass, resolverOpts) {
constructor(
@InjectAssemblerQueryService(AssemblerClass as unknown as Class<Assembler<DTO, unknown, C, unknown, U, unknown>>)
service: QueryService<DTO, C, U>,
@InjectPubSub() readonly pubSub: PubSub
) {
super(service)
}
}
// need to set class name so DI works properly
Object.defineProperty(AutoResolver, 'name', { value: getResolverToken(DTOClass), writable: false })
return AutoResolver
}
function createServiceAutoResolver<DTO, Service, C, U, R, PS extends PagingStrategies>(
resolverOpts: ServiceCRUDAutoResolverOpts<DTO, Service, C, U, R, PS>
): Provider {
const { DTOClass, ServiceClass } = resolverOpts
@Resolver(() => DTOClass)
class AutoResolver extends CRUDResolver(DTOClass, resolverOpts) {
constructor(
@Inject(ServiceClass) service: QueryService<DTO, C, U>,
@InjectPubSub() readonly pubSub: PubSub
) {
super(service)
}
}
// need to set class name so DI works properly
Object.defineProperty(AutoResolver, 'name', { value: getResolverToken(DTOClass), writable: false })
return AutoResolver
}
function createResolver<
DTO,
EntityServiceOrAssembler extends DeepPartial<EntityServiceOrAssembler>,
C,
U,
R,
PS extends PagingStrategies
>(resolverOpts: AutoResolverOpts<DTO, EntityServiceOrAssembler, C, U, R, PS>): Provider {
if (isFederatedResolverOpts(resolverOpts)) {
return createFederatedResolver(resolverOpts)
}
if (isAssemblerCRUDAutoResolverOpts(resolverOpts)) {
return createAssemblerAutoResolver(resolverOpts)
}
if (isServiceCRUDAutoResolverOpts(resolverOpts)) {
return createServiceAutoResolver(resolverOpts)
}
return createEntityAutoResolver(resolverOpts)
}
export const createResolvers = (
opts: AutoResolverOpts<unknown, unknown, unknown, unknown, unknown, PagingStrategies>[]
): Provider[] => opts.map((opt) => createResolver(opt))