Skip to content

Commit

Permalink
Merge pull request #332 from edlerd/use-relative-ui-paths
Browse files Browse the repository at this point in the history
fix: ui uses relative base paths.
  • Loading branch information
edlerd authored Jun 21, 2024
2 parents e857604 + 85da4c0 commit d627f82
Show file tree
Hide file tree
Showing 15 changed files with 533 additions and 48 deletions.
2 changes: 1 addition & 1 deletion ui/entrypoint
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#! /usr/bin/env bash
set -e

echo "Listening on http://localhost:8411"
echo "Listening on http://localhost:8411/ui/"

haproxy -f haproxy.cfg -db
6 changes: 4 additions & 2 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"hooks-add": "husky install",
"hooks-remove": "husky uninstall",
"start": "concurrently --kill-others --raw 'yarn dev-serve' 'yarn proxy-serve'",
"test-js": "vitest --run",
"dev-serve": "vite --host | grep -v localhost",
"proxy-serve": "./entrypoint"
},
Expand Down Expand Up @@ -67,8 +68,9 @@
"stylelint-prettier": "5.0.0",
"stylelint-scss": "6.2.1",
"typescript": "5.4.3",
"vite": "^5.2.2",
"vite-tsconfig-paths": "4.3.2"
"vite": "5.2.8",
"vite-tsconfig-paths": "4.3.2",
"vitest": "1.2.1"
},
"lint-staged": {
"src/**/*.{json,jsx,ts,tsx}": [
Expand Down
3 changes: 2 additions & 1 deletion ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SchemaList from "pages/schemas/SchemaList";
import Navigation from "components/Navigation";
import Panels from "components/Panels";
import useLocalStorage from "util/useLocalStorage";
import { apiBasePath } from "util/basePaths";

const App: FC = () => {
// Store a user token that will be passed to the API using the
Expand Down Expand Up @@ -50,7 +51,7 @@ const App: FC = () => {
path="/*"
element={
<ReBACAdmin
apiURL={import.meta.env.VITE_API_URL}
apiURL={apiBasePath}
asidePanelId="rebac-admin-panel"
authToken={authUser?.token}
/>
Expand Down
13 changes: 6 additions & 7 deletions ui/src/api/client.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Client } from "types/client";
import { ApiResponse, PaginatedResponse } from "types/api";
import { handleResponse, PAGE_SIZE } from "util/api";
import { apiBasePath } from "util/basePaths";

export const fetchClients = (
pageToken: string,
): Promise<PaginatedResponse<Client[]>> => {
return new Promise((resolve, reject) => {
fetch(
`${import.meta.env.VITE_API_URL}/clients?page_token=${pageToken}&size=${PAGE_SIZE}`,
)
fetch(`${apiBasePath}clients?page_token=${pageToken}&size=${PAGE_SIZE}`)
.then(handleResponse)
.then((result: PaginatedResponse<Client[]>) => resolve(result))
.catch(reject);
Expand All @@ -17,7 +16,7 @@ export const fetchClients = (

export const fetchClient = (clientId: string): Promise<Client> => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/clients/${clientId}`)
fetch(`${apiBasePath}clients/${clientId}`)
.then(handleResponse)
.then((result: ApiResponse<Client>) => resolve(result.data))
.catch(reject);
Expand All @@ -26,7 +25,7 @@ export const fetchClient = (clientId: string): Promise<Client> => {

export const createClient = (values: string): Promise<Client> => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/clients`, {
fetch(`${apiBasePath}clients`, {
method: "POST",
body: values,
})
Expand All @@ -41,7 +40,7 @@ export const updateClient = (
values: string,
): Promise<Client> => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/clients/${clientId}`, {
fetch(`${apiBasePath}clients/${clientId}`, {
method: "PUT",
body: values,
})
Expand All @@ -53,7 +52,7 @@ export const updateClient = (

export const deleteClient = (client: string) => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/clients/${client}`, {
fetch(`${apiBasePath}clients/${client}`, {
method: "DELETE",
})
.then(resolve)
Expand Down
11 changes: 6 additions & 5 deletions ui/src/api/identities.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { ApiResponse, PaginatedResponse } from "types/api";
import { handleResponse, PAGE_SIZE } from "util/api";
import { Identity } from "types/identity";
import { apiBasePath } from "util/basePaths";

export const fetchIdentities = (
pageToken: string,
): Promise<PaginatedResponse<Identity[]>> => {
return new Promise((resolve, reject) => {
fetch(`/api/v0/identities?page_token=${pageToken}&size=${PAGE_SIZE}`)
fetch(`${apiBasePath}identities?page_token=${pageToken}&size=${PAGE_SIZE}`)
.then(handleResponse)
.then((result: PaginatedResponse<Identity[]>) => resolve(result))
.catch(reject);
Expand All @@ -15,7 +16,7 @@ export const fetchIdentities = (

export const fetchIdentity = (identityId: string): Promise<Identity> => {
return new Promise((resolve, reject) => {
fetch(`/api/v0/identities/${identityId}`)
fetch(`${apiBasePath}identities/${identityId}`)
.then(handleResponse)
.then((result: ApiResponse<Identity[]>) => resolve(result.data[0]))
.catch(reject);
Expand All @@ -24,7 +25,7 @@ export const fetchIdentity = (identityId: string): Promise<Identity> => {

export const createIdentity = (body: string): Promise<void> => {
return new Promise((resolve, reject) => {
fetch("/api/v0/identities", {
fetch(`${apiBasePath}identities`, {
method: "POST",
body: body,
})
Expand All @@ -39,7 +40,7 @@ export const updateIdentity = (
values: string,
): Promise<Identity> => {
return new Promise((resolve, reject) => {
fetch(`/api/v0/identities/${identityId}`, {
fetch(`${apiBasePath}identities/${identityId}`, {
method: "PATCH",
body: values,
})
Expand All @@ -51,7 +52,7 @@ export const updateIdentity = (

export const deleteIdentity = (identityId: string) => {
return new Promise((resolve, reject) => {
fetch(`/api/v0/identities/${identityId}`, {
fetch(`${apiBasePath}identities/${identityId}`, {
method: "DELETE",
})
.then(resolve)
Expand Down
17 changes: 9 additions & 8 deletions ui/src/api/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
import { ApiResponse } from "types/api";
import { handleResponse } from "util/api";
import { IdentityProvider } from "types/provider";
import { apiBasePath } from "util/basePaths";

export const fetchProviders = (): Promise<IdentityProvider[]> => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/idps`)
fetch(`${apiBasePath}idps`)
.then(handleResponse)
.then((result: ApiResponse<IdentityProvider[]>) => resolve(result.data))
.catch(reject);
});
};

export const fetchProvider = (
providerId: string
providerId: string,
): Promise<IdentityProvider> => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/idps/${providerId}`)
fetch(`${apiBasePath}idps/${providerId}`)
.then(handleResponse)
.then((result: ApiResponse<IdentityProvider[]>) =>
resolve(result.data[0])
resolve(result.data[0]),
)
.catch(reject);
});
};

export const createProvider = (body: string): Promise<void> => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/idps`, {
fetch(`${apiBasePath}idps`, {
method: "POST",
body: body,
})
Expand All @@ -38,10 +39,10 @@ export const createProvider = (body: string): Promise<void> => {

export const updateProvider = (
providerId: string,
values: string
values: string,
): Promise<IdentityProvider> => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/idps/${providerId}`, {
fetch(`${apiBasePath}idps/${providerId}`, {
method: "PATCH",
body: values,
})
Expand All @@ -53,7 +54,7 @@ export const updateProvider = (

export const deleteProvider = (providerId: string) => {
return new Promise((resolve, reject) => {
fetch(`${import.meta.env.VITE_API_URL}/idps/${providerId}`, {
fetch(`${apiBasePath}idps/${providerId}`, {
method: "DELETE",
})
.then(resolve)
Expand Down
13 changes: 8 additions & 5 deletions ui/src/api/schema.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { ApiResponse, PaginatedResponse } from "types/api";
import { handleResponse, PAGE_SIZE } from "util/api";
import { Schema } from "types/schema";
import { apiBasePath } from "util/basePaths";

export const fetchSchemas = (
pageToken: string,
): Promise<PaginatedResponse<Schema[]>> => {
return new Promise((resolve, reject) => {
fetch(`/api/v0/schemas?page_token=${pageToken}&page_size=${PAGE_SIZE}`)
fetch(
`${apiBasePath}schemas?page_token=${pageToken}&page_size=${PAGE_SIZE}`,
)
.then(handleResponse)
.then((result: PaginatedResponse<Schema[]>) => resolve(result))
.catch(reject);
Expand All @@ -15,7 +18,7 @@ export const fetchSchemas = (

export const fetchSchema = (schemaId: string): Promise<Schema> => {
return new Promise((resolve, reject) => {
fetch(`/api/v0/schemas/${schemaId}`)
fetch(`${apiBasePath}schemas/${schemaId}`)
.then(handleResponse)
.then((result: ApiResponse<Schema[]>) => resolve(result.data[0]))
.catch(reject);
Expand All @@ -24,7 +27,7 @@ export const fetchSchema = (schemaId: string): Promise<Schema> => {

export const createSchema = (body: string): Promise<void> => {
return new Promise((resolve, reject) => {
fetch("/api/v0/schemas", {
fetch(`${apiBasePath}schemas`, {
method: "POST",
body: body,
})
Expand All @@ -39,7 +42,7 @@ export const updateSchema = (
values: string,
): Promise<Schema> => {
return new Promise((resolve, reject) => {
fetch(`/api/v0/schemas/${schemaId}`, {
fetch(`${apiBasePath}schemas/${schemaId}`, {
method: "PATCH",
body: values,
})
Expand All @@ -51,7 +54,7 @@ export const updateSchema = (

export const deleteSchema = (schemaId: string) => {
return new Promise((resolve, reject) => {
fetch(`/api/v0/schemas/${schemaId}`, {
fetch(`${apiBasePath}schemas/${schemaId}`, {
method: "DELETE",
})
.then(resolve)
Expand Down
8 changes: 4 additions & 4 deletions ui/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<NavLink
className="p-side-navigation__link"
to={`/provider`}
to="/provider"
title={`Provider list`}
>
<Icon
Expand All @@ -50,7 +50,7 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<NavLink
className="p-side-navigation__link"
to={`/client`}
to="/client"
title={`Client list`}
>
<Icon
Expand All @@ -63,7 +63,7 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<NavLink
className="p-side-navigation__link"
to={`/identity`}
to="/identity"
title={`Identity list`}
>
<Icon
Expand All @@ -76,7 +76,7 @@ const Navigation: FC<Props> = ({ username, logout }) => {
<li className="p-side-navigation__item secondary">
<NavLink
className="p-side-navigation__link"
to={`/schema`}
to="/schema"
title={`Schema list`}
>
<Icon
Expand Down
3 changes: 2 additions & 1 deletion ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import App from "./App";
import "./sass/styles.scss";
import { NotificationProvider } from "@canonical/react-components";
import { basePath } from "util/basePaths";

const queryClient = new QueryClient();

const rootElement = document.getElementById("app");
if (!rootElement) throw new Error("Failed to find the root element");
const root = createRoot(rootElement);
root.render(
<Router>
<Router basename={basePath}>
<QueryClientProvider client={queryClient}>
<NotificationProvider>
<App />
Expand Down
4 changes: 2 additions & 2 deletions ui/src/pages/clients/ClientEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const ClientEdit: FC = () => {
void queryClient.invalidateQueries({
queryKey: [queryKeys.clients],
});
navigate(`/client`, notify.queue(notify.success("Client updated")));
navigate("/client", notify.queue(notify.success("Client updated")));
})
.catch((e) => {
formik.setSubmitting(false);
Expand Down Expand Up @@ -87,7 +87,7 @@ const ClientEdit: FC = () => {
<Button
appearance="base"
className="u-no-margin--bottom u-sv2"
onClick={() => navigate(`/client`)}
onClick={() => navigate("/client")}
>
Cancel
</Button>
Expand Down
33 changes: 33 additions & 0 deletions ui/src/util/basePaths.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { calculateBasePath } from "./basePaths";

describe("calculateBasePath", () => {
it("resolves with ui path", () => {
vi.stubGlobal("location", { pathname: "/ui/" });
const result = calculateBasePath();
expect(result).toBe("/ui/");
});

it("resolves with ui path and discards detail page location", () => {
vi.stubGlobal("location", { pathname: "/ui/foo/bar" });
const result = calculateBasePath();
expect(result).toBe("/ui/");
});

it("resolves with prefixed ui path", () => {
vi.stubGlobal("location", { pathname: "/prefix/ui/" });
const result = calculateBasePath();
expect(result).toBe("/prefix/ui/");
});

it("resolves with prefixed ui path on a detail page", () => {
vi.stubGlobal("location", { pathname: "/prefix/ui/foo/bar/baz" });
const result = calculateBasePath();
expect(result).toBe("/prefix/ui/");
});

it("resolves with root path if /ui/ is not part of the pathname", () => {
vi.stubGlobal("location", { pathname: "/foo/bar/baz" });
const result = calculateBasePath();
expect(result).toBe("/");
});
});
14 changes: 14 additions & 0 deletions ui/src/util/basePaths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
type BasePath = `/${string}`;

export const calculateBasePath = (): BasePath => {
const path = window.location.pathname;
// find first occurrence of /ui/ and return the string before it
const basePath = path.match(/(.*\/ui\/)/);
if (basePath) {
return basePath[0] as BasePath;
}
return "/";
};

export const basePath: BasePath = calculateBasePath();
export const apiBasePath: BasePath = `${basePath}../api/v0/`;
5 changes: 3 additions & 2 deletions ui/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@
"isolatedModules": true,
"noEmit": false,
"sourceMap": true,
"types": ["vite/client"]
"types": ["vite/client", "vitest/globals"]
},
"include": [
"./src",
".eslintrc.js",
"babel.config.js",
".eslintrc.cjs",
"vite.config.ts"
"vite.config.ts",
"vitest.config.ts"
]
}
Loading

0 comments on commit d627f82

Please sign in to comment.