Skip to content

Commit 0d1d090

Browse files
authored
feat(server): implement policy schema & apis (#574)
1 parent c53d76a commit 0d1d090

File tree

7 files changed

+181
-43
lines changed

7 files changed

+181
-43
lines changed

server/package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"@nestjs/axios": "^1.0.0",
3030
"@nestjs/common": "^9.0.0",
3131
"@nestjs/config": "^2.2.0",
32-
"@nestjs/core": "^9.0.0",
32+
"@nestjs/core": "^9.2.1",
3333
"@nestjs/jwt": "^9.0.0",
3434
"@nestjs/mapped-types": "*",
3535
"@nestjs/passport": "^9.0.0",

server/prisma/schema.prisma

+12
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@ model ApplicationConfiguration {
130130
application Application @relation(fields: [appid], references: [appid])
131131
}
132132

133+
model DatabasePolicy {
134+
id String @id @default(auto()) @map("_id") @db.ObjectId
135+
appid String
136+
name String
137+
rules String
138+
injector String?
139+
createdAt DateTime @default(now())
140+
updatedAt DateTime @updatedAt
141+
142+
@@unique([appid, name])
143+
}
144+
133145
enum HttpMethod {
134146
GET
135147
POST
+12-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,12 @@
1-
export class CreatePolicyDto {}
1+
import { ApiProperty } from '@nestjs/swagger'
2+
import { IsNotEmpty } from 'class-validator'
3+
4+
export class CreatePolicyDto {
5+
@ApiProperty()
6+
@IsNotEmpty()
7+
name: string
8+
9+
@ApiProperty()
10+
@IsNotEmpty()
11+
rules: string
12+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { PartialType } from '@nestjs/mapped-types'
2-
import { CreatePolicyDto } from './create-policy.dto'
3-
4-
export class UpdatePolicyDto extends PartialType(CreatePolicyDto) {}
1+
import { ApiProperty } from '@nestjs/swagger'
2+
import { IsNotEmpty } from 'class-validator'
3+
export class UpdatePolicyDto {
4+
@ApiProperty()
5+
@IsNotEmpty()
6+
rules: string
7+
}

server/src/database/policy.controller.ts

+55-20
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,20 @@ import {
66
Patch,
77
Param,
88
Delete,
9+
UseGuards,
910
} from '@nestjs/common'
1011
import { PolicyService } from './policy.service'
1112
import { CreatePolicyDto } from './dto/create-policy.dto'
12-
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'
13+
import {
14+
ApiBearerAuth,
15+
ApiOperation,
16+
ApiResponse,
17+
ApiTags,
18+
} from '@nestjs/swagger'
1319
import { UpdatePolicyDto } from './dto/update-policy.dto'
20+
import { ResponseUtil } from 'src/utils/response'
21+
import { JwtAuthGuard } from 'src/auth/jwt.auth.guard'
22+
import { ApplicationAuthGuard } from 'src/auth/application.auth.guard'
1423

1524
@ApiTags('Database')
1625
@ApiBearerAuth('Authorization')
@@ -19,32 +28,58 @@ export class PolicyController {
1928
constructor(private readonly policiesService: PolicyService) {}
2029

2130
@Post()
22-
@ApiOperation({ summary: 'TODO - ⌛️' })
23-
create(@Body() createPolicyDto: CreatePolicyDto) {
24-
return this.policiesService.create(createPolicyDto)
31+
@ApiOperation({ summary: 'Create database policy' })
32+
@ApiResponse({ type: ResponseUtil })
33+
@UseGuards(JwtAuthGuard, ApplicationAuthGuard)
34+
async create(@Param('appid') appid: string, @Body() dto: CreatePolicyDto) {
35+
// check name existed
36+
const existed = await this.policiesService.findOne(appid, dto.name)
37+
if (existed) {
38+
return ResponseUtil.error('Policy name existed')
39+
}
40+
const doc = await this.policiesService.create(appid, dto)
41+
return ResponseUtil.ok(doc)
2542
}
2643

2744
@Get()
28-
@ApiOperation({ summary: 'TODO - ⌛️' })
29-
findAll() {
30-
return this.policiesService.findAll()
45+
@ApiOperation({ summary: 'Get database policy list' })
46+
@ApiResponse({ type: ResponseUtil })
47+
@UseGuards(JwtAuthGuard, ApplicationAuthGuard)
48+
async findAll(@Param('appid') appid: string) {
49+
const docs = await this.policiesService.findAll(appid)
50+
return ResponseUtil.ok(docs)
3151
}
3252

33-
@Get(':id')
34-
@ApiOperation({ summary: 'TODO - ⌛️' })
35-
findOne(@Param('id') id: string) {
36-
return this.policiesService.findOne(+id)
53+
@Patch(':name')
54+
@ApiOperation({ summary: 'Update policy rules' })
55+
@ApiResponse({ type: ResponseUtil })
56+
@UseGuards(JwtAuthGuard, ApplicationAuthGuard)
57+
async update(
58+
@Param('appid') appid: string,
59+
@Param('name') name: string,
60+
@Body() dto: UpdatePolicyDto,
61+
) {
62+
// check policy exists
63+
const existed = await this.policiesService.findOne(appid, name)
64+
if (!existed) {
65+
return ResponseUtil.error('Policy not found')
66+
}
67+
const res = await this.policiesService.update(appid, name, dto)
68+
return ResponseUtil.ok(res)
3769
}
3870

39-
@Patch(':id')
40-
@ApiOperation({ summary: 'TODO - ⌛️' })
41-
update(@Param('id') id: string, @Body() updatePolicyDto: UpdatePolicyDto) {
42-
return this.policiesService.update(+id, updatePolicyDto)
43-
}
71+
@Delete(':name')
72+
@ApiOperation({ summary: 'Remove a database policy' })
73+
@ApiResponse({ type: ResponseUtil })
74+
@UseGuards(JwtAuthGuard, ApplicationAuthGuard)
75+
async remove(@Param('appid') appid: string, @Param('name') name: string) {
76+
// check policy exists
77+
const existed = await this.policiesService.findOne(appid, name)
78+
if (!existed) {
79+
return ResponseUtil.error('Policy not found')
80+
}
4481

45-
@Delete(':id')
46-
@ApiOperation({ summary: 'TODO - ⌛️' })
47-
remove(@Param('id') id: string) {
48-
return this.policiesService.remove(+id)
82+
const res = await this.policiesService.remove(appid, name)
83+
return ResponseUtil.ok(res)
4984
}
5085
}

server/src/database/policy.service.ts

+87-10
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,103 @@
11
import { Injectable } from '@nestjs/common'
2+
import { DatabasePolicy } from '@prisma/client'
3+
import { CN_PUBLISHED_POLICIES } from 'src/constants'
4+
import { DatabaseCoreService } from 'src/core/database.cr.service'
5+
import { PrismaService } from 'src/prisma.service'
26
import { CreatePolicyDto } from './dto/create-policy.dto'
37
import { UpdatePolicyDto } from './dto/update-policy.dto'
48

59
@Injectable()
610
export class PolicyService {
7-
create(createPolicyDto: CreatePolicyDto) {
8-
return 'This action adds a new policy'
11+
constructor(
12+
private readonly prisma: PrismaService,
13+
private readonly db: DatabaseCoreService,
14+
) {}
15+
16+
async create(appid: string, createPolicyDto: CreatePolicyDto) {
17+
const res = await this.prisma.databasePolicy.create({
18+
data: {
19+
appid,
20+
name: createPolicyDto.name,
21+
rules: createPolicyDto.rules,
22+
},
23+
})
24+
25+
await this.publish(res)
26+
return res
27+
}
28+
29+
async findAll(appid: string) {
30+
const res = await this.prisma.databasePolicy.findMany({
31+
where: {
32+
appid,
33+
},
34+
})
35+
return res
936
}
1037

11-
findAll() {
12-
return `This action returns all policies`
38+
async findOne(appid: string, name: string) {
39+
const res = await this.prisma.databasePolicy.findUnique({
40+
where: {
41+
appid_name: {
42+
appid,
43+
name,
44+
},
45+
},
46+
})
47+
return res
48+
}
49+
50+
async update(appid: string, name: string, dto: UpdatePolicyDto) {
51+
const res = await this.prisma.databasePolicy.update({
52+
where: {
53+
appid_name: {
54+
appid,
55+
name,
56+
},
57+
},
58+
data: {
59+
rules: dto.rules,
60+
},
61+
})
62+
63+
await this.publish(res)
64+
return res
1365
}
1466

15-
findOne(id: number) {
16-
return `This action returns a #${id} policy`
67+
async remove(appid: string, name: string) {
68+
const res = await this.prisma.databasePolicy.delete({
69+
where: {
70+
appid_name: {
71+
appid,
72+
name,
73+
},
74+
},
75+
})
76+
await this.unpublish(res)
77+
return res
1778
}
1879

19-
update(id: number, updatePolicyDto: UpdatePolicyDto) {
20-
return `This action updates a #${id} policy`
80+
async publish(policy: DatabasePolicy) {
81+
const { db, client } = await this.db.findAndConnect(policy.appid)
82+
const session = client.startSession()
83+
try {
84+
await session.withTransaction(async () => {
85+
const coll = db.collection(CN_PUBLISHED_POLICIES)
86+
await coll.deleteOne({ name: policy.name }, { session })
87+
await coll.insertOne(policy, { session })
88+
})
89+
} finally {
90+
await client.close()
91+
}
2192
}
2293

23-
remove(id: number) {
24-
return `This action removes a #${id} policy`
94+
async unpublish(appid: string, name: string) {
95+
const { db, client } = await this.db.findAndConnect(appid)
96+
try {
97+
const coll = db.collection(CN_PUBLISHED_POLICIES)
98+
await coll.deleteOne({ name })
99+
} finally {
100+
await client.close()
101+
}
25102
}
26103
}

0 commit comments

Comments
 (0)