Skip to content

Commit

Permalink
Merge branch 'main' into web-console/chart-tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
insmac committed Nov 22, 2024
2 parents dac4f2f + 0494e85 commit 46b0d13
Show file tree
Hide file tree
Showing 20 changed files with 270 additions and 69 deletions.
20 changes: 6 additions & 14 deletions packages/browser-tests/cypress/integration/auth/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ describe("Auth - UI", () => {
"acl.basic.auth.realm.enabled": false,
"acl.oidc.enabled": false,
"acl.oidc.client.id": null,
"acl.oidc.host": null,
"acl.oidc.port": null,
"acl.oidc.tls.enabled": null,
"acl.oidc.authorization.endpoint": null,
"acl.oidc.token.endpoint": null,
"acl.oidc.pkce.required": null,
"acl.oidc.groups.encoded.in.token": false,
});
cy.visit(baseUrl);
});
Expand All @@ -59,12 +57,10 @@ describe("Auth - OIDC", () => {
"acl.basic.auth.realm.enabled": false,
"acl.oidc.enabled": true,
"acl.oidc.client.id": "test",
"acl.oidc.host": "host",
"acl.oidc.port": 9999,
"acl.oidc.tls.enabled": true,
"acl.oidc.authorization.endpoint": "/auth",
"acl.oidc.token.endpoint": "/token",
"acl.oidc.authorization.endpoint": "https://host:9999/auth",
"acl.oidc.token.endpoint": "https://host:9999/token",
"acl.oidc.pkce.required": true,
"acl.oidc.groups.encoded.in.token": false,
});
cy.visit(baseUrl);
});
Expand All @@ -86,12 +82,10 @@ describe("Auth - Basic", () => {
"acl.basic.auth.realm.enabled": true,
"acl.oidc.enabled": false,
"acl.oidc.client.id": null,
"acl.oidc.host": null,
"acl.oidc.port": null,
"acl.oidc.tls.enabled": null,
"acl.oidc.authorization.endpoint": null,
"acl.oidc.token.endpoint": null,
"acl.oidc.pkce.required": null,
"acl.oidc.groups.encoded.in.token": false,
});
cy.visit(baseUrl);
});
Expand All @@ -111,12 +105,10 @@ describe("Auth - Disabled", () => {
"acl.basic.auth.realm.enabled": true,
"acl.oidc.enabled": false,
"acl.oidc.client.id": null,
"acl.oidc.host": null,
"acl.oidc.port": null,
"acl.oidc.tls.enabled": null,
"acl.oidc.authorization.endpoint": null,
"acl.oidc.token.endpoint": null,
"acl.oidc.pkce.required": null,
"acl.oidc.groups.encoded.in.token": false,
});
cy.visit(baseUrl);
});
Expand Down
13 changes: 13 additions & 0 deletions packages/browser-tests/cypress/integration/console/grid.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ describe("questdb grid", () => {
cy.getGridViewport().scrollTo("bottom");
});

it("multiple scrolls till the bottom with error", () => {
const rows = 1200;
cy.typeQuery(`select simulate_crash('P') from long_sequence(${rows})`);
cy.runLine();

cy.getGridViewport().scrollTo(0, 999 * rowHeight);
cy.getCollapsedNotifications().should("contain", "1,200 rows in");

cy.getGridViewport().scrollTo("bottom");
cy.wait(100);
cy.getCollapsedNotifications().should("contain", "HTTP 400 (Bad request)");
});

