You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@polaris.apache.org by "jbonofre (via GitHub)" <gi...@apache.org> on 2025/11/30 17:48:43 UTC
[PR] Introducing Apache Polaris Console [polaris-tools]
jbonofre opened a new pull request, #85:
URL: https://github.com/apache/polaris-tools/pull/85
This PR is proposing the Apache Polaris Console, an UI to browse and manage Polaris server and entities.
A huge thanks to the Console contributors: @sohanhonavar and @binarycat0
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "MonkeyCanCode (via GitHub)" <gi...@apache.org>.
MonkeyCanCode commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575444138
##########
console/Dockerfile:
##########
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Build stage
+FROM node:20-alpine AS builder
Review Comment:
Does it makes sense to use UBI images as well here as that is what we are using for polaris images. In this case, we will be looking at the following:
1. node: https://catalog.redhat.com/en/software/containers/ubi9/nodejs-20-minimal/64770ddd0e699534bb564b3b
2. nginx: https://catalog.redhat.com/en/software/containers/ubi9/nginx-126/678f5851a09b9e2d461ba1d9
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
+
+// Log OAuth URL in development only
+if (import.meta.env.DEV) {
+ console.log("π Using OAuth token URL:", TOKEN_URL)
+}
+
+export const authApi = {
+ getToken: async (
+ clientId: string,
+ clientSecret: string,
+ realm?: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "client_credentials")
Review Comment:
I would suggest the same for `grant_type` as what @flyrain points out. There are 2 allowed values for it: https://github.com/apache/polaris/blob/ab421e0f1330d1c95eb9a4f07f9bfab10c7f7e2e/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/broker/TokenRequestValidator.java#L32
##########
console/src/api/catalog/namespaces.ts:
##########
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { apiClient } from "../client"
+import type {
+ Namespace,
+ ListNamespacesResponse,
+ CreateNamespaceRequest,
+} from "@/types/api"
+
+/**
+ * Encodes a namespace array to URL format.
+ * Namespace parts are separated by the unit separator character (0x1F).
+ */
+function encodeNamespace(namespace: string[]): string {
+ return namespace.join("\x1F")
+}
+
+
+export const namespacesApi = {
+ /**
+ * List namespaces for a catalog, optionally filtering by parent namespace.
+ * @param prefix - The catalog name (prefix)
+ * @param parent - Optional parent namespace array (e.g., ["accounting", "tax"])
+ */
+ list: async (
+ prefix: string,
+ parent?: string[]
+ ): Promise<Namespace[]> => {
+ const params: Record<string, string> = {}
+ if (parent && parent.length > 0) {
+ params.parent = encodeNamespace(parent)
+ }
+
+ const response = await apiClient
Review Comment:
NIT: should we consider for adding support for `pageSize` as if an user create high number of namespace, the UI loading will taking extremely long of time.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575774155
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
+
+// Log OAuth URL in development only
+if (import.meta.env.DEV) {
+ console.log("π Using OAuth token URL:", TOKEN_URL)
+}
+
+export const authApi = {
+ getToken: async (
+ clientId: string,
+ clientSecret: string,
+ realm?: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "client_credentials")
+ formData.append("client_id", clientId)
+ formData.append("client_secret", clientSecret)
+ formData.append("scope", "PRINCIPAL_ROLE:ALL")
+
+ const headers: Record<string, string> = {
+ "Content-Type": "application/x-www-form-urlencoded",
+ }
+
+ // Add realm header if provided
+ if (realm) {
+ headers[REALM_HEADER_NAME] = realm
+ }
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers,
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ exchangeToken: async (
+ subjectToken: string,
+ subjectTokenType: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
+ formData.append("subject_token", subjectToken)
+ formData.append("subject_token_type", subjectTokenType)
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ Authorization: `Bearer ${apiClient.getAccessToken()}`,
+ },
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ refreshToken: async (accessToken: string): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
+ formData.append("subject_token", accessToken)
+ formData.append("subject_token_type", "urn:ietf:params:oauth:token-type:access_token")
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ logout: (): void => {
+ localStorage.removeItem("polaris_access_token")
Review Comment:
I don't think it's a blocker as it's browser side. However I agree that it would be a nice improvement using browser in memory.
##########
console/public/polaris-logo-star.svg:
##########
@@ -0,0 +1,7 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <!-- Teal background -->
+ <rect width="32" height="32" fill="#0F766E"/>
+ <!-- White four-pointed star -->
+ <path d="M16 4L19.5 12.5L28 16L19.5 19.5L16 28L12.5 19.5L4 16L12.5 12.5L16 4Z" fill="white"/>
Review Comment:
Agree. Let me do that.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "adam-christian-software (via GitHub)" <gi...@apache.org>.
adam-christian-software commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2578490863
##########
console/spec/bundled-polaris-catalog-service.yaml:
##########
@@ -0,0 +1,4629 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+openapi: 3.0.3
Review Comment:
I think that a copy & paste is fine for now, but eventually, I'd expect that we would have some way of pulling in the Polaris APIs from the Polaris instance.
Same with the polaris-management-service spec as well.
Do you have an idea on how we want to do that? It's OK if not. We can just file a Git issue.
##########
console/package.json:
##########
@@ -0,0 +1,58 @@
+{
+ "name": "polaris-console",
+ "private": true,
+ "version": "1.3.0-incubating",
+ "type": "module",
+ "scripts": {
Review Comment:
I'm not seeing any unit tests or any way to test this yet. Can we file a Git issue, so we can handle this after merge?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3661231716
I created the expected GitHub Issues (adding `console` tag).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2579647726
##########
console/package.json:
##########
@@ -0,0 +1,58 @@
+{
+ "name": "polaris-console",
+ "private": true,
+ "version": "1.3.0-incubating",
+ "type": "module",
+ "scripts": {
Review Comment:
I propose to wait the PR merged before creating the corresponding issues as it can look weird for contributors if they don't find the actual code π
I will create all issues after this PR.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "binarycat0 (via GitHub)" <gi...@apache.org>.
binarycat0 commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3603454943
Great job, everything works fine for me as local DEV, except the known issues with Dockerfile.
I was trying to fix Dockerfile issues and introduce a helm chart for unblocking a possibility to build and deploy Console UI into existing K8S namespace and connect to Polaris API.
This PR can be merged and delivered with this PR together - https://github.com/jbonofre/polaris-tools/pull/2
cc: @jbonofre @sohanhonavar
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575772134
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
Review Comment:
Yes we have a WiP about that. I will work on it as well.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "flyrain (via GitHub)" <gi...@apache.org>.
flyrain commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2574950183
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
+
+// Log OAuth URL in development only
+if (import.meta.env.DEV) {
+ console.log("π Using OAuth token URL:", TOKEN_URL)
+}
+
+export const authApi = {
+ getToken: async (
+ clientId: string,
+ clientSecret: string,
+ realm?: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "client_credentials")
+ formData.append("client_id", clientId)
+ formData.append("client_secret", clientSecret)
+ formData.append("scope", "PRINCIPAL_ROLE:ALL")
+
+ const headers: Record<string, string> = {
+ "Content-Type": "application/x-www-form-urlencoded",
+ }
+
+ // Add realm header if provided
+ if (realm) {
+ headers[REALM_HEADER_NAME] = realm
+ }
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers,
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ exchangeToken: async (
+ subjectToken: string,
+ subjectTokenType: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
+ formData.append("subject_token", subjectToken)
+ formData.append("subject_token_type", subjectTokenType)
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ Authorization: `Bearer ${apiClient.getAccessToken()}`,
+ },
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ refreshToken: async (accessToken: string): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
+ formData.append("subject_token", accessToken)
+ formData.append("subject_token_type", "urn:ietf:params:oauth:token-type:access_token")
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ logout: (): void => {
+ localStorage.removeItem("polaris_access_token")
Review Comment:
Storing tokens in `localStorage` has security implications.
Because `localStorage` is readable by any JavaScript running on the page, an XSS vulnerability would allow an attacker to extract the access token.
Not a blocker for an internal/admin console, but we should at least track this as a follow-up improvement (e.g., memory-only tokens).
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
Review Comment:
Can we have a configurable token url for 3-rd party IDPs or different remote Polaris instance? Not a blocker. I'm fine with filing an issue and resolving it later.
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
+
+// Log OAuth URL in development only
+if (import.meta.env.DEV) {
+ console.log("π Using OAuth token URL:", TOKEN_URL)
+}
+
+export const authApi = {
+ getToken: async (
+ clientId: string,
+ clientSecret: string,
+ realm?: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "client_credentials")
+ formData.append("client_id", clientId)
+ formData.append("client_secret", clientSecret)
+ formData.append("scope", "PRINCIPAL_ROLE:ALL")
Review Comment:
I think the scope should be the part of the configuration along with client id and secret. We can provide a default value, but avoid hardcoding it.
##########
console/public/polaris-logo-star.svg:
##########
@@ -0,0 +1,7 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <!-- Teal background -->
+ <rect width="32" height="32" fill="#0F766E"/>
+ <!-- White four-pointed star -->
+ <path d="M16 4L19.5 12.5L28 16L19.5 19.5L16 28L12.5 19.5L4 16L12.5 12.5L16 4Z" fill="white"/>
Review Comment:
Can we use the one in the main repo to be consistent?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2623836944
##########
console/src/api/client.ts:
##########
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios, { type AxiosInstance, type InternalAxiosRequestConfig } from "axios"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+
+// Always use relative URLs to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const API_BASE_URL = ""
+const MANAGEMENT_BASE_URL = `${API_BASE_URL}/api/management/v1`
+const CATALOG_BASE_URL = `${API_BASE_URL}/api/catalog/v1`
+const POLARIS_BASE_URL = `${API_BASE_URL}/polaris/v1`
+
+class ApiClient {
+ private managementClient: AxiosInstance
+ private catalogClient: AxiosInstance
+ private polarisClient: AxiosInstance
+
+ constructor() {
+ this.managementClient = axios.create({
+ baseURL: MANAGEMENT_BASE_URL,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+
+ this.catalogClient = axios.create({
+ baseURL: CATALOG_BASE_URL,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+
+ this.polarisClient = axios.create({
+ baseURL: POLARIS_BASE_URL,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+
+ this.setupInterceptors()
+ }
+
+ private setupInterceptors() {
+ // Request interceptor to add auth token
+ const requestInterceptor = (config: InternalAxiosRequestConfig) => {
+ const token = this.getAccessToken()
+ // Read realm from localStorage, fallback to environment variable for backward compatibility
+ const realm = localStorage.getItem("polaris_realm") || import.meta.env.VITE_POLARIS_REALM
+
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`
+ }
+
+ if (realm) {
+ config.headers[REALM_HEADER_NAME] = realm
+ }
+
+ return config
+ }
+
+ // Response interceptor for error handling
+ const responseErrorInterceptor = (error: unknown) => {
Review Comment:
In an UI, user can resubmit the request. I think it's acceptable for now (usually we don't do retry in UI).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "MonkeyCanCode (via GitHub)" <gi...@apache.org>.
MonkeyCanCode commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575473998
##########
console/Dockerfile:
##########
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Build stage
+FROM node:20-alpine AS builder
+
+WORKDIR /app
+
+# Copy package files
+COPY package*.json ./
Review Comment:
From the checked in code, the `package-lock.json` seems to be missing which will fail the next command.
##########
console/Dockerfile:
##########
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Build stage
+FROM node:20-alpine AS builder
+
+WORKDIR /app
+
+# Copy package files
+COPY package*.json ./
+
+# Install dependencies
+RUN npm ci --only=production=false
Review Comment:
```
> [builder 4/6] RUN npm ci --only=production=false:
0.192 npm warn invalid config only="production=false" set in command line options
```
doesn't appear to be an valid option. Maybe we should do `RUN npm ci --production=false` instead?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "flyrain (via GitHub)" <gi...@apache.org>.
flyrain commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2616608207
##########
console/src/pages/Connections.tsx:
##########
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useState } from "react"
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
+import { toast } from "sonner"
+import { formatDistanceToNow } from "date-fns"
+import { Link as LinkIcon, Plus, RefreshCw, Search, MoreVertical } from "lucide-react"
+import { principalsApi } from "@/api/management/principals"
+import { getErrorMessage } from "@/lib/errorHandler"
+import type { Principal } from "@/types/api"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Badge } from "@/components/ui/badge"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import { ConfigureConnectionModal } from "@/components/forms/ConfigureConnectionModal"
+import { CredentialsModal } from "@/components/forms/CredentialsModal"
+import { PrincipalDetailsModal } from "@/components/users/PrincipalDetailsModal"
+import { EditPrincipalModal } from "@/components/forms/EditPrincipalModal"
+import { ResetCredentialsModal } from "@/components/forms/ResetCredentialsModal"
+
+export function Connections() {
+ const [searchQuery, setSearchQuery] = useState("")
+ const [isConfigureModalOpen, setIsConfigureModalOpen] = useState(false)
+ const [isCredentialsModalOpen, setIsCredentialsModalOpen] = useState(false)
+ const [connectionToDelete, setConnectionToDelete] = useState<string | null>(null)
+ const [connectionToReset, setConnectionToReset] = useState<string | null>(null)
+ const [principalToView, setPrincipalToView] = useState<Principal | null>(null)
+ const [principalToEdit, setPrincipalToEdit] = useState<Principal | null>(null)
+ const [generatedCredentials, setGeneratedCredentials] = useState<{
+ clientId: string
+ clientSecret: string
+ } | null>(null)
+
+ const queryClient = useQueryClient()
+
+ const { data: principals = [], isLoading } = useQuery({
+ queryKey: ["principals"],
+ queryFn: () => principalsApi.list(),
+ })
+
+ const deleteMutation = useMutation({
+ mutationFn: (principalName: string) => principalsApi.delete(principalName),
+ onSuccess: () => {
+ toast.success("Connection deleted successfully")
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ setConnectionToDelete(null)
+ },
+ onError: (error: unknown) => {
+ const errorMsg = getErrorMessage(
+ error,
+ "Failed to delete connection",
+ "connection",
+ "delete"
+ )
+ toast.error("Failed to delete connection", {
+ description: errorMsg,
+ })
+ },
+ })
+
+ const rotateMutation = useMutation({
+ mutationFn: (principalName: string) =>
+ principalsApi.rotateCredentials(principalName),
+ onSuccess: (data) => {
+ toast.success("Credentials rotated successfully")
+ setGeneratedCredentials({
+ clientId: data.credentials.clientId,
+ clientSecret: data.credentials.clientSecret,
+ })
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ },
+ onError: (error: unknown) => {
+ const errorMsg = getErrorMessage(
+ error,
+ "Failed to rotate credentials",
+ "credentials",
+ "rotate"
+ )
+ toast.error("Failed to rotate credentials", {
+ description: errorMsg,
+ })
+ },
+ })
+
+ const handleResetSuccess = (credentials: { clientId: string; clientSecret: string }) => {
+ setGeneratedCredentials(credentials)
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ setConnectionToReset(null)
+ }
+
+ const filteredPrincipals = principals.filter((principal) =>
+ principal.name.toLowerCase().includes(searchQuery.toLowerCase())
+ )
+
+
+ const handleCreateSuccess = (credentials: { clientId: string; clientSecret: string }) => {
+ setGeneratedCredentials(credentials)
+ setIsConfigureModalOpen(false)
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ }
+
+ return (
+ <div className="p-8">
+ {/* Header */}
+ <div className="mb-6">
+ <div className="flex items-center justify-between">
+ <div className="flex items-center gap-3">
+ <LinkIcon className="h-6 w-6 text-foreground" />
+ <h1 className="text-3xl font-bold text-foreground">Service Connections</h1>
+ </div>
+ <div className="flex items-center gap-3">
+ <div className="flex items-center gap-2">
+ <Input
+ placeholder="Q Search"
+ value={searchQuery}
+ onChange={(e) => setSearchQuery(e.target.value)}
+ className="w-48"
+ />
+ <Button variant="ghost" size="icon">
+ <Search className="h-4 w-4" />
+ </Button>
+ </div>
+ <Button variant="ghost" size="icon" onClick={() => queryClient.invalidateQueries({ queryKey: ["principals"] })}>
+ <RefreshCw className="h-4 w-4" />
+ </Button>
+ <Button
+ onClick={() => setIsConfigureModalOpen(true)}
+ variant="default"
+ >
+ <Plus className="mr-2 h-4 w-4" />
+ Connection
+ </Button>
+ </div>
+ </div>
+ </div>
+
+ {/* Table */}
+ <div className="rounded-lg border bg-card">
+ <Table>
+ <TableHeader>
+ <TableRow>
+ <TableHead>PRINCIPAL NAME</TableHead>
+ <TableHead>SERVICE</TableHead>
+ <TableHead>CREATED</TableHead>
+ <TableHead className="w-[50px]"></TableHead>
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {isLoading ? (
+ <TableRow>
+ <TableCell colSpan={4} className="text-center py-8 text-muted-foreground">
+ Loading...
+ </TableCell>
+ </TableRow>
+ ) : filteredPrincipals.length === 0 ? (
+ <TableRow>
+ <TableCell colSpan={4} className="text-center py-8 text-muted-foreground">
+ No connections found
+ </TableCell>
+ </TableRow>
+ ) : (
+ filteredPrincipals.map((principal) => {
+ // Cast to include timestamp fields that may be present in API response
+ const principalWithTimestamps = principal as Principal & {
+ createTimestamp?: number
+ lastUpdateTimestamp?: number
+ }
+ // Try createTimestamp first, then lastUpdateTimestamp as fallback
+ const timestamp =
+ principalWithTimestamps.createTimestamp ||
+ principalWithTimestamps.lastUpdateTimestamp ||
+ (typeof principalWithTimestamps.properties?.createTimestamp === "string"
+ ? parseInt(principalWithTimestamps.properties.createTimestamp, 10)
+ : typeof principalWithTimestamps.properties?.createTimestamp === "number"
+ ? principalWithTimestamps.properties.createTimestamp
+ : typeof principalWithTimestamps.properties?.lastUpdateTimestamp === "string"
+ ? parseInt(principalWithTimestamps.properties.lastUpdateTimestamp, 10)
+ : typeof principalWithTimestamps.properties?.lastUpdateTimestamp === "number"
+ ? principalWithTimestamps.properties.lastUpdateTimestamp
+ : undefined)
+
+ return (
+ <TableRow key={principal.name}>
+ <TableCell className="font-medium">{principal.name}</TableCell>
+ <TableCell>
+ <Badge variant="secondary">
+ {principal.properties?.serviceType || "apache-spark"}
Review Comment:
Thanks for the clarification.
I still have questions to better understand the UX and conceptual model here. Do we really need a separate service type concept in the UI?
1. From a userβs perspective, this seems to overlap with the existing principal concept already exposed on the access control page.
2. How do we expect users to use βservice typeβ? Given the only difference between βprincipalβ and βservice typeβ is having a property like `service-type=Spark`
Iβm trying to understand whether this duplication is intentional and beneficial from a UX perspective, or if it risks confusing users.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575770935
##########
console/Dockerfile:
##########
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Build stage
+FROM node:20-alpine AS builder
+
+WORKDIR /app
+
+# Copy package files
+COPY package*.json ./
Review Comment:
The package lock json should be created by the builder (npm run build). Let me check.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "georgelza (via GitHub)" <gi...@apache.org>.
georgelza commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3635694992
can i ask...
as allot of for the catalog think we can agree is:
- flink, or
- flink-sql or
- pyflink,
- jupyter notebooks
- python,
- java....
to make it easier for user,
how about adding a button at the bottom that "provides" the code to use/interface with the catalog created. for those that have used MongoDB.. where they provide the connection string t use based on catalog crated based on desired interface language.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3637361976
That's great idea.
I propose:
1. To merge this PR (I added the ASF header where it was missing)
2. Create the corresponding GH Issues
3. Create new PRs with Polaris Console improvements and new features
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2581438146
##########
console/README.md:
##########
@@ -0,0 +1,130 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Polaris Console
+
+A modern web interface for Apache Polaris, built with React, TypeScript, TanStack Query, and Tailwind CSS.
+
+## Getting Started
+
+### Prerequisites
+- Node.js 18+ and npm (or yarn)
Review Comment:
Good catch :)
Updated.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "binarycat0 (via GitHub)" <gi...@apache.org>.
binarycat0 commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2581532767
##########
console/README.md:
##########
@@ -0,0 +1,130 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Polaris Console
+
+A modern web interface for Apache Polaris, built with React, TypeScript, TanStack Query, and Tailwind CSS.
+
+## Getting Started
+
+### Prerequisites
+- Node.js 18+ and npm (or yarn)
+
+### Installation
+
+```bash
+# Install dependencies
+npm install
+
+# Start development server
+npm run dev
+
+# Build for production
+npm run build
+```
+
+### Environment Variables
+
+Create a `.env` file based on `.env.example`:
+
+```env
+VITE_POLARIS_API_URL=http://localhost:8181
+VITE_POLARIS_REALM=POLARIS
+VITE_POLARIS_REALM_HEADER_NAME=Polaris-Realm # optional, defaults to "Polaris-Realm"
+VITE_OAUTH_TOKEN_URL=http://localhost:8181/api/v1/oauth/tokens # optional
+```
+
+## Project Structure
+
+```
+src/
+βββ api/ # API client and endpoints
+β βββ client.ts # Axios instance with interceptors
+β βββ auth.ts # Authentication API
+β βββ management/ # Management Service APIs
+βββ components/ # React components
+β βββ ui/ # Shadcn UI components
+β βββ layout/ # Layout components
+β βββ forms/ # Form components
+βββ hooks/ # Custom React hooks
+βββ lib/ # Utilities
+βββ pages/ # Page components
+βββ types/ # TypeScript type definitions
+βββ App.tsx # Main app component
+```
+
+## Technology Stack
+
+- **Framework**: React 19 with TypeScript
+- **Routing**: React Router v7
+- **State Management**: TanStack Query (React Query)
+- **Tables**: TanStack Table (React Table)
+- **Styling**: Tailwind CSS
+- **Components**: Shadcn UI (Radix UI primitives)
+- **Forms**: React Hook Form with Zod validation
+- **HTTP Client**: Axios
+- **Icons**: Lucide React
+
+## Development
+
+The project uses:
+- Vite for fast development and building
+- ESLint for code linting
+- TypeScript for type safety
+
+To start developing:
+
+```bash
+npm run dev
+```
+
+The app will be available at `http://localhost:5173`
+
+## Building
+
+```bash
+npm run build
+```
+
+Output will be in the `dist/` directory.
+
+## Production Deployment
+
+After building, you can serve the production files in several ways:
+
+### Quick Test
+```bash
+bun run preview # or npm run preview
+```
+
+## Docker image
+
+You can build Polaris Console docker image using:
+
+```
+docker build -t apache/polaris-console:latest .
+```
+
+Then, you run Polaris Console using:
+
+```
+docker run -p 8080:80 apache/polaris-console:latest
Review Comment:
I'm trying to cover this by introducing Helm chart that will help to deploy Console into the same `namespace` and `network` as the main Apache Polaris instance.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "sohanhonavar (via GitHub)" <gi...@apache.org>.
sohanhonavar commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575739310
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
Review Comment:
@flyrain i have implemented 3 rd party IDPs support in here https://github.com/jbonofre/polaris-tools/tree/keycloak-support i need to help me with testing this one
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "sohanhonavar (via GitHub)" <gi...@apache.org>.
sohanhonavar commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575739310
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
Review Comment:
@flyrain i have implemented 3 rd party IDPs support in here https://github.com/jbonofre/polaris-tools/tree/keycloak-support i need some help me with testing this one
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "binarycat0 (via GitHub)" <gi...@apache.org>.
binarycat0 commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2582436147
##########
console/README.md:
##########
@@ -0,0 +1,130 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Polaris Console
+
+A modern web interface for Apache Polaris, built with React, TypeScript, TanStack Query, and Tailwind CSS.
+
+## Getting Started
+
+### Prerequisites
+- Node.js 18+ and npm (or yarn)
+
+### Installation
+
+```bash
+# Install dependencies
+npm install
+
+# Start development server
+npm run dev
+
+# Build for production
+npm run build
+```
+
+### Environment Variables
+
+Create a `.env` file based on `.env.example`:
+
+```env
+VITE_POLARIS_API_URL=http://localhost:8181
+VITE_POLARIS_REALM=POLARIS
+VITE_POLARIS_REALM_HEADER_NAME=Polaris-Realm # optional, defaults to "Polaris-Realm"
+VITE_OAUTH_TOKEN_URL=http://localhost:8181/api/v1/oauth/tokens # optional
+```
+
+## Project Structure
+
+```
+src/
+βββ api/ # API client and endpoints
+β βββ client.ts # Axios instance with interceptors
+β βββ auth.ts # Authentication API
+β βββ management/ # Management Service APIs
+βββ components/ # React components
+β βββ ui/ # Shadcn UI components
+β βββ layout/ # Layout components
+β βββ forms/ # Form components
+βββ hooks/ # Custom React hooks
+βββ lib/ # Utilities
+βββ pages/ # Page components
+βββ types/ # TypeScript type definitions
+βββ App.tsx # Main app component
+```
+
+## Technology Stack
+
+- **Framework**: React 19 with TypeScript
+- **Routing**: React Router v7
+- **State Management**: TanStack Query (React Query)
+- **Tables**: TanStack Table (React Table)
+- **Styling**: Tailwind CSS
+- **Components**: Shadcn UI (Radix UI primitives)
+- **Forms**: React Hook Form with Zod validation
+- **HTTP Client**: Axios
+- **Icons**: Lucide React
+
+## Development
+
+The project uses:
+- Vite for fast development and building
+- ESLint for code linting
+- TypeScript for type safety
+
+To start developing:
+
+```bash
+npm run dev
+```
+
+The app will be available at `http://localhost:5173`
+
+## Building
+
+```bash
+npm run build
+```
+
+Output will be in the `dist/` directory.
+
+## Production Deployment
+
+After building, you can serve the production files in several ways:
+
+### Quick Test
+```bash
+bun run preview # or npm run preview
+```
+
+## Docker image
+
+You can build Polaris Console docker image using:
+
+```
+docker build -t apache/polaris-console:latest .
+```
+
+Then, you run Polaris Console using:
+
+```
+docker run -p 8080:80 apache/polaris-console:latest
Review Comment:
delivered here https://github.com/apache/polaris-tools/pull/85/commits/358425d6da1264f39d605f6ecd0ffc9a19f3ab71
added [how-to](https://github.com/jbonofre/polaris-tools/tree/ui/console#k8s-deployment-with-helm) into the readme
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre merged PR #85:
URL: https://github.com/apache/polaris-tools/pull/85
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2579056325
##########
console/README.md:
##########
@@ -0,0 +1,130 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Polaris Console
+
+A modern web interface for Apache Polaris, built with React, TypeScript, TanStack Query, and Tailwind CSS.
+
+## Getting Started
+
+### Prerequisites
+- Node.js 18+ and npm (or yarn)
Review Comment:
I'm getting:
```
$ npm run dev
> polaris-console@1.3.0-incubating dev
> vite
You are using Node.js 18.19.1. Vite requires Node.js version 20.19+ or 22.12+. Please upgrade your Node.js version.
error when starting dev server:
TypeError: crypto.hash is not a function
[...]
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3592989739
You can see the current console in action here:
https://drive.google.com/file/d/1crPlUyQQPtEAZPO37for7SQvp4NwR_Pj/view?usp=sharing
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "flyrain (via GitHub)" <gi...@apache.org>.
flyrain commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2608271166
##########
console/src/pages/Connections.tsx:
##########
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useState } from "react"
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
+import { toast } from "sonner"
+import { formatDistanceToNow } from "date-fns"
+import { Link as LinkIcon, Plus, RefreshCw, Search, MoreVertical } from "lucide-react"
+import { principalsApi } from "@/api/management/principals"
+import { getErrorMessage } from "@/lib/errorHandler"
+import type { Principal } from "@/types/api"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Badge } from "@/components/ui/badge"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import { ConfigureConnectionModal } from "@/components/forms/ConfigureConnectionModal"
+import { CredentialsModal } from "@/components/forms/CredentialsModal"
+import { PrincipalDetailsModal } from "@/components/users/PrincipalDetailsModal"
+import { EditPrincipalModal } from "@/components/forms/EditPrincipalModal"
+import { ResetCredentialsModal } from "@/components/forms/ResetCredentialsModal"
+
+export function Connections() {
+ const [searchQuery, setSearchQuery] = useState("")
+ const [isConfigureModalOpen, setIsConfigureModalOpen] = useState(false)
+ const [isCredentialsModalOpen, setIsCredentialsModalOpen] = useState(false)
+ const [connectionToDelete, setConnectionToDelete] = useState<string | null>(null)
+ const [connectionToReset, setConnectionToReset] = useState<string | null>(null)
+ const [principalToView, setPrincipalToView] = useState<Principal | null>(null)
+ const [principalToEdit, setPrincipalToEdit] = useState<Principal | null>(null)
+ const [generatedCredentials, setGeneratedCredentials] = useState<{
+ clientId: string
+ clientSecret: string
+ } | null>(null)
+
+ const queryClient = useQueryClient()
+
+ const { data: principals = [], isLoading } = useQuery({
+ queryKey: ["principals"],
+ queryFn: () => principalsApi.list(),
+ })
+
+ const deleteMutation = useMutation({
+ mutationFn: (principalName: string) => principalsApi.delete(principalName),
+ onSuccess: () => {
+ toast.success("Connection deleted successfully")
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ setConnectionToDelete(null)
+ },
+ onError: (error: unknown) => {
+ const errorMsg = getErrorMessage(
+ error,
+ "Failed to delete connection",
+ "connection",
+ "delete"
+ )
+ toast.error("Failed to delete connection", {
+ description: errorMsg,
+ })
+ },
+ })
+
+ const rotateMutation = useMutation({
+ mutationFn: (principalName: string) =>
+ principalsApi.rotateCredentials(principalName),
+ onSuccess: (data) => {
+ toast.success("Credentials rotated successfully")
+ setGeneratedCredentials({
+ clientId: data.credentials.clientId,
+ clientSecret: data.credentials.clientSecret,
+ })
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ },
+ onError: (error: unknown) => {
+ const errorMsg = getErrorMessage(
+ error,
+ "Failed to rotate credentials",
+ "credentials",
+ "rotate"
+ )
+ toast.error("Failed to rotate credentials", {
+ description: errorMsg,
+ })
+ },
+ })
+
+ const handleResetSuccess = (credentials: { clientId: string; clientSecret: string }) => {
+ setGeneratedCredentials(credentials)
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ setConnectionToReset(null)
+ }
+
+ const filteredPrincipals = principals.filter((principal) =>
+ principal.name.toLowerCase().includes(searchQuery.toLowerCase())
+ )
+
+
+ const handleCreateSuccess = (credentials: { clientId: string; clientSecret: string }) => {
+ setGeneratedCredentials(credentials)
+ setIsConfigureModalOpen(false)
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ }
+
+ return (
+ <div className="p-8">
+ {/* Header */}
+ <div className="mb-6">
+ <div className="flex items-center justify-between">
+ <div className="flex items-center gap-3">
+ <LinkIcon className="h-6 w-6 text-foreground" />
+ <h1 className="text-3xl font-bold text-foreground">Service Connections</h1>
+ </div>
+ <div className="flex items-center gap-3">
+ <div className="flex items-center gap-2">
+ <Input
+ placeholder="Q Search"
+ value={searchQuery}
+ onChange={(e) => setSearchQuery(e.target.value)}
+ className="w-48"
+ />
+ <Button variant="ghost" size="icon">
+ <Search className="h-4 w-4" />
+ </Button>
+ </div>
+ <Button variant="ghost" size="icon" onClick={() => queryClient.invalidateQueries({ queryKey: ["principals"] })}>
+ <RefreshCw className="h-4 w-4" />
+ </Button>
+ <Button
+ onClick={() => setIsConfigureModalOpen(true)}
+ variant="default"
+ >
+ <Plus className="mr-2 h-4 w-4" />
+ Connection
+ </Button>
+ </div>
+ </div>
+ </div>
+
+ {/* Table */}
+ <div className="rounded-lg border bg-card">
+ <Table>
+ <TableHeader>
+ <TableRow>
+ <TableHead>PRINCIPAL NAME</TableHead>
+ <TableHead>SERVICE</TableHead>
+ <TableHead>CREATED</TableHead>
+ <TableHead className="w-[50px]"></TableHead>
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {isLoading ? (
+ <TableRow>
+ <TableCell colSpan={4} className="text-center py-8 text-muted-foreground">
+ Loading...
+ </TableCell>
+ </TableRow>
+ ) : filteredPrincipals.length === 0 ? (
+ <TableRow>
+ <TableCell colSpan={4} className="text-center py-8 text-muted-foreground">
+ No connections found
+ </TableCell>
+ </TableRow>
+ ) : (
+ filteredPrincipals.map((principal) => {
+ // Cast to include timestamp fields that may be present in API response
+ const principalWithTimestamps = principal as Principal & {
+ createTimestamp?: number
+ lastUpdateTimestamp?: number
+ }
+ // Try createTimestamp first, then lastUpdateTimestamp as fallback
+ const timestamp =
+ principalWithTimestamps.createTimestamp ||
+ principalWithTimestamps.lastUpdateTimestamp ||
+ (typeof principalWithTimestamps.properties?.createTimestamp === "string"
+ ? parseInt(principalWithTimestamps.properties.createTimestamp, 10)
+ : typeof principalWithTimestamps.properties?.createTimestamp === "number"
+ ? principalWithTimestamps.properties.createTimestamp
+ : typeof principalWithTimestamps.properties?.lastUpdateTimestamp === "string"
+ ? parseInt(principalWithTimestamps.properties.lastUpdateTimestamp, 10)
+ : typeof principalWithTimestamps.properties?.lastUpdateTimestamp === "number"
+ ? principalWithTimestamps.properties.lastUpdateTimestamp
+ : undefined)
+
+ return (
+ <TableRow key={principal.name}>
+ <TableCell className="font-medium">{principal.name}</TableCell>
+ <TableCell>
+ <Badge variant="secondary">
+ {principal.properties?.serviceType || "apache-spark"}
Review Comment:
UI introduces a new concept: βservice type,β stored as a principal property.
Polaris itself doesnβt have any notion of service type today. Could we clarify why this new concept is needed here? How do we expect it to be used by Polaris or external systems?
Right now it looks like a UI-level addition that isnβt backed by the Polaris model or APIs, so it would be helpful to understand the intended semantics and whether this should be part of the core Polaris concepts before adding it to UI.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "binarycat0 (via GitHub)" <gi...@apache.org>.
binarycat0 commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3637325811
Hi @georgelza! Great ideas π, IMO it's better to create a new GH Issue with proposal type or even start a new dev-mailing thread and continue discussion there π
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "sohanhonavar (via GitHub)" <gi...@apache.org>.
sohanhonavar commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2579640327
##########
console/package.json:
##########
@@ -0,0 +1,58 @@
+{
+ "name": "polaris-console",
+ "private": true,
+ "version": "1.3.0-incubating",
+ "type": "module",
+ "scripts": {
Review Comment:
yes definetly lets create a Git to track this, will work on them, starting next week
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3654346531
Thanks everyone for your feedback and highly valuable comments. I will merge this PR as a "start" for new contributions (issues, features, ...).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2610046267
##########
console/src/pages/Connections.tsx:
##########
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useState } from "react"
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
+import { toast } from "sonner"
+import { formatDistanceToNow } from "date-fns"
+import { Link as LinkIcon, Plus, RefreshCw, Search, MoreVertical } from "lucide-react"
+import { principalsApi } from "@/api/management/principals"
+import { getErrorMessage } from "@/lib/errorHandler"
+import type { Principal } from "@/types/api"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Badge } from "@/components/ui/badge"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import { ConfigureConnectionModal } from "@/components/forms/ConfigureConnectionModal"
+import { CredentialsModal } from "@/components/forms/CredentialsModal"
+import { PrincipalDetailsModal } from "@/components/users/PrincipalDetailsModal"
+import { EditPrincipalModal } from "@/components/forms/EditPrincipalModal"
+import { ResetCredentialsModal } from "@/components/forms/ResetCredentialsModal"
+
+export function Connections() {
+ const [searchQuery, setSearchQuery] = useState("")
+ const [isConfigureModalOpen, setIsConfigureModalOpen] = useState(false)
+ const [isCredentialsModalOpen, setIsCredentialsModalOpen] = useState(false)
+ const [connectionToDelete, setConnectionToDelete] = useState<string | null>(null)
+ const [connectionToReset, setConnectionToReset] = useState<string | null>(null)
+ const [principalToView, setPrincipalToView] = useState<Principal | null>(null)
+ const [principalToEdit, setPrincipalToEdit] = useState<Principal | null>(null)
+ const [generatedCredentials, setGeneratedCredentials] = useState<{
+ clientId: string
+ clientSecret: string
+ } | null>(null)
+
+ const queryClient = useQueryClient()
+
+ const { data: principals = [], isLoading } = useQuery({
+ queryKey: ["principals"],
+ queryFn: () => principalsApi.list(),
+ })
+
+ const deleteMutation = useMutation({
+ mutationFn: (principalName: string) => principalsApi.delete(principalName),
+ onSuccess: () => {
+ toast.success("Connection deleted successfully")
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ setConnectionToDelete(null)
+ },
+ onError: (error: unknown) => {
+ const errorMsg = getErrorMessage(
+ error,
+ "Failed to delete connection",
+ "connection",
+ "delete"
+ )
+ toast.error("Failed to delete connection", {
+ description: errorMsg,
+ })
+ },
+ })
+
+ const rotateMutation = useMutation({
+ mutationFn: (principalName: string) =>
+ principalsApi.rotateCredentials(principalName),
+ onSuccess: (data) => {
+ toast.success("Credentials rotated successfully")
+ setGeneratedCredentials({
+ clientId: data.credentials.clientId,
+ clientSecret: data.credentials.clientSecret,
+ })
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ },
+ onError: (error: unknown) => {
+ const errorMsg = getErrorMessage(
+ error,
+ "Failed to rotate credentials",
+ "credentials",
+ "rotate"
+ )
+ toast.error("Failed to rotate credentials", {
+ description: errorMsg,
+ })
+ },
+ })
+
+ const handleResetSuccess = (credentials: { clientId: string; clientSecret: string }) => {
+ setGeneratedCredentials(credentials)
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ setConnectionToReset(null)
+ }
+
+ const filteredPrincipals = principals.filter((principal) =>
+ principal.name.toLowerCase().includes(searchQuery.toLowerCase())
+ )
+
+
+ const handleCreateSuccess = (credentials: { clientId: string; clientSecret: string }) => {
+ setGeneratedCredentials(credentials)
+ setIsConfigureModalOpen(false)
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ }
+
+ return (
+ <div className="p-8">
+ {/* Header */}
+ <div className="mb-6">
+ <div className="flex items-center justify-between">
+ <div className="flex items-center gap-3">
+ <LinkIcon className="h-6 w-6 text-foreground" />
+ <h1 className="text-3xl font-bold text-foreground">Service Connections</h1>
+ </div>
+ <div className="flex items-center gap-3">
+ <div className="flex items-center gap-2">
+ <Input
+ placeholder="Q Search"
+ value={searchQuery}
+ onChange={(e) => setSearchQuery(e.target.value)}
+ className="w-48"
+ />
+ <Button variant="ghost" size="icon">
+ <Search className="h-4 w-4" />
+ </Button>
+ </div>
+ <Button variant="ghost" size="icon" onClick={() => queryClient.invalidateQueries({ queryKey: ["principals"] })}>
+ <RefreshCw className="h-4 w-4" />
+ </Button>
+ <Button
+ onClick={() => setIsConfigureModalOpen(true)}
+ variant="default"
+ >
+ <Plus className="mr-2 h-4 w-4" />
+ Connection
+ </Button>
+ </div>
+ </div>
+ </div>
+
+ {/* Table */}
+ <div className="rounded-lg border bg-card">
+ <Table>
+ <TableHeader>
+ <TableRow>
+ <TableHead>PRINCIPAL NAME</TableHead>
+ <TableHead>SERVICE</TableHead>
+ <TableHead>CREATED</TableHead>
+ <TableHead className="w-[50px]"></TableHead>
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {isLoading ? (
+ <TableRow>
+ <TableCell colSpan={4} className="text-center py-8 text-muted-foreground">
+ Loading...
+ </TableCell>
+ </TableRow>
+ ) : filteredPrincipals.length === 0 ? (
+ <TableRow>
+ <TableCell colSpan={4} className="text-center py-8 text-muted-foreground">
+ No connections found
+ </TableCell>
+ </TableRow>
+ ) : (
+ filteredPrincipals.map((principal) => {
+ // Cast to include timestamp fields that may be present in API response
+ const principalWithTimestamps = principal as Principal & {
+ createTimestamp?: number
+ lastUpdateTimestamp?: number
+ }
+ // Try createTimestamp first, then lastUpdateTimestamp as fallback
+ const timestamp =
+ principalWithTimestamps.createTimestamp ||
+ principalWithTimestamps.lastUpdateTimestamp ||
+ (typeof principalWithTimestamps.properties?.createTimestamp === "string"
+ ? parseInt(principalWithTimestamps.properties.createTimestamp, 10)
+ : typeof principalWithTimestamps.properties?.createTimestamp === "number"
+ ? principalWithTimestamps.properties.createTimestamp
+ : typeof principalWithTimestamps.properties?.lastUpdateTimestamp === "string"
+ ? parseInt(principalWithTimestamps.properties.lastUpdateTimestamp, 10)
+ : typeof principalWithTimestamps.properties?.lastUpdateTimestamp === "number"
+ ? principalWithTimestamps.properties.lastUpdateTimestamp
+ : undefined)
+
+ return (
+ <TableRow key={principal.name}>
+ <TableCell className="font-medium">{principal.name}</TableCell>
+ <TableCell>
+ <Badge variant="secondary">
+ {principal.properties?.serviceType || "apache-spark"}
Review Comment:
That's not a new concept and nothing store in Polaris server: it's specific feature from the Console: easily create a connection "string" to a Polaris server for a given query engine / tool.
If you go on the console, you will see the connection menu where you can create a "connection string". When you create, you specify the service type: Spark, flink, etc.
As this is Console specific without impact for the server, no change is needed outside of the Console.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2616763639
##########
console/src/pages/Connections.tsx:
##########
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useState } from "react"
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
+import { toast } from "sonner"
+import { formatDistanceToNow } from "date-fns"
+import { Link as LinkIcon, Plus, RefreshCw, Search, MoreVertical } from "lucide-react"
+import { principalsApi } from "@/api/management/principals"
+import { getErrorMessage } from "@/lib/errorHandler"
+import type { Principal } from "@/types/api"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Badge } from "@/components/ui/badge"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+import { ConfigureConnectionModal } from "@/components/forms/ConfigureConnectionModal"
+import { CredentialsModal } from "@/components/forms/CredentialsModal"
+import { PrincipalDetailsModal } from "@/components/users/PrincipalDetailsModal"
+import { EditPrincipalModal } from "@/components/forms/EditPrincipalModal"
+import { ResetCredentialsModal } from "@/components/forms/ResetCredentialsModal"
+
+export function Connections() {
+ const [searchQuery, setSearchQuery] = useState("")
+ const [isConfigureModalOpen, setIsConfigureModalOpen] = useState(false)
+ const [isCredentialsModalOpen, setIsCredentialsModalOpen] = useState(false)
+ const [connectionToDelete, setConnectionToDelete] = useState<string | null>(null)
+ const [connectionToReset, setConnectionToReset] = useState<string | null>(null)
+ const [principalToView, setPrincipalToView] = useState<Principal | null>(null)
+ const [principalToEdit, setPrincipalToEdit] = useState<Principal | null>(null)
+ const [generatedCredentials, setGeneratedCredentials] = useState<{
+ clientId: string
+ clientSecret: string
+ } | null>(null)
+
+ const queryClient = useQueryClient()
+
+ const { data: principals = [], isLoading } = useQuery({
+ queryKey: ["principals"],
+ queryFn: () => principalsApi.list(),
+ })
+
+ const deleteMutation = useMutation({
+ mutationFn: (principalName: string) => principalsApi.delete(principalName),
+ onSuccess: () => {
+ toast.success("Connection deleted successfully")
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ setConnectionToDelete(null)
+ },
+ onError: (error: unknown) => {
+ const errorMsg = getErrorMessage(
+ error,
+ "Failed to delete connection",
+ "connection",
+ "delete"
+ )
+ toast.error("Failed to delete connection", {
+ description: errorMsg,
+ })
+ },
+ })
+
+ const rotateMutation = useMutation({
+ mutationFn: (principalName: string) =>
+ principalsApi.rotateCredentials(principalName),
+ onSuccess: (data) => {
+ toast.success("Credentials rotated successfully")
+ setGeneratedCredentials({
+ clientId: data.credentials.clientId,
+ clientSecret: data.credentials.clientSecret,
+ })
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ },
+ onError: (error: unknown) => {
+ const errorMsg = getErrorMessage(
+ error,
+ "Failed to rotate credentials",
+ "credentials",
+ "rotate"
+ )
+ toast.error("Failed to rotate credentials", {
+ description: errorMsg,
+ })
+ },
+ })
+
+ const handleResetSuccess = (credentials: { clientId: string; clientSecret: string }) => {
+ setGeneratedCredentials(credentials)
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ setConnectionToReset(null)
+ }
+
+ const filteredPrincipals = principals.filter((principal) =>
+ principal.name.toLowerCase().includes(searchQuery.toLowerCase())
+ )
+
+
+ const handleCreateSuccess = (credentials: { clientId: string; clientSecret: string }) => {
+ setGeneratedCredentials(credentials)
+ setIsConfigureModalOpen(false)
+ setIsCredentialsModalOpen(true)
+ queryClient.invalidateQueries({ queryKey: ["principals"] })
+ }
+
+ return (
+ <div className="p-8">
+ {/* Header */}
+ <div className="mb-6">
+ <div className="flex items-center justify-between">
+ <div className="flex items-center gap-3">
+ <LinkIcon className="h-6 w-6 text-foreground" />
+ <h1 className="text-3xl font-bold text-foreground">Service Connections</h1>
+ </div>
+ <div className="flex items-center gap-3">
+ <div className="flex items-center gap-2">
+ <Input
+ placeholder="Q Search"
+ value={searchQuery}
+ onChange={(e) => setSearchQuery(e.target.value)}
+ className="w-48"
+ />
+ <Button variant="ghost" size="icon">
+ <Search className="h-4 w-4" />
+ </Button>
+ </div>
+ <Button variant="ghost" size="icon" onClick={() => queryClient.invalidateQueries({ queryKey: ["principals"] })}>
+ <RefreshCw className="h-4 w-4" />
+ </Button>
+ <Button
+ onClick={() => setIsConfigureModalOpen(true)}
+ variant="default"
+ >
+ <Plus className="mr-2 h-4 w-4" />
+ Connection
+ </Button>
+ </div>
+ </div>
+ </div>
+
+ {/* Table */}
+ <div className="rounded-lg border bg-card">
+ <Table>
+ <TableHeader>
+ <TableRow>
+ <TableHead>PRINCIPAL NAME</TableHead>
+ <TableHead>SERVICE</TableHead>
+ <TableHead>CREATED</TableHead>
+ <TableHead className="w-[50px]"></TableHead>
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {isLoading ? (
+ <TableRow>
+ <TableCell colSpan={4} className="text-center py-8 text-muted-foreground">
+ Loading...
+ </TableCell>
+ </TableRow>
+ ) : filteredPrincipals.length === 0 ? (
+ <TableRow>
+ <TableCell colSpan={4} className="text-center py-8 text-muted-foreground">
+ No connections found
+ </TableCell>
+ </TableRow>
+ ) : (
+ filteredPrincipals.map((principal) => {
+ // Cast to include timestamp fields that may be present in API response
+ const principalWithTimestamps = principal as Principal & {
+ createTimestamp?: number
+ lastUpdateTimestamp?: number
+ }
+ // Try createTimestamp first, then lastUpdateTimestamp as fallback
+ const timestamp =
+ principalWithTimestamps.createTimestamp ||
+ principalWithTimestamps.lastUpdateTimestamp ||
+ (typeof principalWithTimestamps.properties?.createTimestamp === "string"
+ ? parseInt(principalWithTimestamps.properties.createTimestamp, 10)
+ : typeof principalWithTimestamps.properties?.createTimestamp === "number"
+ ? principalWithTimestamps.properties.createTimestamp
+ : typeof principalWithTimestamps.properties?.lastUpdateTimestamp === "string"
+ ? parseInt(principalWithTimestamps.properties.lastUpdateTimestamp, 10)
+ : typeof principalWithTimestamps.properties?.lastUpdateTimestamp === "number"
+ ? principalWithTimestamps.properties.lastUpdateTimestamp
+ : undefined)
+
+ return (
+ <TableRow key={principal.name}>
+ <TableCell className="font-medium">{principal.name}</TableCell>
+ <TableCell>
+ <Badge variant="secondary">
+ {principal.properties?.serviceType || "apache-spark"}
Review Comment:
I think I was not clear :)
The Principal created in the connection is a "regular" Principal that user can create. The service type property is just a way for the UI to identify the principals for a connection.
For instance, an user can create a principal (using CLI or REST endpoint), adding the service-type property to have this principal in the UI "attached" to a connection.
As the UI has no storage, you can see the service-type property as an UI specific property just to display principals related to a connection. It's exactly the same as an user adds his own principal property for his use case.
So, no new principal, no new concept, just a "hack" in the UI to link principals to connection view.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "flyrain (via GitHub)" <gi...@apache.org>.
flyrain commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2587095164
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
+
+// Log OAuth URL in development only
+if (import.meta.env.DEV) {
+ console.log("π Using OAuth token URL:", TOKEN_URL)
+}
+
+export const authApi = {
+ getToken: async (
+ clientId: string,
+ clientSecret: string,
+ realm?: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "client_credentials")
+ formData.append("client_id", clientId)
+ formData.append("client_secret", clientSecret)
+ formData.append("scope", "PRINCIPAL_ROLE:ALL")
+
+ const headers: Record<string, string> = {
+ "Content-Type": "application/x-www-form-urlencoded",
+ }
+
+ // Add realm header if provided
+ if (realm) {
+ headers[REALM_HEADER_NAME] = realm
+ }
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers,
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ exchangeToken: async (
+ subjectToken: string,
+ subjectTokenType: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
+ formData.append("subject_token", subjectToken)
+ formData.append("subject_token_type", subjectTokenType)
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ Authorization: `Bearer ${apiClient.getAccessToken()}`,
+ },
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ refreshToken: async (accessToken: string): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")
+ formData.append("subject_token", accessToken)
+ formData.append("subject_token_type", "urn:ietf:params:oauth:token-type:access_token")
+
+ const response = await axios.post<OAuthTokenResponse>(
+ TOKEN_URL,
+ formData,
+ {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ },
+ }
+ )
+
+ if (response.data.access_token) {
+ apiClient.setAccessToken(response.data.access_token)
+ }
+
+ return response.data
+ },
+
+ logout: (): void => {
+ localStorage.removeItem("polaris_access_token")
Review Comment:
Can we file an issue for this? Thanks!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575769323
##########
console/Dockerfile:
##########
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Build stage
+FROM node:20-alpine AS builder
+
+WORKDIR /app
+
+# Copy package files
+COPY package*.json ./
+
+# Install dependencies
+RUN npm ci --only=production=false
Review Comment:
Let me check there.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "adam-christian-software (via GitHub)" <gi...@apache.org>.
adam-christian-software commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2578543691
##########
console/src/api/client.ts:
##########
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios, { type AxiosInstance, type InternalAxiosRequestConfig } from "axios"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+
+// Always use relative URLs to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const API_BASE_URL = ""
+const MANAGEMENT_BASE_URL = `${API_BASE_URL}/api/management/v1`
+const CATALOG_BASE_URL = `${API_BASE_URL}/api/catalog/v1`
+const POLARIS_BASE_URL = `${API_BASE_URL}/polaris/v1`
+
+class ApiClient {
+ private managementClient: AxiosInstance
+ private catalogClient: AxiosInstance
+ private polarisClient: AxiosInstance
+
+ constructor() {
+ this.managementClient = axios.create({
+ baseURL: MANAGEMENT_BASE_URL,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+
+ this.catalogClient = axios.create({
+ baseURL: CATALOG_BASE_URL,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+
+ this.polarisClient = axios.create({
+ baseURL: POLARIS_BASE_URL,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ })
+
+ this.setupInterceptors()
+ }
+
+ private setupInterceptors() {
+ // Request interceptor to add auth token
+ const requestInterceptor = (config: InternalAxiosRequestConfig) => {
+ const token = this.getAccessToken()
+ // Read realm from localStorage, fallback to environment variable for backward compatibility
+ const realm = localStorage.getItem("polaris_realm") || import.meta.env.VITE_POLARIS_REALM
+
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`
+ }
+
+ if (realm) {
+ config.headers[REALM_HEADER_NAME] = realm
+ }
+
+ return config
+ }
+
+ // Response interceptor for error handling
+ const responseErrorInterceptor = (error: unknown) => {
Review Comment:
Should we have a way to do a retry for transient failures?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "MonkeyCanCode (via GitHub)" <gi...@apache.org>.
MonkeyCanCode commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575465227
##########
console/README.md:
##########
@@ -0,0 +1,130 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+# Polaris Console
+
+A modern web interface for Apache Polaris, built with React, TypeScript, TanStack Query, and Tailwind CSS.
+
+## Getting Started
+
+### Prerequisites
+- Node.js 18+ and npm (or yarn)
+
+### Installation
+
+```bash
+# Install dependencies
+npm install
+
+# Start development server
+npm run dev
+
+# Build for production
+npm run build
+```
+
+### Environment Variables
+
+Create a `.env` file based on `.env.example`:
+
+```env
+VITE_POLARIS_API_URL=http://localhost:8181
+VITE_POLARIS_REALM=POLARIS
+VITE_POLARIS_REALM_HEADER_NAME=Polaris-Realm # optional, defaults to "Polaris-Realm"
+VITE_OAUTH_TOKEN_URL=http://localhost:8181/api/v1/oauth/tokens # optional
+```
+
+## Project Structure
+
+```
+src/
+βββ api/ # API client and endpoints
+β βββ client.ts # Axios instance with interceptors
+β βββ auth.ts # Authentication API
+β βββ management/ # Management Service APIs
+βββ components/ # React components
+β βββ ui/ # Shadcn UI components
+β βββ layout/ # Layout components
+β βββ forms/ # Form components
+βββ hooks/ # Custom React hooks
+βββ lib/ # Utilities
+βββ pages/ # Page components
+βββ types/ # TypeScript type definitions
+βββ App.tsx # Main app component
+```
+
+## Technology Stack
+
+- **Framework**: React 19 with TypeScript
+- **Routing**: React Router v7
+- **State Management**: TanStack Query (React Query)
+- **Tables**: TanStack Table (React Table)
+- **Styling**: Tailwind CSS
+- **Components**: Shadcn UI (Radix UI primitives)
+- **Forms**: React Hook Form with Zod validation
+- **HTTP Client**: Axios
+- **Icons**: Lucide React
+
+## Development
+
+The project uses:
+- Vite for fast development and building
+- ESLint for code linting
+- TypeScript for type safety
+
+To start developing:
+
+```bash
+npm run dev
+```
+
+The app will be available at `http://localhost:5173`
+
+## Building
+
+```bash
+npm run build
+```
+
+Output will be in the `dist/` directory.
+
+## Production Deployment
+
+After building, you can serve the production files in several ways:
+
+### Quick Test
+```bash
+bun run preview # or npm run preview
+```
+
+## Docker image
+
+You can build Polaris Console docker image using:
+
+```
+docker build -t apache/polaris-console:latest .
+```
+
+Then, you run Polaris Console using:
+
+```
+docker run -p 8080:80 apache/polaris-console:latest
Review Comment:
Does it makes sense to add a docker compose so people can easily try out? As the console and server are running in diff networks, we will need to either bridge it from local or put them in a virtual network with docker network.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "dev1-onesto (via GitHub)" <gi...@apache.org>.
dev1-onesto commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575737639
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
Review Comment:
@flyrain i have implemented 3 rd party IDPs support in here https://github.com/jbonofre/polaris-tools/tree/keycloak-support i need to help me with testing this one
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
Review Comment:
@flyrain i have implemented 3 rd party IDPs support in here https://github.com/jbonofre/polaris-tools/tree/keycloak-support i need to help me with testing this one
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "jbonofre (via GitHub)" <gi...@apache.org>.
jbonofre commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2575772651
##########
console/src/api/auth.ts:
##########
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import axios from "axios"
+import { apiClient } from "./client"
+import { navigate } from "@/lib/navigation"
+import { REALM_HEADER_NAME } from "@/lib/constants"
+import type { OAuthTokenResponse } from "@/types/api"
+
+// Always use relative URL to go through the proxy (dev server or production server)
+// This avoids CORS issues by proxying requests through the server
+// The server.ts proxy handles /api routes in production, and Vite handles them in development
+const TOKEN_URL = "/api/catalog/v1/oauth/tokens"
+
+// Log OAuth URL in development only
+if (import.meta.env.DEV) {
+ console.log("π Using OAuth token URL:", TOKEN_URL)
+}
+
+export const authApi = {
+ getToken: async (
+ clientId: string,
+ clientSecret: string,
+ realm?: string
+ ): Promise<OAuthTokenResponse> => {
+ const formData = new URLSearchParams()
+ formData.append("grant_type", "client_credentials")
+ formData.append("client_id", clientId)
+ formData.append("client_secret", clientSecret)
+ formData.append("scope", "PRINCIPAL_ROLE:ALL")
Review Comment:
Good point. Let's do that.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "adam-christian-software (via GitHub)" <gi...@apache.org>.
adam-christian-software commented on code in PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#discussion_r2581408691
##########
console/package.json:
##########
@@ -0,0 +1,58 @@
+{
+ "name": "polaris-console",
+ "private": true,
+ "version": "1.3.0-incubating",
+ "type": "module",
+ "scripts": {
Review Comment:
Sounds good! I just want to ensure they are tracked. Thanks, @sohanhonavar & @jbonofre, y'all can close this comment when you track it on your side. :)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "snazy (via GitHub)" <gi...@apache.org>.
snazy commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3627070866
Only two general observations from my side:
* The built `dist/` folder content is not reproducible (even with a lock file and `npm ci` beforehands)
* I miss tests
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] Introducing Apache Polaris Console [polaris-tools]
Posted by "MonkeyCanCode (via GitHub)" <gi...@apache.org>.
MonkeyCanCode commented on PR #85:
URL: https://github.com/apache/polaris-tools/pull/85#issuecomment-3623713033
Local testing works now with latest commit and I am able to integrated it with Polaris server in helm as well. There are couple files missed license headers. Other than this, same as @dimas-b , I am not very familiar with the framework as well.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org