Skip to content

Commit 71851b1

Browse files
committed
Generate frontend client endpoints for Secrets API
1 parent eb8fc83 commit 71851b1

File tree

8 files changed

+275
-3
lines changed

8 files changed

+275
-3
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4f0a29dec0d3c9f0d0f02caab4dc84101bfef8b0
1+
be67e887ad7396cf0078edca36201564a208d1b7

workspaces/frontend/src/app/pages/Workspaces/Form/properties/WorkspaceFormPropertiesSecrets.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import { Dropdown, DropdownItem } from '@patternfly/react-core/dist/esm/componen
2323
import { MenuToggle } from '@patternfly/react-core/dist/esm/components/MenuToggle';
2424
import { Form, FormGroup } from '@patternfly/react-core/dist/esm/components/Form';
2525
import { HelperText, HelperTextItem } from '@patternfly/react-core/dist/esm/components/HelperText';
26-
import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon';
27-
import { WorkspacesPodSecretMount } from '~/generated/data-contracts';
26+
import { SecretsSecretListItem, WorkspacesPodSecretMount } from '~/generated/data-contracts';
27+
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
28+
import { useNamespaceContext } from '~/app/context/NamespaceContextProvider';
2829

2930
interface WorkspaceFormPropertiesSecretsProps {
3031
secrets: WorkspacesPodSecretMount[];
@@ -49,6 +50,19 @@ export const WorkspaceFormPropertiesSecrets: React.FC<WorkspaceFormPropertiesSec
4950
const [deleteIndex, setDeleteIndex] = useState<number | null>(null);
5051
const [isDefaultModeValid, setIsDefaultModeValid] = useState(true);
5152
const [dropdownOpen, setDropdownOpen] = useState<number | null>(null);
53+
const [availableSecrets, setAvailableSecrets] = useState<SecretsSecretListItem[]>([]);
54+
const [attachedSecrets, setAttachedSecrets] = useState<SecretsSecretListItem[]>([]);
55+
56+
const { api } = useNotebookAPI();
57+
const { selectedNamespace } = useNamespaceContext();
58+
59+
useEffect(() => {
60+
const fetchSecrets = async () => {
61+
const secretsResponse = await api.secrets.listSecrets(selectedNamespace);
62+
setAvailableSecrets(secretsResponse.data);
63+
};
64+
fetchSecrets();
65+
}, [api.secrets, selectedNamespace]);
5266

5367
const openDeleteModal = useCallback((i: number) => {
5468
setIsDeleteModalOpen(true);
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/* eslint-disable */
2+
/* tslint:disable */
3+
// @ts-nocheck
4+
/*
5+
* ---------------------------------------------------------------
6+
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
7+
* ## ##
8+
* ## AUTHOR: acacode ##
9+
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
10+
* ---------------------------------------------------------------
11+
*/
12+
13+
import {
14+
ApiErrorEnvelope,
15+
ApiSecretCreateEnvelope,
16+
ApiSecretEnvelope,
17+
ApiSecretListEnvelope,
18+
SecretsSecretUpdate,
19+
} from './data-contracts';
20+
import { ContentType, HttpClient, RequestParams } from './http-client';
21+
22+
export class Secrets<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
23+
/**
24+
* @description Provides a list of all secrets that the user has access to in the specified namespace
25+
*
26+
* @tags secrets
27+
* @name ListSecrets
28+
* @summary Returns a list of all secrets in a namespace
29+
* @request GET:/secrets/{namespace}
30+
* @response `200` `ApiSecretListEnvelope` Successful secrets response
31+
* @response `401` `ApiErrorEnvelope` Unauthorized
32+
* @response `403` `ApiErrorEnvelope` Forbidden
33+
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
34+
* @response `500` `ApiErrorEnvelope` Internal server error
35+
*/
36+
listSecrets = (namespace: string, params: RequestParams = {}) =>
37+
this.request<ApiSecretListEnvelope, ApiErrorEnvelope>({
38+
path: `/secrets/${namespace}`,
39+
method: 'GET',
40+
format: 'json',
41+
...params,
42+
});
43+
/**
44+
* @description Creates a new secret in the specified namespace
45+
*
46+
* @tags secrets
47+
* @name CreateSecret
48+
* @summary Creates a new secret
49+
* @request POST:/secrets/{namespace}
50+
* @response `201` `ApiSecretCreateEnvelope` Secret created successfully
51+
* @response `400` `ApiErrorEnvelope` Bad request
52+
* @response `401` `ApiErrorEnvelope` Unauthorized
53+
* @response `403` `ApiErrorEnvelope` Forbidden
54+
* @response `409` `ApiErrorEnvelope` Secret already exists
55+
* @response `413` `ApiErrorEnvelope` Request Entity Too Large. The request body is too large.
56+
* @response `415` `ApiErrorEnvelope` Unsupported Media Type. Content-Type header is not correct.
57+
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
58+
* @response `500` `ApiErrorEnvelope` Internal server error
59+
*/
60+
createSecret = (namespace: string, secret: ApiSecretCreateEnvelope, params: RequestParams = {}) =>
61+
this.request<ApiSecretCreateEnvelope, ApiErrorEnvelope>({
62+
path: `/secrets/${namespace}`,
63+
method: 'POST',
64+
body: secret,
65+
type: ContentType.Json,
66+
format: 'json',
67+
...params,
68+
});
69+
/**
70+
* @description Provides details of a specific secret by name and namespace
71+
*
72+
* @tags secrets
73+
* @name GetSecret
74+
* @summary Returns a specific secret
75+
* @request GET:/secrets/{namespace}/{name}
76+
* @response `200` `ApiSecretEnvelope` Successful secret response
77+
* @response `401` `ApiErrorEnvelope` Unauthorized
78+
* @response `403` `ApiErrorEnvelope` Forbidden
79+
* @response `404` `ApiErrorEnvelope` Secret not found
80+
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
81+
* @response `500` `ApiErrorEnvelope` Internal server error
82+
*/
83+
getSecret = (namespace: string, name: string, params: RequestParams = {}) =>
84+
this.request<ApiSecretEnvelope, ApiErrorEnvelope>({
85+
path: `/secrets/${namespace}/${name}`,
86+
method: 'GET',
87+
format: 'json',
88+
...params,
89+
});
90+
/**
91+
* @description Updates an existing secret in the specified namespace
92+
*
93+
* @tags secrets
94+
* @name UpdateSecret
95+
* @summary Updates an existing secret
96+
* @request PUT:/secrets/{namespace}/{name}
97+
* @response `200` `ApiSecretEnvelope` Secret updated successfully
98+
* @response `400` `ApiErrorEnvelope` Bad request
99+
* @response `401` `ApiErrorEnvelope` Unauthorized
100+
* @response `403` `ApiErrorEnvelope` Forbidden
101+
* @response `404` `ApiErrorEnvelope` Secret not found
102+
* @response `413` `ApiErrorEnvelope` Request Entity Too Large. The request body is too large.
103+
* @response `415` `ApiErrorEnvelope` Unsupported Media Type. Content-Type header is not correct.
104+
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
105+
* @response `500` `ApiErrorEnvelope` Internal server error
106+
*/
107+
updateSecret = (
108+
namespace: string,
109+
name: string,
110+
secret: SecretsSecretUpdate,
111+
params: RequestParams = {},
112+
) =>
113+
this.request<ApiSecretEnvelope, ApiErrorEnvelope>({
114+
path: `/secrets/${namespace}/${name}`,
115+
method: 'PUT',
116+
body: secret,
117+
type: ContentType.Json,
118+
format: 'json',
119+
...params,
120+
});
121+
/**
122+
* @description Deletes a secret from the specified namespace
123+
*
124+
* @tags secrets
125+
* @name DeleteSecret
126+
* @summary Deletes a secret
127+
* @request DELETE:/secrets/{namespace}/{name}
128+
* @response `204` `void` No Content
129+
* @response `401` `ApiErrorEnvelope` Unauthorized
130+
* @response `403` `ApiErrorEnvelope` Forbidden
131+
* @response `404` `ApiErrorEnvelope` Secret not found
132+
* @response `500` `ApiErrorEnvelope` Internal server error
133+
*/
134+
deleteSecret = (namespace: string, name: string, params: RequestParams = {}) =>
135+
this.request<void, ApiErrorEnvelope>({
136+
path: `/secrets/${namespace}/${name}`,
137+
method: 'DELETE',
138+
type: ContentType.Json,
139+
...params,
140+
});
141+
}

workspaces/frontend/src/generated/data-contracts.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ export interface ApiNamespaceListEnvelope {
7777
data: NamespacesNamespace[];
7878
}
7979

80+
export interface ApiSecretCreateEnvelope {
81+
data: SecretsSecretCreate;
82+
}
83+
84+
export interface ApiSecretEnvelope {
85+
data: SecretsSecretUpdate;
86+
}
87+
88+
export interface ApiSecretListEnvelope {
89+
data: SecretsSecretListItem[];
90+
}
91+
8092
export interface ApiValidationError {
8193
field: string;
8294
message: string;
@@ -107,6 +119,13 @@ export interface ApiWorkspaceListEnvelope {
107119
data: WorkspacesWorkspace[];
108120
}
109121

122+
export interface CommonAudit {
123+
createdAt: string;
124+
createdBy: string;
125+
updatedAt: string;
126+
updatedBy: string;
127+
}
128+
110129
export interface HealthCheckHealthCheck {
111130
status: HealthCheckServiceStatus;
112131
systemInfo: HealthCheckSystemInfo;
@@ -120,6 +139,41 @@ export interface NamespacesNamespace {
120139
name: string;
121140
}
122141

142+
export interface SecretsSecretCreate {
143+
contents: SecretsSecretData;
144+
immutable: boolean;
145+
name: string;
146+
type: string;
147+
}
148+
149+
export type SecretsSecretData = Record<string, SecretsSecretValue>;
150+
151+
export interface SecretsSecretListItem {
152+
audit: CommonAudit;
153+
canMount: boolean;
154+
canUpdate: boolean;
155+
immutable: boolean;
156+
mounts?: SecretsSecretMount[];
157+
name: string;
158+
type: string;
159+
}
160+
161+
export interface SecretsSecretMount {
162+
group: string;
163+
kind: string;
164+
name: string;
165+
}
166+
167+
export interface SecretsSecretUpdate {
168+
contents: SecretsSecretData;
169+
immutable: boolean;
170+
type: string;
171+
}
172+
173+
export interface SecretsSecretValue {
174+
base64?: string;
175+
}
176+
123177
export interface WorkspacekindsImageConfig {
124178
default: string;
125179
values: WorkspacekindsImageConfigValue[];

workspaces/frontend/src/shared/api/notebookApi.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Healthcheck } from '~/generated/Healthcheck';
22
import { Namespaces } from '~/generated/Namespaces';
3+
import { Secrets } from '~/generated/Secrets';
34
import { Workspacekinds } from '~/generated/Workspacekinds';
45
import { Workspaces } from '~/generated/Workspaces';
56
import { ApiInstance } from '~/shared/api/types';
@@ -9,6 +10,7 @@ export interface NotebookApis {
910
namespaces: ApiInstance<typeof Namespaces>;
1011
workspaces: ApiInstance<typeof Workspaces>;
1112
workspaceKinds: ApiInstance<typeof Workspacekinds>;
13+
secrets: ApiInstance<typeof Secrets>;
1214
}
1315

1416
export const notebookApisImpl = (path: string): NotebookApis => {
@@ -19,5 +21,6 @@ export const notebookApisImpl = (path: string): NotebookApis => {
1921
namespaces: new Namespaces(commonConfig),
2022
workspaces: new Workspaces(commonConfig),
2123
workspaceKinds: new Workspacekinds(commonConfig),
24+
secrets: new Secrets(commonConfig),
2225
};
2326
};

workspaces/frontend/src/shared/mock/mockBuilder.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
HealthCheckHealthCheck,
44
HealthCheckServiceStatus,
55
NamespacesNamespace,
6+
SecretsSecretListItem,
67
WorkspacekindsRedirectMessageLevel,
78
WorkspacekindsWorkspaceKind,
89
WorkspacesImageConfig,
@@ -475,3 +476,20 @@ export const buildMockWorkspaceList = (args: {
475476
}
476477
return workspaces;
477478
};
479+
480+
export const buildMockSecret = (
481+
secret?: Partial<SecretsSecretListItem>,
482+
): SecretsSecretListItem => ({
483+
name: 'secret-1',
484+
type: 'Opaque',
485+
immutable: false,
486+
canMount: true,
487+
canUpdate: true,
488+
audit: {
489+
createdAt: new Date(2025, 4, 1).toISOString(),
490+
createdBy: 'test',
491+
updatedAt: new Date(2025, 4, 1).toISOString(),
492+
updatedBy: 'test',
493+
},
494+
...secret,
495+
});

workspaces/frontend/src/shared/mock/mockNotebookApis.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
mockAllWorkspaces,
55
mockedHealthCheckResponse,
66
mockNamespaces,
7+
mockSecretCreate,
8+
mockSecretsList,
79
mockWorkspace1,
810
mockWorkspaceKind1,
911
mockWorkspaceKinds,
@@ -80,4 +82,22 @@ export const mockNotebookApisImpl = (): NotebookApis => ({
8082
return { data: mockWorkspaceKind1 };
8183
},
8284
},
85+
secrets: {
86+
listSecrets: async () => ({
87+
data: mockSecretsList,
88+
}),
89+
createSecret: async () => ({
90+
data: mockSecretCreate,
91+
}),
92+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
93+
getSecret: async () => ({
94+
data: mockSecretCreate,
95+
}),
96+
updateSecret: async () => ({
97+
data: mockSecretCreate,
98+
}),
99+
deleteSecret: async () => {
100+
await delay(1500);
101+
},
102+
},
83103
});

workspaces/frontend/src/shared/mock/mockNotebookServiceData.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
SecretsSecretCreate,
23
WorkspacekindsWorkspaceKind,
34
WorkspacesWorkspace,
45
WorkspacesWorkspaceKindInfo,
@@ -11,6 +12,7 @@ import {
1112
buildMockWorkspaceKind,
1213
buildMockWorkspaceKindInfo,
1314
buildMockWorkspaceList,
15+
buildMockSecret,
1416
} from '~/shared/mock/mockBuilder';
1517

1618
// Health
@@ -171,3 +173,23 @@ export const mockAllWorkspaces = [
171173
kind: mockWorkspaceKindInfo1,
172174
}),
173175
];
176+
177+
export const mockSecretCreate: SecretsSecretCreate = {
178+
name: 'secret-1',
179+
type: 'Opaque',
180+
immutable: false,
181+
contents: {
182+
data: {
183+
base64: 'abcd',
184+
},
185+
},
186+
};
187+
188+
export const mockSecretsList = [
189+
buildMockSecret({
190+
name: 'secret-1',
191+
}),
192+
buildMockSecret({
193+
name: 'secret-2',
194+
}),
195+
];

0 commit comments

Comments
 (0)