Skip to content

Commit d33f51a

Browse files
committed
feat: add marketplace applications handling
1 parent 16e8b29 commit d33f51a

File tree

12 files changed

+172
-4
lines changed

12 files changed

+172
-4
lines changed

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/Application.java

+8
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public class Application extends HasIdAndAuditing {
3939
private final Map<String, Object> publishedApplicationDSL;
4040

4141
private final Boolean publicToAll;
42+
private final Boolean publicToMarketplace;
43+
4244
private Map<String, Object> editingApplicationDSL;
4345

4446
@Transient
@@ -75,13 +77,15 @@ public Application(@JsonProperty("orgId") String organizationId,
7577
@JsonProperty("applicationStatus") ApplicationStatus applicationStatus,
7678
@JsonProperty("publishedApplicationDSL") Map<String, Object> publishedApplicationDSL,
7779
@JsonProperty("publicToAll") Boolean publicToAll,
80+
@JsonProperty("publicToMarketplace") Boolean publicToMarketplace,
7881
@JsonProperty("editingApplicationDSL") Map<String, Object> editingApplicationDSL) {
7982
this.organizationId = organizationId;
8083
this.name = name;
8184
this.applicationType = applicationType;
8285
this.applicationStatus = applicationStatus;
8386
this.publishedApplicationDSL = publishedApplicationDSL;
8487
this.publicToAll = publicToAll;
88+
this.publicToMarketplace = publicToMarketplace;
8589
this.editingApplicationDSL = editingApplicationDSL;
8690
}
8791

@@ -105,6 +109,10 @@ public boolean isPublicToAll() {
105109
return BooleanUtils.toBooleanDefaultIfNull(publicToAll, false);
106110
}
107111

112+
public boolean isPublicToMarketplace() {
113+
return BooleanUtils.toBooleanDefaultIfNull(publicToMarketplace, false);
114+
}
115+
108116
public ApplicationQuery getQueryByViewModeAndQueryId(boolean isViewMode, String queryId) {
109117
return (isViewMode ? getLiveQueries() : getEditingQueries())
110118
.stream()

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationRepository.java

+2
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,6 @@ public interface ApplicationRepository extends ReactiveMongoRepository<Applicati
3737
@Query(fields = "{_id : 1}")
3838
Flux<Application> findByPublicToAllIsTrueAndIdIn(Collection<String> ids);
3939

40+
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();
41+
4042
}

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationService.java

+11
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ public Flux<Application> findByOrganizationIdWithoutDsl(String organizationId) {
103103
return repository.findByOrganizationId(organizationId);
104104
}
105105

106+
public Flux<Application> findAllMarketplaceApps() {
107+
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();
108+
}
109+
106110
public Mono<Long> countByOrganizationId(String orgId, ApplicationStatus applicationStatus) {
107111
return repository.countByOrganizationIdAndApplicationStatus(orgId, applicationStatus);
108112
}
@@ -147,6 +151,13 @@ public Mono<Boolean> setApplicationPublicToAll(String applicationId, boolean pub
147151
return mongoUpsertHelper.updateById(application, applicationId);
148152
}
149153

154+
public Mono<Boolean> setApplicationPublicToMarketplace(String applicationId, boolean publicToMarketplace) {
155+
Application application = Application.builder()
156+
.publicToMarketplace(publicToMarketplace)
157+
.build();
158+
return mongoUpsertHelper.updateById(application, applicationId);
159+
}
160+
150161
@NonEmptyMono
151162
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
152163
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds) {

‎server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/model/ResourceAction.java

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public enum ResourceAction {
2424
EDIT_APPLICATIONS(ResourceRole.EDITOR, ResourceType.APPLICATION),
2525

2626
SET_APPLICATIONS_PUBLIC(ResourceRole.EDITOR, ResourceType.APPLICATION),
27+
SET_APPLICATIONS_PUBLIC_TO_MARKETPLACE(ResourceRole.EDITOR, ResourceType.APPLICATION),
2728

2829
// datasource action
2930
MANAGE_DATASOURCES(ResourceRole.OWNER, ResourceType.DATASOURCE),

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public Mono<ApplicationView> create(CreateApplicationRequest createApplicationRe
141141
createApplicationRequest.applicationType(),
142142
NORMAL,
143143
createApplicationRequest.publishedApplicationDSL(),
144-
false, createApplicationRequest.editingApplicationDSL());
144+
false, false, createApplicationRequest.editingApplicationDSL());
145145

146146
if (StringUtils.isBlank(application.getOrganizationId())) {
147147
return deferredError(INVALID_PARAMETER, "ORG_ID_EMPTY");
@@ -429,6 +429,7 @@ public Mono<ApplicationPermissionView> getApplicationPermissions(String applicat
429429
.creatorId(creatorId)
430430
.orgName(organization.getName())
431431
.publicToAll(application.isPublicToAll())
432+
.publicToMarketplace(application.isPublicToMarketplace())
432433
.build();
433434
});
434435
});
@@ -485,6 +486,7 @@ private ApplicationInfoView buildView(Application application, String role, @Nul
485486
.applicationStatus(application.getApplicationStatus())
486487
.folderId(folderId)
487488
.publicToAll(application.isPublicToAll())
489+
.publicToMarketplace(application.isPublicToMarketplace())
488490
.build();
489491
}
490492

