Skip to content

Commit

Permalink
added fe implementation for bucket purge
Browse files Browse the repository at this point in the history
  • Loading branch information
pricelessrabbit committed Jun 22, 2024
1 parent a448c78 commit f33ebb0
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 135 deletions.
21 changes: 21 additions & 0 deletions frontend/docs/entities/kv-bucket/purge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

## PURGE DELETED KEYS
purge all the bucket keys already marked as deleted


### URL
```
DELETE /api/connection/:connection_id/kv/:bucket/purge_deleted
```
- `connection_id`
uuid of the connection to get the bucket from
- `bucket`
name of the bucket


### BODY
`null`


### RESPONSE
204
2 changes: 1 addition & 1 deletion frontend/package.json.md5
Original file line number Diff line number Diff line change
@@ -1 +1 @@
792c39dbe5875ea4aebf6aaeb14763c3
5c50c55ed1d570e6761fe26bdee5a402
2 changes: 1 addition & 1 deletion frontend/src/api/buckets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function create(connectionId: string, bucket: BucketConfig, opt?: CallOptions):

/** PURGE DELETED keys from bucket */
function purgeDeleted(connectionId: string, bucketName: string, opt?: CallOptions): Promise<void> {
return ajax.post(`connection/${connectionId}/kv/${bucketName}/purge`, null, opt)
return ajax.post(`connection/${connectionId}/kv/${bucketName}/purge_deleted`, null, opt)
}


