Skip to content

Commit d06a720

Browse files
committed
feat: add application version rollback capability
- Add rollbackToVersionId to ApplicationPublishRequest - Implement publishWithRollback method for DSL restoration - Add version DSL preview endpoint - Update controller logic to handle rollback requests
1 parent 9a52ed3 commit d06a720

File tree

10 files changed

+207
-95
lines changed

10 files changed

+207
-95
lines changed

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.lowcoder.api.application;
22

3-
import jakarta.annotation.Nonnull;
3+
import java.util.List;
4+
import java.util.Map;
5+
import java.util.Set;
6+
47
import org.lowcoder.api.application.view.ApplicationInfoView;
58
import org.lowcoder.api.application.view.ApplicationPermissionView;
69
import org.lowcoder.api.application.view.ApplicationPublishRequest;
@@ -10,14 +13,11 @@
1013
import org.lowcoder.domain.permission.model.ResourceAction;
1114
import org.lowcoder.domain.permission.model.ResourcePermission;
1215
import org.lowcoder.domain.permission.model.ResourceRole;
13-
import org.springframework.web.bind.annotation.PathVariable;
1416

17+
import jakarta.annotation.Nonnull;
1518
import reactor.core.publisher.Flux;
1619
import reactor.core.publisher.Mono;
1720

