Skip to content

Commit 697c308

Browse files
authored
Merge pull request #677 from lowcoder-org/app-public-to-marketplace-feature
Make App Public To Marketplace Feature
2 parents 16e8b29 + ff6b08f commit 697c308

16 files changed

+235
-12
lines changed

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

Lines changed: 8 additions & 0 deletions
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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public interface ApplicationRepository extends ReactiveMongoRepository<Applicati
3535
Flux<Application> findByIdIn(List<String> ids);
3636

3737
@Query(fields = "{_id : 1}")
38-
Flux<Application> findByPublicToAllIsTrueAndIdIn(Collection<String> ids);
38+
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsAndIdIn(Boolean publicToMarketplace, Collection<String> ids);
39+
40+
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();
3941

4042
}

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

Lines changed: 14 additions & 2 deletions
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,11 +151,19 @@ 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")
152-
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds) {
153-
return repository.findByPublicToAllIsTrueAndIdIn(applicationIds)
163+
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds, Boolean isAnonymous) {
164+
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsAndIdIn(!isAnonymous, applicationIds)
154165
.map(HasIdAndAuditing::getId)
155166
.collect(Collectors.toSet());
167+
156168
}
157169
}

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

Lines changed: 1 addition & 0 deletions
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-domain/src/main/java/org/lowcoder/domain/permission/service/ApplicationPermissionHandler.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,22 @@ protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermission
4646
}
4747

