Skip to content

Commit ea136fc

Browse files
authored
Merge pull request #41258 from appsmithorg/release
24/09 Daily Promotion
2 parents e3cda4a + d5ee690 commit ea136fc

File tree

23 files changed

+227
-108
lines changed

23 files changed

+227
-108
lines changed

app/server/appsmith-plugins/amazons3Plugin/src/main/java/com/external/plugins/AmazonS3Plugin.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import java.io.IOException;
5959
import java.io.InputStream;
6060
import java.net.URL;
61+
import java.security.MessageDigest;
62+
import java.security.NoSuchAlgorithmException;
6163
import java.text.DateFormat;
6264
import java.text.SimpleDateFormat;
6365
import java.util.ArrayList;
@@ -1167,10 +1169,29 @@ void uploadFileInS3(
11671169
TransferManager transferManager =
11681170
TransferManagerBuilder.standard().withS3Client(connection).build();
11691171
final ObjectMetadata objectMetadata = new ObjectMetadata();
1172+
1173+
// Set content length
1174+
objectMetadata.setContentLength(payload.length);
1175+
11701176
// Only add content type if the user has mentioned it in the body
11711177
if (multipartFormDataDTO.getType() != null) {
11721178
objectMetadata.setContentType(multipartFormDataDTO.getType());
11731179
}
1180+
1181+
// Calculate and set Content-MD5 header for Object Lock compliance
1182+
try {
1183+
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
1184+
byte[] md5Hash = md5Digest.digest(payload);
1185+
String md5Base64 = Base64.getEncoder().encodeToString(md5Hash);
1186+
objectMetadata.setContentMD5(md5Base64);
1187+
log.debug("Set Content-MD5 header for S3 upload: {}", md5Base64);
1188+
} catch (NoSuchAlgorithmException e) {
1189+
log.warn(
1190+
"Failed to calculate MD5 checksum for S3 upload. Object Lock enabled buckets may reject this upload.",
1191+
e);
1192+
// Continue with upload without MD5 header - let AWS handle the error if Object Lock is enabled
1193+
}
1194+
11741195
transferManager
11751196
.upload(bucketName, path, inputStream, objectMetadata)
11761197
.waitForUploadResult();

app/server/appsmith-plugins/amazons3Plugin/src/test/java/com/external/plugins/AmazonS3PluginTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import java.lang.reflect.Method;
4646
import java.net.MalformedURLException;
4747
import java.net.URL;
48+
import java.security.MessageDigest;
49+
import java.security.NoSuchAlgorithmException;
4850
import java.util.ArrayList;
4951
import java.util.Arrays;
5052
import java.util.HashMap;
@@ -1584,4 +1586,47 @@ public void verify_sanitizeGenerateCRUDPageTemplateInfo_addsInfoToReplaceTemplat
15841586
.block();
15851587
assertEquals(userSelectedBucketName, mappedColumnsAndTableName.get("templateBucket"));
15861588
}
1589+
1590+
@Test
1591+
public void testContentMD5CalculationForObjectLock() throws NoSuchAlgorithmException {
1592+
// Test that MD5 calculation works correctly for Object Lock compliance
1593+
String testContent = "Hello, World!";
1594+
byte[] payload = testContent.getBytes();
1595+
1596+
// Calculate MD5 hash using the same logic as in uploadFileInS3
1597+
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
1598+
byte[] md5Hash = md5Digest.digest(payload);
1599+
String md5Base64 = java.util.Base64.getEncoder().encodeToString(md5Hash);
1600+
1601+
// Verify MD5 calculation is correct
1602+
assertNotNull(md5Base64);
1603+
assertTrue(md5Base64.length() > 0);
1604+
1605+
// For "Hello, World!" the MD5 hash should be deterministic
1606+
// Expected MD5 for "Hello, World!" is 65a8e27d8879283831b664bd8b7f0ad4
1607+
String expectedMd5Hex = "65a8e27d8879283831b664bd8b7f0ad4";
1608+
String actualMd5Hex = bytesToHex(md5Hash);
1609+
assertEquals(expectedMd5Hex, actualMd5Hex);
1610+
1611+
// Verify base64 encoding
1612+
String expectedBase64 = java.util.Base64.getEncoder().encodeToString(hexToBytes(expectedMd5Hex));
1613+
assertEquals(expectedBase64, md5Base64);
1614+
}
1615+
1616+
private String bytesToHex(byte[] bytes) {
1617+
StringBuilder result = new StringBuilder();
1618+
for (byte b : bytes) {
1619+
result.append(String.format("%02x", b));
1620+
}
1621+
return result.toString();
1622+
}
1623+
1624+
private byte[] hexToBytes(String hex) {
1625+
int len = hex.length();
1626+
byte[] data = new byte[len / 2];
1627+
for (int i = 0; i < len; i += 2) {
1628+
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
1629+
}
1630+
return data;
1631+
}
15871632
}

app/server/appsmith-server/src/main/java/com/appsmith/server/annotations/GitRoute.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@
1818
ArtifactType artifactType();
1919

