hippo-fw 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/client/hippo/__mocks__/config.js +4 -4
  4. data/client/hippo/boot.jsx +8 -8
  5. data/client/hippo/components/form.jsx +1 -2
  6. data/client/hippo/components/form/wrapper.jsx +20 -8
  7. data/client/hippo/config.android.js +8 -0
  8. data/client/hippo/config.ios.js +8 -0
  9. data/client/hippo/config.js +5 -71
  10. data/client/hippo/lib/pub_sub.js +34 -0
  11. data/client/hippo/models/base.js +6 -6
  12. data/client/hippo/models/config.js +60 -0
  13. data/client/hippo/models/pub_sub.js +157 -0
  14. data/client/hippo/models/pub_sub/channel.js +35 -0
  15. data/client/hippo/screens/definition.js +1 -1
  16. data/client/hippo/screens/index.js +2 -10
  17. data/client/hippo/screens/user-management/edit-form.jsx +10 -7
  18. data/client/hippo/user.js +1 -3
  19. data/client/hippo/workspace/index.jsx +16 -15
  20. data/client/hippo/workspace/menu.jsx +2 -8
  21. data/client/hippo/workspace/screen.jsx +6 -6
  22. data/config/database.yml +1 -0
  23. data/config/routes.rb +4 -4
  24. data/config/webpack.config.js +18 -16
  25. data/hippo-fw.gemspec +2 -2
  26. data/lib/hippo.rb +1 -3
  27. data/lib/hippo/api.rb +1 -0
  28. data/lib/hippo/api/cable.rb +19 -20
  29. data/lib/hippo/api/handlers/user_session.rb +1 -1
  30. data/lib/hippo/api/pub_sub.rb +7 -5
  31. data/lib/hippo/api/routing.rb +1 -1
  32. data/lib/hippo/api/updates.rb +1 -1
  33. data/lib/hippo/concerns/api_path.rb +2 -3
  34. data/lib/hippo/configuration.rb +2 -0
  35. data/lib/hippo/rails.rb +9 -0
  36. data/lib/hippo/user.rb +2 -1
  37. data/lib/hippo/version.rb +1 -1
  38. data/package-lock.json +6823 -0
  39. data/package.json +43 -34
  40. data/spec/client/models/pub_sub.spec.js +27 -0
  41. data/templates/js/config-data.js +1 -1
  42. data/templates/js/screen-definitions.js +2 -2
  43. data/yarn.lock +307 -15
  44. metadata +28 -9
  45. data/client/extension.js +0 -0
  46. data/client/hippo/models/PubSub.js +0 -208
  47. data/lib/generators/hippo/migrations/install_generator.rb +0 -42
  48. data/lib/hippo/rails_engine.rb +0 -5
@@ -0,0 +1,35 @@
1
+ import { omit } from 'lodash';
2
+
3
+ import { logger } from '../../lib/util';
4
+ import { BaseModel } from '../base';
5
+
6
+ const CHANNEL_SPLITTER = new RegExp('^(.*):(.*)/([^/]+)$');
7
+
8
+ export default class PubSubCableChannel {
9
+
10
+ constructor(pub_sub) {
11
+ this.channel = pub_sub.cable.subscriptions.create(
12
+ 'pubsub', this,
13
+ );
14
+ this.pub_sub = pub_sub;
15
+ }
16
+
17
+ subscribe(channel) {
18
+ logger.info(`[pubsub] subscribe to: ${channel}`);
19
+ this.channel.perform('on', { channel });
20
+ }
21
+
22
+ unsubscribe(channel) {
23
+ logger.info(`[pubsub] unsubscribe from: ${channel}`);
24
+ this.channel.perform('off', { channel });
25
+ }
26
+
27
+ received(data) {
28
+ const [channel, _, modelId, id] = Array.from(
29
+ data.channel.match(CHANNEL_SPLITTER),
30
+ );
31
+ logger.info(`[pubsub] change recvd for: ${channel}`);
32
+ const model = BaseModel.findDerived(modelId);
33
+ this.pub_sub.onModelChange(model, id, omit(data, 'channel'));
34
+ }
35
+ }
@@ -8,7 +8,7 @@ import Instance, { displaying } from './instance';
8
8
  import Registry from './index';
