Skip to content

Make App Public To Marketplace Feature #677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: add marketplace applications handling
  • Loading branch information
aq-ikhwa-tech committed Feb 1, 2024
commit d33f51a710939a541bc96122cad86a011bd685a8
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public class Application extends HasIdAndAuditing {
private final Map<String, Object> publishedApplicationDSL;

private final Boolean publicToAll;
private final Boolean publicToMarketplace;

private Map<String, Object> editingApplicationDSL;

@Transient
Expand Down Expand Up @@ -75,13 +77,15 @@ public Application(@JsonProperty("orgId") String organizationId,
@JsonProperty("applicationStatus") ApplicationStatus applicationStatus,
@JsonProperty("publishedApplicationDSL") Map<String, Object> publishedApplicationDSL,
@JsonProperty("publicToAll") Boolean publicToAll,
@JsonProperty("publicToMarketplace") Boolean publicToMarketplace,
@JsonProperty("editingApplicationDSL") Map<String, Object> editingApplicationDSL) {
this.organizationId = organizationId;
this.name = name;
this.applicationType = applicationType;
this.applicationStatus = applicationStatus;
this.publishedApplicationDSL = publishedApplicationDSL;
this.publicToAll = publicToAll;
this.publicToMarketplace = publicToMarketplace;
this.editingApplicationDSL = editingApplicationDSL;
}

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

public boolean isPublicToMarketplace() {
return BooleanUtils.toBooleanDefaultIfNull(publicToMarketplace, false);
}

public ApplicationQuery getQueryByViewModeAndQueryId(boolean isViewMode, String queryId) {
return (isViewMode ? getLiveQueries() : getEditingQueries())
.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ public interface ApplicationRepository extends ReactiveMongoRepository<Applicati
@Query(fields = "{_id : 1}")
Flux<Application> findByPublicToAllIsTrueAndIdIn(Collection<String> ids);

Flux<Application> findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();

}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ public Flux<Application> findByOrganizationIdWithoutDsl(String organizationId) {
return repository.findByOrganizationId(organizationId);
}

public Flux<Application> findAllMarketplaceApps() {
return repository.findByPublicToAllIsTrueAndPublicToMarketplaceIsTrue();
}

public Mono<Long> countByOrganizationId(String orgId, ApplicationStatus applicationStatus) {
return repository.countByOrganizationIdAndApplicationStatus(orgId, applicationStatus);
}
Expand Down Expand Up @@ -147,6 +151,13 @@ public Mono<Boolean> setApplicationPublicToAll(String applicationId, boolean pub
return mongoUpsertHelper.updateById(application, applicationId);
}

public Mono<Boolean> setApplicationPublicToMarketplace(String applicationId, boolean publicToMarketplace) {
Application application = Application.builder()
.publicToMarketplace(publicToMarketplace)
.build();
return mongoUpsertHelper.updateById(application, applicationId);
}

@NonEmptyMono
@SuppressWarnings("ReactiveStreamsNullableInLambdaInTransform")
public Mono<Set<String>> getPublicApplicationIds(Collection<String> applicationIds) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public enum ResourceAction {
EDIT_APPLICATIONS(ResourceRole.EDITOR, ResourceType.APPLICATION),

SET_APPLICATIONS_PUBLIC(ResourceRole.EDITOR, ResourceType.APPLICATION),
SET_APPLICATIONS_PUBLIC_TO_MARKETPLACE(ResourceRole.EDITOR, ResourceType.APPLICATION),

// datasource action
MANAGE_DATASOURCES(ResourceRole.OWNER, ResourceType.DATASOURCE),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public Mono<ApplicationView> create(CreateApplicationRequest createApplicationRe
createApplicationRequest.applicationType(),
NORMAL,
createApplicationRequest.publishedApplicationDSL(),
false, createApplicationRequest.editingApplicationDSL());
false, false, createApplicationRequest.editingApplicationDSL());

if (StringUtils.isBlank(application.getOrganizationId())) {
return deferredError(INVALID_PARAMETER, "ORG_ID_EMPTY");
Expand Down Expand Up @@ -429,6 +429,7 @@ public Mono<ApplicationPermissionView> getApplicationPermissions(String applicat
.creatorId(creatorId)
.orgName(organization.getName())
.publicToAll(application.isPublicToAll())
.publicToMarketplace(application.isPublicToMarketplace())
.build();
});
});
Expand Down Expand Up @@ -485,6 +486,7 @@ private ApplicationInfoView buildView(Application application, String role, @Nul
.applicationStatus(application.getApplicationStatus())
.folderId(folderId)
.publicToAll(application.isPublicToAll())
.publicToMarketplace(application.isPublicToMarketplace())
.build();
}

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

