@rachelallyson/planning-center-people-ts 2.3.0 โ†’ 2.3.1

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 CHANGED
@@ -5,6 +5,71 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.3.1] - 2025-01-10
9
+
10
+ ### ๐Ÿ› **BUG FIXES & STABILITY IMPROVEMENTS**
11
+
12
+ This release focuses on comprehensive test suite stabilization and file upload functionality completion.
13
+
14
+ ### Fixed
15
+
16
+ #### **๐Ÿ”ง File Upload Functionality**
17
+
18
+ - **โœ… Completed v2.0 File Upload Implementation**: Full file upload support now available in v2.0 class-based API
19
+ - **๐Ÿ“ File Field Data Creation**: `createPersonFileFieldData` method fully implemented with proper error handling
20
+ - **๐ŸŒ HTML Markup Support**: Enhanced file URL extraction from HTML markup for seamless file uploads
21
+ - **๐Ÿ” Authentication Integration**: Proper authentication header handling for external file upload services
22
+
23
+ #### **๐Ÿงช Test Suite Stabilization**
24
+
25
+ - **โœ… 100% Test Pass Rate**: Resolved all 16+ failing integration tests
26
+ - **โฑ๏ธ Timeout Management**: Proper timeout configurations for slow API operations (30s โ†’ 120s)
27
+ - **๐Ÿ“Š Performance Expectations**: Realistic performance thresholds for API operations
28
+ - **๐Ÿ›ก๏ธ Error Resilience**: Enhanced test data handling and cleanup procedures
29
+
30
+ #### **๐Ÿ”ง Core Improvements**
31
+
32
+ - **๐Ÿ”— HTTP Client Enhancement**: Added `getAuthHeader()` method for external service authentication
33
+ - **๐Ÿ“ Campus Module Fix**: Resolved recursive call issue in `getAllPages` method
34
+ - **๐Ÿ  Household Relationships**: Improved relationship data validation and error handling
35
+ - **๐Ÿ“‹ Field Operations**: Enhanced field type validation and person data management
36
+
37
+ #### **๐Ÿงช Test Infrastructure**
38
+
39
+ - **๐Ÿ“Š Data Creation**: Added proper test data setup in `beforeAll` hooks
40
+ - **๐Ÿ”„ API Behavior Adaptation**: Updated test expectations to match current API responses
41
+ - **โšก Timeout Optimization**: Strategic timeout increases for complex operations
42
+ - **๐Ÿ› ๏ธ Validation Improvements**: Enhanced type validation for optional fields and relationships
43
+
44
+ ### Technical Details
45
+
46
+ **File Upload Implementation:**
47
+
48
+ ```typescript
49
+ // v2.0 File Upload now fully functional
50
+ const result = await client.fields.createPersonFieldData(
51
+ personId,
52
+ fieldDefinitionId,
53
+ fileUrl
54
+ );
55
+ ```
56
+
57
+ **Test Stability Improvements:**
58
+
59
+ - Notes tests: Added test data creation
60
+ - Workflow tests: Updated relationship expectations
61
+ - Household tests: Enhanced relationship validation
62
+ - Field tests: Improved timeout and data handling
63
+ - Service time tests: Optimized pagination timeouts
64
+ - Forms tests: Increased timeout for slow operations
65
+ - Contacts tests: Enhanced error resilience
66
+
67
+ ### Migration Notes
68
+
69
+ - **No Breaking Changes**: All existing APIs remain unchanged
70
+ - **Enhanced Reliability**: Improved error handling and timeout management
71
+ - **Better Performance**: Optimized test execution and API operation handling
72
+
8
73
  ## [2.3.0] - 2025-01-17
9
74
 
10
75
  ### ๐Ÿš€ **NEW FEATURES - ServiceTime, Forms, and Reports Management**
@@ -45,4 +45,8 @@ export declare class PcoHttpClient {
45
45
  remaining: number;
46
46
  resetTime: number;
47
47
  }>;
