@onfleet/node-onfleet
Advanced tools
Comparing version 1.2.3 to 1.2.4
@@ -8,2 +8,2 @@ const constants = { | ||
module.exports = Object.freeze(constants); | ||
module.exports = constants; |
@@ -6,2 +6,3 @@ const DEFAULT_URL = 'https://onfleet.com'; | ||
const constants = require('./constants'); | ||
const util = require('./util'); | ||
@@ -30,3 +31,3 @@ const { ValidationError } = require('./error'); | ||
class Onfleet { | ||
constructor(apiKey, userTimeout) { | ||
constructor(apiKey, userTimeout, bottleneckOptions) { | ||
if (!apiKey) { | ||
@@ -48,2 +49,6 @@ throw new ValidationError('Onfleet API key not found, please obtain an API key from your organization admin'); | ||
}; | ||
if (bottleneckOptions) { | ||
this.initBottleneckOptions(bottleneckOptions) | ||
} | ||
this.resources = resources; | ||
@@ -54,2 +59,31 @@ this.initResources(); | ||
initBottleneckOptions(bottleneckOptions) { | ||
const { | ||
LIMITER_RESERVOIR, | ||
LIMITER_WAIT_UPON_DEPLETION, | ||
LIMITER_MAX_CONCURRENT, | ||
LIMITER_MIN_TIME, | ||
} = bottleneckOptions; | ||
if (Number.isInteger(LIMITER_RESERVOIR) && | ||
LIMITER_RESERVOIR > 0 && | ||
LIMITER_RESERVOIR !== constants.LIMITER_RESERVOIR) { | ||
constants.LIMITER_RESERVOIR = LIMITER_RESERVOIR; | ||
} | ||
if (Number.isInteger(LIMITER_WAIT_UPON_DEPLETION) && | ||
LIMITER_WAIT_UPON_DEPLETION > 0 && | ||
LIMITER_WAIT_UPON_DEPLETION !== constants.LIMITER_WAIT_UPON_DEPLETION) { | ||
constants.LIMITER_WAIT_UPON_DEPLETION = LIMITER_WAIT_UPON_DEPLETION; | ||
} | ||
if (Number.isInteger(LIMITER_MAX_CONCURRENT) && | ||
LIMITER_MAX_CONCURRENT !== constants.LIMITER_MAX_CONCURRENT && | ||
LIMITER_MAX_CONCURRENT > 0) { | ||
constants.LIMITER_MAX_CONCURRENT = LIMITER_MAX_CONCURRENT; | ||
} | ||
if (Number.isInteger(LIMITER_MIN_TIME) && | ||
LIMITER_MIN_TIME > 0 && | ||
LIMITER_MIN_TIME !== constants.LIMITER_MIN_TIME) { | ||
constants.LIMITER_MIN_TIME = LIMITER_MIN_TIME; | ||
} | ||
} | ||
initResources() { | ||
@@ -56,0 +90,0 @@ for (let name in resources) { // eslint-disable-line |
{ | ||
"name": "@onfleet/node-onfleet", | ||
"version": "1.2.3", | ||
"version": "1.2.4", | ||
"description": "Official client library for accessing the Onfleet API", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -69,3 +69,13 @@ # Onfleet Node.js Wrapper | ||
``` | ||
En tant que champ facultatif, vous pouvez introduire un objet d'options pour [Bottleneck](https://www.npmjs.com/package/bottleneck). | ||
```js | ||
const onfleet = new Onfleet("<api_key>", 30000, { | ||
LIMITER_RESERVOIR: 10, // default 20 | ||
LIMITER_WAIT_UPON_DEPLETION: 20000, // default 10000 | ||
LIMITER_MAX_CONCURRENT: 5, // default 1 | ||
LIMITER_MIN_TIME: 50, // default 50 | ||
}); | ||
``` | ||
### Authentification | ||
@@ -72,0 +82,0 @@ Une fois que l'objet Onfleet est créé, vous pouvez utiliser une fonction utilitaire pour tester le noeud final d'authentification. Cette fonction renvoie un booléen: |
230
README.md
@@ -10,3 +10,3 @@ # Onfleet Node.js Wrapper | ||
*Read this document in another language: [English](https://github.com/onfleet/node-onfleet/blob/master/README.md), [French](https://github.com/onfleet/node-onfleet/blob/master/README.fr.md), [正體中文](https://github.com/onfleet/node-onfleet/blob/master/README.zh-tw.md)* | ||
_Read this document in another language: [English](https://github.com/onfleet/node-onfleet/blob/master/README.md), [French](https://github.com/onfleet/node-onfleet/blob/master/README.fr.md), [正體中文](https://github.com/onfleet/node-onfleet/blob/master/README.zh-tw.md)_ | ||
@@ -16,29 +16,30 @@ Visit our [blog post on the API wrapper project](https://onfleet.com/blog/api-wrappers-explained/) to learn more about our initiatives. If you have any questions, please reach out to Onfleet by submitting an issue [here](https://github.com/onfleet/node-onfleet/issues) or contact support@onfleet.com | ||
### Table of Contents | ||
- [Onfleet Node.js Wrapper](#onfleet-nodejs-wrapper) | ||
+ [Table of Contents](#table-of-contents) | ||
- [Table of Contents](#table-of-contents) | ||
* [Synopsis](#synopsis) | ||
* [Installation](#installation) | ||
* [Usage](#usage) | ||
+ [Authentication](#authentication) | ||
+ [Unit Testing](#unit-testing) | ||
+ [Throttling](#throttling) | ||
+ [Responses](#responses) | ||
+ [Supported CRUD Operations](#supported-crud-operations) | ||
- [Authentication](#authentication) | ||
- [Unit Testing](#unit-testing) | ||
- [Throttling](#throttling) | ||
- [Responses](#responses) | ||
- [Supported CRUD Operations](#supported-crud-operations) | ||
- [GET Requests](#get-requests) | ||
* [Examples of get()](#examples-of-get) | ||
* [Examples of get(param)](#examples-of-get-param) | ||
* [Examples of getByLocation](#examples-of-getbylocation) | ||
- [Examples of get()](#examples-of-get) | ||
- [Examples of get(param)](#examples-of-get-param) | ||
- [Examples of getByLocation](#examples-of-getbylocation) | ||
- [POST Requests](#post-requests) | ||
* [Examples of create()](#examples-of-create) | ||
- [Examples of create()](#examples-of-create) | ||
- [PUT Requests](#put-requests) | ||
* [Examples of update()](#examples-of-update) | ||
* [Examples of insertTask()](#examples-of-inserttask) | ||
- [Examples of update()](#examples-of-update) | ||
- [Examples of insertTask()](#examples-of-inserttask) | ||
- [DELETE Requests](#delete-requests) | ||
* [Examples of deleteOne()](#examples-of-deleteone) | ||
+ [Examples of utilizing your CRUD operations](#examples-of-utilizing-your-crud-operations) | ||
+ [Things NOT to do](#things-not-to-do) | ||
- [Examples of deleteOne()](#examples-of-deleteone) | ||
- [Examples of utilizing your CRUD operations](#examples-of-utilizing-your-crud-operations) | ||
- [Things NOT to do](#things-not-to-do) | ||
## Synopsis | ||
The Onfleet Node.js library provides convenient access to the Onfleet API. | ||
The Onfleet Node.js library provides convenient access to the Onfleet API. | ||
@@ -60,16 +61,32 @@ ## Installation | ||
## Usage | ||
Before using the API wrapper, you will need to obtain an API key from your organization admin. Creation and integration of API keys are performed through the [Onfleet dashboard](https://onfleet.com/dashboard#/manage). | ||
To start utilizing the Onfleet API, you simply need to create an Onfleet object with your API key: | ||
```js | ||
const onfleet = new Onfleet('<api_key>'); | ||
const onfleet = new Onfleet("<api_key>"); | ||
``` | ||
As an optional field, you can introduce a customized timeout that is less than the default 70000ms (default Onfleet API timeout) by providing a 2nd parameter: | ||
```js | ||
const onfleet = new Onfleet('<api_key>', 30000) // This will set your wrappers to timeout at 30000ms instead of 70000ms | ||
const onfleet = new Onfleet("<api_key>", 30000); // This will set your wrappers to timeout at 30000ms instead of 70000ms | ||
``` | ||
As an optional field, you can introduce an options object for [Bottleneck](https://www.npmjs.com/package/bottleneck). | ||
```js | ||
const onfleet = new Onfleet("<api_key>", 30000, { | ||
LIMITER_RESERVOIR: 10, // default 20 | ||
LIMITER_WAIT_UPON_DEPLETION: 20000, // default 10000 | ||
LIMITER_MAX_CONCURRENT: 5, // default 1 | ||
LIMITER_MIN_TIME: 50, // default 50 | ||
}); | ||
``` | ||
### Authentication | ||
Once the Onfleet object is created, you can use a utility function to test on the authentication endpoint, this function returns a boolean: | ||
```js | ||
@@ -82,29 +99,34 @@ onfleet.verifyKey(); | ||
### Unit Testing | ||
Run `npm test` | ||
### Throttling | ||
Rate limiting is enforced by the API with a threshold of 20 requests per second across all your organization's API keys, learn more about it [here](https://docs.onfleet.com/reference#throttling). We have implemented a limiter on the wrapper itself to avoid you from unintentionally exceeding your rate limitations and eventually be banned for. | ||
### Responses | ||
The `node-onfleet` API wrapper returns the body of a [Response object](https://developer.mozilla.org/en-US/docs/Web/API/Response). | ||
### Supported CRUD Operations | ||
### Supported CRUD Operations | ||
The base URL for the Onfleet API is `https://onfleet.com/api/v2`, here are the supported CRUD operations for each endpoint: | ||
| `<endpoint>` | GET | POST | PUT | DELETE | | ||
|:------------:|:---------------------------------------------------:|:----------------------------------------------------------------------:|:------------------------------------:|:-------------:| | ||
| [Admins/Administrators](https://docs.onfleet.com/reference#administrators) | get() | create(obj), matchMetadata(obj) | update(id, obj) | deleteOne(id) | | ||
| [Containers](https://docs.onfleet.com/reference#containers) | get(id, 'workers'), get(id, 'teams'), get(id, 'organizations') | x | update(id, obj) | x | | ||
| [Destinations](https://docs.onfleet.com/reference#destinations) | get(id) | create(obj), matchMetadata(obj) | x | x | | ||
| [Hubs](https://docs.onfleet.com/reference#hubs) | get() | x | x | x | | ||
| [Organization](https://docs.onfleet.com/reference#organizations) | get(), get(id) | x | insertTask(id, obj) | x | | ||
| [Recipients](https://docs.onfleet.com/reference#recipients) | get(id), get(name, 'name'), get(phone, 'phone') | create(obj), matchMetadata(obj) | update(id, obj) | x | | ||
| [Tasks](https://docs.onfleet.com/reference#tasks) | get(query), get(id), get(shortId, 'shortId') | create(obj), clone(id), forceComplete(id), batch(obj), autoAssign(obj), matchMetadata(obj) | update(id, obj) | deleteOne(id) | | ||
| [Teams](https://docs.onfleet.com/reference#teams) | get(), get(id), getWorkerEta(id, obj) | create(obj), autoDispatch(id, obj) | update(id, obj), insertTask(id, obj) | deleteOne(id) | | ||
| [Webhooks](https://docs.onfleet.com/reference#webhooks) | get() | create(obj) | x | deleteOne(id) | | ||
| [Workers](https://docs.onfleet.com/reference#workers) | get(), get(query), get(id), getByLocation(obj), getSchedule(id) | create(obj), setSchedule(id, obj), matchMetadata(obj) | update(id, obj), insertTask(id, obj) | deleteOne(id) | | ||
| `<endpoint>` | GET | POST | PUT | DELETE | | ||
| :------------------------------------------------------------------------: | :-------------------------------------------------------------: | :----------------------------------------------------------------------------------------: | :----------------------------------: | :-----------: | | ||
| [Admins/Administrators](https://docs.onfleet.com/reference#administrators) | get() | create(obj), matchMetadata(obj) | update(id, obj) | deleteOne(id) | | ||
| [Containers](https://docs.onfleet.com/reference#containers) | get(id, 'workers'), get(id, 'teams'), get(id, 'organizations') | x | update(id, obj) | x | | ||
| [Destinations](https://docs.onfleet.com/reference#destinations) | get(id) | create(obj), matchMetadata(obj) | x | x | | ||
| [Hubs](https://docs.onfleet.com/reference#hubs) | get() | x | x | x | | ||
| [Organization](https://docs.onfleet.com/reference#organizations) | get(), get(id) | x | insertTask(id, obj) | x | | ||
| [Recipients](https://docs.onfleet.com/reference#recipients) | get(id), get(name, 'name'), get(phone, 'phone') | create(obj), matchMetadata(obj) | update(id, obj) | x | | ||
| [Tasks](https://docs.onfleet.com/reference#tasks) | get(query), get(id), get(shortId, 'shortId') | create(obj), clone(id), forceComplete(id), batch(obj), autoAssign(obj), matchMetadata(obj) | update(id, obj) | deleteOne(id) | | ||
| [Teams](https://docs.onfleet.com/reference#teams) | get(), get(id), getWorkerEta(id, obj) | create(obj), autoDispatch(id, obj) | update(id, obj), insertTask(id, obj) | deleteOne(id) | | ||
| [Webhooks](https://docs.onfleet.com/reference#webhooks) | get() | create(obj) | x | deleteOne(id) | | ||
| [Workers](https://docs.onfleet.com/reference#workers) | get(), get(query), get(id), getByLocation(obj), getSchedule(id) | create(obj), setSchedule(id, obj), matchMetadata(obj) | update(id, obj), insertTask(id, obj) | deleteOne(id) | | ||
#### GET Requests | ||
To get all the documents within an endpoint, this returns a Promise containing an array of results: | ||
```js | ||
@@ -114,20 +136,25 @@ get(); | ||
``` | ||
##### Examples of get() | ||
```js | ||
onfleet.workers.get().then((resultArray) => { | ||
// do something with the array containing all workers | ||
// do something with the array containing all workers | ||
}); | ||
``` | ||
Option to use query parameters for some certain endpoints, refer back to API documents for endpoints that support query parameters: | ||
```js | ||
onfleet.workers.get({phones: '<phone_number>'}).then((res) => { | ||
onfleet.workers.get({ phones: "<phone_number>" }).then((res) => { | ||
// do something with the one result found | ||
}); | ||
onfleet.tasks.get({from: '<from_time>', to: '<to_time>'}).then((result) => { | ||
onfleet.tasks.get({ from: "<from_time>", to: "<to_time>" }).then((result) => { | ||
// do something with the results found | ||
}); | ||
``` | ||
> **Note:** Query parameters can be in any forms as long as it is a JSON object, for example: `{ 'analytics': 'true' }` works, so as `{ analytics: true }` | ||
To get one of the document within an endpoint, if optional *paramName* is not provided, wrapper will search by ID. If *paramName* is provided, it will search by *paramName*: | ||
To get one of the document within an endpoint, if optional _paramName_ is not provided, wrapper will search by ID. If _paramName_ is provided, it will search by _paramName_: | ||
@@ -137,5 +164,7 @@ ```js | ||
``` | ||
Options for *paramName*: id, name, phone, shortId (see table above). | ||
Options for _paramName_: id, name, phone, shortId (see table above). | ||
##### Examples of get(param) | ||
```js | ||
@@ -145,6 +174,6 @@ /** | ||
*/ | ||
onfleet.workers.get('<24_digit_id>').then((result) => { | ||
onfleet.workers.get("<24_digit_id>").then((result) => { | ||
// do something with the one result found | ||
}); | ||
onfleet.workers.get('<24_digit_id>', { analytics: true }).then((result) => { | ||
onfleet.workers.get("<24_digit_id>", { analytics: true }).then((result) => { | ||
// do something with the one result found | ||
@@ -156,3 +185,3 @@ }); | ||
*/ | ||
onfleet.tasks.get('<shortId>', 'shortId').then((result) => { | ||
onfleet.tasks.get("<shortId>", "shortId").then((result) => { | ||
// do something with the one result found | ||
@@ -164,11 +193,13 @@ }); | ||
*/ | ||
onfleet.recipients.get('<phone_number>', 'phone').then((result) => { | ||
onfleet.recipients.get("<phone_number>", "phone").then((result) => { | ||
// do something with the one result found | ||
}); | ||
onfleet.recipients.get('<recipient_name>', 'name').then((result) => { | ||
onfleet.recipients.get("<recipient_name>", "name").then((result) => { | ||
// do something with the one result found | ||
}); | ||
onfleet.recipients.get('<recipient_name>', 'name', { skipPhoneNumberValidation: true}).then((result) => { | ||
// do something with the one result found | ||
}); | ||
onfleet.recipients | ||
.get("<recipient_name>", "name", { skipPhoneNumberValidation: true }) | ||
.then((result) => { | ||
// do something with the one result found | ||
}); | ||
@@ -178,19 +209,21 @@ /** | ||
*/ | ||
onfleet.containers.get('<24_digit_id>', 'workers').then((result) => { | ||
onfleet.containers.get("<24_digit_id>", "workers").then((result) => { | ||
// do something with the one result found | ||
}); | ||
onfleet.containers.get('<24_digit_id>', 'teams').then((result) => { | ||
onfleet.containers.get("<24_digit_id>", "teams").then((result) => { | ||
// do something with the one result found | ||
}); | ||
onfleet.containers.get('<24_digit_id>', 'organizations').then((result) => { | ||
onfleet.containers.get("<24_digit_id>", "organizations").then((result) => { | ||
// do something with the one result found | ||
}); | ||
``` | ||
To get a driver by location, use the `getByLocation` function: | ||
```js | ||
getByLocation(<queryParam>); | ||
``` | ||
##### Examples of getByLocation | ||
```js | ||
@@ -200,4 +233,4 @@ const location = { | ||
latitude: 37.789, | ||
radius: 10000 | ||
} | ||
radius: 10000, | ||
}; | ||
@@ -210,17 +243,21 @@ onfleet.workers.getByLocation(location).then((result) => { | ||
#### POST Requests | ||
To create a document within an endpoint: | ||
```js | ||
create(<object>); | ||
``` | ||
##### Examples of create() | ||
```js | ||
const driver = { | ||
name: 'A Swartz', | ||
phone: '617-342-8853', | ||
teams: ['W*8bF5jY11Rk05E0bXBHiGg2'], | ||
name: "A Swartz", | ||
phone: "617-342-8853", | ||
teams: ["W*8bF5jY11Rk05E0bXBHiGg2"], | ||
vehicle: { | ||
type: 'CAR', | ||
description: 'Tesla Model 3', | ||
licensePlate: 'FKNS9A', | ||
color: 'purple', | ||
type: "CAR", | ||
description: "Tesla Model 3", | ||
licensePlate: "FKNS9A", | ||
color: "purple", | ||
}, | ||
@@ -230,2 +267,3 @@ }; | ||
``` | ||
Extended POST requests include `clone`, `forceComplete`, `batchCreate`, `autoAssign` on the tasks endpoint, `setSchedule` on the workers endpoint, `matchMetadata` on all supported entity endpoints, and `autoDispatch` on the teams endpoint: | ||
@@ -248,44 +286,63 @@ | ||
#### PUT Requests | ||
To update a document within an endpoint: | ||
```js | ||
update(<id>, <object>); | ||
``` | ||
##### Examples of update() | ||
```js | ||
const updateBody = { | ||
name: 'New Driver Name', | ||
name: "New Driver Name", | ||
}; | ||
onfleet.workers.update('<24_digit_id>', updateBody); | ||
onfleet.workers.update("<24_digit_id>", updateBody); | ||
``` | ||
##### Examples of insertTask() | ||
```js | ||
onfleet.workers.insertTask('kAQ*G5hnqlOq4jVvwtGNuacl', insertedTask).then(result => { | ||
// do something | ||
}); | ||
onfleet.workers | ||
.insertTask("kAQ*G5hnqlOq4jVvwtGNuacl", insertedTask) | ||
.then((result) => { | ||
// do something | ||
}); | ||
``` | ||
#### DELETE Requests | ||
To delete a document within an endpoint: | ||
```js | ||
deleteOne(<id>); | ||
``` | ||
##### Examples of deleteOne() | ||
```js | ||
onfleet.workers.deleteOne('<24_digit_id>'); | ||
onfleet.workers.deleteOne("<24_digit_id>"); | ||
``` | ||
### Examples of utilizing your CRUD operations | ||
Get all the recipients (if it exist): | ||
```js | ||
onfleet.tasks.get({from: '1557936000000', to: '1558022400000'}).then((res) => { | ||
for (let element of res) { | ||
if (element.recipients[0] !== undefined) { | ||
onfleet.tasks | ||
.get({ from: "1557936000000", to: "1558022400000" }) | ||
.then((res) => { | ||
for (let element of res) { | ||
if (element.recipients[0] !== undefined) { | ||
// do something with the recipients | ||
} | ||
} | ||
} | ||
}).catch((err) => { | ||
// do something with the error | ||
}); | ||
}) | ||
.catch((err) => { | ||
// do something with the error | ||
}); | ||
``` | ||
Async/await can also be used in this following case: | ||
```js | ||
@@ -296,6 +353,6 @@ async function findAllWorkers() { | ||
// do something with the response | ||
} catch(err) { | ||
} catch (err) { | ||
throw new Error(err); | ||
} | ||
}; | ||
} | ||
@@ -306,15 +363,20 @@ findAllWorkers(); | ||
### Things NOT to do | ||
Inefficient pattern, use metadata instead: | ||
```js | ||
onfleet.workers.get().then((res) => { | ||
for (let element of res) { | ||
if (element.name == 'some_name') { | ||
onfleet.teams.get(element.teams[0]).then((result) => { | ||
// do something with the team | ||
}) | ||
onfleet.workers | ||
.get() | ||
.then((res) => { | ||
for (let element of res) { | ||
if (element.name == "some_name") { | ||
onfleet.teams.get(element.teams[0]).then((result) => { | ||
// do something with the team | ||
}); | ||
} | ||
} | ||
} | ||
}).catch((err) => { | ||
// do something with the error | ||
}); | ||
``` | ||
}) | ||
.catch((err) => { | ||
// do something with the error | ||
}); | ||
``` |
@@ -73,2 +73,12 @@ # Onfleet Node.js Wrapper | ||
作為可選字段,您可以引入一個用於 Bottleneck 的選項對象 [Bottleneck](https://www.npmjs.com/package/bottleneck). | ||
```js | ||
const onfleet = new Onfleet('<api_key>', 30000, { | ||
LIMITER_RESERVOIR: 10, // default 20 | ||
LIMITER_WAIT_UPON_DEPLETION: 20000, // default 10000 | ||
LIMITER_MAX_CONCURRENT: 5, // default 1 | ||
LIMITER_MIN_TIME: 50, // default 50 | ||
}); | ||
``` | ||
### 金鑰認證 | ||
@@ -75,0 +85,0 @@ 當Onfleet物件成功被創建,表示您的應用程式介面金鑰是符合預期的。您可以嘗試使用verifyKey函式來測試您的金鑰是否合法,authentication這個endpoint會認證您的金鑰,回應為一布林值: |
@@ -76,2 +76,27 @@ /* eslint-disable arrow-body-style */ | ||
describe('Initial testing', () => { | ||
it('without bottleneck options', () => { | ||
const onfleetWithoutOptions = new Onfleet(apiKey); | ||
const constans = require('../lib/constants'); | ||
assert.equal(constans.LIMITER_MAX_CONCURRENT, 1); | ||
assert.equal(constans.LIMITER_MIN_TIME, 50); | ||
assert.equal(constans.LIMITER_WAIT_UPON_DEPLETION, 10000); | ||
assert.equal(constans.LIMITER_RESERVOIR, 20); | ||
}); | ||
it('with bottleneck options', () => { | ||
const onfleetWithOptions = new Onfleet(apiKey, undefined, { | ||
LIMITER_RESERVOIR: 10, | ||
LIMITER_WAIT_UPON_DEPLETION: 20000, | ||
LIMITER_MAX_CONCURRENT: 5, | ||
LIMITER_MIN_TIME: 10, | ||
}); | ||
const constans = require('../lib/constants'); | ||
assert.equal(constans.LIMITER_MAX_CONCURRENT, 5); | ||
assert.equal(constans.LIMITER_MIN_TIME, 10); | ||
assert.equal(constans.LIMITER_WAIT_UPON_DEPLETION, 20000); | ||
assert.equal(constans.LIMITER_RESERVOIR, 10); | ||
}); | ||
}); | ||
describe('HTTP Request testing', () => { | ||
@@ -78,0 +103,0 @@ const onfleet = new Onfleet(apiKey); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
75619
1076
367