Skip to main content
Glama
field-validator.ts5.53 kB
import { SanitizedObject } from '../schemas/common/types.js'; import { UniversalResourceType } from '../types.js'; import { ErrorType, HttpStatusCode, UniversalValidationError, } from '../errors/validation-errors.js'; export function suggestResourceType(invalid: string): string | undefined { const validTypes = Object.values(UniversalResourceType); const lower = invalid.toLowerCase(); const suggestions: Record<string, string> = { company: 'companies', person: 'people', record: 'records', task: 'tasks', user: 'people', contact: 'people', organization: 'companies', org: 'companies', }; if (suggestions[lower]) return suggestions[lower]; let bestMatch = ''; let bestScore = 0; for (const validType of validTypes) { const score = getStringSimilarity(lower, validType); if (score > bestScore && score > 0.5) { bestScore = score; bestMatch = validType; } } return bestMatch || undefined; } function getStringSimilarity(str1: string, str2: string): number { const longer = str1.length > str2.length ? str1 : str2; const shorter = str1.length > str2.length ? str2 : str1; if (longer.length === 0) return 1.0; const editDistance = getEditDistance(longer, shorter); return (longer.length - editDistance) / longer.length; } function getEditDistance(str1: string, str2: string): number { const matrix: number[][] = []; for (let i = 0; i <= str2.length; i++) matrix[i] = [i]; for (let j = 0; j <= str1.length; j++) matrix[0][j] = j; for (let i = 1; i <= str2.length; i++) { for (let j = 1; j <= str1.length; j++) { if (str2.charAt(i - 1) === str1.charAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min( matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1 ); } } } return matrix[str2.length][str1.length]; } export function validatePaginationParams(params: SanitizedObject): void { if ( 'limit' in params && params.limit !== null && params.limit !== undefined ) { const limit = Number(params.limit); if (isNaN(limit) || !Number.isInteger(limit)) { throw new UniversalValidationError( 'Parameter "limit" must be an integer', ErrorType.USER_ERROR, { field: 'limit', httpStatusCode: HttpStatusCode.UNPROCESSABLE_ENTITY, suggestion: 'Provide a valid integer for limit', example: 'limit: 10', } ); } if (limit < 1) { throw new UniversalValidationError( 'Parameter "limit" must be at least 1', ErrorType.USER_ERROR, { field: 'limit', httpStatusCode: HttpStatusCode.UNPROCESSABLE_ENTITY, suggestion: 'Use a positive integer for limit', example: 'limit: 10', } ); } if (limit > 100) { throw new UniversalValidationError( 'Parameter "limit" must not exceed 100', ErrorType.USER_ERROR, { field: 'limit', httpStatusCode: HttpStatusCode.UNPROCESSABLE_ENTITY, suggestion: 'Use a value between 1 and 100', example: 'limit: 50', } ); } params.limit = limit; } if ( 'offset' in params && params.offset !== null && params.offset !== undefined ) { const offset = Number(params.offset); if (isNaN(offset) || !Number.isInteger(offset)) { throw new UniversalValidationError( 'Parameter "offset" must be an integer', ErrorType.USER_ERROR, { field: 'offset', httpStatusCode: HttpStatusCode.UNPROCESSABLE_ENTITY, suggestion: 'Provide a valid integer for offset', example: 'offset: 0', } ); } if (offset < 0) { throw new UniversalValidationError( 'Parameter "offset" must be non-negative', ErrorType.USER_ERROR, { field: 'offset', httpStatusCode: HttpStatusCode.UNPROCESSABLE_ENTITY, suggestion: 'Use a non-negative integer for offset', example: 'offset: 0', } ); } params.offset = offset; } } export function validateIdFields(params: SanitizedObject): void { const idFields = [ 'record_id', 'source_id', 'target_id', 'company_id', 'person_id', 'list_id', ]; for (const field of idFields) { if ( field in params && params[field] !== null && params[field] !== undefined ) { const id = String(params[field]); const idRegex = /^[a-zA-Z0-9_-]+$/; if (!idRegex.test(id)) { throw new UniversalValidationError( `Invalid ${field} format: "${id}"`, ErrorType.USER_ERROR, { field, httpStatusCode: HttpStatusCode.UNPROCESSABLE_ENTITY, suggestion: `The ${field} should contain only letters, numbers, underscores, and hyphens`, example: `${field}: 'comp_abc123' or 'person_xyz789'`, } ); } if (id.length < 3 || id.length > 100) { throw new UniversalValidationError( `Invalid ${field} length: ${id.length} characters`, ErrorType.USER_ERROR, { field, httpStatusCode: HttpStatusCode.UNPROCESSABLE_ENTITY, suggestion: `The ${field} should be between 3 and 100 characters`, example: `${field}: 'comp_abc123'`, } ); } } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server