Skip to content

feat: implement UI for top level dynamic parameters diagnostics #17394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 49 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7b61485
feat: create dynamic parameter component
jaaydenh Apr 10, 2025
f5d4a1c
fix: format
jaaydenh Apr 11, 2025
0fc4289
chore: cleanup, update validation
jaaydenh Apr 11, 2025
d109874
chore: update for types from typesGenerated
jaaydenh Apr 11, 2025
7c13eb7
fix: remove filters
jaaydenh Apr 11, 2025
4290284
chore: remove unused typesParameter.ts
jaaydenh Apr 11, 2025
74084fb
fix: updates for PR review
jaaydenh Apr 15, 2025
05adc15
fix: format
jaaydenh Apr 15, 2025
b2c662a
fix: remove websocket code
jaaydenh Apr 15, 2025
2da7d99
feat: connect to dynamic parameters websocket
jaaydenh Apr 14, 2025
98dfee2
chore: cleanup
jaaydenh Apr 14, 2025
d1ada89
fix: set initial values
jaaydenh Apr 14, 2025
e04ce2f
fix: fix commit
jaaydenh Apr 16, 2025
a6f480d
fix: fix rebase issues
jaaydenh Apr 16, 2025
2613100
chore: update valid value methods
jaaydenh Apr 16, 2025
1e66a71
chore: onError is required
jaaydenh Apr 16, 2025
9a9201e
chore: display websocket error in UI
jaaydenh Apr 16, 2025
1777ca9
fix: format
jaaydenh Apr 16, 2025
d7e46ff
feat: create dynamic parameter component
jaaydenh Apr 10, 2025
946d27a
chore: cleanup, update validation
jaaydenh Apr 11, 2025
c59a546
chore: update for types from typesGenerated
jaaydenh Apr 11, 2025
82a4733
fix: remove filters
jaaydenh Apr 11, 2025
c1cc222
chore: remove unused typesParameter.ts
jaaydenh Apr 11, 2025
ef389a9
feat: connect to dynamic parameters websocket
jaaydenh Apr 14, 2025
d21b83f
chore: cleanup
jaaydenh Apr 14, 2025
8c5be29
feat: enable top level diagnostics display
jaaydenh Apr 14, 2025
0658c35
fix: remove useWebsocket.ts
jaaydenh Apr 16, 2025
001944c
fix: add missing icons
jaaydenh Apr 16, 2025
00d6c32
fix: updates for PR review
jaaydenh Apr 16, 2025
57377b5
feat: create dynamic parameter component
jaaydenh Apr 10, 2025
ee37ae5
chore: cleanup, update validation
jaaydenh Apr 11, 2025
951bf12
chore: update for types from typesGenerated
jaaydenh Apr 11, 2025
de71716
fix: remove filters
jaaydenh Apr 11, 2025
193702a
chore: remove unused typesParameter.ts
jaaydenh Apr 11, 2025
c9608b4
fix: updates for PR review
jaaydenh Apr 15, 2025
cd09e42
fix: format
jaaydenh Apr 15, 2025
1a825b2
fix: remove websocket code
jaaydenh Apr 15, 2025
81d4a2b
fix: fix commit
jaaydenh Apr 16, 2025
c486af1
feat: create dynamic parameter component
jaaydenh Apr 10, 2025
53f1c22
chore: cleanup, update validation
jaaydenh Apr 11, 2025
7bcd18d
chore: update for types from typesGenerated
jaaydenh Apr 11, 2025
d6a4fde
fix: remove filters
jaaydenh Apr 11, 2025
ddd58da
chore: remove unused typesParameter.ts
jaaydenh Apr 11, 2025
ed981c6
feat: connect to dynamic parameters websocket
jaaydenh Apr 14, 2025
8d6ba96
chore: cleanup
jaaydenh Apr 14, 2025
99d4c3a
feat: enable top level diagnostics display
jaaydenh Apr 14, 2025
3ba83c9
fix: remove useWebsocket.ts
jaaydenh Apr 16, 2025
1555961
fix: add missing icons
jaaydenh Apr 16, 2025
7a34145
Merge branch 'jaaydenh/dynamic-params-diagnostics' of https://github.…
jaaydenh Apr 16, 2025
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
Prev Previous commit
Next Next commit
feat: create dynamic parameter component
  • Loading branch information
jaaydenh committed Apr 16, 2025
commit c486af13b970e77f964792d83fb413b9d0aab5a1
124 changes: 124 additions & 0 deletions site/src/api/typesParameter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Code generated by 'guts'. DO NOT EDIT.

// From types/diagnostics.go
export type DiagnosticSeverityString = "error" | "warning";

export const DiagnosticSeverityStrings: DiagnosticSeverityString[] = [
"error",
"warning",
];

// From types/diagnostics.go
export type Diagnostics = readonly FriendlyDiagnostic[];

// From types/diagnostics.go
export interface FriendlyDiagnostic {
readonly severity: DiagnosticSeverityString;
readonly summary: string;
readonly detail: string;
}

// From types/value.go
export interface NullHCLString {
readonly value: string;
readonly valid: boolean;
}

// From types/parameter.go
export interface Parameter extends ParameterData {
readonly value: NullHCLString;
readonly diagnostics: Diagnostics;
}

// From types/parameter.go
export interface ParameterData {
readonly name: string;
readonly display_name: string;
readonly description: string;
readonly type: ParameterType;
// this is likely an enum in an external package "github.com/coder/terraform-provider-coder/v2/provider.ParameterFormType"
readonly form_type: string;
// empty interface{} type, falling back to unknown
readonly styling: unknown;
readonly mutable: boolean;
readonly default_value: NullHCLString;
readonly icon: string;
readonly options: readonly ParameterOption[];
readonly validations: readonly ParameterValidation[];
readonly required: boolean;
readonly order: number;
readonly ephemeral: boolean;
}

