jason-rails 0.4.1 → 0.5.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile.lock +152 -2
  4. data/app/controllers/jason/api_controller.rb +1 -1
  5. data/client/lib/JasonContext.d.ts +6 -1
  6. data/client/lib/JasonProvider.d.ts +2 -2
  7. data/client/lib/JasonProvider.js +5 -124
  8. data/client/lib/createJasonReducers.js +41 -3
  9. data/client/lib/createOptDis.js +0 -2
  10. data/client/lib/createPayloadHandler.d.ts +6 -1
  11. data/client/lib/createPayloadHandler.js +42 -54
  12. data/client/lib/createServerActionQueue.d.ts +10 -0
  13. data/client/lib/createServerActionQueue.js +48 -0
  14. data/client/lib/createServerActionQueue.test.d.ts +1 -0
  15. data/client/lib/createServerActionQueue.test.js +37 -0
  16. data/client/lib/index.d.ts +3 -2
  17. data/client/lib/pruneIdsMiddleware.d.ts +2 -0
  18. data/client/lib/pruneIdsMiddleware.js +26 -0
  19. data/client/lib/restClient.d.ts +2 -0
  20. data/client/lib/restClient.js +17 -0
  21. data/client/lib/useJason.d.ts +5 -0
  22. data/client/lib/useJason.js +99 -0
  23. data/client/lib/useJason.test.d.ts +1 -0
  24. data/client/lib/useJason.test.js +79 -0
  25. data/client/lib/useSub.js +1 -0
  26. data/client/package.json +4 -3
  27. data/client/src/JasonProvider.tsx +5 -123
  28. data/client/src/createJasonReducers.ts +49 -3
  29. data/client/src/createOptDis.ts +0 -2
  30. data/client/src/createPayloadHandler.ts +47 -63
  31. data/client/src/createServerActionQueue.test.ts +42 -0
  32. data/client/src/createServerActionQueue.ts +47 -0
  33. data/client/src/pruneIdsMiddleware.ts +24 -0
  34. data/client/src/restClient.ts +13 -0
  35. data/client/src/useJason.test.ts +81 -0
  36. data/client/src/useJason.ts +115 -0
  37. data/client/src/useSub.ts +1 -0
  38. data/client/yarn.lock +59 -3
  39. data/jason-rails.gemspec +4 -0
  40. data/lib/jason.rb +12 -0
  41. data/lib/jason/api_model.rb +2 -12
  42. data/lib/jason/channel.rb +43 -21
  43. data/lib/jason/lua_generator.rb +49 -0
  44. data/lib/jason/publisher.rb +76 -35
  45. data/lib/jason/publisher_old.rb +112 -0
  46. data/lib/jason/subscription.rb +322 -99
  47. data/lib/jason/subscription_old.rb +171 -0
  48. data/lib/jason/version.rb +1 -1
  49. metadata +67 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66c465280dc28e42bdd02b400ef514408a4839df70240aad2d660fc7b7dc1ed2
4
- data.tar.gz: 0ae40af767936631bc8ab174682073a2ad860578ad363bdab7b7080116be2e34
3
+ metadata.gz: 6e6d1d960c2bb555ccaa555a17de7730ce90c35aa57594256bcb73bc96dade09
4
+ data.tar.gz: b096c733bd15195ac383097a8138507915caa5fba29d9099325755972a64fe2a
5
5
  SHA512:
