From 570d184417426bc43a47ee045a779d8e73de92e6 Mon Sep 17 00:00:00 2001 From: Abdul Qadir Date: Wed, 14 Feb 2024 19:00:11 +0500 Subject: [PATCH 1/2] Add feature to set app as agency profile --- .../domain/application/model/Application.java | 8 +++ .../repository/ApplicationRepository.java | 7 ++- .../service/ApplicationService.java | 13 ++++- .../permission/model/ResourceAction.java | 1 + .../application/ApplicationApiService.java | 8 ++- .../application/ApplicationController.java | 25 +++++++++ .../api/application/ApplicationEndpoints.java | 35 ++++++++++++ .../lowcoder/api/home/UserHomeApiService.java | 2 + .../api/home/UserHomeApiServiceImpl.java | 53 +++++++++++++++++++ 9 files changed, 148 insertions(+), 4 deletions(-) diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/Application.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/Application.java index 8144b45e2..89c76c41b 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/Application.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/Application.java @@ -41,6 +41,8 @@ public class Application extends HasIdAndAuditing { private final Boolean publicToAll; private final Boolean publicToMarketplace; + private final Boolean agencyProfile; + private Map editingApplicationDSL; @Transient @@ -78,6 +80,7 @@ public Application(@JsonProperty("orgId") String organizationId, @JsonProperty("publishedApplicationDSL") Map publishedApplicationDSL, @JsonProperty("publicToAll") Boolean publicToAll, @JsonProperty("publicToMarketplace") Boolean publicToMarketplace, + @JsonProperty("agencyProfile") Boolean agencyProfile, @JsonProperty("editingApplicationDSL") Map editingApplicationDSL) { this.organizationId = organizationId; this.name = name; @@ -86,6 +89,7 @@ public Application(@JsonProperty("orgId") String organizationId, this.publishedApplicationDSL = publishedApplicationDSL; this.publicToAll = publicToAll; this.publicToMarketplace = publicToMarketplace; + this.agencyProfile = agencyProfile; this.editingApplicationDSL = editingApplicationDSL; } @@ -113,6 +117,10 @@ public boolean isPublicToMarketplace() { return BooleanUtils.toBooleanDefaultIfNull(publicToMarketplace, false); } + public boolean agencyProfile() { + return BooleanUtils.toBooleanDefaultIfNull(agencyProfile, false); + } + public ApplicationQuery getQueryByViewModeAndQueryId(boolean isViewMode, String queryId) { return (isViewMode ? getLiveQueries() : getEditingQueries()) .stream() diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationRepository.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationRepository.java index 8c018d71e..ac37c6e9d 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationRepository.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationRepository.java @@ -34,9 +34,12 @@ public interface ApplicationRepository extends ReactiveMongoRepository findByIdIn(List ids); - @Query(fields = "{_id : 1}") - Flux findByPublicToAllIsTrueAndPublicToMarketplaceIsAndIdIn(Boolean publicToMarketplace, Collection ids); + @Query(value = "{$and:[{'publicToAll':true},{'$or':[{'publicToMarketplace':?0},{'agencyProfile':?1}]}, {'_id': { $in: ?2}}]}", fields = "{_id : 1}") + Flux findByPublicToAllIsTrueAndPublicToMarketplaceIsOrAgencyProfileIsAndIdIn + (Boolean publicToMarketplace, Boolean agencyProfile, Collection ids); Flux findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue(); + Flux findByPublicToAllIsTrueAndAgencyProfileIsTrue(); + } diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationService.java index ec6997e09..a97f8ca67 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationService.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationService.java @@ -107,6 +107,10 @@ public Flux findAllMarketplaceApps() { return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue(); } + public Flux findAllAgencyProfileApps() { + return repository.findByPublicToAllIsTrueAndAgencyProfileIsTrue(); + } + public Mono countByOrganizationId(String orgId, ApplicationStatus applicationStatus) { return repository.countByOrganizationIdAndApplicationStatus(orgId, applicationStatus); } @@ -158,10 +162,17 @@ public Mono setApplicationPublicToMarketplace(String applicationId, boo return mongoUpsertHelper.updateById(application, applicationId); } + public Mono setApplicationAsAgencyProfile(String applicationId, boolean agencyProfile) { + Application application = Application.builder() + .agencyProfile(agencyProfile) + .build(); + return mongoUpsertHelper.updateById(application, applicationId); + } + @NonEmptyMono @SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform") public Mono> getPublicApplicationIds(Collection applicationIds, Boolean isAnonymous) { - return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsAndIdIn(!isAnonymous, applicationIds) + return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsOrAgencyProfileIsAndIdIn(!isAnonymous, !isAnonymous, applicationIds) .map(HasIdAndAuditing::getId) .collect(Collectors.toSet()); diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/model/ResourceAction.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/model/ResourceAction.java index c11257e41..da442e7e0 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/model/ResourceAction.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/permission/model/ResourceAction.java @@ -25,6 +25,7 @@ public enum ResourceAction { SET_APPLICATIONS_PUBLIC(ResourceRole.EDITOR, ResourceType.APPLICATION), SET_APPLICATIONS_PUBLIC_TO_MARKETPLACE(ResourceRole.EDITOR, ResourceType.APPLICATION), + SET_APPLICATIONS_AS_AGENCY_PROFILE(ResourceRole.EDITOR, ResourceType.APPLICATION), // datasource action MANAGE_DATASOURCES(ResourceRole.OWNER, ResourceType.DATASOURCE), diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java index cf3d68d48..f8b624c2a 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java @@ -141,7 +141,7 @@ public Mono create(CreateApplicationRequest createApplicationRe createApplicationRequest.applicationType(), NORMAL, createApplicationRequest.publishedApplicationDSL(), - false, false, createApplicationRequest.editingApplicationDSL()); + false, false, false, createApplicationRequest.editingApplicationDSL()); if (StringUtils.isBlank(application.getOrganizationId())) { return deferredError(INVALID_PARAMETER, "ORG_ID_EMPTY"); @@ -506,6 +506,12 @@ public Mono setApplicationPublicToMarketplace(String applicationId, boo .then(applicationService.setApplicationPublicToMarketplace(applicationId, publicToMarketplace)); } + public Mono setApplicationAsAgencyProfile(String applicationId, boolean agencyProfile) { + return checkCurrentUserApplicationPermission(applicationId, ResourceAction.SET_APPLICATIONS_AS_AGENCY_PROFILE) + .then(checkApplicationStatus(applicationId, NORMAL)) + .then(applicationService.setApplicationAsAgencyProfile(applicationId, agencyProfile)); + } + private Map sanitizeDsl(Map applicationDsl) { if (applicationDsl.get("queries") instanceof List queries) { List> list = queries.stream().map(this::doSanitizeQuery).toList(); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java index a5c29b132..5f02f9783 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java @@ -105,6 +105,14 @@ public Mono> getPublishedMarketPlaceApplication(@P .map(ResponseView::success); } + @Override + public Mono> getAgencyProfileApplication(@PathVariable String applicationId) { + return applicationApiService.getPublishedApplication(applicationId) + .delayUntil(applicationView -> applicationApiService.updateUserApplicationLastViewTime(applicationId)) + .delayUntil(applicationView -> businessEventPublisher.publishApplicationCommonEvent(applicationView, VIEW)) + .map(ResponseView::success); + } + @Override public Mono> update(@PathVariable String applicationId, @RequestBody Application newApplication) { @@ -144,6 +152,14 @@ public Mono>> getMarketplaceAp .map(ResponseView::success); } + @Override + public Mono>> getAgencyProfileApplications(@RequestParam(required = false) Integer applicationType) { + ApplicationType applicationTypeEnum = applicationType == null ? null : ApplicationType.fromValue(applicationType); + return userHomeApiService.getAllMarketplaceApplications(applicationTypeEnum) + .collectList() + .map(ResponseView::success); + } + @Override public Mono> updatePermission(@PathVariable String applicationId, @PathVariable String permissionId, @@ -201,4 +217,13 @@ public Mono> setApplicationPublicToMarketplace(@PathVariab return applicationApiService.setApplicationPublicToMarketplace(applicationId, request.publicToMarketplace()) .map(ResponseView::success); } + + @Override + public Mono> setApplicationAsAgencyProfile(@PathVariable String applicationId, + @RequestBody ApplicationAsAgencyProfileRequest request) { + return applicationApiService.setApplicationAsAgencyProfile(applicationId, request.agencyProfile()) + .map(ResponseView::success); + } + + } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java index 0d03f608f..2c9733382 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java @@ -120,6 +120,15 @@ public interface ApplicationEndpoints @GetMapping("/{applicationId}/view_marketplace") public Mono> getPublishedMarketPlaceApplication(@PathVariable String applicationId); + @Operation( + tags = TAG_APPLICATION_MANAGEMENT, + operationId = "getAgencyProfileApplicationDataInViewMode", + summary = "Get Agency profile Application data in view mode", + description = "Retrieve the DSL data of a Lowcoder Application in view-mode by its ID marked as agency profile." + ) + @GetMapping("/{applicationId}/view_agency") + public Mono> getAgencyProfileApplication(@PathVariable String applicationId); + @Operation( tags = TAG_APPLICATION_MANAGEMENT, operationId = "updateApplication", @@ -168,6 +177,15 @@ public Mono>> getApplications(@RequestPar @GetMapping("/marketplace-apps") public Mono>> getMarketplaceApplications(@RequestParam(required = false) Integer applicationType); + @Operation( + tags = TAG_APPLICATION_MANAGEMENT, + operationId = "listAgencyProfileApplications", + summary = "List agency profile Applications", + description = "Retrieve a list of Lowcoder Applications that are set as agency profiles" + ) + @GetMapping("/agency-profiles") + public Mono>> getAgencyProfileApplications(@RequestParam(required = false) Integer applicationType); + @Operation( tags = TAG_APPLICATION_PERMISSIONS, operationId = "updateApplicationPermissions", @@ -231,6 +249,16 @@ public Mono> setApplicationPublicToAll(@PathVariable Strin public Mono> setApplicationPublicToMarketplace(@PathVariable String applicationId, @RequestBody ApplicationPublicToMarketplaceRequest request); + @Operation( + tags = TAG_APPLICATION_MANAGEMENT, + operationId = "setApplicationAsAgencyProfile", + summary = "Set Application as agency profile", + description = "Set a Lowcoder Application identified by its ID as as agency profile but to only logged in users." + ) + @PutMapping("/{applicationId}/agency-profile") + public Mono> setApplicationAsAgencyProfile(@PathVariable String applicationId, + @RequestBody ApplicationAsAgencyProfileRequest request); + public record BatchAddPermissionRequest(String role, Set userIds, Set groupIds) { } @@ -249,6 +277,13 @@ public Boolean publicToMarketplace() { } } + public record ApplicationAsAgencyProfileRequest(Boolean agencyProfile) { + @Override + public Boolean agencyProfile() { + return BooleanUtils.isTrue(agencyProfile); + } + } + public record UpdatePermissionRequest(String role) { } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java index 5f194fcc4..454af54d8 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java @@ -25,4 +25,6 @@ Flux getAllAuthorisedApplications4CurrentOrgMember(@Nullabl @Nullable ApplicationStatus applicationStatus, boolean withContainerSize); public Flux getAllMarketplaceApplications(@Nullable ApplicationType applicationType); + + public Flux getAllAgencyProfileApplications(@Nullable ApplicationType applicationType); } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java index 6d7aff678..e10fe0ea1 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java @@ -310,6 +310,59 @@ public Flux getAllMarketplaceApplications(@Nulla }); } + @Override + public Flux getAllAgencyProfileApplications(@Nullable ApplicationType applicationType) { + + return sessionUserService.getVisitorOrgMemberCache() + .flatMapMany(orgMember -> { + // application flux + Flux applicationFlux = Flux.defer(() -> applicationService.findAllAgencyProfileApps()) + .filter(application -> isNull(applicationType) || application.getApplicationType() == applicationType.getValue()) + .cache(); + + // user map + Mono> userMapMono = applicationFlux + .flatMap(application -> emptyIfNull(application.getCreatedBy())) + .collectList() + .flatMap(creatorIds -> userService.getByIds(creatorIds)) + .cache(); + + // org map + Mono> orgMapMono = applicationFlux + .flatMap(application -> emptyIfNull(application.getOrganizationId())) + .collectList() + .flatMap(orgIds -> organizationService.getByIds(orgIds) + .collectList() + .map(it -> it.stream().collect(Collectors.toMap(Organization::getId, Function.identity()))) + ) + .cache(); + + + return applicationFlux + .flatMap(application -> Mono.zip(Mono.just(application), userMapMono, orgMapMono)) + .map(tuple -> { + // build view + Application application = tuple.getT1(); + Map userMap = tuple.getT2(); + Map orgMap = tuple.getT3(); + return MarketplaceApplicationInfoView.builder() + .applicationId(application.getId()) + .name(application.getName()) + .applicationType(application.getApplicationType()) + .applicationStatus(application.getApplicationStatus()) + .orgId(application.getOrganizationId()) + .orgName(orgMap.get(application.getOrganizationId()).getName()) + .creatorEmail(Optional.ofNullable(userMap.get(application.getCreatedBy())) + .map(User::getName) + .orElse("")) + .createAt(application.getCreatedAt().toEpochMilli()) + .createBy(application.getCreatedBy()) + .build(); + }); + + }); + } + private ApplicationInfoView buildView(Application application, ResourceRole maxRole, Map userMap, @Nullable Instant lastViewTime, boolean withContainerSize) { ApplicationInfoViewBuilder applicationInfoViewBuilder = ApplicationInfoView.builder() From acbef3c0e54048b51bac3a1bce624f838afc3c65 Mon Sep 17 00:00:00 2001 From: Abdul Qadir Date: Wed, 14 Feb 2024 19:30:45 +0500 Subject: [PATCH 2/2] Add validation between ptm and agency apis --- .../api/application/ApplicationApiService.java | 18 ++++++++++++++++-- .../api/application/ApplicationController.java | 6 +++--- .../api/application/ApplicationEndpoints.java | 6 ++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java index f8b624c2a..f1eaad172 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java @@ -248,6 +248,19 @@ private Mono checkApplicationStatus(Application application, ApplicationSt return Mono.error(new BizException(BizError.UNSUPPORTED_OPERATION, "BAD_REQUEST")); } + private Mono checkApplicationViewRequest(Application application, ApplicationEndpoints.ApplicationRequestType expected) { + if (expected == ApplicationEndpoints.ApplicationRequestType.PUBLIC_TO_ALL && application.isPublicToAll()) { + return Mono.empty(); + } + if (expected == ApplicationEndpoints.ApplicationRequestType.PUBLIC_TO_MARKETPLACE && application.isPublicToMarketplace()) { + return Mono.empty(); + } + if (expected == ApplicationEndpoints.ApplicationRequestType.AGENCY_PROFILE && application.agencyProfile()) { + return Mono.empty(); + } + return Mono.error(new BizException(BizError.UNSUPPORTED_OPERATION, "BAD_REQUEST")); + } + private Mono updateApplicationStatus(String applicationId, ApplicationStatus applicationStatus) { return checkCurrentUserApplicationPermission(applicationId, MANAGE_APPLICATIONS) .then(Mono.defer(() -> { @@ -280,10 +293,11 @@ public Mono getEditingApplication(String applicationId) { }); } - public Mono getPublishedApplication(String applicationId) { + public Mono getPublishedApplication(String applicationId, ApplicationEndpoints.ApplicationRequestType requestType) { return checkPermissionWithReadableErrorMsg(applicationId, READ_APPLICATIONS) .zipWhen(permission -> applicationService.findById(applicationId) - .delayUntil(application -> checkApplicationStatus(application, NORMAL))) + .delayUntil(application -> checkApplicationStatus(application, NORMAL)) + .delayUntil(application -> checkApplicationViewRequest(application, requestType))) .zipWhen(tuple -> applicationService.getAllDependentModulesFromApplication(tuple.getT2(), true), TupleUtils::merge) .zipWhen(tuple -> organizationService.getOrgCommonSettings(tuple.getT2().getOrganizationId()), TupleUtils::merge) .zipWith(getTemplateIdFromApplicationId(applicationId), TupleUtils::merge) diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java index 5f02f9783..e75de598d 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java @@ -91,7 +91,7 @@ public Mono> getEditingApplication(@PathVariable S @Override public Mono> getPublishedApplication(@PathVariable String applicationId) { - return applicationApiService.getPublishedApplication(applicationId) + return applicationApiService.getPublishedApplication(applicationId, ApplicationRequestType.PUBLIC_TO_ALL) .delayUntil(applicationView -> applicationApiService.updateUserApplicationLastViewTime(applicationId)) .delayUntil(applicationView -> businessEventPublisher.publishApplicationCommonEvent(applicationView, VIEW)) .map(ResponseView::success); @@ -99,7 +99,7 @@ public Mono> getPublishedApplication(@PathVariable @Override public Mono> getPublishedMarketPlaceApplication(@PathVariable String applicationId) { - return applicationApiService.getPublishedApplication(applicationId) + return applicationApiService.getPublishedApplication(applicationId, ApplicationRequestType.PUBLIC_TO_MARKETPLACE) .delayUntil(applicationView -> applicationApiService.updateUserApplicationLastViewTime(applicationId)) .delayUntil(applicationView -> businessEventPublisher.publishApplicationCommonEvent(applicationView, VIEW)) .map(ResponseView::success); @@ -107,7 +107,7 @@ public Mono> getPublishedMarketPlaceApplication(@P @Override public Mono> getAgencyProfileApplication(@PathVariable String applicationId) { - return applicationApiService.getPublishedApplication(applicationId) + return applicationApiService.getPublishedApplication(applicationId, ApplicationRequestType.AGENCY_PROFILE) .delayUntil(applicationView -> applicationApiService.updateUserApplicationLastViewTime(applicationId)) .delayUntil(applicationView -> businessEventPublisher.publishApplicationCommonEvent(applicationView, VIEW)) .map(ResponseView::success); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java index 2c9733382..eae901f21 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java @@ -284,6 +284,12 @@ public Boolean agencyProfile() { } } + public enum ApplicationRequestType { + PUBLIC_TO_ALL, + PUBLIC_TO_MARKETPLACE, + AGENCY_PROFILE, + } + public record UpdatePermissionRequest(String role) { }