9
9
  import Group from './group';
10
10
 
11
- export { createAsyncComponent } from 'react-async-component';
11
+ export { asyncComponent } from 'react-async-component';
12
12
 
13
13
  @identifiedBy('hippo/screen/definition')
14
14
  export default class ScreenDefinition extends BaseModel {
@@ -7,8 +7,6 @@ const Screens = observable({
7
7
 
8
8
  all: observable.map(),
9
9
 
10
- active_screen_ids: observable.array(),
11
-
12
10
  get activeGroups() {
13
11
  const gids = Group.enabled_group_ids || map(Group.all, 'id');
14
12
  const groups = map(gids, gid => Group.forId(gid));
@@ -16,19 +14,13 @@ const Screens = observable({
16
14
  },
17
15
 
18
16
  @computed get active() {
19
- return map(this.active_screen_ids, id => this.all.get(id));
20
- },
21
-
22
- configure(screen_ids) {
23
- this.active_screen_ids.replace(screen_ids);
17
+ return map(Config.screen_ids, id => this.all.get(id));
24
18
  },
25
19
 
26
20
  reset() {
27
- this.active_screen_ids.clear();
21
+ Config.screen_ids.clear();
28
22
  },
29
23
 
30
24
  });
31
25
 
32
- Config.screens = Screens;
33
-
34
26
  export default Screens;
@@ -1,10 +1,9 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Row, Col } from 'react-flexbox-grid';
4
- import { isNil, forIn, get, pick, mapValues, keys, extend } from 'lodash';
5
4
 
6
5
  import { observer } from 'mobx-react';
7
- import { action, observable, computed } from 'mobx';
6
+ import { action } from 'mobx';
8
7
 
9
8
  import Box from 'grommet/components/Box';
10
9
  import Button from 'grommet/components/Button';
@@ -29,7 +28,6 @@ export default class EditForm extends React.PureComponent {
29
28
 
30
29
  constructor(props) {
31
30
  super(props);
32
-
33
31
  this.user = this.props.query.results.modelForRow(this.props.rowIndex);
34
32
  }
35
33
 
@@ -39,7 +37,7 @@ export default class EditForm extends React.PureComponent {
39
37
 
40
38
  @action.bound
41
39
  onSave() {
42
- this.formState.persistTo(this.user);
40
+ this.refs.form.persistTo(this.user);
43
41
  this.user.save().then(this.onSaved);
44
42
  }
45
43
 
@@ -59,16 +57,21 @@ export default class EditForm extends React.PureComponent {
59
57
 
60
58
  render() {
61
59
  return (
62
- <Form tag="div" className="user-edit-form" state={this.formState}>
60
+ <Form
61
+ tag="div"
62
+ ref="form"
63
+ model={this.user}
64
+ className="user-edit-form"
65
+ >
63
66
  <Warning message={this.errorMessage} />
64
67
  <Row middle='sm'>
65
68
  <Field md={4} xs={6} name="login" validate={nonBlank} />
66
- <Field md={4} xs={6} name="name" validate={nonBlank} />
69
+ <Field md={4} xs={6} name="name" validate={nonBlank} />
67
70
  <Field md={4} xs={6} name="email" validate={validEmail} />
68
71
  <Field md={4} xs={6} type="password" name="password" />
69
72
  <Field md={4} xs={6} type="checkbox" name="is_admin" validate={booleanValue} />
70
73
  <Col md={4} xs={6}>
71
- <Box justify="around" direction="row">
74
+ <Box direction="row">
72
75
  <Button label="Cancel" onClick={this.onCancel} accent />
73
76
  <Button
74
77
  label="Save"
@@ -73,9 +73,7 @@ export class UserModel extends BaseModel {
73
73
  setFromSessionRequest(req) {
74
74
  merge(this, pick(req, 'errors', 'lastServerMessage'));
75
75
  if (req.isValid) {
76
- Config.data = req.data;
77
- Config.persistToStorage();
78
- Config.bootstrapUserData();
76
+ Config.update(req.data);
79
77
  this.errors = {};
80
78
  }
81
79
  return this;
@@ -3,9 +3,13 @@ import React from 'react';
3
3
  import { action, observable } from 'mobx';
4
4
  import { observer } from 'mobx-react';
5
5
  import { bindAll } from 'lodash';
6
- import { Router, Route, browserHistory } from 'react-router';
6
+ import {
7
+ BrowserRouter as Router,
8
+ Route,
9
+ } from 'react-router-dom';
7
10
 
8
- import App from 'grommet/components/App';
11
+
12
+ import App from 'grommet/components/App';
9
13
  import Sidebar from 'react-sidebar';
10
14
 
11
15
  import 'hippo/config-data';
@@ -19,6 +23,12 @@ import './styles.scss';
19
23
 
20
24
  const DOCKED_WIDTH_BREAKPOINT = 950;
21
25
 
26
+ function NoMatch() {
27
+ return (
28
+ <h1>Not Found</h1>
29
+ );
30
+ }
31
+
22
32
  @observer
23
33
  class Workspace extends React.Component {
24
34
 
@@ -59,27 +69,18 @@ class Workspace extends React.Component {
59
69
  docked={this.sidebarDocked}
60
70
  onSetOpen={this.onSetSidebarOpen}
61
71
  >
62
- {this.props.children || "Welcome"}
72
+ <Route name='screen' path="/:screenId/:identifier?" component={Screen} />
73
+ <Route path="*" component={NoMatch} />
63
74
  </Sidebar>
64
75
  </App>
65
76
  );
66
77
  }
67
78
  }
68
79
 
69
-
70
- function NoMatch() {
71
- return (
72
- <h1>Not Found</h1>
73
- );
74
- }
75
-
76
80
  export default function WorkspaceRoot() {
77
81
  return (
78
- <Router history={browserHistory}>
79
- <Route path="/" component={Workspace}>
80
- <Route name='screen' path="/:screenId(/:identifier)" component={Screen} />
81
- <Route path="*" component={NoMatch} />
82
- </Route>
82
+ <Router>
83
+ <Route path="/" component={Workspace} />
83
84
  </Router>
84
85
  );
85
86
  }
@@ -1,21 +1,16 @@
1
1
  import React from 'react';
2
- import classnames from 'classnames';
3
2
  import { observer } from 'mobx-react';
4
3
  import { action } from 'mobx';
5
4
  import PropTypes from 'prop-types';
6
5
  import Sidebar from 'grommet/components/Sidebar';
7
- import Heading from 'grommet/components/Heading';
8
- import Footer from 'grommet/components/Footer';
9
6
  import Header from 'grommet/components/Header';
10
7
  import Anchor from 'grommet/components/Anchor';
11
8
  import Menu from 'grommet/components/Menu';
12
9
  import Group from './menu-group';
13
10
  import Screens from '../screens';
14
- import Icon from '../components/icon';
15
11
 
16
- import User from '../user';
17
12
 
18
- //const On
13
+ import User from '../user';
19
14
 
20
15
  @observer
21
16
  class Logout extends React.PureComponent {
@@ -30,8 +25,7 @@ class Logout extends React.PureComponent {
30
25
  ev.stopPropagation();
31
26
  ev.preventDefault();
32
27
  User.logout();
33
- const { router } = this.context;
34
- router.push('/');
28
+ this.context.router.history.push('/');
35
29
  }
36
30
 
37
31
  render() {
@@ -1,8 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import classnames from 'classnames';
4
3
  import { observer } from 'mobx-react';
5
- import { Grid } from 'react-flexbox-grid';
6
4
  import Box from 'grommet/components/Box';
7
5
  import Screens from '../screens';
8
6
  import ScreenInstances from '../screens/instance';
@@ -19,13 +17,15 @@ ScreenView.displayName = 'ScreenView';
19
17
  export default class Screen extends React.Component {
20
18
 
21
19
  static propTypes = {
22
- params: PropTypes.shape({
23
- screenId: PropTypes.string,
20
+ match: PropTypes.shape({
21
+ params: PropTypes.shape({
22
+ screenId: PropTypes.string,
23
+ }),
24
24
  }).isRequired,
25
25
  }
26
26
 
27
27
  componentDidMount() {
28
- const { screenId } = this.props.params;
28
+ const { screenId } = this.props.match.params;
29
29
  if (screenId) {
30
30
  const definition = Screens.all.get(screenId);
31
31
  if (definition) { definition.display(); }
@@ -42,7 +42,7 @@ export default class Screen extends React.Component {
42
42
  flex="grow"
43
43
  >
44
44
  {ScreenInstances.displaying.map(s =>
45
- <ScreenView key={s.id} {...this.props.params} screen={s} />)}
45
+ <ScreenView key={s.id} {...this.props.match.params} screen={s} />)}
46
46
  </Box>
47
47
 
48
48
  );
@@ -2,6 +2,7 @@ development:
2
2
  adapter: postgresql
3
3
  database: hippo-dev
4
4
  host: /tmp
5
+ pool: 10
5
6
 
6
7
  test:
7
8
  adapter: postgresql
@@ -1,7 +1,7 @@
1
1
  module Hippo::API
2
2
 
3
3
  routes.for_extension 'hippo' do
4
- get 'job-statuses/:id.json' do
4
+ get 'job-status/:id.json' do
5
5
  wrap_reply do
6
6
  status = Hippo::Job.status_for_id(params[:id])
7
7
  raise ActiveRecord::RecordNotFound unless status
@@ -9,10 +9,10 @@ module Hippo::API
9
9
  end
10
10
  end
11
11
 
12
- post "user-sessions.json", &Hippo::API::Handlers::UserSession.create
13
- get "user-sessions/test.json", &Hippo::API::Handlers::UserSession.check
12
+ post "user-session.json", &Hippo::API::Handlers::UserSession.create
13
+ get "user-session/test.json", &Hippo::API::Handlers::UserSession.check
14
14
 
15
- delete "user-sessions/:id.json" do
15
+ delete "user-session/:id.json" do
16
16
  wrap_reply do
17
17
  { success: true, message: "Logout succeeded", data: {}, token: '' }
18
18
  end
@@ -3,15 +3,15 @@ const path = require('path');
3
3
 
4
4
  const config = {
5
5
  entry: {
6
- hippo: [
6
+ app: [
7
7
  'react-hot-loader/patch',
8
8
  '<%= "#{Hippo::Extensions.controlling.identifier}/index.js" %>',
9
9
  ],
10
10
  },
11
11
  output: {
12
12
  path: '<%= config_directory.join('..','public', 'assets') %>',
13
- publicPath: '<%= Hippo.env.production? ? '/assets/' : 'http://test.hippo.dev:8889/assets/' %>',
14
13
  filename: '[name]-[hash].js',
14
+ publicPath: '<%= Hippo.env.production? ? '/assets/' : 'http://test.hippo.dev:8889/assets/' %>',
15
15
  },
16
16
  resolve: {
17
17
  modules: [
@@ -44,16 +44,16 @@ const config = {
44
44
  },
45
45
  },
46
46
  { test: /\.scss$/,
47
- use: [
48
- 'style-loader',
49
- 'css-loader',
50
- {
51
- loader: 'sass-loader',
52
- options: {
53
- includePaths: [path.resolve('./node_modules')],
54
- },
55
- },
56
- ],
47
+ use: [
48
+ 'style-loader',
49
+ 'css-loader',
50
+ {
51
+ loader: 'sass-loader',
52
+ options: {
53
+ includePaths: [path.resolve('./node_modules')],
54
+ },
55
+ },
56
+ ],
57
57
  },
58
58
  ],
59
59
  },
@@ -71,10 +71,12 @@ const config = {
71
71
  inline: true,
72
72
  port: 8889,
73
73
  historyApiFallback: true,
74
- proxy: [{
75
- context: '/api',
76
- target: 'http://localhost:9292',
77
- }],
74
+ headers: {
75
+ "Access-Control-Allow-Origin": "*",
76
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
77
+ "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
78
+ },
79
+ disableHostCheck: true,
78
80
  stats: {
79
81
  colors: true,
80
82
  profile: true,
@@ -26,8 +26,8 @@ Gem::Specification.new do |spec|
26
26
 
27
27
  spec.add_dependency "activejob", "~> 5.0.0"
28
28
  spec.add_dependency "activerecord", "~> 5.0.0"
29
- spec.add_dependency "actioncable", "~> 5.0.0"
30
- # spec.add_dependency "activerecord-multi-tenant", "~> 0.5"
29
+ spec.add_dependency "litecable", "~> 0.4.1"
30
+ spec.add_dependency "websocket", "~> 1.2.4"
31
31
  spec.add_dependency "mail", "~> 2.6"
32
32
  spec.add_dependency "scenic", "~> 1.4"
33
33
  spec.add_dependency "liquid", "~> 4.0"
@@ -7,13 +7,11 @@ module Hippo
7
7
  end
8
8
 
9
9
  require_relative "hippo/version"
10
+ require_relative "hippo/rails"
10
11
  require_relative "hippo/environment"
11
12
  require_relative "hippo/configuration"
12
13
  require_relative "hippo/redis"
13
14
  require_relative "hippo/logger"
14
- if defined?(::Rails)
15
- require_relative "hippo/rails_engine"
16
- end
17
15
  require_relative "hippo/db"
18
16
  require_relative "hippo/strings"
19
17
  require_relative "hippo/numbers"
@@ -8,6 +8,7 @@ require_relative 'api/generic_controller'
8
8
  require_relative 'api/cable'
9
9
  require_relative 'api/sprockets_extension'
10
10
  require_relative 'api/helper_methods'
11
+ require_relative 'api/cable'
11
12
  require_relative 'api/pub_sub'
12
13
  require_rel 'api/handlers/*.rb'
13
14
  require_relative 'api/route_set'
@@ -1,5 +1,4 @@
1
- require 'action_cable'
2
- # require 'action_cable/subscription_adapter/postgresql'
1
+ require "lite_cable"
3
2
 
4
3
  module Hippo
5
4
  module API
@@ -11,18 +10,20 @@ module Hippo
11
10
  @@server.call(request.env)
12
11
  end
13
12
 
14
- class Channel < ActionCable::Channel::Base
13
+ class Channel < LiteCable::Channel::Base
15
14
  end
16
15
 
17
- class Connection < ActionCable::Connection::Base
16
+ class Connection < LiteCable::Connection::Base
18
17
  identified_by :current_user
19
18
 
20
19
  def connect
21
- unless cookies['user_id'] &&
22
- self.current_user = Hippo::User
23
- .where(id: cookies['user_id']).first
24
- Hippo.logger.warn("Rejecting ws connection due to unauthorized access by user_id #{cookies['user_id']}")
25
-
20
+ token = request.params['token']
21
+ begin
22
+ self.current_user = User.for_jwt_token(token) if token
23
+ rescue JWT::DecodeError
24
+ end
25
+ unless self.current_user
26
+ Hippo.logger.warn("Rejecting ws connection due to unauthorized access")
26
27
  reject_unauthorized_connection
27
28
  end
28
29
  end
@@ -35,18 +36,16 @@ module Hippo
35
36
  end
36
37
 
37
38
  def self.configure
39
+ require_relative './updates'
38
40
 
39
- require_relative 'updates'
40
- @@config = ActionCable::Server::Configuration.new
41
- config.logger = Hippo.logger
42
- config.cable = Hippo.config.cable
43
- config.connection_class = -> { Connection }
44
- config.allowed_request_origins = -> (host) {
45
- host
46
- }
47
-
48
- ActionCable::Server::Base.config = config
49
- @@server = ActionCable.server
41
+ if Hippo.config.api_use_any_cable
42
+
43
+ else
44
+ require "lite_cable/server"
45
+ @@server = LiteCable::Server::Middleware.new(
46
+ Hippo::API::Root, connection_class: Hippo::API::Cable::Connection
47
+ )
48
+ end
50
49
  Updates.relay!
51
50
  end
52
51