Skip to content
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

feat: API for search entities #29203

Merged
merged 23 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4a27a58
feat: API to fetch workspaces for homepage
abhvsn Nov 16, 2023
169fc5c
test: Add testcases
abhvsn Nov 17, 2023
b64713c
Merge branch 'release' into feat/add-workspece-homepage-api
abhvsn Nov 20, 2023
98bfa7b
Add testcases for domain sorter class
abhvsn Nov 20, 2023
88eae0e
Create basic skeleton with API contract
abhvsn Nov 21, 2023
257bd61
feat(RecentlyUsedEntityDTO): Introduce RecentlyUsedEntityDTO and impl…
abhvsn Nov 24, 2023
40f175e
test: Add testcases
abhvsn Nov 27, 2023
2a267ef
Merge branch 'release' into feat/add-get-applications-for-homepage-api
abhvsn Nov 28, 2023
3beeae6
Add testcases for updating the entities
abhvsn Nov 28, 2023
4962ab9
Merge branch 'release' into feat/add-get-applications-for-homepage-api
abhvsn Nov 28, 2023
042df3d
feat: Add migration to track recently used entities for user data
abhvsn Nov 28, 2023
c62ee13
feat: Add search entity functionality for homepage
abhvsn Nov 29, 2023
4eb0876
feat: Add entity selector option to enhance search functionality
abhvsn Nov 29, 2023
fbedec8
chore: Add indexing comment for search field method
abhvsn Dec 4, 2023
8d8dd6e
Merge branch 'release' into feat/search-api-homepage
abhvsn Dec 4, 2023
0a80de2
feat: Add SearchEntityHelper and its test cases
abhvsn Dec 4, 2023
2f9ea02
test: Add testcases and fix get default application when connected to…
abhvsn Dec 5, 2023
34ff3cf
feat: Trim the search string before fetching results from DB
abhvsn Dec 5, 2023
86c6462
Merge branch 'release' into feat/search-api-homepage
abhvsn Dec 5, 2023
5a6c1bb
chore: Resolve merge conflicts with release
abhvsn Dec 5, 2023
f86074a
Merge branch 'release' into feat/search-api-homepage
abhvsn Dec 6, 2023
fb606c2
chore: Resolve merge conflicts with release
abhvsn Dec 6, 2023
aadb679
refactor method name
abhvsn Dec 6, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.appsmith.server.dtos.GitAuthDTO;
import com.appsmith.server.services.CrudService;
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.codec.multipart.Part;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand All @@ -29,6 +31,8 @@ public interface ApplicationServiceCE extends CrudService<Application, String> {

Flux<Application> findByWorkspaceId(String workspaceId, AclPermission permission);

Flux<Application> findByWorkspaceIdAndDefaultApplicationsInRecentlyUsedOrder(String workspaceId);

Flux<Application> findByClonedFromApplicationId(String applicationId, AclPermission permission);

Mono<Application> findByName(String name, AclPermission permission);
Expand Down Expand Up @@ -103,4 +107,7 @@ Mono<UpdateResult> setAppTheme(
Mono<Boolean> isApplicationConnectedToGit(String applicationId);

Mono<Void> updateProtectedBranches(String applicationId, List<String> protectedBranches);

Flux<Application> filterByFields(
List<String> fieldNames, String searchString, Pageable pageable, Sort sort, AclPermission permission);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
import com.appsmith.server.domains.Page;
import com.appsmith.server.domains.QApplication;
import com.appsmith.server.domains.Theme;
import com.appsmith.server.domains.UserData;
import com.appsmith.server.dtos.ApplicationAccessDTO;
import com.appsmith.server.dtos.GitAuthDTO;
import com.appsmith.server.dtos.GitDeployKeyDTO;
import com.appsmith.server.dtos.RecentlyUsedEntityDTO;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.exceptions.util.DuplicateKeyExceptionUtils;
import com.appsmith.server.helpers.GitDeployKeyGenerator;
import com.appsmith.server.helpers.GitUtils;
import com.appsmith.server.helpers.ResponseUtils;
import com.appsmith.server.helpers.TextUtils;
import com.appsmith.server.migrations.ApplicationVersion;
Expand All @@ -35,6 +38,7 @@
import com.appsmith.server.services.ConfigService;
import com.appsmith.server.services.PermissionGroupService;
import com.appsmith.server.services.SessionUserService;
import com.appsmith.server.services.UserDataService;
import com.appsmith.server.solutions.ApplicationPermission;
import com.appsmith.server.solutions.DatasourcePermission;
import com.appsmith.server.solutions.PolicySolution;
Expand Down Expand Up @@ -67,6 +71,7 @@
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
import static com.appsmith.server.constants.Constraint.MAX_LOGO_SIZE_KB;
import static com.appsmith.server.helpers.ce.DomainSorter.sortDomainsBasedOnOrderedDomainIds;
import static org.apache.commons.lang3.StringUtils.isBlank;

@Slf4j
Expand All @@ -84,6 +89,7 @@ public class ApplicationServiceCEImpl extends BaseService<ApplicationRepository,
private final DatasourcePermission datasourcePermission;
private final ApplicationPermission applicationPermission;
private final SessionUserService sessionUserService;
private final UserDataService userDataService;
private static final Integer MAX_RETRIES = 5;

@Autowired
Expand All @@ -102,7 +108,8 @@ public ApplicationServiceCEImpl(
AssetService assetService,
DatasourcePermission datasourcePermission,
ApplicationPermission applicationPermission,
SessionUserService sessionUserService) {
SessionUserService sessionUserService,
UserDataService userDataService) {

super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
this.policySolution = policySolution;
Expand All @@ -114,6 +121,7 @@ public ApplicationServiceCEImpl(
this.datasourcePermission = datasourcePermission;
this.applicationPermission = applicationPermission;
this.sessionUserService = sessionUserService;
this.userDataService = userDataService;
}

@Override
Expand Down Expand Up @@ -179,6 +187,51 @@ public Flux<Application> findByWorkspaceId(String workspaceId, AclPermission per
return setTransientFields(repository.findByWorkspaceId(workspaceId, permission));
}

/**
* This method is used to fetch all the applications for a given workspaceId. It also sorts the applications based
* on recently used order.
* For git connected applications only default branched application is returned.
* @param workspaceId workspaceId for which applications are to be fetched
* @return Flux of applications
*/
@Override
public Flux<Application> findByWorkspaceIdAndDefaultApplicationsInRecentlyUsedOrder(String workspaceId) {

if (!StringUtils.hasLength(workspaceId)) {
return Flux.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.WORKSPACE_ID));
}

Mono<RecentlyUsedEntityDTO> userDataMono = userDataService
.getForCurrentUser()
.defaultIfEmpty(new UserData())
.map(userData -> {
if (userData.getRecentlyUsedEntityIds() == null) {
return new RecentlyUsedEntityDTO();
}
return userData.getRecentlyUsedEntityIds().stream()
.filter(entityDTO -> workspaceId.equals(entityDTO.getWorkspaceId()))
.findFirst()
.orElse(new RecentlyUsedEntityDTO());
});

// Collect all the applications as a map with workspace id as a key
return userDataMono.flatMapMany(
recentlyUsedEntityDTO -> this.findByWorkspaceId(workspaceId, applicationPermission.getReadPermission())
// sort transformation
.transform(domainFlux -> sortDomainsBasedOnOrderedDomainIds(
domainFlux, recentlyUsedEntityDTO.getApplicationIds()))
.filter(application -> {
/*
* Filter applications based on the following criteria:
* - Applications that are not connected to Git.
* - Applications that, when connected, revert with default branch only.
*/
return !GitUtils.isApplicationConnectedToGit(application)
|| GitUtils.isDefaultBranchedApplication(application);
})
.map(responseUtils::updateApplicationWithDefaultResources));
}

@Override
public Flux<Application> findByClonedFromApplicationId(String applicationId, AclPermission permission) {
return repository.findByClonedFromApplicationId(applicationId, permission);
Expand Down Expand Up @@ -991,10 +1044,7 @@ public Mono<Boolean> isApplicationConnectedToGit(String applicationId) {
if (!StringUtils.hasLength(applicationId)) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
}
return this.getById(applicationId)
.map(application -> application.getGitApplicationMetadata() != null
&& StringUtils.hasLength(
application.getGitApplicationMetadata().getRemoteUrl()));
return this.getById(applicationId).map(GitUtils::isApplicationConnectedToGit);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.appsmith.server.services.ConfigService;
import com.appsmith.server.services.PermissionGroupService;
import com.appsmith.server.services.SessionUserService;
import com.appsmith.server.services.UserDataService;
import com.appsmith.server.services.ce_compatible.ApplicationServiceCECompatibleImpl;
import com.appsmith.server.solutions.ApplicationPermission;
import com.appsmith.server.solutions.DatasourcePermission;
Expand Down Expand Up @@ -38,7 +39,8 @@ public ApplicationServiceImpl(
AssetService assetService,
DatasourcePermission datasourcePermission,
ApplicationPermission applicationPermission,
SessionUserService sessionUserService) {
SessionUserService sessionUserService,
UserDataService userDataService) {

super(
scheduler,
Expand All @@ -55,6 +57,7 @@ public ApplicationServiceImpl(
assetService,
datasourcePermission,
applicationPermission,
sessionUserService);
sessionUserService,
userDataService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public class UrlCE {
public static final String USAGE_PULSE_URL = BASE_URL + VERSION + "/usage-pulse";
public static final String TENANT_URL = BASE_URL + VERSION + "/tenants";
public static final String CUSTOM_JS_LIB_URL = BASE_URL + VERSION + "/libraries";

public static final String PRODUCT_ALERT = BASE_URL + VERSION + "/product-alert";
public static final String SEARCH_ENTITY_URL = BASE_URL + VERSION + "/search-entities";

// Sub-paths
public static final String MOCKS = "/mocks";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.appsmith.server.controllers;

import com.appsmith.server.constants.Url;
import com.appsmith.server.controllers.ce.SearchEntityControllerCE;
import com.appsmith.server.searchentities.SearchEntitySolution;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping(Url.SEARCH_ENTITY_URL)
@RestController
public class SearchEntityController extends SearchEntityControllerCE {

public SearchEntityController(SearchEntitySolution searchEntitySolution) {
super(searchEntitySolution);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public Mono<ResponseDTO<List<Application>>> deleteMultipleApps(@Valid @RequestBo
.map(deletedResources -> new ResponseDTO<>(HttpStatus.OK.value(), deletedResources, null));
}

@Deprecated
@JsonView(Views.Public.class)
@GetMapping("/new")
public Mono<ResponseDTO<UserHomepageDTO>> getAllApplicationsForHome() {
Expand All @@ -169,6 +170,16 @@ public Mono<ResponseDTO<UserHomepageDTO>> getAllApplicationsForHome() {
.map(applications -> new ResponseDTO<>(HttpStatus.OK.value(), applications, null));
}

@JsonView(Views.Public.class)
@GetMapping("/home")
public Mono<ResponseDTO<List<Application>>> findByWorkspaceIdAndRecentlyUsedOrder(
@RequestParam(required = false) String workspaceId) {
log.debug("Going to get all applications by workspace id {}", workspaceId);
return service.findByWorkspaceIdAndDefaultApplicationsInRecentlyUsedOrder(workspaceId)
.collectList()
.map(applications -> new ResponseDTO<>(HttpStatus.OK.value(), applications, null));
}

@JsonView(Views.Public.class)
@GetMapping(Url.RELEASE_ITEMS)
public Mono<ResponseDTO<ReleaseItemsDTO>> getReleaseItemsInformation() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.appsmith.server.controllers.ce;

import com.appsmith.external.views.Views;
import com.appsmith.server.constants.Url;
import com.appsmith.server.dtos.ResponseDTO;
import com.appsmith.server.dtos.SearchEntityDTO;
import com.appsmith.server.searchentities.SearchEntitySolution;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import reactor.core.publisher.Mono;

@RequestMapping(Url.SEARCH_ENTITY_URL)
@Slf4j
public class SearchEntityControllerCE {

private final SearchEntitySolution searchEntitySolution;

public SearchEntityControllerCE(SearchEntitySolution searchEntitySolution) {
this.searchEntitySolution = searchEntitySolution;
}

@JsonView(Views.Public.class)
@GetMapping("")
public Mono<ResponseDTO<SearchEntityDTO>> getAllUnpublishedActionCollections(
@RequestParam(required = false) String[] entities,
@RequestParam(required = false, defaultValue = "") String keyword,
@RequestParam(required = false, defaultValue = "0") int page,
@RequestParam(required = false, defaultValue = "20") int size) {
log.debug("Going to search for entities with search string: {}", keyword);
return searchEntitySolution
.searchEntity(entities, keyword, page, size, Boolean.TRUE)
abhvsn marked this conversation as resolved.
Show resolved Hide resolved
.map(resources -> new ResponseDTO<>(HttpStatus.OK.value(), resources, null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,12 @@ public Mono<ResponseDTO<Workspace>> deleteLogo(@PathVariable String workspaceId)
return service.deleteLogo(workspaceId)
.map(workspace -> new ResponseDTO<>(HttpStatus.OK.value(), workspace, null));
}

@JsonView(Views.Public.class)
@GetMapping("/home")
public Mono<ResponseDTO<List<Workspace>>> getUserWorkspacesByRecentlyUsedOrder() {
return userWorkspaceService
.getUserWorkspacesByRecentlyUsedOrder()
.map(workspaces -> new ResponseDTO<>(HttpStatus.OK.value(), workspaces, null));
abhvsn marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.appsmith.external.models.BaseDomain;
import com.appsmith.external.views.Views;
import com.appsmith.server.dtos.RecentlyUsedEntityDTO;
import com.appsmith.server.helpers.CollectionUtils;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.Getter;
Expand Down Expand Up @@ -56,13 +57,19 @@ public class UserData extends BaseDomain {
private List<String> recentlyUsedOrgIds;

// list of workspace ids that were recently accessed by the user
@Deprecated
@JsonView(Views.Public.class)
private List<String> recentlyUsedWorkspaceIds;

// list of application ids that were recently accessed by the user
@Deprecated
@JsonView(Views.Public.class)
private List<String> recentlyUsedAppIds;

// Map of workspaceId to list of recently used applicationIds. This field should be used to add entities
@JsonView(Views.Public.class)
private List<RecentlyUsedEntityDTO> recentlyUsedEntityIds;

// Map of defaultApplicationIds with the GitProfiles. For fallback/default git profile per user default will be the
// the key for the map
@JsonView(Views.Internal.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.appsmith.server.dtos;

import com.appsmith.server.dtos.ce.RecentlyUsedEntityCE_DTO;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class RecentlyUsedEntityDTO extends RecentlyUsedEntityCE_DTO {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.appsmith.server.dtos;

import com.appsmith.server.dtos.ce.SearchEntityCE_DTO;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class SearchEntityDTO extends SearchEntityCE_DTO {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.appsmith.server.dtos.ce;

import lombok.Getter;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
public class RecentlyUsedEntityCE_DTO {
String workspaceId;
List<String> applicationIds;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.appsmith.server.dtos.ce;

import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Workspace;
import lombok.Getter;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
public class SearchEntityCE_DTO {
List<Application> applications;
List<Workspace> workspaces;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.appsmith.server.helpers;

import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.GitApplicationMetadata;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
Expand Down Expand Up @@ -102,4 +103,29 @@ public static String getDefaultBranchName(GitApplicationMetadata gitApplicationM
? gitApplicationMetadata.getBranchName()
: gitApplicationMetadata.getDefaultBranchName();
}

/**
* This method checks if the application is connected to git and is the default branched.
*
* @param application application to be checked
* @return true if the application is default branched, false otherwise
*/
public static boolean isDefaultBranchedApplication(Application application) {
GitApplicationMetadata metadata = application.getGitApplicationMetadata();
return isApplicationConnectedToGit(application)
&& !StringUtils.isEmptyOrNull(metadata.getBranchName())
&& metadata.getBranchName().equals(metadata.getDefaultBranchName());
}

/**
* This method checks if the application is connected to Git or not.
* @param application application to be checked
* @return true if the application is connected to Git, false otherwise
*/
public static boolean isApplicationConnectedToGit(Application application) {
GitApplicationMetadata metadata = application.getGitApplicationMetadata();
return metadata != null
&& !StringUtils.isEmptyOrNull(metadata.getDefaultApplicationId())
&& !StringUtils.isEmptyOrNull(metadata.getRemoteUrl());
}
}
Loading
Loading