@@ -498,6 +500,12 @@ public Mono<Boolean> setApplicationPublicToAll(String applicationId, boolean pub
498500
.then(applicationService.setApplicationPublicToAll(applicationId, publicToAll));
499501
}
500502

503+
public Mono<Boolean> setApplicationPublicToMarketplace(String applicationId, boolean publicToMarketplace) {
504+
return checkCurrentUserApplicationPermission(applicationId, ResourceAction.SET_APPLICATIONS_PUBLIC_TO_MARKETPLACE)
505+
.then(checkApplicationStatus(applicationId, NORMAL))
506+
.then(applicationService.setApplicationPublicToMarketplace(applicationId, publicToMarketplace));
507+
}
508+
501509
private Map<String, Object> sanitizeDsl(Map<String, Object> applicationDsl) {
502510
if (applicationDsl.get("queries") instanceof List<?> queries) {
503511
List<Map<String, Object>> list = queries.stream().map(this::doSanitizeQuery).toList();

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

+16
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.lowcoder.api.application.view.ApplicationInfoView;
1616
import org.lowcoder.api.application.view.ApplicationPermissionView;
1717
import org.lowcoder.api.application.view.ApplicationView;
18+
import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
1819
import org.lowcoder.api.framework.view.ResponseView;
1920
import org.lowcoder.api.home.UserHomeApiService;
2021
import org.lowcoder.api.home.UserHomepageView;
@@ -127,6 +128,14 @@ public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestPar
127128
.map(ResponseView::success);
128129
}
129130

131+
@Override
132+
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getMarketplaceApplications(@RequestParam(required = false) Integer applicationType) {
133+
ApplicationType applicationTypeEnum = applicationType == null ? null : ApplicationType.fromValue(applicationType);
134+
return userHomeApiService.getAllMarketplaceApplications(applicationTypeEnum)
135+
.collectList()
136+
.map(ResponseView::success);
137+
}
138+
130139
@Override
131140
public Mono<ResponseView<Boolean>> updatePermission(@PathVariable String applicationId,
132141
@PathVariable String permissionId,
@@ -177,4 +186,11 @@ public Mono<ResponseView<Boolean>> setApplicationPublicToAll(@PathVariable Strin
177186
return applicationApiService.setApplicationPublicToAll(applicationId, request.publicToAll())
178187
.map(ResponseView::success);
179188
}
189+
190+
@Override
191+
public Mono<ResponseView<Boolean>> setApplicationPublicToMarketplace(@PathVariable String applicationId,
192+
@RequestBody ApplicationPublicToMarketplaceRequest request) {
193+
return applicationApiService.setApplicationPublicToMarketplace(applicationId, request.publicToMarketplace())
194+
.map(ResponseView::success);
195+
}
180196
}

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

+29-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.lowcoder.api.application.view.ApplicationInfoView;
1111
import org.lowcoder.api.application.view.ApplicationPermissionView;
1212
import org.lowcoder.api.application.view.ApplicationView;
13+
import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
1314
import org.lowcoder.api.framework.view.ResponseView;
1415
import org.lowcoder.api.home.UserHomepageView;
1516
import org.lowcoder.domain.application.model.Application;
@@ -149,6 +150,15 @@ public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestPar
149150
@RequestParam(required = false) ApplicationStatus applicationStatus,
150151
@RequestParam(defaultValue = "true") boolean withContainerSize);
151152

