Skip to main content
Glama
relationships.ts7.52 kB
/** * Relationship-based queries for companies */ import { ResourceType, Company, AttioList, AttioListEntry, } from '../../types/attio.js'; import { ListEntryFilters } from '../../api/operations/index.js'; import { FilterValidationError } from '../../errors/api-errors.js'; import { validateNumericParam } from '../../utils/filters/index.js'; import { getLazyAttioClient } from '../../api/lazy-client.js'; import { getListDetails } from '../lists.js'; import { createCompaniesByPeopleFilter, createCompaniesByPeopleListFilter, createRecordsByNotesFilter, } from '../../utils/relationship-utils.js'; import { advancedSearchCompanies } from './search.js'; /** * Search for companies based on attributes of their associated people * * @param peopleFilter - Filter to apply to people * @param limit - Maximum number of results to return (default: 20) * @param offset - Number of results to skip (default: 0) * @returns Array of matching companies */ export async function searchCompaniesByPeople( peopleFilter: ListEntryFilters | string | Record<string, unknown> | undefined, limit: number | string = 20, offset: number | string = 0 ): Promise<Company[]> { try { // Handle the case where we receive just a person ID (from search-by-relationship) let filterObject: ListEntryFilters; if (typeof peopleFilter === 'string') { // Create a filter to find companies associated with this person ID filterObject = { filters: [ { attribute: { slug: 'associated_people' }, condition: 'contains', value: peopleFilter, }, ], }; } else if ( typeof peopleFilter === 'object' && peopleFilter && peopleFilter.filters ) { filterObject = peopleFilter; } else { throw new FilterValidationError( 'People filter must be a valid ListEntryFilters object or a person ID string' ); } // Validate and normalize limit and offset parameters const validatedLimit = validateNumericParam(limit, 'limit', 20); const validatedOffset = validateNumericParam(offset, 'offset', 0); // Create the relationship-based filter and perform the search const filters = createCompaniesByPeopleFilter(filterObject); const results = await advancedSearchCompanies( filters, validatedLimit, validatedOffset ); return Array.isArray(results) ? results : []; } catch (error: unknown) { // Convert all errors to FilterValidationErrors for consistent handling if (error instanceof FilterValidationError) { throw error; } throw new FilterValidationError( `Failed to search companies by people: ${ error instanceof Error ? error.message : String(error) }` ); } } /** * Search for companies that have employees in a specific list * * @param listId - ID of the list containing people * @param limit - Maximum number of results to return (default: 20) * @param offset - Number of results to skip (default: 0) * @returns Array of matching companies */ export async function searchCompaniesByPeopleList( listId: string, limit: number | string = 20, offset: number | string = 0 ): Promise<Company[]> { try { // Validate listId if (!listId || typeof listId !== 'string' || listId.trim() === '') { throw new FilterValidationError('List ID must be a non-empty string'); } // Validate and normalize limit and offset parameters const validatedLimit = validateNumericParam(limit, 'limit', 20); const validatedOffset = validateNumericParam(offset, 'offset', 0); // Create the relationship-based filter and perform the search const filters = createCompaniesByPeopleListFilter(listId); const results = await advancedSearchCompanies( filters, validatedLimit, validatedOffset ); return Array.isArray(results) ? results : []; } catch (error: unknown) { // Convert all errors to FilterValidationErrors for consistent handling if (error instanceof FilterValidationError) { throw error; } throw new FilterValidationError( `Failed to search companies by people list: ${ error instanceof Error ? error.message : String(error) }` ); } } /** * Search for companies that have notes containing specific text * * @param searchText - Text to search for in notes * @param limit - Maximum number of results to return (default: 20) * @param offset - Number of results to skip (default: 0) * @returns Array of matching companies */ export async function searchCompaniesByNotes( searchText: string, limit: number | string = 20, offset: number | string = 0 ): Promise<Company[]> { try { // Validate searchText if ( !searchText || typeof searchText !== 'string' || searchText.trim() === '' ) { throw new FilterValidationError('Search text must be a non-empty string'); } // Validate and normalize limit and offset parameters const validatedLimit = validateNumericParam(limit, 'limit', 20); const validatedOffset = validateNumericParam(offset, 'offset', 0); // Create the relationship-based filter and perform the search const filters = createRecordsByNotesFilter( ResourceType.COMPANIES, searchText ); const results = await advancedSearchCompanies( filters, validatedLimit, validatedOffset ); return Array.isArray(results) ? results : []; } catch (error: unknown) { // Convert all errors to FilterValidationErrors for consistent handling if (error instanceof FilterValidationError) { throw error; } throw new FilterValidationError( `Failed to search companies by notes: ${ error instanceof Error ? error.message : String(error) }` ); } } /** * Gets lists that a company belongs to * * @param companyId - ID of the company * @param limit - Maximum number of list entries to check (default: 50) * @returns Array of unique lists containing the company */ export async function getCompanyLists( companyId: string, limit: number = 50 ): Promise<AttioList[]> { const api = getLazyAttioClient(); const response = await api.post<{ data: AttioListEntry[] }>( '/lists-entries/query', { filter: { record_id: { $equals: companyId } }, expand: ['list'], limit, } ); const entries = response.data.data || []; const lists: AttioList[] = []; const seen = new Set<string>(); const normalizedEntries = Array.isArray(entries) ? (entries as Array<Record<string, unknown>>) : []; for (const entry of normalizedEntries) { const rawList = entry.list as Record<string, unknown> | undefined; const listIdFromEntry = typeof entry.list_id === 'string' ? entry.list_id : undefined; const listIdFromList = typeof rawList?.id === 'object' && rawList?.id !== null ? (rawList.id as Record<string, unknown>).list_id : undefined; const listId = typeof listIdFromEntry === 'string' ? listIdFromEntry : typeof listIdFromList === 'string' ? listIdFromList : undefined; if (!listId || seen.has(listId)) continue; seen.add(listId); if (rawList) { lists.push(rawList as AttioList); } else { try { const detail = await getListDetails(listId); lists.push(detail); } catch { // ignore retrieval errors } } } return lists; }

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