Skip to content

Commit e254691

Browse files
committed
Folder structure changed
1 parent d3be696 commit e254691

15 files changed

+764
-544
lines changed

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,21 @@ This file contains the changes made to the package.
33

44
The sections are in descending order of the change date.
55

6-
## [0.1.15]: - 2022-08-20
6+
## [0.1.16] - 2022-08-21
7+
### Added
8+
- Basic pluralization for reverse table names if one of the links in navigation
9+
is an array. This will be default and used if not set by the developer.
10+
11+
### Changed
12+
- The structure of the folders.
13+
- `mappers` for custom names for cross table references are not used anymore and
14+
planned to be removed in the following versions.
15+
16+
## [0.1.15] - 2022-08-20
717
### Fixed
818
- `Array.map` error during `*:1` relation mapping.
919

10-
## [0.1.14]: - 2022-08-16
20+
## [0.1.14] - 2022-08-16
1121
### Fixed
1222
- Prisma Selections for deeply nested objects were not working correctly.
1323

@@ -42,6 +52,7 @@ object.
4252
The initial version of the package.
4353

4454
[Unreleased]: https://github.com/incetarik/nestjs-prisma-dynamic-resolvers/compare/v1.0.0...HEAD
55+
[0.1.16]: https://github.com/incetarik/nestjs-prisma-dynamic-resolvers/compare/0.1.15...0.1.16
4556
[0.1.15]: https://github.com/incetarik/nestjs-prisma-dynamic-resolvers/compare/0.1.14...0.1.15
4657
[0.1.14]: https://github.com/incetarik/nestjs-prisma-dynamic-resolvers/compare/0.1.13...0.1.14
4758
[0.1.13]: https://github.com/incetarik/nestjs-prisma-dynamic-resolvers/compare/0.1.9...0.1.13

