Files
supabase/studio/localStores/sqlEditor/SqlEditorStore.js
T

377 lines
9.2 KiB
JavaScript

import { createContext, useContext } from 'react'
import { makeAutoObservable, toJS } from 'mobx'
import { IS_PLATFORM } from 'lib/constants'
import Tab from './Tab'
import QueryTab from './QueryTab'
import Favorite from './Favorite'
import { SchemasQuery, TableColumnsQuery, AllFunctionsQuery } from './queries'
import { isUndefined } from 'lodash'
let store = null
let projectRef = null
let metaProjectRef = null
export function useSqlEditorStore(ref, meta) {
if ((ref && ref !== projectRef) || (meta?.projectRef && meta.projectRef !== metaProjectRef)) {
projectRef = ref
metaProjectRef = meta.projectRef
store = new SqlEditorStore(ref, meta)
}
if (!ref || !meta) {
store = null
}
return store
}
export const TAB_TYPES = {
WELCOME: 'WELCOME',
SQL_QUERY: 'SQL_QUERY',
}
export const UTILITY_TAB_TYPES = {
RESULTS: 'RESULTS',
}
export const SqlEditorContext = createContext(null)
export const useSqlStore = () => {
return useContext(SqlEditorContext)
}
class SqlEditorStore {
meta
projectRef
localStorageKey
tabs = []
favorites = []
selectedTabId
keywordCache = []
schemaCache = []
tableCache = []
functionCache = []
constructor(projectRef, meta) {
makeAutoObservable(this, {
keywordsCache: false,
schemaCache: false,
tableCache: false,
functionCache: false,
})
this.meta = meta
this.projectRef = projectRef
this.localStorageKey = `supabase-sql-${projectRef}`
this.loadKeywords()
this.loadSchemas()
this.loadTablesAndColumns()
this.loadAllFunctions()
}
get LOCALSTORAGE_SAVED_KEY() {
return `supabase-queries-state-${this.projectRef}`
}
get activeTab() {
if (this.tabs.length === 1) {
this.selectedTabId = this.tabs[0].id
}
const found = this.tabs.find((x) => x.id === this.selectedTabId)
return found
}
get nextQueryTabName() {
for (var i = 1; i < 20; i++) {
const name = `Query-${i}`
const found = this.tabs.find((x) => x.name.includes(name))
if (found == undefined) return name
}
return `Query`
}
get isActiveQueyTabFavorited() {
if (this.activeTab.type != TAB_TYPES.SQL_QUERY) return false
return this.favorites.find((x) => x.key === this.activeTab.id) !== undefined
}
get tabsJson() {
const json = this.tabs.map((x) => {
switch (x.type) {
case TAB_TYPES.WELCOME:
return { name: x.name, type: x.type }
case TAB_TYPES.SQL_QUERY:
return { id: x.id, name: x.name, desc: x.desc, type: x.type, query: x.query }
default:
return undefined
}
})
return json
}
get favoriteCards() {
return this.favorites.filter((x) => !!x.name)
}
get isExecuting() {
const tab = this.activeTab
if (tab.type != TAB_TYPES.SQL_QUERY) return false
else return tab.isExecuting
}
tabsFromContentStore(contentStore, user_id) {
const snippets = contentStore
.sqlSnippets((x) => x.owner_id === user_id)
.map((snippet) => toJS(snippet))
// add the welcome tab
let tabs = IS_PLATFORM ? [new Tab('Welcome', TAB_TYPES.WELCOME)] : []
// add the tabs to array, but with structure the localStore expects
snippets.forEach((snippet) => {
const data = {
desc: snippet.description,
id: snippet.id,
name: snippet.name,
query: snippet.content.sql,
type: 'SQL_QUERY',
favorite: snippet.content.favorite,
}
tabs.push(data)
})
return tabs
}
/*
* ! Temporary solution !
*
* New action for retriving data from main db user_content
* You need to pass the contentStore into this
*/
async loadRemotePersistentData(contentStore, user_id) {
await contentStore.load()
const tabs = this.tabsFromContentStore(contentStore, user_id)
/*
* Reshape snippet content to fit the SqlEditorStore shape
*/
const payload = {
version: '1.0',
tabs: [...tabs],
favorites: [],
}
/*
* Run loadPersistentData() as normal, although now with remote data
*/
this.loadPersistentData(payload)
}
loadPersistentData(json) {
if (!json) {
this.loadInitialData()
return
}
if ('version' in json) {
this.loadTabs(json.tabs || [])
} else if ('results' in json) {
// legacy saved data
this.loadLegacyTabs(json.results || [])
}
this.loadFavorites(json.favorites || [])
this.loadPreviousSession()
}
loadInitialData() {
this.tabs = [
new Tab('Welcome', TAB_TYPES.WELCOME),
// new Tab('scripts', TAB_TYPES.WELCOME),
// new Tab('quickstarts', TAB_TYPES.WELCOME),
new QueryTab(this.nextQueryTabName, TAB_TYPES.SQL_QUERY),
]
this.selectedTabId = this.tabs[0].id
}
// for previous sql editor saved data
loadLegacyTabs(values) {
let tabs = [new Tab('Welcome', TAB_TYPES.WELCOME)]
tabs = tabs.concat(
values.map((x, index) => {
const tab = new QueryTab(this.meta, `Query-${index + 1}`, TAB_TYPES.SQL_QUERY)
tab.id = x.key
tab.query = x.query
return tab
})
)
this.tabs = tabs
this.selectedTabId = this.tabs.length ? this.tabs[0].id : undefined
}
loadTabs(values, autoSelectTab = true) {
const tabs = values.map((x) => {
switch (x.type) {
case TAB_TYPES.WELCOME:
return new Tab(x.name, x.type)
case TAB_TYPES.SQL_QUERY:
const tab = new QueryTab(this.meta, x.name, x.type, x.desc, x.favorite || false)
tab.id = x.id
tab.query = x.query
return tab
default:
return undefined
}
})
this.tabs = tabs.filter((x) => x !== undefined)
if (autoSelectTab) {
this.selectedTabId = this.tabs.length ? this.tabs[0].id : undefined
}
}
loadFavorites(values) {
this.favorites = values.map((x) => new Favorite(x.key, x.query, x.name, x.desc))
}
loadPreviousSession() {
const tabId = localStorage.getItem(this.localStorageKey)
const allSnippets = this.tabs.concat(this.favorites)
const selectedTab = allSnippets.find((tab) => tab.id === tabId)
if (!isUndefined(selectedTab)) {
this.selectTab(selectedTab.id)
}
}
async loadKeywords() {
const query = 'select * from pg_get_keywords();'
const response = await this.meta.query(query)
if (!response.error) {
this.keywordCache = response.map((x) => x.word.toLocaleLowerCase())
}
}
async loadSchemas() {
const response = await this.meta.query(SchemasQuery)
if (!response.error) {
this.schemaCache = response
}
}
async loadTablesAndColumns() {
const response = await this.meta.query(TableColumnsQuery)
if (!response.error) {
this.tableCache = response
}
}
async loadAllFunctions() {
const response = await this.meta.query(AllFunctionsQuery)
if (!response.error) {
this.functionCache = response.map((x) => {
const args = x.argument_types
.split(',')
.filter((a) => a)
.map((a) => a.trim())
return { ...x, args }
})
}
}
selectTab(id) {
// Save selected tab in local storage
localStorage.setItem(this.localStorageKey, id)
// reset new selected query tab
const found = this.tabs.find((x) => x.id === this.selectedTabId)
if (found && found.type == TAB_TYPES.SQL_QUERY) {
found.isExecuting = false
}
this.selectedTabId = id
}
closeTab(id) {
const tabs = this.tabs.filter((x) => x.id !== id)
// if user close selectedTab, select the last tab if available
if (tabs.length && this.selectedTabId === id) {
const nextId = tabs[tabs.length - 1].id
if (nextId) {
this.selectedTabId = nextId
}
}
}
createQueryTab(query, name) {
const newTab = new QueryTab(this.meta, name ? name : this.nextQueryTabName, TAB_TYPES.SQL_QUERY)
newTab.setQuery(query)
this.tabs.push(newTab)
this.selectedTabId = newTab.id
}
createQueryTabFromFavorite(key) {
const foundTab = this.tabs.find((x) => x.id == key)
if (foundTab) {
this.selectedTabId = foundTab.id
return
}
const foundFav = this.favorites.find((x) => x.key == key)
if (foundFav) {
const newTab = new QueryTab(this.meta, foundFav.name, TAB_TYPES.SQL_QUERY, foundFav.desc)
newTab.id = foundFav.key
newTab.query = foundFav.query
this.tabs.push(newTab)
this.selectedTabId = newTab.id
}
}
reorderTabs(startIndex, endIndex) {
const result = Array.from(this.tabs)
const [removed] = result.splice(startIndex, 1)
result.splice(endIndex, 0, removed)
this.tabs = result
}
addToFavorite(key, query, name, desc) {
const found = this.favorites.find((x) => x.key === key)
if (!found) {
const newFav = new Favorite(key, query, name, desc)
this.favorites = [newFav, ...this.favorites]
}
}
unFavorite(key) {
this.favorites = this.favorites.filter((x) => x.key != key)
}
renameQuery(id, model) {
const found = this.tabs.find((x) => x.id === id)
found?.rename(model)
const favorite = this.favorites.find((x) => x.key === id)
favorite?.rename(model)
}
async startExecuting(query) {
const tab = this.activeTab
if (!tab || tab.type != TAB_TYPES.SQL_QUERY) return
await tab.startExecuting(query)
}
}
export default SqlEditorStore