From 4364d4096676a4a4ce446c1764c679f1fd1d8369 Mon Sep 17 00:00:00 2001 From: Gulfaraz Rahman Date: Wed, 6 Mar 2024 10:13:48 +0100 Subject: [PATCH] fix(typegen): array types are `unknown` in computed fields & composite types (#703) * fix: array types should not unknown fixes #581 * chore: update test random ids * chore: keep pgType arg non-undefined It should be the resposibility of the caller to make sure the argument is defined * chore: clarify type name --------- Co-authored-by: Bobbie Soedirgo --- package.json | 2 +- src/server/routes/generators/typescript.ts | 3 +- src/server/server.ts | 3 +- src/server/templates/typescript.ts | 50 ++++++---------------- test/db/00-init.sql | 7 +++ test/lib/views.ts | 24 +++++------ test/server/indexes.ts | 10 ++--- test/server/typegen.ts | 22 +++++++++- 8 files changed, 59 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index bfb71eb5..cee9928b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "db:clean": "cd test/db && docker-compose down", "db:run": "cd test/db && docker-compose up --detach && sleep 5", "test:run": "vitest run", - "test:update": "run-s db:clean db:run && vitest run && run-s db:clean" + "test:update": "run-s db:clean db:run && vitest run --update && run-s db:clean" }, "engines": { "node": ">=20", diff --git a/src/server/routes/generators/typescript.ts b/src/server/routes/generators/typescript.ts index e5c69b26..0e2f6b33 100644 --- a/src/server/routes/generators/typescript.ts +++ b/src/server/routes/generators/typescript.ts @@ -111,8 +111,7 @@ export default async (fastify: FastifyInstance) => { functions: functions.filter( ({ return_type }) => !['trigger', 'event_trigger'].includes(return_type) ), - types: types.filter(({ name }) => name[0] !== '_'), - arrayTypes: types.filter(({ name }) => name[0] === '_'), + types, detectOneToOneRelationships, }) }) diff --git a/src/server/server.ts b/src/server/server.ts index 88f2fa50..7137d5fe 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -120,8 +120,7 @@ if (EXPORT_DOCS) { functions: functions!.filter( ({ return_type }) => !['trigger', 'event_trigger'].includes(return_type) ), - types: types!.filter(({ name }) => name[0] !== '_'), - arrayTypes: types!.filter(({ name }) => name[0] === '_'), + types: types!, detectOneToOneRelationships: GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS, }) ) diff --git a/src/server/templates/typescript.ts b/src/server/templates/typescript.ts index 64793605..56b118ed 100644 --- a/src/server/templates/typescript.ts +++ b/src/server/templates/typescript.ts @@ -19,7 +19,6 @@ export const apply = async ({ relationships, functions, types, - arrayTypes, detectOneToOneRelationships, }: { schemas: PostgresSchema[] @@ -30,7 +29,6 @@ export const apply = async ({ relationships: PostgresRelationship[] functions: PostgresFunction[] types: PostgresType[] - arrayTypes: PostgresType[] detectOneToOneRelationships: boolean }): Promise => { const columnsByTableId = Object.fromEntries( @@ -289,25 +287,12 @@ export type Database = { } const argsNameAndType = inArgs.map(({ name, type_id, has_default }) => { - let type = arrayTypes.find(({ id }) => id === type_id) + const type = types.find(({ id }) => id === type_id) + let tsType = 'unknown' if (type) { - // If it's an array type, the name looks like `_int8`. - const elementTypeName = type.name.substring(1) - return { - name, - type: `(${pgTypeToTsType(elementTypeName, types, schemas)})[]`, - has_default, - } - } - type = types.find(({ id }) => id === type_id) - if (type) { - return { - name, - type: pgTypeToTsType(type.name, types, schemas), - has_default, - } + tsType = pgTypeToTsType(type.name, types, schemas) } - return { name, type: 'unknown', has_default } + return { name, type: tsType, has_default } }) return `{ @@ -322,20 +307,12 @@ export type Database = { const tableArgs = args.filter(({ mode }) => mode === 'table') if (tableArgs.length > 0) { const argsNameAndType = tableArgs.map(({ name, type_id }) => { - let type = arrayTypes.find(({ id }) => id === type_id) + const type = types.find(({ id }) => id === type_id) + let tsType = 'unknown' if (type) { - // If it's an array type, the name looks like `_int8`. - const elementTypeName = type.name.substring(1) - return { - name, - type: `(${pgTypeToTsType(elementTypeName, types, schemas)})[]`, - } + tsType = pgTypeToTsType(type.name, types, schemas) } - type = types.find(({ id }) => id === type_id) - if (type) { - return { name, type: pgTypeToTsType(type.name, types, schemas) } - } - return { name, type: 'unknown' } + return { name, type: tsType } }) return `{ @@ -362,7 +339,7 @@ export type Database = { }` } - // Case 3: returns base/composite/enum type. + // Case 3: returns base/array/composite/enum type. const type = types.find(({ id }) => id === return_type_id) if (type) { return pgTypeToTsType(type.name, types, schemas) @@ -399,14 +376,11 @@ export type Database = { `${JSON.stringify(name)}: { ${attributes.map(({ name, type_id }) => { const type = types.find(({ id }) => id === type_id) + let tsType = 'unknown' if (type) { - return `${JSON.stringify(name)}: ${pgTypeToTsType( - type.name, - types, - schemas - )}` + tsType = pgTypeToTsType(type.name, types, schemas) } - return `${JSON.stringify(name)}: unknown` + return `${JSON.stringify(name)}: ${tsType}` })} }` ) diff --git a/test/db/00-init.sql b/test/db/00-init.sql index b625ae03..02aa912b 100644 --- a/test/db/00-init.sql +++ b/test/db/00-init.sql @@ -3,6 +3,8 @@ -- Tables for testing CREATE TYPE public.user_status AS ENUM ('ACTIVE', 'INACTIVE'); +CREATE TYPE composite_type_with_array_attribute AS (my_text_array text[]); + CREATE TABLE public.users ( id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name text, @@ -76,6 +78,11 @@ $$ select $1.details_length > 20; $$ language sql stable; +create function public.details_words(public.todos) returns text[] as +$$ +select string_to_array($1.details, ' '); +$$ language sql stable; + create extension postgres_fdw; create server foreign_server foreign data wrapper postgres_fdw options (host 'localhost', port '5432', dbname 'postgres'); create user mapping for postgres server foreign_server options (user 'postgres', password 'postgres'); diff --git a/test/lib/views.ts b/test/lib/views.ts index 1d421c73..275eb4a3 100644 --- a/test/lib/views.ts +++ b/test/lib/views.ts @@ -15,7 +15,7 @@ test('list', async () => { "default_value": null, "enums": [], "format": "int8", - "id": "16420.1", + "id": "16423.1", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -26,7 +26,7 @@ test('list', async () => { "ordinal_position": 1, "schema": "public", "table": "todos_view", - "table_id": 16420, + "table_id": 16423, }, { "check": null, @@ -35,7 +35,7 @@ test('list', async () => { "default_value": null, "enums": [], "format": "text", - "id": "16420.2", + "id": "16423.2", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -46,7 +46,7 @@ test('list', async () => { "ordinal_position": 2, "schema": "public", "table": "todos_view", - "table_id": 16420, + "table_id": 16423, }, { "check": null, @@ -55,7 +55,7 @@ test('list', async () => { "default_value": null, "enums": [], "format": "int8", - "id": "16420.3", + "id": "16423.3", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -66,7 +66,7 @@ test('list', async () => { "ordinal_position": 3, "schema": "public", "table": "todos_view", - "table_id": 16420, + "table_id": 16423, }, ], "comment": null, @@ -112,7 +112,7 @@ test('retrieve', async () => { "default_value": null, "enums": [], "format": "int8", - "id": "16420.1", + "id": "16423.1", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -123,7 +123,7 @@ test('retrieve', async () => { "ordinal_position": 1, "schema": "public", "table": "todos_view", - "table_id": 16420, + "table_id": 16423, }, { "check": null, @@ -132,7 +132,7 @@ test('retrieve', async () => { "default_value": null, "enums": [], "format": "text", - "id": "16420.2", + "id": "16423.2", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -143,7 +143,7 @@ test('retrieve', async () => { "ordinal_position": 2, "schema": "public", "table": "todos_view", - "table_id": 16420, + "table_id": 16423, }, { "check": null, @@ -152,7 +152,7 @@ test('retrieve', async () => { "default_value": null, "enums": [], "format": "int8", - "id": "16420.3", + "id": "16423.3", "identity_generation": null, "is_generated": false, "is_identity": false, @@ -163,7 +163,7 @@ test('retrieve', async () => { "ordinal_position": 3, "schema": "public", "table": "todos_view", - "table_id": 16420, + "table_id": 16423, }, ], "comment": null, diff --git a/test/server/indexes.ts b/test/server/indexes.ts index 94937046..9de3aef1 100644 --- a/test/server/indexes.ts +++ b/test/server/indexes.ts @@ -18,7 +18,7 @@ test('list indexes', async () => { "class": "3124", "collation": "0", "comment": null, - "id": 16396, + "id": 16399, "index_attributes": [ { "attribute_name": "id", @@ -42,14 +42,14 @@ test('list indexes', async () => { "number_of_key_attributes": 1, "options": "0", "schema": "public", - "table_id": 16390, + "table_id": 16393, } ` ) }) test('retrieve index', async () => { - const res = await app.inject({ method: 'GET', path: '/indexes/16396' }) + const res = await app.inject({ method: 'GET', path: '/indexes/16399' }) const index = res.json() expect(index).toMatchInlineSnapshot( ` @@ -59,7 +59,7 @@ test('retrieve index', async () => { "class": "3124", "collation": "0", "comment": null, - "id": 16396, + "id": 16399, "index_attributes": [ { "attribute_name": "id", @@ -83,7 +83,7 @@ test('retrieve index', async () => { "number_of_key_attributes": 1, "options": "0", "schema": "public", - "table_id": 16390, + "table_id": 16393, } ` ) diff --git a/test/server/typegen.ts b/test/server/typegen.ts index 853764f1..05565038 100644 --- a/test/server/typegen.ts +++ b/test/server/typegen.ts @@ -79,6 +79,7 @@ test('typegen', async () => { blurb_varchar: string | null details_is_long: boolean | null details_length: number | null + details_words: string[] | null } Insert: { details?: string | null @@ -306,6 +307,12 @@ test('typegen', async () => { } Returns: number } + details_words: { + Args: { + "": unknown + } + Returns: string[] + } function_returning_row: { Args: Record Returns: { @@ -366,7 +373,9 @@ test('typegen', async () => { user_status: "ACTIVE" | "INACTIVE" } CompositeTypes: { - [_ in never]: never + composite_type_with_array_attribute: { + my_text_array: string[] + } } } } @@ -539,6 +548,7 @@ test('typegen w/ one-to-one relationships', async () => { blurb_varchar: string | null details_is_long: boolean | null details_length: number | null + details_words: string[] | null } Insert: { details?: string | null @@ -778,6 +788,12 @@ test('typegen w/ one-to-one relationships', async () => { } Returns: number } + details_words: { + Args: { + "": unknown + } + Returns: string[] + } function_returning_row: { Args: Record Returns: { @@ -838,7 +854,9 @@ test('typegen w/ one-to-one relationships', async () => { user_status: "ACTIVE" | "INACTIVE" } CompositeTypes: { - [_ in never]: never + composite_type_with_array_attribute: { + my_text_array: string[] + } } } }