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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/Gemfile.lock +5 -3
- data/README.md +20 -14
- data/app/controllers/jason/jason_controller.rb +26 -4
- data/app/workers/jason/outbound_message_queue_worker.rb +1 -1
- data/client/lib/JasonProvider.d.ts +3 -1
- data/client/lib/JasonProvider.js +2 -2
- data/client/lib/addRelations.d.ts +1 -0
- data/client/lib/addRelations.js +39 -0
- data/client/lib/createJasonReducers.js +4 -2
- data/client/lib/createOptDis.d.ts +1 -1
- data/client/lib/createOptDis.js +9 -8
- data/client/lib/createServerActionQueue.d.ts +3 -2
- data/client/lib/createServerActionQueue.js +32 -6
- data/client/lib/createServerActionQueue.test.js +61 -6
- data/client/lib/createThenable.d.ts +1 -0
- data/client/lib/createThenable.js +5 -0
- data/client/lib/createTransportAdapter.d.ts +1 -1
- data/client/lib/createTransportAdapter.js +2 -2
- data/client/lib/index.d.ts +8 -1
- data/client/lib/index.js +3 -1
- data/client/lib/transportAdapters/actionCableAdapter.d.ts +1 -1
- data/client/lib/transportAdapters/actionCableAdapter.js +27 -6
- data/client/lib/transportAdapters/pusherAdapter.js +1 -1
- data/client/lib/useDraft.d.ts +1 -0
- data/client/lib/useDraft.js +13 -0
- data/client/lib/useEager.d.ts +1 -1
- data/client/lib/useEager.js +10 -5
- data/client/lib/useJason.d.ts +3 -1
- data/client/lib/useJason.js +4 -7
- data/client/package.json +1 -1
- data/client/src/JasonProvider.tsx +2 -2
- data/client/src/addRelations.ts +33 -0
- data/client/src/createJasonReducers.ts +4 -2
- data/client/src/createOptDis.ts +10 -8
- data/client/src/createServerActionQueue.test.ts +60 -6
- data/client/src/createServerActionQueue.ts +41 -6
- data/client/src/createTransportAdapter.ts +2 -2
- data/client/src/index.ts +2 -0
- data/client/src/transportAdapters/actionCableAdapter.ts +28 -7
- data/client/src/transportAdapters/pusherAdapter.ts +1 -2
- data/client/src/useDraft.ts +17 -0
- data/client/src/useEager.ts +9 -6
- data/client/src/useJason.ts +10 -7
- data/lib/jason.rb +9 -2
- data/lib/jason/api_model.rb +0 -4
- data/lib/jason/channel.rb +0 -7
- data/lib/jason/conditions_matcher.rb +88 -0
- data/lib/jason/consistency_checker.rb +65 -0
- data/lib/jason/graph_helper.rb +4 -0
- data/lib/jason/publisher.rb +36 -36
- data/lib/jason/subscription.rb +51 -15
- data/lib/jason/version.rb +1 -1
- metadata +12 -5
- data/client/src/makeEager.ts +0 -46
- data/lib/jason/publisher_old.rb +0 -112
- data/lib/jason/subscription_old.rb +0 -171
@@ -1,31 +1,65 @@
|
|
1
1
|
// A FIFO queue with deduping of actions whose effect will be cancelled by later actions
|
2
|
-
|
2
|
+
import { v4 as uuidv4 } from 'uuid'
|
3
3
|
import _ from 'lodash'
|
4
4
|
|
5
|
+
class Deferred {
|
6
|
+
promise: Promise<any>;
|
7
|
+
resolve: any;
|
8
|
+
reject: any;
|
9
|
+
|
10
|
+
constructor() {
|
11
|
+
this.promise = new Promise((resolve, reject)=> {
|
12
|
+
this.reject = reject
|
13
|
+
this.resolve = resolve
|
14
|
+
})
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
5
18
|
export default function createServerActionQueue() {
|
6
19
|
const queue: any[] = []
|
20
|
+
const deferreds = {}
|
21
|
+
|
7
22
|
let inFlight = false
|
8
23
|
|
9
|
-
function addItem(
|
24
|
+
function addItem(action) {
|
10
25
|
// Check if there are any items ahead in the queue that this item would effectively overwrite.
|
11
26
|
// In that case we can remove them
|
12
27
|
// If this is an upsert && item ID is the same && current item attributes are a superset of the earlier item attributes
|
13
|
-
const { type, payload } =
|
28
|
+
const { type, payload } = action
|
29
|
+
const id = uuidv4()
|
30
|
+
const dfd = new Deferred()
|
31
|
+
deferreds[id] = [dfd]
|
32
|
+
|
33
|
+
const item = { id, action }
|
34
|
+
|
14
35
|
if (type.split('/')[1] !== 'upsert') {
|
15
36
|
queue.push(item)
|
16
|
-
return
|
37
|
+
return dfd.promise
|
17
38
|
}
|
18
39
|
|
19
40
|
_.remove(queue, item => {
|
20
|
-
const { type: itemType, payload: itemPayload } = item
|
41
|
+
const { type: itemType, payload: itemPayload } = item.action
|
21
42
|
if (type !== itemType) return false
|
22
43
|
if (itemPayload.id !== payload.id) return false
|
23
44
|
|
24
45
|
// Check that all keys of itemPayload are in payload.
|
46
|
+
deferreds[id].push(...deferreds[item.id])
|
25
47
|
return _.difference(_.keys(itemPayload),_.keys(payload)).length === 0
|
26
48
|
})
|
27
49
|
|
28
50
|
queue.push(item)
|
51
|
+
return dfd.promise
|
52
|
+
}
|
53
|
+
|
54
|
+
function itemProcessed(id, data?: any) {
|
55
|
+
inFlight = false
|
56
|
+
deferreds[id].forEach(dfd => dfd.resolve(data))
|
57
|
+
}
|
58
|
+
|
59
|
+
function itemFailed(id, error?: any) {
|
60
|
+
queue.length = 0
|
61
|
+
deferreds[id].forEach(dfd => dfd.reject(error))
|
62
|
+
inFlight = false
|
29
63
|
}
|
30
64
|
|
31
65
|
return {
|
@@ -40,7 +74,8 @@ export default function createServerActionQueue() {
|
|
40
74
|
}
|
41
75
|
return false
|
42
76
|
},
|
43
|
-
itemProcessed
|
77
|
+
itemProcessed,
|
78
|
+
itemFailed,
|
44
79
|
fullySynced: () => queue.length === 0 && !inFlight,
|
45
80
|
getData: () => ({ queue, inFlight })
|
46
81
|
}
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import actionCableAdapter from './transportAdapters/actionCableAdapter'
|
2
2
|
import pusherAdapter from './transportAdapters/pusherAdapter'
|
3
3
|
|
4
|
-
export default function createTransportAdapter(jasonConfig, handlePayload, dispatch, onConnect) {
|
4
|
+
export default function createTransportAdapter(jasonConfig, handlePayload, dispatch, onConnect, transportOptions) {
|
5
5
|
const { transportService } = jasonConfig
|
6
6
|
if (transportService === 'action_cable') {
|
7
|
-
return actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnect)
|
7
|
+
return actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnect, transportOptions)
|
8
8
|
} else if (transportService === 'pusher') {
|
9
9
|
return pusherAdapter(jasonConfig, handlePayload, dispatch)
|
10
10
|
} else {
|
data/client/src/index.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
import _JasonContext from './JasonContext'
|
1
2
|
import _JasonProvider from './JasonProvider'
|
2
3
|
import _useAct from './useAct'
|
3
4
|
import _useSub from './useSub'
|
4
5
|
import _useEager from './useEager'
|
5
6
|
|
7
|
+
export const JasonContext = _JasonContext
|
6
8
|
export const JasonProvider = _JasonProvider
|
7
9
|
export const useAct = _useAct
|
8
10
|
export const useSub = _useSub
|
@@ -1,7 +1,14 @@
|
|
1
1
|
import { createConsumer } from "@rails/actioncable"
|
2
|
+
import restClient from '../restClient'
|
3
|
+
import { v4 as uuidv4 } from 'uuid'
|
4
|
+
import _ from 'lodash'
|
5
|
+
|
6
|
+
export default function actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnected, transportOptions) {
|
7
|
+
const consumerId = uuidv4()
|
8
|
+
|
9
|
+
const { cableUrl } = transportOptions
|
10
|
+
const consumer = cableUrl ? createConsumer(cableUrl) : createConsumer()
|
2
11
|
|
3
|
-
export default function actionCableAdapter(jasonConfig, handlePayload, dispatch, onConnected) {
|
4
|
-
const consumer = createConsumer()
|
5
12
|
const subscription = (consumer.subscriptions.create({
|
6
13
|
channel: 'Jason::Channel'
|
7
14
|
}, {
|
@@ -22,16 +29,30 @@ export default function actionCableAdapter(jasonConfig, handlePayload, dispatch,
|
|
22
29
|
}
|
23
30
|
}));
|
24
31
|
|
25
|
-
function getPayload(config, options) {
|
26
|
-
subscription.send({ getPayload: config, ...options })
|
27
|
-
}
|
28
|
-
|
29
32
|
function createSubscription(config) {
|
30
33
|
subscription.send({ createSubscription: config })
|
31
34
|
}
|
32
35
|
|
33
36
|
function removeSubscription(config) {
|
34
|
-
|
37
|
+
restClient.post('/jason/api/remove_subscription', { config, consumerId })
|
38
|
+
.catch(e => console.error(e))
|
39
|
+
}
|
40
|
+
|
41
|
+
function getPayload(config, options) {
|
42
|
+
restClient.post('/jason/api/get_payload', {
|
43
|
+
config,
|
44
|
+
options
|
45
|
+
})
|
46
|
+
.then(({ data }) => {
|
47
|
+
_.map(data, (payload, modelName) => {
|
48
|
+
handlePayload(payload)
|
49
|
+
})
|
50
|
+
})
|
51
|
+
.catch(e => console.error(e))
|
52
|
+
}
|
53
|
+
|
54
|
+
function fullChannelName(channelName) {
|
55
|
+
return channelName
|
35
56
|
}
|
36
57
|
|
37
58
|
return { getPayload, createSubscription, removeSubscription }
|
@@ -1,11 +1,10 @@
|
|
1
1
|
import Pusher from 'pusher-js'
|
2
|
-
import { createConsumer } from "@rails/actioncable"
|
3
2
|
import restClient from '../restClient'
|
4
3
|
import { v4 as uuidv4 } from 'uuid'
|
5
4
|
import _ from 'lodash'
|
6
5
|
|
7
6
|
export default function pusherAdapter(jasonConfig, handlePayload, dispatch) {
|
8
|
-
|
7
|
+
const consumerId = uuidv4()
|
9
8
|
|
10
9
|
const { pusherKey, pusherRegion, pusherChannelPrefix } = jasonConfig
|
11
10
|
const pusher = new Pusher(pusherKey, {
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import _ from 'lodash'
|
2
|
+
import { useSelector } from 'react-redux'
|
3
|
+
import addRelations from './addRelations'
|
4
|
+
|
5
|
+
/* Can be called as
|
6
|
+
useDraft() => draft object for making updates
|
7
|
+
useDraft('entity', id) => returns [draft, object]
|
8
|
+
useDraft('entity', id, relations) => returns [draft, objectWithEmbeddedRelations]
|
9
|
+
*/
|
10
|
+
|
11
|
+
export default function useDraft(entity, id, relations = []) {
|
12
|
+
// const entityDraft =`${entity}Draft`
|
13
|
+
// const object = { ...s[entityDraft].entities[String(id)] }
|
14
|
+
|
15
|
+
// return useSelector(s => addRelations(s, object, entity, relations, 'Draft'), _.isEqual)
|
16
|
+
}
|
17
|
+
|
data/client/src/useEager.ts
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
import
|
2
|
-
import {
|
1
|
+
import _ from 'lodash'
|
2
|
+
import { useSelector } from 'react-redux'
|
3
|
+
import addRelations from './addRelations'
|
3
4
|
|
4
|
-
export default function useEager(entity, id =
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
export default function useEager(entity: string, id = '', relations = [] as any) {
|
6
|
+
if (id) {
|
7
|
+
return useSelector(s => addRelations(s, { ...s[entity].entities[String(id)] }, entity, relations), _.isEqual)
|
8
|
+
} else {
|
9
|
+
return useSelector(s => addRelations(s, _.values(s[entity].entities), entity, relations), _.isEqual)
|
10
|
+
}
|
8
11
|
}
|
9
12
|
|
data/client/src/useJason.ts
CHANGED
@@ -9,13 +9,12 @@ import createTransportAdapater from './createTransportAdapter'
|
|
9
9
|
|
10
10
|
import { createEntityAdapter, createSlice, createReducer, configureStore } from '@reduxjs/toolkit'
|
11
11
|
|
12
|
-
import makeEager from './makeEager'
|
13
12
|
import { camelizeKeys } from 'humps'
|
14
13
|
import md5 from 'blueimp-md5'
|
15
14
|
import _ from 'lodash'
|
16
15
|
import React, { useState, useEffect } from 'react'
|
17
16
|
|
18
|
-
export default function useJason({ reducers, middleware = [], extraActions }: { reducers?: any, middleware?: any[], extraActions?: any }) {
|
17
|
+
export default function useJason({ reducers, middleware = [], enhancers = [], transportOptions = {}, extraActions }: { reducers?: any, middleware?: any[], enhancers?: any[], extraActions?: any, transportOptions?: any }) {
|
19
18
|
const [store, setStore] = useState(null as any)
|
20
19
|
const [value, setValue] = useState(null as any)
|
21
20
|
|
@@ -36,12 +35,11 @@ export default function useJason({ reducers, middleware = [], extraActions }: {
|
|
36
35
|
|
37
36
|
console.debug({ allReducers })
|
38
37
|
|
39
|
-
const store = configureStore({ reducer: allReducers, middleware: [...middleware, pruneIdsMiddleware(schema)] })
|
38
|
+
const store = configureStore({ reducer: allReducers, middleware: [...middleware, pruneIdsMiddleware(schema)], enhancers })
|
40
39
|
const dispatch = store.dispatch
|
41
40
|
|
42
41
|
const optDis = createOptDis(schema, dispatch, restClient, serverActionQueue)
|
43
42
|
const actions = createActions(schema, store, restClient, optDis, extraActions)
|
44
|
-
const eager = makeEager(schema)
|
45
43
|
|
46
44
|
let payloadHandlers = {}
|
47
45
|
let configs = {}
|
@@ -50,7 +48,7 @@ export default function useJason({ reducers, middleware = [], extraActions }: {
|
|
50
48
|
function handlePayload(payload) {
|
51
49
|
const { md5Hash } = payload
|
52
50
|
|
53
|
-
const { handlePayload } = payloadHandlers[md5Hash]
|
51
|
+
const { handlePayload } = payloadHandlers[md5Hash] || {}
|
54
52
|
if (handlePayload) {
|
55
53
|
handlePayload(payload)
|
56
54
|
} else {
|
@@ -58,7 +56,13 @@ export default function useJason({ reducers, middleware = [], extraActions }: {
|
|
58
56
|
}
|
59
57
|
}
|
60
58
|
|
61
|
-
const transportAdapter = createTransportAdapater(
|
59
|
+
const transportAdapter = createTransportAdapater(
|
60
|
+
jasonConfig,
|
61
|
+
handlePayload,
|
62
|
+
dispatch,
|
63
|
+
() => _.keys(configs).forEach(md5Hash => createSubscription(configs[md5Hash], subOptions[md5Hash])),
|
64
|
+
transportOptions
|
65
|
+
)
|
62
66
|
|
63
67
|
function createSubscription(config, options = {}) {
|
64
68
|
// We need the hash to be consistent in Ruby / Javascript
|
@@ -99,7 +103,6 @@ export default function useJason({ reducers, middleware = [], extraActions }: {
|
|
99
103
|
setValue({
|
100
104
|
actions: actions,
|
101
105
|
subscribe: createSubscription,
|
102
|
-
eager,
|
103
106
|
handlePayload
|
104
107
|
})
|
105
108
|
setStore(store)
|
data/lib/jason.rb
CHANGED
@@ -12,6 +12,8 @@ require 'jason/engine'
|
|
12
12
|
require 'jason/lua_generator'
|
13
13
|
require 'jason/includes_helper'
|
14
14
|
require 'jason/graph_helper'
|
15
|
+
require 'jason/conditions_matcher'
|
16
|
+
require 'jason/consistency_checker'
|
15
17
|
|
16
18
|
module Jason
|
17
19
|
class Error < StandardError; end
|
@@ -23,7 +25,8 @@ module Jason
|
|
23
25
|
self.mattr_accessor :pusher_key
|
24
26
|
self.mattr_accessor :pusher_region
|
25
27
|
self.mattr_accessor :pusher_channel_prefix
|
26
|
-
self.mattr_accessor :
|
28
|
+
self.mattr_accessor :subscription_authorization_service
|
29
|
+
self.mattr_accessor :update_authorization_service
|
27
30
|
self.mattr_accessor :sidekiq_queue
|
28
31
|
|
29
32
|
self.schema = {}
|
@@ -48,7 +51,11 @@ module Jason
|
|
48
51
|
puts "Old config was #{previous_schema[model]}"
|
49
52
|
puts "New config is #{config}"
|
50
53
|
puts "Rebuilding cache for #{model}"
|
51
|
-
|
54
|
+
|
55
|
+
# This is necessary to ensure all Rails methods have been added to model before we attempt to cache.
|
56
|
+
Rails.configuration.after_initialize do
|
57
|
+
model.classify.constantize.cache_all
|
58
|
+
end
|
52
59
|
puts "Done"
|
53
60
|
end
|
54
61
|
end
|
data/lib/jason/api_model.rb
CHANGED
@@ -36,11 +36,7 @@ class Jason::ApiModel
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def permit(params)
|
39
|
-
pp self
|
40
|
-
pp params
|
41
39
|
params = params.require(:payload).permit(allowed_params).tap do |allowed|
|
42
|
-
pp "ALLOWED"
|
43
|
-
pp allowed
|
44
40
|
allowed_object_params.each do |key|
|
45
41
|
allowed[key] = params[:payload][key].to_unsafe_h if params[:payload][key]
|
46
42
|
end
|
data/lib/jason/channel.rb
CHANGED
@@ -12,7 +12,6 @@ class Jason::Channel < ActionCable::Channel::Base
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def handle_message(message)
|
15
|
-
pp message['createSubscription']
|
16
15
|
@subscriptions ||= []
|
17
16
|
|
18
17
|
begin # ActionCable swallows errors in this message - ensure they're output to logs.
|
@@ -38,18 +37,12 @@ class Jason::Channel < ActionCable::Channel::Base
|
|
38
37
|
|
39
38
|
subscriptions.push(subscription)
|
40
39
|
subscription.add_consumer(identifier)
|
41
|
-
subscription.get.each do |payload|
|
42
|
-
pp payload
|
43
|
-
transmit(payload) if payload.present?
|
44
|
-
end
|
45
40
|
end
|
46
41
|
|
47
42
|
def remove_subscription(config)
|
48
43
|
subscription = Jason::Subscription.upsert_by_config(config['model'], conditions: config['conditions'], includes: config['includes'])
|
49
44
|
subscriptions.reject! { |s| s.id == subscription.id }
|
50
45
|
subscription.remove_consumer(identifier)
|
51
|
-
|
52
|
-
# TODO Stop streams
|
53
46
|
end
|
54
47
|
|
55
48
|
def get_payload(config, force_refresh = false)
|
@@ -0,0 +1,88 @@
|
|
1
|
+
class Jason::ConditionsMatcher
|
2
|
+
attr_reader :klass
|
3
|
+
|
4
|
+
def initialize(klass)
|
5
|
+
@klass = klass
|
6
|
+
end
|
7
|
+
|
8
|
+
# key, rules = 'post_id', 123
|
9
|
+
# key, rules = 'post_id', { 'value': [123,C456], 'type': 'between' }
|
10
|
+
# key, rules = 'post_id', { 'value': [123,456], 'type': 'between', 'not': true }
|
11
|
+
# key, rules = 'post_id', { 'value': 123, 'type': 'equals', 'not': true }
|
12
|
+
def test_match(key, rules, previous_changes)
|
13
|
+
return nil if !previous_changes.keys.include?(key)
|
14
|
+
|
15
|
+
if rules.is_a?(Hash)
|
16
|
+
matches = false
|
17
|
+
value = convert_to_datatype(key, rules['value'])
|
18
|
+
|
19
|
+
if rules['type'] == 'equals'
|
20
|
+
matches = previous_changes[key][1] == value
|
21
|
+
elsif rules['type'] == 'between'
|
22
|
+
matches = (value[0]..value[1]).cover?(previous_changes[key][1])
|
23
|
+
else
|
24
|
+
raise "Unrecognized rule type #{rules['type']}"
|
25
|
+
end
|
26
|
+
|
27
|
+
if rules['not']
|
28
|
+
return !matches
|
29
|
+
else
|
30
|
+
return matches
|
31
|
+
end
|
32
|
+
|
33
|
+
elsif rules.is_a?(Array)
|
34
|
+
value = convert_to_datatype(key, rules)
|
35
|
+
return previous_changes[key][1].includes?(value)
|
36
|
+
else
|
37
|
+
value = convert_to_datatype(key, rules)
|
38
|
+
return previous_changes[key][1] == value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# conditions = { 'post_id' => 123, 'created_at' => { 'type' => 'between', 'value' => ['2020-01-01', '2020-01-02'] } }
|
43
|
+
def apply_conditions(relation, conditions)
|
44
|
+
conditions.each do |key, rules|
|
45
|
+
relation = apply_condition(relation, key, rules)
|
46
|
+
end
|
47
|
+
|
48
|
+
relation
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def apply_condition(relation, key, rules)
|
54
|
+
if rules.is_a?(Hash)
|
55
|
+
value = convert_to_datatype(key, rules['value'])
|
56
|
+
|
57
|
+
if rules['type'] == 'equals'
|
58
|
+
arg = { key => value }
|
59
|
+
elsif rules['type'] == 'between'
|
60
|
+
arg = { key => value[0]..value[1] }
|
61
|
+
else
|
62
|
+
raise "Unrecognized rule type #{rules['type']}"
|
63
|
+
end
|
64
|
+
|
65
|
+
if rules['not']
|
66
|
+
return relation.where.not(arg)
|
67
|
+
else
|
68
|
+
return relation.where(arg)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
value = convert_to_datatype(key, rules)
|
72
|
+
return relation.where({ key => value })
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def convert_to_datatype(key, value)
|
77
|
+
datatype = klass.type_for_attribute(key).type
|
78
|
+
if datatype == :datetime || datatype == :date
|
79
|
+
if value.is_a?(Array)
|
80
|
+
value.map { |v| v&.to_datetime }
|
81
|
+
else
|
82
|
+
value&.to_datetime
|
83
|
+
end
|
84
|
+
else
|
85
|
+
value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class Jason::ConsistencyChecker
|
2
|
+
attr_reader :subscription
|
3
|
+
attr_reader :inconsistent
|
4
|
+
|
5
|
+
def self.check_all(fix: false)
|
6
|
+
Jason::Subscription.all.each do |sub|
|
7
|
+
next if sub.consumer_count == 0
|
8
|
+
checker = Jason::ConsistencyChecker.new(sub)
|
9
|
+
result = checker.check
|
10
|
+
if checker.inconsistent?
|
11
|
+
pp sub.config
|
12
|
+
pp result
|
13
|
+
if fix
|
14
|
+
sub.reset!(hard: true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.fix_all
|
21
|
+
check_all(fix: true)
|
22
|
+
end
|
23
|
+
|
24
|
+
def wipe_all_subs
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(subscription)
|
29
|
+
@subscription = subscription
|
30
|
+
@inconsistent = false
|
31
|
+
end
|
32
|
+
|
33
|
+
def inconsistent?
|
34
|
+
inconsistent
|
35
|
+
end
|
36
|
+
|
37
|
+
# Take a subscription, get the current cached payload, and compare it to the data retrieved from the database
|
38
|
+
def check
|
39
|
+
cached_payload = subscription.get
|
40
|
+
edge_set = subscription.load_ids_for_sub_models(subscription.model, nil)
|
41
|
+
|
42
|
+
result = cached_payload.map do |model_name, data|
|
43
|
+
cached_payload_instance_ids = data[:payload].map { |row| row['id'] }
|
44
|
+
|
45
|
+
model_idx = edge_set[:model_names].index(model_name)
|
46
|
+
if model_idx.present?
|
47
|
+
edge_set_instance_ids = edge_set[:instance_ids].map { |row| row[model_idx] }
|
48
|
+
else
|
49
|
+
next
|
50
|
+
end
|
51
|
+
|
52
|
+
missing = edge_set_instance_ids - cached_payload_instance_ids
|
53
|
+
intruding = cached_payload_instance_ids - edge_set_instance_ids
|
54
|
+
|
55
|
+
if missing.present? || intruding.present?
|
56
|
+
@inconsistent = true
|
57
|
+
end
|
58
|
+
|
59
|
+
[model_name, {
|
60
|
+
'missing' => missing,
|
61
|
+
'intruding' => intruding
|
62
|
+
}]
|
63
|
+
end.compact.to_h
|
64
|
+
end
|
65
|
+
end
|