From 37de888f03075ad7bb8f7db7a12ef15c4a93f3ad Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Mon, 9 Jan 2017 20:11:01 -0500 Subject: [PATCH 01/15] Creating a brand new admin page --- client/admin/admin.jsx | 52 +++++++++++--------- client/admin/admin.less | 54 ++++++++++++--------- client/admin/adminSearch/adminSearch.jsx | 19 ++++++++ client/admin/adminSearch/adminSearch.less | 3 ++ client/admin/brewLookup/brewLookup.jsx | 45 +++++++++++++++++ client/admin/brewLookup/brewLookup.less | 8 +++ client/admin/invalidBrew/invalidBrew.jsx | 19 ++++++++ client/admin/invalidBrew/invalidBrew.less | 3 ++ client/homebrew/pages/userPage/userPage.jsx | 1 + server/admin.routes.js | 1 + 10 files changed, 159 insertions(+), 46 deletions(-) create mode 100644 client/admin/adminSearch/adminSearch.jsx create mode 100644 client/admin/adminSearch/adminSearch.less create mode 100644 client/admin/brewLookup/brewLookup.jsx create mode 100644 client/admin/brewLookup/brewLookup.less create mode 100644 client/admin/invalidBrew/invalidBrew.jsx create mode 100644 client/admin/invalidBrew/invalidBrew.less diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index 4fd8d5505..174cfafff 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -1,38 +1,42 @@ -var React = require('react'); -var _ = require('lodash'); -var cx = require('classnames'); +const React = require('react'); +const _ = require('lodash'); -var HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx'); +const Nav = require('naturalcrit/nav/nav.jsx'); -var Admin = React.createClass({ +//const HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx'); + + +const BrewLookup = require('./brewLookup/brewLookup.jsx'); + + +const Admin = React.createClass({ getDefaultProps: function() { return { - url : "", - admin_key : "", - homebrews : [], + admin_key : '', }; }, + renderNavbar : function(){ + return + + + Homebrewery Admin + + + + }, + render : function(){ - var self = this; - return( -
+ return
+ {this.renderNavbar()} +
+ -
-
- - naturalcrit admin -
-
- -
- - -
+ Test -
- ); + +
} }); diff --git a/client/admin/admin.less b/client/admin/admin.less index c13063a01..e742b26bc 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -1,19 +1,11 @@ -@import 'naturalcrit/styles/reset.less'; -@import 'naturalcrit/styles/elements.less'; -@import 'naturalcrit/styles/animations.less'; -@import 'naturalcrit/styles/colors.less'; -@import 'naturalcrit/styles/tooltip.less'; +@import 'naturalcrit/styles/core.less'; -@import 'font-awesome/css/font-awesome.css'; - -html,body, #reactContainer, .naturalCrit{ +html,body, #reactRoot{ min-height : 100%; } -@sidebarWidth : 250px; - body{ - background-color : #eee; + background-color : #ddd; font-family : 'Open Sans', sans-serif; color : #4b5055; font-weight : 100; @@ -23,17 +15,35 @@ body{ height : 100%; } -.admin{ - - header{ - background-color : @red; - font-size: 2em; - padding : 20px 0px; - color : white; - margin-bottom: 30px; - i{ - margin-right: 30px; +.admin { + nav { + background-color: @red; + .navItem{ + background-color: @red; + } + .homebreweryLogo{ + font-family : CodeBold; + font-size : 12px; + color : white; + div{ + margin-top : 2px; + margin-bottom : -2px; + } } } -} \ No newline at end of file + h1{ + font-size: 2em; + font-weight: 800; + border-bottom: 1px solid #ddd; + margin-bottom: 10px; + } + + + main.content{ + background-color: white; + padding: 50px 20px; + width : 1000px; + margin: 0 auto; + } +} diff --git a/client/admin/adminSearch/adminSearch.jsx b/client/admin/adminSearch/adminSearch.jsx new file mode 100644 index 000000000..1f95153be --- /dev/null +++ b/client/admin/adminSearch/adminSearch.jsx @@ -0,0 +1,19 @@ + +const React = require('react'); +const _ = require('lodash'); +const cx = require('classnames'); + +const AdminSearch = React.createClass({ + getDefaultProps: function() { + return { + + }; + }, + render: function(){ + return
+ AdminSearch Component Ready. +
+ } +}); + +module.exports = AdminSearch; diff --git a/client/admin/adminSearch/adminSearch.less b/client/admin/adminSearch/adminSearch.less new file mode 100644 index 000000000..dc1de6e64 --- /dev/null +++ b/client/admin/adminSearch/adminSearch.less @@ -0,0 +1,3 @@ +.adminSearch{ + +} \ No newline at end of file diff --git a/client/admin/brewLookup/brewLookup.jsx b/client/admin/brewLookup/brewLookup.jsx new file mode 100644 index 000000000..e6a0ff290 --- /dev/null +++ b/client/admin/brewLookup/brewLookup.jsx @@ -0,0 +1,45 @@ +const React = require('react'); +const _ = require('lodash'); +const cx = require('classnames'); + +const request = require('superagent'); + +const BrewLookup = React.createClass({ + getDefaultProps: function() { + return { + adminKey : '', + }; + }, + getInitialState: function() { + return { + query:'', + resultBrew : null + }; + }, + + handleChange : function(e){ + this.setState({ + query : e.target.value + }) + }, + lookup : function(){ + + + }, + + renderFoundBrew : function(){ + if(!this.state.resultBrew) return null; + }, + + render: function(){ + return
+

Brew Lookup

+ + + + {this.renderFoundBrew()} +
+ } +}); + +module.exports = BrewLookup; diff --git a/client/admin/brewLookup/brewLookup.less b/client/admin/brewLookup/brewLookup.less new file mode 100644 index 000000000..31b0daea6 --- /dev/null +++ b/client/admin/brewLookup/brewLookup.less @@ -0,0 +1,8 @@ + +.brewLookup{ + height : 200px; + input{ + height : 33px; + padding : 0px 10px; + } +} \ No newline at end of file diff --git a/client/admin/invalidBrew/invalidBrew.jsx b/client/admin/invalidBrew/invalidBrew.jsx new file mode 100644 index 000000000..a70c443ec --- /dev/null +++ b/client/admin/invalidBrew/invalidBrew.jsx @@ -0,0 +1,19 @@ + +const React = require('react'); +const _ = require('lodash'); +const cx = require('classnames'); + +const InvalidBrew = React.createClass({ + getDefaultProps: function() { + return { + + }; + }, + render: function(){ + return
+ InvalidBrew Component Ready. +
+ } +}); + +module.exports = InvalidBrew; diff --git a/client/admin/invalidBrew/invalidBrew.less b/client/admin/invalidBrew/invalidBrew.less new file mode 100644 index 000000000..f9370957f --- /dev/null +++ b/client/admin/invalidBrew/invalidBrew.less @@ -0,0 +1,3 @@ +.invalidBrew{ + +} \ No newline at end of file diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index b6bdbeffc..6dd386d5a 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -52,6 +52,7 @@ const UserPage = React.createClass({ render : function(){ const brews = this.getSortedBrews(); + console.log('user brews', brews); return
diff --git a/server/admin.routes.js b/server/admin.routes.js index a526e5e5e..6310d5eb1 100644 --- a/server/admin.routes.js +++ b/server/admin.routes.js @@ -8,6 +8,7 @@ const mw = require('./middleware.js'); const BrewData = require('./brew.data.js'); router.get('/admin', mw.adminLogin, (req, res, next) => { + console.log('yo'); return vitreumRender('admin', templateFn, { url : req.originalUrl, admin_key : config.get('admin:key') From cd2eb5fdce3c28554368589e03fae111dcd76dbc Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Mon, 9 Jan 2017 20:51:37 -0500 Subject: [PATCH 02/15] Adding in lookup route --- client/admin/admin.jsx | 11 +++++---- client/admin/admin.less | 50 ++++++++++++++++++++++------------------- server/admin.routes.js | 23 ++++++++++++++++++- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index 174cfafff..5a725b76b 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -3,11 +3,9 @@ const _ = require('lodash'); const Nav = require('naturalcrit/nav/nav.jsx'); -//const HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx'); - - const BrewLookup = require('./brewLookup/brewLookup.jsx'); - +const AdminSearch = require('./adminSearch/adminSearch.jsx'); +const InvalidBrew = require('./invalidBrew/invalidBrew.jsx'); const Admin = React.createClass({ getDefaultProps: function() { @@ -31,10 +29,11 @@ const Admin = React.createClass({ {this.renderNavbar()}
+ - Test - +
Danger Zone
+
} diff --git a/client/admin/admin.less b/client/admin/admin.less index e742b26bc..ef7d38775 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -1,25 +1,23 @@ -@import 'naturalcrit/styles/core.less'; +@import 'naturalcrit/styles/core.less'; html,body, #reactRoot{ min-height : 100%; } - body{ - background-color : #ddd; - font-family : 'Open Sans', sans-serif; - color : #4b5055; - font-weight : 100; - text-rendering : optimizeLegibility; + height : 100%; margin : 0; padding : 0; - height : 100%; + background-color : #ddd; + font-family : 'Open Sans', sans-serif; + font-weight : 100; + color : #4b5055; + text-rendering : optimizeLegibility; } - .admin { nav { - background-color: @red; + background-color : @red; .navItem{ - background-color: @red; + background-color : @red; } .homebreweryLogo{ font-family : CodeBold; @@ -31,19 +29,25 @@ body{ } } } - h1{ - font-size: 2em; - font-weight: 800; - border-bottom: 1px solid #ddd; - margin-bottom: 10px; + margin-bottom : 10px; + font-size : 2em; + font-weight : 800; + border-bottom : 1px solid #ddd; } - - main.content{ - background-color: white; - padding: 50px 20px; - width : 1000px; - margin: 0 auto; + width : 1000px; + margin : 0 auto; + padding : 50px 20px; + background-color : white; + .dangerZone{ + margin : 30px 0px; + padding : 10px 20px; + background : repeating-linear-gradient(45deg, @yellow, @yellow 10px, darken(#333, 10%) 10px, darken(#333, 10%) 20px); + font-size : 1em; + font-weight : 800; + color : white; + text-transform : uppercase; + } } -} +} \ No newline at end of file diff --git a/server/admin.routes.js b/server/admin.routes.js index 6310d5eb1..7445cb376 100644 --- a/server/admin.routes.js +++ b/server/admin.routes.js @@ -8,7 +8,6 @@ const mw = require('./middleware.js'); const BrewData = require('./brew.data.js'); router.get('/admin', mw.adminLogin, (req, res, next) => { - console.log('yo'); return vitreumRender('admin', templateFn, { url : req.originalUrl, admin_key : config.get('admin:key') @@ -30,4 +29,26 @@ router.delete('/admin/invalid', mw.adminOnly, (req, res, next)=>{ .catch(next); }); +router.get('/admin/lookup/:search', (req, res) => { + //search for mathcing edit id + //search for matching share id + // search for partial match + + BrewData.get({editId : req.params.search}) + .then((brew) => { + + }) + + BrewData.get({ $or:[ + {editId : { "$regex": req.params.search, "$options": "i" }}, + {shareId : { "$regex": req.params.search, "$options": "i" }}, + ]}) + .then((brews) => { + console.log(brews); + return res.json(brews); + }) + + +}) + module.exports = router; \ No newline at end of file From e77532acef203ba9e7efe428294c00cf4950c6b5 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Tue, 10 Jan 2017 14:44:48 -0500 Subject: [PATCH 03/15] Added tests for admin routes, lookup is working --- server/admin.routes.js | 13 ++---- test/admin.test.js | 89 +++++++++++++++++++++++++++++++++++++++++ test/middleware.test.js | 7 ++-- 3 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 test/admin.test.js diff --git a/server/admin.routes.js b/server/admin.routes.js index 7445cb376..be83166a8 100644 --- a/server/admin.routes.js +++ b/server/admin.routes.js @@ -29,26 +29,19 @@ router.delete('/admin/invalid', mw.adminOnly, (req, res, next)=>{ .catch(next); }); -router.get('/admin/lookup/:search', (req, res) => { +router.get('/admin/lookup/:search', mw.adminOnly, (req, res, next) => { //search for mathcing edit id //search for matching share id // search for partial match - BrewData.get({editId : req.params.search}) - .then((brew) => { - - }) - BrewData.get({ $or:[ {editId : { "$regex": req.params.search, "$options": "i" }}, {shareId : { "$regex": req.params.search, "$options": "i" }}, ]}) .then((brews) => { - console.log(brews); return res.json(brews); }) - - -}) + .catch(next); +}); module.exports = router; \ No newline at end of file diff --git a/test/admin.test.js b/test/admin.test.js new file mode 100644 index 000000000..6d2cccece --- /dev/null +++ b/test/admin.test.js @@ -0,0 +1,89 @@ +const testing = require('./test.init.js'); +const request = require('supertest-as-promised'); + +const config = require('nconf'); + +const app = require('app.js'); +const DB = require('db.js'); +const BrewData = require('brew.data.js'); +const Error = require('error.js'); + + +let brewA = { + title : 'good title', + text : 'original text', + authors : ['your_dm'] +}; + +describe('Admin API', ()=>{ + before('Connect DB', DB.connect); + + + describe('Brew Lookup', ()=>{ + before('Clear DB', BrewData.removeAll); + before('Create brew', ()=>{ + return BrewData.create(brewA) + .then((brew)=>{ brewA = brew; }); + }); + + + it('throws an error if not admin', ()=>{ + return request(app) + .get(`/admin/lookup/${brewA.editId}`) + .expect(401); + }); + it('looks up a brew based on the share id', () => { + return request(app) + .get(`/admin/lookup/${brewA.shareId}`) + .query({ admin_key : config.get('admin:key') }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').equal(brewA.editId); + brew.should.have.property('shareId').equal(brewA.shareId); + brew.should.have.property('text').equal(brewA.text); + }); + }); + it('looks up a brew based on the edit id', () => { + return request(app) + .get(`/admin/lookup/${brewA.editId}`) + .query({ admin_key : config.get('admin:key') }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').equal(brewA.editId); + brew.should.have.property('shareId').equal(brewA.shareId); + brew.should.have.property('text').equal(brewA.text); + }); + }); + it('looks up a brew based on a partial id', () => { + const query = brewA.editId.substring(0, brewA.editId.length -2); + return request(app) + .get(`/admin/lookup/${query}`) + .query({ admin_key : config.get('admin:key') }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').equal(brewA.editId); + brew.should.have.property('shareId').equal(brewA.shareId); + brew.should.have.property('text').equal(brewA.text); + }); + }); + it('throws an error if it can not find a brew', ()=>{ + return request(app) + .get(`/admin/lookup/BADID`) + .query({ admin_key : config.get('admin:key') }) + .expect(404); + }); + }); + + describe('Invalid Brew', ()=>{ + before('Clear DB', BrewData.removeAll); + before('Create brew', ()=>{ + return BrewData.create(brewA) + .then((brew)=>{ brewA = brew; }); + }); + }); + + +}); \ No newline at end of file diff --git a/test/middleware.test.js b/test/middleware.test.js index c4fd820eb..44c822d69 100644 --- a/test/middleware.test.js +++ b/test/middleware.test.js @@ -105,8 +105,8 @@ describe('Middleware', () => { it('should detect when you use the admin key', () => { app.use(mw.admin); app.use(requestHandler) - return request(app).get(`/?admin_key=${config.get('admin:key')}`) - .send() + return request(app).get('/') + .query({ admin_key : config.get('admin:key') }) .expect(200) .then((res) => { const req = res.body; @@ -118,7 +118,8 @@ describe('Middleware', () => { app.use(mw.adminOnly); app.get(requestHandler); app.use(Error.expressHandler); - return request(app).get(`/?admin_key=BADKEY`) + return request(app).get('/') + .query({ admin_key : 'BADUSER' }) .send() .expect(401); }); From 0878439750540db54de08f8fde7ce4b88f429e7c Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Tue, 10 Jan 2017 15:38:13 -0500 Subject: [PATCH 04/15] Fixed issue with arrays not being saved --- client/admin/brewLookup/brewLookup.jsx | 35 +++++++++++++++++++++++--- client/homebrew/homebrew.jsx | 2 ++ server/brew.api.js | 3 +++ server/brew.data.js | 3 +++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/client/admin/brewLookup/brewLookup.jsx b/client/admin/brewLookup/brewLookup.jsx index e6a0ff290..5910426e4 100644 --- a/client/admin/brewLookup/brewLookup.jsx +++ b/client/admin/brewLookup/brewLookup.jsx @@ -2,6 +2,8 @@ const React = require('react'); const _ = require('lodash'); const cx = require('classnames'); +const Moment = require('moment'); + const request = require('superagent'); const BrewLookup = React.createClass({ @@ -13,7 +15,8 @@ const BrewLookup = React.createClass({ getInitialState: function() { return { query:'', - resultBrew : null + resultBrew : null, + searching : false }; }, @@ -23,12 +26,38 @@ const BrewLookup = React.createClass({ }) }, lookup : function(){ + this.setState({ searching : true }); - + request.get(`/admin/lookup/${this.state.query}`) + .query({ admin_key : this.props.adminKey }) + .end((err, res) => { + this.setState({ + searching : false, + resultBrew : (err ? null : res.body) + }); + }) }, renderFoundBrew : function(){ - if(!this.state.resultBrew) return null; + if(this.state.searching) return
; + if(!this.state.resultBrew) return
No brew found.
; + + console.log(this.state.resultBrew); + + const brew = this.state.resultBrew; + return
+
{brew.title}
+
{brew.authors.join(', ')}
+ + +
{Moment(brew.updatedAt).fromNow()}
+
{brew.views}
+
+
+ +
+
+
}, render: function(){ diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 97f66bb0a..411da9953 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -37,6 +37,8 @@ const Homebrew = React.createClass({ loginPath : this.props.loginPath }); + console.log(this.props.brew); + Router = CreateRouter({ '/edit/:id' : , diff --git a/server/brew.api.js b/server/brew.api.js index d7e30bf6b..be9365b45 100644 --- a/server/brew.api.js +++ b/server/brew.api.js @@ -30,12 +30,15 @@ router.post('/api/brew', (req, res, next)=>{ //Update router.put('/api/brew/:editId', mw.loadBrew, (req, res, next)=>{ + console.log(req.account); const brew = req.body || {}; if(req.account){ brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); } + console.log(brew); BrewData.update(req.params.editId, brew) .then((brew) => { + console.log(brew.toJSON()); return res.json(brew.toJSON()); }) .catch(next); diff --git a/server/brew.data.js b/server/brew.data.js index a6408853e..528fcc290 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -65,6 +65,9 @@ const BrewData = { delete newBrew.shareId; delete newBrew.editId; brew = _.merge(brew, newBrew, { updatedAt : Date.now() }); + + brew.markModified('authors'); + brew.markModified('systems'); return brew.save(); }); }, From 728277f86131255e13bbca4bc1e815205456ced7 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Tue, 10 Jan 2017 17:20:33 -0500 Subject: [PATCH 05/15] Removing invalid brews is working --- client/admin/brewLookup/brewLookup.jsx | 5 +-- client/admin/brewLookup/brewLookup.less | 1 + client/admin/brewTable/brewTable.jsx | 54 ++++++++++++++++++++++++ client/admin/brewTable/brewTable.less | 44 +++++++++++++++++++ client/admin/invalidBrew/invalidBrew.jsx | 39 ++++++++++++++++- server/admin.routes.js | 26 +++++++++--- server/brew.api.js | 3 -- server/brew.data.js | 15 ------- 8 files changed, 159 insertions(+), 28 deletions(-) create mode 100644 client/admin/brewTable/brewTable.jsx create mode 100644 client/admin/brewTable/brewTable.less diff --git a/client/admin/brewLookup/brewLookup.jsx b/client/admin/brewLookup/brewLookup.jsx index 5910426e4..b7aa03393 100644 --- a/client/admin/brewLookup/brewLookup.jsx +++ b/client/admin/brewLookup/brewLookup.jsx @@ -2,9 +2,8 @@ const React = require('react'); const _ = require('lodash'); const cx = require('classnames'); -const Moment = require('moment'); - const request = require('superagent'); +const BrewTable = require('../brewTable/brewTable.jsx'); const BrewLookup = React.createClass({ getDefaultProps: function() { @@ -42,7 +41,7 @@ const BrewLookup = React.createClass({ if(this.state.searching) return
; if(!this.state.resultBrew) return
No brew found.
; - console.log(this.state.resultBrew); + return const brew = this.state.resultBrew; return
diff --git a/client/admin/brewLookup/brewLookup.less b/client/admin/brewLookup/brewLookup.less index 31b0daea6..dfec8618f 100644 --- a/client/admin/brewLookup/brewLookup.less +++ b/client/admin/brewLookup/brewLookup.less @@ -4,5 +4,6 @@ input{ height : 33px; padding : 0px 10px; + margin-bottom: 20px; } } \ No newline at end of file diff --git a/client/admin/brewTable/brewTable.jsx b/client/admin/brewTable/brewTable.jsx new file mode 100644 index 000000000..dfa450041 --- /dev/null +++ b/client/admin/brewTable/brewTable.jsx @@ -0,0 +1,54 @@ +const React = require('react'); +const _ = require('lodash'); +const cx = require('classnames'); + +const Moment = require('moment'); + +//TODO: Add in delete + +const BrewTable = React.createClass({ + getDefaultProps: function() { + return { + brews : [] + }; + }, + renderRows : function(){ + return _.map(this.props.brews, (brew) => { + let authors = 'None.'; + if(brew.authors && brew.authors.length) authors = brew.authors.join(', '); + + return + {brew.title} + {authors} + {brew.editId} + {brew.shareId} + {Moment(brew.updatedAt).fromNow()} + {brew.views} + + + + + + }); + }, + render: function(){ + return + + + + + + + + + + + + + {this.renderRows()} + +
TitleAuthorsEdit LinkShare LinkLast UpdatedViewsRemove
+ } +}); + +module.exports = BrewTable; diff --git a/client/admin/brewTable/brewTable.less b/client/admin/brewTable/brewTable.less new file mode 100644 index 000000000..927242b15 --- /dev/null +++ b/client/admin/brewTable/brewTable.less @@ -0,0 +1,44 @@ + +table.brewTable{ + th{ + padding : 10px; + background-color : fade(@blue, 20%); + font-weight : 800; + } + tr:nth-child(even){ + background-color : fade(@green, 10%); + } + tr.isEmpty{ + background-color : fade(@red, 30%); + } + td{ + min-width : 100px; + padding : 10px; + text-align : center; + + /* + &.preview{ + position : relative; + &:hover{ + .content{ + display : block; + } + } + .content{ + position : absolute; + display : none; + top : 100%; + left : 0px; + z-index : 1000; + max-height : 500px; + width : 300px; + padding : 30px; + background-color : white; + font-family : monospace; + text-align : left; + pointer-events : none; + } + } + */ + } +} \ No newline at end of file diff --git a/client/admin/invalidBrew/invalidBrew.jsx b/client/admin/invalidBrew/invalidBrew.jsx index a70c443ec..bafeb2f2f 100644 --- a/client/admin/invalidBrew/invalidBrew.jsx +++ b/client/admin/invalidBrew/invalidBrew.jsx @@ -1,17 +1,52 @@ - const React = require('react'); const _ = require('lodash'); const cx = require('classnames'); +const request = require('superagent'); +const BrewTable = require('../brewTable/brewTable.jsx'); + + const InvalidBrew = React.createClass({ getDefaultProps: function() { return { }; }, + getInitialState: function() { + return { + brews: [] + }; + }, + getInvalid : function(){ + request.get(`/admin/invalid`) + .query({ admin_key : this.props.adminKey }) + .end((err, res) => { + this.setState({ + brews : res.body + }); + }) + }, + removeInvalid : function(){ + if(!this.state.brews.length) return; + if(!confirm(`Are you sure you want to remove ${this.state.brews.length} brews`)) return; + if(!confirm('Sure you are sure?')) return; + + request.delete(`/admin/invalid`) + .query({ admin_key : this.props.adminKey }) + .end((err, res) => { + console.log(err, res.body); + alert('Invalid brews removed!'); + this.getInvalid(); + }) + }, render: function(){ return
- InvalidBrew Component Ready. +

Remove Invalid Brews

+ This will removes all brews older than 3 days and shorter than a tweet. + + + +
} }); diff --git a/server/admin.routes.js b/server/admin.routes.js index be83166a8..956b73028 100644 --- a/server/admin.routes.js +++ b/server/admin.routes.js @@ -3,10 +3,21 @@ const router = require('express').Router(); const vitreumRender = require('vitreum/steps/render'); const templateFn = require('../client/template.js'); const config = require('nconf'); +const Moment = require('moment'); const mw = require('./middleware.js'); const BrewData = require('./brew.data.js'); + +const getInvalidBrewQuery = ()=>{ + return BrewData.model.find({ + '$where' : "this.text.length < 140", + createdAt: { + $lt: Moment().subtract(3, 'days').toDate() + } + }).select({ text : false }); +} + router.get('/admin', mw.adminLogin, (req, res, next) => { return vitreumRender('admin', templateFn, { url : req.originalUrl, @@ -19,12 +30,17 @@ router.get('/admin', mw.adminLogin, (req, res, next) => { }); //Removes all empty brews that are older than 3 days and that are shorter than a tweet +router.get('/admin/invalid', mw.adminOnly, (req, res, next)=>{ + getInvalidBrewQuery().exec() + .then((brews) => { + return res.json(brews); + }) + .catch(next); +}); router.delete('/admin/invalid', mw.adminOnly, (req, res, next)=>{ - BrewData.removeInvalid(!!req.query.do_it) - .then((removedCount) => { - return res.join({ - count : removedCount - }); + getInvalidBrewQuery().remove() + .then(()=>{ + return res.status(200).send(); }) .catch(next); }); diff --git a/server/brew.api.js b/server/brew.api.js index be9365b45..d7e30bf6b 100644 --- a/server/brew.api.js +++ b/server/brew.api.js @@ -30,15 +30,12 @@ router.post('/api/brew', (req, res, next)=>{ //Update router.put('/api/brew/:editId', mw.loadBrew, (req, res, next)=>{ - console.log(req.account); const brew = req.body || {}; if(req.account){ brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); } - console.log(brew); BrewData.update(req.params.editId, brew) .then((brew) => { - console.log(brew.toJSON()); return res.json(brew.toJSON()); }) .catch(next); diff --git a/server/brew.data.js b/server/brew.data.js index 528fcc290..367d33b07 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -93,21 +93,6 @@ const BrewData = { return BrewData.get({ editId }); }, - //Removes all empty brews that are older than 3 days and that are shorter than a tweet - removeInvalid : (force = false) => { - const invalidBrewQuery = Brew.find({ - '$where' : "this.text.length < 140", - createdAt: { - $lt: Moment().subtract(3, 'days').toDate() - } - }); - if(force) return invalidBrewQuery.remove().exec(); - return invalidBrewQuery.exec() - .then((objs) => { - return objs.length; - }); - }, - search : (query, req={}) => { //defaults with page and count //returns a non-text version of brews From dfcb04fd090cb712c31c5b888fd123126124a131 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Wed, 11 Jan 2017 13:41:07 -0500 Subject: [PATCH 06/15] Setting up search tests --- client/admin/adminSearch/adminSearch.jsx | 2 +- test/search.test.js | 69 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/client/admin/adminSearch/adminSearch.jsx b/client/admin/adminSearch/adminSearch.jsx index 1f95153be..15d09724c 100644 --- a/client/admin/adminSearch/adminSearch.jsx +++ b/client/admin/adminSearch/adminSearch.jsx @@ -11,7 +11,7 @@ const AdminSearch = React.createClass({ }, render: function(){ return
- AdminSearch Component Ready. +

Admin Search

} }); diff --git a/test/search.test.js b/test/search.test.js index e69de29bb..c4ab4e0ba 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -0,0 +1,69 @@ +const testing = require('./test.init.js'); +const _ = require('lodash'); + +const DB = require('db.js'); +const BrewData = require('brew.data.js'); +const Error = require('error.js'); + +const ids = (brewIds) => { + return _.map(brewIds, (brewId) => { + return { editId : brews[brewId].editId }; + }); +} + +const brews = { + BrewA : { + title : 'BrewA', + description : 'fancy', + authors : [], + systems : [] + }, + BrewB : { + title : 'BrewB', + description : 'fancy', + authors : [], + systems : [] + }, + BrewC : { + title : 'BrewC', + description : 'test', + authors : [], + systems : [] + }, + BrewD : { + title : 'BrewD', + description : 'test', + authors : [], + systems : [] + } +}; + +describe('Brew Search', () => { + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return Promise.all(_.map(brews, (brewData, id) => { + return BrewData.create(brewData) + .then((brew)=>{ brews[id] = brew; }); + })); + }); + + it('should find brews based on title and/or description', () => { + + return new Promise((resolve, reject) => { + return reject() + }) + .catch(()=>{ console.log('here1');}) + .catch(()=>{ console.log('here2');}) + + return BrewData.create({ + text : 'Brew Text' + }).then((brew) => { + +}); + //result.count.should.be.equal(2) + //result.brews.should.deep.include.members(ids(['BrewA', 'BrewB']); + }); + + +}); \ No newline at end of file From a405c7cfb21d303125f5318f830c36b87cf8d42e Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Wed, 11 Jan 2017 19:56:57 -0800 Subject: [PATCH 07/15] Stubbing out tests for searching --- server/brew.data.js | 10 ++++++- test/search.test.js | 66 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/server/brew.data.js b/server/brew.data.js index 367d33b07..217c8cfa3 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -93,7 +93,15 @@ const BrewData = { return BrewData.get({ editId }); }, - search : (query, req={}) => { + search : (query, pagniation, sorting, permissions) => { + + + //search with query, add in `published = false` + // filter out editId and text + + //if admin, removed published=false, remove filtering editId + + //defaults with page and count //returns a non-text version of brews //assume sanatized ? diff --git a/test/search.test.js b/test/search.test.js index c4ab4e0ba..091f2df52 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -16,25 +16,33 @@ const brews = { title : 'BrewA', description : 'fancy', authors : [], - systems : [] + systems : [], + views : 12, + published : true }, BrewB : { title : 'BrewB', description : 'fancy', authors : [], - systems : [] + systems : [], + views : 7, + published : true }, BrewC : { title : 'BrewC', description : 'test', authors : [], - systems : [] + systems : [], + views : 0, + published : false }, BrewD : { title : 'BrewD', description : 'test', authors : [], - systems : [] + systems : [], + views : 1, + published : true } }; @@ -48,21 +56,47 @@ describe('Brew Search', () => { })); }); - it('should find brews based on title and/or description', () => { - return new Promise((resolve, reject) => { - return reject() - }) - .catch(()=>{ console.log('here1');}) - .catch(()=>{ console.log('here2');}) + describe('Searching', ()=>{ + it('should be able to search for all brews', ()=>{ - return BrewData.create({ - text : 'Brew Text' - }).then((brew) => { -}); - //result.count.should.be.equal(2) - //result.brews.should.deep.include.members(ids(['BrewA', 'BrewB']); + }); + it('should find brews based on title and/or description', () => { + + //result.count.should.be.equal(2) + //result.brews.should.deep.include.members(ids(['BrewA', 'BrewB']); + }); + + it('should return the total number of brews and page info for query', ()=>{ + + }); + }) + + describe('Permissions', () => { + it('should only fetch published brews', () => { + + }); + it('fetched brews should not have text or editId', () => { + + }); + it('if admin, fetches also non-published brews, with editid', () => { + + }); + it('if author, fetches also non-published brews, with editid', ()=>{ + + }); + }); + + describe('Pagniation', () => { + it('should return the exact number of brews based on limit', () => { + + }); + + }); + + desscribe('Sorting', ()=>{ + }); From efee8ff05cb7112b956f0ce74d3072215a1db015 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Mon, 23 Jan 2017 00:35:30 -0500 Subject: [PATCH 08/15] Basic search is working --- package.json | 1 + server/brew.data.js | 55 +++++++++++++++++++++++++++++------ test/search.test.js | 71 +++++++++++++++++++++++++++++++++++---------- test/test.init.js | 6 +++- 4 files changed, 107 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index e553ba58f..951b9a67e 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "app-module-path": "^2.1.0", "chai": "^3.5.0", "chai-as-promised": "^6.0.0", + "chai-subset": "^1.4.0", "mocha": "^3.2.0", "supertest": "^2.0.1", "supertest-as-promised": "^4.0.2" diff --git a/server/brew.data.js b/server/brew.data.js index 217c8cfa3..d8281e118 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -21,7 +21,8 @@ const BrewSchema = mongoose.Schema({ createdAt : { type: Date, default: Date.now }, updatedAt : { type: Date, default: Date.now}, lastViewed : { type: Date, default: Date.now}, - views : {type:Number, default:0} + views : {type:Number, default:0}, + version : {type: Number, default:1} }, { versionKey: false, toJSON : { @@ -32,6 +33,8 @@ const BrewSchema = mongoose.Schema({ } }); +BrewSchema.index({ title: "text", description: "text" }); + BrewSchema.methods.increaseView = function(){ this.views = this.views + 1; return this.save(); @@ -93,19 +96,53 @@ const BrewData = { return BrewData.get({ editId }); }, - search : (query, pagniation, sorting, permissions) => { + search : (searchTerms, pagination, sorting, fullAccess = true) => { + let query = {}; + if(searchTerms){ + query = {$text: { + //Wrap terms in quots to perform an AND operator + $search: _.map(searchTerms.split(' '), (term)=>{ + return `\"${term}\"`; + }).join(' '), + $caseSensitive : false + }}; + } + + pagination = _.defaults(pagination, { + limit : 25, + page : 0 + }); + sorting = _.defaults(sorting, { + 'views' : 1 + }); + let filter = { + //editId : 0, + text : 0 + }; - //search with query, add in `published = false` - // filter out editId and text + if(!fullAccess){ + filter.editId = 0; + query.published = false; + } - //if admin, removed published=false, remove filtering editId + const searchQuery = Brew + .find(query) + .sort(sorting) + .select(filter) + .limit(pagination.limit) + .skip(pagination.page * pagination.limit) + .exec(); + const countQuery = Brew.count(query).exec(); - //defaults with page and count - //returns a non-text version of brews - //assume sanatized ? - return Promise.resolve([]); + return Promise.all([searchQuery, countQuery]) + .then((result) => { + return { + brews : result[0], + total : result[1] + } + }); }, diff --git a/test/search.test.js b/test/search.test.js index 091f2df52..56352ddad 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -18,11 +18,11 @@ const brews = { authors : [], systems : [], views : 12, - published : true + published : false }, BrewB : { title : 'BrewB', - description : 'fancy', + description : 'very fancy', authors : [], systems : [], views : 7, @@ -38,7 +38,7 @@ const brews = { }, BrewD : { title : 'BrewD', - description : 'test', + description : 'test super amazing brew for 5e. Geared for Rangers.', authors : [], systems : [], views : 1, @@ -59,43 +59,82 @@ describe('Brew Search', () => { describe('Searching', ()=>{ it('should be able to search for all brews', ()=>{ - - + return BrewData.search() + .then((result) => { + result.total.should.be.equal(_.size(brews)); + result.brews.length.should.be.equal(_.size(brews)); + }) }); - it('should find brews based on title and/or description', () => { - - //result.count.should.be.equal(2) - //result.brews.should.deep.include.members(ids(['BrewA', 'BrewB']); + it('should search brews based on title', () => { + return BrewData.search('BrewC') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewC'])); + }) }); - it('should return the total number of brews and page info for query', ()=>{ + it('should search brews based on description', () => { + return BrewData.search('fancy') + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.containSubset(ids(['BrewA', 'BrewB'])); + }) + }); + + it('should search brews based on multiple terms', () => { + return BrewData.search('ranger 5e') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewD'])); + }) + }); + + it('should perform an AND operation on the provided terms', () => { + return BrewData.search('BrewD GARBAGE') + .then((result) => { + result.total.should.be.equal(0); + }); + }); + + it('should search brews based on a combination of both', () => { + return BrewData.search('BrewB fancy') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewB'])); + }); + }); + + it.skip('should be able to search for a specific system', ()=>{ + + }); + it.skip('should be able to search for a specifc user', ()=>{ }); }) describe('Permissions', () => { - it('should only fetch published brews', () => { + it.skip('should only fetch published brews', () => { }); - it('fetched brews should not have text or editId', () => { + it.skip('fetched brews should not have text or editId', () => { }); - it('if admin, fetches also non-published brews, with editid', () => { + it.skip('if admin, fetches also non-published brews, with editid', () => { }); - it('if author, fetches also non-published brews, with editid', ()=>{ + it.skip('if author, fetches also non-published brews, with editid', ()=>{ }); }); describe('Pagniation', () => { - it('should return the exact number of brews based on limit', () => { + it.skip('should return the exact number of brews based on limit', () => { }); }); - desscribe('Sorting', ()=>{ + describe('Sorting', ()=>{ }); diff --git a/test/test.init.js b/test/test.init.js index 7dda4f811..2c8c8ecdd 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -7,7 +7,11 @@ const config = require('nconf') .file('environment', { file: `config/${process.env.NODE_ENV}.json` }) .file('defaults', { file: 'config/default.json' }); -const should = require('chai').use(require('chai-as-promised')).should(); +const should = require('chai') + .use(require('chai-as-promised')) + .use(require('chai-subset')) + .should(); + const log = require('loglevel'); log.setLevel(config.get('log_level')); From 2f82d3875e9a1c3ba7edc7485bf8daf76e5a3862 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 09:56:44 -0500 Subject: [PATCH 09/15] Adding a new script from populating the DB with a bunch of random brews --- package.json | 1 + scripts/genbrews.js | 51 +++++++++++++++++++ server/brew.data.js | 12 +++-- server/db.js | 6 +-- .../metadataEditor/metadataEditor.less | 3 ++ 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 scripts/genbrews.js diff --git a/package.json b/package.json index 951b9a67e..df28a4292 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "quick": "node scripts/quick.js", "build": "node scripts/build.js", "phb": "node scripts/phb.js", + "genbrews": "node scripts/genbrews.js", "prod": "set NODE_ENV=production&& npm run build", "postinstall": "npm run build", "start": "node server.js", diff --git a/scripts/genbrews.js b/scripts/genbrews.js new file mode 100644 index 000000000..00053044d --- /dev/null +++ b/scripts/genbrews.js @@ -0,0 +1,51 @@ +//Populates the DB with a bunch of brews for UI testing + +const _ = require('lodash'); + +const DB = require('../server/db.js'); +const BrewData = require('../server/brew.data.js'); + + +//TODO: pull in snippets and randomly add them + +const genBrew = () => { + return { + title : 'BrewA', + description : '', + text : '', + authors : _.sampleSize(['userA','userB','userC','userD'], _.random(0, 3)), + systems : _.sampleSize(['5e', '4e', '3.5e', 'Pathfinder'], _.random(0,2)), + views : _.random(0,1000), + published : !!_.random(0,1) + } +} + +const randomBrews = _.times(20, genBrew); + +const specificBrews = [ + { + text : 'Cool test', + authors : ['test'] + } +]; + + +return Promise.resolve() + .then(DB.connect) + .then(BrewData.removeAll) + .then(() => { + console.log('Adding random brews...'); + return Promise.all(_.map(randomBrews, (brew) => { + return BrewData.create(brew); + })); + }) + .then(() => { + console.log('Adding specific brews...'); + return Promise.all(_.map(specificBrews, (brew) => { + return BrewData.create(brew); + })); + }) + .then(() => { + console.log(`\n Added ${randomBrews.length + specificBrews.length} brews.`); + }) + .catch(console.error); diff --git a/server/brew.data.js b/server/brew.data.js index d8281e118..b6a5a854b 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -22,7 +22,7 @@ const BrewSchema = mongoose.Schema({ updatedAt : { type: Date, default: Date.now}, lastViewed : { type: Date, default: Date.now}, views : {type:Number, default:0}, - version : {type: Number, default:1} + version : {type: Number, default:1} }, { versionKey: false, toJSON : { @@ -33,6 +33,7 @@ const BrewSchema = mongoose.Schema({ } }); +//Index these fields for fast text searching BrewSchema.index({ title: "text", description: "text" }); BrewSchema.methods.increaseView = function(){ @@ -42,9 +43,6 @@ BrewSchema.methods.increaseView = function(){ const Brew = mongoose.model('Brew', BrewSchema); - - - const BrewData = { schema : BrewSchema, model : Brew, @@ -96,6 +94,12 @@ const BrewData = { return BrewData.get({ editId }); }, + + //TODO: Add a 'core search' which just takes a search object + //TODO: extend the core search with a user search and a term search + //TODO: break these functions off into a 'brew.search.js' file + //TODO: pagniation, sorting and full access should be an 'opts' param + search : (searchTerms, pagination, sorting, fullAccess = true) => { let query = {}; if(searchTerms){ diff --git a/server/db.js b/server/db.js index a9c540050..6cb098648 100644 --- a/server/db.js +++ b/server/db.js @@ -8,14 +8,14 @@ module.exports = { connect : ()=>{ return new Promise((resolve, reject)=>{ if(mongoose.connection.readyState == 1){ - log.info('DB already connected'); + log.warn('DB already connected'); return resolve(); } mongoose.connect(dbPath, (err) => { if(err){ - log.info('Error : Could not connect to a Mongo Database.'); - log.info(' If you are running locally, make sure mongodb.exe is running.'); + log.error('Error : Could not connect to a Mongo Database.'); + log.error(' If you are running locally, make sure mongodb.exe is running.'); return reject(err); } log.info('DB connected.'); diff --git a/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less b/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less index c3141f0d4..2f9812d68 100644 --- a/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less +++ b/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less @@ -76,4 +76,7 @@ font-size: 0.8em; line-height : 1.5em; } + .thumbnail.field{ + + } } \ No newline at end of file From 8e58e5aca92fe931e42d217c28c23c3a9ad2d34d Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 10:36:07 -0500 Subject: [PATCH 10/15] MOved the brews search to its own file, writing out more tests --- scripts/genbrews.js | 1 + server/brew.data.js | 61 +------------------ server/brew.search.js | 59 ++++++++++++++++++ server/db.js | 8 +++ test/api.test.js | 24 ++++++++ test/search.test.js | 136 ++++++++++++++++++++++++++---------------- 6 files changed, 180 insertions(+), 109 deletions(-) create mode 100644 server/brew.search.js diff --git a/scripts/genbrews.js b/scripts/genbrews.js index 00053044d..e6dac103b 100644 --- a/scripts/genbrews.js +++ b/scripts/genbrews.js @@ -47,5 +47,6 @@ return Promise.resolve() }) .then(() => { console.log(`\n Added ${randomBrews.length + specificBrews.length} brews.`); + return DB.close(); }) .catch(console.error); diff --git a/server/brew.data.js b/server/brew.data.js index b6a5a854b..c6e92ee3f 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -93,63 +93,8 @@ const BrewData = { getByEdit : (editId) => { return BrewData.get({ editId }); }, - - - //TODO: Add a 'core search' which just takes a search object - //TODO: extend the core search with a user search and a term search - //TODO: break these functions off into a 'brew.search.js' file - //TODO: pagniation, sorting and full access should be an 'opts' param - - search : (searchTerms, pagination, sorting, fullAccess = true) => { - let query = {}; - if(searchTerms){ - query = {$text: { - //Wrap terms in quots to perform an AND operator - $search: _.map(searchTerms.split(' '), (term)=>{ - return `\"${term}\"`; - }).join(' '), - $caseSensitive : false - }}; - } - - pagination = _.defaults(pagination, { - limit : 25, - page : 0 - }); - sorting = _.defaults(sorting, { - 'views' : 1 - }); - let filter = { - //editId : 0, - text : 0 - }; - - - if(!fullAccess){ - filter.editId = 0; - query.published = false; - } - - const searchQuery = Brew - .find(query) - .sort(sorting) - .select(filter) - .limit(pagination.limit) - .skip(pagination.page * pagination.limit) - .exec(); - - const countQuery = Brew.count(query).exec(); - - return Promise.all([searchQuery, countQuery]) - .then((result) => { - return { - brews : result[0], - total : result[1] - } - }); - }, - - }; -module.exports = BrewData; \ No newline at end of file +const BrewSearch = require('./brew.search.js')(Brew); + +module.exports = _.merge(BrewData, BrewSearch); \ No newline at end of file diff --git a/server/brew.search.js b/server/brew.search.js new file mode 100644 index 000000000..99e993e06 --- /dev/null +++ b/server/brew.search.js @@ -0,0 +1,59 @@ +const _ = require('lodash'); + +module.exports = (Brew) => { + const cmds = { + termSearch : (terms='', opts, fullAccess) => { + const query = {$text: { + //Wrap terms in quotes to perform an AND operation + $search: _.map(terms.split(' '), (term)=>{ + return `\"${term}\"`; + }).join(' '), + $caseSensitive : false + }}; + return cmds.search(query, opts, fullAccess); + }, + + userSearch : (username, opts, fullAccess) => { + const query = { + authors : username + }; + + return cmds.search(query, opts, fullAccess); + }, + + search : (queryObj={}, opts={}, fullAccess = true) => { + const pagination = _.defaults(opts.pagination, { + limit : 25, + page : 0 + }); + const sorting = _.defaults(opts.sorting, { + 'views' : 1 + }); + let filter = { + text : 0 + }; + if(!fullAccess){ + filter.editId = 0; + queryObj.published = false; + } + + const searchQuery = Brew + .find(queryObj) + .sort(sorting) + .select(filter) + .limit(pagination.limit) + .skip(pagination.page * pagination.limit) + .exec(); + const countQuery = Brew.count(queryObj).exec(); + + return Promise.all([searchQuery, countQuery]) + .then((result) => { + return { + brews : result[0], + total : result[1] + } + }); + } + }; + return cmds; +}; \ No newline at end of file diff --git a/server/db.js b/server/db.js index 6cb098648..d0756bfc9 100644 --- a/server/db.js +++ b/server/db.js @@ -24,5 +24,13 @@ module.exports = { ); }); }, + close : ()=>{ + return new Promise((resolve, reject) => { + mongoose.connection.close(()=>{ + log.info('DB connection closed.'); + return resolve(); + }); + }); + }, instance : mongoose } \ No newline at end of file diff --git a/test/api.test.js b/test/api.test.js index 822683c58..31330e597 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -111,4 +111,28 @@ describe('Brew API', () => { }); }); + describe('Search', () => { + it.skip('should be able to search for brews with given terms', ()=>{ + + }); + it.skip('should exclude unpublished brews and have no editIdsh', ()=>{ + + }); + it.skip('should sort the search', ()=>{ + + }); + it.skip('should use pagniation on the search', ()=>{ + + }); + }); + + describe('User', () => { + it.skip('should be able to query brews for a specific user', ()=>{ + + }); + it.skip('should return full access to brews if loggedin user is queried user', ()=>{ + + }); + }); + }); \ No newline at end of file diff --git a/test/search.test.js b/test/search.test.js index 56352ddad..5efdd1fc9 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -11,11 +11,13 @@ const ids = (brewIds) => { }); } + +//TODO: Move this brew generator to test.init const brews = { BrewA : { title : 'BrewA', description : 'fancy', - authors : [], + authors : ['userA'], systems : [], views : 12, published : false @@ -23,7 +25,7 @@ const brews = { BrewB : { title : 'BrewB', description : 'very fancy', - authors : [], + authors : ['userA'], systems : [], views : 7, published : true @@ -31,7 +33,7 @@ const brews = { BrewC : { title : 'BrewC', description : 'test', - authors : [], + authors : ['userA', 'userB'], systems : [], views : 0, published : false @@ -39,7 +41,7 @@ const brews = { BrewD : { title : 'BrewD', description : 'test super amazing brew for 5e. Geared for Rangers.', - authors : [], + authors : ['userC'], systems : [], views : 1, published : true @@ -58,6 +60,10 @@ describe('Brew Search', () => { describe('Searching', ()=>{ + it.skip('should return a total and a brew array', ()=>{ + + }); + it('should be able to search for all brews', ()=>{ return BrewData.search() .then((result) => { @@ -65,52 +71,34 @@ describe('Brew Search', () => { result.brews.length.should.be.equal(_.size(brews)); }) }); - it('should search brews based on title', () => { - return BrewData.search('BrewC') - .then((result) => { - result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewC'])); - }) - }); + }); - it('should search brews based on description', () => { - return BrewData.search('fancy') - .then((result) => { - result.total.should.be.equal(2); - result.brews.should.containSubset(ids(['BrewA', 'BrewB'])); - }) - }); - - it('should search brews based on multiple terms', () => { - return BrewData.search('ranger 5e') - .then((result) => { - result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewD'])); - }) - }); - - it('should perform an AND operation on the provided terms', () => { - return BrewData.search('BrewD GARBAGE') - .then((result) => { - result.total.should.be.equal(0); - }); - }); - - it('should search brews based on a combination of both', () => { - return BrewData.search('BrewB fancy') - .then((result) => { - result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewB'])); - }); - }); - - it.skip('should be able to search for a specific system', ()=>{ + describe('Pagniation', () => { + it.skip('should return the exact number of brews based on limit', () => { }); - it.skip('should be able to search for a specifc user', ()=>{ + + it.skip('should return the correct pages when specified', () => { }); - }) + + it.skip('should return a partial list if on the last page', () => { + + }); + + }); + + describe('Sorting', ()=>{ + it.skip('should sort ASC', () => { + + }); + it.skip('should sort DESC', () => { + + }); + it.skip('should sort based on multiple fields', () => { + + }); + }); describe('Permissions', () => { it.skip('should only fetch published brews', () => { @@ -127,16 +115,62 @@ describe('Brew Search', () => { }); }); - describe('Pagniation', () => { - it.skip('should return the exact number of brews based on limit', () => { + + describe('Term Search', ()=>{ + it('should search brews based on title', () => { + return BrewData.termSearch('BrewC') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewC'])); + }) }); + it('should search brews based on description', () => { + return BrewData.termSearch('fancy') + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.containSubset(ids(['BrewA', 'BrewB'])); + }) + }); + + it('should search brews based on multiple terms', () => { + return BrewData.termSearch('ranger 5e') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewD'])); + }) + }); + + it('should perform an AND operation on the provided terms', () => { + return BrewData.termSearch('BrewD GARBAGE') + .then((result) => { + result.total.should.be.equal(0); + }); + }); + + it('should search brews based on a combination of both', () => { + return BrewData.termSearch('BrewB fancy') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewB'])); + }); + }); }); - describe('Sorting', ()=>{ - + describe('User Search', ()=>{ + it('should return brews just for a single user', () => { + return BrewData.userSearch('userA') + .then((result) => { + result.total.should.be.equal(3); + result.brews.should.containSubset(ids(['BrewA', 'BrewB', 'BrewC'])); + }); + }); + it('should return nothing if provided a non-exsistent user', () => { + return BrewData.userSearch('userXYZ') + .then((result) => { + result.total.should.be.equal(0); + }); + }); }); - - }); \ No newline at end of file From 8018442f25e158b12c7a83c01b8e6427f404b53b Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 10:47:38 -0500 Subject: [PATCH 11/15] Upgrading the brew generation for testing --- test/search.test.js | 19 +++++++++++-------- test/test.init.js | 11 +++++++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/test/search.test.js b/test/search.test.js index 5efdd1fc9..4d88145ae 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -1,4 +1,4 @@ -const testing = require('./test.init.js'); +const Test = require('./test.init.js'); const _ = require('lodash'); const DB = require('db.js'); @@ -15,7 +15,7 @@ const ids = (brewIds) => { //TODO: Move this brew generator to test.init const brews = { BrewA : { - title : 'BrewA', + title : 'Brew-Alpha', description : 'fancy', authors : ['userA'], systems : [], @@ -23,7 +23,7 @@ const brews = { published : false }, BrewB : { - title : 'BrewB', + title : 'Brew-Beta', description : 'very fancy', authors : ['userA'], systems : [], @@ -31,7 +31,7 @@ const brews = { published : true }, BrewC : { - title : 'BrewC', + title : 'Brew-Charlie', description : 'test', authors : ['userA', 'userB'], systems : [], @@ -39,7 +39,7 @@ const brews = { published : false }, BrewD : { - title : 'BrewD', + title : 'Brew-Delta', description : 'test super amazing brew for 5e. Geared for Rangers.', authors : ['userC'], systems : [], @@ -119,7 +119,7 @@ describe('Brew Search', () => { describe('Term Search', ()=>{ it('should search brews based on title', () => { - return BrewData.termSearch('BrewC') + return BrewData.termSearch('Charlie') .then((result) => { result.total.should.be.equal(1); result.brews.should.containSubset(ids(['BrewC'])); @@ -143,19 +143,22 @@ describe('Brew Search', () => { }); it('should perform an AND operation on the provided terms', () => { - return BrewData.termSearch('BrewD GARBAGE') + return BrewData.termSearch('Brew Delta GARBAGE') .then((result) => { result.total.should.be.equal(0); }); }); it('should search brews based on a combination of both', () => { - return BrewData.termSearch('BrewB fancy') + return BrewData.termSearch('Brew Beta fancy') .then((result) => { result.total.should.be.equal(1); result.brews.should.containSubset(ids(['BrewB'])); }); }); + it.skip('should not worry about the case of the terms', () => { + + }); }); describe('User Search', ()=>{ diff --git a/test/test.init.js b/test/test.init.js index 2c8c8ecdd..317df50af 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -7,14 +7,17 @@ const config = require('nconf') .file('environment', { file: `config/${process.env.NODE_ENV}.json` }) .file('defaults', { file: 'config/default.json' }); -const should = require('chai') +const Chai = require('chai') .use(require('chai-as-promised')) - .use(require('chai-subset')) - .should(); + .use(require('chai-subset')); const log = require('loglevel'); log.setLevel(config.get('log_level')); +//TODO: extend should to have a brewCheck +// eg. result.brews.should.haveBrews('BrewA', 'BrewB') +// Then can remove chai-subset + module.exports = { - should: should + should: Chai.should() }; From a826aaffd95a157008ad7b84b327253107122315 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 18:38:09 -0500 Subject: [PATCH 12/15] created a brew generator and chai plugin for easier testing --- package.json | 2 +- scripts/genbrews.js | 52 --------------------------- scripts/populate.js | 22 ++++++++++++ test/brew.gen.js | 88 +++++++++++++++++++++++++++++++++++++++++++++ test/search.test.js | 64 ++++++--------------------------- test/test.init.js | 7 ++-- 6 files changed, 124 insertions(+), 111 deletions(-) delete mode 100644 scripts/genbrews.js create mode 100644 scripts/populate.js create mode 100644 test/brew.gen.js diff --git a/package.json b/package.json index df28a4292..02d5ab858 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "quick": "node scripts/quick.js", "build": "node scripts/build.js", "phb": "node scripts/phb.js", - "genbrews": "node scripts/genbrews.js", + "populate": "node scripts/populate.js", "prod": "set NODE_ENV=production&& npm run build", "postinstall": "npm run build", "start": "node server.js", diff --git a/scripts/genbrews.js b/scripts/genbrews.js deleted file mode 100644 index e6dac103b..000000000 --- a/scripts/genbrews.js +++ /dev/null @@ -1,52 +0,0 @@ -//Populates the DB with a bunch of brews for UI testing - -const _ = require('lodash'); - -const DB = require('../server/db.js'); -const BrewData = require('../server/brew.data.js'); - - -//TODO: pull in snippets and randomly add them - -const genBrew = () => { - return { - title : 'BrewA', - description : '', - text : '', - authors : _.sampleSize(['userA','userB','userC','userD'], _.random(0, 3)), - systems : _.sampleSize(['5e', '4e', '3.5e', 'Pathfinder'], _.random(0,2)), - views : _.random(0,1000), - published : !!_.random(0,1) - } -} - -const randomBrews = _.times(20, genBrew); - -const specificBrews = [ - { - text : 'Cool test', - authors : ['test'] - } -]; - - -return Promise.resolve() - .then(DB.connect) - .then(BrewData.removeAll) - .then(() => { - console.log('Adding random brews...'); - return Promise.all(_.map(randomBrews, (brew) => { - return BrewData.create(brew); - })); - }) - .then(() => { - console.log('Adding specific brews...'); - return Promise.all(_.map(specificBrews, (brew) => { - return BrewData.create(brew); - })); - }) - .then(() => { - console.log(`\n Added ${randomBrews.length + specificBrews.length} brews.`); - return DB.close(); - }) - .catch(console.error); diff --git a/scripts/populate.js b/scripts/populate.js new file mode 100644 index 000000000..7a392af77 --- /dev/null +++ b/scripts/populate.js @@ -0,0 +1,22 @@ +//Populates the DB with a bunch of brews for UI testing +const _ = require('lodash'); + +const DB = require('../server/db.js'); +const BrewData = require('../server/brew.data.js'); +const BrewGen = require('../test/brew.gen.js'); + +return Promise.resolve() + .then(DB.connect) + .then(BrewData.removeAll) + .then(() => { + console.log('Adding random brews...'); + return return BrewGen.populateDB(BrewGen.random(5)); + }) + .then(() => { + console.log('Adding specific brews...'); + return return BrewGen.populateDB(BrewGen.static()); + }) + .then(() => { + return DB.close(); + }) + .catch(console.error); diff --git a/test/brew.gen.js b/test/brew.gen.js new file mode 100644 index 000000000..fa6764374 --- /dev/null +++ b/test/brew.gen.js @@ -0,0 +1,88 @@ +const _ = require('lodash'); +const BrewData = require('../server/brew.data.js'); + +let PopulatedBrews = {}; + +module.exports = { + random : (num = 20)=>{ + return _.times(num, ()=>{ + //TODO: Build better generator + return { + title : 'BrewA', + description : '', + text : '', + authors : _.sampleSize(['userA','userB','userC','userD'], _.random(0, 3)), + systems : _.sampleSize(['5e', '4e', '3.5e', 'Pathfinder'], _.random(0,2)), + views : _.random(0,1000), + published : !!_.random(0,1) + }; + }); + }, + static : () => { + return { + BrewA : { + title : 'Brew-Alpha', + description : 'fancy', + authors : ['userA'], + systems : [], + views : 12, + published : false + }, + BrewB : { + title : 'Brew-Beta', + description : 'very fancy', + authors : ['userA'], + systems : [], + views : 7, + published : true + }, + BrewC : { + title : 'Brew-Charlie', + description : 'test', + authors : ['userA', 'userB'], + systems : [], + views : 0, + published : false + }, + BrewD : { + title : 'Brew-Delta', + description : 'test super amazing brew for 5e. Geared for Rangers.', + authors : ['userC'], + systems : [], + views : 1, + published : true + } + }; + }, + + populateDB : (brewCollection)=>{ + return Promise.all(_.map(brewCollection, (brewData, id) => { + return BrewData.create(brewData) + .then((brew)=>{ + PopulatedBrews[id] = brew; + }); + }) + ); + }, + + chaiPlugin : (chai, utils) => { + chai.Assertion.addMethod('brews', function(...brewIds){ + new chai.Assertion(this._obj).to.be.instanceof(Array); + const valid = _.every(brewIds, (brewId) => { + const storedBrew = PopulatedBrews[brewId]; + if(!storedBrew) return false; + return _.some(this._obj, (brew)=>{ + return brew.editId == storedBrew.editId && + brew.shareId == storedBrew.shareId && + brew.title == storedBrew.title && + brew.views == storedBrew.views; + }); + }); + this.assert( + valid, + `expect #{this} to have brews ${brewIds.join(', ')}`, + `expect #{this} to not have brews ${brewIds.join(', ')}` + ) + }); + } +}; \ No newline at end of file diff --git a/test/search.test.js b/test/search.test.js index 4d88145ae..058199e5a 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -3,59 +3,16 @@ const _ = require('lodash'); const DB = require('db.js'); const BrewData = require('brew.data.js'); -const Error = require('error.js'); - -const ids = (brewIds) => { - return _.map(brewIds, (brewId) => { - return { editId : brews[brewId].editId }; - }); -} +const BrewGen = require('./brew.gen.js'); +//const Error = require('error.js'); -//TODO: Move this brew generator to test.init -const brews = { - BrewA : { - title : 'Brew-Alpha', - description : 'fancy', - authors : ['userA'], - systems : [], - views : 12, - published : false - }, - BrewB : { - title : 'Brew-Beta', - description : 'very fancy', - authors : ['userA'], - systems : [], - views : 7, - published : true - }, - BrewC : { - title : 'Brew-Charlie', - description : 'test', - authors : ['userA', 'userB'], - systems : [], - views : 0, - published : false - }, - BrewD : { - title : 'Brew-Delta', - description : 'test super amazing brew for 5e. Geared for Rangers.', - authors : ['userC'], - systems : [], - views : 1, - published : true - } -}; describe('Brew Search', () => { before('Connect DB', DB.connect); before('Clear DB', BrewData.removeAll); before('Populate brews', ()=>{ - return Promise.all(_.map(brews, (brewData, id) => { - return BrewData.create(brewData) - .then((brew)=>{ brews[id] = brew; }); - })); + return BrewGen.populateDB(BrewGen.static()); }); @@ -67,8 +24,9 @@ describe('Brew Search', () => { it('should be able to search for all brews', ()=>{ return BrewData.search() .then((result) => { - result.total.should.be.equal(_.size(brews)); - result.brews.length.should.be.equal(_.size(brews)); + const brewCount = _.size(BrewGen.static()); + result.total.should.be.equal(brewCount); + result.brews.length.should.be.equal(brewCount); }) }); }); @@ -122,7 +80,7 @@ describe('Brew Search', () => { return BrewData.termSearch('Charlie') .then((result) => { result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewC'])); + result.brews.should.have.brews('BrewC'); }) }); @@ -130,7 +88,7 @@ describe('Brew Search', () => { return BrewData.termSearch('fancy') .then((result) => { result.total.should.be.equal(2); - result.brews.should.containSubset(ids(['BrewA', 'BrewB'])); + result.brews.should.have.brews('BrewA', 'BrewB'); }) }); @@ -138,7 +96,7 @@ describe('Brew Search', () => { return BrewData.termSearch('ranger 5e') .then((result) => { result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewD'])); + result.brews.should.have.brews('BrewD'); }) }); @@ -153,7 +111,7 @@ describe('Brew Search', () => { return BrewData.termSearch('Brew Beta fancy') .then((result) => { result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewB'])); + result.brews.should.have.brews('BrewB'); }); }); it.skip('should not worry about the case of the terms', () => { @@ -166,7 +124,7 @@ describe('Brew Search', () => { return BrewData.userSearch('userA') .then((result) => { result.total.should.be.equal(3); - result.brews.should.containSubset(ids(['BrewA', 'BrewB', 'BrewC'])); + result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC'); }); }); it('should return nothing if provided a non-exsistent user', () => { diff --git a/test/test.init.js b/test/test.init.js index 317df50af..ca80e7fb7 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -9,15 +9,12 @@ const config = require('nconf') const Chai = require('chai') .use(require('chai-as-promised')) - .use(require('chai-subset')); + .use(require('chai-subset')) + .use(require('./brew.gen.js').chaiPlugin); const log = require('loglevel'); log.setLevel(config.get('log_level')); -//TODO: extend should to have a brewCheck -// eg. result.brews.should.haveBrews('BrewA', 'BrewB') -// Then can remove chai-subset - module.exports = { should: Chai.should() }; From 26bcb3395a495fb307e735c3bed11055ef4ef28e Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 19:47:45 -0500 Subject: [PATCH 13/15] Fixing edge cases in the search tests --- server/brew.search.js | 24 ++++++++------- server/middleware.js | 2 +- test/admin.test.js | 9 +++--- test/api.test.js | 6 ++-- test/brew.gen.js | 4 +-- test/middleware.test.js | 6 ++-- test/search.test.js | 66 ++++++++++++++++++++++++++++++----------- test/test.init.js | 6 +++- 8 files changed, 80 insertions(+), 43 deletions(-) diff --git a/server/brew.search.js b/server/brew.search.js index 99e993e06..611b1bce5 100644 --- a/server/brew.search.js +++ b/server/brew.search.js @@ -21,29 +21,31 @@ module.exports = (Brew) => { return cmds.search(query, opts, fullAccess); }, - search : (queryObj={}, opts={}, fullAccess = true) => { - const pagination = _.defaults(opts.pagination, { + search : (queryObj={}, options={}, fullAccess = true) => { + const opts = _.merge({ limit : 25, - page : 0 - }); - const sorting = _.defaults(opts.sorting, { - 'views' : 1 - }); + page : 0, + sort : {} + }, options); + let filter = { text : 0 }; + if(!fullAccess){ filter.editId = 0; - queryObj.published = false; + queryObj.published = true; } const searchQuery = Brew .find(queryObj) - .sort(sorting) + .sort(opts.sort) .select(filter) - .limit(pagination.limit) - .skip(pagination.page * pagination.limit) + .limit(opts.limit) + .skip(opts.page * opts.limit) + .lean() .exec(); + const countQuery = Brew.count(queryObj).exec(); return Promise.all([searchQuery, countQuery]) diff --git a/server/middleware.js b/server/middleware.js index cc2a53589..fb19fb1a0 100644 --- a/server/middleware.js +++ b/server/middleware.js @@ -16,7 +16,7 @@ const Middleware = { return next(); }, admin : (req, res, next) => { - if(req.query.admin_key === config.get('admin:key')){ + if(req.headers['x-homebrew-admin'] === config.get('admin:key')){ req.admin = true; } return next(); diff --git a/test/admin.test.js b/test/admin.test.js index 6d2cccece..b2590caa5 100644 --- a/test/admin.test.js +++ b/test/admin.test.js @@ -15,6 +15,7 @@ let brewA = { authors : ['your_dm'] }; + describe('Admin API', ()=>{ before('Connect DB', DB.connect); @@ -35,7 +36,7 @@ describe('Admin API', ()=>{ it('looks up a brew based on the share id', () => { return request(app) .get(`/admin/lookup/${brewA.shareId}`) - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const brew = res.body; @@ -47,7 +48,7 @@ describe('Admin API', ()=>{ it('looks up a brew based on the edit id', () => { return request(app) .get(`/admin/lookup/${brewA.editId}`) - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const brew = res.body; @@ -60,7 +61,7 @@ describe('Admin API', ()=>{ const query = brewA.editId.substring(0, brewA.editId.length -2); return request(app) .get(`/admin/lookup/${query}`) - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const brew = res.body; @@ -72,7 +73,7 @@ describe('Admin API', ()=>{ it('throws an error if it can not find a brew', ()=>{ return request(app) .get(`/admin/lookup/BADID`) - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(404); }); }); diff --git a/test/api.test.js b/test/api.test.js index 31330e597..8017c8cc4 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,6 +1,6 @@ -const testing = require('./test.init.js'); +const Test = require('./test.init.js'); const request = require('supertest-as-promised'); -const jwt = require('jwt-simple'); + const config = require('nconf'); const app = require('app.js'); @@ -24,7 +24,7 @@ describe('Brew API', () => { before('Connect DB', DB.connect); before('Clear DB', BrewData.removeAll); before('Create session token', () => { - session_token = jwt.encode(test_user, config.get('jwt_secret')); + session_token = Test.getSessionToken(test_user); }); before('Create brew', ()=>{ return BrewData.create(storedBrew) diff --git a/test/brew.gen.js b/test/brew.gen.js index fa6764374..0d1fc1308 100644 --- a/test/brew.gen.js +++ b/test/brew.gen.js @@ -56,6 +56,7 @@ module.exports = { }, populateDB : (brewCollection)=>{ + PopulatedBrews = {}; return Promise.all(_.map(brewCollection, (brewData, id) => { return BrewData.create(brewData) .then((brew)=>{ @@ -72,8 +73,7 @@ module.exports = { const storedBrew = PopulatedBrews[brewId]; if(!storedBrew) return false; return _.some(this._obj, (brew)=>{ - return brew.editId == storedBrew.editId && - brew.shareId == storedBrew.shareId && + return brew.shareId == storedBrew.shareId && brew.title == storedBrew.title && brew.views == storedBrew.views; }); diff --git a/test/middleware.test.js b/test/middleware.test.js index 44c822d69..c40751602 100644 --- a/test/middleware.test.js +++ b/test/middleware.test.js @@ -15,8 +15,6 @@ const requestHandler = (req, res) => { }; -console.log(config.get('admin:key')); - const test_user = { username : 'cool guy' }; @@ -106,7 +104,7 @@ describe('Middleware', () => { app.use(mw.admin); app.use(requestHandler) return request(app).get('/') - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const req = res.body; @@ -119,7 +117,7 @@ describe('Middleware', () => { app.get(requestHandler); app.use(Error.expressHandler); return request(app).get('/') - .query({ admin_key : 'BADUSER' }) + .set('x-homebrew-admin', 'BADADMIN') .send() .expect(401); }); diff --git a/test/search.test.js b/test/search.test.js index 058199e5a..04c30d99d 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -17,8 +17,12 @@ describe('Brew Search', () => { describe('Searching', ()=>{ - it.skip('should return a total and a brew array', ()=>{ - + it('should return a total and a brew array', ()=>{ + return BrewData.search() + .then((result) => { + result.total.should.be.a('number'); + result.brews.should.be.an('array'); + }) }); it('should be able to search for all brews', ()=>{ @@ -32,16 +36,35 @@ describe('Brew Search', () => { }); describe('Pagniation', () => { - it.skip('should return the exact number of brews based on limit', () => { - + it('should return the exact number of brews based on limit', () => { + return BrewData.search({}, { + limit : 2 + }) + .then((result) => { + result.total.should.be.equal(_.size(BrewGen.static())); + result.brews.length.should.be.equal(2); + }) }); - it.skip('should return the correct pages when specified', () => { - + it('should return the correct pages when specified', () => { + return BrewData.search({}, { + limit : 2, + page : 1, + sort : { views : 1 } + }) + .then((result) => { + result.brews.should.have.brews('BrewA', 'BrewB'); + }) }); - it.skip('should return a partial list if on the last page', () => { - + it('should return a partial list if on the last page', () => { + return BrewData.search({}, { + limit : 3, + page : 1 + }) + .then((result) => { + result.brews.length.should.be.equal(1); + }); }); }); @@ -59,17 +82,26 @@ describe('Brew Search', () => { }); describe('Permissions', () => { - it.skip('should only fetch published brews', () => { - + it('should only fetch published brews', () => { + return BrewData.search({}, {}, false) + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.have.brews('BrewB', 'BrewD'); + }) }); - it.skip('fetched brews should not have text or editId', () => { - + it('fetched brews should not have text or editId', () => { + return BrewData.search({}, {}, false) + .then((result) => { + result.brews[0].should.not.have.property('text'); + result.brews[0].should.not.have.property('editId'); + }) }); - it.skip('if admin, fetches also non-published brews, with editid', () => { - - }); - it.skip('if author, fetches also non-published brews, with editid', ()=>{ - + it('if full access, brews should have editid, but no text', () => { + return BrewData.search({}, {}, true) + .then((result) => { + result.brews[0].should.not.have.property('text'); + result.brews[0].should.have.property('editId'); + }) }); }); diff --git a/test/test.init.js b/test/test.init.js index ca80e7fb7..ab1567a1b 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -15,6 +15,10 @@ const Chai = require('chai') const log = require('loglevel'); log.setLevel(config.get('log_level')); +const jwt = require('jwt-simple'); module.exports = { - should: Chai.should() + should: Chai.should(), + getSessionToken : (userInfo) => { + return jwt.encode(userInfo, config.get('jwt_secret')); + } }; From 75111acefb572b16378e7f1718824088fa581f04 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Sat, 28 Jan 2017 12:25:26 -0500 Subject: [PATCH 14/15] All tests should be done, phew --- server/brew.api.js | 17 ++- server/brew.search.js | 25 ++-- server/middleware.js | 2 + test/api.test.js | 304 ++++++++++++++++++++++++++++-------------- test/brew.gen.js | 23 ++++ test/search.test.js | 35 +++-- 6 files changed, 289 insertions(+), 117 deletions(-) diff --git a/server/brew.api.js b/server/brew.api.js index d7e30bf6b..d0565b4e4 100644 --- a/server/brew.api.js +++ b/server/brew.api.js @@ -6,10 +6,25 @@ const mw = require('./middleware.js'); //Search router.get('/api/brew', (req, res, next) => { + const opts = _.pick(req.query, ['limit', 'sort', 'page']); - //TODO + BrewData.termSearch(req.query.terms, opts, req.admin) + .then((result) => { + return res.status(200).json(result); + }) + .catch(next); +}); +//User +router.get('/api/user/:username', (req, res, next) => { + const fullAccess = req.admin || + !!(req.account && req.params.username == req.account.username); + BrewData.userSearch(req.params.username, fullAccess) + .then((result) => { + return res.status(200).json(result); + }) + .catch(next); }); //Get diff --git a/server/brew.search.js b/server/brew.search.js index 611b1bce5..a75b768a2 100644 --- a/server/brew.search.js +++ b/server/brew.search.js @@ -3,30 +3,35 @@ const _ = require('lodash'); module.exports = (Brew) => { const cmds = { termSearch : (terms='', opts, fullAccess) => { - const query = {$text: { - //Wrap terms in quotes to perform an AND operation - $search: _.map(terms.split(' '), (term)=>{ - return `\"${term}\"`; - }).join(' '), - $caseSensitive : false - }}; + let query = {}; + if(terms){ + query = {$text: { + //Wrap terms in quotes to perform an AND operation + $search: _.map(terms.split(' '), (term)=>{ + return `\"${term}\"`; + }).join(' '), + $caseSensitive : false + }}; + } return cmds.search(query, opts, fullAccess); }, - userSearch : (username, opts, fullAccess) => { + userSearch : (username, fullAccess) => { const query = { authors : username }; - return cmds.search(query, opts, fullAccess); + return cmds.search(query, {}, fullAccess); }, search : (queryObj={}, options={}, fullAccess = true) => { const opts = _.merge({ limit : 25, page : 0, - sort : {} + sort : {} }, options); + opts.limit = _.toNumber(opts.limit); + opts.page = _.toNumber(opts.page); let filter = { text : 0 diff --git a/server/middleware.js b/server/middleware.js index fb19fb1a0..fb4c42418 100644 --- a/server/middleware.js +++ b/server/middleware.js @@ -16,6 +16,7 @@ const Middleware = { return next(); }, admin : (req, res, next) => { + req.admin = false; if(req.headers['x-homebrew-admin'] === config.get('admin:key')){ req.admin = true; } @@ -44,6 +45,7 @@ const Middleware = { }, + //TODO: REMOVE //Loaders loadBrew : (req, res, next) => { BrewData.getByEdit(req.params.editId) diff --git a/test/api.test.js b/test/api.test.js index 8017c8cc4..0ba5fdbe5 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,4 +1,5 @@ const Test = require('./test.init.js'); +const _ = require('lodash'); const request = require('supertest-as-promised'); const config = require('nconf'); @@ -6,132 +7,241 @@ const config = require('nconf'); const app = require('app.js'); const DB = require('db.js'); const BrewData = require('brew.data.js'); +const BrewGen = require('./brew.gen.js'); const Error = require('error.js'); -const apiPath = '/api/brew'; -let session_token; -const test_user = { - username : 'cool guy' -}; -let storedBrew = { - title : 'good title', - text : 'original text', - authors : ['your_dm'] -}; +const UserX = { username : 'userX' }; +const UserA = { username : 'userA' }; +let UserXToken, UserAToken; describe('Brew API', () => { - before('Connect DB', DB.connect); - before('Clear DB', BrewData.removeAll); before('Create session token', () => { - session_token = Test.getSessionToken(test_user); - }); - before('Create brew', ()=>{ - return BrewData.create(storedBrew) - .then((brew)=>{ storedBrew = brew; }); + UserXToken = Test.getSessionToken(UserX); + UserAToken = Test.getSessionToken(UserA); }); + describe('CRUD', ()=>{ + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); + }); + describe('Create', () => { + it('creates a new brew', () => { + return request(app) + .post(`/api/brew`) + .send({ text : 'Brew Text' }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').that.is.a('string'); + brew.should.have.property('shareId').that.is.a('string'); + brew.should.have.property('text').equal('Brew Text'); + brew.should.not.have.property('_id'); + }); + }); - describe('Create', () => { - it('creates a new brew', () => { - return request(app) - .post(apiPath) - .send({ text : 'Brew Text' }) - .expect(200) - .then((res) => { - const brew = res.body; - brew.should.have.property('editId').that.is.a('string'); - brew.should.have.property('shareId').that.is.a('string'); - brew.should.have.property('text').equal('Brew Text'); - brew.should.not.have.property('_id'); - }); + it('creates a new brew with a session author', () => { + return request(app) + .post(`/api/brew`) + .set('Cookie', `nc_session=${UserXToken}`) + .send({ text : 'Brew Text' }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('authors').include(UserX.username); + }); + }); }); - it('creates a new brew with a session author', () => { - return request(app) - .post(apiPath) - .set('Cookie', `nc_session=${session_token}`) - .send({ text : 'Brew Text' }) - .expect(200) - .then((res) => { - const brew = res.body; - brew.should.have.property('authors').include(test_user.username); - }); - }); - }); + describe('Update', () => { + it('updates an existing brew', () => { + const storedBrew = BrewGen.get('BrewA'); + return request(app) + .put(`/api/brew/${storedBrew.editId}`) + .send({ text : 'New Text' }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').equal(storedBrew.editId); + brew.should.have.property('text').equal('New Text'); + brew.should.have.property('authors').include(storedBrew.authors[0]); + brew.should.not.have.property('_id'); + }); + }); - describe('Update', () => { - it('updates an existing brew', () => { - return request(app) - .put(`${apiPath}/${storedBrew.editId}`) - .send({ text : 'New Text' }) - .expect(200) - .then((res) => { - const brew = res.body; - brew.should.have.property('editId').equal(storedBrew.editId); - brew.should.have.property('text').equal('New Text'); - brew.should.have.property('authors').include('your_dm'); - brew.should.not.have.property('_id'); - }); + it('adds the user as author', () => { + const storedBrew = BrewGen.get('BrewA'); + return request(app) + .put(`/api/brew/${storedBrew.editId}`) + .set('Cookie', `nc_session=${UserXToken}`) + .send({ text : 'New Text' }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('authors').include(UserX.username); + brew.should.have.property('authors').include(storedBrew.authors[0]); + }); + }); + it('should throw error on bad edit id', ()=>{ + const storedBrew = BrewGen.get('BrewA'); + return request(app) + .put(`/api/brew/BADEDITID`) + .send({ text : 'New Text' }) + .expect(404) + }); }); - it('adds the user as author', () => { - return request(app) - .put(`${apiPath}/${storedBrew.editId}`) - .set('Cookie', `nc_session=${session_token}`) - .send({ text : 'New Text' }) - .expect(200) - .then((res) => { - const brew = res.body; - brew.should.have.property('authors').include(test_user.username); - brew.should.have.property('authors').include('your_dm'); - }); + describe('Remove', () => { + it('should removes a brew', ()=>{ + const storedBrew = BrewGen.get('BrewA'); + return request(app) + .del(`/api/brew/${storedBrew.editId}`) + .send() + .expect(200) + .then(() => { + BrewData.getByEdit(storedBrew.editId) + .then(() => { throw 'Brew found when one should not have been'; }) + .catch((err) => { + err.should.be.instanceof(Error.noBrew); + }) + }); + }); }); - it('should throw error on bad edit id', ()=>{ - return request(app) - .put(`${apiPath}/BADEDITID`) - .send({ text : 'New Text' }) - .expect(404) - }); - }); + }) - describe('Remove', () => { - it('should removes a brew', ()=>{ - return request(app) - .del(`${apiPath}/${storedBrew.editId}`) - .send() - .expect(200) - .then(() => { - BrewData.getByEdit(storedBrew.editId) - .then(() => { throw 'Brew found when one should not have been'; }) - .catch((err) => { - err.should.be.instanceof(Error.noBrew); - }) - }); - }); - }); describe('Search', () => { - it.skip('should be able to search for brews with given terms', ()=>{ - + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); }); - it.skip('should exclude unpublished brews and have no editIdsh', ()=>{ + it('should be able to search for all published brews', ()=>{ + return request(app) + .get(`/api/brew`) + .query({}) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(2); + result.brews.should.have.brews('BrewB','BrewD'); + result.brews[0].should.not.have.property('editId'); + }); }); - it.skip('should sort the search', ()=>{ + it('should be able to search for brews with given terms', ()=>{ + return request(app) + .get(`/api/brew`) + .query({ + terms : '5e ranger' + }) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(1); + result.brews.should.have.brews('BrewD'); + }); }); - it.skip('should use pagniation on the search', ()=>{ - + it('should be able to sort the search', ()=>{ + return request(app) + .get(`/api/brew`) + .query({ + sort : { views : 1} + }) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(2); + result.brews[0].should.be.brew('BrewD'); + result.brews[1].should.be.brew('BrewB'); + }); + }); + it('should use pagniation on the search', ()=>{ + return request(app) + .get(`/api/brew`) + .query({ + limit : 1, + page : 1, + sort : { views : -1} + }) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(2); + result.brews[0].should.be.brew('BrewD'); + }) + }); + it('should return all brews and editIds if admin', ()=>{ + return request(app) + .get(`/api/brew`) + .query({}) + .set('x-homebrew-admin', config.get('admin:key')) + .send() + .expect(200) + .then((res) => { + const result = res.body; + const brewCount = _.size(BrewGen.static()); + result.total.should.be.equal(brewCount); + result.brews.length.should.be.equal(brewCount); + result.brews[0].should.have.property('editId'); + }); }); }); describe('User', () => { - it.skip('should be able to query brews for a specific user', ()=>{ - + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); }); - it.skip('should return full access to brews if loggedin user is queried user', ()=>{ + it('should be able to query brews for a specific user', ()=>{ + return request(app) + .get(`/api/user/userA`) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(1); + result.brews.length.should.be.equal(1); + result.brews.should.have.brews('BrewB'); + result.brews[0].should.not.have.property('editId'); + }); + }); + it('should have full access if loggedin user is queried user', ()=>{ + return request(app) + .get(`/api/user/userA`) + .set('Cookie', `nc_session=${UserAToken}`) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(3); + result.brews.length.should.be.equal(3); + result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC'); + result.brews[0].should.have.property('editId'); + }); + }); + it('should have full access if admin', ()=>{ + return request(app) + .get(`/api/user/userA`) + .set('x-homebrew-admin', config.get('admin:key')) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(3); + result.brews.length.should.be.equal(3); + result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC'); + result.brews[0].should.have.property('editId'); + }); }); }); diff --git a/test/brew.gen.js b/test/brew.gen.js index 0d1fc1308..c7d59b309 100644 --- a/test/brew.gen.js +++ b/test/brew.gen.js @@ -4,6 +4,8 @@ const BrewData = require('../server/brew.data.js'); let PopulatedBrews = {}; module.exports = { + //TODO: Add in a generator for old brews to test the old rendering code + random : (num = 20)=>{ return _.times(num, ()=>{ //TODO: Build better generator @@ -66,6 +68,10 @@ module.exports = { ); }, + get : (brewId) => { + return PopulatedBrews[brewId] + }, + chaiPlugin : (chai, utils) => { chai.Assertion.addMethod('brews', function(...brewIds){ new chai.Assertion(this._obj).to.be.instanceof(Array); @@ -84,5 +90,22 @@ module.exports = { `expect #{this} to not have brews ${brewIds.join(', ')}` ) }); + + chai.Assertion.addMethod('brew', function(brewId){ + new chai.Assertion(this._obj).to.be.instanceof(Object); + const brew = this._obj; + const storedBrew = PopulatedBrews[brewId]; + + const valid = storedBrew && + brew.shareId == storedBrew.shareId && + brew.title == storedBrew.title && + brew.views == storedBrew.views; + + this.assert( + valid, + `expect #{this} to be brew ${brewId}`, + `expect #{this} to not be brew ${brewId}` + ) + }); } }; \ No newline at end of file diff --git a/test/search.test.js b/test/search.test.js index 04c30d99d..66495002b 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -70,14 +70,27 @@ describe('Brew Search', () => { }); describe('Sorting', ()=>{ - it.skip('should sort ASC', () => { - + it('should sort ASC', () => { + return BrewData.search({}, { + sort : { views : 1 } + }) + .then((result) => { + result.brews[0].should.be.brew('BrewC'); + result.brews[1].should.be.brew('BrewD'); + result.brews[2].should.be.brew('BrewB'); + result.brews[3].should.be.brew('BrewA'); + }) }); - it.skip('should sort DESC', () => { - - }); - it.skip('should sort based on multiple fields', () => { - + it('should sort DESC', () => { + return BrewData.search({}, { + sort : { views : -1 } + }) + .then((result) => { + result.brews[0].should.be.brew('BrewA'); + result.brews[1].should.be.brew('BrewB'); + result.brews[2].should.be.brew('BrewD'); + result.brews[3].should.be.brew('BrewC'); + }) }); }); @@ -146,8 +159,12 @@ describe('Brew Search', () => { result.brews.should.have.brews('BrewB'); }); }); - it.skip('should not worry about the case of the terms', () => { - + it('should not worry about the case of the terms', () => { + return BrewData.termSearch('FANCY') + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.have.brews('BrewA', 'BrewB'); + }); }); }); From 6030134de2a176c5390310912636f62a2c42651d Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Sat, 28 Jan 2017 16:35:48 -0500 Subject: [PATCH 15/15] New brew search finished on admin page --- client/admin/adminSearch/adminSearch.jsx | 69 ++++++- client/admin/adminSearch/adminSearch.less | 16 +- client/admin/brewLookup/brewLookup.jsx | 19 +- client/admin/brewLookup/brewLookup.less | 10 +- .../homebrewAdmin/brewLookup/brewLookup.jsx | 67 ------- .../homebrewAdmin/brewLookup/brewLookup.less | 8 - client/admin/homebrewAdmin/brewSearch.jsx | 72 -------- client/admin/homebrewAdmin/homebrewAdmin.jsx | 172 ------------------ client/admin/homebrewAdmin/homebrewAdmin.less | 53 ------ client/admin/invalidBrew/invalidBrew.jsx | 10 +- client/admin/invalidBrew/invalidBrew.less | 4 +- client/homebrew/homebrew.jsx | 3 - scripts/populate.js | 4 +- server/admin.routes.js | 5 +- shared/homebrewery/account.store.js | 1 - 15 files changed, 117 insertions(+), 396 deletions(-) delete mode 100644 client/admin/homebrewAdmin/brewLookup/brewLookup.jsx delete mode 100644 client/admin/homebrewAdmin/brewLookup/brewLookup.less delete mode 100644 client/admin/homebrewAdmin/brewSearch.jsx delete mode 100644 client/admin/homebrewAdmin/homebrewAdmin.jsx delete mode 100644 client/admin/homebrewAdmin/homebrewAdmin.less diff --git a/client/admin/adminSearch/adminSearch.jsx b/client/admin/adminSearch/adminSearch.jsx index 15d09724c..a9ab8764c 100644 --- a/client/admin/adminSearch/adminSearch.jsx +++ b/client/admin/adminSearch/adminSearch.jsx @@ -2,16 +2,83 @@ const React = require('react'); const _ = require('lodash'); const cx = require('classnames'); +const request = require('superagent'); + +const BrewTable = require('../brewTable/brewTable.jsx'); + +const LIMIT = 10; const AdminSearch = React.createClass({ getDefaultProps: function() { return { - + adminKey : '', }; }, + getInitialState: function() { + return { + totalBrews : 1, + brews: [], + + searching : false, + error : null, + + page : 1, + searchTerms : '' + }; + }, + + handleSearch : function(e){ + this.setState({ + searchTerms : e.target.value + }); + }, + handlePage : function(e){ + this.setState({ + page : e.target.value + }); + }, + + search : function(){ + this.setState({ searching : true, error : null }); + + request.get(`/api/brew`) + .query({ + terms : this.state.searchTerms, + limit : LIMIT, + page : this.state.page - 1 + }) + .set('x-homebrew-admin', this.props.adminKey) + .end((err, res) => { + if(err){ + this.setState({ + searching : false, + error : err && err.toString() + }); + }else{ + this.setState({ + brews : res.body.brews, + totalBrews : res.body.total + }); + } + }); + }, + render: function(){ return

Admin Search

+
+ + + + + +
+ page: + + / {Math.ceil(this.state.totalBrews / LIMIT)} +
+
+
} }); diff --git a/client/admin/adminSearch/adminSearch.less b/client/admin/adminSearch/adminSearch.less index dc1de6e64..b66896d9e 100644 --- a/client/admin/adminSearch/adminSearch.less +++ b/client/admin/adminSearch/adminSearch.less @@ -1,3 +1,17 @@ -.adminSearch{ +.adminSearch{ + .controls{ + margin-bottom : 20px; + input.search{ + height : 33px; + padding : 10px; + } + .page { + float : right; + font-weight : 800; + input{ + width : 20px; + } + } + } } \ No newline at end of file diff --git a/client/admin/brewLookup/brewLookup.jsx b/client/admin/brewLookup/brewLookup.jsx index b7aa03393..abb38dc24 100644 --- a/client/admin/brewLookup/brewLookup.jsx +++ b/client/admin/brewLookup/brewLookup.jsx @@ -15,7 +15,8 @@ const BrewLookup = React.createClass({ return { query:'', resultBrew : null, - searching : false + searching : false, + error : null }; }, @@ -25,13 +26,14 @@ const BrewLookup = React.createClass({ }) }, lookup : function(){ - this.setState({ searching : true }); + this.setState({ searching : true, error : null }); request.get(`/admin/lookup/${this.state.query}`) - .query({ admin_key : this.props.adminKey }) + .set('x-homebrew-admin', this.props.adminKey) .end((err, res) => { this.setState({ searching : false, + error : err && err.toString(), resultBrew : (err ? null : res.body) }); }) @@ -43,6 +45,7 @@ const BrewLookup = React.createClass({ return + /* const brew = this.state.resultBrew; return
{brew.title}
@@ -57,6 +60,15 @@ const BrewLookup = React.createClass({
+ */ + }, + + renderError : function(){ + if(!this.state.error) return; + + return
+ {this.state.error} +
}, render: function(){ @@ -66,6 +78,7 @@ const BrewLookup = React.createClass({ {this.renderFoundBrew()} + {this.renderError()} } }); diff --git a/client/admin/brewLookup/brewLookup.less b/client/admin/brewLookup/brewLookup.less index dfec8618f..87434c42b 100644 --- a/client/admin/brewLookup/brewLookup.less +++ b/client/admin/brewLookup/brewLookup.less @@ -2,8 +2,12 @@ .brewLookup{ height : 200px; input{ - height : 33px; - padding : 0px 10px; - margin-bottom: 20px; + height : 33px; + margin-bottom : 20px; + padding : 0px 10px; + } + .error{ + font-weight : 800; + color : @red; } } \ No newline at end of file diff --git a/client/admin/homebrewAdmin/brewLookup/brewLookup.jsx b/client/admin/homebrewAdmin/brewLookup/brewLookup.jsx deleted file mode 100644 index ad43b13b6..000000000 --- a/client/admin/homebrewAdmin/brewLookup/brewLookup.jsx +++ /dev/null @@ -1,67 +0,0 @@ -const React = require('react'); -const _ = require('lodash'); -const cx = require('classnames'); - -const request = require('superagent'); -const Moment = require('moment'); - - -const BrewLookup = React.createClass({ - getDefaultProps: function() { - return { - adminKey : '', - }; - }, - getInitialState: function() { - return { - query:'', - resultBrew : null, - searching : false - }; - }, - - handleChange : function(e){ - this.setState({ - query : e.target.value - }) - }, - lookup : function(){ - this.setState({ searching : true }); - - request.get(`/admin/lookup/${this.state.query}`) - .query({ admin_key : this.props.adminKey }) - .end((err, res) => { - this.setState({ - searching : false, - resultBrew : (err ? null : res.body) - }); - }) - }, - - renderFoundBrew : function(){ - if(this.state.searching) return
; - if(!this.state.resultBrew) return
No brew found.
; - - const brew = this.state.resultBrew; - return
-
{brew.title}
-
{brew.authors.join(', ')}
- - -
{Moment(brew.updatedAt).fromNow()}
-
{brew.views}
-
- }, - - render: function(){ - return
-

Brew Lookup

- - - - {this.renderFoundBrew()} -
- } -}); - -module.exports = BrewLookup; diff --git a/client/admin/homebrewAdmin/brewLookup/brewLookup.less b/client/admin/homebrewAdmin/brewLookup/brewLookup.less deleted file mode 100644 index 071028c20..000000000 --- a/client/admin/homebrewAdmin/brewLookup/brewLookup.less +++ /dev/null @@ -1,8 +0,0 @@ -.brewLookup{ - height : 200px; - input{ - height : 33px; - padding : 0px 10px; - margin-bottom: 20px; - } -} \ No newline at end of file diff --git a/client/admin/homebrewAdmin/brewSearch.jsx b/client/admin/homebrewAdmin/brewSearch.jsx deleted file mode 100644 index f2334d7f5..000000000 --- a/client/admin/homebrewAdmin/brewSearch.jsx +++ /dev/null @@ -1,72 +0,0 @@ -var React = require('react'); -var _ = require('lodash'); -var cx = require('classnames'); - -var request = require('superagent'); - -var BrewSearch = React.createClass({ - - getDefaultProps: function() { - return { - admin_key : '' - }; - }, - - getInitialState: function() { - return { - searchTerm: '', - brew : null, - searching : false - }; - }, - - - search : function(){ - this.setState({ - searching : true - }); - - request.get('/homebrew/api/search?id=' + this.state.searchTerm) - .query({ - admin_key : this.props.admin_key, - }) - .end((err, res)=>{ - console.log(err, res, res.body.brews[0]); - this.setState({ - brew : res.body.brews[0], - - searching : false - }) - }); - }, - - handleChange : function(e){ - this.setState({ - searchTerm : e.target.value - }); - }, - handleSearchClick : function(){ - this.search(); - }, - - renderBrew : function(){ - if(!this.state.brew) return null; - return
-
Edit id : {this.state.brew.editId}
-
Share id : {this.state.brew.shareId}
-
- }, - - render : function(){ - return
- - - - - {this.renderBrew()} -
- }, - -}); - -module.exports = BrewSearch; \ No newline at end of file diff --git a/client/admin/homebrewAdmin/homebrewAdmin.jsx b/client/admin/homebrewAdmin/homebrewAdmin.jsx deleted file mode 100644 index 70b593c62..000000000 --- a/client/admin/homebrewAdmin/homebrewAdmin.jsx +++ /dev/null @@ -1,172 +0,0 @@ -var React = require('react'); -var _ = require('lodash'); -var cx = require('classnames'); -var request = require('superagent'); - -var Moment = require('moment'); - -var BrewSearch = require('./brewSearch.jsx'); - -var BrewLookup = require('./brewLookup/brewLookup.jsx'); - - -var HomebrewAdmin = React.createClass({ - getDefaultProps: function() { - return { - admin_key : '' - }; - }, - - getInitialState: function() { - return { - page: 0, - count : 20, - brewCache : {}, - total : 0, - - processingOldBrews : false - }; - }, - - - fetchBrews : function(page){ - request.get('/api/search') - .query({ - admin_key : this.props.admin_key, - count : this.state.count, - page : page - }) - .end((err, res)=>{ - if(err || !res.body || !res.body.brews) return; - this.state.brewCache[page] = res.body.brews; - this.setState({ - brewCache : this.state.brewCache, - total : res.body.total, - count : res.body.count - }) - }) - }, - - componentDidMount: function() { - this.fetchBrews(this.state.page); - }, - - changePageTo : function(page){ - if(!this.state.brewCache[page]){ - this.fetchBrews(page); - } - this.setState({ - page : page - }) - }, - - - clearInvalidBrews : function(){ - request.get('/api/invalid') - .query({admin_key : this.props.admin_key}) - .end((err, res)=>{ - if(!confirm("This will remove " + res.body.count + " brews. Are you sure?")) return; - request.get('/api/invalid') - .query({admin_key : this.props.admin_key, do_it : true}) - .end((err, res)=>{ - alert("Done!") - }); - }); - }, - - - deleteBrew : function(brewId){ - if(!confirm("Are you sure you want to delete '" + brewId + "'?")) return; - request.get('/api/remove/' + brewId) - .query({admin_key : this.props.admin_key}) - .end(function(err, res){ - window.location.reload(); - }) - }, - - handlePageChange : function(dir){ - this.changePageTo(this.state.page + dir); - }, - - - renderPagnination : function(){ - var outOf; - if(this.state.total){ - outOf = this.state.page + ' / ' + Math.round(this.state.total/this.state.count); - } - return
- - {outOf} - -
- }, - - - renderBrews : function(){ - var brews = this.state.brewCache[this.state.page] || _.times(this.state.count); - return _.map(brews, (brew)=>{ - return - {brew.editId} - {brew.shareId} - {Moment(brew.createdAt).fromNow()} - {Moment(brew.updatedAt).fromNow()} - {Moment(brew.lastViewed).fromNow()} - {brew.views} - -
- -
- - - }); - }, - - renderBrewTable : function(){ - return
- - - - - - - - - - - - - {this.renderBrews()} - -
Edit IdShare IdCreated AtLast UpdatedLast ViewedViews
-
- }, - - render : function(){ - var self = this; - return
- - - - {/* -

- Homebrews - {this.state.total} -

- - - - - - {this.renderPagnination()} - {this.renderBrewTable()} - - - - - */} -
- } -}); - -module.exports = HomebrewAdmin; diff --git a/client/admin/homebrewAdmin/homebrewAdmin.less b/client/admin/homebrewAdmin/homebrewAdmin.less deleted file mode 100644 index 6cfc3d5a0..000000000 --- a/client/admin/homebrewAdmin/homebrewAdmin.less +++ /dev/null @@ -1,53 +0,0 @@ - -.homebrewAdmin{ - margin-bottom: 80px; - .brewTable{ - table{ - - th{ - padding : 10px; - font-weight : 800; - } - tr:nth-child(even){ - background-color : fade(@green, 10%); - } - tr.isEmpty{ - background-color : fade(@red, 30%); - } - td{ - min-width : 100px; - padding : 10px; - text-align : center; - - &.preview{ - position : relative; - &:hover{ - .content{ - display : block; - } - } - .content{ - position : absolute; - display : none; - top : 100%; - left : 0px; - z-index : 1000; - max-height : 500px; - width : 300px; - padding : 30px; - background-color : white; - font-family : monospace; - text-align : left; - pointer-events : none; - } - } - } - } - } - .deleteButton{ - cursor: pointer; - } - button.clearOldButton{ - float : right; - } -} \ No newline at end of file diff --git a/client/admin/invalidBrew/invalidBrew.jsx b/client/admin/invalidBrew/invalidBrew.jsx index bafeb2f2f..28ae01d93 100644 --- a/client/admin/invalidBrew/invalidBrew.jsx +++ b/client/admin/invalidBrew/invalidBrew.jsx @@ -9,7 +9,7 @@ const BrewTable = require('../brewTable/brewTable.jsx'); const InvalidBrew = React.createClass({ getDefaultProps: function() { return { - + adminKey : '', }; }, getInitialState: function() { @@ -19,7 +19,7 @@ const InvalidBrew = React.createClass({ }, getInvalid : function(){ request.get(`/admin/invalid`) - .query({ admin_key : this.props.adminKey }) + .set('x-homebrew-admin', this.props.adminKey) .end((err, res) => { this.setState({ brews : res.body @@ -32,7 +32,7 @@ const InvalidBrew = React.createClass({ if(!confirm('Sure you are sure?')) return; request.delete(`/admin/invalid`) - .query({ admin_key : this.props.adminKey }) + .set('x-homebrew-admin', this.props.adminKey) .end((err, res) => { console.log(err, res.body); alert('Invalid brews removed!'); @@ -42,9 +42,9 @@ const InvalidBrew = React.createClass({ render: function(){ return

Remove Invalid Brews

- This will removes all brews older than 3 days and shorter than a tweet. +
This will removes all brews older than 3 days and shorter than a tweet.
- +
diff --git a/client/admin/invalidBrew/invalidBrew.less b/client/admin/invalidBrew/invalidBrew.less index f9370957f..495d04f32 100644 --- a/client/admin/invalidBrew/invalidBrew.less +++ b/client/admin/invalidBrew/invalidBrew.less @@ -1,3 +1,5 @@ .invalidBrew{ - + button{ + margin: 10px 4px; + } } \ No newline at end of file diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 411da9953..4cf976973 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -37,9 +37,6 @@ const Homebrew = React.createClass({ loginPath : this.props.loginPath }); - console.log(this.props.brew); - - Router = CreateRouter({ '/edit/:id' : , '/share/:id' : , diff --git a/scripts/populate.js b/scripts/populate.js index 7a392af77..e002590ad 100644 --- a/scripts/populate.js +++ b/scripts/populate.js @@ -10,11 +10,11 @@ return Promise.resolve() .then(BrewData.removeAll) .then(() => { console.log('Adding random brews...'); - return return BrewGen.populateDB(BrewGen.random(5)); + return BrewGen.populateDB(BrewGen.random(50)); }) .then(() => { console.log('Adding specific brews...'); - return return BrewGen.populateDB(BrewGen.static()); + return BrewGen.populateDB(BrewGen.static()); }) .then(() => { return DB.close(); diff --git a/server/admin.routes.js b/server/admin.routes.js index 956b73028..cf3cf85eb 100644 --- a/server/admin.routes.js +++ b/server/admin.routes.js @@ -46,11 +46,8 @@ router.delete('/admin/invalid', mw.adminOnly, (req, res, next)=>{ }); router.get('/admin/lookup/:search', mw.adminOnly, (req, res, next) => { - //search for mathcing edit id - //search for matching share id - // search for partial match - BrewData.get({ $or:[ + //Searches for partial matches on either edit or share {editId : { "$regex": req.params.search, "$options": "i" }}, {shareId : { "$regex": req.params.search, "$options": "i" }}, ]}) diff --git a/shared/homebrewery/account.store.js b/shared/homebrewery/account.store.js index b79bd9f6e..c1813f746 100644 --- a/shared/homebrewery/account.store.js +++ b/shared/homebrewery/account.store.js @@ -16,7 +16,6 @@ Store.init = (state)=>{ Store.getLoginPath = ()=>{ let path = State.loginPath; if(typeof window !== 'undefined'){ - console.log('yo here'); path = `${path}?redirect=${encodeURIComponent(window.location.href)}`; } return path;