Skip to content

Commit 457afab

Browse files
TeddyCrIceS2github-actions[bot]
authored
MINOR - React and Java telemetry (open-metadata#27773)
* chore: added sentry for UI * chore: added sentry for UI * chore: added sentry for UI * chore: added sentry for UI and backend * chore: move setup to config * Update generated TypeScript types * chore: addressed CI comments * chore: handle no ui startup in IndexResource --------- Co-authored-by: IceS2 <pablo.takara@getcollate.io> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 0fb6954 commit 457afab

6 files changed

Lines changed: 155 additions & 36 deletions

File tree

openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
import org.openmetadata.service.resources.filters.ETagResponseFilter;
138138
import org.openmetadata.service.resources.settings.SettingsCache;
139139
import org.openmetadata.service.resources.system.DiagnosticsResource;
140+
import org.openmetadata.service.resources.system.IndexResource;
140141
import org.openmetadata.service.search.SearchIndexRetryWorker;
141142
import org.openmetadata.service.search.SearchRepository;
142143
import org.openmetadata.service.search.SearchRepositoryFactory;
@@ -364,6 +365,7 @@ public void run(OpenMetadataApplicationConfig catalogConfig, Environment environ
364365
EventPubSub.start();
365366

366367
ApplicationHandler.initialize(catalogConfig);
368+
IndexResource.initialize(catalogConfig);
367369
registerResources(catalogConfig, environment, jdbi);
368370

369371
// Register Event Handler

openmetadata-service/src/main/java/org/openmetadata/service/OpenMetadataApplicationConfig.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.openmetadata.schema.configuration.AdminOpsConfiguration;
3535
import org.openmetadata.schema.configuration.AiPlatformConfiguration;
3636
import org.openmetadata.schema.configuration.LimitsConfiguration;
37+
import org.openmetadata.schema.configuration.SentryConfiguration;
3738
import org.openmetadata.schema.security.scim.ScimConfiguration;
3839
import org.openmetadata.schema.security.secrets.SecretsManagerConfiguration;
3940
import org.openmetadata.schema.service.configuration.elasticsearch.ElasticSearchConfiguration;
@@ -167,6 +168,16 @@ public AdminOpsConfiguration getAdminOpsConfiguration() {
167168
return adminOpsConfiguration;
168169
}
169170

171+
@JsonProperty("sentry")
172+
private SentryConfiguration sentryConfiguration;
173+
174+
public SentryConfiguration getSentryConfiguration() {
175+
if (sentryConfiguration == null) {
176+
sentryConfiguration = new SentryConfiguration();
177+
}
178+
return sentryConfiguration;
179+
}
180+
170181
@JsonProperty("mcpConfiguration")
171182
private org.openmetadata.schema.api.configuration.MCPConfiguration mcpConfiguration;
172183

openmetadata-service/src/main/java/org/openmetadata/service/resources/system/IndexResource.java

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,65 @@
1414
import java.nio.charset.StandardCharsets;
1515
import java.util.stream.Collectors;
1616
import lombok.extern.slf4j.Slf4j;
17+
import org.apache.commons.text.StringEscapeUtils;
18+
import org.openmetadata.schema.configuration.SentryConfiguration;
1719
import org.openmetadata.service.OpenMetadataApplicationConfig;
20+
import org.openmetadata.service.resources.version.VersionResource;
1821
import org.openmetadata.service.security.CspNonceHandler;
1922

2023
@Slf4j
2124
@Path("/")
2225
public class IndexResource {
23-
private static final String RAW_INDEX_HTML;
26+
private static volatile String configProcessedHtml;
27+
private static volatile String configuredBasePath = "/";
2428

25-
static {
29+
public static void initialize(OpenMetadataApplicationConfig catalogConfig) {
30+
String rawIndexHtml;
2631
try (InputStream inputStream = IndexResource.class.getResourceAsStream("/assets/index.html")) {
2732
if (inputStream == null) {
28-
throw new IllegalStateException("Missing required resource: /assets/index.html");
33+
LOG.warn("UI assets not found on classpath. Running in no-ui mode.");
34+
return;
2935
}
30-
RAW_INDEX_HTML =
36+
rawIndexHtml =
3137
new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
3238
.lines()
3339
.collect(Collectors.joining("\n"));
3440
} catch (IOException e) {
3541
throw new IllegalStateException("Failed to load /assets/index.html", e);
3642
}
37-
}
38-
39-
private String indexHtml;
4043

41-
public IndexResource() {
42-
indexHtml = RAW_INDEX_HTML;
44+
String basePath = catalogConfig.getBasePath();
45+
configuredBasePath = (basePath != null && !basePath.isEmpty()) ? basePath : "/";
46+
SentryConfiguration sentryConfig = catalogConfig.getSentryConfiguration();
47+
String clusterName = catalogConfig.getClusterName();
48+
configProcessedHtml =
49+
rawIndexHtml
50+
.replace("${sentryEnabled}", String.valueOf(sentryConfig.getEnabled()))
51+
.replace("${sentryDsn}", escapeJs(sentryConfig.getUiDsn()))
52+
.replace("${sentryEnvironment}", escapeJs(sentryConfig.getEnvironment()))
53+
.replace(
54+
"${sentryTraceSampleRate}",
55+
escapeJs(String.valueOf(sentryConfig.getTracesSampleRate())))
56+
.replace("${clusterName}", escapeJs(clusterName != null ? clusterName : "openmetadata"))
57+
.replace(
58+
"${appVersion}", escapeJs(new VersionResource().getCatalogVersion().getVersion()));
4359
}
4460

45-
public void initialize(OpenMetadataApplicationConfig config) {
46-
this.indexHtml = this.indexHtml.replace("${basePath}", config.getBasePath());
61+
private static String escapeJs(String value) {
62+
if (value == null) {
63+
return "";
64+
}
65+
return StringEscapeUtils.escapeEcmaScript(value);
4766
}
4867

4968
public static String getIndexFile(String basePath) {
50-
LOG.debug("IndexResource.getIndexFile called with basePath: [{}]", basePath);
51-
52-
String result = RAW_INDEX_HTML.replace("${basePath}", basePath);
53-
String basePathLine =
54-
result
55-
.lines()
56-
.filter(line -> line.contains("window.BASE_PATH"))
57-
.findFirst()
58-
.orElse("NOT FOUND");
59-
LOG.debug("After replacement, window.BASE_PATH line: {}", basePathLine.trim());
69+
String html = configProcessedHtml;
70+
if (html == null) {
71+
throw new IllegalStateException("IndexResource not initialized. Call initialize() first.");
72+
}
6073

61-
return result;
74+
LOG.debug("IndexResource.getIndexFile called with basePath: [{}]", basePath);
75+
return html.replace("${basePath}", basePath);
6276
}
6377

6478
public static String getIndexFile(String basePath, String cspNonce) {
@@ -73,10 +87,6 @@ public static String getIndexFile(String basePath, String cspNonce) {
7387
@Produces(MediaType.TEXT_HTML)
7488
public Response getIndex(@Context HttpServletRequest request) {
7589
final String cspNonce = (String) request.getAttribute(CspNonceHandler.CSP_NONCE_ATTRIBUTE);
76-
String html = indexHtml;
77-
if (cspNonce != null && !cspNonce.isEmpty()) {
78-
html = html.replace("${cspNonce}", cspNonce);
79-
}
80-
return Response.ok(html).build();
90+
return Response.ok(getIndexFile(configuredBasePath, cspNonce)).build();
8191
}
8292
}

openmetadata-service/src/test/java/org/openmetadata/service/resources/system/IndexResourceTest.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,28 @@
2525
import java.util.Base64;
2626
import java.util.regex.Matcher;
2727
import java.util.regex.Pattern;
28+
import org.junit.jupiter.api.BeforeAll;
2829
import org.junit.jupiter.api.BeforeEach;
2930
import org.junit.jupiter.api.Test;
31+
import org.openmetadata.schema.configuration.SentryConfiguration;
3032
import org.openmetadata.service.OpenMetadataApplicationConfig;
3133
import org.openmetadata.service.security.CspNonceHandler;
3234

3335
class IndexResourceTest {
3436
private IndexResource resource;
35-
private OpenMetadataApplicationConfig config;
37+
38+
@BeforeAll
39+
static void initIndex() {
40+
OpenMetadataApplicationConfig config = mock(OpenMetadataApplicationConfig.class);
41+
SentryConfiguration sentryConfig = new SentryConfiguration();
42+
when(config.getSentryConfiguration()).thenReturn(sentryConfig);
43+
when(config.getClusterName()).thenReturn("test-cluster");
44+
IndexResource.initialize(config);
45+
}
3646

3747
@BeforeEach
3848
void setUp() {
3949
resource = new IndexResource();
40-
config = mock(OpenMetadataApplicationConfig.class);
41-
when(config.getBasePath()).thenReturn("/");
4250
}
4351

4452
@Test
@@ -76,8 +84,6 @@ void testStaticGetIndexFileWithNonce() {
7684

7785
@Test
7886
void testGetIndexWithRequest() {
79-
resource.initialize(config);
80-
8187
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
8288
String testNonce = Base64.getEncoder().encodeToString("test-nonce-bytes".getBytes());
8389
when(mockRequest.getAttribute(CspNonceHandler.CSP_NONCE_ATTRIBUTE)).thenReturn(testNonce);
@@ -97,8 +103,6 @@ void testGetIndexWithRequest() {
97103

98104
@Test
99105
void testGetIndexWithNullNonce() {
100-
resource.initialize(config);
101-
102106
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
103107
when(mockRequest.getAttribute(CspNonceHandler.CSP_NONCE_ATTRIBUTE)).thenReturn(null);
104108

@@ -111,8 +115,6 @@ void testGetIndexWithNullNonce() {
111115

112116
@Test
113117
void testGetIndexWithEmptyNonce() {
114-
resource.initialize(config);
115-
116118
HttpServletRequest mockRequest = mock(HttpServletRequest.class);
117119
when(mockRequest.getAttribute(CspNonceHandler.CSP_NONCE_ATTRIBUTE)).thenReturn("");
118120

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"$id": "https://open-metadata.org/schema/configuration/sentryConfiguration.json",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"title": "SentryConfiguration",
5+
"description": "This schema defines the Sentry Configuration for error tracking and performance monitoring.",
6+
"type": "object",
7+
"javaType": "org.openmetadata.schema.configuration.SentryConfiguration",
8+
"properties": {
9+
"enabled": {
10+
"description": "Indicates whether Sentry error tracking is enabled.",
11+
"type": "boolean",
12+
"default": false
13+
},
14+
"backendDsn": {
15+
"description": "Sentry Data Source Name (DSN) for the backend.",
16+
"type": "string",
17+
"default": ""
18+
},
19+
"uiDsn": {
20+
"description": "Sentry Data Source Name (DSN) for the UI.",
21+
"type": "string",
22+
"default": ""
23+
},
24+
"environment": {
25+
"description": "Environment label sent to Sentry (e.g., development, staging, production).",
26+
"type": "string",
27+
"default": "development"
28+
},
29+
"serverName": {
30+
"description": "Server name reported to Sentry.",
31+
"type": "string",
32+
"default": "openmetadata"
33+
},
34+
"tracesSampleRate": {
35+
"description": "Sample rate for performance traces (0.0 to 1.0).",
36+
"type": "number",
37+
"default": 0.5,
38+
"minimum": 0.0,
39+
"maximum": 1.0
40+
},
41+
"debug": {
42+
"description": "Enable Sentry SDK debug mode.",
43+
"type": "boolean",
44+
"default": false
45+
}
46+
},
47+
"additionalProperties": false
48+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2026 Collate.
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
/**
14+
* This schema defines the Sentry Configuration for error tracking and performance
15+
* monitoring.
16+
*/
17+
export interface SentryConfiguration {
18+
/**
19+
* Sentry Data Source Name (DSN) for the backend.
20+
*/
21+
backendDsn?: string;
22+
/**
23+
* Enable Sentry SDK debug mode.
24+
*/
25+
debug?: boolean;
26+
/**
27+
* Indicates whether Sentry error tracking is enabled.
28+
*/
29+
enabled?: boolean;
30+
/**
31+
* Environment label sent to Sentry (e.g., development, staging, production).
32+
*/
33+
environment?: string;
34+
/**
35+
* Server name reported to Sentry.
36+
*/
37+
serverName?: string;
38+
/**
39+
* Sample rate for performance traces (0.0 to 1.0).
40+
*/
41+
tracesSampleRate?: number;
42+
/**
43+
* Sentry Data Source Name (DSN) for the UI.
44+
*/
45+
uiDsn?: string;
46+
}

0 commit comments

Comments
 (0)