/
Terra.ts
314 lines (293 loc) · 11.5 KB
/
Terra.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
import { Activity } from '../models/Activity';
import { dataType, GetData, TerraDataResponse } from './Data';
import { TerraAthleteResponse, GetAthlete } from './Athlete';
import { GenerateWidgetSession, TerraWidgetResponse } from './GenerateWidgetSessions';
import { GenerateAuthToken, TerraAuthTokenResponse } from './GenerateAuthToken';
import { GetProviders, TerraProvidersResponse } from './Providers';
import { GetSubscribers, TerraSubscriptionsResponse } from './Subscribers';
import { DeauthUser, GetUser, TerraUserResponse } from './UserInfo';
import { Body } from '../models/Body';
import { Sleep } from '../models/Sleep';
import { Daily } from '../models/Daily';
import { Nutrition } from '../models/Nutrition';
import { Menstruation } from '../models/Menstruation';
import { checkForServerSideAndWarn, CheckTerraSignature, RequestWrapper } from './Helpers';
import { AuthUser, TerraAuthUserResponse } from './AuthUser';
import { Mutex } from 'async-mutex';
import { TerraUser } from '../models/TerraUser';
export class Terra {
private devID: string;
private apiKey: string;
private secret: string;
private mutex: Mutex = new Mutex();
constructor(devID: string, apiKey: string, secret: string) {
checkForServerSideAndWarn();
this.devID = devID;
this.apiKey = apiKey;
this.secret = secret;
}
/**
* Generate an Auth Token to be used with the SDK initialization
*
* @returns {Promise<TerraAuthTokenResponse>} A promise of type Auth Token Response
*/
generateAuthToken(): Promise<TerraAuthTokenResponse> {
return GenerateAuthToken(this.devID, this.apiKey);
}
/**
* Generate an Auth URL to be authenticate a user
*
* @param {string} referenceID - Terra user ID
* @param {string} resource - Wearable provider
* @param {string} language - Language the widget page is showed in
* @param {string} authSuccessRedirectUrl - Redirect URL when the session succeeds
* @param {string} authFailureRedirectUrl - Redirect URL when the session fails
* @param {string} facilityId - Facility ID for applicable resource (e.g. INBODY)
*
* @returns {Promise<TerraAuthUserResponse>} A promise of type Authenticate User Response
*/
authUser(params: {
resource: string;
referenceId?: string;
language?: string;
authSuccessRedirectUrl?: string;
authFailureRedirectUrl?: string;
facilityId?: string;
}): Promise<TerraAuthUserResponse> {
return AuthUser(
this.devID,
this.apiKey,
params.resource,
params.referenceId,
params.language,
params.authSuccessRedirectUrl,
params.authFailureRedirectUrl,
params.facilityId,
);
}
/**
* Generate a widget session
*
* @param {string} referenceID - Terra user ID
* @param {string[]} providers - Array of strings for wearable providers
* @param {string} language - Language the widget page is showed in
* @param {string} authSuccessRedirectUrl - Redirect URL when the session succeeds
* @param {string} authFailureRedirectUrl - Redirect URL when the session fails
* @param {boolean} showDisconnect - Show disconnect button on widget for same reference ID
*
* @returns {Promise<TerraWidgetResponse>} A promise of type Widget Response
*/
generateWidgetSession(params: {
referenceID: string;
language: string;
providers?: string[];
authSuccessRedirectUrl?: string;
authFailureRedirectUrl?: string;
showDisconnect?: boolean;
}): Promise<TerraWidgetResponse> {
return GenerateWidgetSession(
this.devID,
this.apiKey,
params.referenceID,
params.language,
params.providers,
params.authSuccessRedirectUrl,
params.authFailureRedirectUrl,
params.showDisconnect,
);
}
/**
* Get available Terra providers
*
* @return {Promise<TerraProvidersResponse>} A promise of type Providers
*
*/
getProviders(): Promise<TerraProvidersResponse> {
return GetProviders();
}
/**
* Get all subscribed users
*
* @return {Promise<TerraSubscriptionsResponse>} A promise of type Subscriptions
*
*/
getUsers(): Promise<TerraSubscriptionsResponse> {
return GetSubscribers(this.devID, this.apiKey);
}
/**
* Get information about a Terra user
*
* @param {string} userID - Terra user ID
*
* @return {Promise<TerraUserResponse>} A promise of type User
*
*/
getUser(params: { userID?: string; referenceID?: string }): Promise<TerraUserResponse> {
return GetUser(this.devID, this.apiKey, params.userID, params.referenceID);
}
/**
* Deauthenticate Terra user
*
* @param {string} userID - Terra user ID
*
* @return {Promise<void>} A promise fulfilled when deauth succeeds
*
*/
deauthUser(userID: string): Promise<void> {
return DeauthUser(this.devID, this.apiKey, userID);
}
// Data getters
private getDataWrapper<T>(
type: dataType,
): (params: {
userId: string;
startDate: Date;
endDate?: Date;
toWebhook?: boolean;
withSamples?: boolean;
retryIfRateLimited?: boolean;
}) => Promise<TerraDataResponse<T>> {
return (params: {
userId: string;
startDate: Date;
endDate?: Date;
toWebhook?: boolean;
withSamples?: boolean;
retryIfRateLimited?: boolean;
}) => {
return GetData<T>(
type,
this.devID,
this.apiKey,
params.userId,
params.startDate,
params.endDate,
params.toWebhook,
params.withSamples,
params.retryIfRateLimited,
);
};
}
/**
* Get Athlete data for current user
* @param {boolean} toWebhook - True for sending data to webhook and false for sending data in response body
*
* @return {Promise<TerraAthleteResponse>} A promise of type Athlete Data
*
*/
getAthlete(params: { userId: string; toWebhook?: boolean }): Promise<TerraAthleteResponse> {
return GetAthlete(this.devID, this.apiKey, params.userId, params.toWebhook);
}
/**
* Get Activity data for current user
* @param {boolean} toWebhook - True for sending data to webhook and false for sending data in response body
* @param {Date} startDate - Start date for the date range limit
* @param {Date} endDate - End date for the date range limit
* @param {boolean} withSamples - True for getting samples and false for only getting summaries
* @param {boolean} retryIfRateLimited - True for retrying if rate limited and false for not retrying
* @return {Promise<TerraDataResponse<Activity>>} A promise of type Activity Data
*
*/
getActivity = this.getDataWrapper<Activity>('activity');
/**
* Get Body data for current user
* @param {boolean} toWebhook - True for sending data to webhook and false for sending data in response body
* @param {Date} startDate - Start date for the date range limit
* @param {Date} endDate - End date for the date range limit
* @param {boolean} withSamples - True for getting samples and false for only getting summaries
* @param {boolean} retryIfRateLimited - True for retrying if rate limited and false for not retrying
* @return {Promise<TerraDataResponse<Body>>} A promise of type Body Data
*
*/
getBody = this.getDataWrapper<Body>('body');
/**
* Get Daily data for current user
* @param {boolean} toWebhook - True for sending data to webhook and false for sending data in response body
* @param {Date} startDate - Start date for the date range limit
* @param {Date} endDate - End date for the date range limit
* @param {boolean} withSamples - True for getting samples and false for only getting summaries
* @param {boolean} retryIfRateLimited - True for retrying if rate limited and false for not retrying
* @return {Promise<TerraDataResponse<Daily>>} A promise of type Daily Data
*
*/
getDaily = this.getDataWrapper<Daily>('daily');
/**
* Get Sleep data for current user
* @param {boolean} toWebhook - True for sending data to webhook and false for sending data in response body
* @param {Date} startDate - Start date for the date range limit
* @param {Date} endDate - End date for the date range limit
* @param {boolean} withSamples - True for getting samples and false for only getting summaries
* @param {boolean} retryIfRateLimited - True for retrying if rate limited and false for not retrying
* @return {Promise<TerraDataResponse<Sleep>>} A promise of type Sleep Data
*
*/
getSleep = this.getDataWrapper<Sleep>('sleep');
/**
* Get Nutrition data for current user
* @param {boolean} toWebhook - True for sending data to webhook and false for sending data in response body
* @param {Date} startDate - Start date for the date range limit
* @param {Date} endDate - End date for the date range limit
* @param {boolean} withSamples - True for getting samples and false for only getting summaries
* @param {boolean} retryIfRateLimited - True for retrying if rate limited and false for not retrying
* @return {Promise<TerraDataResponse<Nutrition>>} A promise of type Nutrition Data
*
*/
getNutrition = this.getDataWrapper<Nutrition>('nutrition');
/**
* Get Menstruation data for current user
* @param {boolean} toWebhook - True for sending data to webhook and false for sending data in response body
* @param {Date} startDate - Start date for the date range limit
* @param {Date} endDate - End date for the date range limit
* @param {boolean} withSamples - True for getting samples and false for only getting summaries
* @param {boolean} retryIfRateLimited - True for retrying if rate limited and false for not retrying
* @return {Promise<TerraDataResponse<Menstruation>>} A promise of type Menstruation Data
*
*/
getMenstruation = this.getDataWrapper<Menstruation>('menstruation');
/**
* Checks webhook signature
* @param {string} terraSignature - Terra signature string found in header of request sent to webhook endpoint
* @param {ReqBody} payload - Body of request sent to webhook endpoint
* @param {string} secret - Signing secret used to verify webhook
* @return {Boolean} - A boolean, true if the signature is valid
*
*/
checkTerraSignature(terraSignature: string, payload: string) {
return CheckTerraSignature(terraSignature, payload, this.secret);
}
/**
* Checks webhook signature
* @param {string} userId - The user ID to patch for
* @param {String} referenceId - The reference ID to set the userID to (or null to not change)
* @param {boolean} setActive - Set if the user is active or not
* @return {Promise<TerraUser>} - The new patched Terra user object.
*/
patchUser(userId: string, referenceId: string | null, setActive: boolean): Promise<TerraUser> {
const requestOptions = {
method: 'PATCH',
headers: {
'X-API-Key': this.apiKey,
'dev-id': this.devID,
'Content-Type': 'application/json',
},
body: JSON.stringify({ active: setActive, reference_id: referenceId }),
};
return RequestWrapper<TerraUser>(`users/${userId}`, requestOptions);
}
/**
* Synchronises a set of functions. It is meant to be used to handle webhooks if
* the infrastructure could face race conditions
* @param {(...args: any[]) => void} callback - Function to be executed, usually the webhook handler
* @param {any[]} args - Extra arguments passed to the handler, usually the request contents
*/
executeSynchronously(callback: (...args: any[]) => void, ...args: any[]) {
this.mutex.acquire().then(async (release) => {
try {
await callback(...args);
release();
} catch (e) {
release();
}
});
}
}