Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/server/templates/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,10 @@ export type Database = {
{} as Record<string, typeof schemaFunctions>
)
for (const fnName in schemaFunctionsGroupedByName) {
schemaFunctionsGroupedByName[fnName].sort((a, b) =>
b.fn.definition.localeCompare(a.fn.definition)
schemaFunctionsGroupedByName[fnName].sort(
(a, b) =>
a.fn.argument_types.localeCompare(b.fn.argument_types) ||
a.fn.return_type.localeCompare(b.fn.return_type)
)
}

Expand Down
119 changes: 119 additions & 0 deletions test/server/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

test('typegen: typescript', async () => {
const { body } = await app.inject({ method: 'GET', path: '/generators/typescript' })
expect(body).toMatchInlineSnapshot(

Check failure on line 6 in test/server/typegen.ts

View workflow job for this annotation

GitHub Actions / Test

test/index.test.ts > typegen: typescript

Error: Snapshot `typegen: typescript 1` mismatched - Expected + Received @@ -658,22 +658,20 @@ isSetofReturn: true } } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null user_id: number | null user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true } } @@ -692,20 +690,22 @@ isOneToOne: true isSetofReturn: true } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null user_id: number | null user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true } } @@ -723,20 +723,18 @@ isSetofReturn: true } } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true } } @@ -753,47 +751,49 @@ isOneToOne: false isSetofReturn: true } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true } } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true
`
"export type Json =
| string
Expand Down Expand Up @@ -1165,7 +1165,7 @@
path: '/generators/typescript',
query: { detect_one_to_one_relationships: 'true' },
})
expect(body).toMatchInlineSnapshot(

Check failure on line 1168 in test/server/typegen.ts

View workflow job for this annotation

GitHub Actions / Test

test/index.test.ts > typegen w/ one-to-one relationships

Error: Snapshot `typegen w/ one-to-one relationships 1` mismatched - Expected + Received @@ -683,22 +683,20 @@ isSetofReturn: true } } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null user_id: number | null user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true } } @@ -717,20 +715,22 @@ isOneToOne: true isSetofReturn: true } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null user_id: number | null user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true } } @@ -748,20 +748,18 @@ isSetofReturn: true } } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true } } @@ -778,47 +776,49 @@ isOneToOne: false isSetofReturn: true } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true } } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofR
`
"export type Json =
| string
Expand Down Expand Up @@ -2352,7 +2352,7 @@
path: '/generators/typescript',
query: { detect_one_to_one_relationships: 'true' },
})
expect(body).toMatchInlineSnapshot(

Check failure on line 2355 in test/server/typegen.ts

View workflow job for this annotation

GitHub Actions / Test

test/index.test.ts > typegen: typescript w/ one-to-one relationships

Error: Snapshot `typegen: typescript w/ one-to-one relationships 1` mismatched - Expected + Received @@ -683,22 +683,20 @@ isSetofReturn: true } } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null user_id: number | null user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true } } @@ -717,20 +715,22 @@ isOneToOne: true isSetofReturn: true } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null user_id: number | null user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true } } @@ -748,20 +748,18 @@ isSetofReturn: true } } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true } } @@ -778,47 +776,49 @@ isOneToOne: false isSetofReturn: true } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true } } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false
`
"export type Json =
| string
Expand Down Expand Up @@ -3539,7 +3539,7 @@
path: '/generators/typescript',
query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' },
})
expect(body).toMatchInlineSnapshot(

Check failure on line 3542 in test/server/typegen.ts

View workflow job for this annotation

GitHub Actions / Test

test/index.test.ts > typegen: typescript w/ postgrestVersion

Error: Snapshot `typegen: typescript w/ postgrestVersion 1` mismatched - Expected + Received @@ -688,22 +688,20 @@ isSetofReturn: true } } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null user_id: number | null user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true } } @@ -722,20 +720,22 @@ isOneToOne: true isSetofReturn: true } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null user_id: number | null user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true } } @@ -753,20 +753,18 @@ isSetofReturn: true } } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true } } @@ -783,47 +781,49 @@ isOneToOne: false isSetofReturn: true } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true } } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSe
`
"export type Json =
| string
Expand Down Expand Up @@ -4992,6 +4992,125 @@
expect(firstCall).toEqual(secondCall)
})