6
- metadata.gz: 6e27560fdd5953ef4a86cc371577b1e8e4f88d3e8f9099abd3f6fd41ad234071a348f67236c24106345acb700ffc16ed99d0036811e8d2bb29914462d89577cb
7
- data.tar.gz: 8deefbe678cebd020e5439ed3264f5b016d3c066d1d24baf3c9146ee32cd8404a6ef2c6e9bda350f276f4d1775ec3a50a9c5570960228c1f253b7adaae45f7ed
6
+ metadata.gz: 4ce914a0e658fe97051ff6a64a48a29bccde202ad3fa60bc3aadc287f4a50a4f169d647b66f456e3147da8551b5b321185caa05c9790c964202d5275b20a8732
7
+ data.tar.gz: ebde6f3323cb516915e3ddea2f747595a864df2cac9f52e05c67b1320c46f20693c479d7b39b11f391638dc9e5027b92e86ad65089282c6c190b6ba4982b3de3
@@ -0,0 +1 @@
1
+ 2.7.2
@@ -1,13 +1,137 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jason-rails (0.3.0)
4
+ jason-rails (0.4.1)
5
+ connection_pool (>= 2.2.3)
6
+ jsondiff
7
+ rails (>= 5)
8
+ redis (>= 4)
5
9
 
6
10
  GEM
7
11
  remote: https://rubygems.org/
8
12
  specs:
13
+ actioncable (6.1.1)
14
+ actionpack (= 6.1.1)
15
+ activesupport (= 6.1.1)
16
+ nio4r (~> 2.0)
17
+ websocket-driver (>= 0.6.1)
18
+ actionmailbox (6.1.1)
19
+ actionpack (= 6.1.1)
20
+ activejob (= 6.1.1)
21
+ activerecord (= 6.1.1)
22
+ activestorage (= 6.1.1)
23
+ activesupport (= 6.1.1)
24
+ mail (>= 2.7.1)
25
+ actionmailer (6.1.1)
26
+ actionpack (= 6.1.1)
27
+ actionview (= 6.1.1)
28
+ activejob (= 6.1.1)
29
+ activesupport (= 6.1.1)
30
+ mail (~> 2.5, >= 2.5.4)
31
+ rails-dom-testing (~> 2.0)
32
+ actionpack (6.1.1)
33
+ actionview (= 6.1.1)
34
+ activesupport (= 6.1.1)
35
+ rack (~> 2.0, >= 2.0.9)
36
+ rack-test (>= 0.6.3)
37
+ rails-dom-testing (~> 2.0)
38
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
39
+ actiontext (6.1.1)
40
+ actionpack (= 6.1.1)
41
+ activerecord (= 6.1.1)
42
+ activestorage (= 6.1.1)
43
+ activesupport (= 6.1.1)
44
+ nokogiri (>= 1.8.5)
45
+ actionview (6.1.1)
46
+ activesupport (= 6.1.1)
47
+ builder (~> 3.1)
48
+ erubi (~> 1.4)
49
+ rails-dom-testing (~> 2.0)
50
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
51
+ activejob (6.1.1)
52
+ activesupport (= 6.1.1)
53
+ globalid (>= 0.3.6)
54
+ activemodel (6.1.1)
55
+ activesupport (= 6.1.1)
56
+ activerecord (6.1.1)
57
+ activemodel (= 6.1.1)
58
+ activesupport (= 6.1.1)
59
+ activestorage (6.1.1)
60
+ actionpack (= 6.1.1)
61
+ activejob (= 6.1.1)
62
+ activerecord (= 6.1.1)
63
+ activesupport (= 6.1.1)
64
+ marcel (~> 0.3.1)
65
+ mimemagic (~> 0.3.2)
66
+ activesupport (6.1.1)
67
+ concurrent-ruby (~> 1.0, >= 1.0.2)
68
+ i18n (>= 1.6, < 2)
69
+ minitest (>= 5.1)
70
+ tzinfo (~> 2.0)
71
+ zeitwerk (~> 2.3)
72
+ builder (3.2.4)
73
+ coderay (1.1.3)
74
+ concurrent-ruby (1.1.7)
75
+ connection_pool (2.2.3)
76
+ crass (1.0.6)
9
77
  diff-lcs (1.4.4)
