summaryrefslogtreecommitdiff
path: root/src/frontend/gateways
diff options
context:
space:
mode:
authorSaumit <justsaumit@protonmail.com>2025-09-27 02:14:26 +0530
committerSaumit <justsaumit@protonmail.com>2025-09-27 02:14:26 +0530
commit82e03978b89938219958032efb1448cc76baa181 (patch)
tree626f3e54d52ecd49be0ed3bee30abacc0453d081 /src/frontend/gateways
Initial snapshot - OpenTelemetry demo 2.1.3 -f
Diffstat (limited to 'src/frontend/gateways')
-rw-r--r--src/frontend/gateways/Api.gateway.ts119
-rw-r--r--src/frontend/gateways/Session.gateway.ts33
-rw-r--r--src/frontend/gateways/http/Shipping.gateway.ts60
-rw-r--r--src/frontend/gateways/rpc/Ad.gateway.ts19
-rw-r--r--src/frontend/gateways/rpc/Cart.gateway.ts29
-rw-r--r--src/frontend/gateways/rpc/Checkout.gateway.ts19
-rw-r--r--src/frontend/gateways/rpc/Currency.gateway.ts24
-rw-r--r--src/frontend/gateways/rpc/ProductCatalog.gateway.ts24
-rw-r--r--src/frontend/gateways/rpc/Recommendations.gateway.ts21
9 files changed, 348 insertions, 0 deletions
diff --git a/src/frontend/gateways/Api.gateway.ts b/src/frontend/gateways/Api.gateway.ts
new file mode 100644
index 0000000..eb9438c
--- /dev/null
+++ b/src/frontend/gateways/Api.gateway.ts
@@ -0,0 +1,119 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { Ad, Address, Cart, CartItem, Money, PlaceOrderRequest, Product } from '../protos/demo';
+import { IProductCart, IProductCartItem, IProductCheckout } from '../types/Cart';
+import request from '../utils/Request';
+import { AttributeNames } from '../utils/enums/AttributeNames';
+import SessionGateway from './Session.gateway';
+import { context, propagation } from "@opentelemetry/api";
+
+const { userId } = SessionGateway.getSession();
+
+const basePath = '/api';
+
+const Apis = () => ({
+ getCart(currencyCode: string) {
+ return request<IProductCart>({
+ url: `${basePath}/cart`,
+ queryParams: { sessionId: userId, currencyCode },
+ });
+ },
+ addCartItem({ currencyCode, ...item }: CartItem & { currencyCode: string }) {
+ return request<Cart>({
+ url: `${basePath}/cart`,
+ body: { item, userId },
+ queryParams: { currencyCode },
+ method: 'POST',
+ });
+ },
+ emptyCart() {
+ return request<undefined>({
+ url: `${basePath}/cart`,
+ method: 'DELETE',
+ body: { userId },
+ });
+ },
+
+ getSupportedCurrencyList() {
+ return request<string[]>({
+ url: `${basePath}/currency`,
+ });
+ },
+
+ getShippingCost(itemList: IProductCartItem[], currencyCode: string, address: Address) {
+ return request<Money>({
+ 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<IProductCheckout>({
+ url: `${basePath}/checkout`,
+ method: 'POST',
+ queryParams: { currencyCode },
+ body: order,
+ });
+ },
+
+ listProducts(currencyCode: string) {
+ return request<Product[]>({
+ url: `${basePath}/products`,
+ queryParams: { currencyCode },
+ });
+ },
+ getProduct(productId: string, currencyCode: string) {
+ return request<Product>({
+ url: `${basePath}/products/${productId}`,
+ queryParams: { currencyCode },
+ });
+ },
+ listRecommendations(productIds: string[], currencyCode: string) {
+ return request<Product[]>({
+ url: `${basePath}/recommendations`,
+ queryParams: {
+ productIds,
+ sessionId: userId,
+ currencyCode,
+ },
+ });
+ },
+ listAds(contextKeys: string[]) {
+ return request<Ad[]>({
+ 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 function (...args: any[]) {
+ const baggage = propagation.getActiveBaggage() || propagation.createBaggage();
+ const newBaggage = baggage.setEntry(AttributeNames.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/frontend/gateways/Session.gateway.ts b/src/frontend/gateways/Session.gateway.ts
new file mode 100644
index 0000000..d5791b5
--- /dev/null
+++ b/src/frontend/gateways/Session.gateway.ts
@@ -0,0 +1,33 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { v4 } from 'uuid';
+
+interface ISession {
+ userId: string;
+ currencyCode: string;
+}
+
+const sessionKey = 'session';
+const defaultSession = {
+ userId: v4(),
+ currencyCode: 'USD',
+};
+
+const SessionGateway = () => ({
+ getSession(): ISession {
+ if (typeof window === 'undefined') return defaultSession;
+ const sessionString = localStorage.getItem(sessionKey);
+
+ if (!sessionString) localStorage.setItem(sessionKey, JSON.stringify(defaultSession));
+
+ return JSON.parse(sessionString || JSON.stringify(defaultSession)) as ISession;
+ },
+ setSessionValue<K extends keyof ISession>(key: K, value: ISession[K]) {
+ const session = this.getSession();
+
+ localStorage.setItem(sessionKey, JSON.stringify({ ...session, [key]: value }));
+ },
+});
+
+export default SessionGateway();
diff --git a/src/frontend/gateways/http/Shipping.gateway.ts b/src/frontend/gateways/http/Shipping.gateway.ts
new file mode 100644
index 0000000..fad3264
--- /dev/null
+++ b/src/frontend/gateways/http/Shipping.gateway.ts
@@ -0,0 +1,60 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { Address, CartItem, GetQuoteResponse } from '../../protos/demo';
+
+const { SHIPPING_ADDR = '' } = process.env;
+
+// Transform address from camelCase to snake_case for HTTP API
+const transformAddress = (address: Address) => ({
+ street_address: address.streetAddress,
+ city: address.city,
+ state: address.state,
+ country: address.country,
+ zip_code: address.zipCode,
+});
+
+// Transform cart items from camelCase to snake_case for HTTP API
+const transformCartItems = (items: CartItem[]) =>
+ items.map(item => ({
+ product_id: item.productId,
+ quantity: item.quantity,
+ }));
+
+const ShippingGateway = () => ({
+ async getShippingCost(itemList: CartItem[], address: Address): Promise<GetQuoteResponse> {
+ const requestBody = {
+ items: transformCartItems(itemList),
+ address: transformAddress(address),
+ };
+
+ const response = await fetch(`${SHIPPING_ADDR}/get-quote`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(requestBody),
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(`HTTP error: ${response.status} ${response.statusText} - ${errorText}`);
+ }
+
+ const data = await response.json();
+
+ const costUsd = data.cost_usd ? {
+ currencyCode: data.cost_usd.currency_code,
+ units: data.cost_usd.units,
+ nanos: data.cost_usd.nanos,
+ } : undefined;
+
+ const transformedResponse: GetQuoteResponse = {
+ costUsd,
+ };
+
+ return transformedResponse;
+ },
+});
+
+export default ShippingGateway();
diff --git a/src/frontend/gateways/rpc/Ad.gateway.ts b/src/frontend/gateways/rpc/Ad.gateway.ts
new file mode 100644
index 0000000..1fd7244
--- /dev/null
+++ b/src/frontend/gateways/rpc/Ad.gateway.ts
@@ -0,0 +1,19 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { ChannelCredentials } from '@grpc/grpc-js';
+import { AdResponse, AdServiceClient } from '../../protos/demo';
+
+const { AD_ADDR = '' } = process.env;
+
+const client = new AdServiceClient(AD_ADDR, ChannelCredentials.createInsecure());
+
+const AdGateway = () => ({
+ listAds(contextKeys: string[]) {
+ return new Promise<AdResponse>((resolve, reject) =>
+ client.getAds({ contextKeys: contextKeys }, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+});
+
+export default AdGateway();
diff --git a/src/frontend/gateways/rpc/Cart.gateway.ts b/src/frontend/gateways/rpc/Cart.gateway.ts
new file mode 100644
index 0000000..25dfc87
--- /dev/null
+++ b/src/frontend/gateways/rpc/Cart.gateway.ts
@@ -0,0 +1,29 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { ChannelCredentials } from '@grpc/grpc-js';
+import { Cart, CartItem, CartServiceClient, Empty } from '../../protos/demo';
+
+const { CART_ADDR = '' } = process.env;
+
+const client = new CartServiceClient(CART_ADDR, ChannelCredentials.createInsecure());
+
+const CartGateway = () => ({
+ getCart(userId: string) {
+ return new Promise<Cart>((resolve, reject) =>
+ client.getCart({ userId }, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+ addItem(userId: string, item: CartItem) {
+ return new Promise<Empty>((resolve, reject) =>
+ client.addItem({ userId, item }, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+ emptyCart(userId: string) {
+ return new Promise<Empty>((resolve, reject) =>
+ client.emptyCart({ userId }, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+});
+
+export default CartGateway();
diff --git a/src/frontend/gateways/rpc/Checkout.gateway.ts b/src/frontend/gateways/rpc/Checkout.gateway.ts
new file mode 100644
index 0000000..3d54f9e
--- /dev/null
+++ b/src/frontend/gateways/rpc/Checkout.gateway.ts
@@ -0,0 +1,19 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { ChannelCredentials } from '@grpc/grpc-js';
+import { CheckoutServiceClient, PlaceOrderRequest, PlaceOrderResponse } from '../../protos/demo';
+
+const { CHECKOUT_ADDR = '' } = process.env;
+
+const client = new CheckoutServiceClient(CHECKOUT_ADDR, ChannelCredentials.createInsecure());
+
+const CheckoutGateway = () => ({
+ placeOrder(order: PlaceOrderRequest) {
+ return new Promise<PlaceOrderResponse>((resolve, reject) =>
+ client.placeOrder(order, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+});
+
+export default CheckoutGateway();
diff --git a/src/frontend/gateways/rpc/Currency.gateway.ts b/src/frontend/gateways/rpc/Currency.gateway.ts
new file mode 100644
index 0000000..ac25719
--- /dev/null
+++ b/src/frontend/gateways/rpc/Currency.gateway.ts
@@ -0,0 +1,24 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { ChannelCredentials } from '@grpc/grpc-js';
+import { GetSupportedCurrenciesResponse, CurrencyServiceClient, Money } from '../../protos/demo';
+
+const { CURRENCY_ADDR = '' } = process.env;
+
+const client = new CurrencyServiceClient(CURRENCY_ADDR, ChannelCredentials.createInsecure());
+
+const CurrencyGateway = () => ({
+ convert(from: Money, toCode: string) {
+ return new Promise<Money>((resolve, reject) =>
+ client.convert({ from, toCode }, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+ getSupportedCurrencies() {
+ return new Promise<GetSupportedCurrenciesResponse>((resolve, reject) =>
+ client.getSupportedCurrencies({}, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+});
+
+export default CurrencyGateway();
diff --git a/src/frontend/gateways/rpc/ProductCatalog.gateway.ts b/src/frontend/gateways/rpc/ProductCatalog.gateway.ts
new file mode 100644
index 0000000..18d8305
--- /dev/null
+++ b/src/frontend/gateways/rpc/ProductCatalog.gateway.ts
@@ -0,0 +1,24 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { ChannelCredentials } from '@grpc/grpc-js';
+import { ListProductsResponse, Product, ProductCatalogServiceClient } from '../../protos/demo';
+
+const { PRODUCT_CATALOG_ADDR = '' } = process.env;
+
+const client = new ProductCatalogServiceClient(PRODUCT_CATALOG_ADDR, ChannelCredentials.createInsecure());
+
+const ProductCatalogGateway = () => ({
+ listProducts() {
+ return new Promise<ListProductsResponse>((resolve, reject) =>
+ client.listProducts({}, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+ getProduct(id: string) {
+ return new Promise<Product>((resolve, reject) =>
+ client.getProduct({ id }, (error, response) => (error ? reject(error) : resolve(response)))
+ );
+ },
+});
+
+export default ProductCatalogGateway();
diff --git a/src/frontend/gateways/rpc/Recommendations.gateway.ts b/src/frontend/gateways/rpc/Recommendations.gateway.ts
new file mode 100644
index 0000000..c58a470
--- /dev/null
+++ b/src/frontend/gateways/rpc/Recommendations.gateway.ts
@@ -0,0 +1,21 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+import { ChannelCredentials } from '@grpc/grpc-js';
+import { ListRecommendationsResponse, RecommendationServiceClient } from '../../protos/demo';
+
+const { RECOMMENDATION_ADDR = '' } = process.env;
+
+const client = new RecommendationServiceClient(RECOMMENDATION_ADDR, ChannelCredentials.createInsecure());
+
+const RecommendationsGateway = () => ({
+ listRecommendations(userId: string, productIds: string[]) {
+ return new Promise<ListRecommendationsResponse>((resolve, reject) =>
+ client.listRecommendations({ userId, productIds }, (error, response) =>
+ error ? reject(error) : resolve(response)
+ )
+ );
+ },
+});
+
+export default RecommendationsGateway();