lamp-core-lst 2025.11.1-9.3 → 2025.11.2-6.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/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/model/Activity.d.ts +4 -0
- package/dist/model/ResearcherSettings.d.ts +12 -0
- package/dist/service/Activity.service.d.ts +8 -1
- package/dist/service/Activity.service.js +50 -7
- package/dist/service/Fetch.js +23 -10
- package/dist/service/ImageUpload.service.d.ts +14 -0
- package/dist/service/ImageUpload.service.js +108 -0
- package/dist/service/ResearcherSettings.d.ts +69 -0
- package/dist/service/ResearcherSettings.js +12 -0
- package/dist/service/ResearcherSettings.service.js +1 -1
- package/dist/service/index.d.ts +1 -0
- package/dist/service/index.js +1 -0
- package/package.json +1 -1
- package/src/index.ts +3 -0
- package/src/model/Activity.ts +5 -0
- package/src/model/ResearcherSettings.ts +12 -0
- package/src/service/Activity.service.ts +58 -4
- package/src/service/Fetch.ts +21 -3
- package/src/service/ImageUpload.service.ts +69 -0
- package/src/service/ResearcherSettings.service.ts +1 -4
- package/src/service/ResearcherSettings.ts +77 -0
- package/src/service/index.ts +1 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "isomorphic-fetch";
|
|
2
2
|
import { Researcher, Participant } from "./model/index";
|
|
3
|
-
import { APIService, ActivityService, ActivityEventService, ActivitySpecService, CredentialService, ParticipantService, ResearcherService, SensorService, SensorEventService, SensorSpecService, StudyService, TypeService, ResearcherSettingsService } from "./service/index";
|
|
3
|
+
import { APIService, ActivityService, ActivityEventService, ActivitySpecService, CredentialService, ParticipantService, ResearcherService, SensorService, SensorEventService, SensorSpecService, StudyService, TypeService, ResearcherSettingsService, ImageUploadService } from "./service/index";
|
|
4
4
|
export * from "./service/index";
|
|
5
5
|
export * from "./model/index";
|
|
6
6
|
/**
|
|
@@ -29,6 +29,7 @@ export default class LAMP {
|
|
|
29
29
|
static SensorEvent: SensorEventService;
|
|
30
30
|
static SensorSpec: SensorSpecService;
|
|
31
31
|
static ResearcherSettings: ResearcherSettingsService;
|
|
32
|
+
static ImageUpload: ImageUploadService;
|
|
32
33
|
private static get configuration();
|
|
33
34
|
private static set configuration(value);
|
|
34
35
|
private static protocol;
|
package/dist/index.js
CHANGED
|
@@ -90,6 +90,7 @@ var LAMP = /** @class */ (function () {
|
|
|
90
90
|
LAMP.Study.configuration = configuration;
|
|
91
91
|
LAMP.Type.configuration = configuration;
|
|
92
92
|
LAMP.ResearcherSettings.configuration = configuration;
|
|
93
|
+
LAMP.ImageUpload.configuration = configuration;
|
|
93
94
|
},
|
|
94
95
|
enumerable: false,
|
|
95
96
|
configurable: true
|
|
@@ -170,6 +171,7 @@ var LAMP = /** @class */ (function () {
|
|
|
170
171
|
LAMP.SensorEvent = new index_1.SensorEventService();
|
|
171
172
|
LAMP.SensorSpec = new index_1.SensorSpecService();
|
|
172
173
|
LAMP.ResearcherSettings = new index_1.ResearcherSettingsService();
|
|
174
|
+
LAMP.ImageUpload = new index_1.ImageUploadService();
|
|
173
175
|
LAMP.protocol = "https://";
|
|
174
176
|
LAMP.Auth = (_b = /** @class */ (function () {
|
|
175
177
|
function class_1() {
|
package/dist/model/Activity.d.ts
CHANGED
|
@@ -73,6 +73,10 @@ export declare class Activity {
|
|
|
73
73
|
* The tab settings for the activity.
|
|
74
74
|
*/
|
|
75
75
|
category?: Tab[] | null;
|
|
76
|
+
/**
|
|
77
|
+
* The photo/image URL for the activity (stored in Azure Blob Storage).
|
|
78
|
+
*/
|
|
79
|
+
photo?: any;
|
|
76
80
|
/**
|
|
77
81
|
* The type of streak for the activity.
|
|
78
82
|
*/
|
|
@@ -43,6 +43,18 @@ export declare class ResearcherSettings {
|
|
|
43
43
|
* The featured activity
|
|
44
44
|
*/
|
|
45
45
|
featuredActivity?: string;
|
|
46
|
+
/**
|
|
47
|
+
* The streak type
|
|
48
|
+
*/
|
|
49
|
+
streakType?: string;
|
|
50
|
+
/**
|
|
51
|
+
* The Streak title
|
|
52
|
+
*/
|
|
53
|
+
streakTitle?: string;
|
|
54
|
+
/**
|
|
55
|
+
* The Streak description
|
|
56
|
+
*/
|
|
57
|
+
streakDesc?: string;
|
|
46
58
|
/**
|
|
47
59
|
* The timestamp
|
|
48
60
|
*/
|
|
@@ -23,8 +23,15 @@ export declare class ActivityService {
|
|
|
23
23
|
/**
|
|
24
24
|
* Get the set of all activities available to participants of a single study, by study identifier.
|
|
25
25
|
* @param studyId
|
|
26
|
+
* @param transform
|
|
27
|
+
* @param ignore_binary
|
|
28
|
+
* @param limit Optional limit for pagination
|
|
29
|
+
* @param offset Optional offset for pagination
|
|
26
30
|
*/
|
|
27
|
-
allByStudy(studyId: Identifier, transform?: string, ignore_binary?: boolean): Promise<Activity[]
|
|
31
|
+
allByStudy(studyId: Identifier, transform?: string, ignore_binary?: boolean, limit?: number, offset?: number): Promise<Activity[] | {
|
|
32
|
+
data: Activity[];
|
|
33
|
+
total: number;
|
|
34
|
+
}>;
|
|
28
35
|
/**
|
|
29
36
|
* Get the set of all activities available to participants of a single study, by participant identifier.
|
|
30
37
|
* @param participantId
|
|
@@ -199,13 +199,17 @@ var ActivityService = /** @class */ (function () {
|
|
|
199
199
|
/**
|
|
200
200
|
* Get the set of all activities available to participants of a single study, by study identifier.
|
|
201
201
|
* @param studyId
|
|
202
|
+
* @param transform
|
|
203
|
+
* @param ignore_binary
|
|
204
|
+
* @param limit Optional limit for pagination
|
|
205
|
+
* @param offset Optional offset for pagination
|
|
202
206
|
*/
|
|
203
|
-
ActivityService.prototype.allByStudy = function (studyId, transform, ignore_binary) {
|
|
204
|
-
var _a
|
|
207
|
+
ActivityService.prototype.allByStudy = function (studyId, transform, ignore_binary, limit, offset) {
|
|
208
|
+
var _a;
|
|
205
209
|
return __awaiter(this, void 0, void 0, function () {
|
|
206
|
-
var auth_4, credential, output;
|
|
207
|
-
return __generator(this, function (
|
|
208
|
-
switch (
|
|
210
|
+
var auth_4, credential, output, total, paginatedOutput, params, queryString, result, activitiesArray, totalCount, mappedActivities;
|
|
211
|
+
return __generator(this, function (_b) {
|
|
212
|
+
switch (_b.label) {
|
|
209
213
|
case 0:
|
|
210
214
|
if (studyId === null || studyId === undefined)
|
|
211
215
|
throw new Error("Required parameter studyId was null or undefined when calling activityAllByStudy.");
|
|
@@ -221,14 +225,53 @@ var ActivityService = /** @class */ (function () {
|
|
|
221
225
|
if (Demo_1.Demo.Study.filter(function (x) { return x["id"] === studyId; }).length > 0) {
|
|
222
226
|
output = (_a = Demo_1.Demo.Activity.filter(function (x) { return x["#parent"] === studyId; })) === null || _a === void 0 ? void 0 : _a.map(function (x) { return Object.assign(new Activity_1.Activity(), x); });
|
|
223
227
|
output = typeof transform === "string" ? jsonata_1.default(transform).evaluate(output) : output;
|
|
228
|
+
// If pagination is requested, return paginated result with total
|
|
229
|
+
if (typeof limit === 'number' && limit > 0 || typeof offset === 'number' && offset > 0) {
|
|
230
|
+
total = output.length;
|
|
231
|
+
paginatedOutput = limit !== undefined && offset !== undefined
|
|
232
|
+
? output.slice(offset, offset + limit)
|
|
233
|
+
: output;
|
|
234
|
+
return [2 /*return*/, Promise.resolve({ data: paginatedOutput, total: total })];
|
|
235
|
+
}
|
|
224
236
|
return [2 /*return*/, Promise.resolve(output)];
|
|
225
237
|
}
|
|
226
238
|
else {
|
|
227
239
|
return [2 /*return*/, Promise.resolve({ error: "404.not-found" })];
|
|
228
240
|
}
|
|
229
241
|
}
|
|
230
|
-
|
|
231
|
-
|
|
242
|
+
params = new URLSearchParams();
|
|
243
|
+
params.append('ignore_binary', String(ignore_binary));
|
|
244
|
+
if (limit !== undefined && limit !== null) {
|
|
245
|
+
params.append('limit', limit.toString());
|
|
246
|
+
}
|
|
247
|
+
if (offset !== undefined && offset !== null) {
|
|
248
|
+
params.append('offset', offset.toString());
|
|
249
|
+
}
|
|
250
|
+
queryString = params.toString();
|
|
251
|
+
return [4 /*yield*/, Fetch_1.Fetch.get("/study/" + studyId + "/activity?" + queryString, this.configuration)
|
|
252
|
+
// Handle the response structure based on _select function behavior:
|
|
253
|
+
// - With pagination (limit > 0 OR offset >= 0): { data: Activity[], total: number }
|
|
254
|
+
// - Without pagination: { data: Activity[] } (total property is missing)
|
|
255
|
+
];
|
|
256
|
+
case 1:
|
|
257
|
+
result = _b.sent();
|
|
258
|
+
activitiesArray = [];
|
|
259
|
+
totalCount = 0;
|
|
260
|
+
if (result && result.data && Array.isArray(result.data)) {
|
|
261
|
+
activitiesArray = result.data;
|
|
262
|
+
totalCount = typeof result.total === 'number' ? result.total : activitiesArray.length;
|
|
263
|
+
}
|
|
264
|
+
else if (Array.isArray(result)) {
|
|
265
|
+
// Legacy fallback: if result is directly an array
|
|
266
|
+
activitiesArray = result;
|
|
267
|
+
totalCount = result.length;
|
|
268
|
+
}
|
|
269
|
+
mappedActivities = activitiesArray.map(function (x) { return Object.assign(new Activity_1.Activity(), x); });
|
|
270
|
+
// If pagination was requested, return { data, total }, otherwise return array for backward compatibility
|
|
271
|
+
if (typeof limit === 'number' && limit > 0 || typeof offset === 'number' && offset > 0) {
|
|
272
|
+
return [2 /*return*/, { data: mappedActivities, total: totalCount }];
|
|
273
|
+
}
|
|
274
|
+
return [2 /*return*/, mappedActivities];
|
|
232
275
|
}
|
|
233
276
|
});
|
|
234
277
|
});
|
package/dist/service/Fetch.js
CHANGED
|
@@ -60,24 +60,37 @@ var handleSessionExpiry = function () { return __awaiter(void 0, void 0, void 0,
|
|
|
60
60
|
});
|
|
61
61
|
}); };
|
|
62
62
|
//If access Token expired then call api for renewing the tokens
|
|
63
|
-
var handleRenewToken = function (refreshToken, base) { return __awaiter(void 0, void 0, void 0, function () {
|
|
64
|
-
var credService, res, accessToken, error_1;
|
|
65
|
-
var _a, _b, _c;
|
|
66
|
-
return __generator(this, function (
|
|
67
|
-
switch (
|
|
63
|
+
var handleRenewToken = function (refreshToken, base, configuration) { return __awaiter(void 0, void 0, void 0, function () {
|
|
64
|
+
var credService, res, accessToken, newRefreshToken, LAMP, identityObject, serverAddress, error_1;
|
|
65
|
+
var _a, _b, _c, _d, _e, _f;
|
|
66
|
+
return __generator(this, function (_g) {
|
|
67
|
+
switch (_g.label) {
|
|
68
68
|
case 0:
|
|
69
|
-
|
|
69
|
+
_g.trys.push([0, 2, , 3]);
|
|
70
70
|
credService = new Credential_service_1.CredentialService();
|
|
71
71
|
return [4 /*yield*/, credService.renewToken(refreshToken, base)];
|
|
72
72
|
case 1:
|
|
73
|
-
res =
|
|
73
|
+
res = _g.sent();
|
|
74
74
|
accessToken = (_a = res === null || res === void 0 ? void 0 : res.data) === null || _a === void 0 ? void 0 : _a.access_token;
|
|
75
75
|
if (accessToken) {
|
|
76
|
-
|
|
76
|
+
newRefreshToken = ((_b = res === null || res === void 0 ? void 0 : res.data) === null || _b === void 0 ? void 0 : _b.refresh_token) || refreshToken;
|
|
77
|
+
sessionStorage.setItem(userTokenKey, JSON.stringify({ accessToken: (_c = res === null || res === void 0 ? void 0 : res.data) === null || _c === void 0 ? void 0 : _c.access_token, refreshToken: newRefreshToken }));
|
|
78
|
+
LAMP = global.LAMP || (typeof window !== "undefined" ? window.LAMP : null);
|
|
79
|
+
if (LAMP && typeof LAMP.dispatchEvent === "function") {
|
|
80
|
+
identityObject = ((_d = LAMP.Auth) === null || _d === void 0 ? void 0 : _d._me) || null;
|
|
81
|
+
serverAddress = (configuration === null || configuration === void 0 ? void 0 : configuration.base) || base || ((_e = LAMP.configuration) === null || _e === void 0 ? void 0 : _e.base);
|
|
82
|
+
LAMP.dispatchEvent("renewToken", {
|
|
83
|
+
authorizationToken: (configuration === null || configuration === void 0 ? void 0 : configuration.authorization) || ((_f = LAMP.configuration) === null || _f === void 0 ? void 0 : _f.authorization),
|
|
84
|
+
identityObject: identityObject,
|
|
85
|
+
serverAddress: serverAddress,
|
|
86
|
+
accessToken: accessToken,
|
|
87
|
+
refreshToken: newRefreshToken,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
77
90
|
}
|
|
78
91
|
return [2 /*return*/, accessToken];
|
|
79
92
|
case 2:
|
|
80
|
-
error_1 =
|
|
93
|
+
error_1 = _g.sent();
|
|
81
94
|
console.log(error_1);
|
|
82
95
|
return [3 /*break*/, 3];
|
|
83
96
|
case 3: return [2 /*return*/];
|
|
@@ -129,7 +142,7 @@ function _fetch(method, route, configuration, body) {
|
|
|
129
142
|
handleSessionExpiry();
|
|
130
143
|
return [2 /*return*/, { data: [], error: "401.invalid-token" }];
|
|
131
144
|
}
|
|
132
|
-
return [4 /*yield*/, handleRenewToken(refreshToken, configuration.base)];
|
|
145
|
+
return [4 /*yield*/, handleRenewToken(refreshToken, configuration.base, configuration)];
|
|
133
146
|
case 5:
|
|
134
147
|
token = _d.sent();
|
|
135
148
|
if (!token) return [3 /*break*/, 17];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Configuration } from "./Fetch";
|
|
2
|
+
export declare class ImageUploadService {
|
|
3
|
+
configuration?: Configuration;
|
|
4
|
+
/**
|
|
5
|
+
* Upload an image file to Azure Blob Storage
|
|
6
|
+
* @param imageType - Type of image: "tip_images", "survey_question_icons", or "activity_images"
|
|
7
|
+
* @param file - The image file to upload
|
|
8
|
+
* @returns Promise with originalUrl and thumbnailUrl
|
|
9
|
+
*/
|
|
10
|
+
uploadImage(imageType: "tip_images" | "survey_question_icons" | "activity_images", file: File): Promise<{
|
|
11
|
+
originalUrl: string;
|
|
12
|
+
thumbnailUrl: string;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (_) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.ImageUploadService = void 0;
|
|
51
|
+
var ImageUploadService = /** @class */ (function () {
|
|
52
|
+
function ImageUploadService() {
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Upload an image file to Azure Blob Storage
|
|
56
|
+
* @param imageType - Type of image: "tip_images", "survey_question_icons", or "activity_images"
|
|
57
|
+
* @param file - The image file to upload
|
|
58
|
+
* @returns Promise with originalUrl and thumbnailUrl
|
|
59
|
+
*/
|
|
60
|
+
ImageUploadService.prototype.uploadImage = function (imageType, file) {
|
|
61
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
62
|
+
var formData, authorization, userTokenFromLocalStore, headers, response, error, result;
|
|
63
|
+
return __generator(this, function (_a) {
|
|
64
|
+
switch (_a.label) {
|
|
65
|
+
case 0:
|
|
66
|
+
if (!file) {
|
|
67
|
+
throw new Error("File is required for image upload");
|
|
68
|
+
}
|
|
69
|
+
if (!["tip_images", "survey_question_icons", "activity_images"].includes(imageType)) {
|
|
70
|
+
throw new Error("Invalid image type. Must be one of: tip_images, survey_question_icons, activity_images");
|
|
71
|
+
}
|
|
72
|
+
if (!this.configuration) {
|
|
73
|
+
throw new Error("Cannot make HTTP request due to invalid configuration.");
|
|
74
|
+
}
|
|
75
|
+
formData = new FormData();
|
|
76
|
+
formData.append("image", file);
|
|
77
|
+
userTokenFromLocalStore = JSON.parse(sessionStorage.getItem("tokenInfo") || "null");
|
|
78
|
+
if (userTokenFromLocalStore === null || userTokenFromLocalStore === void 0 ? void 0 : userTokenFromLocalStore.accessToken) {
|
|
79
|
+
authorization = "Bearer " + (this.configuration.accessToken ? this.configuration.accessToken : userTokenFromLocalStore === null || userTokenFromLocalStore === void 0 ? void 0 : userTokenFromLocalStore.accessToken);
|
|
80
|
+
}
|
|
81
|
+
headers = __assign({ "Access-Control-Allow-Origin": "*", Accept: "application/json" }, (this.configuration.headers || {}));
|
|
82
|
+
if (authorization) {
|
|
83
|
+
headers.Authorization = authorization;
|
|
84
|
+
}
|
|
85
|
+
return [4 /*yield*/, fetch(this.configuration.base + "/upload/image/" + imageType, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: headers,
|
|
88
|
+
credentials: "include",
|
|
89
|
+
body: formData,
|
|
90
|
+
})];
|
|
91
|
+
case 1:
|
|
92
|
+
response = _a.sent();
|
|
93
|
+
if (!!response.ok) return [3 /*break*/, 3];
|
|
94
|
+
return [4 /*yield*/, response.json().catch(function () { return ({ error: "HTTP " + response.status }); })];
|
|
95
|
+
case 2:
|
|
96
|
+
error = _a.sent();
|
|
97
|
+
throw new Error(error.error || "Upload failed with status " + response.status);
|
|
98
|
+
case 3: return [4 /*yield*/, response.json()];
|
|
99
|
+
case 4:
|
|
100
|
+
result = _a.sent();
|
|
101
|
+
return [2 /*return*/, result.data || result];
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
return ImageUploadService;
|
|
107
|
+
}());
|
|
108
|
+
exports.ImageUploadService = ImageUploadService;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Timestamp } from "../model/Type";
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
export declare class ResearcherSettings {
|
|
6
|
+
/**
|
|
7
|
+
* The banner greeting.
|
|
8
|
+
*/
|
|
9
|
+
bannerSettings?: string;
|
|
10
|
+
/**
|
|
11
|
+
* The banner heading
|
|
12
|
+
*/
|
|
13
|
+
bannerHeading?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The banner sub heading
|
|
16
|
+
*/
|
|
17
|
+
bannerSubHeading?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The image icon for banner
|
|
20
|
+
*/
|
|
21
|
+
imageBase64?: string;
|
|
22
|
+
/**
|
|
23
|
+
* The selected activity
|
|
24
|
+
*/
|
|
25
|
+
selectedActivity?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The selected group
|
|
28
|
+
*/
|
|
29
|
+
selectedGroup?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The button text
|
|
32
|
+
*/
|
|
33
|
+
buttonText?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The activityId
|
|
36
|
+
*/
|
|
37
|
+
activityId?: string;
|
|
38
|
+
/**
|
|
39
|
+
* The favourite activities
|
|
40
|
+
*/
|
|
41
|
+
favouriteActivities?: any;
|
|
42
|
+
/**
|
|
43
|
+
* The featured activity
|
|
44
|
+
*/
|
|
45
|
+
featuredActivity?: string;
|
|
46
|
+
/**
|
|
47
|
+
* The streak type
|
|
48
|
+
*/
|
|
49
|
+
streak?: string;
|
|
50
|
+
/**
|
|
51
|
+
* The Streak title
|
|
52
|
+
*/
|
|
53
|
+
streakTitle?: string;
|
|
54
|
+
/**
|
|
55
|
+
* The Streak description
|
|
56
|
+
*/
|
|
57
|
+
streakDesc?: string;
|
|
58
|
+
/**
|
|
59
|
+
* The timestamp
|
|
60
|
+
*/
|
|
61
|
+
timestamp?: Timestamp;
|
|
62
|
+
/**
|
|
63
|
+
* The deleted flag
|
|
64
|
+
*/
|
|
65
|
+
deleted?: boolean;
|
|
66
|
+
}
|
|
67
|
+
export declare type ResearcherBanner = {
|
|
68
|
+
data: ResearcherSettings;
|
|
69
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResearcherSettings = void 0;
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
var ResearcherSettings = /** @class */ (function () {
|
|
8
|
+
function ResearcherSettings() {
|
|
9
|
+
}
|
|
10
|
+
return ResearcherSettings;
|
|
11
|
+
}());
|
|
12
|
+
exports.ResearcherSettings = ResearcherSettings;
|
|
@@ -103,7 +103,7 @@ var ResearcherSettingsService = /** @class */ (function () {
|
|
|
103
103
|
}
|
|
104
104
|
return [4 /*yield*/, Fetch_1.Fetch.get("/participant/researcherSettings/" + participantId, this.configuration)];
|
|
105
105
|
case 1:
|
|
106
|
-
result =
|
|
106
|
+
result = _a.sent();
|
|
107
107
|
return [2 /*return*/, result.data];
|
|
108
108
|
}
|
|
109
109
|
});
|
package/dist/service/index.d.ts
CHANGED
package/dist/service/index.js
CHANGED
|
@@ -23,3 +23,4 @@ __exportStar(require("./SensorSpec.service"), exports);
|
|
|
23
23
|
__exportStar(require("./Study.service"), exports);
|
|
24
24
|
__exportStar(require("./Type.service"), exports);
|
|
25
25
|
__exportStar(require("./API.service"), exports);
|
|
26
|
+
__exportStar(require("./ImageUpload.service"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lamp-core-lst",
|
|
3
|
-
"version": "2025.11.
|
|
3
|
+
"version": "2025.11.2-6.1",
|
|
4
4
|
"author": "BIDMC Division of Digital Psychiatry <team@digitalpsych.org>",
|
|
5
5
|
"description": "The JavaScript and TypeScript API client for the LAMP Platform.",
|
|
6
6
|
"homepage": "https://docs.lamp.digital/",
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
StudyService,
|
|
15
15
|
TypeService,
|
|
16
16
|
ResearcherSettingsService,
|
|
17
|
+
ImageUploadService,
|
|
17
18
|
} from "./service/index"
|
|
18
19
|
import { Configuration } from "./service/Fetch"
|
|
19
20
|
import { Demo } from "./service/Demo"
|
|
@@ -51,6 +52,7 @@ export default class LAMP {
|
|
|
51
52
|
public static SensorEvent = new SensorEventService()
|
|
52
53
|
public static SensorSpec = new SensorSpecService()
|
|
53
54
|
public static ResearcherSettings = new ResearcherSettingsService()
|
|
55
|
+
public static ImageUpload = new ImageUploadService()
|
|
54
56
|
private static get configuration(): Configuration | undefined {
|
|
55
57
|
return LAMP.API.configuration
|
|
56
58
|
}
|
|
@@ -68,6 +70,7 @@ export default class LAMP {
|
|
|
68
70
|
LAMP.Study.configuration = configuration
|
|
69
71
|
LAMP.Type.configuration = configuration
|
|
70
72
|
LAMP.ResearcherSettings.configuration = configuration
|
|
73
|
+
LAMP.ImageUpload.configuration = configuration
|
|
71
74
|
}
|
|
72
75
|
private static protocol = "https://"
|
|
73
76
|
|
package/src/model/Activity.ts
CHANGED
|
@@ -49,6 +49,18 @@ export class ResearcherSettings {
|
|
|
49
49
|
* The featured activity
|
|
50
50
|
*/
|
|
51
51
|
featuredActivity?: string
|
|
52
|
+
/**
|
|
53
|
+
* The streak type
|
|
54
|
+
*/
|
|
55
|
+
streakType?: string
|
|
56
|
+
/**
|
|
57
|
+
* The Streak title
|
|
58
|
+
*/
|
|
59
|
+
streakTitle?: string
|
|
60
|
+
/**
|
|
61
|
+
* The Streak description
|
|
62
|
+
*/
|
|
63
|
+
streakDesc?: string
|
|
52
64
|
/**
|
|
53
65
|
* The timestamp
|
|
54
66
|
*/
|
|
@@ -142,8 +142,18 @@ export class ActivityService {
|
|
|
142
142
|
/**
|
|
143
143
|
* Get the set of all activities available to participants of a single study, by study identifier.
|
|
144
144
|
* @param studyId
|
|
145
|
+
* @param transform
|
|
146
|
+
* @param ignore_binary
|
|
147
|
+
* @param limit Optional limit for pagination
|
|
148
|
+
* @param offset Optional offset for pagination
|
|
145
149
|
*/
|
|
146
|
-
public async allByStudy(
|
|
150
|
+
public async allByStudy(
|
|
151
|
+
studyId: Identifier,
|
|
152
|
+
transform?: string,
|
|
153
|
+
ignore_binary?: boolean,
|
|
154
|
+
limit?: number,
|
|
155
|
+
offset?: number
|
|
156
|
+
): Promise<Activity[] | { data: Activity[], total: number }> {
|
|
147
157
|
if (studyId === null || studyId === undefined)
|
|
148
158
|
throw new Error("Required parameter studyId was null or undefined when calling activityAllByStudy.")
|
|
149
159
|
if (ignore_binary === null || ignore_binary === undefined) ignore_binary = false
|
|
@@ -157,14 +167,58 @@ export class ActivityService {
|
|
|
157
167
|
if (Demo.Study.filter((x) => x["id"] === studyId).length > 0) {
|
|
158
168
|
let output = Demo.Activity.filter((x) => x["#parent"] === studyId)?.map((x) => Object.assign(new Activity(), x))
|
|
159
169
|
output = typeof transform === "string" ? jsonata(transform).evaluate(output) : output
|
|
170
|
+
// If pagination is requested, return paginated result with total
|
|
171
|
+
if (typeof limit === 'number' && limit > 0 || typeof offset === 'number' && offset > 0) {
|
|
172
|
+
const total = output.length
|
|
173
|
+
const paginatedOutput = limit !== undefined && offset !== undefined
|
|
174
|
+
? output.slice(offset, offset + limit)
|
|
175
|
+
: output
|
|
176
|
+
return Promise.resolve({ data: paginatedOutput, total })
|
|
177
|
+
}
|
|
160
178
|
return Promise.resolve(output)
|
|
161
179
|
} else {
|
|
162
180
|
return Promise.resolve({ error: "404.not-found" } as any)
|
|
163
181
|
}
|
|
164
182
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
183
|
+
|
|
184
|
+
// Build query string with pagination parameters
|
|
185
|
+
const params = new URLSearchParams()
|
|
186
|
+
params.append('ignore_binary', String(ignore_binary))
|
|
187
|
+
if (limit !== undefined && limit !== null) {
|
|
188
|
+
params.append('limit', limit.toString())
|
|
189
|
+
}
|
|
190
|
+
if (offset !== undefined && offset !== null) {
|
|
191
|
+
params.append('offset', offset.toString())
|
|
192
|
+
}
|
|
193
|
+
const queryString = params.toString()
|
|
194
|
+
|
|
195
|
+
const result: any = await Fetch.get<{ data: Activity[], total?: number }>(
|
|
196
|
+
`/study/${studyId}/activity?${queryString}`,
|
|
197
|
+
this.configuration
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
// Handle the response structure based on _select function behavior:
|
|
201
|
+
// - With pagination (limit > 0 OR offset >= 0): { data: Activity[], total: number }
|
|
202
|
+
// - Without pagination: { data: Activity[] } (total property is missing)
|
|
203
|
+
let activitiesArray: any[] = []
|
|
204
|
+
let totalCount = 0
|
|
205
|
+
|
|
206
|
+
if (result && result.data && Array.isArray(result.data)) {
|
|
207
|
+
activitiesArray = result.data
|
|
208
|
+
totalCount = typeof result.total === 'number' ? result.total : activitiesArray.length
|
|
209
|
+
} else if (Array.isArray(result)) {
|
|
210
|
+
// Legacy fallback: if result is directly an array
|
|
211
|
+
activitiesArray = result
|
|
212
|
+
totalCount = result.length
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const mappedActivities = activitiesArray.map((x: any) => Object.assign(new Activity(), x))
|
|
216
|
+
|
|
217
|
+
// If pagination was requested, return { data, total }, otherwise return array for backward compatibility
|
|
218
|
+
if (typeof limit === 'number' && limit > 0 || typeof offset === 'number' && offset > 0) {
|
|
219
|
+
return { data: mappedActivities, total: totalCount }
|
|
220
|
+
}
|
|
221
|
+
return mappedActivities
|
|
168
222
|
}
|
|
169
223
|
/**
|
|
170
224
|
* Get the set of all activities available to participants of a single study, by participant identifier.
|
package/src/service/Fetch.ts
CHANGED
|
@@ -38,7 +38,7 @@ const handleSessionExpiry = async () => {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
//If access Token expired then call api for renewing the tokens
|
|
41
|
-
const handleRenewToken = async (refreshToken: string, base: string) => {
|
|
41
|
+
const handleRenewToken = async (refreshToken: string, base: string, configuration?: Configuration) => {
|
|
42
42
|
try {
|
|
43
43
|
const credService = new CredentialService()
|
|
44
44
|
const res = await credService.renewToken(refreshToken, base)
|
|
@@ -46,10 +46,28 @@ const handleRenewToken = async (refreshToken: string, base: string) => {
|
|
|
46
46
|
const accessToken = res?.data?.access_token
|
|
47
47
|
|
|
48
48
|
if (accessToken) {
|
|
49
|
+
const newRefreshToken = res?.data?.refresh_token || refreshToken
|
|
49
50
|
sessionStorage.setItem(
|
|
50
51
|
userTokenKey,
|
|
51
|
-
JSON.stringify({ accessToken: res?.data?.access_token, refreshToken:
|
|
52
|
+
JSON.stringify({ accessToken: res?.data?.access_token, refreshToken: newRefreshToken })
|
|
52
53
|
)
|
|
54
|
+
|
|
55
|
+
// Dispatch renewToken event similar to LOGIN event
|
|
56
|
+
// Access LAMP from global scope to avoid circular dependency
|
|
57
|
+
const LAMP = (global as any).LAMP || (typeof window !== "undefined" ? (window as any).LAMP : null)
|
|
58
|
+
if (LAMP && typeof LAMP.dispatchEvent === "function") {
|
|
59
|
+
// Get identity object from LAMP.Auth if available
|
|
60
|
+
const identityObject = LAMP.Auth?._me || null
|
|
61
|
+
const serverAddress = configuration?.base || base || LAMP.configuration?.base
|
|
62
|
+
|
|
63
|
+
LAMP.dispatchEvent("renewToken", {
|
|
64
|
+
authorizationToken: configuration?.authorization || LAMP.configuration?.authorization,
|
|
65
|
+
identityObject: identityObject,
|
|
66
|
+
serverAddress: serverAddress,
|
|
67
|
+
accessToken: accessToken,
|
|
68
|
+
refreshToken: newRefreshToken,
|
|
69
|
+
})
|
|
70
|
+
}
|
|
53
71
|
}
|
|
54
72
|
return accessToken
|
|
55
73
|
} catch (error) {
|
|
@@ -113,7 +131,7 @@ async function _fetch<ResultType>(
|
|
|
113
131
|
handleSessionExpiry()
|
|
114
132
|
return { data: [], error: "401.invalid-token" } as unknown as ResultType
|
|
115
133
|
}
|
|
116
|
-
const token = await handleRenewToken(refreshToken, configuration.base)
|
|
134
|
+
const token = await handleRenewToken(refreshToken, configuration.base, configuration)
|
|
117
135
|
if (token) {
|
|
118
136
|
configuration.authorization = token
|
|
119
137
|
// retry the same request
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Fetch, Configuration } from "./Fetch"
|
|
2
|
+
import { Identifier } from "../model/Type"
|
|
3
|
+
|
|
4
|
+
export class ImageUploadService {
|
|
5
|
+
public configuration?: Configuration
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Upload an image file to Azure Blob Storage
|
|
9
|
+
* @param imageType - Type of image: "tip_images", "survey_question_icons", or "activity_images"
|
|
10
|
+
* @param file - The image file to upload
|
|
11
|
+
* @returns Promise with originalUrl and thumbnailUrl
|
|
12
|
+
*/
|
|
13
|
+
public async uploadImage(
|
|
14
|
+
imageType: "tip_images" | "survey_question_icons" | "activity_images",
|
|
15
|
+
file: File
|
|
16
|
+
): Promise<{ originalUrl: string; thumbnailUrl: string }> {
|
|
17
|
+
if (!file) {
|
|
18
|
+
throw new Error("File is required for image upload")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!["tip_images", "survey_question_icons", "activity_images"].includes(imageType)) {
|
|
22
|
+
throw new Error("Invalid image type. Must be one of: tip_images, survey_question_icons, activity_images")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!this.configuration) {
|
|
26
|
+
throw new Error("Cannot make HTTP request due to invalid configuration.")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Create FormData for file upload
|
|
30
|
+
const formData = new FormData()
|
|
31
|
+
formData.append("image", file)
|
|
32
|
+
|
|
33
|
+
// Get authorization header - use the same format as Fetch class
|
|
34
|
+
let authorization: string | undefined
|
|
35
|
+
const userTokenFromLocalStore: any = JSON.parse(sessionStorage.getItem("tokenInfo") || "null")
|
|
36
|
+
if (userTokenFromLocalStore?.accessToken) {
|
|
37
|
+
authorization = `Bearer ${
|
|
38
|
+
this.configuration.accessToken ? this.configuration.accessToken : userTokenFromLocalStore?.accessToken
|
|
39
|
+
}`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Build headers - don't set Content-Type for FormData (browser will set it with boundary)
|
|
43
|
+
const headers: HeadersInit = {
|
|
44
|
+
"Access-Control-Allow-Origin": "*",
|
|
45
|
+
Accept: "application/json",
|
|
46
|
+
...(this.configuration.headers || {}),
|
|
47
|
+
}
|
|
48
|
+
if (authorization) {
|
|
49
|
+
headers.Authorization = authorization
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Make POST request with FormData
|
|
53
|
+
const response = await fetch(`${this.configuration.base}/upload/image/${imageType}`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers,
|
|
56
|
+
credentials: "include",
|
|
57
|
+
body: formData,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
const error = await response.json().catch(() => ({ error: `HTTP ${response.status}` }))
|
|
62
|
+
throw new Error(error.error || `Upload failed with status ${response.status}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const result = await response.json()
|
|
66
|
+
return result.data || result
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
@@ -53,10 +53,7 @@ export class ResearcherSettingsService {
|
|
|
53
53
|
// DEMO
|
|
54
54
|
return Promise.resolve({ error: "500.demo-restriction" } as any)
|
|
55
55
|
}
|
|
56
|
-
const result =
|
|
57
|
-
`/participant/researcherSettings/${participantId}`,
|
|
58
|
-
this.configuration
|
|
59
|
-
)) as ResearcherBanner
|
|
56
|
+
const result: any = await Fetch.get(`/participant/researcherSettings/${participantId}`, this.configuration)
|
|
60
57
|
return result.data
|
|
61
58
|
}
|
|
62
59
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Identifier, Timestamp } from "../model/Type"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
export class ResearcherSettings {
|
|
7
|
+
/**
|
|
8
|
+
* The banner greeting.
|
|
9
|
+
*/
|
|
10
|
+
bannerSettings?: string
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The banner heading
|
|
14
|
+
*/
|
|
15
|
+
bannerHeading?: string
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The banner sub heading
|
|
19
|
+
*/
|
|
20
|
+
bannerSubHeading?: string
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The image icon for banner
|
|
24
|
+
*/
|
|
25
|
+
imageBase64?: string
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The selected activity
|
|
29
|
+
*/
|
|
30
|
+
selectedActivity?: string
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The selected group
|
|
34
|
+
*/
|
|
35
|
+
selectedGroup?: string
|
|
36
|
+
/**
|
|
37
|
+
* The button text
|
|
38
|
+
*/
|
|
39
|
+
buttonText?: string
|
|
40
|
+
/**
|
|
41
|
+
* The activityId
|
|
42
|
+
*/
|
|
43
|
+
activityId?: string
|
|
44
|
+
/**
|
|
45
|
+
* The favourite activities
|
|
46
|
+
*/
|
|
47
|
+
favouriteActivities?: any
|
|
48
|
+
/**
|
|
49
|
+
* The featured activity
|
|
50
|
+
*/
|
|
51
|
+
featuredActivity?: string
|
|
52
|
+
/**
|
|
53
|
+
* The streak type
|
|
54
|
+
*/
|
|
55
|
+
streak?: string
|
|
56
|
+
/**
|
|
57
|
+
* The Streak title
|
|
58
|
+
*/
|
|
59
|
+
streakTitle?: string
|
|
60
|
+
/**
|
|
61
|
+
* The Streak description
|
|
62
|
+
*/
|
|
63
|
+
streakDesc?: string
|
|
64
|
+
/**
|
|
65
|
+
* The timestamp
|
|
66
|
+
*/
|
|
67
|
+
timestamp?: Timestamp
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The deleted flag
|
|
71
|
+
*/
|
|
72
|
+
deleted?: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type ResearcherBanner = {
|
|
76
|
+
data: ResearcherSettings
|
|
77
|
+
}
|
package/src/service/index.ts
CHANGED