it("copy cell into the clipboard", () => {
cy.typeQuery("select x from long_sequence(10)");
cy.runLine();
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-tests/questdb
Submodule questdb updated 469 files
2 changes: 2 additions & 0 deletions packages/web-console/src/modules/OAuth2/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export type AuthPayload = {
access_token: string
id_token: string
refresh_token: string
token_type: string
expires_in: number
expires_at?: string
groups_encoded_in_token?: boolean
}
30 changes: 18 additions & 12 deletions packages/web-console/src/modules/OAuth2/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,33 @@ type TokenPayload = Partial<{
refresh_token: string
}>

const getBaseURL = (config: Settings) => {
return `${config["acl.oidc.tls.enabled"] ? "https" : "http"}://${
config["acl.oidc.host"]
}:${config["acl.oidc.port"]}`
const getBaseURL = (settings: Settings) => {
// if there is no host in settings, no need to construct base URL at all
if (!settings["acl.oidc.host"]) {
return "";
}

// if there is host in settings, we are in legacy mode, and we should construct the base URL
return `${settings["acl.oidc.tls.enabled"] ? "https" : "http"}://${
settings["acl.oidc.host"]
}:${settings["acl.oidc.port"]}`
}

export const getAuthorisationURL = ({
config,
settings,
code_challenge = null,
login,
redirect_uri,
}: {
config: Settings
settings: Settings
code_challenge: string | null
login?: boolean
redirect_uri: string
}) => {
const params = {
client_id: config["acl.oidc.client.id"] || "",
client_id: settings["acl.oidc.client.id"] || "",
response_type: "code",
scope: config["acl.oidc.scope"] || "openid",
scope: settings["acl.oidc.scope"] || "openid",
redirect_uri,
}

Expand All @@ -43,8 +49,8 @@ export const getAuthorisationURL = ({
}

return (
getBaseURL(config) +
config["acl.oidc.authorization.endpoint"] +
getBaseURL(settings) +
settings["acl.oidc.authorization.endpoint"] +
"?" +
urlParams
)
Expand All @@ -70,5 +76,5 @@ export const getAuthToken = async (
)
}

export const hasUIAuth = (config: Settings) =>
config["acl.enabled"] && !config["acl.basic.auth.realm.enabled"]
export const hasUIAuth = (settings: Settings) =>
settings["acl.enabled"] && !settings["acl.basic.auth.realm.enabled"]
23 changes: 11 additions & 12 deletions packages/web-console/src/providers/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,13 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
)
const [state, dispatch] = useReducer(reducer, initialState)

const setAuthToken = (tokenResponse: AuthPayload) => {
if (tokenResponse.access_token) {
const setAuthToken = (tokenResponse: AuthPayload, settings: Settings) => {
if (tokenResponse.access_token && tokenResponse.id_token) {
tokenResponse.groups_encoded_in_token = settings["acl.oidc.groups.encoded.in.token"]
tokenResponse.expires_at = getTokenExpirationDate(tokenResponse.expires_in).toString() // convert from the sec offset
setValue(
StoreKey.AUTH_PAYLOAD,
JSON.stringify({
...tokenResponse,
expires_at: getTokenExpirationDate(tokenResponse.expires_in), // convert from the sec offset
}),
JSON.stringify(tokenResponse),
)
// if the token payload does not contain the rolling refresh token, we'll keep the old one
if (tokenResponse.refresh_token) {
Expand Down Expand Up @@ -119,7 +118,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
client_id: settings["acl.oidc.client.id"],
})
const tokenResponse = await response.json()
setAuthToken(tokenResponse)
setAuthToken(tokenResponse, settings)
return tokenResponse
}

Expand Down Expand Up @@ -175,15 +174,15 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {

// User is authenticated already
if (authPayload !== "") {
const token = JSON.parse(authPayload)
const tokenResponse = JSON.parse(authPayload)
// Check if the token expired or is about to in 30 seconds
if (
new Date(token.expires_at).getTime() - Date.now() < 30000 &&
new Date(tokenResponse.expires_at).getTime() - Date.now() < 30000 &&
getValue(StoreKey.AUTH_REFRESH_TOKEN) !== ""
) {
await refreshAuthToken(settings)
} else {
setSessionData(token)
setSessionData(tokenResponse)
}
} else {
// User has just been redirected back from the OAuth2 provider and has the code
Expand All @@ -198,7 +197,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
redirect_uri: settings["acl.oidc.redirect.uri"] || window.location.origin + window.location.pathname,
})
const tokenResponse = await response.json()
setAuthToken(tokenResponse)
setAuthToken(tokenResponse, settings)
} catch (e) {
throw e
}
Expand Down Expand Up @@ -253,7 +252,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const code_verifier = generateCodeVerifier(settings)
const code_challenge = generateCodeChallenge(code_verifier)
window.location.href = getAuthorisationURL({
config: settings,
settings,
code_challenge,
login,
redirect_uri: settings["acl.oidc.redirect.uri"] || window.location.href,
Expand Down
8 changes: 4 additions & 4 deletions packages/web-console/src/providers/QuestProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const QuestProvider = ({ children }: PropsWithChildren<Props>) => {

const setupClient = async (sessionData: Partial<AuthPayload>) => {
questClient.setCommonHeaders({
Authorization: `Bearer ${sessionData.access_token}`,
Authorization: `Bearer ${sessionData.groups_encoded_in_token ? sessionData.id_token : sessionData.access_token}`,
})

questClient.refreshTokenMethod = () => {
Expand All @@ -101,11 +101,11 @@ export const QuestProvider = ({ children }: PropsWithChildren<Props>) => {
}, [sessionData])

useEffect(() => {
const token = getValue(StoreKey.REST_TOKEN)
const restToken = getValue(StoreKey.REST_TOKEN)
// User has provided the basic auth credentials
if (token) {
if (restToken) {
questClient.setCommonHeaders({
Authorization: `Bearer ${token}`,
Authorization: `Bearer ${restToken}`,
})
void finishAuthCheck()
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type Settings = Partial<{
"release.version": string
"acl.enabled": boolean
"acl.basic.auth.realm.enabled": boolean
"acl.oidc.groups.encoded.in.token": boolean
"acl.oidc.enabled": boolean
"acl.oidc.client.id": string
"acl.oidc.redirect.uri": string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ export const language: monaco.languages.IMonarchLanguage = {
],
],
numbers: [
[/0[xX][0-9a-fA-F]*/, "number"],
[/[$][+-]*\d*(\.\d*)?/, "number"],
[/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, "number"],
[/0[xX][0-9a-fA-F]*/, "number"], // hex integers
[/[+-]?\d+((_)?\d+)*[Ll]?/, "number"], // integers
[/[+-]?\d*(\.\d*)?[Ee]/, "number"], // floating point number
],
strings: [
[/N'/, { token: "string", next: "@string" }],
Expand Down
3 changes: 3 additions & 0 deletions packages/web-console/src/scenes/Layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { CreateTableDialog } from "../../components/CreateTableDialog"
import { EditorProvider } from "../../providers"
import { Help } from "./help"
import { Warnings } from "./warning"
import { ImageZoom } from "../News/image-zoom"

import "allotment/dist/style.css"

Expand All @@ -62,6 +63,7 @@ const Root = styled.div`
`

const Main = styled.div<{ sideOpened: boolean }>`
position: relative;
flex: 1;
display: flex;
width: ${({ sideOpened }) =>
Expand All @@ -81,6 +83,7 @@ const Layout = () => {
<Warnings />
<Root>
<Main sideOpened={activeSidebar !== undefined}>
<ImageZoom />
<Page>
<Console />
</Page>
Expand Down
87 changes: 87 additions & 0 deletions packages/web-console/src/scenes/News/image-zoom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useEffect, useRef, useState } from "react"
import styled from "styled-components"
import { Box } from "@questdb/react-components"
import { useSelector, useDispatch } from "react-redux"
import { selectors, actions } from "../../store"
import { Thumbnail } from "./thumbnail"

const Root = styled(Box).attrs({ align: "center", justifyContent: "center" })<{
visible: boolean
}>`
width: 100%;
height: 100%;
position: absolute;
z-index: 1000;
opacity: ${({ visible }) => (visible ? 1 : 0)};
pointer-events: ${({ visible }) => (visible ? "auto" : "none")};
`

const Overlay = styled.div<{ visible: boolean }>`
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(33, 34, 44, 0.9);
opacity: ${({ visible }) => (visible ? 1 : 0)};
pointer-events: ${({ visible }) => (visible ? "auto" : "none")};
transition: opacity 0.2s ease-in-out;
`

const Wrapper = styled.div`
z-index: 1001;
img {
border: 1px solid ${({ theme }) => theme.color.offWhite};
border-radius: ${({ theme }) => theme.borderRadius};
}
`

export const ImageZoom = () => {
const imageToZoom = useSelector(selectors.console.getImageToZoom)
const dispatch = useDispatch()
const rootRef = useRef<HTMLDivElement>(null)
const [rootWidth, setRootWidth] = useState(0)
const [rootHeight, setRootHeight] = useState(0)
const activeSidebar = useSelector(selectors.console.getActiveSidebar)

const handleEsc = (event: KeyboardEvent) => {
if (event.key === "Escape") {
dispatch(actions.console.setImageToZoom(undefined))
}
}

useEffect(() => {
if (rootRef.current) {
setRootWidth(rootRef.current.offsetWidth)
setRootHeight(rootRef.current.offsetHeight)
}
}, [imageToZoom])

useEffect(() => {
if (activeSidebar === "news") {
document.addEventListener("keydown", handleEsc)
} else {
document.removeEventListener("keydown", handleEsc)
}
}, [activeSidebar])

if (activeSidebar !== "news") {
return null
}

return (
<Root ref={rootRef} visible={imageToZoom !== undefined}>
<Overlay visible={imageToZoom !== undefined} />
{imageToZoom && (
<Wrapper>
<Thumbnail
{...imageToZoom}
containerWidth={rootWidth ? rootWidth * 0.9 : 460}
containerHeight={rootHeight ? rootHeight * 0.75 : 460}
/>
</Wrapper>
)}
</Root>
)
}
Loading

0 comments on commit 46b0d13

Please sign in to comment.