18-
import java.util.List;
19-
import java.util.Set;
20-
2121
public interface ApplicationApiService {
2222
Mono<ApplicationView> create(ApplicationEndpoints.CreateApplicationRequest createApplicationRequest);
2323

@@ -39,6 +39,8 @@ public interface ApplicationApiService {
3939

4040
Mono<ApplicationView> publish(String applicationId, ApplicationPublishRequest applicationPublishRequest);
4141

42+
Mono<ApplicationView> publishWithRollback(String applicationId, ApplicationPublishRequest applicationPublishRequest, Map<String, Object> rollbackDsl);
43+
4244
Mono<Boolean> updateEditState(String applicationId, ApplicationEndpoints.UpdateEditStateRequest updateEditStateRequest);
4345

4446
Mono<Boolean> grantPermission(String applicationId,

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiServiceImpl.java

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
package org.lowcoder.api.application;
22

3-
import com.github.f4b6a3.uuid.UuidCreator;
4-
import com.google.common.collect.Maps;
5-
import com.google.common.collect.Sets;
6-
import jakarta.annotation.Nonnull;
7-
import jakarta.annotation.Nullable;
8-
import lombok.RequiredArgsConstructor;
9-
import lombok.extern.slf4j.Slf4j;
3+
import static org.lowcoder.domain.application.model.ApplicationStatus.NORMAL;
4+
import static org.lowcoder.domain.permission.model.ResourceAction.EDIT_APPLICATIONS;
5+
import static org.lowcoder.domain.permission.model.ResourceAction.MANAGE_APPLICATIONS;
6+
import static org.lowcoder.domain.permission.model.ResourceAction.PUBLISH_APPLICATIONS;
7+
import static org.lowcoder.domain.permission.model.ResourceAction.READ_APPLICATIONS;
8+
import static org.lowcoder.domain.permission.model.ResourceAction.USE_DATASOURCES;
9+
import static org.lowcoder.sdk.exception.BizError.ILLEGAL_APPLICATION_PERMISSION_ID;
10+
import static org.lowcoder.sdk.exception.BizError.INVALID_PARAMETER;
11+
import static org.lowcoder.sdk.exception.BizError.NOT_AUTHORIZED;
12+
import static org.lowcoder.sdk.exception.BizError.NO_PERMISSION_TO_REQUEST_APP;
13+
import static org.lowcoder.sdk.exception.BizError.USER_NOT_SIGNED_IN;
14+
import static org.lowcoder.sdk.util.ExceptionUtils.deferredError;
15+
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
16+
import static org.lowcoder.sdk.util.ExceptionUtils.ofErrorWithHeaders;
17+
18+
import java.time.Duration;
19+
import java.time.Instant;
20+
import java.util.ArrayList;
21+
import java.util.HashMap;
22+
import java.util.HashSet;
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.Set;
26+
import java.util.stream.Collectors;
27+
1028
import org.apache.commons.collections4.CollectionUtils;
1129
import org.apache.commons.collections4.SetUtils;
1230
import org.apache.commons.lang3.ObjectUtils;
@@ -27,21 +45,26 @@
2745
import org.lowcoder.api.usermanagement.OrgDevChecker;
2846
import org.lowcoder.api.usermanagement.view.GroupView;
2947
import org.lowcoder.api.usermanagement.view.OrgMemberListView;
30-
import org.lowcoder.domain.application.model.*;
48+
import org.lowcoder.domain.application.model.Application;
49+
import org.lowcoder.domain.application.model.ApplicationRequestType;
50+
import org.lowcoder.domain.application.model.ApplicationStatus;
51+
import org.lowcoder.domain.application.model.ApplicationType;
52+
import org.lowcoder.domain.application.model.ApplicationVersion;
3153
import org.lowcoder.domain.application.service.ApplicationHistorySnapshotService;
3254
import org.lowcoder.domain.application.service.ApplicationRecordService;
3355
import org.lowcoder.domain.application.service.ApplicationService;
3456
import org.lowcoder.domain.datasource.model.Datasource;
3557
import org.lowcoder.domain.datasource.service.DatasourceService;
3658
import org.lowcoder.domain.folder.service.FolderElementRelationService;
37-
import org.lowcoder.domain.group.model.Group;
38-
import org.lowcoder.domain.group.model.GroupMember;
3959
import org.lowcoder.domain.interaction.UserApplicationInteractionService;
40-
import org.lowcoder.domain.organization.model.OrgMember;
4160
import org.lowcoder.domain.organization.model.Organization;
4261
import org.lowcoder.domain.organization.service.OrgMemberService;
4362
import org.lowcoder.domain.organization.service.OrganizationService;
44-
import org.lowcoder.domain.permission.model.*;
63+
import org.lowcoder.domain.permission.model.ResourceAction;
64+
import org.lowcoder.domain.permission.model.ResourceHolder;
65+
import org.lowcoder.domain.permission.model.ResourcePermission;
66+
import org.lowcoder.domain.permission.model.ResourceRole;
67+
import org.lowcoder.domain.permission.model.ResourceType;
4568
import org.lowcoder.domain.permission.service.ResourcePermissionService;
4669
import org.lowcoder.domain.permission.solution.SuggestAppAdminSolutionService;
4770
import org.lowcoder.domain.plugin.service.DatasourceMetaInfoService;
@@ -56,22 +79,18 @@
5679
import org.lowcoder.sdk.util.ExceptionUtils;
5780
import org.springframework.http.HttpHeaders;
5881
import org.springframework.stereotype.Service;
59-
import org.springframework.web.bind.annotation.PathVariable;
6082

83+
import com.github.f4b6a3.uuid.UuidCreator;
84+
import com.google.common.collect.Maps;
85+
import com.google.common.collect.Sets;
86+
87+
import jakarta.annotation.Nonnull;
88+
import jakarta.annotation.Nullable;
89+
import lombok.RequiredArgsConstructor;
90+
import lombok.extern.slf4j.Slf4j;
6191
import reactor.core.publisher.Flux;
6292
import reactor.core.publisher.Mono;
6393
import reactor.core.scheduler.Schedulers;
64-
import reactor.util.function.Tuple2;
65-
66-
import java.time.Duration;
67-
import java.time.Instant;
68-
import java.util.*;
69-
import java.util.stream.Collectors;
70-
71-
import static org.lowcoder.domain.application.model.ApplicationStatus.NORMAL;
72-
import static org.lowcoder.domain.permission.model.ResourceAction.*;
73-
import static org.lowcoder.sdk.exception.BizError.*;
74-
import static org.lowcoder.sdk.util.ExceptionUtils.*;
7594

7695
@RequiredArgsConstructor
7796
@Service
@@ -398,6 +417,27 @@ public Mono<ApplicationView> publish(String applicationId, ApplicationPublishReq
398417
.build())));
399418
}
400419

