@opentermsarchive/engine 0.25.2 → 0.26.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/README.md +1 -469
- package/bin/ota-lint.js +41 -4
- package/bin/ota-validate.js +3 -2
- package/config/test.json +2 -2
- package/package.json +4 -4
- package/scripts/dataset/export/test/fixtures/dataset/README.md +1 -1
- package/scripts/declarations/lint/index.mocha.js +105 -0
- package/scripts/declarations/validate/index.mocha.js +0 -48
- package/scripts/rewrite/config/rewrite-snapshots.json +1 -1
- package/scripts/rewrite/config/rewrite-versions.json +1 -1
- package/src/archivist/recorder/repositories/git/dataMapper.js +1 -1
- package/scripts/declarations/lint/index.js +0 -36
package/README.md
CHANGED
|
@@ -4,475 +4,7 @@ _The document you are reading now is targeted at developers wanting to use or co
|
|
|
4
4
|
|
|
5
5
|
This codebase is a Node.js module enabling downloading, archiving and publishing versions of documents obtained online. It can be used independently from the Open Terms Archive ecosystem.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- [Motivation](#motivation)
|
|
10
|
-
- [Main concepts](#main-concepts)
|
|
11
|
-
- [How to add documents to a collection](#how-to-add-documents-to-a-collection)
|
|
12
|
-
- [How to use the engine](#how-to-use-the-engine)
|
|
13
|
-
- [Configuring](#configuring)
|
|
14
|
-
- [Deploying](#deploying)
|
|
15
|
-
- [Contributing](#contributing)
|
|
16
|
-
- [License](#license)
|
|
17
|
-
|
|
18
|
-
## Motivation
|
|
19
|
-
|
|
20
|
-
_Words in bold are [business domain names](https://en.wikipedia.org/wiki/Domain-driven_design)._
|
|
21
|
-
|
|
22
|
-
**Services** have **terms** written in **documents**, contractual (Terms of Services, Privacy Policy…) or not (Community Guidelines, Deceased User Policy…), that can change over time. Open Terms Archive enables users rights advocates, regulatory bodies and interested citizens to follow the **changes** to these **terms**, to be notified whenever a new **version** is published, to explore their entire **history** and to collaborate in analysing them. This free and open-source engine is developed to support these goals.
|
|
23
|
-
|
|
24
|
-
## Main concepts
|
|
25
|
-
|
|
26
|
-
### Instances
|
|
27
|
-
|
|
28
|
-
Open Terms Archive is a decentralised system.
|
|
29
|
-
|
|
30
|
-
It aims at enabling any entity to **track** **terms** on its own and at federating a number of public **instances** in a single ecosystem to maximise discoverability, collaboration and political power. To that end, the Open Terms Archive **engine** can be run on any server, thus making it a dedicated **instance**.
|
|
31
|
-
|
|
32
|
-
> Federated public instances can be [found on GitHub](
|
|
33
|
-
https://github.com/OpenTermsArchive?q=declarations).
|
|
34
|
-
|
|
35
|
-
### Collections
|
|
36
|
-
|
|
37
|
-
An **instance** **tracks** **documents** of a single **collection**.
|
|
38
|
-
|
|
39
|
-
A **collection** is characterised by a **scope** across **dimensions** that describe the **terms** it **tracks**, such as **language**, **jurisdiction** and **industry**.
|
|
40
|
-
|
|
41
|
-
> Federated public collections can be [found on GitHub](https://github.com/OpenTermsArchive?q=versions).
|
|
42
|
-
|
|
43
|
-
#### Example scope
|
|
44
|
-
|
|
45
|
-
> The documents declared in this collection are:
|
|
46
|
-
> - Related to dating services used in Europe.
|
|
47
|
-
> - In the European Union and Switzerland jurisdictions.
|
|
48
|
-
> - In English, unless no English version exists, in which case the primary official language of the jurisdiction of incorporation of the service operator will be used.
|
|
49
|
-
|
|
50
|
-
### Terms types
|
|
51
|
-
|
|
52
|
-
To distinguish between the different **terms** of a **service**, each has a **type**, such as “Terms of Service”, “Privacy Policy”, “Developer Agreement”…
|
|
53
|
-
|
|
54
|
-
This **type** matches the topic, but not necessarily the title the **service** gives to it. Unifying the **types** enables comparing **terms** across **services**.
|
|
55
|
-
|
|
56
|
-
> More information on terms types can be found in the [dedicated repository](https://github.com/OpenTermsArchive/terms-types). They are published on NPM under [`@opentermsarchive/terms-types`](https://www.npmjs.com/package/@opentermsarchive/terms-types), enabling standardisation and interoperability beyond the Open Terms Archive engine.
|
|
57
|
-
|
|
58
|
-
### Declarations
|
|
59
|
-
|
|
60
|
-
The **documents** that constitute a **collection** are defined in simple JSON files called **declarations**.
|
|
61
|
-
|
|
62
|
-
A **declaration** also contains some metadata on the **service** the **documents** relate to.
|
|
63
|
-
|
|
64
|
-
> Here is an example declaration tracking the Privacy Policy of Open Terms Archive:
|
|
65
|
-
>
|
|
66
|
-
> ```json
|
|
67
|
-
> {
|
|
68
|
-
> "name": "Open Terms Archive",
|
|
69
|
-
> "documents": {
|
|
70
|
-
> "Privacy Policy": {
|
|
71
|
-
> "fetch": "https://opentermsarchive.org/en/privacy-policy",
|
|
72
|
-
> "select": ".TextContent_textContent__ToW2S"
|
|
73
|
-
> }
|
|
74
|
-
> }
|
|
75
|
-
> }
|
|
76
|
-
> ```
|
|
77
|
-
|
|
78
|
-
## How to add documents to a collection
|
|
79
|
-
|
|
80
|
-
Open Terms Archive **acquires** **documents** to deliver an explorable **history** of **changes**. This can be done in two ways:
|
|
81
|
-
|
|
82
|
-
1. For the present and future, by **tracking** **documents**.
|
|
83
|
-
2. For the past, by **importing** from an existing **fonds** such as [ToSBack](https://tosback.org), the [Internet Archive](https://archive.org/web/), [Common Crawl](https://commoncrawl.org) or any other in-house format.
|
|
84
|
-
|
|
85
|
-
### Tracking documents
|
|
86
|
-
|
|
87
|
-
The **engine** **reads** **declarations** to **record** a **snapshot** by **fetching** the declared web **location** periodically. The **engine** then **extracts** a **version** from this **snapshot** by:
|
|
88
|
-
|
|
89
|
-
1. **Selecting** the subset of the **snapshot** that contains the **terms** (instead of navigation menus, footers, cookies banners…).
|
|
90
|
-
2. **Removing** residual content in this subset that is not part of the **terms** (ads, illustrative pictures, internal navigation links…).
|
|
91
|
-
3. **Filtering noise** by preventing parts that change frequently from triggering false positives for **changes** (tracker identifiers in links, relative dates…). The **engine** can execute custom **filters** written in JavaScript to that end.
|
|
92
|
-
|
|
93
|
-
After these steps, if **changes** are spotted in the resulting **document**, a new **version** is **recorded**.
|
|
94
|
-
|
|
95
|
-
Preserving **snapshots** enables recovering after the fact information potentially lost in the **extraction** step: if **declarations** were wrong, they can be **maintained** and corrected **versions** can be **extracted** from the original **snapshots**.
|
|
96
|
-
|
|
97
|
-
### Importing documents
|
|
98
|
-
|
|
99
|
-
Existing **fonds** can be prepared for easier analysis by unifying their format to the **Open Terms Archive dataset format**. This unique format enables building interoperable tools, fostering collaboration across reusers.
|
|
100
|
-
Such a dataset can be generated from **versions** alone. If **snapshots** and **declarations** can be retrieved from the **fonds** too, then a full-fledged **collection** can be created.
|
|
101
|
-
|
|
102
|
-
## How to use the engine
|
|
103
|
-
|
|
104
|
-
This documentation describes how to execute the **engine** independently from any specific **instance**. For other use cases, other parts of the documentation could be more relevant:
|
|
105
|
-
|
|
106
|
-
- to contribute **declarations** to an existing **instance**, see [how to contribute documents](./docs/doc-contributing-documents.md);
|
|
107
|
-
- to create a new **collection**, see the [collection bootstrap](https://github.com/OpenTermsArchive/template-declarations) script;
|
|
108
|
-
- to create a new public **instance**, see the [governance](./docs/doc-governance.md) documentation.
|
|
109
|
-
|
|
110
|
-
### Requirements
|
|
111
|
-
|
|
112
|
-
This module is tested to work across operating systems (continuous testing on UNIX, macOS and Windows).
|
|
113
|
-
|
|
114
|
-
A [Node.js](https://nodejs.org/en/download/) runtime is required to execute this engine.
|
|
115
|
-
|
|
116
|
-

|
|
117
|
-
|
|
118
|
-
### Getting started
|
|
119
|
-
|
|
120
|
-
This engine is published as a [module on NPM](https://npmjs.com/package/@opentermsarchive/engine). The recommended install is as a dependency in a `package.json` file, next to a folder containing [declaration files](#declarations).
|
|
121
|
-
|
|
122
|
-
```sh
|
|
123
|
-
npm install --save @opentermsarchive/engine
|
|
124
|
-
mkdir declarations
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
In an editor, create the following declaration file in `declarations/Open Terms Archive.json` to track the terms of the Open Terms Archive website:
|
|
128
|
-
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"name": "Open Terms Archive",
|
|
132
|
-
"documents": {
|
|
133
|
-
"Privacy Policy": {
|
|
134
|
-
"fetch": "https://opentermsarchive.org/en/privacy-policy",
|
|
135
|
-
"select": ".TextContent_textContent__ToW2S"
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
In the terminal:
|
|
142
|
-
|
|
143
|
-
```sh
|
|
144
|
-
npx ota-track
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
The tracked documents can be found in the `data` folder.
|
|
148
|
-
|
|
149
|
-
This quick example aimed at letting you try the engine quickly. Most likely, you will simply `npm install` from an existing collection, or create a new collection from the [collection template](https://github.com/OpenTermsArchive/template-declarations).
|
|
150
|
-
|
|
151
|
-
### CLI
|
|
152
|
-
|
|
153
|
-
Once the engine module is installed as a dependency within another module, the `ota` command with the following subcommands is available.
|
|
154
|
-
|
|
155
|
-
In these commands:
|
|
156
|
-
|
|
157
|
-
- **`<service_id>`** is the case sensitive name of the service declaration file without the extension. For example, for `Twitter.json`, the service ID is `Twitter`.
|
|
158
|
-
- **`<terms_type>`** is the property name used under the `documents` property in the declaration to declare a terms. For example, in the getting started declaration, the terms type declared is `Privacy Policy`.
|
|
159
|
-
|
|
160
|
-
#### `ota track`
|
|
161
|
-
|
|
162
|
-
```sh
|
|
163
|
-
npx ota track
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
[Track](#tracking-documents) the current terms of services according to provided declarations.
|
|
167
|
-
|
|
168
|
-
The declarations, snapshots and versions paths are defined in the [configuration](#configuring).
|
|
169
|
-
|
|
170
|
-
> Note that the snapshots and versions will be recorded at the moment the command is executed, on top of the existing local history. If a shared history already exists and the goal is to add on top of it, that history has to be downloaded before executing that command.
|
|
171
|
-
|
|
172
|
-
##### Recap of available options
|
|
173
|
-
|
|
174
|
-
```sh
|
|
175
|
-
npx ota track --help
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
##### Track terms of specific services
|
|
179
|
-
|
|
180
|
-
```sh
|
|
181
|
-
npx ota track --services "<service_id>" ["<service_id>"...]
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
##### Track specific terms of specific services
|
|
185
|
-
|
|
186
|
-
```sh
|
|
187
|
-
npx ota track --services "<service_id>" ["<service_id>"...] --terms-types "<terms_type>" ["<terms_type>"...]
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
##### Track documents four times a day
|
|
191
|
-
|
|
192
|
-
```sh
|
|
193
|
-
npx ota track --schedule
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
#### `ota validate`
|
|
197
|
-
|
|
198
|
-
```sh
|
|
199
|
-
npx ota validate [--services <service_id>...] [--terms-types <terms_type>...]
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
Check that all declarations allow recording a snapshot and a version properly.
|
|
203
|
-
|
|
204
|
-
If one or several `<service_id>` are provided, check only those services.
|
|
205
|
-
|
|
206
|
-
##### Validate schema only
|
|
207
|
-
|
|
208
|
-
```sh
|
|
209
|
-
npx ota validate --schema-only [--services <service_id>...] [--terms-types <terms_type>...]
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Check that all declarations are readable by the engine.
|
|
213
|
-
|
|
214
|
-
Allows for a much faster check of declarations, but does not check that the documents are actually accessible.
|
|
215
|
-
|
|
216
|
-
If one or several `<service_id>` are provided, check only those services.
|
|
217
|
-
|
|
218
|
-
##### Validate modified terms only
|
|
219
|
-
|
|
220
|
-
```sh
|
|
221
|
-
npx ota validate --modified
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Run [`ota validate`](#ota-validate) only on files that have been modified in Git.
|
|
225
|
-
|
|
226
|
-
#### `ota lint`
|
|
227
|
-
|
|
228
|
-
```sh
|
|
229
|
-
npx ota lint [--services <service_id>...]
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Normalise the format of declarations.
|
|
233
|
-
|
|
234
|
-
Automatically correct formatting mistakes and ensure that all declarations are standardised.
|
|
235
|
-
|
|
236
|
-
If one or several `<service_id>` are provided, check only those services.
|
|
237
|
-
|
|
238
|
-
#### `ota dataset`
|
|
239
|
-
|
|
240
|
-
Export the versions dataset into a ZIP file and publish it to GitHub releases.
|
|
241
|
-
|
|
242
|
-
The dataset title and the URL of the versions repository are defined in the [configuration](#configuring).
|
|
243
|
-
|
|
244
|
-
To export the dataset into a local ZIP file:
|
|
245
|
-
|
|
246
|
-
```sh
|
|
247
|
-
npx ota dataset [--file <filename>]
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
To export the dataset into a ZIP file and publish it on GitHub releases:
|
|
251
|
-
|
|
252
|
-
```sh
|
|
253
|
-
GITHUB_TOKEN=ghp_XXXXXXXXX npx ota dataset --publish
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
The `GITHUB_TOKEN` can also be defined in a [`.env` file](#environment-variables).
|
|
257
|
-
|
|
258
|
-
To export, publish the dataset and remove the local copy that was created after it has been uploaded:
|
|
259
|
-
|
|
260
|
-
```sh
|
|
261
|
-
GITHUB_TOKEN=ghp_XXXXXXXXX npx ota dataset --publish --remove-local-copy
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
##### Publish dataset on monday every week
|
|
265
|
-
|
|
266
|
-
To schedule export, publishing and local copy removal:
|
|
267
|
-
|
|
268
|
-
```sh
|
|
269
|
-
GITHUB_TOKEN=ghp_XXXXXXXXX npx ota dataset --schedule --publish --remove-local-copy
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### API
|
|
273
|
-
|
|
274
|
-
Once added as a dependency, the engine exposes a JavaScript API that can be called in your own code. The following modules are available.
|
|
275
|
-
|
|
276
|
-
#### `fetch`
|
|
277
|
-
|
|
278
|
-
The `fetch` module gets the MIME type and content of a document from its URL
|
|
279
|
-
|
|
280
|
-
```js
|
|
281
|
-
import fetch from '@opentermsarchive/engine/fetch';
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
Documentation on how to use `fetch` is provided [as JSDoc](./src/archivist/fetcher/index.js).
|
|
285
|
-
|
|
286
|
-
##### Headless browser management
|
|
287
|
-
|
|
288
|
-
If you pass the `executeClientScripts` option to `fetch`, a headless browser will be used to download and execute the page before serialising its DOM. For performance reasons, the starting and stopping of the browser is your responsibility to avoid instantiating a browser on each fetch. Here is an example on how to use this feature:
|
|
289
|
-
|
|
290
|
-
```js
|
|
291
|
-
import fetch, { launchHeadlessBrowser, stopHeadlessBrowser } from '@opentermsarchive/engine/fetch';
|
|
292
|
-
|
|
293
|
-
await launchHeadlessBrowser();
|
|
294
|
-
await fetch({ executeClientScripts: true, ... });
|
|
295
|
-
await fetch({ executeClientScripts: true, ... });
|
|
296
|
-
await fetch({ executeClientScripts: true, ... });
|
|
297
|
-
await stopHeadlessBrowser();
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
The `fetch` module options are defined as a [`node-config` submodule](https://github.com/node-config/node-config/wiki/Sub-Module-Configuration). The default `fetcher` configuration can be overridden by adding a `fetcher` object to the [local configuration file](#configuration-file).
|
|
301
|
-
|
|
302
|
-
#### `filter`
|
|
303
|
-
|
|
304
|
-
The `filter` module transforms HTML or PDF content into a Markdown string according to a [declaration](#declarations).
|
|
305
|
-
|
|
306
|
-
```js
|
|
307
|
-
import filter from '@opentermsarchive/engine/filter';
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
The `filter` function documentation is available [as JSDoc](./src/archivist/filter/index.js).
|
|
311
|
-
|
|
312
|
-
#### `PageDeclaration`
|
|
313
|
-
|
|
314
|
-
The `PageDeclaration` class encapsulates information about a page tracked by Open Terms Archive.
|
|
315
|
-
|
|
316
|
-
```js
|
|
317
|
-
import pageDeclaration from '@opentermsarchive/engine/page-declaration';
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
The `PageDeclaration` format is defined [in source code](./src/archivist/services/pageDeclaration.js).
|
|
321
|
-
|
|
322
|
-
## Configuring
|
|
323
|
-
|
|
324
|
-
### Configuration file
|
|
325
|
-
|
|
326
|
-
The default configuration can be found in `config/default.json`. The full reference is given below. You are unlikely to want to edit all of these elements.
|
|
327
|
-
|
|
328
|
-
```js
|
|
329
|
-
{
|
|
330
|
-
"services": {
|
|
331
|
-
"declarationsPath": "Directory containing services declarations and associated filters"
|
|
332
|
-
},
|
|
333
|
-
"recorder": {
|
|
334
|
-
"versions": {
|
|
335
|
-
"storage": {
|
|
336
|
-
"<storage-repository>": "Storage repository configuration object; see below"
|
|
337
|
-
}
|
|
338
|
-
},
|
|
339
|
-
"snapshots": {
|
|
340
|
-
"storage": {
|
|
341
|
-
"<storage-repository>": "Storage repository configuration object; see below"
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
"fetcher": {
|
|
346
|
-
"waitForElementsTimeout": "Maximum time (in milliseconds) to wait for elements to be present in the page when fetching document in a headless browser"
|
|
347
|
-
"navigationTimeout": "Maximum time (in milliseconds) to wait for page to load",
|
|
348
|
-
"language": "Language (in ISO 639-1 format) to pass in request headers"
|
|
349
|
-
},
|
|
350
|
-
"notifier": { // Notify specified mailing lists when new versions are recorded
|
|
351
|
-
"sendInBlue": { // SendInBlue API Key is defined in environment variables, see the “Environment variables” section below
|
|
352
|
-
"updatesListId": "SendInBlue contacts list ID of persons to notify on document updates",
|
|
353
|
-
"updateTemplateId": "SendInBlue email template ID used for updates notifications"
|
|
354
|
-
}
|
|
355
|
-
},
|
|
356
|
-
"logger": { // Logging mechanism to be notified upon error
|
|
357
|
-
"smtp": {
|
|
358
|
-
"host": "SMTP server hostname",
|
|
359
|
-
"username": "User for server authentication" // Password for server authentication is defined in environment variables, see the “Environment variables” section below
|
|
360
|
-
},
|
|
361
|
-
"sendMailOnError": { // Can be set to `false` if sending email on error is not needed
|
|
362
|
-
"to": "The address to send the email to in case of an error",
|
|
363
|
-
"from": "The address from which to send the email",
|
|
364
|
-
"sendWarnings": "Boolean. Set to true to also send email in case of warning",
|
|
365
|
-
}
|
|
366
|
-
},
|
|
367
|
-
"tracker": { // Tracking mechanism to create GitHub issues when document content is inaccessible
|
|
368
|
-
"githubIssues": {
|
|
369
|
-
"repository": "GitHub repository where to create isssues",
|
|
370
|
-
"label": {
|
|
371
|
-
"name": "Label to attach to bot-created issues. This specific label will be created automatically in the target repository",
|
|
372
|
-
"color": "The hexadecimal color code for the label, without the leading #",
|
|
373
|
-
"description": "A short description of the label"
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
},
|
|
377
|
-
"dataset": { // Release mechanism to create dataset periodically
|
|
378
|
-
"title": "Title of the dataset; recommended to be the name of the instance that generated it",
|
|
379
|
-
"versionsRepositoryURL": "GitHub repository where the dataset will be published as a release; recommended to be the versions repository for discoverability and tagging purposes"
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
The default configuration is merged with (and overridden by) environment-specific configuration that can be specified at startup with the `NODE_ENV` environment variable. See [node-config](https://github.com/node-config/node-config) for more information about configuration files.
|
|
385
|
-
|
|
386
|
-
In order to have a local configuration that override all exisiting config, it is recommended to create a `config/development.json` file with overridden values.
|
|
387
|
-
|
|
388
|
-
#### Storage repositories
|
|
389
|
-
|
|
390
|
-
Two storage repositories are currently supported: Git and MongoDB. Each one can be used independently for versions and snapshots.
|
|
391
|
-
|
|
392
|
-
##### Git
|
|
393
|
-
|
|
394
|
-
```json
|
|
395
|
-
{
|
|
396
|
-
…
|
|
397
|
-
"storage": {
|
|
398
|
-
"git": {
|
|
399
|
-
"path": "Versions database directory path, relative to the root of this project",
|
|
400
|
-
"publish": "Boolean. Set to true to push changes to the origin of the cloned repository at the end of every run. Recommended for production only.",
|
|
401
|
-
"snapshotIdentiferTemplate": "Text. Template used to explicit where to find the referenced snapshot id. Must contain a %SNAPSHOT_ID that will be replaced by the snapshot ID. Only useful for versions",
|
|
402
|
-
"author": {
|
|
403
|
-
"name": "Name to which changes in tracked documents will be credited",
|
|
404
|
-
"email": "Email to which changes in tracked documents will be credited"
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
…
|
|
409
|
-
}
|
|
410
|
-
```
|
|
411
|
-
##### MongoDB
|
|
412
|
-
|
|
413
|
-
```json
|
|
414
|
-
{
|
|
415
|
-
…
|
|
416
|
-
"storage": {
|
|
417
|
-
"mongo": {
|
|
418
|
-
"connectionURI": "URI for defining connection to the MongoDB instance. See https://docs.mongodb.com/manual/reference/connection-string/",
|
|
419
|
-
"database": "Database name",
|
|
420
|
-
"collection": "Collection name"
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
…
|
|
424
|
-
}
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
### Environment variables
|
|
428
|
-
|
|
429
|
-
Environment variables can be passed in the command-line or provided in a `.env` file at the root of the repository. See `.env.example` for an example of such a file.
|
|
430
|
-
|
|
431
|
-
- `SMTP_PASSWORD`: a password for email server authentication, in order to send email notifications.
|
|
432
|
-
- `SENDINBLUE_API_KEY`: a SendInBlue API key, in order to send email notifications with that service.
|
|
433
|
-
- `GITHUB_TOKEN`: a token with repository privileges to access the [GitHub API](https://github.com/settings/tokens).
|
|
434
|
-
|
|
435
|
-
If an outgoing HTTP/HTTPS proxy to access the Internet is required, it is possible to provide it through the `HTTP_PROXY` and `HTTPS_PROXY` environment variable.
|
|
436
|
-
|
|
437
|
-
## Deploying
|
|
438
|
-
|
|
439
|
-
Deployment recipes are available in a [dedicated repository](https://github.com/OpenTermsArchive/deployment). Look at the [README](https://github.com/OpenTermsArchive/deployment#readme) to know how to deploy the engine.
|
|
440
|
-
## Contributing
|
|
441
|
-
|
|
442
|
-
### Getting a copy
|
|
443
|
-
|
|
444
|
-
In order to edit the code of the engine itself, an editable and executable copy is necessary.
|
|
445
|
-
|
|
446
|
-
First of all, follow the [requirements](#requirements) above. Then, clone the repository:
|
|
447
|
-
|
|
448
|
-
```sh
|
|
449
|
-
git clone https://github.com/ambanum/OpenTermsArchive.git
|
|
450
|
-
cd OpenTermsArchive
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
Install dependencies:
|
|
454
|
-
|
|
455
|
-
```sh
|
|
456
|
-
npm install
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
### Testing
|
|
460
|
-
|
|
461
|
-
If changes are made to the engine, check that all parts covered by tests still work properly:
|
|
462
|
-
|
|
463
|
-
```sh
|
|
464
|
-
npm test
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
If existing features are changed or new ones are added, relevant tests must be added too.
|
|
468
|
-
|
|
469
|
-
### Suggesting changes
|
|
470
|
-
|
|
471
|
-
To contribute to the core engine of Open Terms Archive, see the [CONTRIBUTING](CONTRIBUTING.md) file of this repository. You will need knowledge of JavaScript and Node.js.
|
|
472
|
-
|
|
473
|
-
### Sponsorship and partnerships
|
|
474
|
-
|
|
475
|
-
Beyond individual contributions, we need funds and committed partners to pay for a core team to maintain and grow Open Terms Archive. If you know of opportunities, please let us know over email at `contact@[project name without spaces].org`!
|
|
7
|
+
For documentation, visit [docs.opentermsarchive.org](https://docs.opentermsarchive.org/)
|
|
476
8
|
|
|
477
9
|
- - -
|
|
478
10
|
|
package/bin/ota-lint.js
CHANGED
|
@@ -2,18 +2,55 @@
|
|
|
2
2
|
import './env.js';
|
|
3
3
|
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import { fileURLToPath
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
6
|
|
|
7
7
|
import { program } from 'commander';
|
|
8
|
+
import Mocha from 'mocha';
|
|
8
9
|
|
|
9
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
|
|
12
|
+
const LINT_TEST_FILEPATH = '../scripts/declarations/lint/index.mocha.js';
|
|
13
|
+
const LINT_PATH = path.resolve(__dirname, LINT_TEST_FILEPATH);
|
|
14
|
+
|
|
15
|
+
// Mocha catches unhandled rejection from the user code and re-emits them to the process (see https://github.com/mochajs/mocha/blob/master/lib/runner.js#L198)
|
|
16
|
+
process.on('unhandledRejection', reason => {
|
|
17
|
+
// Re-throw them so that the validation command fails in these cases (for example, if there is a syntax error when parsing JSON declaration files)
|
|
18
|
+
throw reason;
|
|
19
|
+
});
|
|
20
|
+
|
|
11
21
|
program
|
|
12
22
|
.name('ota lint')
|
|
13
23
|
.description('Check format and stylistic errors in declarations and auto fix them')
|
|
14
24
|
.option('-s, --services [serviceId...]', 'service IDs of services to lint')
|
|
15
|
-
.option('-m, --modified', 'to only lint modified services already commited to git')
|
|
25
|
+
.option('-m, --modified', 'to only lint modified services already commited to git')
|
|
26
|
+
.option('-f, --fix', 'to fix the declarations');
|
|
27
|
+
|
|
28
|
+
const mocha = new Mocha({
|
|
29
|
+
delay: true, // as the validation script performs an asynchronous load before running the tests, the execution of the tests are delayed until run() is called
|
|
30
|
+
failZero: true, // consider that being called with no service to validate is a failure
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
(async () => {
|
|
34
|
+
mocha.addFile(LINT_PATH); // As `delay` has been called, this statement will not load the file directly, `loadFilesAsync` is required.
|
|
35
|
+
await mocha.loadFilesAsync() // Load files previously added to the Mocha cache with `addFile`.
|
|
36
|
+
.catch(error => {
|
|
37
|
+
console.error(error);
|
|
38
|
+
process.exit(2);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
let hasFailedTests = false;
|
|
42
|
+
|
|
43
|
+
const lintFiles = (await import(LINT_TEST_FILEPATH)).default;
|
|
44
|
+
|
|
45
|
+
lintFiles(program.parse().opts());
|
|
16
46
|
|
|
17
|
-
|
|
47
|
+
mocha.run()
|
|
48
|
+
.on('fail', () => { hasFailedTests = true; })
|
|
49
|
+
.on('end', () => {
|
|
50
|
+
if (hasFailedTests) {
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
18
53
|
|
|
19
|
-
|
|
54
|
+
process.exit(0);
|
|
55
|
+
});
|
|
56
|
+
})();
|
package/bin/ota-validate.js
CHANGED
|
@@ -9,7 +9,8 @@ import Mocha from 'mocha';
|
|
|
9
9
|
|
|
10
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const VALIDATE_TEST_FILEPATH = '../scripts/declarations/validate/index.mocha.js';
|
|
13
|
+
const VALIDATE_PATH = path.resolve(__dirname, VALIDATE_TEST_FILEPATH);
|
|
13
14
|
|
|
14
15
|
// Mocha catches unhandled rejection from the user code and re-emits them to the process (see https://github.com/mochajs/mocha/blob/master/lib/runner.js#L198)
|
|
15
16
|
process.on('unhandledRejection', reason => {
|
|
@@ -40,7 +41,7 @@ const mocha = new Mocha({
|
|
|
40
41
|
|
|
41
42
|
let hasFailedTests = false;
|
|
42
43
|
|
|
43
|
-
const generateValidationTestSuite = (await import(
|
|
44
|
+
const generateValidationTestSuite = (await import(VALIDATE_TEST_FILEPATH)).default;
|
|
44
45
|
|
|
45
46
|
generateValidationTestSuite(program.parse().opts());
|
|
46
47
|
|
package/config/test.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"git": {
|
|
9
9
|
"path": "./test/data/versions",
|
|
10
10
|
"publish": false,
|
|
11
|
-
"snapshotIdentiferTemplate": "https://github.com/
|
|
11
|
+
"snapshotIdentiferTemplate": "https://github.com/OpenTermsArchive/sandbox-snapshots/commit/%SNAPSHOT_ID",
|
|
12
12
|
"author": {
|
|
13
13
|
"name": "Open Terms Archive Testing Bot",
|
|
14
14
|
"email": "bot@opentermsarchive.org"
|
|
@@ -44,6 +44,6 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dataset": {
|
|
46
46
|
"title": "sandbox",
|
|
47
|
-
"versionsRepositoryURL": "https://github.com/OpenTermsArchive/sandbox"
|
|
47
|
+
"versionsRepositoryURL": "https://github.com/OpenTermsArchive/sandbox-versions"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentermsarchive/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Tracks and makes visible changes to the terms of online services",
|
|
5
|
-
"homepage": "https://github.com/
|
|
5
|
+
"homepage": "https://github.com/OpenTermsArchive/engine#readme",
|
|
6
6
|
"bugs": {
|
|
7
|
-
"url": "https://github.com/
|
|
7
|
+
"url": "https://github.com/OpenTermsArchive/engine/issues"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/
|
|
11
|
+
"url": "git+https://github.com/OpenTermsArchive/engine.git"
|
|
12
12
|
},
|
|
13
13
|
"license": "EUPL-1.2",
|
|
14
14
|
"author": "ambanum",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This dataset consolidates the contractual documents of 2 service providers, in all their versions that were accessible online between January 1, 2021 and January 6, 2022.
|
|
4
4
|
|
|
5
|
-
This dataset is tailored for datascientists and other analysts. You can also explore all these versions interactively on [https://github.com/OpenTermsArchive/sandbox](https://github.com/OpenTermsArchive/sandbox).
|
|
5
|
+
This dataset is tailored for datascientists and other analysts. You can also explore all these versions interactively on [https://github.com/OpenTermsArchive/sandbox-versions](https://github.com/OpenTermsArchive/sandbox-versions).
|
|
6
6
|
|
|
7
7
|
It has been generated with [Open Terms Archive](https://opentermsarchive.org).
|
|
8
8
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fsApi from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
import config from 'config';
|
|
7
|
+
import { ESLint } from 'eslint';
|
|
8
|
+
|
|
9
|
+
import * as services from '../../../src/archivist/services/index.js';
|
|
10
|
+
import DeclarationUtils from '../utils/index.js';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const ROOT_PATH = path.resolve(__dirname, '../../../');
|
|
14
|
+
const ESLINT_CONFIG_PATH = path.join(ROOT_PATH, '.eslintrc.yaml');
|
|
15
|
+
|
|
16
|
+
const eslint = new ESLint({ overrideConfigFile: ESLINT_CONFIG_PATH, fix: false });
|
|
17
|
+
const eslintWithFix = new ESLint({ overrideConfigFile: ESLINT_CONFIG_PATH, fix: true });
|
|
18
|
+
|
|
19
|
+
const declarationsPath = path.resolve(process.cwd(), config.get('services.declarationsPath'));
|
|
20
|
+
const instancePath = path.resolve(declarationsPath, '../');
|
|
21
|
+
|
|
22
|
+
export default async options => {
|
|
23
|
+
let servicesToValidate = options.services || [];
|
|
24
|
+
|
|
25
|
+
const serviceDeclarations = await services.loadWithHistory(servicesToValidate);
|
|
26
|
+
|
|
27
|
+
if (!servicesToValidate.length) {
|
|
28
|
+
servicesToValidate = Object.keys(serviceDeclarations);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (options.modified) {
|
|
32
|
+
const declarationUtils = new DeclarationUtils(instancePath);
|
|
33
|
+
|
|
34
|
+
({ services: servicesToValidate } = await declarationUtils.getModifiedServiceDocumentTypes());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const lintFile = lintAndFixFile(options.fix);
|
|
38
|
+
|
|
39
|
+
describe('Service declarations lint validation', async function () {
|
|
40
|
+
this.timeout(30000);
|
|
41
|
+
|
|
42
|
+
servicesToValidate.forEach(serviceId => {
|
|
43
|
+
const service = serviceDeclarations[serviceId];
|
|
44
|
+
const filePath = path.join(declarationsPath, `${serviceId}.json`);
|
|
45
|
+
const historyFilePath = path.join(declarationsPath, `${serviceId}.history.json`);
|
|
46
|
+
const filtersFilePath = path.join(declarationsPath, `${serviceId}.filters.js`);
|
|
47
|
+
const filtersHistoryFilePath = path.join(declarationsPath, `${serviceId}.filters.history.js`);
|
|
48
|
+
|
|
49
|
+
context(serviceId, async () => {
|
|
50
|
+
before(async function () {
|
|
51
|
+
if (!service) {
|
|
52
|
+
console.log(' (Tests skipped as declaration has been archived)');
|
|
53
|
+
this.skip();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('valid declaration file lint', async () => {
|
|
58
|
+
await lintFile(filePath);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (service && service.hasHistory()) {
|
|
62
|
+
it('valid history declaration file lint', async () => {
|
|
63
|
+
await lintFile(historyFilePath);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (fsApi.existsSync(filtersFilePath)) {
|
|
68
|
+
it('valid filters file lint', async () => {
|
|
69
|
+
await lintFile(filtersFilePath);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (fsApi.existsSync(filtersHistoryFilePath)) {
|
|
74
|
+
it('valid filters history file lint', async () => {
|
|
75
|
+
await lintFile(filtersHistoryFilePath);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
run();
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const lintAndFixFile = fix => async filePath => {
|
|
86
|
+
// Create a new instance of linter with option `fix` set to true to get a fixed output.
|
|
87
|
+
// It is not possible to use only a linter with this option enabled because when
|
|
88
|
+
// this option is set, if it can fix errors, it considers that there are no errors and returns `0` for the `errorCount`.
|
|
89
|
+
// So use two linters to have access both to `errorCount` and fix `output` variables.
|
|
90
|
+
const [lintResult] = await eslint.lintFiles(filePath);
|
|
91
|
+
|
|
92
|
+
if (!lintResult.errorCount) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const [lintResultFixed] = await eslintWithFix.lintFiles(filePath);
|
|
97
|
+
|
|
98
|
+
if (fix) {
|
|
99
|
+
await ESLint.outputFixes([lintResultFixed]);
|
|
100
|
+
|
|
101
|
+
return lintAndFixFile(false)(filePath);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
expect(lintResult.source).to.equal(lintResultFixed.output, `${path.basename(filePath)} is not properly formatted. Use the lint script to format it correctly.\n`);
|
|
105
|
+
};
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import fsApi from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
3
|
|
|
5
4
|
import Ajv from 'ajv';
|
|
6
5
|
import { expect } from 'chai';
|
|
7
6
|
import config from 'config';
|
|
8
|
-
import { ESLint } from 'eslint';
|
|
9
7
|
import jsonSourceMap from 'json-source-map';
|
|
10
8
|
|
|
11
9
|
import fetch, { launchHeadlessBrowser, stopHeadlessBrowser } from '../../../src/archivist/fetcher/index.js';
|
|
@@ -18,16 +16,9 @@ import serviceSchema from './service.schema.js';
|
|
|
18
16
|
|
|
19
17
|
const fs = fsApi.promises;
|
|
20
18
|
|
|
21
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
-
const ROOT_PATH = path.resolve(__dirname, '../../../');
|
|
23
|
-
const ESLINT_CONFIG_PATH = path.join(ROOT_PATH, '.eslintrc.yaml');
|
|
24
|
-
|
|
25
19
|
const MIN_DOC_LENGTH = 100;
|
|
26
20
|
const SLOW_DOCUMENT_THRESHOLD = 10 * 1000; // number of milliseconds after which a document fetch is considered slow
|
|
27
21
|
|
|
28
|
-
const eslint = new ESLint({ overrideConfigFile: ESLINT_CONFIG_PATH, fix: false });
|
|
29
|
-
const eslintWithFix = new ESLint({ overrideConfigFile: ESLINT_CONFIG_PATH, fix: true });
|
|
30
|
-
|
|
31
22
|
const declarationsPath = path.resolve(process.cwd(), config.get('services.declarationsPath'));
|
|
32
23
|
const instancePath = path.resolve(declarationsPath, '../');
|
|
33
24
|
|
|
@@ -76,36 +67,12 @@ export default async options => {
|
|
|
76
67
|
assertValid(serviceSchema, declaration);
|
|
77
68
|
});
|
|
78
69
|
|
|
79
|
-
it('valid declaration file format', async () => {
|
|
80
|
-
await lintFile(filePath);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
70
|
if (service && service.hasHistory()) {
|
|
84
71
|
it('valid history declaration schema', async () => {
|
|
85
72
|
const declarationHistory = JSON.parse(await fs.readFile(historyFilePath));
|
|
86
73
|
|
|
87
74
|
assertValid(serviceHistorySchema, declarationHistory);
|
|
88
75
|
});
|
|
89
|
-
|
|
90
|
-
it('valid history declaration file format', async () => {
|
|
91
|
-
await lintFile(path.join(declarationsPath, `${serviceId}.history.json`));
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const filtersFilePath = path.join(declarationsPath, `${serviceId}.filters.js`);
|
|
96
|
-
|
|
97
|
-
if (fsApi.existsSync(filtersFilePath)) {
|
|
98
|
-
it('valid filters file format', async () => {
|
|
99
|
-
await lintFile(filtersFilePath);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const filtersHistoryFilePath = path.join(declarationsPath, `${serviceId}.filters.history.js`);
|
|
104
|
-
|
|
105
|
-
if (fsApi.existsSync(filtersHistoryFilePath)) {
|
|
106
|
-
it('valid filters history file format', async () => {
|
|
107
|
-
await lintFile(filtersHistoryFilePath);
|
|
108
|
-
});
|
|
109
76
|
}
|
|
110
77
|
|
|
111
78
|
if (!schemaOnly && service) {
|
|
@@ -245,18 +212,3 @@ function assertValid(schema, subject) {
|
|
|
245
212
|
throw new Error(errorMessage);
|
|
246
213
|
}
|
|
247
214
|
}
|
|
248
|
-
|
|
249
|
-
async function lintFile(filePath) {
|
|
250
|
-
const [lintResult] = await eslint.lintFiles(filePath);
|
|
251
|
-
|
|
252
|
-
if (!lintResult.errorCount) {
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Create a new instance of linter with option `fix` set to true to get a fixed output.
|
|
257
|
-
// It is not possible to use only a linter with this option enabled because when this option is set, if it can fix errors, it considers that there are no errors and returns `0` for the `errorCount`.
|
|
258
|
-
// So use two linters to have access both to `errorCount` and fix `output` variables.
|
|
259
|
-
const [lintResultFixed] = await eslintWithFix.lintFiles(filePath);
|
|
260
|
-
|
|
261
|
-
expect(lintResult.source).to.equal(lintResultFixed.output, `${path.basename(filePath)} is not properly formatted. Use the lint script to format it correctly.\n`);
|
|
262
|
-
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"git": {
|
|
6
6
|
"path": "./data/versions",
|
|
7
7
|
"publish": false,
|
|
8
|
-
"prefixMessageToSnapshotId": "This version was recorded after filtering snapshot https://github.com/
|
|
8
|
+
"prefixMessageToSnapshotId": "This version was recorded after filtering snapshot https://github.com/OpenTermsArchive/contrib-snapshots/commit/",
|
|
9
9
|
"author": {
|
|
10
10
|
"name": "Open Terms Archive Bot",
|
|
11
11
|
"email": "bot@opentermsarchive.org"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"git": {
|
|
6
6
|
"path": "./data/versions-rewritten",
|
|
7
7
|
"publish": false,
|
|
8
|
-
"prefixMessageToSnapshotId": "This version was recorded after filtering snapshot https://github.com/
|
|
8
|
+
"prefixMessageToSnapshotId": "This version was recorded after filtering snapshot https://github.com/OpenTermsArchive/contrib-snapshots/commit/",
|
|
9
9
|
"author": {
|
|
10
10
|
"name": "Open Terms Archive Bot",
|
|
11
11
|
"email": "bot@opentermsarchive.org"
|
|
@@ -79,5 +79,5 @@ function generateFileName(documentType, pageId, extension) {
|
|
|
79
79
|
export function generateFilePath(serviceId, documentType, pageId, mimeType) {
|
|
80
80
|
const extension = mime.getExtension(mimeType) || '*'; // If mime type is undefined, an asterisk is set as an extension. Used to match all files for the given service ID, terms type and page ID when mime type is unknown.
|
|
81
81
|
|
|
82
|
-
return `${serviceId}/${generateFileName(documentType, pageId, extension)}`; // Do not use `path.join` as even for Windows, the path should be with `/` and not
|
|
82
|
+
return `${serviceId}/${generateFileName(documentType, pageId, extension)}`; // Do not use `path.join` as even for Windows, the path should be with `/` and not `\`
|
|
83
83
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
|
-
|
|
4
|
-
import config from 'config';
|
|
5
|
-
import { ESLint } from 'eslint';
|
|
6
|
-
|
|
7
|
-
import DeclarationUtils from '../utils/index.js';
|
|
8
|
-
|
|
9
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
|
|
11
|
-
const declarationsPath = config.get('services.declarationsPath');
|
|
12
|
-
const instancePath = path.resolve(declarationsPath, '../');
|
|
13
|
-
const ESLINT_CONFIG_PATH = path.join(__dirname, '../../../.eslintrc.yaml');
|
|
14
|
-
|
|
15
|
-
const lintDeclarations = async ({ services, modified }) => {
|
|
16
|
-
console.log(`Linting declaration files in ${instancePath}`);
|
|
17
|
-
let servicesToValidate = services || ['*'];
|
|
18
|
-
|
|
19
|
-
if (modified) {
|
|
20
|
-
const declarationUtils = new DeclarationUtils(instancePath);
|
|
21
|
-
|
|
22
|
-
servicesToValidate = await declarationUtils.getModifiedServices();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
for (const service of servicesToValidate) {
|
|
26
|
-
/* eslint-disable no-await-in-loop */
|
|
27
|
-
const lintResults = await new ESLint({ overrideConfigFile: ESLINT_CONFIG_PATH, fix: true })
|
|
28
|
-
.lintFiles(path.join(declarationsPath, `${service}.*`));
|
|
29
|
-
|
|
30
|
-
await ESLint.outputFixes(lintResults);
|
|
31
|
-
console.log(lintResults.map(lintResult => `${path.basename(lintResult.filePath)} linted`).join('\n'));
|
|
32
|
-
/* eslint-enable no-await-in-loop */
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default lintDeclarations;
|