Skip to content

Commit 1c81e72

Browse files
Make frontend proxy URL configurable within the react-native app (#2531)
* Make frontend proxy URL configurable within the react-native app * update CHANGELOG --------- Co-authored-by: Juliano Costa <[email protected]>
1 parent 4537b16 commit 1c81e72

File tree

9 files changed

+175
-14
lines changed

9 files changed

+175
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ the release.
105105
([#2503](https://github.com/open-telemetry/opentelemetry-demo/pull/2503))
106106
* [grafana] APM dashboard: Add host metrics per service instance
107107
([#2507](https://github.com/open-telemetry/opentelemetry-demo/pull/2507))
108+
* [react-native-app] Make frontend proxy URL configurable through app settings
109+
([#2531](https://github.com/open-telemetry/opentelemetry-demo/pull/2531))
108110

109111
## 2.0.2
110112

src/react-native-app/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ This will create a `react-native-app.apk` file in the directory where you ran
100100
the command. If you have an Android emulator running on your machine then you
101101
can drag and drop this file onto the emulator's window in order to install it.
102102

103+
## Pointing to another demo environment
104+
105+
By default, the app will point to `EXPO_PUBLIC_FRONTEND_PROXY_PORT` on
106+
localhost to interact with the demo APIs. This can be changed in the Settings
107+
tab when running the app to point to a demo environment running on a
108+
different server.
109+
103110
## Troubleshooting
104111

105112
### JS Bundle: build issues

src/react-native-app/app/(tabs)/_layout.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ export default function TabLayout() {
4444
),
4545
}}
4646
/>
47+
<Tabs.Screen
48+
name="settings"
49+
options={{
50+
title: "Settings",
51+
tabBarShowLabel: false,
52+
tabBarIcon: ({ color, focused }) => (
53+
<TabBarIcon
54+
name={focused ? "settings" : "settings-outline"}
55+
color={color}
56+
/>
57+
),
58+
}}
59+
/>
4760
</Tabs>
4861
);
4962
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
import { useQueryClient } from '@tanstack/react-query'
4+
import { ThemedView } from "@/components/ThemedView";
5+
import { StyleSheet } from "react-native";
6+
import { getFrontendProxyURL, setFrontendProxyURL } from "@/utils/Settings";
7+
import { setupTracerProvider } from "@/hooks/useTracer";
8+
import { trace } from "@opentelemetry/api";
9+
import { Setting } from "@/components/Setting";
10+
11+
export default function Settings() {
12+
const queryClient = useQueryClient()
13+
14+
const onSetFrontendProxyURL = async (value: string) => {
15+
await setFrontendProxyURL(value);
16+
17+
// Clear any cached queries since we now have a new endpoint to hit for everything
18+
await queryClient.invalidateQueries();
19+
20+
// Need to setup a new tracer provider since the export URL for traces has now changed
21+
trace.disable();
22+
const provider = setupTracerProvider(value);
23+
trace.setGlobalTracerProvider(provider);
24+
};
25+
26+
return (
27+
<ThemedView style={styles.container}>
28+
<Setting name="Frontend Proxy URL" get={getFrontendProxyURL} set={onSetFrontendProxyURL} />
29+
</ThemedView>
30+
);
31+
}
32+
33+
const styles = StyleSheet.create({
34+
container: {
35+
display: "flex",
36+
gap: 20,
37+
paddingLeft: 20,
38+
height: "100%",
39+
},
40+
});

src/react-native-app/components/ProductCard/ProductCard.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@
66
import { Product } from "@/protos/demo";
77
import { ThemedView } from "@/components/ThemedView";
88
import { useState, useEffect, useMemo } from "react";
9-
import getLocalhost from "@/utils/Localhost";
109
import { Image, Pressable, StyleSheet } from "react-native";
1110
import { ThemedText } from "@/components/ThemedText";
1211
import { useThemeColor } from "@/hooks/useThemeColor";
12+
import getFrontendProxyURL from "@/utils/Settings";
1313

1414
interface IProps {
1515
product: Product;
1616
onClickAdd: () => void;
1717
}
1818

1919
async function getImageURL(picture: string) {
20-
const localhost = await getLocalhost();
21-
return `http://${localhost}:${process.env.EXPO_PUBLIC_FRONTEND_PROXY_PORT}/images/products/${picture}`;
20+
const proxyURL = await getFrontendProxyURL();
21+
return `${proxyURL}/images/products/${picture}`;
2222
}
2323

2424
const ProductCard = ({
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
import {Pressable, StyleSheet, TextInput, type TextInputProps} from "react-native";
4+
import { ThemedView } from "@/components/ThemedView";
5+
import { ThemedText } from "@/components/ThemedText";
6+
import { useThemeColor } from "@/hooks/useThemeColor";
7+
import {useCallback, useEffect, useState} from "react";
8+
import Toast from "react-native-toast-message";
9+
10+
export type SettingProps = TextInputProps & {
11+
name: string;
12+
get: () => Promise<string>;
13+
set: (value: string) => Promise<void>;
14+
};
15+
16+
export function Setting({ name, get, set, ...otherProps }: SettingProps) {
17+
const color = useThemeColor({}, "text");
18+
const [loading, setLoading] = useState(false);
19+
const [text, setText] = useState('');
20+
21+
useEffect(() => {
22+
get().then(existingValue => {
23+
setText(existingValue);
24+
setLoading(false);
25+
});
26+
}, []);
27+
28+
const onApply = useCallback(async () => {
29+
await set(text);
30+
31+
Toast.show({
32+
type: "success",
33+
position: "bottom",
34+
text1: `${name} applied`,
35+
});
36+
}, [text]);
37+
38+
return (
39+
<ThemedView style={styles.container}>
40+
<ThemedText>{name}:</ThemedText>
41+
{loading ? (
42+
<ThemedText>Fetching current value...</ThemedText>
43+
) : (
44+
<>
45+
<TextInput
46+
style={{ color }}
47+
onChangeText={setText}
48+
value={text}
49+
{...otherProps}
50+
/>
51+
<Pressable style={styles.apply} onPress={onApply}>
52+
<ThemedText style={styles.applyText}>Apply</ThemedText>
53+
</Pressable>
54+
</>
55+
)}
56+
</ThemedView>
57+
);
58+
}
59+
60+
61+
const styles = StyleSheet.create({
62+
container: {
63+
display: "flex",
64+
gap: 5,
65+
},
66+
apply: {
67+
borderRadius: 4,
68+
backgroundColor: "green",
69+
alignItems: "center",
70+
width: 100,
71+
position: "relative",
72+
},
73+
applyText: {
74+
color: "white",
75+
},
76+
});

src/react-native-app/hooks/useTracer.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
ATTR_SERVICE_VERSION,
2222
} from "@opentelemetry/semantic-conventions/incubating";
2323
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
24-
import getLocalhost from "@/utils/Localhost";
2524
import { useEffect, useState } from "react";
2625
import {
2726
getDeviceId,
@@ -30,10 +29,9 @@ import {
3029
} from "react-native-device-info";
3130
import { Platform } from "react-native";
3231
import { SessionIdProcessor } from "@/utils/SessionIdProcessor";
32+
import getFrontendProxyURL from "@/utils/Settings";
3333

34-
const Tracer = async () => {
35-
const localhost = await getLocalhost();
36-
34+
export const setupTracerProvider = (proxyURL: string) => {
3735
// TODO Should add a resource detector for React Native that provides this automatically
3836
const resource = resourceFromAttributes({
3937
[ATTR_SERVICE_NAME]: "react-native-app",
@@ -49,23 +47,27 @@ const Tracer = async () => {
4947
// for React Native.
5048
// Alternatively could offer a TracerProvider that exposed a JS interface on top of the OTEL Android and Swift SDKS,
5149
// giving developers the option of collecting telemetry at the native mobile layer
52-
const provider = new WebTracerProvider({
50+
return new WebTracerProvider({
5351
resource,
5452
spanProcessors: [
5553
new BatchSpanProcessor(
5654
new OTLPTraceExporter({
57-
url: `http://${localhost}:${process.env.EXPO_PUBLIC_FRONTEND_PROXY_PORT}/otlp-http/v1/traces`,
55+
url: `${proxyURL}/otlp-http/v1/traces`,
5856
}),
5957
{
6058
scheduledDelayMillis: 500,
6159
},
6260
),
63-
6461
// TODO introduce a React Native session processor package that could be used here, taking into account mobile
6562
// specific considerations for the session such as putting the app into the background
6663
new SessionIdProcessor(),
6764
],
6865
});
66+
}
67+
68+
const Tracer = async () => {
69+
const proxyURL = await getFrontendProxyURL();
70+
const provider = setupTracerProvider(proxyURL);
6971

7072
provider.register({
7173
propagator: new CompositePropagator({

src/react-native-app/utils/Request.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/**
44
* Copied with modification from src/frontend/utils/Request.ts
55
*/
6-
import getLocalhost from "@/utils/Localhost";
6+
import getFrontendProxyURL from "@/utils/Settings";
77

88
interface IRequestParams {
99
url: string;
@@ -22,9 +22,8 @@ const request = async <T>({
2222
"content-type": "application/json",
2323
},
2424
}: IRequestParams): Promise<T> => {
25-
const localhost = await getLocalhost();
26-
const API_URL = `http://${localhost}:${process.env.EXPO_PUBLIC_FRONTEND_PROXY_PORT}`;
27-
const requestURL = `${API_URL}${url}?${new URLSearchParams(queryParams).toString()}`;
25+
const proxyURL = await getFrontendProxyURL();
26+
const requestURL = `${proxyURL}${url}?${new URLSearchParams(queryParams).toString()}`;
2827
const requestBody = body ? JSON.stringify(body) : undefined;
2928
const response = await fetch(requestURL, {
3029
method,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
import AsyncStorage from "@react-native-async-storage/async-storage";
4+
import getLocalhost from "@/utils/Localhost";
5+
6+
const FRONTEND_PROXY_URL_SETTING = 'frontend_proxy_url';
7+
8+
export const getFrontendProxyURL = async (): Promise<string> => {
9+
const proxyURL = await AsyncStorage.getItem(FRONTEND_PROXY_URL_SETTING);
10+
if (proxyURL) {
11+
return proxyURL
12+
} else {
13+
const localhost = await getLocalhost();
14+
return `http://${localhost}:${process.env.EXPO_PUBLIC_FRONTEND_PROXY_PORT}`;
15+
}
16+
};
17+
18+
export const setFrontendProxyURL = async (url: string) => {
19+
await AsyncStorage.setItem(FRONTEND_PROXY_URL_SETTING, url);
20+
}
21+
22+
export default getFrontendProxyURL;

0 commit comments

Comments
 (0)