test('typegen: typescript function override order stability', async () => {
// Helper function to clean up test entities
const cleanupTestEntities = async () => {
await app.inject({
method: 'POST',
path: '/query',
payload: {
query: `
-- Drop functions with all possible signatures
DROP FUNCTION IF EXISTS test_func_override(integer, text) CASCADE;
DROP FUNCTION IF EXISTS test_func_override(text, integer) CASCADE;
DROP FUNCTION IF EXISTS test_func_override(boolean, integer, text) CASCADE;
DROP FUNCTION IF EXISTS test_func_override(text, boolean) CASCADE;
`,
},
})
}

// Clean up any existing test entities
await cleanupTestEntities()

// === FIRST ROUND: Create function overrides in order 1 ===
await app.inject({
method: 'POST',
path: '/query',
payload: {
query: `
-- Create function overrides in specific order
CREATE FUNCTION test_func_override(param_a integer, param_b text)
RETURNS integer AS 'SELECT param_a + 1' LANGUAGE sql IMMUTABLE;

CREATE FUNCTION test_func_override(param_a text, param_b integer)
RETURNS text AS 'SELECT param_a || param_b::text' LANGUAGE sql IMMUTABLE;

CREATE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text)
RETURNS boolean AS 'SELECT param_a' LANGUAGE sql IMMUTABLE;

CREATE FUNCTION test_func_override(param_a text, param_b boolean)
RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a ELSE '''' END' LANGUAGE sql IMMUTABLE;
`,
},
})

// Generate types for first configuration
const { body: firstCall } = await app.inject({
method: 'GET',
path: '/generators/typescript',
query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' },
})

// === SECOND ROUND: Modify function definitions without changing signatures ===
await app.inject({
method: 'POST',
path: '/query',
payload: {
query: `
-- Modify function definitions (using CREATE OR REPLACE)
-- This should preserve the order
CREATE OR REPLACE FUNCTION test_func_override(param_a integer, param_b text)
RETURNS integer AS 'SELECT param_a + 100' LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION test_func_override(param_a text, param_b integer)
RETURNS text AS 'SELECT param_a || ''_'' || param_b::text' LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text)
RETURNS boolean AS 'SELECT NOT param_a' LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION test_func_override(param_a text, param_b boolean)
RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a || ''_true'' ELSE ''false'' END' LANGUAGE sql IMMUTABLE;
`,
},
})

// Generate types for second configuration (after modifying definitions)
const { body: secondCall } = await app.inject({
method: 'GET',
path: '/generators/typescript',
query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' },
})

// === THIRD ROUND: Drop and recreate in different order ===
await cleanupTestEntities()

// Create functions in reverse order
await app.inject({
method: 'POST',
path: '/query',
payload: {
query: `
-- Create function overrides in reverse order
CREATE FUNCTION test_func_override(param_a text, param_b boolean)
RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a ELSE '''' END' LANGUAGE sql IMMUTABLE;

CREATE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text)
RETURNS boolean AS 'SELECT param_a' LANGUAGE sql IMMUTABLE;

CREATE FUNCTION test_func_override(param_a text, param_b integer)
RETURNS text AS 'SELECT param_a || param_b::text' LANGUAGE sql IMMUTABLE;

CREATE FUNCTION test_func_override(param_a integer, param_b text)
RETURNS integer AS 'SELECT param_a + 1' LANGUAGE sql IMMUTABLE;
`,
},
})

// Generate types for third configuration (recreated in different order)
const { body: thirdCall } = await app.inject({
method: 'GET',
path: '/generators/typescript',
query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' },
})

// Clean up test entities
await cleanupTestEntities()

expect(firstCall).toEqual(secondCall)
expect(secondCall).toEqual(thirdCall)
})

test('typegen: go', async () => {
const { body } = await app.inject({ method: 'GET', path: '/generators/go' })
expect(body).toMatchInlineSnapshot(`
Expand Down
Loading