Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| baff59181c | |||
| 490d015f80 | |||
| f1cc2c4ebe | |||
| 2f4421d9e0 | |||
| 92cc094787 | |||
| f30b46c384 | |||
| d9f679603a | |||
| 64bd9d1e14 | |||
| 721826d454 | |||
| 242fe3515c | |||
| ccf40d2161 | |||
| 832b8ba539 | |||
|
|
0477e49eca | ||
| 451b67630a | |||
| 096bbdf085 | |||
| e914e4ab45 | |||
| c1461e1f41 | |||
| 91bf2c1e2a | |||
| da3df383ed | |||
| 9816b978d3 | |||
| 8e22b0f6ea | |||
| 18359f442c | |||
| 42b8b5ea0e | |||
| 996295b1fe | |||
| 1b08c8d3d1 | |||
| 9e145f7068 | |||
| 7051edb212 |
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "torrent_search"]
|
||||
path = torrent_search
|
||||
url = git@github.com:KevinMidboe/torrent_search.git
|
||||
url = https://github.com/KevinMidboe/torrent_search.git
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
language: node_js
|
||||
node_js: '8.7.0'
|
||||
git:
|
||||
submodules: false
|
||||
submodules: true
|
||||
script:
|
||||
yarn test
|
||||
- yarn test
|
||||
- yarn coverage
|
||||
before_install:
|
||||
- cd seasoned_api
|
||||
before_script: yarn
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# 🌶 seasonedShows
|
||||
[](https://travis-ci.org/KevinMidboe/seasonedShows)
|
||||
[](https://coveralls.io/github/KevinMidboe/seasonedShows?branch=coverage)
|
||||
[](https://www.versioneye.com/user/projects/5ac541370fb24f4489396e02)
|
||||
[](https://snyk.io/test/github/KevinMidboe/seasonedShows?targetFile=seasoned_api/package.json)
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
Your customly *seasoned* movie and show requester, downloader and organizer. Demo page can be viewed [here](https://kevinmidboe.com/request)
|
||||
Your customly *seasoned* movie and show requester, downloader and organizer.
|
||||
📺 [Demo](https://kevinmidboe.com/request)
|
||||
|
||||
## About
|
||||
The goal of this project is to create a full custom stack that can to everything surround downloading, organizing and notifiyng of new media. From the top down we have a website using [tmdb](https://www.themoviedb.com) api to search for from over 350k movies and 70k tv shows. Using [hjone72](https://github.com/hjone72/PlexAuth) great PHP reverse proxy we can have a secure way of allowing users to login with their plex credentials which limits request capabilites to only users that are authenticated to use your plex library.
|
||||
|
||||
17
seasoned_api/conf/test.json
Normal file
17
seasoned_api/conf/test.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"database": {
|
||||
"host": ":memory:"
|
||||
},
|
||||
"webserver": {
|
||||
"port": 31400
|
||||
},
|
||||
"tmdb": {
|
||||
"apiKey": "bogus-api-key"
|
||||
},
|
||||
"raven": {
|
||||
"DSN": ""
|
||||
},
|
||||
"authentication": {
|
||||
"secret": "secret"
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,42 @@
|
||||
{
|
||||
"name": "seasoned-api",
|
||||
"main": "webserver/server.js",
|
||||
"scripts": {
|
||||
"start": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/webserver/server.js",
|
||||
"test": "cross-env SEASONED_CONFIG=conf/development.json TESTING=true NODE_PATH=. mocha --recursive test/system",
|
||||
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. istanbul cover -x script/autogenerate-documentation.js --include-all-sources --dir test/.coverage node_modules/mocha/bin/_mocha --recursive test/**/* -- --report lcovonly && cat test/.coverage/lcov.info | coveralls && rm -rf test/.coverage",
|
||||
"lint": "./node_modules/.bin/eslint src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt-nodejs": "^0.0.3",
|
||||
"body-parser": "~1.0.1",
|
||||
"cross-env": "^3.1.3",
|
||||
"express": "~4.11.0",
|
||||
"jsonwebtoken": "^8.0.1",
|
||||
"mongoose": "^3.6.13",
|
||||
"moviedb": "^0.2.10",
|
||||
"node-cache": "^4.1.1",
|
||||
"nodemailer": "^4.0.1",
|
||||
"python-shell": "^0.4.0",
|
||||
"raven": "^2.2.1",
|
||||
"request": "^2.81.0",
|
||||
"request-promise": "^4.2",
|
||||
"sqlite3": "3.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^4.9.0",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^3.1.0",
|
||||
"supertest": "^2.0.1",
|
||||
"supertest-as-promised": "^4.0.1"
|
||||
}
|
||||
"name": "seasoned-api",
|
||||
"description": "Packages needed to build and commands to run seasoned api node server.",
|
||||
"license": {
|
||||
"type": "MIT",
|
||||
"url": "https://www.opensource.org/licenses/mit-license.php"
|
||||
},
|
||||
"main": "webserver/server.js",
|
||||
"scripts": {
|
||||
"start": "cross-env SEASONED_CONFIG=conf/development.json NODE_PATH=. node src/webserver/server.js",
|
||||
"test": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. mocha --recursive test",
|
||||
"coverage": "cross-env SEASONED_CONFIG=conf/test.json NODE_PATH=. nyc mocha --recursive test && nyc report --reporter=text-lcov | coveralls",
|
||||
"lint": "./node_modules/.bin/eslint src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt-nodejs": "^0.0.3",
|
||||
"body-parser": "~1.18.2",
|
||||
"cross-env": "~5.1.4",
|
||||
"express": "~4.16.0",
|
||||
"jsonwebtoken": "^8.0.1",
|
||||
"mongoose": "~5.0.11",
|
||||
"moviedb": "^0.2.10",
|
||||
"node-cache": "^4.1.1",
|
||||
"python-shell": "^0.5.0",
|
||||
"request": "^2.85.0",
|
||||
"request-promise": "^4.2",
|
||||
"sqlite3": "4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coveralls": "^3.0.0",
|
||||
"eslint": "^4.9.0",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^5.0.4",
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"nyc": "^11.6.0",
|
||||
"raven": "^2.4.2",
|
||||
"supertest": "^3.0.0",
|
||||
"supertest-as-promised": "^4.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class Config {
|
||||
const field = new Field(this.fields[section][option]);
|
||||
|
||||
if (field.value === '') {
|
||||
const envField = process.env[[section.toUpperCase(), option.toUpperCase()].join('_')];
|
||||
const envField = process.env[['SEASONED', section.toUpperCase(), option.toUpperCase()].join('_')];
|
||||
if (envField !== undefined && envField.length !== 0) { return envField; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
const configuration = require('src/config/configuration').getInstance();
|
||||
const SqliteDatabase = require('src/database/sqliteDatabase');
|
||||
|
||||
const host = process.env.TESTING ? ':memory:' : configuration.get('database', 'host');
|
||||
const database = new SqliteDatabase(host);
|
||||
const database = new SqliteDatabase(configuration.get('database', 'host'));
|
||||
/**
|
||||
* This module establishes a connection to the database
|
||||
* specified in the confgiuration file. It tries to setup
|
||||
|
||||
@@ -14,8 +14,8 @@ class RequestRepository {
|
||||
this.queries = {
|
||||
insertRequest: `INSERT INTO requests(id,title,year,poster_path,background_path,requested_by,ip,user_agent,type)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
fetchRequestedItems: 'SELECT * FROM requests ORDER BY date DESC',
|
||||
fetchRequestedItemsByStatus: 'SELECT * FROM requests WHERE status IS ? AND type LIKE ? ORDER BY date DESC',
|
||||
fetchRequestedItems: 'SELECT * FROM requests ORDER BY date DESC LIMIT 25 OFFSET ?*25-25',
|
||||
fetchRequestedItemsByStatus: 'SELECT * FROM requests WHERE status IS ? AND type LIKE ? ORDER BY date DESC LIMIT 25 OFFSET ?*25-25',
|
||||
updateRequestedById: 'UPDATE requests SET status = ? WHERE id is ? AND type is ?',
|
||||
checkIfIdRequested: 'SELECT * FROM requests WHERE id IS ? AND type IS ?',
|
||||
userRequests: 'SELECT * FROM requests WHERE requested_by IS ?',
|
||||
@@ -58,23 +58,23 @@ class RequestRepository {
|
||||
* @returns {Promise} If nothing has gone wrong.
|
||||
*/
|
||||
sendRequest(identifier, type, ip, user_agent, user) {
|
||||
return Promise.resolve()
|
||||
.then(() => tmdb.lookup(identifier, type))
|
||||
.then((movie) => {
|
||||
const username = user === undefined ? undefined : user.username;
|
||||
// Add request to database
|
||||
return this.database.run(this.queries.insertRequest, [movie.id, movie.title, movie.year, movie.poster_path, movie.background_path, username, ip, user_agent, movie.type]);
|
||||
});
|
||||
return Promise.resolve()
|
||||
.then(() => tmdb.lookup(identifier, type))
|
||||
.then((movie) => {
|
||||
const username = user === undefined ? undefined : user.username;
|
||||
// Add request to database
|
||||
return this.database.run(this.queries.insertRequest, [movie.id, movie.title, movie.year, movie.poster_path, movie.background_path, username, ip, user_agent, movie.type]);
|
||||
});
|
||||
}
|
||||
|
||||
fetchRequested(status, type = '%') {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
if (status === 'requested' || status === 'downloading' || status === 'downloaded') {
|
||||
return this.database.all(this.queries.fetchRequestedItemsByStatus, [status, type]);
|
||||
}
|
||||
return this.database.all(this.queries.fetchRequestedItems);
|
||||
});
|
||||
fetchRequested(status, page = '1', type = '%') {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
if (status === 'requested' || status === 'downloading' || status === 'downloaded')
|
||||
return this.database.all(this.queries.fetchRequestedItemsByStatus, [status, type, page]);
|
||||
else
|
||||
return this.database.all(this.queries.fetchRequestedItems, page);
|
||||
})
|
||||
}
|
||||
|
||||
userRequests(user) {
|
||||
|
||||
@@ -29,6 +29,7 @@ class TMDB {
|
||||
/**
|
||||
* Retrieve a specific movie by id from TMDB.
|
||||
* @param {Number} identifier of the movie you want to retrieve
|
||||
* @param {String} type filter results by type (default movie).
|
||||
* @returns {Promise} succeeds if movie was found
|
||||
*/
|
||||
lookup(identifier, type = 'movie') {
|
||||
@@ -36,7 +37,7 @@ class TMDB {
|
||||
const cacheKey = `${this.cacheTags.info}:${type}:${identifier}`;
|
||||
return Promise.resolve()
|
||||
.then(() => this.cache.get(cacheKey))
|
||||
.catch(() => this.tmdb(this.tmdbMethod('info', type), query))
|
||||
.catch(() => this.tmdb(TMDB_METHODS['info'][type], query))
|
||||
.catch(() => { throw new Error('Could not find a movie with that id.'); })
|
||||
.then(response => this.cache.set(cacheKey, response))
|
||||
.then((response) => {
|
||||
@@ -50,70 +51,66 @@ class TMDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive list of of items from TMDB matching the query and/or type given.
|
||||
* @param {queryText, page, type} the page number to specify in the request for discover,
|
||||
* Retrive search results from TMDB.
|
||||
* @param {String} text query you want to search for
|
||||
* @param {Number} page representing pagination of results
|
||||
* @param {String} type filter results by type (default multi)
|
||||
* @returns {Promise} dict with query results, current page and total_pages
|
||||
*/
|
||||
search(text, page = 1, type = 'multi') {
|
||||
const query = { query: text, page };
|
||||
const query = { query: text, page: page };
|
||||
const cacheKey = `${this.cacheTags.search}:${page}:${type}:${text}`;
|
||||
return Promise.resolve()
|
||||
.then(() => this.cache.get(cacheKey))
|
||||
.catch(() => this.tmdb(this.tmdbMethod('search', type), query))
|
||||
.catch(() => this.tmdb(TMDB_METHODS['search'][type], query))
|
||||
.catch(() => { throw new Error('Could not search for movies/shows at tmdb.'); })
|
||||
.then(response => this.cache.set(cacheKey, response))
|
||||
.then(response => this.mapResults(response))
|
||||
.catch((error) => { throw new Error(error); })
|
||||
.then(([mappedResults, pagenumber, totalpages, total_results]) => ({
|
||||
results: mappedResults, page: pagenumber, total_results, total_pages: totalpages,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a given list from tmdb.
|
||||
* @param {listName} List we want to fetch.
|
||||
* @param {type} The to specify in the request for discover (default 'movie').
|
||||
* @param {id} When finding similar a id can be added to query
|
||||
* @param {page} Page number we want to fetch.
|
||||
* @param {String} listName Name of list
|
||||
* @param {String} type filter results by type (default movie)
|
||||
* @param {Number} page representing pagination of results
|
||||
* @returns {Promise} dict with query results, current page and total_pages
|
||||
*/
|
||||
listSearch(listName, type = 'movie', id, page = '1') {
|
||||
const params = { id, page };
|
||||
const cacheKey = `${this.cacheTags[listName]}:${type}:${id}:${page}`;
|
||||
listSearch(listName, type = 'movie', page = '1') {
|
||||
const query = { page: page }
|
||||
console.log(query)
|
||||
const cacheKey = `${this.cacheTags[listName]}:${type}:${page}`;
|
||||
return Promise.resolve()
|
||||
.then(() => this.cache.get(cacheKey))
|
||||
.catch(() => this.tmdb(this.tmdbMethod(listName, type), params))
|
||||
.catch(() => this.tmdb(TMDB_METHODS[listName][type], query))
|
||||
.catch(() => { throw new Error('Error fetching list from tmdb.')})
|
||||
.then(response => this.cache.set(cacheKey, response))
|
||||
.then(response => this.mapResults(response, type))
|
||||
.catch((error) => { throw new Error(error); })
|
||||
.then(([mappedResults, pagenumber, totalpages, total_results]) => ({
|
||||
results: mappedResults, page: pagenumber, total_pages: totalpages, total_results,
|
||||
}));
|
||||
}
|
||||
|
||||
tmdbMethod(apiMethod, type) {
|
||||
const method = TMDB_METHODS[apiMethod][type];
|
||||
if (method !== undefined) return method;
|
||||
throw new Error('Could not find tmdb api method.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps our response from tmdb api to a movie/show object.
|
||||
* @param {response} JSON response from tmdb.
|
||||
* @param {type} The type declared in listSearch.
|
||||
* @param {String} response from tmdb.
|
||||
* @param {String} The type declared in listSearch.
|
||||
* @returns {Promise} dict with tmdb results, mapped as movie/show objects.
|
||||
*/
|
||||
mapResults(response, type) {
|
||||
console.log(response.page)
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
const mappedResults = response.results.filter((element) => {
|
||||
return (element.media_type === 'movie' || element.media_type === 'tv' || element.media_type === undefined);
|
||||
}).map((element) => convertTmdbToSeasoned(element, type));
|
||||
return [mappedResults, response.page, response.total_pages, response.total_results];
|
||||
return {results: mappedResults, page: response.page, total_pages: response.total_pages, total_results: response.total_results}
|
||||
})
|
||||
.catch((error) => { throw new Error(error); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps moviedb library to support Promises.
|
||||
* @param {String} method function name in the library
|
||||
* @param {Object} argument argument to function being called
|
||||
* @returns {Promise} succeeds if callback succeeds
|
||||
*/
|
||||
tmdb(method, argument) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callback = (error, reponse) => {
|
||||
|
||||
@@ -10,9 +10,9 @@ const requestRepository = new RequestRepository();
|
||||
*/
|
||||
function fetchRequestedController(req, res) {
|
||||
// const user = req.loggedInUser;
|
||||
const { status } = req.query;
|
||||
const { status, page } = req.query;
|
||||
|
||||
requestRepository.fetchRequested(status)
|
||||
requestRepository.fetchRequested(status, page)
|
||||
.then((requestedItems) => {
|
||||
res.send({ success: true, results: requestedItems, total_results: requestedItems.length });
|
||||
})
|
||||
|
||||
@@ -13,8 +13,8 @@ const tmdb = new TMDB(cache, configuration.get('tmdb', 'apiKey'));
|
||||
*/
|
||||
function listSearchController(req, res) {
|
||||
const listname = req.params.listname;
|
||||
const { type, id, page } = req.query;
|
||||
tmdb.listSearch(listname, type, id, page)
|
||||
const { type, page } = req.query;
|
||||
tmdb.listSearch(listname, type, page)
|
||||
.then((results) => {
|
||||
res.send(results);
|
||||
}).catch((error) => {
|
||||
|
||||
13
seasoned_api/test/fixtures/arrival-info-success-response.json
vendored
Normal file
13
seasoned_api/test/fixtures/arrival-info-success-response.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"background_path": "/yIZ1xendyqKvY3FGeeUYUd5X9Mm.jpg",
|
||||
"id": 329865,
|
||||
"popularity": 26.978601,
|
||||
"poster_path": "/hLudzvGfpi6JlwUnsNhXwKKg4j.jpg",
|
||||
"release_status": "Released",
|
||||
"score": 7.3,
|
||||
"summary": "Taking place after alien crafts land around the world, an expert linguist is recruited by the military to determine whether they come in peace or are a threat.",
|
||||
"tagline": "Why are they here?",
|
||||
"title": "Arrival",
|
||||
"type": "movie",
|
||||
"year": 2016
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@ const popularMoviesSuccess = require('test/fixtures/popular-movies-success-respo
|
||||
|
||||
describe('As a user I want to get popular movies', () => {
|
||||
before(() => resetDatabase());
|
||||
before(() => createCacheEntry('p:movie::1', popularMoviesSuccess));
|
||||
before(() => createCacheEntry('p:movie:1', popularMoviesSuccess));
|
||||
|
||||
it('should return 200 with the information', () =>
|
||||
request(app)
|
||||
|
||||
@@ -7,7 +7,7 @@ const popularShowsSuccess = require('test/fixtures/popular-show-success-response
|
||||
|
||||
describe('As a user I want to get popular shows', () => {
|
||||
before(() => resetDatabase());
|
||||
before(() => createCacheEntry('p:show::1', popularShowsSuccess));
|
||||
before(() => createCacheEntry('p:show:1', popularShowsSuccess));
|
||||
|
||||
it('should return 200 with the information', () =>
|
||||
request(app)
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
const resetDatabase = require('test/helpers/resetDatabase');
|
||||
const createCacheEntry = require('test/helpers/createCacheEntry');
|
||||
const app = require('src/webserver/app');
|
||||
const request = require('supertest-as-promised');
|
||||
const createUser = require('test/helpers/createUser');
|
||||
const createToken = require('test/helpers/createToken');
|
||||
const infoMovieSuccess = require('test/fixtures/arrival-info-success-response.json');
|
||||
|
||||
describe('As a user I want to request a movie', () => {
|
||||
before(() => {
|
||||
return resetDatabase()
|
||||
.then(() => createUser('test_user', 'test@gmail.com', 'password'));
|
||||
})
|
||||
before(() => createCacheEntry('i:movie:329865', infoMovieSuccess));
|
||||
|
||||
it('should return 200 when item is requested', () =>
|
||||
request(app)
|
||||
|
||||
@@ -6,7 +6,7 @@ const interstellarQuerySuccess = require('test/fixtures/interstellar-query-succe
|
||||
|
||||
describe('As an anonymous user I want to search for a movie', () => {
|
||||
before(() => resetDatabase());
|
||||
before(() => createCacheEntry('s:1:movie:interstellar', interstellarQuerySuccess));
|
||||
before(() => createCacheEntry('se:1:multi:interstellar', interstellarQuerySuccess));
|
||||
|
||||
it('should return 200 with the search results even if user is not logged in', () =>
|
||||
request(app)
|
||||
|
||||
63
seasoned_api/test/unit/config/testConfig.js
Normal file
63
seasoned_api/test/unit/config/testConfig.js
Normal file
@@ -0,0 +1,63 @@
|
||||
const assert = require('assert');
|
||||
const Config = require('src/config/configuration.js');
|
||||
|
||||
describe('Config', () => {
|
||||
before(() => {
|
||||
this.backedUpEnvironmentVariables = Object.assign({}, process.env);
|
||||
this.backedUpConfigFields = Object.assign({}, Config.getInstance().fields);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
process.env = this.backedUpEnvironmentVariables;
|
||||
Config.getInstance().fields = this.backedUpConfigFields;
|
||||
});
|
||||
|
||||
it('should retrieve section and option from config file', () => {
|
||||
Config.getInstance().fields = { 'webserver': { 'port': 1337 } };
|
||||
assert.equal(Config.getInstance().get('webserver', 'port'), 1337);
|
||||
});
|
||||
|
||||
it('should resolve to environment variables if option is filtered with env', () => {
|
||||
Config.getInstance().fields = { 'webserver': { 'port': 'env|SEASONED_WEBSERVER_PORT' } };
|
||||
process.env.SEASONED_WEBSERVER_PORT = '1338';
|
||||
assert.equal(Config.getInstance().get('webserver', 'port'), 1338);
|
||||
});
|
||||
|
||||
it('raises an exception if the environment variable does not exist', () => {
|
||||
Config.getInstance().fields = { 'webserver': { 'port': 'env|DOES_NOT_EXIST' } };
|
||||
process.env.SEASONED_WEBSERVER_PORT = '1338';
|
||||
assert.throws(() => Config.getInstance().get('webserver', 'port'), /empty/);
|
||||
});
|
||||
|
||||
it('raises an exception if the environment variable is empty', () => {
|
||||
Config.getInstance().fields = { 'webserver': { 'port': 'env|SEASONED_WEBSERVER_PORT' } };
|
||||
process.env.SEASONED_WEBSERVER_PORT = '';
|
||||
assert.throws(() => Config.getInstance().get('webserver', 'port'), /empty/);
|
||||
});
|
||||
|
||||
it('raises an exception if the section does not exist in the file', () => {
|
||||
Config.getInstance().fields = { 'webserver': { 'port': '1338' } };
|
||||
assert.throws(() => Config.getInstance().get('woops', 'port'), /does not exist/);
|
||||
});
|
||||
|
||||
it('raises an exception if the option does not exist in the file', () => {
|
||||
Config.getInstance().fields = { 'webserver': { 'port': '1338' } };
|
||||
assert.throws(() => Config.getInstance().get('webserver', 'woops'), /does not exist/);
|
||||
});
|
||||
|
||||
it('returns an array if field is an array', () => {
|
||||
Config.getInstance().fields = { 'bouncer': { 'whitelist': [1, 2, 3] } };
|
||||
assert.deepEqual(Config.getInstance().get('bouncer', 'whitelist'), [1, 2, 3]);
|
||||
});
|
||||
|
||||
it('decodes field as base64 if base64| is before the variable', () => {
|
||||
Config.getInstance().fields = { 'webserver': { 'port': 'base64|MTMzOA==' } };
|
||||
assert.equal(Config.getInstance().get('webserver', 'port'), 1338);
|
||||
});
|
||||
|
||||
it('decodes environment variable as base64 if BASE64= is before the variable', () => {
|
||||
Config.getInstance().fields = { 'webserver': { 'port': 'env|base64|SEASONED_WEBSERVER_PORT' } };
|
||||
process.env.SEASONED_WEBSERVER_PORT = 'MTMzOA==';
|
||||
assert.equal(Config.getInstance().get('webserver', 'port'), 1338);
|
||||
});
|
||||
});
|
||||
72
seasoned_api/test/unit/config/testField.js
Normal file
72
seasoned_api/test/unit/config/testField.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const assert = require('assert');
|
||||
const Field = require('src/config/field.js');
|
||||
|
||||
describe('Field', () => {
|
||||
it('should return an array if it is an array', () => {
|
||||
const field = new Field([1, 2, 3]);
|
||||
assert.deepEqual(field.value, [1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should return the plain value if it is an ordinary field', () => {
|
||||
const field = new Field('plain value');
|
||||
assert.equal(field.value, 'plain value');
|
||||
});
|
||||
|
||||
it('should return false if boolean false is field', () => {
|
||||
const field = new Field(false);
|
||||
assert.equal(field.value, false);
|
||||
});
|
||||
|
||||
it('should not include any invalid filters', () => {
|
||||
const field = new Field('invalid-filter|plain value');
|
||||
assert.equal(field.value, 'plain value');
|
||||
});
|
||||
|
||||
it('should return the decoded value if it is filtered through base64', () => {
|
||||
const field = new Field('base64|ZW5jb2RlZCB2YWx1ZQ==');
|
||||
assert.equal(field.value, 'encoded value');
|
||||
});
|
||||
|
||||
it('should not decode the value if it missing the filter', () => {
|
||||
const field = new Field('ZW5jb2RlZCB2YWx1ZQ==');
|
||||
assert.equal(field.value, 'ZW5jb2RlZCB2YWx1ZQ==');
|
||||
});
|
||||
|
||||
it('should retrieve the environment variable if env filter is used', () => {
|
||||
const environmentVariables = { REDIS_URL: 'redis://127.0.0.1:1234' };
|
||||
const field = new Field('env|REDIS_URL', environmentVariables);
|
||||
assert.equal(field.value, 'redis://127.0.0.1:1234');
|
||||
});
|
||||
|
||||
it('should return undefined if the environment variable does not exist', () => {
|
||||
const environmentVariables = { HTTP_PORT: 8080 };
|
||||
const field = new Field('env|REDIS_URL', environmentVariables);
|
||||
assert.equal(field.value, undefined);
|
||||
});
|
||||
|
||||
it('should return undefined if the environment variable is an empty string', () => {
|
||||
const environmentVariables = { REDIS_URL: '' };
|
||||
const field = new Field('env|REDIS_URL', environmentVariables);
|
||||
assert.deepEqual(field.value, undefined);
|
||||
});
|
||||
|
||||
describe('Multiple filters', () => {
|
||||
it('should decode the environment variable if base64 and env filter are used', () => {
|
||||
const environmentVariables = { REDIS_URL: 'cmVkaXM6Ly9kYWdibGFkZXQubm8vMTIzNA==' };
|
||||
const field = new Field('env|base64|REDIS_URL', environmentVariables);
|
||||
assert.equal(field.value, 'redis://dagbladet.no/1234');
|
||||
});
|
||||
|
||||
it('should disregard the order of filters when env and base64 are used', () => {
|
||||
const environmentVariables = { REDIS_URL: 'cmVkaXM6Ly9kYWdibGFkZXQubm8vMTIzNA==' };
|
||||
const field = new Field('base64|env|REDIS_URL', environmentVariables);
|
||||
assert.equal(field.value, 'redis://dagbladet.no/1234');
|
||||
});
|
||||
|
||||
it('should return undefined if both filters are used and env var does not exist', () => {
|
||||
const environmentVariables = { REDIS_URL: 'cmVkaXM6Ly9kYWdibGFkZXQubm8vMTIzNA==' };
|
||||
const field = new Field('base64|env|REDIS_LOL', environmentVariables);
|
||||
assert.equal(field.value, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
34
seasoned_api/test/unit/config/testFilters.js
Normal file
34
seasoned_api/test/unit/config/testFilters.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const assert = require('assert');
|
||||
const Filters = require('src/config/filters.js');
|
||||
|
||||
describe('Filters', () => {
|
||||
it('should extract base64 as filter if it is at start of string followed by pipe', () => {
|
||||
const filters = new Filters('base64|');
|
||||
assert.deepEqual(filters.filters, ['base64']);
|
||||
});
|
||||
|
||||
it('should extract base64 and env as filters if both are separated by pipe', () => {
|
||||
const filters = new Filters('base64|env|');
|
||||
assert.deepEqual(filters.filters, ['base64', 'env']);
|
||||
});
|
||||
|
||||
it('should not extract any filters if none are present', () => {
|
||||
const filters = new Filters('base64');
|
||||
assert.deepEqual(filters.filters, []);
|
||||
});
|
||||
|
||||
it('should strip env filter from the value', () => {
|
||||
const filters = new Filters('env|HELLO');
|
||||
assert.deepEqual(filters.removeFiltersFromValue(), 'HELLO');
|
||||
});
|
||||
|
||||
it('should strip env and base64 filter from the value', () => {
|
||||
const filters = new Filters('env|base64|HELLO');
|
||||
assert.deepEqual(filters.removeFiltersFromValue(), 'HELLO');
|
||||
});
|
||||
|
||||
it('should strip no filters from the value if there are no filters', () => {
|
||||
const filters = new Filters('HELLO');
|
||||
assert.deepEqual(filters.removeFiltersFromValue(), 'HELLO');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user