mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 09:59:03 +08:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature (data layer only — PR 1 of the SQL-editor query-source stack; nothing user-visible yet, no consumers). ## What is the current behavior? The Studio SQL editor only runs queries against Postgres. There is no type-safe brand for user-authored logs SQL and no execution/normalization layer for running SQL against the logs/analytics (ClickHouse) backend. ## What is the new behavior? Pure additions, no behavior change: - `data/logs/safe-analytics-sql.ts` — adds distinct untrusted/safe brands for user-authored logs SQL (`UntrustedLogSqlFragment`, `untrustedLogSql`, `acceptUntrustedLogsSql`), mirroring pg-meta's `UntrustedSqlFragment` but kept intentionally disjoint so Postgres and logs SQL can never cross boundaries. - `data/logs/execute-logs-sql-mutation.ts` (new) — `executeLogsSql` wraps `executeAnalyticsSql`, attaches the resolved time range as request params (`iso_timestamp_start/end`, never spliced into SQL), and normalizes to `{ rows, error? }`; `mapLogsError` normalizes the analytics backend's structured 200-body error into the `{ message }` shape the result pane reads; `useExecuteLogsSqlMutation` collapses transport and 200-body errors into React Query's single `onError` path. - Unit tests for `mapLogsError`, the brands (including compile-time disjointness vs pg-meta brands), and safe composition. Verification: `pnpm test:studio` (new suites, 26 passed), `pnpm typecheck`, `lint:ratchet` (no new warnings), and Prettier all pass. ## Additional context <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added the ability to run user-authored logs SQL with resolved start/end timestamps. * Normalized query error handling so failures surface a clear message (including sensible fallbacks) and integrates with mutation error flows (with a default error toast when not customized). * Introduced safety branding for logs SQL fragments, including promotion to runnable safe SQL. * **Tests** * Added tests covering error normalization across multiple malformed/empty error shapes. * Added tests ensuring logs SQL branding preserves/accepts only the intended types and rejects unsafe inputs. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
58 lines
2.1 KiB
TypeScript
58 lines
2.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { mapLogsError } from './execute-logs-sql-mutation'
|
|
|
|
describe('mapLogsError', () => {
|
|
it('returns undefined when there is no error', () => {
|
|
expect(mapLogsError(undefined)).toBeUndefined()
|
|
expect(mapLogsError(null)).toBeUndefined()
|
|
})
|
|
|
|
it('extracts the top-level message from the structured backend error body', () => {
|
|
const structured = {
|
|
code: 400,
|
|
errors: [{ domain: 'global', message: 'nested detail', reason: 'invalidQuery' }],
|
|
message: 'Syntax error near LIMIT',
|
|
status: 'INVALID_ARGUMENT',
|
|
}
|
|
expect(mapLogsError(structured)).toEqual({ message: 'Syntax error near LIMIT' })
|
|
})
|
|
|
|
it('falls back to the first nested errors[].message when top-level message is missing', () => {
|
|
const structured = {
|
|
code: 400,
|
|
errors: [
|
|
{ domain: 'global', message: '', reason: 'invalidQuery' },
|
|
{ domain: 'global', message: 'first usable detail', reason: 'invalidQuery' },
|
|
],
|
|
status: 'INVALID_ARGUMENT',
|
|
}
|
|
expect(mapLogsError(structured)).toEqual({ message: 'first usable detail' })
|
|
})
|
|
|
|
it('does not throw and falls back when errors is a malformed non-array value', () => {
|
|
expect(mapLogsError({ code: 400, errors: {}, status: 'INVALID_ARGUMENT' })).toEqual({
|
|
message: 'An unexpected error occurred while running the logs query.',
|
|
})
|
|
expect(mapLogsError({ message: 'top-level wins', errors: 'nope' })).toEqual({
|
|
message: 'top-level wins',
|
|
})
|
|
})
|
|
|
|
it('normalizes a plain string error', () => {
|
|
expect(mapLogsError('boom')).toEqual({ message: 'boom' })
|
|
})
|
|
|
|
it('returns a generic fallback when an error is present but carries no usable text', () => {
|
|
expect(mapLogsError({ code: 500, errors: [], status: 'INTERNAL' })).toEqual({
|
|
message: 'An unexpected error occurred while running the logs query.',
|
|
})
|
|
expect(mapLogsError('')).toEqual({
|
|
message: 'An unexpected error occurred while running the logs query.',
|
|
})
|
|
expect(mapLogsError({ message: '' })).toEqual({
|
|
message: 'An unexpected error occurred while running the logs query.',
|
|
})
|
|
})
|
|
})
|