jason-rails 0.6.8 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/Gemfile.lock +5 -3
  4. data/README.md +20 -14
  5. data/app/controllers/jason/jason_controller.rb +26 -4
  6. data/app/workers/jason/outbound_message_queue_worker.rb +1 -1
  7. data/client/lib/JasonProvider.d.ts +3 -1
  8. data/client/lib/JasonProvider.js +2 -2
  9. data/client/lib/addRelations.d.ts +1 -0
  10. data/client/lib/addRelations.js +39 -0
  11. data/client/lib/createJasonReducers.js +4 -2
  12. data/client/lib/createOptDis.d.ts +1 -1
  13. data/client/lib/createOptDis.js +9 -8
  14. data/client/lib/createServerActionQueue.d.ts +3 -2
  15. data/client/lib/createServerActionQueue.js +32 -6
  16. data/client/lib/createServerActionQueue.test.js +61 -6
  17. data/client/lib/createThenable.d.ts +1 -0
  18. data/client/lib/createThenable.js +5 -0
  19. data/client/lib/createTransportAdapter.d.ts +1 -1
  20. data/client/lib/createTransportAdapter.js +2 -2
  21. data/client/lib/index.d.ts +8 -1
  22. data/client/lib/index.js +3 -1
  23. data/client/lib/transportAdapters/actionCableAdapter.d.ts +1 -1
  24. data/client/lib/transportAdapters/actionCableAdapter.js +27 -6
  25. data/client/lib/transportAdapters/pusherAdapter.js +1 -1
  26. data/client/lib/useDraft.d.ts +1 -0
  27. data/client/lib/useDraft.js +13 -0
  28. data/client/lib/useEager.d.ts +1 -1
  29. data/client/lib/useEager.js +10 -5
  30. data/client/lib/useJason.d.ts +3 -1
  31. data/client/lib/useJason.js +4 -7
  32. data/client/package.json +1 -1
  33. data/client/src/JasonProvider.tsx +2 -2
  34. data/client/src/addRelations.ts +33 -0
  35. data/client/src/createJasonReducers.ts +4 -2
  36. data/client/src/createOptDis.ts +10 -8
  37. data/client/src/createServerActionQueue.test.ts +60 -6
  38. data/client/src/createServerActionQueue.ts +41 -6
  39. data/client/src/createTransportAdapter.ts +2 -2
  40. data/client/src/index.ts +2 -0
  41. data/client/src/transportAdapters/actionCableAdapter.ts +28 -7
  42. data/client/src/transportAdapters/pusherAdapter.ts +1 -2
  43. data/client/src/useDraft.ts +17 -0
  44. data/client/src/useEager.ts +9 -6
  45. data/client/src/useJason.ts +10 -7
  46. data/lib/jason.rb +9 -2
  47. data/lib/jason/api_model.rb +0 -4
  48. data/lib/jason/channel.rb +0 -7
  49. data/lib/jason/conditions_matcher.rb +88 -0
  50. data/lib/jason/consistency_checker.rb +65 -0
  51. data/lib/jason/graph_helper.rb +4 -0
  52. data/lib/jason/publisher.rb +36 -36
  53. data/lib/jason/subscription.rb +51 -15
  54. data/lib/jason/version.rb +1 -1
  55. metadata +12 -5
  56. data/client/src/makeEager.ts +0 -46
  57. data/lib/jason/publisher_old.rb +0 -112
  58. data/lib/jason/subscription_old.rb +0 -171
