diff --git a/client/package.json b/client/package.json
index e4b48e3391..cea0aff8ad 100644
--- a/client/package.json
+++ b/client/package.json
@@ -22,7 +22,7 @@
"storybook-wait-server": "wait-on http://127.0.0.1:6006",
"storybook-test": "test-storybook",
"storybook-compile-and-test": "concurrently -k -s first -n 'BUILD,TEST' -c 'magenta,blue' 'npm run storybook-build && npm run storybook-start-server' 'npm run storybook-wait-server && npm run storybook-test'",
- "generate-api": "npm run generate-api:dataServicesUser && npm run generate-api:namespaceV2 && npm run generate-api:projectV2 && generate-api:platform && npm run generate-api:searchV2",
+ "generate-api": "npm run generate-api:dataServicesUser && npm run generate-api:namespaceV2 && npm run generate-api:projectV2 && npm run generate-api:platform && npm run generate-api:searchV2",
"generate-api:dataServicesUser": "rtk-query-codegen-openapi src/features/user/dataServicesUser.api/dataServicesUser.api-config.ts",
"generate-api:namespaceV2": "rtk-query-codegen-openapi src/features/projectsV2/api/namespace.api-config.ts",
"generate-api:projectV2": "rtk-query-codegen-openapi src/features/projectsV2/api/projectV2.api-config.ts",
diff --git a/client/src/features/ProjectPageV2/ProjectPageContent/ProjectInformation/ProjectInformation.tsx b/client/src/features/ProjectPageV2/ProjectPageContent/ProjectInformation/ProjectInformation.tsx
index e847cf2ec0..632b9dfadf 100644
--- a/client/src/features/ProjectPageV2/ProjectPageContent/ProjectInformation/ProjectInformation.tsx
+++ b/client/src/features/ProjectPageV2/ProjectPageContent/ProjectInformation/ProjectInformation.tsx
@@ -34,7 +34,7 @@ import {
useGetNamespacesByNamespaceSlugQuery,
useGetProjectsByProjectIdMembersQuery,
} from "../../../projectsV2/api/projectV2.enhanced-api";
-import { useGetUsersByUserIdQuery } from "../../../user/dataServicesUser.api";
+import { useGetUsersByUserIdQuery } from "../../../user/dataServicesUser.api/dataServicesUser.api";
import { useProject } from "../../ProjectPageContainer/ProjectPageContainer";
import MembershipGuard from "../../utils/MembershipGuard";
import { toSortedMembers } from "../../utils/roleUtils";
diff --git a/client/src/features/ProjectPageV2/utils/MembershipGuard.tsx b/client/src/features/ProjectPageV2/utils/MembershipGuard.tsx
index 0e523ac04f..88943dbb67 100644
--- a/client/src/features/ProjectPageV2/utils/MembershipGuard.tsx
+++ b/client/src/features/ProjectPageV2/utils/MembershipGuard.tsx
@@ -17,14 +17,15 @@
*/
import { skipToken } from "@reduxjs/toolkit/query";
-import { useGetUserQuery } from "../../../features/user/dataServicesUser.api";
+
+import { useGetUserQuery } from "../../../features/user/dataServicesUser.api/dataServicesUser.api";
import useLegacySelector from "../../../utils/customHooks/useLegacySelector.hook";
import type {
ProjectMemberListResponse,
ProjectMemberResponse,
-} from "../../projectsV2/api/projectV2.api.ts";
-import AccessGuard from "./AccessGuard.tsx";
-import { toNumericRole } from "./roleUtils.ts";
+} from "../../projectsV2/api/projectV2.api";
+import AccessGuard from "./AccessGuard";
+import { toNumericRole } from "./roleUtils";
interface SelfOverride {
disabled: React.ReactNode | undefined;
diff --git a/client/src/features/ProjectPageV2/utils/useProjectAccess.hook.ts b/client/src/features/ProjectPageV2/utils/useProjectAccess.hook.ts
index c378c7a482..07678e2569 100644
--- a/client/src/features/ProjectPageV2/utils/useProjectAccess.hook.ts
+++ b/client/src/features/ProjectPageV2/utils/useProjectAccess.hook.ts
@@ -17,10 +17,11 @@
*/
import { skipToken } from "@reduxjs/toolkit/query";
-import { useGetUserQuery } from "../../user/dataServicesUser.api/index.ts";
-import useLegacySelector from "../../../utils/customHooks/useLegacySelector.hook.ts";
-import type { Role } from "../../projectsV2/api/projectV2.api.ts";
-import { useGetProjectsByProjectIdMembersQuery } from "../../projectsV2/api/projectV2.enhanced-api.ts";
+
+import useLegacySelector from "../../../utils/customHooks/useLegacySelector.hook";
+import type { Role } from "../../projectsV2/api/projectV2.api";
+import { useGetProjectsByProjectIdMembersQuery } from "../../projectsV2/api/projectV2.enhanced-api";
+import { useGetUserQuery } from "../../user/dataServicesUser.api/dataServicesUser.api";
interface UseProjectAccessArgs {
projectId: string;
diff --git a/client/src/features/groupsV2/members/GroupV2MemberListDisplay.tsx b/client/src/features/groupsV2/members/GroupV2MemberListDisplay.tsx
index 6fc7822b04..be21de8218 100644
--- a/client/src/features/groupsV2/members/GroupV2MemberListDisplay.tsx
+++ b/client/src/features/groupsV2/members/GroupV2MemberListDisplay.tsx
@@ -28,8 +28,8 @@ import { ABSOLUTE_ROUTES } from "../../../routing/routes.constants";
import { toSortedMembers } from "../../ProjectPageV2/utils/roleUtils";
import type { ProjectMemberResponse } from "../../projectsV2/api/projectV2.api";
import { useGetGroupsByGroupSlugMembersQuery } from "../../projectsV2/api/projectV2.enhanced-api";
-import { useGetUsersByUserIdQuery } from "../../user/dataServicesUser.api";
import UserAvatar from "../../usersV2/show/UserAvatar";
+import { useGetUsersByUserIdQuery } from "../../user/dataServicesUser.api/dataServicesUser.api";
interface GroupV2MemberListDisplayProps {
group: string;
diff --git a/client/src/features/projectsV2/fields/AddEntityMemberLookupForm.tsx b/client/src/features/projectsV2/fields/AddEntityMemberLookupForm.tsx
index 3e4655e243..0a3f397132 100644
--- a/client/src/features/projectsV2/fields/AddEntityMemberLookupForm.tsx
+++ b/client/src/features/projectsV2/fields/AddEntityMemberLookupForm.tsx
@@ -17,15 +17,15 @@
*/
import cx from "classnames";
-
import { useCallback, useEffect, useState } from "react";
import { PlusLg, XLg } from "react-bootstrap-icons";
import { Controller, useForm } from "react-hook-form";
-
import { Button, Form, Input, Label, ModalBody, ModalFooter } from "reactstrap";
-import { useGetUsersQuery } from "../../user/dataServicesUser.api";
-import type { UserWithId } from "../../user/dataServicesUser.api";
+import {
+ useGetUsersQuery,
+ type UserWithId,
+} from "../../user/dataServicesUser.api/dataServicesUser.api";
const emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
diff --git a/client/src/features/projectsV2/fields/AddGroupMemberModal.tsx b/client/src/features/projectsV2/fields/AddGroupMemberModal.tsx
index 222ed85fe2..6624cd25e6 100644
--- a/client/src/features/projectsV2/fields/AddGroupMemberModal.tsx
+++ b/client/src/features/projectsV2/fields/AddGroupMemberModal.tsx
@@ -35,7 +35,7 @@ import {
import { RtkErrorAlert } from "../../../components/errors/RtkErrorAlert";
-import type { UserWithId } from "../../user/dataServicesUser.api";
+import type { UserWithId } from "../../user/dataServicesUser.api/dataServicesUser.api";
import type {
GroupMemberResponseList,
diff --git a/client/src/features/projectsV2/fields/AddProjectMemberModal.tsx b/client/src/features/projectsV2/fields/AddProjectMemberModal.tsx
index 301f5fe0c7..4c48bdeffa 100644
--- a/client/src/features/projectsV2/fields/AddProjectMemberModal.tsx
+++ b/client/src/features/projectsV2/fields/AddProjectMemberModal.tsx
@@ -35,7 +35,7 @@ import {
import { RtkErrorAlert } from "../../../components/errors/RtkErrorAlert";
-import type { UserWithId } from "../../user/dataServicesUser.api";
+import type { UserWithId } from "../../user/dataServicesUser.api/dataServicesUser.api";
import type {
ProjectMemberPatchRequest,
diff --git a/client/src/features/secrets/SecretDelete.tsx b/client/src/features/secrets/SecretDelete.tsx
index 0592790446..fa4c5811a2 100644
--- a/client/src/features/secrets/SecretDelete.tsx
+++ b/client/src/features/secrets/SecretDelete.tsx
@@ -21,9 +21,9 @@ import { useCallback, useEffect, useState } from "react";
import { TrashFill, XLg } from "react-bootstrap-icons";
import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap";
-import { useDeleteSecretMutation } from "./secrets.api";
import { RtkOrNotebooksError } from "../../components/errors/RtkErrorAlert";
import { SecretDetails } from "./secrets.types";
+import { useDeleteUserSecretsBySecretIdMutation } from "../user/dataServicesUser.api/dataServicesUser.api";
interface SecretsDeleteProps {
secret: SecretDetails;
@@ -36,9 +36,10 @@ export default function SecretDelete({ secret }: SecretsDeleteProps) {
}, []);
// Handle posting data
- const [deleteSecretMutation, result] = useDeleteSecretMutation();
+ const [deleteSecretMutation, result] =
+ useDeleteUserSecretsBySecretIdMutation();
const deleteSecret = useCallback(() => {
- deleteSecretMutation(secret.id);
+ deleteSecretMutation({ secretId: secret.id });
}, [deleteSecretMutation, secret.id]);
// Automatically close the modal when the secret is modified
diff --git a/client/src/features/secrets/SecretEdit.tsx b/client/src/features/secrets/SecretEdit.tsx
index 1b2e193a30..1d4ff51bfa 100644
--- a/client/src/features/secrets/SecretEdit.tsx
+++ b/client/src/features/secrets/SecretEdit.tsx
@@ -38,8 +38,8 @@ import {
UncontrolledTooltip,
} from "reactstrap";
-import { useEditSecretMutation } from "./secrets.api";
import { RtkOrNotebooksError } from "../../components/errors/RtkErrorAlert";
+import { usePatchUserSecretsBySecretIdMutation } from "../user/dataServicesUser.api/dataServicesUser.api";
import { EditSecretForm, SecretDetails } from "./secrets.types";
import { SECRETS_VALUE_LENGTH_LIMIT } from "./secrets.utils";
@@ -75,10 +75,10 @@ export default function SecretEdit({ secret }: SecretsEditProps) {
});
// Handle posting data
- const [editSecretMutation, result] = useEditSecretMutation();
+ const [editSecretMutation, result] = usePatchUserSecretsBySecretIdMutation();
const onSubmit = useCallback(
(newSecret: EditSecretForm) => {
- editSecretMutation({ id: secret.id, ...newSecret });
+ editSecretMutation({ secretId: secret.id, secretPatch: newSecret });
},
[editSecretMutation, secret.id]
);
diff --git a/client/src/features/secrets/SecretNew.tsx b/client/src/features/secrets/SecretNew.tsx
index 1bc5d91a2a..57b91a69f5 100644
--- a/client/src/features/secrets/SecretNew.tsx
+++ b/client/src/features/secrets/SecretNew.tsx
@@ -33,11 +33,14 @@ import {
UncontrolledTooltip,
} from "reactstrap";
-import secretsApi, { useAddSecretMutation } from "./secrets.api";
import { RtkOrNotebooksError } from "../../components/errors/RtkErrorAlert";
import { Loader } from "../../components/Loader";
import { AddSecretForm } from "./secrets.types";
import { SECRETS_VALUE_LENGTH_LIMIT } from "./secrets.utils";
+import {
+ dataServicesUserApi,
+ usePostUserSecretsMutation,
+} from "../user/dataServicesUser.api/dataServicesUser.api";
import styles from "./secrets.module.scss";
@@ -69,11 +72,12 @@ export default function SecretsNew() {
});
// Handle fetching/posting data
- const [getSecrets, secrets] = secretsApi.useLazyGetSecretsQuery();
- const [addSecretMutation, result] = useAddSecretMutation();
+ const [getSecrets, secrets] =
+ dataServicesUserApi.useLazyGetUserSecretsQuery();
+ const [addSecretMutation, result] = usePostUserSecretsMutation();
const onSubmit = useCallback(
(newSecret: AddSecretForm) => {
- addSecretMutation(newSecret);
+ addSecretMutation({ secretPost: { ...newSecret, kind: "general" } });
},
[addSecretMutation]
);
@@ -81,7 +85,7 @@ export default function SecretsNew() {
// Force fetching the secrets when trying to add a new one to try to prevent duplicates
useEffect(() => {
if (showModal) {
- getSecrets();
+ getSecrets({});
}
}, [getSecrets, showModal]);
diff --git a/client/src/features/secrets/SecretsList.tsx b/client/src/features/secrets/SecretsList.tsx
index 5c1d2463ab..cf2972f6e8 100644
--- a/client/src/features/secrets/SecretsList.tsx
+++ b/client/src/features/secrets/SecretsList.tsx
@@ -20,12 +20,12 @@ import cx from "classnames";
import { Col, Container, Row } from "reactstrap";
import { Loader } from "../../components/Loader";
-import { useGetSecretsQuery } from "./secrets.api";
import { RtkOrNotebooksError } from "../../components/errors/RtkErrorAlert";
import SecretsListItem from "./SecretsListItem";
+import { useGetUserSecretsQuery } from "../user/dataServicesUser.api/dataServicesUser.api";
export default function SecretsList() {
- const secrets = useGetSecretsQuery();
+ const secrets = useGetUserSecretsQuery({});
if (secrets.isLoading) return ;
diff --git a/client/src/features/secrets/secrets.api.ts b/client/src/features/secrets/secrets.api.ts
deleted file mode 100644
index 72b7db0594..0000000000
--- a/client/src/features/secrets/secrets.api.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-/*!
- * Copyright 2024 - Swiss Data Science Center (SDSC)
- * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
- * Eidgenössische Technische Hochschule Zürich (ETHZ).
- *
- * Licensed 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 { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
-
-import {
- AddSecretParams,
- EditSecretParams,
- SecretDetails,
-} from "./secrets.types";
-
-const secretsApi = createApi({
- reducerPath: "secrets",
- baseQuery: fetchBaseQuery({
- baseUrl: "/ui-server/api/data/user/secrets",
- }),
- tagTypes: ["Secrets", "Secret"],
- endpoints: (builder) => ({
- getSecrets: builder.query({
- query: () => {
- return {
- url: "",
- };
- },
- providesTags: ["Secrets"],
- }),
- getSecretDetails: builder.query({
- query: (secretId) => {
- return {
- url: secretId,
- };
- },
- providesTags: (result, _error, secretId) =>
- result && result.id === secretId
- ? [{ type: "Secret", id: secretId }]
- : [],
- }),
- addSecret: builder.mutation({
- query: (secret) => {
- return {
- url: "",
- method: "POST",
- body: secret,
- };
- },
- invalidatesTags: ["Secrets"],
- }),
- editSecret: builder.mutation({
- query: (secret) => {
- return {
- url: secret.id,
- method: "PATCH",
- body: { value: secret.value },
- };
- },
- invalidatesTags: (_result, _error, params) => [
- "Secrets",
- { type: "Secret", id: params.id },
- ],
- }),
- deleteSecret: builder.mutation({
- query: (secretId) => {
- return {
- url: secretId,
- method: "DELETE",
- };
- },
- invalidatesTags: (_result, _error, param) => [
- "Secrets",
- { type: "Secret", id: param },
- ],
- }),
- }),
-});
-
-export default secretsApi;
-export const {
- useGetSecretsQuery,
- useGetSecretDetailsQuery,
- useAddSecretMutation,
- useEditSecretMutation,
- useDeleteSecretMutation,
-} = secretsApi;
diff --git a/client/src/features/secrets/secrets.types.ts b/client/src/features/secrets/secrets.types.ts
index b151e9e90e..19c2474c3c 100644
--- a/client/src/features/secrets/secrets.types.ts
+++ b/client/src/features/secrets/secrets.types.ts
@@ -18,7 +18,7 @@
export interface SecretDetails {
id: string;
- modification_date: Date;
+ modification_date: string;
name: string;
}
diff --git a/client/src/features/session/components/options/SessionUserSecrets.tsx b/client/src/features/session/components/options/SessionUserSecrets.tsx
index a4e541717a..e052803018 100644
--- a/client/src/features/session/components/options/SessionUserSecrets.tsx
+++ b/client/src/features/session/components/options/SessionUserSecrets.tsx
@@ -21,17 +21,17 @@ import { useCallback, useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { Button, Card, CardBody, Collapse, Input, Label } from "reactstrap";
+import { RtkOrNotebooksError } from "../../../../components/errors/RtkErrorAlert";
+import ChevronFlippedIcon from "../../../../components/icons/ChevronFlippedIcon";
import { Loader } from "../../../../components/Loader";
import { User } from "../../../../model/renkuModels.types";
-import useLegacySelector from "../../../../utils/customHooks/useLegacySelector.hook";
-import { Url } from "../../../../utils/helpers/url";
-import ChevronFlippedIcon from "../../../../components/icons/ChevronFlippedIcon";
-import { useGetSecretsQuery } from "../../../secrets/secrets.api";
-import { RtkOrNotebooksError } from "../../../../components/errors/RtkErrorAlert";
-import { setSecretsList, setSecretsPath } from "../../startSessionOptionsSlice";
import useAppDispatch from "../../../../utils/customHooks/useAppDispatch.hook";
import useAppSelector from "../../../../utils/customHooks/useAppSelector.hook";
+import useLegacySelector from "../../../../utils/customHooks/useLegacySelector.hook";
+import { Url } from "../../../../utils/helpers/url";
+import { useGetUserSecretsQuery } from "../../../user/dataServicesUser.api/dataServicesUser.api";
import { SessionSecrets } from "../../startSessionOptions.types";
+import { setSecretsList, setSecretsPath } from "../../startSessionOptionsSlice";
export default function SessionUserSecrets() {
const secretsUrl = Url.get(Url.pages.secrets);
@@ -68,7 +68,7 @@ function SessionUserSecretsSection() {
const toggleIsOpen = useCallback(() => setIsOpen((isOpen) => !isOpen), []);
// Fetch the secrets
- const secrets = useGetSecretsQuery();
+ const secrets = useGetUserSecretsQuery({});
// Get current values from the store
const sessionOptions = useAppSelector((state) => state.startSessionOptions);
diff --git a/client/src/features/user/dataServicesUser.api/dataServicesUser.api-config.ts b/client/src/features/user/dataServicesUser.api/dataServicesUser.api-config.ts
index 2c58024d05..264c580799 100644
--- a/client/src/features/user/dataServicesUser.api/dataServicesUser.api-config.ts
+++ b/client/src/features/user/dataServicesUser.api/dataServicesUser.api-config.ts
@@ -21,10 +21,10 @@ import type { ConfigFile } from "@rtk-query/codegen-openapi";
import path from "path";
const config: ConfigFile = {
- apiFile: "./dataServicesUser-empty.api.ts",
+ apiFile: "./dataServicesUser.empty-api.ts",
apiImport: "dataServicesUserEmptyApi",
- outputFile: "./dataServicesUser.api.ts",
- exportName: "dataServicesUserApi",
+ outputFile: "./dataServicesUser.generated-api.ts",
+ exportName: "dataServicesUserGeneratedApi",
hooks: true,
schemaFile: path.join(__dirname, "dataServicesUser.openapi.json"),
};
diff --git a/client/src/features/user/dataServicesUser.api/dataServicesUser.api.ts b/client/src/features/user/dataServicesUser.api/dataServicesUser.api.ts
index 07d9314899..d75d11891b 100644
--- a/client/src/features/user/dataServicesUser.api/dataServicesUser.api.ts
+++ b/client/src/features/user/dataServicesUser.api/dataServicesUser.api.ts
@@ -1,75 +1,75 @@
-import { dataServicesUserEmptyApi as api } from "./dataServicesUser-empty.api";
-const injectedRtkApi = api.injectEndpoints({
- endpoints: (build) => ({
- getUser: build.query({
- query: () => ({ url: `/user` }),
- }),
- getUsers: build.query({
- query: (queryArg) => ({
- url: `/users`,
- params: { exact_email: queryArg.exactEmail },
- }),
- }),
- getUsersByUserId: build.query<
- GetUsersByUserIdApiResponse,
- GetUsersByUserIdApiArg
- >({
- query: (queryArg) => ({ url: `/users/${queryArg.userId}` }),
- }),
- getError: build.query({
- query: () => ({ url: `/error` }),
- }),
- getVersion: build.query({
- query: () => ({ url: `/version` }),
- }),
- }),
- overrideExisting: false,
-});
-export { injectedRtkApi as dataServicesUserApi };
-export type GetUserApiResponse =
- /** status 200 The currently logged in user */ UserWithId;
-export type GetUserApiArg = void;
-export type GetUsersApiResponse =
- /** status 200 The list of users in the service (this is a subset of what is in Keycloak) */ UsersWithId;
-export type GetUsersApiArg = {
- /** Return the user(s) with an exact match on the email provided */
- exactEmail?: string;
-};
-export type GetUsersByUserIdApiResponse =
- /** status 200 The requested user */ UserWithId;
-export type GetUsersByUserIdApiArg = {
- userId: string;
-};
-export type GetErrorApiResponse = unknown;
-export type GetErrorApiArg = void;
-export type GetVersionApiResponse = /** status 200 The error */ Version;
-export type GetVersionApiArg = void;
-export type UserId = string;
-export type Username = string;
-export type UserEmail = string;
-export type UserFirstLastName = string;
-export type UserWithId = {
- id: UserId;
- username: Username;
- email?: UserEmail;
- first_name?: UserFirstLastName;
- last_name?: UserFirstLastName;
-};
-export type ErrorResponse = {
- error: {
- code: number;
- detail?: string;
- message: string;
- };
-};
-export type UsersWithId = UserWithId[];
-export type Version = {
- version: string;
-};
+/*!
+ * Copyright 2024 - Swiss Data Science Center (SDSC)
+ * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
+ * Eidgenössische Technische Hochschule Zürich (ETHZ).
+ *
+ * Licensed 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 { dataServicesUserGeneratedApi } from "./dataServicesUser.generated-api";
+
+export const dataServicesUserApi =
+ dataServicesUserGeneratedApi.enhanceEndpoints({
+ addTagTypes: ["User", "UserSecret", "UserSecretKey"],
+ endpoints: {
+ getUser: {
+ providesTags: (result) =>
+ result ? [{ type: "User", id: result.id }] : [],
+ },
+ getUsers: {
+ providesTags: (result) =>
+ result
+ ? [
+ ...result.map(({ id }) => ({ type: "User" as const, id })),
+ "User",
+ ]
+ : ["User"],
+ },
+ getUsersByUserId: {
+ providesTags: (result) =>
+ result ? [{ type: "User", id: result.id }] : [],
+ },
+ getUserSecretKey: {
+ providesTags: ["UserSecretKey"],
+ },
+ getUserSecrets: {
+ providesTags: ["UserSecret", { type: "UserSecret", id: "LIST" }],
+ },
+ getUserSecretsBySecretId: {
+ providesTags: (result) =>
+ result ? [{ type: "UserSecret", id: result.id }] : [],
+ },
+ postUserSecrets: {
+ invalidatesTags: ["UserSecret"],
+ },
+ patchUserSecretsBySecretId: {
+ invalidatesTags: (result) =>
+ result ? [{ type: "UserSecret", id: result.id }] : ["UserSecret"],
+ },
+ deleteUserSecretsBySecretId: {
+ invalidatesTags: ["UserSecret"],
+ },
+ },
+ });
export const {
useGetUserQuery,
useGetUsersQuery,
useGetUsersByUserIdQuery,
- useGetErrorQuery,
- useGetVersionQuery,
-} = injectedRtkApi;
+ useGetUserSecretKeyQuery,
+ useGetUserSecretsQuery,
+ useGetUserSecretsBySecretIdQuery,
+ usePostUserSecretsMutation,
+ usePatchUserSecretsBySecretIdMutation,
+ useDeleteUserSecretsBySecretIdMutation,
+} = dataServicesUserApi;
+export type * from "./dataServicesUser.generated-api";
diff --git a/client/src/features/user/dataServicesUser.api/dataServicesUser-empty.api.ts b/client/src/features/user/dataServicesUser.api/dataServicesUser.empty-api.ts
similarity index 100%
rename from client/src/features/user/dataServicesUser.api/dataServicesUser-empty.api.ts
rename to client/src/features/user/dataServicesUser.api/dataServicesUser.empty-api.ts
diff --git a/client/src/features/user/dataServicesUser.api/dataServicesUser.generated-api.ts b/client/src/features/user/dataServicesUser.api/dataServicesUser.generated-api.ts
new file mode 100644
index 0000000000..c4c916ba28
--- /dev/null
+++ b/client/src/features/user/dataServicesUser.api/dataServicesUser.generated-api.ts
@@ -0,0 +1,187 @@
+import { dataServicesUserEmptyApi as api } from "./dataServicesUser.empty-api";
+const injectedRtkApi = api.injectEndpoints({
+ endpoints: (build) => ({
+ getUser: build.query({
+ query: () => ({ url: `/user` }),
+ }),
+ getUserSecretKey: build.query<
+ GetUserSecretKeyApiResponse,
+ GetUserSecretKeyApiArg
+ >({
+ query: () => ({ url: `/user/secret_key` }),
+ }),
+ getUsers: build.query({
+ query: (queryArg) => ({
+ url: `/users`,
+ params: { exact_email: queryArg.exactEmail },
+ }),
+ }),
+ getUsersByUserId: build.query<
+ GetUsersByUserIdApiResponse,
+ GetUsersByUserIdApiArg
+ >({
+ query: (queryArg) => ({ url: `/users/${queryArg.userId}` }),
+ }),
+ getUserSecrets: build.query<
+ GetUserSecretsApiResponse,
+ GetUserSecretsApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/user/secrets`,
+ params: { kind: queryArg.kind },
+ }),
+ }),
+ postUserSecrets: build.mutation<
+ PostUserSecretsApiResponse,
+ PostUserSecretsApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/user/secrets`,
+ method: "POST",
+ body: queryArg.secretPost,
+ }),
+ }),
+ getUserSecretsBySecretId: build.query<
+ GetUserSecretsBySecretIdApiResponse,
+ GetUserSecretsBySecretIdApiArg
+ >({
+ query: (queryArg) => ({ url: `/user/secrets/${queryArg.secretId}` }),
+ }),
+ patchUserSecretsBySecretId: build.mutation<
+ PatchUserSecretsBySecretIdApiResponse,
+ PatchUserSecretsBySecretIdApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/user/secrets/${queryArg.secretId}`,
+ method: "PATCH",
+ body: queryArg.secretPatch,
+ }),
+ }),
+ deleteUserSecretsBySecretId: build.mutation<
+ DeleteUserSecretsBySecretIdApiResponse,
+ DeleteUserSecretsBySecretIdApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/user/secrets/${queryArg.secretId}`,
+ method: "DELETE",
+ }),
+ }),
+ getError: build.query({
+ query: () => ({ url: `/error` }),
+ }),
+ getVersion: build.query({
+ query: () => ({ url: `/version` }),
+ }),
+ }),
+ overrideExisting: false,
+});
+export { injectedRtkApi as dataServicesUserGeneratedApi };
+export type GetUserApiResponse =
+ /** status 200 The currently logged in user */ UserWithId;
+export type GetUserApiArg = void;
+export type GetUserSecretKeyApiResponse =
+ /** status 200 The secret key of the currently logged in user.
+This endpoint is not publicly accessible.
+ */ UserSecretKey;
+export type GetUserSecretKeyApiArg = void;
+export type GetUsersApiResponse =
+ /** status 200 The list of users in the service (this is a subset of what is in Keycloak) */ UsersWithId;
+export type GetUsersApiArg = {
+ /** Return the user(s) with an exact match on the email provided */
+ exactEmail?: string;
+};
+export type GetUsersByUserIdApiResponse =
+ /** status 200 The requested user */ UserWithId;
+export type GetUsersByUserIdApiArg = {
+ userId: string;
+};
+export type GetUserSecretsApiResponse =
+ /** status 200 The user's secrets */ SecretsList;
+export type GetUserSecretsApiArg = {
+ /** Filter results based on secret kind */
+ kind?: SecretKind;
+};
+export type PostUserSecretsApiResponse =
+ /** status 201 Secret successfully created */ SecretWithId;
+export type PostUserSecretsApiArg = {
+ secretPost: SecretPost;
+};
+export type GetUserSecretsBySecretIdApiResponse =
+ /** status 200 The secret */ SecretWithId;
+export type GetUserSecretsBySecretIdApiArg = {
+ secretId: string;
+};
+export type PatchUserSecretsBySecretIdApiResponse =
+ /** status 201 Secret successfully changed */ SecretWithId;
+export type PatchUserSecretsBySecretIdApiArg = {
+ secretId: string;
+ secretPatch: SecretPatch;
+};
+export type DeleteUserSecretsBySecretIdApiResponse =
+ /** status 204 The secret was deleted or didn't exist */ void;
+export type DeleteUserSecretsBySecretIdApiArg = {
+ secretId: string;
+};
+export type GetErrorApiResponse = unknown;
+export type GetErrorApiArg = void;
+export type GetVersionApiResponse = /** status 200 The error */ Version;
+export type GetVersionApiArg = void;
+export type UserId = string;
+export type Username = string;
+export type UserEmail = string;
+export type UserFirstLastName = string;
+export type UserWithId = {
+ id: UserId;
+ username: Username;
+ email?: UserEmail;
+ first_name?: UserFirstLastName;
+ last_name?: UserFirstLastName;
+};
+export type ErrorResponse = {
+ error: {
+ code: number;
+ detail?: string;
+ message: string;
+ };
+};
+export type UserSecretKey = {
+ /** The users secret key */
+ secret_key?: string;
+};
+export type UsersWithId = UserWithId[];
+export type Ulid = string;
+export type SecretName = string;
+export type ModificationDate = string;
+export type SecretKind = "general" | "storage";
+export type SecretWithId = {
+ id: Ulid;
+ name: SecretName;
+ modification_date: ModificationDate;
+ kind: SecretKind;
+};
+export type SecretsList = SecretWithId[];
+export type SecretValue = string;
+export type SecretPost = {
+ name: SecretName;
+ value: SecretValue;
+ kind: SecretKind;
+};
+export type SecretPatch = {
+ value: SecretValue;
+};
+export type Version = {
+ version: string;
+};
+export const {
+ useGetUserQuery,
+ useGetUserSecretKeyQuery,
+ useGetUsersQuery,
+ useGetUsersByUserIdQuery,
+ useGetUserSecretsQuery,
+ usePostUserSecretsMutation,
+ useGetUserSecretsBySecretIdQuery,
+ usePatchUserSecretsBySecretIdMutation,
+ useDeleteUserSecretsBySecretIdMutation,
+ useGetErrorQuery,
+ useGetVersionQuery,
+} = injectedRtkApi;
diff --git a/client/src/features/user/dataServicesUser.api/dataServicesUser.openapi.json b/client/src/features/user/dataServicesUser.api/dataServicesUser.openapi.json
index 9841d639f6..12c7dcede7 100644
--- a/client/src/features/user/dataServicesUser.api/dataServicesUser.openapi.json
+++ b/client/src/features/user/dataServicesUser.api/dataServicesUser.openapi.json
@@ -35,6 +35,27 @@
"tags": ["users"]
}
},
+ "/user/secret_key": {
+ "get": {
+ "summary": "Get the current users secret key",
+ "responses": {
+ "200": {
+ "description": "The secret key of the currently logged in user.\nThis endpoint is not publicly accessible.\n",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UserSecretKey"
+ }
+ }
+ }
+ },
+ "default": {
+ "$ref": "#/components/responses/Error"
+ }
+ },
+ "tags": ["secrets"]
+ }
+ },
"/users": {
"get": {
"summary": "List all users",
@@ -108,6 +129,180 @@
"tags": ["users"]
}
},
+ "/user/secrets": {
+ "get": {
+ "summary": "List all secrets for a user (keys only)",
+ "parameters": [
+ {
+ "in": "query",
+ "description": "Filter results based on secret kind",
+ "name": "kind",
+ "required": false,
+ "schema": {
+ "$ref": "#/components/schemas/SecretKind",
+ "default": "general"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The user's secrets",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SecretsList"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The user does not exist",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ },
+ "default": {
+ "$ref": "#/components/responses/Error"
+ }
+ },
+ "tags": ["secrets"]
+ },
+ "post": {
+ "summary": "Create a new secret for the user",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SecretPost"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Secret successfully created",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SecretWithId"
+ }
+ }
+ }
+ },
+ "default": {
+ "$ref": "#/components/responses/Error"
+ }
+ },
+ "tags": ["secrets"]
+ }
+ },
+ "/user/secrets/{secret_id}": {
+ "get": {
+ "summary": "Get a secret key by id",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "secret_id",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The secret",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SecretWithId"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The user does not exist",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ },
+ "default": {
+ "$ref": "#/components/responses/Error"
+ }
+ },
+ "tags": ["secrets"]
+ },
+ "patch": {
+ "summary": "Change a secret",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "secret_id",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SecretPatch"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Secret successfully changed",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SecretWithId"
+ }
+ }
+ }
+ },
+ "default": {
+ "$ref": "#/components/responses/Error"
+ }
+ },
+ "tags": ["secrets"]
+ },
+ "delete": {
+ "summary": "Delete a secret",
+ "parameters": [
+ {
+ "in": "path",
+ "name": "secret_id",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "The secret was deleted or didn't exist"
+ },
+ "default": {
+ "$ref": "#/components/responses/Error"
+ }
+ },
+ "tags": ["secrets"]
+ }
+ },
"/error": {
"get": {
"summary": "Get a sample error response with status code 422",
@@ -246,9 +441,12 @@
},
"modification_date": {
"$ref": "#/components/schemas/ModificationDate"
+ },
+ "kind": {
+ "$ref": "#/components/schemas/SecretKind"
}
},
- "required": ["id", "name", "modification_date"],
+ "required": ["id", "name", "modification_date", "kind"],
"example": {
"id": "01AN4Z79ZS5XN0F25N3DB94T4R",
"name": "S3-Credentials",
@@ -265,9 +463,12 @@
},
"value": {
"$ref": "#/components/schemas/SecretValue"
+ },
+ "kind": {
+ "$ref": "#/components/schemas/SecretKind"
}
},
- "required": ["name", "value"]
+ "required": ["name", "value", "kind"]
},
"SecretPatch": {
"type": "object",
@@ -307,6 +508,11 @@
"minLength": 1,
"maxLength": 5000
},
+ "SecretKind": {
+ "description": "Kind of secret",
+ "type": "string",
+ "enum": ["general", "storage"]
+ },
"ErrorResponse": {
"type": "object",
"properties": {
diff --git a/client/src/features/user/dataServicesUser.api/index.ts b/client/src/features/user/dataServicesUser.api/index.ts
deleted file mode 100644
index 4690795f1b..0000000000
--- a/client/src/features/user/dataServicesUser.api/index.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export { dataServicesUserApi } from "./dataServicesUser.api";
-
-export type { UserWithId } from "./dataServicesUser.api";
-
-export {
- useGetUserQuery,
- useGetUsersQuery,
- useGetUsersByUserIdQuery,
- useGetErrorQuery,
-} from "./dataServicesUser.api";
diff --git a/client/src/features/usersV2/show/UserRedirect.tsx b/client/src/features/usersV2/show/UserRedirect.tsx
index 1b508d71f7..370067bfe5 100644
--- a/client/src/features/usersV2/show/UserRedirect.tsx
+++ b/client/src/features/usersV2/show/UserRedirect.tsx
@@ -34,7 +34,7 @@ import { ABSOLUTE_ROUTES } from "../../../routing/routes.constants";
import useLegacySelector from "../../../utils/customHooks/useLegacySelector.hook";
import { Url } from "../../../utils/helpers/url";
import UserNotFound from "../../projectsV2/notFound/UserNotFound";
-import { useGetUserQuery } from "../../user/dataServicesUser.api";
+import { useGetUserQuery } from "../../user/dataServicesUser.api/dataServicesUser.api";
export default function UserRedirect() {
const navigate = useNavigate();
diff --git a/client/src/features/usersV2/show/UserShow.tsx b/client/src/features/usersV2/show/UserShow.tsx
index 93d6a12aa0..4035c4cf2b 100644
--- a/client/src/features/usersV2/show/UserShow.tsx
+++ b/client/src/features/usersV2/show/UserShow.tsx
@@ -35,7 +35,7 @@ import UserNotFound from "../../projectsV2/notFound/UserNotFound";
import {
useGetUserQuery,
useGetUsersByUserIdQuery,
-} from "../../user/dataServicesUser.api";
+} from "../../user/dataServicesUser.api/dataServicesUser.api";
import UserAvatar from "./UserAvatar";
export default function UserShow() {
diff --git a/client/src/utils/helpers/EnhancedState.ts b/client/src/utils/helpers/EnhancedState.ts
index 3907c18637..9ba4d811af 100644
--- a/client/src/utils/helpers/EnhancedState.ts
+++ b/client/src/utils/helpers/EnhancedState.ts
@@ -50,7 +50,6 @@ import { recentUserActivityApi } from "../../features/recentUserActivity/RecentU
import repositoriesApi from "../../features/repositories/repositories.api";
import { searchV2EmptyApi as searchV2Api } from "../../features/searchV2/api/searchV2-empty.api";
import { searchV2Slice } from "../../features/searchV2/searchV2.slice";
-import secretsApi from "../../features/secrets/secrets.api";
import sessionsApi from "../../features/session/sessions.api";
import sessionSidecarApi from "../../features/session/sidecar.api";
import startSessionSlice from "../../features/session/startSession.slice";
@@ -58,7 +57,7 @@ import { startSessionOptionsSlice } from "../../features/session/startSessionOpt
import sessionsV2Api from "../../features/sessionsV2/sessionsV2.api";
import startSessionOptionsV2Slice from "../../features/sessionsV2/startSessionOptionsV2.slice";
import termsApi from "../../features/terms/terms.api";
-import { dataServicesUserApi } from "../../features/user/dataServicesUser.api";
+import { dataServicesUserEmptyApi as dataServicesUserApi } from "../../features/user/dataServicesUser.api/dataServicesUser.empty-api";
import keycloakUserApi from "../../features/user/keycloakUser.api";
import userPreferencesApi from "../../features/user/userPreferences.api";
import { versionsApi } from "../../features/versions/versions.api";
@@ -107,7 +106,6 @@ export const createStore = (
[recentUserActivityApi.reducerPath]: recentUserActivityApi.reducer,
[repositoriesApi.reducerPath]: repositoriesApi.reducer,
[searchV2Api.reducerPath]: searchV2Api.reducer,
- [secretsApi.reducerPath]: secretsApi.reducer,
[sessionsApi.reducerPath]: sessionsApi.reducer,
[sessionSidecarApi.reducerPath]: sessionSidecarApi.reducer,
[sessionsV2Api.reducerPath]: sessionsV2Api.reducer,
@@ -146,7 +144,6 @@ export const createStore = (
.concat(recentUserActivityApi.middleware)
.concat(repositoriesApi.middleware)
.concat(searchV2Api.middleware)
- .concat(secretsApi.middleware)
.concat(sessionsApi.middleware)
.concat(sessionSidecarApi.middleware)
.concat(sessionSidecarApi.middleware)