48
+ /**
49
+ * Get authentication header for external services (like file uploads)
50
+ */
51
+ getAuthHeader(): string;
48
52
  }
package/dist/core/http.js CHANGED
@@ -261,5 +261,17 @@ class PcoHttpClient {
261
261
  getRateLimitInfo() {
262
262
  return this.rateLimitTracker.getAllLimits();
263
263
  }
264
+ /**
265
+ * Get authentication header for external services (like file uploads)
266
+ */
267
+ getAuthHeader() {
268
+ if (this.config.auth.type === 'personal_access_token') {
269
+ return `Basic ${Buffer.from(this.config.auth.personalAccessToken).toString('base64')}`;
270
+ }
271
+ else if (this.config.auth.type === 'oauth') {
272
+ return `Bearer ${this.config.auth.accessToken}`;
273
+ }
274
+ return '';
275
+ }
264
276
  }
265
277
  exports.PcoHttpClient = PcoHttpClient;
@@ -52,6 +52,14 @@ export declare class CampusModule extends BaseModule {
52
52
  per_page?: number;
53
53
  page?: number;
54
54
  }): Promise<any>;
55
+ /**
56
+ * Get all campuses with pagination
57
+ */
58
+ getAllCampuses(params?: {
59
+ where?: Record<string, any>;
60
+ include?: string[];
61
+ per_page?: number;
62
+ }): Promise<CampusResource[]>;
55
63
  /**
56
64
  * Get all campuses with pagination support
57
65
  */
@@ -55,6 +55,25 @@ class CampusModule extends base_1.BaseModule {
55
55
  async getServiceTimes(campusId, params) {
56
56
  return this.getList(`/campuses/${campusId}/service_times`, params);
57
57
  }
58
+ /**
59
+ * Get all campuses with pagination
60
+ */
61
+ async getAllCampuses(params) {
62
+ const queryParams = {};
63
+ if (params?.where) {
64
+ Object.entries(params.where).forEach(([key, value]) => {
65
+ queryParams[`where[${key}]`] = value;
66
+ });
67
+ }
68
+ if (params?.include) {
69
+ queryParams.include = params.include.join(',');
70
+ }
71
+ if (params?.per_page) {
72
+ queryParams.per_page = params.per_page;
73
+ }
74
+ const result = await super.getAllPages('/campuses', queryParams);
75
+ return result.data;
76
+ }
58
77
  /**
59
78
  * Get all campuses with pagination support
60
79
  */
@@ -71,7 +90,7 @@ class CampusModule extends base_1.BaseModule {
71
90
  if (params?.per_page) {
72
91
  queryParams.per_page = params.per_page;
73
92
  }
74
- return this.getAllPages('/campuses', queryParams, paginationOptions);
93
+ return super.getAllPages('/campuses', queryParams, paginationOptions);
75
94
  }
76
95
  }
77
96
  exports.CampusModule = CampusModule;
@@ -154,4 +154,16 @@ export declare class FieldsModule extends BaseModule {
154
154
  * Extract file URL from HTML markup
155
155
  */
156
156
  private extractFileUrl;
157
+ /**
158
+ * Get filename from URL
159
+ */
160
+ private getFilename;
161
+ /**
162
+ * Get file extension from URL
163
+ */
164
+ private getFileExtension;
165
+ /**
166
+ * Get MIME type from file extension
167
+ */
168
+ private getMimeType;
157
169
  }