2020
GitRouteOperation operation();
21+
22+
String authorName() default "";
23+
24+
String authorEmail() default "";
2125
}

app/server/appsmith-server/src/main/java/com/appsmith/server/aspect/GitRouteAspect.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ private static class Context {
142142

143143
// Intermediate Inputs
144144
private String fieldValue;
145+
private String authorName;
146+
private String authorEmail;
145147

146148
// Tasks
147149
private Artifact artifact;
@@ -229,6 +231,14 @@ public Object handleGitRoute(ProceedingJoinPoint joinPoint, GitRoute gitRoute) {
229231
return execute(ctx);
230232
}
231233

234+
String authorName = extractFieldValue(joinPoint, gitRoute.authorName());
235+
String authorEmail = extractFieldValue(joinPoint, gitRoute.authorEmail());
236+
237+
if (StringUtils.hasText(authorName) && StringUtils.hasText(authorEmail)) {
238+
ctx.setAuthorEmail(authorEmail);
239+
ctx.setAuthorName(authorName);
240+
}
241+
232242
String fieldValue = extractFieldValue(joinPoint, gitRoute.fieldName());
233243
ctx.setFieldValue(fieldValue);
234244
return run(ctx, State.ROUTE_FILTER).flatMap(unused -> {
@@ -267,14 +277,15 @@ private Mono<Object> run(Context ctx, State current) {
267277
Outcome.SUCCESS.name(),
268278
result,
269279
duration);
280+
} else {
281+
log.info(
282+
"Operation : {}, State {} : {}, Time: {}ms",
283+
ctx.getGitRoute().operation(),
284+
current,
285+
Outcome.SUCCESS.name(),
286+
duration);
270287
}
271288

272-
log.info(
273-
"Operation : {}, State {} : {}, Time: {}ms",
274-
ctx.getGitRoute().operation(),
275-
current,
276-
Outcome.SUCCESS.name(),
277-
duration);
278289
return run(ctx, config.next(Outcome.SUCCESS));
279290
})
280291
.onErrorResume(e -> {
@@ -460,10 +471,23 @@ private Mono<?> lockKey(Context ctx) {
460471
* @return Mono emitting the Git profile, or error if not configured
461472
*/
462473
private Mono<GitProfile> gitProfile(Context ctx) {
463-
return gitProfileUtils
464-
.getGitProfileForUser(ctx.getFieldValue())
474+
Mono<GitProfile> alternativeGitProfileMono = Mono.defer(() -> Mono.justOrEmpty(getProfileFromArgs(ctx)))
465475
.switchIfEmpty(Mono.error(new AppsmithException(
466476
AppsmithError.INVALID_GIT_CONFIGURATION, "Git profile is not configured")));
477+
478+
return gitProfileUtils.getGitProfileForUser(ctx.getFieldValue()).switchIfEmpty(alternativeGitProfileMono);
479+
}
480+
481+
private GitProfile getProfileFromArgs(Context ctx) {
482+
if (!StringUtils.hasText(ctx.getAuthorEmail()) || !StringUtils.hasText(ctx.getAuthorName())) {
483+
return null;
484+
}
485+
486+
GitProfile gitProfile = new GitProfile();
487+
gitProfile.setAuthorName(ctx.getAuthorName());
488+
gitProfile.setAuthorEmail(ctx.getAuthorEmail());
489+
gitProfile.setUseGlobalProfile(Boolean.TRUE);
490+
return gitProfile;
467491
}
468492

469493
/**

app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/AutoCommitEventHandler.java

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.appsmith.server.git.autocommit;
2+
3+
public interface AutoCommitSolution extends AutoCommitSolutionCE {}

app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/AutoCommitEventHandlerCE.java renamed to app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/AutoCommitSolutionCE.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import com.appsmith.server.events.AutoCommitEvent;
44
import reactor.core.publisher.Mono;
55

6-
public interface AutoCommitEventHandlerCE {
7-
void publish(AutoCommitEvent autoCommitEvent);
6+
public interface AutoCommitSolutionCE {
87

9-
void handle(AutoCommitEvent event);
8+
Mono<Boolean> startApplicationAutoCommit(
9+
String baseArtifactId, String authorName, String authorEmail, AutoCommitEvent event);
1010

1111
Mono<Boolean> autoCommitDSLMigration(AutoCommitEvent autoCommitEvent);
1212

app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/AutoCommitEventHandlerCEImpl.java renamed to app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/AutoCommitSolutionCEImpl.java

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.appsmith.external.git.constants.ce.RefType;
77
import com.appsmith.external.git.handler.FSGitHandler;
88
import com.appsmith.external.git.models.GitResourceType;
9+
import com.appsmith.server.annotations.GitRoute;
910
import com.appsmith.server.configurations.ProjectProperties;
1011
import com.appsmith.server.constants.ArtifactType;
1112
import com.appsmith.server.constants.FieldName;
@@ -18,6 +19,7 @@
1819
import com.appsmith.server.exceptions.AppsmithError;
1920
import com.appsmith.server.exceptions.AppsmithException;
2021
import com.appsmith.server.git.GitRedisUtils;
22+
import com.appsmith.server.git.constants.GitRouteOperation;
2123
import com.appsmith.server.git.dtos.ArtifactJsonTransformationDTO;
2224
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
2325
import com.appsmith.server.helpers.CollectionUtils;
@@ -29,9 +31,6 @@
2931
import com.appsmith.server.services.GitArtifactHelper;
3032
import lombok.RequiredArgsConstructor;
3133
import lombok.extern.slf4j.Slf4j;
32-
import org.springframework.context.ApplicationEventPublisher;
33-
import org.springframework.context.event.EventListener;
34-
import org.springframework.scheduling.annotation.Async;
3534
import reactor.core.publisher.Flux;
3635
import reactor.core.publisher.Mono;
3736
import reactor.core.scheduler.Schedulers;
@@ -51,8 +50,7 @@
5150

5251
@RequiredArgsConstructor
5352
@Slf4j
54-
public class AutoCommitEventHandlerCEImpl implements AutoCommitEventHandlerCE {
55-
private final ApplicationEventPublisher applicationEventPublisher;
53+
public class AutoCommitSolutionCEImpl implements AutoCommitSolutionCE {
5654
private final GitRedisUtils gitRedisUtils;
5755
private final RedisUtils redisUtils;
5856
private final DSLMigrationUtils dslMigrationUtils;
@@ -66,30 +64,23 @@ public class AutoCommitEventHandlerCEImpl implements AutoCommitEventHandlerCE {
6664
"System generated commit, to support new features in Appsmith %s";
6765

6866
@Override
69-
public void publish(AutoCommitEvent autoCommitEvent) {
70-
applicationEventPublisher.publishEvent(autoCommitEvent);
71-
log.info("published event for auto commit: {}", autoCommitEvent);
72-
}
73-
74-
@Async
75-
@EventListener
76-
@Override
77-
public void handle(AutoCommitEvent event) {
78-
log.info("received event for auto commit: {}", event);
67+
@GitRoute(
68+
artifactType = ArtifactType.APPLICATION,
69+
operation = GitRouteOperation.AUTO_COMMIT_SOLUTION,
70+
fieldName = "baseArtifactId",
71+
authorEmail = "authorEmail",
72+
authorName = "authorName")
73+
public Mono<Boolean> startApplicationAutoCommit(
74+
String baseArtifactId, String authorName, String authorEmail, AutoCommitEvent event) {
75+
log.info("Starting auto-commit process for event: {}", event);
7976
Mono<Boolean> autocommitMigration;
8077
if (Boolean.TRUE.equals(event.getIsServerSideEvent())) {
8178
autocommitMigration = this.autoCommitServerMigration(event);
8279
} else {
8380
autocommitMigration = this.autoCommitDSLMigration(event);
8481
}
8582

86-
autocommitMigration
87-
.subscribeOn(Schedulers.boundedElastic())
88-
.subscribe(
89-
result -> log.info(
90-
"Auto-commit completed successfully for application: {}", event.getApplicationId()),
91-
error -> log.error(
92-
"Error during auto-commit for application: {}", event.getApplicationId(), error));
83+
return autocommitMigration.subscribeOn(Schedulers.boundedElastic());
9384
}
9485

9586
private <T> Mono<T> setProgress(T result, String applicationId, int progress) {

app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/AutoCommitEventHandlerImpl.java renamed to app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/AutoCommitSolutionImpl.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88
import com.appsmith.server.helpers.DSLMigrationUtils;
99
import com.appsmith.server.helpers.RedisUtils;
1010
import com.appsmith.server.services.AnalyticsService;
11-
import org.springframework.context.ApplicationEventPublisher;
1211
import org.springframework.stereotype.Component;
1312

1413
@Component
15-
public class AutoCommitEventHandlerImpl extends AutoCommitEventHandlerCEImpl implements AutoCommitEventHandler {
14+
public class AutoCommitSolutionImpl extends AutoCommitSolutionCEImpl implements AutoCommitSolution {
1615

17-
public AutoCommitEventHandlerImpl(
18-
ApplicationEventPublisher applicationEventPublisher,
16+
public AutoCommitSolutionImpl(
1917
GitRedisUtils gitRedisUtils,
2018
RedisUtils redisUtils,
2119
DSLMigrationUtils dslMigrationUtils,
@@ -25,7 +23,6 @@ public AutoCommitEventHandlerImpl(
2523
ProjectProperties projectProperties,
2624
AnalyticsService analyticsService) {
2725
super(
28-
applicationEventPublisher,
2926
gitRedisUtils,
3027
redisUtils,
3128
dslMigrationUtils,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.appsmith.server.git.autocommit.helpers;
2+
3+
import com.appsmith.server.events.AutoCommitEvent;
4+
5+
public interface AutoCommitAsyncEventManager {
6+
7+
void publishAsyncEvent(AutoCommitEvent autoCommitEvent);
8+
9+
void autoCommitPublishEventListener(AutoCommitEvent event);
10+
}

0 commit comments

Comments
 (0)