From 5c464ae13764d753ceeccfcbd28e93b60f47dc4c Mon Sep 17 00:00:00 2001 From: Nawaz Dhandala Date: Thu, 26 Jun 2025 14:24:15 +0100 Subject: [PATCH] refactor: Add type annotations for improved type safety across multiple files --- Common/Server/Services/DatabaseService.ts | 2 - Common/Types/Text.ts | 6 +- Common/Utils/Schema/AnalyticsModelSchema.ts | 2 +- Common/Utils/Schema/ModelSchema.ts | 8 +- .../MCPProvider/Core/MCPServerGenerator.ts | 50 ++++++------ Scripts/MCPProvider/Core/OpenAPIParser.ts | 20 ++--- Scripts/MCPProvider/Core/StringUtils.ts | 4 +- Scripts/MCPProvider/GenerateMCPServer.ts | 14 ++-- .../Core/ResourceGenerator.ts | 78 +++++++++++-------- Scripts/TerraformProvider/Core/StringUtils.ts | 2 +- 10 files changed, 98 insertions(+), 88 deletions(-) diff --git a/Common/Server/Services/DatabaseService.ts b/Common/Server/Services/DatabaseService.ts index 31110c42bb..52fb8423c5 100644 --- a/Common/Server/Services/DatabaseService.ts +++ b/Common/Server/Services/DatabaseService.ts @@ -523,8 +523,6 @@ class DatabaseService extends BaseService { (data as any)[columnName] && typeof (data as any)[columnName] === Typeof.String ) { - console.log("Here!"); - const fileBuffer: Buffer = Buffer.from( (data as any)[columnName] as string, "base64", diff --git a/Common/Types/Text.ts b/Common/Types/Text.ts index 7221655f4a..18d51a280b 100644 --- a/Common/Types/Text.ts +++ b/Common/Types/Text.ts @@ -119,7 +119,7 @@ export default class Text { } // Remove data URI prefix if present (e.g., data:image/jpeg;base64,) - const base64String = text.replace(/^data:[^;]+;base64,/, ""); + const base64String: string = text.replace(/^data:[^;]+;base64,/, ""); // Check if string is empty after removing prefix if (!base64String) { @@ -143,7 +143,7 @@ export default class Text { // Check if it's a data URI if (text.startsWith("data:")) { - const base64Index = text.indexOf(";base64,"); + const base64Index: number = text.indexOf(";base64,"); if (base64Index !== -1) { return text.substring(base64Index + 8); // 8 is length of ';base64,' } @@ -160,7 +160,7 @@ export default class Text { // Check if it's a data URI if (text.startsWith("data:")) { - const mimeTypeEnd = text.indexOf(";"); + const mimeTypeEnd: number = text.indexOf(";"); if (mimeTypeEnd !== -1) { return text.substring(5, mimeTypeEnd); // 5 is length of 'data:' } diff --git a/Common/Utils/Schema/AnalyticsModelSchema.ts b/Common/Utils/Schema/AnalyticsModelSchema.ts index 32e6cf508d..f361761664 100644 --- a/Common/Utils/Schema/AnalyticsModelSchema.ts +++ b/Common/Utils/Schema/AnalyticsModelSchema.ts @@ -16,7 +16,7 @@ export class AnalyticsModelSchema extends BaseSchema { private static addDefaultToOpenApi( openApiConfig: any, column: AnalyticsTableColumn, - ) { + ): any { if (column.defaultValue !== undefined && column.defaultValue !== null) { return { ...openApiConfig, default: column.defaultValue }; } diff --git a/Common/Utils/Schema/ModelSchema.ts b/Common/Utils/Schema/ModelSchema.ts index e454204521..5e51badd3e 100644 --- a/Common/Utils/Schema/ModelSchema.ts +++ b/Common/Utils/Schema/ModelSchema.ts @@ -383,7 +383,7 @@ export class ModelSchema extends BaseSchema { } // add title and description to the schema - let finalDescription = ""; + let finalDescription: string = ""; // Add column description first if it exists if (column.description) { @@ -1174,7 +1174,7 @@ export class ModelSchema extends BaseSchema { } // Add title and description to the schema - let finalDescription = ""; + let finalDescription: string = ""; // Add column description first if it exists if (column.description) { @@ -1219,7 +1219,9 @@ export class ModelSchema extends BaseSchema { let zodType: ZodTypes.ZodTypeAny; // Helper function to add default value to openapi schema if it exists - const addDefaultToOpenApi = (openApiConfig: any) => { + const addDefaultToOpenApi: (openApiConfig: any) => any = ( + openApiConfig: any, + ): any => { if (column.defaultValue !== undefined && column.defaultValue !== null) { return { ...openApiConfig, default: column.defaultValue }; } diff --git a/Scripts/MCPProvider/Core/MCPServerGenerator.ts b/Scripts/MCPProvider/Core/MCPServerGenerator.ts index 85c37ff7d8..5bbd0ceb5d 100644 --- a/Scripts/MCPProvider/Core/MCPServerGenerator.ts +++ b/Scripts/MCPProvider/Core/MCPServerGenerator.ts @@ -27,7 +27,7 @@ export class MCPServerGenerator { } private async generatePackageJson(): Promise { - const packageJson = { + const packageJson: any = { name: this.config.npmPackageName, version: this.config.serverVersion, description: this.config.description, @@ -92,7 +92,7 @@ export class MCPServerGenerator { } private async generateIndexFile(): Promise { - const indexContent = [ + const indexContent: string = [ "#!/usr/bin/env node", "", 'import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";', @@ -152,12 +152,12 @@ export class MCPServerGenerator { } private async generateMCPService(): Promise { - const parser = new OpenAPIParser(); + const parser: OpenAPIParser = new OpenAPIParser(); parser.setSpec(this.spec); - const tools = parser.getMCPTools(); + const tools: any[] = parser.getMCPTools(); - const toolRegistrations = tools - .map((tool) => { + const toolRegistrations: string = tools + .map((tool: any) => { return [ ` server.tool(`, ` "${tool.name}",`, @@ -171,13 +171,13 @@ export class MCPServerGenerator { }) .join("\n\n"); - const toolMethods = tools - .map((tool) => { + const toolMethods: string = tools + .map((tool: any) => { return this.generateToolMethod(tool); }) .join("\n\n"); - const serviceContent = [ + const serviceContent: string = [ 'import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";', 'import { OneUptimeAPIClient } from "./APIClient.js";', "", @@ -202,8 +202,8 @@ export class MCPServerGenerator { } private generateToolMethod(tool: MCPTool): string { - const methodName = StringUtils.toCamelCase(tool.name); - const operation = tool.operation; + const methodName: string = StringUtils.toCamelCase(tool.name); + const operation: any = tool.operation; return [ ` private async ${methodName}(args: any): Promise {`, @@ -230,7 +230,7 @@ export class MCPServerGenerator { } private async generateAPIClient(): Promise { - const clientContent = [ + const clientContent: string = [ 'import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";', "", "export interface APIRequestConfig {", @@ -355,7 +355,7 @@ export class MCPServerGenerator { } private async generateConfigUtils(): Promise { - const configContent = [ + const configContent: string = [ "export const ServerConfig = {", ` name: "${this.config.serverName}",`, ` version: "${this.config.serverVersion}",`, @@ -397,28 +397,28 @@ export class MCPServerGenerator { } private async generateReadme(): Promise { - const parser = new OpenAPIParser(); + const parser: OpenAPIParser = new OpenAPIParser(); parser.setSpec(this.spec); - const tools = parser.getMCPTools(); - const resourceTags = parser.getResourceTags(); + const tools: any[] = parser.getMCPTools(); + const resourceTags: string[] = parser.getResourceTags(); - const toolList = tools + const toolList: string = tools .slice(0, 20) - .map((tool) => { + .map((tool: any) => { return `- **${tool.name}**: ${tool.description}`; }) .join("\n"); - const resourceList = resourceTags - .map((tag) => { + const resourceList: string = resourceTags + .map((tag: string) => { return `- **${StringUtils.toPascalCase(tag)}**`; }) .join("\n"); - const additionalToolsNote = + const additionalToolsNote: string = tools.length > 20 ? `\n...and ${tools.length - 20} more tools` : ""; - const readmeContent = `# ${this.config.serverName} + const readmeContent: string = `# ${this.config.serverName} ${this.config.description} @@ -560,7 +560,7 @@ Generated from OneUptime OpenAPI specification v${this.spec.info.version} } private async generateTsConfig(): Promise { - const tsConfigContent = `{ + const tsConfigContent: string = `{ "compilerOptions": { "target": "ES2022", "module": "Node16", @@ -608,7 +608,7 @@ Generated from OneUptime OpenAPI specification v${this.spec.info.version} } private async generateNodemonConfig(): Promise { - const nodemonContent = `{ + const nodemonContent: string = `{ "watch": ["**/*.ts"], "ext": "ts", "ignore": ["build/**/*", "node_modules/**/*"], @@ -623,7 +623,7 @@ Generated from OneUptime OpenAPI specification v${this.spec.info.version} } private async generateDockerfile(): Promise { - const dockerContent = `FROM node:18-alpine + const dockerContent: string = `FROM node:18-alpine WORKDIR /app diff --git a/Scripts/MCPProvider/Core/OpenAPIParser.ts b/Scripts/MCPProvider/Core/OpenAPIParser.ts index 40e1897da5..b7f7822f4c 100644 --- a/Scripts/MCPProvider/Core/OpenAPIParser.ts +++ b/Scripts/MCPProvider/Core/OpenAPIParser.ts @@ -78,8 +78,8 @@ export class OpenAPIParser { } // Fallback to tag + summary - const tag = operation.tags?.[0] || "api"; - const summary = operation.summary || "operation"; + const tag: string = operation.tags?.[0] || "api"; + const summary: string = operation.summary || "operation"; return StringUtils.toCamelCase(`${tag}_${summary}`); } @@ -95,7 +95,7 @@ export class OpenAPIParser { param.in === "query" || param.in === "header" ) { - const paramName = StringUtils.toCamelCase(param.name); + const paramName: string = StringUtils.toCamelCase(param.name); properties[paramName] = this.convertOpenAPISchemaToJsonSchema( param.schema, ); @@ -110,8 +110,8 @@ export class OpenAPIParser { // Add request body if (operation.requestBody) { - const content = operation.requestBody.content; - const jsonContent = content["application/json"]; + const content: any = operation.requestBody.content; + const jsonContent: any = content["application/json"]; if (jsonContent && jsonContent.schema) { if (jsonContent.schema.properties) { @@ -145,7 +145,7 @@ export class OpenAPIParser { private convertOpenAPISchemaToJsonSchema(schema: OpenAPISchema): any { if (schema.$ref) { - const resolvedSchema = this.resolveSchemaRef(schema.$ref); + const resolvedSchema: OpenAPISchema = this.resolveSchemaRef(schema.$ref); return this.convertOpenAPISchemaToJsonSchema(resolvedSchema); } @@ -190,13 +190,13 @@ export class OpenAPIParser { } // Handle #/components/schemas/SchemeName format - const refParts = ref.split("/"); + const refParts: string[] = ref.split("/"); if ( refParts[0] === "#" && refParts[1] === "components" && refParts[2] === "schemas" ) { - const schemaName = refParts[3]; + const schemaName: string | undefined = refParts[3]; if (schemaName && this.spec.components?.schemas?.[schemaName]) { return this.spec.components.schemas[schemaName]; } @@ -210,12 +210,12 @@ export class OpenAPIParser { return []; } - const tags = new Set(); + const tags: Set = new Set(); for (const [, pathItem] of Object.entries(this.spec.paths)) { for (const [, operation] of Object.entries(pathItem)) { if (operation.tags) { - operation.tags.forEach((tag) => { + operation.tags.forEach((tag: string) => { return tags.add(tag); }); } diff --git a/Scripts/MCPProvider/Core/StringUtils.ts b/Scripts/MCPProvider/Core/StringUtils.ts index d531454ec7..6231e7a759 100644 --- a/Scripts/MCPProvider/Core/StringUtils.ts +++ b/Scripts/MCPProvider/Core/StringUtils.ts @@ -1,7 +1,7 @@ export class StringUtils { public static toCamelCase(str: string): string { return str - .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => { + .replace(/(?:^\w|[A-Z]|\b\w)/g, (word: string, index: number) => { return index === 0 ? word.toLowerCase() : word.toUpperCase(); }) .replace(/\s+/g, ""); @@ -9,7 +9,7 @@ export class StringUtils { public static toPascalCase(str: string): string { return str - .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => { + .replace(/(?:^\w|[A-Z]|\b\w)/g, (word: string) => { return word.toUpperCase(); }) .replace(/\s+/g, ""); diff --git a/Scripts/MCPProvider/GenerateMCPServer.ts b/Scripts/MCPProvider/GenerateMCPServer.ts index b30fb1297f..50ab0dece5 100644 --- a/Scripts/MCPProvider/GenerateMCPServer.ts +++ b/Scripts/MCPProvider/GenerateMCPServer.ts @@ -28,12 +28,12 @@ async function main(): Promise { // Step 3: Parse OpenAPI spec Logger.info("🔍 Step 2: Parsing OpenAPI specification..."); - const parser = new OpenAPIParser(); - const apiSpec = await parser.parseOpenAPISpec(openApiSpecPath); + const parser: OpenAPIParser = new OpenAPIParser(); + const apiSpec: any = await parser.parseOpenAPISpec(openApiSpecPath); // Step 4: Initialize MCP server generator Logger.info("⚙️ Step 3: Initializing MCP server generator..."); - const generator = new MCPServerGenerator( + const generator: MCPServerGenerator = new MCPServerGenerator( { outputDir: mcpDir, serverName: "oneuptime-mcp", @@ -70,7 +70,7 @@ async function main(): Promise { async function createAdditionalFiles(mcpDir: string): Promise { // Create .env.example - const envExample = `# OneUptime MCP Server Configuration + const envExample: string = `# OneUptime MCP Server Configuration # Required: Your OneUptime API key ONEUPTIME_API_KEY=your-api-key-here @@ -89,7 +89,7 @@ NODE_ENV=development fs.writeFileSync(path.join(mcpDir, ".env.example"), envExample); // Create .gitignore - const gitignore = `# Dependencies + const gitignore: string = `# Dependencies node_modules/ npm-debug.log* yarn-debug.log* @@ -153,7 +153,7 @@ Thumbs.db fs.writeFileSync(path.join(mcpDir, ".gitignore"), gitignore); // Create CHANGELOG.md - const changelog = `# Changelog + const changelog: string = `# Changelog All notable changes to the OneUptime MCP Server will be documented in this file. @@ -184,7 +184,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 fs.writeFileSync(path.join(mcpDir, "CHANGELOG.md"), changelog); // Create LICENSE - const license = `Apache License + const license: string = `Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/Scripts/TerraformProvider/Core/ResourceGenerator.ts b/Scripts/TerraformProvider/Core/ResourceGenerator.ts index 8468693ba7..d754c01f21 100644 --- a/Scripts/TerraformProvider/Core/ResourceGenerator.ts +++ b/Scripts/TerraformProvider/Core/ResourceGenerator.ts @@ -114,18 +114,20 @@ export class ResourceGenerator { ); const hasDefaultNumbers: boolean = Object.entries(resource.schema).some( ([name, attr]: [string, any]) => { - const isInCreateSchema = + const isInCreateSchema: boolean = Boolean( resource?.operationSchemas?.create && - Object.prototype.hasOwnProperty.call( - resource.operationSchemas.create, - name, - ); - const isInUpdateSchema = + Object.prototype.hasOwnProperty.call( + resource.operationSchemas.create, + name, + ), + ); + const isInUpdateSchema: boolean = Boolean( resource?.operationSchemas?.update && - Object.prototype.hasOwnProperty.call( - resource.operationSchemas.update, - name, - ); + Object.prototype.hasOwnProperty.call( + resource.operationSchemas.update, + name, + ), + ); return ( attr.default !== undefined && attr.default !== null && @@ -141,18 +143,20 @@ export class ResourceGenerator { ); const hasDefaultStrings: boolean = Object.entries(resource.schema).some( ([name, attr]: [string, any]) => { - const isInCreateSchema = + const isInCreateSchema: boolean = Boolean( resource?.operationSchemas?.create && - Object.prototype.hasOwnProperty.call( - resource.operationSchemas.create, - name, - ); - const isInUpdateSchema = + Object.prototype.hasOwnProperty.call( + resource.operationSchemas.create, + name, + ), + ); + const isInUpdateSchema: boolean = Boolean( resource?.operationSchemas?.update && - Object.prototype.hasOwnProperty.call( - resource.operationSchemas.update, - name, - ); + Object.prototype.hasOwnProperty.call( + resource.operationSchemas.update, + name, + ), + ); return ( attr.default !== undefined && attr.default !== null && @@ -408,18 +412,20 @@ func (r *${resourceTypeName}Resource) parseJSONField(terraformString types.Strin } // Check if this field is in the create or update schema (for fields with defaults) - const isInCreateSchema = + const isInCreateSchema: boolean = Boolean( resource?.operationSchemas?.create && - Object.prototype.hasOwnProperty.call( - resource.operationSchemas.create, - name, - ); - const isInUpdateSchema = + Object.prototype.hasOwnProperty.call( + resource.operationSchemas.create, + name, + ), + ); + const isInUpdateSchema: boolean = Boolean( resource?.operationSchemas?.update && - Object.prototype.hasOwnProperty.call( - resource.operationSchemas.update, - name, - ); + Object.prototype.hasOwnProperty.call( + resource.operationSchemas.update, + name, + ), + ); if (attr.required) { options.push("Required: true"); @@ -944,16 +950,20 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D resource: TerraformResource, resourceVarName: string, ): string { - const updateSchema = resource.operationSchemas?.update || {}; + const updateSchema: any = resource.operationSchemas?.update || {}; const conditionalAssignments: string[] = []; // Fields that should not be included in update requests const immutableFields: Array = ["projectId", "project_id"]; // Check if there are any fields to process - const hasFields = Object.entries(updateSchema).some(([name, attr]) => { - return name !== "id" && !attr.computed && !immutableFields.includes(name); - }); + const hasFields: boolean = Object.entries(updateSchema).some( + ([name, attr]: [string, any]) => { + return ( + name !== "id" && !attr.computed && !immutableFields.includes(name) + ); + }, + ); // If no fields to process, return empty string if (!hasFields) { diff --git a/Scripts/TerraformProvider/Core/StringUtils.ts b/Scripts/TerraformProvider/Core/StringUtils.ts index df57fe7876..8fe622cf24 100644 --- a/Scripts/TerraformProvider/Core/StringUtils.ts +++ b/Scripts/TerraformProvider/Core/StringUtils.ts @@ -44,7 +44,7 @@ export class StringUtils { public static sanitizeGoIdentifier(str: string): string { // Remove special characters including apostrophes and ensure it starts with a letter const sanitized: string = str.replace(/[^a-zA-Z0-9_]/g, ""); - return (/^[a-zA-Z]/).test(sanitized) ? sanitized : `_${sanitized}`; + return /^[a-zA-Z]/.test(sanitized) ? sanitized : `_${sanitized}`; } public static escapeGoString(str: string): string {