Skip to content

Commit fd955b5

Browse files
authored
feat(json-api-nestjs): new-version 3.x.x
support TypeOrm 3.x.x and NestJS 9 BREAKING CHANGE: change init custom controller and config
1 parent dcc03d7 commit fd955b5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2148
-2700
lines changed

.github/workflows/Pre-Release.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Pre-Release
2+
on:
3+
push:
4+
branches:
5+
- next
6+
- beta
7+
- "*.x" # maintenance releases branches
8+
env:
9+
BEFORE_SHA: ${{ github.event.before }}
10+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
11+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
12+
jobs:
13+
release:
14+
name: Release
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v2
19+
with:
20+
fetch-depth: 0
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v1
23+
with:
24+
node-version: 14
25+
- uses: actions/cache@v2
26+
with:
27+
path: ~/.npm
28+
key: ${{ runner.os }}-node-${{ hashFiles('./package-lock.json') }}
29+
restore-keys: |
30+
${{ runner.os }}-node-
31+
- name: Install dependencies
32+
run: npm ci
33+
- name: Test
34+
run: npx nx affected:test --base=remotes/origin/master
35+
- name: Deploy
36+
run: npx nx affected --target=deploy --base=remotes/origin/master
37+

libs/json-api-nestjs/README.md

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
****strong text****
22
# json-api-nestjs
33

44
## Installation
@@ -25,7 +25,7 @@ import { Users } from 'database';
2525
})
2626
export class AppModule {}
2727
```
28-
After this, you have prepare crude with ready to use end point:
28+
After this, you have to prepare CRUDs with ready-to-use endpoints:
2929

3030

3131
- GET /users
@@ -53,7 +53,7 @@ export interface ModuleOptions {
5353
};
5454
}
5555
```
56-
You can extend default controller:
56+
You can extend the default controller:
5757
```typescript
5858
import { Get, Param, Inject, BadRequestException } from '@nestjs/common';
5959

@@ -90,24 +90,79 @@ export class ExtendUserController extends JsonBaseController<Users> {
9090
}
9191
```
9292

93+
You can overwrite the default config for the current controller using options in the decorator **JsonAPi**.
94+
Also you can specify an API method necessary for you, using **allowMethod**
95+
9396
## Swagger UI
9497