// From types/parameter.go
export interface ParameterOption {
readonly name: string;
readonly description: string;
readonly value: NullHCLString;
readonly icon: string;
}

// From types/enum.go
export type ParameterType = "bool" | "list(string)" | "number" | "string";

export const ParameterTypes: ParameterType[] = [
"bool",
"list(string)",
"number",
"string",
];

// From types/parameter.go
export interface ParameterValidation {
readonly validation_error: string;
readonly validation_regex: string | null;
readonly validation_min: number | null;
readonly validation_max: number | null;
readonly validation_monotonic: string | null;
readonly validation_invalid: boolean | null;
}

// From web/session.go
export interface Request {
readonly id: number;
readonly inputs: Record<string, string>;
}

// From web/session.go
export interface Response {
readonly id: number;
readonly diagnostics: Diagnostics;
readonly parameters: readonly Parameter[];
}

// From web/session.go
export interface SessionInputs {
readonly PlanPath: string;
readonly User: WorkspaceOwner;
}

// From types/parameter.go
export const ValidationMonotonicDecreasing = "decreasing";

// From types/parameter.go
export const ValidationMonotonicIncreasing = "increasing";

// From types/owner.go
export interface WorkspaceOwner {
readonly id: string;
readonly name: string;
readonly full_name: string;
readonly email: string;
readonly ssh_public_key: string;
readonly groups: readonly string[];
readonly session_token: string;
readonly oidc_access_token: string;
readonly login_type: string;
readonly rbac_roles: readonly WorkspaceOwnerRBACRole[];
}

// From types/owner.go
export interface WorkspaceOwnerRBACRole {
readonly name: string;
readonly org_id: string;
}
94 changes: 94 additions & 0 deletions site/src/hooks/useWebsocket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// This file is temporary until we have a proper websocket implementation for dynamic parameters
import { useCallback, useEffect, useRef, useState } from "react";

export function useWebSocket<T>(
url: string,
testdata: string,
user: string,
plan: string,
) {
const [message, setMessage] = useState<T | null>(null);
const [connectionStatus, setConnectionStatus] = useState<
"connecting" | "connected" | "disconnected"
>("connecting");
const wsRef = useRef<WebSocket | null>(null);
const urlRef = useRef(url);

const connectWebSocket = useCallback(() => {
try {
const ws = new WebSocket(urlRef.current);
wsRef.current = ws;
setConnectionStatus("connecting");

ws.onopen = () => {
// console.log("Connected to WebSocket");
setConnectionStatus("connected");
ws.send(JSON.stringify({}));
};

ws.onmessage = (event) => {
try {
const data: T = JSON.parse(event.data);
// console.log("Received message:", data);
setMessage(data);
} catch (err) {
console.error("Invalid JSON from server: ", event.data);
console.error("Error: ", err);
}
};

ws.onerror = (event) => {
console.error("WebSocket error:", event);
};

ws.onclose = (event) => {
// console.log(
// `WebSocket closed with code ${event.code}. Reason: ${event.reason}`,
// );
setConnectionStatus("disconnected");
};
} catch (error) {
console.error("Failed to create WebSocket connection:", error);
setConnectionStatus("disconnected");
}
}, []);

useEffect(() => {
if (!testdata) {
return;
}

setMessage(null);
setConnectionStatus("connecting");

const createConnection = () => {
urlRef.current = url;
connectWebSocket();
};

if (wsRef.current) {
wsRef.current.close();
wsRef.current = null;
}

const timeoutId = setTimeout(createConnection, 100);

return () => {
clearTimeout(timeoutId);
if (wsRef.current) {
wsRef.current.close();
wsRef.current = null;
}
};
}, [testdata, connectWebSocket, url]);

const sendMessage = (data: unknown) => {
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(data));
} else {
console.warn("Cannot send message: WebSocket is not connected");
}
};

return { message, sendMessage, connectionStatus };
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ import {
} from "./permissions";
export type ExternalAuthPollingState = "idle" | "polling" | "abandoned";

const serverAddress = "localhost:8100";
const urlTestdata = "demo";
const wsUrl = `ws://${serverAddress}/ws/${encodeURIComponent(urlTestdata)}`;

const CreateWorkspacePageExperimental: FC = () => {
const { organization: organizationName = "default", template: templateName } =
useParams() as { organization?: string; template: string };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,44 @@ export interface CreateWorkspacePageViewExperimentalProps {
startPollingExternalAuth: () => void;
}

// const getInitialParameterValues = (
// params: Parameter[],
// autofillParams?: AutofillBuildParameter[],
// ): WorkspaceBuildParameter[] => {
// return params.map((parameter) => {
// // Short-circuit for ephemeral parameters, which are always reset to
// // the template-defined default.
// if (parameter.ephemeral) {
// return {
// name: parameter.name,
// value: parameter.default_value,
// };
// }

// const autofillParam = autofillParams?.find(
// ({ name }) => name === parameter.name,
// );

// return {
// name: parameter.name,
// value:
// autofillParam &&
// // isValidValue(parameter, autofillParam) &&
// autofillParam.source !== "user_history"
// ? autofillParam.value
// : parameter.default_value,
// };
// });
// };

const getInitialParameterValues = (parameters: Parameter[]) => {
return parameters.map((parameter) => {
return {
name: parameter.name,
value: parameter.default_value.valid ? parameter.default_value.value : "",
};
});
};
export const CreateWorkspacePageViewExperimental: FC<
CreateWorkspacePageViewExperimentalProps
> = ({
Expand Down