@@ -0,0 +1 @@
1
+ export default function createThenable(): void;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function createThenable() {
4
+ }
5
+ exports.default = createThenable;
@@ -1,4 +1,4 @@
1
- export default function createTransportAdapter(jasonConfig: any, handlePayload: any, dispatch: any, onConnect: any): {
1
+ export default function createTransportAdapter(jasonConfig: any, handlePayload: any, dispatch: any, onConnect: any, transportOptions: any): {
2
2
  getPayload: (config: any, options: any) => void;
3
3
  createSubscription: (config: any) => void;
4
4
  removeSubscription: (config: any) => void;
@@ -5,10 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const actionCableAdapter_1 = __importDefault(require("./transportAdapters/actionCableAdapter"));
7
7
  const pusherAdapter_1 = __importDefault(require("./transportAdapters/pusherAdapter"));
8
- function createTransportAdapter(jasonConfig, handlePayload, dispatch, onConnect) {
8
+ function createTransportAdapter(jasonConfig, handlePayload, dispatch, onConnect, transportOptions) {
9
9
  const { transportService } = jasonConfig;
10
10
  if (transportService === 'action_cable') {
11
- return actionCableAdapter_1.default(jasonConfig, handlePayload, dispatch, onConnect);
11
+ return actionCableAdapter_1.default(jasonConfig, handlePayload, dispatch, onConnect, transportOptions);
12
12
  }
13
13
  else if (transportService === 'pusher') {
14
14
  return pusherAdapter_1.default(jasonConfig, handlePayload, dispatch);
@@ -2,10 +2,17 @@
2
2
  import _useAct from './useAct';
3
3
  import _useSub from './useSub';
4
4
  import _useEager from './useEager';
5
- export declare const JasonProvider: ({ reducers, middleware, extraActions, children }: {
5
+ export declare const JasonContext: import("react").Context<{
6
+ actions: any;
7
+ subscribe: null;
8
+ eager: (entity: any, id: any, relations: any) => void;
9
+ }>;
10
+ export declare const JasonProvider: ({ reducers, middleware, enhancers, extraActions, transportOptions, children }: {
6
11
  reducers?: any;
7
12
  middleware?: any;
13
+ enhancers?: any;
8
14
  extraActions?: any;
15
+ transportOptions?: any;
9
16
  children?: import("react").FC<{}> | undefined;
10
17
  }) => JSX.Element;
11
18
  export declare const useAct: typeof _useAct;
data/client/lib/index.js CHANGED
@@ -3,11 +3,13 @@ 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
- exports.useEager = exports.useSub = exports.useAct = exports.JasonProvider = void 0;
6
+ exports.useEager = exports.useSub = exports.useAct = exports.JasonProvider = exports.JasonContext = void 0;
7
+ const JasonContext_1 = __importDefault(require("./JasonContext"));
7
8
  const JasonProvider_1 = __importDefault(require("./JasonProvider"));
8
9
  const useAct_1 = __importDefault(require("./useAct"));
9
10
  const useSub_1 = __importDefault(require("./useSub"));
10
11
  const useEager_1 = __importDefault(require("./useEager"));
12
+ exports.JasonContext = JasonContext_1.default;
11
13
  exports.JasonProvider = JasonProvider_1.default;
12
14
  exports.useAct = useAct_1.default;
13
15
  exports.useSub = useSub_1.default;
@@ -1,4 +1,4 @@
1
- export default function actionCableAdapter(jasonConfig: any, handlePayload: any, dispatch: any, onConnected: any): {
1
+ export default function actionCableAdapter(jasonConfig: any, handlePayload: any, dispatch: any, onConnected: any, transportOptions: any): {
2
2
  getPayload: (config: any, options: any) => void;
3
3
  createSubscription: (config: any) => void;
4
4
  removeSubscription: (config: any) => void;
@@ -1,8 +1,16 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  const actioncable_1 = require("@rails/actioncable");
4
- function actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnected) {
5
- const consumer = actioncable_1.createConsumer();
7
+ const restClient_1 = __importDefault(require("../restClient"));
8
+ const uuid_1 = require("uuid");
9
+ const lodash_1 = __importDefault(require("lodash"));
10
+ function actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnected, transportOptions) {
11
+ const consumerId = uuid_1.v4();
12
+ const { cableUrl } = transportOptions;
13
+ const consumer = cableUrl ? actioncable_1.createConsumer(cableUrl) : actioncable_1.createConsumer();
6
14
  const subscription = (consumer.subscriptions.create({
7
15
  channel: 'Jason::Channel'
8
16
  }, {
@@ -21,14 +29,27 @@ function actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnected) {
21
29
  console.warn('Disconnected from ActionCable');
22
30
  }
23
31
  }));
24
- function getPayload(config, options) {
25
- subscription.send(Object.assign({ getPayload: config }, options));
26
- }
27
32
  function createSubscription(config) {
28
33
  subscription.send({ createSubscription: config });
29
34
  }
30
35
  function removeSubscription(config) {
31
- subscription.send({ removeSubscription: config });
36
+ restClient_1.default.post('/jason/api/remove_subscription', { config, consumerId })
37
+ .catch(e => console.error(e));
38
+ }
39
+ function getPayload(config, options) {
40
+ restClient_1.default.post('/jason/api/get_payload', {
41
+ config,
42
+ options
43
+ })
44
+ .then(({ data }) => {
45
+ lodash_1.default.map(data, (payload, modelName) => {
46
+ handlePayload(payload);
47
+ });
48
+ })
49
+ .catch(e => console.error(e));
50
+ }
51
+ function fullChannelName(channelName) {
52
+ return channelName;
32
53
  }
33
54
  return { getPayload, createSubscription, removeSubscription };
34
55
  }
@@ -8,7 +8,7 @@ const restClient_1 = __importDefault(require("../restClient"));
8
8
  const uuid_1 = require("uuid");
9
9
  const lodash_1 = __importDefault(require("lodash"));
10
10
  function pusherAdapter(jasonConfig, handlePayload, dispatch) {
11
- let consumerId = uuid_1.v4();
11
+ const consumerId = uuid_1.v4();
12
12
  const { pusherKey, pusherRegion, pusherChannelPrefix } = jasonConfig;
13
13
  const pusher = new pusher_js_1.default(pusherKey, {
14
14
  cluster: 'eu',
@@ -0,0 +1 @@
1
+ export default function useDraft(entity: any, id: any, relations?: never[]): void;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /* Can be called as
4
+ useDraft() => draft object for making updates
5
+ useDraft('entity', id) => returns [draft, object]
6
+ useDraft('entity', id, relations) => returns [draft, objectWithEmbeddedRelations]
7
+ */
8
+ function useDraft(entity, id, relations = []) {
9
+ // const entityDraft =`${entity}Draft`
10
+ // const object = { ...s[entityDraft].entities[String(id)] }
11
+ // return useSelector(s => addRelations(s, object, entity, relations, 'Draft'), _.isEqual)
12
+ }
13
+ exports.default = useDraft;
@@ -1 +1 @@
1
- export default function useEager(entity: any, id?: null, relations?: never[]): void;
1
+ export default function useEager(entity: string, id?: string, relations?: any): any;
@@ -3,10 +3,15 @@ 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 JasonContext_1 = __importDefault(require("./JasonContext"));
7
- const react_1 = require("react");
8
- function useEager(entity, id = null, relations = []) {
9
- const { eager } = react_1.useContext(JasonContext_1.default);
10
- return eager(entity, id, relations);
6
+ const lodash_1 = __importDefault(require("lodash"));
7
+ const react_redux_1 = require("react-redux");
8
+ const addRelations_1 = __importDefault(require("./addRelations"));
9
+ function useEager(entity, id = '', relations = []) {
10
+ if (id) {
11
+ return react_redux_1.useSelector(s => addRelations_1.default(s, Object.assign({}, s[entity].entities[String(id)]), entity, relations), lodash_1.default.isEqual);
12
+ }
13
+ else {
14
+ return react_redux_1.useSelector(s => addRelations_1.default(s, lodash_1.default.values(s[entity].entities), entity, relations), lodash_1.default.isEqual);
15
+ }
11
16
  }
12
17
  exports.default = useEager;
@@ -1,5 +1,7 @@
1
- export default function useJason({ reducers, middleware, extraActions }: {
1
+ export default function useJason({ reducers, middleware, enhancers, transportOptions, extraActions }: {
2
2
  reducers?: any;
3
3
  middleware?: any[];
4
+ enhancers?: any[];
4
5
  extraActions?: any;
6
+ transportOptions?: any;
5
7
  }): any[];
@@ -12,12 +12,11 @@ const restClient_1 = __importDefault(require("./restClient"));
12
12
  const pruneIdsMiddleware_1 = __importDefault(require("./pruneIdsMiddleware"));
13
13
  const createTransportAdapter_1 = __importDefault(require("./createTransportAdapter"));
14
14
  const toolkit_1 = require("@reduxjs/toolkit");
15
- const makeEager_1 = __importDefault(require("./makeEager"));
16
15
  const humps_1 = require("humps");
17
16
  const blueimp_md5_1 = __importDefault(require("blueimp-md5"));
18
17
  const lodash_1 = __importDefault(require("lodash"));
19
18
  const react_1 = require("react");
20
- function useJason({ reducers, middleware = [], extraActions }) {
19
+ function useJason({ reducers, middleware = [], enhancers = [], transportOptions = {}, extraActions }) {
21
20
  const [store, setStore] = react_1.useState(null);
22
21
  const [value, setValue] = react_1.useState(null);
23
22
  react_1.useEffect(() => {
@@ -29,17 +28,16 @@ function useJason({ reducers, middleware = [], extraActions }) {
29
28
  const serverActionQueue = createServerActionQueue_1.default();
30
29
  const allReducers = Object.assign(Object.assign({}, reducers), createJasonReducers_1.default(schema));
31
30
  console.debug({ allReducers });
32
- const store = toolkit_1.configureStore({ reducer: allReducers, middleware: [...middleware, pruneIdsMiddleware_1.default(schema)] });
31
+ const store = toolkit_1.configureStore({ reducer: allReducers, middleware: [...middleware, pruneIdsMiddleware_1.default(schema)], enhancers });
33
32
  const dispatch = store.dispatch;
34
33
  const optDis = createOptDis_1.default(schema, dispatch, restClient_1.default, serverActionQueue);
35
34
  const actions = createActions_1.default(schema, store, restClient_1.default, optDis, extraActions);
36
- const eager = makeEager_1.default(schema);
37
35
  let payloadHandlers = {};
38
36
  let configs = {};
39
37
  let subOptions = {};
40
38
  function handlePayload(payload) {
41
39
  const { md5Hash } = payload;
42
- const { handlePayload } = payloadHandlers[md5Hash];
40
+ const { handlePayload } = payloadHandlers[md5Hash] || {};
43
41
  if (handlePayload) {
44
42
  handlePayload(payload);
45
43
  }
@@ -47,7 +45,7 @@ function useJason({ reducers, middleware = [], extraActions }) {
47
45
  console.warn("Payload arrived with no handler", payload, payloadHandlers);
48
46
  }
49
47
  }
50
- const transportAdapter = createTransportAdapter_1.default(jasonConfig, handlePayload, dispatch, () => lodash_1.default.keys(configs).forEach(md5Hash => createSubscription(configs[md5Hash], subOptions[md5Hash])));
48
+ const transportAdapter = createTransportAdapter_1.default(jasonConfig, handlePayload, dispatch, () => lodash_1.default.keys(configs).forEach(md5Hash => createSubscription(configs[md5Hash], subOptions[md5Hash])), transportOptions);
51
49
  function createSubscription(config, options = {}) {
52
50
  // We need the hash to be consistent in Ruby / Javascript
53
51
  const hashableConfig = lodash_1.default(Object.assign({ conditions: {}, includes: {} }, config)).toPairs().sortBy(0).fromPairs().value();
@@ -84,7 +82,6 @@ function useJason({ reducers, middleware = [], extraActions }) {
84
82
  setValue({
85
83
  actions: actions,
86
84
  subscribe: createSubscription,
87
- eager,
88
85
  handlePayload
89
86
  });
90
87
  setStore(store);
data/client/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jamesr2323/jason",
3
- "version": "0.6.7",
3
+ "version": "0.7.4",
4
4
  "module": "./lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "scripts": {
@@ -3,8 +3,8 @@ import useJason from './useJason'
3
3
  import { Provider } from 'react-redux'
4
4
  import JasonContext from './JasonContext'
5
5
 
6
- const JasonProvider = ({ reducers, middleware, extraActions, children }: { reducers?: any, middleware?: any, extraActions?: any, children?: React.FC }) => {
7
- const [store, value] = useJason({ reducers, middleware, extraActions })
6
+ const JasonProvider = ({ reducers, middleware, enhancers, extraActions, transportOptions = {}, children }: { reducers?: any, middleware?: any, enhancers?: any, extraActions?: any, transportOptions?: any, children?: React.FC }) => {
7
+ const [store, value] = useJason({ reducers, middleware, enhancers, extraActions, transportOptions })
8
8
 
9
9
  if(!(store && value)) return <div /> // Wait for async fetch of schema to complete
10
10
 
@@ -0,0 +1,33 @@
1
+ import pluralize from 'pluralize'
2
+ import _ from 'lodash'
3
+
4
+ export default function addRelations(s, objects, objectType, relations, suffix = '') {
5
+ // first find out relation name
6
+ if (_.isArray(relations)) {
7
+ relations.forEach(relation => {
8
+ objects = addRelations(s, objects, objectType, relation)
9
+ })
10
+ } else if (typeof(relations) === 'object') {
11
+ const relation = Object.keys(relations)[0]
12
+ const subRelations = relations[relation]
13
+
14
+ objects = addRelations(s, objects, objectType, relation)
15
+ objects[relation] = addRelations(s, objects[relation], pluralize(relation), subRelations)
16
+ // #
17
+ } else if (typeof(relations) === 'string') {
18
+ const relation = relations
19
+ if (_.isArray(objects)) {
20
+ objects = objects.map(obj => addRelations(s, obj, objectType, relation))
21
+ } else if (_.isObject(objects)) {
22
+ const relatedObjects = _.values(s[pluralize(relation) + suffix].entities)
23
+
24
+ if(pluralize.isSingular(relation)) {
25
+ objects = { ...objects, [relation]: _.find(relatedObjects, { id: objects[relation + 'Id'] }) }
26
+ } else {
27
+ objects = { ...objects, [relation]: relatedObjects.filter(e => e[pluralize.singular(objectType) + 'Id'] === objects.id) }
28
+ }
29
+ }
30
+ }
31
+
32
+ return objects
33
+ }
@@ -3,7 +3,8 @@ import pluralize from 'pluralize'
3
3
  import _ from 'lodash'
4
4
 
5
5
  function generateSlices(models) {
6
- const sliceNames = models.map(k => pluralize(k))
6
+ // create two slices for each model. One to hold the persisted data, and one to hold draft data
7
+ const sliceNames = models.map(k => pluralize(k)).concat(models.map(k => `${pluralize(k)}Drafts`))
7
8
  const adapter = createEntityAdapter()
8
9
 
9
10
  return _.fromPairs(_.map(sliceNames, name => {
@@ -67,7 +68,8 @@ function generateJasonSlices(models) {
67
68
  name: 'jason',
68
69
  initialState: {
69
70
  connected: false,
70
- queueSize: 0
71
+ queueSize: 0,
72
+ error: null
71
73
  },
72
74
  reducers: {
73
75
  upsert: (s,a) => ({ ...s, ...a.payload })
@@ -15,18 +15,20 @@ export default function createOptDis(schema, dispatch, restClient, serverActionQ
15
15
  const plurals = _.keys(schema).map(k => pluralize(k))
16
16
 
17
17
  function enqueueServerAction (action) {
18
- serverActionQueue.addItem(action)
18
+ return serverActionQueue.addItem(action)
19
19
  }
20
20
 
21
21
  function dispatchServerAction() {
22
- const action = serverActionQueue.getItem()
23
- if (!action) return
22
+ const item = serverActionQueue.getItem()
23
+ if (!item) return
24
+
25
+ const { id, action } = item
24
26
 
25
27
  restClient.post('/jason/api/action', action)
26
- .then(serverActionQueue.itemProcessed)
27
- .catch(e => {
28
- dispatch({ type: 'upsertLocalUi', data: { error: JSON.stringify(e) } })
29
- serverActionQueue.itemProcessed()
28
+ .then(({ data }) => serverActionQueue.itemProcessed(id, data))
29
+ .catch(error => {
30
+ dispatch({ type: 'jason/upsert', payload: { error } })
31
+ serverActionQueue.itemFailed(id, error)
30
32
  })
31
33
  }
32
34
 
@@ -39,7 +41,7 @@ export default function createOptDis(schema, dispatch, restClient, serverActionQ
39
41
  dispatch({ type, payload: data })
40
42
 
41
43
  if (plurals.indexOf(type.split('/')[0]) > -1) {
42
- enqueueServerAction({ type, payload: data })
44
+ return enqueueServerAction({ type, payload: data })
43
45
  }
44
46
  }
45
47
  }
@@ -4,7 +4,7 @@ test('Adding items', () => {
4
4
  const serverActionQueue = createServerActionQueue()
5
5
  serverActionQueue.addItem({ type: 'entity/add', payload: { id: 'abc', attribute: 1 } })
6
6
  const item = serverActionQueue.getItem()
7
- expect(item).toStrictEqual({ type: 'entity/add', payload: { id: 'abc', attribute: 1 } })
7
+ expect(item.action).toStrictEqual({ type: 'entity/add', payload: { id: 'abc', attribute: 1 } })
8
8
  })
9
9
 
10
10
  test('Deduping of items that will overwrite each other', () => {
@@ -15,7 +15,7 @@ test('Deduping of items that will overwrite each other', () => {
15
15
 
16
16
  const item = serverActionQueue.getItem()
17
17
 
18
- expect(item).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 3 } })
18
+ expect(item.action).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 3 } })
19
19
  })
20
20
 
21
21
  test('Deduping of items with a superset', () => {
@@ -25,7 +25,7 @@ test('Deduping of items with a superset', () => {
25
25
 
26
26
  const item = serverActionQueue.getItem()
27
27
 
28
- expect(item).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 2, attribute2: 'test' } })
28
+ expect(item.action).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 2, attribute2: 'test' } })
29
29
  })
30
30
 
31
31
  test("doesn't dedupe items with some attributes missing", () => {
@@ -34,9 +34,63 @@ test("doesn't dedupe items with some attributes missing", () => {
34
34
  serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute2: 'test' } })
35
35
 
36
36
  const item = serverActionQueue.getItem()
37
- serverActionQueue.itemProcessed()
37
+ serverActionQueue.itemProcessed(item.id)
38
38
  const item2 = serverActionQueue.getItem()
39
39
 
40
- expect(item).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } })
41
- expect(item2).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute2: 'test' } })
40
+ expect(item.action).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } })
41
+ expect(item2.action).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute2: 'test' } })
42
42
  })
43
+
44
+ test("executes success callback", async function() {
45
+ const serverActionQueue = createServerActionQueue()
46
+ let cb = ''
47
+ let data = ''
48
+
49
+ // Check it can resolve chained promises
50
+ const promise = serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } })
51
+ .then(d => data = d)
52
+ .then(() => cb = 'resolved')
53
+
54
+ const item = serverActionQueue.getItem()
55
+ serverActionQueue.itemProcessed(item.id, 'testdata');
56
+
57
+ await promise
58
+ expect(data).toEqual('testdata')
59
+ expect(cb).toEqual('resolved')
60
+ })
61
+
62
+ test("executes error callback", async function() {
63
+ const serverActionQueue = createServerActionQueue()
64
+ let cb = ''
65
+ let error = ''
66
+
67
+ // Check it can resolve chained promises
68
+ const promise = serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } })
69
+ .then(() => cb = 'resolved')
70
+ .catch(e => error = e)
71
+
72
+ const item = serverActionQueue.getItem()
73
+ serverActionQueue.itemFailed(item.id, 'testerror');
74
+
75
+ await promise
76
+ expect(cb).toEqual('')
77
+ expect(error).toEqual('testerror')
78
+ })
79
+
80
+
81
+ test("merges success callbacks", async function() {
82
+ const results: any[] = []
83
+
84
+ const serverActionQueue = createServerActionQueue()
85
+ const p1 = serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } })
86
+ .then(data => results.push(data))
87
+
88
+ const p2 = serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 2, attribute2: 'test' } })
89
+ .then(data => results.push(data))
90
+
91
+ const item = serverActionQueue.getItem()
92
+ serverActionQueue.itemProcessed(item.id, 'complete')
93
+
94
+ await Promise.all([p1,p2])
95
+ expect(results).toEqual(['complete', 'complete'])
96
+ })