Skip to main content
Glama
agent-requirements-manager.ts4.69 kB
import type { RawAxiosRequestConfig } from 'axios'; import type { AgentRequirement, Properties } from '@/teamcity-client/models'; import type { TeamCityClientAdapter } from './types/client'; type StringMap = Record<string, string>; interface ManageRequirementInput { buildTypeId: string; requirementId?: string; properties?: Record<string, unknown>; disabled?: boolean; } const JSON_HEADERS: RawAxiosRequestConfig = { headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, }; const JSON_GET_HEADERS: RawAxiosRequestConfig = { headers: { Accept: 'application/json', }, }; const toStringRecord = (input?: Record<string, unknown>): StringMap => { if (!input) { return {}; } return Object.fromEntries( Object.entries(input).map(([name, value]) => { if (value === undefined || value === null) { return [name, '']; } if (typeof value === 'boolean') { return [name, value ? 'true' : 'false']; } return [name, String(value)]; }) ); }; const propertiesToRecord = (properties?: Properties | null): StringMap => { if (properties == null) { return {}; } const propertyEntries = properties.property; const items = Array.isArray(propertyEntries) ? propertyEntries : propertyEntries != null ? [propertyEntries] : []; const record: StringMap = {}; for (const item of items) { if (!item?.name) { continue; } record[item.name] = item.value != null ? String(item.value) : ''; } return record; }; const recordToProperties = (record: StringMap): Properties | undefined => { const entries = Object.entries(record); if (entries.length === 0) { return undefined; } return { property: entries.map(([name, value]) => ({ name, value })), }; }; const mergeRecords = (base: StringMap, override: StringMap): StringMap => ({ ...base, ...override, }); export class AgentRequirementsManager { constructor(private readonly client: TeamCityClientAdapter) {} async addRequirement(input: ManageRequirementInput): Promise<{ id: string }> { const { buildTypeId } = input; const payload = this.buildPayload(undefined, input); const response = await this.client.modules.buildTypes.addAgentRequirementToBuildType( buildTypeId, undefined, payload, JSON_HEADERS ); const id = response.data?.id; if (!id) { throw new Error('TeamCity did not return an agent requirement identifier.'); } return { id }; } async updateRequirement( requirementId: string, input: ManageRequirementInput ): Promise<{ id: string }> { const { buildTypeId } = input; const existing = await this.fetchRequirement(buildTypeId, requirementId); if (!existing) { throw new Error( `Agent requirement ${requirementId} was not found on ${buildTypeId}; verify the ID or update via the TeamCity UI.` ); } const payload = this.buildPayload(existing, input); await this.client.modules.buildTypes.replaceAgentRequirement( buildTypeId, requirementId, undefined, payload, JSON_HEADERS ); return { id: requirementId }; } async deleteRequirement(buildTypeId: string, requirementId: string): Promise<void> { await this.client.modules.buildTypes.deleteAgentRequirement( buildTypeId, requirementId, JSON_HEADERS ); } private async fetchRequirement( buildTypeId: string, requirementId: string ): Promise<AgentRequirement | null> { try { const response = await this.client.modules.buildTypes.getAgentRequirement( buildTypeId, requirementId, 'id,type,disabled,properties(property(name,value))', JSON_GET_HEADERS ); return response.data as AgentRequirement; } catch (error) { if ( typeof error === 'object' && error !== null && 'response' in error && (error as { response?: { status?: number } }).response?.status === 404 ) { return null; } throw error; } } private buildPayload( existing: AgentRequirement | undefined, input: ManageRequirementInput ): AgentRequirement { const baseProps = propertiesToRecord(existing?.properties as Properties | undefined); const mergedProps = mergeRecords(baseProps, toStringRecord(input.properties)); const payload: AgentRequirement = { ...(existing ?? {}), disabled: input.disabled ?? existing?.disabled, }; const properties = recordToProperties(mergedProps); if (properties) { payload.properties = properties; } return payload; } }

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/Daghis/teamcity-mcp'

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