diff --git a/client/packages/lowcoder-sdk/package.json b/client/packages/lowcoder-sdk/package.json index f5df08715..360c79e3b 100644 --- a/client/packages/lowcoder-sdk/package.json +++ b/client/packages/lowcoder-sdk/package.json @@ -1,6 +1,6 @@ { "name": "lowcoder-sdk", - "version": "2.6.3", + "version": "2.6.4", "type": "module", "files": [ "src", @@ -25,6 +25,9 @@ }, "./dist/style.css": { "import": "./dist/style.css" + }, + "./dist/chunks/": { + "import": "./dist/chunks/" } }, "scripts": { diff --git a/client/packages/lowcoder-sdk/vite.config.mts b/client/packages/lowcoder-sdk/vite.config.mts index 903b336f6..3cd7b5c45 100644 --- a/client/packages/lowcoder-sdk/vite.config.mts +++ b/client/packages/lowcoder-sdk/vite.config.mts @@ -36,17 +36,7 @@ export const viteConfig: UserConfig = { }, base: ensureLastSlash(process.env.PUBLIC_URL), build: { - minify: "terser", - terserOptions: { - compress: { - drop_console: true, - drop_debugger: true, - pure_funcs: ["console.info", "console.debug", "console.log"], - }, - format: { - comments: false, - }, - }, + minify: "terser", chunkSizeWarningLimit: 500, lib: { formats: ["es"], @@ -56,7 +46,7 @@ export const viteConfig: UserConfig = { }, rollupOptions: { treeshake: { - moduleSideEffects: false, + moduleSideEffects: true, propertyReadSideEffects: false, tryCatchDeoptimization: false, unknownGlobalSideEffects: false, @@ -64,38 +54,23 @@ export const viteConfig: UserConfig = { external: ["react", "react-dom"], output: { chunkFileNames: "chunks/[name]-[hash].js", - entryFileNames: "entry/[name]-[hash].js", - assetFileNames: "assets/[name]-[hash].[ext]", + entryFileNames: "lowcoder-sdk.js", + assetFileNames: "style.css", manualChunks: (id) => { if (id.includes("node_modules")) { - // CORE FRAMEWORK CHUNKS - if (id.includes("react")) return "react"; - if (id.includes("react-dom")) return "react-dom"; - if (id.includes("react-router")) return "react-router"; - if (id.includes("react-redux")) return "react-redux"; - if (id.includes("redux")) return "redux"; - if (id.includes("redux-saga")) return "redux-saga"; - // UI LIBRARIES if (id.includes("@ant-design/icons")) return "ant-design-icons"; - if (id.includes("antd")) return "antd"; + if (id.includes("node_modules/antd")) return "antd"; if (id.includes("styled-components")) return "styled-components"; // 🔹 BARCODE & QR CODE PROCESSING - if (id.includes("zxing") || id.includes("Barcode") || id.includes("QRCode") || id.includes("PDF417")) return "barcode"; - - // CHARTING & DATA VISUALIZATION - if (id.includes("echarts")) return "echarts"; - if (id.includes("echarts-wordcloud")) return "echarts-wordcloud"; - if (id.includes("d3")) return "d3"; + if (id.includes("react-qr-barcode-scanner")) return "barcode"; // TEXT EDITORS & PARSERS if (id.includes("codemirror")) return "codemirror"; if (id.includes("quill")) return "quill"; if (id.includes("react-json-view")) return "react-json-view"; - if (id.includes("react-markdown")) return "react-markdown"; if (id.includes("react-quill")) return "react-quill"; - if (id.includes("remark") || id.includes("rehype") || id.includes("markdown")) return "markdown-parsers"; if (id.includes("remark-gfm")) return "remark-gfm"; if (id.includes("rehype-raw")) return "rehype-raw"; if (id.includes("rehype-sanitize")) return "rehype-sanitize"; @@ -133,7 +108,6 @@ export const viteConfig: UserConfig = { if (id.includes("xlsx")) return "xlsx"; if (id.includes("alasql")) return "alasql"; if (id.includes("sql-formatter")) return "sql-formatter"; - if (id.includes("tern")) return "tern"; // NETWORK & HTTP if (id.includes("axios")) return "axios"; @@ -158,41 +132,38 @@ export const viteConfig: UserConfig = { if (id.includes("cnchar")) return "cnchar"; if (id.includes("hotkeys-js")) return "hotkeys-js"; if (id.includes("loglevel")) return "loglevel"; - if (id.includes("qrcode-react")) return "qrcode-react"; + if (id.includes("qrcode.react")) return "qrcode-react"; if (id.includes("react-joyride")) return "react-joyride"; if (id.includes("rc-trigger")) return "rc-trigger"; if (id.includes("really-relaxed-json")) return "really-relaxed-json"; if (id.includes("simplebar-react")) return "simplebar-react"; - return "vendor"; + if (id.includes("react-documents")) return "react-documents"; + if (id.includes("react-colorful")) return "react-colorful"; + if (id.includes("react-best-gradient-color-picker")) return "react-best-gradient-color-picker"; + if (id.includes("@supabase/supabase-js")) return "supabase"; + return null; } - if (id.includes("src/api")) return "api"; - if (id.includes("src/appView")) return "appView"; - if (id.includes("src/base")) return "base"; - if (id.includes("src/constants")) return "constants"; - if (id.includes("src/i18n")) return "i18n"; - if (id.includes("src/ide")) return "ide"; - if (id.includes("src/layout")) return "layout"; - if (id.includes("src/pages")) return "pages"; - if (id.includes("src/redux")) return "app_redux"; - if (id.includes("src/comps")) return "comps"; - if (id.includes("comps/comps")) return "comps2"; - if (id.includes("comps/controls")) return "controls"; - if (id.includes("comps/queries")) return "queries"; - if (id.includes("comps/utils")) return "utils"; - if (id.includes("src/hooks")) return "hooks"; - if (id.includes("src/util")) return "util"; - return "common"; // 📦 Internal app shared code - }, + return null; + } }, experimental: { minChunkSize: 300000, // 📏 Force smaller chunks (~300KB) }, plugins: [ - terser(), + terser({ + compress: { + drop_console: true, + drop_debugger: true, + pure_funcs: ["console.info", "console.debug", "console.log"], + }, + format: { + comments: /(@vite-ignore|webpackIgnore)/ + }, + }) as PluginOption, strip({ functions: ["console.log", "debugger"], // ✅ Remove logs sourceMap: true, - }), + }) as PluginOption, ], onwarn: (warning, warn) => { if (warning.code === 'MODULE_LEVEL_DIRECTIVE') { diff --git a/server/api-service/lowcoder-dependencies/pom.xml b/server/api-service/lowcoder-dependencies/pom.xml index 3f8b6b703..0787bcfcf 100644 --- a/server/api-service/lowcoder-dependencies/pom.xml +++ b/server/api-service/lowcoder-dependencies/pom.xml @@ -26,7 +26,7 @@ org.lowcoder.plugin lowcoder-plugin-api - 2.3.0 + 2.3.1 diff --git a/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/event/AbstractEvent.java b/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/event/AbstractEvent.java index 8514624a2..7f96689f5 100644 --- a/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/event/AbstractEvent.java +++ b/server/api-service/lowcoder-infra/src/main/java/org/lowcoder/infra/event/AbstractEvent.java @@ -4,6 +4,8 @@ import lombok.Setter; import lombok.experimental.SuperBuilder; import org.lowcoder.plugin.api.event.LowcoderEvent; +import org.lowcoder.sdk.constants.GlobalContext; +import reactor.util.context.ContextView; import java.lang.reflect.Field; import java.util.HashMap; @@ -19,6 +21,7 @@ public abstract class AbstractEvent implements LowcoderEvent protected final Boolean isAnonymous; private final String ipAddress; protected Map details; + protected Map eventHeaders; @Setter private static String environmentID; @@ -42,7 +45,10 @@ public B detail(String name, String value) } } - public void populateDetails() { + public void populateDetails(ContextView contextView) { + //populate eventHeaders field + eventHeaders = contextView.get(GlobalContext.HEADERS); + if (details == null) { details = new HashMap<>(); } @@ -57,5 +63,8 @@ public void populateDetails() { } details.put("environmentId", environmentID); + if(!details.containsKey("headers")) { + details.put("headers", eventHeaders); + } } } diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/GlobalContext.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/GlobalContext.java index e52624cec..735ec57ff 100644 --- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/GlobalContext.java +++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/constants/GlobalContext.java @@ -16,4 +16,5 @@ public class GlobalContext { public static final String CURRENT_ORG_MEMBER = "currentOrgMember"; public static final String DOMAIN = "domain"; + public static final String HEADERS = "headers"; } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/ApiEventFilter.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/ApiEventFilter.java index e8b75f8c3..b593469b8 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/ApiEventFilter.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/ApiEventFilter.java @@ -17,6 +17,7 @@ import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; +import reactor.util.context.ContextView; import java.nio.charset.StandardCharsets; import java.util.Optional; @@ -40,7 +41,7 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { String token = contextView.get(VISITOR_TOKEN); ((Mono) contextView.get(CURRENT_ORG_MEMBER)) .flatMap(orgMember -> { - emitEvent(exchange.getRequest(), token, orgMember); + emitEvent(exchange.getRequest(), token, orgMember, contextView); return Mono.empty(); }) .subscribeOn(Schedulers.boundedElastic()) @@ -51,7 +52,7 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { ); } - private void emitEvent(ServerHttpRequest request, String token, OrgMember orgMember) { + private void emitEvent(ServerHttpRequest request, String token, OrgMember orgMember, ContextView contextView) { MultiValueMap headers = writableHttpHeaders(request.getHeaders()); headers.remove("Cookie"); Optional ipAddressOptional = headers.remove("X-Real-IP").stream().findFirst(); @@ -69,7 +70,7 @@ private void emitEvent(ServerHttpRequest request, String token, OrgMember orgMem .queryParams(request.getQueryParams()) .ipAddress(ipAddress) .build(); - event.populateDetails(); + event.populateDetails(contextView); log.debug("API call event emitted for '{}' from org '{}' on URI: {}", orgMember.getUserId(), orgMember.getUserId(), request.getURI().getPath()); applicationEventPublisher.publishEvent(event); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/GlobalContextFilter.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/GlobalContextFilter.java index f2ab85ca7..fe00a0065 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/GlobalContextFilter.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/GlobalContextFilter.java @@ -16,6 +16,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; @@ -32,6 +33,7 @@ import static org.lowcoder.sdk.constants.Authentication.isAnonymousUser; import static org.lowcoder.sdk.constants.GlobalContext.*; import static org.lowcoder.sdk.util.IDUtils.generate; +import static org.springframework.http.HttpHeaders.writableHttpHeaders; @Component @RequiredArgsConstructor @@ -107,6 +109,11 @@ private Map buildContextMap(ServerWebExchange serverWebExchange, contextMap.put(CURRENT_ORG_MEMBER, orgMemberService.getCurrentOrgMember(visitorId).cache()); contextMap.put(VISITOR_TOKEN, cookieHelper.getCookieToken(serverWebExchange)); contextMap.put(DOMAIN, UriUtils.getRefererDomainFromRequest(serverWebExchange)); + + //Analytics related fields + MultiValueMap headers = writableHttpHeaders(request.getHeaders()); + headers.remove("Cookie"); + contextMap.put(HEADERS, headers); return contextMap; } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/ApiCallEventPublisher.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/ApiCallEventPublisher.java index 590c8c7a8..5e6619000 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/ApiCallEventPublisher.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/ApiCallEventPublisher.java @@ -54,13 +54,13 @@ public Object handleAPICallEvent(ProceedingJoinPoint joinPoint) throws Throwable return sessionUserService.getVisitorToken() .zipWith(sessionUserService.getVisitorOrgMemberCacheSilent().defaultIfEmpty(OrgMember.NOT_EXIST)) .zipWith(ReactiveRequestContextHolder.getRequest()) - .doOnNext( + .delayUntil( tuple -> { String token = tuple.getT1().getT1(); OrgMember orgMember = tuple.getT1().getT2(); ServerHttpRequest request = tuple.getT2(); if (orgMember == OrgMember.NOT_EXIST) { - return; + return Mono.empty(); } MultiValueMap headers = writableHttpHeaders(request.getHeaders()); headers.remove("Cookie"); @@ -77,8 +77,11 @@ public Object handleAPICallEvent(ProceedingJoinPoint joinPoint) throws Throwable .queryParams(request.getQueryParams()) .ipAddress(ipAddress) .build(); - event.populateDetails(); - applicationEventPublisher.publishEvent(event); + return Mono.deferContextual(contextView -> { + event.populateDetails(contextView); + applicationEventPublisher.publishEvent(event); + return Mono.empty(); + }); }) .onErrorResume(throwable -> { log.error("handleAPICallEvent error {} for: {} ", joinPoint.getSignature().getName(), EventType.API_CALL_EVENT, throwable); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/BusinessEventPublisher.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/BusinessEventPublisher.java index fe3119d32..85932a56f 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/BusinessEventPublisher.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/util/BusinessEventPublisher.java @@ -74,7 +74,7 @@ public Mono publishFolderCommonEvent(String folderId, String folderName, E return sessionUserService.getVisitorToken() .zipWith(sessionUserService.getVisitorOrgMemberCache()) - .doOnNext( + .delayUntil( tuple -> { String token = tuple.getT1(); OrgMember orgMember = tuple.getT2(); @@ -87,8 +87,11 @@ public Mono publishFolderCommonEvent(String folderId, String folderName, E .isAnonymous(Authentication.isAnonymousUser(orgMember.getUserId())) .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); - applicationEventPublisher.publishEvent(event); + return Mono.deferContextual(contextView -> { + event.populateDetails(contextView); + applicationEventPublisher.publishEvent(event); + return Mono.empty(); + }); }) .then() .onErrorResume(throwable -> { @@ -151,7 +154,7 @@ public Mono publishApplicationCommonEvent(ApplicationView applicationView, return Pair.of(category, description); }); }), TupleUtils::merge) - .doOnNext(tuple -> { + .delayUntil(tuple -> { OrgMember orgMember = tuple.getT1().getT1(); Optional optional = tuple.getT1().getT2(); Optional optionalFrom = tuple.getT1().getT3(); @@ -175,8 +178,11 @@ public Mono publishApplicationCommonEvent(ApplicationView applicationView, .isAnonymous(anonymous) .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); - applicationEventPublisher.publishEvent(event); + return Mono.deferContextual(contextView -> { + event.populateDetails(contextView); + applicationEventPublisher.publishEvent(event); + return Mono.empty(); + }); }) .then() .onErrorResume(throwable -> { @@ -188,7 +194,7 @@ public Mono publishApplicationCommonEvent(ApplicationView applicationView, public Mono publishUserLoginEvent(String source) { return sessionUserService.getVisitorOrgMember().zipWith(sessionUserService.getVisitorToken()) - .doOnNext(tuple -> { + .delayUntil(tuple -> { OrgMember orgMember = tuple.getT1(); String token = tuple.getT2(); UserLoginEvent event = UserLoginEvent.builder() @@ -198,8 +204,11 @@ public Mono publishUserLoginEvent(String source) { .isAnonymous(Authentication.isAnonymousUser(orgMember.getUserId())) .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); - applicationEventPublisher.publishEvent(event); + return Mono.deferContextual(contextView -> { + event.populateDetails(contextView); + applicationEventPublisher.publishEvent(event); + return Mono.empty(); + }); }) .then() .onErrorResume(throwable -> { @@ -211,7 +220,7 @@ public Mono publishUserLoginEvent(String source) { public Mono publishUserLogoutEvent() { return sessionUserService.getVisitorOrgMemberCache() .zipWith(sessionUserService.getVisitorToken()) - .doOnNext(tuple -> { + .delayUntil(tuple -> { OrgMember orgMember = tuple.getT1(); String token = tuple.getT2(); UserLogoutEvent event = UserLogoutEvent.builder() @@ -220,8 +229,11 @@ public Mono publishUserLogoutEvent() { .isAnonymous(Authentication.isAnonymousUser(orgMember.getUserId())) .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); - applicationEventPublisher.publishEvent(event); + return Mono.deferContextual(contextView -> { + event.populateDetails(contextView); + applicationEventPublisher.publishEvent(event); + return Mono.empty(); + }); }) .then() .onErrorResume(throwable -> { @@ -244,7 +256,7 @@ public Mono publishGroupCreateEvent(Group group) { .isAnonymous(Authentication.isAnonymousUser(tuple.getT1().getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT2(), StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); + event.populateDetails(contextView); applicationEventPublisher.publishEvent(event); return Mono.empty(); })) @@ -272,7 +284,7 @@ public Mono publishGroupUpdateEvent(boolean publish, Group previousGroup, .isAnonymous(Authentication.isAnonymousUser(tuple.getT1().getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT2(), StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); + event.populateDetails(contextView); applicationEventPublisher.publishEvent(event); return Mono.empty(); })) @@ -300,7 +312,7 @@ public Mono publishGroupDeleteEvent(boolean publish, Group previousGroup) .isAnonymous(Authentication.isAnonymousUser(tuple.getT1().getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT2(), StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); + event.populateDetails(contextView); applicationEventPublisher.publishEvent(event); return Mono.empty(); })) @@ -337,7 +349,7 @@ public Mono publishGroupMemberAddEvent(boolean publish, String groupId, Ad .isAnonymous(Authentication.isAnonymousUser(orgMember.getUserId())) .sessionHash(Hashing.sha512().hashString(token, StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); + event.populateDetails(contextView); applicationEventPublisher.publishEvent(event); return Mono.empty(); })) @@ -374,7 +386,7 @@ public Mono publishGroupMemberRoleUpdateEvent(boolean publish, String grou .isAnonymous(Authentication.isAnonymousUser(orgMember.getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT4(), StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); + event.populateDetails(contextView); applicationEventPublisher.publishEvent(event); return Mono.empty(); })) @@ -410,7 +422,7 @@ public Mono publishGroupMemberLeaveEvent(boolean publish, GroupMember grou .isAnonymous(Authentication.isAnonymousUser(orgMember.getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT4(), StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); + event.populateDetails(contextView); applicationEventPublisher.publishEvent(event); return Mono.empty(); })) @@ -446,7 +458,7 @@ public Mono publishGroupMemberRemoveEvent(boolean publish, GroupMember pre .isAnonymous(Authentication.isAnonymousUser(orgMember.getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT4(), StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); + event.populateDetails(contextView); applicationEventPublisher.publishEvent(event); return Mono.empty(); })) @@ -484,9 +496,11 @@ public Mono publishDatasourceEvent(Datasource datasource, EventType eventT .isAnonymous(Authentication.isAnonymousUser(tuple.getT1().getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT2(), StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); - applicationEventPublisher.publishEvent(event); - return Mono. empty(); + return Mono.deferContextual(contextView -> { + event.populateDetails(contextView); + applicationEventPublisher.publishEvent(event); + return Mono.empty(); + }); }) .onErrorResume(throwable -> { log.error("publishDatasourceEvent error.", throwable); @@ -535,9 +549,11 @@ public Mono publishDatasourcePermissionEvent(String datasourceId, .isAnonymous(Authentication.isAnonymousUser(orgMember.getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT3(), StandardCharsets.UTF_8).toString()) .build(); - datasourcePermissionEvent.populateDetails(); - applicationEventPublisher.publishEvent(datasourcePermissionEvent); - return Mono. empty(); + return Mono.deferContextual(contextView -> { + datasourcePermissionEvent.populateDetails(contextView); + applicationEventPublisher.publishEvent(datasourcePermissionEvent); + return Mono.empty(); + }); }) .onErrorResume(throwable -> { log.error("publishDatasourcePermissionEvent error.", throwable); @@ -552,7 +568,7 @@ public Mono publishLibraryQuery(LibraryQuery libraryQuery, EventType event public Mono publishLibraryQueryEvent(String id, String name, EventType eventType) { return sessionUserService.getVisitorOrgMemberCache() .zipWith(sessionUserService.getVisitorToken()) - .map(tuple -> { + .flatMap(tuple -> { LibraryQueryEvent event = LibraryQueryEvent.builder() .userId(tuple.getT1().getUserId()) .orgId(tuple.getT1().getOrgId()) @@ -562,10 +578,12 @@ public Mono publishLibraryQueryEvent(String id, String name, EventType eve .isAnonymous(Authentication.isAnonymousUser(tuple.getT1().getUserId())) .sessionHash(Hashing.sha512().hashString(tuple.getT2(), StandardCharsets.UTF_8).toString()) .build(); - event.populateDetails(); - return event; + return Mono.deferContextual(contextView -> { + event.populateDetails(contextView); + applicationEventPublisher.publishEvent(event); + return Mono.empty(); + }); }) - .doOnNext(applicationEventPublisher::publishEvent) .then() .onErrorResume(throwable -> { log.error("publishLibraryQueryEvent error.", throwable); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy