jason-rails 0.3.0 → 0.6.0
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.
- 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
|
}
|