From 84f413d18a479405ab04d883e6a53abc07b1170b Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Mon, 18 Sep 2023 23:34:21 +0800 Subject: [PATCH] fix: bundle resources cache version may duplicate (#4591) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core /milestone 2.10.x #### What this PR does / why we need it: 修复 bundle resource 的缓存 key 生成可能会重复的问题 #### Which issue(s) this PR fixes: Fixes #4586 #### Does this PR introduce a user-facing change? ```release-note 修复 bundle resource 的缓存 key 生成可能会重复的问题 ``` --- .../service/impl/PluginServiceImpl.java | 8 ++- .../service/impl/PluginServiceImplTest.java | 59 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java b/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java index f404520ef..e5c621c10 100644 --- a/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java +++ b/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.Validate; import org.pf4j.PluginWrapper; +import org.pf4j.RuntimeMode; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Component; @@ -186,10 +187,15 @@ public class PluginServiceImpl implements PluginService { @Override public Mono generateJsBundleVersion() { return Mono.fromSupplier(() -> { + if (RuntimeMode.DEVELOPMENT.equals(pluginManager.getRuntimeMode())) { + return String.valueOf(System.currentTimeMillis()); + } var compactVersion = pluginManager.getStartedPlugins() .stream() .sorted(Comparator.comparing(PluginWrapper::getPluginId)) - .map(pluginWrapper -> pluginWrapper.getDescriptor().getVersion()) + .map(pluginWrapper -> pluginWrapper.getPluginId() + ":" + + pluginWrapper.getDescriptor().getVersion() + ) .collect(Collectors.joining()); return Hashing.sha256().hashUnencodedChars(compactVersion).toString(); }); diff --git a/application/src/test/java/run/halo/app/core/extension/service/impl/PluginServiceImplTest.java b/application/src/test/java/run/halo/app/core/extension/service/impl/PluginServiceImplTest.java index fb5690e39..ea444c636 100644 --- a/application/src/test/java/run/halo/app/core/extension/service/impl/PluginServiceImplTest.java +++ b/application/src/test/java/run/halo/app/core/extension/service/impl/PluginServiceImplTest.java @@ -14,11 +14,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.github.zafarkhaja.semver.Version; +import com.google.common.hash.Hashing; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -27,8 +29,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.pf4j.PluginDescriptor; import org.pf4j.PluginState; import org.pf4j.PluginWrapper; +import org.pf4j.RuntimeMode; import org.springframework.web.server.ServerWebInputException; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -226,4 +230,59 @@ class PluginServiceImplTest { ); verify(pluginWrapper, times(1)).getPluginPath(); } + + @Test + void generateJsBundleVersionTest() { + when(pluginManager.getRuntimeMode()).thenReturn(RuntimeMode.DEVELOPMENT); + + pluginService.generateJsBundleVersion() + .as(StepVerifier::create) + .consumeNextWith(version -> assertThat(version).isNotNull()) + .verifyComplete(); + + when(pluginManager.getRuntimeMode()).thenReturn(RuntimeMode.DEPLOYMENT); + var plugin1 = mock(PluginWrapper.class); + var plugin2 = mock(PluginWrapper.class); + var plugin3 = mock(PluginWrapper.class); + when(pluginManager.getStartedPlugins()).thenReturn(List.of(plugin1, plugin2, plugin3)); + + var descriptor1 = mock(PluginDescriptor.class); + var descriptor2 = mock(PluginDescriptor.class); + var descriptor3 = mock(PluginDescriptor.class); + when(plugin1.getDescriptor()).thenReturn(descriptor1); + when(plugin2.getDescriptor()).thenReturn(descriptor2); + when(plugin3.getDescriptor()).thenReturn(descriptor3); + + when(plugin1.getPluginId()).thenReturn("fake-1"); + when(plugin2.getPluginId()).thenReturn("fake-2"); + when(plugin3.getPluginId()).thenReturn("fake-3"); + + when(descriptor1.getVersion()).thenReturn("1.0.0"); + when(descriptor2.getVersion()).thenReturn("2.0.0"); + when(descriptor3.getVersion()).thenReturn("3.0.0"); + + var str = "fake-1:1.0.0fake-2:2.0.0fake-3:3.0.0"; + var result = Hashing.sha256().hashUnencodedChars(str).toString(); + assertThat(result.length()).isEqualTo(64); + + pluginService.generateJsBundleVersion() + .as(StepVerifier::create) + .consumeNextWith(version -> assertThat(version).isEqualTo(result)) + .verifyComplete(); + + var plugin4 = mock(PluginWrapper.class); + var descriptor4 = mock(PluginDescriptor.class); + when(plugin4.getDescriptor()).thenReturn(descriptor4); + when(plugin4.getPluginId()).thenReturn("fake-4"); + when(descriptor4.getVersion()).thenReturn("3.0.0"); + var str2 = "fake-1:1.0.0fake-2:2.0.0fake-4:3.0.0"; + var result2 = Hashing.sha256().hashUnencodedChars(str2).toString(); + when(pluginManager.getStartedPlugins()).thenReturn(List.of(plugin1, plugin2, plugin4)); + pluginService.generateJsBundleVersion() + .as(StepVerifier::create) + .consumeNextWith(version -> assertThat(version).isEqualTo(result2)) + .verifyComplete(); + + assertThat(result).isNotEqualTo(result2); + } }