Skip to main content
Glama
AttributeOptionsService.ts4.49 kB
/** * Service for retrieving attribute options (select, multi-select, status). * Provides a unified interface for fetching valid options from Attio. */ import { getSelectOptions, getListSelectOptions, getStatusOptions, } from '@/api/attio-client.js'; import type { AttioSelectOption, AttioStatusOption } from '@/api/types.js'; import { error as logError } from '@/utils/logger.js'; /** * Result structure for attribute options */ export interface AttributeOptionsResult { options: (AttioSelectOption | AttioStatusOption)[]; attributeType: 'select' | 'status'; } /** * Service for fetching attribute options from Attio. * Handles both select/multi-select and status attribute types. */ export class AttributeOptionsService { /** * Gets options for any option-based attribute (select, multi-select, status). * Auto-detects attribute type by trying select first, then status. * * @param objectSlug - The object slug (e.g., "companies", "people", "deals") * @param attributeSlug - The attribute slug (e.g., "channel", "stage") * @param showArchived - Whether to include archived options * @returns Options with attribute type information */ static async getOptions( objectSlug: string, attributeSlug: string, showArchived?: boolean ): Promise<AttributeOptionsResult> { let selectOptions: AttioSelectOption[] = []; let selectError: unknown = null; let statusError: unknown = null; // Try select endpoint first (covers select and multi-select) try { selectOptions = await getSelectOptions( objectSlug, attributeSlug, showArchived ); } catch (err) { selectError = err; } // Select succeeded with results → return as select if (!selectError && selectOptions.length > 0) { return { options: selectOptions, attributeType: 'select', }; } // Select empty or failed → try status endpoint // This handles status attributes like deals.stage where /options returns [] // but /statuses returns actual values (Issue #987) try { const statuses = await getStatusOptions( objectSlug, attributeSlug, showArchived ); // Status endpoint succeeded → treat as status type (even if empty) return { options: statuses, attributeType: 'status', }; } catch (err) { statusError = err; logError( 'AttributeOptionsService', `Status endpoint failed for ${objectSlug}.${attributeSlug}`, err, { objectSlug, attributeSlug } ); } // Status failed but select succeeded with empty → return empty select // (legitimate case for unconfigured select attributes) if (!selectError) { return { options: selectOptions, attributeType: 'select', }; } // Both failed → throw with detailed error context const selectMsg = selectError instanceof Error ? selectError.message : String(selectError); const statusMsg = statusError instanceof Error ? statusError.message : String(statusError); logError( 'AttributeOptionsService', `Failed to get options for ${objectSlug}.${attributeSlug}`, selectError instanceof Error ? selectError : new Error(selectMsg), { selectError: selectMsg, statusError: statusMsg } ); throw new Error( `Attribute "${attributeSlug}" on "${objectSlug}" does not support options. ` + `This may not be a select, multi-select, or status attribute. ` + `Select error: ${selectMsg}; Status error: ${statusMsg}` ); } /** * Gets options for a select attribute on a list. * * @param listId - The list ID or slug * @param attributeSlug - The attribute slug * @param showArchived - Whether to include archived options * @returns Options with attribute type information */ static async getListOptions( listId: string, attributeSlug: string, showArchived?: boolean ): Promise<AttributeOptionsResult> { try { const options = await getListSelectOptions( listId, attributeSlug, showArchived ); return { options, attributeType: 'select', }; } catch (err) { logError( 'AttributeOptionsService', `Failed to get list options for ${listId}.${attributeSlug}`, err, { listId, attributeSlug } ); throw err; } } }

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