420+
@Override
421+
public Mono<ApplicationView> publishWithRollback(String applicationId, ApplicationPublishRequest applicationPublishRequest, Map<String, Object> rollbackDsl) {
422+
return checkApplicationStatus(applicationId, NORMAL)
423+
.then(sessionUserService.getVisitorId())
424+
.flatMap(userId -> resourcePermissionService.checkAndReturnMaxPermission(userId,
425+
applicationId, PUBLISH_APPLICATIONS))
426+
.delayUntil(__ -> applicationService.findById(applicationId)
427+
.map(application -> ApplicationVersion.builder()
428+
.tag(applicationPublishRequest.tag())
429+
.commitMessage(applicationPublishRequest.commitMessage())
430+
.applicationId(application.getId())
431+
.applicationDSL(rollbackDsl) // Use the rollback DSL instead of current editing DSL
432+
.build())
433+
.flatMap(applicationRecordService::insert))
434+
.flatMap(permission -> applicationService.findById(applicationId)
435+
.flatMap(applicationUpdated -> buildView(applicationUpdated, permission.getResourceRole().getValue()).map(appInfoView -> ApplicationView.builder()
436+
.applicationInfoView(appInfoView)
437+
.applicationDSL(rollbackDsl) // Return the rollback DSL in the response
438+
.build())));
439+
}
440+
401441
@Override
402442
public Mono<Boolean> updateEditState(String applicationId, ApplicationEndpoints.UpdateEditStateRequest updateEditStateRequest) {
403443
return checkApplicationStatus(applicationId, NORMAL)

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
package org.lowcoder.api.application;
22

3-
import lombok.RequiredArgsConstructor;
4-
import org.lowcoder.api.application.view.*;
3+
import static org.apache.commons.collections4.SetUtils.emptyIfNull;
4+
import static org.lowcoder.plugin.api.event.LowcoderEvent.EventType.APPLICATION_CREATE;
5+
import static org.lowcoder.plugin.api.event.LowcoderEvent.EventType.APPLICATION_DELETE;
6+
import static org.lowcoder.plugin.api.event.LowcoderEvent.EventType.APPLICATION_RECYCLED;
7+
import static org.lowcoder.plugin.api.event.LowcoderEvent.EventType.APPLICATION_RESTORE;
8+
import static org.lowcoder.plugin.api.event.LowcoderEvent.EventType.APPLICATION_UPDATE;
9+
import static org.lowcoder.plugin.api.event.LowcoderEvent.EventType.APPLICATION_VIEW;
10+
import static org.lowcoder.sdk.exception.BizError.INVALID_PARAMETER;
11+
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
12+
13+
import java.util.List;
14+
import java.util.Map;
15+
import java.util.Objects;
16+
17+
import org.lowcoder.api.application.view.ApplicationInfoView;
18+
import org.lowcoder.api.application.view.ApplicationPermissionView;
19+
import org.lowcoder.api.application.view.ApplicationPublishRequest;
20+
import org.lowcoder.api.application.view.ApplicationView;
21+
import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
522
import org.lowcoder.api.framework.view.PageResponseView;
623
import org.lowcoder.api.framework.view.ResponseView;
724
import org.lowcoder.api.home.UserHomeApiService;
@@ -18,17 +35,10 @@
1835
import org.springframework.web.bind.annotation.RequestBody;
1936
import org.springframework.web.bind.annotation.RequestParam;
2037
import org.springframework.web.bind.annotation.RestController;
21-
import reactor.core.publisher.Mono;
2238

23-
import java.util.List;
24-
import java.util.Objects;
25-
26-
import static org.apache.commons.collections4.SetUtils.emptyIfNull;
27-
import static org.lowcoder.plugin.api.event.LowcoderEvent.EventType.*;
28-
import static org.lowcoder.sdk.exception.BizError.INVALID_PARAMETER;
29-
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
39+
import lombok.RequiredArgsConstructor;
3040
import reactor.core.publisher.Flux;
31-
import java.util.Map;
41+
import reactor.core.publisher.Mono;
3242

3343
@RequiredArgsConstructor
3444
@RestController
@@ -157,14 +167,32 @@ public Mono<ResponseView<ApplicationView>> publish(@PathVariable String applicat
157167
})
158168
.switchIfEmpty(Mono.just("1.0.0"))
159169
.delayUntil(newtag -> {
160-
ApplicationPublishRequest req = Objects.requireNonNullElse(applicationPublishRequest, new ApplicationPublishRequest("", newtag));
170+
ApplicationPublishRequest req = Objects.requireNonNullElse(applicationPublishRequest, new ApplicationPublishRequest("", newtag, null));
161171
return businessEventPublisher.publishApplicationPublishEvent(appId, req).then(Mono.defer(() -> {
162172
if(newtag.equals(req.tag())) {
163173
return businessEventPublisher.publishApplicationVersionChangeEvent(appId, newtag);
164174
} else return Mono.empty();
165175
}));
166176
})
167-
.flatMap(newtag -> applicationApiService.publish(appId, Objects.requireNonNullElse(applicationPublishRequest, new ApplicationPublishRequest("", newtag))))
177+
.flatMap(newtag -> {
178+
ApplicationPublishRequest req = Objects.requireNonNullElse(applicationPublishRequest, new ApplicationPublishRequest("", newtag, null));
179+
180+
// If rollback is requested, get the DSL from the specified version
181+
if (req.rollbackToVersionId() != null && !req.rollbackToVersionId().isEmpty()) {
182+
return applicationRecordService.getById(req.rollbackToVersionId())
183+
.flatMap(rollbackVersion -> {
184+
// Create a new request with the rollback DSL
185+
ApplicationPublishRequest rollbackRequest = new ApplicationPublishRequest(
186+
req.commitMessage(),
187+
req.tag(),
188+
req.rollbackToVersionId()
189+
);
190+
return applicationApiService.publishWithRollback(appId, rollbackRequest, rollbackVersion.getApplicationDSL());
191+
});
192+
} else {
193+
return applicationApiService.publish(appId, req);
194+
}
195+
})
168196
.map(ResponseView::success));
169197
}
170198

Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package org.lowcoder.api.application;
22

