diff --git a/airbyte-webapp/src/App.tsx b/airbyte-webapp/src/App.tsx index c5343c3504465..afef5ca17b28a 100644 --- a/airbyte-webapp/src/App.tsx +++ b/airbyte-webapp/src/App.tsx @@ -5,6 +5,7 @@ import { ThemeProvider } from "styled-components"; import { ApiErrorBoundary } from "components/common/ApiErrorBoundary"; +import { config } from "config"; import { ApiServices } from "core/ApiServices"; import { I18nProvider } from "core/i18n"; import { ServicesProvider } from "core/servicesProvider"; @@ -18,14 +19,7 @@ import { AnalyticsProvider } from "views/common/AnalyticsProvider"; import { StoreProvider } from "views/common/StoreProvider"; import LoadingPage from "./components/LoadingPage"; -import { - Config, - ConfigServiceProvider, - defaultConfig, - envConfigProvider, - ValueProvider, - windowConfigProvider, -} from "./config"; +import { ConfigServiceProvider } from "./config"; import en from "./locales/en.json"; import { Routing } from "./pages/routes"; import { WorkspaceServiceProvider } from "./services/workspaces/WorkspacesService"; @@ -35,8 +29,6 @@ const StyleProvider: React.FC> = ({ children }) {children} ); -const configProviders: ValueProvider = [envConfigProvider, windowConfigProvider]; - const Services: React.FC> = ({ children }) => ( @@ -69,7 +61,7 @@ const App: React.FC = () => { }> - + diff --git a/airbyte-webapp/src/config/ConfigServiceProvider.tsx b/airbyte-webapp/src/config/ConfigServiceProvider.tsx index 0c5733520c1a4..4b0282e48a7bd 100644 --- a/airbyte-webapp/src/config/ConfigServiceProvider.tsx +++ b/airbyte-webapp/src/config/ConfigServiceProvider.tsx @@ -1,44 +1,27 @@ -import React, { useContext, useMemo } from "react"; -import { useAsync } from "react-use"; +import React, { useContext } from "react"; -import { LoadingPage } from "components"; +import { AirbyteWebappConfig } from "./types"; -import { applyProviders } from "./configProviders"; -import { Config, ValueProvider } from "./types"; - -export interface ConfigContextData { - config: T; +export interface ConfigContextData { + config: AirbyteWebappConfig; } export const ConfigContext = React.createContext(null); -export function useConfig(): T { +export function useConfig(): AirbyteWebappConfig { const configService = useContext(ConfigContext); if (configService === null) { throw new Error("useConfig must be used within a ConfigProvider"); } - return useMemo(() => configService.config as unknown as T, [configService.config]); + return configService.config; } -const ConfigServiceInner: React.FC< +export const ConfigServiceProvider: React.FC< React.PropsWithChildren<{ - defaultConfig: Config; - providers?: ValueProvider; + config: AirbyteWebappConfig; }> -> = ({ children, defaultConfig, providers }) => { - const { loading, value } = useAsync( - async () => (providers ? applyProviders(defaultConfig, providers) : defaultConfig), - [providers] - ); - const config: ConfigContextData | null = useMemo(() => (value ? { config: value } : null), [value]); - - if (loading) { - return ; - } - - return {children}; +> = ({ children, config }) => { + return {children}; }; - -export const ConfigServiceProvider = React.memo(ConfigServiceInner); diff --git a/airbyte-webapp/src/config/config.ts b/airbyte-webapp/src/config/config.ts new file mode 100644 index 0000000000000..6fc941f47c7cb --- /dev/null +++ b/airbyte-webapp/src/config/config.ts @@ -0,0 +1,39 @@ +import { AirbyteWebappConfig } from "./types"; + +export const config: AirbyteWebappConfig = { + segment: { + token: window.SEGMENT_TOKEN ?? process.env.REACT_APP_SEGMENT_TOKEN, + enabled: window.TRACKING_STRATEGY === "segment", + }, + apiUrl: window.API_URL ?? process.env.REACT_APP_API_URL ?? `http://${window.location.hostname}:8001/api`, + connectorBuilderApiUrl: process.env.REACT_APP_CONNECTOR_BUILDER_API_URL ?? `http://${window.location.hostname}:8003`, + version: window.AIRBYTE_VERSION ?? "dev", + integrationUrl: process.env.REACT_APP_INTEGRATION_DOCS_URLS ?? "/docs", + oauthRedirectUrl: `${window.location.protocol}//${window.location.host}`, + cloudApiUrl: window.CLOUD_API_URL ?? process.env.REACT_APP_CLOUD_API_URL, + cloudPublicApiUrl: process.env.REACT_APP_CLOUD_PUBLIC_API_URL, + firebase: { + apiKey: window.FIREBASE_API_KEY ?? process.env.REACT_APP_FIREBASE_API_KEY, + authDomain: window.FIREBASE_AUTH_DOMAIN ?? process.env.REACT_APP_FIREBASE_AUTH_DOMAIN, + authEmulatorHost: window.FIREBASE_AUTH_EMULATOR_HOST ?? process.env.REACT_APP_FIREBASE_AUTH_EMULATOR_HOST, + }, + intercom: { + appId: process.env.REACT_APP_INTERCOM_APP_ID, + }, + launchDarkly: window.LAUNCHDARKLY_KEY ?? process.env.REACT_APP_LAUNCHDARKLY_KEY, + datadog: { + applicationId: window.REACT_APP_DATADOG_APPLICATION_ID ?? process.env.REACT_APP_DATADOG_APPLICATION_ID, + clientToken: window.REACT_APP_DATADOG_CLIENT_TOKEN ?? process.env.REACT_APP_DATADOG_CLIENT_TOKEN, + site: window.REACT_APP_DATADOG_SITE ?? process.env.REACT_APP_DATADOG_SITE, + service: window.REACT_APP_DATADOG_SERVICE ?? process.env.REACT_APP_DATADOG_SERVICE, + }, + sentryDsn: window.REACT_APP_SENTRY_DSN ?? process.env.REACT_APP_SENTRY_DSN, + webappTag: window.REACT_APP_WEBAPP_TAG ?? process.env.REACT_APP_WEBAPP_TAG ?? "dev", +}; + +export class MissingConfigError extends Error { + constructor(message: string) { + super(message); + this.name = "MissingConfigError"; + } +} diff --git a/airbyte-webapp/src/config/configProviders.test.ts b/airbyte-webapp/src/config/configProviders.test.ts deleted file mode 100644 index 92c20457ace57..0000000000000 --- a/airbyte-webapp/src/config/configProviders.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { applyProviders } from "./configProviders"; -import { DeepPartial, ProviderAsync } from "./types"; - -interface Value { - prop1: { - innerProp: string; - innerProp2: string; - }; - prop2: { - innerProp: string; - innerProp2: string; - }; - prop3: { - innerProp: string; - }; -} -describe("applyProviders", () => { - it("should deepMerge config returned from providers", async () => { - const defaultValue: Value = { - prop1: { - innerProp: "Alex", - innerProp2: "Phil", - }, - prop2: { - innerProp: "Alex", - innerProp2: "Phil", - }, - prop3: { - innerProp: "1", - }, - }; - const providers: Array>> = [ - async () => ({ - prop1: { - innerProp: "John", - }, - prop2: { - innerProp: "Tom", - }, - }), - ]; - - const result = await applyProviders(defaultValue, providers); - expect(result).toEqual({ - prop1: { - innerProp: "John", - innerProp2: "Phil", - }, - prop2: { - innerProp: "Tom", - innerProp2: "Phil", - }, - prop3: { - innerProp: "1", - }, - }); - }); -}); diff --git a/airbyte-webapp/src/config/configProviders.ts b/airbyte-webapp/src/config/configProviders.ts deleted file mode 100644 index 8fcdf4887c97d..0000000000000 --- a/airbyte-webapp/src/config/configProviders.ts +++ /dev/null @@ -1,45 +0,0 @@ -import merge from "lodash/merge"; - -import { isDefined } from "utils/common"; - -import { ConfigProvider, DeepPartial, ValueProvider } from "./types"; - -const windowConfigProvider: ConfigProvider = async () => { - return { - segment: { - enabled: isDefined(window.TRACKING_STRATEGY) ? window.TRACKING_STRATEGY === "segment" : undefined, - token: window.SEGMENT_TOKEN, - }, - apiUrl: window.API_URL, - connectorBuilderApiUrl: window.CONNECTOR_BUILDER_API_URL, - version: window.AIRBYTE_VERSION, - // cloud only start - // TODO: remove when infra team supports proper webapp building - cloud: window.CLOUD === "true", - // cloud only end - }; -}; - -const envConfigProvider: ConfigProvider = async () => { - return { - apiUrl: process.env.REACT_APP_API_URL, - integrationUrl: process.env.REACT_APP_INTEGRATION_DOCS_URLS, - segment: { - token: process.env.REACT_APP_SEGMENT_TOKEN, - }, - }; -}; - -async function applyProviders(defaultValue: T, providers: ValueProvider): Promise { - let value: DeepPartial = {}; - - for (const provider of providers) { - const partialConfig = await provider(); - - value = merge(value, partialConfig); - } - - return merge(defaultValue, value); -} - -export { windowConfigProvider, envConfigProvider, applyProviders }; diff --git a/airbyte-webapp/src/config/defaultConfig.ts b/airbyte-webapp/src/config/defaultConfig.ts deleted file mode 100644 index d4c08aff96c24..0000000000000 --- a/airbyte-webapp/src/config/defaultConfig.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Config } from "./types"; - -const defaultConfig: Config = { - segment: { enabled: true, token: "" }, - healthCheckInterval: 20000, - version: "dev", - apiUrl: `http://${window.location.hostname}:8001/api`, - connectorBuilderApiUrl: `http://${window.location.hostname}:8003`, - integrationUrl: "/docs", - oauthRedirectUrl: `${window.location.protocol}//${window.location.host}`, -}; - -export { defaultConfig }; diff --git a/airbyte-webapp/src/config/index.ts b/airbyte-webapp/src/config/index.ts index 0e5fb6d34791f..87a0212e66dc4 100644 --- a/airbyte-webapp/src/config/index.ts +++ b/airbyte-webapp/src/config/index.ts @@ -1,4 +1,3 @@ -export * from "./defaultConfig"; -export * from "./configProviders"; +export * from "./config"; export * from "./ConfigServiceProvider"; export * from "./types"; diff --git a/airbyte-webapp/src/config/types.ts b/airbyte-webapp/src/config/types.ts index 514d4b5ff917e..bff0c157ef04a 100644 --- a/airbyte-webapp/src/config/types.ts +++ b/airbyte-webapp/src/config/types.ts @@ -5,37 +5,50 @@ declare global { API_URL?: string; CONNECTOR_BUILDER_API_URL?: string; CLOUD?: string; - REACT_APP_DATADOG_APPLICATION_ID: string; - REACT_APP_DATADOG_CLIENT_TOKEN: string; - REACT_APP_DATADOG_SITE: string; - REACT_APP_DATADOG_SERVICE: string; - REACT_APP_SENTRY_DSN?: string; + REACT_APP_DATADOG_APPLICATION_ID?: string; + REACT_APP_DATADOG_CLIENT_TOKEN?: string; + REACT_APP_DATADOG_SITE?: string; + REACT_APP_DATADOG_SERVICE?: string; REACT_APP_WEBAPP_TAG?: string; REACT_APP_INTERCOM_APP_ID?: string; REACT_APP_INTEGRATION_DOCS_URLS?: string; SEGMENT_TOKEN?: string; LAUNCHDARKLY_KEY?: string; + // Cloud specific properties + FIREBASE_API_KEY?: string; + FIREBASE_AUTH_DOMAIN?: string; + FIREBASE_AUTH_EMULATOR_HOST?: string; + CLOUD_API_URL?: string; + CLOUD_PUBLIC_API_URL?: string; + REACT_APP_SENTRY_DSN?: string; } } -export interface Config { - segment: { token: string; enabled: boolean }; +export interface AirbyteWebappConfig { + segment: { token?: string; enabled: boolean }; apiUrl: string; connectorBuilderApiUrl: string; - oauthRedirectUrl: string; - healthCheckInterval: number; - version?: string; + version: string; integrationUrl: string; + oauthRedirectUrl: string; + cloudApiUrl?: string; + cloudPublicApiUrl?: string; + firebase: { + apiKey?: string; + authDomain?: string; + authEmulatorHost?: string; + }; + intercom: { + appId?: string; + }; launchDarkly?: string; + datadog: { + applicationId?: string; + clientToken?: string; + site?: string; + service?: string; + tag?: string; + }; + webappTag?: string; + sentryDsn?: string; } - -export type DeepPartial = { - [P in keyof T]+?: DeepPartial; -}; - -export type ProviderAsync = () => Promise; -export type Provider = () => T; - -export type ValueProvider = Array>>; - -export type ConfigProvider = ProviderAsync>; diff --git a/airbyte-webapp/src/core/request/apiOverride.ts b/airbyte-webapp/src/core/request/apiOverride.ts index 79332ecf6c7b8..0d663a258d978 100644 --- a/airbyte-webapp/src/core/request/apiOverride.ts +++ b/airbyte-webapp/src/core/request/apiOverride.ts @@ -1,10 +1,10 @@ import { CommonRequestError } from "./CommonRequestError"; import { RequestMiddleware } from "./RequestMiddleware"; import { VersionError } from "./VersionError"; -import { Config } from "../../config"; +import { AirbyteWebappConfig } from "../../config"; export interface ApiOverrideRequestOptions { - config: Pick; + config: Pick; middlewares: RequestMiddleware[]; signal?: RequestInit["signal"]; } @@ -15,7 +15,6 @@ function getRequestBody(data: U) { if (nonJsonObject) { // The app tries to stringify blobs which results in broken functionality. // There may be some edge cases where we pass in an empty object. - // @ts-expect-error There may be a better way to do this, but for now it solves the problem. return data as BodyInit; } return stringifiedData; diff --git a/airbyte-webapp/src/hooks/services/Health/HealthPollService.tsx b/airbyte-webapp/src/hooks/services/Health/HealthPollService.tsx index 000714fc210d6..df6c84a34deef 100644 --- a/airbyte-webapp/src/hooks/services/Health/HealthPollService.tsx +++ b/airbyte-webapp/src/hooks/services/Health/HealthPollService.tsx @@ -1,7 +1,6 @@ import { useEffect, useState } from "react"; import { useIntl } from "react-intl"; -import { useConfig } from "config"; import { HealthService } from "core/health/HealthService"; import { useGetService } from "core/servicesProvider"; import { useNotificationService } from "hooks/services/Notification/NotificationService"; @@ -11,11 +10,11 @@ import { Notification } from "../Notification"; const HEALTH_NOTIFICATION_ID = "health.error"; const HEALTHCHECK_MAX_COUNT = 3; +const HEALTHCHECK_INTERVAL = 20000; function useApiHealthPoll(): void { const [count, setCount] = useState(0); const { formatMessage } = useIntl(); - const { healthCheckInterval } = useConfig(); const healthService = useGetService("HealthService"); const { registerNotification, unregisterNotificationById } = useNotificationService(); @@ -40,10 +39,10 @@ function useApiHealthPoll(): void { registerNotification(errorNotification); } } - }, healthCheckInterval); + }, HEALTHCHECK_INTERVAL); return () => clearInterval(interval); - }, [count, healthCheckInterval, formatMessage, unregisterNotificationById, registerNotification, healthService]); + }, [count, formatMessage, unregisterNotificationById, registerNotification, healthService]); } export { useApiHealthPoll }; diff --git a/airbyte-webapp/src/packages/cloud/App.tsx b/airbyte-webapp/src/packages/cloud/App.tsx index f8027f3ff9dbd..545f5c9fc48dc 100644 --- a/airbyte-webapp/src/packages/cloud/App.tsx +++ b/airbyte-webapp/src/packages/cloud/App.tsx @@ -6,6 +6,7 @@ import { ThemeProvider } from "styled-components"; import { ApiErrorBoundary } from "components/common/ApiErrorBoundary"; import LoadingPage from "components/LoadingPage"; +import { ConfigServiceProvider, config } from "config"; import { I18nProvider } from "core/i18n"; import { AppMonitoringServiceProvider } from "hooks/services/AppMonitoringService"; import { ConfirmationModalService } from "hooks/services/ConfirmationModal"; @@ -22,7 +23,6 @@ import { AnalyticsProvider } from "views/common/AnalyticsProvider"; import { StoreProvider } from "views/common/StoreProvider"; import { AppServicesProvider } from "./services/AppServicesProvider"; -import { ConfigProvider } from "./services/ConfigProvider"; import { IntercomProvider } from "./services/thirdParty/intercom/IntercomProvider"; const messages = { ...en, ...cloudLocales }; @@ -64,13 +64,13 @@ const App: React.FC = () => { }> - + - + diff --git a/airbyte-webapp/src/packages/cloud/components/experiments/FreeConnectorProgram/hooks/useFreeConnectorProgram.ts b/airbyte-webapp/src/packages/cloud/components/experiments/FreeConnectorProgram/hooks/useFreeConnectorProgram.ts index 2a2cdbe460d4e..9afc925ea1b3e 100644 --- a/airbyte-webapp/src/packages/cloud/components/experiments/FreeConnectorProgram/hooks/useFreeConnectorProgram.ts +++ b/airbyte-webapp/src/packages/cloud/components/experiments/FreeConnectorProgram/hooks/useFreeConnectorProgram.ts @@ -6,9 +6,9 @@ import { useEffectOnce } from "react-use"; import { ToastType } from "components/ui/Toast"; +import { MissingConfigError, useConfig } from "config"; import { useExperiment } from "hooks/services/Experiment"; import { useNotificationService } from "hooks/services/Notification"; -import { useConfig } from "packages/cloud/services/config"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; import { useCurrentWorkspaceId } from "services/workspaces/WorkspacesService"; @@ -19,6 +19,9 @@ export const STRIPE_SUCCESS_QUERY = "fcpEnrollmentSuccess"; export const useFreeConnectorProgram = () => { const workspaceId = useCurrentWorkspaceId(); const { cloudApiUrl } = useConfig(); + if (!cloudApiUrl) { + throw new MissingConfigError("Missing required configuration cloudApiUrl"); + } const config = { apiUrl: cloudApiUrl }; const middlewares = useDefaultRequestMiddlewares(); const requestOptions = { config, middlewares }; diff --git a/airbyte-webapp/src/packages/cloud/lib/auth/GoogleAuthService.ts b/airbyte-webapp/src/packages/cloud/lib/auth/GoogleAuthService.ts index df0f42e91754c..7818b8e9e0840 100644 --- a/airbyte-webapp/src/packages/cloud/lib/auth/GoogleAuthService.ts +++ b/airbyte-webapp/src/packages/cloud/lib/auth/GoogleAuthService.ts @@ -24,12 +24,11 @@ import { reload, } from "firebase/auth"; -import { Provider } from "config"; import { FieldError } from "packages/cloud/lib/errors/FieldError"; import { EmailLinkErrorCodes, ErrorCodes } from "packages/cloud/services/auth/types"; export class GoogleAuthService { - constructor(private firebaseAuthProvider: Provider) {} + constructor(private firebaseAuthProvider: () => Auth) {} get auth(): Auth { return this.firebaseAuthProvider(); diff --git a/airbyte-webapp/src/packages/cloud/services/AppServicesProvider.tsx b/airbyte-webapp/src/packages/cloud/services/AppServicesProvider.tsx index 8d5ffad134423..a36fe93420e48 100644 --- a/airbyte-webapp/src/packages/cloud/services/AppServicesProvider.tsx +++ b/airbyte-webapp/src/packages/cloud/services/AppServicesProvider.tsx @@ -2,6 +2,7 @@ import React, { useMemo } from "react"; import { LoadingPage } from "components"; +import { MissingConfigError, useConfig } from "config"; import { ApiServices } from "core/ApiServices"; import { RequestMiddleware } from "core/request/RequestMiddleware"; import { ServicesProvider, useGetService, useInjectServices } from "core/servicesProvider"; @@ -9,7 +10,6 @@ import { RequestAuthMiddleware } from "packages/cloud/lib/auth/RequestAuthMiddle import { UserService } from "packages/cloud/lib/domain/users"; import { useAuth } from "packages/firebaseReact"; -import { useConfig } from "./config"; import { FirebaseSdkProvider } from "./FirebaseSdkProvider"; /** @@ -43,6 +43,10 @@ const ServiceOverrides: React.FC> = React.memo( const { cloudApiUrl } = useConfig(); + if (!cloudApiUrl) { + throw new MissingConfigError("Missing required configuration cloudApiUrl"); + } + const inject = useMemo( () => ({ UserService: new UserService(cloudApiUrl, middlewares), diff --git a/airbyte-webapp/src/packages/cloud/services/ConfigProvider.tsx b/airbyte-webapp/src/packages/cloud/services/ConfigProvider.tsx deleted file mode 100644 index 9360e343b5856..0000000000000 --- a/airbyte-webapp/src/packages/cloud/services/ConfigProvider.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; - -import { Config, ConfigServiceProvider, ValueProvider, envConfigProvider, windowConfigProvider } from "config"; - -import { - cloudEnvConfigProvider, - // fileConfigProvider, - defaultConfig, - cloudWindowConfigProvider, -} from "./config"; - -const configProviders: ValueProvider = [ - // fileConfigProvider, - cloudEnvConfigProvider, - cloudWindowConfigProvider, - envConfigProvider, - windowConfigProvider, -]; - -/** - * This Provider is responsible for injecting config in context and loading - * all required subconfigs if necessary - */ -const ConfigProvider: React.FC> = ({ children }) => ( - - {children} - -); - -export { ConfigProvider }; diff --git a/airbyte-webapp/src/packages/cloud/services/FirebaseSdkProvider.tsx b/airbyte-webapp/src/packages/cloud/services/FirebaseSdkProvider.tsx index 5c0c5400973cd..332bc0e0c4159 100644 --- a/airbyte-webapp/src/packages/cloud/services/FirebaseSdkProvider.tsx +++ b/airbyte-webapp/src/packages/cloud/services/FirebaseSdkProvider.tsx @@ -1,7 +1,7 @@ import { getAuth, connectAuthEmulator } from "firebase/auth"; import React from "react"; -import { useConfig } from "packages/cloud/services/config"; +import { useConfig } from "config"; import { FirebaseAppProvider, useFirebaseApp, AuthProvider } from "packages/firebaseReact"; const FirebaseAppSdksProvider: React.FC> = ({ children }) => { diff --git a/airbyte-webapp/src/packages/cloud/services/config/configProviders.ts b/airbyte-webapp/src/packages/cloud/services/config/configProviders.ts deleted file mode 100644 index 1a08b895fc967..0000000000000 --- a/airbyte-webapp/src/packages/cloud/services/config/configProviders.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { CloudConfig } from "./types"; - -import type { ConfigProvider } from "config/types"; - -const CONFIG_PATH = "/config.json"; - -const fileConfigProvider: ConfigProvider = async () => { - const response = await fetch(CONFIG_PATH); - - if (response.ok) { - try { - const config = await response.json(); - - return config; - } catch (e) { - console.error("error occurred while parsing the json config"); - return {}; - } - } - - return {}; -}; - -const cloudWindowConfigProvider: ConfigProvider = async () => { - return { - intercom: { - appId: window.REACT_APP_INTERCOM_APP_ID, - }, - firebase: { - apiKey: window.FIREBASE_API_KEY, - authDomain: window.FIREBASE_AUTH_DOMAIN, - authEmulatorHost: window.FIREBASE_AUTH_EMULATOR_HOST, - }, - cloudApiUrl: window.CLOUD_API_URL, - launchDarkly: window.LAUNCHDARKLY_KEY, - }; -}; - -const cloudEnvConfigProvider: ConfigProvider = async () => { - return { - cloudApiUrl: process.env.REACT_APP_CLOUD_API_URL, - cloudPublicApiUrl: process.env.REACT_APP_CLOUD_PUBLIC_API_URL, - firebase: { - apiKey: process.env.REACT_APP_FIREBASE_API_KEY, - authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN, - authEmulatorHost: process.env.REACT_APP_FIREBASE_AUTH_EMULATOR_HOST, - }, - intercom: { - appId: process.env.REACT_APP_INTERCOM_APP_ID, - }, - launchDarkly: process.env.REACT_APP_LAUNCHDARKLY_KEY, - }; -}; - -export { fileConfigProvider, cloudWindowConfigProvider, cloudEnvConfigProvider }; diff --git a/airbyte-webapp/src/packages/cloud/services/config/index.ts b/airbyte-webapp/src/packages/cloud/services/config/index.ts deleted file mode 100644 index 1063c3d68a715..0000000000000 --- a/airbyte-webapp/src/packages/cloud/services/config/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { defaultConfig as coreDefaultConfig, useConfig as useCoreConfig } from "config"; - -import { CloudConfig, CloudConfigExtension } from "./types"; - -export function useConfig(): CloudConfig { - return useCoreConfig(); -} - -const cloudConfigExtensionDefault: CloudConfigExtension = { - cloudApiUrl: "", - cloudPublicApiUrl: "/cloud_api", - firebase: { - apiKey: "", - authDomain: "", - authEmulatorHost: "", - }, - intercom: { - appId: "", - }, -}; - -export const defaultConfig: CloudConfig = { - ...coreDefaultConfig, - ...cloudConfigExtensionDefault, -}; - -export * from "./configProviders"; -export * from "./types"; diff --git a/airbyte-webapp/src/packages/cloud/services/config/types.ts b/airbyte-webapp/src/packages/cloud/services/config/types.ts deleted file mode 100644 index 990b50f5b7f58..0000000000000 --- a/airbyte-webapp/src/packages/cloud/services/config/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Config } from "config"; - -declare global { - interface Window { - // Cloud specific params that should be moved to cloud repo - FIREBASE_API_KEY?: string; - FIREBASE_AUTH_DOMAIN?: string; - FIREBASE_AUTH_EMULATOR_HOST?: string; - CLOUD_API_URL?: string; - CLOUD_PUBLIC_API_URL?: string; - } -} - -export interface CloudConfigExtension { - cloudApiUrl: string; - cloudPublicApiUrl: string; - firebase: { - apiKey: string; - authDomain: string; - authEmulatorHost: string; - }; - intercom: { - appId: string; - }; -} - -export type CloudConfig = Config & CloudConfigExtension; diff --git a/airbyte-webapp/src/packages/cloud/services/dbtCloud.ts b/airbyte-webapp/src/packages/cloud/services/dbtCloud.ts index 9a816e0793e84..6aa828468fcfd 100644 --- a/airbyte-webapp/src/packages/cloud/services/dbtCloud.ts +++ b/airbyte-webapp/src/packages/cloud/services/dbtCloud.ts @@ -11,6 +11,7 @@ import isEmpty from "lodash/isEmpty"; import { useMutation, useQuery } from "react-query"; +import { MissingConfigError, useConfig } from "config"; import { OperatorType, WebBackendConnectionRead, @@ -29,8 +30,6 @@ import { import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; import { useUpdateWorkspace } from "services/workspaces/WorkspacesService"; -import { useConfig } from "./config"; - export interface DbtCloudJob { accountId: number; jobId: number; @@ -135,6 +134,10 @@ export const useDbtIntegration = (connection: WebBackendConnectionRead) => { export const useAvailableDbtJobs = () => { const { cloudApiUrl } = useConfig(); + if (!cloudApiUrl) { + throw new MissingConfigError("Missing required configuration cloudApiUrl"); + } + const config = { apiUrl: cloudApiUrl }; const middlewares = useDefaultRequestMiddlewares(); const requestOptions = { config, middlewares }; diff --git a/airbyte-webapp/src/packages/cloud/services/geographies/GeographiesService.ts b/airbyte-webapp/src/packages/cloud/services/geographies/GeographiesService.ts index 1ea3010085a25..749374b43e144 100644 --- a/airbyte-webapp/src/packages/cloud/services/geographies/GeographiesService.ts +++ b/airbyte-webapp/src/packages/cloud/services/geographies/GeographiesService.ts @@ -1,6 +1,6 @@ +import { useConfig } from "config"; import { WebBackendGeographiesListResult } from "core/request/AirbyteClient"; import { GeographiesService } from "packages/cloud/lib/domain/geographies/GeographiesService"; -import { useConfig } from "packages/cloud/services/config"; import { useSuspenseQuery } from "services/connector/useSuspenseQuery"; import { SCOPE_USER } from "services/Scope"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; diff --git a/airbyte-webapp/src/packages/cloud/services/speakeasy/useSpeakeasyRedirect.ts b/airbyte-webapp/src/packages/cloud/services/speakeasy/useSpeakeasyRedirect.ts index d38a7a71fd6ab..3b199409c0ece 100644 --- a/airbyte-webapp/src/packages/cloud/services/speakeasy/useSpeakeasyRedirect.ts +++ b/airbyte-webapp/src/packages/cloud/services/speakeasy/useSpeakeasyRedirect.ts @@ -1,13 +1,17 @@ +import { MissingConfigError, useConfig } from "config"; import { getSpeakeasyCallbackUrl } from "packages/cloud/lib/domain/speakeasy"; import { useSuspenseQuery } from "services/connector/useSuspenseQuery"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; -import { useConfig } from "../config"; - const SPEAKEASY_QUERY_KEY = "speakeasy-redirect"; export const useSpeakeasyRedirect = () => { const { cloudPublicApiUrl } = useConfig(); + + if (!cloudPublicApiUrl) { + throw new MissingConfigError("Missing required configuration cloudPublicApiUrl"); + } + const config = { apiUrl: cloudPublicApiUrl }; const middlewares = useDefaultRequestMiddlewares(); const requestOptions = { config, middlewares }; diff --git a/airbyte-webapp/src/packages/cloud/services/stripe/StripeService.tsx b/airbyte-webapp/src/packages/cloud/services/stripe/StripeService.tsx index c8f2710e83583..2e65a78bab816 100644 --- a/airbyte-webapp/src/packages/cloud/services/stripe/StripeService.tsx +++ b/airbyte-webapp/src/packages/cloud/services/stripe/StripeService.tsx @@ -1,14 +1,18 @@ import { useMemo } from "react"; import { useMutation } from "react-query"; +import { useConfig, MissingConfigError } from "config"; import { StripeCheckoutSessionCreate, StripeService } from "packages/cloud/lib/domain/stripe"; -import { useConfig } from "packages/cloud/services/config"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; export function useStripeService(): StripeService { const requestAuthMiddleware = useDefaultRequestMiddlewares(); const { cloudApiUrl } = useConfig(); + if (!cloudApiUrl) { + throw new MissingConfigError("Missing required configuration cloudApiUrl"); + } + return useMemo(() => new StripeService(cloudApiUrl, requestAuthMiddleware), [cloudApiUrl, requestAuthMiddleware]); } diff --git a/airbyte-webapp/src/packages/cloud/services/thirdParty/intercom/IntercomProvider.tsx b/airbyte-webapp/src/packages/cloud/services/thirdParty/intercom/IntercomProvider.tsx index b9dbc9ade99c3..fa1784c861f8e 100644 --- a/airbyte-webapp/src/packages/cloud/services/thirdParty/intercom/IntercomProvider.tsx +++ b/airbyte-webapp/src/packages/cloud/services/thirdParty/intercom/IntercomProvider.tsx @@ -1,12 +1,15 @@ import React from "react"; import { IntercomProvider as IntercomProviderCore } from "react-use-intercom"; -import { useConfig } from "packages/cloud/services/config"; +import { MissingConfigError, useConfig } from "config"; const IntercomProvider: React.FC> = ({ children }) => { - const config = useConfig(); + const { intercom } = useConfig(); - return {children}; + if (!intercom.appId) { + throw new MissingConfigError("Missing required configuration intercom.appId"); + } + return {children}; }; export { IntercomProvider }; diff --git a/airbyte-webapp/src/packages/cloud/services/users/UserService.tsx b/airbyte-webapp/src/packages/cloud/services/users/UserService.tsx index 7833f6708da3c..94f6091c4fb48 100644 --- a/airbyte-webapp/src/packages/cloud/services/users/UserService.tsx +++ b/airbyte-webapp/src/packages/cloud/services/users/UserService.tsx @@ -1,12 +1,15 @@ import { useMemo } from "react"; +import { MissingConfigError, useConfig } from "config"; import { UserService } from "packages/cloud/lib/domain/users"; -import { useConfig } from "packages/cloud/services/config"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; export function useGetUserService(): UserService { const requestAuthMiddleware = useDefaultRequestMiddlewares(); const { cloudApiUrl } = useConfig(); + if (!cloudApiUrl) { + throw new MissingConfigError("Missing required configuration cloudApiUrl"); + } return useMemo(() => new UserService(cloudApiUrl, requestAuthMiddleware), [cloudApiUrl, requestAuthMiddleware]); } diff --git a/airbyte-webapp/src/packages/cloud/services/workspaces/CloudWorkspacesService.ts b/airbyte-webapp/src/packages/cloud/services/workspaces/CloudWorkspacesService.ts index c35342bb3db26..d45c0c5bc609b 100644 --- a/airbyte-webapp/src/packages/cloud/services/workspaces/CloudWorkspacesService.ts +++ b/airbyte-webapp/src/packages/cloud/services/workspaces/CloudWorkspacesService.ts @@ -1,10 +1,10 @@ import { useCallback } from "react"; import { QueryObserverResult, useMutation, useQuery, useQueryClient } from "react-query"; +import { MissingConfigError, useConfig } from "config"; import { CloudWorkspacesService } from "packages/cloud/lib/domain/cloudWorkspaces/CloudWorkspacesService"; import { CloudWorkspace, CloudWorkspaceUsage } from "packages/cloud/lib/domain/cloudWorkspaces/types"; import { useCurrentUser } from "packages/cloud/services/auth/AuthService"; -import { useConfig } from "packages/cloud/services/config"; import { useSuspenseQuery } from "services/connector/useSuspenseQuery"; import { SCOPE_USER } from "services/Scope"; import { useDefaultRequestMiddlewares } from "services/useDefaultRequestMiddlewares"; @@ -22,6 +22,10 @@ export const workspaceKeys = { function useGetWorkspaceService(): CloudWorkspacesService { const { cloudApiUrl } = useConfig(); + if (!cloudApiUrl) { + throw new MissingConfigError("Missing required configuration cloudApiUrl"); + } + const requestAuthMiddleware = useDefaultRequestMiddlewares(); return useInitService( diff --git a/airbyte-webapp/src/packages/cloud/views/auth/components/GitBlock/GitBlock.test.tsx b/airbyte-webapp/src/packages/cloud/views/auth/components/GitBlock/GitBlock.test.tsx index a028c175f766b..b6397f66e6a97 100644 --- a/airbyte-webapp/src/packages/cloud/views/auth/components/GitBlock/GitBlock.test.tsx +++ b/airbyte-webapp/src/packages/cloud/views/auth/components/GitBlock/GitBlock.test.tsx @@ -1,7 +1,7 @@ import { render } from "@testing-library/react"; import { IntlProvider } from "react-intl"; -import { ConfigContext, defaultConfig } from "config"; +import { ConfigContext, config } from "config"; import { GitBlock, GitBlockProps } from "./GitBlock"; import en from "../../../../locales/en.json"; @@ -9,7 +9,7 @@ import en from "../../../../locales/en.json"; const renderGitBlock = (props?: GitBlockProps) => render( - + diff --git a/airbyte-webapp/src/pages/connections/ConnectionSettingsPage/ConnectionSettingsPage.module.scss b/airbyte-webapp/src/pages/connections/ConnectionSettingsPage/ConnectionSettingsPage.module.scss index 8e67e05a82c98..3f3a63c771851 100644 --- a/airbyte-webapp/src/pages/connections/ConnectionSettingsPage/ConnectionSettingsPage.module.scss +++ b/airbyte-webapp/src/pages/connections/ConnectionSettingsPage/ConnectionSettingsPage.module.scss @@ -12,4 +12,4 @@ .advancedPanel { margin-top: variables.$spacing-sm; -} \ No newline at end of file +} diff --git a/airbyte-webapp/src/test-utils/testutils.tsx b/airbyte-webapp/src/test-utils/testutils.tsx index 8bb11118f9da6..22e229d268ef2 100644 --- a/airbyte-webapp/src/test-utils/testutils.tsx +++ b/airbyte-webapp/src/test-utils/testutils.tsx @@ -5,7 +5,7 @@ import { QueryClient, QueryClientProvider } from "react-query"; import { MemoryRouter } from "react-router-dom"; import { ThemeProvider } from "styled-components"; -import { ConfigContext, defaultConfig } from "config"; +import { ConfigContext, config } from "config"; import { ConnectionStatus, DestinationRead, @@ -54,7 +54,7 @@ export const TestWrapper: React.FC> }) => ( null}> - + diff --git a/airbyte-webapp/src/utils/datadog.ts b/airbyte-webapp/src/utils/datadog.ts index 4fa03ed735001..c825a056a87fa 100644 --- a/airbyte-webapp/src/utils/datadog.ts +++ b/airbyte-webapp/src/utils/datadog.ts @@ -1,21 +1,23 @@ import { datadogRum } from "@datadog/browser-rum"; + +import { config } from "config"; + export const loadDatadog = (): void => { - const applicationId = window.REACT_APP_DATADOG_APPLICATION_ID ?? process.env.REACT_APP_DATADOG_APPLICATION_ID; - if (!applicationId) { + const { + webappTag, + datadog: { applicationId, clientToken, site, service }, + } = config; + + if (!applicationId || !clientToken) { return; } - const clientToken = window.REACT_APP_DATADOG_CLIENT_TOKEN ?? process.env.REACT_APP_DATADOG_CLIENT_TOKEN; - const site = window.REACT_APP_DATADOG_SITE ?? process.env.REACT_APP_DATADOG_SITE; - const service = window.REACT_APP_DATADOG_SERVICE ?? process.env.REACT_APP_DATADOG_SERVICE; - const version = window.REACT_APP_WEBAPP_TAG ?? process.env.REACT_APP_WEBAPP_TAG ?? "dev"; - datadogRum.init({ applicationId, clientToken, site, service, - version, + version: webappTag, sampleRate: 100, sessionReplaySampleRate: 0, trackInteractions: false, diff --git a/airbyte-webapp/src/utils/sentry.ts b/airbyte-webapp/src/utils/sentry.ts index d8f63339f019d..c5c04bc231836 100644 --- a/airbyte-webapp/src/utils/sentry.ts +++ b/airbyte-webapp/src/utils/sentry.ts @@ -1,18 +1,19 @@ import * as Sentry from "@sentry/react"; import { Integrations } from "@sentry/tracing"; +import { config } from "config"; + export const loadSentry = (): void => { - const dsn = window.REACT_APP_SENTRY_DSN ?? process.env.REACT_APP_SENTRY_DSN; - if (!dsn) { + const { sentryDsn, webappTag } = config; + if (!sentryDsn) { return; } - const release = window.REACT_APP_WEBAPP_TAG ?? process.env.REACT_APP_WEBAPP_TAG ?? "dev"; const integrations = [new Integrations.BrowserTracing()]; Sentry.init({ - dsn, - release, + dsn: sentryDsn, + release: webappTag, integrations, tracesSampleRate: 1.0, });