import { ITGlueClient } from '../client'; import { QueryUtilOptions, QueryParams, RequestBody, BaseListResponse, BaseItemResponse, AttachmentResource } from '../types'; /** * Attachments resource module for IT Glue API * * Provides methods to interact with the /attachments endpoint, including file uploads and filtering. * Attachments represent files that can be associated with various IT Glue resources such as documents, * passwords, configurations, and other entities. Supported file types include documents, images, * spreadsheets, and other common file formats. File size limits and security scanning may apply. * * **File Handling Notes:** * - Maximum file size limits are enforced by the IT Glue API * - Files are scanned for security threats before storage * - Supported formats include PDF, DOC, XLS, images, and text files * - Binary files and executables may be restricted * * ## Related Resources * Attachments can be associated with various IT Glue resources: * - {@link Documents} - Primary use case for storing files with documentation * - {@link Organizations} - Store organizational files, logos, and documentation * - {@link Configurations} - Attach technical diagrams, manuals, and specifications * - {@link Contacts} - Store contact photos, business cards, and related files * - {@link Passwords} - Attach related documentation and access procedures * - {@link FlexibleAssets} - Store custom files and media for flexible assets * - {@link Locations} - Attach site plans, photos, and facility documentation * - {@link Manufacturers} - Store product documentation and specification sheets * - {@link RelatedItems} - Create file-based relationships between resources * - {@link Tags} - Categorize attachments by type, purpose, or content * * @see {@link Documents#list} for retrieving documents with attachments * @see {@link Organizations#list} for retrieving organizations with attachments * @see {@link Configurations#list} for retrieving configurations with attachments * @see {@link Contacts#list} for retrieving contacts with attachments * * @example * import { ITGlueClient } from '../client'; * import { Attachments } from './resources/attachments'; * * const client = new ITGlueClient({ apiKey: 'your-api-key' }); * const attachments = new Attachments(client); * * // List attachments * const list = await attachments.list(); * * // List attachments for an organization and parent * const filtered = await attachments.list({ * organization_id: '123', * parent_id: '456' * }); * * // Get a single attachment * const att = await attachments.get('789'); * * // Create an attachment (file upload) * const fileInput = document.getElementById('file') as HTMLInputElement; * const file = fileInput.files[0]; * const created = await attachments.create({ * data: { * type: 'attachments', * attributes: { * name: 'server-diagram.pdf', * notes: 'Network infrastructure diagram' * } * } * }, file); * * // Update an attachment * const updated = await attachments.update('789', { * data: { * type: 'attachments', * attributes: { name: 'updated-diagram.pdf' } * } * }); * * // Delete an attachment * await attachments.delete('789'); * * @category Documents & Attachments */ export declare class Attachments { private client; private basePath; private paginationUtil; /** * Create an Attachments resource instance * @param {ITGlueClient} client - ITGlueClient instance */ constructor(client: ITGlueClient); /** * List all attachments * @param {QueryUtilOptions} [options] - Optional query parameters (filter, sort, page, etc.) * @param {boolean} [allPages=false] - If true, fetches all pages automatically * @returns {Promise>} List of attachments and pagination metadata * @example * // Basic usage - get first page of attachments * const results = await client.attachments.list(); * console.log(`Found ${results.data.length} attachments`); * console.log('Total pages:', results.meta.pagination.total_pages); * * @example * // Advanced usage with pagination and sorting * const results = await client.attachments.list({ * page: { number: 2, size: 50 }, * sort: '-created_at', // Sort by most recently uploaded * include: ['attachable', 'organization'] // Include related data * }); * * @example * // Filtering attachments by organization and parent resource * const orgAttachments = await client.attachments.list({ * organization_id: '123', * parent_id: '456', * sort: 'name' * }); * * console.log('Organization attachments:'); * orgAttachments.data.forEach(att => { * console.log(`- ${att.attributes.name} (${att.attributes.file_size} bytes)`); * }); * * @example * // Filtering by file type and content * const pdfAttachments = await client.attachments.list({ * filter: { name: '.pdf' }, * include: ['attachable'] * }); * * console.log('PDF attachments found:'); * pdfAttachments.data.forEach(att => { * console.log(`- ${att.attributes.name}: ${att.attributes.notes || 'No notes'}`); * }); * * @example * // Get all attachments with file analysis * const allAttachments = await client.attachments.list({}, true); // allPages = true * * // Analyze file types and sizes * const analysis = { * totalFiles: allAttachments.data.length, * totalSize: 0, * fileTypes: {}, * largeFiles: [], * recentUploads: [] * }; * * const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); * * allAttachments.data.forEach(att => { * const size = att.attributes.file_size || 0; * analysis.totalSize += size; * * // Extract file extension * const extension = att.attributes.name.split('.').pop()?.toLowerCase() || 'unknown'; * analysis.fileTypes[extension] = (analysis.fileTypes[extension] || 0) + 1; * * // Track large files (>10MB) * if (size > 10 * 1024 * 1024) { * analysis.largeFiles.push({ * name: att.attributes.name, * size: size, * id: att.id * }); * } * * // Track recent uploads * if (new Date(att.attributes.created_at) > oneWeekAgo) { * analysis.recentUploads.push(att); * } * }); * * console.log('File analysis:', analysis); * * @example * // Manual pagination for large attachment datasets * async function getAllAttachments(filterParams = {}) { * let page = 1; * let allResults = []; * let hasMore = true; * * while (hasMore) { * const response = await client.attachments.list({ * ...filterParams, * page: { number: page, size: 100 }, * sort: 'created_at' * }); * * 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.attachments.list({ * organization_id: 'invalid-id' * }); * } 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 if (error.response?.status === 403) { * console.log('Access denied - insufficient permissions to view attachments'); * } else { * console.log('Request failed:', error.message); * } * } */ list(options?: QueryUtilOptions, allPages?: boolean): Promise>; /** * Get a single attachment by ID * @param {string} id - Attachment ID * @param {QueryParams} [params] - Optional query parameters * @returns {Promise>} Attachment resource * @example * // Basic usage - get attachment by ID * const attachment = await client.attachments.get('789'); * console.log('Attachment name:', attachment.data.attributes.name); * console.log('File size:', attachment.data.attributes.file_size); * console.log('Content type:', attachment.data.attributes.content_type); * * @example * // Get attachment with related data included * const attachmentWithDetails = await client.attachments.get('789', { * include: ['attachable', 'organization'] * }); * * // Access included data * const included = attachmentWithDetails.included || []; * const attachable = included.find(item => item.type !== 'organizations'); * const organization = included.find(item => item.type === 'organizations'); * * console.log('Attached to:', attachable?.type, attachable?.id); * console.log('Organization:', organization?.attributes?.name); * * @example * // Detailed file information analysis * const attachment = await client.attachments.get('789'); * const attrs = attachment.data.attributes; * * const fileInfo = { * name: attrs.name, * size: attrs.file_size, * sizeFormatted: formatFileSize(attrs.file_size), * type: attrs.content_type, * extension: attrs.name.split('.').pop()?.toLowerCase(), * uploadDate: attrs.created_at, * lastModified: attrs.updated_at, * notes: attrs.notes || 'No notes', * downloadUrl: attrs.download_url * }; * * function formatFileSize(bytes) { * if (bytes === 0) return '0 Bytes'; * const k = 1024; * const sizes = ['Bytes', 'KB', 'MB', 'GB']; * const i = Math.floor(Math.log(bytes) / Math.log(k)); * return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; * } * * console.log('File information:', fileInfo); * * @example * // Error handling for get operations * try { * const attachment = await client.attachments.get('invalid-id'); * } catch (error) { * if (error.response?.status === 404) { * console.log('Attachment not found'); * } else if (error.response?.status === 403) { * console.log('Access denied - insufficient permissions to view attachment'); * } else { * console.log('Error retrieving attachment:', error.message); * } * } * * @example * // Safe get with existence check * async function safeGetAttachment(id) { * try { * const attachment = await client.attachments.get(id); * return attachment.data; * } catch (error) { * if (error.response?.status === 404) { * return null; // Attachment doesn't exist * } * throw error; // Re-throw other errors * } * } * @see * {@link Organizations#get} - Get specific organization details * {@link Organizations#list} - List organizations related to attachments */ get(id: string, params?: QueryParams): Promise>; /** * Create a new attachment with file upload * * Uploads a file and creates an attachment record in IT Glue. The file is uploaded * using multipart/form-data encoding and will be scanned for security threats. * File size limits and format restrictions may apply based on your IT Glue configuration. * * @param {RequestBody} data - Attachment data (must be formatted according to JSON:API spec) * @param {File | Blob} file - File to upload (File or Blob object) * @returns {Promise>} Created attachment resource * @throws {Error} When file is too large (413), unsupported format (422), or upload fails * @example * // Upload a file from file input * const fileInput = document.getElementById('file') as HTMLInputElement; * const file = fileInput.files[0]; * const created = await attachments.create({ * data: { * type: 'attachments', * attributes: { * name: 'server-diagram.pdf', * notes: 'Network infrastructure diagram' * }, * relationships: { * attachable: { * data: { type: 'documents', id: '123' } * } * } * } * }, file); * @example * // Upload with organization association * const created = await attachments.create({ * data: { * type: 'attachments', * attributes: { * name: 'policy-document.docx', * notes: 'Updated security policy' * }, * relationships: { * organization: { * data: { type: 'organizations', id: '456' } * } * } * } * }, policyFile); * @example * // Error handling for file upload * try { * const created = await attachments.create({ * data: { * type: 'attachments', * attributes: { name: 'large-file.zip' } * } * }, largeFile); * } catch (error) { * if (error.response?.status === 413) { * console.log('File too large'); * } else if (error.response?.status === 422) { * console.log('Unsupported file format or validation failed'); * } * } * @see * {@link Organizations#create} - Create new organization * {@link Organizations#list} - List organizations related to attachments */ create(data: RequestBody, file: File | Blob): Promise>; /** * Update an attachment by ID * * Updates attachment metadata such as name, notes, and relationships. * Note that this method updates the attachment record but does not replace * the actual file. To replace a file, delete the attachment and create a new one. * * @param {string} id - Attachment ID * @param {RequestBody} data - Updated attachment data (JSON:API format) * @returns {Promise>} Updated attachment resource * @throws {Error} When attachment not found (404) or validation fails (422) * @example * // Basic update - modify name and notes * const updated = await client.attachments.update('789', { * data: { * type: 'attachments', * attributes: { * name: 'updated-server-diagram.pdf', * notes: 'Updated network infrastructure diagram with new servers' * } * } * }); * * console.log('Updated attachment:', updated.data.attributes.name); * * @example * // Update attachment relationships and associations * const updated = await client.attachments.update('789', { * data: { * type: 'attachments', * attributes: { * notes: 'Moved to configuration documentation' * }, * relationships: { * attachable: { * data: { type: 'configurations', id: '999' } * } * } * } * }); * * @example * // Conditional update based on current state * async function conditionalUpdateAttachment(id, updates) { * try { * // First, get current state * const current = await client.attachments.get(id); * * // Check if update is needed * const currentAttrs = current.data.attributes; * const needsUpdate = Object.keys(updates.attributes || {}).some( * key => currentAttrs[key] !== updates.attributes[key] * ); * * if (!needsUpdate) { * console.log('Attachment is already up to date'); * return current; * } * * // Perform update * return await client.attachments.update(id, { * data: { * type: 'attachments', * attributes: updates.attributes * } * }); * } catch (error) { * console.error('Update failed:', error.message); * throw error; * } * } * * @example * // Error handling for attachment updates * try { * const updated = await client.attachments.update('789', { * data: { * type: 'attachments', * attributes: { name: '' } // Invalid empty name * } * }); * } catch (error) { * if (error.response?.status === 404) { * console.log('Attachment not found'); * } else if (error.response?.status === 422) { * console.log('Validation failed:', error.response.data.errors); * } else if (error.response?.status === 403) { * console.log('Permission denied - cannot update attachment'); * } else { * console.log('Update failed:', error.message); * } * } * @see * {@link Organizations#update} - Update organization * {@link Organizations#get} - Get specific organization details */ update(id: string, data: RequestBody): Promise>; /** * Delete an attachment by ID * * Permanently deletes an attachment and its associated file from IT Glue. * This action cannot be undone. The file will be removed from storage and * any references to this attachment will be broken. * * @param {string} id - Attachment ID * @returns {Promise} * @throws {Error} When attachment not found (404) or deletion not allowed (403) * @example * // Basic deletion * await client.attachments.delete('789'); * console.log('Attachment deleted successfully'); * * @example * // Safe deletion with confirmation * async function safeDeleteAttachment(id) { * try { * // First verify the attachment exists * const attachment = await client.attachments.get(id); * console.log(`Deleting attachment: ${attachment.data.attributes.name}`); * * // Perform deletion * await client.attachments.delete(id); * console.log('Attachment deleted successfully'); * return true; * } catch (error) { * if (error.response?.status === 404) { * console.log('Attachment not found - may already be deleted'); * return false; * } * throw error; * } * } * * @example * // Bulk deletion with error handling * async function deleteMultipleAttachments(ids) { * const results = []; * * for (const id of ids) { * try { * await client.attachments.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 attachment deletion * try { * await client.attachments.delete('789'); * } catch (error) { * if (error.response?.status === 403) { * console.log('Insufficient permissions to delete attachment'); * } else if (error.response?.status === 404) { * console.log('Attachment not found - may already be deleted'); * } else if (error.response?.status === 409) { * console.log('Cannot delete - attachment may be referenced by other resources'); * } else { * console.log('Deletion failed:', error.message); * } * } * @see * {@link Organizations#list} - List organizations related to attachments * {@link Organizations#get} - Get specific organization details */ delete(id: string): Promise; }