Expand Down
5 changes: 5 additions & 0 deletions frontend/src/components/stacks/buckets/list/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const BucketsListView: FunctionComponent<Props> = ({
const handleSelect = (bucket: BucketState) => bucketsSo.select(bucket.bucket)
const handleNew = () => bucketsSo.create()
const handleDelete = () => bucketsSo.delete()
const handlePurge = () => bucketsSo.purgeDeleted()

// RENDER
const buckets = bucketsSo.getFiltered() ?? []
Expand All @@ -52,6 +53,10 @@ const BucketsListView: FunctionComponent<Props> = ({
value={bucketsSa.textSearch}
onChange={text => bucketsSo.setTextSearch(text)}
/>
{!!selected && <Button
children="PURGE"
onClick={handlePurge}
/>}
{!!selected && <Button
children="DELETE"
onClick={handleDelete}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/mocks/ajax/handlers/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const handlers = [
)
}),

rest.post('/api/connection/:cnnId/kv/:bucketName', async (req, res, ctx) => {
rest.post('/api/connection/:cnnId/kv/:bucketName/purge_deleted', async (req, res, ctx) => {
keyValueEntries_S.splice(0, keyValueEntries_S.length, ...keyValueEntries_S.filter((entry) => !entry.is_deleted))
return res(ctx.status(204))
})
Expand Down
284 changes: 152 additions & 132 deletions frontend/src/stores/stacks/buckets/index.ts
Original file line number Diff line number Diff line change
@@ -1,149 +1,169 @@
import bucketApi from "@/api/buckets"
import cnnSo from "@/stores/connections"
import { COLOR_VAR } from "@/stores/layout"
import { ViewState, ViewStore, default as docSetup, default as viewSetup } from "@/stores/stacks/viewBase"
import { BucketState } from "@/types/Bucket"
import { StoreCore, mixStores } from "@priolo/jon"
import loadBaseSetup, { LoadBaseState, LoadBaseStore } from "../loadBase"
import { buildBucket, buildBucketNew } from "./utils/factory"
import { MESSAGE_TYPE } from "@/stores/log/utils"
import { findAll } from "@/stores/docs/utils/manage"
import { GetAllCards } from "@/stores/docs/cards"
import { DOC_TYPE } from "@/types"

import {COLOR_VAR} from "@/stores/layout"
import {default as docSetup, default as viewSetup, ViewState, ViewStore} from "@/stores/stacks/viewBase"
import {BucketState} from "@/types/Bucket"
import {mixStores, StoreCore} from "@priolo/jon"
import loadBaseSetup, {LoadBaseState, LoadBaseStore} from "../loadBase"
import {buildBucket, buildBucketNew} from "./utils/factory"
import {MESSAGE_TYPE} from "@/stores/log/utils"
import {findAll, forEachViews} from "@/stores/docs/utils/manage"
import {GetAllCards} from "@/stores/docs/cards"
import {DOC_TYPE} from "@/types"
import {KVEntriesStore} from "@/stores/stacks/kventry";


/** BUCKETS COLLECTION */
const setup = {

state: {
/** connessione di riferimento */
connectionId: <string>null,
/** elemento selezionato */
select: <string>null,
/** all elements */
all: <BucketState[]>null,
textSearch: <string>null,

//#region VIEWBASE
width: 310,
colorVar: COLOR_VAR.MINT,
//#endregion
},

getters: {

//#region VIEWBASE
getTitle: (_: void, store?: ViewStore) => "BUCKETS",
getSubTitle: (_: void, store?: ViewStore) => cnnSo.getById((<BucketsStore>store).state.connectionId)?.name,
getSerialization: (_: void, store?: ViewStore) => {
const state = store.state as BucketsState
return {
...viewSetup.getters.getSerialization(null, store),
connectionId: state.connectionId,
select: state.select,
}
},
//#endregion

getByName(name: string, store?: BucketsStore) {
if (!name) return null
return store.state.all?.find(s => s.bucket == name)
},
getIndexByName(name: string, store?: BucketsStore) {
if (!name) return -1
return store.state.all?.findIndex(s => s.bucket == name)
},
/** filtrati e da visualizzare in lista */
getFiltered(_: void, store?: BucketsStore) {
const text = store.state.textSearch?.toLocaleLowerCase()?.trim()
if (!text || text.trim().length == 0 || !store.state.all) return store.state.all
return store.state.all.filter(bucket =>
bucket.bucket.toLowerCase().includes(text)
)
}
},

actions: {

//#region OVERWRITE
setSerialization: (data: any, store?: ViewStore) => {
viewSetup.actions.setSerialization(data, store)
const state = store.state as BucketsState
state.connectionId = data.connectionId
state.select = data.select
},
async fetch(_: void, store?: LoadBaseStore) {
const s = <BucketsStore>store
const buckets = await bucketApi.index(s.state.connectionId, { store, manageAbort: true })
s.setAll(buckets)
await loadBaseSetup.actions.fetch(_, store)
},
//#endregion

async fetchIfVoid(_: void, store?: BucketsStore) {
if (!!store.state.all) return
await store.fetch()
},
/** apro la CARD per creare un nuovo elemento */
create(_: void, store?: BucketsStore) {
const view = buildBucketNew(store.state.connectionId)
store.state.group.addLink({ view, parent: store, anim: true })
store.setSelect(null)
},
async delete(_: void, store?: BucketsStore) {
if (!await store.alertOpen({
title: "BUCKET DELETION",
body: "This action is irreversible.\nAre you sure you want to delete the BUCKET?",
})) return

const name = store.state.select
await bucketApi.remove(store.state.connectionId, name, { store })
store.setAll(store.state.all.filter(b => b.bucket != name))
store.setSelect(null)

// cerco eventuali CARD di questo stream e lo chiudo
const cardStreams = findAll(GetAllCards(), { type: DOC_TYPE.BUCKET, connectionId: store.state.connectionId })
cardStreams.forEach(view => view.state.group.remove({ view, anim: true }))

store.setSnackbar({
open: true, type: MESSAGE_TYPE.SUCCESS, timeout: 5000,
title: "DELETED",
body: "it is gone forever",
})
},



update(bucket: BucketState, store?: BucketsStore) {
const all = [...store.state.all]
const index = store.getIndexByName(bucket.bucket)
index == -1 ? all.push(bucket) : (all[index] = { ...all[index], ...bucket })
store.setAll(all)
},
/** apro la CARD del dettaglio */
select(name: string, store?: BucketsStore) {
const nameOld = store.state.select
const nameNew = (name && nameOld !== name) ? name : null
const view = nameNew ? buildBucket(store.state.connectionId, store.getByName(nameNew)) : null
store.setSelect(nameNew)
store.state.group.addLink({ view, parent: store, anim: !nameOld || !nameNew })
},
},

mutators: {
setAll: (all: BucketState[]) => ({ all }),
setSelect: (select: string) => ({ select }),
setTextSearch: (textSearch: string) => ({ textSearch }),
},
state: {
/** connessione di riferimento */
connectionId: <string>null,
/** elemento selezionato */
select: <string>null,
/** all elements */
all: <BucketState[]>null,
textSearch: <string>null,

//#region VIEWBASE
width: 310,
colorVar: COLOR_VAR.MINT,
//#endregion
},

getters: {

//#region VIEWBASE
getTitle: (_: void, store?: ViewStore) => "BUCKETS",
getSubTitle: (_: void, store?: ViewStore) => cnnSo.getById((<BucketsStore>store).state.connectionId)?.name,
getSerialization: (_: void, store?: ViewStore) => {
const state = store.state as BucketsState
return {
...viewSetup.getters.getSerialization(null, store),
connectionId: state.connectionId,
select: state.select,
}
},
//#endregion

getByName(name: string, store?: BucketsStore) {
if (!name) return null
return store.state.all?.find(s => s.bucket == name)
},
getIndexByName(name: string, store?: BucketsStore) {
if (!name) return -1
return store.state.all?.findIndex(s => s.bucket == name)
},
/** filtrati e da visualizzare in lista */
getFiltered(_: void, store?: BucketsStore) {
const text = store.state.textSearch?.toLocaleLowerCase()?.trim()
if (!text || text.trim().length == 0 || !store.state.all) return store.state.all
return store.state.all.filter(bucket =>
bucket.bucket.toLowerCase().includes(text)
)
}
},

actions: {

//#region OVERWRITE
setSerialization: (data: any, store?: ViewStore) => {
viewSetup.actions.setSerialization(data, store)
const state = store.state as BucketsState
state.connectionId = data.connectionId
state.select = data.select
},
async fetch(_: void, store?: LoadBaseStore) {
const s = <BucketsStore>store
const buckets = await bucketApi.index(s.state.connectionId, {store, manageAbort: true})
s.setAll(buckets)
await loadBaseSetup.actions.fetch(_, store)
},
//#endregion

async fetchIfVoid(_: void, store?: BucketsStore) {
if (!!store.state.all) return
await store.fetch()
},
/** open related card to create new element */
create(_: void, store?: BucketsStore) {
const view = buildBucketNew(store.state.connectionId)
store.state.group.addLink({view, parent: store, anim: true})
store.setSelect(null)
},
async delete(_: void, store?: BucketsStore) {
if (!await store.alertOpen({
title: "BUCKET DELETION",
body: "This action is irreversible.\nAre you sure you want to delete the BUCKET?",
})) return

const name = store.state.select
await bucketApi.remove(store.state.connectionId, name, {store})
store.setAll(store.state.all.filter(b => b.bucket != name))
store.setSelect(null)

// find other cards related to the bucket to close it
const cardbuckets = findAll(GetAllCards(), {type: DOC_TYPE.BUCKET, connectionId: store.state.connectionId})
cardbuckets.forEach(view => view.state.group.remove({view, anim: true}))

store.setSnackbar({
open: true, type: MESSAGE_TYPE.SUCCESS, timeout: 5000,
title: "DELETED",
body: "it is gone forever",
})
},
async purgeDeleted(_: void, store?: BucketsStore) {
if (!await store.alertOpen({
title: "PURGE DELETED KEYS",
body: "This action is irreversible and will remove all the keys flagged as deleted.\nAre you sure?",
})) return

const name = store.state.select
await bucketApi.purgeDeleted(store.state.connectionId, name, {store})

//find other cards related to the same bucket keys and refresh them to remove the deleted ones
forEachViews(GetAllCards(),(view) => {
if (view.state.type === DOC_TYPE.KVENTRIES) {
const v = view as KVEntriesStore
if (v.state.connectionId == store.state.connectionId && v.state.bucket.bucket == name) {
v.fetch()
}
}
})

},

update(bucket: BucketState, store?: BucketsStore) {
const all = [...store.state.all]
const index = store.getIndexByName(bucket.bucket)
index == -1 ? all.push(bucket) : (all[index] = {...all[index], ...bucket})
store.setAll(all)
},
/** apro la CARD del dettaglio */
select(name: string, store?: BucketsStore) {
const nameOld = store.state.select
const nameNew = (name && nameOld !== name) ? name : null
const view = nameNew ? buildBucket(store.state.connectionId, store.getByName(nameNew)) : null
store.setSelect(nameNew)
store.state.group.addLink({view, parent: store, anim: !nameOld || !nameNew})
},
},

mutators: {
setAll: (all: BucketState[]) => ({all}),
setSelect: (select: string) => ({select}),
setTextSearch: (textSearch: string) => ({textSearch}),
},
}

export type BucketsState = typeof setup.state & ViewState & LoadBaseState
export type BucketsGetters = typeof setup.getters
export type BucketsActions = typeof setup.actions
export type BucketsMutators = typeof setup.mutators

export interface BucketsStore extends ViewStore, LoadBaseStore, StoreCore<BucketsState>, BucketsGetters, BucketsActions, BucketsMutators {
state: BucketsState
state: BucketsState
}

const bucketsSetup = mixStores(docSetup, loadBaseSetup, setup)
export default bucketsSetup

0 comments on commit f33ebb0

Please sign in to comment.