Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion workspaces/frontend/scripts/swagger.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4f0a29dec0d3c9f0d0f02caab4dc84101bfef8b0
ec35a7c28bea82770c2f6af1ee35d3135114d451
2 changes: 2 additions & 0 deletions workspaces/frontend/src/app/components/WorkspaceTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,8 @@ const WorkspaceTable = React.forwardRef<WorkspaceTableRef, WorkspaceTableProps>(
imageSrc={kindLogoDict[workspace.workspaceKind.name]}
/>
}
assetType="logo"
kindName={workspace.workspaceKind.name}
>
{(validSrc) => (
<Tooltip content={workspace.workspaceKind.name}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ describe('useWorkspaceCountPerKind', () => {
listWorkspaceKinds: mockListWorkspaceKinds,
createWorkspaceKind: jest.fn(),
getWorkspaceKind: jest.fn(),
getWorkspaceKindIcon: jest.fn(),
getWorkspaceKindLogo: jest.fn(),
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,8 @@ export const WorkspaceKinds: React.FunctionComponent = () => {
imageSrc={workspaceKind.icon.url}
skeletonWidth="20px"
fallback={<ImageFallback imageSrc={workspaceKind.icon.url} />}
assetType="icon"
kindName={workspaceKind.name}
>
{(validSrc) => (
<img
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export const WorkspaceKindDetailsOverview: React.FunctionComponent<
message="Cannot load icon image"
/>
}
assetType="icon"
kindName={workspaceKind.name}
>
{(validSrc) => <img src={validSrc} alt={workspaceKind.name} style={{ width: '40px' }} />}
</WithValidImage>
Expand All @@ -84,6 +86,8 @@ export const WorkspaceKindDetailsOverview: React.FunctionComponent<
message="Cannot load logo image"
/>
}
assetType="logo"
kindName={workspaceKind.name}
>
{(validSrc) => <img src={validSrc} alt={workspaceKind.name} style={{ width: '40px' }} />}
</WithValidImage>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ export const WorkspaceFormKindList: React.FunctionComponent<WorkspaceFormKindLis
message="Cannot load logo image"
/>
}
assetType="logo"
kindName={kind.name}
>
{(validSrc) => (
<img
Expand Down
6 changes: 3 additions & 3 deletions workspaces/frontend/src/app/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
AssetsImageRef,
WorkspacekindsImageConfigValue,
WorkspacekindsImageRef,
WorkspacekindsPodConfigValue,
WorkspacekindsPodMetadata,
WorkspacekindsPodVolumeMounts,
Expand Down Expand Up @@ -50,8 +50,8 @@ export interface WorkspaceKindProperties {
deprecated: boolean;
deprecationMessage: string;
hidden: boolean;
icon: WorkspacekindsImageRef;
logo: WorkspacekindsImageRef;
icon: AssetsImageRef;
logo: AssetsImageRef;
}

export interface WorkspaceKindImageConfigValue extends WorkspacekindsImageConfigValue {
Expand Down
38 changes: 38 additions & 0 deletions workspaces/frontend/src/generated/Workspacekinds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,42 @@ export class Workspacekinds<SecurityDataType = unknown> extends HttpClient<Secur
format: 'json',
...params,
});
/**
* @description Returns the icon image for a specific workspace kind. If the icon is stored in a ConfigMap, it serves the image content. If the icon is a remote URL, returns 404 (browser should fetch directly).
*
* @tags workspacekinds
* @name GetWorkspaceKindIcon
* @summary Get workspace kind icon
* @request GET:/workspacekinds/{name}/assets/icon.svg
* @response `200` `string` SVG image content
* @response `404` `ApiErrorEnvelope` Not Found. Icon uses remote URL or resource does not exist.
* @response `500` `ApiErrorEnvelope` Internal server error.
*/
getWorkspaceKindIcon = (name: string, params: RequestParams = {}) =>
this.request<string, ApiErrorEnvelope>({
path: `/workspacekinds/${name}/assets/icon.svg`,
method: 'GET',
type: ContentType.Json,
format: 'blob',
...params,
});
/**
* @description Returns the logo image for a specific workspace kind. If the logo is stored in a ConfigMap, it serves the image content. If the logo is a remote URL, returns 404 (browser should fetch directly).
*
* @tags workspacekinds
* @name GetWorkspaceKindLogo
* @summary Get workspace kind logo
* @request GET:/workspacekinds/{name}/assets/logo.svg
* @response `200` `string` SVG image content
* @response `404` `ApiErrorEnvelope` Not Found. Logo uses remote URL or resource does not exist.
* @response `500` `ApiErrorEnvelope` Internal server error.
*/
getWorkspaceKindLogo = (name: string, params: RequestParams = {}) =>
this.request<string, ApiErrorEnvelope>({
path: `/workspacekinds/${name}/assets/logo.svg`,
method: 'GET',
type: ContentType.Json,
format: 'blob',
...params,
});
}
12 changes: 7 additions & 5 deletions workspaces/frontend/src/generated/Workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ export class Workspaces<SecurityDataType = unknown> extends HttpClient<SecurityD
* @summary List workspaces by namespace
* @request GET:/workspaces/{namespace}
* @response `200` `ApiWorkspaceListEnvelope` Successful operation. Returns a list of workspaces in the specified namespace.
* @response `400` `ApiErrorEnvelope` Bad Request. Invalid namespace format.
* @response `401` `ApiErrorEnvelope` Unauthorized. Authentication is required.
* @response `403` `ApiErrorEnvelope` Forbidden. User does not have permission to list workspaces.
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
* @response `500` `ApiErrorEnvelope` Internal server error. An unexpected error occurred on the server.
*/
listWorkspacesByNamespace = (namespace: string, params: RequestParams = {}) =>
Expand All @@ -69,12 +69,13 @@ export class Workspaces<SecurityDataType = unknown> extends HttpClient<SecurityD
* @summary Create workspace
* @request POST:/workspaces/{namespace}
* @response `201` `ApiWorkspaceEnvelope` Workspace created successfully
* @response `400` `ApiErrorEnvelope` Bad Request. Invalid request body or namespace format.
* @response `400` `ApiErrorEnvelope` Bad Request.
* @response `401` `ApiErrorEnvelope` Unauthorized. Authentication is required.
* @response `403` `ApiErrorEnvelope` Forbidden. User does not have permission to create workspace.
* @response `409` `ApiErrorEnvelope` Conflict. Workspace with the same name already exists.
* @response `413` `ApiErrorEnvelope` Request Entity Too Large. The request body is too large.
* @response `415` `ApiErrorEnvelope` Unsupported Media Type. Content-Type header is not correct.
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
* @response `500` `ApiErrorEnvelope` Internal server error. An unexpected error occurred on the server.
*/
createWorkspace = (
Expand Down Expand Up @@ -102,9 +103,10 @@ export class Workspaces<SecurityDataType = unknown> extends HttpClient<SecurityD
* @response `401` `ApiErrorEnvelope` Unauthorized. Authentication is required.
* @response `403` `ApiErrorEnvelope` Forbidden. User does not have permission to access the workspace.
* @response `404` `ApiErrorEnvelope` Not Found. Workspace does not exist.
* @response `409` `ApiErrorEnvelope` Conflict. Workspace not in valid state for action.
* @response `413` `ApiErrorEnvelope` Request Entity Too Large. The request body is too large.
* @response `415` `ApiErrorEnvelope` Unsupported Media Type. Content-Type header is not correct.
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Workspace is not in appropriate state.
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
* @response `500` `ApiErrorEnvelope` Internal server error. An unexpected error occurred on the server.
*/
updateWorkspacePauseState = (
Expand All @@ -129,10 +131,10 @@ export class Workspaces<SecurityDataType = unknown> extends HttpClient<SecurityD
* @summary Get workspace
* @request GET:/workspaces/{namespace}/{workspace_name}
* @response `200` `ApiWorkspaceEnvelope` Successful operation. Returns the requested workspace details.
* @response `400` `ApiErrorEnvelope` Bad Request. Invalid namespace or workspace name format.
* @response `401` `ApiErrorEnvelope` Unauthorized. Authentication is required.
* @response `403` `ApiErrorEnvelope` Forbidden. User does not have permission to access the workspace.
* @response `404` `ApiErrorEnvelope` Not Found. Workspace does not exist.
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
* @response `500` `ApiErrorEnvelope` Internal server error. An unexpected error occurred on the server.
*/
getWorkspace = (namespace: string, workspaceName: string, params: RequestParams = {}) =>
Expand All @@ -151,10 +153,10 @@ export class Workspaces<SecurityDataType = unknown> extends HttpClient<SecurityD
* @summary Delete workspace
* @request DELETE:/workspaces/{namespace}/{workspace_name}
* @response `204` `void` Workspace deleted successfully
* @response `400` `ApiErrorEnvelope` Bad Request. Invalid namespace or workspace name format.
* @response `401` `ApiErrorEnvelope` Unauthorized. Authentication is required.
* @response `403` `ApiErrorEnvelope` Forbidden. User does not have permission to delete the workspace.
* @response `404` `ApiErrorEnvelope` Not Found. Workspace does not exist.
* @response `422` `ApiErrorEnvelope` Unprocessable Entity. Validation error.
* @response `500` `ApiErrorEnvelope` Internal server error. An unexpected error occurred on the server.
*/
deleteWorkspace = (namespace: string, workspaceName: string, params: RequestParams = {}) =>
Expand Down
81 changes: 66 additions & 15 deletions workspaces/frontend/src/generated/data-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,39 @@ export enum FieldErrorType {
ErrorTypeTypeInvalid = 'FieldValueTypeInvalid',
}

export enum AssetsImageRefErrorCode {
ImageRefErrorCodeConfigMapMissing = 'CONFIGMAP_MISSING',
ImageRefErrorCodeConfigMapKeyMissing = 'CONFIGMAP_KEY_MISSING',
ImageRefErrorCodeConfigMapUnknown = 'CONFIGMAP_UNKNOWN',
ImageRefErrorCodeUnknown = 'UNKNOWN',
}

export enum ApiErrorCauseOrigin {
OriginInternal = 'INTERNAL',
OriginKubernetes = 'KUBERNETES',
}

export interface ActionsWorkspaceActionPause {
paused: boolean;
}

export interface ApiConflictError {
/**
* A human-readable description of the cause of the error.
* This field may be presented as-is to a reader.
*/
message?: string;
/**
* Origin indicates where the conflict error originated.
* If value is empty, the origin is unknown.
*/
origin?: ApiErrorCauseOrigin;
}

export interface ApiErrorCause {
/** ConflictCauses contains details about conflict errors that caused the request to fail. */
conflict_cause?: ApiConflictError[];
/** ValidationErrors contains details about validation errors that caused the request to fail. */
validation_errors?: ApiValidationError[];
}

Expand All @@ -68,8 +96,11 @@ export interface ApiErrorEnvelope {
}

export interface ApiHTTPError {
/** Cause contains detailed information about the cause of the error. */
cause?: ApiErrorCause;
/** Code is a string representation of the HTTP status code. */
code: string;
/** Message is a human-readable description of the error. */
message: string;
}

Expand All @@ -78,9 +109,32 @@ export interface ApiNamespaceListEnvelope {
}

export interface ApiValidationError {
field: string;
message: string;
type: FieldErrorType;
/**
* The field of the resource that has caused this error, as named by its JSON serialization.
* May include dot and postfix notation for nested attributes.
* Arrays are zero-indexed.
* Fields may appear more than once in an array of causes due to fields having multiple errors.
*
* Examples:
* "name" - the field "name" on the current resource
* "items[0].name" - the field "name" on the first array entry in "items"
*/
field?: string;
/**
* A human-readable description of the cause of the error.
* This field may be presented as-is to a reader.
*/
message?: string;
/**
* Origin indicates where the validation error originated.
* If value is empty, the origin is unknown.
*/
origin?: ApiErrorCauseOrigin;
/**
* A machine-readable description of the cause of the error.
* If value is empty, there is no information available.
*/
type?: FieldErrorType;
}

export interface ApiWorkspaceActionPauseEnvelope {
Expand All @@ -107,6 +161,11 @@ export interface ApiWorkspaceListEnvelope {
data: WorkspacesWorkspace[];
}

export interface AssetsImageRef {
error?: AssetsImageRefErrorCode;
url: string;
}

export interface HealthCheckHealthCheck {
status: HealthCheckServiceStatus;
systemInfo: HealthCheckSystemInfo;
Expand Down Expand Up @@ -135,10 +194,6 @@ export interface WorkspacekindsImageConfigValue {
redirect?: WorkspacekindsOptionRedirect;
}

export interface WorkspacekindsImageRef {
url: string;
}

export interface WorkspacekindsOptionLabel {
key: string;
value: string;
Expand Down Expand Up @@ -196,8 +251,8 @@ export interface WorkspacekindsWorkspaceKind {
description: string;
displayName: string;
hidden: boolean;
icon: WorkspacekindsImageRef;
logo: WorkspacekindsImageRef;
icon: AssetsImageRef;
logo: AssetsImageRef;
name: string;
podTemplate: WorkspacekindsPodTemplate;
}
Expand Down Expand Up @@ -225,10 +280,6 @@ export interface WorkspacesImageConfig {
redirectChain?: WorkspacesRedirectStep[];
}

export interface WorkspacesImageRef {
url: string;
}

export interface WorkspacesLastProbeInfo {
/** Unix Epoch time in milliseconds */
endTimeMs: number;
Expand Down Expand Up @@ -363,8 +414,8 @@ export interface WorkspacesWorkspaceCreate {
}

export interface WorkspacesWorkspaceKindInfo {
icon: WorkspacesImageRef;
logo: WorkspacesImageRef;
icon: AssetsImageRef;
logo: AssetsImageRef;
missing: boolean;
name: string;
}
Expand Down
Loading
Loading