Skip to content

Commit 9d33fb2

Browse files
authored
Merge pull request #679 from lowcoder-org/agency-profile-feature
Feature To Set Application AS Agency Profile
2 parents ad537a3 + 10643ce commit 9d33fb2

File tree

9 files changed

+172
-8
lines changed

9 files changed

+172
-8
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
@@ -41,6 +41,8 @@ public class Application extends HasIdAndAuditing {
4141
private final Boolean publicToAll;
4242
private final Boolean publicToMarketplace;
4343

44+
private final Boolean agencyProfile;
45+
4446
private Map<String, Object> editingApplicationDSL;
4547

4648
@Transient
@@ -78,6 +80,7 @@ public Application(@JsonProperty("orgId") String organizationId,
7880
@JsonProperty("publishedApplicationDSL") Map<String, Object> publishedApplicationDSL,
7981
@JsonProperty("publicToAll") Boolean publicToAll,
8082
@JsonProperty("publicToMarketplace") Boolean publicToMarketplace,
83+
@JsonProperty("agencyProfile") Boolean agencyProfile,
8184
@JsonProperty("editingApplicationDSL") Map<String, Object> editingApplicationDSL) {
8285
this.organizationId = organizationId;
8386
this.name = name;
@@ -86,6 +89,7 @@ public Application(@JsonProperty("orgId") String organizationId,
8689
this.publishedApplicationDSL = publishedApplicationDSL;
8790
this.publicToAll = publicToAll;
8891
this.publicToMarketplace = publicToMarketplace;
92+
this.agencyProfile = agencyProfile;
8993
this.editingApplicationDSL = editingApplicationDSL;
9094
}
9195

@@ -113,6 +117,10 @@ public boolean isPublicToMarketplace() {
113117
return BooleanUtils.toBooleanDefaultIfNull(publicToMarketplace, false);
114118
}
115119

120+
public boolean agencyProfile() {
121+
return BooleanUtils.toBooleanDefaultIfNull(agencyProfile, false);
122+
}
123+
116124
public ApplicationQuery getQueryByViewModeAndQueryId(boolean isViewMode, String queryId) {
117125
return (isViewMode ? getLiveQueries() : getEditingQueries())
118126
.stream()

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@ public interface ApplicationRepository extends ReactiveMongoRepository<Applicati
3434

3535
Flux<Application> findByIdIn(List<String> ids);
3636

37-
@Query(fields = "{_id : 1}")
38-
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsAndIdIn(Boolean publicToMarketplace, Collection<String> ids);
37+
@Query(value = "{$and:[{'publicToAll':true},{'$or':[{'publicToMarketplace':?0},{'agencyProfile':?1}]}, {'_id': { $in: ?2}}]}", fields = "{_id : 1}")
38+
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsOrAgencyProfileIsAndIdIn
39+
(Boolean publicToMarketplace, Boolean agencyProfile, Collection<String> ids);
3940

4041
Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();
4142

43+
Flux<Application> findByPublicToAllIsTrueAndAgencyProfileIsTrue();
44+
4245
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ public Flux<Application> findAllMarketplaceApps() {
107107
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();
108108
}
109109

110+
public Flux<Application> findAllAgencyProfileApps() {
111+
return repository.findByPublicToAllIsTrueAndAgencyProfileIsTrue();
112+
}
113+
110114
public Mono<Long> countByOrganizationId(String orgId, ApplicationStatus applicationStatus) {
111115
return repository.countByOrganizationIdAndApplicationStatus(orgId, applicationStatus);
112116
}
@@ -158,10 +162,17 @@ public Mono<Boolean> setApplicationPublicToMarketplace(String applicationId, boo
158162
return mongoUpsertHelper.updateById(application, applicationId);
159163
}
160164

165+
public Mono<Boolean> setApplicationAsAgencyProfile(String applicationId, boolean agencyProfile) {
166+
Application application = Application.builder()
167+
.agencyProfile(agencyProfile)
168+
.build();
169+
return mongoUpsertHelper.updateById(application, applicationId);
170+
}
171+
161172
@NonEmptyMono
162173
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
163174
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds, Boolean isAnonymous) {
164-
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsAndIdIn(!isAnonymous, applicationIds)
175+
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsOrAgencyProfileIsAndIdIn(!isAnonymous, !isAnonymous, applicationIds)
165176
.map(HasIdAndAuditing::getId)
166177
.collect(Collectors.toSet());
167178

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
@@ -25,6 +25,7 @@ public enum ResourceAction {
2525

2626
SET_APPLICATIONS_PUBLIC(ResourceRole.EDITOR, ResourceType.APPLICATION),
2727
SET_APPLICATIONS_PUBLIC_TO_MARKETPLACE(ResourceRole.EDITOR, ResourceType.APPLICATION),
28+
SET_APPLICATIONS_AS_AGENCY_PROFILE(ResourceRole.EDITOR, ResourceType.APPLICATION),
2829

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

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

Lines changed: 23 additions & 3 deletions
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, false, createApplicationRequest.editingApplicationDSL());
144+
false, false, false, createApplicationRequest.editingApplicationDSL());
145145

146146
if (StringUtils.isBlank(application.getOrganizationId())) {
147147
return deferredError(INVALID_PARAMETER, "ORG_ID_EMPTY");
@@ -248,6 +248,19 @@ private Mono<Void> checkApplicationStatus(Application application, ApplicationSt
248248
return Mono.error(new BizException(BizError.UNSUPPORTED_OPERATION, "BAD_REQUEST"));
249249
}
250250

251+
private Mono<Void> checkApplicationViewRequest(Application application, ApplicationEndpoints.ApplicationRequestType expected) {
252+
if (expected == ApplicationEndpoints.ApplicationRequestType.PUBLIC_TO_ALL && application.isPublicToAll()) {
253+
return Mono.empty();
254+
}
255+
if (expected == ApplicationEndpoints.ApplicationRequestType.PUBLIC_TO_MARKETPLACE && application.isPublicToMarketplace()) {
256+
return Mono.empty();
257+
}
258+
if (expected == ApplicationEndpoints.ApplicationRequestType.AGENCY_PROFILE && application.agencyProfile()) {
259+
return Mono.empty();
260+
}
261+
return Mono.error(new BizException(BizError.UNSUPPORTED_OPERATION, "BAD_REQUEST"));
262+
}
263+
251264
private Mono<Boolean> updateApplicationStatus(String applicationId, ApplicationStatus applicationStatus) {
252265
return checkCurrentUserApplicationPermission(applicationId, MANAGE_APPLICATIONS)
253266
.then(Mono.defer(() -> {
@@ -280,10 +293,11 @@ public Mono<ApplicationView> getEditingApplication(String applicationId) {
280293
});
281294
}
282295

283-
public Mono<ApplicationView> getPublishedApplication(String applicationId) {
296+
public Mono<ApplicationView> getPublishedApplication(String applicationId, ApplicationEndpoints.ApplicationRequestType requestType) {
284297
return checkPermissionWithReadableErrorMsg(applicationId, READ_APPLICATIONS)
285298
.zipWhen(permission -> applicationService.findById(applicationId)
286-
.delayUntil(application -> checkApplicationStatus(application, NORMAL)))
299+
.delayUntil(application -> checkApplicationStatus(application, NORMAL))
300+
.delayUntil(application -> checkApplicationViewRequest(application, requestType)))
287301
.zipWhen(tuple -> applicationService.getAllDependentModulesFromApplication(tuple.getT2(), true), TupleUtils::merge)
288302
.zipWhen(tuple -> organizationService.getOrgCommonSettings(tuple.getT2().getOrganizationId()), TupleUtils::merge)
289303
.zipWith(getTemplateIdFromApplicationId(applicationId), TupleUtils::merge)
@@ -506,6 +520,12 @@ public Mono<Boolean> setApplicationPublicToMarketplace(String applicationId, boo
506520
.then(applicationService.setApplicationPublicToMarketplace(applicationId, publicToMarketplace));
507521
}
508522

523+
public Mono<Boolean> setApplicationAsAgencyProfile(String applicationId, boolean agencyProfile) {
524+
return checkCurrentUserApplicationPermission(applicationId, ResourceAction.SET_APPLICATIONS_AS_AGENCY_PROFILE)
525+
.then(checkApplicationStatus(applicationId, NORMAL))
526+
.then(applicationService.setApplicationAsAgencyProfile(applicationId, agencyProfile));
527+
}
528+
509529
private Map<String, Object> sanitizeDsl(Map<String, Object> applicationDsl) {
510530
if (applicationDsl.get("queries") instanceof List<?> queries) {
511531
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: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,23 @@ public Mono<ResponseView<ApplicationView>> getEditingApplication(@PathVariable S
9191

9292
@Override
9393
public Mono<ResponseView<ApplicationView>> getPublishedApplication(@PathVariable String applicationId) {
94-
return applicationApiService.getPublishedApplication(applicationId)
94+
return applicationApiService.getPublishedApplication(applicationId, ApplicationRequestType.PUBLIC_TO_ALL)
9595
.delayUntil(applicationView -> applicationApiService.updateUserApplicationLastViewTime(applicationId))
9696
.delayUntil(applicationView -> businessEventPublisher.publishApplicationCommonEvent(applicationView, VIEW))
9797
.map(ResponseView::success);
9898
}
9999

100100
@Override
101101
public Mono<ResponseView<ApplicationView>> getPublishedMarketPlaceApplication(@PathVariable String applicationId) {
102-
return applicationApiService.getPublishedApplication(applicationId)
102+
return applicationApiService.getPublishedApplication(applicationId, ApplicationRequestType.PUBLIC_TO_MARKETPLACE)
103+
.delayUntil(applicationView -> applicationApiService.updateUserApplicationLastViewTime(applicationId))
104+
.delayUntil(applicationView -> businessEventPublisher.publishApplicationCommonEvent(applicationView, VIEW))
105+
.map(ResponseView::success);
106+
}
107+
108+
@Override
109+
public Mono<ResponseView<ApplicationView>> getAgencyProfileApplication(@PathVariable String applicationId) {
110+
return applicationApiService.getPublishedApplication(applicationId, ApplicationRequestType.AGENCY_PROFILE)
103111
.delayUntil(applicationView -> applicationApiService.updateUserApplicationLastViewTime(applicationId))
104112
.delayUntil(applicationView -> businessEventPublisher.publishApplicationCommonEvent(applicationView, VIEW))
105113
.map(ResponseView::success);
@@ -144,6 +152,14 @@ public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getMarketplaceAp
144152
.map(ResponseView::success);
145153
}
146154

155+
@Override
156+
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getAgencyProfileApplications(@RequestParam(required = false) Integer applicationType) {
157+
ApplicationType applicationTypeEnum = applicationType == null ? null : ApplicationType.fromValue(applicationType);
158+
return userHomeApiService.getAllMarketplaceApplications(applicationTypeEnum)
159+
.collectList()
160+
.map(ResponseView::success);
161+
}
162+
147163
@Override
148164
public Mono<ResponseView<Boolean>> updatePermission(@PathVariable String applicationId,
149165
@PathVariable String permissionId,
@@ -201,4 +217,13 @@ public Mono<ResponseView<Boolean>> setApplicationPublicToMarketplace(@PathVariab
201217
return applicationApiService.setApplicationPublicToMarketplace(applicationId, request.publicToMarketplace())
202218
.map(ResponseView::success);
203219
}
220+
221+
@Override
222+
public Mono<ResponseView<Boolean>> setApplicationAsAgencyProfile(@PathVariable String applicationId,
223+
@RequestBody ApplicationAsAgencyProfileRequest request) {
224+
return applicationApiService.setApplicationAsAgencyProfile(applicationId, request.agencyProfile())
225+
.map(ResponseView::success);
226+
}
227+
228+
204229
}

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ public interface ApplicationEndpoints
120120
@GetMapping("/{applicationId}/view_marketplace")
121121
public Mono<ResponseView<ApplicationView>> getPublishedMarketPlaceApplication(@PathVariable String applicationId);
122122

123+
@Operation(
124+
tags = TAG_APPLICATION_MANAGEMENT,
125+
operationId = "getAgencyProfileApplicationDataInViewMode",
126+
summary = "Get Agency profile Application data in view mode",
127+
description = "Retrieve the DSL data of a Lowcoder Application in view-mode by its ID marked as agency profile."
128+
)
129+
@GetMapping("/{applicationId}/view_agency")
130+
public Mono<ResponseView<ApplicationView>> getAgencyProfileApplication(@PathVariable String applicationId);
131+
123132
@Operation(
124133
tags = TAG_APPLICATION_MANAGEMENT,
125134
operationId = "updateApplication",
@@ -168,6 +177,15 @@ public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestPar
168177
@GetMapping("/marketplace-apps")
169178
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getMarketplaceApplications(@RequestParam(required = false) Integer applicationType);
170179

180+
@Operation(
181+
tags = TAG_APPLICATION_MANAGEMENT,
182+
operationId = "listAgencyProfileApplications",
183+
summary = "List agency profile Applications",
184+
description = "Retrieve a list of Lowcoder Applications that are set as agency profiles"
185+
)
186+
@GetMapping("/agency-profiles")
187+
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getAgencyProfileApplications(@RequestParam(required = false) Integer applicationType);
188+
171189
@Operation(
172190
tags = TAG_APPLICATION_PERMISSIONS,
173191
operationId = "updateApplicationPermissions",
@@ -231,6 +249,16 @@ public Mono<ResponseView<Boolean>> setApplicationPublicToAll(@PathVariable Strin
231249
public Mono<ResponseView<Boolean>> setApplicationPublicToMarketplace(@PathVariable String applicationId,
232250
@RequestBody ApplicationPublicToMarketplaceRequest request);
233251

252+
@Operation(
253+
tags = TAG_APPLICATION_MANAGEMENT,
254+
operationId = "setApplicationAsAgencyProfile",
255+
summary = "Set Application as agency profile",
256+
description = "Set a Lowcoder Application identified by its ID as as agency profile but to only logged in users."
257+
)
258+
@PutMapping("/{applicationId}/agency-profile")
259+
public Mono<ResponseView<Boolean>> setApplicationAsAgencyProfile(@PathVariable String applicationId,
260+
@RequestBody ApplicationAsAgencyProfileRequest request);
261+
234262

235263
public record BatchAddPermissionRequest(String role, Set<String> userIds, Set<String> groupIds) {
236264
}
@@ -249,6 +277,19 @@ public Boolean publicToMarketplace() {
249277
}
250278
}
251279

280+
public record ApplicationAsAgencyProfileRequest(Boolean agencyProfile) {
281+
@Override
282+
public Boolean agencyProfile() {
283+
return BooleanUtils.isTrue(agencyProfile);
284+
}
285+
}
286+
287+
public enum ApplicationRequestType {
288+
PUBLIC_TO_ALL,
289+
PUBLIC_TO_MARKETPLACE,
290+
AGENCY_PROFILE,
291+
}
292+
252293
public record UpdatePermissionRequest(String role) {
253294
}
254295

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ Flux<ApplicationInfoView> getAllAuthorisedApplications4CurrentOrgMember(@Nullabl
2525
@Nullable ApplicationStatus applicationStatus, boolean withContainerSize);
2626

2727
public Flux<MarketplaceApplicationInfoView> getAllMarketplaceApplications(@Nullable ApplicationType applicationType);
28+
29+
public Flux<MarketplaceApplicationInfoView> getAllAgencyProfileApplications(@Nullable ApplicationType applicationType);
2830
}

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,59 @@ public Flux<MarketplaceApplicationInfoView> getAllMarketplaceApplications(@Nulla
310310
});
311311
}
312312

313+
@Override
314+
public Flux<MarketplaceApplicationInfoView> getAllAgencyProfileApplications(@Nullable ApplicationType applicationType) {
315+
316+
return sessionUserService.getVisitorOrgMemberCache()
317+
.flatMapMany(orgMember -> {
318+
// application flux
319+
Flux<Application> applicationFlux = Flux.defer(() -> applicationService.findAllAgencyProfileApps())
320+
.filter(application -> isNull(applicationType) || application.getApplicationType() == applicationType.getValue())
321+
.cache();
322+
323+
// user map
324+
Mono<Map<String, User>> userMapMono = applicationFlux
325+
.flatMap(application -> emptyIfNull(application.getCreatedBy()))
326+
.collectList()
327+
.flatMap(creatorIds -> userService.getByIds(creatorIds))
328+
.cache();
329+
330+
// org map
331+
Mono<Map<String, Organization>> orgMapMono = applicationFlux
332+
.flatMap(application -> emptyIfNull(application.getOrganizationId()))
333+
.collectList()
334+
.flatMap(orgIds -> organizationService.getByIds(orgIds)
335+
.collectList()
336+
.map(it -> it.stream().collect(Collectors.toMap(Organization::getId, Function.identity())))
337+
)
338+
.cache();
339+
340+
341+
return applicationFlux
342+
.flatMap(application -> Mono.zip(Mono.just(application), userMapMono, orgMapMono))
343+
.map(tuple -> {
344+
// build view
345+
Application application = tuple.getT1();
346+
Map<String, User> userMap = tuple.getT2();
347+
Map<String, Organization> orgMap = tuple.getT3();
348+
return MarketplaceApplicationInfoView.builder()
349+
.applicationId(application.getId())
350+
.name(application.getName())
351+
.applicationType(application.getApplicationType())
352+
.applicationStatus(application.getApplicationStatus())
353+
.orgId(application.getOrganizationId())
354+
.orgName(orgMap.get(application.getOrganizationId()).getName())
355+
.creatorEmail(Optional.ofNullable(userMap.get(application.getCreatedBy()))
356+
.map(User::getName)
357+
.orElse(""))
358+
.createAt(application.getCreatedAt().toEpochMilli())
359+
.createBy(application.getCreatedBy())
360+
.build();
361+
});
362+
363+
});
364+
}
365+
313366
private ApplicationInfoView buildView(Application application, ResourceRole maxRole, Map<String, User> userMap, @Nullable Instant lastViewTime,
314367
boolean withContainerSize) {
315368
ApplicationInfoViewBuilder applicationInfoViewBuilder = ApplicationInfoView.builder()

0 commit comments

Comments
 (0)