@rachelallyson/planning-center-people-ts 2.14.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -7
- package/README.md +42 -4
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +14 -6
- package/dist/client.d.ts +33 -8
- package/dist/client.js +47 -22
- package/dist/core.d.ts +4 -2
- package/dist/core.js +3 -2
- package/dist/error-handling.d.ts +4 -4
- package/dist/error-handling.js +13 -2
- package/dist/error-scenarios.d.ts +11 -7
- package/dist/error-scenarios.js +26 -10
- package/dist/helpers.d.ts +124 -48
- package/dist/helpers.js +237 -93
- package/dist/index.d.ts +10 -8
- package/dist/index.js +31 -72
- package/dist/matching/matcher.d.ts +8 -4
- package/dist/matching/matcher.js +51 -58
- package/dist/matching/scoring.d.ts +9 -6
- package/dist/matching/scoring.js +18 -14
- package/dist/modules/campus.d.ts +31 -36
- package/dist/modules/campus.js +36 -49
- package/dist/modules/contacts.d.ts +33 -29
- package/dist/modules/contacts.js +36 -12
- package/dist/modules/fields.d.ts +39 -55
- package/dist/modules/fields.js +65 -105
- package/dist/modules/forms.d.ts +35 -24
- package/dist/modules/forms.js +41 -23
- package/dist/modules/households.d.ts +17 -19
- package/dist/modules/households.js +25 -34
- package/dist/modules/lists.d.ts +38 -28
- package/dist/modules/lists.js +62 -42
- package/dist/modules/notes.d.ts +32 -30
- package/dist/modules/notes.js +40 -52
- package/dist/modules/people.d.ts +83 -71
- package/dist/modules/people.js +323 -172
- package/dist/modules/reports.d.ts +18 -32
- package/dist/modules/reports.js +28 -40
- package/dist/modules/service-time.d.ts +19 -24
- package/dist/modules/service-time.js +28 -28
- package/dist/modules/workflows.d.ts +42 -47
- package/dist/modules/workflows.js +52 -53
- package/dist/performance.d.ts +14 -10
- package/dist/performance.js +61 -25
- package/dist/testing/recorder.js +11 -2
- package/dist/testing/simple-builders.d.ts +6 -4
- package/dist/testing/simple-builders.js +36 -49
- package/dist/testing/types.d.ts +4 -0
- package/dist/types/api-options.d.ts +380 -0
- package/dist/types/api-options.js +6 -0
- package/dist/types/client.d.ts +4 -2
- package/dist/types/client.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/people.d.ts +61 -9
- package/package.json +7 -7
- package/dist/core/http.d.ts +0 -56
- package/dist/core/http.js +0 -360
- package/dist/core/pagination.d.ts +0 -34
- package/dist/core/pagination.js +0 -178
- package/dist/people/contacts.d.ts +0 -43
- package/dist/people/contacts.js +0 -122
- package/dist/people/core.d.ts +0 -28
- package/dist/people/core.js +0 -69
- package/dist/people/fields.d.ts +0 -68
- package/dist/people/fields.js +0 -305
- package/dist/people/households.d.ts +0 -15
- package/dist/people/households.js +0 -31
- package/dist/people/index.d.ts +0 -8
- package/dist/people/index.js +0 -25
- package/dist/people/lists.d.ts +0 -34
- package/dist/people/lists.js +0 -48
- package/dist/people/notes.d.ts +0 -30
- package/dist/people/notes.js +0 -37
- package/dist/people/organization.d.ts +0 -12
- package/dist/people/organization.js +0 -15
- package/dist/people/workflows.d.ts +0 -37
- package/dist/people/workflows.js +0 -75
|
@@ -7,94 +7,91 @@ exports.WorkflowsModule = void 0;
|
|
|
7
7
|
const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
|
|
8
8
|
class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
9
9
|
/**
|
|
10
|
-
* Get all workflows
|
|
10
|
+
* Get all workflows across all pages
|
|
11
11
|
*/
|
|
12
12
|
async getAll(options = {}) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
if (options.include) {
|
|
20
|
-
params.include = options.include.join(',');
|
|
21
|
-
}
|
|
22
|
-
if (options.perPage) {
|
|
23
|
-
params.per_page = options.perPage;
|
|
24
|
-
}
|
|
25
|
-
if (options.page) {
|
|
26
|
-
params.page = options.page;
|
|
27
|
-
}
|
|
28
|
-
return this.getList('/workflows', params);
|
|
13
|
+
this.debugLog('workflows.getAll', { options });
|
|
14
|
+
return await this.getAllPages('/workflows', {
|
|
15
|
+
where: options.where,
|
|
16
|
+
include: options.include,
|
|
17
|
+
order: options.order
|
|
18
|
+
});
|
|
29
19
|
}
|
|
30
20
|
/**
|
|
31
|
-
* Get
|
|
21
|
+
* Get a single page of workflows with optional filtering and pagination control
|
|
22
|
+
* Use this when you need a specific page or want to limit the number of results
|
|
23
|
+
* @param options - List options including where, include, perPage, and page
|
|
24
|
+
* @returns A single page of results with meta and links for pagination
|
|
32
25
|
*/
|
|
33
|
-
async
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
return this.getAllPages('/workflows', params, paginationOptions);
|
|
26
|
+
async getPage(options = {}) {
|
|
27
|
+
this.debugLog('workflows.getPage', { options });
|
|
28
|
+
return this.getList('/workflows', {
|
|
29
|
+
where: options.where,
|
|
30
|
+
include: options.include,
|
|
31
|
+
per_page: options.perPage,
|
|
32
|
+
page: options.page,
|
|
33
|
+
order: options.order
|
|
34
|
+
});
|
|
44
35
|
}
|
|
45
36
|
/**
|
|
46
37
|
* Get a single workflow by ID
|
|
47
38
|
*/
|
|
48
39
|
async getById(id, include) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
params.include = include.join(',');
|
|
52
|
-
}
|
|
53
|
-
return this.getSingle(`/workflows/${id}`, params);
|
|
40
|
+
this.debugLog('workflows.getById', { id, include });
|
|
41
|
+
return this.getSingle(`/workflows/${id}`, include);
|
|
54
42
|
}
|
|
55
43
|
/**
|
|
56
44
|
* Create a workflow
|
|
57
45
|
*/
|
|
58
46
|
async create(data) {
|
|
47
|
+
this.debugLog('workflows.create', { data });
|
|
59
48
|
return this.createResource('/workflows', data);
|
|
60
49
|
}
|
|
61
50
|
/**
|
|
62
51
|
* Update a workflow
|
|
63
52
|
*/
|
|
64
53
|
async update(id, data) {
|
|
54
|
+
this.debugLog('workflows.update', { id, data });
|
|
65
55
|
return this.updateResource(`/workflows/${id}`, data);
|
|
66
56
|
}
|
|
67
57
|
/**
|
|
68
58
|
* Delete a workflow
|
|
69
59
|
*/
|
|
70
60
|
async delete(id) {
|
|
61
|
+
this.debugLog('workflows.delete', { id });
|
|
71
62
|
return this.deleteResource(`/workflows/${id}`);
|
|
72
63
|
}
|
|
73
64
|
/**
|
|
74
65
|
* Get workflow cards for a person
|
|
66
|
+
* @param personId - The person ID
|
|
67
|
+
* @param options - Optional pagination options
|
|
75
68
|
*/
|
|
76
|
-
async getPersonWorkflowCards(personId) {
|
|
77
|
-
|
|
69
|
+
async getPersonWorkflowCards(personId, options) {
|
|
70
|
+
this.debugLog('workflows.getPersonWorkflowCards', { personId, options });
|
|
71
|
+
return this.getList(`/people/${personId}/workflow_cards`, options);
|
|
78
72
|
}
|
|
79
73
|
/**
|
|
80
74
|
* Add a person to a workflow with smart duplicate detection
|
|
81
75
|
*/
|
|
82
76
|
async addPersonToWorkflow(personId, workflowId, options = {}) {
|
|
77
|
+
this.debugLog('workflows.addPersonToWorkflow', { personId, workflowId, options });
|
|
83
78
|
const { skipIfExists = true, skipIfActive = true } = options;
|
|
84
79
|
// Check for existing workflow cards if requested
|
|
85
80
|
if (skipIfExists || skipIfActive) {
|
|
86
81
|
const existingCards = await this.getPersonWorkflowCards(personId);
|
|
87
82
|
const existingCard = existingCards.data.find(card => {
|
|
88
|
-
|
|
83
|
+
// workflow may be Relationship (with .data) or flattened resource/identifier; support both
|
|
84
|
+
const raw = card?.workflow;
|
|
85
|
+
const workflowData = raw && 'id' in raw ? raw : raw?.data;
|
|
89
86
|
return workflowData && !Array.isArray(workflowData) && workflowData.id === workflowId;
|
|
90
87
|
});
|
|
91
|
-
if (existingCard
|
|
88
|
+
if (existingCard) {
|
|
92
89
|
// Check if card is completed or removed
|
|
93
|
-
if (skipIfExists && (existingCard.
|
|
90
|
+
if (skipIfExists && (existingCard.completed_at || existingCard.removed_at)) {
|
|
94
91
|
throw new Error(`Person already has a completed/removed card in this workflow`);
|
|
95
92
|
}
|
|
96
93
|
// Check if card is active
|
|
97
|
-
if (skipIfActive && !existingCard.
|
|
94
|
+
if (skipIfActive && !existingCard.completed_at && !existingCard.removed_at) {
|
|
98
95
|
throw new Error(`Person already has an active card in this workflow`);
|
|
99
96
|
}
|
|
100
97
|
}
|
|
@@ -114,6 +111,7 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
114
111
|
* Create a workflow card
|
|
115
112
|
*/
|
|
116
113
|
async createWorkflowCard(workflowId, personId) {
|
|
114
|
+
this.debugLog('workflows.createWorkflowCard', { workflowId, personId });
|
|
117
115
|
return this.createResource(`/workflows/${workflowId}/cards`, {
|
|
118
116
|
person_id: personId,
|
|
119
117
|
});
|
|
@@ -122,6 +120,7 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
122
120
|
* Update a workflow card
|
|
123
121
|
*/
|
|
124
122
|
async updateWorkflowCard(workflowCardId, data, personId) {
|
|
123
|
+
this.debugLog('workflows.updateWorkflowCard', { workflowCardId, data, personId });
|
|
125
124
|
// If personId is provided, use the person-specific endpoint
|
|
126
125
|
if (personId) {
|
|
127
126
|
return this.updateResource(`/people/${personId}/workflow_cards/${workflowCardId}`, data);
|
|
@@ -133,30 +132,21 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
133
132
|
* Get workflow card notes
|
|
134
133
|
*/
|
|
135
134
|
async getWorkflowCardNotes(personId, workflowCardId) {
|
|
135
|
+
this.debugLog('workflows.getWorkflowCardNotes', { personId, workflowCardId });
|
|
136
136
|
return this.getList(`/people/${personId}/workflow_cards/${workflowCardId}/notes`);
|
|
137
137
|
}
|
|
138
138
|
/**
|
|
139
139
|
* Create a workflow card note
|
|
140
140
|
*/
|
|
141
141
|
async createWorkflowCardNote(personId, workflowCardId, data) {
|
|
142
|
+
this.debugLog('workflows.createWorkflowCardNote', { personId, workflowCardId, data });
|
|
142
143
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/notes`, data);
|
|
143
144
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Update a workflow card note
|
|
146
|
-
*/
|
|
147
|
-
async updateWorkflowCardNote(personId, workflowCardId, noteId, data) {
|
|
148
|
-
return this.updateResource(`/people/${personId}/workflow_cards/${workflowCardId}/notes/${noteId}`, data);
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Delete a workflow card note
|
|
152
|
-
*/
|
|
153
|
-
async deleteWorkflowCardNote(personId, workflowCardId, noteId) {
|
|
154
|
-
return this.deleteResource(`/people/${personId}/workflow_cards/${workflowCardId}/notes/${noteId}`);
|
|
155
|
-
}
|
|
156
145
|
/**
|
|
157
146
|
* Create a workflow card with a note
|
|
158
147
|
*/
|
|
159
148
|
async createWorkflowCardWithNote(workflowId, personId, noteData) {
|
|
149
|
+
this.debugLog('workflows.createWorkflowCardWithNote', { workflowId, personId, noteData });
|
|
160
150
|
const workflowCard = await this.createWorkflowCard(workflowId, personId);
|
|
161
151
|
const note = await this.createWorkflowCardNote(personId, workflowCard.id, noteData);
|
|
162
152
|
return { workflowCard, note };
|
|
@@ -165,48 +155,56 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
165
155
|
* Move a workflow card back to the previous step
|
|
166
156
|
*/
|
|
167
157
|
async goBackWorkflowCard(personId, workflowCardId) {
|
|
158
|
+
this.debugLog('workflows.goBackWorkflowCard', { personId, workflowCardId });
|
|
168
159
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/go_back`, {});
|
|
169
160
|
}
|
|
170
161
|
/**
|
|
171
162
|
* Move a workflow card to the next step
|
|
172
163
|
*/
|
|
173
164
|
async promoteWorkflowCard(personId, workflowCardId) {
|
|
165
|
+
this.debugLog('workflows.promoteWorkflowCard', { personId, workflowCardId });
|
|
174
166
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/promote`, {});
|
|
175
167
|
}
|
|
176
168
|
/**
|
|
177
169
|
* Remove a workflow card
|
|
178
170
|
*/
|
|
179
171
|
async removeWorkflowCard(personId, workflowCardId) {
|
|
172
|
+
this.debugLog('workflows.removeWorkflowCard', { personId, workflowCardId });
|
|
180
173
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/remove`, {});
|
|
181
174
|
}
|
|
182
175
|
/**
|
|
183
176
|
* Restore a workflow card
|
|
184
177
|
*/
|
|
185
178
|
async restoreWorkflowCard(personId, workflowCardId) {
|
|
179
|
+
this.debugLog('workflows.restoreWorkflowCard', { personId, workflowCardId });
|
|
186
180
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/restore`, {});
|
|
187
181
|
}
|
|
188
182
|
/**
|
|
189
183
|
* Send an email to the subject of the workflow card
|
|
190
184
|
*/
|
|
191
185
|
async sendEmailWorkflowCard(personId, workflowCardId, data) {
|
|
186
|
+
this.debugLog('workflows.sendEmailWorkflowCard', { personId, workflowCardId, data });
|
|
192
187
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/send_email`, data);
|
|
193
188
|
}
|
|
194
189
|
/**
|
|
195
190
|
* Move a workflow card to the next step without completing the current step
|
|
196
191
|
*/
|
|
197
192
|
async skipStepWorkflowCard(personId, workflowCardId) {
|
|
193
|
+
this.debugLog('workflows.skipStepWorkflowCard', { personId, workflowCardId });
|
|
198
194
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/skip_step`, {});
|
|
199
195
|
}
|
|
200
196
|
/**
|
|
201
197
|
* Snooze a workflow card for a specific duration
|
|
202
198
|
*/
|
|
203
199
|
async snoozeWorkflowCard(personId, workflowCardId, data) {
|
|
200
|
+
this.debugLog('workflows.snoozeWorkflowCard', { personId, workflowCardId, data });
|
|
204
201
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/snooze`, data);
|
|
205
202
|
}
|
|
206
203
|
/**
|
|
207
204
|
* Unsnooze a workflow card
|
|
208
205
|
*/
|
|
209
206
|
async unsnoozeWorkflowCard(personId, workflowCardId) {
|
|
207
|
+
this.debugLog('workflows.unsnoozeWorkflowCard', { personId, workflowCardId });
|
|
210
208
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/unsnooze`, {});
|
|
211
209
|
}
|
|
212
210
|
/**
|
|
@@ -214,7 +212,8 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
214
212
|
*/
|
|
215
213
|
formatNoteTemplate(template, variables) {
|
|
216
214
|
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
217
|
-
|
|
215
|
+
const value = variables[key];
|
|
216
|
+
return value !== undefined ? String(value) : match;
|
|
218
217
|
});
|
|
219
218
|
}
|
|
220
219
|
}
|
package/dist/performance.d.ts
CHANGED
|
@@ -4,16 +4,20 @@
|
|
|
4
4
|
* This module provides utilities for optimizing performance when working
|
|
5
5
|
* with large datasets and high-volume API operations.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
8
|
-
import type { EmailResource, PersonResource, PhoneNumberResource } from './types';
|
|
7
|
+
import type { PcoClient } from './client';
|
|
8
|
+
import type { EmailResource, PersonResource, PhoneNumberResource, PeopleList } from './types';
|
|
9
|
+
import type { PersonListOptions, PersonWhereClause } from './types/api-options';
|
|
9
10
|
/**
|
|
10
|
-
* Process items in batches to avoid overwhelming the API
|
|
11
|
+
* Process items in batches to avoid overwhelming the API.
|
|
12
|
+
* Pass `client` in options to log batch progress when debug is enabled.
|
|
11
13
|
*/
|
|
12
|
-
export declare function processInBatches<T, R>(items: T[], batchSize: number, processor: (batch: T[]) => Promise<R[]
|
|
14
|
+
export declare function processInBatches<T, R>(items: T[], batchSize: number, processor: (batch: T[]) => Promise<R[]>, options?: {
|
|
15
|
+
client?: PcoClient;
|
|
16
|
+
}): Promise<R[]>;
|
|
13
17
|
/**
|
|
14
18
|
* Batch fetch person details with related data
|
|
15
19
|
*/
|
|
16
|
-
export declare function batchFetchPersonDetails(client:
|
|
20
|
+
export declare function batchFetchPersonDetails(client: PcoClient, personIds: string[], options?: {
|
|
17
21
|
includeEmails?: boolean;
|
|
18
22
|
includePhones?: boolean;
|
|
19
23
|
batchSize?: number;
|
|
@@ -35,11 +39,11 @@ export declare class ApiCache {
|
|
|
35
39
|
/**
|
|
36
40
|
* Cached version of getPeople with configurable TTL
|
|
37
41
|
*/
|
|
38
|
-
export declare function getCachedPeople(client:
|
|
42
|
+
export declare function getCachedPeople(client: PcoClient, cache: ApiCache, params?: PersonListOptions, ttlMs?: number): Promise<PeopleList>;
|
|
39
43
|
/**
|
|
40
44
|
* Efficiently fetch all pages of data with progress tracking
|
|
41
45
|
*/
|
|
42
|
-
export declare function fetchAllPages<T>(client:
|
|
46
|
+
export declare function fetchAllPages<T>(client: PcoClient, fetchFunction: (page: number, perPage: number) => Promise<{
|
|
43
47
|
data: T[];
|
|
44
48
|
links?: {
|
|
45
49
|
next?: string;
|
|
@@ -55,16 +59,16 @@ export declare function fetchAllPages<T>(client: PcoClientState, fetchFunction:
|
|
|
55
59
|
/**
|
|
56
60
|
* Stream large datasets with backpressure control
|
|
57
61
|
*/
|
|
58
|
-
export declare function streamPeopleData(client:
|
|
62
|
+
export declare function streamPeopleData(client: PcoClient, options?: {
|
|
59
63
|
perPage?: number;
|
|
60
64
|
maxConcurrent?: number;
|
|
61
|
-
where?:
|
|
65
|
+
where?: PersonWhereClause;
|
|
62
66
|
include?: string[];
|
|
63
67
|
}): AsyncGenerator<PersonResource[], void, unknown>;
|
|
64
68
|
/**
|
|
65
69
|
* Process large datasets without loading everything into memory
|
|
66
70
|
*/
|
|
67
|
-
export declare function processLargeDataset<T, R>(client:
|
|
71
|
+
export declare function processLargeDataset<T, R>(client: PcoClient, fetchFunction: (page: number, perPage: number) => Promise<{
|
|
68
72
|
data: T[];
|
|
69
73
|
links?: {
|
|
70
74
|
next?: string;
|
package/dist/performance.js
CHANGED
|
@@ -14,17 +14,29 @@ exports.fetchAllPages = fetchAllPages;
|
|
|
14
14
|
exports.streamPeopleData = streamPeopleData;
|
|
15
15
|
exports.processLargeDataset = processLargeDataset;
|
|
16
16
|
exports.monitorPerformance = monitorPerformance;
|
|
17
|
-
const
|
|
17
|
+
const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
|
|
18
18
|
// ===== Batch Processing =====
|
|
19
19
|
/**
|
|
20
|
-
* Process items in batches to avoid overwhelming the API
|
|
20
|
+
* Process items in batches to avoid overwhelming the API.
|
|
21
|
+
* Pass `client` in options to log batch progress when debug is enabled.
|
|
21
22
|
*/
|
|
22
|
-
async function processInBatches(items, batchSize, processor) {
|
|
23
|
+
async function processInBatches(items, batchSize, processor, options) {
|
|
24
|
+
const logger = options?.client && typeof options.client.getConfig === 'function'
|
|
25
|
+
? (0, planning_center_base_ts_1.createDebugLogger)(options.client.getConfig())
|
|
26
|
+
: null;
|
|
27
|
+
if (logger?.enabled) {
|
|
28
|
+
logger.log('performance.processInBatches', { itemCount: items.length, batchSize, batchCount: Math.ceil(items.length / batchSize) });
|
|
29
|
+
}
|
|
23
30
|
const results = [];
|
|
24
31
|
for (let i = 0; i < items.length; i += batchSize) {
|
|
25
32
|
const batch = items.slice(i, i + batchSize);
|
|
33
|
+
const batchIndex = Math.floor(i / batchSize) + 1;
|
|
34
|
+
if (logger?.enabled)
|
|
35
|
+
logger.log('performance.processInBatches batch start', { batchIndex, batchSize: batch.length });
|
|
26
36
|
const batchResults = await processor(batch);
|
|
27
37
|
results.push(...batchResults);
|
|
38
|
+
if (logger?.enabled)
|
|
39
|
+
logger.log('performance.processInBatches batch complete', { batchIndex, resultCount: batchResults.length, totalSoFar: results.length });
|
|
28
40
|
// Add a small delay between batches to respect rate limits
|
|
29
41
|
if (i + batchSize < items.length) {
|
|
30
42
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
@@ -36,31 +48,35 @@ async function processInBatches(items, batchSize, processor) {
|
|
|
36
48
|
* Batch fetch person details with related data
|
|
37
49
|
*/
|
|
38
50
|
async function batchFetchPersonDetails(client, personIds, options = {}) {
|
|
51
|
+
const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
|
|
52
|
+
? client.getConfig()
|
|
53
|
+
: undefined);
|
|
54
|
+
if (logger.enabled)
|
|
55
|
+
logger.log('performance.batchFetchPersonDetails', { personCount: personIds.length, options });
|
|
39
56
|
const { batchSize = 10, includeEmails = true, includePhones = true, } = options;
|
|
40
57
|
const results = new Map();
|
|
41
58
|
await processInBatches(personIds, batchSize, async (batch) => {
|
|
42
59
|
const batchResults = await Promise.all(batch.map(async (personId) => {
|
|
43
|
-
const personPromise = (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
const responses = await Promise.all(promises_array);
|
|
55
|
-
const person = responses[0].data[0];
|
|
60
|
+
const personPromise = client.people.getById(personId);
|
|
61
|
+
const emailPromise = includeEmails ? client.people.getEmails(personId) : null;
|
|
62
|
+
const phonePromise = includePhones ? client.people.getPhoneNumbers(personId) : null;
|
|
63
|
+
const promises = [personPromise];
|
|
64
|
+
if (emailPromise)
|
|
65
|
+
promises.push(emailPromise);
|
|
66
|
+
if (phonePromise)
|
|
67
|
+
promises.push(phonePromise);
|
|
68
|
+
const responses = await Promise.all(promises);
|
|
69
|
+
const person = responses[0];
|
|
56
70
|
if (!person)
|
|
57
71
|
return null;
|
|
58
72
|
const result = { person };
|
|
59
|
-
if (includeEmails) {
|
|
60
|
-
|
|
73
|
+
if (includeEmails && emailPromise) {
|
|
74
|
+
const emailsResponse = await emailPromise;
|
|
75
|
+
result.emails = emailsResponse.data || [];
|
|
61
76
|
}
|
|
62
|
-
if (includePhones) {
|
|
63
|
-
|
|
77
|
+
if (includePhones && phonePromise) {
|
|
78
|
+
const phonesResponse = await phonePromise;
|
|
79
|
+
result.phoneNumbers = phonesResponse.data || [];
|
|
64
80
|
}
|
|
65
81
|
return { personId, result };
|
|
66
82
|
}));
|
|
@@ -70,7 +86,7 @@ async function batchFetchPersonDetails(client, personIds, options = {}) {
|
|
|
70
86
|
}
|
|
71
87
|
});
|
|
72
88
|
return batchResults; // Return for processInBatches
|
|
73
|
-
});
|
|
89
|
+
}, { client });
|
|
74
90
|
return results;
|
|
75
91
|
}
|
|
76
92
|
// ===== Caching =====
|
|
@@ -111,12 +127,17 @@ exports.ApiCache = ApiCache;
|
|
|
111
127
|
* Cached version of getPeople with configurable TTL
|
|
112
128
|
*/
|
|
113
129
|
async function getCachedPeople(client, cache, params = {}, ttlMs = 300000) {
|
|
130
|
+
const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
|
|
131
|
+
? client.getConfig()
|
|
132
|
+
: undefined);
|
|
133
|
+
if (logger.enabled)
|
|
134
|
+
logger.log('performance.getCachedPeople', { params, ttlMs });
|
|
114
135
|
const cacheKey = `people:${JSON.stringify(params)}`;
|
|
115
136
|
const cached = cache.get(cacheKey);
|
|
116
137
|
if (cached) {
|
|
117
138
|
return cached;
|
|
118
139
|
}
|
|
119
|
-
const result = await
|
|
140
|
+
const result = await client.people.getAll(params);
|
|
120
141
|
cache.set(cacheKey, result, ttlMs);
|
|
121
142
|
return result;
|
|
122
143
|
}
|
|
@@ -125,6 +146,11 @@ async function getCachedPeople(client, cache, params = {}, ttlMs = 300000) {
|
|
|
125
146
|
* Efficiently fetch all pages of data with progress tracking
|
|
126
147
|
*/
|
|
127
148
|
async function fetchAllPages(client, fetchFunction, options = {}) {
|
|
149
|
+
const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
|
|
150
|
+
? client.getConfig()
|
|
151
|
+
: undefined);
|
|
152
|
+
if (logger.enabled)
|
|
153
|
+
logger.log('performance.fetchAllPages', { options });
|
|
128
154
|
const { maxPages = 1000, onProgress, perPage = 100 } = options;
|
|
129
155
|
const allData = [];
|
|
130
156
|
let page = 1;
|
|
@@ -152,6 +178,11 @@ async function fetchAllPages(client, fetchFunction, options = {}) {
|
|
|
152
178
|
* Stream large datasets with backpressure control
|
|
153
179
|
*/
|
|
154
180
|
async function* streamPeopleData(client, options = {}) {
|
|
181
|
+
const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
|
|
182
|
+
? client.getConfig()
|
|
183
|
+
: undefined);
|
|
184
|
+
if (logger.enabled)
|
|
185
|
+
logger.log('performance.streamPeopleData', { options });
|
|
155
186
|
const { include = [], maxConcurrent = 3, perPage = 50, where = {} } = options;
|
|
156
187
|
let page = 1;
|
|
157
188
|
let hasMore = true;
|
|
@@ -159,10 +190,10 @@ async function* streamPeopleData(client, options = {}) {
|
|
|
159
190
|
while (hasMore) {
|
|
160
191
|
await semaphore.acquire();
|
|
161
192
|
try {
|
|
162
|
-
const response = await
|
|
163
|
-
include,
|
|
193
|
+
const response = await client.people.getPage({
|
|
194
|
+
include: include,
|
|
164
195
|
page,
|
|
165
|
-
|
|
196
|
+
perPage,
|
|
166
197
|
where,
|
|
167
198
|
});
|
|
168
199
|
yield response.data;
|
|
@@ -203,6 +234,11 @@ class Semaphore {
|
|
|
203
234
|
* Process large datasets without loading everything into memory
|
|
204
235
|
*/
|
|
205
236
|
async function processLargeDataset(client, fetchFunction, processor, options = {}) {
|
|
237
|
+
const logger = (0, planning_center_base_ts_1.createDebugLogger)(typeof client.getConfig === 'function'
|
|
238
|
+
? client.getConfig()
|
|
239
|
+
: undefined);
|
|
240
|
+
if (logger.enabled)
|
|
241
|
+
logger.log('performance.processLargeDataset', { options });
|
|
206
242
|
const { maxMemoryItems = 1000, onBatchProcessed, perPage = 100 } = options;
|
|
207
243
|
const allResults = [];
|
|
208
244
|
let page = 1;
|
package/dist/testing/recorder.js
CHANGED
|
@@ -37,6 +37,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
})();
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.RequestRecorder = void 0;
|
|
40
|
+
const planning_center_base_ts_1 = require("@rachelallyson/planning-center-base-ts");
|
|
40
41
|
const fs = __importStar(require("fs"));
|
|
41
42
|
const path = __importStar(require("path"));
|
|
42
43
|
class RequestRecorder {
|
|
@@ -133,7 +134,11 @@ class RequestRecorder {
|
|
|
133
134
|
fs.writeFileSync(this.config.recordPath, JSON.stringify(session, null, 2));
|
|
134
135
|
}
|
|
135
136
|
catch (error) {
|
|
136
|
-
|
|
137
|
+
const logger = (0, planning_center_base_ts_1.createDebugLogger)(this.config.getConfig?.());
|
|
138
|
+
if (logger.enabled)
|
|
139
|
+
logger.log('recorder failed to save recording session', { error: String(error) });
|
|
140
|
+
else
|
|
141
|
+
console.error('Failed to save recording session:', error);
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
144
|
/**
|
|
@@ -148,7 +153,11 @@ class RequestRecorder {
|
|
|
148
153
|
return JSON.parse(content);
|
|
149
154
|
}
|
|
150
155
|
catch (error) {
|
|
151
|
-
|
|
156
|
+
const logger = (0, planning_center_base_ts_1.createDebugLogger)(this.config.getConfig?.());
|
|
157
|
+
if (logger.enabled)
|
|
158
|
+
logger.log('recorder failed to load recording session', { error: String(error) });
|
|
159
|
+
else
|
|
160
|
+
console.error('Failed to load recording session:', error);
|
|
152
161
|
return null;
|
|
153
162
|
}
|
|
154
163
|
}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Simplified Mock Response Builders for Testing
|
|
3
|
+
*
|
|
4
|
+
* Builders return flattened shape (same as API): attributes and relationships at top level.
|
|
3
5
|
*/
|
|
4
6
|
export declare class SimpleMockResponseBuilder {
|
|
5
7
|
/**
|
|
6
|
-
* Build a simple mock person resource
|
|
8
|
+
* Build a simple mock person resource (flattened shape)
|
|
7
9
|
*/
|
|
8
10
|
static person(overrides?: any): any;
|
|
9
11
|
/**
|
|
10
|
-
* Build a simple mock email resource
|
|
12
|
+
* Build a simple mock email resource (flattened shape)
|
|
11
13
|
*/
|
|
12
14
|
static email(overrides?: any): any;
|
|
13
15
|
/**
|
|
14
|
-
* Build a simple mock phone number resource
|
|
16
|
+
* Build a simple mock phone number resource (flattened shape)
|
|
15
17
|
*/
|
|
16
18
|
static phoneNumber(overrides?: any): any;
|
|
17
19
|
/**
|
|
18
|
-
* Build a simple mock workflow resource
|
|
20
|
+
* Build a simple mock workflow resource (flattened shape)
|
|
19
21
|
*/
|
|
20
22
|
static workflow(overrides?: any): any;
|
|
21
23
|
/**
|
|
@@ -1,85 +1,72 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Simplified Mock Response Builders for Testing
|
|
4
|
+
*
|
|
5
|
+
* Builders return flattened shape (same as API): attributes and relationships at top level.
|
|
4
6
|
*/
|
|
5
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
8
|
exports.SimpleMockResponseBuilder = void 0;
|
|
7
9
|
class SimpleMockResponseBuilder {
|
|
8
10
|
/**
|
|
9
|
-
* Build a simple mock person resource
|
|
11
|
+
* Build a simple mock person resource (flattened shape)
|
|
10
12
|
*/
|
|
11
13
|
static person(overrides = {}) {
|
|
14
|
+
const id = overrides.id || `person_${Date.now()}`;
|
|
12
15
|
return {
|
|
13
16
|
type: 'Person',
|
|
14
|
-
id
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
phone_numbers: { data: [] },
|
|
25
|
-
field_data: { data: [] },
|
|
26
|
-
workflow_cards: { data: [] },
|
|
27
|
-
household: { data: null },
|
|
28
|
-
},
|
|
17
|
+
id,
|
|
18
|
+
first_name: overrides.first_name ?? 'John',
|
|
19
|
+
last_name: overrides.last_name ?? 'Doe',
|
|
20
|
+
status: 'active',
|
|
21
|
+
...overrides,
|
|
22
|
+
emails: [],
|
|
23
|
+
phone_numbers: [],
|
|
24
|
+
field_data: [],
|
|
25
|
+
workflow_cards: [],
|
|
26
|
+
household: null,
|
|
29
27
|
};
|
|
30
28
|
}
|
|
31
29
|
/**
|
|
32
|
-
* Build a simple mock email resource
|
|
30
|
+
* Build a simple mock email resource (flattened shape)
|
|
33
31
|
*/
|
|
34
32
|
static email(overrides = {}) {
|
|
33
|
+
const id = overrides.id || `email_${Date.now()}`;
|
|
35
34
|
return {
|
|
36
35
|
type: 'Email',
|
|
37
|
-
id
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
...overrides,
|
|
44
|
-
},
|
|
45
|
-
relationships: {
|
|
46
|
-
person: { data: { type: 'Person', id: 'person_123' } },
|
|
47
|
-
},
|
|
36
|
+
id,
|
|
37
|
+
address: overrides.address ?? 'john@gmail.com',
|
|
38
|
+
location: 'Home',
|
|
39
|
+
primary: true,
|
|
40
|
+
...overrides,
|
|
41
|
+
person: { type: 'Person', id: 'person_123' },
|
|
48
42
|
};
|
|
49
43
|
}
|
|
50
44
|
/**
|
|
51
|
-
* Build a simple mock phone number resource
|
|
45
|
+
* Build a simple mock phone number resource (flattened shape)
|
|
52
46
|
*/
|
|
53
47
|
static phoneNumber(overrides = {}) {
|
|
48
|
+
const id = overrides.id || `phone_${Date.now()}`;
|
|
54
49
|
return {
|
|
55
50
|
type: 'PhoneNumber',
|
|
56
|
-
id
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
...overrides,
|
|
63
|
-
},
|
|
64
|
-
relationships: {
|
|
65
|
-
person: { data: { type: 'Person', id: 'person_123' } },
|
|
66
|
-
},
|
|
51
|
+
id,
|
|
52
|
+
number: overrides.number ?? '555-1234',
|
|
53
|
+
location: 'Mobile',
|
|
54
|
+
primary: true,
|
|
55
|
+
...overrides,
|
|
56
|
+
person: { type: 'Person', id: 'person_123' },
|
|
67
57
|
};
|
|
68
58
|
}
|
|
69
59
|
/**
|
|
70
|
-
* Build a simple mock workflow resource
|
|
60
|
+
* Build a simple mock workflow resource (flattened shape)
|
|
71
61
|
*/
|
|
72
62
|
static workflow(overrides = {}) {
|
|
63
|
+
const id = overrides.id || `workflow_${Date.now()}`;
|
|
73
64
|
return {
|
|
74
65
|
type: 'Workflow',
|
|
75
|
-
id
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
description: overrides.description || 'Workflow for new members',
|
|
80
|
-
...overrides,
|
|
81
|
-
},
|
|
82
|
-
relationships: {},
|
|
66
|
+
id,
|
|
67
|
+
name: overrides.name ?? 'New Member Workflow',
|
|
68
|
+
description: overrides.description ?? 'Workflow for new members',
|
|
69
|
+
...overrides,
|
|
83
70
|
};
|
|
84
71
|
}
|
|
85
72
|
/**
|