3+
import java.util.List;
4+
import java.util.Map;
5+
36
import org.lowcoder.api.application.view.ApplicationRecordMetaView;
47
import org.lowcoder.domain.application.model.ApplicationCombineId;
5-
import reactor.core.publisher.Mono;
68

7-
import java.util.List;
8-
import java.util.Map;
9+
import reactor.core.publisher.Mono;
910

1011
public interface ApplicationRecordApiService {
1112
Mono<Map<String, Object>> getRecordDSLFromApplicationCombineId(ApplicationCombineId applicationCombineId);
1213

1314
Mono<Void> delete(String id);
1415

1516
Mono<List<ApplicationRecordMetaView>> getByApplicationId(String applicationId);
17+
18+
Mono<Map<String, Object>> getVersionDsl(String applicationRecordId);
1619
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationRecordApiServiceImpl.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
package org.lowcoder.api.application;
22

3-
import lombok.RequiredArgsConstructor;
4-
import org.lowcoder.api.home.SessionUserService;
3+
import static org.lowcoder.api.util.ViewBuilder.multiBuild;
4+
import static org.lowcoder.sdk.exception.BizError.APPLICATION_AND_ORG_NOT_MATCH;
5+
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
6+
7+
import java.util.List;
8+
import java.util.Map;
9+
510
import org.lowcoder.api.application.view.ApplicationRecordMetaView;
11+
import org.lowcoder.api.home.SessionUserService;
612
import org.lowcoder.api.usermanagement.OrgDevChecker;
7-
import org.lowcoder.domain.application.model.ApplicationVersion;
8-
import org.lowcoder.domain.organization.model.OrgMember;
913
import org.lowcoder.domain.application.model.Application;
1014
import org.lowcoder.domain.application.model.ApplicationCombineId;
15+
import org.lowcoder.domain.application.model.ApplicationVersion;
1116
import org.lowcoder.domain.application.service.ApplicationRecordService;
1217
import org.lowcoder.domain.application.service.ApplicationService;
18+
import org.lowcoder.domain.organization.model.OrgMember;
1319
import org.lowcoder.domain.user.service.UserService;
1420
import org.springframework.stereotype.Service;
15-
import reactor.core.publisher.Mono;
1621

17-
import java.util.List;
18-
import java.util.Map;
19-
20-
import static org.lowcoder.api.util.ViewBuilder.multiBuild;
21-
import static org.lowcoder.sdk.exception.BizError.APPLICATION_AND_ORG_NOT_MATCH;
22-
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
22+
import lombok.RequiredArgsConstructor;
23+
import reactor.core.publisher.Mono;
2324

2425
@RequiredArgsConstructor
2526
@Service
@@ -60,6 +61,13 @@ public Mono<List<ApplicationRecordMetaView>> getByApplicationId(String applicati
6061
));
6162
}
6263

