Skip to content

Commit d99dfd7

Browse files
committed
Feature: 新增、修改接口,显示所有可用table接口;修改entity config结构
1 parent 39913c7 commit d99dfd7

14 files changed

+179
-31
lines changed

README.md

+7-12
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,14 @@ This example repo uses the NestJS swagger module for API documentation. [NestJS
5656

5757
# 使用文档
5858

59+
5960
> 以下例子中 默认存在两张表(Comment, User),实际使用时,需要在添加对应的entity,在service中引入即可
6061
62+
查看当前可用的table 可访问 `GET /table`
63+
64+
- 通用查询接口 `POST /apijson/get`
65+
- 通用新增接口 `POST /apijson/add`
66+
- 通用修改接口 `POST /apijson/update`
6167

6268
已经实现的操作符
6369

@@ -85,7 +91,6 @@ This example repo uses the NestJS swagger module for API documentation. [NestJS
8591
}
8692
}
8793
```
88-
![list](./asserts/list.png)
8994
- \#
9095

9196
操作符名称: 别名
@@ -113,10 +118,6 @@ This example repo uses the NestJS swagger module for API documentation. [NestJS
113118
}
114119
}
115120
```
116-
117-
![column](./asserts/column.png)
118-
119-
120121
- 联表查询
121122

122123
例子:
@@ -132,8 +133,6 @@ This example repo uses the NestJS swagger module for API documentation. [NestJS
132133
}
133134
}
134135
```
135-
![union-query](./asserts/union-query.png)
136-
137136
```json
138137
// 查询所有符合条件的comment 显示 第1页 每页2条
139138
// (因为默认page = 1 count = 10 所以默认最多为10条)
@@ -151,8 +150,6 @@ This example repo uses the NestJS swagger module for API documentation. [NestJS
151150
}
152151

