ministry-platform-provider
Version:
TypeScript client library for Ministry Platform API integration
415 lines (414 loc) • 19 kB
JavaScript
import { ministryPlatformProvider } from "./ministryPlatformProvider.js";
/**
* MPHelper - Main Public API for Ministry Platform Operations
*
* Provides a simplified, type-safe interface for all Ministry Platform functionality.
* Acts as a facade over the ministryPlatformProvider singleton, offering:
* - Simplified parameter handling
* - Type safety with generics
* - Comprehensive error handling and logging
* - Consistent API patterns across all operations
*
* This is the primary entry point for all Ministry Platform operations in the application.
*/
export class MPHelper {
provider; // Reference to the singleton provider instance
/**
* Creates a new MPHelper instance
* Gets the singleton provider instance for all operations
*/
constructor() {
this.provider = ministryPlatformProvider.getInstance();
}
// =================================================================
// TABLE SERVICE METHODS
// =================================================================
/**
* Retrieves records from any Ministry Platform table with comprehensive query capabilities
* @template T The type to deserialize the response records to
* @param params Configuration object containing table name and query parameters
* @param params.table - Name of the Ministry Platform table (e.g., 'Contacts', 'Contact_Log')
* @param params.select - Comma-separated list of columns to retrieve (e.g., 'Contact_ID,Display_Name,Email_Address')
* @param params.filter - WHERE clause filter (e.g., 'Contact_Status_ID=1 AND Last_Name LIKE "Smith%"')
* @param params.orderBy - ORDER BY clause (e.g., 'Last_Name,First_Name' or 'Contact_Date DESC')
* @param params.groupBy - GROUP BY clause for aggregation (e.g., 'Congregation_ID')
* @param params.having - HAVING clause for grouped results (e.g., 'COUNT(*) > 5')
* @param params.top - Maximum number of records to return (for pagination)
* @param params.skip - Number of records to skip (for pagination, use with top)
* @param params.distinct - Whether to remove duplicate records
* @param params.userId - User context for security and auditing
* @param params.globalFilterId - Apply domain global filter by ID
* @returns Promise resolving to array of records typed as T
* @throws Error if table doesn't exist, filter is invalid, or authentication fails
*
* @example
* // Get active contacts with basic information
* 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
* });
*
* @example
* // Get paginated results (second page of 50 records)
* const contactsPage2 = await mp.getTableRecords<Contact>({
* table: 'Contacts',
* filter: 'Contact_Status_ID=1',
* orderBy: 'Contact_ID',
* top: 50,
* skip: 50
* });
*/
async getTableRecords(params) {
// Destructure parameters for easier access
const { table, select, filter, orderBy, groupBy, having, top, skip, distinct, userId, globalFilterId } = params;
// Convert simplified parameters to Ministry Platform query format
const queryParams = {
$select: select,
$filter: filter,
$orderby: orderBy,
$groupby: groupBy,
$having: having,
$top: top,
$skip: skip,
$distinct: distinct,
$userId: userId,
$globalFilterId: globalFilterId,
};
// Delegate to provider with formatted parameters
return await this.provider.getTableRecords(table, queryParams);
}
/**
* Creates new records in the specified Ministry Platform table
* @template T The type of records being created (must extend TableRecord)
* @param table - Name of the Ministry Platform table where records will be created
* @param records - Array of record objects to be created (without IDs, which will be auto-generated)
* @param params - Optional parameters for the creation operation
* @param params.$select - Comma-separated list of columns to return in response
* @param params.$userId - User ID for auditing and security context
* @returns Promise resolving to array of created records with generated IDs
* @throws Error if table doesn't exist, records are invalid, or authentication fails
*
* @example
* // Create a single contact record
* const newContacts = await mp.createTableRecords('Contacts', [{
* First_Name: 'John',
* Last_Name: 'Doe',
* Email_Address: 'john.doe@example.com',
* Contact_Status_ID: 1
* }], {
* $select: 'Contact_ID,Display_Name',
* $userId: 1
* });
*
* @example
* // Create multiple contact log entries
* const contactLogs = await mp.createTableRecords('Contact_Log', [
* {
* Contact_ID: 12345,
* Contact_Date: new Date().toISOString(),
* Made_By: 1,
* Notes: 'Initial contact via phone'
* },
* {
* Contact_ID: 12346,
* Contact_Date: new Date().toISOString(),
* Made_By: 1,
* Notes: 'Follow-up email sent'
* }
* ]);
*/
async createTableRecords(table, records, params) {
// Enhanced logging for debugging and monitoring
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));
try {
console.log('MPHELPER: About to call provider.createTableRecords');
// Delegate to provider for actual creation
const result = await this.provider.createTableRecords(table, records, params);
console.log('MPHELPER: provider.createTableRecords completed successfully');
console.log('MPHELPER: Result from provider:', JSON.stringify(result, null, 2));
return result;
}
catch (error) {
// Comprehensive error logging for debugging
console.error('MPHELPER: Error in createTableRecords:');
console.error('MPHELPER: Error type:', error?.constructor?.name);
console.error('MPHELPER: Error message:', error?.message);
console.error('MPHELPER: Error stack:', error?.stack);
console.error('MPHELPER: Full error object:', error);
// Re-throw the original error to maintain error chain
throw error;
}
}
/**
* Updates existing records in the specified Ministry Platform table
* @template T The type of records being updated (must extend TableRecord)
* @param table - Name of the Ministry Platform table where records will be updated
* @param records - Array of record objects to update (must include primary key IDs)
* @param params - Optional parameters for the update operation
* @param params.$select - Comma-separated list of columns to return in response
* @param params.$userId - User ID for auditing and security context
* @param params.$allowCreate - Whether to create records if they don't exist (upsert functionality)
* @returns Promise resolving to array of updated records
* @throws Error if table doesn't exist, records are invalid, IDs not found, or authentication fails
*
* @example
* // 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'
* }]);
*
* @example
* // Update with upsert capability (create if doesn't exist)
* const upsertedRecords = await mp.updateTableRecords('Contact_Log', [{
* Contact_Log_ID: 67890,
* Notes: 'Updated notes after follow-up'
* }], {
* $allowCreate: true, // Will create if ID doesn't exist
* $userId: 1
* });
*/
async updateTableRecords(table, records, params) {
// Delegate to provider for update operation
return await this.provider.updateTableRecords(table, records, params);
}
/**
* Deletes multiple records from the specified Ministry Platform table
* @template T The type of records being deleted (must extend TableRecord)
* @param table - Name of the Ministry Platform table where records will be deleted
* @param ids - Array of primary key IDs corresponding to records to be deleted
* @param params - Optional parameters for the delete operation
* @param params.$select - Comma-separated list of columns to return for deleted records
* @param params.$userId - User ID for auditing and security context
* @returns Promise resolving to array of deleted records (as they existed before deletion)
* @throws Error if table doesn't exist, IDs not found, or authentication fails
*
* @example
* // Delete specific contact logs
* const deletedLogs = await mp.deleteTableRecords('Contact_Log', [67890, 67891], {
* $userId: 1
* });
*
* @example
* // Delete multiple contacts and get basic info back
* const deletedContacts = await mp.deleteTableRecords('Contacts', [12345, 12346], {
* $select: 'Contact_ID,Display_Name',
* $userId: 1
* });
*/
async deleteTableRecords(table, ids, params) {
// Delegate to provider for delete operation
return await this.provider.deleteTableRecords(table, ids, params);
}
// =================================================================
// DOMAIN SERVICE METHODS
// =================================================================
/**
* Retrieves basic information about the current Ministry Platform domain
* @returns Promise resolving to domain configuration and settings
* @throws Error if authentication fails or domain is inaccessible
*
* @example
* // Get domain configuration
* const domainInfo = await mp.getDomainInfo();
* console.log('Organization:', domainInfo.DisplayName);
* console.log('Time Zone:', domainInfo.TimeZoneName);
* console.log('Culture:', domainInfo.CultureName);
* console.log('MFA Enabled:', domainInfo.IsSmsMfaEnabled);
*/
async getDomainInfo() {
// Delegate to provider for domain information retrieval
return await this.provider.getDomainInfo();
}
/**
* Retrieves available global filters for the domain
* Global filters provide domain-wide data filtering capabilities
* @param params - Optional parameters for the global filters request
* @param params.$ignorePermissions - Whether to ignore user permissions when retrieving filters
* @param params.$userId - User context for permission-based filter access
* @returns Promise resolving to array of global filter items with keys and display values
* @throws Error if authentication fails or filters are inaccessible
*
* @example
* // Get all available global filters
* const filters = await mp.getGlobalFilters();
* filters.forEach(filter => {
* console.log(`Filter ${filter.Key}: ${filter.Value}`);
* });
*
* @example
* // Get filters for specific user context
* const userFilters = await mp.getGlobalFilters({
* $userId: 123
* });
*/
async getGlobalFilters(params) {
// Delegate to provider for global filters retrieval
return await this.provider.getGlobalFilters(params);
}
// =================================================================
// METADATA SERVICE METHODS
// =================================================================
/**
* Triggers an update of the metadata cache on all servers and in all applications
* Use this method after making schema changes or when experiencing metadata-related issues
* @returns Promise that resolves when metadata refresh is complete
* @throws Error if authentication fails or refresh operation is not permitted
*
* @example
* // Refresh metadata cache after schema changes
* await mp.refreshMetadata();
* console.log('Metadata cache refreshed successfully');
*/
async refreshMetadata() {
// Delegate to provider for metadata cache refresh
return await this.provider.refreshMetadata();
}
/**
* Retrieves the list of tables available to the current user with basic metadata
* Includes access level information and table descriptions
* @param search - Optional search term to filter tables by name (case-insensitive)
* @returns Promise resolving to array of table metadata objects
* @throws Error if authentication fails or metadata is inaccessible
*
* @example
* // Get all available tables
* const allTables = await mp.getTables();
* allTables.forEach(table => {
* console.log(`${table.Name}: ${table.AccessLevel}`);
* });
*
* @example
* // Search for contact-related tables
* const contactTables = await mp.getTables('contact');
* contactTables.forEach(table => {
* console.log(`Found: ${table.Name} - ${table.AccessLevel}`);
* });
*/
async getTables(search) {
// Delegate to provider for table metadata retrieval
return await this.provider.getTables(search);
}
// Procedure Service Methods
/**
* Returns the list of procedures available to the current user with basic metadata.
* @param search Optional search term to filter procedures
* @returns Promise with an array of ProcedureInfo objects
*/
async getProcedures(search) {
return await this.provider.getProcedures(search);
}
/**
* Executes the requested stored procedure retrieving parameters from the query string.
* @param procedure Stored procedure name
* @param params Query parameters to pass to the procedure
* @returns Promise with the procedure results
*/
async executeProcedure(procedure, params) {
return await this.provider.executeProcedure(procedure, params);
}
/**
* Executes the requested stored procedure with provided parameters in the request body.
* @param procedure Stored procedure name
* @param parameters Parameters to be used for calling stored procedure
* @returns Promise with the procedure results
*/
async executeProcedureWithBody(procedure, parameters) {
return await this.provider.executeProcedureWithBody(procedure, parameters);
}
// Communication Service Methods
/**
* Creates a new communication, immediately renders it and schedules for delivery.
* Supports both simple JSON communication and multipart form data with file attachments.
* @param communication Communication information object
* @param attachments Optional array of file attachments
* @returns Promise with the created communication
*/
async createCommunication(communication, attachments) {
return await this.provider.createCommunication(communication, attachments);
}
/**
* Creates email messages from the provided information and immediately schedules them for delivery.
* Supports both simple JSON message and multipart form data with file attachments.
* @param message Message information object
* @param attachments Optional array of file attachments
* @returns Promise with the created communication
*/
async sendMessage(message, attachments) {
return await this.provider.sendMessage(message, attachments);
}
// File Service Methods
/**
* Returns the metadata (descriptions) of the files attached to the specified record.
* @param params Object containing table name, record ID, and optional parameters
* @returns Promise with an array of file descriptions
*/
async getFilesByRecord(params) {
const { table, recordId, defaultOnly } = params;
return await this.provider.getFilesByRecord(table, recordId, defaultOnly);
}
/**
* Uploads and attaches multiple files to the specified record.
* @param params Object containing table name, record ID, files, and optional upload parameters
* @returns Promise with an array of uploaded file descriptions
*/
async uploadFiles(params) {
const { table, recordId, files, uploadParams } = params;
return await this.provider.uploadFiles(table, recordId, files, uploadParams);
}
/**
* Updates the content and/or metadata of the file corresponding to provided identifier.
* @param params Object containing file ID, optional new file content, and update parameters
* @returns Promise with the updated file description
*/
async updateFile(params) {
const { fileId, file, updateParams } = params;
return await this.provider.updateFile(fileId, file, updateParams);
}
/**
* Deletes the file corresponding to provided identifier.
* @param params Object containing file ID and optional user ID for auditing
* @returns Promise that resolves when file is deleted
*/
async deleteFile(params) {
const { fileId, userId } = params;
return await this.provider.deleteFile(fileId, userId);
}
/**
* Returns the content of the file corresponding to provided globally unique identifier.
* This method does NOT require authentication.
* @param params Object containing unique file ID and optional thumbnail flag
* @returns Promise with the file content as a Blob
*/
async getFileContentByUniqueId(params) {
const { uniqueFileId, thumbnail } = params;
return await this.provider.getFileContentByUniqueId(uniqueFileId, thumbnail);
}
/**
* Returns the file metadata (description) corresponding to provided database identifier.
* @param params Object containing file ID
* @returns Promise with the file description
*/
async getFileMetadata(params) {
const { fileId } = params;
return await this.provider.getFileMetadata(fileId);
}
/**
* Returns the file metadata (description) corresponding to provided globally unique identifier.
* @param params Object containing unique file ID
* @returns Promise with the file description
*/
async getFileMetadataByUniqueId(params) {
const { uniqueFileId } = params;
return await this.provider.getFileMetadataByUniqueId(uniqueFileId);
}
}