refactor: auth policies page

- remove mobx local store
- filter table policies from meta.policies
- clean up
This commit is contained in:
phamhieu authored and Joshen Lim committed 2022-05-23 18:08:24 +08:00
1 parent c05d165b9e
commit 8a9b46250a
3 files changed
+88 -124

No files matched your search

@@ -1,5 +1,7 @@
import { FC } from 'react'
import { PostgresTable, PostgresPolicy } from '@supabase/postgres-meta'
import { observer } from 'mobx-react-lite'
import { useStore } from 'hooks'
import PolicyTableRowHeader from './PolicyTableRowHeader'
import PolicyRow from './PolicyRow'
@@ -13,40 +15,44 @@ interface Props {
onSelectDeletePolicy: (policy: PostgresPolicy) => void
}
const PolicyTableRow: FC<Props> = ({
table,
onSelectToggleRLS = () => {},
onSelectCreatePolicy = () => {},
onSelectEditPolicy = () => {},
onSelectDeletePolicy = () => {},
}) => {
return (
<Panel
title={
<PolicyTableRowHeader
table={table}
onSelectToggleRLS={onSelectToggleRLS}
onSelectCreatePolicy={onSelectCreatePolicy}
/>
}
>
{table.policies.length === 0 && (
<div className="p-4 px-6">
<p className="text-scale-900 text-sm">No policies created yet</p>
</div>
)}
const PolicyTableRow: FC<Props> = observer(
({
table,
onSelectToggleRLS = () => {},
onSelectCreatePolicy = () => {},
onSelectEditPolicy = () => {},
onSelectDeletePolicy = () => {},
}) => {
const { meta } = useStore()
const policies = meta.policies.list((x: PostgresPolicy) => x.table === table.name)
return (
<Panel
title={
<PolicyTableRowHeader
table={table}
onSelectToggleRLS={onSelectToggleRLS}
onSelectCreatePolicy={onSelectCreatePolicy}
/>
}
>
{policies.length === 0 && (
<div className="p-4 px-6">
<p className="text-scale-900 text-sm">No policies created yet</p>
</div>
)}
{/* @ts-ignore */}
{table.policies.map((policy) => (
<PolicyRow
key={policy.id}
policy={policy}
onSelectEditPolicy={onSelectEditPolicy}
onSelectDeletePolicy={onSelectDeletePolicy}
/>
))}
</Panel>
)
}
{/* @ts-ignore */}
{policies.map((policy) => (
<PolicyRow
key={policy.id}
policy={policy}
onSelectEditPolicy={onSelectEditPolicy}
onSelectDeletePolicy={onSelectDeletePolicy}
/>
))}
</Panel>
)
}
)
export default PolicyTableRow
@@ -25,6 +25,7 @@ const AuthLayout: FC<Props> = ({ title, children }) => {
useEffect(() => {
if (ui.selectedProject) {
meta.policies.load()
meta.tables.load()
meta.roles.load()
}
+47 -90
View File
@@ -1,82 +1,50 @@
import React, { useState, PropsWithChildren, FC } from 'react'
import { isEmpty } from 'lodash'
import React, { createContext, useContext, useState, useEffect, PropsWithChildren } from 'react'
import { Button, IconSearch, Input } from '@supabase/ui'
import { observer, useLocalObservable } from 'mobx-react-lite'
import { observer } from 'mobx-react-lite'
import { useStore } from 'hooks'
import { AuthLayout } from 'components/layouts'
import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog'
import NoTableState from 'components/ui/States/NoTableState'
import { NextPageWithLayout } from 'types'
import { PolicyEditorModal, PolicyTableRow } from 'components/interfaces/Authentication/Policies'
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
import { PostgresRole } from '@supabase/postgres-meta'
const PageContext = createContext(null)
import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog'
import NoTableState from 'components/ui/States/NoTableState'
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
const AuthPoliciesLayout = ({ children }: PropsWithChildren<{}>) => {
const PageState: any = useLocalObservable(() => ({
meta: null,
project: null,
policiesFilter: '',
selectedTableId: null,
tables: [],
tablesLoading: true,
get filteredTables() {
if (!PageState.policiesFilter)
return PageState.tables.slice().sort((a: any, b: any) => a.name.localeCompare(b.name))
else {
let filter = PageState.policiesFilter.toLowerCase()
let stringSearch = (s: string) => s.toLowerCase().indexOf(filter) != -1
return PageState.tables
.slice()
.filter((x: any) => {
let searchTableName = stringSearch(x.name)
let searchPolicyName = x.policies.some((p: any) => stringSearch(p.name))
return searchTableName || searchPolicyName
})
.sort((a: any, b: any) => a.name.localeCompare(b.name))
}
},
get selectedTable() {
if (!PageState.selectedTableId) return null
for (let i = 0; i < PageState.tables.length; i++) {
const element: any = PageState.tables[i]
if (element.id == PageState.selectedTableId) return element
}
},
onTableUpdated(table: any) {
for (let i = 0; i < PageState.tables.length; i++) {
let el: any = PageState.tables[i]
if (el.id == table.id) {
PageState.tables[i] = { ...el, ...table }
}
}
},
}))
return <div className="p-4">{children}</div>
}
const { meta, ui } = useStore()
PageState.meta = meta as any
PageState.project = ui.selectedProject as any
return (
<PageContext.Provider value={PageState}>
<div className="p-4">{children}</div>
</PageContext.Provider>
)
const onFilterTables = (
tables: {
name: string
policies: {
name: string
}[]
}[],
keywords?: string
) => {
if (!keywords) return tables.slice().sort((a: any, b: any) => a.name.localeCompare(b.name))
else {
let filter = keywords.toLowerCase()
let stringSearch = (s: string) => s.toLowerCase().indexOf(filter) != -1
return tables
.slice()
.filter((x: any) => {
let searchTableName = stringSearch(x.name)
let searchPolicyName = x.policies.some((p: any) => stringSearch(p.name))
return searchTableName || searchPolicyName
})
.sort((a: any, b: any) => a.name.localeCompare(b.name))
}
}
const AuthPoliciesPage: NextPageWithLayout = () => {
const PageState: any = useContext(PageContext)
const { meta } = useStore()
const tables = meta.tables.list((table: any) => table.schema === 'public')
useEffect(() => {
PageState.tablesLoading = false
PageState.tables = tables.sort((a: any, b: any) => a.name.localeCompare(b.name))
}, [])
const [policiesFilter, setPoliciesFilter] = useState<string | undefined>(undefined)
const publicTables = meta.tables.list((table: { schema: string }) => table.schema === 'public')
const filteredTables = onFilterTables(publicTables, policiesFilter)
return (
<>
@@ -87,8 +55,8 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
size="small"
placeholder="Filter tables"
className="block w-full text-sm placeholder-gray-400"
value={PageState.policiesFilter}
onChange={(e) => (PageState.policiesFilter = e.target.value)}
value={policiesFilter}
onChange={(e) => setPoliciesFilter(e.target.value)}
icon={<IconSearch size="tiny" />}
/>
</div>
@@ -103,7 +71,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
</div>
</div>
<div>
<AuthPoliciesTables />
<AuthPoliciesTables hasPublicTables={publicTables.length > 0} tables={filteredTables} />
</div>
</>
)
@@ -117,10 +85,12 @@ AuthPoliciesPage.getLayout = (page) => (
export default observer(AuthPoliciesPage)
const AuthPoliciesTables = observer(() => {
interface AuthPoliciesTablesProps {
hasPublicTables: boolean
tables: any[]
}
const AuthPoliciesTables: FC<AuthPoliciesTablesProps> = observer(({ tables, hasPublicTables }) => {
const { ui, meta } = useStore()
const PageState: any = useContext(PageContext)
const roles = meta.roles.list((role: PostgresRole) => !meta.roles.systemRoles.includes(role.name))
const [selectedSchemaAndTable, setSelectedSchemaAndTable] = useState<any>({})
@@ -157,18 +127,10 @@ const AuthPoliciesTables = observer(() => {
const onSavePolicySuccess = async () => {
ui.setNotification({ category: 'success', message: 'Policy successfully saved!' })
await refreshTables()
closePolicyEditorModal()
}
// Methods that involve some API
const refreshTables = async () => {
await meta.tables.load()
const res: any = meta.tables.list((table: any) => table.schema === 'public')
if (!res.error) PageState.tables.replace(res)
}
const onToggleRLS = async () => {
const payload = {
id: selectedTableToToggleRLS.id,
@@ -176,21 +138,17 @@ const AuthPoliciesTables = observer(() => {
}
const res: any = await meta.tables.update(payload.id, payload)
// const url = `${API_URL}/database/${router.query.ref}/tables?id=${payload.id}`
// const res = await patch(url, payload)
if (res.error) {
ui.setNotification({
category: 'error',
message: `Failed to toggle RLS: ${res.error.message}`,
})
} else {
PageState.onTableUpdated(res)
}
closeConfirmModal()
}
const onCreatePolicy = async (payload: any) => {
const res = await PageState.meta.policies.create(payload)
const res = await meta.policies.create(payload)
if (res.error) {
ui.setNotification({
category: 'error',
@@ -202,7 +160,7 @@ const AuthPoliciesTables = observer(() => {
}
const onUpdatePolicy = async (payload: any) => {
const res = await PageState.meta.policies.update(payload.id, payload)
const res = await meta.policies.update(payload.id, payload)
if (res.error) {
ui.setNotification({
category: 'error',
@@ -214,8 +172,8 @@ const AuthPoliciesTables = observer(() => {
}
const onDeletePolicy = async () => {
const res = await PageState.meta.policies.del(selectedPolicyToDelete.id)
if (res.error) {
const res = await meta.policies.del(selectedPolicyToDelete.id)
if (typeof res !== 'boolean' && res.error) {
ui.setNotification({
category: 'error',
message: `Error deleting policy: ${res.error.message}`,
@@ -223,14 +181,13 @@ const AuthPoliciesTables = observer(() => {
} else {
ui.setNotification({ category: 'success', message: 'Successfully deleted policy!' })
}
await refreshTables()
closeConfirmModal()
}
return (
<div>
{PageState.filteredTables.length > 0 ? (
PageState.filteredTables.map((table: any) => (
{tables.length > 0 ? (
tables.map((table: any) => (
<section key={table.id}>
<PolicyTableRow
table={table}
@@ -245,7 +202,7 @@ const AuthPoliciesTables = observer(() => {
/>
</section>
))
) : PageState.tables.length > 0 ? (
) : hasPublicTables ? (
<NoSearchResults />
) : (
<NoTableState message="A public schema table is required before you can create a row-level security policy" />