From 0ef1b717aaaa4b25099fca01ee751962f193828b Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Fri, 13 Jun 2025 13:50:06 +0100 Subject: [PATCH] refactor: add type annotations for improved type safety across various components --- .../UI/Components/MarkdownEditor.test.tsx | 8 ++-- .../UI/Components/CodeEditor/CodeEditor.tsx | 12 ++--- .../Announcement/AnnouncementsTable.tsx | 2 +- Scripts/OpenAPI/GenerateSpec.ts | 2 +- .../TerraformProvider/FrameworkGenerator.ts | 39 +++++++++++++--- Scripts/TerraformProvider/GenerateProvider.ts | 35 ++++++++++++--- Scripts/TerraformProvider/GeneratorConfig.ts | 39 ++++++++-------- Scripts/TerraformProvider/InstallTools.ts | 39 +++++++++++++--- Scripts/TerraformProvider/ProviderSpec.ts | 8 ++-- .../SpecificationConverter.ts | 44 +++++++++++-------- 10 files changed, 157 insertions(+), 71 deletions(-) diff --git a/Common/Tests/UI/Components/MarkdownEditor.test.tsx b/Common/Tests/UI/Components/MarkdownEditor.test.tsx index 55bd4bcc36..e68a005cb2 100644 --- a/Common/Tests/UI/Components/MarkdownEditor.test.tsx +++ b/Common/Tests/UI/Components/MarkdownEditor.test.tsx @@ -12,7 +12,7 @@ describe("MarkdownEditor with SpellCheck", () => { />, ); - const textarea = screen.getByRole("textbox"); + const textarea: HTMLTextAreaElement = screen.getByRole("textbox") as HTMLTextAreaElement; expect(textarea.spellcheck).toBe(true); }); @@ -25,7 +25,7 @@ describe("MarkdownEditor with SpellCheck", () => { />, ); - const textarea = screen.getByRole("textbox"); + const textarea: HTMLTextAreaElement = screen.getByRole("textbox") as HTMLTextAreaElement; expect(textarea.spellcheck).toBe(false); }); @@ -38,7 +38,7 @@ describe("MarkdownEditor with SpellCheck", () => { />, ); - let textarea = screen.getByRole("textbox"); + let textarea: HTMLTextAreaElement = screen.getByRole("textbox") as HTMLTextAreaElement; expect(textarea.spellcheck).toBe(true); rerender( @@ -49,7 +49,7 @@ describe("MarkdownEditor with SpellCheck", () => { />, ); - textarea = screen.getByRole("textbox"); + textarea = screen.getByRole("textbox") as HTMLTextAreaElement; expect(textarea.spellcheck).toBe(false); }); }); diff --git a/Common/UI/Components/CodeEditor/CodeEditor.tsx b/Common/UI/Components/CodeEditor/CodeEditor.tsx index 951d5cd286..db1bbda04c 100644 --- a/Common/UI/Components/CodeEditor/CodeEditor.tsx +++ b/Common/UI/Components/CodeEditor/CodeEditor.tsx @@ -93,10 +93,10 @@ const CodeEditor: FunctionComponent = ( // Handle spell check configuration for Monaco Editor useEffect(() => { if (editorRef.current && props.type === CodeType.Markdown) { - const editor = editorRef.current; - const domNode = editor.getDomNode(); + const editor: any = editorRef.current; + const domNode: HTMLElement | null = editor.getDomNode(); if (domNode) { - const textareaElement = domNode.querySelector("textarea"); + const textareaElement: HTMLTextAreaElement | null = domNode.querySelector("textarea"); if (textareaElement) { textareaElement.spellcheck = !props.disableSpellCheck; } @@ -150,14 +150,14 @@ const CodeEditor: FunctionComponent = ( props.onChange(code); } }} - onMount={(editor, monaco) => { + onMount={(editor: any, _monaco: any) => { editorRef.current = editor; // Configure spell check for Markdown if (props.type === CodeType.Markdown) { - const domNode = editor.getDomNode(); + const domNode: HTMLElement | null = editor.getDomNode(); if (domNode) { - const textareaElement = domNode.querySelector("textarea"); + const textareaElement: HTMLTextAreaElement | null = domNode.querySelector("textarea"); if (textareaElement) { textareaElement.spellcheck = !props.disableSpellCheck; } diff --git a/Dashboard/src/Components/Announcement/AnnouncementsTable.tsx b/Dashboard/src/Components/Announcement/AnnouncementsTable.tsx index 1bbb8194af..c285ef9e20 100644 --- a/Dashboard/src/Components/Announcement/AnnouncementsTable.tsx +++ b/Dashboard/src/Components/Announcement/AnnouncementsTable.tsx @@ -383,7 +383,7 @@ const AnnouncementTable: FunctionComponent = ( ] as ObjectID; // Find the selected template - const selectedTemplate = announcementTemplates.find((template) => { + const selectedTemplate: StatusPageAnnouncementTemplate | undefined = announcementTemplates.find((template: StatusPageAnnouncementTemplate) => { return ( template._id?.toString() === announcementTemplateId.toString() ); diff --git a/Scripts/OpenAPI/GenerateSpec.ts b/Scripts/OpenAPI/GenerateSpec.ts index c653a0013f..2fb357edf9 100644 --- a/Scripts/OpenAPI/GenerateSpec.ts +++ b/Scripts/OpenAPI/GenerateSpec.ts @@ -12,7 +12,7 @@ export async function generateOpenAPISpec(outputPath?: string): Promise { const finalOutputPath: string = outputPath || "./openapi.json"; // Ensure the directory exists - const directory = path.dirname(finalOutputPath); + const directory: string = path.dirname(finalOutputPath); if (!fs.existsSync(directory)) { fs.mkdirSync(directory, { recursive: true }); } diff --git a/Scripts/TerraformProvider/FrameworkGenerator.ts b/Scripts/TerraformProvider/FrameworkGenerator.ts index b281ac14f3..2d2b012bdb 100644 --- a/Scripts/TerraformProvider/FrameworkGenerator.ts +++ b/Scripts/TerraformProvider/FrameworkGenerator.ts @@ -51,9 +51,9 @@ export default class FrameworkGenerator { * Scaffold starter code for a data source, provider, or resource */ public static scaffold(options: FrameworkScaffoldOptions): void { - const binaryPath = this.getTerraformFrameworkGeneratorPath(); + const binaryPath: string = this.getTerraformFrameworkGeneratorPath(); - let command = `"${binaryPath}" scaffold ${options.type} --name "${options.name}" --output-dir "${options.outputDir}"`; + let command: string = `"${binaryPath}" scaffold ${options.type} --name "${options.name}" --output-dir "${options.outputDir}"`; if (options.packageName) { command += ` --package "${options.packageName}"`; @@ -64,15 +64,20 @@ export default class FrameworkGenerator { } try { + // eslint-disable-next-line no-console console.log(`šŸ—ļø Scaffolding ${options.type}: ${options.name}`); + // eslint-disable-next-line no-console console.log(`šŸ“ Output directory: ${options.outputDir}`); + // eslint-disable-next-line no-console console.log(`šŸ”§ Running command: ${command}`); execSync(command, { stdio: "inherit" }); + // eslint-disable-next-line no-console console.log( `āœ… Successfully scaffolded ${options.type}: ${options.name}`, ); } catch (error) { + // eslint-disable-next-line no-console console.error(`āŒ Error scaffolding ${options.type}:`, error); throw new Error( `Failed to scaffold ${options.type}: ${error instanceof Error ? error.message : "Unknown error"}`, @@ -84,18 +89,22 @@ export default class FrameworkGenerator { subcommand: "all" | "data-sources" | "resources" | "provider", options: FrameworkGeneratorOptions, ): void { - const binaryPath = this.getTerraformFrameworkGeneratorPath(); + const binaryPath: string = this.getTerraformFrameworkGeneratorPath(); - let command = `"${binaryPath}" generate ${subcommand} --input "${options.specificationPath}" --output "${options.outputPath}"`; + let command: string = `"${binaryPath}" generate ${subcommand} --input "${options.specificationPath}" --output "${options.outputPath}"`; if (options.packageName) { command += ` --package "${options.packageName}"`; } try { + // eslint-disable-next-line no-console console.log(`šŸ”„ Generating ${subcommand} from specification...`); + // eslint-disable-next-line no-console console.log(`šŸ“„ Input specification: ${options.specificationPath}`); + // eslint-disable-next-line no-console console.log(`šŸ“ Output directory: ${options.outputPath}`); + // eslint-disable-next-line no-console console.log(`šŸ”§ Running command: ${command}`); // Ensure output directory exists @@ -104,10 +113,12 @@ export default class FrameworkGenerator { } execSync(command, { stdio: "inherit" }); + // eslint-disable-next-line no-console console.log( `āœ… Successfully generated ${subcommand} at: ${options.outputPath}`, ); } catch (error) { + // eslint-disable-next-line no-console console.error(`āŒ Error generating ${subcommand}:`, error); throw new Error( `Failed to generate ${subcommand}: ${error instanceof Error ? error.message : "Unknown error"}`, @@ -117,7 +128,7 @@ export default class FrameworkGenerator { private static getTerraformFrameworkGeneratorPath(): string { // Get the Go path and construct the full path to the tfplugingen-framework binary - const goPath = execSync("go env GOPATH", { encoding: "utf8" }).trim(); + const goPath: string = execSync("go env GOPATH", { encoding: "utf8" }).trim(); return path.join(goPath, "bin", this.TOOL_NAME); } @@ -126,7 +137,7 @@ export default class FrameworkGenerator { */ public static isInstalled(): boolean { try { - const binaryPath = this.getTerraformFrameworkGeneratorPath(); + const binaryPath: string = this.getTerraformFrameworkGeneratorPath(); return fs.existsSync(binaryPath); } catch { return false; @@ -137,25 +148,41 @@ export default class FrameworkGenerator { * Print usage information for the framework generator */ public static printUsageInfo(): void { + // eslint-disable-next-line no-console console.log("šŸ“– Terraform Plugin Framework Generator Usage:"); + // eslint-disable-next-line no-console console.log(""); + // eslint-disable-next-line no-console console.log("šŸ”„ Generate Commands:"); + // eslint-disable-next-line no-console console.log( " generateAll() - Generate all provider code (data sources, resources, and provider)", ); + // eslint-disable-next-line no-console console.log(" generateDataSources()- Generate only data source code"); + // eslint-disable-next-line no-console console.log(" generateResources() - Generate only resource code"); + // eslint-disable-next-line no-console console.log(" generateProvider() - Generate only provider code"); + // eslint-disable-next-line no-console console.log(""); + // eslint-disable-next-line no-console console.log("šŸ—ļø Scaffold Commands:"); + // eslint-disable-next-line no-console console.log( " scaffold() - Create starter code for data source, provider, or resource", ); + // eslint-disable-next-line no-console console.log(""); + // eslint-disable-next-line no-console console.log("šŸ“‹ Requirements:"); + // eslint-disable-next-line no-console console.log(" - Provider Code Specification file (JSON format)"); + // eslint-disable-next-line no-console console.log(" - tfplugingen-framework tool installed"); + // eslint-disable-next-line no-console console.log(" - Go installed and properly configured"); + // eslint-disable-next-line no-console console.log(""); } } diff --git a/Scripts/TerraformProvider/GenerateProvider.ts b/Scripts/TerraformProvider/GenerateProvider.ts index 37b9144325..2dd336389d 100644 --- a/Scripts/TerraformProvider/GenerateProvider.ts +++ b/Scripts/TerraformProvider/GenerateProvider.ts @@ -4,21 +4,24 @@ import FrameworkGenerator from "./FrameworkGenerator"; import SpecificationConverter from "./SpecificationConverter"; import path from "path"; -async function main() { +async function main(): Promise { + // eslint-disable-next-line no-console console.log("šŸš€ Starting Terraform Provider Generation Process..."); try { // 1. Generate OpenAPI spec + // eslint-disable-next-line no-console console.log("\nšŸ“„ Step 1: Generating OpenAPI specification..."); - const openApiSpecPath = path.resolve( + const openApiSpecPath: string = path.resolve( __dirname, "../../Terraform/openapi.json", ); await generateOpenAPISpec(openApiSpecPath); // 2. Convert OpenAPI spec to Provider Code Specification + // eslint-disable-next-line no-console console.log("\nšŸ”„ Step 2: Converting to Provider Code Specification..."); - const providerSpecPath = path.resolve( + const providerSpecPath: string = path.resolve( __dirname, "../../Terraform/provider-code-spec.json", ); @@ -29,23 +32,26 @@ async function main() { }); // 3. Install Framework Generator tool + // eslint-disable-next-line no-console console.log( "\nšŸ”§ Step 3: Installing Terraform Plugin Framework Generator...", ); - const frameworkInstallResult = + const frameworkInstallResult: any = await ToolInstaller.installTerraformPluginFrameworkGenerator(); if (!frameworkInstallResult.success) { throw new Error( `Failed to install framework generator: ${frameworkInstallResult.message}`, ); } + // eslint-disable-next-line no-console console.log(`āœ… ${frameworkInstallResult.message}`); // 4. Generate Terraform Provider Framework code + // eslint-disable-next-line no-console console.log( "\nšŸ—ļø Step 4: Generating Terraform Provider Framework code...", ); - const frameworkOutputPath = path.resolve( + const frameworkOutputPath: string = path.resolve( __dirname, "../../Terraform/terraform-provider-framework", ); @@ -56,31 +62,48 @@ async function main() { packageName: "oneuptime", // Optional: specify a package name }); + // eslint-disable-next-line no-console console.log("\nšŸŽ‰ Provider generation completed successfully!"); + // eslint-disable-next-line no-console console.log("\nšŸ“‹ Generated Files:"); + // eslint-disable-next-line no-console console.log(` šŸ“„ OpenAPI Spec: ${openApiSpecPath}`); + // eslint-disable-next-line no-console console.log(` šŸ“„ Provider Code Spec: ${providerSpecPath}`); + // eslint-disable-next-line no-console console.log(` šŸ“ Framework Provider Code: ${frameworkOutputPath}`); + // eslint-disable-next-line no-console console.log("\nšŸ“– Next Steps:"); + // eslint-disable-next-line no-console console.log(" 1. Review the generated Provider Code Specification"); + // eslint-disable-next-line no-console console.log( " 2. Customize the specification as needed for your use case", ); + // eslint-disable-next-line no-console console.log( " 3. Use the Framework Generator to regenerate code after modifications", ); + // eslint-disable-next-line no-console console.log( " 4. Implement the actual provider logic in the generated Go files", ); FrameworkGenerator.printUsageInfo(); } catch (error) { - console.error("\nāŒ Error during provider generation:", error); + const err: Error = error as Error; + // eslint-disable-next-line no-console + console.error("\nāŒ Error during provider generation:", err); + // eslint-disable-next-line no-console console.error("\nšŸ” Troubleshooting Tips:"); + // eslint-disable-next-line no-console console.error(" - Ensure Go is installed and properly configured"); + // eslint-disable-next-line no-console console.error(" - Check that GOPATH is set correctly"); + // eslint-disable-next-line no-console console.error(" - Verify internet connectivity for downloading tools"); + // eslint-disable-next-line no-console console.error( " - Make sure you have write permissions in the output directories", ); diff --git a/Scripts/TerraformProvider/GeneratorConfig.ts b/Scripts/TerraformProvider/GeneratorConfig.ts index ec1c3528e7..a21e38a05d 100644 --- a/Scripts/TerraformProvider/GeneratorConfig.ts +++ b/Scripts/TerraformProvider/GeneratorConfig.ts @@ -22,7 +22,7 @@ export default class GeneratorConfig { providerName: string; }): void { // Read the OpenAPI spec JSON file - const openApiSpec = JSON.parse( + const openApiSpec: any = JSON.parse( fs.readFileSync(data.openApiSpecInJsonFilePath, "utf-8"), ); const config: any = { @@ -37,7 +37,7 @@ export default class GeneratorConfig { if (openApiSpec.paths) { for (const [pathKey, pathObj] of Object.entries(openApiSpec.paths)) { for (const [method, opRaw] of Object.entries(pathObj as any)) { - const op = opRaw as any; + const op: any = opRaw as any; if ( !op || typeof op !== "object" || @@ -46,27 +46,27 @@ export default class GeneratorConfig { continue; } - const operationId = op.operationId.toLowerCase(); - const isReadOperation = + const operationId: string = op.operationId.toLowerCase(); + const isReadOperation: boolean = operationId.startsWith("get") || operationId.startsWith("list") || operationId.startsWith("count") || operationId.includes("read") || operationId.includes("fetch"); - const isCreateOperation = + const isCreateOperation: boolean = operationId.startsWith("create") || operationId.startsWith("add") || method.toLowerCase() === "post"; - const isUpdateOperation = + const isUpdateOperation: boolean = operationId.startsWith("update") || operationId.startsWith("put") || method.toLowerCase() === "put"; - const isDeleteOperation = + const isDeleteOperation: boolean = operationId.startsWith("delete") || operationId.includes("remove"); if (isReadOperation) { // Generate data source for read operations - const dsName = + const dsName: string = this.extractResourceNameFromPath(pathKey).toLowerCase(); if (dsName) { if (!config.data_sources[dsName]) { @@ -79,7 +79,7 @@ export default class GeneratorConfig { } // Also add as resource read operation - const resourceName = + const resourceName: string = this.extractResourceNameFromPath(pathKey).toLowerCase(); if (resourceName) { if (!config.resources[resourceName]) { @@ -92,7 +92,7 @@ export default class GeneratorConfig { } } else if (isCreateOperation) { // Generate resource for create operations - const resourceName = + const resourceName: string = this.extractResourceNameFromPath(pathKey).toLowerCase(); if (resourceName) { if (!config.resources[resourceName]) { @@ -105,7 +105,7 @@ export default class GeneratorConfig { } } else if (isUpdateOperation) { // Generate resource for update operations - const resourceName = + const resourceName: string = this.extractResourceNameFromPath(pathKey).toLowerCase(); if (resourceName) { if (!config.resources[resourceName]) { @@ -118,7 +118,7 @@ export default class GeneratorConfig { } } else if (isDeleteOperation) { // Handle delete operations - const resourceName = + const resourceName: string = this.extractResourceNameFromPath(pathKey).toLowerCase(); if (resourceName) { if (!config.resources[resourceName]) { @@ -141,7 +141,7 @@ export default class GeneratorConfig { for (const [resourceName, resourceConfig] of Object.entries( config.resources, )) { - const resource = resourceConfig as any; + const resource: any = resourceConfig as any; // If resource doesn't have 'create', try to use 'post' operation if (!resource.create && resource.post) { @@ -151,7 +151,7 @@ export default class GeneratorConfig { // If resource doesn't have 'read', try to find it in data sources if (!resource.read) { - const matchingDataSource = config.data_sources[resourceName]; + const matchingDataSource: any = config.data_sources[resourceName]; if (matchingDataSource && matchingDataSource.read) { resource.read = matchingDataSource.read; } @@ -159,6 +159,7 @@ export default class GeneratorConfig { // If resource still doesn't have both 'create' and 'read', remove it if (!resource.create || !resource.read) { + // eslint-disable-next-line no-console console.log( `Removing resource '${resourceName}' - missing required operations (create: ${Boolean(resource.create)}, read: ${Boolean(resource.read)})`, ); @@ -180,7 +181,7 @@ export default class GeneratorConfig { } // Convert the config object to YAML - const yamlStr = yaml.dump(config, { noRefs: true, lineWidth: 120 }); + const yamlStr: string = yaml.dump(config, { noRefs: true, lineWidth: 120 }); // Ensure output directory exists if (!fs.existsSync(data.outputPath)) { @@ -188,7 +189,7 @@ export default class GeneratorConfig { } // Write the YAML string to the output file - const outputFile = path.join(data.outputPath, data.outputFileName); + const outputFile: string = path.join(data.outputPath, data.outputFileName); fs.writeFileSync(outputFile, yamlStr, "utf-8"); } @@ -199,8 +200,8 @@ export default class GeneratorConfig { */ private static extractResourceNameFromPath(path: string): string { // Remove leading slash and anything after the first parameter - const pathParts = path.replace(/^\//, "").split("/"); - let resourcePath = pathParts[0] || ""; + const pathParts: string[] = path.replace(/^\//, "").split("/"); + let resourcePath: string = pathParts[0] || ""; // Handle paths that end with specific patterns like /count, /get-list, etc. if (resourcePath.includes("-count") || resourcePath.includes("-get-list")) { @@ -208,7 +209,7 @@ export default class GeneratorConfig { } // Convert kebab-case to snake_case and remove special characters - const resourceName = resourcePath + const resourceName: string = resourcePath .replace(/-/g, "") // Remove hyphens .replace(/[^a-zA-Z0-9]/g, "") // Remove any other special characters .toLowerCase(); diff --git a/Scripts/TerraformProvider/InstallTools.ts b/Scripts/TerraformProvider/InstallTools.ts index 6ac9d815d1..9bf0272d92 100644 --- a/Scripts/TerraformProvider/InstallTools.ts +++ b/Scripts/TerraformProvider/InstallTools.ts @@ -19,6 +19,7 @@ class ToolInstaller { public static async installTerraformPluginCodegenOpenAPI(): Promise { try { + // eslint-disable-next-line no-console console.log("šŸ”§ Installing Terraform Plugin Codegen OpenAPI..."); // Check if Go is installed @@ -30,6 +31,7 @@ class ToolInstaller { } // Install the tool + // eslint-disable-next-line no-console console.log(`šŸ“¦ Running: go install ${this.OPENAPI_TOOL_PACKAGE}`); execSync(`go install ${this.OPENAPI_TOOL_PACKAGE}`, { stdio: "inherit", @@ -37,8 +39,9 @@ class ToolInstaller { }); // Verify installation - const version = this.getToolVersion(this.OPENAPI_TOOL_NAME); + const version: string | null = this.getToolVersion(this.OPENAPI_TOOL_NAME); if (version) { + // eslint-disable-next-line no-console console.log("āœ… Installation successful!"); return { success: true, @@ -51,6 +54,7 @@ class ToolInstaller { message: `Installation completed but ${this.OPENAPI_TOOL_NAME} is not available in PATH`, }; } catch (error) { + // eslint-disable-next-line no-console console.error("āŒ Installation failed:", error); return { success: false, @@ -61,6 +65,7 @@ class ToolInstaller { public static async installTerraformPluginFrameworkGenerator(): Promise { try { + // eslint-disable-next-line no-console console.log("šŸ”§ Installing Terraform Plugin Framework Generator..."); // Check if Go is installed @@ -72,6 +77,7 @@ class ToolInstaller { } // Install the tool + // eslint-disable-next-line no-console console.log(`šŸ“¦ Running: go install ${this.FRAMEWORK_TOOL_PACKAGE}`); execSync(`go install ${this.FRAMEWORK_TOOL_PACKAGE}`, { stdio: "inherit", @@ -79,8 +85,9 @@ class ToolInstaller { }); // Verify installation - const version = this.getToolVersion(this.FRAMEWORK_TOOL_NAME); + const version: string | null = this.getToolVersion(this.FRAMEWORK_TOOL_NAME); if (version) { + // eslint-disable-next-line no-console console.log("āœ… Framework Generator installation successful!"); return { success: true, @@ -93,6 +100,7 @@ class ToolInstaller { message: `Installation completed but ${this.FRAMEWORK_TOOL_NAME} is not available in PATH`, }; } catch (error) { + // eslint-disable-next-line no-console console.error("āŒ Framework Generator installation failed:", error); return { success: false, @@ -121,8 +129,8 @@ class ToolInstaller { } catch { try { // Try to find the binary in GOPATH/bin or GOBIN - const goPath = this.getGoPath(); - const binaryPath = path.join(goPath, "bin", toolName); + const goPath: string = this.getGoPath(); + const binaryPath: string = path.join(goPath, "bin", toolName); if (fs.existsSync(binaryPath)) { return "latest"; } @@ -135,25 +143,33 @@ class ToolInstaller { private static getGoPath(): string { try { - const goPath = execSync("go env GOPATH", { encoding: "utf8" }).trim(); + const goPath: string = execSync("go env GOPATH", { encoding: "utf8" }).trim(); return goPath; } catch { // Default GOPATH - const homeDir = process.env["HOME"] || process.env["USERPROFILE"] || ""; + const homeDir: string = process.env["HOME"] || process.env["USERPROFILE"] || ""; return path.join(homeDir, "go"); } } public static printInstallationInfo(): void { + // eslint-disable-next-line no-console console.log("šŸ“‹ Installation Information:"); + // eslint-disable-next-line no-console console.log(` OpenAPI Tool: ${this.OPENAPI_TOOL_NAME}`); + // eslint-disable-next-line no-console console.log(` OpenAPI Package: ${this.OPENAPI_TOOL_PACKAGE}`); + // eslint-disable-next-line no-console console.log(` Framework Tool: ${this.FRAMEWORK_TOOL_NAME}`); + // eslint-disable-next-line no-console console.log(` Framework Package: ${this.FRAMEWORK_TOOL_PACKAGE}`); + // eslint-disable-next-line no-console console.log(" Prerequisites: Go must be installed"); + // eslint-disable-next-line no-console console.log( " Usage: Use different methods to install the specific tool needed", ); + // eslint-disable-next-line no-console console.log(""); } } @@ -163,29 +179,38 @@ async function main(): Promise { try { ToolInstaller.printInstallationInfo(); - const result = await ToolInstaller.installTerraformPluginCodegenOpenAPI(); + const result: InstallResult = await ToolInstaller.installTerraformPluginCodegenOpenAPI(); if (result.success) { + // eslint-disable-next-line no-console console.log(`šŸŽ‰ ${result.message}`); if (result.version) { + // eslint-disable-next-line no-console console.log(`šŸ“Œ Version: ${result.version}`); } // Print usage instructions + // eslint-disable-next-line no-console console.log(""); + // eslint-disable-next-line no-console console.log("šŸ“– Usage Instructions:"); + // eslint-disable-next-line no-console console.log( " The tfplugingen-openapi tool is now available in your PATH", ); + // eslint-disable-next-line no-console console.log( " You can use it to generate Terraform provider code from OpenAPI specs", ); + // eslint-disable-next-line no-console console.log(" Example: tfplugingen-openapi generate --help"); } else { + // eslint-disable-next-line no-console console.error(`šŸ’„ ${result.message}`); process.exit(1); } } catch (error) { + // eslint-disable-next-line no-console console.error("🚨 Unexpected error:", error); process.exit(1); } diff --git a/Scripts/TerraformProvider/ProviderSpec.ts b/Scripts/TerraformProvider/ProviderSpec.ts index 79d78a6e5e..c958bc22bd 100644 --- a/Scripts/TerraformProvider/ProviderSpec.ts +++ b/Scripts/TerraformProvider/ProviderSpec.ts @@ -8,18 +8,19 @@ export default class ProviderSpec { outputPath: string; }): void { // Get the Go path and construct the full path to the tfplugingen-openapi binary - const goPath = execSync("go env GOPATH", { encoding: "utf8" }).trim(); - const tfplugigenBinaryPath = path.join( + const goPath: string = execSync("go env GOPATH", { encoding: "utf8" }).trim(); + const tfplugigenBinaryPath: string = path.join( goPath, "bin", "tfplugingen-openapi", ); - const command = `"${tfplugigenBinaryPath}" generate --config "${options.generatorConfigPath}" --output "${options.outputPath}" "${options.openApiSpecPath}"`; + const command: string = `"${tfplugigenBinaryPath}" generate --config "${options.generatorConfigPath}" --output "${options.outputPath}" "${options.openApiSpecPath}"`; try { execSync(command, { stdio: "inherit" }); } catch (error) { + // eslint-disable-next-line no-console console.error( "Error executing Terraform provider code generation command:", error, @@ -29,6 +30,7 @@ export default class ProviderSpec { ); } + // eslint-disable-next-line no-console console.log( "Terraform provider code generated successfully at:", options.outputPath, diff --git a/Scripts/TerraformProvider/SpecificationConverter.ts b/Scripts/TerraformProvider/SpecificationConverter.ts index 7b80d0ffcb..7497ae22ab 100644 --- a/Scripts/TerraformProvider/SpecificationConverter.ts +++ b/Scripts/TerraformProvider/SpecificationConverter.ts @@ -48,31 +48,37 @@ export default class SpecificationConverter { providerName: string; }): void { try { + // eslint-disable-next-line no-console console.log( "šŸ”„ Converting OpenAPI spec to Provider Code Specification...", ); + // eslint-disable-next-line no-console console.log(`šŸ“„ Input OpenAPI spec: ${options.openApiSpecPath}`); + // eslint-disable-next-line no-console console.log(`šŸ“ Output path: ${options.outputPath}`); // Read OpenAPI specification - const openApiContent = fs.readFileSync(options.openApiSpecPath, "utf8"); + const openApiContent: string = fs.readFileSync(options.openApiSpecPath, "utf8"); const openApiSpec: OpenAPISpec = JSON.parse(openApiContent); // Generate Provider Code Specification - const providerSpec = this.generateProviderSpecification( + const providerSpec: ProviderCodeSpecification = this.generateProviderSpecification( openApiSpec, options.providerName, ); // Write specification to file - const outputContent = JSON.stringify(providerSpec, null, 2); + const outputContent: string = JSON.stringify(providerSpec, null, 2); fs.writeFileSync(options.outputPath, outputContent, "utf8"); + // eslint-disable-next-line no-console console.log( "āœ… Successfully converted OpenAPI spec to Provider Code Specification", ); + // eslint-disable-next-line no-console console.log(`šŸ“ Generated specification saved to: ${options.outputPath}`); } catch (error) { + // eslint-disable-next-line no-console console.error("āŒ Error converting specification:", error); throw new Error( `Failed to convert specification: ${error instanceof Error ? error.message : "Unknown error"}`, @@ -154,7 +160,7 @@ export default class SpecificationConverter { } // Extract resource name from path (e.g., /api/v1/monitor -> monitor) - const pathSegments = pathKey.split("/").filter((segment) => { + const pathSegments: string[] = pathKey.split("/").filter((segment: string) => { return ( segment && !segment.startsWith("{") && @@ -167,28 +173,28 @@ export default class SpecificationConverter { continue; } - const lastSegment = pathSegments[pathSegments.length - 1]; + const lastSegment: string | undefined = pathSegments[pathSegments.length - 1]; if (!lastSegment) { continue; } - const resourceName = this.sanitizeResourceName(lastSegment); + const resourceName: string = this.sanitizeResourceName(lastSegment); if (!resourceName) { continue; } // Sanitize resource name to be Terraform-compatible - const sanitizedResourceName = this.sanitizeResourceName(resourceName); + const sanitizedResourceName: string = this.sanitizeResourceName(resourceName); // Determine if this is a resource (has POST/PUT/DELETE) or data source (only GET) - const methods = Object.keys(pathValue); - const hasWriteOperations = methods.some((method) => { + const methods: string[] = Object.keys(pathValue); + const hasWriteOperations: boolean = methods.some((method: string) => { return ["post", "put", "patch", "delete"].includes( method.toLowerCase(), ); }); - const schema = this.generateSchemaFromPath( + const schema: any = this.generateSchemaFromPath( pathValue, openApiSpec.components?.schemas, ); @@ -196,7 +202,7 @@ export default class SpecificationConverter { if (hasWriteOperations) { // This is a resource if ( - !resources.find((r) => { + !resources.find((r: any) => { return r.name === sanitizedResourceName; }) ) { @@ -208,7 +214,7 @@ export default class SpecificationConverter { } else if (methods.includes("get")) { // This is a data source if ( - !datasources.find((d) => { + !datasources.find((d: any) => { return d.name === sanitizedResourceName; }) ) { @@ -255,9 +261,9 @@ export default class SpecificationConverter { // Try to extract more attributes from request/response schemas if available if (pathSpec.post?.requestBody?.content?.["application/json"]?.schema) { - const requestSchema = + const requestSchema: any = pathSpec.post.requestBody.content["application/json"].schema; - const extractedAttributes = this.extractAttributesFromSchema( + const extractedAttributes: any[] = this.extractAttributesFromSchema( requestSchema, schemas, ); @@ -281,7 +287,7 @@ export default class SpecificationConverter { continue; } - const attribute = this.convertPropertyToAttribute( + const attribute: any = this.convertPropertyToAttribute( propName, propSchema as any, ); @@ -304,7 +310,7 @@ export default class SpecificationConverter { } let attributeType: any; - const computedOptionalRequired = "optional"; + const computedOptionalRequired: string = "optional"; switch (schema.type) { case "string": @@ -348,7 +354,7 @@ export default class SpecificationConverter { return null; } - const typeKey = Object.keys(attributeType)[0]; + const typeKey: string | undefined = Object.keys(attributeType)[0]; if (!typeKey) { return null; } @@ -442,10 +448,12 @@ export default class SpecificationConverter { ], }; - const content = JSON.stringify(basicSpec, null, 2); + const content: string = JSON.stringify(basicSpec, null, 2); fs.writeFileSync(options.outputPath, content, "utf8"); + // eslint-disable-next-line no-console console.log("āœ… Generated basic Provider Code Specification template"); + // eslint-disable-next-line no-console console.log(`šŸ“ Template saved to: ${options.outputPath}`); } }