From e3d6a571844e921e4c000af995f2367a36766960 Mon Sep 17 00:00:00 2001 From: OlgaGuretskaya Date: Fri, 18 Dec 2020 16:48:27 +0300 Subject: [PATCH] JS SDK update to support io/v2 features (#35) * add lint, prettier and husky and run them for all files * add ignrore files * move eslintConfig to package.json * resolve comment * delete dist * move integration tests from e2e to integration folder and unit outdated tests to this folder - #2 (#36) * add lint-staged * move linter to dev-dependency * Core-940 - JS SDK update to support io/v2 features - #3 (#37) * add functionality * add tests * fix integration test * change docs * fix version in readme * fix comments * fix comments * fix comments * resolve comments * resolve comment * fix comments * fix comments * add an example for access token scope --- .circleci/config.yml | 2 +- .gitignore | 3 +- .npmignore | 3 +- .prettierignore | 7 + CHANGELOG.md | 54 +- README.md | 223 +- examples/browser-npm-browserify/index.js | 7 +- examples/node/index.js | 9 +- package-lock.json | 2493 ++++++++++++++++- package.json | 65 +- src/alias.js | 9 +- src/api.js | 41 +- src/entities.js | 52 +- src/entity/ADIOrder.js | 4 +- src/entity/AccessPolicy.js | 25 + src/entity/AccessToken.js | 25 + src/entity/Account.js | 4 +- src/entity/Action.js | 31 +- src/entity/ActionType.js | 39 +- src/entity/AppUser.js | 21 +- src/entity/Application.js | 3 +- src/entity/Batch.js | 4 +- src/entity/Collection.js | 3 +- src/entity/Entity.js | 14 +- src/entity/File.js | 9 +- src/entity/Location.js | 19 +- src/entity/Me.js | 25 + src/entity/OperatorAccess.js | 25 + src/entity/Permission.js | 1 - src/entity/Product.js | 6 +- src/entity/Project.js | 4 +- src/entity/Property.js | 27 +- src/entity/ReactorLog.js | 11 +- src/entity/ReactorSchedule.js | 3 +- src/entity/ReactorScript.js | 7 +- src/entity/Redirection.js | 23 +- src/entity/Role.js | 4 +- src/entity/Rule.js | 11 +- src/entity/ShipmentNotice.js | 4 +- src/entity/Thng.js | 8 +- src/evrythng.js | 1 + src/resource/Resource.js | 66 +- src/scope/AccessToken.js | 72 + src/scope/ActionApp.js | 17 +- src/scope/Application.js | 5 +- src/scope/Device.js | 5 +- src/scope/Operator.js | 41 +- src/scope/Scope.js | 2 +- src/scope/User.js | 5 +- src/settings.js | 6 +- src/setup.js | 35 +- src/use.js | 34 +- src/util/buildParams.js | 4 +- src/util/callback.js | 16 +- src/util/getCurrentPosition.js | 4 +- src/util/mixin.js | 14 +- test/README.md | 17 + test/e2e/README.md | 11 - test/e2e/config.js | 8 + test/e2e/dataGenerator.js | 85 + test/e2e/entity/accesses.spec.js | 39 - test/e2e/entity/accountRedirector.spec.js | 32 - test/e2e/entity/applicationRedirector.spec.js | 36 - test/e2e/entity/batches.spec.js | 56 - test/e2e/entity/files.spec.js | 69 - test/e2e/entity/secretKey.spec.js | 23 - test/e2e/entity/shipmentNotice.spec.js | 132 - test/e2e/index.spec.js | 186 +- test/e2e/misc/alias.spec.js | 63 - test/e2e/misc/pages.spec.js | 42 - test/e2e/misc/stream.spec.js | 42 - test/e2e/misc/streamPages.spec.js | 44 - test/e2e/util.js | 99 - test/helpers/apiMock.js | 15 +- test/helpers/data.js | 2 +- test/helpers/paths.js | 7 +- .../integration/entity/accessPolicies.spec.js | 66 + test/integration/entity/accessTokens.spec.js | 65 + test/integration/entity/accesses.spec.js | 36 + .../entity/accountRedirector.spec.js | 38 + .../entity/accounts.spec.js | 22 +- .../entity/actionTypes.spec.js | 20 +- .../entity/actions.spec.js | 27 +- .../integration/entity/adiOrderEvents.spec.js | 33 + .../entity/adiOrders.spec.js | 33 +- .../entity/applicationRedirector.spec.js | 48 + .../entity/applications.spec.js | 20 +- test/integration/entity/batches.spec.js | 117 + .../entity/collections.spec.js | 24 +- .../entity/commissionState.spec.js | 10 +- .../entity/domains.spec.js | 12 +- test/integration/entity/files.spec.js | 57 + .../entity/locations.spec.js | 32 +- test/integration/entity/me.spec.js | 36 + .../entity/operatorAccesses.spec.js | 79 + .../entity/permissions.spec.js | 34 +- .../entity/places.spec.js | 20 +- .../entity/products.spec.js | 20 +- .../entity/projects.spec.js | 32 +- .../entity/properties.spec.js | 20 +- .../entity/purchaseOrders.spec.js | 24 +- .../entity/reactor.spec.js | 63 +- .../entity/redirection.spec.js | 22 +- .../{e2e => integration}/entity/roles.spec.js | 20 +- .../{e2e => integration}/entity/rules.spec.js | 10 +- test/integration/entity/secretKey.spec.js | 23 + .../integration/entity/shipmentNotice.spec.js | 70 + .../entity/shortDomains.spec.js | 12 +- .../{e2e => integration}/entity/tasks.spec.js | 24 +- .../{e2e => integration}/entity/thngs.spec.js | 23 +- test/{e2e => integration}/entity/user.spec.js | 55 +- test/integration/es.spec.js | 8 - test/integration/misc/alias.spec.js | 59 + test/{e2e => integration}/misc/api.spec.js | 47 +- test/{e2e => integration}/misc/find.spec.js | 20 +- test/integration/misc/pages.spec.js | 35 + .../misc/paramSetters.spec.js | 58 +- .../{e2e => integration}/misc/rescope.spec.js | 86 +- test/integration/misc/stream.spec.js | 36 + test/integration/misc/streamPages.spec.js | 38 + test/{e2e => integration}/misc/upsert.spec.js | 54 +- test/{e2e => integration}/misc/use.spec.js | 16 +- .../scope/actionApp.spec.js | 57 +- .../{e2e => integration}/scope/device.spec.js | 8 +- .../scope/operator.spec.js | 12 +- test/integration/testsForApiVersion1.spec.js | 111 + test/integration/testsForApiVersion2.spec.js | 95 + test/integration/umd.spec.js | 18 - test/integration/util.js | 146 + test/require-main.js | 6 +- test/unit/entity/Location.spec.js | 130 - test/unit/{ => outDated}/api.spec.js | 71 +- .../unit/{ => outDated}/entity/Action.spec.js | 163 +- .../{ => outDated}/entity/ActionType.spec.js | 24 +- .../{ => outDated}/entity/AppUser.spec.js | 112 +- .../{ => outDated}/entity/Application.spec.js | 5 +- test/unit/{ => outDated}/entity/Batch.spec.js | 0 .../{ => outDated}/entity/Collection.spec.js | 4 +- .../unit/{ => outDated}/entity/Entity.spec.js | 16 +- test/unit/{ => outDated}/entity/File.spec.js | 0 test/unit/outDated/entity/Location.spec.js | 143 + .../{ => outDated}/entity/Permission.spec.js | 3 +- test/unit/{ => outDated}/entity/Place.spec.js | 0 .../{ => outDated}/entity/Product.spec.js | 0 .../{ => outDated}/entity/Project.spec.js | 0 .../{ => outDated}/entity/Property.spec.js | 30 +- .../{ => outDated}/entity/ReactorLog.spec.js | 25 +- .../entity/ReactorSchedule.spec.js | 5 +- .../entity/ReactorScript.spec.js | 0 test/unit/{ => outDated}/entity/Role.spec.js | 0 .../unit/{ => outDated}/entity/Status.spec.js | 0 test/unit/{ => outDated}/entity/Task.spec.js | 0 test/unit/{ => outDated}/entity/Thng.spec.js | 0 test/unit/{ => outDated}/entity/User.spec.js | 0 test/unit/{ => outDated}/evrythng.spec.js | 0 .../{ => outDated}/resource/Resource.spec.js | 45 +- .../{ => outDated}/scope/Application.spec.js | 35 +- .../{ => outDated}/scope/Operator.spec.js | 28 +- test/unit/{ => outDated}/scope/Scope.spec.js | 2 +- test/unit/{ => outDated}/settings.spec.js | 0 test/unit/outDated/setup.spec.js | 22 + .../{ => outDated}/util/buildParams.spec.js | 0 .../unit/{ => outDated}/util/buildUrl.spec.js | 3 +- .../util/getCurrentPosition.spec.js | 32 +- test/unit/{ => outDated}/util/mixin.spec.js | 0 test/unit/setup.spec.js | 118 +- webpack.config.js | 37 +- 167 files changed, 5585 insertions(+), 2288 deletions(-) create mode 100644 .prettierignore create mode 100644 src/entity/AccessPolicy.js create mode 100644 src/entity/AccessToken.js create mode 100644 src/entity/Me.js create mode 100644 src/entity/OperatorAccess.js create mode 100644 src/scope/AccessToken.js create mode 100644 test/README.md delete mode 100644 test/e2e/README.md create mode 100644 test/e2e/config.js create mode 100644 test/e2e/dataGenerator.js delete mode 100644 test/e2e/entity/accesses.spec.js delete mode 100644 test/e2e/entity/accountRedirector.spec.js delete mode 100644 test/e2e/entity/applicationRedirector.spec.js delete mode 100644 test/e2e/entity/batches.spec.js delete mode 100644 test/e2e/entity/files.spec.js delete mode 100644 test/e2e/entity/secretKey.spec.js delete mode 100644 test/e2e/entity/shipmentNotice.spec.js delete mode 100644 test/e2e/misc/alias.spec.js delete mode 100644 test/e2e/misc/pages.spec.js delete mode 100644 test/e2e/misc/stream.spec.js delete mode 100644 test/e2e/misc/streamPages.spec.js delete mode 100644 test/e2e/util.js create mode 100644 test/integration/entity/accessPolicies.spec.js create mode 100644 test/integration/entity/accessTokens.spec.js create mode 100644 test/integration/entity/accesses.spec.js create mode 100644 test/integration/entity/accountRedirector.spec.js rename test/{e2e => integration}/entity/accounts.spec.js (55%) rename test/{e2e => integration}/entity/actionTypes.spec.js (74%) rename test/{e2e => integration}/entity/actions.spec.js (66%) create mode 100644 test/integration/entity/adiOrderEvents.spec.js rename test/{e2e => integration}/entity/adiOrders.spec.js (66%) create mode 100644 test/integration/entity/applicationRedirector.spec.js rename test/{e2e => integration}/entity/applications.spec.js (71%) create mode 100644 test/integration/entity/batches.spec.js rename test/{e2e => integration}/entity/collections.spec.js (69%) rename test/{e2e => integration}/entity/commissionState.spec.js (66%) rename test/{e2e => integration}/entity/domains.spec.js (51%) create mode 100644 test/integration/entity/files.spec.js rename test/{e2e => integration}/entity/locations.spec.js (52%) create mode 100644 test/integration/entity/me.spec.js create mode 100644 test/integration/entity/operatorAccesses.spec.js rename test/{e2e => integration}/entity/permissions.spec.js (69%) rename test/{e2e => integration}/entity/places.spec.js (74%) rename test/{e2e => integration}/entity/products.spec.js (74%) rename test/{e2e => integration}/entity/projects.spec.js (53%) rename test/{e2e => integration}/entity/properties.spec.js (72%) rename test/{e2e => integration}/entity/purchaseOrders.spec.js (75%) rename test/{e2e => integration}/entity/reactor.spec.js (61%) rename test/{e2e => integration}/entity/redirection.spec.js (76%) rename test/{e2e => integration}/entity/roles.spec.js (74%) rename test/{e2e => integration}/entity/rules.spec.js (54%) create mode 100644 test/integration/entity/secretKey.spec.js create mode 100644 test/integration/entity/shipmentNotice.spec.js rename test/{e2e => integration}/entity/shortDomains.spec.js (52%) rename test/{e2e => integration}/entity/tasks.spec.js (62%) rename test/{e2e => integration}/entity/thngs.spec.js (71%) rename test/{e2e => integration}/entity/user.spec.js (64%) delete mode 100644 test/integration/es.spec.js create mode 100644 test/integration/misc/alias.spec.js rename test/{e2e => integration}/misc/api.spec.js (58%) rename test/{e2e => integration}/misc/find.spec.js (64%) create mode 100644 test/integration/misc/pages.spec.js rename test/{e2e => integration}/misc/paramSetters.spec.js (60%) rename test/{e2e => integration}/misc/rescope.spec.js (58%) create mode 100644 test/integration/misc/stream.spec.js create mode 100644 test/integration/misc/streamPages.spec.js rename test/{e2e => integration}/misc/upsert.spec.js (52%) rename test/{e2e => integration}/misc/use.spec.js (75%) rename test/{e2e => integration}/scope/actionApp.spec.js (75%) rename test/{e2e => integration}/scope/device.spec.js (68%) rename test/{e2e => integration}/scope/operator.spec.js (83%) create mode 100644 test/integration/testsForApiVersion1.spec.js create mode 100644 test/integration/testsForApiVersion2.spec.js delete mode 100644 test/integration/umd.spec.js create mode 100644 test/integration/util.js delete mode 100644 test/unit/entity/Location.spec.js rename test/unit/{ => outDated}/api.spec.js (87%) rename test/unit/{ => outDated}/entity/Action.spec.js (56%) rename test/unit/{ => outDated}/entity/ActionType.spec.js (84%) rename test/unit/{ => outDated}/entity/AppUser.spec.js (52%) rename test/unit/{ => outDated}/entity/Application.spec.js (94%) rename test/unit/{ => outDated}/entity/Batch.spec.js (100%) rename test/unit/{ => outDated}/entity/Collection.spec.js (94%) rename test/unit/{ => outDated}/entity/Entity.spec.js (88%) rename test/unit/{ => outDated}/entity/File.spec.js (100%) create mode 100644 test/unit/outDated/entity/Location.spec.js rename test/unit/{ => outDated}/entity/Permission.spec.js (93%) rename test/unit/{ => outDated}/entity/Place.spec.js (100%) rename test/unit/{ => outDated}/entity/Product.spec.js (100%) rename test/unit/{ => outDated}/entity/Project.spec.js (100%) rename test/unit/{ => outDated}/entity/Property.spec.js (88%) rename test/unit/{ => outDated}/entity/ReactorLog.spec.js (76%) rename test/unit/{ => outDated}/entity/ReactorSchedule.spec.js (86%) rename test/unit/{ => outDated}/entity/ReactorScript.spec.js (100%) rename test/unit/{ => outDated}/entity/Role.spec.js (100%) rename test/unit/{ => outDated}/entity/Status.spec.js (100%) rename test/unit/{ => outDated}/entity/Task.spec.js (100%) rename test/unit/{ => outDated}/entity/Thng.spec.js (100%) rename test/unit/{ => outDated}/entity/User.spec.js (100%) rename test/unit/{ => outDated}/evrythng.spec.js (100%) rename test/unit/{ => outDated}/resource/Resource.spec.js (89%) rename test/unit/{ => outDated}/scope/Application.spec.js (76%) rename test/unit/{ => outDated}/scope/Operator.spec.js (78%) rename test/unit/{ => outDated}/scope/Scope.spec.js (96%) rename test/unit/{ => outDated}/settings.spec.js (100%) create mode 100644 test/unit/outDated/setup.spec.js rename test/unit/{ => outDated}/util/buildParams.spec.js (100%) rename test/unit/{ => outDated}/util/buildUrl.spec.js (84%) rename test/unit/{ => outDated}/util/getCurrentPosition.spec.js (55%) rename test/unit/{ => outDated}/util/mixin.spec.js (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index ef09ef3..c37d873 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,4 +13,4 @@ jobs: command: 'npm run build' - run: name: 'E2E tests' - command: 'npm test' \ No newline at end of file + command: 'npm test' diff --git a/.gitignore b/.gitignore index d363ae7..887a39f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ bower_components dist report .idea -*.DS_Store \ No newline at end of file +*.DS_Store +.vscode diff --git a/.npmignore b/.npmignore index 2883457..e61bb01 100644 --- a/.npmignore +++ b/.npmignore @@ -6,4 +6,5 @@ bower_components examples report .idea -*.html \ No newline at end of file +*.html +.vscode diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1cb7cb3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +terraform/* +reports/* +test-results/* +mochawesome-report/* +.nyc_output +.vscode +dist \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f5ccc87..7a7794c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# v6.0.0 (08-12-2020) + +## Features + +- **Setup**: Add `apiVersion:2` as default and add URL configuration based on apiVersion and region + +- **Scopes**: Add `Operator` and `AccessToken` scopes for `apiVersion:2` + +- **Access Policies**: Add `accessPolicies()` for access policies API to Operator and AccessToken scopes for `apiVersion:2` + +- **Operator Accesses**: Add `operatorAccess()` for operator accesses API to Operator and AccessToken scopes for `apiVersion:2` + +- **Me**: Add `me()` for me API to Operator and AccessToken scopes for `apiVersion:2` + +- **Access Tokens**: Add `accessToken()` for access tokens API to Operator and AccessToken scopes for `apiVersion:2` + # v5.9.0 (1-7-2020) ## Features @@ -12,19 +28,16 @@ - **ActionApp**: All `getAnonymousUser()` to allow usage of the managed anonymous Application User. - ## Fixes - **ActionApp**: Fix bug preventing use of built-in action types. - # v5.7.1 (14-1-2020) ## Features - **Pagination**: Add `streamPages()` to all resources to allow asynchronously streaming pages of resources. - # v5.6.0 (27-9-2019) ## Features @@ -33,19 +46,16 @@ - **Shipment Notices**: Add `shipmentNotice()` and `shipmentNotice().container()` for shipment notices API to Operator scope. - ## Fixes - **Scopes**: Prevent scopes from reading access twice when using `init()`. - # v5.5.0 (29-8-2019) ## Features - **ADI Orders**: Added `adiOrder()` resources to Operator scope for creating and reading ADI Orders. Also includes `event()` for creating ADI Order events. - # v5.4.0 (7-8-2019) ## Changes @@ -53,12 +63,10 @@ - **redirections**:`redirection()` resource now allows nominating the `shortDomain` in that call, and no longer requires some templating (`{shortId}`/`{productId}`) as part of the URL submitted. - **redirections**: `settings` now includes `defaultShortDomain` to allow setting the default short domain used for `redirection()` requests. - ## Fixes - **api**: Better handle parts of the API that return non-standard empty response bodies. - # v5.3.0 (29-7-2019) ## Features @@ -67,25 +75,22 @@ - **files**: Add `upload()` method for files to upload file data. - **upsert**: `upsert()` can now be performed by `name` in addition to an identifier object where a resource supports filtering by `name`. - ## Fixes - **param setters**: Add missing `setIds()` param setter. - **permissions**: Support referring to permission by name, such as `permission('global_read')` - # v5.1.0 (30-5-2019) ## Features - **plugins**: Added the `use()` method in order to support plugins. See `src/use.js` for API details. - # v5.0.0 (23-5-2019) ## Breaking Changes -> If you are updating from a previous version, please see the +> If you are updating from a previous version, please see the > [Migration Guide](https://developers.evrythng.com/docs/evrythngjs-v500). - **evrythng-extended.js**: `evrythng-extended.js` is no longer required as a separate dependency. @@ -107,7 +112,6 @@ - **Parameter setters**: Instead of creating a `params` object, chainable setters such as `setPerPage()` are available on most resources to easily build complex requests. - **Resource methods**: The `rescope()`, `upsert()`, and `find()` methods have been added to most resources to allow easier _changing of project/user scopes_, _updating by key else creating_, and _finding by identifiers_ as common operations. - # v4.7.2 (11-15-2017) ## Bug fixes @@ -172,8 +176,8 @@ ## Features -- **Reactor scripts**: Add nested reactor script status -resource `app.reactor.script().status()`. +- **Reactor scripts**: Add nested reactor script status + resource `app.reactor.script().status()`. # v4.0.1 (11-10-2016) @@ -186,17 +190,17 @@ resource `app.reactor.script().status()`. ## Breaking changes -- **Reactor logs**: Move `app.reactorLog()` resource within the `app.reactor.log()` namespace -(only available in **evrythng-extended.js**). +- **Reactor logs**: Move `app.reactorLog()` resource within the `app.reactor.log()` namespace + (only available in **evrythng-extended.js**). - **Search**: Global `.search()` has been removed to match API. Use filters instead. - **Multimedia**: Multimedia resource has been removed to match API. ## Features -- **Reactor schedules**: Add Reactor schedules resource in the `app.reactor.schedule()` namespace -(only available in **evrythng-extended.js**). -- **Reactor scripts**: Add Reactor scripts resource in the `app.reactor.script()` namespace -(only available in **evrythng-extended.js**). +- **Reactor schedules**: Add Reactor schedules resource in the `app.reactor.schedule()` namespace + (only available in **evrythng-extended.js**). +- **Reactor scripts**: Add Reactor scripts resource in the `app.reactor.script()` namespace + (only available in **evrythng-extended.js**). # v3.7.0 (16-06-2016) @@ -223,8 +227,8 @@ resource `app.reactor.script().status()`. ## Features -- **Iterator API**: Async generator `iterator()` added to every Resource, supporting looping -through the new pagination links. +- **Iterator API**: Async generator `iterator()` added to every Resource, supporting looping + through the new pagination links. - **Utils**: Added `forEachAsync()` utility to loop through async generator values. - **Utils**: Added `spawn()` utility to run through generator function. @@ -302,12 +306,12 @@ through the new pagination links. ## Features - **Trusted App**: scope to use with you **Application Secret Key**, mainly used in Reactor scripts -(only available in **evrythng-extended.js**). + (only available in **evrythng-extended.js**). ## Breaking changes - **plugins**: `EVT.use()` to install plugin is now synchronous. Callback has been removed. Required dependencies -now use `$inject` property instead of `requires`. + now use `$inject` property instead of `requires`. # v3.1.2 (25-06-2015) diff --git a/README.md b/README.md index 31df825..fce5500 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,12 @@ The official `evrythng.js` SDK facilitates communication with the resource oriented API. It can be used both for server-side scripting (Node.js) or in client-side web applications in modern browsers. -* [Installation](#installation) -* [Compatibility](#compatibility) -* [Scopes](#scopes) -* [API](#api) -* [Plugins](#plugins) -* [Documentation and Examples](#documentation-and-examples) - +- [Installation](#installation) +- [Compatibility](#compatibility) +- [Scopes](#scopes) +- [API](#api) +- [Plugins](#plugins) +- [Documentation and Examples](#documentation-and-examples) ## Installation @@ -19,7 +18,6 @@ or in client-side web applications in modern browsers. and the EVRYTHNG CDN, allowing you to manage the version of the library that your application or scripts uses. - ### NPM Install as an app dependency: @@ -37,46 +35,40 @@ npm install --save-dev evrythng Then require it in any module: ```js -const evrythng = require('evrythng') +const evrythng = require('evrythng'); -evrythng.api({ url: '/time' }) - .then(console.log) - .error(console.error) +evrythng.api({ url: '/time' }).then(console.log).error(console.error); ``` Or using ES6 `import`/`export` syntax when available: ```js -import { Application } from 'evrythng' +import { Application } from 'evrythng'; ``` ```js -import evrythng from 'evrythng' +import evrythng from 'evrythng'; // Alternatively -import * as evrythng from 'evrythng' +import * as evrythng from 'evrythng'; ``` - ### CDN Or use a simple script tag to load it from the CDN. ```html - + ``` Then use in a browser `script` tag using the `evrythng` global variable: ```html ``` - ## Compatibility `evrythng.js` relies on the standard resource fetching API (`fetch`) to @@ -86,7 +78,6 @@ dependency of this project should take care of this for you. When using Node.js, version 10 and above is required. - ## Scopes There are several types of @@ -95,75 +86,89 @@ that are used to interact with the API. Each represents a type of user or resource in an EVRYTHNG account. > Note: Only the **Application API Key** can be safely versioned in public code! - The other API key types are secret and secure API Keys with higher permission - sets and should not be hard-coded - ideally encrypted in configuration files - or fetched at runtime from a server. +> The other API key types are secret and secure API Keys with higher permission +> sets and should not be hard-coded - ideally encrypted in configuration files +> or fetched at runtime from a server. In a nutshell, `evrythng.js` provides the following scopes. Once a scope is created it provides an appropriate API for the resources it can manage -(see [API](#api) below): +(see [API](#api) below). + +For `apiVersion:2` these scopes are avaliable: + +- `Operator` +- `AccessToken`- AccessToken is a Scope type for the v2 API with the potential to manage any account resources, depending on the API key's permissions. It represents an API access for a specific purpose, instead of a type of Actor, such as an Operator. + +`Operator` and `AccessToken` scopes can have a different set of permissions, which is defined in an [access policy](https://developers.evrythng.com/reference/access-policies) and assigned during creation of [operator access](https://developers.evrythng.com/reference/operator-access) and [access token](https://developers.evrythng.com/reference/access-tokens). + +```js +const accessToken = new evrythng.AccessToken(ACCESS_TOKEN_API_KEY); +``` + +For `apiVersion:1` these scopes are avaliable: -* `Operator` - Highest level scope that can manage the account structure, all +- `Operator` - Highest level scope that can manage the account structure, all its resources and projects, etc. ```js -const operator = new evrythng.Operator(OPERATOR_API_KEY) +const operator = new evrythng.Operator(OPERATOR_API_KEY); ``` -* `Application` - Public application scopes used for Identifier Recognition and +- `Application` - Public application scopes used for Identifier Recognition and to authenticate Application Users. ```js -const application = new evrythng.Application(APPLICATION_API_KEY) +const application = new evrythng.Application(APPLICATION_API_KEY); ``` -* `TrustedApplication` - Secret type of `Application` scope with expended +- `TrustedApplication` - Secret type of `Application` scope with expended permissions, intended for use for scripting and backend integrations on behalf of the application (e.g. trigger rules or system integration functionality). ```js -const trustedApplication = new evrythng.TrustedApplication(TRUSTED_APP_API_KEY) +const trustedApplication = new evrythng.TrustedApplication(TRUSTED_APP_API_KEY); ``` -* `User` - Usually returned from authentication via an `Application` scope, but +- `User` - Usually returned from authentication via an `Application` scope, but can also be created manually with an Application User API Key: ```js // Registered user with email + password -const credentials = { email: 'example@evrythng.com', password } -app.login(credentials) - .then(user => console.log(user.apiKey)) +const credentials = { email: 'example@evrythng.com', password }; +app.login(credentials).then((user) => console.log(user.apiKey)); // Or, an anonymous user -app.appUser().create({ anonymous: true }) - .then(anonUser => console.log(anonUser.apiKey)) +app + .appUser() + .create({ anonymous: true }) + .then((anonUser) => console.log(anonUser.apiKey)); // Or using a pre-existing API key -const userApiKey = localStorage.getItem('user_api_key') -const user = new evrythng.User(userApiKey) +const userApiKey = localStorage.getItem('user_api_key'); +const user = new evrythng.User(userApiKey); ``` -* `ActionApp` - Special version of the Application scope designed to make it as +- `ActionApp` - Special version of the Application scope designed to make it as simple as possible to create instrumentation actions in web apps. It creates and remembers an anonymous Application User in LocalStorage and provides a simple interface for creating actions: ```js -import { ActionApp } from 'evrythng' +import { ActionApp } from 'evrythng'; -const actionApp = new ActionApp(appApiKey) -await actionApp.init() +const actionApp = new ActionApp(appApiKey); +await actionApp.init(); // Create a scan action on a Thng identified in the query -const thng = getQueryParam('thng') -const data = { thng, userAgent } -const action = await actionApp.createAction('scans', data) +const thng = getQueryParam('thng'); +const data = { thng, userAgent }; +const action = await actionApp.createAction('scans', data); // Log a page was visited (the current URL) -await actionApp.pageVisited() +await actionApp.pageVisited(); // Retrieve the managed Application User -const anonymousUser = await actionApp.getAnonymousUser() +const anonymousUser = await actionApp.getAnonymousUser(); ``` For any scope, if the scope's own data (such as an Application's `customFields`) @@ -171,23 +176,20 @@ is required immediately, use the `init()` method to wait until this data is available. If not, this step can be ignored: ```js -import { Application } from 'evrythng' +import { Application } from 'evrythng'; -const application = new Application(apiKey) -application.init() - .then(() => console.log(application.customFields)) +const application = new Application(apiKey); +application.init().then(() => console.log(application.customFields)); ``` - ## API The methods available for each of the above scope types matches the general access level defined for each type of -[API Key](https://developers.evrythng.com/docs/api-scope-and-key-permissions). +API Key for [apiVersion:2](https://developers.evrythng.com/docs/api-keys-and-key-permissions-v2) or [apiVersion:1](https://developers.evrythng.com/docs/api-scope-and-key-permissions). For example - the `Application` scope can read products in its project, but can only create `User`s who in turn have higher access to manage resources. - ### Methods The API for each scope follows a fluent pattern that decreases the time required @@ -203,50 +205,56 @@ SCOPE Where: -* `SCOPE` - One of the scope types shown above. -* `RESOURCE` - can be any resource type, such as `thng`, `product`, `collection` +- `SCOPE` - One of the scope types shown above. +- `RESOURCE` - can be any resource type, such as `thng`, `product`, `collection` etc. found in the [API Reference](https://developers.evrythng.com/reference). - * `id` - specified if manipulating a specific resource of this type. -* `METHOD` - one of `create`, `read`, `update`, `delete`, `rescope`, `find`, or `upsert`. - * `payload` - JSON payload object if performing a create or update. - * `params` - Parameters object used if required. - + - `id` - specified if manipulating a specific resource of this type. +- `METHOD` - one of `create`, `read`, `update`, `delete`, `rescope`, `find`, or `upsert`. + - `payload` - JSON payload object if performing a create or update. + - `params` - Parameters object used if required. Therefore to read all Thngs as a `TrustedApplication` scope: ```js -trustedApplication.thng().read() - .then(thngs => console.log(`Read ${thngs.length} Thngs!`)) +trustedApplication + .thng() + .read() + .then((thngs) => console.log(`Read ${thngs.length} Thngs!`)); ``` or to create a product as a `User`: ```js -const payload = { name: 'Test Product', tags: ['evrythng.js'] } -user.product().create(payload) - .then(product => console.log(`Created product ${product.id}!`)) +const payload = { name: 'Test Product', tags: ['evrythng.js'] }; +user + .product() + .create(payload) + .then((product) => console.log(`Created product ${product.id}!`)); ``` or to read a known Thng using its `id` as an Operator: ```js -const thngId = 'UqKWAsTpdxCA3KwaaGmTxAhp' +const thngId = 'UqKWAsTpdxCA3KwaaGmTxAhp'; -operator.thng(thngId).read() - .then(thng => console.log(`Thng tags: ${thng.tags.join(', ')}`)) +operator + .thng(thngId) + .read() + .then((thng) => console.log(`Thng tags: ${thng.tags.join(', ')}`)); ``` - ### Promises All methods return Promises, making chaining operations and catching errors very simple: ```js -user.thng().create(payload) - .then(res => console.log('Success!')) - .catch(err => console.log(`Oh no! Error: ${err.message}`)) +user + .thng() + .create(payload) + .then((res) => console.log('Success!')) + .catch((err) => console.log(`Oh no! Error: ${err.message}`)); ``` Users of modern browsers and Node.js 8+ can take advantage `async`/`await` @@ -256,19 +264,18 @@ operations: ```js const testThngUpdate = async () => { // Read all Thngs and find one - const thngs = await operator.thng().read() - const testThng = thngs.find(p => p.tags.includes('test')) + const thngs = await operator.thng().read(); + const testThng = thngs.find((p) => p.tags.includes('test')); // Update its tags - const payload = { tags: ['updated'] } - const updatedThng = await operator.thng(testThng.id).update(payload) + const payload = { tags: ['updated'] }; + const updatedThng = await operator.thng(testThng.id).update(payload); // Check the update was successful - expect(updatedThng.tags).to.equal(payload.tags) -} + expect(updatedThng.tags).to.equal(payload.tags); +}; ``` - ### Parameters Each of the methods described above can accept parameters identical to those @@ -279,48 +286,52 @@ shown below: const params = { // Only with these tags filter: { - tags: 'test' + tags: 'test', }, // More items per page - perPage: 100 -} + perPage: 100, +}; -user.product().read({ params }) - .then(products => console.log(`Found ${products.length} 'test' products`)) +user + .product() + .read({ params }) + .then((products) => console.log(`Found ${products.length} 'test' products`)); ``` Another example is creating resources in a specific project scope: ```js -const params = { project: projectId } -const payload = { name: 'Test Thng' } +const params = { project: projectId }; +const payload = { name: 'Test Thng' }; -user.thng().create(payload, { params }) - .then(thng => console.log(`Created Thng ${thng.id} in project ${projectId}`)) +user + .thng() + .create(payload, { params }) + .then((thng) => console.log(`Created Thng ${thng.id} in project ${projectId}`)); ``` Parameters can also be specified using chainable parameter setter methods: ```js -user.product() +user + .product() .setFilter({ tags: 'test' }) .setPerPage(100) .read() - .then(products => console.log(`Found ${products.length} 'test' products`)) + .then((products) => console.log(`Found ${products.length} 'test' products`)); ``` Other parameter setters include `setWithScopes()`, `setContext()`, `setPerPage()`, `setProject()` and `setFilter()`. - ## Plugins This SDK can be extended with plugins that enhance existing functionality by modifying the capabilities of Scopes and Entities. This is done by supplying an -object with at least an `install()` method, that is provided an `api` object -(see `src/use.js` for details of this API). +object with at least an `install()` method, that is provided an `api` object +(see `src/use.js` for details of this API). -For example, adding a `getSummary()` method to Thngs: +For example, adding a `getSummary()` method to Thngs: ```js const SummaryPlugin = { @@ -328,42 +339,40 @@ const SummaryPlugin = { install: (api) => { // Add new functionality to all Thng entities api.entities.Thng.prototype.getSummary = function () { - return `${this.name} (${this.id})` - } - } -} + return `${this.name} (${this.id})`; + }; + }, +}; ``` The plugin is then installed using `use()`: ```js -const SummaryPlugin = require('summary-plugin') +const SummaryPlugin = require('summary-plugin'); -evrythng.use(SummaryPlugin) +evrythng.use(SummaryPlugin); ``` Then, the plugin's functionality can be used: ```js // Read one Thng -const [thng] = await user.thng().setPerPage(1).read() +const [thng] = await user.thng().setPerPage(1).read(); // Use the newly installed method -console.log(thng.getSummary()) +console.log(thng.getSummary()); ``` ``` Test Thng (U6ssDxRBD8kQATawwGrEyaRm) ``` - ## Documentation and Examples For specific resource examples, see the relevant section of the [API Reference](https://developers.evrythng.com/reference), or look in the `examples` directory in this repository. - ## Build and Deploy See `./jenkins/deploy.sh` for instructions on deploying new versions. diff --git a/examples/browser-npm-browserify/index.js b/examples/browser-npm-browserify/index.js index 3b13e60..2a9d06a 100644 --- a/examples/browser-npm-browserify/index.js +++ b/examples/browser-npm-browserify/index.js @@ -1,8 +1,9 @@ const evrythng = require('evrythng') -evrythng.api({ - url: '/time' -}) +evrythng + .api({ + url: '/time' + }) .then((res) => { const output = document.getElementById('output') output.innerHTML = res.timestamp diff --git a/examples/node/index.js b/examples/node/index.js index b656191..0e75860 100644 --- a/examples/node/index.js +++ b/examples/node/index.js @@ -1,7 +1,8 @@ const evrythng = require('evrythng') -evrythng.api({ - url: '/time' -}) - .then(res => console.log(`Current time: ${res.timestamp}`)) +evrythng + .api({ + url: '/time' + }) + .then((res) => console.log(`Current time: ${res.timestamp}`)) .catch(console.log) diff --git a/package-lock.json b/package-lock.json index f7dbd9f..afdf1f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "evrythng", - "version": "5.10.1", + "version": "6.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2391,6 +2391,86 @@ "to-fast-properties": "^2.0.0" } }, + "@eslint/eslintrc": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", + "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", @@ -2584,6 +2664,22 @@ "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", @@ -2614,6 +2710,23 @@ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", "dev": true }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -2673,12 +2786,180 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-includes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", + "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "get-intrinsic": "^1.0.1", + "is-string": "^1.0.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -2737,6 +3018,12 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -3159,6 +3446,22 @@ "unset-value": "^1.0.0" } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camelcase": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.2.0.tgz", @@ -3205,6 +3508,12 @@ "supports-color": "^5.3.0" } }, + "chance": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.7.tgz", + "integrity": "sha512-bua/2cZEfzS6qPm0vi3JEvGNbriDLcMj9lKxCQOjUcCJRcyjA7umP0zZm6bKWWlBN04vA0L99QGH/CZQawr0eg==", + "dev": true + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -3327,6 +3636,12 @@ "tslib": "^1.9.0" } }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -3360,6 +3675,112 @@ } } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -3436,6 +3857,12 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -3472,6 +3899,12 @@ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "convert-source-map": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", @@ -3531,6 +3964,39 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "dependencies": { + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -3643,6 +4109,12 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -3652,6 +4124,12 @@ "type-detect": "^4.0.0" } }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -3743,6 +4221,15 @@ } } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", @@ -3822,6 +4309,23 @@ "tapable": "^1.0.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + } + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -3831,6 +4335,15 @@ "prr": "~1.0.1" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, "es-abstract": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", @@ -3868,27 +4381,520 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "eslint": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz", + "integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.1", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "eslint-config-standard": { + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.2.tgz", + "integrity": "sha512-fx3f1rJDsl9bY7qzyX8SAtP8GBSk6MfXFaTfaGgk12aAYW4gJSyRm7dM790L6cbXv63fvjY4XeSzXnb4WM+SKw==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true + }, + "espree": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, "requires": { "estraverse": "^4.1.0" } @@ -4078,6 +5084,12 @@ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fetch-mock": { "version": "9.10.7", "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.10.7.tgz", @@ -4118,6 +5130,24 @@ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", "dev": true }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -4168,6 +5198,15 @@ "locate-path": "^3.0.0" } }, + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "requires": { + "semver-regex": "^2.0.0" + } + }, "findup-sync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", @@ -4189,14 +5228,42 @@ "is-buffer": "~2.0.3" }, "dependencies": { - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + } + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } } } }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -4263,6 +5330,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -4275,6 +5348,31 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + } + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -4309,7 +5407,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, - "optional": true, "requires": { "is-glob": "^4.0.1" } @@ -4498,12 +5595,136 @@ "parse-passwd": "^1.0.0" } }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz", + "integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", @@ -4516,6 +5737,30 @@ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -4532,6 +5777,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "infer-owner": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", @@ -4601,6 +5852,12 @@ } } }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4623,6 +5880,15 @@ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -4695,6 +5961,12 @@ "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -4715,6 +5987,12 @@ } } }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -4733,12 +6011,24 @@ "has": "^1.0.1" } }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, "is-subset": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", @@ -4766,6 +6056,12 @@ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4794,67 +6090,411 @@ "esprima": "^4.0.0" } }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "requires": { + "leven": "^3.1.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lint-staged": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.2.tgz", + "integrity": "sha512-e8AYR1TDlzwB8VVd38Xu2lXDZf6BcshVqKVuBQThDJRaJLobqKnpbm4dkwJ2puypQNbLr9KF/9mfA649mAGvjA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.2.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.2.0", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "listr2": "^3.2.2", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "listr2": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.2.3.tgz", + "integrity": "sha512-vUb80S2dSUi8YxXahO8/I/s29GqnOL8ozgHVLjfWQXa03BNEeS1TpBLjh2ruaqq5ufx46BRGvfymdBSuoXET5w==", "dev": true, "requires": { - "minimist": "^1.2.0" + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.3", + "through": "^2.3.8" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "lcid": { + "load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { - "leven": "^3.1.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } } }, "loader-runner": { @@ -4917,6 +6557,110 @@ "chalk": "^2.4.2" } }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5001,6 +6745,12 @@ "readable-stream": "^2.0.1" } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -5225,6 +6975,12 @@ "to-regex": "^3.0.1" } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "neo-async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", @@ -5320,6 +7076,18 @@ "integrity": "sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g==", "dev": true }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5372,6 +7140,12 @@ } } }, + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true + }, "object-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", @@ -5399,32 +7173,153 @@ "object-keys": "^1.0.11" } }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", + "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "wrappy": "1" } }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { - "isobject": "^3.0.1" + "mimic-fn": "^2.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + } } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "requires": { - "wrappy": "1" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" } }, "os-browserify": { @@ -5480,6 +7375,15 @@ "p-limit": "^2.0.0" } }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, "p-try": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.1.0.tgz", @@ -5503,6 +7407,15 @@ "readable-stream": "^2.1.5" } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-asn1": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", @@ -5517,6 +7430,15 @@ "safe-buffer": "^5.1.1" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, "parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", @@ -5572,6 +7494,23 @@ "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", "dev": true }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", @@ -5612,12 +7551,33 @@ "find-up": "^3.0.0" } }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -5630,6 +7590,12 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -5740,6 +7706,72 @@ "safe-buffer": "^5.1.0" } }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -5829,6 +7861,12 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, "regexpu-core": { "version": "4.7.1", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", @@ -5950,6 +7988,16 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -5984,6 +8032,15 @@ "aproba": "^1.1.1" } }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -6016,6 +8073,18 @@ "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -6082,6 +8151,23 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -6238,6 +8324,38 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -6322,6 +8440,12 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -6332,6 +8456,26 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -6341,6 +8485,17 @@ "safe-buffer": "~5.1.0" } }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -6350,12 +8505,24 @@ "ansi-regex": "^3.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -6371,6 +8538,64 @@ "has-flag": "^3.0.0" } }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "tapable": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", @@ -6430,6 +8655,18 @@ } } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -6526,6 +8763,18 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -6538,12 +8787,27 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -6739,6 +9003,16 @@ "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", "dev": true }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -7158,6 +9432,12 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -7167,6 +9447,12 @@ "string-width": "^1.0.2 || 2" } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", @@ -7221,6 +9507,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -7239,6 +9534,12 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/package.json b/package.json index 69ea4f1..60e5d08 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,64 @@ { "name": "evrythng", - "version": "5.10.1", + "version": "6.0.0", "description": "Official Javascript SDK for the EVRYTHNG API.", "main": "./dist/evrythng.node.js", "scripts": { - "lint": "standard", + "lint": "eslint --fix --max-warnings 0 --ext .js .", + "format": "./node_modules/.bin/prettier --write '**/*.{js,json,md,yaml,yml}'", "build": "webpack --config webpack.config.js --mode production", "build-dev": "webpack --config webpack.config.js --mode development", - "show-notice": "echo '\n New to evrythng.js v5? Make sure to read the migration guide:\n https://developers.evrythng.com/docs/evrythngjs-v500\n'", + "show-notice": "echo '\n New to evrythng.js v6? Make sure to read the migration guide:\n https://developers.evrythng.com/docs/evrythngjs-v600\n'", "postinstall": "npm run --silent show-notice", - "test": "mocha test/e2e/index.spec.js", + "test": "mocha test/integration/**.spec.js", + "test:unit": "mocha --require esm test/unit/**.spec.js", + "test:e2e": "mocha test/e2e/**.spec.js", "prepublishOnly": "npm run build" }, + "husky": { + "hooks": { + "pre-commit": "lint-staged -p false" + } + }, + "lint-staged": { + "*.{js,json,md,yaml,yml}": "prettier --write", + "*.js": "eslint --fix" + }, + "prettier": { + "printWidth": 100, + "singleQuote": true, + "trailingComma": "all" + }, + "eslintConfig": { + "env": { + "browser": true, + "commonjs": true, + "mocha": true + }, + "extends": [ + "standard" + ], + "parserOptions": { + "ecmaVersion": 12 + }, + "rules": { + "eqeqeq": [ + 0, + "smart" + ], + "no-unused-expressions": "off", + "no-prototype-builtins": "off", + "prefer-promise-reject-errors": "off", + "operator-linebreak": "off" + } + }, + "eslintIgnore": [ + "node_modules", + "reports", + "mochawesome-report", + ".nyc_output", + "dist" + ], "repository": { "type": "git", "url": "git+https://github.com/evrythng/evrythng.js.git" @@ -43,10 +90,20 @@ "babel-loader": "^8.0.5", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", + "chance": "^1.1.7", + "eslint": "^7.14.0", + "eslint-config-standard": "^16.0.2", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1", + "esm": "^3.2.25", "fetch-mock": "^9.10.7", + "husky": "^4.3.0", + "lint-staged": "^10.5.2", "lodash-es": "^4.17.15", "mocha": "^7.2.0", "nock": "^11.3.3", + "prettier": "^2.2.1", "webpack": "^4.43.0", "webpack-cli": "^3.3.11" }, diff --git a/src/alias.js b/src/alias.js index 59eba3a..cefd2d4 100644 --- a/src/alias.js +++ b/src/alias.js @@ -38,11 +38,8 @@ export default function alias (map, target) { throw new Error(`${existing} does not exist for ${target}`) } - Object.assign( - original.prototype, - { - [map[existing]]: original.prototype[existing] - } - ) + Object.assign(original.prototype, { + [map[existing]]: original.prototype[existing] + }) } } diff --git a/src/api.js b/src/api.js index 7cf6e9b..dbbc4da 100644 --- a/src/api.js +++ b/src/api.js @@ -16,10 +16,10 @@ import { success, failure } from './util/callback' * @returns {Promise} - Response promise */ export default function api (customOptions = {}, callback) { - let initialOptions = mergeInitialOptions(customOptions) + const initialOptions = mergeInitialOptions(customOptions) return applyRequestInterceptors(initialOptions) - .then(options => { + .then((options) => { return makeFetch(options) .then(handleResponse(options)) .then(applyResponseInterceptors(options)) @@ -36,12 +36,9 @@ export default function api (customOptions = {}, callback) { * @returns {Settings} - Merged options for fetch */ function mergeInitialOptions (customOptions) { - const options = Object.assign( - { method: 'get', url: '' }, - settings, - customOptions, - { headers: Object.assign({}, settings.headers, customOptions.headers) } - ) + const options = Object.assign({ method: 'get', url: '' }, settings, customOptions, { + headers: Object.assign({}, settings.headers, customOptions.headers) + }) // Use apiKey if authorization header is not explicitly provided. if (!options.headers.authorization && options.apiKey) { @@ -74,17 +71,17 @@ function applyRequestInterceptors (options) { if (Array.isArray(options.interceptors)) { options.interceptors - .filter(interceptor => isFunction(interceptor.request)) - .forEach(interceptor => { + .filter((interceptor) => isFunction(interceptor.request)) + .forEach((interceptor) => { // Chain promises. If interceptor returns undefined, use previous options - intercepted = intercepted.then(prevOptions => { + intercepted = intercepted.then((prevOptions) => { if (cancelled) return prevOptions return interceptor.request(prevOptions, cancel) || prevOptions }) }) } - return intercepted.then(finalOptions => { + return intercepted.then((finalOptions) => { // Reject request if it has been cancelled by request interceptors. if (cancelled) { return Promise.reject({ @@ -131,13 +128,13 @@ function makeFetch (options) { function handleResponse (options) { return async (response) => { const res = options.fullResponse - // Full response requested by user - ? Promise.resolve(response) + ? // Full response requested by user + Promise.resolve(response) : response.status === 204 || options.method.toLowerCase() === 'delete' - // Accepted or DELETE requests have no body - ? Promise.resolve() - // Attempt to decode the response JSON body - : response.json() + ? // Accepted or DELETE requests have no body + Promise.resolve() + : // Attempt to decode the response JSON body + response.json() let data = '' try { @@ -148,7 +145,7 @@ function handleResponse (options) { if (!response.ok) { // If a request we expect to have no response body fails, we are still interested in the error - throw (typeof data === 'object') ? data : await response.json() + throw typeof data === 'object' ? data : await response.json() } return data @@ -165,13 +162,13 @@ function handleResponse (options) { * @returns {function} - Response handler function */ function applyResponseInterceptors (options) { - return response => { + return (response) => { let intercepted = Promise.resolve(response) if (Array.isArray(options.interceptors)) { options.interceptors - .filter(interceptor => isFunction(interceptor.response)) - .forEach(interceptor => { + .filter((interceptor) => isFunction(interceptor.response)) + .forEach((interceptor) => { // Chain promises. intercepted = intercepted.then(interceptor.response) }) diff --git a/src/entities.js b/src/entities.js index ecacdca..456adba 100644 --- a/src/entities.js +++ b/src/entities.js @@ -12,20 +12,60 @@ import Permission from './entity/Permission' import Project from './entity/Project' import Role from './entity/Role' import Task from './entity/Task' +import OperatorAccess from './entity/OperatorAccess' +import AccessPolicy from './entity/AccessPolicy' +import AccessTokens from './entity/AccessToken' +import Me from './entity/Me' +import Access from './entity/Access' +import ADIOrder from './entity/ADIOrder' +import ADIOrderEvent from './entity/ADIOrderEvent' +import Account from './entity/Account' +import CommissionState from './entity/CommissionState' +import Domain from './entity/Domain' +import Place from './entity/Place' +import PurchaseOrder from './entity/PurchaseOrder' +import ReactorLog from './entity/ReactorLog' +import ReactorSchedule from './entity/ReactorSchedule' +import ReactorScript from './entity/ReactorScript' +import Redirection from './entity/Redirection' +import Redirector from './entity/Redirector' +import ShipmentNotice from './entity/ShipmentNotice' +import ShortDomain from './entity/ShortDomain' +import File from './entity/File' export default { - Product, - Thng, - Collection, - Property, + Access, + AccessPolicy, + AccessTokens, + Account, Action, ActionType, + ADIOrder, + ADIOrderEvent, Application, - User, Batch, + Collection, + CommissionState, + Domain, + File, Location, + Me, + OperatorAccess, Permission, + Place, + Product, Project, + Property, + PurchaseOrder, Role, - Task + ReactorLog, + ReactorSchedule, + ReactorScript, + Redirection, + Redirector, + ShipmentNotice, + ShortDomain, + Task, + Thng, + User } diff --git a/src/entity/ADIOrder.js b/src/entity/ADIOrder.js index e2600b8..f06f6c0 100644 --- a/src/entity/ADIOrder.js +++ b/src/entity/ADIOrder.js @@ -4,9 +4,7 @@ import Resource from '../resource/Resource' import { mixinResources } from '../util/mixin' const path = '/adis/orders' -const ADIOrderResources = mixinResources([ - ADIOrderEvent -]) +const ADIOrderResources = mixinResources([ADIOrderEvent]) /** * Represents a ADIOrder entity object. diff --git a/src/entity/AccessPolicy.js b/src/entity/AccessPolicy.js new file mode 100644 index 0000000..9d18cac --- /dev/null +++ b/src/entity/AccessPolicy.js @@ -0,0 +1,25 @@ +import Entity from './Entity' +import Resource from '../resource/Resource' + +const path = '/accessPolicies' + +/** + * Represents an Access Policy entity. + * + * @extends Entity + */ +export default class AccessPolicy extends Entity { + /** + * Return simple resource factory for Access Policies. + * + * @static + * @return {{accessPolicy: Function}} + */ + static resourceFactory () { + return { + accessPolicy (id) { + return Resource.factoryFor(AccessPolicy, path).call(this, id) + } + } + } +} diff --git a/src/entity/AccessToken.js b/src/entity/AccessToken.js new file mode 100644 index 0000000..1e1a801 --- /dev/null +++ b/src/entity/AccessToken.js @@ -0,0 +1,25 @@ +import Entity from './Entity' +import Resource from '../resource/Resource' + +const path = '/accessTokens' + +/** + * Represents an Access Token entity. + * + * @extends Entity + */ +export default class AccessToken extends Entity { + /** + * Return simple resource factory for Access Tokens. + * + * @static + * @return {{accessToken: Function}} + */ + static resourceFactory () { + return { + accessToken (id) { + return Resource.factoryFor(AccessToken, path).call(this, id) + } + } + } +} diff --git a/src/entity/Account.js b/src/entity/Account.js index cff863e..5be19f6 100644 --- a/src/entity/Account.js +++ b/src/entity/Account.js @@ -1,5 +1,6 @@ import Access from './Access' import Domain from './Domain' +import OperatorAccess from './OperatorAccess' import Entity from './Entity' import ShortDomain from './ShortDomain' import Resource from '../resource/Resource' @@ -9,7 +10,8 @@ const path = '/accounts' const AccountResources = mixinResources([ Domain, // R ShortDomain, // R - Access // RU + Access, // RU + OperatorAccess // CRUD ]) /** diff --git a/src/entity/Action.js b/src/entity/Action.js index eb56153..9e5388e 100644 --- a/src/entity/Action.js +++ b/src/entity/Action.js @@ -41,14 +41,11 @@ export default class Action extends Entity { // Creates and returns Resource of type Action. // Override property resource create to allow custom value params and // fetch the user's geolocation. See `createAction()`. - return Object.assign( - Resource.factoryFor(Action, relativePath).call(this, id), - { - create (...args) { - return createAction.call(this, context, actionType, ...args) - } + return Object.assign(Resource.factoryFor(Action, relativePath).call(this, id), { + create (...args) { + return createAction.call(this, context, actionType, ...args) } - ) + }) } } } @@ -65,23 +62,23 @@ export default class Action extends Entity { */ function createAction (caller, actionType, ...args) { let [data, ...rest] = normalizeArguments(...args) - let [options] = rest + const [options] = rest // Auto-fill action payload with resource type and entity id. data = Array.isArray(data) - ? data.map(action => fillAction(action, caller, actionType)) - : data = fillAction(data, caller, actionType) + ? data.map((action) => fillAction(action, caller, actionType)) + : (data = fillAction(data, caller, actionType)) const baseCreate = Resource.prototype.create.bind(this) const updatedArgs = () => [data, ...rest] if (useGeolocation(options)) { return getCurrentPosition() - .then(position => { + .then((position) => { data = fillActionLocation(data, position) return baseCreate(...updatedArgs()) }) - .catch(err => { + .catch((err) => { console.info(`Unable to get position: ${err}`) return baseCreate(...updatedArgs()) }) @@ -101,7 +98,7 @@ function createAction (caller, actionType, ...args) { * product.action().create() */ function normalizeArguments (...args) { - let firstArg = args[0] + const firstArg = args[0] if (isUndefined(firstArg) || isFunction(firstArg)) { args.unshift({}) } @@ -143,8 +140,8 @@ function fillAction (data, caller, actionType) { function getIdentifier (caller) { return caller instanceof Entity ? caller[symbols.actionIdentifier] - ? caller[symbols.actionIdentifier] - : '' + ? caller[symbols.actionIdentifier] + : '' : '' } @@ -156,9 +153,7 @@ function getIdentifier (caller) { * @return {boolean} */ function useGeolocation (options) { - return options && !isUndefined(options.geolocation) - ? options.geolocation - : settings.geolocation + return options && !isUndefined(options.geolocation) ? options.geolocation : settings.geolocation } /** diff --git a/src/entity/ActionType.js b/src/entity/ActionType.js index 00869a2..6606452 100644 --- a/src/entity/ActionType.js +++ b/src/entity/ActionType.js @@ -23,14 +23,11 @@ export default class ActionType extends Entity { static resourceFactory () { return { actionType (id) { - return Object.assign( - Resource.factoryFor(ActionType, path).call(this, id), - { - read (...args) { - return readActionType.call(this, id, ...args) - } + return Object.assign(Resource.factoryFor(ActionType, path).call(this, id), { + read (...args) { + return readActionType.call(this, id, ...args) } - ) + }) } } } @@ -52,17 +49,16 @@ function readActionType (id, ...args) { // If reading an action type, only use the root of the path this.path = this.path.split('/').slice(0, 2).join('/') - Resource.prototype.read.call(this, ...normalizedArgs) - .then(actionTypes => { - if (!actionTypes.length) { - // Fake 404 - reject({ - status: 404, - errors: ['The action type was not found.'] - }) - } - resolve(actionTypes[0]) - }) + Resource.prototype.read.call(this, ...normalizedArgs).then((actionTypes) => { + if (!actionTypes.length) { + // Fake 404 + reject({ + status: 404, + errors: ['The action type was not found.'] + }) + } + resolve(actionTypes[0]) + }) }) } } @@ -78,7 +74,7 @@ function readActionType (id, ...args) { function normalizeArguments (id) { return (...args) => { let options - let firstArg = args[0] + const firstArg = args[0] if (isUndefined(firstArg) || isFunction(firstArg)) { options = {} @@ -88,10 +84,7 @@ function normalizeArguments (id) { } options.url = path - options.params = Object.assign( - { filter: { name: decodeURIComponent(id) } }, - options.params - ) + options.params = Object.assign({ filter: { name: decodeURIComponent(id) } }, options.params) return args } diff --git a/src/entity/AppUser.js b/src/entity/AppUser.js index 0b64a0c..979c670 100644 --- a/src/entity/AppUser.js +++ b/src/entity/AppUser.js @@ -23,17 +23,14 @@ export default class AppUser extends Entity { static resourceFactory () { return { appUser (id) { - return Object.assign( - Resource.factoryFor(AppUser, path).call(this, id), - { - create (...args) { - return createAppUser.call(this, ...args) - }, - validate (...args) { - return validate.call(this, ...args) - } + return Object.assign(Resource.factoryFor(AppUser, path).call(this, id), { + create (...args) { + return createAppUser.call(this, ...args) + }, + validate (...args) { + return validate.call(this, ...args) } - ) + }) } } } @@ -129,5 +126,7 @@ function createAnonymousUser () { }, data: {}, apiKey: this.scope.apiKey - }).then(access => new User(access.evrythngApiKey, Object.assign(access, { type: 'anonymous' }))) + }).then( + (access) => new User(access.evrythngApiKey, Object.assign(access, { type: 'anonymous' })) + ) } diff --git a/src/entity/Application.js b/src/entity/Application.js index 1826589..795d040 100644 --- a/src/entity/Application.js +++ b/src/entity/Application.js @@ -37,8 +37,7 @@ export default class Application extends ApplicationResources(Entity) { throw new Error('Application is not a top-level resource.') } - return Resource.factoryFor(Application, path, ApplicationResources) - .call(this, id) + return Resource.factoryFor(Application, path, ApplicationResources).call(this, id) } } } diff --git a/src/entity/Batch.js b/src/entity/Batch.js index aeb47eb..6537948 100644 --- a/src/entity/Batch.js +++ b/src/entity/Batch.js @@ -4,9 +4,7 @@ import Resource from '../resource/Resource' import { mixinResources } from '../util/mixin' const path = '/batches' -const BatchResources = mixinResources([ - Task -]) +const BatchResources = mixinResources([Task]) /** * Represents a Batch entity object. diff --git a/src/entity/Collection.js b/src/entity/Collection.js index 2973545..4034cf5 100644 --- a/src/entity/Collection.js +++ b/src/entity/Collection.js @@ -34,8 +34,7 @@ export default class Collection extends CollectionResources(Entity) { collection (id) { // Explicitly add Collection resource mixin to nested resource. return Object.assign( - Resource.factoryFor(Collection, path, CollectionResources) - .call(this, id), + Resource.factoryFor(Collection, path, CollectionResources).call(this, id), Collection.resourceFactory() ) } diff --git a/src/entity/Entity.js b/src/entity/Entity.js index 4c24277..b3fd9b9 100644 --- a/src/entity/Entity.js +++ b/src/entity/Entity.js @@ -33,8 +33,7 @@ export default class Entity { * @returns {Object} */ json () { - return Object.entries(this) - .reduce((ret, [k, v]) => Object.assign(ret, { [k]: v }), {}) + return Object.entries(this).reduce((ret, [k, v]) => Object.assign(ret, { [k]: v }), {}) } /** @@ -46,12 +45,11 @@ export default class Entity { * @returns {Promise.} */ update (body = this.json(), callback) { - return this[symbols.resource].update(body, callback) - .then(updated => { - // Update self and keep chaining with API response. - Object.assign(this, updated) - return updated - }) + return this[symbols.resource].update(body, callback).then((updated) => { + // Update self and keep chaining with API response. + Object.assign(this, updated) + return updated + }) } /** diff --git a/src/entity/File.js b/src/entity/File.js index 7dfa6a3..d5536e6 100644 --- a/src/entity/File.js +++ b/src/entity/File.js @@ -18,12 +18,9 @@ export default class File extends Entity { static resourceFactory () { return { file (id) { - return Object.assign( - Resource.factoryFor(File, path).call(this, id), - { - upload - } - ) + return Object.assign(Resource.factoryFor(File, path).call(this, id), { + upload + }) } } } diff --git a/src/entity/Location.js b/src/entity/Location.js index 11e6ebd..0b30ba5 100644 --- a/src/entity/Location.js +++ b/src/entity/Location.js @@ -40,14 +40,11 @@ export default class Location extends Entity { // Creates and returns Resource of type Location. // Override property resource update to allow empty updates. // See `updateLocation()`. - return Object.assign( - Resource.factoryFor(Location, thngPath + path).call(this), - { - update (...args) { - return updateLocation.call(this, ...args) - } + return Object.assign(Resource.factoryFor(Location, thngPath + path).call(this), { + update (...args) { + return updateLocation.call(this, ...args) } - ) + }) } } } @@ -61,17 +58,17 @@ export default class Location extends Entity { * @return {Promise} */ function updateLocation (...args) { - let [data, ...rest] = normalizeArguments(...args) + const [data, ...rest] = normalizeArguments(...args) const baseUpdate = Resource.prototype.update.bind(this) const updatedArgs = () => [data, ...rest] if (useGeolocation(data)) { return getCurrentPosition() - .then(position => { + .then((position) => { data[0] = fillLocation(data[0], position) return baseUpdate(...updatedArgs()) }) - .catch(err => { + .catch((err) => { console.info(`Unable to get position: ${err}`) return baseUpdate(...updatedArgs()) }) @@ -92,7 +89,7 @@ function updateLocation (...args) { * thng.location().update([]) */ function normalizeArguments (...args) { - let firstArg = args[0] + const firstArg = args[0] if (isPlainObject(firstArg)) { args[0] = [firstArg] } else if (isUndefined(firstArg) || isFunction(firstArg)) { diff --git a/src/entity/Me.js b/src/entity/Me.js new file mode 100644 index 0000000..1bccee8 --- /dev/null +++ b/src/entity/Me.js @@ -0,0 +1,25 @@ +import Entity from './Entity' +import Resource from '../resource/Resource' + +const path = '/me' + +/** + * Represents a Me entity. + * + * @extends Entity + */ +export default class Me extends Entity { + /** + * Return simple resource factory for Me. + * + * @static + * @return {{me: Function}} + */ + static resourceFactory () { + return { + me () { + return Resource.factoryFor(Me, path).call(this) + } + } + } +} diff --git a/src/entity/OperatorAccess.js b/src/entity/OperatorAccess.js new file mode 100644 index 0000000..be0ebfb --- /dev/null +++ b/src/entity/OperatorAccess.js @@ -0,0 +1,25 @@ +import Entity from './Entity' +import Resource from '../resource/Resource' + +const path = '/operatorAccess' + +/** + * Represents an Operator Access entity. + * + * @extends Entity + */ +export default class OperatorAccess extends Entity { + /** + * Return simple resource factory for Operator Accesses. + * + * @static + * @return {{operatorAccess: Function}} + */ + static resourceFactory () { + return { + operatorAccess (id) { + return Resource.factoryFor(OperatorAccess, path).call(this, id) + } + } + } +} diff --git a/src/entity/Permission.js b/src/entity/Permission.js index fa9aa48..416925b 100644 --- a/src/entity/Permission.js +++ b/src/entity/Permission.js @@ -1,7 +1,6 @@ import Entity from './Entity' import Resource from '../resource/Resource' import Scope from '../scope/Scope' -import isString from 'lodash-es/isString' const path = '/permissions' diff --git a/src/entity/Product.js b/src/entity/Product.js index b3cb237..c31c03e 100644 --- a/src/entity/Product.js +++ b/src/entity/Product.js @@ -6,11 +6,7 @@ import Resource from '../resource/Resource' import { mixinResources } from '../util/mixin' const path = '/products' -const ProductResources = mixinResources([ - Property, - Action, - Redirection -]) +const ProductResources = mixinResources([Property, Action, Redirection]) /** * Represents a Product entity object. diff --git a/src/entity/Project.js b/src/entity/Project.js index cd830af..19e7b97 100644 --- a/src/entity/Project.js +++ b/src/entity/Project.js @@ -4,9 +4,7 @@ import Resource from '../resource/Resource' import { mixinResources } from '../util/mixin' const path = '/projects' -const ProjectResources = mixinResources([ - Application -]) +const ProjectResources = mixinResources([Application]) /** * Represents a Project entity object. diff --git a/src/entity/Property.js b/src/entity/Property.js index bd76677..37f6f57 100644 --- a/src/entity/Property.js +++ b/src/entity/Property.js @@ -31,19 +31,14 @@ export default class Property extends Entity { // Creates and returns Resource of type Property. // Override property resource create/update to allow custom value // params. See `normalizeArguments()`. - return Object.assign( - Resource.factoryFor(Property, thngPath + path).call(this, id), - { - create (...args) { - return Resource.prototype.create - .call(this, ...normalizeArguments(...args)) - }, - update (...args) { - return Resource.prototype.update - .call(this, ...normalizeArguments(...args)) - } + return Object.assign(Resource.factoryFor(Property, thngPath + path).call(this, id), { + create (...args) { + return Resource.prototype.create.call(this, ...normalizeArguments(...args)) + }, + update (...args) { + return Resource.prototype.update.call(this, ...normalizeArguments(...args)) } - ) + }) } } } @@ -66,11 +61,7 @@ export default class Property extends Entity { * }) */ function normalizeArguments (data, ...rest) { - if ( - isString(data) || - typeof data === 'number' || - typeof data === 'boolean' - ) { + if (isString(data) || typeof data === 'number' || typeof data === 'boolean') { // Convert simple property values to API format. data = [{ value: data }] } else if (isPlainObject(data)) { @@ -79,7 +70,7 @@ function normalizeArguments (data, ...rest) { data = [data] } else { // Update multiple properties creating an object for each key-value pair. - data = Object.entries(data).map(val => ({ + data = Object.entries(data).map((val) => ({ key: val[0], value: val[1] })) diff --git a/src/entity/ReactorLog.js b/src/entity/ReactorLog.js index 8e06090..52f0ec1 100644 --- a/src/entity/ReactorLog.js +++ b/src/entity/ReactorLog.js @@ -24,14 +24,11 @@ export default class ReactorLog extends Entity { const appPath = this instanceof Scope ? this[symbols.path] : '' - return Object.assign( - Resource.factoryFor(ReactorLog, appPath + path).call(this, id), - { - create (...args) { - return createLogs.call(this, ...args) - } + return Object.assign(Resource.factoryFor(ReactorLog, appPath + path).call(this, id), { + create (...args) { + return createLogs.call(this, ...args) } - ) + }) } } } diff --git a/src/entity/ReactorSchedule.js b/src/entity/ReactorSchedule.js index 17ffb64..6cdec3a 100644 --- a/src/entity/ReactorSchedule.js +++ b/src/entity/ReactorSchedule.js @@ -16,8 +16,7 @@ export default class ReactorSchedule extends Entity { reactorSchedule (id) { const appPath = this instanceof Scope ? this[symbols.path] : '' - return Resource.factoryFor(ReactorSchedule, appPath + path) - .call(this, id) + return Resource.factoryFor(ReactorSchedule, appPath + path).call(this, id) } } } diff --git a/src/entity/ReactorScript.js b/src/entity/ReactorScript.js index 5a9f9c3..873d078 100644 --- a/src/entity/ReactorScript.js +++ b/src/entity/ReactorScript.js @@ -5,9 +5,7 @@ import { mixinResources } from '../util/mixin' import isString from 'lodash-es/isString' const path = '/reactor/script' -const ReactorScriptResources = mixinResources([ - Status -]) +const ReactorScriptResources = mixinResources([Status]) /** * Represents a ReactorScript entity object. @@ -23,8 +21,7 @@ export default class ReactorScript extends ReactorScriptResources(Entity) { throw new TypeError('There is no single resource for Reactor Scripts') } - return Resource.factoryFor(ReactorScript, path, ReactorScriptResources) - .call(this) + return Resource.factoryFor(ReactorScript, path, ReactorScriptResources).call(this) } } } diff --git a/src/entity/Redirection.js b/src/entity/Redirection.js index a04c7d3..bd5d9b9 100644 --- a/src/entity/Redirection.js +++ b/src/entity/Redirection.js @@ -1,4 +1,3 @@ -import isString from 'lodash-es/isString' import Entity from './Entity' import Resource from '../resource/Resource' import Scope from '../scope/Scope' @@ -32,14 +31,20 @@ export default class Redirection extends Entity { * @param {object} changes - Additional options. * @returns {Promise} API response. */ - const shortDomainRequest = async changes => - Resource.prototype._request.call(_this, Object.assign({ - apiUrl: `https://${shortDomain}`, - url: '/redirections', - headers: { - Accept: 'application/json' - } - }, changes)) + const shortDomainRequest = async (changes) => + Resource.prototype._request.call( + _this, + Object.assign( + { + apiUrl: `https://${shortDomain}`, + url: '/redirections', + headers: { + Accept: 'application/json' + } + }, + changes + ) + ) // Special case: use the shortDomain API instead of the /redirector API. return { diff --git a/src/entity/Role.js b/src/entity/Role.js index 96cf1f1..f197ff4 100644 --- a/src/entity/Role.js +++ b/src/entity/Role.js @@ -4,9 +4,7 @@ import Resource from '../resource/Resource' import { mixinResources } from '../util/mixin' const path = '/roles' -const RoleResources = mixinResources([ - Permission -]) +const RoleResources = mixinResources([Permission]) /** * Represents a Role entity object. diff --git a/src/entity/Rule.js b/src/entity/Rule.js index 5dfabeb..350cf2b 100644 --- a/src/entity/Rule.js +++ b/src/entity/Rule.js @@ -23,14 +23,11 @@ export default class Rule extends Entity { throw new TypeError('Rule name must be a string') } - return Object.assign( - Resource.factoryFor(Rule, path).call(this, ruleName), - { - run (...args) { - return Resource.prototype.create.call(this, ...args) - } + return Object.assign(Resource.factoryFor(Rule, path).call(this, ruleName), { + run (...args) { + return Resource.prototype.create.call(this, ...args) } - ) + }) } } } diff --git a/src/entity/ShipmentNotice.js b/src/entity/ShipmentNotice.js index bd4f153..24d7c12 100644 --- a/src/entity/ShipmentNotice.js +++ b/src/entity/ShipmentNotice.js @@ -5,9 +5,7 @@ import Resource from '../resource/Resource' const path = '/shipmentNotices' -const ShipmentNoticeResources = mixinResources([ - Container -]) +const ShipmentNoticeResources = mixinResources([Container]) /** * Represents a ShipmentNotice entity object. diff --git a/src/entity/Thng.js b/src/entity/Thng.js index 7df7a96..6069e5b 100644 --- a/src/entity/Thng.js +++ b/src/entity/Thng.js @@ -8,13 +8,7 @@ import Resource from '../resource/Resource' import { mixinResources } from '../util/mixin' const path = '/thngs' -const ThngResources = mixinResources([ - Property, - Action, - Location, - Redirection, - CommissionState -]) +const ThngResources = mixinResources([Property, Action, Location, Redirection, CommissionState]) /** * Represents a Thng entity object. diff --git a/src/evrythng.js b/src/evrythng.js index 574fca6..0a11cbd 100644 --- a/src/evrythng.js +++ b/src/evrythng.js @@ -6,6 +6,7 @@ export { default as alias } from './alias' export { default as use } from './use' // Scopes +export { default as AccessToken } from './scope/AccessToken' export { default as Operator } from './scope/Operator' export { default as ActionApp } from './scope/ActionApp' export { default as Application } from './scope/Application' diff --git a/src/resource/Resource.js b/src/resource/Resource.js index 47bc468..487514e 100644 --- a/src/resource/Resource.js +++ b/src/resource/Resource.js @@ -58,9 +58,7 @@ export default class Resource { newPath += `/${encodeURIComponent(id)}` } - const XResource = MixinNestedResources - ? MixinNestedResources(Resource) - : Resource + const XResource = MixinNestedResources ? MixinNestedResources(Resource) : Resource return new XResource(parentScope, newPath, type, id, typeName) } } @@ -145,8 +143,7 @@ export default class Resource { if (response.body) { // Full response, add deserialize method to deserialize the Response's // json body - response.deserialize = () => response.json() - .then(this.deserialize.bind(this)) + response.deserialize = () => response.json().then(this.deserialize.bind(this)) } else { // JSON response, base case. // Create new entity with updated resource derived from current. @@ -271,10 +268,14 @@ export default class Resource { * @returns {Promise} */ read (options, callback) { - return this._request({ - url: this.path, - method: 'get' - }, options, callback) + return this._request( + { + url: this.path, + method: 'get' + }, + options, + callback + ) } /** @@ -290,11 +291,15 @@ export default class Resource { throw new TypeError('Update method must have payload.') } - return this._request({ - url: this.path, - data, - method: 'put' - }, options, callback) + return this._request( + { + url: this.path, + data, + method: 'put' + }, + options, + callback + ) } /** @@ -305,10 +310,14 @@ export default class Resource { * @returns {Promise} */ ['delete'] (options, callback) { - return this._request({ - url: this.path, - method: 'delete' - }, options, callback) + return this._request( + { + url: this.path, + method: 'delete' + }, + options, + callback + ) } /** @@ -382,7 +391,9 @@ export default class Resource { */ async upsert (data, updateKey, allowPlural) { if (!updateKey || !(typeof updateKey === 'string' || typeof updateKey === 'object')) { - throw new Error('updateKey must be a \'name\' string or an object, eg: { shortId: \'a7ysf8hd\' }') + throw new Error( + "updateKey must be a 'name' string or an object, eg: { shortId: 'a7ysf8hd' }" + ) } const params = { filter: `name=${updateKey}` } @@ -394,7 +405,9 @@ export default class Resource { const found = await this.read({ params }) if (found.length > 1) { if (!allowPlural) { - throw new Error('More than one resource was found. Set \'allowPlural\' to \'true\' as third parameter to update the first returned.') + throw new Error( + "More than one resource was found. Set 'allowPlural' to 'true' as third parameter to update the first returned." + ) } } if (found.length) { @@ -414,7 +427,7 @@ export default class Resource { async find (updateKey) { const params = { filter: `name=${updateKey}` } if (typeof updateKey === 'object') { - const pairs = Object.entries(updateKey); + const pairs = Object.entries(updateKey) if (pairs.length > 1) { throw new Error('Only one key-value pair may be specified for find()') } @@ -500,14 +513,9 @@ export default class Resource { } // Merge options, priority to mandatory ones. - const options = Object.assign( - {}, - userOptions, - requestOptions, - { - apiKey: this.scope.apiKey - } - ) + const options = Object.assign({}, userOptions, requestOptions, { + apiKey: this.scope.apiKey + }) // Serialize Entity into JSON payload. if (options.body) { diff --git a/src/scope/AccessToken.js b/src/scope/AccessToken.js new file mode 100644 index 0000000..92f7dc7 --- /dev/null +++ b/src/scope/AccessToken.js @@ -0,0 +1,72 @@ +import Scope from './Scope' +import Account from '../entity/Account' +import ADIOrder from '../entity/ADIOrder' +import Product from '../entity/Product' +import Thng from '../entity/Thng' +import Collection from '../entity/Collection' +import Action from '../entity/Action' +import ActionType from '../entity/ActionType' +import Project from '../entity/Project' +import PurchaseOrder from '../entity/PurchaseOrder' +import Redirector from '../entity/Redirector' +import ShipmentNotice from '../entity/ShipmentNotice' +import Place from '../entity/Place' +import File from '../entity/File' +import AccessPolicy from '../entity/AccessPolicy' +import AccessTokenEntity from '../entity/AccessToken' +import Me from '../entity/Me' +import { mixinResources } from '../util/mixin' + +/** + * Mixin with all AccessToken resources. + * + * @mixin + */ +const AccessTokenResources = mixinResources([ + AccessPolicy, + AccessTokenEntity, + Account, + Action, + ActionType, + ADIOrder, + Collection, + File, + Me, + Place, + Product, + Project, + PurchaseOrder, + Redirector, + ShipmentNotice, + Thng +]) + +/** + * AccessToken is a Scope type for the v2 API with the potential to manage any account resources, depending on the API key's + * permissions. It represents an API access for a specific purpose, instead of a type of Actor, such as an Operator. + * + * @extends Scope + * @mixes AccessTokenResources + */ +export default class AccessToken extends AccessTokenResources(Scope) { + /** + * Creates an instance of AccessToken. + * + * @param {string} apiKey - API Key of scope + * @param {Object} [data={}] - Optional data + */ + constructor (apiKey, data = {}) { + super(apiKey, data) + + this.initPromise = super.readAccess() + } + + /** + * Read the access token's data asynchronously. + * + * @returns {Promise} + */ + init () { + return this.initPromise + } +} diff --git a/src/scope/ActionApp.js b/src/scope/ActionApp.js index f0abad2..2ad3338 100644 --- a/src/scope/ActionApp.js +++ b/src/scope/ActionApp.js @@ -1,6 +1,4 @@ import { mixinResources } from '../util/mixin' -import Action from '../entity/Action' -import api from '../api' import AppUser from '../entity/AppUser' import Scope from './Scope' import symbols from '../symbols' @@ -34,15 +32,16 @@ export default class ActionApp extends ApplicationAccess(Scope) { constructor (apiKey) { super(apiKey) - this.initPromise = super.readAccess() - .then(access => { + this.initPromise = super + .readAccess() + .then((access) => { this.id = access.actor.id this.project = access.project this[symbols.path] = this._getPath() }) .then(() => this.read()) .then(() => this._getAnonUser()) - .then(anonUser => { + .then((anonUser) => { this.anonUser = anonUser }) } @@ -86,14 +85,14 @@ export default class ActionApp extends ApplicationAccess(Scope) { try { await this.anonUser.actionType(type).read() } catch (e) { - console.error(e); + console.error(e) throw new Error('The action type was not found. Is it in project scope?') } } - const { thng, product, ...customFields } = data; + const { thng, product, ...customFields } = data if (thng && product) { - throw new Error('Either thng or product can be specified as target, not both'); + throw new Error('Either thng or product can be specified as target, not both') } const payload = { type, customFields } @@ -118,7 +117,7 @@ export default class ActionApp extends ApplicationAccess(Scope) { throw new Error('Anonymous user not yet prepared. Use actionApp.init() to wait for this.') } - return this.anonUser; + return this.anonUser } // PRIVATE diff --git a/src/scope/Application.js b/src/scope/Application.js index 7cc00b6..82d3d65 100644 --- a/src/scope/Application.js +++ b/src/scope/Application.js @@ -46,8 +46,9 @@ export default class Application extends ApplicationAccess(Scope) { constructor (apiKey, data = {}) { super(apiKey, data) - this.initPromise = super.readAccess() - .then(access => { + this.initPromise = super + .readAccess() + .then((access) => { this.id = access.actor.id this.project = access.project this[symbols.path] = this._getPath() diff --git a/src/scope/Device.js b/src/scope/Device.js index c257db3..159efe2 100644 --- a/src/scope/Device.js +++ b/src/scope/Device.js @@ -33,8 +33,9 @@ export default class Device extends DeviceAccess(Scope) { constructor (apiKey, data = {}) { super(apiKey, data) - this.initPromise = super.readAccess() - .then(access => { + this.initPromise = super + .readAccess() + .then((access) => { this.id = access.actor.id this[symbols.path] = this._getPath() }) diff --git a/src/scope/Operator.js b/src/scope/Operator.js index 64fa7db..41f1146 100644 --- a/src/scope/Operator.js +++ b/src/scope/Operator.js @@ -16,6 +16,9 @@ import User from '../entity/User' import Batch from '../entity/Batch' import Place from '../entity/Place' import File from '../entity/File' +import AccessPolicy from '../entity/AccessPolicy' +import AccessToken from '../entity/AccessToken' +import Me from '../entity/Me' import { mixinResources } from '../util/mixin' import symbols from '../symbols' @@ -25,23 +28,26 @@ import symbols from '../symbols' * @mixin */ const OperatorAccess = mixinResources([ - Account, // RU - ADIOrder, // CR - Product, // CRUD - Thng, // CRUD - Collection, // CRUD - Action, // CR - ActionType, // CRUD - Project, // CRUD - PurchaseOrder, // CRUD - Redirector, // RU + AccessPolicy, // CRUDL + AccessToken, // CL + Account, // LRU + Action, // CRLD + ActionType, // CRULD + ADIOrder, // CRL + Batch, // CRUD + Collection, // CRUDL + File, // CRUD + Me, // R + Place, // CRUDL + Product, // CRUDL + Project, // CRUDL + PurchaseOrder, // CRUDL + Redirector, // RUD Role, // CRUD Rule, - ShipmentNotice, // CRUD - User, // R - Batch, // CRUD - Place, // CRUD - File // CRUD + ShipmentNotice, // CRUDL + Thng, // CRUDL + User // R ]) /** @@ -61,8 +67,9 @@ export default class Operator extends OperatorAccess(Scope) { constructor (apiKey, data = {}) { super(apiKey, data) - this.initPromise = super.readAccess() - .then(access => { + this.initPromise = super + .readAccess() + .then((access) => { this.id = access.actor.id this[symbols.path] = this._getPath() }) diff --git a/src/scope/Scope.js b/src/scope/Scope.js index 9ce0d93..2f643a8 100644 --- a/src/scope/Scope.js +++ b/src/scope/Scope.js @@ -80,6 +80,6 @@ export default class Scope { * @private */ _request (options) { - return api(options).then(data => Object.assign(this, data)) + return api(options).then((data) => Object.assign(this, data)) } } diff --git a/src/scope/User.js b/src/scope/User.js index 011dafc..059c50f 100644 --- a/src/scope/User.js +++ b/src/scope/User.js @@ -49,8 +49,9 @@ export default class User extends AppUser(Scope) { constructor (apiKey, data = {}) { super(apiKey, data) - this.initPromise = super.readAccess() - .then(access => { + this.initPromise = super + .readAccess() + .then((access) => { this.id = access.actor.id this[symbols.path] = this._getPath() }) diff --git a/src/settings.js b/src/settings.js index f1b937c..ee74182 100644 --- a/src/settings.js +++ b/src/settings.js @@ -31,7 +31,7 @@ * @type {Settings} */ const defaultSettings = { - apiUrl: 'https://api.evrythng.com', + apiUrl: 'https://api.evrythng.io/v2', apiKey: '', fullResponse: false, geolocation: true, @@ -40,7 +40,9 @@ const defaultSettings = { 'content-type': 'application/json' }, interceptors: [], - defaultShortDomain: 'tn.gg' + defaultShortDomain: 'tn.gg', + apiVersion: 2, + region: 'us' } // Initialize settings with defaults. diff --git a/src/setup.js b/src/setup.js index 3541c89..7be32ec 100644 --- a/src/setup.js +++ b/src/setup.js @@ -6,6 +6,37 @@ import settings from './settings' * @param {Object} customSettings - Custom settings * @returns {Object} new */ -export default function setup (customSettings) { - return Object.assign(settings, customSettings) + +/** Allowed API versions */ +const VERSIONS = [1, 2] +/** Allowed API regions */ +const REGIONS = ['us', 'eu'] +/** Map of API URLs by version and region */ +const API_MAP = { + 2: { + us: 'https://api.evrythng.io/v2', + eu: 'https://api.eu.evrythng.io/v2' + }, + 1: { + us: 'https://api.evrythng.com', + eu: 'https://api-eu.evrythng.com' + } +} + +export default function setup (newSettings = {}) { + const { apiUrl, apiVersion = 2, region = 'us' } = newSettings + + if (!VERSIONS.includes(apiVersion)) { + throw new Error(`Invalid apiVersion: ${apiVersion}. Choose from ${VERSIONS.join(', ')}`) + } + if (!REGIONS.includes(region)) { + throw new Error(`Invalid region: ${region}. Choose from ${REGIONS.join(', ')}`) + } + + // Set the API URL and region + newSettings.apiUrl = apiUrl || API_MAP[apiVersion][region] + newSettings.apiVersion = apiVersion + newSettings.region = region + + return Object.assign(settings, newSettings) } diff --git a/src/use.js b/src/use.js index 635890b..350ef5b 100644 --- a/src/use.js +++ b/src/use.js @@ -3,6 +3,7 @@ import Device from './scope/Device' import Operator from './scope/Operator' import TrustedApplication from './scope/TrustedApplication' import User from './scope/User' +import AccessToken from './scope/AccessToken' import Action from './entity/Action' import ActionType from './entity/ActionType' @@ -10,7 +11,7 @@ import ApplicationEntity from './entity/Application' import AppUser from './entity/AppUser' import Batch from './entity/Batch' import Collection from './entity/Collection' -import Entity from './entity/Entity'; +import Entity from './entity/Entity' import File from './entity/File' import Location from './entity/Location' import Permission from './entity/Permission' @@ -26,6 +27,20 @@ import Resource from './resource/Resource' import Role from './entity/Role' import Thng from './entity/Thng' import UserEntity from './entity/User' +import OperatorAccess from './entity/OperatorAccess' +import AccessPolicy from './entity/AccessPolicy' +import Me from './entity/Me' +import AccessTokens from './entity/AccessToken' +import Account from './entity/Account' +import Access from './entity/Access' +import ADIOrder from './entity/ADIOrder' +import ADIOrderEvent from './entity/ADIOrderEvent' +import CommissionState from './entity/CommissionState' +import Domain from './entity/Domain' +import PurchaseOrder from './entity/PurchaseOrder' +import ReactorLog from './entity/ReactorLog' +import ShipmentNotice from './entity/ShipmentNotice' +import ShortDomain from './entity/ShortDomain' /** * The items that the plugin may access and manipulate to install new functionality. @@ -34,6 +49,7 @@ import UserEntity from './entity/User' */ const API = { scopes: { + AccessToken, Operator, Application, TrustedApplication, @@ -41,25 +57,39 @@ const API = { Device }, entities: { + Access, + AccessPolicy, + AccessTokens, + Account, Action, ActionType, + ADIOrder, + ADIOrderEvent, Application: ApplicationEntity, AppUser, Batch, Collection, + CommissionState, + Domain, Entity, File, Location, + Me, + OperatorAccess, Permission, Place, Product, Project, Property, + PurchaseOrder, + ReactorLog, ReactorSchedule, ReactorScript, Redirection, Redirector, Role, + ShipmentNotice, + ShortDomain, Thng, User: UserEntity }, @@ -78,7 +108,7 @@ const API = { */ export default function use (plugin) { if (!plugin.install || typeof plugin.install !== 'function') { - throw new Error('Plugin must implement an \'install()\' method') + throw new Error("Plugin must implement an 'install()' method") } try { diff --git a/src/util/buildParams.js b/src/util/buildParams.js index c6523db..523c7fa 100644 --- a/src/util/buildParams.js +++ b/src/util/buildParams.js @@ -39,9 +39,7 @@ function buildParam (useEncoding) { * @returns {Function} */ function uriEncoder (useEncoding) { - return (value) => useEncoding - ? encodeURIComponent(value) - : escapeSpecials(value) + return (value) => (useEncoding ? encodeURIComponent(value) : escapeSpecials(value)) } /** diff --git a/src/util/callback.js b/src/util/callback.js index 32a4f6b..e8e63a1 100644 --- a/src/util/callback.js +++ b/src/util/callback.js @@ -5,7 +5,7 @@ * @returns {function} - Response handler function */ export function success (callback) { - return response => { + return (response) => { if (callback) callback(null, response) return response } @@ -18,11 +18,21 @@ export function success (callback) { * @returns {function} - Response handler function */ export function failure (callback) { - return err => { + return async (err) => { if (callback) callback(err) if (!err) { - throw new Error(`No error message available, err was: ${JSON.stringify(err)}`); + throw new Error(`No error message available, err was: ${JSON.stringify(err)}`) + } + + // If a Fetch API response + if (typeof err.ok !== 'undefined' && !err.ok) { + err = await err.text() + } + try { + err = JSON.parse(err) + } catch (e) { + // The error text is not JSON } // Throw a native Error to play nicer with error handling/retry libraries diff --git a/src/util/getCurrentPosition.js b/src/util/getCurrentPosition.js index f069fbe..6c4a730 100644 --- a/src/util/getCurrentPosition.js +++ b/src/util/getCurrentPosition.js @@ -1,5 +1,5 @@ // Maximum acceptable age of cached location from the browser -const maximumAge = 5 * 60 * 1000; +const maximumAge = 5 * 60 * 1000 /** * Get browser's current position from Geolocation API. @@ -21,7 +21,7 @@ export default function getCurrentPosition () { window.navigator.geolocation.getCurrentPosition( resolve, - err => reject(err), + (err) => reject(err), geolocationOptions ) }) diff --git a/src/util/mixin.js b/src/util/mixin.js index 2551de0..e2a8661 100644 --- a/src/util/mixin.js +++ b/src/util/mixin.js @@ -15,9 +15,9 @@ * @return {function(Scope)} */ export function mixinResources (entities) { - const resourceFactories = entities.map(e => e.resourceFactory()) + const resourceFactories = entities.map((e) => e.resourceFactory()) const accessResources = Object.assign({}, ...resourceFactories) - return Superclass => mixin(accessResources)(class extends Superclass {}) + return (Superclass) => mixin(accessResources)(class extends Superclass {}) } /** @@ -30,12 +30,10 @@ export function mixinResources (entities) { */ export default function mixin (behaviour, proto = true) { return (target) => { - for (let property of Reflect.ownKeys(behaviour)) { - Object.defineProperty( - proto ? target.prototype : target, - property, - { value: behaviour[property] } - ) + for (const property of Reflect.ownKeys(behaviour)) { + Object.defineProperty(proto ? target.prototype : target, property, { + value: behaviour[property] + }) } return target } diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..3d27232 --- /dev/null +++ b/test/README.md @@ -0,0 +1,17 @@ +# Presetup + +1. `npm i` +2. Run `npm run build` or `npm run build-dev` in the main `evrythng.js` repository. + +# Unit tests + +Run `npm run test:unit` + +# Integration tests + +Run `npm run test` + +# e2e + +1. Ensure you export all environment variables from `e2e/config.js` file +2. `npm run test:e2e` diff --git a/test/e2e/README.md b/test/e2e/README.md deleted file mode 100644 index cb4d305..0000000 --- a/test/e2e/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# e2e - -End to end tests for resource types as a variety of scope types. - - -## Usage - -First, ensure you have run `npm run build` in the main `evrythng.js` repository. - -1. `npm i` -2. `npm test` diff --git a/test/e2e/config.js b/test/e2e/config.js new file mode 100644 index 0000000..800e921 --- /dev/null +++ b/test/e2e/config.js @@ -0,0 +1,8 @@ +const { OPERATOR_API_KEY, OPERATOR_EMAIL, ACCOUNT_ID } = process.env +const config = { + OPERATOR_API_KEY, + OPERATOR_EMAIL, + ACCOUNT_ID +} + +module.exports = config diff --git a/test/e2e/dataGenerator.js b/test/e2e/dataGenerator.js new file mode 100644 index 0000000..26e347c --- /dev/null +++ b/test/e2e/dataGenerator.js @@ -0,0 +1,85 @@ +const Chance = require('chance') + +const chance = new Chance() + +const characterPool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +const numberPool = '1234567890' +const wordPool = `${characterPool}${numberPool}` +const permissionPool = [ + ['actions:create', 'places:list,read,update'], + ['products:list,read', 'purchaseOrders:list,read', 'thngs:read'] +] +const uiPermissionPool = [ + ['activation', 'counterfeit'], + ['authenticate', 'inventoryVisibility', 'inventoryTrace'] +] +const customFieldsPool = [ + { + brand: 12 + }, + { + size: 'EU 45' + } +] +const identifiersPool = [ + { + 'ean:': 'sadivnojs89324' + }, + { + 'random:': 'random' + } +] +const tagsPool = [ + ['roles', 'and', 'permissions'], + ['end', 'to'] +] + +const policyData = () => { + const uiPermissions = chance.pickone(uiPermissionPool) + return { + name: chance.string({ length: 30, pool: wordPool }), + permissions: chance.pickone(permissionPool), + uiPermissions, + homepage: chance.pickone(uiPermissions), + description: chance.string({ length: 100, pool: wordPool }), + customFields: chance.pickone(customFieldsPool), + identifiers: chance.pickone(identifiersPool), + tags: chance.pickone(tagsPool) + } +} + +const operatorAccessData = (operatorEmail, evtAccountAdminPolicy) => ({ + email: operatorEmail, + policies: [evtAccountAdminPolicy], + conditions: [], + customFields: {}, + identifiers: {}, + tags: [] +}) + +const accessTokenData = (evtManagedPolicy) => ({ + conditions: [], + name: chance.word({ length: 6 }), + description: chance.sentence({ words: 10 }), + policies: [evtManagedPolicy], + customFields: {}, + identifiers: chance.pickone(identifiersPool), + tags: chance.pickone(tagsPool) +}) + +const thngData = () => ({ + name: chance.word({ length: 3 }), + description: chance.sentence({ words: 10 }), + identifiers: {}, + customFields: {}, + properties: { + status: chance.word({ length: 5 }) + } +}) + +module.exports = { + policyData, + operatorAccessData, + accessTokenData, + thngData +} diff --git a/test/e2e/entity/accesses.spec.js b/test/e2e/entity/accesses.spec.js deleted file mode 100644 index 7903c8f..0000000 --- a/test/e2e/entity/accesses.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -module.exports = () => { - describe('Accesses', () => { - let operator - - before(async () => { - operator = getScope('operator') - }) - - it('should read all account accesses', async () => { - mockApi().get('/accounts/accountId/accesses') - .reply(200, [{ id: 'accessId' }]) - const res = await operator.sharedAccount('accountId').access().read() - - expect(res).to.have.length.gte(1) - }) - - it('should read a single account access', async () => { - mockApi().get('/accounts/accountId/accesses/accessId') - .reply(200, { id: 'accessId' }) - const res = await operator.sharedAccount('accountId').access('accessId').read() - - expect(res).to.be.an('object') - expect(res.id).to.be.a('string') - }) - - it('should update a single account access', async () => { - const payload = { role: 'admin' } - mockApi().put('/accounts/accountId/accesses/accessId', payload) - .reply(200, { id: 'accessId' }) - const res = await operator.sharedAccount('accountId').access('accessId').update(payload) - - expect(res).to.be.an('object') - expect(res.id).to.equal('accessId') - }) - }) -} diff --git a/test/e2e/entity/accountRedirector.spec.js b/test/e2e/entity/accountRedirector.spec.js deleted file mode 100644 index a8ab849..0000000 --- a/test/e2e/entity/accountRedirector.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -module.exports = () => { - describe('Account Redirector', () => { - let operator - - before(() => { - operator = getScope('operator') - }) - - it('should read the account Redirector', async () => { - mockApi().get('/redirector') - .reply(200, { rules: [] }) - const res = await operator.redirector().read() - - expect(res).to.be.an('object') - expect(res.rules).to.be.an('array') - }) - - it('should update the account redirector', async () => { - const payload = { - rules: [{ match: 'thng.name=test' }] - } - mockApi().put('/redirector', payload) - .reply(200, payload) - const res = await operator.redirector().update(payload) - - expect(res.rules).to.deep.equal(payload.rules) - }) - }) -} diff --git a/test/e2e/entity/applicationRedirector.spec.js b/test/e2e/entity/applicationRedirector.spec.js deleted file mode 100644 index 76c070c..0000000 --- a/test/e2e/entity/applicationRedirector.spec.js +++ /dev/null @@ -1,36 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -module.exports = () => { - describe('Application Redirector', () => { - let operator - - before(async () => { - operator = getScope('operator') - }) - - it('should read the application Redirector', async () => { - mockApi().get('/projects/projectId/applications/applicationId/redirector') - .reply(200, { rules: [] }) - const res = await operator.project('projectId').application('applicationId') - .redirector() - .read() - - expect(res).to.be.an('object') - expect(res.rules).to.be.an('array') - }) - - it('should update the application redirector', async () => { - const payload = { - rules: [{ match: 'thng.name=test' }] - } - mockApi().put('/projects/projectId/applications/applicationId/redirector') - .reply(200, payload) - const res = await operator.project('projectId').application('applicationId') - .redirector() - .update(payload) - - expect(res.rules).to.deep.equal(payload.rules) - }) - }) -} diff --git a/test/e2e/entity/batches.spec.js b/test/e2e/entity/batches.spec.js deleted file mode 100644 index c688e5e..0000000 --- a/test/e2e/entity/batches.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -module.exports = () => { - describe('Batches', () => { - let operator - - before(() => { - operator = getScope('operator') - }) - - it('should create a batch', async () => { - const payload = { name: 'Test Batch' } - mockApi().post('/batches') - .reply(201, payload) - const res = await operator.batch().create(payload) - - expect(res).to.be.an('object') - expect(res.name).to.equal(payload.name) - }) - - it('should read all batches', async () => { - mockApi().get('/batches') - .reply(200, [{ id: 'batchId' }]) - const res = await operator.batch().read() - - expect(res).to.be.an('array') - expect(res).to.have.length.gte(1) - }) - - it('should read a batch', async () => { - mockApi().get('/batches/batchId') - .reply(200, { id: 'batchId' }) - const res = await operator.batch('batchId').read() - - expect(res).to.be.an('object') - expect(res.id).to.equal('batchId') - }) - - it('should update a batch', async () => { - const payload = { tags: ['updated'] } - mockApi().put('/batches/batchId', payload) - .reply(200, payload) - const res = await operator.batch('batchId').update(payload) - - expect(res).to.be.an('object') - expect(res.tags).to.deep.equal(payload.tags) - }) - - it('should delete a batch', async () => { - mockApi().delete('/batches/batchId') - .reply(200) - await operator.batch('batchId').delete() - }) - }) -} diff --git a/test/e2e/entity/files.spec.js b/test/e2e/entity/files.spec.js deleted file mode 100644 index 6e5336d..0000000 --- a/test/e2e/entity/files.spec.js +++ /dev/null @@ -1,69 +0,0 @@ -const { expect } = require('chai') -const nock = require('nock') -const { getScope, mockApi } = require('../util') - -module.exports = () => { - describe('Files', () => { - let operator - - before(() => { - operator = getScope('operator') - }) - - it('should create a file', async () => { - const payload = { name: 'TestFile.txt' } - mockApi().post('/files', payload) - .reply(201, payload) - const res = await operator.file().create(payload) - - expect(res).to.be.an('object') - expect(res.name).to.equal(payload.name) - }) - - it('should read all files', async () => { - mockApi().get('/files') - .reply(200, [{ id: 'fileId' }]) - const res = await operator.file().read() - - expect(res).to.be.an('array') - expect(res).to.have.length.gte(1) - }) - - it('should read a file', async () => { - mockApi().get('/files/fileId') - .reply(200, { id: 'fileId' }) - const res = await operator.file('fileId').read() - - expect(res).to.be.an('object') - expect(res.id).to.equal('fileId') - }) - - it('should upload file content - text', async function () { - this.timeout(10000); - - const fileData = 'This is example text file content' - - mockApi().get('/files/fileId') - .reply(200, { id: 'fileId', uploadUrl: 'https://s3.amazonaws.com' }) - await operator.file('fileId').upload(fileData) - - mockApi().get('/files/fileId') - .reply(200, { contentUrl: 'https://s3.amazonaws.com/upload' }) - const resource = await operator.file('fileId').read() - nock('https://s3.amazonaws.com') - .get('/upload') - .reply(200, fileData) - const readData = await fetch(resource.contentUrl).then(res => res.text()) - - expect(readData).to.equal(fileData) - }) - - it('should upload file content - image data') - - it('should delete a file', async () => { - mockApi().delete('/files/fileId') - .reply(200) - await operator.file('fileId').delete() - }) - }) -} diff --git a/test/e2e/entity/secretKey.spec.js b/test/e2e/entity/secretKey.spec.js deleted file mode 100644 index 81bf7ea..0000000 --- a/test/e2e/entity/secretKey.spec.js +++ /dev/null @@ -1,23 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -module.exports = () => { - describe('Secret Key', () => { - let operator - - before(async () => { - operator = getScope('operator') - }) - - it('should read an application\'s secret key', async () => { - mockApi().get('/projects/projectId/applications/applicationId/secretKey') - .reply(200, { secretApiKey: 'secretApiKey' }) - const res = await operator.project('projectId').application('applicationId') - .secretKey() - .read() - - expect(res).to.be.an('object') - expect(res.secretApiKey).to.be.a('string') - }) - }) -} diff --git a/test/e2e/entity/shipmentNotice.spec.js b/test/e2e/entity/shipmentNotice.spec.js deleted file mode 100644 index e2c6982..0000000 --- a/test/e2e/entity/shipmentNotice.spec.js +++ /dev/null @@ -1,132 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -const noticePayload = { - asnId: '2343689643', - version: '1', - issueDate: '2019-06-19T16:39:57-08:00', - transportation: 'Expedited Freight', - parties: [{ - id: 'gs1:414:01251', - type: 'ship-from' - }, { - name: 'The Landmark, Shop No. G14', - type: 'ship-to', - address: { - street: '113-114, Central', - city: 'Hong Kong' - } - }], - tags: ['ongoing'] -} - -const containerPayload = { - containerId: '82347927', - transportationType: 'Pallet', - products: [{ - id: 'gs1:01:000000001234', - quantity: 562, - unitOfMeasure: 'piece' - }], - tags: ['important'] -} - -module.exports = () => { - describe('Shipment Notices', () => { - let operator - - before(() => { - operator = getScope('operator') - }) - - it('should create a shipment notice', async () => { - mockApi() - .post('/shipmentNotices', noticePayload) - .reply(201, noticePayload) - - const res = await operator.shipmentNotice().create(noticePayload) - - expect(res).to.be.an('object') - expect(res.asnId).to.equal(noticePayload.asnId) - }) - - it('should read a shipment notice', async () => { - mockApi() - .get('/shipmentNotices/shipmentNoticeId') - .reply(200, noticePayload) - - const res = await operator.shipmentNotice('shipmentNoticeId').read() - - expect(res).to.be.an('object') - expect(res.asnId).to.equal(noticePayload.asnId) - }) - - it('should update a shipment notice', async () => { - mockApi() - .put('/shipmentNotices/shipmentNoticeId') - .reply(200, noticePayload) - - const res = await operator.shipmentNotice('shipmentNoticeId').update(noticePayload) - - expect(res).to.be.an('object') - expect(res.tags).to.deep.equal(noticePayload.tags) - }) - - it('should delete a shipment notice', async () => { - mockApi() - .delete('/shipmentNotices/shipmentNoticeId') - .reply(204) - - await operator.shipmentNotice('shipmentNoticeId').delete() - }) - }) - - describe('Shipment Notice Containers', () => { - let operator - - before(() => { - operator = getScope('operator') - }) - - it('should create a shipment notice container', async () => { - mockApi() - .post('/shipmentNotices/containers', containerPayload) - .reply(201, containerPayload) - - const res = await operator.shipmentNotice().container().create(containerPayload) - - expect(res).to.be.an('object') - expect(res.containerId).to.equal(containerPayload.containerId) - }) - - it('should read a shipment notice container', async () => { - mockApi() - .get('/shipmentNotices/containers/containerId') - .reply(200, containerPayload) - - const res = await operator.shipmentNotice().container('containerId').read() - - expect(res).to.be.an('object') - expect(res.containerId).to.equal(containerPayload.containerId) - }) - - it('should update a shipment notice container', async () => { - mockApi() - .put('/shipmentNotices/containers/containerId') - .reply(200, containerPayload) - - const res = await operator.shipmentNotice().container('containerId').update(containerPayload) - - expect(res).to.be.an('object') - expect(res.tags).to.deep.equal(containerPayload.tags) - }) - - it('should delete a shipment notice container', async () => { - mockApi() - .delete('/shipmentNotices/containers/containerId') - .reply(204) - - await operator.shipmentNotice().container('containerId').delete() - }) - }) -} diff --git a/test/e2e/index.spec.js b/test/e2e/index.spec.js index 8702e0f..ec10434 100644 --- a/test/e2e/index.spec.js +++ b/test/e2e/index.spec.js @@ -1,102 +1,100 @@ -const { resources, getScope, setup, teardown } = require('./util') -const evrythng = require('../../') - -evrythng.setup({ geolocation: false }) - -process.on('unhandledRejection', console.error) - -describe('evrythng.js', () => { - before(setup) - - describe('as Application', () => { - require('./entity/user.spec')('application') - }) - - describe('as anonymous Application User', () => { - require('./entity/actions.spec')('anonUser') - require('./entity/actionTypes.spec')('anonUser') - require('./entity/collections.spec')('anonUser') - require('./entity/commissionState.spec')('anonUser') - require('./entity/places.spec')('anonUser') - require('./entity/products.spec')('anonUser') - require('./entity/properties.spec')('anonUser', 'product') - require('./entity/properties.spec')('anonUser', 'thng') - require('./entity/purchaseOrders.spec')('anonUser') - require('./entity/roles.spec')('anonUser') - require('./entity/thngs.spec')('anonUser') - }) - - describe('as Trusted Application', () => { - require('./entity/actions.spec')('trustedApplication') - require('./entity/actionTypes.spec')('trustedApplication') - require('./entity/collections.spec')('trustedApplication') - require('./entity/commissionState.spec')('trustedApplication') - require('./entity/places.spec')('trustedApplication') - require('./entity/products.spec')('trustedApplication') - require('./entity/properties.spec')('trustedApplication', 'product') - require('./entity/properties.spec')('trustedApplication', 'thng') - require('./entity/purchaseOrders.spec')('trustedApplication') - require('./entity/thngs.spec')('trustedApplication') - }) +const evrythng = require('../../dist/evrythng.node') +const { assert } = require('chai') +const { OPERATOR_API_KEY, OPERATOR_EMAIL, ACCOUNT_ID } = require('./config') +const { policyData, operatorAccessData, accessTokenData, thngData } = require('./dataGenerator') +describe('e2e tests for apiVersion=2', () => { describe('as Operator', () => { - require('./entity/accesses.spec')() - require('./entity/accountRedirector.spec')() - require('./entity/accounts.spec')() - require('./entity/actions.spec')('operator') - require('./entity/actionTypes.spec')('operator') - require('./entity/adiOrders.spec')() - require('./entity/applicationRedirector.spec')() - require('./entity/applications.spec')('operator') - require('./entity/batches.spec')() - require('./entity/commissionState.spec')('operator') - require('./entity/collections.spec')('operator') - require('./entity/domains.spec')() - require('./entity/files.spec')('operator') - require('./entity/locations.spec')('operator') - require('./entity/permissions.spec')('operator') - require('./entity/permissions.spec')('userInApp') - require('./entity/places.spec')('operator') - require('./entity/products.spec')('operator') - require('./entity/projects.spec')() - require('./entity/properties.spec')('operator', 'product') - require('./entity/properties.spec')('operator', 'thng') - require('./entity/purchaseOrders.spec')('operator') - require('./entity/reactor.spec')('operator') - require('./entity/redirection.spec')('operator', 'product') - require('./entity/redirection.spec')('operator', 'thng') - require('./entity/roles.spec')('operator') - require('./entity/rules.spec')() - require('./entity/secretKey.spec')() - require('./entity/shipmentNotice.spec')() - require('./entity/shortDomains.spec')() - require('./entity/tasks.spec')() - require('./entity/thngs.spec')('operator') - require('./entity/user.spec')('operator') + let operator + before(async () => { + evrythng.setup({ apiKey: OPERATOR_API_KEY }) + operator = new evrythng.Operator(OPERATOR_API_KEY) + await operator.init() + }) + it('should create and read access policy', async () => { + const data = await policyData() + const policy = await operator.accessPolicy().create(data) + assert.deepInclude(policy, data, 'Incorrect policy is created') + const readPolicy = await operator.accessPolicy(policy.id).read() + assert.equal(readPolicy.id, policy.id, `Can not read policy by ${policy.id}`) + }) + it('should create operator access and read it', async function test () { + this.timeout(10000) + const findAdminAccountPolicy = await operator + .accessPolicy() + .setFilter('name=evt:accountAdmin') + .read() + const adminAccountPolicy = findAdminAccountPolicy[0].id + const operatorData = await operatorAccessData(OPERATOR_EMAIL, adminAccountPolicy) + const createdOperarorAccess = await operator + .sharedAccount(ACCOUNT_ID) + .operatorAccess() + .create(operatorData) + assert.deepInclude( + createdOperarorAccess, + operatorData, + 'Incorrect operatorAccess is created' + ) + const readOperatorAccess = await operator + .sharedAccount(ACCOUNT_ID) + .operatorAccess(createdOperarorAccess.id) + .read() + assert.equal( + readOperatorAccess.id, + createdOperarorAccess.id, + `Can not read operator access by ${createdOperarorAccess.id}` + ) + }) + it('should get your own access', async function () { + const findAdminAccountPolicy = await operator + .accessPolicy() + .setFilter('name=evt:accountAdmin') + .read() + const adminAccountPolicy = findAdminAccountPolicy[0].id + const myAccess = await operator.me().read() + assert.equal( + myAccess.permissions, + adminAccountPolicy.permissions, + 'Cannot get my own access' + ) + }) }) + describe('as AccessToken', () => { + let accessToken, adminAccountPolicy + before(async function () { + this.timeout(10000) + evrythng.setup({ apiKey: OPERATOR_API_KEY }) + const operator = new evrythng.Operator(OPERATOR_API_KEY) - describe('as Device', () => { - require('./scope/device.spec')() - }) + const findAdminAccountPolicy = await operator + .accessPolicy() + .setFilter('name=evt:accountAdmin') + .read() + adminAccountPolicy = findAdminAccountPolicy[0].id - describe('as ActionApp', () => { - require('./scope/actionApp.spec')() - }) - - describe('as Operator', () => { - require('./scope/operator.spec')() - }) + const data = accessTokenData(adminAccountPolicy) + const createdAccessToken = await operator.accessToken().create(data) - describe('Misc', () => { - require('./misc/alias.spec')() - require('./misc/api.spec')('operator') - require('./misc/find.spec')() - require('./misc/pages.spec')('operator') - require('./misc/paramSetters.spec')() - require('./misc/rescope.spec')() - require('./misc/stream.spec')() - require('./misc/streamPages.spec')() - require('./misc/upsert.spec')() - require('./misc/use.spec')() + const accessTokenApiKey = createdAccessToken.apiKey + accessToken = new evrythng.AccessToken(accessTokenApiKey) + await accessToken.init() + }) + it('should create access policy', async () => { + const data = await policyData() + const policy = await accessToken.accessPolicy().create(data) + assert.deepInclude(policy, data, 'Incorrect policy is created') + const readPolicy = await accessToken.accessPolicy(policy.id).read() + assert.equal(readPolicy.id, policy.id, `Can not read policy by ${policy.id}`) + }) + it('should create thng', async () => { + const data = thngData() + const thng = await accessToken.thng().create(data) + assert.deepInclude(thng, data, 'Can not create thng') + }) + it('should create access token', async () => { + const data = accessTokenData(adminAccountPolicy) + const createdAccessToken = await accessToken.accessToken().create(data) + assert.deepInclude(createdAccessToken, data, 'Can not create access token') + }) }) }) diff --git a/test/e2e/misc/alias.spec.js b/test/e2e/misc/alias.spec.js deleted file mode 100644 index 49c9645..0000000 --- a/test/e2e/misc/alias.spec.js +++ /dev/null @@ -1,63 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') -const evrythng = require('../../../') - -module.exports = () => { - describe('alias', () => { - let operator - - before(() => { - operator = getScope('operator') - }) - - it('should alias \'product\' to \'sku\' for Operator scope', async () => { - evrythng.alias({ product: 'sku' }, 'Operator') - - expect(operator.sku).to.be.a('function') - }) - - it('should use the alias to read all \'sku\'s', async () => { - mockApi().get('/products') - .reply(200, [{ id: 'productId' }]) - const skus = await operator.sku().read() - - expect(skus).to.be.an('array') - expect(skus).to.have.length.gte(0) - }) - - it('should use the alias to create a \'sku\'', async () => { - const payload = { name: 'Example SKU' } - mockApi().post('/products', payload) - .reply(200, { id: 'productId' }) - const res = await operator.sku().create(payload) - - expect(res).to.be.an('object') - expect(res.id).to.be.a('string') - }) - - it('should use the alias to read a \'sku\'', async () => { - mockApi().get('/products/productId') - .reply(200, { id: 'productId' }) - const res = await operator.sku('productId').read() - - expect(res).to.be.an('object') - expect(res.id).to.equal('productId') - }) - - it('should use the alias to update a \'sku\'', async () => { - const payload = { tags: ['updated'] } - mockApi().put('/products/productId', payload) - .reply(200, { id: 'productId' }) - const res = await operator.sku('productId').update(payload) - - expect(res).to.be.an('object') - expect(res.id).to.equal('productId') - }) - - it('should use the alias to delete a \'sku\'', async () => { - mockApi().delete('/products/productId') - .reply(200) - await operator.sku('productId').delete() - }) - }) -} diff --git a/test/e2e/misc/pages.spec.js b/test/e2e/misc/pages.spec.js deleted file mode 100644 index b466f43..0000000 --- a/test/e2e/misc/pages.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -module.exports = (scopeType) => { - describe('pages', () => { - let scope - - before(async () => { - scope = getScope(scopeType) - }) - - it('should read thngs through an async iterator', async () => { - const params = { perPage: 2 } - const it = scope.thng().pages({ params }) - mockApi().get('/thngs?perPage=2') - .reply( - 200, - [{ name: 'Thng 1' }, { name: 'Thng 2' }], - { - link: '; rel="next"' - } - ) - let page = await it.next() - - expect(page.value.length).to.equal(2) - expect(page.done).to.equal(false) - - mockApi().get('/thngs?perPage=2&sortOrder=DESCENDING&nextPageToken=U7hXyw5DVQ8QT7fYsbyEpdAp') - .reply( - 200, - [{ name: 'Thng 3' }, { name: 'Thng 4' }], - { - link: '; rel="next"' - } - ) - page = await it.next() - - expect(page.value.length).to.equal(2) - expect(page.done).to.equal(false) - }) - }) -} diff --git a/test/e2e/misc/stream.spec.js b/test/e2e/misc/stream.spec.js deleted file mode 100644 index f5c3cda..0000000 --- a/test/e2e/misc/stream.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -module.exports = () => { - describe('stream', () => { - let operator - - before(async () => { - operator = getScope('operator') - }) - - it('should stream Thngs once at a time', (done) => { - mockApi().get('/thngs') - .reply( - 200, - [{ name: 'Thng 1' }, { name: 'Thng 2' }], - { - link: '; rel="next"' - } - ) - mockApi().get('/thngs?perPage=30&sortOrder=DESCENDING&nextPageToken=U7hXyw5DVQ8QT7fYsbyEpdAp') - .reply( - 200, - [{ name: 'Thng 3' }, { name: 'Thng 4' }], - { - link: '; rel="next"' - } - ) - const cb = (item, index) => { - expect(item.name).to.be.a('string') - expect(index).to.be.a('number') - - if (index === 2) { - done() - return true - } - } - - operator.thng().stream(cb) - }) - }) -} diff --git a/test/e2e/misc/streamPages.spec.js b/test/e2e/misc/streamPages.spec.js deleted file mode 100644 index 202d8c9..0000000 --- a/test/e2e/misc/streamPages.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -const { expect } = require('chai') -const { getScope, mockApi } = require('../util') - -module.exports = () => { - describe('streamPages', () => { - let operator - - before(async () => { - operator = getScope('operator') - }) - - it('should stream pages of Thngs', (done) => { - mockApi().get('/thngs') - .reply( - 200, - [{ name: 'Thng 1' }, { name: 'Thng 2' }], - { - link: '; rel="next"' - } - ) - mockApi().get('/thngs?perPage=30&sortOrder=DESCENDING&nextPageToken=U7hXyw5DVQ8QT7fYsbyEpdAp') - .reply( - 200, - [{ name: 'Thng 3' }, { name: 'Thng 4' }], - { - link: '; rel="next"' - } - ) - const eachPageCb = (page, totalSoFar) => { - expect(page.length).to.equal(2) - - const [item] = page; - expect(item.name).to.be.a('string') - - if (totalSoFar === 2) { - done() - return true - } - } - - operator.thng().streamPages(eachPageCb) - }) - }) -} diff --git a/test/e2e/util.js b/test/e2e/util.js deleted file mode 100644 index ec6903d..0000000 --- a/test/e2e/util.js +++ /dev/null @@ -1,99 +0,0 @@ -const { Operator, Application, TrustedApplication, Device, api } = require('../..') -const nock = require('nock') - -const OPERATOR_API_KEY = 'OPERATOR_API_KEY' -const API_URL = 'https://api.evrythng.com' - -let scopes = {} -let resources = {} - -/** - * Mock an API response with nock. - * - * @param {string} [apiUrl] - Override API URL from the default. - * @returns {object} nock mock. - */ -const mockApi = (apiUrl = API_URL) => nock(apiUrl) - -/** - * Initialise reusable entities in the specified Platform account. - */ -const setup = async () => { - mockApi().get('/access').reply(200, { actor: { id: 'operatorId' } }) - mockApi().get('/operators/operatorId').reply(200, { - id: 'operatorId', - createdAt: 1471862430968, - updatedAt: 1607002260749, - email: 'test.user@evrythng.com', - firstName: 'Test', - lastName: 'User', - }) - const operator = new Operator(OPERATOR_API_KEY) - await operator.init() - - const projectPayload = { name: 'Test Project' } - mockApi().post('/projects', projectPayload) - .reply(201, { name: 'Test Project', id: 'projectId' }) - const appProject = await operator.project().create(projectPayload) - - const appPayload = { name: 'Test App', socialNetworks: {} } - mockApi().post('/projects/projectId/applications', appPayload) - .reply(201, { - name: 'Test App', - socialNetworks: {}, - id: 'applicationId', - appApiKey: 'appApiKey' - }) - const appResource = await operator.project(appProject.id).application() - .create(appPayload) - mockApi().get('/access').reply(200, { actor: { id: 'applicationId' } }) - mockApi().get('/applications/me').reply(200, { id: 'applicationId' }) - const application = new Application(appResource.appApiKey) - - mockApi().get('/projects/projectId/applications/applicationId/secretKey') - .reply(200, { secretApiKey: 'secretApiKey' }) - const { secretApiKey } = await api({ - url: '/projects/projectId/applications/applicationId/secretKey', - apiKey: operator.apiKey - }) - mockApi().get('/access').reply(200, { actor: { id: 'applicationId' } }) - mockApi().get('/applications/me').reply(200, { id: 'applicationId' }) - const trustedApplication = new TrustedApplication(secretApiKey) - - mockApi().post('/auth/evrythng/users?anonymous=true') - .reply(201, { id: 'evrythngUser', evrythngApiKey: 'evrythngApiKey' }) - mockApi().get('/access').reply(200, { actor: { id: 'evrythngUser' } }) - mockApi().get('/users/evrythngUser').reply(200, { id: 'evrythngUser' }) - const anonUser = await application.appUser().create({ anonymous: true }) - - mockApi().post('/thngs').reply(201, { id: 'deviceThngId' }) - const deviceThng = await operator.thng().create({ name: 'Test Device' }) - mockApi().post('/auth/evrythng/thngs', { thngId: 'deviceThngId' }) - .reply(201, { thngId: 'deviceThngId', thngApiKey: 'thngApiKey'}) - const { thngApiKey } = await api({ - url: '/auth/evrythng/thngs', - method: 'post', - apiKey: operator.apiKey, - data: { thngId: deviceThng.id } - }) - mockApi().get('/access').reply(200, { actor: { id: 'deviceThngId' } }) - mockApi().get('/thngs/deviceThngId').reply(200, { id: 'deviceThngId' }) - const device = new Device(thngApiKey) - - scopes = { - operator, - application, - trustedApplication, - anonUser, - device - } -} - -const getScope = type => scopes[type] - -module.exports = { - resources, - setup, - getScope, - mockApi -} diff --git a/test/helpers/apiMock.js b/test/helpers/apiMock.js index b48d902..5f962f8 100644 --- a/test/helpers/apiMock.js +++ b/test/helpers/apiMock.js @@ -30,7 +30,7 @@ export default function mockApi () { * * @param {Number} time - Delay time in milliseconds */ -const delay = time => new Promise(resolve => setTimeout(resolve, time)) +const delay = (time) => new Promise((resolve) => setTimeout(resolve, time)) /** * Init API mock as a whole. @@ -63,14 +63,8 @@ function prepare () { fetchMock.put(apiUrl(paths.application), responses.application.one) // Validate user - fetchMock.post( - apiUrl(`${paths.dummy}/${paths.usersAccessValidate}`), - responses.ok - ) - fetchMock.post( - apiUrl(`${paths.usersAccess}/${paths.usersAccessValidate}`), - responses.ok - ) + fetchMock.post(apiUrl(`${paths.dummy}/${paths.usersAccessValidate}`), responses.ok) + fetchMock.post(apiUrl(`${paths.usersAccess}/${paths.usersAccessValidate}`), responses.ok) } /** @@ -90,8 +84,7 @@ function tearDown (done) { * @return {Object} - Response */ function validAccess (opts) { - if (opts.headers.authorization === apiKey || - opts.headers.authorization === operatorApiKey) { + if (opts.headers.authorization === apiKey || opts.headers.authorization === operatorApiKey) { return responses.access.operator } else if (opts.headers.authorization === appApiKey) { return responses.access.application diff --git a/test/helpers/data.js b/test/helpers/data.js index ff4eb73..39d8f7b 100644 --- a/test/helpers/data.js +++ b/test/helpers/data.js @@ -5,7 +5,7 @@ export const appApiKey = 'appApiKey' export const optionsTemplate = { fullResponse: true, headers: { - 'Accept': '*/*' + Accept: '*/*' } } diff --git a/test/helpers/paths.js b/test/helpers/paths.js index 58b35b7..80d662e 100644 --- a/test/helpers/paths.js +++ b/test/helpers/paths.js @@ -1,9 +1,4 @@ -import { - operatorTemplate, - projectTemplate, - applicationTemplate, - userAccessTemplate -} from './data' +import { operatorTemplate, projectTemplate, applicationTemplate, userAccessTemplate } from './data' export default { testBase: 'https://api-test.evrythng.net', diff --git a/test/integration/entity/accessPolicies.spec.js b/test/integration/entity/accessPolicies.spec.js new file mode 100644 index 0000000..07119a5 --- /dev/null +++ b/test/integration/entity/accessPolicies.spec.js @@ -0,0 +1,66 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +const payload = { + name: 'custom policy', + description: 'Create custom policy', + policies: ['UPb7E6shapktcaaabfahfpds'], + conditions: [], + operator: 'UtnGTkaPDphkYDC9F2KHBtPp', + tags: ['operatorAccess'], + identifiers: {}, + customFields: {} +} + +module.exports = (scopeType, settings) => { + describe('AccessPolicies', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(settings.apiUrl) + }) + + if (settings.apiVersion == 2) { + it('should create access policy', async () => { + api.post('/accessPolicies', payload).reply(201, { id: 'accessPolicyId' }) + const res = await scope.accessPolicy().create(payload) + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + + it('should read access policy by id', async () => { + api.get('/accessPolicies/accessPolicyId').reply(200, { id: 'accessPolicyId' }) + const res = await scope.accessPolicy('accessPolicyId').read() + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + + it('should read all access policies', async () => { + api.get('/accessPolicies').reply(200, [payload]) + const res = await scope.accessPolicy().read() + + expect(res).to.be.an('array') + expect(res).to.have.length.gte(1) + }) + + it('should update access policy', async () => { + api.put('/accessPolicies/accessPolicyId', payload).reply(200, payload) + + const res = await scope.accessPolicy('accessPolicyId').update(payload) + + expect(res).to.be.an('object') + expect(res.name).to.deep.equal(payload.name) + }) + + it('should delete access policy', async () => { + api.delete('/accessPolicies/accessPolicyId').reply(204) + const res = await scope.accessPolicy('accessPolicyId').delete() + + expect(res).to.not.exist + }) + } + }) +} diff --git a/test/integration/entity/accessTokens.spec.js b/test/integration/entity/accessTokens.spec.js new file mode 100644 index 0000000..b94ca62 --- /dev/null +++ b/test/integration/entity/accessTokens.spec.js @@ -0,0 +1,65 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +const payload = { + name: 'accessTokens', + description: 'create accessTokens', + policies: ['UPb7E6shapktcaaabfahfpds'], + conditions: [], + tags: ['operatorAccess'], + identifiers: {}, + customFields: {} +} + +module.exports = (scopeType, settings) => { + describe('AccessTokens', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(settings.apiUrl) + }) + + if (settings.apiVersion == 2) { + it('should create access token', async () => { + api.post('/accessTokens', payload).reply(201, { id: 'accessTokenId' }) + const res = await scope.accessToken().create(payload) + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + + it('should read access token by id', async () => { + api.get('/accessTokens/accessTokenId').reply(200, { id: 'accessTokenId' }) + const res = await scope.accessToken('accessTokenId').read() + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + + it('should read all accessTokens', async () => { + api.get('/accessTokens').reply(200, [payload]) + const res = await scope.accessToken().read() + + expect(res).to.be.an('array') + expect(res).to.have.length.gte(1) + }) + + it('should update accessToken', async () => { + api.put('/accessTokens/accessTokenId', payload).reply(200, payload) + + const res = await scope.accessToken('accessTokenId').update(payload) + + expect(res).to.be.an('object') + expect(res.name).to.deep.equal(payload.name) + }) + + it('should delete access policy', async () => { + api.delete('/accessTokens/accessTokenId').reply(204) + const res = await scope.accessToken('accessTokenId').delete() + + expect(res).to.not.exist + }) + } + }) +} diff --git a/test/integration/entity/accesses.spec.js b/test/integration/entity/accesses.spec.js new file mode 100644 index 0000000..3efce7d --- /dev/null +++ b/test/integration/entity/accesses.spec.js @@ -0,0 +1,36 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, url) => { + describe('Accesses', () => { + let scope, api + before(() => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it('should read all account accesses', async () => { + api.get('/accounts/accountId/accesses').reply(200, [{ id: 'accessId' }]) + const res = await scope.sharedAccount('accountId').access().read() + + expect(res).to.have.length.gte(1) + }) + + it('should read a single account access', async () => { + api.get('/accounts/accountId/accesses/accessId').reply(200, { id: 'accessId' }) + const res = await scope.sharedAccount('accountId').access('accessId').read() + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + + it('should update a single account access', async () => { + const payload = { role: 'admin' } + api.put('/accounts/accountId/accesses/accessId', payload).reply(200, { id: 'accessId' }) + const res = await scope.sharedAccount('accountId').access('accessId').update(payload) + + expect(res).to.be.an('object') + expect(res.id).to.equal('accessId') + }) + }) +} diff --git a/test/integration/entity/accountRedirector.spec.js b/test/integration/entity/accountRedirector.spec.js new file mode 100644 index 0000000..17af0bc --- /dev/null +++ b/test/integration/entity/accountRedirector.spec.js @@ -0,0 +1,38 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, url) => { + describe('Account Redirector', () => { + let scope, api + + before(() => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it('should read the account Redirector', async () => { + api.get('/redirector').reply(200, { rules: [] }) + const res = await scope.redirector().read() + + expect(res).to.be.an('object') + expect(res.rules).to.be.an('array') + }) + + it('should update the account redirector', async () => { + const payload = { + rules: [{ match: 'thng.name=test' }] + } + api.put('/redirector', payload).reply(200, payload) + const res = await scope.redirector().update(payload) + + expect(res.rules).to.deep.equal(payload.rules) + }) + + it('should delete the account redirector', async () => { + api.delete('/redirector').reply(204) + const res = await scope.redirector().delete() + + expect(res).to.not.exist + }) + }) +} diff --git a/test/e2e/entity/accounts.spec.js b/test/integration/entity/accounts.spec.js similarity index 55% rename from test/e2e/entity/accounts.spec.js rename to test/integration/entity/accounts.spec.js index d9ebc2d..ac18cc5 100644 --- a/test/e2e/entity/accounts.spec.js +++ b/test/integration/entity/accounts.spec.js @@ -1,27 +1,26 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (scopeType, url) => { describe('Accounts', () => { - let operator + let scope, api before(() => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should read all shared accounts', async () => { - mockApi().get('/accounts') - .reply(200, [{ id: 'accountId' }]) - const res = await operator.sharedAccount().read() + api.get('/accounts').reply(200, [{ id: 'accountId' }]) + const res = await scope.sharedAccount().read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) }) it('should read a shared account', async () => { - mockApi().get('/accounts/accountId') - .reply(200, { id: 'accountId' }) - const res = await operator.sharedAccount('accountId').read() + api.get('/accounts/accountId').reply(200, { id: 'accountId' }) + const res = await scope.sharedAccount('accountId').read() expect(res).to.be.an('object') expect(res.id).to.be.a('string') @@ -29,9 +28,8 @@ module.exports = () => { it('should update a shared account', async () => { const payload = { customFields: { env: 'test' } } - mockApi().put('/accounts/accountId', payload) - .reply(200, payload) - const res = await operator.sharedAccount('accountId').update(payload) + api.put('/accounts/accountId', payload).reply(200, payload) + const res = await scope.sharedAccount('accountId').update(payload) expect(res).to.be.an('object') expect(res.customFields).to.deep.equal(payload.customFields) diff --git a/test/e2e/entity/actionTypes.spec.js b/test/integration/entity/actionTypes.spec.js similarity index 74% rename from test/e2e/entity/actionTypes.spec.js rename to test/integration/entity/actionTypes.spec.js index 4a5b36b..23f7202 100644 --- a/test/e2e/entity/actionTypes.spec.js +++ b/test/integration/entity/actionTypes.spec.js @@ -1,17 +1,17 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Action Types', () => { - let scope + let scope, api before(() => { scope = getScope(scopeType) + api = mockApi(url) }) it('should read all action types', async () => { - mockApi().get('/actions') - .reply(200, [{ name: '_CustomType' }]) + api.get('/actions').reply(200, [{ name: '_CustomType' }]) const res = await scope.actionType().read() expect(res).to.be.an('array') @@ -21,8 +21,7 @@ module.exports = (scopeType) => { if (['operator', 'trustedApp'].includes(scopeType)) { it('should create an action type', async () => { const payload = { name: '_CustomType' } - mockApi().post('/actions', payload) - .reply(201, { name: '_CustomType' }) + api.post('/actions', payload).reply(201, { name: '_CustomType' }) const res = await scope.actionType().create(payload) expect(res).to.be.an('object') @@ -30,8 +29,7 @@ module.exports = (scopeType) => { }) it('should read an action type', async () => { - mockApi().get('/actions?filter=name%3D_CustomType') - .reply(200, [{ name: '_CustomType' }]) + api.get('/actions?filter=name%3D_CustomType').reply(200, [{ name: '_CustomType' }]) const res = await scope.actionType('_CustomType').read() expect(res).to.be.an('object') @@ -42,8 +40,7 @@ module.exports = (scopeType) => { if (scopeType === 'operator') { it('should update an action type', async () => { const payload = { tags: ['updated'] } - mockApi().put('/actions/_CustomType', payload) - .reply(200, payload) + api.put('/actions/_CustomType', payload).reply(200, payload) const res = await scope.actionType('_CustomType').update(payload) expect(res).to.be.an('object') @@ -53,8 +50,7 @@ module.exports = (scopeType) => { if (['operator', 'trustedApp'].includes(scopeType)) { it('should delete an actionType', async () => { - mockApi().delete('/actions/_CustomType') - .reply(200) + api.delete('/actions/_CustomType').reply(200) await scope.actionType('_CustomType').delete() }) } diff --git a/test/e2e/entity/actions.spec.js b/test/integration/entity/actions.spec.js similarity index 66% rename from test/e2e/entity/actions.spec.js rename to test/integration/entity/actions.spec.js index 1cc5154..b368f2d 100644 --- a/test/e2e/entity/actions.spec.js +++ b/test/integration/entity/actions.spec.js @@ -1,19 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { - let scope, operatorScope +module.exports = (scopeType, url) => { + let scope, api describe('Actions', () => { before(async () => { scope = getScope(scopeType) - operatorScope = getScope('operator') + api = mockApi(url) }) it('should create an action', async () => { const payload = { type: 'scans', thng: 'thngId', tags: ['foo'] } - mockApi().post('/actions/scans', payload) - .reply(201, { id: 'actionId' }) + api.post('/actions/scans', payload).reply(201, { id: 'actionId' }) const res = await scope.action('scans').create(payload) expect(res).to.be.an('object') @@ -21,8 +20,7 @@ module.exports = (scopeType) => { }) it('should read all actions of a type', async () => { - mockApi().get('/actions/scans') - .reply(200, [{ id: 'actionId' }]) + api.get('/actions/scans').reply(200, [{ id: 'actionId' }]) const res = await scope.action('scans').read() expect(res).to.be.an('array') @@ -31,18 +29,15 @@ module.exports = (scopeType) => { it('should create an aliased action', async () => { const payload = { type: 'scans' } - mockApi().post('/thngs/thngId/actions/scans', payload) - .reply(201, { id: 'actionId' }) - const res = await scope.thng('thngId').action('scans') - .create(payload) + api.post('/thngs/thngId/actions/scans', payload).reply(201, { id: 'actionId' }) + const res = await scope.thng('thngId').action('scans').create(payload) expect(res).to.be.an('object') expect(res.id).to.be.a('string') }) it('should read all aliased actions', async () => { - mockApi().get('/thngs/thngId/actions/scans') - .reply(200, [{ id: 'actionId' }]) + api.get('/thngs/thngId/actions/scans').reply(200, [{ id: 'actionId' }]) const res = await scope.thng('thngId').action('scans').read() expect(res).to.be.an('array') @@ -51,8 +46,7 @@ module.exports = (scopeType) => { if (scopeType === 'operator') { it('should read a single action', async () => { - mockApi().get('/actions/scans/actionId') - .reply(200, { id: 'actionId' }) + api.get('/actions/scans/actionId').reply(200, { id: 'actionId' }) const res = await scope.action('scans', 'actionId').read() expect(res).to.be.an('object') @@ -60,8 +54,7 @@ module.exports = (scopeType) => { }) it('should delete an action', async () => { - mockApi().delete('/actions/scans/actionId') - .reply(200) + api.delete('/actions/scans/actionId').reply(200) await scope.action('scans', 'actionId').delete() }) } diff --git a/test/integration/entity/adiOrderEvents.spec.js b/test/integration/entity/adiOrderEvents.spec.js new file mode 100644 index 0000000..4fd38d0 --- /dev/null +++ b/test/integration/entity/adiOrderEvents.spec.js @@ -0,0 +1,33 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, url) => { + describe('ADI Orders', () => { + let scope, api + + before(() => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it('should create an ADI Order event', async () => { + const payload = { + metadata: { + type: 'encodings', + tags: ['example'] + }, + ids: ['serial1', 'serial2'], + customFields: { internalId: 'X7JF' }, + tags: ['X7JF'] + } + + api + .post('/adis/orders/adiOrderId/events', payload) + .reply(201, { id: 'UrCPgMhMMmPEY6awwEf6gKfb' }) + const res = await scope.adiOrder('adiOrderId').event().create(payload) + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + }) +} diff --git a/test/e2e/entity/adiOrders.spec.js b/test/integration/entity/adiOrders.spec.js similarity index 66% rename from test/e2e/entity/adiOrders.spec.js rename to test/integration/entity/adiOrders.spec.js index 8a76e32..dad934c 100644 --- a/test/e2e/entity/adiOrders.spec.js +++ b/test/integration/entity/adiOrders.spec.js @@ -1,20 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (scopeType, url) => { describe('ADI Orders', () => { - let operator + let scope, api before(() => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should create an ADI Order', async () => { const payload = { - ids: [ - 'serial1', - 'serial2' - ], + ids: ['serial1', 'serial2'], purchaseOrder: '234567890', metadata: { identifierKey: 'gs1:21', @@ -27,20 +25,16 @@ module.exports = () => { tags: ['X7JF'] } - mockApi() - .post('/adis/orders', payload) - .reply(201, { id: 'UEp4rDGsnpCAF6xABbys5Amc' }) - const res = await operator.adiOrder().create(payload) + api.post('/adis/orders', payload).reply(201, { id: 'UEp4rDGsnpCAF6xABbys5Amc' }) + const res = await scope.adiOrder().create(payload) expect(res).to.be.an('object') expect(res.id).to.have.length(24) }) it('should read an ADI Order by ID', async () => { - mockApi() - .get('/adis/orders/adiOrderId') - .reply(200, { id: 'adiOrderId' }) - const res = await operator.adiOrder('adiOrderId').read() + api.get('/adis/orders/adiOrderId').reply(200, { id: 'adiOrderId' }) + const res = await scope.adiOrder('adiOrderId').read() expect(res).to.be.an('object') expect(res.id).to.be.a('string') @@ -52,18 +46,15 @@ module.exports = () => { type: 'encodings', tags: ['example'] }, - ids: [ - 'serial1', - 'serial2' - ], + ids: ['serial1', 'serial2'], customFields: { internalId: 'X7JF' }, tags: ['X7JF'] } - mockApi() + api .post('/adis/orders/adiOrderId/events', payload) .reply(201, { id: 'UrCPgMhMMmPEY6awwEf6gKfb' }) - const res = await operator.adiOrder('adiOrderId').event().create(payload) + const res = await scope.adiOrder('adiOrderId').event().create(payload) expect(res).to.be.an('object') expect(res.id).to.be.a('string') diff --git a/test/integration/entity/applicationRedirector.spec.js b/test/integration/entity/applicationRedirector.spec.js new file mode 100644 index 0000000..c9e9c2b --- /dev/null +++ b/test/integration/entity/applicationRedirector.spec.js @@ -0,0 +1,48 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, url) => { + describe('Application Redirector', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it('should read the application Redirector', async () => { + api + .get('/projects/projectId/applications/applicationId/redirector') + .reply(200, { rules: [] }) + const res = await scope.project('projectId').application('applicationId').redirector().read() + + expect(res).to.be.an('object') + expect(res.rules).to.be.an('array') + }) + + it('should update the application redirector', async () => { + const payload = { + rules: [{ match: 'thng.name=test' }] + } + api.put('/projects/projectId/applications/applicationId/redirector').reply(200, payload) + const res = await scope + .project('projectId') + .application('applicationId') + .redirector() + .update(payload) + + expect(res.rules).to.deep.equal(payload.rules) + }) + + it('should delete the application redirector', async () => { + api.delete('/projects/projectId/applications/applicationId/redirector').reply(204) + const res = await scope + .project('projectId') + .application('applicationId') + .redirector() + .delete() + + expect(res).to.not.exist + }) + }) +} diff --git a/test/e2e/entity/applications.spec.js b/test/integration/entity/applications.spec.js similarity index 71% rename from test/e2e/entity/applications.spec.js rename to test/integration/entity/applications.spec.js index a2a9150..69085e5 100644 --- a/test/e2e/entity/applications.spec.js +++ b/test/integration/entity/applications.spec.js @@ -1,18 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Applications', () => { - let scope + let scope, api before(async () => { scope = getScope(scopeType) + api = mockApi(url) }) it('should create an application', async () => { const payload = { name: 'Application Name', socialNetworks: {} } - mockApi().post('/projects/projectId/applications') - .reply(201, payload) + api.post('/projects/projectId/applications').reply(201, payload) const res = await scope.project('projectId').application().create(payload) expect(res).to.be.an('object') @@ -20,8 +20,7 @@ module.exports = (scopeType) => { }) it('should read all applications', async () => { - mockApi().get('/projects/projectId/applications') - .reply(200, [{ id: 'applicationId' }]) + api.get('/projects/projectId/applications').reply(200, [{ id: 'applicationId' }]) const res = await scope.project('projectId').application().read() expect(res).to.be.an('array') @@ -29,8 +28,7 @@ module.exports = (scopeType) => { }) it('should read an application', async () => { - mockApi().get('/projects/projectId/applications/applicationId') - .reply(200, { id: 'applicationId' }) + api.get('/projects/projectId/applications/applicationId').reply(200, { id: 'applicationId' }) const res = await scope.project('projectId').application('applicationId').read() expect(res).to.be.an('object') @@ -39,8 +37,7 @@ module.exports = (scopeType) => { it('should update an application', async () => { const payload = { tags: ['updated'] } - mockApi().put('/projects/projectId/applications/applicationId') - .reply(200, payload) + api.put('/projects/projectId/applications/applicationId').reply(200, payload) const res = await scope.project('projectId').application('applicationId').update(payload) expect(res).to.be.an('object') @@ -48,8 +45,7 @@ module.exports = (scopeType) => { }) it('should delete an application', async () => { - mockApi().delete('/projects/projectId/applications/applicationId') - .reply(200) + api.delete('/projects/projectId/applications/applicationId').reply(200) await scope.project('projectId').application('applicationId').delete() }) }) diff --git a/test/integration/entity/batches.spec.js b/test/integration/entity/batches.spec.js new file mode 100644 index 0000000..f20935f --- /dev/null +++ b/test/integration/entity/batches.spec.js @@ -0,0 +1,117 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, settings) => { + describe('Batches', () => { + let scope, api + + before(() => { + scope = getScope(scopeType) + api = mockApi(settings.apiUrl) + }) + + if (settings.apiVersion == 1) { + it('should create a batch', async () => { + const payload = { name: 'Test Batch' } + api.post('/batches', payload).reply(201, payload) + const res = await scope.batch().create(payload) + + expect(res).to.be.an('object') + expect(res.name).to.equal(payload.name) + }) + + it('should read all batches', async () => { + api.get('/batches').reply(200, [{ id: 'batchId' }]) + const res = await scope.batch().read() + + expect(res).to.be.an('array') + expect(res).to.have.length.gte(1) + }) + + it('should read a batch', async () => { + api.get('/batches/batchId').reply(200, { id: 'batchId' }) + const res = await scope.batch('batchId').read() + + expect(res).to.be.an('object') + expect(res.id).to.equal('batchId') + }) + + it('should update a batch', async () => { + const payload = { tags: ['updated'] } + api.put('/batches/batchId', payload).reply(200, payload) + const res = await scope.batch('batchId').update(payload) + + expect(res).to.be.an('object') + expect(res.tags).to.deep.equal(payload.tags) + }) + + it('should delete a batch', async () => { + api.delete('/batches/batchId').reply(200) + await scope.batch('batchId').delete() + }) + } + if (settings.apiVersion == 2) { + it('should NOT create a batch', async () => { + let caughtError = false + const payload = { name: 'Test Batch' } + try { + api.post('/batches', payload).reply(403, { status: 403 }) + await scope.batch().create(payload) + } catch (err) { + caughtError = true + expect(err) + } + expect(caughtError).to.be.equal(true) + }) + + it('should NOT read all batches', async () => { + let caughtError = false + try { + api.get('/batches').reply(403, { status: 403 }) + await scope.batch().read() + } catch (err) { + caughtError = true + expect(err) + } + expect(caughtError).to.be.equal(true) + }) + + it('should NOT read a batch', async () => { + let caughtError = false + try { + api.get('/batches/batchId').reply(403, { status: 403 }) + await scope.batch('batchId').read() + } catch (err) { + caughtError = true + expect(err) + } + expect(caughtError).to.be.equal(true) + }) + + it('should NOT update a batch', async () => { + let caughtError = false + const payload = { tags: ['updated'] } + try { + api.put('/batches/batchId', payload).reply(403, { status: 403 }) + await scope.batch('batchId').update(payload) + } catch (err) { + caughtError = true + expect(err) + } + expect(caughtError).to.be.equal(true) + }) + + it('should NOT delete a batch', async () => { + let caughtError = false + try { + api.delete('/batches/batchId').reply(403) + await scope.batch('batchId').delete() + } catch (err) { + caughtError = true + expect(err) + } + expect(caughtError).to.be.equal(true) + }) + } + }) +} diff --git a/test/e2e/entity/collections.spec.js b/test/integration/entity/collections.spec.js similarity index 69% rename from test/e2e/entity/collections.spec.js rename to test/integration/entity/collections.spec.js index 31878b0..791d3b0 100644 --- a/test/e2e/entity/collections.spec.js +++ b/test/integration/entity/collections.spec.js @@ -1,18 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Collections', () => { - let scope + let scope, api before(() => { scope = getScope(scopeType) + api = mockApi(url) }) it('should create a collection', async () => { const payload = { name: 'Test Collection' } - mockApi().post('/collections', payload) - .reply(200, payload) + api.post('/collections', payload).reply(200, payload) const res = await scope.collection().create(payload) expect(res).to.be.an('object') @@ -20,8 +20,7 @@ module.exports = (scopeType) => { }) it('should read a collection', async () => { - mockApi().get('/collections/collectionId') - .reply(200, { id: 'collectionId' }) + api.get('/collections/collectionId').reply(200, { id: 'collectionId' }) const res = await scope.collection('collectionId').read() expect(res).to.be.an('object') @@ -29,8 +28,7 @@ module.exports = (scopeType) => { }) it('should read all collections', async () => { - mockApi().get('/collections') - .reply(200, [{ id: 'collectionId' }]) + api.get('/collections').reply(200, [{ id: 'collectionId' }]) const res = await scope.collection().read() expect(res).to.be.an('array') @@ -39,8 +37,7 @@ module.exports = (scopeType) => { it('should update a collection', async () => { const payload = { tags: ['updated'] } - mockApi().put('/collections/collectionId') - .reply(200, { tags: ['updated'] }) + api.put('/collections/collectionId').reply(200, { tags: ['updated'] }) const res = await scope.collection('collectionId').update(payload) expect(res).to.be.an('object') @@ -49,9 +46,10 @@ module.exports = (scopeType) => { if (['operator', 'trustedApp'].includes(scopeType)) { it('should delete a collection', async () => { - mockApi().delete('/collections/collectionId') - .reply(200) - await scope.collection('collectionId').delete() + api.delete('/collections/collectionId').reply(200) + const res = await scope.collection('collectionId').delete() + + expect(res).to.not.exist }) } }) diff --git a/test/e2e/entity/commissionState.spec.js b/test/integration/entity/commissionState.spec.js similarity index 66% rename from test/e2e/entity/commissionState.spec.js rename to test/integration/entity/commissionState.spec.js index fc0a812..680f4fb 100644 --- a/test/e2e/entity/commissionState.spec.js +++ b/test/integration/entity/commissionState.spec.js @@ -1,16 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { - let scope +module.exports = (scopeType, url) => { + let scope, api describe('Commission State', () => { before(async () => { scope = getScope(scopeType) + api = mockApi(url) }) - it('should read a Thng\'s commissioning state', async () => { - mockApi().get('/thngs/gs1%3A21%3A23984736/commissionState') + it("should read a Thng's commissioning state", async () => { + api + .get('/thngs/gs1%3A21%3A23984736/commissionState') .reply(200, { state: 'not_commissioned' }) const res = await scope.thng('gs1:21:23984736').commissionState().read() diff --git a/test/e2e/entity/domains.spec.js b/test/integration/entity/domains.spec.js similarity index 51% rename from test/e2e/entity/domains.spec.js rename to test/integration/entity/domains.spec.js index eb6cc9d..1083381 100644 --- a/test/e2e/entity/domains.spec.js +++ b/test/integration/entity/domains.spec.js @@ -1,18 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (scopeType, url) => { describe('Domains', () => { - let operator + let scope, api before(async () => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should read all domains', async () => { - mockApi().get('/accounts/accountId/domains') - .reply(200, [{ domain: 'wrxfq.tn.gg' }]) - const res = await operator.sharedAccount('accountId').domain().read() + api.get('/accounts/accountId/domains').reply(200, [{ domain: 'wrxfq.tn.gg' }]) + const res = await scope.sharedAccount('accountId').domain().read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) diff --git a/test/integration/entity/files.spec.js b/test/integration/entity/files.spec.js new file mode 100644 index 0000000..aede759 --- /dev/null +++ b/test/integration/entity/files.spec.js @@ -0,0 +1,57 @@ +const { expect } = require('chai') +const nock = require('nock') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, apiUrl) => { + describe('Files', () => { + let scope, api + + before(() => { + scope = getScope(scopeType) + api = mockApi(apiUrl) + }) + it('should create a file', async () => { + const payload = { name: 'TestFile.txt' } + api.post('/files', payload).reply(201, payload) + const res = await scope.file().create(payload) + + expect(res).to.be.an('object') + expect(res.name).to.equal(payload.name) + }) + + it('should read all files', async () => { + api.get('/files').reply(200, [{ id: 'fileId' }]) + const res = await scope.file().read() + + expect(res).to.be.an('array') + expect(res).to.have.length.gte(1) + }) + + it('should read a file', async () => { + api.get('/files/fileId').reply(200, { id: 'fileId' }) + const res = await scope.file('fileId').read() + + expect(res).to.be.an('object') + expect(res.id).to.equal('fileId') + }) + + it.skip('should upload file content - text', async () => { + const fileData = 'This is example text file content' + + api.get('/files/fileId').reply(200, { id: 'fileId', uploadUrl: 'https://s3.amazonaws.com' }) + await scope.file('fileId').upload(fileData) + + api.get('/files/fileId').reply(200, { contentUrl: 'https://s3.amazonaws.com/upload' }) + const resource = await scope.file('fileId').read() + nock('https://s3.amazonaws.com').get('/upload').reply(200, fileData) + const readData = await fetch(resource.contentUrl).then((res) => res.text()) + + expect(readData).to.equal(fileData) + }) + + it('should delete a file', async () => { + api.delete('/files/fileId').reply(200) + await scope.file('fileId').delete() + }) + }) +} diff --git a/test/e2e/entity/locations.spec.js b/test/integration/entity/locations.spec.js similarity index 52% rename from test/e2e/entity/locations.spec.js rename to test/integration/entity/locations.spec.js index c7b3960..4ab8677 100644 --- a/test/e2e/entity/locations.spec.js +++ b/test/integration/entity/locations.spec.js @@ -1,40 +1,42 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { - let scope +module.exports = (scopeType, url) => { + let scope, api describe('Locations', () => { before(async () => { scope = getScope(scopeType) + api = mockApi(url) }) - it('should update a Thng\'s location', async () => { - const payload = [{ - position: { type: 'Point', coordinates: [-17.3, 36] } - }] - mockApi().put('/thngs/thngId/location', payload) - .reply(200, payload) + it("should update a Thng's location", async () => { + const payload = [ + { + position: { type: 'Point', coordinates: [-17.3, 36] } + } + ] + api.put('/thngs/thngId/location', payload).reply(200, payload) const res = await scope.thng('thngId').locations().update(payload) expect(res).to.be.an('array') expect(res).to.have.length.gte(1) }) - it('should read a Thng\'s location', async () => { - mockApi().get('/thngs/thngId/location') - .reply(200, [{ + it("should read a Thng's location", async () => { + api.get('/thngs/thngId/location').reply(200, [ + { position: { type: 'Point', coordinates: [-17.3, 36] } - }]) + } + ]) const res = await scope.thng('thngId').locations().read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) }) - it('should delete a Thng\'s location', async () => { - mockApi().delete('/thngs/thngId/location') - .reply(200) + it("should delete a Thng's location", async () => { + api.delete('/thngs/thngId/location').reply(200) await scope.thng('thngId').locations().delete() }) }) diff --git a/test/integration/entity/me.spec.js b/test/integration/entity/me.spec.js new file mode 100644 index 0000000..b250ee3 --- /dev/null +++ b/test/integration/entity/me.spec.js @@ -0,0 +1,36 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, settings) => { + describe('Me', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(settings.apiUrl) + }) + + if (settings.apiVersion == 2) { + it('should read access', async () => { + api.get('/me').reply(200, { id: 'operatorId' }) + const res = await scope.me().read() + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + } + if (settings.apiVersion == 1) { + it('should NOT read access', async () => { + let caughtError = false + try { + api.get('/me').reply(403, {}) + await scope.me().read() + } catch (err) { + caughtError = true + expect(err).to.exist + } + expect(caughtError).to.be.equal(true) + }) + } + }) +} diff --git a/test/integration/entity/operatorAccesses.spec.js b/test/integration/entity/operatorAccesses.spec.js new file mode 100644 index 0000000..db9c98d --- /dev/null +++ b/test/integration/entity/operatorAccesses.spec.js @@ -0,0 +1,79 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +const payload = { + email: 'operatorAccess@gmail.com', + description: 'Create operator access', + policies: ['UPb7E6shapktcaaabfahfpds'], + conditions: [], + operator: 'UtnGTkaPDphkYDC9F2KHBtPp', + tags: ['operatorAccess'], + identifiers: {}, + customFields: {} +} + +module.exports = (scopeType, settings) => { + describe('OperatorAccesses', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(settings.apiUrl) + }) + + if (settings.apiVersion == 2) { + it('should create operator access', async () => { + api + .post('/accounts/accountId/operatorAccess', payload) + .reply(201, { id: 'operatorAccessId' }) + const res = await scope.sharedAccount('accountId').operatorAccess().create(payload) + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + + it('should read operator access by id', async () => { + api + .get('/accounts/accountId/operatorAccess/operatorAccessId') + .reply(200, { id: 'operatorAccessId' }) + const res = await scope + .sharedAccount('accountId') + .operatorAccess('operatorAccessId') + .read() + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + + it('should read all operator accesses', async () => { + api.get('/accounts/accountId/operatorAccess').reply(200, [payload]) + const res = await scope.sharedAccount('accountId').operatorAccess().read() + + expect(res).to.be.an('array') + expect(res).to.have.length.gte(1) + }) + + it('should update operator access', async () => { + api.put('/accounts/accountId/operatorAccess/operatorAccessId', payload).reply(200, payload) + + const res = await scope + .sharedAccount('accountId') + .operatorAccess('operatorAccessId') + .update(payload) + + expect(res).to.be.an('object') + expect(res.name).to.deep.equal(payload.name) + }) + + it('should delete operator access', async () => { + api.delete('/accounts/accountId/operatorAccess/operatorAccessId').reply(204) + const res = await scope + .sharedAccount('accountId') + .operatorAccess('operatorAccessId') + .delete() + + expect(res).to.not.exist + }) + } + }) +} diff --git a/test/e2e/entity/permissions.spec.js b/test/integration/entity/permissions.spec.js similarity index 69% rename from test/e2e/entity/permissions.spec.js rename to test/integration/entity/permissions.spec.js index 154670e..fcbf3ce 100644 --- a/test/e2e/entity/permissions.spec.js +++ b/test/integration/entity/permissions.spec.js @@ -1,17 +1,17 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -const describeOperatorPermissionTests = () => { +const describeOperatorPermissionTests = (url) => { describe('Permissions (Operator Roles)', () => { - let operator + let operator, api before(async () => { operator = getScope('operator') + api = mockApi(url) }) it('should read all role permissions', async () => { - mockApi().get('/roles/roleId/permissions') - .reply(200, [{ name: 'global_read' }]) + api.get('/roles/roleId/permissions').reply(200, [{ name: 'global_read' }]) const res = await operator.role('roleId').permission().read() expect(res).to.be.an('array') @@ -19,8 +19,7 @@ const describeOperatorPermissionTests = () => { }) it('should read a single role permission', async () => { - mockApi().get('/roles/roleId/permissions/global_read') - .reply(200, { name: 'global_read' }) + api.get('/roles/roleId/permissions/global_read').reply(200, { name: 'global_read' }) const res = await operator.role('roleId').permission('global_read').read() expect(res).to.be.an('object') @@ -29,8 +28,7 @@ const describeOperatorPermissionTests = () => { it('should update a single role permission', async () => { const payload = { name: 'global_read', enabled: false } - mockApi().put('/roles/roleId/permissions/global_read', payload) - .reply(200, payload) + api.put('/roles/roleId/permissions/global_read', payload).reply(200, payload) const res = await operator.role('roleId').permission('global_read').update(payload) expect(res).to.be.an('object') @@ -39,17 +37,17 @@ const describeOperatorPermissionTests = () => { }) } -const describeAppUserPermissionTests = () => { +const describeAppUserPermissionTests = (url) => { describe('Permissions (App User Roles)', () => { - let operator + let operator, api before(async () => { operator = getScope('operator') + api = mockApi(url) }) it('should read all role permissions', async () => { - mockApi().get('/roles/roleId/permissions') - .reply(200, [{ access: 'cru', path: '/thngs' }]) + api.get('/roles/roleId/permissions').reply(200, [{ access: 'cru', path: '/thngs' }]) const res = await operator.role('roleId').permission().read() expect(res).to.be.an('array') @@ -58,8 +56,7 @@ const describeAppUserPermissionTests = () => { it('should update role permissions', async () => { const payload = [{ path: '/thngs', access: 'cr' }] - mockApi().put('/roles/roleId/permissions', payload) - .reply(200, payload) + api.put('/roles/roleId/permissions', payload).reply(200, payload) const res = await operator.role('roleId').permission().update(payload) expect(res).to.be.an('array') @@ -68,8 +65,7 @@ const describeAppUserPermissionTests = () => { it('should minimise role permissions', async () => { const payload = [] - mockApi().put('/roles/roleId/permissions', payload) - .reply(200, payload) + api.put('/roles/roleId/permissions', payload).reply(200, payload) const res = await operator.role('roleId').permission().update(payload) expect(res).to.be.an('array') @@ -77,12 +73,12 @@ const describeAppUserPermissionTests = () => { }) } -module.exports = (type) => { +module.exports = (type, url) => { if (type === 'operator') { - describeOperatorPermissionTests() + describeOperatorPermissionTests(url) } if (type === 'userInApp') { - describeAppUserPermissionTests() + describeAppUserPermissionTests(url) } } diff --git a/test/e2e/entity/places.spec.js b/test/integration/entity/places.spec.js similarity index 74% rename from test/e2e/entity/places.spec.js rename to test/integration/entity/places.spec.js index 49847b2..b33dfa3 100644 --- a/test/e2e/entity/places.spec.js +++ b/test/integration/entity/places.spec.js @@ -1,17 +1,17 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Places', () => { - let scope + let scope, api before(() => { scope = getScope(scopeType) + api = mockApi(url) }) it('should read all places', async () => { - mockApi().get('/places') - .reply(200, [{ id: 'placeId' }]) + api.get('/places').reply(200, [{ id: 'placeId' }]) const res = await scope.place().read() expect(res).to.be.an('array') @@ -21,8 +21,7 @@ module.exports = (scopeType) => { if (['operator', 'trustedApp'].includes(scopeType)) { it('should create a place', async () => { const payload = { name: 'Test Place' } - mockApi().post('/places', payload) - .reply(201, payload) + api.post('/places', payload).reply(201, payload) const res = await scope.place().create(payload) expect(res).to.be.an('object') @@ -30,8 +29,7 @@ module.exports = (scopeType) => { }) it('should read a place', async () => { - mockApi().get('/places/placeId') - .reply(200, { id: 'placeId' }) + api.get('/places/placeId').reply(200, { id: 'placeId' }) const res = await scope.place('placeId').read() expect(res).to.be.an('object') @@ -40,8 +38,7 @@ module.exports = (scopeType) => { it('should update a place', async () => { const payload = { tags: ['updated'] } - mockApi().put('/places/placeId', payload) - .reply(200, payload) + api.put('/places/placeId', payload).reply(200, payload) const res = await scope.place('placeId').update(payload) expect(res).to.be.an('object') @@ -49,8 +46,7 @@ module.exports = (scopeType) => { }) it('should delete a place', async () => { - mockApi().delete('/places/placeId') - .reply(200) + api.delete('/places/placeId').reply(200) await scope.place('placeId').delete() }) } diff --git a/test/e2e/entity/products.spec.js b/test/integration/entity/products.spec.js similarity index 74% rename from test/e2e/entity/products.spec.js rename to test/integration/entity/products.spec.js index 2ff7c32..2ed9537 100644 --- a/test/e2e/entity/products.spec.js +++ b/test/integration/entity/products.spec.js @@ -1,18 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Products', () => { - let scope + let scope, api before(() => { scope = getScope(scopeType) + api = mockApi(url) }) it('should create a product', async () => { const payload = { name: 'Test Product' } - mockApi().post('/products', payload) - .reply(201, payload) + api.post('/products', payload).reply(201, payload) const res = await scope.product().create(payload) expect(res).to.be.an('object') @@ -20,8 +20,7 @@ module.exports = (scopeType) => { }) it('should read a product', async () => { - mockApi().get('/products/productId') - .reply(200, { id: 'productId' }) + api.get('/products/productId').reply(200, { id: 'productId' }) const res = await scope.product('productId').read() expect(res).to.be.an('object') @@ -29,8 +28,7 @@ module.exports = (scopeType) => { }) it('should read all products', async () => { - mockApi().get('/products') - .reply(200, [{ id: 'productId' }]) + api.get('/products').reply(200, [{ id: 'productId' }]) const res = await scope.product().read() expect(res).to.be.an('array') @@ -39,8 +37,7 @@ module.exports = (scopeType) => { it('should update a product', async () => { const payload = { tags: ['updated'] } - mockApi().put('/products/productId', payload) - .reply(200, payload) + api.put('/products/productId', payload).reply(200, payload) const res = await scope.product('productId').update(payload) expect(res).to.be.an('object') @@ -49,8 +46,7 @@ module.exports = (scopeType) => { if (['operator', 'trustedApp'].includes(scopeType)) { it('should delete a product', async () => { - mockApi().delete('/products/productId') - .reply(200) + api.delete('/products/productId').reply(200) await scope.product('productId').delete() }) } diff --git a/test/e2e/entity/projects.spec.js b/test/integration/entity/projects.spec.js similarity index 53% rename from test/e2e/entity/projects.spec.js rename to test/integration/entity/projects.spec.js index c39f270..213bc1f 100644 --- a/test/e2e/entity/projects.spec.js +++ b/test/integration/entity/projects.spec.js @@ -1,38 +1,36 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (scopeType, url) => { describe('Projects', () => { - let operator + let scope, api before(() => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should create a project', async () => { const payload = { name: 'Test Project' } - mockApi().post('/projects', payload) - .reply(201, payload) + api.post('/projects', payload).reply(201, payload) - const res = await operator.project().create(payload) + const res = await scope.project().create(payload) expect(res).to.be.an('object') expect(res.name).to.equal('Test Project') }) it('should read all projects', async () => { - mockApi().get('/projects') - .reply(200, [{ id: 'projectId' }]) - const res = await operator.project().read() + api.get('/projects').reply(200, [{ id: 'projectId' }]) + const res = await scope.project().read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) }) it('should read a project', async () => { - mockApi().get('/projects/projectId') - .reply(200, { id: 'projectId' }) - const res = await operator.project('projectId').read() + api.get('/projects/projectId').reply(200, { id: 'projectId' }) + const res = await scope.project('projectId').read() expect(res).to.be.an('object') expect(res.id).to.be.a('string') @@ -40,18 +38,16 @@ module.exports = () => { it('should update a project', async () => { const payload = { tags: ['updated'] } - mockApi().put('/projects/projectId', payload) - .reply(200, payload) - const res = await operator.project('projectId').update(payload) + api.put('/projects/projectId', payload).reply(200, payload) + const res = await scope.project('projectId').update(payload) expect(res).to.be.an('object') expect(res.tags).to.deep.equal(['updated']) }) it('should delete a project', async () => { - mockApi().delete('/projects/projectId') - .reply(200) - await operator.project('projectId').delete() + api.delete('/projects/projectId').reply(200) + await scope.project('projectId').delete() }) }) } diff --git a/test/e2e/entity/properties.spec.js b/test/integration/entity/properties.spec.js similarity index 72% rename from test/e2e/entity/properties.spec.js rename to test/integration/entity/properties.spec.js index 1091e90..dab85ca 100644 --- a/test/e2e/entity/properties.spec.js +++ b/test/integration/entity/properties.spec.js @@ -1,18 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType, targetType) => { - let scope +module.exports = (scopeType, targetType, url) => { + let scope, api describe(`Properties (${targetType})`, () => { before(async () => { scope = getScope(scopeType) + api = mockApi(url) }) it(`should create a ${targetType} property`, async () => { const payload = { key: 'temp_c', value: 42 } - mockApi().post(`/${targetType}s/targetId/properties`) - .reply(200, [payload]) + api.post(`/${targetType}s/targetId/properties`).reply(200, [payload]) const res = await scope[targetType]('targetId').property().create(payload) expect(res).to.be.an('array') @@ -20,8 +20,7 @@ module.exports = (scopeType, targetType) => { }) it(`should read all ${targetType} properties`, async () => { - mockApi().get(`/${targetType}s/targetId/properties`) - .reply(200, [{ key: 'temp_c', value: 42 }]) + api.get(`/${targetType}s/targetId/properties`).reply(200, [{ key: 'temp_c', value: 42 }]) const res = await scope[targetType]('targetId').property().read() expect(res).to.be.an('array') @@ -29,8 +28,7 @@ module.exports = (scopeType, targetType) => { }) it(`should read a single ${targetType} property`, async () => { - mockApi().get(`/${targetType}s/targetId/properties/temp_c`) - .reply(200, [{ value: 42 }]) + api.get(`/${targetType}s/targetId/properties/temp_c`).reply(200, [{ value: 42 }]) const res = await scope[targetType]('targetId').property('temp_c').read() expect(res).to.be.an('array') @@ -38,7 +36,8 @@ module.exports = (scopeType, targetType) => { }) it(`should update a single ${targetType} property`, async () => { - mockApi().put(`/${targetType}s/targetId/properties/temp_c`, [{ value: 43 }]) + api + .put(`/${targetType}s/targetId/properties/temp_c`, [{ value: 43 }]) .reply(200, [{ value: 43 }]) const res = await scope[targetType]('targetId').property('temp_c').update(43) @@ -48,8 +47,7 @@ module.exports = (scopeType, targetType) => { if (scopeType !== 'anonUser') { it(`should delete a single ${targetType} property`, async () => { - mockApi().delete(`/${targetType}s/targetId/properties/temp_c`) - .reply(200) + api.delete(`/${targetType}s/targetId/properties/temp_c`).reply(200) await scope[targetType]('targetId').property('temp_c').delete() }) } diff --git a/test/e2e/entity/purchaseOrders.spec.js b/test/integration/entity/purchaseOrders.spec.js similarity index 75% rename from test/e2e/entity/purchaseOrders.spec.js rename to test/integration/entity/purchaseOrders.spec.js index 6fa1d97..2fdcf0e 100644 --- a/test/e2e/entity/purchaseOrders.spec.js +++ b/test/integration/entity/purchaseOrders.spec.js @@ -23,17 +23,17 @@ const payload = { ] } -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Purchase Orders', () => { - let scope + let scope, api before(() => { scope = getScope(scopeType) + api = mockApi(url) }) it('should read all purchase orders', async () => { - mockApi().get('/purchaseOrders') - .reply(200, [payload]) + api.get('/purchaseOrders').reply(200, [payload]) const res = await scope.purchaseOrder().read() @@ -43,8 +43,7 @@ module.exports = (scopeType) => { if (scopeType === 'operator') { it('should create a purchase order', async () => { - mockApi().post('/purchaseOrders', payload) - .reply(201, payload) + api.post('/purchaseOrders', payload).reply(201, payload) const res = await scope.purchaseOrder().create(payload) @@ -52,9 +51,7 @@ module.exports = (scopeType) => { }) it('should read a purchase order', async () => { - mockApi().get('/purchaseOrders/purchaseOrderId') - .reply(200, payload) - + api.get('/purchaseOrders/purchaseOrderId').reply(200, payload) const res = await scope.purchaseOrder('purchaseOrderId').read() expect(res).to.be.an('object') @@ -62,8 +59,7 @@ module.exports = (scopeType) => { }) it('should update a purchase order', async () => { - mockApi().put('/purchaseOrders/purchaseOrderId', payload) - .reply(200, payload) + api.put('/purchaseOrders/purchaseOrderId', payload).reply(200, payload) const res = await scope.purchaseOrder('purchaseOrderId').update(payload) @@ -72,10 +68,10 @@ module.exports = (scopeType) => { }) it('should delete a purchaseOrder', async () => { - mockApi().delete('/purchaseOrders/purchaseOrderId') - .reply(204) + api.delete('/purchaseOrders/purchaseOrderId').reply(204) - await scope.purchaseOrder('purchaseOrderId').delete() + const res = await scope.purchaseOrder('purchaseOrderId').delete() + expect(res).to.not.exist }) } }) diff --git a/test/e2e/entity/reactor.spec.js b/test/integration/entity/reactor.spec.js similarity index 61% rename from test/e2e/entity/reactor.spec.js rename to test/integration/entity/reactor.spec.js index dce8131..decc99f 100644 --- a/test/e2e/entity/reactor.spec.js +++ b/test/integration/entity/reactor.spec.js @@ -3,14 +3,17 @@ const { getScope, mockApi } = require('../util') const SCRIPT = 'function onActionCreated(event) { done(); }' -let operator +let operator, api const describeReactorScriptTests = () => { it('should update the Reactor script', async () => { const payload = { script: SCRIPT } - mockApi().put('/projects/projectId/applications/applicationId/reactor/script', payload) + api + .put('/projects/projectId/applications/applicationId/reactor/script', payload) .reply(200, payload) - const res = await operator.project('projectId').application('applicationId') + const res = await operator + .project('projectId') + .application('applicationId') .reactorScript() .update(payload) @@ -18,9 +21,12 @@ const describeReactorScriptTests = () => { }) it('should read the Reactor script status', async () => { - mockApi().get('/projects/projectId/applications/applicationId/reactor/script/status') + api + .get('/projects/projectId/applications/applicationId/reactor/script/status') .reply(200, { updating: false }) - const res = await operator.project('projectId').application('applicationId') + const res = await operator + .project('projectId') + .application('applicationId') .reactorScript() .status() .read() @@ -29,9 +35,12 @@ const describeReactorScriptTests = () => { }) it('should read the Reactor script', async () => { - mockApi().get('/projects/projectId/applications/applicationId/reactor/script') + api + .get('/projects/projectId/applications/applicationId/reactor/script') .reply(200, { type: 'simple' }) - const res = await operator.project('projectId').application('applicationId') + const res = await operator + .project('projectId') + .application('applicationId') .reactorScript() .read() @@ -47,9 +56,12 @@ const describeReactorScheduleTests = () => { cron: '0 0 * * * ?', description: 'Example Reactor schedule' } - mockApi().post('/projects/projectId/applications/applicationId/reactor/schedules', payload) + api + .post('/projects/projectId/applications/applicationId/reactor/schedules', payload) .reply(201, payload) - const res = await operator.project('projectId').application('applicationId') + const res = await operator + .project('projectId') + .application('applicationId') .reactorSchedule() .create(payload) @@ -58,9 +70,12 @@ const describeReactorScheduleTests = () => { }) it('should read all Reactor schedules', async () => { - mockApi().get('/projects/projectId/applications/applicationId/reactor/schedules') + api + .get('/projects/projectId/applications/applicationId/reactor/schedules') .reply(200, [{ id: 'scheduleId' }]) - const res = await operator.project('projectId').application('applicationId') + const res = await operator + .project('projectId') + .application('applicationId') .reactorSchedule() .read() @@ -68,9 +83,12 @@ const describeReactorScheduleTests = () => { }) it('should read a single Reactor schedule', async () => { - mockApi().get('/projects/projectId/applications/applicationId/reactor/schedules/scheduleId') + api + .get('/projects/projectId/applications/applicationId/reactor/schedules/scheduleId') .reply(200, { id: 'scheduleId' }) - const res = await operator.project('projectId').application('applicationId') + const res = await operator + .project('projectId') + .application('applicationId') .reactorSchedule('scheduleId') .read() @@ -79,10 +97,12 @@ const describeReactorScheduleTests = () => { it('should update a single Reactor schedule', async () => { const payload = { enabled: false } - mockApi() + api .put('/projects/projectId/applications/applicationId/reactor/schedules/scheduleId', payload) .reply(200, { id: 'scheduleId', enabled: false }) - const res = await operator.project('projectId').application('applicationId') + const res = await operator + .project('projectId') + .application('applicationId') .reactorSchedule('scheduleId') .update({ enabled: false }) @@ -92,19 +112,24 @@ const describeReactorScheduleTests = () => { }) it('should delete a single Reactor schedule', async () => { - mockApi() + api .delete('/projects/projectId/applications/applicationId/reactor/schedules/scheduleId') .reply(200) - await operator.project('projectId').application('applicationId') + const res = await operator + .project('projectId') + .application('applicationId') .reactorSchedule('scheduleId') .delete() + + expect(res).to.not.exist }) } -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Reactor', () => { before(async () => { - operator = getScope('operator') + operator = getScope(scopeType) + api = mockApi(url) }) describeReactorScriptTests() diff --git a/test/e2e/entity/redirection.spec.js b/test/integration/entity/redirection.spec.js similarity index 76% rename from test/e2e/entity/redirection.spec.js rename to test/integration/entity/redirection.spec.js index 4013aa7..8db5ff9 100644 --- a/test/e2e/entity/redirection.spec.js +++ b/test/integration/entity/redirection.spec.js @@ -1,7 +1,6 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') - module.exports = (scopeType, targetType) => { let scope @@ -12,8 +11,7 @@ module.exports = (scopeType, targetType) => { it(`should create a ${targetType} redirection`, async () => { const payload = { defaultRedirectUrl: 'https://www.google.com' } - mockApi('https://tn.gg').post('/redirections', payload) - .reply(201, payload) + mockApi('https://tn.gg').post('/redirections', payload).reply(201, payload) const res = await scope[targetType]('targetId').redirection().create(payload) expect(res).to.be.an('object') @@ -21,7 +19,8 @@ module.exports = (scopeType, targetType) => { }) it(`should read a ${targetType} redirection`, async () => { - mockApi('https://tn.gg').get('/redirections?evrythngId=targetId') + mockApi('https://tn.gg') + .get('/redirections?evrythngId=targetId') .reply(200, [{ hits: 0 }]) const res = await scope[targetType]('targetId').redirection().read() @@ -29,7 +28,8 @@ module.exports = (scopeType, targetType) => { }) it(`should read a ${targetType} redirection with explicit shortDomain`, async () => { - mockApi('https://abc.tn.gg').get('/redirections?evrythngId=targetId') + mockApi('https://abc.tn.gg') + .get('/redirections?evrythngId=targetId') .reply(200, [{ hits: 0 }]) const res = await scope[targetType]('targetId').redirection('abc.tn.gg').read() @@ -38,20 +38,20 @@ module.exports = (scopeType, targetType) => { it(`should update a ${targetType} redirection`, async () => { const payload = { defaultRedirectUrl: 'https://google.com/updated?item={shortId}' } - mockApi('https://tn.gg').get('/redirections?evrythngId=targetId') + mockApi('https://tn.gg') + .get('/redirections?evrythngId=targetId') .reply(200, [{ hits: 0, shortId: 'shortId' }]) - mockApi('https://tn.gg').put('/redirections/shortId', payload) - .reply(200, payload) + mockApi('https://tn.gg').put('/redirections/shortId', payload).reply(200, payload) const res = await scope[targetType]('targetId').redirection().update(payload) expect(res).to.be.an('object') }) it(`should delete a ${targetType} redirection`, async () => { - mockApi('https://tn.gg').get('/redirections?evrythngId=targetId') + mockApi('https://tn.gg') + .get('/redirections?evrythngId=targetId') .reply(200, [{ hits: 0, shortId: 'shortId' }]) - mockApi('https://tn.gg').delete('/redirections/shortId') - .reply(200) + mockApi('https://tn.gg').delete('/redirections/shortId').reply(200) await scope[targetType]('targetId').redirection().delete() }) }) diff --git a/test/e2e/entity/roles.spec.js b/test/integration/entity/roles.spec.js similarity index 74% rename from test/e2e/entity/roles.spec.js rename to test/integration/entity/roles.spec.js index 9e55bd9..93837fa 100644 --- a/test/e2e/entity/roles.spec.js +++ b/test/integration/entity/roles.spec.js @@ -1,17 +1,17 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Roles', () => { - let scope + let scope, api before(() => { scope = getScope(scopeType) + api = mockApi(url) }) it('should read all roles', async () => { - mockApi().get('/roles') - .reply(200, [{ id: 'roleId' }]) + api.get('/roles').reply(200, [{ id: 'roleId' }]) const res = await scope.role().read() expect(res).to.be.an('array') @@ -21,8 +21,7 @@ module.exports = (scopeType) => { if (scopeType === 'operator') { it('should create a role', async () => { const payload = { name: 'Test Role' } - mockApi().post('/roles', payload) - .reply(201, payload) + api.post('/roles', payload).reply(201, payload) const res = await scope.role().create(payload) expect(res).to.be.an('object') @@ -30,8 +29,7 @@ module.exports = (scopeType) => { }) it('should read a role', async () => { - mockApi().get('/roles/roleId') - .reply(200, { id: 'roleId' }) + api.get('/roles/roleId').reply(200, { id: 'roleId' }) const res = await scope.role('roleId').read() expect(res).to.be.an('object') @@ -40,8 +38,7 @@ module.exports = (scopeType) => { it('should update a role', async () => { const payload = { description: 'updated' } - mockApi().put('/roles/roleId', payload) - .reply(200, payload) + api.put('/roles/roleId', payload).reply(200, payload) const res = await scope.role('roleId').update(payload) expect(res).to.be.an('object') @@ -49,8 +46,7 @@ module.exports = (scopeType) => { }) it('should delete a role', async () => { - mockApi().delete('/roles/roleId') - .reply(200) + api.delete('/roles/roleId').reply(200) await scope.role('roleId').delete() }) } diff --git a/test/e2e/entity/rules.spec.js b/test/integration/entity/rules.spec.js similarity index 54% rename from test/e2e/entity/rules.spec.js rename to test/integration/entity/rules.spec.js index c1af372..df6a041 100644 --- a/test/e2e/entity/rules.spec.js +++ b/test/integration/entity/rules.spec.js @@ -1,18 +1,18 @@ const { expect } = require('chai') const { getScope } = require('../util') -module.exports = () => { +module.exports = (scopeType, url) => { describe('Rules', () => { - let operator + let scope before(() => { - operator = getScope('operator') + scope = getScope(scopeType) }) it('should add a rule resource', async () => { - expect(operator.rule).to.be.a('function') + expect(scope.rule).to.be.a('function') }) - it('should run a given rule'); + it('should run a given rule') }) } diff --git a/test/integration/entity/secretKey.spec.js b/test/integration/entity/secretKey.spec.js new file mode 100644 index 0000000..89db3b8 --- /dev/null +++ b/test/integration/entity/secretKey.spec.js @@ -0,0 +1,23 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, url) => { + describe('Secret Key', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it("should read an application's secret key", async () => { + api + .get('/projects/projectId/applications/applicationId/secretKey') + .reply(200, { secretApiKey: 'secretApiKey' }) + const res = await scope.project('projectId').application('applicationId').secretKey().read() + + expect(res).to.be.an('object') + expect(res.secretApiKey).to.be.a('string') + }) + }) +} diff --git a/test/integration/entity/shipmentNotice.spec.js b/test/integration/entity/shipmentNotice.spec.js new file mode 100644 index 0000000..3c647b0 --- /dev/null +++ b/test/integration/entity/shipmentNotice.spec.js @@ -0,0 +1,70 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +const noticePayload = { + asnId: '2343689643', + version: '1', + issueDate: '2019-06-19T16:39:57-08:00', + transportation: 'Expedited Freight', + parties: [ + { + id: 'gs1:414:01251', + type: 'ship-from' + }, + { + name: 'The Landmark, Shop No. G14', + type: 'ship-to', + address: { + street: '113-114, Central', + city: 'Hong Kong' + } + } + ], + tags: ['ongoing'] +} + +module.exports = (scopeType, url) => { + describe('Shipment Notices', () => { + let scope, api + + before(() => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it('should create a shipment notice', async () => { + api.post('/shipmentNotices', noticePayload).reply(201, noticePayload) + + const res = await scope.shipmentNotice().create(noticePayload) + + expect(res).to.be.an('object') + expect(res.asnId).to.equal(noticePayload.asnId) + }) + + it('should read a shipment notice', async () => { + api.get('/shipmentNotices/shipmentNoticeId').reply(200, noticePayload) + + const res = await scope.shipmentNotice('shipmentNoticeId').read() + + expect(res).to.be.an('object') + expect(res.asnId).to.equal(noticePayload.asnId) + }) + + it('should update a shipment notice', async () => { + api.put('/shipmentNotices/shipmentNoticeId').reply(200, noticePayload) + + const res = await scope.shipmentNotice('shipmentNoticeId').update(noticePayload) + + expect(res).to.be.an('object') + expect(res.tags).to.deep.equal(noticePayload.tags) + }) + + it('should delete a shipment notice', async () => { + api.delete('/shipmentNotices/shipmentNoticeId').reply(204) + + const res = await scope.shipmentNotice('shipmentNoticeId').delete() + + expect(res).to.not.exist + }) + }) +} diff --git a/test/e2e/entity/shortDomains.spec.js b/test/integration/entity/shortDomains.spec.js similarity index 52% rename from test/e2e/entity/shortDomains.spec.js rename to test/integration/entity/shortDomains.spec.js index 0e0b691..ae7bae2 100644 --- a/test/e2e/entity/shortDomains.spec.js +++ b/test/integration/entity/shortDomains.spec.js @@ -1,18 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (scopeType, url) => { describe('Short Domains', () => { - let operator + let scope, api before(async () => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should read all short domains', async () => { - mockApi().get('/accounts/accountId/shortDomains') - .reply(200, ['tn.gg']) - const res = await operator.sharedAccount('accountId').shortDomain().read() + api.get('/accounts/accountId/shortDomains').reply(200, ['tn.gg']) + const res = await scope.sharedAccount('accountId').shortDomain().read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) diff --git a/test/e2e/entity/tasks.spec.js b/test/integration/entity/tasks.spec.js similarity index 62% rename from test/e2e/entity/tasks.spec.js rename to test/integration/entity/tasks.spec.js index 9f63fa0..5ff5c9f 100644 --- a/test/e2e/entity/tasks.spec.js +++ b/test/integration/entity/tasks.spec.js @@ -1,14 +1,13 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -let operator - -module.exports = () => { +module.exports = (scopeType, url) => { describe('Tasks', () => { - let batch, task + let scope, api before(async () => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should create a task', async () => { @@ -25,27 +24,24 @@ module.exports = () => { shortIdTemplate: { type: 'THNG_ID' } } } - mockApi().post('/batches/batchId/tasks', payload) - .reply(202) - const res = await operator.batch('batchId').task().create(payload, { fullResponse: true }) + api.post('/batches/batchId/tasks', payload).reply(202) + const res = await scope.batch('batchId').task().create(payload, { fullResponse: true }) expect(typeof res).to.equal('object') expect(res.status).to.equal(202) }) it('should read all tasks', async () => { - mockApi().get('/batches/batchId/tasks') - .reply(200, [{ id: 'taskId' }]) - const res = await operator.batch('batchId').task().read() + api.get('/batches/batchId/tasks').reply(200, [{ id: 'taskId' }]) + const res = await scope.batch('batchId').task().read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) }) it('should read a task', async () => { - mockApi().get('/batches/batchId/tasks/taskId') - .reply(200, { id: 'taskId' }) - const res = await operator.batch('batchId').task('taskId').read() + api.get('/batches/batchId/tasks/taskId').reply(200, { id: 'taskId' }) + const res = await scope.batch('batchId').task('taskId').read() expect(res).to.be.an('object') expect(res.id).to.equal('taskId') diff --git a/test/e2e/entity/thngs.spec.js b/test/integration/entity/thngs.spec.js similarity index 71% rename from test/e2e/entity/thngs.spec.js rename to test/integration/entity/thngs.spec.js index 26e6dac..48cb0e3 100644 --- a/test/e2e/entity/thngs.spec.js +++ b/test/integration/entity/thngs.spec.js @@ -1,18 +1,18 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Thngs', () => { - let scope + let scope, api before(() => { scope = getScope(scopeType) + api = mockApi(url) }) it('should create a Thng', async () => { const payload = { name: 'Test Thng' } - mockApi().post('/thngs', payload) - .reply(201, payload) + api.post('/thngs', payload).reply(201, payload) const res = await scope.thng().create(payload) expect(res).to.be.an('object') @@ -20,8 +20,7 @@ module.exports = (scopeType) => { }) it('should read a Thng', async () => { - mockApi().get('/thngs/thngId') - .reply(200, { id: 'thngId' }) + api.get('/thngs/thngId').reply(200, { id: 'thngId' }) const res = await scope.thng('thngId').read() expect(res).to.be.an('object') @@ -29,8 +28,7 @@ module.exports = (scopeType) => { }) it('should read all Thngs', async () => { - mockApi().get('/thngs') - .reply(200, [{ id: 'thngId' }]) + api.get('/thngs').reply(200, [{ id: 'thngId' }]) const res = await scope.thng().read() expect(res).to.be.an('array') @@ -39,8 +37,7 @@ module.exports = (scopeType) => { it('should update a Thng', async () => { const payload = { tags: ['updated'] } - mockApi().put('/thngs/thngId', payload) - .reply(200, payload) + api.put('/thngs/thngId', payload).reply(200, payload) const res = await scope.thng('thngId').update(payload) expect(res).to.be.an('object') @@ -49,9 +46,9 @@ module.exports = (scopeType) => { if (['operator', 'trustedApp'].includes(scopeType)) { it('should delete a Thng', async () => { - mockApi().delete('/thngs/thngId') - .reply(200) - await scope.thng('thngId').delete() + api.delete('/thngs/thngId').reply(200) + const res = await scope.thng('thngId').delete() + expect(res).to.not.exist }) } }) diff --git a/test/e2e/entity/user.spec.js b/test/integration/entity/user.spec.js similarity index 64% rename from test/e2e/entity/user.spec.js rename to test/integration/entity/user.spec.js index 3a9d97d..490b630 100644 --- a/test/e2e/entity/user.spec.js +++ b/test/integration/entity/user.spec.js @@ -8,22 +8,22 @@ const USER = { password: 'password' } -module.exports = (scopeType) => { +module.exports = (scopeType, url) => { describe('Application Users', () => { - let scope + let scope, api before(() => { scope = getScope(scopeType) + api = mockApi(url) }) if (scopeType === 'application') { it('should create an anonymous Application User', async () => { - mockApi().post('/auth/evrythng/users?anonymous=true') + api + .post('/auth/evrythng/users?anonymous=true') .reply(201, { id: 'anonUser', evrythngApiKey: 'evrythngApiKey' }) - mockApi().get('/access') - .reply(200, { actor: { id: 'anonUser'} }) - mockApi().get('/users/anonUser') - .reply(200, { id: 'anonUser'}) + api.get('/access').reply(200, { actor: { id: 'anonUser' } }) + api.get('/users/anonUser').reply(200, { id: 'anonUser' }) const res = await scope.appUser().create({ anonymous: true }) resources.anonUser = res @@ -32,16 +32,18 @@ module.exports = (scopeType) => { }) it('should create and validate a named user', async () => { - mockApi().post('/auth/evrythng/users', USER) + api + .post('/auth/evrythng/users', USER) .reply(201, { evrythngUser: 'evrythngUser', activationCode: 'code' }) - mockApi().post('/auth/evrythng/users/evrythngUser/validate', { activationCode: 'code' }) + api + .post('/auth/evrythng/users/evrythngUser/validate', { activationCode: 'code' }) .reply(201, { id: 'evrythngUser', evrythngApiKey: 'evrythngApiKey' }) - mockApi().get('/access') - .reply(200, { actor: { id: 'evrythngUser'} }) - mockApi().get('/users/evrythngUser') - .reply(200, { id: 'evrythngUser'}) - const res = await scope.appUser().create(USER) - .then(res => res.validate()) + api.get('/access').reply(200, { actor: { id: 'evrythngUser' } }) + api.get('/users/evrythngUser').reply(200, { id: 'evrythngUser' }) + const res = await scope + .appUser() + .create(USER) + .then((res) => res.validate()) resources.namedUser = res expect(res).to.be.an('object') @@ -51,12 +53,11 @@ module.exports = (scopeType) => { it('should login a named user', async () => { const loginDocument = { email: USER.email, password: USER.password } - mockApi().post('/users/login', loginDocument) + api + .post('/users/login', loginDocument) .reply(201, { id: 'evrythngUser', access: { apiKey: 'evrythngApiKey' } }) - mockApi().get('/access') - .reply(200, { actor: { id: 'evrythngUser'} }) - mockApi().get('/users/evrythngUser') - .reply(200, { id: 'evrythngUser'}) + api.get('/access').reply(200, { actor: { id: 'evrythngUser' } }) + api.get('/users/evrythngUser').reply(200, { id: 'evrythngUser' }) const res = await scope.login(loginDocument) expect(res).to.be.an('object') @@ -64,7 +65,7 @@ module.exports = (scopeType) => { }) it('should logout a named user', async () => { - mockApi().post('/auth/all/logout').reply(201, { logout: 'ok' }) + api.post('/auth/all/logout').reply(201, { logout: 'ok' }) const res = await resources.namedUser.logout() expect(res).to.be.an('object') @@ -74,8 +75,7 @@ module.exports = (scopeType) => { } it('should read an Application User', async () => { - mockApi().get('/users/evrythngUser') - .reply(200, { id: 'evrythngUser'}) + api.get('/users/evrythngUser').reply(200, { id: 'evrythngUser' }) const res = await scope.user('evrythngUser').read() expect(res).to.be.an('object') @@ -83,8 +83,7 @@ module.exports = (scopeType) => { }) it('should read all Application Users', async () => { - mockApi().get('/users') - .reply(200, [{ id: 'evrythngUser'}]) + api.get('/users').reply(200, [{ id: 'evrythngUser' }]) const res = await scope.user().read() expect(res).to.be.an('array') @@ -93,8 +92,7 @@ module.exports = (scopeType) => { it('should update an Application User', async () => { const payload = { firstName: 'updated' } - mockApi().put('/users/evrythngUser', payload) - .reply(200, payload) + api.put('/users/evrythngUser', payload).reply(200, payload) const res = await scope.user('evrythngUser').update(payload) expect(res).to.be.an('object') @@ -102,8 +100,7 @@ module.exports = (scopeType) => { }) it('should delete an Application User', async () => { - mockApi().delete('/users/evrythngUser') - .reply(200) + api.delete('/users/evrythngUser').reply(200) await scope.user('evrythngUser').delete() }) }) diff --git a/test/integration/es.spec.js b/test/integration/es.spec.js deleted file mode 100644 index b4a1168..0000000 --- a/test/integration/es.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint-env jasmine */ -import * as EVT from '../../dist/evrythng.es' - -describe('EVT Distribution', () => { - it('should exist', () => { - expect(EVT).toBeDefined() - }) -}) diff --git a/test/integration/misc/alias.spec.js b/test/integration/misc/alias.spec.js new file mode 100644 index 0000000..4f65872 --- /dev/null +++ b/test/integration/misc/alias.spec.js @@ -0,0 +1,59 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') +const evrythng = require('../../../') + +module.exports = (scopeType, url) => { + describe('alias', () => { + let scope, api + + before(() => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it("should alias 'product' to 'sku' for Operator scope", async () => { + evrythng.alias({ product: 'sku' }, 'Operator') + + expect(scope.sku).to.be.a('function') + }) + + it("should use the alias to read all 'sku's", async () => { + api.get('/products').reply(200, [{ id: 'productId' }]) + const skus = await scope.sku().read() + + expect(skus).to.be.an('array') + expect(skus).to.have.length.gte(0) + }) + + it("should use the alias to create a 'sku'", async () => { + const payload = { name: 'Example SKU' } + api.post('/products', payload).reply(200, { id: 'productId' }) + const res = await scope.sku().create(payload) + + expect(res).to.be.an('object') + expect(res.id).to.be.a('string') + }) + + it("should use the alias to read a 'sku'", async () => { + api.get('/products/productId').reply(200, { id: 'productId' }) + const res = await scope.sku('productId').read() + + expect(res).to.be.an('object') + expect(res.id).to.equal('productId') + }) + + it("should use the alias to update a 'sku'", async () => { + const payload = { tags: ['updated'] } + api.put('/products/productId', payload).reply(200, { id: 'productId' }) + const res = await scope.sku('productId').update(payload) + + expect(res).to.be.an('object') + expect(res.id).to.equal('productId') + }) + + it("should use the alias to delete a 'sku'", async () => { + api.delete('/products/productId').reply(200) + await scope.sku('productId').delete() + }) + }) +} diff --git a/test/e2e/misc/api.spec.js b/test/integration/misc/api.spec.js similarity index 58% rename from test/e2e/misc/api.spec.js rename to test/integration/misc/api.spec.js index 2c9fb75..5acdaf6 100644 --- a/test/e2e/misc/api.spec.js +++ b/test/integration/misc/api.spec.js @@ -6,60 +6,72 @@ const apiKey = process.env.OPERATOR_API_KEY const _api = (url, method = 'get', data) => api({ url, method, apiKey, data }) -module.exports = () => { +module.exports = (settings) => { describe('api', () => { + let apiMock + + before(async () => { + apiMock = mockApi(settings.apiUrl) + }) + it('should read access information', async () => { - mockApi().get('/access') - .reply(200, { actor: { type: 'operator' } }) + apiMock.get('/access').reply(200, { actor: { type: 'operator' } }) const res = await _api('/access') expect(res).to.be.an('object') expect(res.actor.type).to.equal('operator') }) + if (settings.apiVersion == 2) { + it('should read access information with /me', async () => { + apiMock.get('/me').reply(200, { actor: { type: 'operator' } }) + const res = await _api('/me') + + expect(res).to.be.an('object') + expect(res.actor.type).to.equal('operator') + }) + } + it('should manipulate some resource', async () => { // Create const payload = { name: 'test' } - mockApi().post('/thngs', payload) - .reply(201, { id: 'thngId' }) + apiMock.post('/thngs', payload).reply(201, { id: 'thngId' }) let res = await _api('/thngs', 'post', payload) expect(res.id).to.be.a('string') // Read - mockApi().get('/thngs/thngId') - .reply(200, { id: 'thngId' }) + apiMock.get('/thngs/thngId').reply(200, { id: 'thngId' }) res = await _api('/thngs/thngId') expect(res.id).to.be.a('string') // Update payload.name = 'updated' - mockApi().put('/thngs/thngId', payload) - .reply(200, payload) + apiMock.put('/thngs/thngId', payload).reply(200, payload) res = await _api('/thngs/thngId', 'put', payload) expect(res.name).to.equal(payload.name) // Delete - mockApi().delete('/thngs/thngId') - .reply(200) + apiMock.delete('/thngs/thngId').reply(200) await _api('/thngs/thngId', 'delete') }) it('should throw a native Error', async () => { + let caughtError = false const payload = { foo: 'bar' } - mockApi().post('/thngs', payload) - .reply(404, { - errors: ['Thng was not found'], - moreInfo: 'https://developers.evrythng.com', - status: 404 - }) + apiMock.post('/thngs', payload).reply(404, { + errors: ['Thng was not found'], + moreInfo: 'https://developers.evrythng.com', + status: 404 + }) try { await _api('/thngs', 'post', payload) throw new Error('Error was not raised by api()') } catch (e) { + caughtError = true expect(e.message).to.be.a('string') const json = JSON.parse(e.message) @@ -67,6 +79,7 @@ module.exports = () => { expect(json.moreInfo).to.be.a('string') expect(json.status).to.be.a('number') } + expect(caughtError).to.be.equal(true) }) }) } diff --git a/test/e2e/misc/find.spec.js b/test/integration/misc/find.spec.js similarity index 64% rename from test/e2e/misc/find.spec.js rename to test/integration/misc/find.spec.js index eccabc7..4b7f4e1 100644 --- a/test/e2e/misc/find.spec.js +++ b/test/integration/misc/find.spec.js @@ -7,18 +7,20 @@ chai.use(chaiAsPromised) const payload = { name: 'Test Thng', identifiers: { serial: '78fd6hsd' } } -module.exports = () => { +module.exports = (scopeType, url) => { describe('find', () => { - let operator + let operator, api before(async () => { - operator = getScope('operator') + operator = getScope(scopeType) + api = mockApi(url) }) it('should find Thngs by identifiers', async () => { - mockApi().get('/thngs?filter=identifiers.serial%3D78fd6hsd') - .reply(200, [payload]) - const res = await operator.thng().find(payload.identifiers) + const payload1 = { name: 'Test Thng', identifiers: { serial: '78fd6hsd' } } + api.get('/thngs?filter=identifiers.serial=78fd6hsd').reply(200, [payload1]) + + const res = await operator.thng().find(payload1.identifiers) expect(res).to.be.an('array') expect(res).to.have.length.gte(1) @@ -27,15 +29,13 @@ module.exports = () => { it('should refuse to find if given more than one key-value', async () => { payload.identifiers.foo = 'bar' - mockApi().get('/thngs?filter=identifiers.serial%3D78fd6hsd') - .reply(200, payload) + api.get('/thngs?filter=identifiers.serial%3D78fd6hsd').reply(200, payload) const attempt = operator.thng().find(payload.identifiers) return expect(attempt).to.eventually.be.rejected }) it('should find Thngs by name', async () => { - mockApi().get('/thngs?filter=name%3DTest%20Thng') - .reply(200, [payload]) + api.get('/thngs?filter=name%3DTest%20Thng').reply(200, [payload]) const res = await operator.thng().find(payload.name) expect(res).to.be.an('array') diff --git a/test/integration/misc/pages.spec.js b/test/integration/misc/pages.spec.js new file mode 100644 index 0000000..7d6dce1 --- /dev/null +++ b/test/integration/misc/pages.spec.js @@ -0,0 +1,35 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, url) => { + describe('pages', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it('should read thngs through an async iterator', async () => { + const params = { perPage: 2 } + const linkUrl = encodeURIComponent(url) + const it = scope.thng().pages({ params }) + api.get('/thngs?perPage=2').reply(200, [{ name: 'Thng 1' }, { name: 'Thng 2' }], { + link: `<${linkUrl}%2Fthngs%3FperPage%3D2%26sortOrder%3DDESCENDING%26nextPageToken%3DU7hXyw5DVQ8QT7fYsbyEpdAp>; rel="next"` + }) + let page = await it.next() + + expect(page.value.length).to.equal(2) + expect(page.done).to.equal(false) + + api + .get('/thngs?perPage=2&sortOrder=DESCENDING&nextPageToken=U7hXyw5DVQ8QT7fYsbyEpdAp') + .reply(200, [{ name: 'Thng 3' }, { name: 'Thng 4' }], { + link: `<${linkUrl}; rel="next"` + }) + page = await it.next() + expect(page.value.length).to.equal(2) + expect(page.done).to.equal(false) + }) + }) +} diff --git a/test/e2e/misc/paramSetters.spec.js b/test/integration/misc/paramSetters.spec.js similarity index 60% rename from test/e2e/misc/paramSetters.spec.js rename to test/integration/misc/paramSetters.spec.js index db0cbe2..f4d09db 100644 --- a/test/e2e/misc/paramSetters.spec.js +++ b/test/integration/misc/paramSetters.spec.js @@ -1,24 +1,26 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (scopeType, url) => { describe('Param Setters', () => { - let operator + let scope, api before(async () => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should set withScopes via setWithScopes', async () => { - mockApi().get('/thngs?withScopes=true') - .reply(200, [{ + api.get('/thngs?withScopes=true').reply(200, [ + { name: 'Thng 1', scopes: { project: [], users: ['all'] } - }]) - const res = await operator.thng().setWithScopes().read() + } + ]) + const res = await scope.thng().setWithScopes().read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) @@ -26,12 +28,13 @@ module.exports = () => { }) it('should set context via setContext', async () => { - mockApi().get('/actions/all?context=true') - .reply(200, [{ + api.get('/actions/all?context=true').reply(200, [ + { type: 'scans', context: { countryCode: 'GB' } - }]) - const res = await operator.action('all').setContext().read() + } + ]) + const res = await scope.action('all').setContext().read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) @@ -39,27 +42,24 @@ module.exports = () => { }) it('should set perPage via setPerPage', async () => { - mockApi().get('/thngs?perPage=1') - .reply(200, [{ name: 'Thng 1' }]) - const res = await operator.thng().setPerPage(1).read() + api.get('/thngs?perPage=1').reply(200, [{ name: 'Thng 1' }]) + const res = await scope.thng().setPerPage(1).read() expect(res).to.be.an('array') expect(res).to.have.length(1) }) it('should set project via setProject', async () => { - mockApi().get('/thngs?project=projectId') - .reply(200, [{ name: 'Thng 1' }]) - const res = await operator.thng().setProject('projectId').read() + api.get('/thngs?project=projectId').reply(200, [{ name: 'Thng 1' }]) + const res = await scope.thng().setProject('projectId').read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) }) it('should set filter via setFilter', async () => { - mockApi().get('/thngs?filter=name%3DTest') - .reply(200, [{ name: 'Test' }]) - const res = await operator.thng().setFilter('name=Test').read() + api.get('/thngs?filter=name%3DTest').reply(200, [{ name: 'Test' }]) + const res = await scope.thng().setFilter('name=Test').read() expect(res).to.be.an('array') expect(res).to.have.length.gte(1) @@ -67,23 +67,27 @@ module.exports = () => { it('should set ids via setIds', async () => { const payload = [{ id: 'thngId1' }, { id: 'thngId2' }] - mockApi().get('/thngs?ids=thngId1%2CthngId2') - .reply(200, payload) - const res = await operator.thng().setIds(payload.map(p => p.id)).read() + api.get('/thngs?ids=thngId1%2CthngId2').reply(200, payload) + const res = await scope + .thng() + .setIds(payload.map((p) => p.id)) + .read() expect(res.length).to.equal(payload.length) }) it('should allow chaining of multiple param setters', async () => { - mockApi().get('/thngs?project=projectId&filter=name%3DTest&perPage=1&withScopes=true') - .reply(200, [{ + api.get('/thngs?project=projectId&filter=name%3DTest&perPage=1&withScopes=true').reply(200, [ + { name: 'Test', scopes: { project: [], users: ['all'] } - }]) - const res = await operator.thng() + } + ]) + const res = await scope + .thng() .setProject('projectId') .setFilter('name=Test') .setPerPage(1) diff --git a/test/e2e/misc/rescope.spec.js b/test/integration/misc/rescope.spec.js similarity index 58% rename from test/e2e/misc/rescope.spec.js rename to test/integration/misc/rescope.spec.js index f22c364..3ff85be 100644 --- a/test/e2e/misc/rescope.spec.js +++ b/test/integration/misc/rescope.spec.js @@ -1,84 +1,84 @@ -const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (scopeType, url) => { describe('rescope', () => { - let operator + let scope, api before(async () => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should remove all scopes', async () => { - mockApi().get('/thngs/thngId?withScopes=true') - .reply(200, { - scopes: { - projects: ['projectId'], - users: ['all'] - } - }) - mockApi().put('/thngs/thngId', { + api.get('/thngs/thngId?withScopes=true').reply(200, { scopes: { projects: ['projectId'], - users: [] + users: ['all'] } }) - .reply(200, {}) - await operator.thng('thngId').rescope(['projectId'], []) - }) - - it('should set one project scope', async () => { - mockApi().get('/thngs/thngId?withScopes=true') - .reply(200, { + api + .put('/thngs/thngId', { scopes: { projects: ['projectId'], - users: ['all'] + users: [] } }) - mockApi().put('/thngs/thngId', { + .reply(200, {}) + await scope.thng('thngId').rescope(['projectId'], []) + }) + + it('should set one project scope', async () => { + api.get('/thngs/thngId?withScopes=true').reply(200, { scopes: { projects: ['projectId'], users: ['all'] } }) - .reply(200, {}) - await operator.thng('thngId').rescope(['projectId']) - }) - - it('should set all users scope', async () => { - mockApi().get('/thngs/thngId?withScopes=true') - .reply(200, { + api + .put('/thngs/thngId', { scopes: { projects: ['projectId'], - users: [] + users: ['all'] } }) - mockApi().put('/thngs/thngId', { + .reply(200, {}) + await scope.thng('thngId').rescope(['projectId']) + }) + + it('should set all users scope', async () => { + api.get('/thngs/thngId?withScopes=true').reply(200, { scopes: { projects: ['projectId'], - users: ['all'] + users: [] } }) - .reply(200, {}) - await operator.thng('thngId').rescope(['projectId'], ['all']) - }) - - it('should remove only the project scopes', async () => { - mockApi().get('/thngs/thngId?withScopes=true') - .reply(200, { + api + .put('/thngs/thngId', { scopes: { projects: ['projectId'], - users: ['userId'] + users: ['all'] } }) - mockApi().put('/thngs/thngId', { + .reply(200, {}) + await scope.thng('thngId').rescope(['projectId'], ['all']) + }) + + it('should remove only the project scopes', async () => { + api.get('/thngs/thngId?withScopes=true').reply(200, { scopes: { - projects: [], + projects: ['projectId'], users: ['userId'] } }) + api + .put('/thngs/thngId', { + scopes: { + projects: [], + users: ['userId'] + } + }) .reply(200, {}) - await operator.thng('thngId').rescope([]) + await scope.thng('thngId').rescope([]) }) }) } diff --git a/test/integration/misc/stream.spec.js b/test/integration/misc/stream.spec.js new file mode 100644 index 0000000..1660631 --- /dev/null +++ b/test/integration/misc/stream.spec.js @@ -0,0 +1,36 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, url) => { + describe('stream', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it('should stream Thngs once at a time', (done) => { + const linkUrl = encodeURIComponent(url) + api.get('/thngs').reply(200, [{ name: 'Thng 1' }, { name: 'Thng 2' }], { + link: `<${linkUrl}%2Fthngs%3FperPage%3D30%26sortOrder%3DDESCENDING%26nextPageToken%3DU7hXyw5DVQ8QT7fYsbyEpdAp>; rel="next"` + }) + api + .get('/thngs?perPage=30&sortOrder=DESCENDING&nextPageToken=U7hXyw5DVQ8QT7fYsbyEpdAp') + .reply(200, [{ name: 'Thng 3' }, { name: 'Thng 4' }], { + link: `<${linkUrl}%2Fthngs%3FperPage%3D2%26sortOrder%3DDESCENDING%26nextPageToken%3DUprntQaysgRph8aRwFTAKPtn>; rel="next"` + }) + const cb = (item, index) => { + expect(item.name).to.be.a('string') + expect(index).to.be.a('number') + + if (index === 2) { + done() + return true + } + } + + scope.thng().stream(cb) + }) + }) +} diff --git a/test/integration/misc/streamPages.spec.js b/test/integration/misc/streamPages.spec.js new file mode 100644 index 0000000..9e90b53 --- /dev/null +++ b/test/integration/misc/streamPages.spec.js @@ -0,0 +1,38 @@ +const { expect } = require('chai') +const { getScope, mockApi } = require('../util') + +module.exports = (scopeType, url) => { + describe('streamPages', () => { + let scope, api + + before(async () => { + scope = getScope(scopeType) + api = mockApi(url) + }) + + it('should stream pages of Thngs', (done) => { + const linkUrl = encodeURIComponent(url) + api.get('/thngs').reply(200, [{ name: 'Thng 1' }, { name: 'Thng 2' }], { + link: `<${linkUrl}%2Fthngs%3FperPage%3D30%26sortOrder%3DDESCENDING%26nextPageToken%3DU7hXyw5DVQ8QT7fYsbyEpdAp>; rel="next"` + }) + api + .get('/thngs?perPage=30&sortOrder=DESCENDING&nextPageToken=U7hXyw5DVQ8QT7fYsbyEpdAp') + .reply(200, [{ name: 'Thng 3' }, { name: 'Thng 4' }], { + link: `<${linkUrl}%2Fthngs%3FperPage%3D2%26sortOrder%3DDESCENDING%26nextPageToken%3DUprntQaysgRph8aRwFTAKPtn>; rel="next"` + }) + const eachPageCb = (page, totalSoFar) => { + expect(page.length).to.equal(2) + + const [item] = page + expect(item.name).to.be.a('string') + + if (totalSoFar === 2) { + done() + return true + } + } + + scope.thng().streamPages(eachPageCb) + }) + }) +} diff --git a/test/e2e/misc/upsert.spec.js b/test/integration/misc/upsert.spec.js similarity index 52% rename from test/e2e/misc/upsert.spec.js rename to test/integration/misc/upsert.spec.js index 4eab462..8187343 100644 --- a/test/e2e/misc/upsert.spec.js +++ b/test/integration/misc/upsert.spec.js @@ -7,20 +7,19 @@ chai.use(chaiAsPromised) const payload = { name: 'Test Thng', identifiers: { serial: '8230947' } } -module.exports = () => { +module.exports = (scopeType, url) => { describe('upsert', () => { - let operator + let scope, api before(async () => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) }) it('should create a Thng by identifiers', async () => { - mockApi().get('/thngs?filter=identifiers.serial%3D8230947') - .reply(200, []) - mockApi().post('/thngs', payload) - .reply(201, payload) - const res = await operator.thng().upsert(payload, payload.identifiers) + api.get('/thngs?filter=identifiers.serial%3D8230947').reply(200, []) + api.post('/thngs', payload).reply(201, payload) + const res = await scope.thng().upsert(payload, payload.identifiers) expect(res).to.be.an('object') expect(res.name).to.equal(payload.name) @@ -29,42 +28,41 @@ module.exports = () => { it('should update the same Thng by identifiers', async () => { payload.name = 'Updated Thng' - mockApi().get('/thngs?filter=identifiers.serial%3D8230947') + api + .get('/thngs?filter=identifiers.serial%3D8230947') .reply(200, [{ id: 'thngId', name: 'Updated Thng' }]) - mockApi().put('/thngs/thngId', payload) - .reply(200, payload) - const res = await operator.thng().upsert(payload, payload.identifiers) + api.put('/thngs/thngId', payload).reply(200, payload) + const res = await scope.thng().upsert(payload, payload.identifiers) expect(res).to.be.an('object') expect(res.name).to.equal(payload.name) }) it('should refuse to update if more than one Thng is found', async () => { - mockApi().get('/thngs?filter=identifiers.serial%3D8230947') + api + .get('/thngs?filter=identifiers.serial%3D8230947') .reply(200, [{ id: 'thngId' }, { id: 'thngId2' }]) - const attempt = operator.thng().upsert(payload, payload.identifiers) + const attempt = scope.thng().upsert(payload, payload.identifiers) return expect(attempt).to.eventually.be.rejected }) it('should allow overriding with allowPlural', async () => { payload.name = 'Twice Updated Thng' - mockApi().get('/thngs?filter=identifiers.serial%3D8230947') + api + .get('/thngs?filter=identifiers.serial%3D8230947') .reply(200, [{ id: 'thngId' }, { id: 'thngId2' }]) - mockApi().put('/thngs/thngId', payload) - .reply(200, { id: 'thngId' }) - const res = await operator.thng().upsert(payload, payload.identifiers, true) + api.put('/thngs/thngId', payload).reply(200, { id: 'thngId' }) + const res = await scope.thng().upsert(payload, payload.identifiers, true) expect(res).to.be.an('object') expect(res.id).to.equal('thngId') }) it('should create a Thng by name', async () => { - payload.name = `New Thng Name` - mockApi().get('/thngs?filter=name%3DNew%20Thng%20Name') - .reply(200, []) - mockApi().post('/thngs', payload) - .reply(200, { id: 'thngId', name: 'New Thng Name' }) - const res = await operator.thng().upsert(payload, payload.name) + payload.name = 'New Thng Name' + api.get('/thngs?filter=name%3DNew%20Thng%20Name').reply(200, []) + api.post('/thngs', payload).reply(200, { id: 'thngId', name: 'New Thng Name' }) + const res = await scope.thng().upsert(payload, payload.name) expect(res).to.be.an('object') expect(res.name).to.equal(payload.name) @@ -72,11 +70,9 @@ module.exports = () => { it('should update a Thng by name', async () => { payload.tags = ['test', 'tags'] - mockApi().get('/thngs?filter=name%3DNew%20Thng%20Name') - .reply(200, [{ id: 'thngId' }]) - mockApi().put('/thngs/thngId', payload) - .reply(200, payload) - const res = await operator.thng().upsert(payload, payload.name) + api.get('/thngs?filter=name%3DNew%20Thng%20Name').reply(200, [{ id: 'thngId' }]) + api.put('/thngs/thngId', payload).reply(200, payload) + const res = await scope.thng().upsert(payload, payload.name) expect(res).to.be.an('object') expect(res.name).to.equal(payload.name) diff --git a/test/e2e/misc/use.spec.js b/test/integration/misc/use.spec.js similarity index 75% rename from test/e2e/misc/use.spec.js rename to test/integration/misc/use.spec.js index 222d4b8..2e0b4ed 100644 --- a/test/e2e/misc/use.spec.js +++ b/test/integration/misc/use.spec.js @@ -14,17 +14,17 @@ const TestPlugin = { } } -module.exports = () => { +module.exports = (scopeType, url) => { describe('use', () => { - let operator, thng + let scope, api, thng before(async () => { - operator = getScope('operator') + scope = getScope(scopeType) + api = mockApi(url) const payload = { name: 'test' } - mockApi().post('/thngs', payload) - .reply(201, payload) - thng = await operator.thng().create(payload) + api.post('/thngs', payload).reply(201, payload) + thng = await scope.thng().create(payload) }) it('should not throw when installing a plugin', async () => { @@ -33,8 +33,8 @@ module.exports = () => { it('should extend a scope with a new method', async () => { expect(evrythng.Operator.prototype.getFoo).to.be.a('function') - expect(operator.getFoo).to.be.a('function') - expect(operator.getFoo()).to.equal('foo') + expect(scope.getFoo).to.be.a('function') + expect(scope.getFoo()).to.equal('foo') }) it('should expect an entity with a new method', async () => { diff --git a/test/e2e/scope/actionApp.spec.js b/test/integration/scope/actionApp.spec.js similarity index 75% rename from test/e2e/scope/actionApp.spec.js rename to test/integration/scope/actionApp.spec.js index b8a71b4..f09c6d3 100644 --- a/test/e2e/scope/actionApp.spec.js +++ b/test/integration/scope/actionApp.spec.js @@ -1,32 +1,35 @@ const { expect } = require('chai') -const { getScope, mockApi } = require('../util') +const { mockApi } = require('../util') const { ActionApp } = require('../../../') // Mocks global.localStorage = { data: {}, - getItem: key => global.localStorage.data[key], + getItem: (key) => global.localStorage.data[key], setItem: (key, value) => { global.localStorage.data[key] = value - }, + } } global.window = { location: { href: 'mocha test location' } } -module.exports = () => { +module.exports = (url) => { describe('ActionApp', () => { - let operator, actionApp + let actionApp, api before(async () => { - mockApi().get('/access').times(2) + api = mockApi(url) + api + .get('/access') + .times(2) .reply(200, { actor: { id: 'actorId' } }) - mockApi().get('/applications/me') - .reply(200, { id: 'applicationId' }) - mockApi().post('/auth/evrythng/users?anonymous=true') - .reply(201, { evrythngApiKey: '12341234123412341234123412341234123412341234123412341234123412341234123412341234' }) - mockApi().get('/users/actorId') - .reply(200, { id: 'U5FfSmUtQt4emDwaR3hw2tfc' }) + api.get('/applications/me').reply(200, { id: 'applicationId' }) + api.post('/auth/evrythng/users?anonymous=true').reply(201, { + evrythngApiKey: + '12341234123412341234123412341234123412341234123412341234123412341234123412341234' + }) + api.get('/users/actorId').reply(200, { id: 'U5FfSmUtQt4emDwaR3hw2tfc' }) actionApp = new ActionApp('actionAppApiKey') await actionApp.init() @@ -38,10 +41,9 @@ module.exports = () => { expect(actionApp.getAnonymousUser).to.be.a('function') }) - it('should create a \'_PageVisited\' action with pageVisited()', async () => { - mockApi().get('/actions?filter=name%3D_PageVisited') - .reply(200, [{ name: '_PageVisited' }]) - mockApi() + it("should create a '_PageVisited' action with pageVisited()", async () => { + api.get('/actions?filter=name%3D_PageVisited').reply(200, [{ name: '_PageVisited' }]) + api .post('/actions/_PageVisited', { type: '_PageVisited', customFields: { url: global.window.location.href } @@ -60,9 +62,8 @@ module.exports = () => { }) it('should create an action with custom data', async () => { - mockApi().get('/actions?filter=name%3D_PageVisited') - .reply(200, [{ name: '_PageVisited' }]) - mockApi() + api.get('/actions?filter=name%3D_PageVisited').reply(200, [{ name: '_PageVisited' }]) + api .post('/actions/_PageVisited', { type: '_PageVisited', customFields: { foo: 'bar' } @@ -90,11 +91,11 @@ module.exports = () => { it('should create a scans action with a Thng specified', async () => { const thng = 'UKYDHeYCQbgDppRwRkHVMHhg' - mockApi() + api .post('/actions/scans', { type: 'scans', customFields: { foo: 'bar' }, - thng, + thng }) .reply(201, { id: 'actionId', @@ -112,11 +113,11 @@ module.exports = () => { it('should create a scans action with a product specified', async () => { const product = 'UKYDHeYCQbgDppRwRkHVMHhg' - mockApi() + api .post('/actions/scans', { type: 'scans', customFields: { foo: 'bar' }, - product, + product }) .reply(201, { id: 'actionId', @@ -133,12 +134,12 @@ module.exports = () => { }) it('should re-use previous Application User credentials', async () => { - mockApi().get('/access').times(2) + api + .get('/access') + .times(2) .reply(200, { actor: { id: 'actorId' } }) - mockApi().get('/applications/me') - .reply(200, { id: 'applicationId' }) - mockApi().get('/users/actorId') - .reply(200, { id: 'U5FfSmUtQt4emDwaR3hw2tfc' }) + api.get('/applications/me').reply(200, { id: 'applicationId' }) + api.get('/users/actorId').reply(200, { id: 'U5FfSmUtQt4emDwaR3hw2tfc' }) const otherActionApp = new ActionApp('actionAppApiKey') await otherActionApp.init() diff --git a/test/e2e/scope/device.spec.js b/test/integration/scope/device.spec.js similarity index 68% rename from test/e2e/scope/device.spec.js rename to test/integration/scope/device.spec.js index 5aab899..03aeea3 100644 --- a/test/e2e/scope/device.spec.js +++ b/test/integration/scope/device.spec.js @@ -1,17 +1,17 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (url) => { describe('Device', () => { - let device + let device, api before(() => { device = getScope('device') + api = mockApi(url) }) it('should represent a Thng', async () => { - mockApi().get('/thngs/deviceThngId') - .reply(200, { id: 'deviceThngId', apiKey: 'apiKey' }) + api.get('/thngs/deviceThngId').reply(200, { id: 'deviceThngId', apiKey: 'apiKey' }) const res = await device.init() expect(res).to.be.an('object') diff --git a/test/e2e/scope/operator.spec.js b/test/integration/scope/operator.spec.js similarity index 83% rename from test/e2e/scope/operator.spec.js rename to test/integration/scope/operator.spec.js index 961287d..bf9518d 100644 --- a/test/e2e/scope/operator.spec.js +++ b/test/integration/scope/operator.spec.js @@ -1,12 +1,13 @@ const { expect } = require('chai') const { getScope, mockApi } = require('../util') -module.exports = () => { +module.exports = (url) => { describe('Operator', () => { - let operator + let operator, api before(() => { operator = getScope('operator') + api = mockApi(url) }) it('should read Operator resource', async () => { @@ -17,10 +18,9 @@ module.exports = () => { }) it('should allow self-same Operator update', async () => { - mockApi().put('/operators/operatorId') - .reply(200, { - customFields: { foo: 'bar' } - }) + api.put('/operators/operatorId').reply(200, { + customFields: { foo: 'bar' } + }) const res = await operator.update({ customFields: { foo: 'bar' diff --git a/test/integration/testsForApiVersion1.spec.js b/test/integration/testsForApiVersion1.spec.js new file mode 100644 index 0000000..006aea5 --- /dev/null +++ b/test/integration/testsForApiVersion1.spec.js @@ -0,0 +1,111 @@ +const { setupForApiVersion1 } = require('./util') +const evrythng = require('../../dist/evrythng.node') + +describe('evrythng.js for apiVersion = 1', function () { + let settings = evrythng.setup({ apiVersion: 1, geolocation: false }) + const { apiUrl, apiVersion } = settings + + before(async () => { + settings = await evrythng.setup({ apiVersion: 1, geolocation: false }) + await setupForApiVersion1(apiUrl) + }) + + describe(`as Application for API v${apiVersion}`, () => { + require('./entity/user.spec')('application', apiUrl) + }) + + describe(`as anonymous Application User for API ${apiVersion}`, () => { + const scopeType = 'anonUser' + require('./entity/actions.spec')(scopeType, apiUrl) + require('./entity/actionTypes.spec')(scopeType, apiUrl) + require('./entity/collections.spec')(scopeType, apiUrl) + require('./entity/commissionState.spec')(scopeType, apiUrl) + require('./entity/places.spec')(scopeType, apiUrl) + require('./entity/products.spec')(scopeType, apiUrl) + require('./entity/properties.spec')(scopeType, 'product', apiUrl) + require('./entity/properties.spec')(scopeType, 'thng', apiUrl) + require('./entity/purchaseOrders.spec')(scopeType, apiUrl) + require('./entity/roles.spec')(scopeType, apiUrl) + require('./entity/thngs.spec')(scopeType, apiUrl) + }) + + describe(`as Trusted Application for API v${apiVersion}`, () => { + const scopeType = 'trustedApplication' + require('./entity/actions.spec')(scopeType, apiUrl) + require('./entity/actionTypes.spec')(scopeType, apiUrl) + require('./entity/collections.spec')(scopeType, apiUrl) + require('./entity/commissionState.spec')(scopeType, apiUrl) + require('./entity/places.spec')(scopeType, apiUrl) + require('./entity/products.spec')(scopeType, apiUrl) + require('./entity/properties.spec')(scopeType, 'product', apiUrl) + require('./entity/properties.spec')(scopeType, 'thng', apiUrl) + require('./entity/purchaseOrders.spec')(scopeType, apiUrl) + require('./entity/thngs.spec')(scopeType, apiUrl) + }) + + describe(`as Operator for API v${apiVersion}`, () => { + const scopeType = 'operator' + require('./entity/accesses.spec')(scopeType, apiUrl) + require('./entity/accountRedirector.spec')(scopeType, apiUrl) + require('./entity/accounts.spec')(scopeType, apiUrl) + require('./entity/actions.spec')(scopeType, apiUrl) + require('./entity/actionTypes.spec')(scopeType, apiUrl) + require('./entity/adiOrders.spec')(scopeType, apiUrl) + require('./entity/adiOrderEvents.spec')(scopeType, apiUrl) + require('./entity/applicationRedirector.spec')(scopeType, apiUrl) + require('./entity/applications.spec')(scopeType, apiUrl) + require('./entity/batches.spec')(scopeType, settings) + require('./entity/collections.spec')(scopeType, apiUrl) + require('./entity/commissionState.spec')(scopeType, apiUrl) + require('./entity/domains.spec')(scopeType, apiUrl) + require('./entity/files.spec')(scopeType, apiUrl) + require('./entity/locations.spec')(scopeType, apiUrl) + require('./entity/me.spec')(scopeType, settings) + require('./entity/permissions.spec')(scopeType, apiUrl) + require('./entity/permissions.spec')('userInApp', apiUrl) + require('./entity/places.spec')(scopeType, apiUrl) + require('./entity/products.spec')(scopeType, apiUrl) + require('./entity/projects.spec')(scopeType, apiUrl) + require('./entity/properties.spec')(scopeType, 'product', apiUrl) + require('./entity/properties.spec')(scopeType, 'thng', apiUrl) + require('./entity/purchaseOrders.spec')(scopeType, apiUrl) + require('./entity/reactor.spec')(scopeType, apiUrl) + require('./entity/redirection.spec')(scopeType, 'product', apiUrl) + require('./entity/redirection.spec')(scopeType, 'thng', apiUrl) + require('./entity/roles.spec')(scopeType, apiUrl) + require('./entity/rules.spec')(scopeType, apiUrl) + require('./entity/secretKey.spec')(scopeType, apiUrl) + require('./entity/shipmentNotice.spec')(scopeType, apiUrl) + require('./entity/shortDomains.spec')(scopeType, apiUrl) + require('./entity/tasks.spec')(scopeType, apiUrl) + require('./entity/thngs.spec')(scopeType, apiUrl) + require('./entity/user.spec')(scopeType, apiUrl) + }) + + describe(`as Device for API v${apiVersion}`, () => { + const scopeType = 'device' + require('./scope/device.spec')(scopeType, apiUrl) + }) + + describe(`as ActionAppfor API v${apiVersion}`, () => { + require('./scope/actionApp.spec')(apiUrl) + }) + + describe(`as Operator for API v${apiVersion}`, () => { + require('./scope/operator.spec')(apiUrl) + }) + + describe(`Misc for API v${apiVersion}`, () => { + const scopeType = 'operator' + require('./misc/alias.spec')(scopeType, apiUrl) + require('./misc/api.spec')(settings) + require('./misc/find.spec')(scopeType, apiUrl) + require('./misc/pages.spec')(scopeType, apiUrl) + require('./misc/paramSetters.spec')(scopeType, apiUrl) + require('./misc/rescope.spec')(scopeType, apiUrl) + require('./misc/stream.spec')(scopeType, apiUrl) + require('./misc/streamPages.spec')(scopeType, apiUrl) + require('./misc/upsert.spec')(scopeType, apiUrl) + require('./misc/use.spec')(scopeType, apiUrl) + }) +}) diff --git a/test/integration/testsForApiVersion2.spec.js b/test/integration/testsForApiVersion2.spec.js new file mode 100644 index 0000000..1757b6d --- /dev/null +++ b/test/integration/testsForApiVersion2.spec.js @@ -0,0 +1,95 @@ +const { setupForApiVersion2 } = require('./util') +const evrythng = require('../../dist/evrythng.node') + +describe('evrythng.js for apiVersion = 2', function () { + let settings = evrythng.setup({ apiVersion: 2, geolocation: false }) + const { apiUrl, apiVersion } = settings + + before(async () => { + settings = await evrythng.setup({ apiVersion: 2, geolocation: false }) + await setupForApiVersion2(apiUrl) + }) + + describe(`as Operator for API v${apiVersion}`, () => { + console.log(settings) + const scopeType = 'operator' + require('./entity/accessPolicies.spec')(scopeType, settings) + require('./entity/accessTokens.spec')(scopeType, settings) + require('./entity/accountRedirector.spec')(scopeType, apiUrl) + require('./entity/accounts.spec')(scopeType, apiUrl) + require('./entity/actions.spec')(scopeType, apiUrl) + require('./entity/actionTypes.spec')(scopeType, apiUrl) + require('./entity/adiOrders.spec')(scopeType, apiUrl) + require('./entity/adiOrderEvents.spec')(scopeType, apiUrl) + require('./entity/applicationRedirector.spec')(scopeType, apiUrl) + require('./entity/applications.spec')(scopeType, apiUrl) + require('./entity/batches.spec')(scopeType, settings) + require('./entity/collections.spec')(scopeType, apiUrl) + require('./entity/commissionState.spec')(scopeType, apiUrl) + require('./entity/domains.spec')(scopeType, apiUrl) + require('./entity/files.spec')(scopeType, apiUrl) + require('./entity/locations.spec')(scopeType, apiUrl) + require('./entity/me.spec')(scopeType, settings) + require('./entity/operatorAccesses.spec')(scopeType, settings) + require('./entity/places.spec')(scopeType, apiUrl) + require('./entity/products.spec')(scopeType, apiUrl) + require('./entity/projects.spec')(scopeType, apiUrl) + require('./entity/properties.spec')(scopeType, 'product', apiUrl) + require('./entity/properties.spec')(scopeType, 'thng', apiUrl) + require('./entity/purchaseOrders.spec')(scopeType, apiUrl) + require('./entity/reactor.spec')(scopeType, apiUrl) + require('./entity/redirection.spec')(scopeType, 'product') + require('./entity/redirection.spec')(scopeType, 'thng') + require('./entity/shipmentNotice.spec')(scopeType, apiUrl) + require('./entity/shortDomains.spec')(scopeType, apiUrl) + require('./entity/thngs.spec')(scopeType, apiUrl) + }) + describe(`as Access Token for API v${apiVersion}`, () => { + const scopeType = 'accessToken' + require('./entity/accessPolicies.spec')(scopeType, settings) + require('./entity/accessTokens.spec')(scopeType, settings) + require('./entity/accountRedirector.spec')(scopeType, apiUrl) + require('./entity/accounts.spec')(scopeType, apiUrl) + require('./entity/actions.spec')(scopeType, apiUrl) + require('./entity/actionTypes.spec')(scopeType, apiUrl) + require('./entity/adiOrders.spec')(scopeType, apiUrl) + require('./entity/adiOrderEvents.spec')(scopeType, apiUrl) + require('./entity/applicationRedirector.spec')(scopeType, apiUrl) + require('./entity/applications.spec')(scopeType, apiUrl) + require('./entity/batches.spec')(scopeType, settings) + require('./entity/collections.spec')(scopeType, apiUrl) + require('./entity/commissionState.spec')(scopeType, apiUrl) + require('./entity/domains.spec')(scopeType, apiUrl) + require('./entity/files.spec')(scopeType, apiUrl) + require('./entity/locations.spec')(scopeType, apiUrl) + require('./entity/me.spec')(scopeType, settings) + require('./entity/operatorAccesses.spec')(scopeType, settings) + require('./entity/places.spec')(scopeType, apiUrl) + require('./entity/products.spec')(scopeType, apiUrl) + require('./entity/projects.spec')(scopeType, apiUrl) + require('./entity/properties.spec')(scopeType, 'product', apiUrl) + require('./entity/properties.spec')(scopeType, 'thng', apiUrl) + require('./entity/purchaseOrders.spec')(scopeType, apiUrl) + require('./entity/reactor.spec')(scopeType, apiUrl) + require('./entity/redirection.spec')(scopeType, 'product') + require('./entity/redirection.spec')(scopeType, 'thng') + require('./entity/shipmentNotice.spec')(scopeType, apiUrl) + require('./entity/shortDomains.spec')(scopeType, apiUrl) + require('./entity/thngs.spec')(scopeType, apiUrl) + }) + describe(`as Operator for API v${apiVersion}`, () => { + require('./scope/operator.spec')(apiUrl) + }) + + describe(`Misc for API v${apiVersion}`, () => { + const scopeType = 'operator' + require('./misc/api.spec')(settings) + require('./misc/find.spec')(scopeType, apiUrl) + require('./misc/pages.spec')(scopeType, apiUrl) + require('./misc/paramSetters.spec')(scopeType, apiUrl) + require('./misc/stream.spec')(scopeType, apiUrl) + require('./misc/streamPages.spec')(scopeType, apiUrl) + require('./misc/upsert.spec')(scopeType, apiUrl) + require('./misc/use.spec')(scopeType, apiUrl) + }) +}) diff --git a/test/integration/umd.spec.js b/test/integration/umd.spec.js deleted file mode 100644 index 7faf2c2..0000000 --- a/test/integration/umd.spec.js +++ /dev/null @@ -1,18 +0,0 @@ -(function (root, factory) { - /* global define */ - if (typeof define === 'function' && define.amd) { - define(['evrythng'], factory) - } else if (typeof module === 'object' && module.exports) { - factory(require('../../dist/evrythng.polyfill')) - } else { - factory(root.EVT) - } -}(this, function factory (EVT) { - /* eslint-env jasmine */ - - describe('EVT Distribution', () => { - it('should exist', () => { - expect(EVT).toBeDefined() - }) - }) -})) diff --git a/test/integration/util.js b/test/integration/util.js new file mode 100644 index 0000000..e1538b1 --- /dev/null +++ b/test/integration/util.js @@ -0,0 +1,146 @@ +const { + Operator, + Application, + TrustedApplication, + Device, + api, + AccessToken +} = require('../../dist/evrythng.node') +const nock = require('nock') + +const OPERATOR_API_KEY = 'OPERATOR_API_KEY' + +let scopes = {} +const resources = {} + +/** + * Mock an API response with nock. + * + * @param {string} [apiUrl] - Override API URL from the default. + * @returns {object} nock mock. + */ + +const mockApi = (apiUrl) => nock(apiUrl) + +/** + * Initialise reusable entities in the specified Platform account. + */ +const setupForApiVersion1 = async (apiUrl) => { + mockApi(apiUrl) + .get('/access') + .reply(200, { actor: { id: 'operatorId' } }) + mockApi(apiUrl).get('/operators/operatorId').reply(200, { + id: 'operatorId', + createdAt: 1471862430968, + updatedAt: 1607002260749, + email: 'test.user@evrythng.com', + firstName: 'Test', + lastName: 'User' + }) + const operator = new Operator(OPERATOR_API_KEY) + + const projectPayload = { name: 'Test Project' } + mockApi(apiUrl) + .post('/projects', projectPayload) + .reply(201, { name: 'Test Project', id: 'projectId' }) + const appProject = await operator.project().create(projectPayload) + + const appPayload = { name: 'Test App', socialNetworks: {} } + mockApi(apiUrl).post('/projects/projectId/applications', appPayload).reply(201, { + name: 'Test App', + socialNetworks: {}, + id: 'applicationId', + appApiKey: 'appApiKey' + }) + const appResource = await operator.project(appProject.id).application().create(appPayload) + mockApi(apiUrl) + .get('/access') + .reply(200, { actor: { id: 'applicationId' } }) + mockApi(apiUrl).get('/applications/me').reply(200, { id: 'applicationId' }) + const application = new Application(appResource.appApiKey) + + mockApi(apiUrl) + .get('/projects/projectId/applications/applicationId/secretKey') + .reply(200, { secretApiKey: 'secretApiKey' }) + const { secretApiKey } = await api({ + url: '/projects/projectId/applications/applicationId/secretKey', + apiKey: operator.apiKey + }) + mockApi(apiUrl) + .get('/access') + .reply(200, { actor: { id: 'applicationId' } }) + mockApi(apiUrl).get('/applications/me').reply(200, { id: 'applicationId' }) + const trustedApplication = new TrustedApplication(secretApiKey) + + mockApi(apiUrl) + .post('/auth/evrythng/users?anonymous=true') + .reply(201, { id: 'evrythngUser', evrythngApiKey: 'evrythngApiKey' }) + mockApi(apiUrl) + .get('/access') + .reply(200, { actor: { id: 'evrythngUser' } }) + mockApi(apiUrl).get('/users/evrythngUser').reply(200, { id: 'evrythngUser' }) + const anonUser = await application.appUser().create({ anonymous: true }) + + mockApi(apiUrl).post('/thngs').reply(201, { id: 'deviceThngId' }) + const deviceThng = await operator.thng().create({ name: 'Test Device' }) + mockApi(apiUrl) + .post('/auth/evrythng/thngs', { thngId: 'deviceThngId' }) + .reply(201, { thngId: 'deviceThngId', thngApiKey: 'thngApiKey' }) + const { thngApiKey } = await api({ + url: '/auth/evrythng/thngs', + method: 'post', + apiKey: operator.apiKey, + data: { thngId: deviceThng.id } + }) + mockApi(apiUrl) + .get('/access') + .reply(200, { actor: { id: 'deviceThngId' } }) + mockApi(apiUrl).get('/thngs/deviceThngId').reply(200, { id: 'deviceThngId' }) + const device = new Device(thngApiKey) + + scopes = { + operator, + application, + trustedApplication, + anonUser, + device + } + return scopes +} + +const setupForApiVersion2 = async (apiUrl) => { + mockApi(apiUrl) + .get('/access') + .reply(200, { actor: { id: 'operatorId' } }) + mockApi(apiUrl).get('/operators/operatorId').reply(200, { + id: 'operatorId', + createdAt: 1471862430968, + updatedAt: 1607002260749, + email: 'test.user@evrythng.com', + firstName: 'Test', + lastName: 'User' + }) + const operator = new Operator(OPERATOR_API_KEY) + + const accessTokenApiKey = 'accessTokenApiKey' + mockApi(apiUrl) + .get('/access') + .reply(200, { actor: { id: 'accessTokenId' } }) + const accessToken = new AccessToken(accessTokenApiKey) + + scopes = { + operator, + accessToken + } + return scopes +} + +const getScope = (type) => scopes[type] + +module.exports = { + setupForApiVersion1, + setupForApiVersion2, + getScope, + mockApi, + resources +} diff --git a/test/require-main.js b/test/require-main.js index 39d6896..09ff701 100644 --- a/test/require-main.js +++ b/test/require-main.js @@ -1,13 +1,13 @@ const paths = { - 'evrythng': 'evrythng.polyfill', + evrythng: 'evrythng.polyfill', 'isomorphic-fetch': '../node_modules/whatwg-fetch/fetch' } if (typeof module !== 'undefined') { module.exports = paths } else { - let tests = [] - for (let file in window.__karma__.files) { + const tests = [] + for (const file in window.__karma__.files) { if (window.__karma__.files.hasOwnProperty(file)) { if (/spec\.js$/.test(file)) { tests.push(file) diff --git a/test/unit/entity/Location.spec.js b/test/unit/entity/Location.spec.js deleted file mode 100644 index d288517..0000000 --- a/test/unit/entity/Location.spec.js +++ /dev/null @@ -1,130 +0,0 @@ -/* eslint-env jasmine */ -import Resource from '../../../src/resource/Resource' -import Location from '../../../src/entity/Location' -import setup from '../../../src/setup' -import mockApi from '../../helpers/apiMock' -import paths from '../../helpers/paths' -import { dummyResource } from '../../helpers/dummy' -import { - locationTemplate, - positionTemplate, - optionsTemplate -} from '../../helpers/data' - -const cb = () => {} -let locationResource -let resource - -describe('Location', () => { - mockApi() - - describe('resourceFactory', () => { - beforeEach(() => { - resource = Object.assign(dummyResource(), Location.resourceFactory()) - }) - - it('should not allow single resource access', () => { - const singleResource = () => resource.location('id') - expect(singleResource).toThrow() - }) - - it('should create new Location resource', () => { - locationResource = resource.location() - expect(locationResource instanceof Resource).toBe(true) - expect(locationResource.type).toBe(Location) - }) - - it('should add locations path', () => { - locationResource = resource.location() - expect(locationResource.path) - .toEqual(`${paths.dummy}${paths.locations}`) - }) - - describe('with normalization', () => { - beforeEach(() => { - spyOn(Resource.prototype, 'update').and.returnValue(Promise.resolve()) - setup({ geolocation: false }) - }) - - describe('update', () => { - beforeEach(() => { - locationResource = resource.location() - }) - - it('should support empty invocation', done => { - locationResource.update().then(() => { - expect(Resource.prototype.update).toHaveBeenCalledWith([]) - done() - }) - }) - - it('should wrap single location update into list', done => { - locationResource.update(locationTemplate).then(() => { - expect(Resource.prototype.update) - .toHaveBeenCalledWith([locationTemplate]) - }).then(done) - }) - - it('should support callback in first param', done => { - locationResource.update(cb).then(() => { - expect(Resource.prototype.update.calls.mostRecent().args[1]) - .toEqual(cb) - }).then(done) - }) - - it('should support callback in second param', done => { - locationResource.update(locationTemplate, cb).then(() => { - expect(Resource.prototype.update.calls.mostRecent().args[1]) - .toEqual(cb) - }).then(done) - }) - - it('should support callback in third param', done => { - locationResource.update(locationTemplate, optionsTemplate, cb) - .then(() => { - expect(Resource.prototype.update.calls.mostRecent().args[2]) - .toEqual(cb) - }) - .then(done) - }) - - if (typeof window !== 'undefined') { - describe('with Geolocation', () => { - beforeEach(() => setup({ geolocation: true })) - afterEach(() => setup({ geolocation: false })) - - it('should request user location none defined', done => { - spyOn(window.navigator.geolocation, 'getCurrentPosition') - .and.callFake(success => success(positionTemplate)) - - locationResource.update().then(() => { - expect(window.navigator.geolocation.getCurrentPosition).toHaveBeenCalled() - expect(Resource.prototype.update.calls.mostRecent().args[0]) - .toEqual(jasmine.objectContaining([{ - position: { - type: 'Point', - coordinates: [ - positionTemplate.coords.longitude, - positionTemplate.coords.latitude - ] - } - }])) - }).then(done) - }) - - it('should create action even if geolocation failed', done => { - spyOn(window.navigator.geolocation, 'getCurrentPosition') - .and.callFake((success, error) => error(new Error())) - spyOn(console, 'info').and.callFake(() => {}) - - locationResource.update().catch(() => { - expect(window.navigator.geolocation.getCurrentPosition).toHaveBeenCalled() - expect(Resource.prototype.update).toHaveBeenCalled() - }).then(done) - }) - }) - } - }) - }) - }) -}) diff --git a/test/unit/api.spec.js b/test/unit/outDated/api.spec.js similarity index 87% rename from test/unit/api.spec.js rename to test/unit/outDated/api.spec.js index 19efea3..80bd198 100644 --- a/test/unit/api.spec.js +++ b/test/unit/outDated/api.spec.js @@ -17,7 +17,7 @@ const initialSettings = Object.assign({}, settings) describe('api', () => { mockApi() - afterEach(done => { + afterEach((done) => { setup(initialSettings) request.then(done).catch(done.fail) }) @@ -64,13 +64,11 @@ describe('api', () => { it('should merge nested headers', () => { const headers = { 'content-type': 'text/plain', - 'accept': 'application/json' + accept: 'application/json' } request = api({ headers }).then(() => { - expect(fetchMock.lastOptions().headers['content-type']) - .toEqual(headers['content-type']) - expect(fetchMock.lastOptions().headers.accept) - .toEqual(headers.accept) + expect(fetchMock.lastOptions().headers['content-type']).toEqual(headers['content-type']) + expect(fetchMock.lastOptions().headers.accept).toEqual(headers.accept) }) }) @@ -116,7 +114,7 @@ describe('api', () => { } const asyncIncrementInterceptor = { request (options) { - return new Promise(resolve => { + return new Promise((resolve) => { setTimeout(() => { options.body = options.body || { count: 0 } options.body.count++ @@ -126,7 +124,7 @@ describe('api', () => { }, response (res) { // assuming fullResponse: false - return new Promise(resolve => { + return new Promise((resolve) => { setTimeout(() => { res.count = res.count || 0 res.count++ @@ -142,7 +140,7 @@ describe('api', () => { } const asyncCancelInterceptor = { request (options, cancel) { - return new Promise(resolve => { + return new Promise((resolve) => { setTimeout(() => { cancel() resolve() @@ -159,7 +157,7 @@ describe('api', () => { response (res) { const json = res.json res.json = function () { - return json.apply(this, arguments).then(j => { + return json.apply(this, arguments).then((j) => { j.patch = 'patched' return j }) @@ -190,8 +188,7 @@ describe('api', () => { const interceptors = [mutatorInterceptor] request = api({ interceptors }).then(() => { expect(fetchMock.lastOptions().method).toEqual('post') - expect(fetchMock.lastOptions().headers.accept) - .toEqual('application/json') + expect(fetchMock.lastOptions().headers.accept).toEqual('application/json') }) }) @@ -211,10 +208,7 @@ describe('api', () => { }) it('should allow async interceptors (return promises)', () => { - const interceptors = [ - asyncIncrementInterceptor, - asyncIncrementInterceptor - ] + const interceptors = [asyncIncrementInterceptor, asyncIncrementInterceptor] request = api({ interceptors }).then(() => { expect(fetchMock.lastOptions().body).toBeDefined() expect(fetchMock.lastOptions().body.count).toEqual(2) @@ -224,7 +218,7 @@ describe('api', () => { it('should be able to cancel requests', () => { fetchMock.reset() const interceptors = [cancelInterceptor] - request = api({ interceptors }).catch(err => { + request = api({ interceptors }).catch((err) => { expect(err.cancelled).toBe(true) expect(fetchMock.called()).toBe(false) }) @@ -233,7 +227,7 @@ describe('api', () => { it('should be able to cancel request from async interceptor', () => { fetchMock.reset() const interceptors = [asyncCancelInterceptor] - request = api({ interceptors }).catch(err => { + request = api({ interceptors }).catch((err) => { expect(err.cancelled).toBe(true) expect(fetchMock.called()).toBe(false) }) @@ -273,7 +267,7 @@ describe('api', () => { it('should be able to mutate simple response', () => { const interceptors = [mutatorInterceptor] const url = paths.operators - request = api({ interceptors, url }).then(res => { + request = api({ interceptors, url }).then((res) => { expect(res.id).toBeDefined() expect(res.id).toEqual(operatorTemplate.id) expect(res.foo).toBeDefined() @@ -283,18 +277,15 @@ describe('api', () => { it('should allow multiple interceptors that run in order', () => { const interceptors = [incrementInterceptor, incrementInterceptor] - request = api({ interceptors }).then(res => { + request = api({ interceptors }).then((res) => { expect(res.count).toBeDefined() expect(res.count).toEqual(2) }) }) it('should allow async interceptors (return promises)', () => { - const interceptors = [ - asyncIncrementInterceptor, - asyncIncrementInterceptor - ] - request = api({ interceptors }).then(res => { + const interceptors = [asyncIncrementInterceptor, asyncIncrementInterceptor] + request = api({ interceptors }).then((res) => { expect(res.count).toBeDefined() expect(res.count).toEqual(2) }) @@ -310,8 +301,8 @@ describe('api', () => { it('should allow monkey patch of full responses', () => { const interceptors = [monkeyPatchInterceptor] request = api({ interceptors, fullResponse: true }) - .then(res => res.json()) - .then(res => { + .then((res) => res.json()) + .then((res) => { expect(res.patch).toBeDefined() expect(res.patch).toEqual('patched') }) @@ -326,8 +317,7 @@ describe('api', () => { url: paths.dummy } request = api(customOptions).then(() => { - expect(fetchMock.lastUrl()) - .toEqual(`${customOptions.apiUrl}${customOptions.url}`) + expect(fetchMock.lastUrl()).toEqual(`${customOptions.apiUrl}${customOptions.url}`) }) }) @@ -352,7 +342,7 @@ describe('api', () => { describe('handle response', () => { it('should return json body by default', () => { - request = api().then(res => { + request = api().then((res) => { expect(res).toEqual(responses.ok.body) }) }) @@ -361,7 +351,7 @@ describe('api', () => { request = api({ method: 'delete', url: paths.dummy - }).then(res => { + }).then((res) => { expect(res).toBeUndefined() }) }) @@ -369,16 +359,16 @@ describe('api', () => { it('should reject on HTTP error code', function () { request = api({ url: paths.error }) .then(() => expect(true).toBe(false)) // should not get here - .catch(res => expect(res).toEqual(responses.error.generic.body)) + .catch((res) => expect(res).toEqual(responses.error.generic.body)) }) it('should return full Response object with fullResponse option', () => { - request = api({ fullResponse: true }).then(res => { + request = api({ fullResponse: true }).then((res) => { expect(res instanceof Response).toBe(true) expect(res.headers).toBeDefined() expect(res.ok).toBe(true) - return res.json().then(body => { + return res.json().then((body) => { expect(body).toEqual(responses.ok.body) }) }) @@ -390,11 +380,11 @@ describe('api', () => { fullResponse: true }) .then(() => expect(true).toBe(false)) // should not get here - .catch(res => { + .catch((res) => { expect(res instanceof Response).toBe(true) expect(res.ok).toBe(false) - return res.json().then(body => { + return res.json().then((body) => { expect(body).toEqual(responses.error.generic.body) }) }) @@ -408,15 +398,14 @@ describe('api', () => { it('should call callback without error on success', () => { request = api({ url: paths.dummy }, callbackSpy).then(() => { - expect(callbackSpy) - .toHaveBeenCalledWith(null, responses.entity.multiple.body) + expect(callbackSpy).toHaveBeenCalledWith(null, responses.entity.multiple.body) }) }) it('should call callback with error on error', () => { - request = api({ url: paths.error }, callbackSpy) - .catch(() => expect(callbackSpy) - .toHaveBeenCalledWith(responses.error.generic.body)) + request = api({ url: paths.error }, callbackSpy).catch(() => + expect(callbackSpy).toHaveBeenCalledWith(responses.error.generic.body) + ) }) }) }) diff --git a/test/unit/entity/Action.spec.js b/test/unit/outDated/entity/Action.spec.js similarity index 56% rename from test/unit/entity/Action.spec.js rename to test/unit/outDated/entity/Action.spec.js index b06e602..df09dc3 100644 --- a/test/unit/entity/Action.spec.js +++ b/test/unit/outDated/entity/Action.spec.js @@ -44,14 +44,14 @@ describe('Action', () => { it('should add actions path', () => { const actionResource = resource.action(actionTemplate.type) - expect(actionResource.path) - .toEqual(`${paths.dummy}${paths.actions}/${actionTemplate.type}`) + expect(actionResource.path).toEqual(`${paths.dummy}${paths.actions}/${actionTemplate.type}`) }) it('should add action to path', () => { const actionResource = resource.action(actionTemplate.type, actionTemplate.id) - expect(actionResource.path) - .toEqual(`${paths.dummy}${paths.actions}/${actionTemplate.type}/${actionTemplate.id}`) + expect(actionResource.path).toEqual( + `${paths.dummy}${paths.actions}/${actionTemplate.type}/${actionTemplate.id}` + ) }) describe('with normalization', () => { @@ -66,7 +66,7 @@ describe('Action', () => { resource = Object.assign(dummyResource(), Action.resourceFactory()) }) - it('should support empty invocation', done => { + it('should support empty invocation', (done) => { actionResource = resource.action(actionTemplate.type) actionResource.create().then(() => { expect(Resource.prototype.create).toHaveBeenCalled() @@ -74,55 +74,64 @@ describe('Action', () => { }) }) - it('should fill correct action type', done => { + it('should fill correct action type', (done) => { Promise.all([ - resource.action(actionTemplate.type).create().then(() => { - expect(Resource.prototype.create).toHaveBeenCalledWith( - jasmine.objectContaining({ - type: actionTemplate.type - }) - ) - }), - resource.action('all').create().then(() => { - expect(Resource.prototype.create).toHaveBeenCalledWith( - jasmine.objectContaining({ - type: '' - }) - ) - }), - resource.action('all').create({ type: 'test' }).then(() => { - expect(Resource.prototype.create).toHaveBeenCalledWith( - jasmine.objectContaining({ - type: 'test' - }) - ) - }), - resource.action(actionTemplate.type).create([{ foo: 1 }, { foo: 2 }]).then(() => { - expect(Resource.prototype.create).toHaveBeenCalledWith([ - jasmine.objectContaining({ - type: actionTemplate.type, - foo: 1 - }), - jasmine.objectContaining({ - type: actionTemplate.type, - foo: 2 - }) - ]) - }) + resource + .action(actionTemplate.type) + .create() + .then(() => { + expect(Resource.prototype.create).toHaveBeenCalledWith( + jasmine.objectContaining({ + type: actionTemplate.type + }) + ) + }), + resource + .action('all') + .create() + .then(() => { + expect(Resource.prototype.create).toHaveBeenCalledWith( + jasmine.objectContaining({ + type: '' + }) + ) + }), + resource + .action('all') + .create({ type: 'test' }) + .then(() => { + expect(Resource.prototype.create).toHaveBeenCalledWith( + jasmine.objectContaining({ + type: 'test' + }) + ) + }), + resource + .action(actionTemplate.type) + .create([{ foo: 1 }, { foo: 2 }]) + .then(() => { + expect(Resource.prototype.create).toHaveBeenCalledWith([ + jasmine.objectContaining({ + type: actionTemplate.type, + foo: 1 + }), + jasmine.objectContaining({ + type: actionTemplate.type, + foo: 2 + }) + ]) + }) ]).then(done) }) }) describe('on Entity base', () => { beforeEach(() => { - entity = Object.assign( - dummyEntity(Entity, entityTemplate), - Action.resourceFactory() - ) + entity = Object.assign(dummyEntity(Entity, entityTemplate), Action.resourceFactory()) actionResource = entity.action(actionTemplate.type) }) - it('should fill correct entity ID', done => { + it('should fill correct entity ID', (done) => { Promise.all([ actionResource.create().then(() => { expect(Resource.prototype.create).toHaveBeenCalledWith( @@ -146,25 +155,29 @@ describe('Action', () => { ]).then(done) }) - it('should support callback in first param', done => { - actionResource.create(cb).then(() => { - expect(Resource.prototype.create.calls.mostRecent().args[1]) - .toEqual(cb) - }).then(done) + it('should support callback in first param', (done) => { + actionResource + .create(cb) + .then(() => { + expect(Resource.prototype.create.calls.mostRecent().args[1]).toEqual(cb) + }) + .then(done) }) - it('should support callback in second param', done => { - actionResource.create(actionTemplate, cb).then(() => { - expect(Resource.prototype.create.calls.mostRecent().args[1]) - .toEqual(cb) - }).then(done) + it('should support callback in second param', (done) => { + actionResource + .create(actionTemplate, cb) + .then(() => { + expect(Resource.prototype.create.calls.mostRecent().args[1]).toEqual(cb) + }) + .then(done) }) - it('should support callback in third param', done => { - actionResource.create(actionTemplate, optionsTemplate, cb) + it('should support callback in third param', (done) => { + actionResource + .create(actionTemplate, optionsTemplate, cb) .then(() => { - expect(Resource.prototype.create.calls.mostRecent().args[2]) - .toEqual(cb) + expect(Resource.prototype.create.calls.mostRecent().args[2]).toEqual(cb) }) .then(done) }) @@ -177,31 +190,35 @@ describe('Action', () => { actionResource = resource.action(actionTemplate.type) }) - it('should request user location if local config is passed', done => { - spyOn(window.navigator.geolocation, 'getCurrentPosition') - .and.callFake(success => success(positionTemplate)) + it('should request user location if local config is passed', (done) => { + spyOn(window.navigator.geolocation, 'getCurrentPosition').and.callFake((success) => + success(positionTemplate) + ) - actionResource.create(actionTemplate, { geolocation: true }) + actionResource + .create(actionTemplate, { geolocation: true }) .then(() => { - expect(window.navigator.geolocation.getCurrentPosition) - .toHaveBeenCalled() - expect(Resource.prototype.create.calls.mostRecent().args[0]) - .toEqual(jasmine.objectContaining({ + expect(window.navigator.geolocation.getCurrentPosition).toHaveBeenCalled() + expect(Resource.prototype.create.calls.mostRecent().args[0]).toEqual( + jasmine.objectContaining({ location: positionTemplate.coords - })) + }) + ) }) .then(done) }) - it('should create action even if geolocation failed', done => { - spyOn(window.navigator.geolocation, 'getCurrentPosition') - .and.callFake((success, error) => error(new Error())) + it('should create action even if geolocation failed', (done) => { + spyOn( + window.navigator.geolocation, + 'getCurrentPosition' + ).and.callFake((success, error) => error(new Error())) spyOn(console, 'info').and.callFake(() => {}) - actionResource.create(actionTemplate, { geolocation: true }) + actionResource + .create(actionTemplate, { geolocation: true }) .catch(() => { - expect(window.navigator.geolocation.getCurrentPosition) - .toHaveBeenCalled() + expect(window.navigator.geolocation.getCurrentPosition).toHaveBeenCalled() expect(Resource.prototype.create).toHaveBeenCalled() }) .then(done) diff --git a/test/unit/entity/ActionType.spec.js b/test/unit/outDated/entity/ActionType.spec.js similarity index 84% rename from test/unit/entity/ActionType.spec.js rename to test/unit/outDated/entity/ActionType.spec.js index 7b4622c..a22756e 100644 --- a/test/unit/entity/ActionType.spec.js +++ b/test/unit/outDated/entity/ActionType.spec.js @@ -30,8 +30,7 @@ describe('ActionType', () => { it('should add action type to path', () => { actionTypeResource = scope.actionType(actionTypeTemplate.name) - expect(actionTypeResource.path) - .toEqual(`${paths.actions}/${actionTypeTemplate.name}`) + expect(actionTypeResource.path).toEqual(`${paths.actions}/${actionTypeTemplate.name}`) }) describe('with normalization', () => { @@ -44,14 +43,14 @@ describe('ActionType', () => { describe('read', () => { describe('plural', () => { - it('should support empty invocation', done => { + it('should support empty invocation', (done) => { actionTypeResource.read().then(() => { expect(Resource.prototype.read).toHaveBeenCalledWith() done() }) }) - it('should support flexible arguments', done => { + it('should support flexible arguments', (done) => { Promise.all([ actionTypeResource.read(optionsTemplate).then(() => { expect(Resource.prototype.read).toHaveBeenCalledWith(optionsTemplate) @@ -71,7 +70,7 @@ describe('ActionType', () => { actionTypeResource = scope.actionType(actionTypeTemplate.name) }) - it('should make a filter request to action types', done => { + it('should make a filter request to action types', (done) => { actionTypeResource.read().then(() => { expect(Resource.prototype.read).toHaveBeenCalledWith({ url: paths.actionTypes, @@ -85,7 +84,7 @@ describe('ActionType', () => { }) }) - it('should support flexible arguments', done => { + it('should support flexible arguments', (done) => { Promise.all([ actionTypeResource.read(optionsTemplate).then(() => { expect(Resource.prototype.read).toHaveBeenCalledWith( @@ -93,10 +92,7 @@ describe('ActionType', () => { ) }), actionTypeResource.read(cb).then(() => { - expect(Resource.prototype.read).toHaveBeenCalledWith( - jasmine.any(Object), - cb - ) + expect(Resource.prototype.read).toHaveBeenCalledWith(jasmine.any(Object), cb) }), actionTypeResource.read(optionsTemplate, cb).then(() => { expect(Resource.prototype.read).toHaveBeenCalledWith( @@ -107,16 +103,16 @@ describe('ActionType', () => { ]).then(done) }) - it('should respond with single entity', done => { - actionTypeResource.read().then(actionType => { + it('should respond with single entity', (done) => { + actionTypeResource.read().then((actionType) => { expect(actionType).toEqual(response[0]) done() }) }) - it('should reject promise if not found', done => { + it('should reject promise if not found', (done) => { response = [] - actionTypeResource.read().catch(err => { + actionTypeResource.read().catch((err) => { expect(err.status).toEqual(404) expect(err.errors).toBeDefined() expect(err.errors[0]).toEqual('The action type was not found.') diff --git a/test/unit/entity/AppUser.spec.js b/test/unit/outDated/entity/AppUser.spec.js similarity index 52% rename from test/unit/entity/AppUser.spec.js rename to test/unit/outDated/entity/AppUser.spec.js index 07a915e..7db6a11 100644 --- a/test/unit/entity/AppUser.spec.js +++ b/test/unit/outDated/entity/AppUser.spec.js @@ -6,12 +6,7 @@ import apiUrl from '../../helpers/apiUrl' import mockApi from '../../helpers/apiMock' import paths from '../../helpers/paths' import { dummyScope, dummyResource } from '../../helpers/dummy' -import { - apiKey, - optionsTemplate, - userTemplate, - userAccessTemplate -} from '../../helpers/data' +import { apiKey, optionsTemplate, userTemplate, userAccessTemplate } from '../../helpers/data' const cb = () => {} let userAccessResource @@ -44,16 +39,21 @@ describe('AppUser', () => { expect(invalid).toThrow() }) - it('should validate itself', done => { + it('should validate itself', (done) => { const path = `${paths.dummy}/${userAccessTemplate.evrythngUser}/validate` - userAccess.validate().then(() => { - expect(fetchMock.lastUrl()).toEqual(apiUrl(path)) - expect(fetchMock.lastOptions()).toEqual(jasmine.objectContaining({ - apiKey, - method: 'post', - body: JSON.stringify({ activationCode: userAccessTemplate.activationCode }) - })) - }).then(done) + userAccess + .validate() + .then(() => { + expect(fetchMock.lastUrl()).toEqual(apiUrl(path)) + expect(fetchMock.lastOptions()).toEqual( + jasmine.objectContaining({ + apiKey, + method: 'post', + body: JSON.stringify({ activationCode: userAccessTemplate.activationCode }) + }) + ) + }) + .then(done) }) }) @@ -74,56 +74,68 @@ describe('AppUser', () => { spyOn(Resource.prototype, 'create').and.returnValue(Promise.resolve()) }) - it('should create normal user', done => { - userAccessResource.create(userTemplate).then(() => { - expect(Resource.prototype.create).toHaveBeenCalledWith(userTemplate) - }).then(done) + it('should create normal user', (done) => { + userAccessResource + .create(userTemplate) + .then(() => { + expect(Resource.prototype.create).toHaveBeenCalledWith(userTemplate) + }) + .then(done) }) - it('should support callback in second param', done => { - userAccessResource.create(userTemplate, cb).then(() => { - expect(Resource.prototype.create.calls.mostRecent().args[1]) - .toEqual(cb) - }).then(done) + it('should support callback in second param', (done) => { + userAccessResource + .create(userTemplate, cb) + .then(() => { + expect(Resource.prototype.create.calls.mostRecent().args[1]).toEqual(cb) + }) + .then(done) }) - it('should support callback in third param', done => { - userAccessResource.create(userTemplate, optionsTemplate, cb) + it('should support callback in third param', (done) => { + userAccessResource + .create(userTemplate, optionsTemplate, cb) .then(() => { - expect(Resource.prototype.create.calls.mostRecent().args[2]) - .toEqual(cb) + expect(Resource.prototype.create.calls.mostRecent().args[2]).toEqual(cb) }) .then(done) }) - it('should create anonymous user', done => { - const anonymousUserTemplate = Object.assign( - { anonymous: true }, - userTemplate - ) - userAccessResource.create(anonymousUserTemplate).then(() => { - expect(fetchMock.lastOptions()).toEqual(jasmine.objectContaining({ - apiKey, - method: 'post', - params: { anonymous: true }, - body: JSON.stringify({}) - })) - }).then(done) + it('should create anonymous user', (done) => { + const anonymousUserTemplate = Object.assign({ anonymous: true }, userTemplate) + userAccessResource + .create(anonymousUserTemplate) + .then(() => { + expect(fetchMock.lastOptions()).toEqual( + jasmine.objectContaining({ + apiKey, + method: 'post', + params: { anonymous: true }, + body: JSON.stringify({}) + }) + ) + }) + .then(done) }) describe('validate', () => { - it('should validate', done => { + it('should validate', (done) => { const activationCode = 'code' const path = `${paths.usersAccess}/${userAccessTemplate.evrythngUser}/validate` userAccessResource = scope.userAccess(userAccessTemplate.evrythngUser) - userAccessResource.validate(activationCode).then(() => { - expect(fetchMock.lastUrl()).toEqual(apiUrl(path)) - expect(fetchMock.lastOptions()).toEqual(jasmine.objectContaining({ - apiKey, - method: 'post', - body: JSON.stringify({ activationCode }) - })) - }).then(done) + userAccessResource + .validate(activationCode) + .then(() => { + expect(fetchMock.lastUrl()).toEqual(apiUrl(path)) + expect(fetchMock.lastOptions()).toEqual( + jasmine.objectContaining({ + apiKey, + method: 'post', + body: JSON.stringify({ activationCode }) + }) + ) + }) + .then(done) }) }) }) diff --git a/test/unit/entity/Application.spec.js b/test/unit/outDated/entity/Application.spec.js similarity index 94% rename from test/unit/entity/Application.spec.js rename to test/unit/outDated/entity/Application.spec.js index f8f9839..5edcf76 100644 --- a/test/unit/entity/Application.spec.js +++ b/test/unit/outDated/entity/Application.spec.js @@ -36,8 +36,9 @@ describe('Application', () => { it('should create new Product resource', () => { expect(applicationResource instanceof Resource).toBe(true) expect(applicationResource.type).toBe(Application) - expect(applicationResource.path) - .toEqual(`${paths.dummy}${paths.applications}/${applicationTemplate.id}`) + expect(applicationResource.path).toEqual( + `${paths.dummy}${paths.applications}/${applicationTemplate.id}` + ) }) it('should have nested reactorScript resource', () => { diff --git a/test/unit/entity/Batch.spec.js b/test/unit/outDated/entity/Batch.spec.js similarity index 100% rename from test/unit/entity/Batch.spec.js rename to test/unit/outDated/entity/Batch.spec.js diff --git a/test/unit/entity/Collection.spec.js b/test/unit/outDated/entity/Collection.spec.js similarity index 94% rename from test/unit/entity/Collection.spec.js rename to test/unit/outDated/entity/Collection.spec.js index 7a00a4f..218116d 100644 --- a/test/unit/entity/Collection.spec.js +++ b/test/unit/outDated/entity/Collection.spec.js @@ -37,7 +37,9 @@ describe('Collection', () => { it('should have nested action resource', () => { expect(collectionResource.action).toBeDefined() - expect(collectionResource.action('all').path).toEqual(`${collectionPath}${paths.actions}/all`) + expect(collectionResource.action('all').path).toEqual( + `${collectionPath}${paths.actions}/all` + ) }) }) diff --git a/test/unit/entity/Entity.spec.js b/test/unit/outDated/entity/Entity.spec.js similarity index 88% rename from test/unit/entity/Entity.spec.js rename to test/unit/outDated/entity/Entity.spec.js index 4b679c6..23579eb 100644 --- a/test/unit/entity/Entity.spec.js +++ b/test/unit/outDated/entity/Entity.spec.js @@ -71,36 +71,36 @@ describe('Entity', () => { spyOn(resource, 'update').and.returnValue(Promise.resolve(updatedEntity)) }) - it('should call resource update with entity\'s JSON', done => { + it("should call resource update with entity's JSON", (done) => { entity.update().then(() => { expect(resource.update.calls.mostRecent().args[0]).toEqual(entityTemplate) done() }) }) - it('should call resource update with provided body', done => { + it('should call resource update with provided body', (done) => { entity.update(dataToUpdate).then(() => { expect(resource.update.calls.mostRecent().args[0]).toEqual(dataToUpdate) done() }) }) - it('should allow callback in first argument', done => { + it('should allow callback in first argument', (done) => { entity.update(cb).then(() => { expect(resource.update.calls.mostRecent().args[0]).toEqual(cb) done() }) }) - it('should allow callback in second argument', done => { + it('should allow callback in second argument', (done) => { entity.update(dataToUpdate, cb).then(() => { expect(resource.update.calls.mostRecent().args[1]).toEqual(cb) done() }) }) - it('should update', done => { - entity.update().then(updated => { + it('should update', (done) => { + entity.update().then((updated) => { expect(entity.updated).toBe(true) expect(entity.foo).toBeDefined() expect(updated).toEqual(updatedEntity) @@ -115,14 +115,14 @@ describe('Entity', () => { spyOn(resource, 'delete').and.returnValue(Promise.resolve()) }) - it('should call resource delete', done => { + it('should call resource delete', (done) => { entity.delete().then(() => { expect(resource.delete).toHaveBeenCalled() done() }) }) - it('should support callback', done => { + it('should support callback', (done) => { entity.delete(cb).then(() => { expect(resource.delete.calls.mostRecent().args[0]).toBe(cb) done() diff --git a/test/unit/entity/File.spec.js b/test/unit/outDated/entity/File.spec.js similarity index 100% rename from test/unit/entity/File.spec.js rename to test/unit/outDated/entity/File.spec.js diff --git a/test/unit/outDated/entity/Location.spec.js b/test/unit/outDated/entity/Location.spec.js new file mode 100644 index 0000000..8a7cb63 --- /dev/null +++ b/test/unit/outDated/entity/Location.spec.js @@ -0,0 +1,143 @@ +/* eslint-env jasmine */ +import Resource from '../../../src/resource/Resource' +import Location from '../../../src/entity/Location' +import setup from '../../../src/setup' +import mockApi from '../../helpers/apiMock' +import paths from '../../helpers/paths' +import { dummyResource } from '../../helpers/dummy' +import { locationTemplate, positionTemplate, optionsTemplate } from '../../helpers/data' + +const cb = () => {} +let locationResource +let resource + +describe('Location', () => { + mockApi() + + describe('resourceFactory', () => { + beforeEach(() => { + resource = Object.assign(dummyResource(), Location.resourceFactory()) + }) + + it('should not allow single resource access', () => { + const singleResource = () => resource.location('id') + expect(singleResource).toThrow() + }) + + it('should create new Location resource', () => { + locationResource = resource.location() + expect(locationResource instanceof Resource).toBe(true) + expect(locationResource.type).toBe(Location) + }) + + it('should add locations path', () => { + locationResource = resource.location() + expect(locationResource.path).toEqual(`${paths.dummy}${paths.locations}`) + }) + + describe('with normalization', () => { + beforeEach(() => { + spyOn(Resource.prototype, 'update').and.returnValue(Promise.resolve()) + setup({ geolocation: false }) + }) + + describe('update', () => { + beforeEach(() => { + locationResource = resource.location() + }) + + it('should support empty invocation', (done) => { + locationResource.update().then(() => { + expect(Resource.prototype.update).toHaveBeenCalledWith([]) + done() + }) + }) + + it('should wrap single location update into list', (done) => { + locationResource + .update(locationTemplate) + .then(() => { + expect(Resource.prototype.update).toHaveBeenCalledWith([locationTemplate]) + }) + .then(done) + }) + + it('should support callback in first param', (done) => { + locationResource + .update(cb) + .then(() => { + expect(Resource.prototype.update.calls.mostRecent().args[1]).toEqual(cb) + }) + .then(done) + }) + + it('should support callback in second param', (done) => { + locationResource + .update(locationTemplate, cb) + .then(() => { + expect(Resource.prototype.update.calls.mostRecent().args[1]).toEqual(cb) + }) + .then(done) + }) + + it('should support callback in third param', (done) => { + locationResource + .update(locationTemplate, optionsTemplate, cb) + .then(() => { + expect(Resource.prototype.update.calls.mostRecent().args[2]).toEqual(cb) + }) + .then(done) + }) + + if (typeof window !== 'undefined') { + describe('with Geolocation', () => { + beforeEach(() => setup({ geolocation: true })) + afterEach(() => setup({ geolocation: false })) + + it('should request user location none defined', (done) => { + spyOn(window.navigator.geolocation, 'getCurrentPosition').and.callFake((success) => + success(positionTemplate) + ) + + locationResource + .update() + .then(() => { + expect(window.navigator.geolocation.getCurrentPosition).toHaveBeenCalled() + expect(Resource.prototype.update.calls.mostRecent().args[0]).toEqual( + jasmine.objectContaining([ + { + position: { + type: 'Point', + coordinates: [ + positionTemplate.coords.longitude, + positionTemplate.coords.latitude + ] + } + } + ]) + ) + }) + .then(done) + }) + + it('should create action even if geolocation failed', (done) => { + spyOn( + window.navigator.geolocation, + 'getCurrentPosition' + ).and.callFake((success, error) => error(new Error())) + spyOn(console, 'info').and.callFake(() => {}) + + locationResource + .update() + .catch(() => { + expect(window.navigator.geolocation.getCurrentPosition).toHaveBeenCalled() + expect(Resource.prototype.update).toHaveBeenCalled() + }) + .then(done) + }) + }) + } + }) + }) + }) +}) diff --git a/test/unit/entity/Permission.spec.js b/test/unit/outDated/entity/Permission.spec.js similarity index 93% rename from test/unit/entity/Permission.spec.js rename to test/unit/outDated/entity/Permission.spec.js index 9fa5819..30a7497 100644 --- a/test/unit/entity/Permission.spec.js +++ b/test/unit/outDated/entity/Permission.spec.js @@ -35,8 +35,7 @@ describe('Permission', () => { it('should add permissions path', () => { permissionResource = resource.permission() - expect(permissionResource.path) - .toEqual(`${paths.dummy}${paths.permissions}`) + expect(permissionResource.path).toEqual(`${paths.dummy}${paths.permissions}`) }) }) }) diff --git a/test/unit/entity/Place.spec.js b/test/unit/outDated/entity/Place.spec.js similarity index 100% rename from test/unit/entity/Place.spec.js rename to test/unit/outDated/entity/Place.spec.js diff --git a/test/unit/entity/Product.spec.js b/test/unit/outDated/entity/Product.spec.js similarity index 100% rename from test/unit/entity/Product.spec.js rename to test/unit/outDated/entity/Product.spec.js diff --git a/test/unit/entity/Project.spec.js b/test/unit/outDated/entity/Project.spec.js similarity index 100% rename from test/unit/entity/Project.spec.js rename to test/unit/outDated/entity/Project.spec.js diff --git a/test/unit/entity/Property.spec.js b/test/unit/outDated/entity/Property.spec.js similarity index 88% rename from test/unit/entity/Property.spec.js rename to test/unit/outDated/entity/Property.spec.js index c169b2f..7f609c8 100644 --- a/test/unit/entity/Property.spec.js +++ b/test/unit/outDated/entity/Property.spec.js @@ -36,8 +36,9 @@ describe('Property', () => { it('should add property to path', () => { propertyResource = resource.property(propertyTemplate.key) - expect(propertyResource.path) - .toEqual(`${paths.dummy}${paths.properties}/${propertyTemplate.key}`) + expect(propertyResource.path).toEqual( + `${paths.dummy}${paths.properties}/${propertyTemplate.key}` + ) }) describe('with normalization', () => { @@ -45,9 +46,8 @@ describe('Property', () => { propertyResource = resource.property(propertyTemplate.key) spyOn(Resource.prototype, 'create') spyOn(Resource.prototype, 'update') - }) - - ;['create', 'update'].forEach(method => { + }); + ['create', 'update'].forEach((method) => { describe(method, () => { it('should support simple values', () => { propertyResource[method](1) @@ -74,19 +74,21 @@ describe('Property', () => { it('should support shorthand object update', () => { const shortHandProp = { foo: 'bar', bar: 'foo' } propertyResource[method](shortHandProp) - expect(Resource.prototype[method]).toHaveBeenCalledWith([{ - key: 'foo', - value: 'bar' - }, { - key: 'bar', - value: 'foo' - }]) + expect(Resource.prototype[method]).toHaveBeenCalledWith([ + { + key: 'foo', + value: 'bar' + }, + { + key: 'bar', + value: 'foo' + } + ]) }) it('should support options', () => { propertyResource[method](1, optionsTemplate) - expect(Resource.prototype[method].calls.mostRecent().args[1]) - .toEqual(optionsTemplate) + expect(Resource.prototype[method].calls.mostRecent().args[1]).toEqual(optionsTemplate) }) it('should support callbacks', () => { diff --git a/test/unit/entity/ReactorLog.spec.js b/test/unit/outDated/entity/ReactorLog.spec.js similarity index 76% rename from test/unit/entity/ReactorLog.spec.js rename to test/unit/outDated/entity/ReactorLog.spec.js index 354cb35..c03e72d 100644 --- a/test/unit/entity/ReactorLog.spec.js +++ b/test/unit/outDated/entity/ReactorLog.spec.js @@ -36,7 +36,7 @@ describe('ReactorLog', () => { }) describe('create', () => { - it('should do nothing for single log', done => { + it('should do nothing for single log', (done) => { reactorLogResource = resource.reactorLog() reactorLogResource.create(reactorLogTemplate).then(() => { expect(Resource.prototype.create).toHaveBeenCalledWith(reactorLogTemplate) @@ -44,17 +44,20 @@ describe('ReactorLog', () => { }) }) - it('should use bulk endpoint for multiple logs', done => { + it('should use bulk endpoint for multiple logs', (done) => { const data = [reactorLogTemplate, reactorLogTemplate] - resource.reactorLog().create(data).then(() => { - expect(Resource.prototype.create).toHaveBeenCalledWith( - data, - jasmine.objectContaining({ - url: `${paths.dummy}${paths.reactorLogs}/bulk` - }) - ) - done() - }) + resource + .reactorLog() + .create(data) + .then(() => { + expect(Resource.prototype.create).toHaveBeenCalledWith( + data, + jasmine.objectContaining({ + url: `${paths.dummy}${paths.reactorLogs}/bulk` + }) + ) + done() + }) }) }) }) diff --git a/test/unit/entity/ReactorSchedule.spec.js b/test/unit/outDated/entity/ReactorSchedule.spec.js similarity index 86% rename from test/unit/entity/ReactorSchedule.spec.js rename to test/unit/outDated/entity/ReactorSchedule.spec.js index acb103e..eea3281 100644 --- a/test/unit/entity/ReactorSchedule.spec.js +++ b/test/unit/outDated/entity/ReactorSchedule.spec.js @@ -20,8 +20,9 @@ describe('ReactorSchedule', () => { it('should create new ReactorSchedule resource', () => { expect(reactorScheduleResource instanceof Resource).toBe(true) expect(reactorScheduleResource.type).toBe(ReactorSchedule) - expect(reactorScheduleResource.path) - .toEqual(`${paths.dummy}${paths.reactorSchedules}/${reactorScheduleTemplate.id}`) + expect(reactorScheduleResource.path).toEqual( + `${paths.dummy}${paths.reactorSchedules}/${reactorScheduleTemplate.id}` + ) }) }) }) diff --git a/test/unit/entity/ReactorScript.spec.js b/test/unit/outDated/entity/ReactorScript.spec.js similarity index 100% rename from test/unit/entity/ReactorScript.spec.js rename to test/unit/outDated/entity/ReactorScript.spec.js diff --git a/test/unit/entity/Role.spec.js b/test/unit/outDated/entity/Role.spec.js similarity index 100% rename from test/unit/entity/Role.spec.js rename to test/unit/outDated/entity/Role.spec.js diff --git a/test/unit/entity/Status.spec.js b/test/unit/outDated/entity/Status.spec.js similarity index 100% rename from test/unit/entity/Status.spec.js rename to test/unit/outDated/entity/Status.spec.js diff --git a/test/unit/entity/Task.spec.js b/test/unit/outDated/entity/Task.spec.js similarity index 100% rename from test/unit/entity/Task.spec.js rename to test/unit/outDated/entity/Task.spec.js diff --git a/test/unit/entity/Thng.spec.js b/test/unit/outDated/entity/Thng.spec.js similarity index 100% rename from test/unit/entity/Thng.spec.js rename to test/unit/outDated/entity/Thng.spec.js diff --git a/test/unit/entity/User.spec.js b/test/unit/outDated/entity/User.spec.js similarity index 100% rename from test/unit/entity/User.spec.js rename to test/unit/outDated/entity/User.spec.js diff --git a/test/unit/evrythng.spec.js b/test/unit/outDated/evrythng.spec.js similarity index 100% rename from test/unit/evrythng.spec.js rename to test/unit/outDated/evrythng.spec.js diff --git a/test/unit/resource/Resource.spec.js b/test/unit/outDated/resource/Resource.spec.js similarity index 89% rename from test/unit/resource/Resource.spec.js rename to test/unit/outDated/resource/Resource.spec.js index 5132dcf..853263d 100644 --- a/test/unit/resource/Resource.spec.js +++ b/test/unit/outDated/resource/Resource.spec.js @@ -97,11 +97,11 @@ describe('Resource', () => { expect(res[symbols.resource].path).toEqual(`${resource.path}/${newBody.id}`) }) - it('should add serialize method if Response object', done => { + it('should add serialize method if Response object', (done) => { const response = new Response(JSON.stringify(entityTemplate)) const res = resource.deserialize(response) - res.deserialize().then(res => { + res.deserialize().then((res) => { expect(res.foo).toEqual(entityTemplate.foo) done() }) @@ -110,7 +110,7 @@ describe('Resource', () => { it('should deserialize every element if array', () => { const res = resource.deserialize([entityTemplate, entityTemplate]) expect(res.length).toEqual(2) - res.forEach(item => { + res.forEach((item) => { expect(item instanceof Entity).toBe(true) expect(item.foo).toEqual(entityTemplate.foo) }) @@ -125,7 +125,7 @@ describe('Resource', () => { describe('read', () => { describe('valid', () => { - it('should send get request to path', done => { + it('should send get request to path', (done) => { resource.read().then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.dummy)) expect(fetchMock.lastOptions().method).toEqual('get') @@ -135,10 +135,10 @@ describe('Resource', () => { // Bellow is testing the internal _request method - it('should deserialize response', done => { - resource.read().then(res => { + it('should deserialize response', (done) => { + resource.read().then((res) => { expect(res.length).toEqual(2) - res.forEach(item => { + res.forEach((item) => { expect(item instanceof Entity).toBe(true) expect(item.foo).toEqual(entityTemplate.foo) }) @@ -146,33 +146,31 @@ describe('Resource', () => { }) }) - it('should accept user options', done => { + it('should accept user options', (done) => { const headers = { accept: 'text/html' } resource.read({ headers }).then(() => { - expect(fetchMock.lastOptions().headers.accept) - .toEqual(headers.accept) + expect(fetchMock.lastOptions().headers.accept).toEqual(headers.accept) done() }) }) - it('should override user options with mandatory ones', done => { + it('should override user options with mandatory ones', (done) => { resource.read({ method: 'post' }).then(() => { expect(fetchMock.lastOptions().method).toEqual('get') done() }) }) - it('should use scope apiKey', done => { + it('should use scope apiKey', (done) => { resource.read().then(() => { - expect(fetchMock.lastOptions().headers.authorization) - .toEqual(scope.apiKey) + expect(fetchMock.lastOptions().headers.authorization).toEqual(scope.apiKey) done() }) }) - it('should allow callback in first param', done => { + it('should allow callback in first param', (done) => { const cb = jasmine.createSpy('callback') - resource.read(cb).then(res => { + resource.read(cb).then((res) => { expect(cb).toHaveBeenCalledWith(null, res) done() }) @@ -187,7 +185,7 @@ describe('Resource', () => { }) describe('valid', () => { - it('should send post request to path', done => { + it('should send post request to path', (done) => { resource.create(entityTemplate).then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.dummy)) expect(fetchMock.lastOptions().method).toEqual('post') @@ -198,7 +196,7 @@ describe('Resource', () => { // Bellow is testing the internal _request method - it('should serialize body', done => { + it('should serialize body', (done) => { const newEntity = new Entity(resource, entityTemplate) resource.create(newEntity).then(() => { expect(fetchMock.lastOptions().body).toEqual(entityTemplate) @@ -215,7 +213,7 @@ describe('Resource', () => { }) describe('valid', () => { - it('should send put request to path', done => { + it('should send put request to path', (done) => { resource.update(entityTemplate).then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.dummy)) expect(fetchMock.lastOptions().method).toEqual('put') @@ -226,7 +224,7 @@ describe('Resource', () => { }) describe('delete', () => { - it('should send delete request to path', done => { + it('should send delete request to path', (done) => { resource.delete().then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.dummy)) expect(fetchMock.lastOptions().method).toEqual('delete') @@ -305,9 +303,10 @@ describe('Resource', () => { }) it('should allow extending the Resource with a Mixin', () => { - const Mixin = C => class extends C { - mixedIn () {} - } + const Mixin = (C) => + class extends C { + mixedIn () {} + } factory = Resource.factoryFor(Entity, paths.dummy, Mixin) testMixin = { test: factory } extendedScope = Object.assign(dummyScope(), testMixin) diff --git a/test/unit/scope/Application.spec.js b/test/unit/outDated/scope/Application.spec.js similarity index 76% rename from test/unit/scope/Application.spec.js rename to test/unit/outDated/scope/Application.spec.js index 89e1ace..4057391 100644 --- a/test/unit/scope/Application.spec.js +++ b/test/unit/outDated/scope/Application.spec.js @@ -19,7 +19,7 @@ describe('Application', () => { application = new Application(appApiKey) }) - it('should read itself', done => { + it('should read itself', (done) => { application[symbols.init].then(() => { expect(Application.prototype.read).toHaveBeenCalled() done() @@ -32,21 +32,19 @@ describe('Application', () => { application = new Application('invalidKey') }) - it('should throw error', done => { - application[symbols.init] - .catch(() => expect(true).toBe(true)) - .then(done) + it('should throw error', (done) => { + application[symbols.init].catch(() => expect(true).toBe(true)).then(done) }) }) }) describe('read', () => { - beforeEach(done => { + beforeEach((done) => { application = new Application(appApiKey) application[symbols.init].then(done) }) - it('should send get request to project and application ID', done => { + it('should send get request to project and application ID', (done) => { application.read().then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.application)) expect(fetchMock.lastOptions().method).toEqual('get') @@ -54,8 +52,8 @@ describe('Application', () => { }) }) - it('should fill operator scope with details', done => { - application.read().then(response => { + it('should fill operator scope with details', (done) => { + application.read().then((response) => { expect(application).toBe(response) expect(application).toEqual(jasmine.objectContaining(applicationTemplate)) done() @@ -64,12 +62,12 @@ describe('Application', () => { }) describe('update', () => { - beforeEach(done => { + beforeEach((done) => { application = new Application(appApiKey) application[symbols.init].then(done) }) - it('should send get request to operator ID', done => { + it('should send get request to operator ID', (done) => { application.update(applicationTemplate).then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.application)) expect(fetchMock.lastOptions().method).toEqual('put') @@ -77,8 +75,8 @@ describe('Application', () => { }) }) - it('should fill operator scope with details', done => { - application.update(applicationTemplate).then(response => { + it('should fill operator scope with details', (done) => { + application.update(applicationTemplate).then((response) => { expect(application).toBe(response) expect(application).toEqual(jasmine.objectContaining(applicationTemplate)) done() @@ -87,19 +85,14 @@ describe('Application', () => { }) describe('access', () => { - const operatorResources = [ - 'product', - 'action', - 'place', - 'appUser' - ] + const operatorResources = ['product', 'action', 'place', 'appUser'] - beforeEach(done => { + beforeEach((done) => { application = new Application(appApiKey) application[symbols.init].then(done) }) - operatorResources.forEach(resource => { + operatorResources.forEach((resource) => { it(`should have ${resource} resource`, () => { expect(application[resource]).toBeDefined() }) diff --git a/test/unit/scope/Operator.spec.js b/test/unit/outDated/scope/Operator.spec.js similarity index 78% rename from test/unit/scope/Operator.spec.js rename to test/unit/outDated/scope/Operator.spec.js index e2b673c..48ba568 100644 --- a/test/unit/scope/Operator.spec.js +++ b/test/unit/outDated/scope/Operator.spec.js @@ -19,7 +19,7 @@ describe('Operator', () => { operator = new Operator(operatorApiKey) }) - it('should read itself', done => { + it('should read itself', (done) => { operator[symbols.init].then(() => { expect(Operator.prototype.read).toHaveBeenCalled() done() @@ -32,21 +32,19 @@ describe('Operator', () => { operator = new Operator('invalidKey') }) - it('should throw error', done => { - operator[symbols.init] - .catch(() => expect(true).toBe(true)) - .then(done) + it('should throw error', (done) => { + operator[symbols.init].catch(() => expect(true).toBe(true)).then(done) }) }) }) describe('read', () => { - beforeEach(done => { + beforeEach((done) => { operator = new Operator(operatorApiKey) operator[symbols.init].then(done) }) - it('should send get request to operator ID', done => { + it('should send get request to operator ID', (done) => { operator.read().then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.operator)) expect(fetchMock.lastOptions().method).toEqual('get') @@ -54,8 +52,8 @@ describe('Operator', () => { }) }) - it('should fill operator scope with details', done => { - operator.read().then(response => { + it('should fill operator scope with details', (done) => { + operator.read().then((response) => { expect(operator).toBe(response) expect(operator).toEqual(jasmine.objectContaining(operatorTemplate)) done() @@ -64,12 +62,12 @@ describe('Operator', () => { }) describe('update', () => { - beforeEach(done => { + beforeEach((done) => { operator = new Operator(operatorApiKey) operator[symbols.init].then(done) }) - it('should send get request to operator ID', done => { + it('should send get request to operator ID', (done) => { operator.update(operatorTemplate).then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.operator)) expect(fetchMock.lastOptions().method).toEqual('put') @@ -77,8 +75,8 @@ describe('Operator', () => { }) }) - it('should fill operator scope with details', done => { - operator.update(operatorTemplate).then(response => { + it('should fill operator scope with details', (done) => { + operator.update(operatorTemplate).then((response) => { expect(operator).toBe(response) expect(operator).toEqual(jasmine.objectContaining(operatorTemplate)) done() @@ -100,12 +98,12 @@ describe('Operator', () => { 'file' ] - beforeEach(done => { + beforeEach((done) => { operator = new Operator(operatorApiKey) operator[symbols.init].then(done) }) - operatorResources.forEach(resource => { + operatorResources.forEach((resource) => { it(`should have ${resource} resource`, () => { expect(operator[resource]).toBeDefined() }) diff --git a/test/unit/scope/Scope.spec.js b/test/unit/outDated/scope/Scope.spec.js similarity index 96% rename from test/unit/scope/Scope.spec.js rename to test/unit/outDated/scope/Scope.spec.js index 257c526..4d39625 100644 --- a/test/unit/scope/Scope.spec.js +++ b/test/unit/outDated/scope/Scope.spec.js @@ -44,7 +44,7 @@ describe('Scope', () => { expect(scope[symbols.init] instanceof Promise).toBe(true) }) - it('should fetch scope access using scope apiKey', done => { + it('should fetch scope access using scope apiKey', (done) => { scope[symbols.init].then(() => { expect(fetchMock.lastUrl()).toEqual(apiUrl(paths.access)) expect(fetchMock.lastOptions()).toEqual( diff --git a/test/unit/settings.spec.js b/test/unit/outDated/settings.spec.js similarity index 100% rename from test/unit/settings.spec.js rename to test/unit/outDated/settings.spec.js diff --git a/test/unit/outDated/setup.spec.js b/test/unit/outDated/setup.spec.js new file mode 100644 index 0000000..769017f --- /dev/null +++ b/test/unit/outDated/setup.spec.js @@ -0,0 +1,22 @@ +/* eslint-env jasmine */ +import settings from '../../src/settings' +import setup from '../../src/setup' + +// Copy initial settings +const initialSettings = Object.assign({}, settings) + +describe('setup', () => { + afterEach(() => setup(initialSettings)) + + it('should merge settings with custom settings', () => { + const customSettings = { + apiUrl: 'https://test-api.evrythng.net', + fullResponse: true + } + setup(customSettings) + + expect(settings.apiUrl).toEqual(customSettings.apiUrl) + expect(settings.fullResponse).toEqual(customSettings.fullResponse) + expect(settings.geolocation).toEqual(initialSettings.geolocation) + }) +}) diff --git a/test/unit/util/buildParams.spec.js b/test/unit/outDated/util/buildParams.spec.js similarity index 100% rename from test/unit/util/buildParams.spec.js rename to test/unit/outDated/util/buildParams.spec.js diff --git a/test/unit/util/buildUrl.spec.js b/test/unit/outDated/util/buildUrl.spec.js similarity index 84% rename from test/unit/util/buildUrl.spec.js rename to test/unit/outDated/util/buildUrl.spec.js index 4989379..be54666 100644 --- a/test/unit/util/buildUrl.spec.js +++ b/test/unit/outDated/util/buildUrl.spec.js @@ -16,7 +16,6 @@ describe('buildUrl', () => { baz: 1 } const paramsStr = 'foo=bar&baz=1' - expect(buildUrl({ apiUrl, url, params })) - .toEqual(`${apiUrl}${url}?${paramsStr}`) + expect(buildUrl({ apiUrl, url, params })).toEqual(`${apiUrl}${url}?${paramsStr}`) }) }) diff --git a/test/unit/util/getCurrentPosition.spec.js b/test/unit/outDated/util/getCurrentPosition.spec.js similarity index 55% rename from test/unit/util/getCurrentPosition.spec.js rename to test/unit/outDated/util/getCurrentPosition.spec.js index 231647d..e7e465c 100644 --- a/test/unit/util/getCurrentPosition.spec.js +++ b/test/unit/outDated/util/getCurrentPosition.spec.js @@ -6,23 +6,24 @@ if (typeof window !== 'undefined') { describe('getCurrentPosition', () => { describe('success', () => { beforeEach(() => { - spyOn(window.navigator.geolocation, 'getCurrentPosition') - .and.callFake(success => success(positionTemplate)) + spyOn(window.navigator.geolocation, 'getCurrentPosition').and.callFake((success) => + success(positionTemplate) + ) }) it('should use defaults', () => { getCurrentPosition() - expect( - window.navigator.geolocation.getCurrentPosition.calls.mostRecent().args[2] - ).toEqual(jasmine.objectContaining({ - maximumAge: 0, - timeout: 10000, - enableHighAccuracy: true - })) + expect(window.navigator.geolocation.getCurrentPosition.calls.mostRecent().args[2]).toEqual( + jasmine.objectContaining({ + maximumAge: 0, + timeout: 10000, + enableHighAccuracy: true + }) + ) }) - it('should resolve with Geolocation', done => { - getCurrentPosition().then(resp => { + it('should resolve with Geolocation', (done) => { + getCurrentPosition().then((resp) => { expect(resp).toEqual(positionTemplate) done() }) @@ -31,12 +32,13 @@ if (typeof window !== 'undefined') { describe('error', () => { beforeEach(() => { - spyOn(window.navigator.geolocation, 'getCurrentPosition') - .and.callFake((success, error) => error(new Error('Location error'))) + spyOn(window.navigator.geolocation, 'getCurrentPosition').and.callFake((success, error) => + error(new Error('Location error')) + ) }) - it('should reject with text message', done => { - getCurrentPosition().catch(resp => { + it('should reject with text message', (done) => { + getCurrentPosition().catch((resp) => { expect(resp).toEqual(jasmine.any(String)) done() }) diff --git a/test/unit/util/mixin.spec.js b/test/unit/outDated/util/mixin.spec.js similarity index 100% rename from test/unit/util/mixin.spec.js rename to test/unit/outDated/util/mixin.spec.js diff --git a/test/unit/setup.spec.js b/test/unit/setup.spec.js index 769017f..81894fb 100644 --- a/test/unit/setup.spec.js +++ b/test/unit/setup.spec.js @@ -1,22 +1,104 @@ -/* eslint-env jasmine */ -import settings from '../../src/settings' +import { expect, assert } from 'chai' import setup from '../../src/setup' -// Copy initial settings -const initialSettings = Object.assign({}, settings) - -describe('setup', () => { - afterEach(() => setup(initialSettings)) - - it('should merge settings with custom settings', () => { - const customSettings = { - apiUrl: 'https://test-api.evrythng.net', - fullResponse: true - } - setup(customSettings) - - expect(settings.apiUrl).toEqual(customSettings.apiUrl) - expect(settings.fullResponse).toEqual(customSettings.fullResponse) - expect(settings.geolocation).toEqual(initialSettings.geolocation) +describe('unit tests for setup.js', () => { + it('should use default settings', async () => { + const settings = setup({}) + expect(settings.apiVersion).to.be.equal(2) + expect(settings.apiUrl).to.be.equal('https://api.evrythng.io/v2') + expect(settings.region).to.be.equal('us') + }) + it('should use custom apiUrl', async () => { + const settings = setup({ apiUrl: 'google.com' }) + expect(settings.apiVersion).to.be.equal(2) + expect(settings.apiUrl).to.be.equal('google.com') + expect(settings.region).to.be.equal('us') + }) + it('should use custom apiUrl even if apiVersion is setup', async () => { + const settings = setup({ apiVersion: 1, apiUrl: 'google.com' }) + expect(settings.apiVersion).to.be.equal(1) + expect(settings.apiUrl).to.be.equal('google.com') + expect(settings.region).to.be.equal('us') + }) + it('should use custom apiUrl even if apiVersion and region are setup', async () => { + const settings = setup({ apiVersion: 2, region: 'eu', apiUrl: 'google.com' }) + expect(settings.apiVersion).to.be.equal(2) + expect(settings.apiUrl).to.be.equal('google.com') + expect(settings.region).to.be.equal('eu') + }) + it('should set apiUrl based on apiVersion:2 and region:eu', async () => { + const settings = setup({ apiVersion: 2, region: 'eu' }) + expect(settings.apiVersion).to.be.equal(2) + expect(settings.apiUrl).to.be.equal('https://api.eu.evrythng.io/v2') + expect(settings.region).to.be.equal('eu') + }) + it('should set apiUrl based on apiVersion:2 and region:us', async () => { + const settings = setup({ apiVersion: 2, region: 'us' }) + expect(settings.apiVersion).to.be.equal(2) + expect(settings.apiUrl).to.be.equal('https://api.evrythng.io/v2') + expect(settings.region).to.be.equal('us') + }) + it('should set apiUrl based on default apiVersion and region:eu', async () => { + const settings = await setup({ region: 'eu' }) + expect(settings.apiVersion).to.be.equal(2) + expect(settings.apiUrl).to.be.equal('https://api.eu.evrythng.io/v2') + expect(settings.region).to.be.equal('eu') + }) + it('should set apiUrl based on apiVersion:1 and region:eu', async () => { + const settings = setup({ apiVersion: 1, region: 'eu' }) + expect(settings.apiVersion).to.be.equal(1) + expect(settings.apiUrl).to.be.equal('https://api-eu.evrythng.com') + expect(settings.region).to.be.equal('eu') + }) + it('should set apiUrl based on apiVersion:1 and region:us', async () => { + const settings = setup({ apiVersion: 1, region: 'us' }) + expect(settings.apiVersion).to.be.equal(1) + expect(settings.apiUrl).to.be.equal('https://api.evrythng.com') + expect(settings.region).to.be.equal('us') + }) + it('should throw error if apiVersion is 3', async () => { + assert.throw(() => setup({ apiVersion: 3 }), 'Invalid apiVersion: 3. Choose from 1, 2') + }) + it('should throw error if apiVersion is sdfsdf', async () => { + assert.throw( + () => setup({ apiVersion: 'sdfsdf' }), + 'Invalid apiVersion: sdfsdf. Choose from 1, 2' + ) + }) + it('should throw error if apiVersion is null', async () => { + assert.throw(() => setup({ apiVersion: null }), 'Invalid apiVersion: null. Choose from 1, 2') + }) + it('should throw error if apiVersion is empty', async () => { + assert.throw(() => setup({ apiVersion: '' }), 'Invalid apiVersion: . Choose from 1, 2') + }) + it('should throw error if apiVersion is space', async () => { + assert.throw(() => setup({ apiVersion: ' ' }), 'Invalid apiVersion: . Choose from 1, 2') + }) + it('should set up default apiVersion if apiVersion is undefined', async () => { + const settings = await setup({ apiVersion: undefined }) + expect(settings.apiVersion).to.be.equal(2) + expect(settings.apiUrl).to.be.equal('https://api.evrythng.io/v2') + expect(settings.region).to.be.equal('us') + }) + it('should throw error if region is br', async () => { + assert.throw(() => setup({ region: 'br' }), 'Invalid region: br. Choose from us, eu') + }) + it('should throw error if region is number', async () => { + assert.throw(() => setup({ region: 1 }), 'Invalid region: 1. Choose from us, eu') + }) + it('should throw error if region is null', async () => { + assert.throw(() => setup({ region: null }), 'Invalid region: null. Choose from us, eu') + }) + it('should throw error if region is empty', async () => { + assert.throw(() => setup({ region: '' }), 'Invalid region: . Choose from us, eu') + }) + it('should throw error if region is space', async () => { + assert.throw(() => setup({ region: ' ' }), 'Invalid region: . Choose from us, eu') + }) + it('should set up default region if region is undefined', async () => { + const settings = setup({ region: undefined }) + expect(settings.apiVersion).to.be.equal(2) + expect(settings.apiUrl).to.be.equal('https://api.evrythng.io/v2') + expect(settings.region).to.be.equal('us') }) }) diff --git a/webpack.config.js b/webpack.config.js index e3f1f62..60ecdac 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,9 +6,7 @@ const library = 'evrythng' const babelrc = { presets: ['@babel/preset-env'], - plugins: [ - ['@babel/transform-runtime', { regenerator: true }] - ] + plugins: [['@babel/transform-runtime', { regenerator: true }]] } const browserConfig = { @@ -20,14 +18,16 @@ const browserConfig = { libraryTarget: 'var' }, module: { - rules: [{ - test: /\.js$/, - exclude: /node_modules/, - use: { - loader: 'babel-loader', - options: babelrc + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: babelrc + } } - }] + ] } } @@ -40,17 +40,16 @@ const nodeConfig = { filename: 'evrythng.node.js', libraryTarget: 'umd', umdNamedDefine: true, - globalObject: 'typeof self !== \'undefined\' ? self : this' + globalObject: "typeof self !== 'undefined' ? self : this" }, module: { - rules: [{ - test: /\.js$/, - exclude: /node_modules/ - }] + rules: [ + { + test: /\.js$/, + exclude: /node_modules/ + } + ] } } -module.exports = [ - browserConfig, - nodeConfig -] +module.exports = [browserConfig, nodeConfig]