153152
```
154-
![union-list-query](./asserts/union-list-query.png)
155-
156153

157154
- 综合例子
158155

@@ -174,6 +171,4 @@ This example repo uses the NestJS swagger module for API documentation. [NestJS
174171
}
175172
}
176173
}
177-
```
178-
179-
![mix](./asserts/mix.png)
174+
```

asserts/column.png

-41.4 KB
Binary file not shown.

asserts/list.png

-57.2 KB
Binary file not shown.

asserts/mix.png

-83.6 KB
Binary file not shown.

asserts/union-list-query.png

-74.7 KB
Binary file not shown.

asserts/union-query.png

-56.8 KB
Binary file not shown.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "nestjs-realworld-example-app",
2+
"name": "apijson-node",
33
"version": "1.0.0",
44
"description": "",
55
"main": "index.js",

pm2.config.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"apps": [{
3-
"name": "api-json-node",
3+
"name": "apijson-node",
44
"script": "./dist/http/main.js",
55
"log_date_format": "YYYY-MM-DD HH:mm:ss",
66
"instances" : 4,

src/core/traverse.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export interface JobInterface {
77
table: string,
88
options: any,
99
ListOptions?: any,
10-
replacePath: string[]
10+
replacePath: string[],
11+
config?: any
1112
}
1213

1314
interface listUnionOptionsInterface {
@@ -74,7 +75,7 @@ export class RequestParser {
7475
return
7576
}
7677
// 校验column合法性,entity合法性,或为别名(#结尾)的情况
77-
const COLUMN_LIST = safeGet(TABLE_CONFIG, `${parentTableName}.column`, [])
78+
const COLUMN_LIST = safeGet(TABLE_CONFIG, `${parentTableName}.column`, []).map(i => i.key)
7879
if ([
7980
...ENTITY_LIST,
8081
...COLUMN_LIST,
@@ -161,7 +162,7 @@ export class RequestParser {
161162
return
162163
} else {
163164
let queryTable = targetTables[0]
164-
const { column } = TABLE_CONFIG[queryTable] || []
165+
const column = TABLE_CONFIG[queryTable] && TABLE_CONFIG[queryTable].column.map(i => i.key) || []
165166
const options = {}
166167
const ListOptions = {
167168
page: 1,
@@ -180,7 +181,6 @@ export class RequestParser {
180181
} else if (column.indexOf(normalizedKey) > -1) {
181182
// 数组中的联表查询
182183
LIST_JOB_FLAG = false
183-
console.log('??', key, subKey, field, parentPath)
184184
listUnionOptions = {
185185
key: subKey,
186186
body: body[field][subKey],
@@ -223,7 +223,7 @@ export class RequestParser {
223223

224224
handleSingleJob(key: string, body: any, parentPath: string[]): void {
225225
console.log('start SINGLE_JOB', key, body, parentPath)
226-
const column = TABLE_CONFIG[key] && TABLE_CONFIG[key].column || []
226+
const column = TABLE_CONFIG[key] && TABLE_CONFIG[key].column.map(i => i.key) || []
227227

228228
let SINGLE_JOB_FLAG = true
229229

@@ -263,6 +263,12 @@ export class RequestParser {
263263
}
264264

265265
createJob (job: JobInterface, tail: boolean = true) {
266+
job = {
267+
config: {
268+
primary: safeGet(TABLE_CONFIG, `${job.table}.primary`, '')
269+
},
270+
...job
271+
}
266272
if (tail) {
267273
this.queue.push(job)
268274
} else {

src/entities/comment.entity.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,16 @@ export class CommentEntity {
1313
}
1414

1515
export const Comment = {
16-
column: ['id', 'comment', 'userId']
16+
column: [{
17+
key: 'id',
18+
desc: 'ID'
19+
}, {
20+
key: 'comment',
21+
desc: '评论'
22+
}, {
23+
key: 'userId',
24+
desc: '关联的用户Id'
25+
}],
26+
primary: 'id',
27+
desc: '评论表'
1728
}

src/entities/user.entity.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,19 @@ export class UserEntity {
1616
}
1717

1818
export const User = {
19-
column: ['id', 'user', 'nickname', 'role']
19+
column: [{
20+
key: 'id',
21+
desc: 'ID'
22+
}, {
23+
key: 'user',
24+
desc: '用户名'
25+
}, {
26+
key: 'nickname',
27+
desc: '昵称'
28+
}, {
29+
key: 'role',
30+
desc: '角色'
31+
}],
32+
primary: ['id'],
33+
desc: '用户表'
2034
}

src/http/app.controller.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Get, Controller } from '@nestjs/common'
2+
import { config } from '../entities'
23

34
@Controller()
45
export class AppController {
@@ -9,4 +10,9 @@ export class AppController {
910
root(): string {
1011
return 'ok'
1112
}
13+
14+
@Get('/table')
15+
getTable(): any {
16+
return config
17+
}
1218
}

src/http/json/json.controller.ts

+111-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Post, Controller, Body, HttpException, HttpStatus } from '@nestjs/common'
1+
import { Post, Controller, Body, HttpException, HttpStatus, Logger } from '@nestjs/common'
22
import { ApiUseTags, ApiBearerAuth } from '@nestjs/swagger'
33
import { set as safeSet, get as safeGet } from 'lodash'
44

@@ -7,25 +7,129 @@ import { jsonBodyParser } from '../../core'
77
import { successHandler, baseHandler, HTTP_CODE } from '../../helper'
88
import { JOB_TYPE } from '../../core/traverse'
99

10+
function throwError(msg: string, code: number) {
11+
Logger.error(msg)
12+
throw new HttpException(msg, code)
13+
}
14+
1015
@ApiBearerAuth()
1116
@ApiUseTags('apijson')
1217
@Controller('apijson')
1318
export class JsonController {
1419
constructor(private readonly jsonService: JsonService) {}
1520

16-
@Post()
21+
@Post('/update')
22+
async update(@Body() queryBody: object): Promise<any> {
23+
24+
const parserResult = jsonBodyParser(queryBody)
25+
if (!parserResult) {
26+
throwError('Too many requests, please try again later', HttpStatus.TOO_MANY_REQUESTS)
27+
}
28+
29+
const { queue:jobList, errors, originalJson, RequestParser } = parserResult
30+
console.log('RELEASE requestParser instance')
31+
RequestParser.flushJob()
32+
if (errors.length) {
33+
throwError(errors.join('; '), HttpStatus.BAD_REQUEST)
34+
}
35+
36+
if (jobList.find(job => {
37+
return job.$$type === JOB_TYPE.LIST_UNION || job.$$type === JOB_TYPE.UNION
38+
})) {
39+
throwError(
40+
`[ERROR] update request is not support union operation "@", please check your request body`,
41+
HttpStatus.BAD_REQUEST)
42+
}
43+
44+
if (jobList.length > 1) {
45+
throwError(
46+
`[ERROR] update request is not support transaction insert, please request single table payload.`,
47+
HttpStatus.BAD_REQUEST)
48+
}
49+
50+
while (jobList.length) {
51+
let currentJob = jobList.pop()
52+
console.log('JOB ID =>', currentJob.id)
53+
const primaryKey = currentJob.config.primary
54+
if (!currentJob.options[primaryKey]) {
55+
throwError(`[ERROR] "${primaryKey}" is required in update method`, HttpStatus.BAD_REQUEST)
56+
}
57+
try {
58+
await this.jsonService.update(
59+
currentJob.options[primaryKey],
60+
currentJob.table,
61+
currentJob.options
62+
)
63+
} catch (e) {
64+
throwError(e.message, HttpStatus.BAD_REQUEST)
65+
}
66+
}
67+
68+
return successHandler({
69+
message: '[INFO] update success',
70+
requestBody: originalJson
71+
})
72+
}
73+
74+
@Post('/add')
75+
async add(@Body() queryBody: object): Promise<any> {
76+
77+
const parserResult = jsonBodyParser(queryBody)
78+
if (!parserResult) {
79+
throwError('Too many requests, please try again later', HttpStatus.TOO_MANY_REQUESTS)
80+
}
81+
82+
const { queue:jobList, errors, originalJson, RequestParser } = parserResult
83+
console.log('RELEASE requestParser instance')
84+
RequestParser.flushJob()
85+
if (errors.length) {
86+
throwError(errors.join('; '), HttpStatus.BAD_REQUEST)
87+
}
88+
89+
if (jobList.find(job => {
90+
return job.$$type === JOB_TYPE.LIST_UNION || job.$$type === JOB_TYPE.UNION
91+
})) {
92+
throwError(
93+
`[ERROR] add request is not support union operation "@", please check your request body`,
94+
HttpStatus.BAD_REQUEST)
95+
}
96+
97+
if (jobList.length > 1) {
98+
throwError(
99+
`[ERROR] add request is not support transaction insert, please request single table payload.`,
100+
HttpStatus.BAD_REQUEST)
101+
}
102+
while (jobList.length) {
103+
let currentJob = jobList.pop()
104+
console.log('JOB ID =>', currentJob.id)
105+
106+
const {...payload} = currentJob.options // incase for functional column
107+
try {
108+
await this.jsonService.insert(currentJob.table, payload)
109+
} catch (e) {
110+
throwError(e.message, HttpStatus.BAD_REQUEST)
111+
}
112+
}
113+
114+
return successHandler({
115+
message: '[INFO] insert success',
116+
requestBody: originalJson
117+
})
118+
}
119+
120+
@Post('/get')
17121
async query(@Body() queryBody: object): Promise<any> {
18122

19123
const parserResult = jsonBodyParser(queryBody)
20124
if (!parserResult) {
21-
throw new HttpException('Too many requests, please try again later', HttpStatus.TOO_MANY_REQUESTS)
125+
throwError('Too many requests, please try again later', HttpStatus.TOO_MANY_REQUESTS)
22126
}
23127

24128
const { queue:jobList, errors, originalJson, RequestParser } = parserResult
25129
console.log('RELEASE requestParser instance')
26130
RequestParser.flushJob()
27131
if (errors.length) {
28-
throw new HttpException(errors.join('; '), HttpStatus.BAD_REQUEST)
132+
throwError(errors.join('; '), HttpStatus.BAD_REQUEST)
29133
}
30134
let res = JSON.parse(JSON.stringify(originalJson))
31135
if (!jobList.length) {
@@ -37,6 +141,7 @@ export class JsonController {
37141
}
38142

39143
const normalizedData = data => {
144+
if (!data) return {}
40145
Object.keys(data).forEach(key => {
41146
const normalizedKey = key
42147
.replace(/\#/g, '') // replace "#" operation arg
@@ -76,7 +181,7 @@ export class JsonController {
76181
const { referArgs, referVar } = currentJob.options
77182
const referData = safeGet(res, referArgs, undefined)
78183
if (!referData) {
79-
throw new HttpException(`[ERROR] union job exec failed, for "${referArgs.join('/')}" is undefined`, HttpStatus.BAD_REQUEST)
184+
throwError(`[ERROR] union job exec failed, for "${referArgs.join('/')}" is undefined`, HttpStatus.BAD_REQUEST)
80185
}
81186
data = await this.jsonService.findOne(currentJob.table, {
82187
[referVar]: referData
@@ -89,7 +194,7 @@ export class JsonController {
89194

90195
const referData = safeGet(res, referArgs, undefined)
91196
if (!referData) {
92-
throw new HttpException(`[ERROR] union job exec failed, for "${referArgs.join('/')}" is undefined`, HttpStatus.BAD_REQUEST)
197+
throwError(`[ERROR] union job exec failed, for "${referArgs.join('/')}" is undefined`, HttpStatus.BAD_REQUEST)
93198
}
94199
data = await this.jsonService.find(currentJob.table, {
95200
[referVar]: referData,

src/http/json/json.service.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,31 @@ import { UserEntity, CommentEntity } from '../../entities'
77
@Injectable()
88
export class JsonService {
99
@InjectRepository(UserEntity)
10-
private readonly userRepository: Repository<UserEntity>
10+
private readonly UserRepository: Repository<UserEntity>
1111
@InjectRepository(CommentEntity)
12-
private readonly commentRepository: Repository<CommentEntity>
12+
private readonly CommentRepository: Repository<CommentEntity>
13+
14+
async insert(entityName: string, payload: any = {}): Promise<any> {
15+
const currentRepository = this[`${entityName}Repository`]
16+
17+
return currentRepository && currentRepository.insert(payload)
18+
}
19+
20+
async update(id: any, entityName: string, payload: any = {}): Promise<any> {
21+
const currentRepository = this[`${entityName}Repository`]
22+
return currentRepository && currentRepository.update(id, payload)
23+
}
1324

1425
async findOne(entityName: string, options: any = {}): Promise<any> {
15-
const currentRepository = this[`${entityName.toLowerCase()}Repository`]
26+
const currentRepository = this[`${entityName}Repository`]
1627

1728
return currentRepository && currentRepository.findOne(options)
1829
}
1930

2031
async find(entityName: string, options: any = {}, listOptions: any = {
2132
page: 1, count: 10
2233
}): Promise<any[]> {
23-
const currentRepository = this[`${entityName.toLowerCase()}Repository`]
34+
const currentRepository = this[`${entityName}Repository`]
2435
const { select, ...restOptions} = options
2536
return currentRepository && currentRepository.find({
2637
where: {

0 commit comments

Comments
 (0)