@rachelallyson/planning-center-people-ts 2.14.0 → 3.0.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 +93 -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 +236 -93
- package/dist/index.d.ts +9 -7
- package/dist/index.js +31 -71
- 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 +42 -54
- package/dist/modules/fields.js +70 -100
- 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 +30 -28
- package/dist/modules/lists.js +55 -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 +50 -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 +47 -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 -62
- package/dist/people/fields.js +0 -294
- 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,89 @@ 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
|
-
const workflowData = card
|
|
83
|
+
const workflowData = card?.workflow?.data;
|
|
89
84
|
return workflowData && !Array.isArray(workflowData) && workflowData.id === workflowId;
|
|
90
85
|
});
|
|
91
|
-
if (existingCard
|
|
86
|
+
if (existingCard) {
|
|
92
87
|
// Check if card is completed or removed
|
|
93
|
-
if (skipIfExists && (existingCard.
|
|
88
|
+
if (skipIfExists && (existingCard.completed_at || existingCard.removed_at)) {
|
|
94
89
|
throw new Error(`Person already has a completed/removed card in this workflow`);
|
|
95
90
|
}
|
|
96
91
|
// Check if card is active
|
|
97
|
-
if (skipIfActive && !existingCard.
|
|
92
|
+
if (skipIfActive && !existingCard.completed_at && !existingCard.removed_at) {
|
|
98
93
|
throw new Error(`Person already has an active card in this workflow`);
|
|
99
94
|
}
|
|
100
95
|
}
|
|
@@ -114,6 +109,7 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
114
109
|
* Create a workflow card
|
|
115
110
|
*/
|
|
116
111
|
async createWorkflowCard(workflowId, personId) {
|
|
112
|
+
this.debugLog('workflows.createWorkflowCard', { workflowId, personId });
|
|
117
113
|
return this.createResource(`/workflows/${workflowId}/cards`, {
|
|
118
114
|
person_id: personId,
|
|
119
115
|
});
|
|
@@ -122,6 +118,7 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
122
118
|
* Update a workflow card
|
|
123
119
|
*/
|
|
124
120
|
async updateWorkflowCard(workflowCardId, data, personId) {
|
|
121
|
+
this.debugLog('workflows.updateWorkflowCard', { workflowCardId, data, personId });
|
|
125
122
|
// If personId is provided, use the person-specific endpoint
|
|
126
123
|
if (personId) {
|
|
127
124
|
return this.updateResource(`/people/${personId}/workflow_cards/${workflowCardId}`, data);
|
|
@@ -133,30 +130,21 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
133
130
|
* Get workflow card notes
|
|
134
131
|
*/
|
|
135
132
|
async getWorkflowCardNotes(personId, workflowCardId) {
|
|
133
|
+
this.debugLog('workflows.getWorkflowCardNotes', { personId, workflowCardId });
|
|
136
134
|
return this.getList(`/people/${personId}/workflow_cards/${workflowCardId}/notes`);
|
|
137
135
|
}
|
|
138
136
|
/**
|
|
139
137
|
* Create a workflow card note
|
|
140
138
|
*/
|
|
141
139
|
async createWorkflowCardNote(personId, workflowCardId, data) {
|
|
140
|
+
this.debugLog('workflows.createWorkflowCardNote', { personId, workflowCardId, data });
|
|
142
141
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/notes`, data);
|
|
143
142
|
}
|
|
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
143
|
/**
|
|
157
144
|
* Create a workflow card with a note
|
|
158
145
|
*/
|
|
159
146
|
async createWorkflowCardWithNote(workflowId, personId, noteData) {
|
|
147
|
+
this.debugLog('workflows.createWorkflowCardWithNote', { workflowId, personId, noteData });
|
|
160
148
|
const workflowCard = await this.createWorkflowCard(workflowId, personId);
|
|
161
149
|
const note = await this.createWorkflowCardNote(personId, workflowCard.id, noteData);
|
|
162
150
|
return { workflowCard, note };
|
|
@@ -165,48 +153,56 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
165
153
|
* Move a workflow card back to the previous step
|
|
166
154
|
*/
|
|
167
155
|
async goBackWorkflowCard(personId, workflowCardId) {
|
|
156
|
+
this.debugLog('workflows.goBackWorkflowCard', { personId, workflowCardId });
|
|
168
157
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/go_back`, {});
|
|
169
158
|
}
|
|
170
159
|
/**
|
|
171
160
|
* Move a workflow card to the next step
|
|
172
161
|
*/
|
|
173
162
|
async promoteWorkflowCard(personId, workflowCardId) {
|
|
163
|
+
this.debugLog('workflows.promoteWorkflowCard', { personId, workflowCardId });
|
|
174
164
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/promote`, {});
|
|
175
165
|
}
|
|
176
166
|
/**
|
|
177
167
|
* Remove a workflow card
|
|
178
168
|
*/
|
|
179
169
|
async removeWorkflowCard(personId, workflowCardId) {
|
|
170
|
+
this.debugLog('workflows.removeWorkflowCard', { personId, workflowCardId });
|
|
180
171
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/remove`, {});
|
|
181
172
|
}
|
|
182
173
|
/**
|
|
183
174
|
* Restore a workflow card
|
|
184
175
|
*/
|
|
185
176
|
async restoreWorkflowCard(personId, workflowCardId) {
|
|
177
|
+
this.debugLog('workflows.restoreWorkflowCard', { personId, workflowCardId });
|
|
186
178
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/restore`, {});
|
|
187
179
|
}
|
|
188
180
|
/**
|
|
189
181
|
* Send an email to the subject of the workflow card
|
|
190
182
|
*/
|
|
191
183
|
async sendEmailWorkflowCard(personId, workflowCardId, data) {
|
|
184
|
+
this.debugLog('workflows.sendEmailWorkflowCard', { personId, workflowCardId, data });
|
|
192
185
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/send_email`, data);
|
|
193
186
|
}
|
|
194
187
|
/**
|
|
195
188
|
* Move a workflow card to the next step without completing the current step
|
|
196
189
|
*/
|
|
197
190
|
async skipStepWorkflowCard(personId, workflowCardId) {
|
|
191
|
+
this.debugLog('workflows.skipStepWorkflowCard', { personId, workflowCardId });
|
|
198
192
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/skip_step`, {});
|
|
199
193
|
}
|
|
200
194
|
/**
|
|
201
195
|
* Snooze a workflow card for a specific duration
|
|
202
196
|
*/
|
|
203
197
|
async snoozeWorkflowCard(personId, workflowCardId, data) {
|
|
198
|
+
this.debugLog('workflows.snoozeWorkflowCard', { personId, workflowCardId, data });
|
|
204
199
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/snooze`, data);
|
|
205
200
|
}
|
|
206
201
|
/**
|
|
207
202
|
* Unsnooze a workflow card
|
|
208
203
|
*/
|
|
209
204
|
async unsnoozeWorkflowCard(personId, workflowCardId) {
|
|
205
|
+
this.debugLog('workflows.unsnoozeWorkflowCard', { personId, workflowCardId });
|
|
210
206
|
return this.createResource(`/people/${personId}/workflow_cards/${workflowCardId}/unsnooze`, {});
|
|
211
207
|
}
|
|
212
208
|
/**
|
|
@@ -214,7 +210,8 @@ class WorkflowsModule extends planning_center_base_ts_1.BaseModule {
|
|
|
214
210
|
*/
|
|
215
211
|
formatNoteTemplate(template, variables) {
|
|
216
212
|
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
217
|
-
|
|
213
|
+
const value = variables[key];
|
|
214
|
+
return value !== undefined ? String(value) : match;
|
|
218
215
|
});
|
|
219
216
|
}
|
|
220
217
|
}
|
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
|
/**
|
package/dist/testing/types.d.ts
CHANGED
|
@@ -141,6 +141,10 @@ export interface RecordingConfig {
|
|
|
141
141
|
filter?: (endpoint: string, method: string) => boolean;
|
|
142
142
|
/** Transform recorded responses */
|
|
143
143
|
transform?: (response: any) => any;
|
|
144
|
+
/** Optional getConfig for debug logging (e.g. when recording client has debug) */
|
|
145
|
+
getConfig?: () => {
|
|
146
|
+
debug?: boolean | import('@rachelallyson/planning-center-base-ts').PcoDebugOptions;
|
|
147
|
+
};
|
|
144
148
|
}
|
|
145
149
|
export interface RecordedRequest {
|
|
146
150
|
endpoint: string;
|