288 lines
8.4 KiB
JavaScript
288 lines
8.4 KiB
JavaScript
import { writable, get } from "svelte/store"
|
|
import { cloneDeep } from "lodash/fp"
|
|
import api from "../api"
|
|
|
|
const INITIAL_BACKEND_UI_STATE = {
|
|
tables: [],
|
|
views: [],
|
|
users: [],
|
|
roles: [],
|
|
datasources: [],
|
|
selectedDatabase: {},
|
|
selectedTable: {},
|
|
draftTable: {},
|
|
}
|
|
|
|
export const getBackendUiStore = () => {
|
|
const store = writable({ ...INITIAL_BACKEND_UI_STATE })
|
|
|
|
store.actions = {
|
|
reset: () => store.set({ ...INITIAL_BACKEND_UI_STATE }),
|
|
database: {
|
|
select: async db => {
|
|
const tablesResponse = await api.get(`/api/tables`)
|
|
const tables = await tablesResponse.json()
|
|
const datasourcesResponse = await api.get(`/api/datasources`)
|
|
const datasources = await datasourcesResponse.json()
|
|
store.update(state => {
|
|
state.selectedDatabase = db
|
|
state.tables = tables
|
|
state.datasources = datasources
|
|
return state
|
|
})
|
|
},
|
|
},
|
|
rows: {
|
|
save: () =>
|
|
store.update(state => {
|
|
state.selectedView = state.selectedView
|
|
return state
|
|
}),
|
|
delete: () =>
|
|
store.update(state => {
|
|
state.selectedView = state.selectedView
|
|
return state
|
|
}),
|
|
select: row =>
|
|
store.update(state => {
|
|
state.selectedRow = row
|
|
return state
|
|
}),
|
|
},
|
|
datasources: {
|
|
fetch: async () => {
|
|
const response = await api.get(`/api/datasources`)
|
|
const json = await response.json()
|
|
store.update(state => {
|
|
state.datasources = json
|
|
return state
|
|
})
|
|
return json
|
|
},
|
|
select: async datasourceId => {
|
|
store.update(state => {
|
|
state.selectedDatasourceId = datasourceId
|
|
state.selectedQueryId = null
|
|
return state
|
|
})
|
|
},
|
|
save: async datasource => {
|
|
const response = await api.post("/api/datasources", datasource)
|
|
const json = await response.json()
|
|
store.update(state => {
|
|
const currentIdx = state.datasources.findIndex(
|
|
ds => ds._id === json._id
|
|
)
|
|
|
|
if (currentIdx >= 0) {
|
|
state.datasources.splice(currentIdx, 1, json)
|
|
} else {
|
|
state.datasources.push(json)
|
|
}
|
|
|
|
state.datasources = state.datasources
|
|
state.selectedDatasourceId = json._id
|
|
return state
|
|
})
|
|
return json
|
|
},
|
|
delete: async datasource => {
|
|
await api.delete(
|
|
`/api/datasources/${datasource._id}/${datasource._rev}`
|
|
)
|
|
store.update(state => {
|
|
state.datasources = state.datasources.filter(
|
|
existing => existing._id !== datasource._id
|
|
)
|
|
state.selectedDatasourceId = null
|
|
return state
|
|
})
|
|
},
|
|
saveQuery: async (datasourceId, query) => {
|
|
const response = await api.post(
|
|
`/api/datasources/${datasourceId}/queries`,
|
|
query
|
|
)
|
|
const json = await response.json()
|
|
store.update(state => {
|
|
const currentIdx = state.datasources.findIndex(
|
|
ds => ds._id === json._id
|
|
)
|
|
|
|
if (currentIdx >= 0) {
|
|
state.datasources.splice(currentIdx, 1, json)
|
|
} else {
|
|
state.datasources.push(json)
|
|
}
|
|
|
|
state.datasources = state.datasources
|
|
return state
|
|
})
|
|
},
|
|
},
|
|
queries: {
|
|
select: queryId =>
|
|
store.update(state => {
|
|
state.selectedDatasourceId = null
|
|
state.selectedQueryId = queryId
|
|
return state
|
|
}),
|
|
},
|
|
tables: {
|
|
fetch: async () => {
|
|
const tablesResponse = await api.get(`/api/tables`)
|
|
const tables = await tablesResponse.json()
|
|
store.update(state => {
|
|
state.tables = tables
|
|
return state
|
|
})
|
|
},
|
|
select: table =>
|
|
store.update(state => {
|
|
state.selectedTable = table
|
|
state.draftTable = cloneDeep(table)
|
|
state.selectedView = { name: `all_${table._id}` }
|
|
return state
|
|
}),
|
|
save: async table => {
|
|
const updatedTable = cloneDeep(table)
|
|
const oldTable = get(store).tables.filter(t => t._id === table._id)[0]
|
|
|
|
const fieldNames = []
|
|
// update any renamed schema keys to reflect their names
|
|
for (let key of Object.keys(updatedTable.schema)) {
|
|
// if field name has been seen before remove it
|
|
if (fieldNames.indexOf(key.toLowerCase()) !== -1) {
|
|
delete updatedTable.schema[key]
|
|
continue
|
|
}
|
|
const field = updatedTable.schema[key]
|
|
const oldField = oldTable?.schema[key]
|
|
// if the type has changed then revert back to the old field
|
|
if (oldField != null && oldField.type !== field.type) {
|
|
updatedTable.schema[key] = oldField
|
|
}
|
|
// field has been renamed
|
|
if (field.name && field.name !== key) {
|
|
updatedTable.schema[field.name] = field
|
|
updatedTable._rename = { old: key, updated: field.name }
|
|
delete updatedTable.schema[key]
|
|
}
|
|
// finally record this field has been used
|
|
fieldNames.push(key.toLowerCase())
|
|
}
|
|
|
|
const SAVE_TABLE_URL = `/api/tables`
|
|
const response = await api.post(SAVE_TABLE_URL, updatedTable)
|
|
const savedTable = await response.json()
|
|
await store.actions.tables.fetch()
|
|
store.actions.tables.select(savedTable)
|
|
return savedTable
|
|
},
|
|
delete: async table => {
|
|
await api.delete(`/api/tables/${table._id}/${table._rev}`)
|
|
store.update(state => {
|
|
state.tables = state.tables.filter(
|
|
existing => existing._id !== table._id
|
|
)
|
|
state.selectedTable = {}
|
|
return state
|
|
})
|
|
},
|
|
saveField: ({ originalName, field, primaryDisplay = false }) => {
|
|
store.update(state => {
|
|
// delete the original if renaming
|
|
// need to handle if the column had no name, empty string
|
|
if (originalName || originalName === "") {
|
|
delete state.draftTable.schema[originalName]
|
|
state.draftTable._rename = {
|
|
old: originalName,
|
|
updated: field.name,
|
|
}
|
|
}
|
|
|
|
// Optionally set display column
|
|
if (primaryDisplay) {
|
|
state.draftTable.primaryDisplay = field.name
|
|
}
|
|
|
|
state.draftTable.schema[field.name] = cloneDeep(field)
|
|
store.actions.tables.save(state.draftTable)
|
|
return state
|
|
})
|
|
},
|
|
deleteField: field => {
|
|
store.update(state => {
|
|
delete state.draftTable.schema[field.name]
|
|
store.actions.tables.save(state.draftTable)
|
|
return state
|
|
})
|
|
},
|
|
},
|
|
views: {
|
|
select: view =>
|
|
store.update(state => {
|
|
state.selectedView = view
|
|
state.selectedTable = {}
|
|
return state
|
|
}),
|
|
delete: async view => {
|
|
await api.delete(`/api/views/${view}`)
|
|
await store.actions.tables.fetch()
|
|
},
|
|
save: async view => {
|
|
const response = await api.post(`/api/views`, view)
|
|
const json = await response.json()
|
|
|
|
const viewMeta = {
|
|
name: view.name,
|
|
...json,
|
|
}
|
|
|
|
store.update(state => {
|
|
const viewTable = state.tables.find(
|
|
table => table._id === view.tableId
|
|
)
|
|
|
|
if (view.originalName) delete viewTable.views[view.originalName]
|
|
viewTable.views[view.name] = viewMeta
|
|
|
|
state.tables = state.tables
|
|
state.selectedView = viewMeta
|
|
return state
|
|
})
|
|
},
|
|
},
|
|
users: {
|
|
create: user =>
|
|
store.update(state => {
|
|
state.users.push(user)
|
|
state.users = state.users
|
|
return state
|
|
}),
|
|
},
|
|
roles: {
|
|
fetch: async () => {
|
|
const response = await api.get("/api/roles")
|
|
const roles = await response.json()
|
|
store.update(state => {
|
|
state.roles = roles
|
|
return state
|
|
})
|
|
},
|
|
delete: async role => {
|
|
const response = await api.delete(`/api/roles/${role._id}/${role._rev}`)
|
|
await store.actions.roles.fetch()
|
|
return response
|
|
},
|
|
save: async role => {
|
|
const response = await api.post("/api/roles", role)
|
|
await store.actions.roles.fetch()
|
|
return response
|
|
},
|
|
},
|
|
}
|
|
|
|
return store
|
|
}
|