64+
@Override
65+
public Mono<Map<String, Object>> getVersionDsl(String applicationRecordId) {
66+
return checkApplicationRecordViewPermission(new ApplicationCombineId(null, applicationRecordId))
67+
.then(applicationRecordService.getById(applicationRecordId))
68+
.map(ApplicationVersion::getApplicationDSL);
69+
}
70+
6371

6472
Mono<Void> checkApplicationRecordManagementPermission(String applicationRecordId) {
6573
return orgDevChecker.checkCurrentOrgDev()

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationRecordController.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package org.lowcoder.api.application;
22

3-
import lombok.RequiredArgsConstructor;
4-
import org.lowcoder.api.framework.view.ResponseView;
5-
import org.lowcoder.api.application.ApplicationRecordApiService;
3+
import java.util.List;
4+
import java.util.Map;
5+
66
import org.lowcoder.api.application.view.ApplicationRecordMetaView;
7+
import org.lowcoder.api.framework.view.ResponseView;
78
import org.lowcoder.domain.application.model.ApplicationCombineId;
89
import org.springframework.web.bind.annotation.PathVariable;
910
import org.springframework.web.bind.annotation.RequestParam;
1011
import org.springframework.web.bind.annotation.RestController;
11-
import reactor.core.publisher.Mono;
1212

13-
import java.util.List;
14-
import java.util.Map;
13+
import lombok.RequiredArgsConstructor;
14+
import reactor.core.publisher.Mono;
1515

1616
@RequiredArgsConstructor
1717
@RestController
@@ -38,4 +38,9 @@ public Mono<ResponseView<Map<String, Object>>> dslById(@RequestParam(name = "app
3838
.map(ResponseView::success);
3939
}
4040

41+
@Override
42+
public Mono<ResponseView<Map<String, Object>>> getVersionDsl(@PathVariable String applicationRecordId) {
43+
return applicationRecordApiService.getVersionDsl(applicationRecordId)
44+
.map(ResponseView::success);
45+
}
4146
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationRecordEndpoints.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package org.lowcoder.api.application;
22

3-
import io.swagger.v3.oas.annotations.Operation;
4-
import org.lowcoder.api.framework.view.ResponseView;
5-
import org.lowcoder.api.application.view.ApplicationRecordMetaView;
6-
import org.springframework.web.bind.annotation.*;
7-
import reactor.core.publisher.Mono;
3+
import static org.lowcoder.infra.constant.NewUrl.APPLICATION_RECORD_URL;
84

95
import java.util.List;
106
import java.util.Map;
117

12-
import static org.lowcoder.infra.constant.NewUrl.APPLICATION_RECORD_URL;
8+
import org.lowcoder.api.application.view.ApplicationRecordMetaView;
9+
import org.lowcoder.api.framework.view.ResponseView;
10+
import org.springframework.web.bind.annotation.DeleteMapping;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.PathVariable;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.RequestParam;
15+
import org.springframework.web.bind.annotation.RestController;
16+
17+
import io.swagger.v3.oas.annotations.Operation;
18+
import reactor.core.publisher.Mono;
1319

1420
@RestController
1521
@RequestMapping(value = APPLICATION_RECORD_URL)
@@ -45,4 +51,13 @@ public interface ApplicationRecordEndpoints
4551
public Mono<ResponseView<Map<String, Object>>> dslById(@RequestParam(name = "applicationId") String applicationId,
4652
@RequestParam(name = "applicationRecordId") String applicationRecordId);
4753

54+
@Operation(
55+
tags = TAG_APPLICATION_RECORDS,
56+
operationId = "getVersionDsl",
57+
summary = "Get DSL from specific version",
58+
description = "Retrieve the DSL data from a specific application version for rollback preview"
59+
)
60+
@GetMapping("/{applicationRecordId}/dsl")
61+
public Mono<ResponseView<Map<String, Object>>> getVersionDsl(@PathVariable String applicationRecordId);
62+
4863
}

0 commit comments

Comments
 (0)