@raclettejs/core 0.1.36 → 0.1.38
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.
- package/CHANGELOG.md +32 -7
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +2 -2
- package/package.json +2 -2
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/composition/composition.schema.ts +10 -4
- package/services/backend/src/corePlugins/raclette__core/backend/datatypes/interactionLink/interactionLink.schema.ts +4 -1
- package/services/backend/src/shared/schemas/core/CompositionCreate.schema.ts +4 -0
- package/services/backend/src/shared/schemas/core/InteractionLinkCreate.schema.ts +4 -0
- package/services/backend/src/shared/types/core/CompositionCreate.types.ts +1 -0
- package/services/backend/src/shared/types/core/InteractionLinkCreate.types.ts +1 -0
- package/services/frontend/src/core/lib/scheduleTask.ts +35 -0
- package/services/frontend/src/core/store/index.ts +5 -1
- package/services/frontend/src/core/store/reducers/compositionSlots/reducers.ts +1 -5
- package/services/frontend/src/core/store/reducers/data/reducers.ts +101 -65
- package/services/frontend/src/core/store/reducers/metaReducer.ts +23 -0
- package/services/frontend/src/core/store/reducers/notifications/reducers.ts +39 -47
- package/services/frontend/src/core/store/reducers/queries/effects.ts +55 -2
- package/services/frontend/src/core/store/reducers/queries/reducers.ts +60 -62
- package/services/frontend/src/core/store/reducers/queriesCache/effects.ts +105 -0
- package/services/frontend/src/core/store/reducers/queriesCache/index.ts +2 -1
- package/services/frontend/src/core/store/reducers/queriesCache/queryCacheHelper.ts +79 -9
- package/services/frontend/src/core/store/reducers/queriesCache/reducers.ts +95 -100
- package/services/frontend/src/orchestrator/components/CompositionLoadingState.vue +28 -0
- package/services/frontend/src/orchestrator/components/composition/CompositionOverlay.vue +5 -7
- package/services/frontend/src/orchestrator/components/composition/WidgetsLayoutLoader.vue +171 -39
- package/services/frontend/src/orchestrator/components/dataTable/BaseDataTable.vue +336 -56
- package/services/frontend/src/orchestrator/components/dataTable/BaseDataTableConfirmDeleteBtn.vue +27 -0
- package/services/frontend/src/orchestrator/components/dataTable/BaseDataTableFilterDrawerShell.vue +72 -0
- package/services/frontend/src/orchestrator/components/index.ts +4 -0
- package/services/frontend/src/orchestrator/composables/index.ts +1 -0
- package/services/frontend/src/orchestrator/composables/useBaseDataTableDeleteConfirm.ts +23 -0
- package/services/frontend/src/orchestrator/composables/useVueWriteOperationHelper.ts +16 -4
- package/services/frontend/src/orchestrator/composables/useWidgetLifecycle.ts +53 -23
- package/services/frontend/src/orchestrator/constants/widgetSlotType.ts +4 -0
- package/services/frontend/src/orchestrator/i18n/de-DE.json +7 -1
- package/services/frontend/src/orchestrator/i18n/en-EU.json +7 -1
- package/services/frontend/src/orchestrator/i18n/sk.json +7 -1
- package/services/frontend/src/orchestrator/router/routerHooks/afterEach.ts +15 -9
- package/services/frontend/src/orchestrator/types/DataApi.ts +2 -1
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { fixQueryParamTypes } from "@racletteCore/lib/dataHelper"
|
|
6
|
-
import { difference, mergeDeepRight
|
|
6
|
+
import { difference, mergeDeepRight } from "ramda"
|
|
7
7
|
import type { CustomStoreActionParams } from "@racletteCore/types"
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
export type Query = {
|
|
10
10
|
target: string
|
|
11
11
|
actionId: string
|
|
@@ -22,8 +22,7 @@ const updateQueryItems = (state, action) => {
|
|
|
22
22
|
if (!actionId || !state[actionId]) {
|
|
23
23
|
return state
|
|
24
24
|
}
|
|
25
|
-
const
|
|
26
|
-
const query = queries[actionId]
|
|
25
|
+
const query = { ...state[actionId] }
|
|
27
26
|
if (items) {
|
|
28
27
|
const uuids = Object.values(items).map((item) => item._id)
|
|
29
28
|
let dataItemsToRemove = []
|
|
@@ -35,15 +34,14 @@ const updateQueryItems = (state, action) => {
|
|
|
35
34
|
query.loading = false
|
|
36
35
|
query.uuids = uuids
|
|
37
36
|
if (dataItemsToRemove.length > 0) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
37
|
+
action.actionResult.dataCacheCleanup = {
|
|
38
|
+
actionId,
|
|
39
|
+
uuids: dataItemsToRemove,
|
|
40
|
+
}
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
return
|
|
44
|
+
return { ...state, [actionId]: query }
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
const updateQuery = (state, action) => {
|
|
@@ -53,8 +51,7 @@ const updateQuery = (state, action) => {
|
|
|
53
51
|
return state
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
const query = queries[actionId]
|
|
54
|
+
const query = { ...state[actionId] }
|
|
58
55
|
if (payload.target) {
|
|
59
56
|
query.target = payload.target
|
|
60
57
|
}
|
|
@@ -106,20 +103,19 @@ const updateQuery = (state, action) => {
|
|
|
106
103
|
query.backend = fixQueryParamTypes(query.backend)
|
|
107
104
|
}
|
|
108
105
|
|
|
109
|
-
return
|
|
106
|
+
return { ...state, [actionId]: query }
|
|
110
107
|
}
|
|
111
108
|
const queryiesSetCacheKey = (state, action) => {
|
|
112
|
-
let queries = structuredClone(state)
|
|
113
109
|
const payload = action.payload
|
|
114
110
|
const { actionId, cacheKey } = payload
|
|
115
111
|
if (!actionId || !state[actionId]) {
|
|
116
112
|
return state
|
|
117
113
|
}
|
|
118
114
|
|
|
119
|
-
|
|
115
|
+
const updatedQuery = { ...state[actionId], cacheKey }
|
|
120
116
|
action.actionResult.updated.itemBefore = state[actionId]
|
|
121
|
-
action.actionResult.updated.item =
|
|
122
|
-
return
|
|
117
|
+
action.actionResult.updated.item = updatedQuery
|
|
118
|
+
return { ...state, [actionId]: updatedQuery }
|
|
123
119
|
}
|
|
124
120
|
const queriesUpdateLoading = (state, action) => {
|
|
125
121
|
const payload = action.payload
|
|
@@ -127,12 +123,13 @@ const queriesUpdateLoading = (state, action) => {
|
|
|
127
123
|
if (!actionId || !state[actionId]) {
|
|
128
124
|
return state
|
|
129
125
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
126
|
+
if (!payload.hasOwnProperty("loading")) {
|
|
127
|
+
return state
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
...state,
|
|
131
|
+
[actionId]: { ...state[actionId], loading: payload.loading },
|
|
134
132
|
}
|
|
135
|
-
return queries
|
|
136
133
|
}
|
|
137
134
|
/**
|
|
138
135
|
* execute queries
|
|
@@ -141,14 +138,11 @@ const queriesExecute = (state, action) => {
|
|
|
141
138
|
if (Object.keys(state).length === 0) {
|
|
142
139
|
return state
|
|
143
140
|
}
|
|
144
|
-
const { target, params, actionId, reset, type } =
|
|
145
|
-
action.payload,
|
|
146
|
-
)
|
|
141
|
+
const { target, params, actionId, reset, type } = action.payload
|
|
147
142
|
const isResetQuery = reset || params?.reset
|
|
148
|
-
const queries = structuredClone(state)
|
|
149
143
|
|
|
150
|
-
if (actionId &&
|
|
151
|
-
const query =
|
|
144
|
+
if (actionId && state[actionId]) {
|
|
145
|
+
const query = { ...state[actionId] }
|
|
152
146
|
|
|
153
147
|
query.loading = true
|
|
154
148
|
action.actionResult.updated.itemsBefore[actionId] = state[actionId]
|
|
@@ -166,24 +160,27 @@ const queriesExecute = (state, action) => {
|
|
|
166
160
|
}
|
|
167
161
|
query.params = fixQueryParamTypes(query.params)
|
|
168
162
|
action.actionResult.updated.items[actionId] = query
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Object.keys(queries).forEach((actionId) => {
|
|
172
|
-
const query = queries[actionId]
|
|
173
|
-
action.actionResult.updated.itemsBefore[actionId] = state[actionId]
|
|
174
|
-
if (
|
|
175
|
-
!type ||
|
|
176
|
-
(type === "find" && query.params.find) ||
|
|
177
|
-
(type === "search" && query.params.search)
|
|
178
|
-
) {
|
|
179
|
-
query.loading = true
|
|
180
|
-
query.params = fixQueryParamTypes(query.params)
|
|
181
|
-
action.actionResult.updated.itemsBefore[actionId] = state[actionId]
|
|
182
|
-
action.actionResult.updated.items[actionId] = query
|
|
183
|
-
}
|
|
184
|
-
})
|
|
163
|
+
|
|
164
|
+
return { ...state, [actionId]: query }
|
|
185
165
|
}
|
|
186
166
|
|
|
167
|
+
const queries = structuredClone(state)
|
|
168
|
+
|
|
169
|
+
Object.keys(queries).forEach((queryActionId) => {
|
|
170
|
+
const query = queries[queryActionId]
|
|
171
|
+
action.actionResult.updated.itemsBefore[queryActionId] = state[queryActionId]
|
|
172
|
+
if (
|
|
173
|
+
!type ||
|
|
174
|
+
(type === "find" && query.params.find) ||
|
|
175
|
+
(type === "search" && query.params.search)
|
|
176
|
+
) {
|
|
177
|
+
query.loading = true
|
|
178
|
+
query.params = fixQueryParamTypes(query.params)
|
|
179
|
+
action.actionResult.updated.itemsBefore[queryActionId] = state[queryActionId]
|
|
180
|
+
action.actionResult.updated.items[queryActionId] = query
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
|
|
187
184
|
return queries
|
|
188
185
|
}
|
|
189
186
|
/**
|
|
@@ -209,36 +206,37 @@ const queriesAdd = (state, action) => {
|
|
|
209
206
|
}
|
|
210
207
|
|
|
211
208
|
action.actionResult.added.push(query)
|
|
212
|
-
const queries = structuredClone(state)
|
|
213
|
-
queries[actionId] = query
|
|
214
209
|
|
|
215
|
-
return
|
|
210
|
+
return { ...state, [actionId]: query }
|
|
216
211
|
}
|
|
217
212
|
|
|
218
213
|
/**
|
|
219
214
|
* remove queries
|
|
220
215
|
*/
|
|
221
216
|
const queriesRemove = (state, action) => {
|
|
222
|
-
let queries = structuredClone(state)
|
|
223
217
|
const { actionId } = action.payload
|
|
224
|
-
if (actionId
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
218
|
+
if (!actionId || !state[actionId]) {
|
|
219
|
+
return state
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const query = state[actionId]
|
|
223
|
+
action.actionResult.deleted.hard[actionId] = query
|
|
224
|
+
if (query?.uuids?.length) {
|
|
225
|
+
action.actionResult.dataCacheCleanup = {
|
|
226
|
+
actionId,
|
|
227
|
+
uuids: query.uuids,
|
|
234
228
|
}
|
|
229
|
+
}
|
|
235
230
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
231
|
+
action.actionResult.queryDeleted = {
|
|
232
|
+
actionId,
|
|
233
|
+
query: structuredClone(query),
|
|
239
234
|
}
|
|
240
235
|
|
|
241
|
-
|
|
236
|
+
const queries = { ...state }
|
|
237
|
+
delete queries[actionId]
|
|
238
|
+
|
|
239
|
+
return queries
|
|
242
240
|
}
|
|
243
241
|
|
|
244
242
|
export {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { tap, distinctUntilChanged, ignoreElements } from "rxjs/operators"
|
|
2
|
+
import { actions$, ofType } from "mini-rx-store"
|
|
3
|
+
import log from "@racletteCore/lib/logger"
|
|
4
|
+
import {
|
|
5
|
+
scheduleAfterPaint,
|
|
6
|
+
scheduleIdleWork,
|
|
7
|
+
} from "@racletteCore/lib/scheduleTask"
|
|
8
|
+
|
|
9
|
+
const GRACE_PERIOD_MS = 500
|
|
10
|
+
const deleteTimeouts: Record<string, ReturnType<typeof setTimeout>> = {}
|
|
11
|
+
|
|
12
|
+
const clearGraceTimeout = (cacheKey: string) => {
|
|
13
|
+
if (deleteTimeouts[cacheKey]) {
|
|
14
|
+
clearTimeout(deleteTimeouts[cacheKey])
|
|
15
|
+
delete deleteTimeouts[cacheKey]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const queriesCacheEffects = (store) => {
|
|
20
|
+
const queriesCacheScheduleGraceEviction = actions$.pipe(
|
|
21
|
+
ofType("queries/remove"),
|
|
22
|
+
distinctUntilChanged((prev, curr) => !store.hasChanges(prev, curr)),
|
|
23
|
+
|
|
24
|
+
tap((action) => {
|
|
25
|
+
const grace = action.actionResult?.cacheGraceEviction as
|
|
26
|
+
| { cacheKey: string; actionId: string }
|
|
27
|
+
| undefined
|
|
28
|
+
|
|
29
|
+
if (!grace?.cacheKey) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
clearGraceTimeout(grace.cacheKey)
|
|
34
|
+
deleteTimeouts[grace.cacheKey] = setTimeout(() => {
|
|
35
|
+
delete deleteTimeouts[grace.cacheKey]
|
|
36
|
+
scheduleAfterPaint(() => {
|
|
37
|
+
store.dispatch({
|
|
38
|
+
type: "queriesCache/remove",
|
|
39
|
+
payload: { cacheKey: grace.cacheKey, actionId: grace.actionId },
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
}, GRACE_PERIOD_MS)
|
|
43
|
+
}),
|
|
44
|
+
ignoreElements(),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const queriesCacheCancelGraceEviction = actions$.pipe(
|
|
48
|
+
ofType("queriesCache/add"),
|
|
49
|
+
distinctUntilChanged((prev, curr) => !store.hasChanges(prev, curr)),
|
|
50
|
+
|
|
51
|
+
tap((action) => {
|
|
52
|
+
const cancel = action.actionResult?.cacheGraceCancel as
|
|
53
|
+
| { cacheKey: string }
|
|
54
|
+
| undefined
|
|
55
|
+
|
|
56
|
+
if (cancel?.cacheKey) {
|
|
57
|
+
clearGraceTimeout(cancel.cacheKey)
|
|
58
|
+
}
|
|
59
|
+
}),
|
|
60
|
+
ignoreElements(),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const queriesCacheEvictData = actions$.pipe(
|
|
64
|
+
ofType("queriesCache/remove"),
|
|
65
|
+
distinctUntilChanged((prev, curr) => !store.hasChanges(prev, curr)),
|
|
66
|
+
|
|
67
|
+
tap((action) => {
|
|
68
|
+
const cancelled = action.actionResult?.cacheRemoveCancelled as
|
|
69
|
+
| { cacheKey: string; childCount: number }
|
|
70
|
+
| undefined
|
|
71
|
+
|
|
72
|
+
if (cancelled) {
|
|
73
|
+
log.store(
|
|
74
|
+
"cacheRemove",
|
|
75
|
+
`the cache with hash ${cancelled.cacheKey} is still/again in use by ${cancelled.childCount} queries`,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const eviction = action.actionResult?.cacheEviction as
|
|
80
|
+
| { cacheKey: string; uuids: string[] }
|
|
81
|
+
| undefined
|
|
82
|
+
|
|
83
|
+
if (!eviction?.uuids?.length) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
scheduleIdleWork(() => {
|
|
88
|
+
store.dispatch({
|
|
89
|
+
type: "data/deleteFromCache",
|
|
90
|
+
actionId: eviction.cacheKey,
|
|
91
|
+
payload: { uuids: eviction.uuids },
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
}),
|
|
95
|
+
ignoreElements(),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return [
|
|
99
|
+
queriesCacheScheduleGraceEviction,
|
|
100
|
+
queriesCacheCancelGraceEviction,
|
|
101
|
+
queriesCacheEvictData,
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default queriesCacheEffects
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import reducerFromMap from "@racletteCore/lib/reducerFromMap"
|
|
2
2
|
import { applicationState } from "@racletteCore/store/state"
|
|
3
3
|
import * as reducers from "./reducers"
|
|
4
|
+
import queriesCacheEffects from "./effects"
|
|
4
5
|
|
|
5
6
|
const reducerMap = {
|
|
6
7
|
init: () => applicationState.queriesCache || {},
|
|
@@ -12,4 +13,4 @@ const reducerMap = {
|
|
|
12
13
|
|
|
13
14
|
const queriesCacheReducers = reducerFromMap(reducerMap)
|
|
14
15
|
|
|
15
|
-
export { queriesCacheReducers }
|
|
16
|
+
export { queriesCacheEffects, queriesCacheReducers }
|
|
@@ -2,15 +2,82 @@ import createStableHash from "@racletteCore/lib/createStableHash"
|
|
|
2
2
|
import { $store } from "@racletteCore"
|
|
3
3
|
import log from "@racletteCore/lib/logger"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
export type ParentQuery = {
|
|
6
|
+
cacheKey: string
|
|
7
|
+
loading?: boolean
|
|
8
|
+
uuids?: Array<string>
|
|
9
|
+
lastFetched?: number
|
|
10
|
+
deleted?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type QueryRef = {
|
|
14
|
+
target: string
|
|
15
|
+
params: Record<string, unknown>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const DEFAULT_TTL_MS = 1000
|
|
19
|
+
|
|
20
|
+
const countChildQueriesForCacheKey = (
|
|
21
|
+
queries: Record<string, QueryRef>,
|
|
22
|
+
cacheKey: string,
|
|
23
|
+
): number => {
|
|
24
|
+
let count = 0
|
|
25
|
+
Object.values(queries).forEach((query) => {
|
|
26
|
+
const queryCacheKey = createStableHash({
|
|
27
|
+
target: query.target,
|
|
28
|
+
params: query.params,
|
|
29
|
+
})
|
|
30
|
+
if (cacheKey === queryCacheKey) {
|
|
31
|
+
count++
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
return count
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const getCacheEntryForRemovedQuery = (
|
|
38
|
+
queriesCache: Record<string, ParentQuery>,
|
|
39
|
+
removedQuery: QueryRef | null | undefined,
|
|
40
|
+
): { cacheKey: string; entry: ParentQuery } | null => {
|
|
41
|
+
if (!removedQuery) {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const cacheKey = createStableHash({
|
|
46
|
+
target: removedQuery.target,
|
|
47
|
+
params: removedQuery.params,
|
|
48
|
+
})
|
|
49
|
+
const entry = queriesCache[cacheKey]
|
|
50
|
+
|
|
51
|
+
if (!entry) {
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { cacheKey, entry }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const isCacheEntryFresh = (
|
|
59
|
+
entry: ParentQuery | undefined,
|
|
60
|
+
ttl: number = DEFAULT_TTL_MS,
|
|
61
|
+
now: number = Date.now(),
|
|
62
|
+
): boolean => {
|
|
63
|
+
if (
|
|
64
|
+
!entry?.lastFetched ||
|
|
65
|
+
!entry?.uuids ||
|
|
66
|
+
entry.loading === true
|
|
67
|
+
) {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return entry.lastFetched + ttl >= now
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const isParentResultFresh = (cacheKey: string, ttl: number = DEFAULT_TTL_MS): boolean => {
|
|
75
|
+
const queriesState = $store.getState("queriesCache") as Record<string, ParentQuery>
|
|
8
76
|
const parentQuery = queriesState[cacheKey]
|
|
9
77
|
|
|
10
78
|
if (
|
|
11
|
-
!parentQuery
|
|
12
|
-
!parentQuery
|
|
13
|
-
parentQuery.hasOwnProperty("loading") === false ||
|
|
79
|
+
!parentQuery?.lastFetched ||
|
|
80
|
+
!parentQuery?.uuids ||
|
|
14
81
|
parentQuery.loading === true
|
|
15
82
|
) {
|
|
16
83
|
log.store(
|
|
@@ -21,14 +88,14 @@ const isParentResultFresh = (cacheKey: string, ttl: number = 1000): boolean => {
|
|
|
21
88
|
return false
|
|
22
89
|
}
|
|
23
90
|
|
|
24
|
-
const
|
|
91
|
+
const fresh = isCacheEntryFresh(parentQuery, ttl)
|
|
25
92
|
|
|
26
93
|
log.store(
|
|
27
94
|
"queryCacheHelper",
|
|
28
|
-
`cache #${cacheKey} is ${
|
|
95
|
+
`cache #${cacheKey} is ${fresh ? "fresh" : "stale"} for ttl of ${ttl}ms`,
|
|
29
96
|
)
|
|
30
97
|
|
|
31
|
-
return
|
|
98
|
+
return fresh
|
|
32
99
|
}
|
|
33
100
|
|
|
34
101
|
const getParentActionIdHash = (
|
|
@@ -62,6 +129,9 @@ const hasCacheQuery = (
|
|
|
62
129
|
}
|
|
63
130
|
|
|
64
131
|
export {
|
|
132
|
+
countChildQueriesForCacheKey,
|
|
133
|
+
getCacheEntryForRemovedQuery,
|
|
134
|
+
isCacheEntryFresh,
|
|
65
135
|
getCacheQueryAndHash,
|
|
66
136
|
hasCacheQuery,
|
|
67
137
|
getParentActionIdHash,
|
|
@@ -1,135 +1,130 @@
|
|
|
1
1
|
import createStableHash from "@racletteCore/lib/createStableHash"
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
deleted?: boolean
|
|
12
|
-
}
|
|
13
|
-
const cacheMap = {}
|
|
14
|
-
const getAffectedCacheQuery = (state, actionId) => {
|
|
15
|
-
const removedQuery = $store.getState(["queries", actionId])
|
|
16
|
-
if (!removedQuery) {
|
|
17
|
-
return null
|
|
18
|
-
}
|
|
19
|
-
const removedQueryCacheKey = createStableHash({
|
|
20
|
-
target: removedQuery.target,
|
|
21
|
-
params: removedQuery.params,
|
|
22
|
-
})
|
|
23
|
-
return state[removedQueryCacheKey]
|
|
24
|
-
}
|
|
25
|
-
const getChildQueries = (cacheKey: string): Array<Record<string, object>> => {
|
|
26
|
-
const queries = $store.getState("queries")
|
|
27
|
-
const childQueries = []
|
|
28
|
-
Object.values(queries).forEach((query) => {
|
|
29
|
-
const queryCacheKey = createStableHash({
|
|
30
|
-
target: query.target,
|
|
31
|
-
params: query.params,
|
|
32
|
-
})
|
|
33
|
-
if (cacheKey === queryCacheKey) {
|
|
34
|
-
childQueries.push(query)
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
return childQueries
|
|
38
|
-
}
|
|
2
|
+
import {
|
|
3
|
+
countChildQueriesForCacheKey,
|
|
4
|
+
getCacheEntryForRemovedQuery,
|
|
5
|
+
isCacheEntryFresh,
|
|
6
|
+
type ParentQuery,
|
|
7
|
+
} from "./queryCacheHelper"
|
|
8
|
+
|
|
9
|
+
export type { ParentQuery }
|
|
10
|
+
|
|
39
11
|
const cacheAdd = (state, action) => {
|
|
40
|
-
const newState = structuredClone(state)
|
|
41
12
|
const { payload } = action
|
|
42
13
|
const { target, params, cacheKey } = payload
|
|
43
|
-
const parentQueryContent = {
|
|
44
|
-
cacheKey,
|
|
45
|
-
loading: true,
|
|
46
|
-
}
|
|
47
14
|
const hash =
|
|
48
15
|
cacheKey ||
|
|
49
16
|
createStableHash({
|
|
50
17
|
target,
|
|
51
18
|
params,
|
|
52
19
|
})
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
delete newState[hash].deleted
|
|
20
|
+
const existing = state[hash]
|
|
21
|
+
|
|
22
|
+
if (!existing) {
|
|
23
|
+
return {
|
|
24
|
+
...state,
|
|
25
|
+
[hash]: {
|
|
26
|
+
cacheKey,
|
|
27
|
+
loading: true,
|
|
28
|
+
},
|
|
63
29
|
}
|
|
64
30
|
}
|
|
65
31
|
|
|
66
|
-
|
|
32
|
+
if (!existing.deleted) {
|
|
33
|
+
return state
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
action.actionResult.cacheGraceCancel = { cacheKey: hash }
|
|
37
|
+
|
|
38
|
+
const updated = { ...existing }
|
|
39
|
+
if (!isCacheEntryFresh(existing)) {
|
|
40
|
+
// it seems that this chache got requested again during it's grace delte time period. So Let's just set our loading state to true and delete the delete flag
|
|
41
|
+
updated.loading = true
|
|
42
|
+
}
|
|
43
|
+
delete updated.deleted
|
|
44
|
+
|
|
45
|
+
return { ...state, [hash]: updated }
|
|
67
46
|
}
|
|
47
|
+
|
|
68
48
|
const cacheCheck = (state: Record<string, ParentQuery>, action) => {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const affectedCacheQuery = newState[cacheKey]
|
|
74
|
-
if (affectedCacheQuery) {
|
|
75
|
-
affectedCacheQuery.loading = false
|
|
76
|
-
affectedCacheQuery.lastFetched = new Date().getTime()
|
|
77
|
-
action.actionResult.relationships.resolved.push(`cacheQuery_${cacheKey}`)
|
|
78
|
-
if (items) {
|
|
79
|
-
affectedCacheQuery.uuids = Object.values(items).map((item) => item._id)
|
|
80
|
-
}
|
|
49
|
+
const { items, cacheKey } = action.payload
|
|
50
|
+
const affectedCacheQuery = state[cacheKey]
|
|
51
|
+
if (!affectedCacheQuery) {
|
|
52
|
+
return state
|
|
81
53
|
}
|
|
82
54
|
|
|
83
|
-
|
|
55
|
+
const updated: ParentQuery = {
|
|
56
|
+
...affectedCacheQuery,
|
|
57
|
+
loading: false,
|
|
58
|
+
lastFetched: new Date().getTime(),
|
|
59
|
+
}
|
|
60
|
+
action.actionResult.relationships.resolved.push(`cacheQuery_${cacheKey}`)
|
|
61
|
+
if (items) {
|
|
62
|
+
updated.uuids = (Object.values(items) as Array<{ _id: string }>).map(
|
|
63
|
+
(item) => item._id,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { ...state, [cacheKey]: updated }
|
|
84
68
|
}
|
|
69
|
+
|
|
85
70
|
const cacheRemove = (state, action) => {
|
|
86
|
-
const newState = structuredClone(state)
|
|
87
71
|
const { cacheKey } = action.payload
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
72
|
+
const cacheEntry = state[cacheKey]
|
|
73
|
+
const queries = action.storeLookup?.queries ?? {}
|
|
74
|
+
|
|
75
|
+
if (!cacheEntry) {
|
|
76
|
+
return state
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Grace-period timeout: cacheAdd clears `deleted` when the same parent is requested again.
|
|
80
|
+
const isGraceEviction = cacheEntry.deleted === true
|
|
81
|
+
|
|
82
|
+
if (!isGraceEviction) {
|
|
83
|
+
const childCount = countChildQueriesForCacheKey(queries, cacheKey)
|
|
84
|
+
if (childCount > 0) {
|
|
85
|
+
const updated = { ...state[cacheKey] }
|
|
86
|
+
delete updated.deleted
|
|
87
|
+
action.actionResult.cacheRemoveCancelled = { cacheKey, childCount }
|
|
88
|
+
|
|
89
|
+
return { ...state, [cacheKey]: updated }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const uuids = cacheEntry.uuids ?? []
|
|
94
|
+
if (uuids.length > 0) {
|
|
95
|
+
action.actionResult.cacheEviction = { cacheKey, uuids }
|
|
103
96
|
}
|
|
104
97
|
|
|
98
|
+
const newState = { ...state }
|
|
99
|
+
delete newState[cacheKey]
|
|
105
100
|
return newState
|
|
106
101
|
}
|
|
107
102
|
|
|
108
103
|
const cacheChildRemove = (state, action) => {
|
|
109
|
-
const newState = structuredClone(state)
|
|
110
104
|
const { actionId } = action.payload
|
|
111
105
|
if (!actionId) return state
|
|
112
106
|
|
|
113
|
-
const
|
|
114
|
-
|
|
107
|
+
const removedQuery = action.storeLookup?.queries?.[actionId]
|
|
108
|
+
const cacheRef = getCacheEntryForRemovedQuery(state, removedQuery)
|
|
109
|
+
if (!cacheRef) {
|
|
115
110
|
return state
|
|
116
111
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
|
|
113
|
+
const { cacheKey } = cacheRef
|
|
114
|
+
const queries = action.storeLookup?.queries ?? {}
|
|
115
|
+
const childCount = countChildQueriesForCacheKey(queries, cacheKey)
|
|
116
|
+
|
|
117
|
+
if (childCount === 1) {
|
|
118
|
+
// if the action which is currently deleted is the last one needing this cache, we will give it a grace period before we remove the cache
|
|
119
|
+
action.actionResult.cacheGraceEviction = { cacheKey, actionId }
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
...state,
|
|
123
|
+
[cacheKey]: { ...state[cacheKey], deleted: true },
|
|
124
124
|
}
|
|
125
|
-
deleteTimeouts[affectedCacheQuery.cacheKey] = setTimeout(() => {
|
|
126
|
-
$store.dispatch({
|
|
127
|
-
type: "queriesCache/remove",
|
|
128
|
-
payload: { cacheKey: affectedCacheQuery.cacheKey, actionId },
|
|
129
|
-
})
|
|
130
|
-
}, 500)
|
|
131
125
|
}
|
|
132
|
-
|
|
133
|
-
return
|
|
126
|
+
|
|
127
|
+
return state
|
|
134
128
|
}
|
|
129
|
+
|
|
135
130
|
export { cacheAdd, cacheCheck, cacheChildRemove, cacheRemove }
|