refactor: Add type annotations for improved type safety across multiple files

This commit is contained in:
Nawaz Dhandala
2025-06-26 14:24:15 +01:00
parent cf6ee298cc
commit 5c464ae137
10 changed files with 98 additions and 88 deletions

View File

@@ -523,8 +523,6 @@ class DatabaseService<TBaseModel extends BaseModel> 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",

View File

@@ -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:'
}

View File

@@ -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 };
}

View File

@@ -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 };
}

View File

@@ -27,7 +27,7 @@ export class MCPServerGenerator {
}
private async generatePackageJson(): Promise<void> {
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<void> {
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<void> {
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<any> {`,
@@ -230,7 +230,7 @@ export class MCPServerGenerator {
}
private async generateAPIClient(): Promise<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
const dockerContent = `FROM node:18-alpine
const dockerContent: string = `FROM node:18-alpine
WORKDIR /app

View File

@@ -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<string>();
const tags: Set<string> = new Set<string>();
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);
});
}

View File

@@ -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, "");

View File

@@ -28,12 +28,12 @@ async function main(): Promise<void> {
// 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<void> {
async function createAdditionalFiles(mcpDir: string): Promise<void> {
// 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/

View File

@@ -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<string> = ["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) {

View File

@@ -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 {