78
+ erubi (1.10.0)
79
+ globalid (0.4.2)
80
+ activesupport (>= 4.2.0)
81
+ i18n (1.8.7)
82
+ concurrent-ruby (~> 1.0)
83
+ jsondiff (0.0.5)
84
+ loofah (2.8.0)
85
+ crass (~> 1.0.2)
86
+ nokogiri (>= 1.5.9)
87
+ mail (2.7.1)
88
+ mini_mime (>= 0.1.1)
89
+ marcel (0.3.3)
90
+ mimemagic (~> 0.3.2)
91
+ method_source (1.0.0)
92
+ mimemagic (0.3.5)
93
+ mini_mime (1.0.2)
94
+ mini_portile2 (2.5.0)
95
+ minitest (5.14.3)
96
+ nio4r (2.5.4)
97
+ nokogiri (1.11.1)
98
+ mini_portile2 (~> 2.5.0)
99
+ racc (~> 1.4)
100
+ pry (0.13.1)
101
+ coderay (~> 1.1)
102
+ method_source (~> 1.0)
103
+ racc (1.5.2)
104
+ rack (2.2.3)
105
+ rack-test (1.1.0)
106
+ rack (>= 1.0, < 3)
107
+ rails (6.1.1)
108
+ actioncable (= 6.1.1)
109
+ actionmailbox (= 6.1.1)
110
+ actionmailer (= 6.1.1)
111
+ actionpack (= 6.1.1)
112
+ actiontext (= 6.1.1)
113
+ actionview (= 6.1.1)
114
+ activejob (= 6.1.1)
115
+ activemodel (= 6.1.1)
116
+ activerecord (= 6.1.1)
117
+ activestorage (= 6.1.1)
118
+ activesupport (= 6.1.1)
119
+ bundler (>= 1.15.0)
120
+ railties (= 6.1.1)
121
+ sprockets-rails (>= 2.0.0)
122
+ rails-dom-testing (2.0.3)
123
+ activesupport (>= 4.2.0)
124
+ nokogiri (>= 1.6)
125
+ rails-html-sanitizer (1.3.0)
126
+ loofah (~> 2.3)
127
+ railties (6.1.1)
128
+ actionpack (= 6.1.1)
129
+ activesupport (= 6.1.1)
130
+ method_source
131
+ rake (>= 0.8.7)
132
+ thor (~> 1.0)
10
133
  rake (12.3.3)
134
+ redis (4.2.5)
11
135
  rspec (3.10.0)
12
136
  rspec-core (~> 3.10.0)
13
137
  rspec-expectations (~> 3.10.0)
@@ -20,15 +144,41 @@ GEM
20
144
  rspec-mocks (3.10.0)
21
145
  diff-lcs (>= 1.2.0, < 2.0)
22
146
  rspec-support (~> 3.10.0)
147
+ rspec-rails (4.0.2)
148
+ actionpack (>= 4.2)
149
+ activesupport (>= 4.2)
150
+ railties (>= 4.2)
151
+ rspec-core (~> 3.10)
152
+ rspec-expectations (~> 3.10)
153
+ rspec-mocks (~> 3.10)
154
+ rspec-support (~> 3.10)
23
155
  rspec-support (3.10.0)
156
+ sprockets (4.0.2)
157
+ concurrent-ruby (~> 1.0)
158
+ rack (> 1, < 3)
159
+ sprockets-rails (3.2.2)
160
+ actionpack (>= 4.0)
161
+ activesupport (>= 4.0)
162
+ sprockets (>= 3.0.0)
163
+ sqlite3 (1.4.2)
164
+ thor (1.0.1)
165
+ tzinfo (2.0.4)
166
+ concurrent-ruby (~> 1.0)
167
+ websocket-driver (0.7.3)
168
+ websocket-extensions (>= 0.1.0)
169
+ websocket-extensions (0.1.5)
170
+ zeitwerk (2.4.2)
24
171
 
25
172
  PLATFORMS
26
173
  ruby
