UNPKG

ministry-platform-provider

Version:

TypeScript client library for Ministry Platform API integration

798 lines (633 loc) 20.1 kB
# MPHelper - Main Public API ## Overview The `MPHelper` class is the primary public interface for all Ministry Platform operations. It provides a simplified, type-safe API that abstracts the complexity of the underlying provider system while maintaining full access to Ministry Platform functionality. ## Class Structure ```typescript export class MPHelper { private provider: ministryPlatformProvider; constructor() { this.provider = ministryPlatformProvider.getInstance(); } } ``` ## Design Philosophy The `MPHelper` follows these principles: 1. **Simplified API**: Complex parameter objects are flattened into simple method parameters 2. **Type Safety**: All methods are generic and strongly typed 3. **Consistent Interface**: Similar operations follow the same patterns 4. **Developer Experience**: Comprehensive logging and error handling 5. **Abstraction**: Hides provider complexity while maintaining full functionality ## Table Operations ### getTableRecords ```typescript public async getTableRecords<T>(params: { table: string, select?: string, filter?: string, orderBy?: string, groupBy?: string, having?: string, top?: number, skip?: number, distinct?: boolean, userId?: number, globalFilterId?: number }): Promise<T[]> ``` **Purpose**: Retrieves records from any Ministry Platform table with comprehensive query capabilities **Parameters**: - `table`: Table name (e.g., 'Contacts', 'Contact_Log') - `select`: Column list (e.g., 'Contact_ID,Display_Name,Email_Address') - `filter`: WHERE clause (e.g., 'Contact_Status_ID=1') - `orderBy`: ORDER BY clause (e.g., 'Last_Name,First_Name') - `groupBy`: GROUP BY clause - `having`: HAVING clause for grouped results - `top`: Maximum number of records to return - `skip`: Number of records to skip (for pagination) - `distinct`: Remove duplicate records - `userId`: User context for security - `globalFilterId`: Apply global filter **Returns**: Array of records typed as `T` **Example Usage**: ```typescript // Get active contacts with basic info const contacts = await mp.getTableRecords<Contact>({ table: 'Contacts', select: 'Contact_ID,Display_Name,Email_Address,Mobile_Phone', filter: 'Contact_Status_ID=1', orderBy: 'Last_Name,First_Name', top: 100 }); // Get contact logs for a specific person const logs = await mp.getTableRecords<ContactLog>({ table: 'Contact_Log', filter: 'Contact_ID=12345', orderBy: 'Contact_Date DESC', top: 50 }); ``` **Query Parameter Mapping**: ```typescript // Internal mapping to Ministry Platform format const queryParams: TableQueryParams = { $select: params.select, $filter: params.filter, $orderby: params.orderBy, $groupby: params.groupBy, $having: params.having, $top: params.top, $skip: params.skip, $distinct: params.distinct, $userId: params.userId, $globalFilterId: params.globalFilterId, }; ``` ### createTableRecords ```typescript public async createTableRecords<T extends TableRecord = TableRecord>( table: string, records: T[], params?: Pick<TableQueryParams, '$select' | '$userId'> ): Promise<T[]> ``` **Purpose**: Creates new records in the specified table **Parameters**: - `table`: Table name where records will be created - `records`: Array of record objects to create - `params`: Optional parameters for selection and user context **Returns**: Array of created records with generated IDs **Example Usage**: ```typescript // Create a new contact const newContacts = await mp.createTableRecords('Contacts', [{ First_Name: 'John', Last_Name: 'Doe', Email_Address: 'john.doe@example.com', Contact_Status_ID: 1 }]); // Create multiple contact logs const contactLogs = await mp.createTableRecords('Contact_Log', [ { Contact_ID: 12345, Contact_Date: new Date().toISOString(), Made_By: 1, Notes: 'Initial contact made via phone', Contact_Log_Type_ID: 1 }, { Contact_ID: 12346, Contact_Date: new Date().toISOString(), Made_By: 1, Notes: 'Follow-up email sent', Contact_Log_Type_ID: 2 } ]); ``` **Enhanced Logging**: ```typescript console.log('MPHELPER: createTableRecords called'); console.log('MPHELPER: table:', table); console.log('MPHELPER: records:', JSON.stringify(records, null, 2)); console.log('MPHELPER: params:', JSON.stringify(params, null, 2)); ``` ### updateTableRecords ```typescript public async updateTableRecords<T extends TableRecord = TableRecord>( table: string, records: T[], params?: Pick<TableQueryParams, '$select' | '$userId' | '$allowCreate'> ): Promise<T[]> ``` **Purpose**: Updates existing records in the specified table **Parameters**: - `table`: Table name where records will be updated - `records`: Array of record objects with IDs to update - `params`: Optional parameters including create permission **Returns**: Array of updated records **Example Usage**: ```typescript // Update contact information const updatedContacts = await mp.updateTableRecords('Contacts', [{ Contact_ID: 12345, First_Name: 'John', Last_Name: 'Smith', // Changed last name Email_Address: 'john.smith@example.com', Mobile_Phone: '555-0123' }]); // Update with create permission (upsert) const upsertedRecords = await mp.updateTableRecords('Contact_Log', [ { Contact_Log_ID: 67890, Notes: 'Updated notes' } ], { $allowCreate: true, // Will create if doesn't exist $userId: 1 }); ``` ### deleteTableRecords ```typescript public async deleteTableRecords<T extends TableRecord = TableRecord>( table: string, ids: number[], params?: Pick<TableQueryParams, '$select' | '$userId'> ): Promise<T[]> ``` **Purpose**: Deletes multiple records from the specified table **Parameters**: - `table`: Table name where records will be deleted - `ids`: Array of record IDs to delete - `params`: Optional parameters for selection and user context **Returns**: Array of deleted records **Example Usage**: ```typescript // Delete specific contact logs const deletedLogs = await mp.deleteTableRecords('Contact_Log', [67890, 67891], { $userId: 1 }); // Delete inactive contacts const inactiveContacts = await mp.getTableRecords<Contact>({ table: 'Contacts', filter: 'Contact_Status_ID=3', select: 'Contact_ID' }); const contactIds = inactiveContacts.map(c => c.Contact_ID); const deletedContacts = await mp.deleteTableRecords('Contacts', contactIds); ``` ## Domain Operations ### getDomainInfo ```typescript public async getDomainInfo(): Promise<DomainInfo> ``` **Purpose**: Retrieves basic information about the current Ministry Platform domain **Returns**: Domain configuration object **Example Usage**: ```typescript const domainInfo = await mp.getDomainInfo(); console.log('Domain:', domainInfo.DisplayName); console.log('Time Zone:', domainInfo.TimeZoneName); console.log('Culture:', domainInfo.CultureName); ``` ### getGlobalFilters ```typescript public async getGlobalFilters(params?: GlobalFilterParams): Promise<GlobalFilterItem[]> ``` **Purpose**: Retrieves available global filters for the domain **Parameters**: - `params`: Optional parameters for filter request **Returns**: Array of global filter items **Example Usage**: ```typescript const filters = await mp.getGlobalFilters(); filters.forEach(filter => { console.log(`Filter ${filter.Key}: ${filter.Value}`); }); ``` ## Metadata Operations ### refreshMetadata ```typescript public async refreshMetadata(): Promise<void> ``` **Purpose**: Triggers metadata cache refresh on all servers **Example Usage**: ```typescript // Refresh metadata after schema changes await mp.refreshMetadata(); console.log('Metadata cache refreshed'); ``` ### getTables ```typescript public async getTables(search?: string): Promise<TableMetadata[]> ``` **Purpose**: Retrieves list of available tables with metadata **Parameters**: - `search`: Optional search term to filter tables **Returns**: Array of table metadata objects **Example Usage**: ```typescript // Get all tables const allTables = await mp.getTables(); // Search for contact-related tables const contactTables = await mp.getTables('contact'); contactTables.forEach(table => { console.log(`Table: ${table.Name} - ${table.AccessLevel}`); }); ``` ## Procedure Operations ### getProcedures ```typescript public async getProcedures(search?: string): Promise<ProcedureInfo[]> ``` **Purpose**: Retrieves list of available stored procedures **Parameters**: - `search`: Optional search term to filter procedures **Returns**: Array of procedure metadata objects **Example Usage**: ```typescript // Get all procedures const procedures = await mp.getProcedures(); // Search for API procedures const apiProcedures = await mp.getProcedures('api_'); apiProcedures.forEach(proc => { console.log(`Procedure: ${proc.Name}`); proc.Parameters.forEach(param => { console.log(` - ${param.Name}: ${param.DataType}`); }); }); ``` ### executeProcedure ```typescript public async executeProcedure( procedure: string, params?: QueryParams ): Promise<unknown[][]> ``` **Purpose**: Executes stored procedure with query string parameters **Parameters**: - `procedure`: Stored procedure name - `params`: Query parameters object **Returns**: Array of result sets **Example Usage**: ```typescript // Execute procedure with parameters const results = await mp.executeProcedure('api_GetContactsByHousehold', { HouseholdId: 123, IncludeInactive: false }); // Process first result set const contacts = results[0] as Contact[]; ``` ### executeProcedureWithBody ```typescript public async executeProcedureWithBody( procedure: string, parameters: Record<string, unknown> ): Promise<unknown[][]> ``` **Purpose**: Executes stored procedure with request body parameters **Parameters**: - `procedure`: Stored procedure name - `parameters`: Request body parameters object **Returns**: Array of result sets **Example Usage**: ```typescript // Execute complex procedure with body const results = await mp.executeProcedureWithBody('api_ComplexSearch', { SearchCriteria: { FirstName: 'John', LastName: 'Doe', Email: 'john@example.com' }, Options: { IncludeInactive: false, MaxResults: 100 } }); ``` ## Communication Operations ### createCommunication ```typescript public async createCommunication( communication: CommunicationInfo, attachments?: File[] ): Promise<Communication> ``` **Purpose**: Creates and schedules a new communication **Parameters**: - `communication`: Communication configuration object - `attachments`: Optional file attachments **Returns**: Created communication object **Example Usage**: ```typescript // Create email communication const communication = await mp.createCommunication({ AuthorUserId: 1, FromContactId: 1, ReplyToContactId: 1, Subject: 'Weekly Newsletter', Body: '<h1>This Week at Church</h1><p>Join us for...</p>', CommunicationType: 'Email', Contacts: [12345, 67890, 13579], IsBulkEmail: true, SendToContactParents: false, StartDate: new Date().toISOString() }); ``` ### sendMessage ```typescript public async sendMessage( message: MessageInfo, attachments?: File[] ): Promise<Communication> ``` **Purpose**: Sends a direct message immediately **Parameters**: - `message`: Message configuration object - `attachments`: Optional file attachments **Returns**: Created communication object **Example Usage**: ```typescript // Send direct email const message = await mp.sendMessage({ FromAddress: { DisplayName: 'Pastor John', Address: 'pastor@church.com' }, ToAddresses: [{ DisplayName: 'John Doe', Address: 'john@example.com' }], Subject: 'Thank you for visiting', Body: 'It was great to meet you last Sunday!' }); ``` ## File Operations ### getFilesByRecord ```typescript public async getFilesByRecord(params: { table: string, recordId: number, defaultOnly?: boolean }): Promise<FileDescription[]> ``` **Purpose**: Retrieves files attached to a specific record **Parameters**: - `table`: Table name - `recordId`: Record ID - `defaultOnly`: Return only default files **Returns**: Array of file descriptions **Example Usage**: ```typescript // Get all files for a contact const files = await mp.getFilesByRecord({ table: 'Contacts', recordId: 12345 }); // Get only default image const defaultImage = await mp.getFilesByRecord({ table: 'Contacts', recordId: 12345, defaultOnly: true }); ``` ### uploadFiles ```typescript public async uploadFiles(params: { table: string, recordId: number, files: File[], uploadParams?: FileUploadParams }): Promise<FileDescription[]> ``` **Purpose**: Uploads multiple files to a record **Parameters**: - `table`: Table name - `recordId`: Record ID - `files`: Array of File objects - `uploadParams`: Optional upload configuration **Returns**: Array of uploaded file descriptions **Example Usage**: ```typescript // Upload profile photos const uploadedFiles = await mp.uploadFiles({ table: 'Contacts', recordId: 12345, files: [profilePhoto, backgroundCheck], uploadParams: { description: 'Profile documentation', isDefaultImage: true, userId: 1 } }); ``` ### updateFile ```typescript public async updateFile(params: { fileId: number, file?: File, updateParams?: FileUpdateParams }): Promise<FileDescription> ``` **Purpose**: Updates file content and/or metadata **Parameters**: - `fileId`: File ID to update - `file`: Optional new file content - `updateParams`: Optional update configuration **Returns**: Updated file description **Example Usage**: ```typescript // Update file metadata const updatedFile = await mp.updateFile({ fileId: 67890, updateParams: { description: 'Updated profile photo', isDefaultImage: true } }); // Replace file content const replacedFile = await mp.updateFile({ fileId: 67890, file: newProfilePhoto, updateParams: { fileName: 'new-profile.jpg' } }); ``` ### deleteFile ```typescript public async deleteFile(params: { fileId: number, userId?: number }): Promise<void> ``` **Purpose**: Deletes a file **Parameters**: - `fileId`: File ID to delete - `userId`: Optional user ID for auditing **Example Usage**: ```typescript // Delete file await mp.deleteFile({ fileId: 67890, userId: 1 }); ``` ### getFileContentByUniqueId ```typescript public async getFileContentByUniqueId(params: { uniqueFileId: string, thumbnail?: boolean }): Promise<Blob> ``` **Purpose**: Downloads file content by unique ID (no authentication required) **Parameters**: - `uniqueFileId`: Globally unique file identifier - `thumbnail`: Return thumbnail version **Returns**: File content as Blob **Example Usage**: ```typescript // Download file content const fileBlob = await mp.getFileContentByUniqueId({ uniqueFileId: 'abc123-def456-ghi789' }); // Create download link const url = URL.createObjectURL(fileBlob); const link = document.createElement('a'); link.href = url; link.download = 'downloaded-file.pdf'; link.click(); ``` ### getFileMetadata ```typescript public async getFileMetadata(params: { fileId: number }): Promise<FileDescription> ``` **Purpose**: Retrieves file metadata by database ID **Parameters**: - `fileId`: Database file ID **Returns**: File description object **Example Usage**: ```typescript // Get file metadata const metadata = await mp.getFileMetadata({ fileId: 67890 }); console.log(`File: ${metadata.FileName}`); console.log(`Size: ${metadata.FileSize} bytes`); console.log(`Type: ${metadata.FileExtension}`); ``` ### getFileMetadataByUniqueId ```typescript public async getFileMetadataByUniqueId(params: { uniqueFileId: string }): Promise<FileDescription> ``` **Purpose**: Retrieves file metadata by unique ID **Parameters**: - `uniqueFileId`: Globally unique file identifier **Returns**: File description object **Example Usage**: ```typescript // Get file metadata by unique ID const metadata = await mp.getFileMetadataByUniqueId({ uniqueFileId: 'abc123-def456-ghi789' }); ``` ## Error Handling The `MPHelper` includes comprehensive error handling with detailed logging: ```typescript try { console.log('MPHELPER: createTableRecords called'); console.log('MPHELPER: table:', table); console.log('MPHELPER: records:', JSON.stringify(records, null, 2)); const result = await this.provider.createTableRecords<T>(table, records, params); console.log('MPHELPER: Operation completed successfully'); return result; } catch (error) { console.error('MPHELPER: Error in createTableRecords:'); console.error('MPHELPER: Error type:', error?.constructor?.name); console.error('MPHELPER: Error message:', (error as Error)?.message); console.error('MPHELPER: Error stack:', (error as Error)?.stack); throw error; // Re-throw the original error } ``` ## Usage Patterns ### Type-Safe Operations ```typescript // Define your types interface MyContact { Contact_ID: number; Display_Name: string; Email_Address: string; Mobile_Phone: string; } // Use with type safety const mp = new MPHelper(); const contacts = await mp.getTableRecords<MyContact>({ table: 'Contacts', select: 'Contact_ID,Display_Name,Email_Address,Mobile_Phone', filter: 'Contact_Status_ID=1' }); // TypeScript knows the shape of contacts contacts.forEach(contact => { console.log(`${contact.Display_Name}: ${contact.Email_Address}`); }); ``` ### Batch Operations ```typescript // Create multiple records efficiently const newContacts = await mp.createTableRecords('Contacts', [ { First_Name: 'John', Last_Name: 'Doe', Email_Address: 'john@example.com' }, { First_Name: 'Jane', Last_Name: 'Smith', Email_Address: 'jane@example.com' }, { First_Name: 'Bob', Last_Name: 'Johnson', Email_Address: 'bob@example.com' } ]); // Update multiple records const updatedContacts = await mp.updateTableRecords('Contacts', newContacts.map(contact => ({ ...contact, Contact_Status_ID: 1 })) ); ``` ### Pagination ```typescript // Implement pagination const pageSize = 50; let page = 0; let allContacts: Contact[] = []; while (true) { const contacts = await mp.getTableRecords<Contact>({ table: 'Contacts', filter: 'Contact_Status_ID=1', orderBy: 'Contact_ID', top: pageSize, skip: page * pageSize }); if (contacts.length === 0) break; allContacts = allContacts.concat(contacts); page++; } ``` ## Best Practices 1. **Use Type Safety**: Always specify generic types for better IDE support 2. **Handle Errors**: Wrap operations in try-catch blocks 3. **Batch Operations**: Use arrays for bulk operations when possible 4. **Logging**: Enable console logging for debugging 5. **Parameter Validation**: Validate inputs before making API calls 6. **Resource Management**: Dispose of Blob objects after use The `MPHelper` class provides a powerful, type-safe interface for all Ministry Platform operations while maintaining simplicity and developer productivity.