import { ITGlueClient } from '../client'; import { QueryUtilOptions, QueryParams, RequestBody, BaseListResponse, BaseItemResponse, OrganizationResource, LocationResource, ContactResource } from '../types'; /** * Organizations resource module for IT Glue API * * Provides methods to interact with the /organizations endpoint and related resources. * Organizations represent companies in IT Glue - both your own company and your customers. * This resource allows you to manage organization data, retrieve associated locations and contacts, * and maintain organizational hierarchies within your IT documentation system. * * ## Related Resources * Organizations serve as the central hub for most IT Glue resources and are commonly used with: * - {@link Contacts} - Manage people associated with organizations * - {@link Locations} - Manage physical sites and facilities for organizations * - {@link Configurations} - Track IT assets and systems belonging to organizations * - {@link Documents} - Store organizational documentation and policies * - {@link Passwords} - Manage credentials associated with organizational systems * - {@link FlexibleAssets} - Track custom organizational data and assets * - {@link OrganizationTypes} - Classify organizations by type (client, vendor, internal) * - {@link OrganizationStatuses} - Track organizational status and lifecycle * - {@link Attachments} - Store files and documents related to organizations * - {@link RelatedItems} - Create relationships between organizations and other resources * - {@link Tags} - Categorize and label organizations for better organization * - {@link Domains} - Manage domain names owned by organizations * - {@link Expirations} - Track expiring items associated with organizations * * @see {@link Contacts#list} for retrieving organization contacts * @see {@link Locations#list} for retrieving organization locations * @see {@link Configurations#list} for retrieving organization configurations * @see {@link Documents#list} for retrieving organization documents * @see {@link Passwords#list} for retrieving organization passwords * * @example * import { ITGlueClient } from '../client'; * import { Organizations } from './resources/organizations'; * * const client = new ITGlueClient({ apiKey: 'your-api-key' }); * const orgs = new Organizations(client); * * // List organizations * const list = await orgs.list(); * * // Get a single organization * const org = await orgs.get('123'); * * // Create an organization * const created = await orgs.create({ * data: { * type: 'organizations', * attributes: { name: 'New Organization', description: 'A new client company' } * } * }); * * // Update an organization * const updated = await orgs.update('123', { * data: { * type: 'organizations', * attributes: { name: 'Updated Organization Name' } * } * }); * * // Delete an organization * await orgs.delete('123'); * * // Get locations for an organization * const locations = await orgs.getLocations('123'); * * // Get contacts for an organization * const contacts = await orgs.getContacts('123'); * * @category Organizations */ export declare class Organizations { private client; private basePath; private paginationUtil; /** * Create an Organizations resource instance * @param {ITGlueClient} client - ITGlueClient instance */ constructor(client: ITGlueClient); /** * List all organizations * @param {QueryUtilOptions} [options] - Optional query parameters (filter, sort, page, etc.) * @param {boolean} [allPages=false] - If true, fetches all pages automatically * @returns {Promise>} List of organizations and pagination metadata * @example * // Basic usage - get first page of results * const results = await client.organizations.list(); * console.log(`Found ${results.data.length} organizations`); * console.log('Total pages:', results.meta.pagination.total_pages); * * @example * // Advanced usage with pagination and sorting * const results = await client.organizations.list({ * page: { number: 2, size: 50 }, * sort: '-updated_at', // Sort by most recently updated * include: ['organization_type', 'organization_status'] // Include related data * }); * * @example * // Filtering results * const filtered = await client.organizations.list({ * filter: { * name: 'Acme', * organization_type_id: '123' * }, * sort: 'name' * }); * * @example * // Get all results across multiple pages * const allResults = await client.organizations.list({}, true); // allPages = true * console.log(`Retrieved all ${allResults.data.length} organizations`); * * @example * // Manual pagination handling * async function getAllOrganizations() { * let page = 1; * let allResults = []; * let hasMore = true; * * while (hasMore) { * const response = await client.organizations.list({ * page: { number: page, size: 100 } * }); * * allResults = [...allResults, ...response.data]; * hasMore = response.meta.pagination.total_pages > page; * page++; * } * * return allResults; * } * * @example * // Error handling for list operations * try { * const results = await client.organizations.list({ * filter: { invalid_field: 'value' } * }); * } catch (error) { * if (error.response?.status === 400) { * console.log('Invalid filter parameters:', error.response.data.errors); * } else if (error.response?.status === 401) { * console.log('Authentication failed - check your API key'); * } else { * console.log('Request failed:', error.message); * } * } */ list(options?: QueryUtilOptions, allPages?: boolean): Promise>; /** * Get a single organization by ID * @param {string} id - Organization ID * @param {QueryParams} [params] - Optional query parameters * @returns {Promise>} Organization resource * @example * // Basic usage - get by ID * const organization = await client.organizations.get('123'); * console.log('Organization name:', organization.data.attributes.name); * * @example * // Get with related data included * const organizationWithRelated = await client.organizations.get('123', { * include: ['organization_type', 'organization_status', 'locations'] * }); * * // Access included data * const included = organizationWithRelated.included || []; * const orgType = included.find(item => item.type === 'organization_types'); * * @example * // Error handling for get operations * try { * const organization = await client.organizations.get('invalid-id'); * } catch (error) { * if (error.response?.status === 404) { * console.log('Organization not found'); * } else if (error.response?.status === 403) { * console.log('Access denied - insufficient permissions'); * } else { * console.log('Error retrieving organization:', error.message); * } * } * * @example * // Safe get with existence check * async function safeGetOrganization(id) { * try { * const organization = await client.organizations.get(id); * return organization.data; * } catch (error) { * if (error.response?.status === 404) { * return null; // Organization doesn't exist * } * throw error; // Re-throw other errors * } * } * @see * {@link Contacts#get} - Get specific contact details * {@link Contacts#list} - List contacts for this organization * {@link Locations#get} - Get specific location details * {@link Locations#list} - List locations for this organization * {@link Configurations#get} - Get specific configuration details */ get(id: string, params?: QueryParams): Promise>; /** * Create a new organization * @param {RequestBody} data - Organization data (must be formatted according to JSON:API spec) * @returns {Promise>} Created organization resource * @example * // Basic creation with required fields * const newOrganization = await client.organizations.create({ * data: { * type: 'organizations', * attributes: { * name: 'New Organization', * description: 'A new client company' * } * } * }); * * console.log('Created organization with ID:', newOrganization.data.id); * * @example * // Creation with all fields and relationships * const newOrganization = await client.organizations.create({ * data: { * type: 'organizations', * attributes: { * name: 'Tech Solutions Inc', * description: 'Technology consulting and managed services provider', * quick_notes: 'Primary contact: John Doe (john@techsolutions.com)', * alert: 'VIP client - priority support', * website: 'https://techsolutions.com' * }, * relationships: { * organization_type: { * data: { type: 'organization_types', id: '456' } * }, * organization_status: { * data: { type: 'organization_statuses', id: '789' } * } * } * } * }); * * @example * // Bulk creation with error handling * async function createMultipleOrganizations(items) { * const results = []; * const errors = []; * * for (const item of items) { * try { * const created = await client.organizations.create({ * data: { * type: 'organizations', * attributes: item * } * }); * results.push(created.data); * } catch (error) { * errors.push({ item, error: error.message }); * } * } * * return { results, errors }; * } * * @example * // Error handling for validation failures * try { * const newOrganization = await client.organizations.create({ * data: { * type: 'organizations', * attributes: { * // Missing required name field * description: 'Organization without name' * } * } * }); * } catch (error) { * if (error.response?.status === 422) { * console.log('Validation errors:'); * error.response.data.errors.forEach(err => { * console.log(`- ${err.detail} (${err.source?.pointer})`); * }); * } else if (error.response?.status === 403) { * console.log('Permission denied - cannot create organization'); * } else { * console.log('Creation failed:', error.message); * } * } * @see * {@link Contacts#create} - Create new contact * {@link Contacts#list} - List contacts for this organization * {@link Locations#create} - Create new location * {@link Locations#list} - List locations for this organization * {@link Configurations#create} - Create new configuration */ create(data: RequestBody): Promise>; /** * Update an organization by ID * @param {string} id - Organization ID * @param {RequestBody} data - Updated organization data (must be formatted according to JSON:API spec) * @returns {Promise>} Updated organization resource * @example * // Basic update - modify specific fields * const updatedOrganization = await client.organizations.update('123', { * data: { * type: 'organizations', * attributes: { * name: 'Updated Organization Name', * description: 'Updated description' * } * } * }); * * console.log('Updated organization:', updatedOrganization.data.attributes.name); * * @example * // Partial update with relationship changes * const updatedOrganization = await client.organizations.update('123', { * data: { * type: 'organizations', * attributes: { * alert: 'Updated alert message', * quick_notes: 'Recently renewed contract through 2025' * // Only include fields you want to change * }, * relationships: { * organization_status: { * data: { type: 'organization_statuses', id: '456' } // Change status * } * } * } * }); * * @example * // Conditional update based on current state * async function conditionalUpdateOrganization(id, updates) { * try { * // First, get current state * const current = await client.organizations.get(id); * * // Check if update is needed * const needsUpdate = Object.keys(updates).some( * key => current.data.attributes[key] !== updates[key] * ); * * if (!needsUpdate) { * console.log('Organization is already up to date'); * return current; * } * * // Perform update * return await client.organizations.update(id, { * data: { * type: 'organizations', * attributes: updates * } * }); * } catch (error) { * console.error('Update failed:', error.message); * throw error; * } * } * * @example * // Error handling for update operations * try { * const updated = await client.organizations.update('123', { * data: { * type: 'organizations', * attributes: { * invalid_field: 'value' * } * } * }); * } catch (error) { * if (error.response?.status === 404) { * console.log('Organization not found'); * } else if (error.response?.status === 422) { * console.log('Validation failed:', error.response.data.errors); * } else if (error.response?.status === 409) { * console.log('Conflict - organization may have been modified by another user'); * } else { * console.log('Update failed:', error.message); * } * } * @see * {@link Contacts#update} - Update contact * {@link Contacts#get} - Get specific contact details * {@link Locations#update} - Update location * {@link Locations#get} - Get specific location details * {@link Configurations#update} - Update configuration */ update(id: string, data: RequestBody): Promise>; /** * Delete an organization by ID * @param {string} id - Organization ID * @returns {Promise} * @example * // Basic deletion * await client.organizations.delete('123'); * console.log('Organization deleted successfully'); * * @example * // Safe deletion with confirmation * async function safeDeleteOrganization(id) { * try { * // First verify the organization exists * const organization = await client.organizations.get(id); * console.log(`Deleting organization: ${organization.data.attributes.name}`); * * // Perform deletion * await client.organizations.delete(id); * console.log('Organization deleted successfully'); * return true; * } catch (error) { * if (error.response?.status === 404) { * console.log('Organization not found - may already be deleted'); * return false; * } * throw error; * } * } * * @example * // Bulk deletion with error handling * async function deleteMultipleOrganizations(ids) { * const results = []; * * for (const id of ids) { * try { * await client.organizations.delete(id); * results.push({ id, status: 'deleted' }); * } catch (error) { * results.push({ * id, * status: 'error', * error: error.response?.status === 404 ? 'not_found' : error.message * }); * } * } * * return results; * } * * @example * // Error handling for delete operations * try { * await client.organizations.delete('123'); * } catch (error) { * if (error.response?.status === 404) { * console.log('Organization not found - may already be deleted'); * } else if (error.response?.status === 403) { * console.log('Permission denied - cannot delete organization'); * } else if (error.response?.status === 409) { * console.log('Cannot delete - organization is referenced by other resources'); * } else { * console.log('Deletion failed:', error.message); * } * } * @see * {@link Contacts#list} - List contacts for this organization * {@link Contacts#get} - Get specific contact details * {@link Locations#list} - List locations for this organization * {@link Locations#get} - Get specific location details * {@link Configurations#list} - List configurations belonging to this organization */ delete(id: string): Promise; /** * Get locations for an organization * @param {string} id - Organization ID * @param {QueryUtilOptions} [options] - Optional query parameters (filter, sort, page, etc.) * @param {boolean} [allPages=false] - If true, fetches all pages automatically * @returns {Promise>} List of locations related to the organization and pagination metadata * @example * // Basic usage - get organization locations * const locations = await client.organizations.getLocations('123'); * console.log(`Organization has ${locations.data.length} locations`); * * @example * // Get organization locations with filtering and sorting * const filteredLocations = await client.organizations.getLocations('123', { * filter: { city: 'New York' }, * sort: 'name' * }); * * @example * // Get all locations with pagination and related data * const allLocations = await client.organizations.getLocations('123', { * page: { number: 1, size: 50 }, * include: ['country', 'region'], * sort: 'created_at' * }, true); * * @example * // Advanced location analysis and geographic distribution * async function analyzeOrganizationLocations(organizationId) { * try { * const locations = await client.organizations.getLocations(organizationId, { * include: ['country', 'region'] * }, true); * * const analysis = { * totalLocations: locations.data.length, * countries: new Set(), * regions: new Set(), * cities: new Set(), * locationTypes: {}, * addressCompleteness: { * complete: 0, * partial: 0, * minimal: 0 * }, * geographicDistribution: {} * }; * * locations.data.forEach(location => { * const attrs = location.attributes; * * // Geographic analysis * if (attrs.country) analysis.countries.add(attrs.country); * if (attrs.region) analysis.regions.add(attrs.region); * if (attrs.city) analysis.cities.add(attrs.city); * * // Location type analysis * const locationType = attrs.location_type || 'Unknown'; * analysis.locationTypes[locationType] = (analysis.locationTypes[locationType] || 0) + 1; * * // Address completeness analysis * const addressFields = [attrs.address_1, attrs.city, attrs.region, attrs.postal_code, attrs.country]; * const completedFields = addressFields.filter(field => field && field.trim()).length; * * if (completedFields >= 4) { * analysis.addressCompleteness.complete++; * } else if (completedFields >= 2) { * analysis.addressCompleteness.partial++; * } else { * analysis.addressCompleteness.minimal++; * } * * // Geographic distribution by country * const country = attrs.country || 'Unknown'; * if (!analysis.geographicDistribution[country]) { * analysis.geographicDistribution[country] = { count: 0, cities: new Set() }; * } * analysis.geographicDistribution[country].count++; * if (attrs.city) { * analysis.geographicDistribution[country].cities.add(attrs.city); * } * }); * * // Convert Sets to arrays for JSON serialization * analysis.countries = Array.from(analysis.countries); * analysis.regions = Array.from(analysis.regions); * analysis.cities = Array.from(analysis.cities); * * // Convert city Sets to arrays in geographic distribution * Object.keys(analysis.geographicDistribution).forEach(country => { * analysis.geographicDistribution[country].cities = * Array.from(analysis.geographicDistribution[country].cities); * }); * * console.log('Location Analysis:', analysis); * return analysis; * * } catch (error) { * console.error('Location analysis failed:', error.message); * throw error; * } * } * * @example * // Find locations by specific criteria * async function findLocationsByCriteria(organizationId, criteria) { * try { * const allLocations = await client.organizations.getLocations(organizationId, {}, true); * * const matchingLocations = allLocations.data.filter(location => { * const attrs = location.attributes; * * // Check each criteria * if (criteria.country && attrs.country !== criteria.country) return false; * if (criteria.region && attrs.region !== criteria.region) return false; * if (criteria.city && !attrs.city?.toLowerCase().includes(criteria.city.toLowerCase())) return false; * if (criteria.locationType && attrs.location_type !== criteria.locationType) return false; * if (criteria.hasPhone && !attrs.phone) return false; * if (criteria.hasAddress && !attrs.address_1) return false; * * return true; * }); * * console.log(`Found ${matchingLocations.length} locations matching criteria`); * return matchingLocations; * * } catch (error) { * console.error('Location search failed:', error.message); * throw error; * } * } * * @example * // Real-world scenario: Office consolidation analysis * async function analyzeOfficeConsolidation(organizationId) { * try { * const locations = await client.organizations.getLocations(organizationId, { * filter: { location_type: 'Office' }, * include: ['country', 'region'] * }, true); * * const consolidationAnalysis = { * totalOffices: locations.data.length, * byCountry: {}, * byRegion: {}, * consolidationOpportunities: [], * costSavingsPotential: [] * }; * * // Group offices by geographic location * locations.data.forEach(office => { * const attrs = office.attributes; * const country = attrs.country || 'Unknown'; * const region = attrs.region || 'Unknown'; * const city = attrs.city || 'Unknown'; * * // Country grouping * if (!consolidationAnalysis.byCountry[country]) { * consolidationAnalysis.byCountry[country] = { offices: [], cities: new Set() }; * } * consolidationAnalysis.byCountry[country].offices.push(office); * consolidationAnalysis.byCountry[country].cities.add(city); * * // Region grouping * const regionKey = `${country}-${region}`; * if (!consolidationAnalysis.byRegion[regionKey]) { * consolidationAnalysis.byRegion[regionKey] = { offices: [], cities: new Set() }; * } * consolidationAnalysis.byRegion[regionKey].offices.push(office); * consolidationAnalysis.byRegion[regionKey].cities.add(city); * }); * * // Identify consolidation opportunities * Object.entries(consolidationAnalysis.byRegion).forEach(([regionKey, data]) => { * if (data.offices.length > 1 && data.cities.size === 1) { * // Multiple offices in the same city * const city = Array.from(data.cities)[0]; * consolidationAnalysis.consolidationOpportunities.push({ * type: 'same_city', * location: `${city}, ${regionKey}`, * officeCount: data.offices.length, * offices: data.offices.map(o => ({ * id: o.id, * name: o.attributes.name, * address: o.attributes.address_1 * })), * potentialSavings: 'High - Same city consolidation' * }); * } else if (data.offices.length > 2) { * // Multiple offices in the same region * consolidationAnalysis.consolidationOpportunities.push({ * type: 'same_region', * location: regionKey, * officeCount: data.offices.length, * cities: Array.from(data.cities), * potentialSavings: 'Medium - Regional consolidation' * }); * } * }); * * // Convert Sets to arrays * Object.values(consolidationAnalysis.byCountry).forEach(data => { * data.cities = Array.from(data.cities); * }); * Object.values(consolidationAnalysis.byRegion).forEach(data => { * data.cities = Array.from(data.cities); * }); * * console.log('Office Consolidation Analysis:', consolidationAnalysis); * return consolidationAnalysis; * * } catch (error) { * console.error('Office consolidation analysis failed:', error.message); * throw error; * } * } * * @example * // Bulk location data validation and cleanup * async function validateLocationData(organizationId) { * try { * const locations = await client.organizations.getLocations(organizationId, {}, true); * * const validationResults = { * totalLocations: locations.data.length, * valid: [], * issues: [], * suggestions: [] * }; * * locations.data.forEach(location => { * const attrs = location.attributes; * const locationIssues = []; * const locationSuggestions = []; * * // Required field validation * if (!attrs.name || attrs.name.trim() === '') { * locationIssues.push('Missing location name'); * } * * if (!attrs.address_1 || attrs.address_1.trim() === '') { * locationIssues.push('Missing primary address'); * } * * if (!attrs.city || attrs.city.trim() === '') { * locationIssues.push('Missing city'); * } * * if (!attrs.country || attrs.country.trim() === '') { * locationIssues.push('Missing country'); * } * * // Data quality suggestions * if (!attrs.phone || attrs.phone.trim() === '') { * locationSuggestions.push('Consider adding phone number'); * } * * if (!attrs.postal_code || attrs.postal_code.trim() === '') { * locationSuggestions.push('Consider adding postal code'); * } * * if (!attrs.notes || attrs.notes.trim() === '') { * locationSuggestions.push('Consider adding location notes or description'); * } * * // Phone number format validation * if (attrs.phone && !/^[\+]?[1-9][\d\s\-\(\)]{7,15}$/.test(attrs.phone.replace(/\s/g, ''))) { * locationIssues.push('Phone number format may be invalid'); * } * * // Postal code basic validation (varies by country) * if (attrs.postal_code && attrs.country) { * const postalCode = attrs.postal_code.replace(/\s/g, ''); * if (attrs.country.toLowerCase() === 'united states' && !/^\d{5}(-\d{4})?$/.test(postalCode)) { * locationIssues.push('US postal code format may be invalid'); * } else if (attrs.country.toLowerCase() === 'canada' && !/^[A-Z]\d[A-Z]\d[A-Z]\d$/.test(postalCode)) { * locationIssues.push('Canadian postal code format may be invalid'); * } * } * * const locationResult = { * id: location.id, * name: attrs.name, * address: `${attrs.address_1}, ${attrs.city}, ${attrs.region || ''} ${attrs.postal_code || ''}`.trim(), * issues: locationIssues, * suggestions: locationSuggestions * }; * * if (locationIssues.length === 0) { * validationResults.valid.push(locationResult); * } else { * validationResults.issues.push(locationResult); * } * * if (locationSuggestions.length > 0) { * validationResults.suggestions.push(locationResult); * } * }); * * const summary = { * validLocations: validationResults.valid.length, * locationsWithIssues: validationResults.issues.length, * locationsWithSuggestions: validationResults.suggestions.length, * dataQualityScore: Math.round((validationResults.valid.length / validationResults.totalLocations) * 100) * }; * * console.log('Location Validation Summary:', summary); * console.log('Detailed Results:', validationResults); * * return { ...validationResults, summary }; * * } catch (error) { * console.error('Location validation failed:', error.message); * throw error; * } * } * * @example * // Error handling for location retrieval * try { * const locations = await client.organizations.getLocations('invalid-id'); * } catch (error) { * if (error.response?.status === 404) { * console.log('Organization not found'); * } else { * console.log('Error retrieving locations:', error.message); * } * } */ getLocations(id: string, options?: QueryUtilOptions, allPages?: boolean): Promise>; /** * Get contacts for an organization * @param {string} id - Organization ID * @param {QueryUtilOptions} [options] - Optional query parameters (filter, sort, page, etc.) * @param {boolean} [allPages=false] - If true, fetches all pages automatically * @returns {Promise>} List of contacts related to the organization and pagination metadata * @example * // Get organization contacts * const contacts = await client.organizations.getContacts('123'); * console.log(`Organization has ${contacts.data.length} contacts`); * * @example * // Get organization contacts with pagination * const contacts = await client.organizations.getContacts('123', { * page: { number: 1, size: 25 }, * filter: { title: '~Manager' } * }); * * @example * // Get all contacts with related data * const allContacts = await client.organizations.getContacts('123', { * include: ['contact_type', 'location'], * sort: 'last_name' * }, true); * * @example * // Find specific contact types * const managers = await client.organizations.getContacts('123', { * filter: { * title: '~Manager', * contact_type_id: '456' * }, * sort: 'first_name' * }); * * @example * // Error handling for contact retrieval * try { * const contacts = await client.organizations.getContacts('invalid-id'); * } catch (error) { * if (error.response?.status === 404) { * console.log('Organization not found'); * } else if (error.response?.status === 403) { * console.log('Access denied to organization contacts'); * } else { * console.log('Error retrieving contacts:', error.message); * } * } */ getContacts(id: string, options?: QueryUtilOptions, allPages?: boolean): Promise>; }