Skip to content

chore: remove circular dependencies #17585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 28, 2025
4 changes: 3 additions & 1 deletion site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"dev": "vite",
"format": "biome format --write .",
"format:check": "biome format .",
"lint": "pnpm run lint:check && pnpm run lint:types",
"lint": "pnpm run lint:check && pnpm run lint:types && pnpm run lint:circular-deps",
"lint:check": " biome lint --error-on-warnings .",
"lint:circular-deps": "dpdm --no-tree --no-warning -T ./src/App.tsx",
"lint:fix": " biome lint --error-on-warnings --write .",
"lint:types": "tsc -p .",
"playwright:install": "playwright install --with-deps chromium",
Expand Down Expand Up @@ -171,6 +172,7 @@
"@vitejs/plugin-react": "4.3.4",
"autoprefixer": "10.4.20",
"chromatic": "11.25.2",
"dpdm": "3.14.0",
"express": "4.21.2",
"jest": "29.7.0",
"jest-canvas-mock": "2.5.2",
Expand Down
97 changes: 97 additions & 0 deletions site/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion site/src/components/Filter/UserFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type SelectFilterOption,
SelectFilterSearch,
} from "components/Filter/SelectFilter";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import type { FC } from "react";
import { type UseFilterMenuOptions, useFilterMenu } from "./menu";

Expand Down
2 changes: 1 addition & 1 deletion site/src/contexts/ProxyContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { API } from "api/api";
import { cachedQuery } from "api/queries/util";
import type { Region, WorkspaceProxy } from "api/typesGenerated";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import {
type FC,
Expand Down
2 changes: 1 addition & 1 deletion site/src/contexts/auth/RequireAuth.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { renderHook, screen } from "@testing-library/react";
import { useAuthenticated } from "hooks";
import { http, HttpResponse } from "msw";
import type { FC, PropsWithChildren } from "react";
import { QueryClientProvider } from "react-query";
Expand All @@ -9,7 +10,6 @@ import {
} from "testHelpers/renderHelpers";
import { server } from "testHelpers/server";
import { AuthContext, type AuthContextValue } from "./AuthProvider";
import { useAuthenticated } from "./RequireAuth";

describe("RequireAuth", () => {
it("redirects to /login if user is not authenticated", async () => {
Expand Down
27 changes: 1 addition & 26 deletions site/src/contexts/auth/RequireAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { DashboardProvider as ProductionDashboardProvider } from "modules/dashbo
import { type FC, useEffect } from "react";
import { Navigate, Outlet, useLocation } from "react-router-dom";
import { embedRedirect } from "utils/redirect";
import { type AuthContextValue, useAuthContext } from "./AuthProvider";
import { useAuthContext } from "./AuthProvider";

type RequireAuthProps = Readonly<{
ProxyProvider?: typeof ProductionProxyProvider;
Expand Down Expand Up @@ -81,28 +81,3 @@ export const RequireAuth: FC<RequireAuthProps> = ({
</DashboardProvider>
);
};

type RequireKeys<T, R extends keyof T> = Omit<T, R> & {
[K in keyof Pick<T, R>]-?: NonNullable<T[K]>;
};

// We can do some TS magic here but I would rather to be explicit on what
// values are not undefined when authenticated
type AuthenticatedAuthContextValue = RequireKeys<
AuthContextValue,
"user" | "permissions"
>;

export const useAuthenticated = (): AuthenticatedAuthContextValue => {
const auth = useAuthContext();

if (!auth.user) {
throw new Error("User is not authenticated.");
}

if (!auth.permissions) {
throw new Error("Permissions are not available.");
}

return auth as AuthenticatedAuthContextValue;
};
1 change: 1 addition & 0 deletions site/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./useAuthenticated";
export * from "./useClickable";
export * from "./useClickableTableRow";
export * from "./useClipboard";
Expand Down
29 changes: 29 additions & 0 deletions site/src/hooks/useAuthenticated.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
type AuthContextValue,
useAuthContext,
} from "contexts/auth/AuthProvider";

type RequireKeys<T, R extends keyof T> = Omit<T, R> & {
[K in keyof Pick<T, R>]-?: NonNullable<T[K]>;
};

// We can do some TS magic here but I would rather to be explicit on what
// values are not undefined when authenticated
type AuthenticatedAuthContextValue = RequireKeys<
AuthContextValue,
"user" | "permissions"
>;

export const useAuthenticated = (): AuthenticatedAuthContextValue => {
const auth = useAuthContext();

if (!auth.user) {
throw new Error("User is not authenticated.");
}

if (!auth.permissions) {
throw new Error("Permissions are not available.");
}

return auth as AuthenticatedAuthContextValue;
};
2 changes: 1 addition & 1 deletion site/src/modules/dashboard/DashboardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Link from "@mui/material/Link";
import Snackbar from "@mui/material/Snackbar";
import { Button } from "components/Button/Button";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { AnnouncementBanners } from "modules/dashboard/AnnouncementBanners/AnnouncementBanners";
import { LicenseBanner } from "modules/dashboard/LicenseBanner/LicenseBanner";
import { type FC, type HTMLAttributes, Suspense } from "react";
Expand Down
2 changes: 1 addition & 1 deletion site/src/modules/dashboard/DashboardProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
} from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
import { canViewAnyOrganization } from "modules/permissions";
import { type FC, type PropsWithChildren, createContext } from "react";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { health } from "api/queries/debug";
import { deploymentStats } from "api/queries/deployment";
import { useAuthenticated } from "contexts/auth/RequireAuth";
import { useAuthenticated } from "hooks";
import type { FC } from "react";
import { useQuery } from "react-query";
import { DeploymentBannerView } from "./DeploymentBannerView";
Expand Down
Loading
Loading