-
Notifications
You must be signed in to change notification settings - Fork 6
Tbt/gateway status #1506
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
base: main
Are you sure you want to change the base?
Tbt/gateway status #1506
Changes from all commits
171b543
55ca372
e0a9afd
04e058f
a5541dc
eac959e
ac343fc
44a5848
1358727
1ffbce0
4b128ea
fec9d00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,9 @@ hooks = [ | |
| id = "prettier", | ||
| name = "prettier (charts yaml)", | ||
| language = "system", | ||
| entry = "npx --yes prettier@3.6.2 --write", | ||
| entry = "npx --cache /tmp/prek-npx-charts --yes prettier@3.6.2 --write", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this change needed? this directory isn't guaranteed to always available. |
||
| files = "^[^/]+/[^/]*\\.ya?ml$", | ||
| pass_filenames = true, | ||
| require_serial = true, | ||
| }, | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,7 @@ hooks = [ | |
| id = "prettier", | ||
| name = "prettier", | ||
| language = "system", | ||
| entry = "npx --yes prettier@3.6.2 --config .prettierrc --ignore-path ../.gitignore --check \"**/*.{js,ts,tsx,json}\"", | ||
| entry = "npx --cache /tmp/prek-npx-frontend --yes prettier@3.6.2 --config .prettierrc --ignore-path ../.gitignore --check \"**/*.{js,ts,tsx,json}\"", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this change needed? this directory isn't guaranteed to always available.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the CI for this was failing due to race conditions between charts/prek.toml and frontend/prek.toml. The cache is to use whichever came first |
||
| files = "\\.(js|ts|tsx|json)$", | ||
| pass_filenames = false, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 13 commits, could be squashed and cleaned up a little?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't say so - kept them separaed with changes in auth-core, auth-gateway in the backend. Charts for dashboard, along with frontend changes (component, story, test) |
||
| }, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CI is failing |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import CircleIcon from "@mui/icons-material/Circle"; | ||
| import { IconButton, Stack, Tooltip, Typography } from "@mui/material"; | ||
| import { useAuthStatus } from "../../hooks/useAuthStatus"; | ||
| import { buildLoginUrl } from "../../utils/authUtils"; | ||
|
|
||
| export interface AuthStatusIndicatorProps { | ||
| accessToken?: string; | ||
| /** Defaults to the current origin, where the gateway is served under `/auth`. */ | ||
| gatewayUrl?: string; | ||
| cacheTtlMs?: number; | ||
| size?: number; | ||
| returnTo?: string; | ||
| } | ||
|
|
||
| const AuthStatusIndicator = ({ | ||
|
TBThomas56 marked this conversation as resolved.
|
||
| accessToken, | ||
| gatewayUrl, | ||
| cacheTtlMs, | ||
| size = 20, | ||
| returnTo, | ||
| }: AuthStatusIndicatorProps) => { | ||
| const { authenticated } = useAuthStatus({ | ||
| accessToken, | ||
| gatewayUrl, | ||
| cacheTtlMs, | ||
| }); | ||
|
|
||
| const handleClick = () => { | ||
| if (authenticated) return; | ||
| window.open( | ||
| buildLoginUrl(returnTo, gatewayUrl), | ||
| "_blank", | ||
| "noopener,noreferrer", | ||
| ); | ||
| }; | ||
|
|
||
| const text = authenticated | ||
| ? "Workflows Authenticated" | ||
| : "Workflows Unauthenticated"; | ||
| const tooltip = authenticated ? text : `${text} — click to log in`; | ||
|
|
||
| return ( | ||
| <Tooltip title={tooltip}> | ||
| <IconButton | ||
| onClick={handleClick} | ||
| aria-label={tooltip} | ||
| data-testid="auth-status-indicator" | ||
| size="small" | ||
| disableRipple={authenticated} | ||
| sx={{ | ||
| cursor: authenticated ? "default" : "pointer", | ||
| border: "1px solid", | ||
| borderColor: "primary.main", | ||
| borderRadius: 1, | ||
| px: 1.5, | ||
| py: 0.5, | ||
| bgcolor: "primary.main", | ||
| }} | ||
| > | ||
| <Stack direction="row" spacing={1} alignItems="center"> | ||
| <CircleIcon | ||
| sx={{ | ||
| fontSize: size, | ||
| color: authenticated ? "success.main" : "grey.500", | ||
| }} | ||
| /> | ||
| <Typography | ||
| variant="h6" | ||
| fontWeight="bold" | ||
| sx={{ color: "common.white" }} | ||
| > | ||
| {text} | ||
| </Typography> | ||
| </Stack> | ||
| </IconButton> | ||
| </Tooltip> | ||
| ); | ||
| }; | ||
|
|
||
| export default AuthStatusIndicator; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| import { useEffect, useMemo, useState } from "react"; | ||
| import { | ||
| authStatusCacheKey, | ||
| fetchAuthStatus, | ||
| readAuthStatusCache, | ||
| writeAuthStatusCache, | ||
| } from "../utils/authUtils"; | ||
|
|
||
| export interface UseAuthStatusOptions { | ||
| accessToken?: string; | ||
| gatewayUrl?: string; | ||
| cacheTtlMs?: number; | ||
| } | ||
|
|
||
| export interface AuthStatus { | ||
| authenticated: boolean; | ||
| loading: boolean; | ||
| } | ||
|
|
||
| interface FetchedStatus { | ||
| cacheKey: string; | ||
| authenticated: boolean; | ||
| } | ||
|
|
||
| export function useAuthStatus({ | ||
| accessToken, | ||
| gatewayUrl, | ||
| cacheTtlMs = 30000, | ||
| }: UseAuthStatusOptions): AuthStatus { | ||
| const cacheKey = useMemo( | ||
| () => (accessToken ? authStatusCacheKey(accessToken, gatewayUrl) : null), | ||
| [accessToken, gatewayUrl], | ||
| ); | ||
|
|
||
| const known = useMemo( | ||
| () => (cacheKey ? readAuthStatusCache(cacheKey, cacheTtlMs) : false), | ||
| [cacheKey, cacheTtlMs], | ||
| ); | ||
|
|
||
| const [fetched, setFetched] = useState<FetchedStatus | null>(null); | ||
|
|
||
| useEffect(() => { | ||
| if (!accessToken || !cacheKey || known !== null) return; | ||
|
|
||
| const controller = new AbortController(); | ||
| void fetchAuthStatus({ | ||
| accessToken, | ||
| gatewayUrl, | ||
| signal: controller.signal, | ||
| }) | ||
| .then((authenticated) => { | ||
| writeAuthStatusCache(cacheKey, authenticated); | ||
| setFetched({ cacheKey, authenticated }); | ||
| }) | ||
| .catch((error: unknown) => { | ||
| if (controller.signal.aborted) return; | ||
| console.error("Failed to check authentication status", error); | ||
| setFetched({ cacheKey, authenticated: false }); | ||
| }); | ||
|
|
||
| return () => { | ||
| controller.abort(); | ||
| }; | ||
| }, [cacheKey, known, accessToken, gatewayUrl]); | ||
|
|
||
| const resolved = | ||
| known ?? (fetched?.cacheKey === cacheKey ? fetched.authenticated : null); | ||
|
|
||
| return { authenticated: resolved ?? false, loading: resolved === null }; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.