mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
refactor: add type annotations for improved type safety across various components
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,10 +93,10 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
|
||||
// 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<ComponentProps> = (
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ const AnnouncementTable: FunctionComponent<ComponentProps> = (
|
||||
] 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()
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ export async function generateOpenAPISpec(outputPath?: string): Promise<void> {
|
||||
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 });
|
||||
}
|
||||
|
||||
@@ -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("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,21 +4,24 @@ import FrameworkGenerator from "./FrameworkGenerator";
|
||||
import SpecificationConverter from "./SpecificationConverter";
|
||||
import path from "path";
|
||||
|
||||
async function main() {
|
||||
async function main(): Promise<void> {
|
||||
// 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",
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -19,6 +19,7 @@ class ToolInstaller {
|
||||
|
||||
public static async installTerraformPluginCodegenOpenAPI(): Promise<InstallResult> {
|
||||
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<InstallResult> {
|
||||
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<void> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user