chore: Split for custom js libs with local scope (#29216)

This commit is contained in:
Nidhi 2023-11-29 18:39:56 +05:30 committed by GitHub
parent 3b54826d8c
commit 5ae882765b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 188 additions and 128 deletions

View File

@ -4,4 +4,6 @@ public enum CreatorContextType {
PAGE,
MODULE,
WORKFLOW,
APPLICATION,
PACKAGE,
}

View File

@ -1,6 +1,6 @@
package com.appsmith.server.domains;
import com.appsmith.external.models.BranchAwareDomain;
import com.appsmith.server.domains.ce.CustomJSLibCE;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
@ -9,9 +9,6 @@ import lombok.Setter;
import lombok.ToString;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@Getter
@ -19,34 +16,7 @@ import java.util.Set;
@ToString
@NoArgsConstructor
@Document
public class CustomJSLib extends BranchAwareDomain {
/* Library name */
String name;
/**
* This string is used to uniquely identify a given library. We expect this to be universally unique for a given
* JS library
*/
String uidString;
/**
* These are the namespaces under which the library functions reside. User would access lib methods like
* `accessor.method`
*/
Set<String> accessor;
/* Library UMD src url */
String url;
/* Library documentation page URL */
String docsUrl;
/* Library version */
String version;
/* `Tern` tool definitions - it defines the methods exposed by the library. It helps us with auto-complete
feature i.e. the function name showing up as suggestion when user has partially typed it. */
String defs;
public class CustomJSLib extends CustomJSLibCE {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public CustomJSLib(
@ -56,50 +26,6 @@ public class CustomJSLib extends BranchAwareDomain {
@JsonProperty("docsUrl") String docsUrl,
@JsonProperty("version") String version,
@JsonProperty("defs") String defs) {
this.name = name;
this.accessor = accessor;
this.url = url;
this.docsUrl = docsUrl;
this.defs = defs;
this.version = version;
setUidString();
}
public void setUidString() {
List<String> accessorList = new ArrayList(this.accessor);
Collections.sort(accessorList);
this.uidString = String.join("_", accessorList) + "_" + this.url;
}
/**
* The equality operator has been overridden here so that when two custom JS library objects are compared, they
* are compared based on their name and version as opposed to Java object reference. At the moment this check
* helps us to identify which JS library needs to be removed from the list of installed libraries when a user
* chooses to uninstall a library. It also helps us to identify if a library has already been added.
* Please note that this comment may have to be updated once the following issue is closed:
* https://github.com/appsmithorg/appsmith/issues/18226
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof CustomJSLib)) {
return false;
}
/**
* We check the equality using the uidString since this is supposed to be unique for a given library.
*/
return ((CustomJSLib) o).getUidString().equals(this.uidString);
}
@Override
public int hashCode() {
return this.uidString.hashCode();
}
@Override
public void sanitiseToExportDBObject() {
this.setId(null);
this.setCreatedAt(null);
this.setUpdatedAt(null);
super(name, accessor, url, docsUrl, version, defs);
}
}

View File

@ -0,0 +1,103 @@
package com.appsmith.server.domains.ce;
import com.appsmith.external.models.BranchAwareDomain;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class CustomJSLibCE extends BranchAwareDomain {
/* Library name */
String name;
/**
* This string is used to uniquely identify a given library. We expect this to be universally unique for a given
* JS library
*/
String uidString;
/**
* These are the namespaces under which the library functions reside. User would access lib methods like
* `accessor.method`
*/
Set<String> accessor;
/* Library UMD src url */
String url;
/* Library documentation page URL */
String docsUrl;
/* Library version */
String version;
/* `Tern` tool definitions - it defines the methods exposed by the library. It helps us with auto-complete
feature i.e. the function name showing up as suggestion when user has partially typed it. */
String defs;
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public CustomJSLibCE(
@JsonProperty("name") String name,
@JsonProperty("accessor") Set<String> accessor,
@JsonProperty("url") String url,
@JsonProperty("docsUrl") String docsUrl,
@JsonProperty("version") String version,
@JsonProperty("defs") String defs) {
this.name = name;
this.accessor = accessor;
this.url = url;
this.docsUrl = docsUrl;
this.defs = defs;
this.version = version;
setUidString();
}
public void setUidString() {
List<String> accessorList = new ArrayList(this.accessor);
Collections.sort(accessorList);
this.uidString = String.join("_", accessorList) + "_" + this.url;
}
/**
* The equality operator has been overridden here so that when two custom JS library objects are compared, they
* are compared based on their name and version as opposed to Java object reference. At the moment this check
* helps us to identify which JS library needs to be removed from the list of installed libraries when a user
* chooses to uninstall a library. It also helps us to identify if a library has already been added.
* Please note that this comment may have to be updated once the following issue is closed:
* https://github.com/appsmithorg/appsmith/issues/18226
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof CustomJSLibCE)) {
return false;
}
/**
* We check the equality using the uidString since this is supposed to be unique for a given library.
*/
return ((CustomJSLibCE) o).getUidString().equals(this.uidString);
}
@Override
public int hashCode() {
return this.uidString.hashCode();
}
@Override
public void sanitiseToExportDBObject() {
this.setId(null);
this.setCreatedAt(null);
this.setUpdatedAt(null);
}
}

View File

@ -1,39 +1,9 @@
package com.appsmith.server.dtos;
import com.appsmith.server.domains.CustomJSLib;
import com.appsmith.server.dtos.ce.CustomJSLibApplicationCE_DTO;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CustomJSLibApplicationDTO {
/**
* This string is used to uniquely identify a given library. We expect this to be universally unique for a given
* JS library
*/
String uidString;
@Override
public boolean equals(Object o) {
if (!(o instanceof CustomJSLibApplicationDTO)) {
return false;
}
/**
* We check the equality using the uidString since this is supposed to be unique for a given library.
*/
return ((CustomJSLibApplicationDTO) o).getUidString().equals(this.uidString);
}
@Override
public int hashCode() {
return this.uidString.hashCode();
}
public static CustomJSLibApplicationDTO getDTOFromCustomJSLib(CustomJSLib jsLib) {
CustomJSLibApplicationDTO customJSLibApplicationDTO = new CustomJSLibApplicationDTO();
customJSLibApplicationDTO.setUidString(jsLib.getUidString());
return customJSLibApplicationDTO;
}
}
public class CustomJSLibApplicationDTO extends CustomJSLibApplicationCE_DTO {}

View File

@ -0,0 +1,40 @@
package com.appsmith.server.dtos.ce;
import com.appsmith.server.domains.CustomJSLib;
import com.appsmith.server.dtos.CustomJSLibApplicationDTO;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CustomJSLibApplicationCE_DTO {
/**
* This string is used to uniquely identify a given library. We expect this to be universally unique for a given
* JS library
*/
String uidString;
@Override
public boolean equals(Object o) {
if (!(o instanceof CustomJSLibApplicationCE_DTO)) {
return false;
}
/**
* We check the equality using the uidString since this is supposed to be unique for a given library.
*/
return ((CustomJSLibApplicationCE_DTO) o).getUidString().equals(this.uidString);
}
@Override
public int hashCode() {
return this.uidString.hashCode();
}
public static CustomJSLibApplicationDTO getDTOFromCustomJSLib(CustomJSLib jsLib) {
CustomJSLibApplicationDTO customJSLibApplicationDTO = new CustomJSLibApplicationDTO();
customJSLibApplicationDTO.setUidString(jsLib.getUidString());
return customJSLibApplicationDTO;
}
}

View File

@ -1,5 +1,6 @@
package com.appsmith.server.jslibs.base;
import com.appsmith.external.models.CreatorContextType;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.CustomJSLib;
import com.appsmith.server.dtos.CustomJSLibApplicationDTO;
@ -12,7 +13,6 @@ import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
@ -74,7 +74,7 @@ public class CustomJSLibServiceCEImpl extends BaseService<CustomJSLibRepository,
public Mono<CustomJSLibApplicationDTO> persistCustomJSLibMetaDataIfDoesNotExistAndGetDTO(
CustomJSLib jsLib, Boolean isForceInstall) {
return repository
.findByUidString(jsLib.getUidString())
.findUniqueCustomJsLib(jsLib)
.flatMap(foundJSLib -> {
/*
The first check is to make sure that we are able to detect any previously truncated data and overwrite it the next time we receive valid data.
@ -136,10 +136,9 @@ public class CustomJSLibServiceCEImpl extends BaseService<CustomJSLibRepository,
private Mono<List<CustomJSLib>> getAllCustomJSLibsFromApplication(
String applicationId, String branchName, boolean isViewMode) {
return getAllJSLibApplicationDTOFromApplication(applicationId, branchName, isViewMode)
.map(jsLibDTOSet ->
jsLibDTOSet.stream().map(dto -> dto.getUidString()).collect(Collectors.toList()))
.flatMapMany(Flux::fromIterable)
.flatMap(uidString -> repository.findByUidString(uidString))
.map(this::filterAndMapGlobalUidStrings)
.flatMapMany(uidStrings ->
repository.findCustomJsLibsInContext(uidStrings, applicationId, CreatorContextType.APPLICATION))
.collectList()
.map(jsLibList -> {
Collections.sort(jsLibList, Comparator.comparing(CustomJSLib::getUidString));
@ -147,6 +146,12 @@ public class CustomJSLibServiceCEImpl extends BaseService<CustomJSLibRepository,
});
}
protected Set<String> filterAndMapGlobalUidStrings(Set<CustomJSLibApplicationDTO> customJSLibApplicationDTOS) {
return customJSLibApplicationDTOS.stream()
.map(CustomJSLibApplicationDTO::getUidString)
.collect(Collectors.toSet());
}
@Override
public Mono<Set<CustomJSLibApplicationDTO>> getAllJSLibApplicationDTOFromApplication(
@NotNull String applicationId, String branchName, Boolean isViewMode) {

View File

@ -1,9 +1,16 @@
package com.appsmith.server.repositories.ce;
import com.appsmith.external.models.CreatorContextType;
import com.appsmith.server.domains.CustomJSLib;
import com.appsmith.server.repositories.AppsmithRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Set;
public interface CustomJSLibRepositoryCE extends AppsmithRepository<CustomJSLib> {
Mono<CustomJSLib> findByUidString(String uidString);
Mono<CustomJSLib> findUniqueCustomJsLib(CustomJSLib customJSLib);
Flux<CustomJSLib> findCustomJsLibsInContext(
Set<String> uidStrings, String referenceId, CreatorContextType contextType);
}

View File

@ -1,21 +1,24 @@
package com.appsmith.server.repositories.ce;
import com.appsmith.external.models.CreatorContextType;
import com.appsmith.server.domains.CustomJSLib;
import com.appsmith.server.domains.QCustomJSLib;
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
import com.appsmith.server.repositories.CacheableRepositoryHelper;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Criteria;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static org.springframework.data.mongodb.core.query.Criteria.where;
public class CustomJSLibRepositoryCEImpl extends BaseAppsmithRepositoryImpl<CustomJSLib>
implements CustomJSLibRepositoryCE {
private static final String UID_STRING_IDENTIFIER = "uidString";
public CustomJSLibRepositoryCEImpl(
ReactiveMongoOperations mongoOperations,
@ -24,16 +27,20 @@ public class CustomJSLibRepositoryCEImpl extends BaseAppsmithRepositoryImpl<Cust
super(mongoOperations, mongoConverter, cacheableRepositoryHelper);
}
/*
Each custom JS library is supposed to be unique across the branches and applications. This is considered a shared resource and hence
we don't store separate versions of JS library for each branch or user. And this is the reason why branch name is not used.
Custom JS library doesn't have any user or application specific data and carries no risk and hence no ACL check is made while fetching the data.
*/
@Override
public Mono<CustomJSLib> findByUidString(String uidString) {
Criteria uidStringMatchCriteria = where(UID_STRING_IDENTIFIER).is(uidString);
ArrayList<Criteria> listOfCriteria = new ArrayList<>();
listOfCriteria.add(uidStringMatchCriteria);
return queryOne(listOfCriteria, List.of());
public Mono<CustomJSLib> findUniqueCustomJsLib(CustomJSLib customJSLib) {
Criteria criteria = where(fieldName(QCustomJSLib.customJSLib.uidString)).is(customJSLib.getUidString());
return this.queryOne(List.of(criteria));
}
@Override
public Flux<CustomJSLib> findCustomJsLibsInContext(
Set<String> uidStrings, String contextId, CreatorContextType contextType) {
Criteria criteria =
Criteria.where(fieldName(QCustomJSLib.customJSLib.uidString)).in(uidStrings);
return this.queryAll(List.of(criteria), Optional.empty());
}
}