Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add database support for addons #415

Merged
merged 6 commits into from
Sep 1, 2016
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion dist/client/manager/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ var _storybookChannelPagebus = require('@kadira/storybook-channel-pagebus');

var _storybookChannelPagebus2 = _interopRequireDefault(_storybookChannelPagebus);

var _storybookDatabaseLocal = require('@kadira/storybook-database-local');

var _storybookDatabaseLocal2 = _interopRequireDefault(_storybookDatabaseLocal);

var _preview = require('./preview');

var _preview2 = _interopRequireDefault(_preview);
Expand All @@ -58,11 +62,16 @@ var ReactProvider = function (_Provider) {
function ReactProvider() {
(0, _classCallCheck3.default)(this, ReactProvider);

var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(ReactProvider).call(this));
var _this = (0, _possibleConstructorReturn3.default)(this, (ReactProvider.__proto__ || (0, _getPrototypeOf2.default)(ReactProvider)).call(this));

_this.dataId = _uuid2.default.v4();
_this.channel = (0, _storybookChannelPagebus2.default)({ key: _this.dataId });
_storybookAddons2.default.setChannel(_this.channel);
_this.database = _storybookAddons2.default.getDatabase();
if (!_this.database) {
_this.database = (0, _storybookDatabaseLocal2.default)({ url: location.origin + '/db' });
_storybookAddons2.default.setDatabase(_this.database);
}
return _this;
}

Expand Down
11 changes: 8 additions & 3 deletions dist/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ var _fs = require('fs');

var _fs2 = _interopRequireDefault(_fs);

var _middleware = require('./middleware');
var _storybook = require('./middleware/storybook');

var _middleware2 = _interopRequireDefault(_middleware);
var _storybook2 = _interopRequireDefault(_storybook);

var _datastore = require('./middleware/datastore');

var _datastore2 = _interopRequireDefault(_datastore);

var _package = require('../../package.json');