public Mono<Boolean> setApplicationPublicToMarketplace(String applicationId, boolean publicToMarketplace) {
return checkCurrentUserApplicationPermission(applicationId, ResourceAction.SET_APPLICATIONS_PUBLIC_TO_MARKETPLACE)
.then(checkApplicationStatus(applicationId, NORMAL))
.then(applicationService.setApplicationPublicToMarketplace(applicationId, publicToMarketplace));
}

private Map<String, Object> sanitizeDsl(Map<String, Object> applicationDsl) {
if (applicationDsl.get("queries") instanceof List<?> queries) {
List<Map<String, Object>> list = queries.stream().map(this::doSanitizeQuery).toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.lowcoder.api.application.view.ApplicationInfoView;
import org.lowcoder.api.application.view.ApplicationPermissionView;
import org.lowcoder.api.application.view.ApplicationView;
import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
import org.lowcoder.api.framework.view.ResponseView;
import org.lowcoder.api.home.UserHomeApiService;
import org.lowcoder.api.home.UserHomepageView;
Expand Down Expand Up @@ -127,6 +128,14 @@ public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestPar
.map(ResponseView::success);
}

@Override
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getMarketplaceApplications(@RequestParam(required = false) Integer applicationType) {
ApplicationType applicationTypeEnum = applicationType == null ? null : ApplicationType.fromValue(applicationType);
return userHomeApiService.getAllMarketplaceApplications(applicationTypeEnum)
.collectList()
.map(ResponseView::success);
}

@Override
public Mono<ResponseView<Boolean>> updatePermission(@PathVariable String applicationId,
@PathVariable String permissionId,
Expand Down Expand Up @@ -177,4 +186,11 @@ public Mono<ResponseView<Boolean>> setApplicationPublicToAll(@PathVariable Strin
return applicationApiService.setApplicationPublicToAll(applicationId, request.publicToAll())
.map(ResponseView::success);
}

@Override
public Mono<ResponseView<Boolean>> setApplicationPublicToMarketplace(@PathVariable String applicationId,
@RequestBody ApplicationPublicToMarketplaceRequest request) {
return applicationApiService.setApplicationPublicToMarketplace(applicationId, request.publicToMarketplace())
.map(ResponseView::success);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.lowcoder.api.application.view.ApplicationInfoView;
import org.lowcoder.api.application.view.ApplicationPermissionView;
import org.lowcoder.api.application.view.ApplicationView;
import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
import org.lowcoder.api.framework.view.ResponseView;
import org.lowcoder.api.home.UserHomepageView;
import org.lowcoder.domain.application.model.Application;
Expand Down Expand Up @@ -149,6 +150,15 @@ public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestPar
@RequestParam(required = false) ApplicationStatus applicationStatus,
@RequestParam(defaultValue = "true") boolean withContainerSize);

@Operation(
tags = TAG_APPLICATION_MANAGEMENT,
operationId = "listMarketplaceApplications",
summary = "List marketplace Applications",
description = "Retrieve a list of Lowcoder Applications that are published to the marketplace"
)
@GetMapping("/marketplace-apps")
public Mono<ResponseView<List<MarketplaceApplicationInfoView>>> getMarketplaceApplications(@RequestParam(required = false) Integer applicationType);

@Operation(
tags = TAG_APPLICATION_PERMISSIONS,
operationId = "updateApplicationPermissions",
Expand Down Expand Up @@ -202,8 +212,18 @@ public Mono<ResponseView<Boolean>> grantPermission(
public Mono<ResponseView<Boolean>> setApplicationPublicToAll(@PathVariable String applicationId,
@RequestBody ApplicationPublicToAllRequest request);


public record BatchAddPermissionRequest(String role, Set<String> userIds, Set<String> groupIds) {
@Operation(
tags = TAG_APPLICATION_MANAGEMENT,
operationId = "setApplicationAsPublicToMarketplace",
summary = "Set Application as publicly available on marketplace but to only logged in users",
description = "Set a Lowcoder Application identified by its ID as publicly available on marketplace but to only logged in users."
)
@PutMapping("/{applicationId}/public-to-marketplace")
public Mono<ResponseView<Boolean>> setApplicationPublicToMarketplace(@PathVariable String applicationId,
@RequestBody ApplicationPublicToMarketplaceRequest request);


public record BatchAddPermissionRequest(String role, Set<String> userIds, Set<String> groupIds) {
}

public record ApplicationPublicToAllRequest(Boolean publicToAll) {
Expand All @@ -213,6 +233,13 @@ public Boolean publicToAll() {
}
}

public record ApplicationPublicToMarketplaceRequest(Boolean publicToMarketplace) {
@Override
public Boolean publicToMarketplace() {
return BooleanUtils.isTrue(publicToMarketplace);
}
}

public record UpdatePermissionRequest(String role) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public class ApplicationInfoView {

private final boolean publicToAll;

private final boolean publicToMarketplace;

public long getLastViewTime() {
return lastViewTime == null ? 0 : lastViewTime.toEpochMilli();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
public class ApplicationPermissionView extends CommonPermissionView {

private boolean publicToAll;
private boolean publicToMarketplace;

public boolean isPublicToAll() {
return publicToAll;
}

public boolean isPublicToMarketplace() {
return publicToMarketplace;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.lowcoder.api.application.view;

import lombok.Builder;
import lombok.Getter;
import org.lowcoder.domain.application.model.ApplicationStatus;

@Builder
@Getter
public class MarketplaceApplicationInfoView {

// org details
private final String orgId;
private final String orgName;

// creator info
private final String creatorEmail;

// App details
private final String applicationId;
private final String name;
private final long createAt;
private final String createBy;
/**
* @see org.lowcoder.domain.application.model.ApplicationType
*/
private final int applicationType;
private final ApplicationStatus applicationStatus;


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import javax.annotation.Nullable;

import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

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

Flux<ApplicationInfoView> getAllAuthorisedApplications4CurrentOrgMember(@Nullable ApplicationType applicationType,
@Nullable ApplicationStatus applicationStatus, boolean withContainerSize);

public Flux<MarketplaceApplicationInfoView> getAllMarketplaceApplications(@Nullable ApplicationType applicationType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.apache.commons.lang3.StringUtils;
import org.lowcoder.api.application.view.ApplicationInfoView;
import org.lowcoder.api.application.view.ApplicationInfoView.ApplicationInfoViewBuilder;
import org.lowcoder.api.application.view.MarketplaceApplicationInfoView;
import org.lowcoder.api.usermanagement.OrgDevChecker;
import org.lowcoder.api.usermanagement.view.OrgAndVisitorRoleView;
import org.lowcoder.api.usermanagement.view.UserProfileView;
Expand Down Expand Up @@ -256,6 +257,59 @@ public Flux<ApplicationInfoView> getAllAuthorisedApplications4CurrentOrgMember(@
});
}

@Override
public Flux<MarketplaceApplicationInfoView> getAllMarketplaceApplications(@Nullable ApplicationType applicationType) {

return sessionUserService.getVisitorOrgMemberCache()
.flatMapMany(orgMember -> {
// application flux
Flux<Application> applicationFlux = Flux.defer(() -> applicationService.findAllMarketplaceApps())
.filter(application -> isNull(applicationType) || application.getApplicationType() == applicationType.getValue())
.cache();

// user map
Mono<Map<String, User>> userMapMono = applicationFlux
.flatMap(application -> emptyIfNull(application.getCreatedBy()))
.collectList()
.flatMap(creatorIds -> userService.getByIds(creatorIds))
.cache();

// org map
Mono<Map<String, Organization>> 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<String, User> userMap = tuple.getT2();
Map<String, Organization> 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<String, User> userMap, @Nullable Instant lastViewTime,
boolean withContainerSize) {
ApplicationInfoViewBuilder applicationInfoViewBuilder = ApplicationInfoView.builder()
Expand All @@ -271,7 +325,8 @@ private ApplicationInfoView buildView(Application application, ResourceRole maxR
.applicationStatus(application.getApplicationStatus())
.lastModifyTime(application.getUpdatedAt())
.lastViewTime(lastViewTime)
.publicToAll(application.isPublicToAll());
.publicToAll(application.isPublicToAll())
.publicToMarketplace(application.isPublicToMarketplace());
if (withContainerSize) {
return applicationInfoViewBuilder
.containerSize(application.getLiveContainerSize())
Expand Down