ministry-platform-provider
Version:
TypeScript client library for Ministry Platform API integration
772 lines (568 loc) • 21.4 kB
Markdown
# ministryPlatformProvider - Core Provider Singleton
## Overview
The `ministryPlatformProvider` class serves as the central orchestrator for all Ministry Platform operations. It implements the Singleton pattern to ensure a single, shared instance across the application and coordinates between the core client and specialized service classes.
## Class Structure
```typescript
export class ministryPlatformProvider {
private static instance: ministryPlatformProvider;
private client: MinistryPlatformClient;
private tableService: TableService;
private procedureService: ProcedureService;
private communicationService: CommunicationService;
private metadataService: MetadataService;
private domainService: DomainService;
private fileService: FileService;
private constructor()
public static getInstance(): ministryPlatformProvider
}
```
## Singleton Pattern Implementation
### Private Constructor
```typescript
private constructor() {
this.client = new MinistryPlatformClient();
this.tableService = new TableService(this.client);
this.procedureService = new ProcedureService(this.client);
this.communicationService = new CommunicationService(this.client);
this.metadataService = new MetadataService(this.client);
this.domainService = new DomainService(this.client);
this.fileService = new FileService(this.client);
}
```
**Initialization Process**:
1. Creates the core `MinistryPlatformClient` instance
2. Instantiates all service classes with the shared client
3. Ensures single point of authentication and configuration
### Static getInstance Method
```typescript
public static getInstance(): ministryPlatformProvider {
if (!this.instance) {
this.instance = new ministryPlatformProvider();
}
return this.instance;
}
```
**Benefits of Singleton Pattern**:
- **Single Authentication Context**: All services share the same token
- **Resource Efficiency**: One client instance serves all operations
- **Consistent Configuration**: Shared environment settings across services
- **State Management**: Centralized state for connections and cache
## Service Delegation Architecture
The provider acts as a facade, delegating operations to specialized service classes:
```
MPHelper
↓
ministryPlatformProvider (Singleton)
↓
┌─────────────────┬─────────────────┬─────────────────┐
│ TableService │ ProcedureService│CommunicationSvc │
│ DomainService │ MetadataService │ FileService │
└─────────────────┴─────────────────┴─────────────────┘
↓
MinistryPlatformClient
↓
HttpClient
```
## Domain Service Methods
### getDomainInfo
```typescript
public async getDomainInfo(): Promise<DomainInfo>
```
**Purpose**: Retrieves basic domain configuration information
**Delegation**: Calls `this.domainService.getDomainInfo()`
**Returns**: Domain configuration object with settings like time zone, culture, and security options
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const domainInfo = await provider.getDomainInfo();
console.log('Domain Name:', domainInfo.DisplayName);
console.log('Time Zone:', domainInfo.TimeZoneName);
console.log('MFA Enabled:', domainInfo.IsSmsMfaEnabled);
```
### getGlobalFilters
```typescript
public async getGlobalFilters(params?: GlobalFilterParams): Promise<GlobalFilterItem[]>
```
**Purpose**: Retrieves available global filters for data filtering
**Parameters**:
- `params`: Optional parameters for filter configuration
**Delegation**: Calls `this.domainService.getGlobalFilters(params)`
**Returns**: Array of global filter items with keys and display values
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const filters = await provider.getGlobalFilters();
filters.forEach(filter => {
console.log(`Filter ${filter.Key}: ${filter.Value}`);
});
```
## Metadata Service Methods
### refreshMetadata
```typescript
public async refreshMetadata(): Promise<void>
```
**Purpose**: Triggers metadata cache refresh across all servers
**Delegation**: Calls `this.metadataService.refreshMetadata()`
**Use Cases**:
- After schema changes
- When table definitions are updated
- For troubleshooting metadata issues
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
await provider.refreshMetadata();
console.log('Metadata cache refreshed successfully');
```
### getTables
```typescript
public async getTables(search?: string): Promise<TableMetadata[]>
```
**Purpose**: Retrieves list of available tables with metadata
**Parameters**:
- `search`: Optional search term to filter table names
**Delegation**: Calls `this.metadataService.getTables(search)`
**Returns**: Array of table metadata objects including access levels and permissions
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
// Get all tables
const allTables = await provider.getTables();
// Search for contact-related tables
const contactTables = await provider.getTables('contact');
contactTables.forEach(table => {
console.log(`${table.Name}: ${table.AccessLevel}`);
});
```
## Table Service Methods
### getTableRecords
```typescript
public async getTableRecords<T>(table: string, params?: TableQueryParams): Promise<T[]>
```
**Purpose**: Retrieves records from any Ministry Platform table
**Parameters**:
- `table`: Table name
- `params`: Query parameters for filtering, sorting, pagination
**Delegation**: Calls `this.tableService.getTableRecords<T>(table, params)`
**Returns**: Array of typed records
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const contacts = await provider.getTableRecords<Contact>('Contacts', {
$filter: 'Contact_Status_ID=1',
$select: 'Contact_ID,Display_Name,Email_Address',
$orderby: 'Last_Name,First_Name',
$top: 100
});
```
### 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`: Target table name
- `records`: Array of records to create
- `params`: Optional selection and user context parameters
**Delegation**: Calls `this.tableService.createTableRecords<T>(table, records, params)`
**Returns**: Array of created records with generated IDs
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const newContacts = await provider.createTableRecords('Contacts', [
{
First_Name: 'John',
Last_Name: 'Doe',
Email_Address: 'john@example.com',
Contact_Status_ID: 1
}
], {
$select: 'Contact_ID,Display_Name',
$userId: 1
});
```
### 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`: Target table name
- `records`: Array of records to update (must include IDs)
- `params`: Optional parameters including upsert capability
**Delegation**: Calls `this.tableService.updateTableRecords<T>(table, records, params)`
**Returns**: Array of updated records
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const updatedContacts = await provider.updateTableRecords('Contacts', [
{
Contact_ID: 12345,
Mobile_Phone: '555-0123',
Email_Address: 'newemail@example.com'
}
], {
$allowCreate: false,
$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`: Target table name
- `ids`: Array of record IDs to delete
- `params`: Optional selection and user context parameters
**Delegation**: Calls `this.tableService.deleteTableRecords<T>(table, ids, params)`
**Returns**: Array of deleted records
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const deletedLogs = await provider.deleteTableRecords('Contact_Log',
[67890, 67891, 67892],
{
$userId: 1
}
);
```
## Procedure Service Methods
### getProcedures
```typescript
public async getProcedures(search?: string): Promise<ProcedureInfo[]>
```
**Purpose**: Retrieves list of available stored procedures
**Parameters**:
- `search`: Optional search term to filter procedure names
**Delegation**: Calls `this.procedureService.getProcedures(search)`
**Returns**: Array of procedure metadata with parameter information
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const apiProcedures = await provider.getProcedures('api_');
apiProcedures.forEach(proc => {
console.log(`Procedure: ${proc.Name}`);
proc.Parameters.forEach(param => {
console.log(` ${param.Name}: ${param.DataType} (${param.Direction})`);
});
});
```
### 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 string parameters
**Delegation**: Calls `this.procedureService.executeProcedure(procedure, params)`
**Returns**: Array of result sets
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const results = await provider.executeProcedure('api_GetContactsByHousehold', {
HouseholdId: 123,
IncludeInactive: 'false'
});
const householdMembers = 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
**Delegation**: Calls `this.procedureService.executeProcedureWithBody(procedure, parameters)`
**Returns**: Array of result sets
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const results = await provider.executeProcedureWithBody('api_ComplexSearch', {
SearchCriteria: {
FirstName: 'John',
LastName: 'Doe',
MinAge: 18,
MaxAge: 65
},
Options: {
IncludeInactive: false,
SortBy: 'LastName'
}
});
```
## Communication Service Methods
### 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
**Delegation**: Calls `this.communicationService.createCommunication(communication, attachments)`
**Returns**: Created communication object
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const communication = await provider.createCommunication({
AuthorUserId: 1,
FromContactId: 1,
ReplyToContactId: 1,
Subject: 'Weekly Newsletter',
Body: '<h1>This Week at Church</h1>',
CommunicationType: 'Email',
Contacts: [12345, 67890],
IsBulkEmail: true,
SendToContactParents: false,
StartDate: new Date().toISOString()
}, [attachmentFile]);
```
### 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
**Delegation**: Calls `this.communicationService.sendMessage(message, attachments)`
**Returns**: Created communication object
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const message = await provider.sendMessage({
FromAddress: { DisplayName: 'Pastor', Address: 'pastor@church.com' },
ToAddresses: [{ DisplayName: 'Member', Address: 'member@example.com' }],
Subject: 'Personal Follow-up',
Body: 'Thank you for visiting our church!'
});
```
## File Service Methods
### getFilesByRecord
```typescript
public async getFilesByRecord(
table: string,
recordId: number,
defaultOnly?: boolean
): Promise<FileDescription[]>
```
**Purpose**: Retrieves files attached to a specific record
**Parameters**:
- `table`: Table name containing the record
- `recordId`: Record ID to get files for
- `defaultOnly`: Return only default files
**Delegation**: Calls `this.fileService.getFilesByRecord(table, recordId, defaultOnly)`
**Returns**: Array of file descriptions
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const contactFiles = await provider.getFilesByRecord('Contacts', 12345);
const defaultImage = await provider.getFilesByRecord('Contacts', 12345, true);
```
### uploadFiles
```typescript
public async uploadFiles(
table: string,
recordId: number,
files: File[],
params?: FileUploadParams
): Promise<FileDescription[]>
```
**Purpose**: Uploads multiple files to a record
**Parameters**:
- `table`: Target table name
- `recordId`: Record ID to attach files to
- `files`: Array of File objects
- `params`: Optional upload parameters
**Delegation**: Calls `this.fileService.uploadFiles(table, recordId, files, params)`
**Returns**: Array of uploaded file descriptions
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const uploadedFiles = await provider.uploadFiles('Contacts', 12345, [file1, file2], {
description: 'Profile documents',
isDefaultImage: true,
userId: 1
});
```
### updateFile
```typescript
public async updateFile(
fileId: number,
file?: File,
params?: FileUpdateParams
): Promise<FileDescription>
```
**Purpose**: Updates file content and/or metadata
**Parameters**:
- `fileId`: File ID to update
- `file`: Optional new file content
- `params`: Optional update parameters
**Delegation**: Calls `this.fileService.updateFile(fileId, file, params)`
**Returns**: Updated file description
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const updatedFile = await provider.updateFile(67890, newFile, {
description: 'Updated document',
isDefaultImage: false
});
```
### deleteFile
```typescript
public async deleteFile(
fileId: number,
userId?: number
): Promise<void>
```
**Purpose**: Deletes a file
**Parameters**:
- `fileId`: File ID to delete
- `userId`: Optional user ID for auditing
**Delegation**: Calls `this.fileService.deleteFile(fileId, userId)`
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
await provider.deleteFile(67890, 1);
```
### getFileContentByUniqueId
```typescript
public async getFileContentByUniqueId(
uniqueFileId: string,
thumbnail?: boolean
): Promise<Blob>
```
**Purpose**: Downloads file content by unique ID (public access)
**Parameters**:
- `uniqueFileId`: Globally unique file identifier
- `thumbnail`: Return thumbnail version
**Delegation**: Calls `this.fileService.getFileContentByUniqueId(uniqueFileId, thumbnail)`
**Returns**: File content as Blob
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const fileContent = await provider.getFileContentByUniqueId('abc123-def456');
const thumbnailContent = await provider.getFileContentByUniqueId('abc123-def456', true);
```
### getFileMetadata
```typescript
public async getFileMetadata(fileId: number): Promise<FileDescription>
```
**Purpose**: Retrieves file metadata by database ID
**Parameters**:
- `fileId`: Database file ID
**Delegation**: Calls `this.fileService.getFileMetadata(fileId)`
**Returns**: File description object
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const metadata = await provider.getFileMetadata(67890);
console.log(`File: ${metadata.FileName} (${metadata.FileSize} bytes)`);
```
### getFileMetadataByUniqueId
```typescript
public async getFileMetadataByUniqueId(uniqueFileId: string): Promise<FileDescription>
```
**Purpose**: Retrieves file metadata by unique ID
**Parameters**:
- `uniqueFileId`: Globally unique file identifier
**Delegation**: Calls `this.fileService.getFileMetadataByUniqueId(uniqueFileId)`
**Returns**: File description object
**Example Usage**:
```typescript
const provider = ministryPlatformProvider.getInstance();
const metadata = await provider.getFileMetadataByUniqueId('abc123-def456');
```
## Usage Patterns
### Service Coordination
```typescript
const provider = ministryPlatformProvider.getInstance();
// Get domain configuration
const domainInfo = await provider.getDomainInfo();
// Get available tables based on permissions
const tables = await provider.getTables();
// Create records in multiple tables with shared authentication
const contacts = await provider.createTableRecords('Contacts', contactData);
const logs = await provider.createTableRecords('Contact_Log', logData);
// Send communication to new contacts
await provider.createCommunication({
// ... communication config
Contacts: contacts.map(c => c.Contact_ID)
});
```
### Error Handling with Provider
```typescript
const provider = ministryPlatformProvider.getInstance();
try {
// Multiple operations with shared error context
const contacts = await provider.getTableRecords('Contacts', params);
const procedures = await provider.getProcedures('api_');
const files = await provider.uploadFiles('Contacts', 12345, fileList);
console.log('All operations completed successfully');
} catch (error) {
console.error('Provider operation failed:', error);
// All operations share the same authentication and error handling
}
```
### Batch Operations Across Services
```typescript
const provider = ministryPlatformProvider.getInstance();
// Complex workflow using multiple services
async function processNewMember(memberData: any, documents: File[]) {
// Create contact record
const [contact] = await provider.createTableRecords('Contacts', [memberData]);
// Upload documents
await provider.uploadFiles('Contacts', contact.Contact_ID, documents);
// Execute membership procedure
await provider.executeProcedure('api_ProcessNewMember', {
ContactId: contact.Contact_ID
});
// Send welcome communication
await provider.sendMessage({
FromAddress: { DisplayName: 'Church', Address: 'info@church.com' },
ToAddresses: [{ DisplayName: contact.Display_Name, Address: contact.Email_Address }],
Subject: 'Welcome to our church family!',
Body: 'We are excited to have you join us!'
});
return contact;
}
```
## Singleton Benefits
1. **Shared Authentication**: Single token lifecycle across all operations
2. **Resource Efficiency**: One client instance serves all services
3. **Consistent Configuration**: Shared environment settings and base URL
4. **State Management**: Centralized connection and cache management
5. **Error Handling**: Consistent error handling patterns across services
## Best Practices
1. **Use getInstance()**: Always get the provider through the singleton method
2. **Service Delegation**: Use the provider methods rather than direct service access
3. **Error Handling**: Implement try-catch blocks around provider operations
4. **Type Safety**: Specify generic types for table operations
5. **Resource Management**: Let the provider manage service lifecycle
The `ministryPlatformProvider` serves as the central hub for all Ministry Platform operations, providing a clean, consistent interface while managing the complexity of service coordination and authentication.