@rachelallyson/planning-center-people-ts 2.4.0 → 2.6.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 +163 -0
- package/dist/core/http.js +21 -5
- package/dist/modules/people.d.ts +89 -9
- package/dist/modules/people.js +203 -6
- package/dist/people/fields.js +22 -21
- package/dist/types/client.d.ts +11 -1
- package/dist/types/people.d.ts +6 -0
- package/package.json +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,169 @@ 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.6.0] - 2025-01-10
|
|
9
|
+
|
|
10
|
+
### 🎯 **PERFORMANCE & DEPENDENCY OPTIMIZATION**
|
|
11
|
+
|
|
12
|
+
This release focuses on performance improvements and dependency optimization, making the library lighter, faster, and more efficient.
|
|
13
|
+
|
|
14
|
+
### Removed
|
|
15
|
+
|
|
16
|
+
- **📦 Axios Dependency**: Completely removed axios dependency by replacing it with native fetch API
|
|
17
|
+
- **🔧 Simplified Dependencies**: Reduced bundle size by eliminating unnecessary external dependencies
|
|
18
|
+
- **⚡ Performance Boost**: Native fetch API provides better performance than axios
|
|
19
|
+
|
|
20
|
+
### Improved
|
|
21
|
+
|
|
22
|
+
- **🚀 File Upload Performance**: File uploads now use native fetch API for better performance
|
|
23
|
+
- **📱 Better Browser Support**: Native fetch works consistently across all modern environments
|
|
24
|
+
- **🛡️ Enhanced Security**: Fewer external dependencies reduce security surface area
|
|
25
|
+
- **📦 Smaller Bundle Size**: Eliminated ~50KB+ dependency from the bundle
|
|
26
|
+
|
|
27
|
+
### Technical Details
|
|
28
|
+
|
|
29
|
+
- **File Downloads**: Replaced `axios.get()` with native `fetch()` for downloading files from URLs
|
|
30
|
+
- **File Uploads**: Replaced `axios.post()` with native `fetch()` for uploading to PCO's upload service
|
|
31
|
+
- **Error Handling**: Maintained all existing error handling while using native APIs
|
|
32
|
+
- **Authentication**: Preserved all authentication mechanisms with native fetch
|
|
33
|
+
|
|
34
|
+
### Migration
|
|
35
|
+
|
|
36
|
+
No breaking changes - all existing functionality works exactly the same:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// File uploads work exactly the same
|
|
40
|
+
await client.people.setPersonFieldBySlug('person-123', 'resume', fileUrl);
|
|
41
|
+
|
|
42
|
+
// All other functionality unchanged
|
|
43
|
+
const people = await client.people.getAll();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Benefits
|
|
47
|
+
|
|
48
|
+
- **📦 Smaller Bundle**: Reduced dependency footprint
|
|
49
|
+
- **⚡ Better Performance**: Native fetch is faster than axios
|
|
50
|
+
- **🔧 Consistency**: Now using fetch API throughout the entire codebase
|
|
51
|
+
- **🛡️ Security**: Fewer dependencies to audit and maintain
|
|
52
|
+
|
|
53
|
+
## [2.5.0] - 2025-01-10
|
|
54
|
+
|
|
55
|
+
### 🎯 **NEW FEATURES - Person Relationship Management & Token Refresh Fix**
|
|
56
|
+
|
|
57
|
+
This release introduces comprehensive person relationship management endpoints and fixes critical token refresh issues, significantly enhancing the library's functionality and reliability.
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
#### **👥 Person Relationship Management**
|
|
62
|
+
|
|
63
|
+
- **🏢 Campus Management**: Complete campus assignment and retrieval system
|
|
64
|
+
- `getPrimaryCampus(personId)` - Get person's current campus
|
|
65
|
+
- `setPrimaryCampus(personId, campusId)` - Assign/update person's campus
|
|
66
|
+
- `removePrimaryCampus(personId)` - Remove campus assignment
|
|
67
|
+
- `getByCampus(campusId, options)` - Get all people in a campus
|
|
68
|
+
|
|
69
|
+
- **🏠 Household Management**: Full household membership system
|
|
70
|
+
- `getHousehold(personId)` - Get person's household
|
|
71
|
+
- `setHousehold(personId, householdId)` - Assign person to household
|
|
72
|
+
- `removeFromHousehold(personId)` - Remove person from household
|
|
73
|
+
- `getHouseholdMembers(householdId, options)` - Get all household members
|
|
74
|
+
|
|
75
|
+
- **📋 Related Data Access**: Comprehensive access to person-related data
|
|
76
|
+
- `getWorkflowCards(personId, options)` - Get person's workflow cards
|
|
77
|
+
- `getNotes(personId, options)` - Get person's notes
|
|
78
|
+
- `getFieldData(personId, options)` - Get person's field data
|
|
79
|
+
- `getSocialProfiles(personId, options)` - Get person's social profiles
|
|
80
|
+
|
|
81
|
+
#### **🔧 Enhanced Type System**
|
|
82
|
+
|
|
83
|
+
- **📝 Complete PersonRelationships**: Updated interface with all available relationships
|
|
84
|
+
- **🏷️ Type Safety**: Full TypeScript support for all relationship operations
|
|
85
|
+
- **🛡️ Null Handling**: Proper handling of optional relationships
|
|
86
|
+
- **📊 Resource Validation**: Enhanced relationship data validation
|
|
87
|
+
|
|
88
|
+
#### **🔐 Token Refresh Fix**
|
|
89
|
+
|
|
90
|
+
- **🚫 Fixed 401 Unauthorized**: Resolved token refresh failures by including client credentials
|
|
91
|
+
- **🔑 Client Credentials Support**: Added support for `clientId` and `clientSecret` in OAuth config
|
|
92
|
+
- **🌍 Environment Variables**: Support for `PCO_APP_ID` and `PCO_APP_SECRET` environment variables
|
|
93
|
+
- **🔄 Standardized Implementation**: Consistent token refresh across all HTTP clients
|
|
94
|
+
- **🛡️ Enhanced Error Handling**: Better error messages for token refresh failures
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
|
|
98
|
+
- **🔐 Token Refresh 401 Errors**: Fixed "Token refresh failed: 401 Unauthorized" by including required client credentials
|
|
99
|
+
- **🏗️ Missing Auth Types**: Added missing `BasicAuth` type to v2.0.0 client configuration
|
|
100
|
+
- **🔄 Inconsistent Implementations**: Standardized token refresh across `auth.ts` and `http.ts`
|
|
101
|
+
- **📝 Type Definitions**: Enhanced `PersonRelationships` interface with all available relationships
|
|
102
|
+
|
|
103
|
+
### Removed
|
|
104
|
+
|
|
105
|
+
- **📦 Axios Dependency**: Removed axios dependency by replacing it with native fetch API in file upload functionality
|
|
106
|
+
- **🔧 Simplified Dependencies**: Reduced bundle size by eliminating unnecessary external dependencies
|
|
107
|
+
|
|
108
|
+
### Usage Examples
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Campus Management
|
|
112
|
+
const campus = await client.people.getPrimaryCampus('person-123');
|
|
113
|
+
await client.people.setPrimaryCampus('person-123', 'campus-456');
|
|
114
|
+
|
|
115
|
+
// Household Management
|
|
116
|
+
const household = await client.people.getHousehold('person-123');
|
|
117
|
+
await client.people.setHousehold('person-123', 'household-789');
|
|
118
|
+
|
|
119
|
+
// Related Data Access
|
|
120
|
+
const workflowCards = await client.people.getWorkflowCards('person-123');
|
|
121
|
+
const notes = await client.people.getNotes('person-123');
|
|
122
|
+
const fieldData = await client.people.getFieldData('person-123');
|
|
123
|
+
|
|
124
|
+
// Token Refresh with Client Credentials
|
|
125
|
+
const client = new PcoClient({
|
|
126
|
+
auth: {
|
|
127
|
+
type: 'oauth',
|
|
128
|
+
accessToken: 'your-token',
|
|
129
|
+
refreshToken: 'your-refresh-token',
|
|
130
|
+
clientId: 'your-app-id', // NEW: Client credentials
|
|
131
|
+
clientSecret: 'your-app-secret', // NEW: Client credentials
|
|
132
|
+
onRefresh: async (tokens) => { /* handle refresh */ },
|
|
133
|
+
onRefreshFailure: async (error) => { /* handle failure */ }
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Migration Guide
|
|
139
|
+
|
|
140
|
+
**From Direct API Calls:**
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Before: Complex direct API calls
|
|
144
|
+
const response = await client.httpClient.request({
|
|
145
|
+
method: 'PATCH',
|
|
146
|
+
endpoint: `/people/${personId}`,
|
|
147
|
+
data: { /* complex JSON structure */ }
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// After: Simple, intuitive methods
|
|
151
|
+
await client.people.setPrimaryCampus(personId, campusId);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Token Refresh Configuration:**
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Add client credentials to your OAuth configuration
|
|
158
|
+
const client = new PcoClient({
|
|
159
|
+
auth: {
|
|
160
|
+
type: 'oauth',
|
|
161
|
+
accessToken: 'your-token',
|
|
162
|
+
refreshToken: 'your-refresh-token',
|
|
163
|
+
clientId: process.env.PCO_APP_ID, // NEW
|
|
164
|
+
clientSecret: process.env.PCO_APP_SECRET, // NEW
|
|
165
|
+
onRefresh: async (tokens) => { /* save tokens */ },
|
|
166
|
+
onRefreshFailure: async (error) => { /* handle failure */ }
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
8
171
|
## [2.4.0] - 2025-01-10
|
|
9
172
|
|
|
10
173
|
### 🎯 **NEW FEATURES - Age Preference Matching & Exact Name Matching**
|
package/dist/core/http.js
CHANGED
|
@@ -191,6 +191,11 @@ class PcoHttpClient {
|
|
|
191
191
|
else if (this.config.auth.type === 'oauth') {
|
|
192
192
|
headers.Authorization = `Bearer ${this.config.auth.accessToken}`;
|
|
193
193
|
}
|
|
194
|
+
else if (this.config.auth.type === 'basic') {
|
|
195
|
+
// Basic auth with app credentials
|
|
196
|
+
const credentials = Buffer.from(`${this.config.auth.appId}:${this.config.auth.appSecret}`).toString('base64');
|
|
197
|
+
headers.Authorization = `Basic ${credentials}`;
|
|
198
|
+
}
|
|
194
199
|
}
|
|
195
200
|
getResourceTypeFromEndpoint(endpoint) {
|
|
196
201
|
// Extract resource type from endpoint
|
|
@@ -224,18 +229,29 @@ class PcoHttpClient {
|
|
|
224
229
|
}
|
|
225
230
|
const baseURL = this.config.baseURL || 'https://api.planningcenteronline.com/people/v2';
|
|
226
231
|
const tokenUrl = baseURL.replace('/people/v2', '/oauth/token');
|
|
232
|
+
// Prepare the request body for token refresh
|
|
233
|
+
const body = new URLSearchParams({
|
|
234
|
+
grant_type: 'refresh_token',
|
|
235
|
+
refresh_token: this.config.auth.refreshToken,
|
|
236
|
+
});
|
|
237
|
+
// Add client credentials if available from the config or environment
|
|
238
|
+
const clientId = this.config.auth.clientId || process.env.PCO_APP_ID;
|
|
239
|
+
const clientSecret = this.config.auth.clientSecret || process.env.PCO_APP_SECRET;
|
|
240
|
+
if (clientId && clientSecret) {
|
|
241
|
+
body.append('client_id', clientId);
|
|
242
|
+
body.append('client_secret', clientSecret);
|
|
243
|
+
}
|
|
227
244
|
const response = await fetch(tokenUrl, {
|
|
228
245
|
method: 'POST',
|
|
229
246
|
headers: {
|
|
230
247
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
248
|
+
'Accept': 'application/json',
|
|
231
249
|
},
|
|
232
|
-
body:
|
|
233
|
-
grant_type: 'refresh_token',
|
|
234
|
-
refresh_token: this.config.auth.refreshToken,
|
|
235
|
-
}),
|
|
250
|
+
body: body.toString(),
|
|
236
251
|
});
|
|
237
252
|
if (!response.ok) {
|
|
238
|
-
|
|
253
|
+
const errorData = await response.json().catch(() => ({}));
|
|
254
|
+
throw new Error(`Token refresh failed: ${response.status} ${response.statusText}. ${JSON.stringify(errorData)}`);
|
|
239
255
|
}
|
|
240
256
|
const tokens = await response.json();
|
|
241
257
|
// Update the config with new tokens
|
package/dist/modules/people.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { PcoHttpClient } from '../core/http';
|
|
|
6
6
|
import type { PaginationHelper } from '../core/pagination';
|
|
7
7
|
import type { PcoEventEmitter } from '../monitoring';
|
|
8
8
|
import type { PaginationOptions, PaginationResult } from '../core/pagination';
|
|
9
|
-
import type { PersonResource, EmailResource, EmailAttributes, PhoneNumberResource, PhoneNumberAttributes, AddressResource, AddressAttributes, SocialProfileResource, SocialProfileAttributes } from '../types';
|
|
9
|
+
import type { PersonResource, EmailResource, EmailAttributes, PhoneNumberResource, PhoneNumberAttributes, AddressResource, AddressAttributes, SocialProfileResource, SocialProfileAttributes, CampusResource, HouseholdResource } from '../types';
|
|
10
10
|
export interface PeopleListOptions {
|
|
11
11
|
where?: Record<string, any>;
|
|
12
12
|
include?: string[];
|
|
@@ -86,6 +86,94 @@ export declare class PeopleModule extends BaseModule {
|
|
|
86
86
|
* Delete a person
|
|
87
87
|
*/
|
|
88
88
|
delete(id: string): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Get a person's primary campus
|
|
91
|
+
*/
|
|
92
|
+
getPrimaryCampus(personId: string): Promise<CampusResource | null>;
|
|
93
|
+
/**
|
|
94
|
+
* Set a person's primary campus
|
|
95
|
+
*/
|
|
96
|
+
setPrimaryCampus(personId: string, campusId: string): Promise<PersonResource>;
|
|
97
|
+
/**
|
|
98
|
+
* Remove a person's primary campus
|
|
99
|
+
*/
|
|
100
|
+
removePrimaryCampus(personId: string): Promise<PersonResource>;
|
|
101
|
+
/**
|
|
102
|
+
* Get a person's household
|
|
103
|
+
*/
|
|
104
|
+
getHousehold(personId: string): Promise<HouseholdResource | null>;
|
|
105
|
+
/**
|
|
106
|
+
* Set a person's household
|
|
107
|
+
*/
|
|
108
|
+
setHousehold(personId: string, householdId: string): Promise<PersonResource>;
|
|
109
|
+
/**
|
|
110
|
+
* Remove a person from their household
|
|
111
|
+
*/
|
|
112
|
+
removeFromHousehold(personId: string): Promise<PersonResource>;
|
|
113
|
+
/**
|
|
114
|
+
* Get all people in a specific household
|
|
115
|
+
*/
|
|
116
|
+
getHouseholdMembers(householdId: string, options?: PeopleListOptions): Promise<{
|
|
117
|
+
data: PersonResource[];
|
|
118
|
+
meta?: any;
|
|
119
|
+
links?: any;
|
|
120
|
+
}>;
|
|
121
|
+
/**
|
|
122
|
+
* Get people by campus
|
|
123
|
+
*/
|
|
124
|
+
getByCampus(campusId: string, options?: PeopleListOptions): Promise<{
|
|
125
|
+
data: PersonResource[];
|
|
126
|
+
meta?: any;
|
|
127
|
+
links?: any;
|
|
128
|
+
}>;
|
|
129
|
+
/**
|
|
130
|
+
* Get a person's workflow cards
|
|
131
|
+
*/
|
|
132
|
+
getWorkflowCards(personId: string, options?: {
|
|
133
|
+
include?: string[];
|
|
134
|
+
perPage?: number;
|
|
135
|
+
page?: number;
|
|
136
|
+
}): Promise<{
|
|
137
|
+
data: any[];
|
|
138
|
+
meta?: any;
|
|
139
|
+
links?: any;
|
|
140
|
+
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Get a person's notes
|
|
143
|
+
*/
|
|
144
|
+
getNotes(personId: string, options?: {
|
|
145
|
+
include?: string[];
|
|
146
|
+
perPage?: number;
|
|
147
|
+
page?: number;
|
|
148
|
+
}): Promise<{
|
|
149
|
+
data: any[];
|
|
150
|
+
meta?: any;
|
|
151
|
+
links?: any;
|
|
152
|
+
}>;
|
|
153
|
+
/**
|
|
154
|
+
* Get a person's field data
|
|
155
|
+
*/
|
|
156
|
+
getFieldData(personId: string, options?: {
|
|
157
|
+
include?: string[];
|
|
158
|
+
perPage?: number;
|
|
159
|
+
page?: number;
|
|
160
|
+
}): Promise<{
|
|
161
|
+
data: any[];
|
|
162
|
+
meta?: any;
|
|
163
|
+
links?: any;
|
|
164
|
+
}>;
|
|
165
|
+
/**
|
|
166
|
+
* Get a person's social profiles
|
|
167
|
+
*/
|
|
168
|
+
getSocialProfiles(personId: string, options?: {
|
|
169
|
+
include?: string[];
|
|
170
|
+
perPage?: number;
|
|
171
|
+
page?: number;
|
|
172
|
+
}): Promise<{
|
|
173
|
+
data: any[];
|
|
174
|
+
meta?: any;
|
|
175
|
+
links?: any;
|
|
176
|
+
}>;
|
|
89
177
|
/**
|
|
90
178
|
* Find or create a person with smart matching
|
|
91
179
|
*/
|
|
@@ -164,14 +252,6 @@ export declare class PeopleModule extends BaseModule {
|
|
|
164
252
|
* Delete a person's address
|
|
165
253
|
*/
|
|
166
254
|
deleteAddress(personId: string, addressId: string): Promise<void>;
|
|
167
|
-
/**
|
|
168
|
-
* Get person's social profiles
|
|
169
|
-
*/
|
|
170
|
-
getSocialProfiles(personId: string): Promise<{
|
|
171
|
-
data: SocialProfileResource[];
|
|
172
|
-
meta?: any;
|
|
173
|
-
links?: any;
|
|
174
|
-
}>;
|
|
175
255
|
/**
|
|
176
256
|
* Add a social profile to a person
|
|
177
257
|
*/
|
package/dist/modules/people.js
CHANGED
|
@@ -75,6 +75,209 @@ class PeopleModule extends base_1.BaseModule {
|
|
|
75
75
|
async delete(id) {
|
|
76
76
|
return this.deleteResource(`/people/${id}`);
|
|
77
77
|
}
|
|
78
|
+
// ===== Relationship Management =====
|
|
79
|
+
/**
|
|
80
|
+
* Get a person's primary campus
|
|
81
|
+
*/
|
|
82
|
+
async getPrimaryCampus(personId) {
|
|
83
|
+
const person = await this.getById(personId, ['primary_campus']);
|
|
84
|
+
const campusData = person.relationships?.primary_campus?.data;
|
|
85
|
+
if (!campusData || Array.isArray(campusData) || !campusData.id) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
// Get the full campus resource
|
|
89
|
+
return this.httpClient.request({
|
|
90
|
+
method: 'GET',
|
|
91
|
+
endpoint: `/campuses/${campusData.id}`
|
|
92
|
+
}).then(response => response.data);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Set a person's primary campus
|
|
96
|
+
*/
|
|
97
|
+
async setPrimaryCampus(personId, campusId) {
|
|
98
|
+
return this.httpClient.request({
|
|
99
|
+
method: 'PATCH',
|
|
100
|
+
endpoint: `/people/${personId}`,
|
|
101
|
+
data: {
|
|
102
|
+
data: {
|
|
103
|
+
type: 'Person',
|
|
104
|
+
id: personId,
|
|
105
|
+
attributes: {
|
|
106
|
+
primary_campus_id: campusId
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}).then(response => response.data);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Remove a person's primary campus
|
|
114
|
+
*/
|
|
115
|
+
async removePrimaryCampus(personId) {
|
|
116
|
+
return this.httpClient.request({
|
|
117
|
+
method: 'PATCH',
|
|
118
|
+
endpoint: `/people/${personId}`,
|
|
119
|
+
data: {
|
|
120
|
+
data: {
|
|
121
|
+
type: 'Person',
|
|
122
|
+
id: personId,
|
|
123
|
+
attributes: {
|
|
124
|
+
primary_campus_id: null
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}).then(response => response.data);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get a person's household
|
|
132
|
+
*/
|
|
133
|
+
async getHousehold(personId) {
|
|
134
|
+
const person = await this.getById(personId, ['household']);
|
|
135
|
+
const householdData = person.relationships?.household?.data;
|
|
136
|
+
if (!householdData || Array.isArray(householdData) || !householdData.id) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
// Get the full household resource
|
|
140
|
+
return this.httpClient.request({
|
|
141
|
+
method: 'GET',
|
|
142
|
+
endpoint: `/households/${householdData.id}`
|
|
143
|
+
}).then(response => response.data);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Set a person's household
|
|
147
|
+
*/
|
|
148
|
+
async setHousehold(personId, householdId) {
|
|
149
|
+
return this.httpClient.request({
|
|
150
|
+
method: 'PATCH',
|
|
151
|
+
endpoint: `/people/${personId}`,
|
|
152
|
+
data: {
|
|
153
|
+
data: {
|
|
154
|
+
type: 'Person',
|
|
155
|
+
id: personId,
|
|
156
|
+
attributes: {
|
|
157
|
+
household_id: householdId
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}).then(response => response.data);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Remove a person from their household
|
|
165
|
+
*/
|
|
166
|
+
async removeFromHousehold(personId) {
|
|
167
|
+
return this.httpClient.request({
|
|
168
|
+
method: 'PATCH',
|
|
169
|
+
endpoint: `/people/${personId}`,
|
|
170
|
+
data: {
|
|
171
|
+
data: {
|
|
172
|
+
type: 'Person',
|
|
173
|
+
id: personId,
|
|
174
|
+
attributes: {
|
|
175
|
+
household_id: null
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}).then(response => response.data);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get all people in a specific household
|
|
183
|
+
*/
|
|
184
|
+
async getHouseholdMembers(householdId, options = {}) {
|
|
185
|
+
const params = {
|
|
186
|
+
'where[household_id]': householdId
|
|
187
|
+
};
|
|
188
|
+
if (options.include) {
|
|
189
|
+
params.include = options.include.join(',');
|
|
190
|
+
}
|
|
191
|
+
if (options.perPage) {
|
|
192
|
+
params.per_page = options.perPage;
|
|
193
|
+
}
|
|
194
|
+
if (options.page) {
|
|
195
|
+
params.page = options.page;
|
|
196
|
+
}
|
|
197
|
+
return this.getList('/people', params);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get people by campus
|
|
201
|
+
*/
|
|
202
|
+
async getByCampus(campusId, options = {}) {
|
|
203
|
+
const params = {
|
|
204
|
+
'where[primary_campus_id]': campusId
|
|
205
|
+
};
|
|
206
|
+
if (options.include) {
|
|
207
|
+
params.include = options.include.join(',');
|
|
208
|
+
}
|
|
209
|
+
if (options.perPage) {
|
|
210
|
+
params.per_page = options.perPage;
|
|
211
|
+
}
|
|
212
|
+
if (options.page) {
|
|
213
|
+
params.page = options.page;
|
|
214
|
+
}
|
|
215
|
+
return this.getList('/people', params);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get a person's workflow cards
|
|
219
|
+
*/
|
|
220
|
+
async getWorkflowCards(personId, options = {}) {
|
|
221
|
+
const params = {};
|
|
222
|
+
if (options.include) {
|
|
223
|
+
params.include = options.include.join(',');
|
|
224
|
+
}
|
|
225
|
+
if (options.perPage) {
|
|
226
|
+
params.per_page = options.perPage;
|
|
227
|
+
}
|
|
228
|
+
if (options.page) {
|
|
229
|
+
params.page = options.page;
|
|
230
|
+
}
|
|
231
|
+
return this.getList(`/people/${personId}/workflow_cards`, params);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get a person's notes
|
|
235
|
+
*/
|
|
236
|
+
async getNotes(personId, options = {}) {
|
|
237
|
+
const params = {};
|
|
238
|
+
if (options.include) {
|
|
239
|
+
params.include = options.include.join(',');
|
|
240
|
+
}
|
|
241
|
+
if (options.perPage) {
|
|
242
|
+
params.per_page = options.perPage;
|
|
243
|
+
}
|
|
244
|
+
if (options.page) {
|
|
245
|
+
params.page = options.page;
|
|
246
|
+
}
|
|
247
|
+
return this.getList(`/people/${personId}/notes`, params);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get a person's field data
|
|
251
|
+
*/
|
|
252
|
+
async getFieldData(personId, options = {}) {
|
|
253
|
+
const params = {};
|
|
254
|
+
if (options.include) {
|
|
255
|
+
params.include = options.include.join(',');
|
|
256
|
+
}
|
|
257
|
+
if (options.perPage) {
|
|
258
|
+
params.per_page = options.perPage;
|
|
259
|
+
}
|
|
260
|
+
if (options.page) {
|
|
261
|
+
params.page = options.page;
|
|
262
|
+
}
|
|
263
|
+
return this.getList(`/people/${personId}/field_data`, params);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Get a person's social profiles
|
|
267
|
+
*/
|
|
268
|
+
async getSocialProfiles(personId, options = {}) {
|
|
269
|
+
const params = {};
|
|
270
|
+
if (options.include) {
|
|
271
|
+
params.include = options.include.join(',');
|
|
272
|
+
}
|
|
273
|
+
if (options.perPage) {
|
|
274
|
+
params.per_page = options.perPage;
|
|
275
|
+
}
|
|
276
|
+
if (options.page) {
|
|
277
|
+
params.page = options.page;
|
|
278
|
+
}
|
|
279
|
+
return this.getList(`/people/${personId}/social_profiles`, params);
|
|
280
|
+
}
|
|
78
281
|
/**
|
|
79
282
|
* Find or create a person with smart matching
|
|
80
283
|
*/
|
|
@@ -176,12 +379,6 @@ class PeopleModule extends base_1.BaseModule {
|
|
|
176
379
|
async deleteAddress(personId, addressId) {
|
|
177
380
|
return this.deleteResource(`/people/${personId}/addresses/${addressId}`);
|
|
178
381
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Get person's social profiles
|
|
181
|
-
*/
|
|
182
|
-
async getSocialProfiles(personId) {
|
|
183
|
-
return this.getList(`/people/${personId}/social_profiles`);
|
|
184
|
-
}
|
|
185
382
|
/**
|
|
186
383
|
* Add a social profile to a person
|
|
187
384
|
*/
|
package/dist/people/fields.js
CHANGED
|
@@ -54,15 +54,7 @@ async function getPersonFieldData(client, personId, context) {
|
|
|
54
54
|
*/
|
|
55
55
|
async function createPersonFileFieldDataInternal(client, personId, fieldDefinitionId, fileUrl, context) {
|
|
56
56
|
return (0, error_handling_1.withErrorBoundary)(async () => {
|
|
57
|
-
//
|
|
58
|
-
let axios;
|
|
59
|
-
try {
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
61
|
-
axios = require('axios');
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
throw new Error('axios package is required for file uploads. Please install it: npm install axios');
|
|
65
|
-
}
|
|
57
|
+
// No external dependencies needed - using native fetch API
|
|
66
58
|
// Extract filename from URL
|
|
67
59
|
const urlParts = fileUrl.split('/');
|
|
68
60
|
const filename = urlParts[urlParts.length - 1] ?? 'file';
|
|
@@ -87,11 +79,17 @@ async function createPersonFileFieldDataInternal(client, personId, fieldDefiniti
|
|
|
87
79
|
};
|
|
88
80
|
return mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
|
|
89
81
|
};
|
|
90
|
-
// Download the file from the provided URL
|
|
91
|
-
const fileResponse = await
|
|
92
|
-
|
|
93
|
-
|
|
82
|
+
// Download the file from the provided URL using native fetch
|
|
83
|
+
const fileResponse = await fetch(fileUrl, {
|
|
84
|
+
method: 'GET',
|
|
85
|
+
headers: {
|
|
86
|
+
'Accept': '*/*',
|
|
87
|
+
},
|
|
94
88
|
});
|
|
89
|
+
if (!fileResponse.ok) {
|
|
90
|
+
throw new Error(`Failed to download file: ${fileResponse.status} ${fileResponse.statusText}`);
|
|
91
|
+
}
|
|
92
|
+
const fileBuffer = await fileResponse.arrayBuffer();
|
|
95
93
|
// Step 1: Upload to PCO's upload service first
|
|
96
94
|
let FormDataConstructor;
|
|
97
95
|
try {
|
|
@@ -104,24 +102,27 @@ async function createPersonFileFieldDataInternal(client, personId, fieldDefiniti
|
|
|
104
102
|
throw new Error('form-data package is required for file uploads. Please install it: npm install form-data');
|
|
105
103
|
}
|
|
106
104
|
const uploadFormData = new FormDataConstructor();
|
|
107
|
-
uploadFormData.append('file',
|
|
105
|
+
uploadFormData.append('file', Buffer.from(fileBuffer), {
|
|
108
106
|
contentType: getMimeType(extension),
|
|
109
107
|
filename,
|
|
110
108
|
});
|
|
111
|
-
//
|
|
112
|
-
const
|
|
109
|
+
// Upload to PCO's upload service using native fetch
|
|
110
|
+
const uploadResponse = await fetch('https://upload.planningcenteronline.com/v2/files', {
|
|
111
|
+
method: 'POST',
|
|
113
112
|
headers: {
|
|
113
|
+
...uploadFormData.getHeaders(),
|
|
114
114
|
Authorization: client.config.accessToken
|
|
115
115
|
? `Bearer ${client.config.accessToken}`
|
|
116
116
|
: `Basic ${Buffer.from(`${client.config.appId}:${client.config.appSecret}`).toString('base64')}`,
|
|
117
117
|
},
|
|
118
|
+
body: uploadFormData,
|
|
118
119
|
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
if (!uploadResponse.ok) {
|
|
121
|
+
throw new Error(`Failed to upload file: ${uploadResponse.status} ${uploadResponse.statusText}`);
|
|
122
|
+
}
|
|
123
|
+
const uploadData = await uploadResponse.json();
|
|
123
124
|
// Step 2: Get the file UUID from the response
|
|
124
|
-
const fileUUID =
|
|
125
|
+
const fileUUID = uploadData?.data?.[0]?.id;
|
|
125
126
|
if (!fileUUID) {
|
|
126
127
|
throw new Error('Failed to get file UUID from upload response');
|
|
127
128
|
}
|
package/dist/types/client.d.ts
CHANGED
|
@@ -16,9 +16,19 @@ export interface OAuthAuth {
|
|
|
16
16
|
refreshToken: string;
|
|
17
17
|
}) => void | Promise<void>;
|
|
18
18
|
onRefreshFailure: (error: Error) => void | Promise<void>;
|
|
19
|
+
/** Client ID for token refresh (optional, can use environment variable PCO_APP_ID) */
|
|
20
|
+
clientId?: string;
|
|
21
|
+
/** Client Secret for token refresh (optional, can use environment variable PCO_APP_SECRET) */
|
|
22
|
+
clientSecret?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Authentication configuration for Basic Auth with app credentials */
|
|
25
|
+
export interface BasicAuth {
|
|
26
|
+
type: 'basic';
|
|
27
|
+
appId: string;
|
|
28
|
+
appSecret: string;
|
|
19
29
|
}
|
|
20
30
|
/** Union type for authentication configurations */
|
|
21
|
-
export type PcoAuthConfig = PersonalAccessTokenAuth | OAuthAuth;
|
|
31
|
+
export type PcoAuthConfig = PersonalAccessTokenAuth | OAuthAuth | BasicAuth;
|
|
22
32
|
export interface PcoClientConfig {
|
|
23
33
|
/** Authentication configuration */
|
|
24
34
|
auth: PcoAuthConfig;
|
package/dist/types/people.d.ts
CHANGED
|
@@ -39,8 +39,14 @@ export interface PersonAttributes extends Attributes {
|
|
|
39
39
|
export interface PersonRelationships {
|
|
40
40
|
emails?: Relationship;
|
|
41
41
|
phone_numbers?: Relationship;
|
|
42
|
+
addresses?: Relationship;
|
|
43
|
+
household?: Relationship;
|
|
42
44
|
primary_campus?: Relationship;
|
|
43
45
|
gender?: Relationship;
|
|
46
|
+
workflow_cards?: Relationship;
|
|
47
|
+
notes?: Relationship;
|
|
48
|
+
field_data?: Relationship;
|
|
49
|
+
social_profiles?: Relationship;
|
|
44
50
|
}
|
|
45
51
|
export interface PersonResource extends ResourceObject<'Person', PersonAttributes, PersonRelationships> {
|
|
46
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rachelallyson/planning-center-people-ts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
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",
|
|
@@ -72,7 +72,6 @@
|
|
|
72
72
|
"access": "public"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"axios": "^1.12.2",
|
|
76
75
|
"form-data": "^4.0.4"
|
|
77
76
|
}
|
|
78
77
|
}
|