4848
Set<String> applicationIds = newHashSet(resourceIds);
49-
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds),
49+
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds, Boolean.TRUE),
50+
templateSolution.getTemplateApplicationIds(applicationIds))
51+
.map(tuple -> {
52+
Set<String> publicAppIds = tuple.getT1();
53+
Set<String> templateAppIds = tuple.getT2();
54+
return collectMap(union(publicAppIds, templateAppIds), identity(), this::getAnonymousUserPermission);
55+
});
56+
}
57+
58+
// This is for PTM apps that are public but only available to logged-in users
59+
@Override
60+
protected Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserPublicResourcePermissions
61+
(Collection<String> resourceIds, ResourceAction resourceAction) {
62+
63+
Set<String> applicationIds = newHashSet(resourceIds);
64+
return Mono.zip(applicationService.getPublicApplicationIds(applicationIds, Boolean.FALSE),
5065
templateSolution.getTemplateApplicationIds(applicationIds))
5166
.map(tuple -> {
5267
Set<String> publicAppIds = tuple.getT1();

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/service/DatasourcePermissionHandler.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.lowcoder.domain.permission.service;
22

33
import static org.lowcoder.domain.permission.model.ResourceHolder.USER;
4-
import static org.lowcoder.sdk.constants.Authentication.ANONYMOUS_USER_ID;
54

65
import java.util.Collection;
76
import java.util.Collections;
@@ -39,6 +38,11 @@ protected Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermission
3938
return Mono.just(Collections.emptyMap());
4039
}
4140

41+
@Override
42+
protected Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserPublicResourcePermissions(Collection<String> resourceIds, ResourceAction resourceAction) {
43+
return Mono.just(Collections.emptyMap());
44+
}
45+
4246
@Override
4347
protected Mono<String> getOrgId(String resourceId) {
4448
return datasourceService.getById(resourceId)

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/service/ResourcePermissionHandler.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ public Mono<UserPermissionOnResourceStatus> checkUserPermissionStatusOnResource(
9494
return publicResourcePermissionMono;
9595
}
9696

97+
Mono<UserPermissionOnResourceStatus> nonAnonymousPublicResourcePermissionMono = getNonAnonymousUserPublicResourcePermissions(singletonList(resourceId), resourceAction)
98+
.map(it -> it.getOrDefault(resourceId, emptyList()))
99+
.map(it -> {
100+
if (!it.isEmpty()) {
101+
return UserPermissionOnResourceStatus.success(it.get(0));
102+
}
103+
return isAnonymousUser(userId) ? UserPermissionOnResourceStatus.anonymousUser() : UserPermissionOnResourceStatus.notInOrg();
104+
});
105+
106+
97107
Mono<UserPermissionOnResourceStatus> orgUserPermissionMono = getOrgId(resourceId)
98108
.flatMap(orgId -> orgMemberService.getOrgMember(orgId, userId))
99109
.flatMap(orgMember -> {
@@ -107,13 +117,17 @@ public Mono<UserPermissionOnResourceStatus> checkUserPermissionStatusOnResource(
107117
})
108118
.defaultIfEmpty(UserPermissionOnResourceStatus.notInOrg());
109119

110-
return Mono.zip(publicResourcePermissionMono, orgUserPermissionMono)
120+
return Mono.zip(publicResourcePermissionMono, nonAnonymousPublicResourcePermissionMono, orgUserPermissionMono)
111121
.map(tuple -> {
112122
UserPermissionOnResourceStatus publicResourcePermission = tuple.getT1();
113-
UserPermissionOnResourceStatus orgUserPermission = tuple.getT2();
123+
UserPermissionOnResourceStatus nonAnonymousPublicResourcePermission = tuple.getT2();
124+
UserPermissionOnResourceStatus orgUserPermission = tuple.getT3();
114125
if (orgUserPermission.hasPermission()) {
115126
return orgUserPermission;
116127
}
128+
if(nonAnonymousPublicResourcePermission.hasPermission()) {
129+
return nonAnonymousPublicResourcePermission;
130+
}
117131
if (publicResourcePermission.hasPermission()) {
118132
return publicResourcePermission;
119133
}
@@ -132,6 +146,9 @@ private ResourcePermission getMaxPermission(List<ResourcePermission> permissions
132146
protected abstract Mono<Map<String, List<ResourcePermission>>> getAnonymousUserPermissions(Collection<String> resourceIds,
133147
ResourceAction resourceAction);
134148

149+
protected abstract Mono<Map<String, List<ResourcePermission>>> getNonAnonymousUserPublicResourcePermissions
150+
(Collection<String> resourceIds, ResourceAction resourceAction);
151+
135152
private Mono<Map<String, List<ResourcePermission>>> getAllMatchingPermissions0(String userId, String orgId, ResourceType resourceType,
136153
Collection<String> resourceIds,
137154
ResourceAction resourceAction) {

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/service/ResourcePermissionService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ public Mono<ResourcePermission> checkAndReturnMaxPermission(String userId, Strin
214214
});
215215
}
216216

217-
public Mono<UserPermissionOnResourceStatus> checkUserPermissionStatusOnResource(String userId, String resourceId, ResourceAction resourceAction) {
217+
public Mono<UserPermissionOnResourceStatus> checkUserPermissionStatusOnResource
218+
(String userId, String resourceId, ResourceAction resourceAction) {
218219
ResourceType resourceType = resourceAction.getResourceType();
219220
var resourcePermissionHandler = getResourcePermissionHandler(resourceType);
220221
return resourcePermissionHandler.checkUserPermissionStatusOnResource(userId, resourceId, resourceAction);

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

Lines changed: 9 additions & 1 deletion
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

Lines changed: 24 additions & 0 deletions
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;
@@ -96,6 +97,14 @@ public Mono<ResponseView<ApplicationView>> getPublishedApplication(@PathVariable
9697
.map(ResponseView::success);
9798
}
9899

100+
@Override
101+
public Mono<ResponseView<ApplicationView>> getPublishedMarketPlaceApplication(@PathVariable String applicationId) {
102+
return applicationApiService.getPublishedApplication(applicationId)
103+
.delayUntil(applicationView -> applicationApiService.updateUserApplicationLastViewTime(applicationId))
104+
.delayUntil(applicationView -> businessEventPublisher.publishApplicationCommonEvent(applicationView, VIEW))
105+
.map(ResponseView::success);
106+
}
107+
99108
@Override
100109
public Mono<ResponseView<ApplicationView>> update(@PathVariable String applicationId,
101110
@RequestBody Application newApplication) {
@@ -127,6 +136,14 @@ public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestPar
127136
.map(ResponseView::success);
128137
}
129138

139+
@Override
140+
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getMarketplaceApplications(@RequestParam(required = false) Integer applicationType) {
141+
ApplicationType applicationTypeEnum = applicationType == null ? null : ApplicationType.fromValue(applicationType);
142+
return userHomeApiService.getAllMarketplaceApplications(applicationTypeEnum)
143+
.collectList()
144+
.map(ResponseView::success);
145+
}
146+
130147
@Override
131148
public Mono<ResponseView<Boolean>> updatePermission(@PathVariable String applicationId,
132149
@PathVariable String permissionId,
@@ -177,4 +194,11 @@ public Mono<ResponseView<Boolean>> setApplicationPublicToAll(@PathVariable Strin
177194
return applicationApiService.setApplicationPublicToAll(applicationId, request.publicToAll())
178195
.map(ResponseView::success);
179196
}
197+
198+
@Override
199+
public Mono<ResponseView<Boolean>> setApplicationPublicToMarketplace(@PathVariable String applicationId,
200+
@RequestBody ApplicationPublicToMarketplaceRequest request) {
201+
return applicationApiService.setApplicationPublicToMarketplace(applicationId, request.publicToMarketplace())
202+
.map(ResponseView::success);
203+
}
180204
}

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

Lines changed: 38 additions & 2 deletions
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;
@@ -110,6 +111,15 @@ public interface ApplicationEndpoints
110111
@GetMapping("/{applicationId}/view")
111112
public Mono<ResponseView<ApplicationView>> getPublishedApplication(@PathVariable String applicationId);
112113

114+
@Operation(
115+
tags = TAG_APPLICATION_MANAGEMENT,
116+
operationId = "getMarketplaceApplicationDataInViewMode",
117+
summary = "Get Marketplace Application data in view mode",
118+
description = "Retrieve the DSL data of a Lowcoder Application in view-mode by its ID for the marketplace."
119+
)
120+
@GetMapping("/{applicationId}/view_marketplace")
121+
public Mono<ResponseView<ApplicationView>> getPublishedMarketPlaceApplication(@PathVariable String applicationId);
122+
113123
@Operation(
114124
tags = TAG_APPLICATION_MANAGEMENT,
115125
operationId = "updateApplication",
@@ -149,6 +159,15 @@ public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestPar
149159
@RequestParam(required = false) ApplicationStatus applicationStatus,
150160
@RequestParam(defaultValue = "true") boolean withContainerSize);
151161

162+
@Operation(
163+
tags = TAG_APPLICATION_MANAGEMENT,
164+
operationId = "listMarketplaceApplications",
165+
summary = "List marketplace Applications",
166+
description = "Retrieve a list of Lowcoder Applications that are published to the marketplace"
167+
)
168+
@GetMapping("/marketplace-apps")
169+
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getMarketplaceApplications(@RequestParam(required = false) Integer applicationType);
170+
152171
@Operation(
153172
tags = TAG_APPLICATION_PERMISSIONS,
154173
operationId = "updateApplicationPermissions",
@@ -202,8 +221,18 @@ public Mono<ResponseView<Boolean>> grantPermission(
202221
public Mono<ResponseView<Boolean>> setApplicationPublicToAll(@PathVariable String applicationId,
203222
@RequestBody ApplicationPublicToAllRequest request);
204223

205-
206-
public record BatchAddPermissionRequest(String role, Set<String> userIds, Set<String> groupIds) {
224+
@Operation(
225+
tags = TAG_APPLICATION_MANAGEMENT,
226+
operationId = "setApplicationAsPublicToMarketplace",
227+
summary = "Set Application as publicly available on marketplace but to only logged in users",
228+
description = "Set a Lowcoder Application identified by its ID as publicly available on marketplace but to only logged in users."
229+
)
230+
@PutMapping("/{applicationId}/public-to-marketplace")
231+
public Mono<ResponseView<Boolean>> setApplicationPublicToMarketplace(@PathVariable String applicationId,
232+
@RequestBody ApplicationPublicToMarketplaceRequest request);
233+
234+
235+
public record BatchAddPermissionRequest(String role, Set<String> userIds, Set<String> groupIds) {
207236
}
208237

209238
public record ApplicationPublicToAllRequest(Boolean publicToAll) {
@@ -213,6 +242,13 @@ public Boolean publicToAll() {
213242
}
214243
}
215244

245+
public record ApplicationPublicToMarketplaceRequest(Boolean publicToMarketplace) {
246+
@Override
247+
public Boolean publicToMarketplace() {
248+
return BooleanUtils.isTrue(publicToMarketplace);
249+
}
250+
}
251+
216252
public record UpdatePermissionRequest(String role) {
217253
}
218254

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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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
}

0 commit comments

Comments
 (0)