Expand Down Expand Up @@ -79,7 +83,8 @@ if (_commander2.default.staticDir) {
// Build the webpack configuration using the `baseConfig`
// custom `.babelrc` file and `webpack.config.js` files
var configDir = _commander2.default.configDir || './.storybook';
app.use((0, _middleware2.default)(configDir));
app.use((0, _storybook2.default)(configDir));
app.use('/db', (0, _datastore2.default)(configDir));

app.listen.apply(app, listenAddr.concat([function (error) {
if (error) {
Expand Down
128 changes: 128 additions & 0 deletions dist/server/middleware/datastore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Database = undefined;

var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');

var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);

var _keys = require('babel-runtime/core-js/object/keys');

var _keys2 = _interopRequireDefault(_keys);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

exports.default = function (configDir) {
var dbPath = _path2.default.resolve(configDir, 'datastore.json');
var db = new Database(dbPath);

var router = new _express.Router();
router.use(_bodyParser2.default.json());

router.post('/get', function (req, res) {
var _req$body = req.body;
var collection = _req$body.collection;
var query = _req$body.query;
var sort = _req$body.sort;
var limit = _req$body.limit;

var out = db.get(collection, query, sort, limit);
res.send({ data: out });
res.end();
});

router.post('/set', function (req, res) {
var _req$body2 = req.body;
var collection = _req$body2.collection;
var item = _req$body2.item;

var out = db.set(collection, item);
res.send({ data: out });
res.end();
});

return router;
};

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

var _express = require('express');

var _lowdb = require('lowdb');

var _lowdb2 = _interopRequireDefault(_lowdb);

var _fileAsync = require('lowdb/lib/file-async');

var _fileAsync2 = _interopRequireDefault(_fileAsync);

var _bodyParser = require('body-parser');

var _bodyParser2 = _interopRequireDefault(_bodyParser);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var Database = exports.Database = function () {
function Database(dbPath) {
(0, _classCallCheck3.default)(this, Database);

this.db = (0, _lowdb2.default)(dbPath, { storage: _fileAsync2.default });
}

(0, _createClass3.default)(Database, [{
key: 'get',
value: function get(collection, query, sort, limit) {
// if the database doesn't exist, add the document
// and return the inserted document as the result.
if (!this.db.has(collection).value()) {
return [];
}
// If the sort param is not given, use the DB interface
if (!sort) {
return this.db.get(collection).filter(query).take(limit).value();
}
// The db does not support sorting by multiple keys, get all data
// and sort it by each key (and its order) and then apply the limit
var allDocs = this.db.get(collection).filter(query).value();
var sorted = (0, _keys2.default)(sort).reduce(function (unsorted, key) {
return unsorted.sort(function (x, y) {
var order = sort[key];
return x[key] > y[key] ? order * 1 : order * -1;
});
}, allDocs);
// apply the limit after sorting
return sorted.slice(0, limit);
}
}, {
key: 'set',
value: function set(collection, item) {
// if the database doesn't exist, add the item
// and return the inserted item as the result.
if (!this.db.has(collection).value()) {
this.db.set(collection, [item]).value();
return item;
}
// if the item already exists in the database, update it
if (this.db.get(collection).find({ id: item.id }).value()) {
this.db.get(collection).find({ id: item.id }).assign(item).value();
return item;
}
// If the item is not available in the database, insert it
var coll = this.db.get(collection).value();
this.db.set(collection, [].concat((0, _toConsumableArray3.default)(coll), [item])).value();
return item;
}
}]);
return Database;
}();
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,22 @@ var _webpackHotMiddleware = require('webpack-hot-middleware');

var _webpackHotMiddleware2 = _interopRequireDefault(_webpackHotMiddleware);

var _webpack3 = require('./config/webpack.config');
var _webpack3 = require('../config/webpack.config');

var _webpack4 = _interopRequireDefault(_webpack3);

var _config = require('./config');
var _config = require('../config');

var _config2 = _interopRequireDefault(_config);

var _index = require('./index.html');
var _index = require('../index.html');

var _index2 = _interopRequireDefault(_index);

var _iframe = require('./iframe.html');
var _iframe = require('../iframe.html');

var _iframe2 = _interopRequireDefault(_iframe);

var _utils = require('./utils');
var _utils = require('../utils');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
"@kadira/react-split-pane": "^1.4.0",
"@kadira/storybook-addon-actions": "^1.0.2",
"@kadira/storybook-addon-links": "^1.0.0",
"@kadira/storybook-addons": "^1.3.1",
"@kadira/storybook-addons": "^1.5.0",
"@kadira/storybook-channel-pagebus": "^2.0.2",
"@kadira/storybook-database-local": "^1.0.3",
"@kadira/storybook-ui": "^3.3.2",
"autoprefixer": "^6.3.7",
"babel-core": "^6.11.4",
Expand All @@ -42,6 +43,7 @@
"babel-preset-es2016": "^6.11.3",
"babel-preset-react": "^6.11.1",
"babel-runtime": "^6.9.2",
"body-parser": "^1.15.2",
"case-sensitive-paths-webpack-plugin": "^1.1.2",
"commander": "^2.9.0",
"configstore": "^2.0.0",
Expand All @@ -53,6 +55,7 @@
"json-stringify-safe": "^5.0.1",
"json5": "^0.5.0",
"lodash.pick": "^4.2.0",
"lowdb": "^0.13.1",
"postcss-loader": "0.9.1",
"qs": "^6.1.0",
"react-modal": "^1.2.0",
Expand Down
6 changes: 6 additions & 0 deletions src/client/manager/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from 'react';
import { Provider } from '@kadira/storybook-ui';
import addons from '@kadira/storybook-addons';
import createChannel from '@kadira/storybook-channel-pagebus';
import createDatabase from '@kadira/storybook-database-local';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to move this to another repo? If there's a reason we can move the server code too. (I mean the middleware)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need this on RN storybook also. I'll move the middle ware tomorrow. Please hold this PR until then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move it to the same module. We can import the middleware like this:

import middleware from '@kadira/storybook-database-local/dist/middleware'

import Preview from './preview';

export default class ReactProvider extends Provider {
Expand All @@ -12,6 +13,11 @@ export default class ReactProvider extends Provider {
this.dataId = UUID.v4();
this.channel = createChannel({ key: this.dataId });
addons.setChannel(this.channel);
this.database = addons.getDatabase();
if (!this.database) {
this.database = createDatabase({ url: `${location.origin}/db` });
addons.setDatabase(this.database);
}
}

getPanels() {
Expand Down
4 changes: 3 additions & 1 deletion src/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import express from 'express';
import program from 'commander';
import path from 'path';
import fs from 'fs';
import storybook from './middleware';
import storybook from './middleware/storybook';
import datastore from './middleware/datastore';
import packageJson from '../../package.json';
import { parseList, getEnvConfig } from './utils';
import { track, dontTrack } from './track_usage';
Expand Down Expand Up @@ -65,6 +66,7 @@ if (program.staticDir) {
// custom `.babelrc` file and `webpack.config.js` files
const configDir = program.configDir || './.storybook';
app.use(storybook(configDir));
app.use('/db', datastore(configDir));

app.listen(...listenAddr, function (error) {
if (error) {
Expand Down
76 changes: 76 additions & 0 deletions src/server/middleware/datastore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import path from 'path';
import { Router } from 'express';
import lowdb from 'lowdb';
import fileAsyncStorage from 'lowdb/lib/file-async';
import bodyParser from 'body-parser';

export class Database {
constructor(dbPath) {
this.db = lowdb(dbPath, { storage: fileAsyncStorage });
}

get(collection, query, sort, limit) {
// if the database doesn't exist, add the document
// and return the inserted document as the result.
if (!this.db.has(collection).value()) {
return [];
}
// If the sort param is not given, use the DB interface
if (!sort) {
return this.db.get(collection).filter(query).take(limit).value();
}
// The db does not support sorting by multiple keys, get all data
// and sort it by each key (and its order) and then apply the limit
const allDocs = this.db.get(collection).filter(query).value();
const sorted = Object.keys(sort).reduce((unsorted, key) => {
return unsorted.sort(function (x, y) {
const order = sort[key];
return (x[key] > y[key]) ? order * 1 : order * -1;
});
}, allDocs);
// apply the limit after sorting
return sorted.slice(0, limit);
}

set(collection, item) {
// if the database doesn't exist, add the item
// and return the inserted item as the result.
if (!this.db.has(collection).value()) {
this.db.set(collection, [item]).value();
return item;
}
// if the item already exists in the database, update it
if (this.db.get(collection).find({ id: item.id }).value()) {
this.db.get(collection).find({ id: item.id }).assign(item).value();
return item;
}
// If the item is not available in the database, insert it
const coll = this.db.get(collection).value();
this.db.set(collection, [...coll, item]).value();
return item;
}
}

export default function (configDir) {
const dbPath = path.resolve(configDir, 'datastore.json');
const db = new Database(dbPath);

const router = new Router();
router.use(bodyParser.json());

router.post('/get', function (req, res) {
const { collection, query, sort, limit } = req.body;
const out = db.get(collection, query, sort, limit);
res.send({ data: out });
res.end();
});

router.post('/set', function (req, res) {
const { collection, item } = req.body;
const out = db.set(collection, item);
res.send({ data: out });
res.end();
});

return router;
}
10 changes: 5 additions & 5 deletions src/server/middleware.js → src/server/middleware/storybook.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Router } from 'express';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import baseConfig from './config/webpack.config';
import loadConfig from './config';
import getIndexHtml from './index.html';
import getIframeHtml from './iframe.html';
import { getHeadHtml } from './utils';
import baseConfig from '../config/webpack.config';
import loadConfig from '../config';
import getIndexHtml from '../index.html';
import getIframeHtml from '../iframe.html';
import { getHeadHtml } from '../utils';

export default function (configDir) {
// Build the webpack configuration using the `baseConfig`
Expand Down