hippo-fw 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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