mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 09:59:03 +08:00
## Summary - Regenerates `packages/api-types` for the content endpoints now that the Platform API's `notebook` content type has landed (list/get/upsert `type` enums, plus `UpsertContentBody`'s notebook cell shape with `_id`/`y_series`). Unrelated schema drift from the same regen (Warehouse, SSO, notification exceptions, etc.) is excluded — only the content-endpoint hunks are applied. - Removes every local widening cast added while the API support was pending (`content-query.ts`, `content-infinite-query.ts`, `notebook-query.ts`, `notebook-upsert-mutation.ts`, `sql-folders-query.ts`). - What remains is scoped and renamed to match: draft ids (`generateDraftId`/`isDraftId`), used only for cells created client-side in the editor before their first save, dropped before they'd ever reach the backend as a fake `_id`. ## Test plan - [x] `pnpm typecheck` — clean - [x] `pnpm --filter studio test` — full suite passes (518 files / 5471 tests) - [x] `pnpm --filter studio run lint:ratchet` — no new warnings - [x] `pnpm format` / prettier — clean <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved notebook cell tracking during editing, reordering, insertion, and deletion. * Preserved existing cell identifiers while removing temporary draft identifiers before saving. * Improved chart configuration for selecting and displaying multiple Y-axis series. * Strengthened notebook validation and content persistence behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
179 lines
5.2 KiB
TypeScript
179 lines
5.2 KiB
TypeScript
import { untrustedSql } from '@supabase/pg-meta'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
changeCellSource,
|
|
cloneQueryCell,
|
|
DEFAULT_CELL_ROW_LIMIT,
|
|
getCellDisplay,
|
|
setCellRowLimit,
|
|
setCellSql,
|
|
toQueryModel,
|
|
} from './QueryCell.utils'
|
|
import { type ChartConfig, type QueryCell } from '@/data/content/notebooks/notebook-schema'
|
|
import { untrustedLogSql } from '@/data/logs/safe-analytics-sql'
|
|
|
|
const CHART: ChartConfig = {
|
|
type: 'bar',
|
|
x_column: 'day',
|
|
y_series: ['signups'],
|
|
cumulative: false,
|
|
scale: 'linear',
|
|
show_labels: true,
|
|
}
|
|
|
|
const DATABASE_CELL: QueryCell = {
|
|
_tag: 'database_cell',
|
|
_id: 'cell-1',
|
|
title: 'Signups',
|
|
view: 'chart',
|
|
chart: CHART,
|
|
unchecked_sql: untrustedSql('select * from auth.users'),
|
|
row_limit: 50,
|
|
database_identifier: 'replica-1',
|
|
}
|
|
|
|
const LOG_CELL: QueryCell = {
|
|
_tag: 'log_cell',
|
|
_id: 'cell-2',
|
|
title: 'Edge errors',
|
|
view: 'table',
|
|
chart: CHART,
|
|
unchecked_sql: untrustedLogSql('select timestamp from logs'),
|
|
time_range: { _tag: 'relative_time_range', unit: 'hour', amount: 1 },
|
|
}
|
|
|
|
describe('changeCellSource', () => {
|
|
it('keeps the query when only the database changes', () => {
|
|
const next = changeCellSource(DATABASE_CELL, {
|
|
_tag: 'database',
|
|
database_identifier: 'replica-2',
|
|
})
|
|
|
|
expect(next).toEqual({ ...DATABASE_CELL, database_identifier: 'replica-2' })
|
|
})
|
|
|
|
it('keeps the query when only the log time range changes', () => {
|
|
const time_range = { _tag: 'relative_time_range', unit: 'day', amount: 7 } as const
|
|
const next = changeCellSource(LOG_CELL, { _tag: 'logs', time_range })
|
|
|
|
expect(next).toEqual({ ...LOG_CELL, time_range })
|
|
})
|
|
|
|
it('carries the query text over when moving from the database to logs', () => {
|
|
const time_range = { _tag: 'relative_time_range', unit: 'hour', amount: 1 } as const
|
|
const next = changeCellSource(DATABASE_CELL, { _tag: 'logs', time_range })
|
|
|
|
expect(next).toEqual({
|
|
_tag: 'log_cell',
|
|
_id: 'cell-1',
|
|
title: 'Signups',
|
|
view: 'chart',
|
|
chart: CHART,
|
|
unchecked_sql: 'select * from auth.users',
|
|
time_range,
|
|
})
|
|
})
|
|
|
|
it('carries the query text over and restores a default row limit when moving from logs to the database', () => {
|
|
const next = changeCellSource(LOG_CELL, { _tag: 'database', database_identifier: undefined })
|
|
|
|
expect(next).toEqual({
|
|
_tag: 'database_cell',
|
|
_id: 'cell-2',
|
|
title: 'Edge errors',
|
|
view: 'table',
|
|
chart: CHART,
|
|
unchecked_sql: 'select timestamp from logs',
|
|
row_limit: DEFAULT_CELL_ROW_LIMIT,
|
|
database_identifier: undefined,
|
|
})
|
|
})
|
|
|
|
it('applies the selected database when moving from logs to the database', () => {
|
|
const next = changeCellSource(LOG_CELL, {
|
|
_tag: 'database',
|
|
database_identifier: 'replica-2',
|
|
})
|
|
|
|
expect(next).toMatchObject({ _tag: 'database_cell', database_identifier: 'replica-2' })
|
|
})
|
|
|
|
it('preserves the chart across a backend change so display settings survive', () => {
|
|
const next = changeCellSource(DATABASE_CELL, {
|
|
_tag: 'logs',
|
|
time_range: { _tag: 'relative_time_range', unit: 'hour', amount: 1 },
|
|
})
|
|
|
|
expect(next.chart).toEqual(CHART)
|
|
expect(next.chart).not.toBe(DATABASE_CELL.chart)
|
|
})
|
|
})
|
|
|
|
describe('setCellSql', () => {
|
|
it('writes the text back onto a database cell without touching its source', () => {
|
|
expect(setCellSql(DATABASE_CELL, 'select 1')).toEqual({
|
|
...DATABASE_CELL,
|
|
unchecked_sql: 'select 1',
|
|
})
|
|
})
|
|
|
|
it('writes the text back onto a log cell without touching its time range', () => {
|
|
expect(setCellSql(LOG_CELL, 'select 2')).toEqual({ ...LOG_CELL, unchecked_sql: 'select 2' })
|
|
})
|
|
})
|
|
|
|
describe('setCellRowLimit', () => {
|
|
it('writes the row limit onto a database cell without touching its query', () => {
|
|
expect(setCellRowLimit(DATABASE_CELL, 500)).toEqual({ ...DATABASE_CELL, row_limit: 500 })
|
|
})
|
|
|
|
it('leaves a log cell unchanged, since it has no row limit concept', () => {
|
|
expect(setCellRowLimit(LOG_CELL, 500)).toEqual(LOG_CELL)
|
|
})
|
|
})
|
|
|
|
describe('cloneQueryCell', () => {
|
|
it('copies the chart series array rather than aliasing it', () => {
|
|
const clone = cloneQueryCell(DATABASE_CELL)
|
|
|
|
expect(clone).toEqual(DATABASE_CELL)
|
|
expect(clone.chart?.y_series).not.toBe(DATABASE_CELL.chart?.y_series)
|
|
})
|
|
})
|
|
|
|
describe('getCellDisplay', () => {
|
|
it('keeps a configured chart while the table view is selected', () => {
|
|
expect(getCellDisplay({ ...DATABASE_CELL, view: 'table' })).toEqual({
|
|
view: 'table',
|
|
chart: CHART,
|
|
})
|
|
})
|
|
|
|
it('reports no chart when a cell has never configured one', () => {
|
|
expect(getCellDisplay({ ...DATABASE_CELL, view: 'table', chart: undefined })).toEqual({
|
|
view: 'table',
|
|
chart: undefined,
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('toQueryModel', () => {
|
|
it('tags a database cell with its row limit and database', () => {
|
|
expect(toQueryModel(DATABASE_CELL, 'select 3')).toEqual({
|
|
_tag: 'database',
|
|
uncheckedSql: 'select 3',
|
|
database_identifier: 'replica-1',
|
|
rowLimit: 50,
|
|
})
|
|
})
|
|
|
|
it('tags a log cell with its time range and no row limit', () => {
|
|
expect(toQueryModel(LOG_CELL, 'select 4')).toEqual({
|
|
_tag: 'logs',
|
|
uncheckedSql: 'select 4',
|
|
time_range: LOG_CELL.time_range,
|
|
})
|
|
})
|
|
})
|