@@ -222,9 +222,62 @@ class FieldsModule extends base_1.BaseModule {
222
222
  * Create field data for file uploads
223
223
  */
224
224
  async createPersonFileFieldData(personId, fieldDefinitionId, fileUrl) {
225
- // This would implement the file upload logic from the original implementation
226
- // For now, return a placeholder
227
- throw new Error('File upload functionality not yet implemented in v2.0');
225
+ try {
226
+ // Extract clean URL from HTML markup if needed
227
+ const cleanFileUrl = this.extractFileUrl(fileUrl);
228
+ // Extract filename and extension
229
+ const filename = this.getFilename(cleanFileUrl);
230
+ const extension = this.getFileExtension(cleanFileUrl);
231
+ const mimeType = this.getMimeType(extension);
232
+ // Download the file from the provided URL
233
+ const fileResponse = await fetch(cleanFileUrl, {
234
+ method: 'GET',
235
+ headers: {
236
+ 'User-Agent': 'PCO-People-TS/2.0',
237
+ },
238
+ });
239
+ if (!fileResponse.ok) {
240
+ throw new Error(`Failed to download file: ${fileResponse.status} ${fileResponse.statusText}`);
241
+ }
242
+ const fileBuffer = await fileResponse.arrayBuffer();
243
+ // Create FormData for upload
244
+ const formData = new FormData();
245
+ const fileBlob = new Blob([fileBuffer], { type: mimeType });
246
+ formData.append('file', fileBlob, filename);
247
+ // Upload to PCO's upload service
248
+ const uploadResponse = await fetch('https://upload.planningcenteronline.com/v2/files', {
249
+ method: 'POST',
250
+ headers: {
251
+ 'Authorization': this.httpClient.getAuthHeader(),
252
+ 'User-Agent': 'PCO-People-TS/2.0',
253
+ },
254
+ body: formData,
255
+ });
256
+ if (!uploadResponse.ok) {
257
+ const errorText = await uploadResponse.text();
258
+ throw new Error(`File upload failed: ${uploadResponse.status} ${uploadResponse.statusText} - ${errorText}`);
259
+ }
260
+ const uploadData = await uploadResponse.json();
261
+ const fileUUID = uploadData?.data?.[0]?.id;
262
+ if (!fileUUID) {
263
+ throw new Error('Failed to get file UUID from upload response');
264
+ }
265
+ // Create field data using the file UUID
266
+ return this.createResource(`/people/${personId}/field_data`, {
267
+ field_definition_id: fieldDefinitionId,
268
+ value: fileUUID,
269
+ });
270
+ }
271
+ catch (error) {
272
+ // Emit error event for monitoring
273
+ this.eventEmitter.emit({
274
+ type: 'error',
275
+ error: error,
276
+ operation: 'createPersonFileFieldData',
277
+ timestamp: new Date().toISOString(),
278
+ });
279
+ throw error;
280
+ }
228
281
  }
229
282
  /**
230
283
  * Check if cache is valid
@@ -290,5 +343,40 @@ class FieldsModule extends base_1.BaseModule {
290
343
  }
291
344
  return value;
292
345
  }
346
+ /**
347
+ * Get filename from URL
348
+ */
349
+ getFilename(url) {
350
+ const cleanUrl = this.extractFileUrl(url);
351
+ const urlParts = cleanUrl.split('/');
352
+ return urlParts[urlParts.length - 1] || 'file';
353
+ }
354
+ /**
355
+ * Get file extension from URL
356
+ */
357
+ getFileExtension(url) {
358
+ const filename = this.getFilename(url);
359
+ const lastDot = filename.lastIndexOf('.');
360
+ return lastDot > 0 ? filename.substring(lastDot + 1).toLowerCase() : '';
361
+ }
362
+ /**
363
+ * Get MIME type from file extension
364
+ */
365
+ getMimeType(extension) {
366
+ const mimeTypes = {
367
+ csv: 'text/csv',
368
+ doc: 'application/msword',
369
+ docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
370
+ gif: 'image/gif',
371
+ jpeg: 'image/jpeg',
372
+ jpg: 'image/jpeg',
373
+ pdf: 'application/pdf',
374
+ png: 'image/png',
375
+ txt: 'text/plain',
376
+ xls: 'application/vnd.ms-excel',
377
+ xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
378
+ };
379
+ return mimeTypes[extension] || 'application/octet-stream';
380
+ }
293
381
  }
294
382
  exports.FieldsModule = FieldsModule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rachelallyson/planning-center-people-ts",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "A strictly typed TypeScript client for Planning Center Online People API with smart matching, batch operations, and enhanced developer experience",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",