9598
For using swagger, you should only add [@nestjs/swagger](https://docs.nestjs.com/openapi/introduction)
9699

100+
## Available endpoint method
101+
Using **Users** entity and relation **Roles** entity as example
102+
103+
### List item of Users
104+
105+
```
106+
GET /users
107+
```
108+
Available query params:
109+
110+
- **include** - you can extend result with relations (aka join)
111+
```
112+
GET /users?include=roles
113+
```
114+
result of request will have role relation for each **Users** item
115+
116+
- **fields** - you can specify required fields of result query
117+
118+
```
119+
GET /users?fields[target]=login,lastName&fileds[roles]=name,key
120+
```
121+
The "target" is **Users** entity
122+
The "roles" is **Roles** entity
123+
So, result of request will be have only fields *login* and *lastName* for **Users** entity and fields *name* and *key* for **Roles** entity
124+
- **sort** - you can sort result of the request
125+
126+
```
127+
GET /users?sort=target.name,-roles.key
128+
```
129+
The "target" is **Users** entity
130+
The "roles" is **Roles** entity
131+
So, result of the request will be sorted by field *name* of **Users** by *ASC* and field *key* of **Roles** entity by **DESC**.
132+
- **page** - pagination for you request
133+
134+
```
135+
GET /users?page[number]=1page[size]=20
136+
```
137+
- **filter** - filter for query
138+
139+
```
140+
GET /users?filter[name][eq]=1&filter[roles.name][ne]=test&filter[roles.status][eq]=true
141+
```
142+
The "name" is a field of **Users** entity
143+
The "roles.name" is *name* field of **Roles** entity
144+
The "eq", "ne" is *[Filter operand](#filter-operand)*
145+
146+
So, this query will be transformed like sql:
147+
```sql
148+
WHERE users.name = 1 AND roles.name <> 'test' AND roles.status = true
149+
```
97150

98151
## Filter operand
99152

100153
```typescript
101-
type FilterOperand = {
102-
in: string[], // is equal to the conditional of query "WHERE 'attribute_name' IN ('value1', 'value2')"
103-
nin: string[], // is equal to the conditional of query "WHERE 'attribute_name' NOT IN ('value1', 'value1')"
104-
eq: string, // is equal to the conditional of query "WHERE 'attribute_name' = 'value1'
105-
ne: string, // is equal to the conditional of query "WHERE 'attribute_name' <> 'value1'
106-
gte: string, // is equal to the conditional of query "WHERE 'attribute_name' >= 'value1'
107-
gt: string, // is equal to the conditional of query "WHERE 'attribute_name' > 'value1'
108-
lt: string, // is equal to the conditional of query "WHERE 'attribute_name' < 'value1'
109-
lte:string, // is equal to the conditional of query "WHERE 'attribute_name' <= 'value1'
110-
regexp: string, // is equal to the conditional of query "WHERE 'attribute_name' ~* value1
111-
some: string, // is equal to the conditional of query "WHERE 'attribute_name' && [value1]
154+
type FilterOperand {
155+
in: string[] // is equal to the conditional of query "WHERE 'attribute_name' IN ('value1', 'value2')"
156+
nin: string[] // is equal to the conditional of query "WHERE 'attribute_name' NOT IN ('value1', 'value1')"
157+
eq: string // is equal to the conditional of query "WHERE 'attribute_name' = 'value1'
158+
ne: string // is equal to the conditional of query "WHERE 'attribute_name' <> 'value1'
159+
gte: string // is equal to the conditional of query "WHERE 'attribute_name' >= 'value1'
160+
gt: string // is equal to the conditional of query "WHERE 'attribute_name' > 'value1'
161+
lt: string // is equal to the conditional of query "WHERE 'attribute_name' < 'value1'
162+
lte:string // is equal to the conditional of query "WHERE 'attribute_name' <= 'value1'
163+
regexp: string // is equal to the conditional of query "WHERE 'attribute_name' ~* value1
164+
some: string // is equal to the conditional of query "WHERE 'attribute_name' && [value1]
112165
}
113166
```
167+
168+

libs/json-api-nestjs/project.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
"projectType": "library",
66
"targets": {
77
"deploy": {
8-
"executor": "@ng-builders/semrel:release",
9-
"dependsOn": [{ "projects": "self", "target": "release" }],
8+
"executor": "./dist/libs/semrel:release",
9+
"dependsOn": [
10+
{ "projects": "self", "target": "buildSemRel"},
11+
{ "projects": "self", "target": "release" }
12+
],
1013
"options": {
1114
"npm": {
1215
"pkgRoot": "./dist/libs/json-api-nestjs"
@@ -24,6 +27,16 @@
2427
"parallel": false
2528
}
2629
},
30+
"buildSemRel": {
31+
"executor": "nx:run-commands",
32+
"options": {
33+
"commands": [
34+
"npx nx build semrel"
35+
],
36+
"cwd": "./",
37+
"parallel": false
38+
}
39+
},
2740
"build": {
2841
"executor": "@nrwl/js:tsc",
2942
"outputs": [
@@ -73,6 +86,5 @@
7386
}
7487
}
7588
},
76-
"tags": [],
77-
"implicitDependencies": ["sevrel"]
89+
"tags": []
7890
}

libs/json-api-nestjs/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export * from './lib/json-api.module';
22
export * from './lib/decorators';
33
export { excludeMethod } from './lib/config/bindings';
44
export * from './lib/types';
5+
export * from './lib/types-common';
56
export * from './lib/mixin/controller';

libs/json-api-nestjs/src/lib/constants/reflection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
export const JSON_API_DECORATOR_ENTITY = Symbol('JSON_API_ENTITY');
22
export const JSON_API_DECORATOR_OPTIONS = Symbol('JSON_API_OPTIONS');
3+
export const GLOBAL_MODULE_OPTIONS_TOKEN = Symbol('GLOBAL_MODULE_OPTIONS');
4+
export const CURRENT_DATA_SOURCE_TOKEN = Symbol('CURRENT_DATA_SOURCE_TOKEN');
35

46
export const PARAMS_RESOURCE_ID = 'id';
57
export const PARAMS_RELATION_ID = 'relId';
68
export const PARAMS_RELATION_NAME = 'relName';
79

810
export const JSON_API_CONFIG = 'JSON_API_CONFIG';
911

10-
export const GLOBAL_MODULE_OPTIONS_TOKEN = Symbol('GLOBAL_MODULE_OPTIONS');
11-
1212
export const JSON_API_SERVICE_POSTFIX = 'JsonApiService';
1313
export const CONFIG_PARAM_POSTFIX = 'JsonApiConfigParam';
1414
export const JSON_API_CONTROLLER_POSTFIX = 'JsonApiController';

libs/json-api-nestjs/src/lib/factory/ajv/ajv.factory.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import AjvCall from 'ajv';
22
import { FactoryProvider } from '@nestjs/common/interfaces/modules/provider.interface';
3-
import { getDataSourceToken } from '@nestjs/typeorm';
43
import { DataSource } from 'typeorm';
54
import { plainToClass } from 'class-transformer';
65

76
import { ModuleOptions } from '../../types';
8-
import { GLOBAL_MODULE_OPTIONS_TOKEN } from '../../constants';
7+
import {
8+
CURRENT_DATA_SOURCE_TOKEN,
9+
GLOBAL_MODULE_OPTIONS_TOKEN,
10+
} from '../../constants';
11+
912
import {
1013
inputQuerySchema,
1114
inputBodyPostSchema,
@@ -141,7 +144,10 @@ export const ajvFactory: FactoryProvider<AjvCall> = {
141144
provide: AjvCall,
142145
useFactory: AjvCallFactory,
143146
inject: [
144-
getDataSourceToken(),
147+
{
148+
token: CURRENT_DATA_SOURCE_TOKEN,
149+
optional: false,
150+
},
145151
{
146152
token: GLOBAL_MODULE_OPTIONS_TOKEN,
147153
optional: false,

libs/json-api-nestjs/src/lib/json-api-nestjs-common.module.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { DynamicModule, Module } from '@nestjs/common';
2-
import { TypeOrmModule } from '@nestjs/typeorm';
2+
import { getDataSourceToken, TypeOrmModule } from '@nestjs/typeorm';
3+
import { DataSource } from 'typeorm';
34

45
import { ModuleOptions } from '../lib/types';
5-
import { GLOBAL_MODULE_OPTIONS_TOKEN } from './constants';
6+
import {
7+
CURRENT_DATA_SOURCE_TOKEN,
8+
GLOBAL_MODULE_OPTIONS_TOKEN,
9+
} from './constants';
610
import { ajvFactory } from './factory';
711
import { ErrorInterceptors } from './mixin/interceptors';
812

@@ -14,6 +18,12 @@ export class JsonApiNestJsCommonModule {
1418
useValue: options,
1519
};
1620

21+
const currentDataSourceProvider = {
22+
provide: CURRENT_DATA_SOURCE_TOKEN,
23+
useFactory: (dataSource: DataSource) => dataSource,
24+
inject: [getDataSourceToken(options.connectionName)],
25+
};
26+
1727
const typeOrmModule = TypeOrmModule.forFeature(
1828
options.entities,
1929
options.connectionName
@@ -25,6 +35,7 @@ export class JsonApiNestJsCommonModule {
2535
...(options.providers || []),
2636
ajvFactory,
2737
optionProvider,
38+
currentDataSourceProvider,
2839
ErrorInterceptors,
2940
],
3041
exports: [

libs/json-api-nestjs/src/lib/mixin/controller/json-base/json-base.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
JsonApiServiceMethode,
55
QueryParams,
66
} from '../../../types';
7-
import { ResourceRequestObject } from '../../../types-common/request';
7+
import { ResourceRequestObject } from '../../../types-common';
88
import { Relationship } from '../../../types-common';
99

1010
export class JsonBaseController<Entity> implements ControllerTypes<Entity> {

libs/json-api-nestjs/src/lib/mixin/pipes/body-input-patch/body-input-patch.pipe.spec.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
33
import { DataSource } from 'typeorm';
44

55
import {
6+
CURRENT_DATA_SOURCE_TOKEN,
67
DEFAULT_CONNECTION_NAME,
78
GLOBAL_MODULE_OPTIONS_TOKEN,
89
} from '../../../constants';
@@ -35,12 +36,22 @@ describe('BodyInputPatchPipe', () => {
3536
connectionName: DEFAULT_CONNECTION_NAME,
3637
},
3738
},
39+
{
40+
provide: CURRENT_DATA_SOURCE_TOKEN,
41+
useFactory: (dataSource: DataSource) => dataSource,
42+
inject: [getDataSourceToken(mockConnectionName)],
43+
},
3844
{
3945
provide: getRepositoryToken(Users, mockConnectionName),
4046
useFactory(dataSource: DataSource) {
4147
return dataSource.getRepository<Users>(Users);
4248
},
43-
inject: [getDataSourceToken()],
49+
inject: [
50+
{
51+
token: CURRENT_DATA_SOURCE_TOKEN,
52+
optional: false,
53+
},
54+
],
4455
},
4556
],
4657
}).compile();

libs/json-api-nestjs/src/lib/mixin/pipes/body-input-post/body-input-post.pipe.spec.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
33
import { DataSource } from 'typeorm';
44

55
import {
6+
CURRENT_DATA_SOURCE_TOKEN,
67
DEFAULT_CONNECTION_NAME,
78
GLOBAL_MODULE_OPTIONS_TOKEN,
89
} from '../../../constants';
@@ -29,15 +30,25 @@ describe('BodyInputPostPipe', () => {
2930
provide: GLOBAL_MODULE_OPTIONS_TOKEN,
3031
useValue: {
3132
entities: entities,
32-
connectionName: DEFAULT_CONNECTION_NAME,
33+
connectionName: mockConnectionName,
3334
},
3435
},
36+
{
37+
provide: CURRENT_DATA_SOURCE_TOKEN,
38+
useFactory: (dataSource: DataSource) => dataSource,
39+
inject: [getDataSourceToken(mockConnectionName)],
40+
},
3541
{
3642
provide: getRepositoryToken(Users, mockConnectionName),
3743
useFactory(dataSource: DataSource) {
3844
return dataSource.getRepository<Users>(Users);
3945
},
40-
inject: [getDataSourceToken()],
46+
inject: [
47+
{
48+
token: CURRENT_DATA_SOURCE_TOKEN,
49+
optional: false,
50+
},
51+
],
4152
},
4253
],
4354
}).compile();

libs/json-api-nestjs/src/lib/mixin/pipes/body-relationship-patch/body-relationship-patch.pipe.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import { ajvFactory } from '../../../factory';
44
import { entities, mockDBTestModule, Users } from '../../../mock-utils';
55
import { BodyRelationshipPatchPipe } from './body-relationship-patch.pipe';
66
import {
7+
CURRENT_DATA_SOURCE_TOKEN,
78
DEFAULT_CONNECTION_NAME,
89
GLOBAL_MODULE_OPTIONS_TOKEN,
910
} from '../../../constants';
1011
import { BadRequestException } from '@nestjs/common';
12+
import { DataSource } from 'typeorm';
13+
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
1114

1215
describe('BodyRelationshipPatchPipe', () => {
1316
let pipe: BodyRelationshipPatchPipe<Users>;
@@ -25,6 +28,23 @@ describe('BodyRelationshipPatchPipe', () => {
2528
connectionName: DEFAULT_CONNECTION_NAME,
2629
},
2730
},
31+
{
32+
provide: CURRENT_DATA_SOURCE_TOKEN,
33+
useFactory: (dataSource: DataSource) => dataSource,
34+
inject: [getDataSourceToken(DEFAULT_CONNECTION_NAME)],
35+
},
36+
{
37+
provide: getRepositoryToken(Users, DEFAULT_CONNECTION_NAME),
38+
useFactory(dataSource: DataSource) {
39+
return dataSource.getRepository<Users>(Users);
40+
},
41+
inject: [
42+
{
43+
token: CURRENT_DATA_SOURCE_TOKEN,
44+
optional: false,
45+
},
46+
],
47+
},
2848
],
2949
}).compile();
3050

0 commit comments

Comments
 (0)