diff --git a/README.md b/README.md index 5d2fb74..dc14f22 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Public analytics as a Node.js microservice, no sysadmin experience required. [![Build Status](https://travis-ci.org/mxstbr/micro-analytics.svg?branch=master)](https://travis-ci.org/mxstbr/micro-analytics) -A tiny analytics server with ~150 lines of code, easy to run and hack around on. It does one thing, and it does it well: count the views of something and making the views publicly accessible via an API. +A tiny analytics server with ~100 lines of code, easy to run and hack around on. It does one thing, and it does it well: count the views of something and making the views publicly accessible via an API. (there is currently no frontend to display pretty graphs, feel free to build one yourself!) @@ -52,7 +52,7 @@ If you want to get all views for all ids, set the `all` query parameter to `true ### Database adapters -By default, `micro-analytics` uses `flat-file-db`, a fast in-process flat file database, which makes for easy setup and backups. _(change the path to the database with the `DB_PATH` env variable, e.g. `$ DB_PATH=storage/analytics.db micro-analytics`)_ +By default, `micro-analytics` uses `flat-file-db`, a fast in-process flat file database, which makes for easy setup and backups. This works fine for side-project usage, but for a production application with bajillions of visitors you might want to use a real database with a _database adapter_. Install the necessary npm package (e.g. `micro-analytics-adapter-xyz`) and then specify the `DB_ADAPTER` environment variable: `$ DB_ADAPTER=xyz micro-analytics` diff --git a/package.json b/package.json index 32a67e7..f0409c0 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dependencies": { "flat-file-db": "^1.0.0", "micro": "6.1.0", + "micro-analytics-adapter-flat-file-db": "^1.0.3", "promise": "^7.1.1", "shelljs": "^0.7.6" }, @@ -41,5 +42,10 @@ }, "execMap": { "js": "micro" + }, + "jest": { + "collectCoverageFrom": [ + "src/**/*.js" + ] } } diff --git a/src/db.js b/src/db.js index cf4d672..d42dd14 100644 --- a/src/db.js +++ b/src/db.js @@ -1,22 +1,20 @@ const promise = require('promise') let adapter +const adapterName = `micro-analytics-adapter-${process.env.DB_ADAPTER || 'flat-file-db'}` const repeatCharacter = (char, n) => `${Array(n + 1).join(char)}` -if (process.env.DB_ADAPTER) { - const adapterName = `micro-analytics-adapter-${process.env.DB_ADAPTER}` - try { - adapter = require(adapterName) - } catch (err) { - if (err.code === 'MODULE_NOT_FOUND') { - // Console.error a warning message, but normally exit the process to avoid printing ugly npm ERR lines and stack trace. - console.error(`\n${repeatCharacter(' ', 22)}⚠️ ERROR ⚠️\n${repeatCharacter('-', 55)}\nYou specified "${process.env.DB_ADAPTER}" as the DB_ADAPTER, but no package\ncalled "${adapterName}" was found.\n\nPlease make sure you spelled the name correctly and\nhave "npm install"ed the necessary adapter package!\n${repeatCharacter('-', 55)}\n`) - process.exit(0) - } +try { + adapter = require(adapterName) +} catch (err) { + if (err.code === 'MODULE_NOT_FOUND') { + // Console.error a warning message, but normally exit the process to avoid printing ugly npm ERR lines and stack trace. + console.error(`\n${repeatCharacter(' ', 22)}⚠️ ERROR ⚠️\n${repeatCharacter('-', 55)}\nYou specified "${process.env.DB_ADAPTER}" as the DB_ADAPTER, but no package\ncalled "${adapterName}" was found.\n\nPlease make sure you spelled the name correctly and\nhave "npm install"ed the necessary adapter package!\n${repeatCharacter('-', 55)}\n`) + process.exit(0) + } else { + throw err } -} else { - adapter = require('./flat-file-adapter') } module.exports = { diff --git a/src/flat-file-adapter.js b/src/flat-file-adapter.js deleted file mode 100644 index 98b0c59..0000000 --- a/src/flat-file-adapter.js +++ /dev/null @@ -1,35 +0,0 @@ -const flatfile = require('flat-file-db') -const promise = require('promise') - -const db = flatfile.sync(process.env.DB_NAME || 'views.db') - -module.exports = { - put: promise.denodeify(db.put.bind(db)), - - has: (key) => Promise.resolve(db.has(key)), - get: (key, options) => { - const value = db.get(key) || { views: [] } - - return { - views: value.views.filter(view => { - if (options && options.before && view.time > options.before) return false - if (options && options.after && view.time < options.after) return false - return true - }) - } - }, - keys: () => Promise.resolve(db.keys()), - - getAll: async function getAll(options) { - const data = {} - const keys = await module.exports.keys() - - keys - .filter(key => key.startsWith(options.pathname)) - .forEach((key) => { - data[key] = module.exports.get(key, { before: options.before, after: options.after }) - }) - - return data - } -} diff --git a/tests/utils.js b/tests/utils.js index 12a4ad3..a1cf0db 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -1,4 +1,5 @@ const micro = require('micro') +const noop = () => {} // Mock the database const DB = () => { @@ -16,6 +17,10 @@ const DB = () => { }, has: (key) => ({}.hasOwnProperty.call(data, key)), keys: () => Object.keys(data), + del: noop, + clear: noop, + close: noop, + on: noop, }), // Custom methods used in tests diff --git a/writing-adapters.md b/writing-adapters.md index cd684a5..544cdc7 100644 --- a/writing-adapters.md +++ b/writing-adapters.md @@ -2,6 +2,8 @@ `micro-analytics` database adapters are simple JavaScript modules which export an object with some methods. They _have_ to be called `micro-analytics-adapter-xyz`, where `xyz` is the name users will pass to the `DB_ADAPTER` environment variable when starting `micro-analytics`. +If you want to see an example adapter, check out the default [`flat-file-db` adapter](https://github.com/mxstbr/micro-analytics-adapter-flat-file-db)! + ## Overview The methods every adapter has to have are: