From 7193b3a99d76211e34fdd3aa223b1a2b1c776059 Mon Sep 17 00:00:00 2001 From: Mike Solomon Date: Wed, 16 Apr 2025 09:50:08 -0700 Subject: [PATCH] Agent experiment (#220) * Saving progress * Add required fields * Save env vars + hide builder --------- Co-authored-by: Mike Solomon --- .../moderne-platform/experimental-builder.md | 5 + src/components/CommandBuilder.js | 362 ++++++++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 docs/administrator-documentation/moderne-platform/experimental-builder.md create mode 100644 src/components/CommandBuilder.js diff --git a/docs/administrator-documentation/moderne-platform/experimental-builder.md b/docs/administrator-documentation/moderne-platform/experimental-builder.md new file mode 100644 index 00000000..7cf6cfae --- /dev/null +++ b/docs/administrator-documentation/moderne-platform/experimental-builder.md @@ -0,0 +1,5 @@ +# Experimental agent builder + +import CommandBuilder from '@site/src/components/CommandBuilder' + + \ No newline at end of file diff --git a/src/components/CommandBuilder.js b/src/components/CommandBuilder.js new file mode 100644 index 00000000..365730e5 --- /dev/null +++ b/src/components/CommandBuilder.js @@ -0,0 +1,362 @@ +import React, { useState, useEffect } from 'react'; +import { useColorMode } from '@docusaurus/theme-common'; + +const options = [ + { + label: 'Uses GitHub', + value: '--github-auth', + id: 'github', + hasExtraFields: true, + canRepeat: true, + fields: [ + { + label: 'Client ID', + key: 'clientId', + javaCliKey: 'moderne.agent.github[${i}].clientId', + dockerCliKey: 'MODERNE_AGENT_GITHUB_${i}_CLIENT_ID', + required: true, + }, + { + label: 'Client Secret', + key: 'clientSecret', + javaCliKey: 'moderne.agent.github[${i}].clientSecret', + dockerCliKey: 'MODERNE_AGENT_GITHUB_${i}_CLIENT_SECRET', + required: true, + }, + { + label: 'URL', + key: 'url', + javaCliKey: 'moderne.agent.github[${i}].url', + dockerCliKey: 'MODERNE_AGENT_GITHUB_${i}_URL', + required: false, + }, + ] + }, + { + label: 'Uses Bitbucket', + value: '--bitbucket-auth', + id: 'bitbucket', + hasExtraFields: true, + canRepeat: true, + fields: [ + { + label: 'Client ID', + key: 'clientId', + javaCliKey: 'moderne.agent.bitbucket[${i}].clientId', + dockerCliKey: 'MODERNE_AGENT_BITBUCKET_${i}_CLIENT_ID', + required: true, + }, + { + label: 'Client Secret', + key: 'clientSecret', + javaCliKey: 'moderne.agent.bitbucket[${i}].clientSecret', + dockerCliKey: 'MODERNE_AGENT_BITBUCKET_${i}_CLIENT_SECRET', + required: true, + }, + { + label: 'URL', + key: 'url', + javaCliKey: 'moderne.agent.bitbucket[${i}].url', + dockerCliKey: 'MODERNE_AGENT_BITBUCKET_${i}_URL', + required: false, + }, + ] + }, +]; + +export default function CommandBuilder() { + const [cliType, setCliType] = useState('docker'); // or 'java' + const [selectedOptions, setSelectedOptions] = useState([]); + const [providerData, setProviderData] = useState({}); + const [warningMessage, setWarningMessage] = useState(''); + const [copied, setCopied] = useState(false); + const [command, setCommand] = useState(''); + const { colorMode } = useColorMode(); + const isDark = colorMode === 'dark'; + + const handleCheckboxChange = (opt) => { + const isSelected = selectedOptions.includes(opt.value); + const updated = isSelected + ? selectedOptions.filter((val) => val !== opt.value) + : [...selectedOptions, opt.value]; + + setSelectedOptions(updated); + + if (!isSelected && opt.hasExtraFields && opt.canRepeat) { + setProviderData((prev) => ({ + ...prev, + [opt.id]: { count: 1, inputs: {} }, + })); + } else if (isSelected && opt.hasExtraFields) { + setProviderData((prev) => { + const next = { ...prev }; + delete next[opt.id]; + return next; + }); + } + }; + + const handleFieldChange = (providerId, fieldKey, index, value) => { + setProviderData((prev) => ({ + ...prev, + [providerId]: { + ...prev[providerId], + inputs: { + ...prev[providerId]?.inputs, + [`${fieldKey}_${index}`]: value, + }, + }, + })); + }; + + const setProviderCount = (providerId, count) => { + setProviderData((prev) => ({ + ...prev, + [providerId]: { + ...prev[providerId], + count, + }, + })); + }; + + const handleEnvToggle = (providerId, fieldKey, index, checked) => { + setProviderData((prev) => ({ + ...prev, + [providerId]: { + ...prev[providerId], + inputs: { + ...prev[providerId]?.inputs, + [`${fieldKey}_${index}_useEnv`]: checked, + }, + }, + })); + }; + + // This function is used to generate the CLI command. + const buildCommand = () => { + let baseCmd = + cliType === 'java' + ? `mycli ${selectedOptions.filter((opt) => !opt.includes('--auth')).join(' ')}` + : `docker run myimage`; + + let exports = []; + let missingFields = false; + + options.forEach((opt) => { + if ( + selectedOptions.includes(opt.value) && + opt.hasExtraFields && + opt.canRepeat + ) { + const data = providerData[opt.id]; + if (!data) return; + + for (let i = 0; i < data.count; i++) { + opt.fields.forEach((field) => { + const val = data.inputs[`${field.key}_${i}`]; + const useEnv = data.inputs[`${field.key}_${i}_useEnv`] || false; + + if (field.required && !val) { + missingFields = true; + } + + if (val) { + const keyTemplate = + cliType === 'java' ? field.javaCliKey : field.dockerCliKey; + const resolvedKey = keyTemplate.replace('${i}', i); + + if (useEnv) { + exports.push(`export ${resolvedKey}=${val}`); + baseCmd += + cliType === 'java' + ? ` --${resolvedKey}` + : ` -e ${resolvedKey}`; + } else { + baseCmd += + cliType === 'java' + ? ` --${resolvedKey}=${val}` + : ` -e ${resolvedKey}=${val}`; + } + } + }); + } + } + }); + + const exportBlock = exports.length ? exports.map(line => line + '\n').join('') + '\n' : ''; + const message = missingFields + ? '⚠️ Some required fields are missing.' + : ''; + setWarningMessage(message); + + return exportBlock + baseCmd; + }; + + + // Update the command every time the options or data change + useEffect(() => { + const generatedCommand = buildCommand(); + setCommand(generatedCommand); // Update the command in the state + }, [selectedOptions, providerData, cliType]); + + const copyToClipboard = () => { + if (command) { + navigator.clipboard.writeText(command).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }); + } + }; + + return ( +
+

Build Your CLI Command

+ +
+
+ + Select Output Type + + + +
+
+ +
+ {options.map((opt) => ( +
+ + + {selectedOptions.includes(opt.value) && opt.hasExtraFields && opt.canRepeat && ( +
+ + {[...Array(providerData[opt.id]?.count || 1)].map((_, index) => ( +
+ {opt.label} #{index + 1} + {opt.fields.map((field) => ( +
+ + + handleFieldChange(opt.id, field.key, index, e.target.value) + } + required={field.required} + style={{ + border: + field.required && + !providerData[opt.id]?.inputs[`${field.key}_${index}`] + ? '1px solid red' + : '1px solid #ccc', + }} + /> + +
+ ))} +
+ ))} +
+ )} +
+ ))} +
+ + {warningMessage && ( +
+ {warningMessage} +
+ )} + +
+ + {command} + +
+ + {copied && ( + + ✅ Copied! + + )} +
+
+
+ ); +}