Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0296eef
cache templates data
AnaghHegde May 14, 2024
c955b77
fix method parameters
AnaghHegde May 14, 2024
c240600
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
AnaghHegde May 21, 2024
53d1291
chore: add evict cache flow
AnaghHegde May 22, 2024
ad56029
evit cache flow
AnaghHegde May 22, 2024
c0ae652
review comments
AnaghHegde May 23, 2024
0b6ce52
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
AnaghHegde May 23, 2024
c4ecc4b
fix reference issue
AnaghHegde May 24, 2024
68326a5
change data type to string
AnaghHegde May 24, 2024
1da7601
add stop watch
AnaghHegde May 8, 2024
a911225
fix issue wioth component static method with Cache annotation
AnaghHegde May 28, 2024
4d86f9f
update cache name
AnaghHegde May 28, 2024
afaedeb
add review comments and fix comments
AnaghHegde May 28, 2024
6308d28
use in memory instead of release
AnaghHegde May 29, 2024
00afdca
review comments
AnaghHegde May 29, 2024
4cc1652
review comments
AnaghHegde May 29, 2024
5c7f3d9
review comments
AnaghHegde May 29, 2024
ba35c3c
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
AnaghHegde May 29, 2024
1c7f7e3
fix cache logic
AnaghHegde May 29, 2024
d930c9d
add tests
AnaghHegde May 30, 2024
3deb046
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
AnaghHegde May 31, 2024
2070521
reviw changes
AnaghHegde May 31, 2024
86d38bd
reviw changes
AnaghHegde May 31, 2024
799f449
update review comment
AnaghHegde May 31, 2024
01a3c42
review comment
AnaghHegde May 31, 2024
7b43c43
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
AnaghHegde Jun 3, 2024
5cbcbd3
deep copy of POJOs to fix source not modified
AnaghHegde Jun 4, 2024
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
@@ -0,0 +1,13 @@
package com.appsmith.server.dtos;

import lombok.Data;

import java.time.Instant;

