Skip to content

Commit

Permalink
Use composable also for shootStore
Browse files Browse the repository at this point in the history
  • Loading branch information
holgerkoser committed Dec 12, 2024
1 parent 3d71628 commit 17ed53c
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 141 deletions.
6 changes: 0 additions & 6 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,6 @@ onKeyStroke('Escape', e => {
e.preventDefault()
})

watch(visibility, (current, previous) => {
if (current === 'visible' && previous === 'hidden') {
shootStore.invokeSubscriptionEventHandler()
}
})

const documentTitle = computed(() => {
let appTitle = process.env.VITE_APP_TITLE
const branding = configStore.branding ?? loginStore.branding
Expand Down
46 changes: 32 additions & 14 deletions frontend/src/composables/useSocketEventHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
// SPDX-License-Identifier: Apache-2.0
//

import { watch } from 'vue'
import { useDocumentVisibility } from '@vueuse/core'

import { useSocketStore } from '@/store/socket'

import { useLogger } from '@/composables/useLogger'

import { isTooManyRequestsError } from '@/utils/errors'

import partial from 'lodash/partial'
import throttle from 'lodash/throttle'
import findIndex from 'lodash/findIndex'

Expand Down Expand Up @@ -38,9 +42,13 @@ export function createListOperator (list) {
}
}

export function useSocketEventHandler (options = {}) {
export function useSocketEventHandler (useStore, options = {}) {
const kind = useStore.name.replace(/^use([a-zA-Z]+)Store$/, '$1')
const {
singularName = kind.toLowerCase(),
pluralName = singularName + 's',
logger = useLogger(),
socketStore = useSocketStore(),
visibility = useDocumentVisibility(),
createOperator = createDefaultOperator,
} = options
Expand All @@ -61,10 +69,10 @@ export function useSocketEventHandler (options = {}) {
uids.push(uid)
}
}
const items = await store.fetchObjects(uids)
const items = await socketStore.synchronize(pluralName, uids)
for (const item of items) {
if (item.kind === 'Status') {
logger.info('Failed to synchronize a single project: %s', item.message)
logger.info('Failed to synchronize a single %s: %s', singularName, item.message)
if (item.code === 404) {
const uid = item.details?.uid
if (uid) {
Expand Down Expand Up @@ -93,9 +101,9 @@ export function useSocketEventHandler (options = {}) {
})
} catch (err) {
if (isTooManyRequestsError(err)) {
logger.info('Skipped synchronization of modified projects: %s', err.message)
logger.info('Skipped synchronization of modified %s: %s', pluralName, err.message)
} else {
logger.error('Failed to synchronize modified projects: %s', err.message)
logger.error('Failed to synchronize modified %s: %s', pluralName, err.message)
}
// Synchronization failed -> Rollback events
for (const event of events) {
Expand All @@ -115,21 +123,25 @@ export function useSocketEventHandler (options = {}) {
}
}

function startSocketEventHandler (wait = 500) {
function start (wait = 500) {
cancelTrailingInvocation()
eventMap.clear()
const store = useStore()
const boundHandleEvents = partial(handleEvents, store)
throttledHandleEvents = wait > 0
? throttle(handleEvents, wait)
: store => handleEvents(store)
? throttle(boundHandleEvents, wait)
: boundHandleEvents
return this
}

function stopSocketEventHandler () {
function stop () {
cancelTrailingInvocation()
eventMap.clear()
throttledHandleEvents = undefined
return this
}

function onSocketEvent (event) {
function listener (event) {
if (typeof throttledHandleEvents !== 'function') {
return
}
Expand All @@ -142,12 +154,18 @@ export function useSocketEventHandler (options = {}) {
if (visibility.value !== 'visible') {
return
}
throttledHandleEvents(this)
throttledHandleEvents()
}

watch(visibility, (current, previous) => {
if (current === 'visible' && previous === 'hidden') {
throttledHandleEvents()
}
})

return {
startSocketEventHandler,
stopSocketEventHandler,
onSocketEvent,
start,
stop,
listener,
}
}
22 changes: 4 additions & 18 deletions frontend/src/store/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { useSocketEventHandler } from '@/composables/useSocketEventHandler'

import { useAuthzStore } from './authz'
import { useAppStore } from './app'
import { useSocketStore } from './socket'

import filter from 'lodash/filter'
import find from 'lodash/find'
Expand All @@ -35,7 +34,6 @@ export const useProjectStore = defineStore('project', () => {
const logger = useLogger()
const appStore = useAppStore()
const authzStore = useAuthzStore()
const socketStore = useSocketStore()

const list = ref(null)

Expand Down Expand Up @@ -159,20 +157,9 @@ export const useProjectStore = defineStore('project', () => {
// do not remove project from store as it will stay in terminating phase for a while
}

const {
startSocketEventHandler,
onSocketEvent,
} = useSocketEventHandler({ logger })

startSocketEventHandler(500)

function handleEvent (event) {
onSocketEvent.call(this, event)
}

function fetchObjects (uids) {
return socketStore.synchronize('projects', uids)
}
const socketEventHandler = useSocketEventHandler(useProjectStore, {
logger,
}).start(500)

async function $reset () {
list.value = null
Expand All @@ -197,8 +184,7 @@ export const useProjectStore = defineStore('project', () => {
updateProject,
deleteProject,
projectNameByNamespace,
handleEvent,
fetchObjects,
handleEvent: socketEventHandler.listener,
$reset,
}
})
Expand Down
127 changes: 24 additions & 103 deletions frontend/src/store/shoot/shoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ import {
toRaw,
toRef,
} from 'vue'
import { useDocumentVisibility } from '@vueuse/core'

import { useLogger } from '@/composables/useLogger'
import { useApi } from '@/composables/useApi'
import { useProjectShootCustomFields } from '@/composables/useProjectShootCustomFields'
import { useSocketEventHandler } from '@/composables/useSocketEventHandler'

import { isNotFound } from '@/utils/error'
import { isTooManyRequestsError } from '@/utils/errors'

import { useAppStore } from '../app'
import { useAuthnStore } from '../authn'
Expand All @@ -48,7 +47,6 @@ import {

import isEqual from 'lodash/isEqual'
import isEmpty from 'lodash/isEmpty'
import throttle from 'lodash/throttle'
import includes from 'lodash/includes'
import find from 'lodash/find'
import difference from 'lodash/difference'
Expand All @@ -61,7 +59,6 @@ import get from 'lodash/get'
const useShootStore = defineStore('shoot', () => {
const api = useApi()
const logger = useLogger()
const visibility = useDocumentVisibility()

const appStore = useAppStore()
const authnStore = useAuthnStore()
Expand Down Expand Up @@ -558,30 +555,11 @@ const useShootStore = defineStore('shoot', () => {
})
}

function cancelSubscriptionEventHandler (state) {
if (typeof state.subscriptionEventHandler?.cancel === 'function') {
state.subscriptionEventHandler.cancel()
}
}

function setSubscriptionEventHandler (state, func, throttleDelay) {
if (throttleDelay > 0) {
func = throttle(func, throttleDelay)
}
state.subscriptionEventHandler = markRaw(func)
}

function unsetSubscriptionEventHandler (state) {
state.subscriptionEventHandler = undefined
}

async function openSubscription (value, throttleDelay) {
const shootStore = this
shootStore.$patch(({ state }) => {
setSubscriptionState(state, constants.OPENING)
cancelSubscriptionEventHandler(state)
shootEvents.clear()
setSubscriptionEventHandler(state, handleEvents, throttleDelay)
socketEventHandler.start(throttleDelay)
})
try {
await socketStore.emitSubscribe(value)
Expand All @@ -600,9 +578,7 @@ const useShootStore = defineStore('shoot', () => {
shootStore.$patch(({ state }) => {
state.subscription = null
setSubscriptionState(state, constants.CLOSING)
cancelSubscriptionEventHandler(state)
shootEvents.clear()
unsetSubscriptionEventHandler(state)
socketEventHandler.stop()
})
try {
await socketStore.emitUnsubscribe()
Expand All @@ -613,87 +589,33 @@ const useShootStore = defineStore('shoot', () => {
}
}

async function handleEvents (shootStore) {
const events = Array.from(shootEvents.values())
shootEvents.clear()
const uids = []
const deletedUids = []
for (const { type, uid } of events) {
if (type === 'DELETED') {
deletedUids.push(uid)
} else {
uids.push(uid)
}
}
try {
const items = await fetchObjects(uids)
function isShootActive (uid) {
return includes(activeUids.value, uid)
}

const socketEventHandler = useSocketEventHandler('shoots', {
logger,
createOperator ({ state }) {
const notOnlyShootsWithIssues = !onlyAllShootsWithIssues(state, context)
shootStore.$patch(({ state }) => {
for (const uid of deletedUids) {
if (state.focusMode) {
const value = get(state.shoots, [uid])
set(state.staleShoots, [uid], value)
}
unset(state.shoots, [uid])
}
for (const item of items) {
if (item.kind === 'Status') {
logger.info('Failed to synchronize a single shoot: %s', item.message)
if (item.code === 404) {
const uid = item.details?.uid
if (uid) {
unset(state.shoots, [uid])
}
}
} else if (notOnlyShootsWithIssues || shootHasIssue(item)) {
const uid = item.metadata.uid
return {
set (uid, item) {
if (notOnlyShootsWithIssues || shootHasIssue(item)) {
if (state.focusMode) {
unset(state.staleShoots, [uid])
}
set(state.shoots, [uid], markRaw(item))
}
}
})
} catch (err) {
if (isTooManyRequestsError(err)) {
logger.info('Skipped synchronization of modified shoots: %s', err.message)
} else {
logger.error('Failed to synchronize modified shoots: %s', err.message)
}
// Synchronization failed. Rollback shoot events
for (const event of events) {
const { uid } = event
if (!shootEvents.has(uid)) {
shootEvents.set(uid, event)
}
},
delete (uid) {
if (state.focusMode) {
const value = get(state.shoots, [uid])
set(state.staleShoots, [uid], value)
}
unset(state.shoots, [uid])
},
}
}
}

function fetchObjects (uids) {
return socketStore.synchronize('shoots', uids)
}

function handleEvent (event) {
const shootStore = this
const { type, uid } = event
if (!['ADDED', 'MODIFIED', 'DELETED'].includes(type)) {
logger.error('undhandled event type', type)
return
}
shootEvents.set(uid, event)
shootStore.invokeSubscriptionEventHandler()
}

function isShootActive (uid) {
return includes(activeUids.value, uid)
}

function invokeSubscriptionEventHandler () {
if (typeof state.subscriptionEventHandler === 'function' && visibility.value === 'visible') {
state.subscriptionEventHandler(this)
}
}
},
})

return {
// state
Expand Down Expand Up @@ -724,7 +646,6 @@ const useShootStore = defineStore('shoot', () => {
unsubscribe,
unsubscribeShoots,
closeSubscription,
handleEvent,
deleteShoot,
fetchInfo,
setSelection,
Expand All @@ -736,7 +657,7 @@ const useShootStore = defineStore('shoot', () => {
sortItems,
setSortBy,
isShootActive,
invokeSubscriptionEventHandler,
handleEvent: socketEventHandler.listener,
}
})

Expand Down

0 comments on commit 17ed53c

Please sign in to comment.