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.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/client/hippo/__mocks__/config.js +4 -4
- data/client/hippo/boot.jsx +8 -8
- data/client/hippo/components/form.jsx +1 -2
- data/client/hippo/components/form/wrapper.jsx +20 -8
- data/client/hippo/config.android.js +8 -0
- data/client/hippo/config.ios.js +8 -0
- data/client/hippo/config.js +5 -71
- data/client/hippo/lib/pub_sub.js +34 -0
- data/client/hippo/models/base.js +6 -6
- data/client/hippo/models/config.js +60 -0
- data/client/hippo/models/pub_sub.js +157 -0
- data/client/hippo/models/pub_sub/channel.js +35 -0
- data/client/hippo/screens/definition.js +1 -1
- data/client/hippo/screens/index.js +2 -10
- data/client/hippo/screens/user-management/edit-form.jsx +10 -7
- data/client/hippo/user.js +1 -3
- data/client/hippo/workspace/index.jsx +16 -15
- data/client/hippo/workspace/menu.jsx +2 -8
- data/client/hippo/workspace/screen.jsx +6 -6
- data/config/database.yml +1 -0
- data/config/routes.rb +4 -4
- data/config/webpack.config.js +18 -16
- data/hippo-fw.gemspec +2 -2
- data/lib/hippo.rb +1 -3
- data/lib/hippo/api.rb +1 -0
- data/lib/hippo/api/cable.rb +19 -20
- data/lib/hippo/api/handlers/user_session.rb +1 -1
- data/lib/hippo/api/pub_sub.rb +7 -5
- data/lib/hippo/api/routing.rb +1 -1
- data/lib/hippo/api/updates.rb +1 -1
- data/lib/hippo/concerns/api_path.rb +2 -3
- data/lib/hippo/configuration.rb +2 -0
- data/lib/hippo/rails.rb +9 -0
- data/lib/hippo/user.rb +2 -1
- data/lib/hippo/version.rb +1 -1
- data/package-lock.json +6823 -0
- data/package.json +43 -34
- data/spec/client/models/pub_sub.spec.js +27 -0
- data/templates/js/config-data.js +1 -1
- data/templates/js/screen-definitions.js +2 -2
- data/yarn.lock +307 -15
- metadata +28 -9
- data/client/extension.js +0 -0
- data/client/hippo/models/PubSub.js +0 -208
- data/lib/generators/hippo/migrations/install_generator.rb +0 -42
- 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 {
|
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(
|
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
|
-
|
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
|
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.
|
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
|
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"
|
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
|
74
|
+
<Box direction="row">
|
72
75
|
<Button label="Cancel" onClick={this.onCancel} accent />
|
73
76
|
<Button
|
74
77
|
label="Save"
|
data/client/hippo/user.js
CHANGED
@@ -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.
|
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 {
|
6
|
+
import {
|
7
|
+
BrowserRouter as Router,
|
8
|
+
Route,
|
9
|
+
} from 'react-router-dom';
|
7
10
|
|
8
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
23
|
-
|
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
|
);
|
data/config/database.yml
CHANGED
data/config/routes.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Hippo::API
|
2
2
|
|
3
3
|
routes.for_extension 'hippo' do
|
4
|
-
get 'job-
|
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-
|
13
|
-
get "user-
|
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-
|
15
|
+
delete "user-session/:id.json" do
|
16
16
|
wrap_reply do
|
17
17
|
{ success: true, message: "Logout succeeded", data: {}, token: '' }
|
18
18
|
end
|
data/config/webpack.config.js
CHANGED
@@ -3,15 +3,15 @@ const path = require('path');
|
|
3
3
|
|
4
4
|
const config = {
|
5
5
|
entry: {
|
6
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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,
|
data/hippo-fw.gemspec
CHANGED
@@ -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 "
|
30
|
-
|
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"
|
data/lib/hippo.rb
CHANGED
@@ -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"
|
data/lib/hippo/api.rb
CHANGED
@@ -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'
|
data/lib/hippo/api/cable.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require
|
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 <
|
13
|
+
class Channel < LiteCable::Channel::Base
|
15
14
|
end
|
16
15
|
|
17
|
-
class Connection <
|
16
|
+
class Connection < LiteCable::Connection::Base
|
18
17
|
identified_by :current_user
|
19
18
|
|
20
19
|
def connect
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
|