mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-22 13:28:04 -05:00
updates
This commit is contained in:
@@ -26,11 +26,6 @@ import {
|
||||
|
||||
const logger = createLogger('TableRowsAPI')
|
||||
|
||||
/**
|
||||
* Type for sort direction specification.
|
||||
*/
|
||||
type SortDirection = Record<string, 'asc' | 'desc'>
|
||||
|
||||
/**
|
||||
* Zod schema for inserting a single row into a table.
|
||||
*
|
||||
@@ -56,9 +51,6 @@ const BatchInsertRowsSchema = z.object({
|
||||
.max(1000, 'Cannot insert more than 1000 rows per batch'),
|
||||
})
|
||||
|
||||
/** Inferred type for batch insert request body */
|
||||
type BatchInsertBody = z.infer<typeof BatchInsertRowsSchema>
|
||||
|
||||
/**
|
||||
* Zod schema for querying rows with filtering, sorting, and pagination.
|
||||
*/
|
||||
@@ -137,7 +129,7 @@ interface TableRowsRouteParams {
|
||||
async function handleBatchInsert(
|
||||
requestId: string,
|
||||
tableId: string,
|
||||
body: BatchInsertBody,
|
||||
body: z.infer<typeof BatchInsertRowsSchema>,
|
||||
userId: string
|
||||
): Promise<NextResponse> {
|
||||
const validated = BatchInsertRowsSchema.parse(body)
|
||||
@@ -271,7 +263,12 @@ export async function POST(request: NextRequest, { params }: TableRowsRouteParam
|
||||
'rows' in body &&
|
||||
Array.isArray((body as Record<string, unknown>).rows)
|
||||
) {
|
||||
return handleBatchInsert(requestId, tableId, body as BatchInsertBody, authResult.userId)
|
||||
return handleBatchInsert(
|
||||
requestId,
|
||||
tableId,
|
||||
body as z.infer<typeof BatchInsertRowsSchema>,
|
||||
authResult.userId
|
||||
)
|
||||
}
|
||||
|
||||
// Single row insert
|
||||
@@ -406,14 +403,14 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams
|
||||
const offset = searchParams.get('offset')
|
||||
|
||||
let filter: Record<string, unknown> | undefined
|
||||
let sort: SortDirection | undefined
|
||||
let sort: Record<string, 'asc' | 'desc'> | undefined
|
||||
|
||||
try {
|
||||
if (filterParam) {
|
||||
filter = JSON.parse(filterParam) as Record<string, unknown>
|
||||
}
|
||||
if (sortParam) {
|
||||
sort = JSON.parse(sortParam) as SortDirection
|
||||
sort = JSON.parse(sortParam) as Record<string, 'asc' | 'desc'>
|
||||
}
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'Invalid filter or sort JSON' }, { status: 400 })
|
||||
|
||||
@@ -70,19 +70,6 @@ export interface TableAccessDenied {
|
||||
reason?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type for table access check results.
|
||||
*/
|
||||
export type TableAccessCheck = TableAccessResult | TableAccessDenied
|
||||
|
||||
/**
|
||||
* Permission level required for table access.
|
||||
* - 'read': Any workspace permission (read, write, or admin)
|
||||
* - 'write': Write or admin permission required
|
||||
* - 'admin': Admin permission required
|
||||
*/
|
||||
export type TablePermissionLevel = 'read' | 'write' | 'admin'
|
||||
|
||||
/**
|
||||
* Internal function to check if a user has the required permission level for a table.
|
||||
*
|
||||
@@ -100,8 +87,8 @@ export type TablePermissionLevel = 'read' | 'write' | 'admin'
|
||||
async function checkTableAccessInternal(
|
||||
tableId: string,
|
||||
userId: string,
|
||||
requiredLevel: TablePermissionLevel
|
||||
): Promise<TableAccessCheck> {
|
||||
requiredLevel: 'read' | 'write' | 'admin'
|
||||
): Promise<TableAccessResult | TableAccessDenied> {
|
||||
// Fetch table data
|
||||
const table = await db
|
||||
.select({
|
||||
@@ -178,7 +165,10 @@ async function checkTableAccessInternal(
|
||||
* // User has access, proceed with operation
|
||||
* ```
|
||||
*/
|
||||
export async function checkTableAccess(tableId: string, userId: string): Promise<TableAccessCheck> {
|
||||
export async function checkTableAccess(
|
||||
tableId: string,
|
||||
userId: string
|
||||
): Promise<TableAccessResult | TableAccessDenied> {
|
||||
return checkTableAccessInternal(tableId, userId, 'read')
|
||||
}
|
||||
|
||||
@@ -205,7 +195,7 @@ export async function checkTableAccess(tableId: string, userId: string): Promise
|
||||
export async function checkTableWriteAccess(
|
||||
tableId: string,
|
||||
userId: string
|
||||
): Promise<TableAccessCheck> {
|
||||
): Promise<TableAccessResult | TableAccessDenied> {
|
||||
return checkTableAccessInternal(tableId, userId, 'write')
|
||||
}
|
||||
|
||||
@@ -234,7 +224,7 @@ export async function checkAccessOrRespond(
|
||||
tableId: string,
|
||||
userId: string,
|
||||
requestId: string,
|
||||
level: TablePermissionLevel = 'write'
|
||||
level: 'read' | 'write' | 'admin' = 'write'
|
||||
): Promise<TableAccessResult | NextResponse> {
|
||||
const checkFn = level === 'read' ? checkTableAccess : checkTableWriteAccess
|
||||
const accessCheck = await checkFn(tableId, userId)
|
||||
@@ -277,7 +267,7 @@ export async function checkAccessWithFullTable(
|
||||
tableId: string,
|
||||
userId: string,
|
||||
requestId: string,
|
||||
level: TablePermissionLevel = 'write'
|
||||
level: 'read' | 'write' | 'admin' = 'write'
|
||||
): Promise<TableAccessResultFull | NextResponse> {
|
||||
// Fetch full table data in one query
|
||||
const [tableData] = await db
|
||||
|
||||
@@ -32,17 +32,12 @@ export interface TableInfo {
|
||||
schema: TableSchema
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal mode determines the operation and UI.
|
||||
*/
|
||||
export type TableRowModalMode = 'add' | 'edit' | 'delete'
|
||||
|
||||
/**
|
||||
* Props for the TableRowModal component.
|
||||
*/
|
||||
export interface TableRowModalProps {
|
||||
/** The operation mode */
|
||||
mode: TableRowModalMode
|
||||
mode: 'add' | 'edit' | 'delete'
|
||||
/** Whether the modal is open */
|
||||
isOpen: boolean
|
||||
/** Callback when the modal should close */
|
||||
@@ -57,14 +52,11 @@ export interface TableRowModalProps {
|
||||
onSuccess: () => void
|
||||
}
|
||||
|
||||
/** Row data being edited in the form */
|
||||
type RowFormData = Record<string, unknown>
|
||||
|
||||
/**
|
||||
* Creates initial form data for columns.
|
||||
*/
|
||||
function createInitialRowData(columns: ColumnDefinition[]): RowFormData {
|
||||
const initial: RowFormData = {}
|
||||
function createInitialRowData(columns: ColumnDefinition[]): Record<string, unknown> {
|
||||
const initial: Record<string, unknown> = {}
|
||||
columns.forEach((col) => {
|
||||
if (col.type === 'boolean') {
|
||||
initial[col.name] = false
|
||||
@@ -78,7 +70,10 @@ function createInitialRowData(columns: ColumnDefinition[]): RowFormData {
|
||||
/**
|
||||
* Cleans and transforms form data for API submission.
|
||||
*/
|
||||
function cleanRowData(columns: ColumnDefinition[], rowData: RowFormData): Record<string, unknown> {
|
||||
function cleanRowData(
|
||||
columns: ColumnDefinition[],
|
||||
rowData: Record<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
const cleanData: Record<string, unknown> = {}
|
||||
|
||||
columns.forEach((col) => {
|
||||
@@ -181,7 +176,7 @@ export function TableRowModal({
|
||||
const schema = table?.schema
|
||||
const columns = schema?.columns || []
|
||||
|
||||
const [rowData, setRowData] = useState<RowFormData>({})
|
||||
const [rowData, setRowData] = useState<Record<string, unknown>>({})
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
ModalHeader,
|
||||
Textarea,
|
||||
} from '@/components/emcn'
|
||||
import type { ColumnDefinition, ColumnType } from '@/lib/table'
|
||||
import type { ColumnDefinition } from '@/lib/table'
|
||||
import { useCreateTable } from '@/hooks/queries/use-tables'
|
||||
|
||||
const logger = createLogger('CreateTableModal')
|
||||
@@ -35,7 +35,7 @@ interface CreateTableModalProps {
|
||||
/**
|
||||
* Available column type options for the combobox UI.
|
||||
*/
|
||||
const COLUMN_TYPE_OPTIONS: Array<{ value: ColumnType; label: string }> = [
|
||||
const COLUMN_TYPE_OPTIONS: Array<{ value: ColumnDefinition['type']; label: string }> = [
|
||||
{ value: 'string', label: 'String' },
|
||||
{ value: 'number', label: 'Number' },
|
||||
{ value: 'boolean', label: 'Boolean' },
|
||||
@@ -341,7 +341,7 @@ function ColumnRow({ column, index, isRemovable, onChange, onRemove }: ColumnRow
|
||||
options={COLUMN_TYPE_OPTIONS}
|
||||
value={column.type}
|
||||
selectedValue={column.type}
|
||||
onChange={(value) => onChange(index, 'type', value as ColumnType)}
|
||||
onChange={(value) => onChange(index, 'type', value as ColumnDefinition['type'])}
|
||||
placeholder='Type'
|
||||
editable={false}
|
||||
filterOptions={false}
|
||||
|
||||
@@ -27,6 +27,4 @@ export const TABLE_LIMITS = {
|
||||
|
||||
export const COLUMN_TYPES = ['string', 'number', 'boolean', 'date', 'json'] as const
|
||||
|
||||
export type ColumnType = (typeof COLUMN_TYPES)[number]
|
||||
|
||||
export const NAME_PATTERN = /^[a-z_][a-z0-9_]*$/i
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
* @module lib/table/types
|
||||
*/
|
||||
|
||||
import type { ColumnType } from './constants'
|
||||
|
||||
export type { ColumnType }
|
||||
import type { COLUMN_TYPES } from './constants'
|
||||
|
||||
/** Primitive values that can be stored in table columns */
|
||||
export type ColumnValue = string | number | boolean | null | Date
|
||||
@@ -30,7 +28,7 @@ export interface ColumnDefinition {
|
||||
/** Column name (must match NAME_PATTERN) */
|
||||
name: string
|
||||
/** Data type for the column */
|
||||
type: ColumnType
|
||||
type: (typeof COLUMN_TYPES)[number]
|
||||
/** Whether the column is required (non-null) */
|
||||
required?: boolean
|
||||
/** Whether the column must have unique values */
|
||||
|
||||
@@ -34,11 +34,6 @@ interface ValidationFailure {
|
||||
response: NextResponse
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type for validation results.
|
||||
*/
|
||||
export type RowValidationResult = ValidationSuccess | ValidationFailure
|
||||
|
||||
/**
|
||||
* Options for single row validation.
|
||||
*/
|
||||
@@ -77,7 +72,9 @@ export interface ValidateRowOptions {
|
||||
* // Proceed with insert/update
|
||||
* ```
|
||||
*/
|
||||
export async function validateRowData(options: ValidateRowOptions): Promise<RowValidationResult> {
|
||||
export async function validateRowData(
|
||||
options: ValidateRowOptions
|
||||
): Promise<ValidationSuccess | ValidationFailure> {
|
||||
const { rowData, schema, tableId, excludeRowId, checkUnique = true } = options
|
||||
|
||||
// 1. Validate row size
|
||||
@@ -161,11 +158,6 @@ interface BatchValidationFailure {
|
||||
response: NextResponse
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type for batch validation results.
|
||||
*/
|
||||
export type BatchValidationResult = BatchValidationSuccess | BatchValidationFailure
|
||||
|
||||
/**
|
||||
* Options for batch row validation.
|
||||
*/
|
||||
@@ -203,7 +195,7 @@ export interface ValidateBatchRowsOptions {
|
||||
*/
|
||||
export async function validateBatchRows(
|
||||
options: ValidateBatchRowsOptions
|
||||
): Promise<BatchValidationResult> {
|
||||
): Promise<BatchValidationSuccess | BatchValidationFailure> {
|
||||
const { rows, schema, tableId, checkUnique = true } = options
|
||||
const errors: BatchRowError[] = []
|
||||
|
||||
|
||||
@@ -112,8 +112,7 @@ export interface ToolConfig<P = any, R = any> {
|
||||
directExecution?: (params: P) => Promise<ToolResponse>
|
||||
}
|
||||
|
||||
/** Key-value pair row for HTTP request tables (headers, params) */
|
||||
export interface KeyValueRow {
|
||||
export interface TableRow {
|
||||
id: string
|
||||
cells: {
|
||||
Key: string
|
||||
@@ -121,9 +120,6 @@ export interface KeyValueRow {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Use KeyValueRow instead */
|
||||
export type TableRow = KeyValueRow
|
||||
|
||||
export interface OAuthTokenPayload {
|
||||
credentialId?: string
|
||||
credentialAccountUserId?: string
|
||||
|
||||
Reference in New Issue
Block a user