src/dynamic-navigations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ export interface INavigationMap {
2222
/**
2323
* The prototype of the source class.
2424
*
25-
* @type {Object}
25+
* @type {Function}
2626
* @memberof INavigationMap
2727
*/
28-
source: Object
28+
source: Function
2929

3030
/**
3131
* The target type constructor.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { GraphQLResolveInfo } from 'graphql'
2+
3+
import { getNavigationMaps, INavigationMap } from '../dynamic-navigations'
4+
import {
5+
extractGraphQLSelectionPath,
6+
extractGraphQLSelections,
7+
toCamelCase,
8+
updatePathInfoFromSchema,
9+
} from '../helpers'
10+
11+
import type { Dictionary } from '../types'
12+
13+
export abstract class DynamicResolverBase {
14+
constructor(protected readonly prisma: any) {}
15+
16+
protected getSelectionObject(info: GraphQLResolveInfo, selectionMap?: Dictionary<string>) {
17+
const select = extractGraphQLSelections({
18+
node: info.fieldNodes[ 0 ],
19+
selectionMap,
20+
})
21+
22+
return select
23+
}
24+
25+
protected getSelectionPath(info: GraphQLResolveInfo, selectionMap?: Dictionary<string>) {
26+
const selectionPath = extractGraphQLSelectionPath({
27+
path: info.path,
28+
})
29+
30+
const pathInfoList = selectionPath
31+
pathInfoList.forEach(pathInfo => {
32+
if (pathInfo.table) return
33+
34+
const tableName = toCamelCase(pathInfo.tableName)
35+
const maps = getNavigationMaps()
36+
.filter(it => it.sourceTableName === tableName)
37+
38+
pathInfo.table = maps[ 0 ]?.source
39+
pathInfo.navigationMaps = maps
40+
41+
updatePathInfoFromSchema(pathInfo, info.schema)
42+
})
43+
44+
const query = pathInfoList[ 0 ]
45+
46+
if (pathInfoList.length < 3) {
47+
throw new Error(`The navigation source could not be extracted in (${query.propertyName})`)
48+
}
49+
50+
const firstLink = pathInfoList[ 1 ]
51+
52+
if (!firstLink.table) {
53+
throw new Error(`The navigation target could not be extracted in (${query.propertyName}): '${firstLink.propertyName}' → ?`)
54+
}
55+
56+
const secondLink = pathInfoList[ 2 ]
57+
58+
if (!secondLink.table) {
59+
throw new Error(`The navigation target could not be extracted in (${query.propertyName}): ${firstLink.propertyName}${secondLink.propertyName}' → ?`)
60+
}
61+
62+
const sourceTableName
63+
= selectionMap?.[ firstLink.propertyName ]
64+
?? toCamelCase(firstLink.table.name)
65+
66+
const source = this.prisma[ sourceTableName ]
67+
if (!source) {
68+
throw new Error(`Unrecognized Prisma model: "${sourceTableName}". Failed to build resolver for navigation: '${sourceTableName}${secondLink.propertyName}'`)
69+
}
70+
71+
return {
72+
to: secondLink,
73+
root: query,
74+
from: firstLink,
75+
source,
76+
selectionPath,
77+
}
78+
}
79+
80+
protected resolve(parent: any, _primaryKeyName: string, _info: GraphQLResolveInfo, navigationMap: INavigationMap, _selectionMap?: Dictionary<string>) {
81+
const {
82+
relation,
83+
sourceProperty,
84+
sourceTableName,
85+
targetTableName,
86+
} = navigationMap
87+
88+
const [ _left, right ] = relation.split(':')
89+
const isArray = right.indexOf('*') >= 0
90+
91+
// The `parent` is the loaded instance
92+
// If the source we are looking for is loaded, then process it.
93+
if (sourceProperty in parent) {
94+
const data = (parent as any)[ sourceProperty ]
95+
if (isArray) {
96+
return data.map((it: any) => it[ targetTableName ])
97+
}
98+
else {
99+
return data
100+
}
101+
}
102+
else {
103+
throw new Error(`Could not resolve from ${sourceTableName}: ${sourceProperty}`)
104+
}
105+
}
106+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import type { Type } from "@nestjs/common"
2+
import type { IOnResolvedParams, IOnResolvingParams } from "./event-params.interface"
3+
4+
/**
5+
* An interface containing parameters for {@link UseDynamicResolvers} decorator.
6+
*
7+
* @export
8+
* @interface IUseDynamicResolversParams
9+
*/
10+
export interface IUseDynamicResolversParams {
11+
/**
12+
* The name of the module that will contain the resolver.
13+
*
14+
* This name is used for grouping the resolvers and then the grouped resolvers
15+
* can be acquired through {@link NavigationProperty.getDynamicResolvers}.
16+
*
17+
* If the name is not given, then they will be registered as global.
18+
*
19+
* @type {string}
20+
* @memberof IUseDynamicResolversParams
21+
*/
22+
moduleName?: string
23+
24+
/**
25+
* The name of the database table.
26+
*
27+
* This will be used for generating `select` / `include` clauses.
28+
*
29+
* @type {string}
30+
* @memberof IUseDynamicResolversParams
31+
*/
32+
tableName?: string
33+
34+
/**
35+
* The name of the property that is used as ID property of the database table.
36+
*
37+
* @type {string}
38+
* @memberof IUseDynamicResolversParams
39+
* @default 'id
40+
*/
41+
primaryKeyName?: string
42+
43+
/**
44+
* Indicates if the generated navigation map should be kept as metadata of the
45+
* target type.
46+
*
47+
* Set this property to `true` to keep the navigation map and use
48+
* {@link getNavigationMapsOf} function to get the navigation map of the type.
49+
*
50+
* @type {boolean}
51+
* @memberof IUseDynamicResolversParams
52+
* @default false
53+
*/
54+
keepNavigationMap?: boolean
55+
56+
/**
57+
* An event function that will be triggered when the resolver is about to resolve the defined navigation.
58+
*
59+
* @param {IOnResolvingParams<P>} params The parameters.
60+
* @return {*} The
61+
* replace value.
62+
*
63+
* If this function returns a value from this event function, then the
64+
* resolving will be cancelled and the data returned from this function will
65+
* be used as the result of resolving.
66+
*
67+
* @memberof IUseDynamicResolversParams
68+
*/
69+
onResolving?<P extends object = object, TRoot extends object = object>(params: IOnResolvingParams<P, TRoot>): any
70+
71+
/**
72+
* An event function that will be triggered when the resolved is resolved the defined navigation.
73+
*
74+
* @param {IOnResolvedParams<any, P>} params The parameters.
75+
* @return {*} The
76+
* replace value.
77+
*
78+
* If this function returns a value from this event function, then the
79+
* resolving will be ignored and the data returned from this function will be
80+
* used as the result of the resolving.
81+
*
82+
* @memberof IUseDynamicResolversParams
83+
*/
84+
onResolved?<P extends object = object, TRoot extends object = object>(params: IOnResolvedParams<any, P, TRoot>): any
85+
}
86+
87+
export interface IRegisterDynamicResolverParams extends IUseDynamicResolversParams {
88+
/**
89+
* The target class that uses the dynamic resolvers.
90+
*
91+
* @type {Type}
92+
* @memberof IUseDynamicResolversParams
93+
*/
94+
target: Type
95+
}
96+
97+
let _resolverParams: IRegisterDynamicResolverParams[] | undefined
98+
99+
/**
100+
* Registers a type as dynamic resolver.
101+
*
102+
* @export
103+
* @param {IUseDynamicResolversParams} params The parameters.
104+
*/
105+
export function registerDynamicResolver(params: IRegisterDynamicResolverParams) {
106+
_resolverParams ??= []
107+
_resolverParams.push(params)
108+
}
109+
110+
/**
111+
* Gets the resolvers that are created dynamically.
112+
*
113+
* @export
114+
* @param {string} [groupName='_global'] The name of the group.
115+
* @return {Type[]} An array of resolver classes of the group.
116+
*/
117+
export function getDynamicResolverParams(groupName = '_global') {
118+
if (!_resolverParams) return []
119+
return _resolverParams.filter(it => it.moduleName === groupName)
120+
}
121+
122+
/**
123+
* Removes the dynamic resolver parameters of a group.
124+
*
125+
* @export
126+
* @param {string} groupName The name of the groups.
127+
* @return {boolean} A boolean indicating the operation state. If `false`, then
128+
* nothing is done.
129+
*/
130+
export function removeDynamicResolverParamsOfGroup(groupName: string) {
131+
if (!_resolverParams) return false
132+
133+
for (let i = _resolverParams.length - 1; i >= 0; --i) {
134+
const item = _resolverParams[ i ]
135+
if (item.moduleName === groupName) {
136+
_resolverParams.splice(i, 1)
137+
}
138+
}
139+
140+
if (_resolverParams.length === 0) {
141+
_resolverParams = undefined
142+
}
143+
144+
return true
145+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { GraphQLExecutionContext } from "@nestjs/graphql"
2+
import type { GraphQLResolveInfo } from "graphql"
3+
4+
export interface IOnResolvingParams<P extends object = object, TRoot extends object = object> {
5+
/**
6+
* The parent object.
7+
*
8+
* @type {P}
9+
* @memberof IOnResolvingParams
10+
*/
11+
readonly parent: P
12+
13+
/**
14+
* The graphql resolve info.
15+
*
16+
* @type {GraphQLResolveInfo}
17+
* @memberof IOnResolvingParams
18+
*/
19+
readonly resolveInfo: GraphQLResolveInfo
20+
21+
/**
22+
* The execution context.
23+
*
24+
* @type {GraphQLExecutionContext}
25+
* @memberof IOnResolvingParams
26+
*/
27+
readonly context: GraphQLExecutionContext
28+
29+
/**
30+
* The root object.
31+
*
32+
* @type {TRoot}
33+
* @memberof IOnResolvingParams
34+
*/
35+
readonly root: TRoot
36+
}
37+
38+
export interface IOnResolvedParams<R = any, P extends object = object, TRoot extends object = object> extends IOnResolvingParams<P, TRoot> {
39+
/**
40+
* The data resolved by the resolver.
41+
*
42+
* @type {R}
43+
* @memberof IOnResolvedParams
44+
*/
45+
data: R
46+
47+
/**
48+
* Indicates if the {@link data} is resolved from the database or not.
49+
*
50+
* This property will be `false` if the {@link data} is got from the
51+
* {@link IUseDynamicResolversParams.onResolving} method.
52+
*
53+
* @type {boolean}
54+
* @memberof IOnResolvedParams
55+
*/
56+
readonly fromDatabase: boolean
57+
}

0 commit comments

Comments
 (0)