@Data
public class CacheableApplicationJson {

ApplicationJson applicationJson;

Instant cacheExpiryTime;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.appsmith.server.dtos;

import lombok.Data;

import java.time.Instant;
import java.util.List;

@Data
public class CacheableApplicationTemplate {

List<ApplicationTemplate> applicationTemplateList;

Instant cacheExpiryTime;
}
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ public enum AppsmithError {
ErrorType.CONFIGURATION_ERROR,
null),
CLOUD_SERVICES_ERROR(
500,
400,
AppsmithErrorCode.CLOUD_SERVICES_ERROR.getCode(),
"Received error from cloud services {0}",
AppsmithErrorAction.DEFAULT,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package com.appsmith.server.helpers;

import com.appsmith.external.converters.ISOStringToInstantConverter;
import com.appsmith.server.dtos.ApplicationJson;
import com.appsmith.server.dtos.ApplicationTemplate;
import com.appsmith.server.dtos.CacheableApplicationJson;
import com.appsmith.server.dtos.CacheableApplicationTemplate;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.services.ce.ApplicationTemplateServiceCEImpl;
import com.appsmith.util.WebClientUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.reflect.Type;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class CacheableTemplateHelper {
// Template metadata is used for showing the preview of the template

CacheableApplicationTemplate applicationTemplateList = new CacheableApplicationTemplate();

Map<String, CacheableApplicationJson> cacheableApplicationJsonMap = new HashMap<>();
private static final int CACHE_LIFE_TIME_IN_SECONDS = 60 * 60 * 24; // 24 hours

public Mono<CacheableApplicationTemplate> getTemplates(String releaseVersion, String baseUrl) {

if (applicationTemplateList == null) {
applicationTemplateList = new CacheableApplicationTemplate();
}

if (applicationTemplateList.getCacheExpiryTime() != null
&& isCacheValid(applicationTemplateList.getCacheExpiryTime())) {
return Mono.just(applicationTemplateList);
}

UriComponentsBuilder uriComponentsBuilder =
UriComponentsBuilder.newInstance().queryParam("version", releaseVersion);

// uriComponents will build url in format: version=version&id=id1&id=id2&id=id3
UriComponents uriComponents = uriComponentsBuilder.build();

return WebClientUtils.create(baseUrl + "/api/v1/app-templates?" + uriComponents.getQuery())
.get()
.exchangeToFlux(clientResponse -> {
if (clientResponse.statusCode().equals(HttpStatus.OK)) {
return clientResponse.bodyToFlux(ApplicationTemplate.class);
} else if (clientResponse.statusCode().isError()) {
log.error("Error fetching templates from cloud services. Status code: {}", clientResponse);
return Flux.error(
new AppsmithException(AppsmithError.CLOUD_SERVICES_ERROR, clientResponse.statusCode()));
} else {
return clientResponse.createException().flatMapMany(Flux::error);
}
})
.collectList()
.map(applicationTemplates -> {
applicationTemplateList.setApplicationTemplateList(applicationTemplates);
applicationTemplateList.setCacheExpiryTime(Instant.now());
return applicationTemplateList;
});
}

// Actual JSON object of the template
public Mono<CacheableApplicationJson> getApplicationByTemplateId(String templateId, String baseUrl) {
final String templateUrl = baseUrl + "/api/v1/app-templates/" + templateId + "/application";
/*
* using a custom url builder factory because default builder always encodes
* URL.
* It's expected that the appDataUrl is already encoded, so we don't need to
* encode that again.
* Encoding an encoded URL will not work and end up resulting a 404 error
*/
final int size = 4 * 1024 * 1024; // 4 MB

if (cacheableApplicationJsonMap == null) {
cacheableApplicationJsonMap = new HashMap<>();
}

if (cacheableApplicationJsonMap.containsKey(templateId)
&& isCacheValid(cacheableApplicationJsonMap.get(templateId).getCacheExpiryTime())) {
return Mono.just(getCacheableApplicationJsonCopy(cacheableApplicationJsonMap.get(templateId)));
}

final ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(codecs -> codecs.defaultCodecs().maxInMemorySize(size))
.build();

WebClient webClient = WebClientUtils.builder()
.uriBuilderFactory(new ApplicationTemplateServiceCEImpl.NoEncodingUriBuilderFactory(templateUrl))
.exchangeStrategies(strategies)
.build();

return webClient
.get()
.retrieve()
.bodyToMono(String.class)
.map(jsonString -> {
Gson gson = getGson();
Type fileType = new TypeToken<ApplicationJson>() {}.getType();

CacheableApplicationJson cacheableApplicationJson = new CacheableApplicationJson();
cacheableApplicationJson.setApplicationJson(gson.fromJson(jsonString, fileType));
cacheableApplicationJson.setCacheExpiryTime(Instant.now());

// Remove/replace the value from cache
cacheableApplicationJsonMap.put(templateId, cacheableApplicationJson);
return getCacheableApplicationJsonCopy(cacheableApplicationJson);
})
.switchIfEmpty(
Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, "template", templateId)));
}

private CacheableApplicationJson getCacheableApplicationJsonCopy(CacheableApplicationJson src) {
Gson gson = getGson();
return gson.fromJson(gson.toJson(src), CacheableApplicationJson.class);
}

@NotNull private Gson getGson() {
return new GsonBuilder()
.registerTypeAdapter(Instant.class, new ISOStringToInstantConverter())
.create();
}

public boolean isCacheValid(Instant lastUpdatedAt) {
return Instant.now().minusSeconds(CACHE_LIFE_TIME_IN_SECONDS).isBefore(lastUpdatedAt);
}

public Map<String, CacheableApplicationJson> getCacheableApplicationJsonMap() {
return cacheableApplicationJsonMap;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.appsmith.server.imports.internal.partial;

import com.appsmith.external.constants.AnalyticsEvents;
import com.appsmith.external.helpers.Stopwatch;
import com.appsmith.external.models.CreatorContextType;
import com.appsmith.external.models.Datasource;
import com.appsmith.server.acl.AclPermission;
Expand Down Expand Up @@ -237,6 +238,7 @@ private Mono<BuildingBlockImportDTO> importResourceInPage(
if (applicationJson.getPageList() == null) {
return Mono.just(application);
}
Stopwatch processStopwatch1 = new Stopwatch("Refactoring the widget in DSL ");
// The building block is stored as a page in an application
final JsonNode dsl = widgetRefactorUtil.convertDslStringToJsonNode(applicationJson
.getPageList()
Expand Down Expand Up @@ -264,6 +266,7 @@ private Mono<BuildingBlockImportDTO> importResourceInPage(
})
.collectList()
.flatMap(refactoredDsl -> {
processStopwatch1.stopAndLogTimeInMillis();
applicationJson.setWidgets(dsl.toString());
return Mono.just(application);
});
Expand Down Expand Up @@ -414,14 +417,18 @@ public Mono<BuildingBlockResponseDTO> importBuildingBlock(BuildingBlockDTO build
Mono<ApplicationJson> applicationJsonMono =
applicationTemplateService.getApplicationJsonFromTemplate(buildingBlockDTO.getTemplateId());

Stopwatch processStopwatch = new Stopwatch("Download Content from Cloud service");
return applicationJsonMono.flatMap(applicationJson -> {
processStopwatch.stopAndLogTimeInMillis();
Stopwatch processStopwatch1 = new Stopwatch("Importing resource in db ");
return this.importResourceInPage(
buildingBlockDTO.getWorkspaceId(),
buildingBlockDTO.getApplicationId(),
buildingBlockDTO.getPageId(),
branchName,
applicationJson)
.flatMap(buildingBlockImportDTO -> {
processStopwatch1.stopAndLogTimeInMillis();
// Fetch layout and get new onPageLoadActions
// This data is not present in a client, since these are created
// after importing the block
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.appsmith.server.applications.base.ApplicationService;
import com.appsmith.server.configurations.CloudServicesConfig;
import com.appsmith.server.exports.internal.ExportService;
import com.appsmith.server.helpers.CacheableTemplateHelper;
import com.appsmith.server.helpers.ResponseUtils;
import com.appsmith.server.imports.internal.ImportService;
import com.appsmith.server.services.ce.ApplicationTemplateServiceCEImpl;
Expand All @@ -28,7 +29,8 @@ public ApplicationTemplateServiceImpl(
ResponseUtils responseUtils,
ApplicationPermission applicationPermission,
ObjectMapper objectMapper,
SessionUserService sessionUserService) {
SessionUserService sessionUserService,
CacheableTemplateHelper cacheableTemplateHelper) {
super(
cloudServicesConfig,
releaseNotesService,
Expand All @@ -40,6 +42,7 @@ public ApplicationTemplateServiceImpl(
responseUtils,
applicationPermission,
objectMapper,
sessionUserService);
sessionUserService,
cacheableTemplateHelper);
}
}
Loading