jason-rails 0.3.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.ruby-version +1 -0
- data/Gemfile.lock +184 -0
- data/README.md +118 -10
- data/app/controllers/jason/api/pusher_controller.rb +15 -0
- data/app/controllers/jason/api_controller.rb +78 -0
- data/client/babel.config.js +13 -0
- data/client/lib/JasonContext.d.ts +6 -1
- data/client/lib/JasonProvider.d.ts +6 -5
- data/client/lib/JasonProvider.js +5 -97
- data/client/lib/actionFactory.js +1 -1
- data/client/lib/createActions.d.ts +1 -1
- data/client/lib/createActions.js +2 -27
- data/client/lib/createJasonReducers.js +49 -3
- data/client/lib/createOptDis.d.ts +1 -0
- data/client/lib/createOptDis.js +43 -0
- data/client/lib/createPayloadHandler.d.ts +9 -1
- data/client/lib/createPayloadHandler.js +52 -43
- data/client/lib/createServerActionQueue.d.ts +10 -0
- data/client/lib/createServerActionQueue.js +48 -0
- data/client/lib/createServerActionQueue.test.d.ts +1 -0
- data/client/lib/createServerActionQueue.test.js +37 -0
- data/client/lib/createTransportAdapter.d.ts +5 -0
- data/client/lib/createTransportAdapter.js +20 -0
- data/client/lib/deepCamelizeKeys.d.ts +1 -0
- data/client/lib/deepCamelizeKeys.js +23 -0
- data/client/lib/deepCamelizeKeys.test.d.ts +1 -0
- data/client/lib/deepCamelizeKeys.test.js +106 -0
- data/client/lib/index.d.ts +6 -5
- data/client/lib/pruneIdsMiddleware.d.ts +2 -0
- data/client/lib/pruneIdsMiddleware.js +24 -0
- data/client/lib/restClient.d.ts +2 -0
- data/client/lib/restClient.js +17 -0
- data/client/lib/transportAdapters/actionCableAdapter.d.ts +5 -0
- data/client/lib/transportAdapters/actionCableAdapter.js +35 -0
- data/client/lib/transportAdapters/pusherAdapter.d.ts +5 -0
- data/client/lib/transportAdapters/pusherAdapter.js +68 -0
- data/client/lib/useJason.d.ts +5 -0
- data/client/lib/useJason.js +94 -0
- data/client/lib/useJason.test.d.ts +1 -0
- data/client/lib/useJason.test.js +85 -0
- data/client/lib/useSub.d.ts +1 -1
- data/client/lib/useSub.js +6 -3
- data/client/package.json +19 -4
- data/client/src/JasonProvider.tsx +6 -96
- data/client/src/actionFactory.ts +1 -1
- data/client/src/createActions.ts +2 -33
- data/client/src/createJasonReducers.ts +57 -3
- data/client/src/createOptDis.ts +45 -0
- data/client/src/createPayloadHandler.ts +58 -47
- data/client/src/createServerActionQueue.test.ts +42 -0
- data/client/src/createServerActionQueue.ts +47 -0
- data/client/src/createTransportAdapter.ts +13 -0
- data/client/src/deepCamelizeKeys.test.ts +113 -0
- data/client/src/deepCamelizeKeys.ts +17 -0
- data/client/src/pruneIdsMiddleware.ts +24 -0
- data/client/src/restClient.ts +14 -0
- data/client/src/transportAdapters/actionCableAdapter.ts +38 -0
- data/client/src/transportAdapters/pusherAdapter.ts +72 -0
- data/client/src/useJason.test.ts +87 -0
- data/client/src/useJason.ts +110 -0
- data/client/src/useSub.ts +6 -3
- data/client/yarn.lock +4607 -81
- data/config/routes.rb +8 -0
- data/jason-rails.gemspec +9 -0
- data/lib/jason.rb +40 -1
- data/lib/jason/api_model.rb +15 -9
- data/lib/jason/broadcaster.rb +19 -0
- data/lib/jason/channel.rb +50 -21
- data/lib/jason/engine.rb +5 -0
- data/lib/jason/graph_helper.rb +165 -0
- data/lib/jason/includes_helper.rb +108 -0
- data/lib/jason/lua_generator.rb +71 -0
- data/lib/jason/publisher.rb +103 -30
- data/lib/jason/publisher_old.rb +112 -0
- data/lib/jason/subscription.rb +352 -101
- data/lib/jason/subscription_old.rb +171 -0
- data/lib/jason/version.rb +1 -1
- metadata +151 -4
@@ -0,0 +1,94 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const createActions_1 = __importDefault(require("./createActions"));
|
7
|
+
const createJasonReducers_1 = __importDefault(require("./createJasonReducers"));
|
8
|
+
const createPayloadHandler_1 = __importDefault(require("./createPayloadHandler"));
|
9
|
+
const createOptDis_1 = __importDefault(require("./createOptDis"));
|
10
|
+
const createServerActionQueue_1 = __importDefault(require("./createServerActionQueue"));
|
11
|
+
const restClient_1 = __importDefault(require("./restClient"));
|
12
|
+
const pruneIdsMiddleware_1 = __importDefault(require("./pruneIdsMiddleware"));
|
13
|
+
const createTransportAdapter_1 = __importDefault(require("./createTransportAdapter"));
|
14
|
+
const toolkit_1 = require("@reduxjs/toolkit");
|
15
|
+
const makeEager_1 = __importDefault(require("./makeEager"));
|
16
|
+
const humps_1 = require("humps");
|
17
|
+
const blueimp_md5_1 = __importDefault(require("blueimp-md5"));
|
18
|
+
const lodash_1 = __importDefault(require("lodash"));
|
19
|
+
const react_1 = require("react");
|
20
|
+
function useJason({ reducers, middleware = [], extraActions }) {
|
21
|
+
const [store, setStore] = react_1.useState(null);
|
22
|
+
const [value, setValue] = react_1.useState(null);
|
23
|
+
react_1.useEffect(() => {
|
24
|
+
restClient_1.default.get('/jason/api/config')
|
25
|
+
.then(({ data: jasonConfig }) => {
|
26
|
+
const { schema: snakey_schema } = jasonConfig;
|
27
|
+
const schema = humps_1.camelizeKeys(snakey_schema);
|
28
|
+
console.debug({ schema });
|
29
|
+
const serverActionQueue = createServerActionQueue_1.default();
|
30
|
+
const allReducers = Object.assign(Object.assign({}, reducers), createJasonReducers_1.default(schema));
|
31
|
+
console.debug({ allReducers });
|
32
|
+
const store = toolkit_1.configureStore({ reducer: allReducers, middleware: [...middleware, pruneIdsMiddleware_1.default(schema)] });
|
33
|
+
const dispatch = store.dispatch;
|
34
|
+
const optDis = createOptDis_1.default(schema, dispatch, restClient_1.default, serverActionQueue);
|
35
|
+
const actions = createActions_1.default(schema, store, restClient_1.default, optDis, extraActions);
|
36
|
+
const eager = makeEager_1.default(schema);
|
37
|
+
let payloadHandlers = {};
|
38
|
+
let configs = {};
|
39
|
+
let subOptions = {};
|
40
|
+
function handlePayload(payload) {
|
41
|
+
const { md5Hash } = payload;
|
42
|
+
const { handlePayload } = payloadHandlers[md5Hash];
|
43
|
+
if (handlePayload) {
|
44
|
+
handlePayload(payload);
|
45
|
+
}
|
46
|
+
else {
|
47
|
+
console.warn("Payload arrived with no handler", payload, payloadHandlers);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
const transportAdapter = createTransportAdapter_1.default(jasonConfig, handlePayload, dispatch, () => lodash_1.default.keys(configs).forEach(md5Hash => createSubscription(configs[md5Hash], subOptions[md5Hash])));
|
51
|
+
function createSubscription(config, options = {}) {
|
52
|
+
// We need the hash to be consistent in Ruby / Javascript
|
53
|
+
const hashableConfig = lodash_1.default(Object.assign({ conditions: {}, includes: {} }, config)).toPairs().sortBy(0).fromPairs().value();
|
54
|
+
const md5Hash = blueimp_md5_1.default(JSON.stringify(hashableConfig));
|
55
|
+
payloadHandlers[md5Hash] = createPayloadHandler_1.default({ dispatch, serverActionQueue, transportAdapter, config });
|
56
|
+
configs[md5Hash] = hashableConfig;
|
57
|
+
subOptions[md5Hash] = options;
|
58
|
+
setTimeout(() => transportAdapter.createSubscription(hashableConfig), 500);
|
59
|
+
let pollInterval = null;
|
60
|
+
// This is only for debugging / dev - not prod!
|
61
|
+
// @ts-ignore
|
62
|
+
if (options.pollInterval) {
|
63
|
+
// @ts-ignore
|
64
|
+
pollInterval = setInterval(() => transportAdapter.getPayload(hashableConfig, { forceRefresh: true }), options.pollInterval);
|
65
|
+
}
|
66
|
+
return {
|
67
|
+
remove() {
|
68
|
+
removeSubscription(hashableConfig);
|
69
|
+
if (pollInterval)
|
70
|
+
clearInterval(pollInterval);
|
71
|
+
},
|
72
|
+
md5Hash
|
73
|
+
};
|
74
|
+
}
|
75
|
+
function removeSubscription(config) {
|
76
|
+
transportAdapter.removeSubscription(config);
|
77
|
+
const md5Hash = blueimp_md5_1.default(JSON.stringify(config));
|
78
|
+
payloadHandlers[md5Hash].tearDown();
|
79
|
+
delete payloadHandlers[md5Hash];
|
80
|
+
delete configs[md5Hash];
|
81
|
+
delete subOptions[md5Hash];
|
82
|
+
}
|
83
|
+
setValue({
|
84
|
+
actions: actions,
|
85
|
+
subscribe: createSubscription,
|
86
|
+
eager,
|
87
|
+
handlePayload
|
88
|
+
});
|
89
|
+
setStore(store);
|
90
|
+
});
|
91
|
+
}, []);
|
92
|
+
return [store, value];
|
93
|
+
}
|
94
|
+
exports.default = useJason;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,85 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
13
|
+
};
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
15
|
+
const react_hooks_1 = require("@testing-library/react-hooks");
|
16
|
+
const useJason_1 = __importDefault(require("./useJason"));
|
17
|
+
const restClient_1 = __importDefault(require("./restClient"));
|
18
|
+
jest.mock('./restClient');
|
19
|
+
test('it works', () => __awaiter(void 0, void 0, void 0, function* () {
|
20
|
+
const resp = { data: {
|
21
|
+
schema: { post: {} },
|
22
|
+
transportService: 'action_cable'
|
23
|
+
} };
|
24
|
+
// @ts-ignore
|
25
|
+
restClient_1.default.get.mockResolvedValue(resp);
|
26
|
+
const { result, waitForNextUpdate } = react_hooks_1.renderHook(() => useJason_1.default({ reducers: {
|
27
|
+
test: (s, a) => s || {}
|
28
|
+
} }));
|
29
|
+
yield waitForNextUpdate();
|
30
|
+
const [store, value, connected] = result.current;
|
31
|
+
const { handlePayload, subscribe } = value;
|
32
|
+
const subscription = subscribe({ post: {} });
|
33
|
+
handlePayload({
|
34
|
+
type: 'payload',
|
35
|
+
model: 'post',
|
36
|
+
payload: [{ id: 4, name: 'test' }],
|
37
|
+
md5Hash: subscription.md5Hash,
|
38
|
+
idx: 1
|
39
|
+
});
|
40
|
+
handlePayload({
|
41
|
+
id: 4,
|
42
|
+
model: 'post',
|
43
|
+
destroy: true,
|
44
|
+
md5Hash: subscription.md5Hash,
|
45
|
+
idx: 2
|
46
|
+
});
|
47
|
+
handlePayload({
|
48
|
+
id: 5,
|
49
|
+
model: 'post',
|
50
|
+
payload: { id: 5, name: 'test2' },
|
51
|
+
md5Hash: subscription.md5Hash,
|
52
|
+
idx: 3
|
53
|
+
});
|
54
|
+
}));
|
55
|
+
test('pruning IDs', () => __awaiter(void 0, void 0, void 0, function* () {
|
56
|
+
const resp = { data: {
|
57
|
+
schema: { post: {} },
|
58
|
+
transportService: 'action_cable'
|
59
|
+
} };
|
60
|
+
// @ts-ignore
|
61
|
+
restClient_1.default.get.mockResolvedValue(resp);
|
62
|
+
const { result, waitForNextUpdate } = react_hooks_1.renderHook(() => useJason_1.default({ reducers: {
|
63
|
+
test: (s, a) => s || {}
|
64
|
+
} }));
|
65
|
+
yield waitForNextUpdate();
|
66
|
+
const [store, value, connected] = result.current;
|
67
|
+
const { handlePayload, subscribe } = value;
|
68
|
+
const subscription = subscribe({ post: {} });
|
69
|
+
handlePayload({
|
70
|
+
type: 'payload',
|
71
|
+
model: 'post',
|
72
|
+
payload: [{ id: 4, name: 'test' }],
|
73
|
+
md5Hash: subscription.md5Hash,
|
74
|
+
idx: 1
|
75
|
+
});
|
76
|
+
handlePayload({
|
77
|
+
type: 'payload',
|
78
|
+
model: 'post',
|
79
|
+
payload: [{ id: 5, name: 'test it out' }],
|
80
|
+
md5Hash: subscription.md5Hash,
|
81
|
+
idx: 2
|
82
|
+
});
|
83
|
+
// The ID 4 should have been pruned
|
84
|
+
expect(store.getState().posts.ids).toStrictEqual([5]);
|
85
|
+
}));
|
data/client/lib/useSub.d.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export default function useSub(config: any): void;
|
1
|
+
export default function useSub(config: any, options?: {}): void;
|
data/client/lib/useSub.js
CHANGED
@@ -5,10 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
const JasonContext_1 = __importDefault(require("./JasonContext"));
|
7
7
|
const react_1 = require("react");
|
8
|
-
function useSub(config) {
|
8
|
+
function useSub(config, options = {}) {
|
9
|
+
// useEffect uses strict equality
|
10
|
+
const configJson = JSON.stringify(config);
|
9
11
|
const subscribe = react_1.useContext(JasonContext_1.default).subscribe;
|
10
12
|
react_1.useEffect(() => {
|
11
|
-
|
12
|
-
|
13
|
+
// @ts-ignore
|
14
|
+
return subscribe(config, options).remove;
|
15
|
+
}, [configJson]);
|
13
16
|
}
|
14
17
|
exports.default = useSub;
|
data/client/package.json
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "@jamesr2323/jason",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.6.0",
|
4
4
|
"module": "./lib/index.js",
|
5
|
+
"types": "./lib/index.d.ts",
|
5
6
|
"scripts": {
|
6
|
-
"build": "tsc"
|
7
|
+
"build": "tsc",
|
8
|
+
"test": "jest"
|
7
9
|
},
|
8
10
|
"dependencies": {
|
9
11
|
"@rails/actioncable": "^6.0.3-4",
|
@@ -14,14 +16,27 @@
|
|
14
16
|
"jsonpatch": "^3.0.1",
|
15
17
|
"lodash": "^4.17.20",
|
16
18
|
"pluralize": "^8.0.0",
|
19
|
+
"pusher-js": "^7.0.3",
|
17
20
|
"uuid": "^8.3.1"
|
18
21
|
},
|
19
22
|
"devDependencies": {
|
23
|
+
"@babel/core": "^7.12.10",
|
24
|
+
"@babel/preset-env": "^7.12.11",
|
25
|
+
"@babel/preset-typescript": "^7.12.7",
|
26
|
+
"@reduxjs/toolkit": "^1.5.0",
|
27
|
+
"@testing-library/react-hooks": "^5.0.3",
|
28
|
+
"@types/jest": "^26.0.19",
|
29
|
+
"babel-jest": "^26.6.3",
|
30
|
+
"jest": "^26.6.3",
|
31
|
+
"react": "^16.9.x",
|
32
|
+
"react-dom": "^16.9.x",
|
33
|
+
"react-redux": "^7.2.2",
|
20
34
|
"typescript": "^4.1.2"
|
21
35
|
},
|
22
36
|
"peerDependencies": {
|
37
|
+
"@reduxjs/toolkit": "^1.5.0",
|
23
38
|
"react": "^16.8.3",
|
24
|
-
"react-
|
25
|
-
"
|
39
|
+
"react-dom": "^16.8.3",
|
40
|
+
"react-redux": "^7.2.2"
|
26
41
|
}
|
27
42
|
}
|
@@ -1,102 +1,12 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
import JasonContext from './JasonContext'
|
4
|
-
import axios from 'axios'
|
5
|
-
import applyCaseMiddleware from 'axios-case-converter'
|
1
|
+
import React from 'react'
|
2
|
+
import useJason from './useJason'
|
6
3
|
import { Provider } from 'react-redux'
|
7
|
-
import
|
8
|
-
import createJasonReducers from './createJasonReducers'
|
9
|
-
import createPayloadHandler from './createPayloadHandler'
|
10
|
-
import makeEager from './makeEager'
|
11
|
-
import { camelizeKeys } from 'humps'
|
12
|
-
import md5 from 'blueimp-md5'
|
13
|
-
import _ from 'lodash'
|
14
|
-
import React, { useState, useEffect } from 'react'
|
15
|
-
|
16
|
-
const JasonProvider = ({ reducers, middleware, extraActions, children }) => {
|
17
|
-
const [store, setStore] = useState(null)
|
18
|
-
const [value, setValue] = useState(null)
|
19
|
-
const [connected, setConnected] = useState(false)
|
20
|
-
|
21
|
-
const csrfToken = (document.querySelector("meta[name=csrf-token]") as any).content
|
22
|
-
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken
|
23
|
-
const restClient = applyCaseMiddleware(axios.create())
|
24
|
-
|
25
|
-
useEffect(() => {
|
26
|
-
restClient.get('/jason/api/schema')
|
27
|
-
.then(({ data: snakey_schema }) => {
|
28
|
-
const schema = camelizeKeys(snakey_schema)
|
29
|
-
|
30
|
-
const consumer = createConsumer()
|
31
|
-
const allReducers = {
|
32
|
-
...reducers,
|
33
|
-
...createJasonReducers(schema)
|
34
|
-
}
|
35
|
-
|
36
|
-
console.log({ schema, allReducers })
|
37
|
-
const store = configureStore({ reducer: allReducers, middleware })
|
38
|
-
|
39
|
-
let payloadHandlers = {}
|
40
|
-
function handlePayload(payload) {
|
41
|
-
const { model, md5Hash } = payload
|
42
|
-
console.log({ md5Hash, fn: `${model}:${md5Hash}`, payloadHandlers, model: _.camelCase(model), payload })
|
43
|
-
const handler = payloadHandlers[`${_.camelCase(model)}:${md5Hash}`]
|
44
|
-
if (handler) {
|
45
|
-
handler({ ...payload, model: _.camelCase(model) })
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
const subscription = (consumer.subscriptions.create({
|
50
|
-
channel: 'Jason::Channel'
|
51
|
-
}, {
|
52
|
-
connected: () => {
|
53
|
-
setConnected(true)
|
54
|
-
},
|
55
|
-
received: payload => {
|
56
|
-
console.log("Payload received", payload)
|
57
|
-
handlePayload(payload)
|
58
|
-
},
|
59
|
-
disconnected: () => console.warn('Disconnected from ActionCable')
|
60
|
-
}));
|
61
|
-
|
62
|
-
console.log('sending message')
|
63
|
-
subscription.send({ message: 'test' })
|
64
|
-
|
65
|
-
function createSubscription(config) {
|
66
|
-
const md5Hash = md5(JSON.stringify(config))
|
67
|
-
console.log('Subscribe with', config, md5Hash)
|
68
|
-
|
69
|
-
_.map(config, (v, model) => {
|
70
|
-
payloadHandlers[`${model}:${md5Hash}`] = createPayloadHandler(store.dispatch, subscription, model, schema[model])
|
71
|
-
})
|
72
|
-
subscription.send({ createSubscription: config })
|
73
|
-
|
74
|
-
return () => removeSubscription(config)
|
75
|
-
}
|
76
|
-
|
77
|
-
function removeSubscription(config) {
|
78
|
-
subscription.send({ removeSubscription: config })
|
79
|
-
const md5Hash = md5(JSON.stringify(config))
|
80
|
-
_.map(config, (v, model) => {
|
81
|
-
delete payloadHandlers[`${model}:${md5Hash}`]
|
82
|
-
})
|
83
|
-
}
|
84
|
-
|
85
|
-
const actions = createActions(schema, store, restClient, extraActions)
|
86
|
-
const eager = makeEager(schema)
|
87
|
-
|
88
|
-
console.log({ actions })
|
4
|
+
import JasonContext from './JasonContext'
|
89
5
|
|
90
|
-
|
91
|
-
|
92
|
-
subscribe: (config) => createSubscription(config),
|
93
|
-
eager
|
94
|
-
})
|
95
|
-
setStore(store)
|
96
|
-
})
|
97
|
-
}, [])
|
6
|
+
const JasonProvider = ({ reducers, middleware, extraActions, children }: { reducers?: any, middleware?: any, extraActions?: any, children?: React.FC }) => {
|
7
|
+
const [store, value] = useJason({ reducers, middleware, extraActions })
|
98
8
|
|
99
|
-
if(!(store && value
|
9
|
+
if(!(store && value)) return <div /> // Wait for async fetch of schema to complete
|
100
10
|
|
101
11
|
return <Provider store={store}>
|
102
12
|
<JasonContext.Provider value={value}>{ children }</JasonContext.Provider>
|
data/client/src/actionFactory.ts
CHANGED
@@ -24,7 +24,7 @@ export default (dis, store, entity, { extraActions = {}, hasPriority = false } =
|
|
24
24
|
return dis({ type: `${pluralize(entity)}/remove`, payload: id })
|
25
25
|
}
|
26
26
|
|
27
|
-
const extraActionsResolved = _.mapValues(extraActions, v => v(dis, store, entity))
|
27
|
+
const extraActionsResolved = extraActions ? _.mapValues(extraActions, v => v(dis, store, entity)) : {}
|
28
28
|
|
29
29
|
if (hasPriority) {
|
30
30
|
return { add, upsert, setAll, remove, movePriority, ...extraActionsResolved }
|
data/client/src/createActions.ts
CHANGED
@@ -1,39 +1,8 @@
|
|
1
1
|
import actionFactory from './actionFactory'
|
2
2
|
import pluralize from 'pluralize'
|
3
3
|
import _ from 'lodash'
|
4
|
-
import { v4 as uuidv4 } from 'uuid'
|
5
|
-
|
6
|
-
function enrich(type, payload) {
|
7
|
-
if (type.split('/')[1] === 'upsert' && !(type.split('/')[0] === 'session')) {
|
8
|
-
if (!payload.id) {
|
9
|
-
return { ...payload, id: uuidv4() }
|
10
|
-
}
|
11
|
-
}
|
12
|
-
return payload
|
13
|
-
}
|
14
|
-
|
15
|
-
function makeOptDis(schema, dispatch, restClient) {
|
16
|
-
const plurals = _.keys(schema).map(k => pluralize(k))
|
17
|
-
|
18
|
-
return function (action) {
|
19
|
-
const { type, payload } = action
|
20
|
-
const data = enrich(type, payload)
|
21
|
-
|
22
|
-
dispatch(action)
|
23
|
-
|
24
|
-
if (plurals.indexOf(type.split('/')[0]) > -1) {
|
25
|
-
return restClient.post('/jason/api/action', { type, payload: data } )
|
26
|
-
.catch(e => {
|
27
|
-
dispatch({ type: 'upsertLocalUi', data: { error: JSON.stringify(e) } })
|
28
|
-
})
|
29
|
-
}
|
30
|
-
}
|
31
|
-
}
|
32
|
-
|
33
|
-
function createActions(schema, store, restClient, extraActions) {
|
34
|
-
const dis = store.dispatch;
|
35
|
-
const optDis = makeOptDis(schema, dis, restClient)
|
36
4
|
|
5
|
+
function createActions(schema, store, restClient, optDis, extraActions) {
|
37
6
|
const actions = _.fromPairs(_.map(schema, (config, model: string) => {
|
38
7
|
if (config.priorityScope) {
|
39
8
|
return [pluralize(model), actionFactory(optDis, store, model, { hasPriority: true })]
|
@@ -42,7 +11,7 @@ function createActions(schema, store, restClient, extraActions) {
|
|
42
11
|
}
|
43
12
|
}))
|
44
13
|
|
45
|
-
const extraActionsResolved = extraActions(optDis, store, restClient)
|
14
|
+
const extraActionsResolved = extraActions ? extraActions(optDis, store, restClient, actions) : {}
|
46
15
|
|
47
16
|
return _.merge(actions, extraActionsResolved)
|
48
17
|
}
|
@@ -2,8 +2,8 @@ import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'
|
|
2
2
|
import pluralize from 'pluralize'
|
3
3
|
import _ from 'lodash'
|
4
4
|
|
5
|
-
function generateSlices(
|
6
|
-
const sliceNames =
|
5
|
+
function generateSlices(models) {
|
6
|
+
const sliceNames = models.map(k => pluralize(k))
|
7
7
|
const adapter = createEntityAdapter()
|
8
8
|
|
9
9
|
return _.fromPairs(_.map(sliceNames, name => {
|
@@ -16,6 +16,7 @@ function generateSlices(schema) {
|
|
16
16
|
add: adapter.addOne,
|
17
17
|
setAll: adapter.setAll,
|
18
18
|
remove: adapter.removeOne,
|
19
|
+
removeMany: adapter.removeMany,
|
19
20
|
movePriority: (s, { payload: { id, priority, parentFilter } }) => {
|
20
21
|
// Get IDs and insert our item at the new index
|
21
22
|
var affectedIds = _.orderBy(_.filter(_.values(s.entities), parentFilter).filter(e => e.id !== id), 'priority').map(e => e.id)
|
@@ -29,6 +30,59 @@ function generateSlices(schema) {
|
|
29
30
|
}))
|
30
31
|
}
|
31
32
|
|
33
|
+
function generateJasonSlices(models) {
|
34
|
+
const initialState = _.fromPairs(_.map(models, (model_name) => {
|
35
|
+
return [model_name, {}]
|
36
|
+
}))
|
37
|
+
|
38
|
+
const modelSliceReducer = createSlice({
|
39
|
+
name: 'jasonModels',
|
40
|
+
initialState,
|
41
|
+
reducers: {
|
42
|
+
setSubscriptionIds(s,a) {
|
43
|
+
const { payload } = a
|
44
|
+
const { subscriptionId, model, ids } = payload
|
45
|
+
console.log({ initialState })
|
46
|
+
s[model][subscriptionId] = ids
|
47
|
+
},
|
48
|
+
addSubscriptionId(s,a) {
|
49
|
+
const { payload } = a
|
50
|
+
const { subscriptionId, model, id } = payload
|
51
|
+
s[model][subscriptionId] = _.union(s[model][subscriptionId] || [], [id])
|
52
|
+
},
|
53
|
+
removeSubscriptionId(s,a) {
|
54
|
+
const { payload } = a
|
55
|
+
const { subscriptionId, model, id } = payload
|
56
|
+
s[model][subscriptionId] = _.remove(s[model][subscriptionId] || [], id)
|
57
|
+
},
|
58
|
+
removeSubscription(s, a) {
|
59
|
+
const { payload: { subscriptionId } } = a
|
60
|
+
_.map(models, model => {
|
61
|
+
delete s[model][subscriptionId]
|
62
|
+
})
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}).reducer
|
66
|
+
|
67
|
+
const jasonSliceReducer = createSlice({
|
68
|
+
name: 'jason',
|
69
|
+
initialState: {
|
70
|
+
connected: false,
|
71
|
+
queueSize: 0
|
72
|
+
},
|
73
|
+
reducers: {
|
74
|
+
upsert: (s,a) => ({ ...s, ...a.payload })
|
75
|
+
}
|
76
|
+
}).reducer
|
77
|
+
|
78
|
+
return { jason: jasonSliceReducer, jasonModels: modelSliceReducer }
|
79
|
+
}
|
80
|
+
|
32
81
|
export default function createJasonReducers(schema) {
|
33
|
-
|
82
|
+
const models = _.keys(schema)
|
83
|
+
|
84
|
+
return {
|
85
|
+
...generateSlices(models),
|
86
|
+
...generateJasonSlices(models)
|
87
|
+
}
|
34
88
|
}
|