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 multiple files
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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:'
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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, "");
|
||||
|
||||
@@ -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/
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user