UNPKG

ministry-platform-provider

Version:

TypeScript client library for Ministry Platform API integration

390 lines (310 loc) 12.4 kB
# Ministry Platform Services Documentation ## Overview The services directory contains specialized service classes that handle specific domains of Ministry Platform functionality. Each service focuses on a particular aspect of the API while sharing common patterns for authentication, error handling, and HTTP communication. ## Service Architecture ``` ministryPlatformProvider ↓ ┌─────────────────┬─────────────────┬─────────────────┐ │ TableService │ ProcedureService│CommunicationSvc │ │ DomainService │ MetadataService │ FileService │ └─────────────────┴─────────────────┴─────────────────┘ ↓ MinistryPlatformClient ``` ## Common Service Patterns All services follow these consistent patterns: ### Constructor Pattern ```typescript export class ServiceName { private client: MinistryPlatformClient; constructor(client: MinistryPlatformClient) { this.client = client; } } ``` ### Method Pattern ```typescript public async methodName(params): Promise<ReturnType> { try { await this.client.ensureValidToken(); const endpoint = '/api/endpoint'; return await this.client.getHttpClient().method<ReturnType>(endpoint, params); } catch (error) { console.error('Error message:', error); throw error; } } ``` ## Service Classes ### 1. TableService **Purpose**: Handles all CRUD operations for Ministry Platform tables **Key Methods**: - `getTableRecords<T>()` - Query records from any table - `createTableRecords<T>()` - Create new records - `updateTableRecords<T>()` - Update existing records - `deleteTableRecords<T>()` - Delete records by ID **Usage Examples**: ```typescript // Get contacts const contacts = await tableService.getTableRecords<Contact>('Contacts', { $filter: 'Contact_Status_ID=1', $select: 'Contact_ID,Display_Name,Email_Address' }); // Create new contact const newContacts = await tableService.createTableRecords('Contacts', [{ First_Name: 'John', Last_Name: 'Doe', Email_Address: 'john@example.com' }]); ``` ### 2. ProcedureService **Purpose**: Executes stored procedures with parameter handling **Key Methods**: - `getProcedures()` - List available stored procedures - `executeProcedure()` - Execute with query parameters - `executeProcedureWithBody()` - Execute with request body parameters **Usage Examples**: ```typescript // Get available procedures const procedures = await procedureService.getProcedures('api_'); // Execute procedure with query params const results = await procedureService.executeProcedure('api_GetContacts', { HouseholdId: 123 }); // Execute procedure with body params const results = await procedureService.executeProcedureWithBody('api_Search', { SearchCriteria: { FirstName: 'John' } }); ``` ### 3. CommunicationService **Purpose**: Handles email and SMS communications with attachment support **Key Methods**: - `createCommunication()` - Create bulk communications - `sendMessage()` - Send direct messages - `createCommunicationWithAttachments()` - Handle file attachments (private) - `sendMessageWithAttachments()` - Handle message attachments (private) **Usage Examples**: ```typescript // Create bulk email const communication = await communicationService.createCommunication({ AuthorUserId: 1, Subject: 'Newsletter', Body: '<h1>Weekly Update</h1>', CommunicationType: 'Email', Contacts: [12345, 67890] }); // Send direct message const message = await communicationService.sendMessage({ FromAddress: { DisplayName: 'Pastor', Address: 'pastor@church.com' }, ToAddresses: [{ DisplayName: 'Member', Address: 'member@example.com' }], Subject: 'Personal note', Body: 'Thank you for visiting!' }); ``` ### 4. FileService **Purpose**: Manages file uploads, downloads, and metadata **Key Methods**: - `getFilesByRecord()` - Get files attached to records - `uploadFiles()` - Upload multiple files to records - `updateFile()` - Update file content/metadata - `deleteFile()` - Delete files - `getFileContentByUniqueId()` - Public file download - `getFileMetadata()` - Get file information **Usage Examples**: ```typescript // Upload files to contact const uploadedFiles = await fileService.uploadFiles('Contacts', 12345, [file1, file2], { description: 'Profile documents', isDefaultImage: true }); // Get files for record const contactFiles = await fileService.getFilesByRecord('Contacts', 12345); // Download file content const fileBlob = await fileService.getFileContentByUniqueId('unique-file-id'); ``` ### 5. MetadataService **Purpose**: Handles schema information and metadata operations **Key Methods**: - `refreshMetadata()` - Refresh metadata cache - `getTables()` - Get available tables with metadata **Usage Examples**: ```typescript // Refresh metadata cache await metadataService.refreshMetadata(); // Get table information const tables = await metadataService.getTables('contact'); tables.forEach(table => { console.log(`${table.Name}: ${table.AccessLevel}`); }); ``` ### 6. DomainService **Purpose**: Provides domain configuration and global filter information **Key Methods**: - `getDomainInfo()` - Get domain configuration - `getGlobalFilters()` - Get available global filters **Usage Examples**: ```typescript // Get domain information const domainInfo = await domainService.getDomainInfo(); console.log('Domain:', domainInfo.DisplayName); console.log('Time Zone:', domainInfo.TimeZoneName); // Get global filters const filters = await domainService.getGlobalFilters(); filters.forEach(filter => { console.log(`Filter ${filter.Key}: ${filter.Value}`); }); ``` ## Error Handling Patterns ### Standard Error Handling ```typescript try { await this.client.ensureValidToken(); return await this.client.getHttpClient().get<T>(endpoint, params); } catch (error) { console.error(`Error in ${serviceName}.${methodName}:`, error); throw error; } ``` ### Enhanced Logging (TableService Example) ```typescript console.log('TABLESERVICE: createTableRecords called'); console.log('TABLESERVICE: table:', table); console.log('TABLESERVICE: records:', JSON.stringify(records, null, 2)); try { console.log('TABLESERVICE: About to call ensureValidToken'); await this.client.ensureValidToken(); const result = await this.client.getHttpClient().post<T[]>(endpoint, records); console.log('TABLESERVICE: Operation completed successfully'); return result; } catch (error) { console.error('TABLESERVICE: Error type:', error?.constructor?.name); console.error('TABLESERVICE: Error message:', (error as Error)?.message); throw error; } ``` ## Authentication Flow Each service method follows this authentication pattern: 1. **Token Validation**: `await this.client.ensureValidToken()` 2. **HTTP Request**: Use the authenticated HTTP client 3. **Error Handling**: Catch and re-throw with context ```typescript public async serviceMethod(): Promise<ResultType> { try { // Step 1: Ensure valid authentication await this.client.ensureValidToken(); // Step 2: Make authenticated request const endpoint = '/api/endpoint'; const result = await this.client.getHttpClient().get<ResultType>(endpoint); // Step 3: Return result return result; } catch (error) { // Step 4: Handle errors with context console.error(`Error in ${this.constructor.name}.serviceMethod:`, error); throw error; } } ``` ## HTTP Method Usage ### GET Requests - Used for: Reading data, executing procedures with query params - Methods: `tableService.getTableRecords()`, `procedureService.executeProcedure()` ### POST Requests - Used for: Creating records, sending communications, executing procedures with body - Methods: `tableService.createTableRecords()`, `communicationService.createCommunication()` ### PUT Requests - Used for: Updating records, updating files - Methods: `tableService.updateTableRecords()`, `fileService.updateFile()` ### DELETE Requests - Used for: Deleting records and files - Methods: `tableService.deleteTableRecords()`, `fileService.deleteFile()` ### Form Data Requests - Used for: File uploads with metadata - Methods: `fileService.uploadFiles()`, `communicationService` with attachments ## Type Safety Features ### Generic Type Support ```typescript // Service methods are generic public async getTableRecords<T>(table: string, params?: TableQueryParams): Promise<T[]> // Usage with type safety const contacts = await service.getTableRecords<Contact>('Contacts', params); // TypeScript knows contacts is Contact[] ``` ### Interface Constraints ```typescript // Records must extend TableRecord interface public async createTableRecords<T extends TableRecord = TableRecord>( table: string, records: T[] ): Promise<T[]> ``` ### Parameter Type Safety ```typescript // Parameters use well-defined interfaces export interface TableQueryParams { $select?: string; $filter?: string; $orderby?: string; // ... other parameters } ``` ## Performance Considerations ### Batch Operations ```typescript // Services support batch operations for efficiency const multipleRecords = await tableService.createTableRecords('Contacts', [ { First_Name: 'John', Last_Name: 'Doe' }, { First_Name: 'Jane', Last_Name: 'Smith' }, { First_Name: 'Bob', Last_Name: 'Johnson' } ]); ``` ### Query Optimization ```typescript // Use $select to limit returned columns const contacts = await tableService.getTableRecords('Contacts', { $select: 'Contact_ID,Display_Name', // Only fetch needed columns $filter: 'Contact_Status_ID=1', // Filter server-side $top: 100 // Limit results }); ``` ### File Handling ```typescript // Upload multiple files in single request const files = await fileService.uploadFiles('Contacts', recordId, [file1, file2, file3]); // Use unique ID for public file access (no auth required) const fileContent = await fileService.getFileContentByUniqueId(uniqueId); ``` ## Best Practices 1. **Always Check Authentication**: Call `ensureValidToken()` before requests 2. **Use Type Safety**: Specify generic types for better IDE support 3. **Handle Errors Gracefully**: Wrap service calls in try-catch blocks 4. **Log Operations**: Use consistent logging patterns for debugging 5. **Batch When Possible**: Use array parameters for bulk operations 6. **Optimize Queries**: Use `$select` and `$filter` to reduce data transfer 7. **Resource Management**: Properly dispose of file Blobs after use ## Integration Examples ### Multi-Service Workflow ```typescript class MembershipService { constructor(private provider: ministryPlatformProvider) {} async processNewMember(memberData: any, documents: File[]) { // Create contact record const [contact] = await this.provider.createTableRecords('Contacts', [memberData]); // Upload membership documents await this.provider.uploadFiles('Contacts', contact.Contact_ID, documents); // Execute membership procedure await this.provider.executeProcedure('api_ProcessMembership', { ContactId: contact.Contact_ID }); // Send welcome email await this.provider.sendMessage({ FromAddress: { DisplayName: 'Church', Address: 'info@church.com' }, ToAddresses: [{ DisplayName: contact.Display_Name, Address: contact.Email_Address }], Subject: 'Welcome!', Body: 'Welcome to our church family!' }); return contact; } } ``` This service architecture provides a clean separation of concerns while maintaining consistency and type safety across all Ministry Platform operations.