1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-12-21 20:06:40 +01:00

chore: update the UI to use project-based context endpoints when on a project endpoint (#11153)

Update the context field hooks to accept a project ID and to change
their API endpoints and cache keys based on the project ID.

Updating the URL in this many places (API example, multiple hooks, form
actions) feels kinda brittle and I wish there was a better way to do
this, but I don't have any bright ideas right at this moment. We could,
of course, extract a function that creates it, but that's also brittle
because they're all a little different 🤔
This commit is contained in:
Thomas Heartman 2025-12-16 16:21:35 +01:00 committed by GitHub
parent 4ef2050669
commit 7369977b23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 52 additions and 28 deletions

View File

@ -33,9 +33,15 @@ const ContextList: FC = () => {
const projectContextFieldsEnabled = useUiFlag('projectContextFields');
const [showDelDialogue, setShowDelDialogue] = useState(false);
const [name, setName] = useState<string>();
const { context, refetchUnleashContext, loading } = useUnleashContext();
const { removeContext } = useContextsApi();
const { context, refetchUnleashContext, loading } = useUnleashContext(
undefined,
projectId,
);
const { removeContext } = useContextsApi(projectId);
const { setToastData, setToastApiError } = useToast();
const editUrl = projectId
? `/projects/${projectId}/context/${name}`
: `/context/edit/${name}`;
const data = useMemo(() => {
if (loading) {
@ -45,14 +51,7 @@ const ContextList: FC = () => {
});
}
const filteredContextFields =
projectId && projectContextFieldsEnabled
? // @ts-expect-error project doesn't exist yet; todo: fix with flag projectContextFields
context.filter((c) => c.project === projectId)
: // @ts-expect-error project doesn't exist yet; todo: fix with flag projectContextFields
context.filter((c) => !c.project);
return filteredContextFields
return context
.map(
({
name,
@ -89,7 +88,7 @@ const ContextList: FC = () => {
}: any) => (
<LinkCell
title={name}
to={`/context/edit/${name}`}
to={editUrl}
subtitle={description}
/>
),

View File

@ -40,8 +40,8 @@ export const CreateUnleashContext = ({
setErrors,
errors,
} = useContextForm({ initialProject: projectId });
const { createContext, loading } = useContextsApi();
const { refetchUnleashContext } = useUnleashContext();
const { createContext, loading } = useContextsApi(projectId);
const { refetchUnleashContext } = useUnleashContext(undefined, projectId);
const handleSubmit = async (e: Event) => {
e.preventDefault();
@ -64,8 +64,12 @@ export const CreateUnleashContext = ({
}
};
const postTarget = projectId
? `/api/admin/projects/${projectId}/context`
: '/api/admin/context';
const formatApiCode = () => {
return `curl --location --request POST '${uiConfig.unleashUrl}/api/admin/context' \\
return `curl --location --request POST '${uiConfig.unleashUrl}${postTarget}' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${JSON.stringify(getContextPayload(), undefined, 2)}'`;

View File

@ -29,8 +29,8 @@ export const EditContext: FC<EditContextProps> = ({ modal }) => {
const { setToastData, setToastApiError } = useToast();
const projectId = useOptionalPathParam('projectId');
const name = useRequiredPathParam('name');
const { context, refetch } = useContext(name);
const { updateContext, loading } = useContextsApi();
const { context, refetch } = useContext({ name, project: projectId });
const { updateContext, loading } = useContextsApi(projectId);
const navigate = useNavigate();
const {
contextName,
@ -53,10 +53,13 @@ export const EditContext: FC<EditContextProps> = ({ modal }) => {
initialProject: projectId,
});
const apiUrl = projectId
? `/projects/${projectId}/api/admin/context/${name}`
: `/api/admin/context/${name}`;
const formatApiCode = () => {
return `curl --location --request PUT '${
uiConfig.unleashUrl
}/api/admin/context/${name}' \\
}${apiUrl}' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${JSON.stringify(getContextPayload(), undefined, 2)}'`;
@ -65,8 +68,8 @@ export const EditContext: FC<EditContextProps> = ({ modal }) => {
const handleSubmit = async (e: Event) => {
e.preventDefault();
const payload = getContextPayload();
const navigationTarget = payload.project
? `/projects/${payload.project}/settings/context-fields`
const navigationTarget = projectId
? `/projects/${projectId}/settings/context-fields`
: '/context';
try {

View File

@ -24,7 +24,7 @@ export const useContextForm = ({
const [stickiness, setStickiness] = useState(initialStickiness);
const [project, setProject] = useState(initialProject);
const [errors, setErrors] = useState({});
const { validateContextName } = useContextsApi();
const { validateContextName } = useContextsApi(project);
useEffect(() => {
setContextName(initialContextName);
@ -49,7 +49,6 @@ export const useContextForm = ({
description: contextDesc,
legalValues,
stickiness,
project,
};
};

View File

@ -1,11 +1,13 @@
import useAPI from '../useApi/useApi.js';
const useContextsApi = () => {
const useContextsApi = (projectId?: string) => {
const { makeRequest, createRequest, errors, loading } = useAPI({
propagateErrors: true,
});
const URI = 'api/admin/context';
const URI = projectId
? `api/admin/projects/${projectId}/context`
: 'api/admin/context';
const validateContextName = async (name: string) => {
const path = `${URI}/validate`;

View File

@ -3,9 +3,21 @@ import { useState, useEffect } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler.js';
const useContext = (name: string, options: SWRConfiguration = {}) => {
type ContextInfo = {
name: string;
project?: string;
};
const useContext = (
{ name, project }: ContextInfo,
options: SWRConfiguration = {},
) => {
const uri = project
? `api/admin/projects/${project}/context/${name}`
: `api/admin/context/${name}`;
const fetcher = async () => {
const path = formatApiPath(`api/admin/context/${name}`);
const path = formatApiPath(uri);
return fetch(path, {
method: 'GET',
})
@ -13,7 +25,7 @@ const useContext = (name: string, options: SWRConfiguration = {}) => {
.then((res) => res.json());
};
const FEATURE_CACHE_KEY = `api/admin/context/${name}`;
const FEATURE_CACHE_KEY = uri;
const { data, error } = useSWR(FEATURE_CACHE_KEY, fetcher, {
...options,

View File

@ -16,9 +16,14 @@ const useUnleashContext = (
revalidateOnReconnect: true,
revalidateIfStale: true,
},
projectId?: string,
): IUnleashContextOutput => {
const uri = projectId
? formatApiPath(`api/admin/projects/${projectId}/context`)
: formatApiPath(`api/admin/context`);
const fetcher = () => {
const path = formatApiPath(`api/admin/context`);
const path = formatApiPath(uri);
return fetch(path, {
method: 'GET',
})
@ -26,7 +31,7 @@ const useUnleashContext = (
.then((res) => res.json());
};
const CONTEXT_CACHE_KEY = 'api/admin/context';
const CONTEXT_CACHE_KEY = uri;
const { data, mutate, error, isValidating } = useSWR(
CONTEXT_CACHE_KEY,