27
174
 
28
175
  DEPENDENCIES
29
176
  jason-rails!
177
+ pry
30
178
  rake (~> 12.0)
31
179
  rspec (~> 3.0)
180
+ rspec-rails
181
+ sqlite3
32
182
 
33
183
  BUNDLED WITH
34
- 1.17.3
184
+ 2.1.4
@@ -1,6 +1,6 @@
1
1
  class Jason::ApiController < ::ApplicationController
2
2
  def schema
3
- render json: JASON_API_MODEL.to_json
3
+ render json: Jason.schema
4
4
  end
5
5
 
6
6
  def action
@@ -1,2 +1,7 @@
1
- declare const context: any;
1
+ /// <reference types="react" />
2
+ declare const context: import("react").Context<{
3
+ actions: any;
4
+ subscribe: null;
5
+ eager: null;
6
+ }>;
2
7
  export default context;
@@ -3,6 +3,6 @@ declare const JasonProvider: ({ reducers, middleware, extraActions, children }:
3
3
  reducers?: any;
4
4
  middleware?: any;
5
5
  extraActions?: any;
6
- children?: any;
7
- }) => any;
6
+ children?: React.FC<{}> | undefined;
7
+ }) => JSX.Element;
8
8
  export default JasonProvider;
@@ -1,134 +1,15 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
- Object.defineProperty(o, "default", { enumerable: true, value: v });
11
- }) : function(o, v) {
12
- o["default"] = v;
13
- });
14
- var __importStar = (this && this.__importStar) || function (mod) {
15
- if (mod && mod.__esModule) return mod;
16
- var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
- __setModuleDefault(result, mod);
19
- return result;
20
- };
21
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
22
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
4
  };
24
5
  Object.defineProperty(exports, "__esModule", { value: true });
25
- const createActions_1 = __importDefault(require("./createActions"));
26
- const actioncable_1 = require("@rails/actioncable");
27
- const JasonContext_1 = __importDefault(require("./JasonContext"));
28
- const axios_1 = __importDefault(require("axios"));
29
- const axios_case_converter_1 = __importDefault(require("axios-case-converter"));
6
+ const react_1 = __importDefault(require("react"));
7
+ const useJason_1 = __importDefault(require("./useJason"));
30
8
  const react_redux_1 = require("react-redux");