153+
@Operation(
154+
tags = TAG_APPLICATION_MANAGEMENT,
155+
operationId = "listMarketplaceApplications",
156+
summary = "List marketplace Applications",
157+
description = "Retrieve a list of Lowcoder Applications that are published to the marketplace"
158+
)
159+
@GetMapping("/marketplace-apps")
160+
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getMarketplaceApplications(@RequestParam(required = false) Integer applicationType);
161+
152162
@Operation(
153163
tags = TAG_APPLICATION_PERMISSIONS,
154164
operationId = "updateApplicationPermissions",
@@ -202,8 +212,18 @@ public Mono<ResponseView<Boolean>> grantPermission(
202212
public Mono<ResponseView<Boolean>> setApplicationPublicToAll(@PathVariable String applicationId,
203213
@RequestBody ApplicationPublicToAllRequest request);
204214

205-
206-
public record BatchAddPermissionRequest(String role, Set<String> userIds, Set<String> groupIds) {
215+
@Operation(
216+
tags = TAG_APPLICATION_MANAGEMENT,
217+
operationId = "setApplicationAsPublicToMarketplace",
218+
summary = "Set Application as publicly available on marketplace but to only logged in users",
219+
description = "Set a Lowcoder Application identified by its ID as publicly available on marketplace but to only logged in users."
220+
)
221+
@PutMapping("/{applicationId}/public-to-marketplace")
222+
public Mono<ResponseView<Boolean>> setApplicationPublicToMarketplace(@PathVariable String applicationId,
223+
@RequestBody ApplicationPublicToMarketplaceRequest request);
224+
225+
226+
public record BatchAddPermissionRequest(String role, Set<String> userIds, Set<String> groupIds) {
207227
}
208228

209229
public record ApplicationPublicToAllRequest(Boolean publicToAll) {
@@ -213,6 +233,13 @@ public Boolean publicToAll() {
213233
}
214234
}
215235

236+
public record ApplicationPublicToMarketplaceRequest(Boolean publicToMarketplace) {
237+
@Override
238+
public Boolean publicToMarketplace() {
239+
return BooleanUtils.isTrue(publicToMarketplace);
240+
}
241+
}
242+
216243
public record UpdatePermissionRequest(String role) {
217244
}
218245

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/view/ApplicationInfoView.java

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public class ApplicationInfoView {
3838

3939
private final boolean publicToAll;
4040

41+
private final boolean publicToMarketplace;
42+
4143
public long getLastViewTime() {
4244
return lastViewTime == null ? 0 : lastViewTime.toEpochMilli();
4345
}

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/view/ApplicationPermissionView.java

+5
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@
88
public class ApplicationPermissionView extends CommonPermissionView {
99

1010
private boolean publicToAll;
11+
private boolean publicToMarketplace;
1112

1213
public boolean isPublicToAll() {
1314
return publicToAll;
1415
}
16+
17+
public boolean isPublicToMarketplace() {
18+
return publicToMarketplace;
19+
}
1520
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.lowcoder.api.application.view;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
import org.lowcoder.domain.application.model.ApplicationStatus;
6+
7+
@Builder
8+
@Getter
9+
public class MarketplaceApplicationInfoView {
10+
11+
// org details
12+
private final String orgId;
13+
private final String orgName;
14+
15+
// creator info
16+
private final String creatorEmail;
17+
18+
// App details
19+
private final String applicationId;
20+
private final String name;
21+
private final long createAt;
22+
private final String createBy;
23+
/**
24+
* @see org.lowcoder.domain.application.model.ApplicationType
25+
*/
26+
private final int applicationType;
27+
private final ApplicationStatus applicationStatus;
28+
29+
30+
}

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import javax.annotation.Nullable;
44

5+
import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
56
import reactor.core.publisher.Flux;
67
import reactor.core.publisher.Mono;
78

@@ -22,4 +23,6 @@ public interface UserHomeApiService {
2223

2324
Flux<ApplicationInfoView> getAllAuthorisedApplications4CurrentOrgMember(@Nullable ApplicationType applicationType,
2425
@Nullable ApplicationStatus applicationStatus, boolean withContainerSize);
26+
27+
public Flux<MarketplaceApplicationInfoView> getAllMarketplaceApplications(@Nullable ApplicationType applicationType);
2528
}

‎server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java

+56-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.apache.commons.lang3.StringUtils;
1919
import org.lowcoder.api.application.view.ApplicationInfoView;
2020
import org.lowcoder.api.application.view.ApplicationInfoView.ApplicationInfoViewBuilder;
21+
import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
2122
import org.lowcoder.api.usermanagement.OrgDevChecker;
2223
import org.lowcoder.api.usermanagement.view.OrgAndVisitorRoleView;
2324
import org.lowcoder.api.usermanagement.view.UserProfileView;
@@ -256,6 +257,59 @@ public Flux<ApplicationInfoView> getAllAuthorisedApplications4CurrentOrgMember(@
256257
});
257258
}
258259

260+
@Override
261+
public Flux<MarketplaceApplicationInfoView> getAllMarketplaceApplications(@Nullable ApplicationType applicationType) {
262+
263+
return sessionUserService.getVisitorOrgMemberCache()
264+
.flatMapMany(orgMember -> {
265+
// application flux
266+
Flux<Application> applicationFlux = Flux.defer(() -> applicationService.findAllMarketplaceApps())
267+
.filter(application -> isNull(applicationType) || application.getApplicationType() == applicationType.getValue())
268+
.cache();
269+
270+
// user map
271+
Mono<Map<String, User>> userMapMono = applicationFlux
272+
.flatMap(application -> emptyIfNull(application.getCreatedBy()))
273+
.collectList()
274+
.flatMap(creatorIds -> userService.getByIds(creatorIds))
275+
.cache();
276+
277+
// org map
278+
Mono<Map<String, Organization>> orgMapMono = applicationFlux
279+
.flatMap(application -> emptyIfNull(application.getOrganizationId()))
280+
.collectList()
281+
.flatMap(orgIds -> organizationService.getByIds(orgIds)
282+
.collectList()
283+
.map(it -> it.stream().collect(Collectors.toMap(Organization::getId, Function.identity())))
284+
)
285+
.cache();
286+
287+
288+
return applicationFlux
289+
.flatMap(application -> Mono.zip(Mono.just(application), userMapMono, orgMapMono))
290+
.map(tuple -> {
291+
// build view
292+
Application application = tuple.getT1();
293+
Map<String, User> userMap = tuple.getT2();
294+
Map<String, Organization> orgMap = tuple.getT3();
295+
return MarketplaceApplicationInfoView.builder()
296+
.applicationId(application.getId())
297+
.name(application.getName())
298+
.applicationType(application.getApplicationType())
299+
.applicationStatus(application.getApplicationStatus())
300+
.orgId(application.getOrganizationId())
301+
.orgName(orgMap.get(application.getOrganizationId()).getName())
302+
.creatorEmail(Optional.ofNullable(userMap.get(application.getCreatedBy()))
303+
.map(User::getName)
304+
.orElse(""))
305+
.createAt(application.getCreatedAt().toEpochMilli())
306+
.createBy(application.getCreatedBy())
307+
.build();
308+
});
309+
310+
});
311+
}
312+
259313
private ApplicationInfoView buildView(Application application, ResourceRole maxRole, Map<String, User> userMap, @Nullable Instant lastViewTime,
260314
boolean withContainerSize) {
261315
ApplicationInfoViewBuilder applicationInfoViewBuilder = ApplicationInfoView.builder()
@@ -271,7 +325,8 @@ private ApplicationInfoView buildView(Application application, ResourceRole maxR
271325
.applicationStatus(application.getApplicationStatus())
272326
.lastModifyTime(application.getUpdatedAt())
273327
.lastViewTime(lastViewTime)
274-
.publicToAll(application.isPublicToAll());
328+
.publicToAll(application.isPublicToAll())
329+
.publicToMarketplace(application.isPublicToMarketplace());
275330
if (withContainerSize) {
276331
return applicationInfoViewBuilder
277332
.containerSize(application.getLiveContainerSize())

0 commit comments

Comments
 (0)