From 82e03978b89938219958032efb1448cc76baa181 Mon Sep 17 00:00:00 2001 From: Saumit Date: Sat, 27 Sep 2025 02:14:26 +0530 Subject: Initial snapshot - OpenTelemetry demo 2.1.3 -f --- src/react-native-app/gateways/Api.gateway.ts | 150 +++++++++++++++++++++++ src/react-native-app/gateways/Session.gateway.ts | 43 +++++++ 2 files changed, 193 insertions(+) create mode 100644 src/react-native-app/gateways/Api.gateway.ts create mode 100644 src/react-native-app/gateways/Session.gateway.ts (limited to 'src/react-native-app/gateways') diff --git a/src/react-native-app/gateways/Api.gateway.ts b/src/react-native-app/gateways/Api.gateway.ts new file mode 100644 index 0000000..96f898b --- /dev/null +++ b/src/react-native-app/gateways/Api.gateway.ts @@ -0,0 +1,150 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +/** + * Copied with modification from src/frontend/gateways/Api.gateway.ts + * + * TODO The React Native example only implements a subset of the functionality defined here, missing in particular is: + * - showing ads + * - showing recommendations + * - calculating shipping costs + * - currency conversion + */ +import { + Ad, + Address, + Cart, + CartItem, + Money, + PlaceOrderRequest, + Product, +} from "@/protos/demo"; +import { IProductCart, IProductCartItem, IProductCheckout } from "@/types/Cart"; +import request from "@/utils/Request"; +import SessionGateway from "./Session.gateway"; +import { context, propagation } from "@opentelemetry/api"; + +const basePath = "/api"; + +const Apis = () => ({ + async getCart(currencyCode: string) { + const { userId } = await SessionGateway.getSession(); + return request({ + url: `${basePath}/cart`, + queryParams: { sessionId: userId, currencyCode }, + }); + }, + async addCartItem({ + currencyCode, + ...item + }: CartItem & { currencyCode: string }) { + const { userId } = await SessionGateway.getSession(); + return request({ + url: `${basePath}/cart`, + body: { item, userId }, + queryParams: { currencyCode }, + method: "POST", + }); + }, + async emptyCart() { + const { userId } = await SessionGateway.getSession(); + return request({ + url: `${basePath}/cart`, + method: "DELETE", + body: { userId }, + }); + }, + + getSupportedCurrencyList() { + return request({ + url: `${basePath}/currency`, + }); + }, + + getShippingCost( + itemList: IProductCartItem[], + currencyCode: string, + address: Address, + ) { + return request({ + url: `${basePath}/shipping`, + queryParams: { + itemList: JSON.stringify( + itemList.map(({ productId, quantity }) => ({ productId, quantity })), + ), + currencyCode, + address: JSON.stringify(address), + }, + }); + }, + + placeOrder({ + currencyCode, + ...order + }: PlaceOrderRequest & { currencyCode: string }) { + return request({ + url: `${basePath}/checkout`, + method: "POST", + queryParams: { currencyCode }, + body: order, + }); + }, + + listProducts(currencyCode: string) { + return request({ + url: `${basePath}/products`, + queryParams: { currencyCode }, + }); + }, + getProduct(productId: string, currencyCode: string) { + return request({ + url: `${basePath}/products/${productId}`, + queryParams: { currencyCode }, + }); + }, + async listRecommendations(productIds: string[], currencyCode: string) { + const { userId } = await SessionGateway.getSession(); + return request({ + url: `${basePath}/recommendations`, + queryParams: { + productIds, + sessionId: userId, + currencyCode, + }, + }); + }, + listAds(contextKeys: string[]) { + return request({ + url: `${basePath}/data`, + queryParams: { + contextKeys, + }, + }); + }, +}); + +/** + * Extends all the API calls to set baggage automatically. + */ +const ApiGateway = new Proxy(Apis(), { + get(target, prop, receiver) { + const originalFunction = Reflect.get(target, prop, receiver); + + if (typeof originalFunction !== "function") { + return originalFunction; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return async function (...args: any[]) { + const { userId } = await SessionGateway.getSession(); + const baggage = + propagation.getActiveBaggage() || propagation.createBaggage(); + const newBaggage = baggage.setEntry("session.id", { value: userId }); + const newContext = propagation.setBaggage(context.active(), newBaggage); + return context.with(newContext, () => { + return Reflect.apply(originalFunction, undefined, args); + }); + }; + }, +}); + +export default ApiGateway; diff --git a/src/react-native-app/gateways/Session.gateway.ts b/src/react-native-app/gateways/Session.gateway.ts new file mode 100644 index 0000000..d6da9e1 --- /dev/null +++ b/src/react-native-app/gateways/Session.gateway.ts @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +/** + * Copied with modification from src/frontend/gateways/Session.gateway.ts + */ +import "react-native-get-random-values"; // Must be imported before 'uuid', see https://stackoverflow.com/a/68097811 +import { v4 } from "uuid"; +import AsyncStorage from "@react-native-async-storage/async-storage"; + +interface ISession { + userId: string; + currencyCode: string; +} + +const sessionKey = "session"; +const defaultSession = { + userId: v4(), + currencyCode: "USD", +}; + +const SessionGateway = () => ({ + async getSession(): Promise { + const sessionString = await AsyncStorage.getItem(sessionKey); + + if (!sessionString) { + await AsyncStorage.setItem(sessionKey, JSON.stringify(defaultSession)); + } + + return JSON.parse( + sessionString || JSON.stringify(defaultSession), + ) as ISession; + }, + setSessionValue(key: K, value: ISession[K]) { + const session = this.getSession(); + + return AsyncStorage.setItem( + sessionKey, + JSON.stringify({ ...session, [key]: value }), + ); + }, +}); + +export default SessionGateway(); -- cgit v1.2.3