@opentermsarchive/engine 10.6.0 → 11.0.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/config/default.json
CHANGED
|
@@ -42,20 +42,9 @@
|
|
|
42
42
|
"language": "en"
|
|
43
43
|
},
|
|
44
44
|
"logger": {
|
|
45
|
-
"smtp": {
|
|
46
|
-
"host": "smtp-relay.brevo.com",
|
|
47
|
-
"port": 587,
|
|
48
|
-
"username": "admin@opentermsarchive.org"
|
|
49
|
-
},
|
|
50
45
|
"sendMailOnError": false,
|
|
51
46
|
"timestampPrefix": true
|
|
52
47
|
},
|
|
53
|
-
"notifier": {
|
|
54
|
-
"sendInBlue": {
|
|
55
|
-
"updatesListId": 850,
|
|
56
|
-
"updateTemplateId": 7
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
48
|
"dataset": {
|
|
60
49
|
"publishingSchedule": "30 8 * * MON"
|
|
61
50
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentermsarchive/engine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
4
4
|
"description": "Tracks and makes visible changes to the terms of online services",
|
|
5
5
|
"homepage": "https://opentermsarchive.org",
|
|
6
6
|
"bugs": {
|
|
@@ -95,7 +95,6 @@
|
|
|
95
95
|
"puppeteer": "^24.22.0",
|
|
96
96
|
"puppeteer-extra": "^3.3.6",
|
|
97
97
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
98
|
-
"sib-api-v3-sdk": "^8.5.0",
|
|
99
98
|
"simple-git": "^3.28.0",
|
|
100
99
|
"swagger-jsdoc": "^6.2.8",
|
|
101
100
|
"swagger-ui-express": "^5.0.1",
|
|
@@ -50,6 +50,10 @@ export default class DeclarationUtils {
|
|
|
50
50
|
if (modifiedFilePath.endsWith('.filters.js') || modifiedFilePath.endsWith('.filters.history.js')) {
|
|
51
51
|
const declaration = await this.getJSONFromFile(this.defaultBranch, `declarations/${serviceId}.json`);
|
|
52
52
|
|
|
53
|
+
if (!declaration) { // Happens when the file declaration exists in the branch but not in the default branch
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
53
57
|
servicesTermsTypes[serviceId] = Object.keys(declaration.terms); // Considering how rarely filters are used, simply return all term types that could potentially be impacted to spare implementing a function change check
|
|
54
58
|
|
|
55
59
|
return;
|
|
@@ -188,6 +188,23 @@ describe('DeclarationUtils', () => {
|
|
|
188
188
|
});
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
+
context('when a new declaration has been added along with a filters file', () => {
|
|
192
|
+
before(async () => {
|
|
193
|
+
await fs.writeFile(path.resolve(SUBJECT_PATH, COMMIT_PATHS.serviceB), JSON.stringify(FIXTURES.serviceB.content, null, 2));
|
|
194
|
+
await fs.writeFile(path.resolve(SUBJECT_PATH, './declarations/ServiceB.filters.js'), 'module.exports = {};');
|
|
195
|
+
await declarationUtils.git.add([ COMMIT_PATHS.serviceB, './declarations/ServiceB.filters.js' ]);
|
|
196
|
+
await declarationUtils.git.commit('Add declaration with filters for new service');
|
|
197
|
+
});
|
|
198
|
+
after(removeLatestCommit);
|
|
199
|
+
|
|
200
|
+
it('returns the added service ID along with all its terms types', async () => {
|
|
201
|
+
expect(await declarationUtils.getModifiedServicesAndTermsTypes()).to.deep.equal({
|
|
202
|
+
services: ['ServiceB'],
|
|
203
|
+
servicesTermsTypes: { ServiceB: ['Terms of Service'] },
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
191
208
|
context('when history file is modified without declaration changes', () => {
|
|
192
209
|
before(() => commitChanges(COMMIT_PATHS.serviceAHistory, FIXTURES.serviceATermsUpdatedHistory.content));
|
|
193
210
|
after(removeLatestCommit);
|
package/src/index.js
CHANGED
|
@@ -7,7 +7,6 @@ import cronstrue from 'cronstrue';
|
|
|
7
7
|
import { getCollection } from './archivist/collection/index.js';
|
|
8
8
|
import Archivist from './archivist/index.js';
|
|
9
9
|
import logger from './logger/index.js';
|
|
10
|
-
import Notifier from './notifier/index.js';
|
|
11
10
|
import Reporter from './reporter/index.js';
|
|
12
11
|
|
|
13
12
|
const require = createRequire(import.meta.url);
|
|
@@ -52,16 +51,6 @@ export default async function track({ services, types, schedule }) {
|
|
|
52
51
|
// All versions from this pass are labeled as technical upgrades to avoid false notifications about content changes.
|
|
53
52
|
await archivist.applyTechnicalUpgrades({ services: filteredServices, types });
|
|
54
53
|
|
|
55
|
-
if (process.env.OTA_ENGINE_SENDINBLUE_API_KEY) {
|
|
56
|
-
try {
|
|
57
|
-
archivist.attach(new Notifier(archivist.services));
|
|
58
|
-
} catch (error) {
|
|
59
|
-
logger.error('Cannot instantiate the Notifier module; it will be ignored:', error);
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
logger.warn('Environment variable "OTA_ENGINE_SENDINBLUE_API_KEY" was not found; the Notifier module will be ignored');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
54
|
if (process.env.OTA_ENGINE_GITHUB_TOKEN || process.env.OTA_ENGINE_GITLAB_TOKEN) {
|
|
66
55
|
try {
|
|
67
56
|
const reporter = new Reporter(config.get('@opentermsarchive/engine.reporter'));
|
package/src/notifier/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
This module is intended to be considered as a simple consumer of the Archivist application's events API and therefore to be extracted from this repository. It is still in this repository to facilitate quick iterations but is expected to be extracted as soon as the Open Terms Archive app is stabilized.
|
package/src/notifier/index.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import config from 'config';
|
|
2
|
-
import sendInBlue from 'sib-api-v3-sdk';
|
|
3
|
-
|
|
4
|
-
export default class Notifier {
|
|
5
|
-
constructor(passedServiceProviders) {
|
|
6
|
-
const defaultClient = sendInBlue.ApiClient.instance;
|
|
7
|
-
const authentication = defaultClient.authentications['api-key'];
|
|
8
|
-
|
|
9
|
-
authentication.apiKey = process.env.OTA_ENGINE_SENDINBLUE_API_KEY;
|
|
10
|
-
|
|
11
|
-
this.apiInstance = new sendInBlue.TransactionalEmailsApi();
|
|
12
|
-
this.contactsInstance = new sendInBlue.ContactsApi();
|
|
13
|
-
|
|
14
|
-
this.serviceProviders = passedServiceProviders;
|
|
15
|
-
this.delayedVersionNotificationsParams = [];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
onVersionRecorded(record) {
|
|
19
|
-
this.delayedVersionNotificationsParams.push(record);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
onTrackingCompleted() {
|
|
23
|
-
this.delayedVersionNotificationsParams.forEach(async ({ serviceId, termsType, id }) => {
|
|
24
|
-
try {
|
|
25
|
-
await this.notifyVersionRecorded(serviceId, termsType, id);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
console.error(`The notification for version ${id} of ${serviceId} — ${termsType} can not be sent:`, error);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
this.delayedVersionNotificationsParams = [];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async notifyVersionRecorded(serviceProviderId, termsType, versionId) {
|
|
35
|
-
const sendParams = {
|
|
36
|
-
templateId: config.get('@opentermsarchive/engine.notifier.sendInBlue.updateTemplateId'),
|
|
37
|
-
params: {
|
|
38
|
-
SERVICE_PROVIDER_NAME: this.serviceProviders[serviceProviderId].name,
|
|
39
|
-
DOCUMENT_TYPE: termsType,
|
|
40
|
-
SHA: versionId,
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const lists = [
|
|
45
|
-
config.get('@opentermsarchive/engine.notifier.sendInBlue.updatesListId'),
|
|
46
|
-
];
|
|
47
|
-
|
|
48
|
-
const notificationListName = `${this.serviceProviders[serviceProviderId].name} - ${termsType} - Update`;
|
|
49
|
-
const notificationList = await this.searchContactList(notificationListName);
|
|
50
|
-
|
|
51
|
-
if (notificationList?.id) {
|
|
52
|
-
lists.push(notificationList.id);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return this.send(lists, sendParams);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async send(lists, sendParams) {
|
|
59
|
-
const promises = lists.map(listId => this.getListContacts(listId));
|
|
60
|
-
|
|
61
|
-
let contacts = await Promise.all(promises);
|
|
62
|
-
|
|
63
|
-
contacts = contacts.flat();
|
|
64
|
-
|
|
65
|
-
const uniqueContacts = [...new Map(contacts.map(item => [ item.id, item ])).values()];
|
|
66
|
-
|
|
67
|
-
const sendPromises = uniqueContacts.map(contact =>
|
|
68
|
-
this.apiInstance.sendTransacEmail({ ...sendParams, to: [{ email: contact.email }] }));
|
|
69
|
-
|
|
70
|
-
return Promise.all(sendPromises);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async getAllContactLists() {
|
|
74
|
-
const limit = 50;
|
|
75
|
-
const offset = 0;
|
|
76
|
-
const { count, lists } = await this.contactsInstance.getLists({
|
|
77
|
-
limit,
|
|
78
|
-
offset,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
count,
|
|
83
|
-
lists: await this.getAllPaginatedEntries(
|
|
84
|
-
'getLists',
|
|
85
|
-
undefined,
|
|
86
|
-
'lists',
|
|
87
|
-
lists,
|
|
88
|
-
count,
|
|
89
|
-
offset + limit,
|
|
90
|
-
),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async searchContactList(name) {
|
|
95
|
-
const { lists } = await this.getAllContactLists();
|
|
96
|
-
|
|
97
|
-
const list = lists.find(list => list.name === name);
|
|
98
|
-
|
|
99
|
-
return list;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async getListContacts(listId) {
|
|
103
|
-
const list = await this.contactsInstance.getList(listId);
|
|
104
|
-
|
|
105
|
-
return this.getAllPaginatedEntries(
|
|
106
|
-
'getContactsFromList',
|
|
107
|
-
listId,
|
|
108
|
-
'contacts',
|
|
109
|
-
[],
|
|
110
|
-
list.totalSubscribers,
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async getAllPaginatedEntries(
|
|
115
|
-
functionName,
|
|
116
|
-
resourceIdParameter,
|
|
117
|
-
resultKey,
|
|
118
|
-
accumulator,
|
|
119
|
-
count,
|
|
120
|
-
offset = 0,
|
|
121
|
-
paginationSize = 50,
|
|
122
|
-
) {
|
|
123
|
-
if (accumulator.length >= count) {
|
|
124
|
-
return accumulator;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const promise = resourceIdParameter
|
|
128
|
-
? this.contactsInstance[functionName](resourceIdParameter, {
|
|
129
|
-
limit: paginationSize,
|
|
130
|
-
offset,
|
|
131
|
-
})
|
|
132
|
-
: this.contactsInstance[functionName](paginationSize, offset);
|
|
133
|
-
|
|
134
|
-
const result = await promise;
|
|
135
|
-
|
|
136
|
-
accumulator = accumulator.concat(result[resultKey]);
|
|
137
|
-
|
|
138
|
-
return this.getAllPaginatedEntries(
|
|
139
|
-
functionName,
|
|
140
|
-
resourceIdParameter,
|
|
141
|
-
resultKey,
|
|
142
|
-
accumulator,
|
|
143
|
-
count,
|
|
144
|
-
offset + paginationSize,
|
|
145
|
-
paginationSize,
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
}
|