@opentermsarchive/engine 0.15.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/.env.example +3 -0
- package/.eslintrc.yaml +116 -0
- package/.github/workflows/deploy.yml +50 -0
- package/.github/workflows/release.yml +64 -0
- package/.github/workflows/test.yml +77 -0
- package/CHANGELOG.md +14 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +143 -0
- package/LICENSE +153 -0
- package/MIGRATING.md +42 -0
- package/README.fr.md +110 -0
- package/README.md +438 -0
- package/Vagrantfile +38 -0
- package/ansible.cfg +13 -0
- package/bin/.env.js +1 -0
- package/bin/lint-declarations.js +31 -0
- package/bin/track.js +26 -0
- package/bin/validate-declarations.js +68 -0
- package/config/ci.json +5 -0
- package/config/contrib.json +35 -0
- package/config/dating.json +37 -0
- package/config/default.json +71 -0
- package/config/france.json +40 -0
- package/config/p2b-compliance.json +40 -0
- package/config/pga.json +40 -0
- package/config/production.json +27 -0
- package/config/test.json +49 -0
- package/config/vagrant.json +24 -0
- package/decision-records/0001-service-name-and-id.md +73 -0
- package/decision-records/0002-service-history.md +212 -0
- package/decision-records/0003-snapshots-database.md +123 -0
- package/ops/README.md +280 -0
- package/ops/app.yml +5 -0
- package/ops/infra.yml +6 -0
- package/ops/inventories/dev.yml +7 -0
- package/ops/inventories/production.yml +27 -0
- package/ops/roles/infra/defaults/main.yml +2 -0
- package/ops/roles/infra/files/.gitconfig +3 -0
- package/ops/roles/infra/files/mongod.conf +18 -0
- package/ops/roles/infra/files/ota-bot-key.private_key +26 -0
- package/ops/roles/infra/tasks/main.yml +78 -0
- package/ops/roles/infra/tasks/mongo.yml +40 -0
- package/ops/roles/infra/templates/ssh_config.j2 +5 -0
- package/ops/roles/ota/defaults/main.yml +14 -0
- package/ops/roles/ota/files/.env +21 -0
- package/ops/roles/ota/tasks/database.yml +65 -0
- package/ops/roles/ota/tasks/main.yml +110 -0
- package/ops/site.yml +6 -0
- package/package.json +101 -0
- package/pm2.config.cjs +20 -0
- package/scripts/dataset/README.md +37 -0
- package/scripts/dataset/assets/LICENSE +540 -0
- package/scripts/dataset/assets/README.template.js +65 -0
- package/scripts/dataset/export/index.js +106 -0
- package/scripts/dataset/export/index.test.js +155 -0
- package/scripts/dataset/export/test/fixtures/dataset/LICENSE +540 -0
- package/scripts/dataset/export/test/fixtures/dataset/README.md +40 -0
- package/scripts/dataset/export/test/fixtures/dataset/ServiceA/Terms of Service/2021-01-01T11-27-00Z.md +1 -0
- package/scripts/dataset/export/test/fixtures/dataset/ServiceA/Terms of Service/2021-01-11T11-32-47Z.md +1 -0
- package/scripts/dataset/export/test/fixtures/dataset/ServiceB/Privacy Policy/2022-01-01T12-12-24Z.md +1 -0
- package/scripts/dataset/export/test/fixtures/dataset/ServiceB/Terms of Service/2022-01-06T11-32-47Z.md +1 -0
- package/scripts/dataset/index.js +40 -0
- package/scripts/dataset/logger/index.js +17 -0
- package/scripts/dataset/main.js +25 -0
- package/scripts/dataset/publish/index.js +39 -0
- package/scripts/declarations/lint/index.js +36 -0
- package/scripts/declarations/utils/index.js +81 -0
- package/scripts/declarations/validate/definitions.js +63 -0
- package/scripts/declarations/validate/index.mocha.js +262 -0
- package/scripts/declarations/validate/service.history.schema.js +86 -0
- package/scripts/declarations/validate/service.schema.js +91 -0
- package/scripts/history/logger/index.js +39 -0
- package/scripts/history/migrate-services.js +212 -0
- package/scripts/history/update-to-full-hash.js +61 -0
- package/scripts/history/utils/index.js +23 -0
- package/scripts/import/README.md +59 -0
- package/scripts/import/config/import.json +12 -0
- package/scripts/import/index.js +224 -0
- package/scripts/import/loadCommits.js +66 -0
- package/scripts/import/logger/index.js +43 -0
- package/scripts/rewrite/README.md +131 -0
- package/scripts/rewrite/config/rewrite-snapshots.json +32 -0
- package/scripts/rewrite/config/rewrite-versions.json +32 -0
- package/scripts/rewrite/initializer/files/license +428 -0
- package/scripts/rewrite/initializer/files/readme.md +8 -0
- package/scripts/rewrite/initializer/index.js +44 -0
- package/scripts/rewrite/rewrite-snapshots.js +108 -0
- package/scripts/rewrite/rewrite-versions.js +160 -0
- package/scripts/rewrite/utils.js +33 -0
- package/scripts/utils/renamer/README.md +49 -0
- package/scripts/utils/renamer/index.js +45 -0
- package/scripts/utils/renamer/rules/documentTypes.json +25 -0
- package/scripts/utils/renamer/rules/documentTypesByService.json +170 -0
- package/scripts/utils/renamer/rules/serviceNames.json +92 -0
- package/src/archivist/errors.js +9 -0
- package/src/archivist/fetcher/errors.js +6 -0
- package/src/archivist/fetcher/exports.js +18 -0
- package/src/archivist/fetcher/fullDomFetcher.js +84 -0
- package/src/archivist/fetcher/htmlOnlyFetcher.js +62 -0
- package/src/archivist/fetcher/index.js +35 -0
- package/src/archivist/fetcher/index.test.js +239 -0
- package/src/archivist/filter/exports.js +3 -0
- package/src/archivist/filter/index.js +178 -0
- package/src/archivist/filter/index.test.js +561 -0
- package/src/archivist/index.js +276 -0
- package/src/archivist/index.test.js +600 -0
- package/src/archivist/recorder/index.js +77 -0
- package/src/archivist/recorder/index.test.js +463 -0
- package/src/archivist/recorder/record.js +35 -0
- package/src/archivist/recorder/record.test.js +91 -0
- package/src/archivist/recorder/repositories/factory.js +23 -0
- package/src/archivist/recorder/repositories/git/dataMapper.js +83 -0
- package/src/archivist/recorder/repositories/git/git.js +122 -0
- package/src/archivist/recorder/repositories/git/git.test.js +86 -0
- package/src/archivist/recorder/repositories/git/index.js +182 -0
- package/src/archivist/recorder/repositories/git/index.test.js +714 -0
- package/src/archivist/recorder/repositories/interface.js +108 -0
- package/src/archivist/recorder/repositories/mongo/dataMapper.js +32 -0
- package/src/archivist/recorder/repositories/mongo/index.js +121 -0
- package/src/archivist/recorder/repositories/mongo/index.test.js +721 -0
- package/src/archivist/services/documentDeclaration.js +26 -0
- package/src/archivist/services/documentDeclaration.test.js +85 -0
- package/src/archivist/services/documentTypes.json +386 -0
- package/src/archivist/services/index.js +255 -0
- package/src/archivist/services/index.test.js +327 -0
- package/src/archivist/services/pageDeclaration.js +51 -0
- package/src/archivist/services/pageDeclaration.test.js +224 -0
- package/src/archivist/services/service.js +60 -0
- package/src/archivist/services/service.test.js +164 -0
- package/src/exports.js +3 -0
- package/src/index.js +59 -0
- package/src/logger/README.md +1 -0
- package/src/logger/index.js +131 -0
- package/src/main.js +18 -0
- package/src/notifier/README.md +1 -0
- package/src/notifier/index.js +150 -0
- package/src/tracker/README.md +1 -0
- package/src/tracker/index.js +215 -0
- package/test/fixtures/service_A.js +22 -0
- package/test/fixtures/service_A_terms.md +10 -0
- package/test/fixtures/service_A_terms_snapshot.html +14 -0
- package/test/fixtures/service_B.js +22 -0
- package/test/fixtures/service_with_declaration_history.js +65 -0
- package/test/fixtures/service_with_filters_history.js +155 -0
- package/test/fixtures/service_with_history.js +188 -0
- package/test/fixtures/service_with_multipage_document.js +100 -0
- package/test/fixtures/service_without_history.js +31 -0
- package/test/fixtures/services.js +19 -0
- package/test/fixtures/terms.pdf +0 -0
- package/test/fixtures/termsFromPDF.md +25 -0
- package/test/fixtures/termsModified.pdf +0 -0
- package/test/services/service_A.json +9 -0
- package/test/services/service_B.json +9 -0
- package/test/services/service_with_declaration_history.filters.js +7 -0
- package/test/services/service_with_declaration_history.history.json +17 -0
- package/test/services/service_with_declaration_history.json +13 -0
- package/test/services/service_with_filters_history.filters.history.js +29 -0
- package/test/services/service_with_filters_history.filters.js +7 -0
- package/test/services/service_with_filters_history.json +13 -0
- package/test/services/service_with_history.filters.history.js +29 -0
- package/test/services/service_with_history.filters.js +7 -0
- package/test/services/service_with_history.history.json +26 -0
- package/test/services/service_with_history.json +17 -0
- package/test/services/service_with_multipage_document.filters.js +7 -0
- package/test/services/service_with_multipage_document.history.json +37 -0
- package/test/services/service_with_multipage_document.json +28 -0
- package/test/services/service_without_history.filters.js +7 -0
- package/test/services/service_without_history.json +13 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import config from 'config';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import sendInBlue from 'sib-api-v3-sdk';
|
|
4
|
+
|
|
5
|
+
dotenv.config();
|
|
6
|
+
export default class Notifier {
|
|
7
|
+
constructor(passedServiceProviders) {
|
|
8
|
+
const defaultClient = sendInBlue.ApiClient.instance;
|
|
9
|
+
const authentication = defaultClient.authentications['api-key'];
|
|
10
|
+
|
|
11
|
+
authentication.apiKey = process.env.SENDINBLUE_API_KEY;
|
|
12
|
+
|
|
13
|
+
this.apiInstance = new sendInBlue.TransactionalEmailsApi();
|
|
14
|
+
this.contactsInstance = new sendInBlue.ContactsApi();
|
|
15
|
+
|
|
16
|
+
this.serviceProviders = passedServiceProviders;
|
|
17
|
+
this.delayedVersionNotificationsParams = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async onVersionRecorded(serviceId, type, versionId) {
|
|
21
|
+
this.delayedVersionNotificationsParams.push({ serviceId, type, versionId });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async onTrackingCompleted() {
|
|
25
|
+
this.delayedVersionNotificationsParams.forEach(async ({ serviceId, type, versionId }) => {
|
|
26
|
+
try {
|
|
27
|
+
await this.notifyVersionRecorded(serviceId, type, versionId);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(`The notification for version ${versionId} of ${serviceId} — ${type} can not be sent:`, error);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.delayedVersionNotificationsParams = [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async notifyVersionRecorded(serviceProviderId, documentTypeId, versionId) {
|
|
37
|
+
const sendParams = {
|
|
38
|
+
templateId: config.get('notifier.sendInBlue.updateTemplateId'),
|
|
39
|
+
params: {
|
|
40
|
+
SERVICE_PROVIDER_NAME: this.serviceProviders[serviceProviderId].name,
|
|
41
|
+
DOCUMENT_TYPE: documentTypeId,
|
|
42
|
+
SHA: versionId,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const lists = [
|
|
47
|
+
config.get('notifier.sendInBlue.updatesListId'),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const notificationListName = `${this.serviceProviders[serviceProviderId].name} - ${documentTypeId} - Update`;
|
|
51
|
+
const notificationList = await this.searchContactList(notificationListName);
|
|
52
|
+
|
|
53
|
+
if (notificationList?.id) {
|
|
54
|
+
lists.push(notificationList.id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this.send(lists, sendParams);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async send(lists, sendParams) {
|
|
61
|
+
const promises = lists.map(listId => this.getListContacts(listId));
|
|
62
|
+
|
|
63
|
+
let contacts = await Promise.all(promises);
|
|
64
|
+
|
|
65
|
+
contacts = contacts.flat();
|
|
66
|
+
|
|
67
|
+
const uniqueContacts = [...new Map(contacts.map(item => [ item.id, item ])).values()];
|
|
68
|
+
|
|
69
|
+
const sendPromises = uniqueContacts.map(contact =>
|
|
70
|
+
this.apiInstance.sendTransacEmail({ ...sendParams, to: [{ email: contact.email }] }));
|
|
71
|
+
|
|
72
|
+
return Promise.all(sendPromises);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async getAllContactLists() {
|
|
76
|
+
const limit = 50;
|
|
77
|
+
const offset = 0;
|
|
78
|
+
const { count, lists } = await this.contactsInstance.getLists({
|
|
79
|
+
limit,
|
|
80
|
+
offset,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
count,
|
|
85
|
+
lists: await this.getAllPaginatedEntries(
|
|
86
|
+
'getLists',
|
|
87
|
+
undefined,
|
|
88
|
+
'lists',
|
|
89
|
+
lists,
|
|
90
|
+
count,
|
|
91
|
+
offset + limit,
|
|
92
|
+
),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async searchContactList(name) {
|
|
97
|
+
const { lists } = await this.getAllContactLists();
|
|
98
|
+
|
|
99
|
+
const list = lists.find(list => list.name === name);
|
|
100
|
+
|
|
101
|
+
return list;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async getListContacts(listId) {
|
|
105
|
+
const list = await this.contactsInstance.getList(listId);
|
|
106
|
+
|
|
107
|
+
return this.getAllPaginatedEntries(
|
|
108
|
+
'getContactsFromList',
|
|
109
|
+
listId,
|
|
110
|
+
'contacts',
|
|
111
|
+
[],
|
|
112
|
+
list.totalSubscribers,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async getAllPaginatedEntries(
|
|
117
|
+
functionName,
|
|
118
|
+
resourceIdParameter,
|
|
119
|
+
resultKey,
|
|
120
|
+
accumulator,
|
|
121
|
+
count,
|
|
122
|
+
offset = 0,
|
|
123
|
+
paginationSize = 50,
|
|
124
|
+
) {
|
|
125
|
+
if (accumulator.length >= count) {
|
|
126
|
+
return accumulator;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const promise = resourceIdParameter
|
|
130
|
+
? this.contactsInstance[functionName](resourceIdParameter, {
|
|
131
|
+
limit: paginationSize,
|
|
132
|
+
offset,
|
|
133
|
+
})
|
|
134
|
+
: this.contactsInstance[functionName](paginationSize, offset);
|
|
135
|
+
|
|
136
|
+
const result = await promise;
|
|
137
|
+
|
|
138
|
+
accumulator = accumulator.concat(result[resultKey]);
|
|
139
|
+
|
|
140
|
+
return this.getAllPaginatedEntries(
|
|
141
|
+
functionName,
|
|
142
|
+
resourceIdParameter,
|
|
143
|
+
resultKey,
|
|
144
|
+
accumulator,
|
|
145
|
+
count,
|
|
146
|
+
offset + paginationSize,
|
|
147
|
+
paginationSize,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
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.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
import { Octokit } from 'octokit';
|
|
4
|
+
|
|
5
|
+
import logger from '../logger/index.js';
|
|
6
|
+
|
|
7
|
+
const { version } = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url)).toString());
|
|
8
|
+
|
|
9
|
+
const ISSUE_STATE_CLOSED = 'closed';
|
|
10
|
+
const ISSUE_STATE_OPEN = 'open';
|
|
11
|
+
const ISSUE_STATE_ALL = 'all';
|
|
12
|
+
|
|
13
|
+
const CONTRIBUTE_URL = 'https://contribute.opentermsarchive.org/en/service';
|
|
14
|
+
const GOOGLE_URL = 'https://www.google.com/search?q=';
|
|
15
|
+
|
|
16
|
+
export default class Tracker {
|
|
17
|
+
static isRepositoryValid(repository) {
|
|
18
|
+
return repository.includes('/');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
constructor(trackerConfig) {
|
|
22
|
+
const { repository, label } = trackerConfig.githubIssues;
|
|
23
|
+
|
|
24
|
+
if (!Tracker.isRepositoryValid(repository)) {
|
|
25
|
+
throw new Error('tracker.githubIssues.repository should be a string with <owner>/<repo>');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const [ owner, repo ] = repository.split('/');
|
|
29
|
+
|
|
30
|
+
this.octokit = new Octokit({
|
|
31
|
+
auth: process.env.GITHUB_TOKEN,
|
|
32
|
+
userAgent: `opentermsarchive/${version}`,
|
|
33
|
+
});
|
|
34
|
+
this.cachedIssues = {};
|
|
35
|
+
this.commonParams = {
|
|
36
|
+
owner,
|
|
37
|
+
repo,
|
|
38
|
+
accept: 'application/vnd.github.v3+json',
|
|
39
|
+
};
|
|
40
|
+
this.repository = repository;
|
|
41
|
+
this.label = label;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async initialize() {
|
|
45
|
+
await this.createLabel({
|
|
46
|
+
name: this.label.name,
|
|
47
|
+
color: this.label.color,
|
|
48
|
+
description: this.label.description,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async onVersionRecorded(serviceId, type) {
|
|
53
|
+
await this.closeIssueIfExists({
|
|
54
|
+
labels: [this.label.name],
|
|
55
|
+
title: `Fix ${serviceId} - ${type}`,
|
|
56
|
+
comment: '🤖 Closed automatically as data was gathered successfully',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async onVersionNotChanged(serviceId, type) {
|
|
61
|
+
await this.closeIssueIfExists({
|
|
62
|
+
labels: [this.label.name],
|
|
63
|
+
title: `Fix ${serviceId} - ${type}`,
|
|
64
|
+
comment: '🤖 Closed automatically as version is unchanged but data has been fetched correctly',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async onFirstVersionRecorded(serviceId, type) {
|
|
69
|
+
return this.onVersionRecorded(serviceId, type);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async onInaccessibleContent(error, serviceId, type, documentDeclaration) {
|
|
73
|
+
const { title, body } = Tracker.formatIssueTitleAndBody({ message: error.toString(), repository: this.repository, documentDeclaration });
|
|
74
|
+
|
|
75
|
+
await this.createIssueIfNotExists({
|
|
76
|
+
title,
|
|
77
|
+
body,
|
|
78
|
+
labels: [this.label.name],
|
|
79
|
+
comment: '🤖 Reopened automatically as an error occured',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async createLabel(params) {
|
|
84
|
+
return this.octokit.rest.issues.createLabel({ ...this.commonParams, ...params })
|
|
85
|
+
.catch(error => {
|
|
86
|
+
if (error.toString().includes('"code":"already_exists"')) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
logger.error(`Could not create label "${params.name}": ${error.toString()}`);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async createIssue(params) {
|
|
94
|
+
const { data } = await this.octokit.rest.issues.create(params);
|
|
95
|
+
|
|
96
|
+
return data;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async searchIssues({ title, ...searchParams }) {
|
|
100
|
+
const request = {
|
|
101
|
+
per_page: 100,
|
|
102
|
+
...searchParams,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const issues = await this.octokit.paginate(
|
|
106
|
+
this.octokit.rest.issues.listForRepo,
|
|
107
|
+
request,
|
|
108
|
+
response => response.data,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const issuesWithSameTitle = issues.filter(item => item.title === title);
|
|
112
|
+
|
|
113
|
+
return issuesWithSameTitle;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async addCommentToIssue(params) {
|
|
117
|
+
const { data } = await this.octokit.rest.issues.createComment(params);
|
|
118
|
+
|
|
119
|
+
return data;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async createIssueIfNotExists({ title, body, labels, comment }) {
|
|
123
|
+
try {
|
|
124
|
+
const existingIssues = await this.searchIssues({ ...this.commonParams, title, labels, state: ISSUE_STATE_ALL });
|
|
125
|
+
|
|
126
|
+
if (!existingIssues.length) {
|
|
127
|
+
const existingIssue = await this.createIssue({ ...this.commonParams, title, body, labels });
|
|
128
|
+
|
|
129
|
+
logger.info(`🤖 Creating GitHub issue for ${title}: ${existingIssue.html_url}`);
|
|
130
|
+
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const openedIssues = existingIssues.filter(existingIssue => existingIssue.state === ISSUE_STATE_OPEN);
|
|
135
|
+
const hasNoneOpened = openedIssues.length === 0;
|
|
136
|
+
|
|
137
|
+
for (const existingIssue of existingIssues) {
|
|
138
|
+
if (hasNoneOpened) {
|
|
139
|
+
try {
|
|
140
|
+
/* eslint-disable no-await-in-loop */
|
|
141
|
+
await this.octokit.rest.issues.update({
|
|
142
|
+
...this.commonParams,
|
|
143
|
+
issue_number: existingIssue.number,
|
|
144
|
+
state: ISSUE_STATE_OPEN,
|
|
145
|
+
});
|
|
146
|
+
await this.addCommentToIssue({
|
|
147
|
+
...this.commonParams,
|
|
148
|
+
issue_number: existingIssue.number,
|
|
149
|
+
body: `${comment}\n${body}`,
|
|
150
|
+
});
|
|
151
|
+
/* eslint-enable no-await-in-loop */
|
|
152
|
+
logger.info(`🤖 Reopened automatically as an error occured for ${title}: ${existingIssue.html_url}`);
|
|
153
|
+
} catch (e) {
|
|
154
|
+
logger.error(`🤖 Could not update GitHub issue ${existingIssue.html_url}: ${e}`);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} catch (e) {
|
|
160
|
+
logger.error(`🤖 Could not create GitHub issue for ${title}: ${e}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async closeIssueIfExists({ title, comment, labels }) {
|
|
165
|
+
try {
|
|
166
|
+
const openedIssues = await this.searchIssues({ ...this.commonParams, title, labels, state: ISSUE_STATE_OPEN });
|
|
167
|
+
|
|
168
|
+
for (const openedIssue of openedIssues) {
|
|
169
|
+
try {
|
|
170
|
+
await this.octokit.rest.issues.update({ ...this.commonParams, issue_number: openedIssue.number, state: ISSUE_STATE_CLOSED }); // eslint-disable-line no-await-in-loop
|
|
171
|
+
await this.addCommentToIssue({ ...this.commonParams, issue_number: openedIssue.number, body: comment }); // eslint-disable-line no-await-in-loop
|
|
172
|
+
logger.info(`🤖 GitHub issue closed for ${title}: ${openedIssue.html_url}`);
|
|
173
|
+
} catch (e) {
|
|
174
|
+
logger.error(`🤖 Could not close GitHub issue ${openedIssue.html_url}: ${e.toString()}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch (e) {
|
|
178
|
+
logger.error(`🤖 Could not close GitHub issue for ${title}: ${e}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static formatIssueTitleAndBody({ message, repository, documentDeclaration }) {
|
|
183
|
+
const { service: { name }, type } = documentDeclaration;
|
|
184
|
+
const json = documentDeclaration.toPersistence();
|
|
185
|
+
const title = `Fix ${name} - ${type}`;
|
|
186
|
+
|
|
187
|
+
const encodedName = encodeURIComponent(name);
|
|
188
|
+
const encodedType = encodeURIComponent(type);
|
|
189
|
+
|
|
190
|
+
const urlQueryParams = new URLSearchParams({
|
|
191
|
+
json: JSON.stringify(json),
|
|
192
|
+
destination: repository,
|
|
193
|
+
expertMode: 'true',
|
|
194
|
+
step: '2',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const body = `
|
|
198
|
+
This document is no longer properly tracked.
|
|
199
|
+
|
|
200
|
+
\`${message}\`
|
|
201
|
+
|
|
202
|
+
Check what's wrong by:
|
|
203
|
+
- Using the [online contribution tool](${CONTRIBUTE_URL}?${urlQueryParams}).
|
|
204
|
+
${message.includes('404') ? `- [Searching Google](${GOOGLE_URL}%22${encodedName}%22+%22${encodedType}%22) to get for a new URL.` : ''}
|
|
205
|
+
|
|
206
|
+
And some info about what has already been tracked:
|
|
207
|
+
- See [service declaration JSON file](https://github.com/${repository}/blob/main/declarations/${encodedName}.json).
|
|
208
|
+
`;
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
title,
|
|
212
|
+
body,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import DocumentDeclaration from '../../src/archivist/services/documentDeclaration.js';
|
|
2
|
+
import PageDeclaration from '../../src/archivist/services/pageDeclaration.js';
|
|
3
|
+
import Service from '../../src/archivist/services/service.js';
|
|
4
|
+
|
|
5
|
+
const service = new Service({
|
|
6
|
+
id: 'service_A',
|
|
7
|
+
name: 'Service A',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
service.addDocumentDeclaration(new DocumentDeclaration({
|
|
11
|
+
service,
|
|
12
|
+
type: 'Terms of Service',
|
|
13
|
+
validUntil: null,
|
|
14
|
+
pages: [new PageDeclaration({
|
|
15
|
+
location: 'https://www.servicea.example/tos',
|
|
16
|
+
contentSelectors: 'body',
|
|
17
|
+
noiseSelectors: undefined,
|
|
18
|
+
filters: undefined,
|
|
19
|
+
})],
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
export default service;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Terms of service with UTF-8 'çhãràčtęrs"
|
|
2
|
+
========================================
|
|
3
|
+
|
|
4
|
+
Dapibus quis diam sagittis vitae orci fames fusce porta metus risus, gravida pharetra cras rhoncus nullam tempor purus mattis sit condimentum senectus, a sodales varius in natoque molestie tortor velit quisque. Parturient sit facilisis risus dictumst quisque penatibus donec, mollis ultrices nibh viverra consequat porttitor dignissim laoreet, auctor fermentum himenaeos ut fusce volutpat. Dignissim nulla penatibus lacus at augue sem hendrerit sit, et potenti cum aptent ultricies aliquam habitant, porta lobortis cubilia primis sociis nascetur etiam. Torquent duis facilisis aliquam est purus curae quam sit porta, augue netus laoreet per pharetra volutpat lectus sem commodo, senectus primis arcu suspendisse id vivamus consequat sapien. Litora eros hendrerit commodo eleifend scelerisque risus in fusce porttitor cubilia ultrices porta sed blandit cras condimentum quisque, class accumsan odio enim magna tempus facilisis auctor imperdiet fringilla arcu tristique volutpat faucibus molestie velit. Per quam tristique tortor viverra tempus class varius condimentum, curabitur tellus cras fusce elementum velit cubilia, hendrerit orci vestibulum libero in enim gravida. Eget praesent dictumst lobortis pretium dignissim at viverra tempus fusce potenti, in morbi nascetur purus natoque sagittis phasellus urna sit commodo, ad tortor hendrerit ante mauris venenatis dolor consequat donec.
|
|
5
|
+
|
|
6
|
+
* * *
|
|
7
|
+
|
|
8
|
+
Suscipit sed eleifend sodales parturient interdum primis vel accumsan, curabitur sem blandit ut pretium sociosqu nam integer vehicula, amet lacinia augue commodo neque sollicitudin duis quam, malesuada cubilia phasellus gravida ad nisl eros. Curae consectetur justo tortor hendrerit morbi habitasse vel eros feugiat per, tristique ante nostra condimentum facilisi enim neque dui taciti dolor, placerat elit molestie luctus etiam magna gravida sed ad. Senectus habitasse sem commodo consectetur litora tempor leo curabitur gravida vestibulum, suscipit tortor viverra pellentesque rhoncus tristique porttitor convallis. Sodales morbi dictumst ut dolor vivamus elit velit orci non tristique nullam, nunc class scelerisque aenean facilisis urna suscipit etiam lobortis purus tortor commodo, duis vel pellentesque taciti aliquam id adipiscing dictum placerat et. At interdum quis eget ut curabitur fusce aliquet commodo suscipit, platea metus libero tortor eros justo in odio ligula fames, gravida placerat augue amet posuere massa fermentum magna. Enim quisque suspendisse accumsan odio ipsum taciti habitasse viverra, venenatis aliquet ac augue auctor nibh cras dignissim magna, convallis varius in sed quam morbi pretium. Habitasse nunc arcu eleifend purus aliquam lobortis morbi porta netus dignissim nisl, vitae class placerat amet sapien at vivamus et dui. Purus risus tellus netus sodales molestie leo nullam nunc, elit curae integer odio nostra ut vitae malesuada, dictum mus justo commodo curabitur placerat luctus. Dui ultrices auctor ultricies fringilla tortor ligula aptent, lectus pulvinar orci nascetur aenean a proin curabitur, ad felis nam nostra nec blandit.
|
|
9
|
+
|
|
10
|
+
Dui tristique interdum commodo urna blandit fames tortor posuere dictum fermentum sociis, hendrerit ut libero facilisis id morbi habitant iaculis feugiat. Gravida metus netus ac malesuada vel tristique potenti augue cubilia elit, pellentesque suscipit posuere sapien tempus in praesent torquent dapibus, sit viverra iaculis ultrices aliquet mauris non quisque vitae. Senectus gravida a in litora taciti per ullamcorper nascetur, quis auctor adipiscing nostra cras lacinia aliquet, curae id montes vel molestie placerat condimentum. Convallis fermentum eros sagittis viverra curae risus, aliquet lorem dui magna urna suscipit, id condimentum faucibus molestie ridiculus. Mollis enim etiam taciti diam at pharetra sagittis ligula euismod, ridiculus et sociosqu mauris integer lacinia elementum tortor. In sem nibh cubilia curae facilisi mauris massa, facilisis rhoncus velit phasellus etiam cum penatibus neque, tortor vulputate felis est litora mi.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>First provider TOS</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<h1>Terms of service with UTF-8 'çhãràčtęrs"</h1>
|
|
9
|
+
<p>Dapibus quis diam sagittis vitae orci fames fusce porta metus risus, gravida pharetra cras rhoncus nullam tempor purus mattis sit condimentum senectus, a sodales varius in natoque molestie tortor velit quisque. Parturient sit facilisis risus dictumst quisque penatibus donec, mollis ultrices nibh viverra consequat porttitor dignissim laoreet, auctor fermentum himenaeos ut fusce volutpat. Dignissim nulla penatibus lacus at augue sem hendrerit sit, et potenti cum aptent ultricies aliquam habitant, porta lobortis cubilia primis sociis nascetur etiam. Torquent duis facilisis aliquam est purus curae quam sit porta, augue netus laoreet per pharetra volutpat lectus sem commodo, senectus primis arcu suspendisse id vivamus consequat sapien. Litora eros hendrerit commodo eleifend scelerisque risus in fusce porttitor cubilia ultrices porta sed blandit cras condimentum quisque, class accumsan odio enim magna tempus facilisis auctor imperdiet fringilla arcu tristique volutpat faucibus molestie velit. Per quam tristique tortor viverra tempus class varius condimentum, curabitur tellus cras fusce elementum velit cubilia, hendrerit orci vestibulum libero in enim gravida. Eget praesent dictumst lobortis pretium dignissim at viverra tempus fusce potenti, in morbi nascetur purus natoque sagittis phasellus urna sit commodo, ad tortor hendrerit ante mauris venenatis dolor consequat donec.
|
|
10
|
+
<hr>
|
|
11
|
+
<p>Suscipit sed eleifend sodales parturient interdum primis vel accumsan, curabitur sem blandit ut pretium sociosqu nam integer vehicula, amet lacinia augue commodo neque sollicitudin duis quam, malesuada cubilia phasellus gravida ad nisl eros. Curae consectetur justo tortor hendrerit morbi habitasse vel eros feugiat per, tristique ante nostra condimentum facilisi enim neque dui taciti dolor, placerat elit molestie luctus etiam magna gravida sed ad. Senectus habitasse sem commodo consectetur litora tempor leo curabitur gravida vestibulum, suscipit tortor viverra pellentesque rhoncus tristique porttitor convallis. Sodales morbi dictumst ut dolor vivamus elit velit orci non tristique nullam, nunc class scelerisque aenean facilisis urna suscipit etiam lobortis purus tortor commodo, duis vel pellentesque taciti aliquam id adipiscing dictum placerat et. At interdum quis eget ut curabitur fusce aliquet commodo suscipit, platea metus libero tortor eros justo in odio ligula fames, gravida placerat augue amet posuere massa fermentum magna. Enim quisque suspendisse accumsan odio ipsum taciti habitasse viverra, venenatis aliquet ac augue auctor nibh cras dignissim magna, convallis varius in sed quam morbi pretium. Habitasse nunc arcu eleifend purus aliquam lobortis morbi porta netus dignissim nisl, vitae class placerat amet sapien at vivamus et dui. Purus risus tellus netus sodales molestie leo nullam nunc, elit curae integer odio nostra ut vitae malesuada, dictum mus justo commodo curabitur placerat luctus. Dui ultrices auctor ultricies fringilla tortor ligula aptent, lectus pulvinar orci nascetur aenean a proin curabitur, ad felis nam nostra nec blandit.</p>
|
|
12
|
+
<p>Dui tristique interdum commodo urna blandit fames tortor posuere dictum fermentum sociis, hendrerit ut libero facilisis id morbi habitant iaculis feugiat. Gravida metus netus ac malesuada vel tristique potenti augue cubilia elit, pellentesque suscipit posuere sapien tempus in praesent torquent dapibus, sit viverra iaculis ultrices aliquet mauris non quisque vitae. Senectus gravida a in litora taciti per ullamcorper nascetur, quis auctor adipiscing nostra cras lacinia aliquet, curae id montes vel molestie placerat condimentum. Convallis fermentum eros sagittis viverra curae risus, aliquet lorem dui magna urna suscipit, id condimentum faucibus molestie ridiculus. Mollis enim etiam taciti diam at pharetra sagittis ligula euismod, ridiculus et sociosqu mauris integer lacinia elementum tortor. In sem nibh cubilia curae facilisi mauris massa, facilisis rhoncus velit phasellus etiam cum penatibus neque, tortor vulputate felis est litora mi.</p></p>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import DocumentDeclaration from '../../src/archivist/services/documentDeclaration.js';
|
|
2
|
+
import PageDeclaration from '../../src/archivist/services/pageDeclaration.js';
|
|
3
|
+
import Service from '../../src/archivist/services/service.js';
|
|
4
|
+
|
|
5
|
+
const service = new Service({
|
|
6
|
+
id: 'service_B',
|
|
7
|
+
name: 'Service B',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
service.addDocumentDeclaration(new DocumentDeclaration({
|
|
11
|
+
service,
|
|
12
|
+
type: 'Privacy Policy',
|
|
13
|
+
pages: [new PageDeclaration({
|
|
14
|
+
location: 'https://www.serviceb.example/privacy',
|
|
15
|
+
contentSelectors: 'body',
|
|
16
|
+
noiseSelectors: undefined,
|
|
17
|
+
filters: undefined,
|
|
18
|
+
})],
|
|
19
|
+
validUntil: null,
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
export default service;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import DocumentDeclaration from '../../src/archivist/services/documentDeclaration.js';
|
|
2
|
+
import PageDeclaration from '../../src/archivist/services/pageDeclaration.js';
|
|
3
|
+
import Service from '../../src/archivist/services/service.js';
|
|
4
|
+
|
|
5
|
+
const service = new Service({
|
|
6
|
+
id: 'service_with_declaration_history',
|
|
7
|
+
name: 'Service with declaration history',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const filters = [
|
|
11
|
+
async function removeShareButton() {
|
|
12
|
+
return 'last-removeShareButton';
|
|
13
|
+
},
|
|
14
|
+
async function removePrintButton() {
|
|
15
|
+
return 'last-removePrintButton';
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
[
|
|
20
|
+
new DocumentDeclaration({
|
|
21
|
+
service,
|
|
22
|
+
type: 'Terms of Service',
|
|
23
|
+
pages: [
|
|
24
|
+
new PageDeclaration({
|
|
25
|
+
location: 'https://www.service-with-declaration-history.example/terms',
|
|
26
|
+
contentSelectors: 'main',
|
|
27
|
+
noiseSelectors: undefined,
|
|
28
|
+
filters,
|
|
29
|
+
}),
|
|
30
|
+
],
|
|
31
|
+
validUntil: null,
|
|
32
|
+
}),
|
|
33
|
+
new DocumentDeclaration({
|
|
34
|
+
service,
|
|
35
|
+
type: 'Terms of Service',
|
|
36
|
+
pages: [
|
|
37
|
+
new PageDeclaration({
|
|
38
|
+
location: 'https://www.service-with-declaration-history.example/tos',
|
|
39
|
+
contentSelectors: 'body',
|
|
40
|
+
noiseSelectors: undefined,
|
|
41
|
+
filters: undefined,
|
|
42
|
+
}),
|
|
43
|
+
],
|
|
44
|
+
validUntil: '2020-08-22T21:30:21.000Z',
|
|
45
|
+
}),
|
|
46
|
+
new DocumentDeclaration({
|
|
47
|
+
service,
|
|
48
|
+
type: 'Terms of Service',
|
|
49
|
+
pages: [
|
|
50
|
+
new PageDeclaration({
|
|
51
|
+
location: 'https://www.service-with-declaration-history.example/tos',
|
|
52
|
+
contentSelectors: 'main',
|
|
53
|
+
noiseSelectors: undefined,
|
|
54
|
+
filters: [
|
|
55
|
+
async function removeShareButton() {
|
|
56
|
+
return 'last-removeShareButton';
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
validUntil: '2020-09-30T21:30:21.000Z',
|
|
62
|
+
}),
|
|
63
|
+
].forEach(declaration => service.addDocumentDeclaration(declaration));
|
|
64
|
+
|
|
65
|
+
export default service;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import DocumentDeclaration from '../../src/archivist/services/documentDeclaration.js';
|
|
2
|
+
import PageDeclaration from '../../src/archivist/services/pageDeclaration.js';
|
|
3
|
+
import Service from '../../src/archivist/services/service.js';
|
|
4
|
+
|
|
5
|
+
const service = new Service({
|
|
6
|
+
id: 'service_with_filters_history',
|
|
7
|
+
name: 'Service with filters history',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const filters = [
|
|
11
|
+
async function removeShareButton() {
|
|
12
|
+
return 'last-removeShareButton';
|
|
13
|
+
},
|
|
14
|
+
async function removePrintButton() {
|
|
15
|
+
return 'last-removePrintButton';
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
[
|
|
20
|
+
new DocumentDeclaration({
|
|
21
|
+
service,
|
|
22
|
+
type: 'Terms of Service',
|
|
23
|
+
pages: [
|
|
24
|
+
new PageDeclaration({
|
|
25
|
+
location: 'https://www.service-with-filters-history.example/terms',
|
|
26
|
+
contentSelectors: 'main',
|
|
27
|
+
noiseSelectors: undefined,
|
|
28
|
+
filters,
|
|
29
|
+
}),
|
|
30
|
+
],
|
|
31
|
+
validUntil: null,
|
|
32
|
+
}),
|
|
33
|
+
new DocumentDeclaration({
|
|
34
|
+
service,
|
|
35
|
+
type: 'Terms of Service',
|
|
36
|
+
pages: [
|
|
37
|
+
new PageDeclaration({
|
|
38
|
+
location: 'https://www.service-with-filters-history.example/terms',
|
|
39
|
+
contentSelectors: 'main',
|
|
40
|
+
noiseSelectors: undefined,
|
|
41
|
+
filters: [
|
|
42
|
+
async function removeShareButton() {
|
|
43
|
+
return 'first-removeShareButton';
|
|
44
|
+
},
|
|
45
|
+
async function removePrintButton() {
|
|
46
|
+
return 'first-removePrintButton';
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
validUntil: '2020-07-22T11:30:21.000Z',
|
|
52
|
+
}),
|
|
53
|
+
new DocumentDeclaration({
|
|
54
|
+
service,
|
|
55
|
+
type: 'Terms of Service',
|
|
56
|
+
pages: [
|
|
57
|
+
new PageDeclaration({
|
|
58
|
+
location: 'https://www.service-with-filters-history.example/terms',
|
|
59
|
+
contentSelectors: 'main',
|
|
60
|
+
noiseSelectors: undefined,
|
|
61
|
+
filters: [
|
|
62
|
+
async function removeShareButton() {
|
|
63
|
+
return 'first-removeShareButton';
|
|
64
|
+
},
|
|
65
|
+
async function removePrintButton() {
|
|
66
|
+
return 'second-removePrintButton';
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
}),
|
|
70
|
+
],
|
|
71
|
+
validUntil: '2020-08-22T11:30:21.000Z',
|
|
72
|
+
}),
|
|
73
|
+
new DocumentDeclaration({
|
|
74
|
+
service,
|
|
75
|
+
type: 'Terms of Service',
|
|
76
|
+
pages: [
|
|
77
|
+
new PageDeclaration({
|
|
78
|
+
location: 'https://www.service-with-filters-history.example/terms',
|
|
79
|
+
contentSelectors: 'main',
|
|
80
|
+
noiseSelectors: undefined,
|
|
81
|
+
filters: [
|
|
82
|
+
async function removeShareButton() {
|
|
83
|
+
return 'second-removeShareButton';
|
|
84
|
+
},
|
|
85
|
+
async function removePrintButton() {
|
|
86
|
+
return 'second-removePrintButton';
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
}),
|
|
90
|
+
],
|
|
91
|
+
validUntil: '2020-09-29T21:30:21.000Z',
|
|
92
|
+
}),
|
|
93
|
+
new DocumentDeclaration({
|
|
94
|
+
service,
|
|
95
|
+
type: 'Terms of Service',
|
|
96
|
+
pages: [
|
|
97
|
+
new PageDeclaration({
|
|
98
|
+
location: 'https://www.service-with-filters-history.example/terms',
|
|
99
|
+
contentSelectors: 'main',
|
|
100
|
+
noiseSelectors: undefined,
|
|
101
|
+
filters: [
|
|
102
|
+
async function removeShareButton() {
|
|
103
|
+
return 'second-removeShareButton';
|
|
104
|
+
},
|
|
105
|
+
async function removePrintButton() {
|
|
106
|
+
return 'third-removePrintButton';
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
111
|
+
validUntil: '2020-09-30T21:30:21.000Z',
|
|
112
|
+
}),
|
|
113
|
+
new DocumentDeclaration({
|
|
114
|
+
service,
|
|
115
|
+
type: 'Terms of Service',
|
|
116
|
+
pages: [
|
|
117
|
+
new PageDeclaration({
|
|
118
|
+
location: 'https://www.service-with-filters-history.example/terms',
|
|
119
|
+
contentSelectors: 'main',
|
|
120
|
+
noiseSelectors: undefined,
|
|
121
|
+
filters: [
|
|
122
|
+
async function removeShareButton() {
|
|
123
|
+
return 'third-removeShareButton';
|
|
124
|
+
},
|
|
125
|
+
async function removePrintButton() {
|
|
126
|
+
return 'third-removePrintButton';
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
}),
|
|
130
|
+
],
|
|
131
|
+
validUntil: '2020-10-20T12:30:21.000Z',
|
|
132
|
+
}),
|
|
133
|
+
new DocumentDeclaration({
|
|
134
|
+
service,
|
|
135
|
+
type: 'Terms of Service',
|
|
136
|
+
pages: [
|
|
137
|
+
new PageDeclaration({
|
|
138
|
+
location: 'https://www.service-with-filters-history.example/terms',
|
|
139
|
+
contentSelectors: 'main',
|
|
140
|
+
noiseSelectors: undefined,
|
|
141
|
+
filters: [
|
|
142
|
+
async function removeShareButton() {
|
|
143
|
+
return 'third-removeShareButton';
|
|
144
|
+
},
|
|
145
|
+
async function removePrintButton() {
|
|
146
|
+
return 'last-removePrintButton';
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
}),
|
|
150
|
+
],
|
|
151
|
+
validUntil: '2020-11-01T12:30:21.000Z',
|
|
152
|
+
}),
|
|
153
|
+
].forEach(declaration => service.addDocumentDeclaration(declaration));
|
|
154
|
+
|
|
155
|
+
export default service;
|