Skip to content

Commit bb60c16

Browse files
author
jianggang
authored
new Feature webhook (#41)
add webhook feature
1 parent 2334086 commit bb60c16

78 files changed

Lines changed: 2083 additions & 292 deletions

File tree

Some content is hidden

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

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ RUN apk --no-cache add curl
55

66
ENV JVM_ARGS=${JVM_ARGS}
77

8-
COPY feature-probe-admin/target/feature-probe-admin-1.0.0.jar feature-probe-admin-1.0.0.jar
8+
COPY feature-probe-admin/target/feature-probe-api.jar feature-probe-api.jar
99

1010

1111

12-
ENTRYPOINT java ${JVM_ARGS} -jar feature-probe-admin-1.0.0.jar
12+
ENTRYPOINT java ${JVM_ARGS} -jar feature-probe-api.jar

feature-probe-admin/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
</dependencies>
6767

6868
<build>
69+
<finalName>feature-probe-api</finalName>
6970
<pluginManagement>
7071
<plugins>
7172
<plugin>
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.featureprobe.api.aop;
2+
3+
import com.featureprobe.api.auth.TokenHelper;
4+
import com.featureprobe.api.base.hook.Action;
5+
import com.featureprobe.api.base.hook.IHookQueue;
6+
import com.featureprobe.api.base.hook.Resource;
7+
import com.featureprobe.api.base.hook.Hook;
8+
import com.featureprobe.api.base.model.HookContext;
9+
import com.featureprobe.api.base.tenant.TenantContext;
10+
import lombok.extern.slf4j.Slf4j;
11+
import org.apache.commons.lang3.StringUtils;
12+
import org.aspectj.lang.JoinPoint;
13+
import org.aspectj.lang.ProceedingJoinPoint;
14+
import org.aspectj.lang.annotation.Around;
15+
import org.aspectj.lang.annotation.Aspect;
16+
import org.aspectj.lang.annotation.Pointcut;
17+
import org.aspectj.lang.reflect.MethodSignature;
18+
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
19+
import org.springframework.stereotype.Component;
20+
import org.springframework.web.bind.annotation.PathVariable;
21+
import org.springframework.web.bind.annotation.RequestBody;
22+
import java.lang.annotation.Annotation;
23+
import java.lang.reflect.Method;
24+
import java.util.Objects;
25+
26+
@Slf4j
27+
@Aspect
28+
@Component
29+
public class HookAspect {
30+
31+
@javax.annotation.Resource
32+
IHookQueue hookQueue;
33+
34+
@Pointcut("@annotation(com.featureprobe.api.base.hook.Hook)")
35+
public void hook (){}
36+
37+
@Around("hook()")
38+
public Object around(ProceedingJoinPoint jp) throws Throwable {
39+
Hook webHookAnnotation = getMethodAnnotation(jp, Hook.class);
40+
Action action = webHookAnnotation.action();
41+
Resource resource = webHookAnnotation.resource();
42+
HookContext context = new HookContext(resource, action);
43+
context.setOperator(TokenHelper.getAccount());
44+
context.setOrganizationId(Long.parseLong(TenantContext.getCurrentTenant()));
45+
Object ret = jp.proceed();
46+
composeParam(jp, context);
47+
context.setResponse(ret);
48+
hookQueue.push(context);
49+
return ret;
50+
}
51+
52+
private void composeParam(ProceedingJoinPoint jp, HookContext context) {
53+
Object requestBody = getRequestBody(jp);
54+
if (Objects.nonNull(requestBody)) {
55+
context.setRequest(requestBody);
56+
}
57+
String projectKey = getProjectKeyArg(jp);
58+
if (StringUtils.isNotBlank(projectKey)) {
59+
context.setProjectKey(projectKey);
60+
}
61+
String environmentKey = getEnvironmentKeyArg(jp);
62+
if(StringUtils.isNotBlank(environmentKey)) {
63+
context.setEnvironmentKey(environmentKey);
64+
}
65+
String toggleKey = getToggleKeyArg(jp);
66+
if (StringUtils.isNotBlank(toggleKey)) {
67+
context.setToggleKey(toggleKey);
68+
}
69+
String segmentKey = getSegmentKeyArg(jp);
70+
if (StringUtils.isNotBlank(segmentKey)) {
71+
context.setSegmentKey(segmentKey);
72+
}
73+
Long id = getIdArg(jp);
74+
if (Objects.nonNull(id)){
75+
context.setId(id);
76+
}
77+
}
78+
79+
private Object getRequestBody(ProceedingJoinPoint jp) {
80+
MethodSignature signature = (MethodSignature) jp.getSignature();
81+
Method method = signature.getMethod();
82+
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
83+
Object[] args = jp.getArgs();
84+
for (int i = 0 ; i < parameterAnnotations.length; i++) {
85+
for (Annotation annotation : parameterAnnotations[i]) {
86+
if (annotation instanceof RequestBody) {
87+
return args[i];
88+
}
89+
}
90+
}
91+
return null;
92+
}
93+
94+
private String getProjectKeyArg(ProceedingJoinPoint jp) {
95+
Object projectKey = getPathArg(jp, "projectKey");
96+
if (Objects.isNull(projectKey)) {
97+
return null;
98+
}
99+
return String.valueOf(projectKey);
100+
}
101+
102+
private String getEnvironmentKeyArg(ProceedingJoinPoint jp) {
103+
Object environmentKey = getPathArg(jp, "environmentKey");
104+
if (Objects.isNull(environmentKey)) {
105+
return null;
106+
}
107+
return String.valueOf(environmentKey);
108+
}
109+
110+
private String getToggleKeyArg(ProceedingJoinPoint jp) {
111+
Object toggleKey = getPathArg(jp, "toggleKey");
112+
if (Objects.isNull(toggleKey)) {
113+
return null;
114+
}
115+
return String.valueOf(toggleKey);
116+
}
117+
118+
private String getSegmentKeyArg(ProceedingJoinPoint jp) {
119+
Object segmentKey = getPathArg(jp, "segmentKey");
120+
if (Objects.isNull(segmentKey)) {
121+
return null;
122+
}
123+
return String.valueOf(segmentKey);
124+
}
125+
126+
private Long getIdArg(ProceedingJoinPoint jp) {
127+
Object idArg = getPathArg(jp, "id");
128+
Long id = null;
129+
if (Objects.isNull(idArg)) {
130+
return null;
131+
}
132+
try {
133+
id = (Long) idArg;
134+
} catch (Exception e) {
135+
return null;
136+
}
137+
return id;
138+
}
139+
140+
private Object getPathArg(ProceedingJoinPoint jp, String name) {
141+
MethodSignature signature = (MethodSignature) jp.getSignature();
142+
Method method = signature.getMethod();
143+
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
144+
Object[] args = jp.getArgs();
145+
for (int i = 0 ; i < parameterAnnotations.length; i++) {
146+
for (Annotation annotation : parameterAnnotations[i]) {
147+
if (annotation instanceof PathVariable && ((PathVariable) annotation).value().equals(name)) {
148+
return args[i];
149+
}
150+
}
151+
}
152+
return null;
153+
}
154+
155+
private <T extends Annotation> T getMethodAnnotation(JoinPoint joinPoint, Class<T> clazz) {
156+
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
157+
Method method = methodSignature.getMethod();
158+
return method.getAnnotation(clazz);
159+
}
160+
161+
}

feature-probe-admin/src/main/java/com/featureprobe/api/controller/EnvironmentController.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import com.featureprobe.api.base.doc.DefaultApiResponses;
55
import com.featureprobe.api.base.doc.GetApiResponse;
66
import com.featureprobe.api.base.doc.PatchApiResponse;
7+
import com.featureprobe.api.base.hook.Action;
8+
import com.featureprobe.api.base.hook.Hook;
9+
import com.featureprobe.api.base.hook.Resource;
710
import com.featureprobe.api.dto.EnvironmentCreateRequest;
811
import com.featureprobe.api.dto.EnvironmentQueryRequest;
912
import com.featureprobe.api.dto.EnvironmentResponse;
@@ -44,6 +47,7 @@ public class EnvironmentController {
4447
@PostMapping
4548
@CreateApiResponse
4649
@Operation(summary = "Create environment", description = "Create a new environment.")
50+
@Hook(resource = Resource.ENVIRONMENT, action = Action.CREATE)
4751
public EnvironmentResponse create(@PathVariable("projectKey") String projectKey,
4852
@RequestBody @Validated EnvironmentCreateRequest createRequest) {
4953
includeArchivedEnvironmentService.validateIncludeArchivedEnvironmentByKey(projectKey, createRequest.getKey());
@@ -54,12 +58,31 @@ public EnvironmentResponse create(@PathVariable("projectKey") String projectKey,
5458
@PatchMapping("/{environmentKey}")
5559
@PatchApiResponse
5660
@Operation(summary = "Update environment", description = "Update a environment.")
61+
@Hook(resource = Resource.ENVIRONMENT, action = Action.UPDATE)
5762
public EnvironmentResponse update(@PathVariable("projectKey") String projectKey,
5863
@PathVariable("environmentKey") String environmentKey,
5964
@RequestBody @Validated EnvironmentUpdateRequest updateRequest) {
6065
return environmentService.update(projectKey, environmentKey, updateRequest);
6166
}
6267

68+
@PatchMapping("/{environmentKey}/offline")
69+
@PatchApiResponse
70+
@Operation(summary = "Offline environment", description = "Offline a environment.")
71+
@Hook(resource = Resource.ENVIRONMENT, action = Action.OFFLINE)
72+
public EnvironmentResponse offline(@PathVariable("projectKey") String projectKey,
73+
@PathVariable("environmentKey") String environmentKey) {
74+
return environmentService.offline(projectKey, environmentKey);
75+
}
76+
77+
@PatchMapping("/{environmentKey}/restore")
78+
@PatchApiResponse
79+
@Operation(summary = "Restore environment", description = "Restore a offline environment.")
80+
@Hook(resource = Resource.ENVIRONMENT, action = Action.RESTORE)
81+
public EnvironmentResponse restore(@PathVariable("projectKey") String projectKey,
82+
@PathVariable("environmentKey") String environmentKey) {
83+
return environmentService.restore(projectKey, environmentKey);
84+
}
85+
6386
@GetMapping
6487
@GetApiResponse
6588
@Operation(summary = "List environment", description = "Get a list of all environment")

feature-probe-admin/src/main/java/com/featureprobe/api/controller/MemberController.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
import com.featureprobe.api.auth.TokenHelper;
44
import com.featureprobe.api.base.doc.DefaultApiResponses;
5+
import com.featureprobe.api.base.hook.Action;
6+
import com.featureprobe.api.base.hook.Hook;
7+
import com.featureprobe.api.base.hook.Resource;
58
import com.featureprobe.api.dto.MemberCreateRequest;
69
import com.featureprobe.api.dto.MemberDeleteRequest;
710
import com.featureprobe.api.dto.MemberModifyPasswordRequest;
11+
import com.featureprobe.api.dto.MemberItemResponse;
812
import com.featureprobe.api.dto.MemberResponse;
913
import com.featureprobe.api.dto.MemberSearchRequest;
1014
import com.featureprobe.api.dto.MemberUpdateRequest;
@@ -39,47 +43,51 @@ public class MemberController {
3943
@GetMapping("/current")
4044
@Operation(summary = "Current login member", description = "")
4145
@PreAuthorize("hasAnyAuthority('OWNER', 'WRITER')")
42-
public MemberResponse currentLoginMember() {
43-
return new MemberResponse(TokenHelper.getAccount(), TokenHelper.getRole());
46+
public MemberItemResponse currentLoginMember() {
47+
return new MemberItemResponse(TokenHelper.getAccount(), TokenHelper.getRole());
4448
}
4549

4650
@PostMapping
4751
@Operation(summary = "Create member", description = "Create a new member")
4852
@PreAuthorize("hasAuthority('OWNER')")
53+
@Hook(resource = Resource.MEMBER, action = Action.CREATE)
4954
public List<MemberResponse> create(@Validated @RequestBody MemberCreateRequest createRequest) {
5055
return memberService.create(createRequest);
5156
}
5257

5358
@GetMapping
5459
@Operation(summary = "List Member", description = "Get a list of all member")
5560
@PreAuthorize("hasAnyAuthority('OWNER', 'WRITER')")
56-
public Page<MemberResponse> list(MemberSearchRequest searchRequest) {
61+
public Page<MemberItemResponse> list(MemberSearchRequest searchRequest) {
5762
return memberService.list(searchRequest);
5863
}
5964

6065
@PatchMapping
6166
@Operation(summary = "Update member", description = "Update a member")
6267
@PreAuthorize("hasAuthority('OWNER')")
68+
@Hook(resource = Resource.MEMBER, action = Action.UPDATE)
6369
public MemberResponse update(@Validated @RequestBody MemberUpdateRequest updateRequest) {
6470
return memberService.update(updateRequest);
6571
}
6672

6773
@PatchMapping("/modifyPassword")
6874
@Operation(summary = "Modify member password", description = "Modify a member password")
69-
public MemberResponse modifyPassword(@Validated @RequestBody MemberModifyPasswordRequest modifyPasswordRequest) {
75+
public MemberItemResponse modifyPassword(
76+
@Validated @RequestBody MemberModifyPasswordRequest modifyPasswordRequest) {
7077
return memberService.modifyPassword(modifyPasswordRequest);
7178
}
7279

7380
@DeleteMapping
7481
@Operation(summary = "Delete member", description = "Logical delete a member")
7582
@PreAuthorize("hasAuthority('OWNER')")
83+
@Hook(resource = Resource.MEMBER, action = Action.DELETE)
7684
public MemberResponse delete(@Validated @RequestBody MemberDeleteRequest deleteRequest) {
7785
return memberService.delete(deleteRequest.getAccount());
7886
}
7987

8088
@GetMapping("/query")
8189
@Operation(summary = "Get member", description = "Get a single member by account.")
82-
public MemberResponse query(String account) {
90+
public MemberItemResponse query(String account) {
8391
return memberService.queryByAccount(account);
8492
}
8593
}

feature-probe-admin/src/main/java/com/featureprobe/api/controller/ProjectController.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
import com.featureprobe.api.base.doc.CreateApiResponse;
44
import com.featureprobe.api.base.doc.DefaultApiResponses;
5+
import com.featureprobe.api.base.doc.DeleteApiResponse;
56
import com.featureprobe.api.base.doc.GetApiResponse;
67
import com.featureprobe.api.base.doc.PatchApiResponse;
78
import com.featureprobe.api.base.doc.ProjectKeyParameter;
9+
import com.featureprobe.api.base.hook.Action;
10+
import com.featureprobe.api.base.hook.Resource;
11+
import com.featureprobe.api.base.hook.Hook;
812
import com.featureprobe.api.dto.ApprovalSettings;
913
import com.featureprobe.api.dto.PreferenceCreateRequest;
1014
import com.featureprobe.api.dto.ProjectCreateRequest;
@@ -21,6 +25,7 @@
2125
import lombok.AllArgsConstructor;
2226
import lombok.extern.slf4j.Slf4j;
2327
import org.springframework.validation.annotation.Validated;
28+
import org.springframework.web.bind.annotation.DeleteMapping;
2429
import org.springframework.web.bind.annotation.GetMapping;
2530
import org.springframework.web.bind.annotation.PatchMapping;
2631
import org.springframework.web.bind.annotation.PathVariable;
@@ -47,18 +52,28 @@ public class ProjectController {
4752
@PostMapping
4853
@CreateApiResponse
4954
@Operation(summary = "Create project", description = "Create a new project.")
55+
@Hook(resource = Resource.PROJECT, action = Action.CREATE)
5056
public ProjectResponse create(@RequestBody @Validated ProjectCreateRequest createRequest) {
5157
return projectService.create(createRequest);
5258
}
5359

5460
@PatchMapping("/{projectKey}")
5561
@PatchApiResponse
5662
@Operation(summary = "Update project", description = "Update a project.")
63+
@Hook(resource = Resource.PROJECT, action = Action.UPDATE)
5764
public ProjectResponse update(@PathVariable("projectKey") String projectKey,
5865
@RequestBody @Validated ProjectUpdateRequest updateRequest) {
5966
return projectService.update(projectKey, updateRequest);
6067
}
6168

69+
@DeleteMapping("/{projectKey}")
70+
@DeleteApiResponse
71+
@Operation(summary = "Delete project", description = "Delete a project.")
72+
@Hook(resource = Resource.PROJECT, action = Action.DELETE)
73+
public ProjectResponse delete(@PathVariable("projectKey") String projectKey) {
74+
return projectService.delete(projectKey);
75+
}
76+
6277
@GetMapping
6378
@GetApiResponse
6479
@Operation(summary = "List projects", description = "Get a list of all project")
@@ -84,19 +99,20 @@ public BaseResponse exists(
8499
return new BaseResponse(ResponseCodeEnum.SUCCESS);
85100
}
86101

87-
@PostMapping("/{projectKey}")
88-
@CreateApiResponse
89-
@Operation(summary = "Save project setting", description = "Update a project settings.")
90-
public BaseResponse preference(@PathVariable("projectKey") String projectKey,
102+
@PatchMapping("/{projectKey}/approvalSettings")
103+
@PatchApiResponse
104+
@Operation(summary = "Update project approval settings", description = "Update a project approval settings.")
105+
@Hook(resource = Resource.PROJECT, action = Action.UPDATE_APPROVAL_SETTINGS)
106+
public List<ApprovalSettings> updateApprovalSettings(@PathVariable("projectKey") String projectKey,
91107
@RequestBody @Validated PreferenceCreateRequest createRequest) {
92-
projectService.createPreference(projectKey, createRequest);
93-
return new BaseResponse(ResponseCodeEnum.SUCCESS);
108+
return projectService.updateApprovalSettings(projectKey, createRequest);
94109
}
95110

96111
@GetMapping("/{projectKey}/approvalSettings")
97-
@CreateApiResponse
112+
@GetApiResponse
98113
@Operation(summary = "Query project settings", description = "Query a project settings.")
99114
public List<ApprovalSettings> approvalSettingsList(@PathVariable("projectKey") String projectKey) {
100115
return projectService.approvalSettingsList(projectKey);
101116
}
117+
102118
}

0 commit comments

Comments
 (0)