31
- const toolkit_1 = require("@reduxjs/toolkit");
32
- const createJasonReducers_1 = __importDefault(require("./createJasonReducers"));
33
- const createPayloadHandler_1 = __importDefault(require("./createPayloadHandler"));
34
- const createOptDis_1 = __importDefault(require("./createOptDis"));
35
- const makeEager_1 = __importDefault(require("./makeEager"));
36
- const humps_1 = require("humps");
37
- const blueimp_md5_1 = __importDefault(require("blueimp-md5"));
38
- const lodash_1 = __importDefault(require("lodash"));
39
- const react_1 = __importStar(require("react"));
40
- const uuid_1 = require("uuid");
9
+ const JasonContext_1 = __importDefault(require("./JasonContext"));
41
10
  const JasonProvider = ({ reducers, middleware, extraActions, children }) => {
42
- const [store, setStore] = react_1.useState(null);
43
- const [value, setValue] = react_1.useState(null);
44
- const [connected, setConnected] = react_1.useState(false);
45
- const csrfToken = document.querySelector("meta[name=csrf-token]").content;
46
- axios_1.default.defaults.headers.common['X-CSRF-Token'] = csrfToken;
47
- const restClient = axios_case_converter_1.default(axios_1.default.create(), {
48
- preservedKeys: (key) => {
49
- return uuid_1.validate(key);
50
- }
51
- });
52
- react_1.useEffect(() => {
53
- restClient.get('/jason/api/schema')
54
- .then(({ data: snakey_schema }) => {
55
- const schema = humps_1.camelizeKeys(snakey_schema);
56
- const serverActionQueue = function () {
57
- const queue = [];
58
- let inFlight = false;
59
- return {
60
- addItem: (item) => queue.push(item),
61
- getItem: () => {
62
- if (inFlight)
63
- return false;
64
- const item = queue.shift();
65
- if (item) {
66
- inFlight = true;
67
- return item;
68
- }
69
- return false;
70
- },
71
- itemProcessed: () => inFlight = false,
72
- fullySynced: () => queue.length === 0 && !inFlight,
73
- getData: () => ({ queue, inFlight })
74
- };
75
- }();
76
- const consumer = actioncable_1.createConsumer();
77
- const allReducers = Object.assign(Object.assign({}, reducers), createJasonReducers_1.default(schema));
78
- console.log({ schema, allReducers });
79
- const store = toolkit_1.configureStore({ reducer: allReducers, middleware });
80
- let payloadHandlers = {};
81
- function handlePayload(payload) {
82
- const { model, md5Hash } = payload;
83
- console.log({ md5Hash, fn: `${model}:${md5Hash}`, payloadHandlers, model: lodash_1.default.camelCase(model), payload });
84
- const handler = payloadHandlers[`${lodash_1.default.camelCase(model)}:${md5Hash}`];
85
- if (handler) {
86
- handler(Object.assign(Object.assign({}, payload), { model: lodash_1.default.camelCase(model) }));
87
- }
88
- }
89
- const subscription = (consumer.subscriptions.create({
90
- channel: 'Jason::Channel'
91
- }, {
92
- connected: () => {
93
- setConnected(true);
94
- },
95
- received: payload => {
96
- console.log("Payload received", payload);
97
- handlePayload(payload);
98
- },
99
- disconnected: () => console.warn('Disconnected from ActionCable')
100
- }));
101
- console.log('sending message');
102
- subscription.send({ message: 'test' });
103
- function createSubscription(config) {
104
- const md5Hash = blueimp_md5_1.default(JSON.stringify(config));
105
- console.log('Subscribe with', config, md5Hash);
106
- lodash_1.default.map(config, (v, model) => {
107
- payloadHandlers[`${model}:${md5Hash}`] = createPayloadHandler_1.default(store.dispatch, serverActionQueue, subscription, model, schema[model]);
108
- });
109
- subscription.send({ createSubscription: config });
110
- return () => removeSubscription(config);
111
- }
112
- function removeSubscription(config) {
113
- subscription.send({ removeSubscription: config });
114
- const md5Hash = blueimp_md5_1.default(JSON.stringify(config));
115
- lodash_1.default.map(config, (v, model) => {
116
- delete payloadHandlers[`${model}:${md5Hash}`];
117
- });
118
- }
119
- const optDis = createOptDis_1.default(schema, store.dispatch, restClient, serverActionQueue);
120
- const actions = createActions_1.default(schema, store, restClient, optDis, extraActions);
121
- const eager = makeEager_1.default(schema);
122
- console.log({ actions });
123
- setValue({
124
- actions: actions,
125
- subscribe: (config) => createSubscription(config),
126
- eager
127
- });
128
- setStore(store);
129
- });
130
- }, []);
131
- if (!(store && value && connected))
11
+ const [store, value, connected] = useJason_1.default({ reducers, middleware, extraActions });
12
+ if (!(store && value))
132
13
  return react_1.default.createElement("div", null); // Wait for async fetch of schema to complete
133
14
  return react_1.default.createElement(react_redux_1.Provider, { store: store },
134
15
  react_1.default.createElement(JasonContext_1.default.Provider, { value: value }, children));
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const toolkit_1 = require("@reduxjs/toolkit");
7
7
  const pluralize_1 = __importDefault(require("pluralize"));
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
- function generateSlices(schema) {
10
- const sliceNames = schema.map(k => pluralize_1.default(k));
9
+ function generateSlices(models) {
10
+ const sliceNames = models.map(k => pluralize_1.default(k));
11
11
  const adapter = toolkit_1.createEntityAdapter();
12
12
  return lodash_1.default.fromPairs(lodash_1.default.map(sliceNames, name => {
13
13
  return [name, toolkit_1.createSlice({
@@ -31,7 +31,45 @@ function generateSlices(schema) {
31
31
  }).reducer];
32
32
  }));
33
33
  }
34
+ function generateJasonSlices(models) {
35
+ const initialState = lodash_1.default.fromPairs(lodash_1.default.map(models, (model_name) => {
36
+ return [model_name, {}];
37
+ }));
38
+ const modelSliceReducer = toolkit_1.createSlice({
39
+ name: 'jasonModels',
40
+ initialState,
41
+ reducers: {
42
+ setSubscriptionIds(s, a) {
43
+ const { payload } = a;
44
+ const { subscriptionId, model, ids } = payload;
45
+ s[model][subscriptionId] = ids;
46
+ },
47
+ addSubscriptionId(s, a) {
48
+ const { payload } = a;
49
+ const { subscriptionId, model, id } = payload;
50
+ s[model][subscriptionId] = lodash_1.default.union(s[model][subscriptionId] || [], [id]);
51
+ },
52
+ removeSubscriptionId(s, a) {
53
+ const { payload } = a;
54
+ const { subscriptionId, model, id } = payload;
55
+ s[model][subscriptionId] = lodash_1.default.remove(s[model][subscriptionId] || [], id);
56
+ }
57
+ }
58
+ }).reducer;
59
+ const jasonSliceReducer = toolkit_1.createSlice({
60
+ name: 'jason',
61
+ initialState: {
62
+ connected: false,
63
+ queueSize: 0
64
+ },
65
+ reducers: {
66
+ upsert: (s, a) => (Object.assign(Object.assign({}, s), a.payload))
67
+ }
68
+ }).reducer;
69
+ return { jason: jasonSliceReducer, jasonModels: modelSliceReducer };
70
+ }
34
71
  function createJasonReducers(schema) {
35
- return generateSlices(lodash_1.default.keys(schema));
72
+ const models = lodash_1.default.keys(schema);
73
+ return Object.assign(Object.assign({}, generateSlices(models)), generateJasonSlices(models));
36
74
  }
37
75
  exports.default = createJasonReducers;
@@ -16,7 +16,6 @@ function enrich(type, payload) {
16
16
  }
17
17
  function createOptDis(schema, dispatch, restClient, serverActionQueue) {
18
18
  const plurals = lodash_1.default.keys(schema).map(k => pluralize_1.default(k));
19
- let inFlight = false;
20
19
  function enqueueServerAction(action) {
21
20
  serverActionQueue.addItem(action);
22
21
  }
@@ -24,7 +23,6 @@ function createOptDis(schema, dispatch, restClient, serverActionQueue) {
24
23
  const action = serverActionQueue.getItem();
25
24
  if (!action)
26
25
  return;
27
- inFlight = true;
28
26
  restClient.post('/jason/api/action', action)
29
27
  .then(serverActionQueue.itemProcessed)
30
28
  .catch(e => {
@@ -1 +1,6 @@
1
- export default function createPayloadHandler(dispatch: any, serverActionQueue: any, subscription: any, model: any, config: any): (data: any) => null | undefined;
1
+ export default function createPayloadHandler({ dispatch, serverActionQueue, subscription, config }: {
2
+ dispatch: any;
3
+ serverActionQueue: any;
4
+ subscription: any;
5
+ config: any;
6
+ }): (data: any) => void;
@@ -3,7 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const jsonpatch_1 = require("jsonpatch");
7
6
  const deepCamelizeKeys_1 = __importDefault(require("./deepCamelizeKeys"));
8
7
  const pluralize_1 = __importDefault(require("pluralize"));
9
8
  const lodash_1 = __importDefault(require("lodash"));
@@ -12,93 +11,82 @@ function diffSeconds(dt2, dt1) {
12
11
  var diff = (dt2.getTime() - dt1.getTime()) / 1000;
13
12
  return Math.abs(Math.round(diff));
14
13
  }
15
- function createPayloadHandler(dispatch, serverActionQueue, subscription, model, config) {
16
- console.log({ model, config });
17
- let payload = [];
18
- let previousPayload = [];
19
- let idx = 0;
14
+ function createPayloadHandler({ dispatch, serverActionQueue, subscription, config }) {
15
+ const subscriptionId = uuid_1.v4();
16
+ let idx = {};
20
17
  let patchQueue = {};
21
18
  let lastCheckAt = new Date();
22
19
  let updateDeadline = null;
23
20
  let checkInterval;
24
21
  function getPayload() {
25
- console.log({ getPayload: model, subscription });
26
- subscription.send({ getPayload: { model, config } });
22
+ setTimeout(() => subscription.send({ getPayload: config }), 1000);
27
23
  }
28
24
  function camelizeKeys(item) {
29
25
  return deepCamelizeKeys_1.default(item, key => uuid_1.validate(key));
30
26
  }
31
27
  const tGetPayload = lodash_1.default.throttle(getPayload, 10000);
32
- function dispatchPayload() {
33
- // We want to avoid updates from server overwriting changes to local state, so if there is a queue then wait.
34
- if (!serverActionQueue.fullySynced()) {
35
- console.log(serverActionQueue.getData());
36
- setTimeout(dispatchPayload, 100);
37
- return;
38
- }
39
- const includeModels = (config.includeModels || []).map(m => lodash_1.default.camelCase(m));
40
- console.log("Dispatching", { payload, includeModels });
41
- includeModels.forEach(m => {
42
- const subPayload = lodash_1.default.flatten(lodash_1.default.compact(camelizeKeys(payload).map(instance => instance[m])));
43
- const previousSubPayload = lodash_1.default.flatten(lodash_1.default.compact(camelizeKeys(previousPayload).map(instance => instance[m])));
44
- // Find IDs that were in the payload but are no longer
45
- const idsToRemove = lodash_1.default.difference(previousSubPayload.map(i => i.id), subPayload.map(i => i.id));
46
- dispatch({ type: `${pluralize_1.default(m)}/upsertMany`, payload: subPayload });
47
- dispatch({ type: `${pluralize_1.default(m)}/removeMany`, payload: idsToRemove });
48
- });
49
- const idsToRemove = lodash_1.default.difference(previousPayload.map(i => i.id), payload.map(i => i.id));
50
- dispatch({ type: `${pluralize_1.default(model)}/upsertMany`, payload: camelizeKeys(payload) });
51
- dispatch({ type: `${pluralize_1.default(model)}/removeMany`, payload: idsToRemove });
52
- previousPayload = payload;
53
- }
54
- function processQueue() {
55
- console.log({ idx, patchQueue });
28
+ function processQueue(model) {
29
+ console.debug("processQueue", model, idx[model], patchQueue[model]);
56
30
  lastCheckAt = new Date();
57
- if (patchQueue[idx]) {
58
- payload = jsonpatch_1.apply_patch(payload, patchQueue[idx]);
59
- if (patchQueue[idx]) {
60
- dispatchPayload();
31
+ if (patchQueue[model][idx[model]]) {
32
+ if (!serverActionQueue.fullySynced()) {
33
+ console.debug(serverActionQueue.getData());
34
+ setTimeout(() => processQueue(model), 100);
35
+ return;
36
+ }
37
+ const { payload, destroy, id, type } = patchQueue[model][idx[model]];
38
+ if (type === 'payload') {
39
+ dispatch({ type: `${pluralize_1.default(model)}/upsertMany`, payload });
40
+ const ids = payload.map(instance => instance.id);
41
+ dispatch({ type: `jasonModels/setSubscriptionIds`, payload: { model, subscriptionId, ids } });
42
+ }
43
+ else if (destroy) {
44
+ dispatch({ type: `${pluralize_1.default(model)}/remove`, payload: id });
45
+ dispatch({ type: `jasonModels/removeSubscriptionId`, payload: { model, subscriptionId, id } });
46
+ }
47
+ else {
48
+ dispatch({ type: `${pluralize_1.default(model)}/upsert`, payload });
49
+ dispatch({ type: `jasonModels/addSubscriptionId`, payload: { model, subscriptionId, id } });
61
50
  }
62
- delete patchQueue[idx];
63
- idx++;
51
+ delete patchQueue[model][idx[model]];
52
+ idx[model]++;
64
53
  updateDeadline = null;
65
- processQueue();
54
+ processQueue(model);
66
55
  // If there are updates in the queue that are ahead of the index, some have arrived out of order
67
56
  // Set a deadline for new updates before it declares the update missing and refetches.
68
57
  }
69
- else if (lodash_1.default.keys(patchQueue).length > 0 && !updateDeadline) {
58
+ else if (lodash_1.default.keys(patchQueue[model]).length > 0 && !updateDeadline) {
70
59
  var t = new Date();
71
60
  t.setSeconds(t.getSeconds() + 3);
72
61
  updateDeadline = t;
73
- setTimeout(processQueue, 3100);
62
+ setTimeout(() => processQueue(model), 3100);
74
63
  // If more than 10 updates in queue, or deadline has passed, restart
75
64
  }
76
- else if (lodash_1.default.keys(patchQueue).length > 10 || (updateDeadline && diffSeconds(updateDeadline, new Date()) < 0)) {
65
+ else if (lodash_1.default.keys(patchQueue[model]).length > 10 || (updateDeadline && diffSeconds(updateDeadline, new Date()) < 0)) {
77
66
  tGetPayload();
78
67
  updateDeadline = null;
79
68
  }
80
69
  }
81
70
  function handlePayload(data) {
82
- const { value, idx: newIdx, diff, latency, type } = data;
83
- console.log({ data });
71
+ const { idx: newIdx, model: snake_model, type } = data;
72
+ const model = lodash_1.default.camelCase(snake_model);
73
+ idx[model] = idx[model] || 0;
74
+ patchQueue[model] = patchQueue[model] || {};
84
75
  if (type === 'payload') {
85
- if (!value)
86
- return null;
87
- payload = value;
88
- dispatchPayload();
89
- idx = newIdx + 1;
76
+ idx[model] = newIdx;
90
77
  // Clear any old changes left in the queue
91
- patchQueue = lodash_1.default.pick(patchQueue, lodash_1.default.keys(patchQueue).filter(k => k > newIdx + 1));
92
- return;
78
+ patchQueue[model] = lodash_1.default.pick(patchQueue[model], lodash_1.default.keys(patchQueue[model]).filter(k => k > newIdx + 1));
93
79
  }
94
- patchQueue[newIdx] = diff;
95
- processQueue();
80
+ patchQueue[model][newIdx] = camelizeKeys(Object.assign(Object.assign({}, data), { model }));
81
+ console.debug("Added to queue", model, idx[model], camelizeKeys(Object.assign(Object.assign({}, data), { model })), serverActionQueue.getData());
82
+ processQueue(model);
96
83
  if (diffSeconds((new Date()), lastCheckAt) >= 3) {
97
84
  lastCheckAt = new Date();
98
- console.log('Interval lost. Pulling from server');
85
+ console.debug('Interval lost. Pulling from server');
99
86
  tGetPayload();
100
87
  }
101
88
  }
89
+ tGetPayload();
102
90
  return handlePayload;
103
91
  }
104
92
  exports.default = createPayloadHandler;