spypoint-api-wrapper
Advanced tools
Comparing version
{ | ||
"name": "spypoint-api-wrapper", | ||
"version": "2.0.2", | ||
"version": "2.1.0", | ||
"description": "Node.js wrapper for the Spypoint game camera API", | ||
"main": "spypoint.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "jest test.js" | ||
}, | ||
@@ -15,2 +15,4 @@ "keywords": [ | ||
"game", | ||
"camera", | ||
"trail", | ||
"camera" | ||
@@ -23,3 +25,7 @@ ], | ||
}, | ||
"type": "module" | ||
} | ||
"type": "module", | ||
"devDependencies": { | ||
"dotenv": "^15.0.0", | ||
"jest": "^27.4.7" | ||
} | ||
} |
@@ -11,6 +11,40 @@ # spypoint-api-wrapper | ||
const Spypoint = new SpypointClient('YOUR_EMAIL_OR_USERNAME', 'YOUR_PASSWORD') | ||
await Spypoint.login() | ||
const Spypoint = new SpypointClient() | ||
await Spypoint.login('YOUR_EMAIL_OR_USERNAME', 'YOUR_PASSWORD') | ||
``` | ||
### Using within express.js routes and individual users | ||
```js | ||
import SpypointClient from './spypoint.js' | ||
const SpypointInit = (req, res, next) => { | ||
if (!req.cookies.authorization) throw Error('You need to login with valid credentials first!') | ||
req.Spypoint = new SpypointClient(req.cookies.authorization) | ||
next() | ||
} | ||
// Send user crederntials to login route and set auth token on the cookie | ||
router.post('/login', async (req, res) => { | ||
const Spypoint = new SpypointClient() | ||
const bearer = await Spypoint.login(req.body.email, req.body.password) | ||
res.cookie('authorization', bearer, { | ||
expire: '2100-01-01T00:00:00.000Z, | ||
httpOnly: process.env.NODE_ENV === 'production' ? true : false | ||
}) | ||
res.send() | ||
}) | ||
router.get('/', SpypointInit, (req, res) => { | ||
const cameras = await req.Spypoint.cameras() | ||
res.send(cameras) | ||
}) | ||
``` | ||
## API | ||
@@ -21,3 +55,2 @@ | ||
### Spypoint.login() ⇒ <code>Promise.<string></code> | ||
**Kind**: Spypoint method | ||
@@ -29,3 +62,2 @@ **Returns**: <code>Promise.<string></code> - Bearer token used for authorization (this is automatically set and added to all requests) | ||
### Spypoint.cameras() ⇒ <code>Promise.<Array></code> | ||
**Kind**: Spypoint method | ||
@@ -36,6 +68,5 @@ **Returns**: <code>Promise.<Array></code> - List of all available `cameraId`s | ||
### Spypoint.filters() ⇒ <code>Promise.<Array></code> | ||
**Kind**: Spypoint method | ||
### Spypoint.filters() ⇒ <code>Promise.<Object></code> | ||
**Returns**: <code>Promise.<Array></code> - List of all available filter tags | ||
**Returns**: <code>Promise.<Object></code> - Object containing a property `.species` w/ list of all available filter tags | ||
@@ -45,3 +76,2 @@ <a name="Spypoint.mostRecentPhotosByCamera"></a> | ||
### Spypoint.mostRecentPhotosByCamera() ⇒ <code>Promise.<Array></code> | ||
**Kind**: Spypoint method | ||
@@ -53,3 +83,2 @@ **Returns**: <code>Promise.<Array></code> - List of most recent photo taken from each camera | ||
### Spypoint.photosByCamera(cameraId, [options]) ⇒ <code>Promise.<Array></code> | ||
**Kind**: Spypoint method | ||
@@ -65,6 +94,5 @@ **Returns**: <code>Promise.<Array></code> - List of photos from an individual camera | ||
<a name="Spypoint.allPhotosByFilter"></a> | ||
<a name="Spypoint.queryAllPhotos"></a> | ||
### Spypoint.allPhotosByFilter([options]) ⇒ <code>Promise.<Array></code> | ||
**Kind**: Spypoint method | ||
### Spypoint.queryAllPhotos([options]) ⇒ <code>Promise.<Array></code> | ||
@@ -77,2 +105,4 @@ **Returns**: <code>Promise.<Array></code> - List of photo by individual camera | ||
| [options.tags] | <code>Array</code> \| <code>string</code> | Array of filter tag options or a single tag as a string | | ||
| [options.limit] | <code>Number</code> | Maximum number of results to return | | ||
| [options.limit] | <code>Number</code> | Maximum number of results to return | | ||
113
spypoint.js
@@ -13,14 +13,10 @@ import fetch from 'node-fetch' | ||
constructor( | ||
username = isRequired(), | ||
password = isRequired() | ||
){ | ||
this._username = username | ||
this._password = password | ||
constructor(authorization) { | ||
this._headers = { | ||
'Content-Type': 'application/json' | ||
} | ||
this._headers.authorization = authorization | ||
} | ||
async get(apiEndpoint) { | ||
async _get(apiEndpoint) { | ||
const data = await fetch(apiEndpoint, { | ||
@@ -32,4 +28,6 @@ headers: this._headers | ||
async post(cameraId = isRequired(), options = { tags: [], limit: 100 }) { | ||
async _post(cameraId = isRequired(), { limit: 100, tags: []} = {}) { | ||
const { limit, tags } = options | ||
const data = await fetch(PHOTOS, { | ||
@@ -39,3 +37,3 @@ method: 'POST', | ||
body: JSON.stringify({ | ||
cameraId: [cameraId], | ||
camera: [cameraId], | ||
dateEnd: "2100-01-01T00:00:00.000Z", | ||
@@ -52,7 +50,40 @@ favorite: false, | ||
/** | ||
* Note: This is an async function, it retrieves a list of available tags from Spytpoint and | ||
* compares the provided tags to ensure they match. Spypoint occasionally changes these hence why | ||
* this is necessary otherwise it will return an error if you add a no longer supported tag. | ||
*/ | ||
async _tagParamCheck(tags){ | ||
if (typeof tags === 'string'){ | ||
tags = [tags] | ||
} | ||
if (!Array.isArray(tags)){ | ||
return console.error('Tag parameter needs to either be a string or an array of strings') | ||
} | ||
tags = tags.map(tag => tag.toLowerCase()) | ||
const filters = await this.filters() | ||
const filteredNames = filters.species.map(({nameId}) => nameId) | ||
const cleanTags = tags.filter( tag => { | ||
if (!filteredNames.includes(tag)){ | ||
console.error(`The tag "${tag}" is not an available option. | ||
Please check your spelling or use Spypoint.filters() to | ||
see all available tags.`) | ||
return false | ||
} | ||
return tag | ||
}) | ||
return cleanTags | ||
} | ||
/** | ||
* @return {Object} - Auth token and uuid credentials | ||
*/ | ||
async login() { | ||
async login(username, password) { | ||
const credentialRes = await fetch(LOGIN, { | ||
@@ -62,4 +93,4 @@ method: 'POST', | ||
body: JSON.stringify({ | ||
username: this._username, | ||
password: this._password | ||
username, | ||
password | ||
}) | ||
@@ -83,4 +114,4 @@ }) | ||
async cameras() { | ||
const cameras = await this.get(CAMERAS) | ||
return cameras.json() | ||
const cameras = await this._get(CAMERAS) | ||
return cameras | ||
} | ||
@@ -93,4 +124,4 @@ | ||
async filters() { | ||
const filters = await this.get(FILTERS) | ||
return filters.json() | ||
const filters = await this._get(FILTERS) | ||
return filters | ||
} | ||
@@ -105,19 +136,25 @@ | ||
async photosByCamera(cameraId = isRequired(), options = { limit: 100, tags: [] }){ | ||
const photos = await this.post(cameraId, {limit, tags}) | ||
return photos.json() | ||
async photosByCamera(cameraId = isRequired(), { limit = 100, tags = []} = {}){ | ||
tags = await this._tagParamCheck(tags) | ||
const photos = await this._post(cameraId, { limit, tags }) | ||
return photos | ||
} | ||
/** | ||
* @param {string[]|string} [tags=[]] - Query to limit results by photo subject i.e. 'deer', 'bears' | ||
* @return {Object[]} - The most recent photo from each camera | ||
*/ | ||
async mostRecentPhotosByCamera() { | ||
async mostRecentPhotosByCamera(tags = []) { | ||
const cameras = await this.cameras() | ||
const photoReq = cameras.map(({ id }) => this.photosByCamera(id, { limit: 1 })) | ||
const photoReq = cameras.map(({ id }) => this.photosByCamera(id, { limit: 1, tags })) | ||
const photoDataRes = await Promise.all(photoReq) | ||
return photoDataRes | ||
.filter(({ photos }) => !!photos.length) | ||
.map(({ photos }) => photos[0]) | ||
.filter(({ cameraIds }) => !!cameraIds.length) | ||
.map(photoList => photoList.photos[0]) | ||
@@ -132,30 +169,14 @@ } | ||
async queryAllPhotos(options = { limit: 100, tags: []}) { | ||
/** | ||
* Note: The Spypoint API limits results to most recent 25 photos | ||
*/ | ||
if (typeof options.tags === 'string'){ | ||
options.tags = [options.tags] | ||
} | ||
async queryAllPhotos({ limit = 100, tags = []} = { limit, tags }) { | ||
if (!Array.isArray(options.tags)){ | ||
return console.error('Tag parameter needs to either be a string or an array of strings') | ||
} | ||
tags = await this._tagParamCheck(tags) | ||
options.tags = options.tags.map(tag => tag.toLowerCase()) | ||
const filters = await this.filters() | ||
const filteredNames = filters.species.map(({nameId}) => nameId) | ||
const cleanTags = options.tags.filter( tag => { | ||
if (!filteredNames.includes(tag)){ | ||
console.error(`The tag "${tag}" is not an available option. Please check your spelling.`) | ||
return false | ||
} | ||
return tag | ||
}) | ||
const cameras = await this.cameras() | ||
const photoReq = cameras.map(({ id }) => { | ||
return this.photosByCamera(id, {limit: options.limit, tags: cleanTags}) | ||
return this.photosByCamera(id, {limit, tags}) | ||
}) | ||
@@ -162,0 +183,0 @@ const photoRes = await Promise.all(photoReq) |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
11638
62.29%5
66.67%207
68.29%1
-50%101
44.29%2
Infinity%6
200%