@terreno/api 0.7.1 → 0.8.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/dist/__tests__/{versionCheck.test.js → versionCheckPlugin.test.js} +2 -2
- package/dist/api.d.ts +4 -2
- package/dist/api.js +7 -2
- package/dist/consentApp.d.ts +33 -0
- package/dist/consentApp.js +484 -0
- package/dist/consentApp.test.d.ts +1 -0
- package/dist/consentApp.test.js +1132 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/models/consentForm.d.ts +2 -0
- package/dist/models/consentForm.js +115 -0
- package/dist/models/consentResponse.d.ts +2 -0
- package/dist/models/consentResponse.js +73 -0
- package/dist/models/versionConfig.d.ts +1 -1
- package/dist/openApiValidator.js +2 -0
- package/dist/populate.d.ts +1 -0
- package/dist/populate.js +53 -13
- package/dist/syncConsents.d.ts +67 -0
- package/dist/syncConsents.js +334 -0
- package/dist/syncConsents.test.d.ts +1 -0
- package/dist/syncConsents.test.js +249 -0
- package/dist/terrenoApp.js +6 -5
- package/dist/terrenoPlugin.d.ts +1 -1
- package/dist/types/consentForm.d.ts +32 -0
- package/dist/types/consentForm.js +2 -0
- package/dist/types/consentResponse.d.ts +23 -0
- package/dist/types/consentResponse.js +2 -0
- package/dist/vendor/wesleytodd-openapi/lib/generate-doc.js +1 -1
- package/dist/versionCheckPlugin.d.ts +2 -0
- package/dist/versionCheckPlugin.js +3 -6
- package/package.json +1 -1
- package/src/__tests__/{versionCheck.test.ts → versionCheckPlugin.test.ts} +2 -2
- package/src/api.ts +11 -4
- package/src/consentApp.test.ts +749 -0
- package/src/consentApp.ts +463 -0
- package/src/index.ts +6 -0
- package/src/models/consentForm.ts +123 -0
- package/src/models/consentResponse.ts +78 -0
- package/src/models/versionConfig.ts +1 -1
- package/src/openApiValidator.ts +2 -0
- package/src/populate.ts +33 -0
- package/src/syncConsents.test.ts +124 -0
- package/src/syncConsents.ts +263 -0
- package/src/terrenoApp.ts +6 -6
- package/src/terrenoPlugin.ts +1 -1
- package/src/types/consentForm.ts +41 -0
- package/src/types/consentResponse.ts +34 -0
- package/src/vendor/wesleytodd-openapi/lib/generate-doc.js +1 -1
- package/src/versionCheckPlugin.ts +5 -6
- /package/dist/__tests__/{versionCheck.test.d.ts → versionCheckPlugin.test.d.ts} +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Sync consent form definitions from code to the database.
|
|
4
|
+
*
|
|
5
|
+
* Compares the provided definitions (keyed by slug) against what's in the database
|
|
6
|
+
* and creates, updates, or deactivates forms to match. When content changes, a new
|
|
7
|
+
* version is published so users are prompted to re-consent.
|
|
8
|
+
*/
|
|
9
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
10
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
11
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
12
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
13
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
14
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
15
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
19
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
20
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
21
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
22
|
+
function step(op) {
|
|
23
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
24
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
25
|
+
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;
|
|
26
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
27
|
+
switch (op[0]) {
|
|
28
|
+
case 0: case 1: t = op; break;
|
|
29
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
30
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
31
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
32
|
+
default:
|
|
33
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
34
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
35
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
36
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
37
|
+
if (t[2]) _.ops.pop();
|
|
38
|
+
_.trys.pop(); continue;
|
|
39
|
+
}
|
|
40
|
+
op = body.call(thisArg, _);
|
|
41
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
42
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
46
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
47
|
+
if (!m) return o;
|
|
48
|
+
var i = m.call(o), r, ar = [], e;
|
|
49
|
+
try {
|
|
50
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
51
|
+
}
|
|
52
|
+
catch (error) { e = { error: error }; }
|
|
53
|
+
finally {
|
|
54
|
+
try {
|
|
55
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
56
|
+
}
|
|
57
|
+
finally { if (e) throw e.error; }
|
|
58
|
+
}
|
|
59
|
+
return ar;
|
|
60
|
+
};
|
|
61
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
62
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
63
|
+
if (ar || !(i in from)) {
|
|
64
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
65
|
+
ar[i] = from[i];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
69
|
+
};
|
|
70
|
+
var __values = (this && this.__values) || function(o) {
|
|
71
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
72
|
+
if (m) return m.call(o);
|
|
73
|
+
if (o && typeof o.length === "number") return {
|
|
74
|
+
next: function () {
|
|
75
|
+
if (o && i >= o.length) o = void 0;
|
|
76
|
+
return { value: o && o[i++], done: !o };
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
80
|
+
};
|
|
81
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
82
|
+
exports.syncConsents = void 0;
|
|
83
|
+
var logger_1 = require("./logger");
|
|
84
|
+
var consentForm_1 = require("./models/consentForm");
|
|
85
|
+
var contentEqual = function (a, b) {
|
|
86
|
+
var aKeys = __spreadArray([], __read(a.keys()), false).sort();
|
|
87
|
+
var bKeys = Object.keys(b).sort();
|
|
88
|
+
if (aKeys.length !== bKeys.length) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return aKeys.every(function (key, i) { return key === bKeys[i] && a.get(key) === b[key]; });
|
|
92
|
+
};
|
|
93
|
+
var formFieldsMatch = function (existing, def) {
|
|
94
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
95
|
+
if (existing.title !== def.title) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (existing.type !== def.type) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (existing.order !== ((_a = def.order) !== null && _a !== void 0 ? _a : 0)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
if (existing.required !== ((_b = def.required) !== null && _b !== void 0 ? _b : true)) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (existing.requireScrollToBottom !== ((_c = def.requireScrollToBottom) !== null && _c !== void 0 ? _c : false)) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (existing.captureSignature !== ((_d = def.captureSignature) !== null && _d !== void 0 ? _d : false)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (existing.agreeButtonText !== ((_e = def.agreeButtonText) !== null && _e !== void 0 ? _e : "I Agree")) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (existing.allowDecline !== ((_f = def.allowDecline) !== null && _f !== void 0 ? _f : false)) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
if (existing.declineButtonText !== ((_g = def.declineButtonText) !== null && _g !== void 0 ? _g : "Decline")) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (existing.defaultLocale !== ((_h = def.defaultLocale) !== null && _h !== void 0 ? _h : "en")) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (!contentEqual(existing.content, def.content)) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
var existingCheckboxes = (_j = existing.checkboxes) !== null && _j !== void 0 ? _j : [];
|
|
129
|
+
var defCheckboxes = (_k = def.checkboxes) !== null && _k !== void 0 ? _k : [];
|
|
130
|
+
if (existingCheckboxes.length !== defCheckboxes.length) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
for (var i = 0; i < existingCheckboxes.length; i++) {
|
|
134
|
+
var ec = existingCheckboxes[i];
|
|
135
|
+
var dc = defCheckboxes[i];
|
|
136
|
+
if (ec.label !== dc.label || ec.required !== ((_l = dc.required) !== null && _l !== void 0 ? _l : false)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
if (((_m = ec.confirmationPrompt) !== null && _m !== void 0 ? _m : undefined) !== ((_o = dc.confirmationPrompt) !== null && _o !== void 0 ? _o : undefined)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Sync consent form definitions to the database.
|
|
147
|
+
*
|
|
148
|
+
* @param definitions - Map of slug to consent form definition
|
|
149
|
+
* @param options - Sync options
|
|
150
|
+
* @returns Summary of what was created, updated, deactivated, or unchanged
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* import {syncConsents} from "@terreno/api";
|
|
155
|
+
*
|
|
156
|
+
* await syncConsents({
|
|
157
|
+
* "terms-of-service": {
|
|
158
|
+
* title: "Terms of Service",
|
|
159
|
+
* type: "terms",
|
|
160
|
+
* content: {"en": "# Terms\n...", "es": "# Términos\n..."},
|
|
161
|
+
* required: true,
|
|
162
|
+
* order: 1,
|
|
163
|
+
* },
|
|
164
|
+
* "privacy-policy": {
|
|
165
|
+
* title: "Privacy Policy",
|
|
166
|
+
* type: "privacy",
|
|
167
|
+
* content: {"en": "# Privacy\n..."},
|
|
168
|
+
* order: 2,
|
|
169
|
+
* },
|
|
170
|
+
* });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
var syncConsents = function (definitions_1) {
|
|
174
|
+
var args_1 = [];
|
|
175
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
176
|
+
args_1[_i - 1] = arguments[_i];
|
|
177
|
+
}
|
|
178
|
+
return __awaiter(void 0, __spreadArray([definitions_1], __read(args_1), false), void 0, function (definitions, options) {
|
|
179
|
+
var _a, deactivateRemoved, _b, dryRun, result, slugs, activeForms, activeBySlug, slugs_1, slugs_1_1, slug, def, existing, newVersion, e_1_1, activeBySlug_1, activeBySlug_1_1, _c, slug, form, e_2_1, summary;
|
|
180
|
+
var e_1, _d, e_2, _e;
|
|
181
|
+
var _f, _g, _h, _j;
|
|
182
|
+
if (options === void 0) { options = {}; }
|
|
183
|
+
return __generator(this, function (_k) {
|
|
184
|
+
switch (_k.label) {
|
|
185
|
+
case 0:
|
|
186
|
+
_a = options.deactivateRemoved, deactivateRemoved = _a === void 0 ? false : _a, _b = options.dryRun, dryRun = _b === void 0 ? false : _b;
|
|
187
|
+
result = {
|
|
188
|
+
created: [],
|
|
189
|
+
deactivated: [],
|
|
190
|
+
unchanged: [],
|
|
191
|
+
updated: [],
|
|
192
|
+
};
|
|
193
|
+
slugs = Object.keys(definitions);
|
|
194
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.find({ active: true })];
|
|
195
|
+
case 1:
|
|
196
|
+
activeForms = _k.sent();
|
|
197
|
+
activeBySlug = new Map(activeForms.map(function (f) { return [f.slug, f]; }));
|
|
198
|
+
_k.label = 2;
|
|
199
|
+
case 2:
|
|
200
|
+
_k.trys.push([2, 12, 13, 14]);
|
|
201
|
+
slugs_1 = __values(slugs), slugs_1_1 = slugs_1.next();
|
|
202
|
+
_k.label = 3;
|
|
203
|
+
case 3:
|
|
204
|
+
if (!!slugs_1_1.done) return [3 /*break*/, 11];
|
|
205
|
+
slug = slugs_1_1.value;
|
|
206
|
+
def = definitions[slug];
|
|
207
|
+
existing = activeBySlug.get(slug);
|
|
208
|
+
if (!!existing) return [3 /*break*/, 6];
|
|
209
|
+
// No active form for this slug — create version 1
|
|
210
|
+
logger_1.logger.info("syncConsents: creating \"".concat(slug, "\""), { dryRun: dryRun });
|
|
211
|
+
if (!!dryRun) return [3 /*break*/, 5];
|
|
212
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.create({
|
|
213
|
+
active: true,
|
|
214
|
+
agreeButtonText: def.agreeButtonText,
|
|
215
|
+
allowDecline: def.allowDecline,
|
|
216
|
+
captureSignature: def.captureSignature,
|
|
217
|
+
checkboxes: def.checkboxes,
|
|
218
|
+
content: new Map(Object.entries(def.content)),
|
|
219
|
+
declineButtonText: def.declineButtonText,
|
|
220
|
+
defaultLocale: def.defaultLocale,
|
|
221
|
+
order: (_f = def.order) !== null && _f !== void 0 ? _f : 0,
|
|
222
|
+
required: (_g = def.required) !== null && _g !== void 0 ? _g : true,
|
|
223
|
+
requireScrollToBottom: def.requireScrollToBottom,
|
|
224
|
+
slug: slug,
|
|
225
|
+
title: def.title,
|
|
226
|
+
type: def.type,
|
|
227
|
+
version: 1,
|
|
228
|
+
})];
|
|
229
|
+
case 4:
|
|
230
|
+
_k.sent();
|
|
231
|
+
_k.label = 5;
|
|
232
|
+
case 5:
|
|
233
|
+
result.created.push(slug);
|
|
234
|
+
return [3 /*break*/, 10];
|
|
235
|
+
case 6:
|
|
236
|
+
if (formFieldsMatch(existing, def)) {
|
|
237
|
+
result.unchanged.push(slug);
|
|
238
|
+
return [3 /*break*/, 10];
|
|
239
|
+
}
|
|
240
|
+
newVersion = existing.version + 1;
|
|
241
|
+
logger_1.logger.info("syncConsents: updating \"".concat(slug, "\" v").concat(existing.version, " -> v").concat(newVersion), {
|
|
242
|
+
dryRun: dryRun,
|
|
243
|
+
});
|
|
244
|
+
if (!!dryRun) return [3 /*break*/, 9];
|
|
245
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.create({
|
|
246
|
+
active: true,
|
|
247
|
+
agreeButtonText: def.agreeButtonText,
|
|
248
|
+
allowDecline: def.allowDecline,
|
|
249
|
+
captureSignature: def.captureSignature,
|
|
250
|
+
checkboxes: def.checkboxes,
|
|
251
|
+
content: new Map(Object.entries(def.content)),
|
|
252
|
+
declineButtonText: def.declineButtonText,
|
|
253
|
+
defaultLocale: def.defaultLocale,
|
|
254
|
+
order: (_h = def.order) !== null && _h !== void 0 ? _h : 0,
|
|
255
|
+
required: (_j = def.required) !== null && _j !== void 0 ? _j : true,
|
|
256
|
+
requireScrollToBottom: def.requireScrollToBottom,
|
|
257
|
+
slug: slug,
|
|
258
|
+
title: def.title,
|
|
259
|
+
type: def.type,
|
|
260
|
+
version: newVersion,
|
|
261
|
+
})];
|
|
262
|
+
case 7:
|
|
263
|
+
_k.sent();
|
|
264
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.updateMany({ _id: { $ne: undefined }, slug: slug, version: { $lt: newVersion } }, { active: false })];
|
|
265
|
+
case 8:
|
|
266
|
+
_k.sent();
|
|
267
|
+
_k.label = 9;
|
|
268
|
+
case 9:
|
|
269
|
+
result.updated.push(slug);
|
|
270
|
+
_k.label = 10;
|
|
271
|
+
case 10:
|
|
272
|
+
slugs_1_1 = slugs_1.next();
|
|
273
|
+
return [3 /*break*/, 3];
|
|
274
|
+
case 11: return [3 /*break*/, 14];
|
|
275
|
+
case 12:
|
|
276
|
+
e_1_1 = _k.sent();
|
|
277
|
+
e_1 = { error: e_1_1 };
|
|
278
|
+
return [3 /*break*/, 14];
|
|
279
|
+
case 13:
|
|
280
|
+
try {
|
|
281
|
+
if (slugs_1_1 && !slugs_1_1.done && (_d = slugs_1.return)) _d.call(slugs_1);
|
|
282
|
+
}
|
|
283
|
+
finally { if (e_1) throw e_1.error; }
|
|
284
|
+
return [7 /*endfinally*/];
|
|
285
|
+
case 14:
|
|
286
|
+
if (!deactivateRemoved) return [3 /*break*/, 23];
|
|
287
|
+
_k.label = 15;
|
|
288
|
+
case 15:
|
|
289
|
+
_k.trys.push([15, 21, 22, 23]);
|
|
290
|
+
activeBySlug_1 = __values(activeBySlug), activeBySlug_1_1 = activeBySlug_1.next();
|
|
291
|
+
_k.label = 16;
|
|
292
|
+
case 16:
|
|
293
|
+
if (!!activeBySlug_1_1.done) return [3 /*break*/, 20];
|
|
294
|
+
_c = __read(activeBySlug_1_1.value, 2), slug = _c[0], form = _c[1];
|
|
295
|
+
if (!!definitions[slug]) return [3 /*break*/, 19];
|
|
296
|
+
logger_1.logger.info("syncConsents: deactivating \"".concat(slug, "\""), { dryRun: dryRun });
|
|
297
|
+
if (!!dryRun) return [3 /*break*/, 18];
|
|
298
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.updateMany({ slug: slug }, { active: false })];
|
|
299
|
+
case 17:
|
|
300
|
+
_k.sent();
|
|
301
|
+
_k.label = 18;
|
|
302
|
+
case 18:
|
|
303
|
+
result.deactivated.push(slug);
|
|
304
|
+
_k.label = 19;
|
|
305
|
+
case 19:
|
|
306
|
+
activeBySlug_1_1 = activeBySlug_1.next();
|
|
307
|
+
return [3 /*break*/, 16];
|
|
308
|
+
case 20: return [3 /*break*/, 23];
|
|
309
|
+
case 21:
|
|
310
|
+
e_2_1 = _k.sent();
|
|
311
|
+
e_2 = { error: e_2_1 };
|
|
312
|
+
return [3 /*break*/, 23];
|
|
313
|
+
case 22:
|
|
314
|
+
try {
|
|
315
|
+
if (activeBySlug_1_1 && !activeBySlug_1_1.done && (_e = activeBySlug_1.return)) _e.call(activeBySlug_1);
|
|
316
|
+
}
|
|
317
|
+
finally { if (e_2) throw e_2.error; }
|
|
318
|
+
return [7 /*endfinally*/];
|
|
319
|
+
case 23:
|
|
320
|
+
summary = [
|
|
321
|
+
result.created.length > 0 ? "created: ".concat(result.created.join(", ")) : null,
|
|
322
|
+
result.updated.length > 0 ? "updated: ".concat(result.updated.join(", ")) : null,
|
|
323
|
+
result.deactivated.length > 0 ? "deactivated: ".concat(result.deactivated.join(", ")) : null,
|
|
324
|
+
result.unchanged.length > 0 ? "unchanged: ".concat(result.unchanged.join(", ")) : null,
|
|
325
|
+
]
|
|
326
|
+
.filter(Boolean)
|
|
327
|
+
.join(" | ");
|
|
328
|
+
logger_1.logger.info("syncConsents: ".concat(dryRun ? "[DRY RUN] " : "").concat(summary || "nothing to do"));
|
|
329
|
+
return [2 /*return*/, result];
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
};
|
|
334
|
+
exports.syncConsents = syncConsents;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,249 @@
|
|
|
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 = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["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 (g && (g = 0, op[0] && (_ = 0)), _) 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
|
+
var bun_test_1 = require("bun:test");
|
|
51
|
+
var consentForm_1 = require("./models/consentForm");
|
|
52
|
+
var syncConsents_1 = require("./syncConsents");
|
|
53
|
+
var tests_1 = require("./tests");
|
|
54
|
+
var baseDef = {
|
|
55
|
+
content: { en: "# Terms\nPlease agree." },
|
|
56
|
+
order: 1,
|
|
57
|
+
required: true,
|
|
58
|
+
title: "Terms of Service",
|
|
59
|
+
type: "terms",
|
|
60
|
+
};
|
|
61
|
+
(0, bun_test_1.describe)("syncConsents", function () {
|
|
62
|
+
(0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
63
|
+
return __generator(this, function (_a) {
|
|
64
|
+
switch (_a.label) {
|
|
65
|
+
case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
|
|
66
|
+
case 1:
|
|
67
|
+
_a.sent();
|
|
68
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.deleteMany({})];
|
|
69
|
+
case 2:
|
|
70
|
+
_a.sent();
|
|
71
|
+
return [2 /*return*/];
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}); });
|
|
75
|
+
(0, bun_test_1.afterEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
76
|
+
return __generator(this, function (_a) {
|
|
77
|
+
switch (_a.label) {
|
|
78
|
+
case 0: return [4 /*yield*/, consentForm_1.ConsentForm.deleteMany({})];
|
|
79
|
+
case 1:
|
|
80
|
+
_a.sent();
|
|
81
|
+
return [2 /*return*/];
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}); });
|
|
85
|
+
(0, bun_test_1.it)("creates a new consent form when none exists", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
86
|
+
var result, forms;
|
|
87
|
+
return __generator(this, function (_a) {
|
|
88
|
+
switch (_a.label) {
|
|
89
|
+
case 0: return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: baseDef })];
|
|
90
|
+
case 1:
|
|
91
|
+
result = _a.sent();
|
|
92
|
+
(0, bun_test_1.expect)(result.created).toEqual(["terms"]);
|
|
93
|
+
(0, bun_test_1.expect)(result.updated).toHaveLength(0);
|
|
94
|
+
(0, bun_test_1.expect)(result.unchanged).toHaveLength(0);
|
|
95
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.find({ slug: "terms" })];
|
|
96
|
+
case 2:
|
|
97
|
+
forms = _a.sent();
|
|
98
|
+
(0, bun_test_1.expect)(forms).toHaveLength(1);
|
|
99
|
+
(0, bun_test_1.expect)(forms[0].active).toBe(true);
|
|
100
|
+
(0, bun_test_1.expect)(forms[0].version).toBe(1);
|
|
101
|
+
(0, bun_test_1.expect)(forms[0].title).toBe("Terms of Service");
|
|
102
|
+
return [2 /*return*/];
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}); });
|
|
106
|
+
(0, bun_test_1.it)("leaves unchanged forms alone", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
107
|
+
var result, forms;
|
|
108
|
+
return __generator(this, function (_a) {
|
|
109
|
+
switch (_a.label) {
|
|
110
|
+
case 0: return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: baseDef })];
|
|
111
|
+
case 1:
|
|
112
|
+
_a.sent();
|
|
113
|
+
return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: baseDef })];
|
|
114
|
+
case 2:
|
|
115
|
+
result = _a.sent();
|
|
116
|
+
(0, bun_test_1.expect)(result.unchanged).toEqual(["terms"]);
|
|
117
|
+
(0, bun_test_1.expect)(result.created).toHaveLength(0);
|
|
118
|
+
(0, bun_test_1.expect)(result.updated).toHaveLength(0);
|
|
119
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.find({ slug: "terms" })];
|
|
120
|
+
case 3:
|
|
121
|
+
forms = _a.sent();
|
|
122
|
+
(0, bun_test_1.expect)(forms).toHaveLength(1);
|
|
123
|
+
(0, bun_test_1.expect)(forms[0].version).toBe(1);
|
|
124
|
+
return [2 /*return*/];
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}); });
|
|
128
|
+
(0, bun_test_1.it)("publishes a new version when content changes", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
129
|
+
var updated, result, forms;
|
|
130
|
+
return __generator(this, function (_a) {
|
|
131
|
+
switch (_a.label) {
|
|
132
|
+
case 0: return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: baseDef })];
|
|
133
|
+
case 1:
|
|
134
|
+
_a.sent();
|
|
135
|
+
updated = __assign(__assign({}, baseDef), { content: { en: "# Updated Terms\nNew content." } });
|
|
136
|
+
return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: updated })];
|
|
137
|
+
case 2:
|
|
138
|
+
result = _a.sent();
|
|
139
|
+
(0, bun_test_1.expect)(result.updated).toEqual(["terms"]);
|
|
140
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.find({ slug: "terms" }).sort({ version: 1 })];
|
|
141
|
+
case 3:
|
|
142
|
+
forms = _a.sent();
|
|
143
|
+
(0, bun_test_1.expect)(forms).toHaveLength(2);
|
|
144
|
+
(0, bun_test_1.expect)(forms[0].active).toBe(false);
|
|
145
|
+
(0, bun_test_1.expect)(forms[0].version).toBe(1);
|
|
146
|
+
(0, bun_test_1.expect)(forms[1].active).toBe(true);
|
|
147
|
+
(0, bun_test_1.expect)(forms[1].version).toBe(2);
|
|
148
|
+
return [2 /*return*/];
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}); });
|
|
152
|
+
(0, bun_test_1.it)("publishes a new version when title changes", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
153
|
+
var updated, result, active;
|
|
154
|
+
return __generator(this, function (_a) {
|
|
155
|
+
switch (_a.label) {
|
|
156
|
+
case 0: return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: baseDef })];
|
|
157
|
+
case 1:
|
|
158
|
+
_a.sent();
|
|
159
|
+
updated = __assign(__assign({}, baseDef), { title: "Updated Terms" });
|
|
160
|
+
return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: updated })];
|
|
161
|
+
case 2:
|
|
162
|
+
result = _a.sent();
|
|
163
|
+
(0, bun_test_1.expect)(result.updated).toEqual(["terms"]);
|
|
164
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.findOne({ active: true, slug: "terms" })];
|
|
165
|
+
case 3:
|
|
166
|
+
active = _a.sent();
|
|
167
|
+
(0, bun_test_1.expect)(active === null || active === void 0 ? void 0 : active.version).toBe(2);
|
|
168
|
+
(0, bun_test_1.expect)(active === null || active === void 0 ? void 0 : active.title).toBe("Updated Terms");
|
|
169
|
+
return [2 /*return*/];
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}); });
|
|
173
|
+
(0, bun_test_1.it)("deactivates removed forms when deactivateRemoved is true", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
174
|
+
var result, privacy;
|
|
175
|
+
return __generator(this, function (_a) {
|
|
176
|
+
switch (_a.label) {
|
|
177
|
+
case 0: return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ privacy: __assign(__assign({}, baseDef), { title: "Privacy", type: "privacy" }), terms: baseDef })];
|
|
178
|
+
case 1:
|
|
179
|
+
_a.sent();
|
|
180
|
+
return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: baseDef }, { deactivateRemoved: true })];
|
|
181
|
+
case 2:
|
|
182
|
+
result = _a.sent();
|
|
183
|
+
(0, bun_test_1.expect)(result.deactivated).toEqual(["privacy"]);
|
|
184
|
+
(0, bun_test_1.expect)(result.unchanged).toEqual(["terms"]);
|
|
185
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.findOne({ slug: "privacy" })];
|
|
186
|
+
case 3:
|
|
187
|
+
privacy = _a.sent();
|
|
188
|
+
(0, bun_test_1.expect)(privacy === null || privacy === void 0 ? void 0 : privacy.active).toBe(false);
|
|
189
|
+
return [2 /*return*/];
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}); });
|
|
193
|
+
(0, bun_test_1.it)("does not deactivate removed forms by default", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
194
|
+
var result, privacy;
|
|
195
|
+
return __generator(this, function (_a) {
|
|
196
|
+
switch (_a.label) {
|
|
197
|
+
case 0: return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ privacy: __assign(__assign({}, baseDef), { title: "Privacy", type: "privacy" }), terms: baseDef })];
|
|
198
|
+
case 1:
|
|
199
|
+
_a.sent();
|
|
200
|
+
return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: baseDef })];
|
|
201
|
+
case 2:
|
|
202
|
+
result = _a.sent();
|
|
203
|
+
(0, bun_test_1.expect)(result.deactivated).toHaveLength(0);
|
|
204
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.findOne({ slug: "privacy" })];
|
|
205
|
+
case 3:
|
|
206
|
+
privacy = _a.sent();
|
|
207
|
+
(0, bun_test_1.expect)(privacy === null || privacy === void 0 ? void 0 : privacy.active).toBe(true);
|
|
208
|
+
return [2 /*return*/];
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}); });
|
|
212
|
+
(0, bun_test_1.it)("does not write to the database in dry run mode", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
213
|
+
var result, forms;
|
|
214
|
+
return __generator(this, function (_a) {
|
|
215
|
+
switch (_a.label) {
|
|
216
|
+
case 0: return [4 /*yield*/, (0, syncConsents_1.syncConsents)({ terms: baseDef }, { dryRun: true })];
|
|
217
|
+
case 1:
|
|
218
|
+
result = _a.sent();
|
|
219
|
+
(0, bun_test_1.expect)(result.created).toEqual(["terms"]);
|
|
220
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.find({ slug: "terms" })];
|
|
221
|
+
case 2:
|
|
222
|
+
forms = _a.sent();
|
|
223
|
+
(0, bun_test_1.expect)(forms).toHaveLength(0);
|
|
224
|
+
return [2 /*return*/];
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}); });
|
|
228
|
+
(0, bun_test_1.it)("handles multiple forms in a single sync", function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
229
|
+
var result, forms;
|
|
230
|
+
return __generator(this, function (_a) {
|
|
231
|
+
switch (_a.label) {
|
|
232
|
+
case 0: return [4 /*yield*/, (0, syncConsents_1.syncConsents)({
|
|
233
|
+
privacy: __assign(__assign({}, baseDef), { order: 2, title: "Privacy Policy", type: "privacy" }),
|
|
234
|
+
terms: baseDef,
|
|
235
|
+
})];
|
|
236
|
+
case 1:
|
|
237
|
+
result = _a.sent();
|
|
238
|
+
(0, bun_test_1.expect)(result.created.sort()).toEqual(["privacy", "terms"]);
|
|
239
|
+
return [4 /*yield*/, consentForm_1.ConsentForm.find({}).sort({ order: 1 })];
|
|
240
|
+
case 2:
|
|
241
|
+
forms = _a.sent();
|
|
242
|
+
(0, bun_test_1.expect)(forms).toHaveLength(2);
|
|
243
|
+
(0, bun_test_1.expect)(forms[0].slug).toBe("terms");
|
|
244
|
+
(0, bun_test_1.expect)(forms[1].slug).toBe("privacy");
|
|
245
|
+
return [2 /*return*/];
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}); });
|
|
249
|
+
});
|
package/dist/terrenoApp.js
CHANGED
|
@@ -312,7 +312,6 @@ var TerrenoApp = /** @class */ (function () {
|
|
|
312
312
|
if (process.env.ENABLE_SWAGGER === "true") {
|
|
313
313
|
app.use("/swagger", oapi.swaggerui());
|
|
314
314
|
}
|
|
315
|
-
(0, auth_1.addMeRoutes)(app, options.userModel, options.authOptions);
|
|
316
315
|
// GitHub OAuth
|
|
317
316
|
if (options.githubAuth) {
|
|
318
317
|
(0, githubAuth_1.setupGitHubAuth)(app, options.userModel, options.githubAuth);
|
|
@@ -327,10 +326,11 @@ var TerrenoApp = /** @class */ (function () {
|
|
|
327
326
|
for (var _f = __values(this.registrations), _g = _f.next(); !_g.done; _g = _f.next()) {
|
|
328
327
|
var registration = _g.value;
|
|
329
328
|
if (this.isModelRouterRegistration(registration)) {
|
|
330
|
-
|
|
329
|
+
var router = registration._buildWithOpenApi(oapi);
|
|
330
|
+
app.use(registration.path, router);
|
|
331
331
|
}
|
|
332
332
|
else {
|
|
333
|
-
registration.register(app);
|
|
333
|
+
registration.register(app, oapi);
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
336
|
}
|
|
@@ -341,8 +341,9 @@ var TerrenoApp = /** @class */ (function () {
|
|
|
341
341
|
}
|
|
342
342
|
finally { if (e_2) throw e_2.error; }
|
|
343
343
|
}
|
|
344
|
-
//
|
|
345
|
-
//
|
|
344
|
+
// /auth/me must be registered after plugins so that session middleware
|
|
345
|
+
// (e.g. Better Auth) has a chance to populate req.user first.
|
|
346
|
+
(0, auth_1.addMeRoutes)(app, options.userModel, options.authOptions);
|
|
346
347
|
Sentry.setupExpressErrorHandler(app);
|
|
347
348
|
// Error middleware
|
|
348
349
|
app.use(errors_1.apiUnauthorizedMiddleware);
|
package/dist/terrenoPlugin.d.ts
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type mongoose from "mongoose";
|
|
2
|
+
import type { FindExactlyOnePlugin, FindOneOrNonePlugin } from "../plugins";
|
|
3
|
+
export interface ConsentFormCheckbox {
|
|
4
|
+
label: string;
|
|
5
|
+
required: boolean;
|
|
6
|
+
confirmationPrompt?: string;
|
|
7
|
+
}
|
|
8
|
+
export type ConsentFormType = "agreement" | "privacy" | "hipaa" | "research" | "terms" | "custom";
|
|
9
|
+
export type ConsentFormMethods = {};
|
|
10
|
+
export type ConsentFormStatics = FindExactlyOnePlugin<ConsentFormDocument> & FindOneOrNonePlugin<ConsentFormDocument>;
|
|
11
|
+
export type ConsentFormModel = mongoose.Model<ConsentFormDocument, object, ConsentFormMethods> & ConsentFormStatics;
|
|
12
|
+
export interface ConsentFormDocument extends mongoose.Document {
|
|
13
|
+
_id: mongoose.Types.ObjectId;
|
|
14
|
+
title: string;
|
|
15
|
+
slug: string;
|
|
16
|
+
version: number;
|
|
17
|
+
order: number;
|
|
18
|
+
type: ConsentFormType;
|
|
19
|
+
content: Map<string, string>;
|
|
20
|
+
defaultLocale: string;
|
|
21
|
+
active: boolean;
|
|
22
|
+
captureSignature: boolean;
|
|
23
|
+
requireScrollToBottom: boolean;
|
|
24
|
+
checkboxes: ConsentFormCheckbox[];
|
|
25
|
+
agreeButtonText: string;
|
|
26
|
+
allowDecline: boolean;
|
|
27
|
+
declineButtonText: string;
|
|
28
|
+
required: boolean;
|
|
29
|
+
created: Date;
|
|
30
|
+
updated: Date;
|
|
31
|
+
deleted: boolean;
|
|
32
|
+
}
|