From e3578a7c109254df01c1dd362cda990c836b6c97 Mon Sep 17 00:00:00 2001 From: Nancy <9d.24.nancy.sangani@gmail.com> Date: Sat, 11 Apr 2026 18:54:23 +0530 Subject: [PATCH 1/2] fix: generate Insert/Update types for views with INSTEAD OF triggers --- src/lib/sql/views.sql.ts | 44 +++ src/lib/types.ts | 2 + src/server/templates/typescript.ts | 12 +- test/db/00-init.sql | 37 ++- test/lib/triggers.ts | 32 +++ test/lib/views.ts | 24 +- test/server/typegen.ts | 430 +++++++++++++++++++++++++++++ 7 files changed, 565 insertions(+), 16 deletions(-) diff --git a/src/lib/sql/views.sql.ts b/src/lib/sql/views.sql.ts index 95a707e2..f08abe33 100644 --- a/src/lib/sql/views.sql.ts +++ b/src/lib/sql/views.sql.ts @@ -11,6 +11,50 @@ SELECT c.relname AS name, -- See definition of information_schema.views (pg_relation_is_updatable(c.oid, false) & 20) = 20 AS is_updatable, + -- A view supports INSERT if it is auto-updatable OR has an INSTEAD OF INSERT trigger + ( + (pg_relation_is_updatable(c.oid, false) & 8) = 8 + OR EXISTS ( + SELECT 1 FROM pg_trigger t + WHERE t.tgrelid = c.oid + AND t.tgtype & 64 > 0 + AND t.tgtype & 4 > 0 + AND NOT t.tgisinternal + ) + ) AS is_insert_enabled, + -- A view supports UPDATE if it is auto-updatable OR has an INSTEAD OF UPDATE trigger + ( + (pg_relation_is_updatable(c.oid, false) & 4) = 4 + OR EXISTS ( + SELECT 1 FROM pg_trigger t + WHERE t.tgrelid = c.oid + AND t.tgtype & 64 > 0 + AND t.tgtype & 16 > 0 + AND NOT t.tgisinternal + ) + ) AS is_update_enabled, + -- A view supports INSERT if it is auto-updatable OR has an INSTEAD OF INSERT trigger + ( + (pg_relation_is_updatable(c.oid, false) & 8) = 8 + OR EXISTS ( + SELECT 1 FROM pg_trigger t + WHERE t.tgrelid = c.oid + AND t.tgtype & (1 << 2) > 0 -- INSTEAD OF + AND t.tgtype & (1 << 3) > 0 -- INSERT event + AND NOT t.tgisinternal + ) + ) AS is_insert_enabled, + -- A view supports UPDATE if it is auto-updatable OR has an INSTEAD OF UPDATE trigger + ( + (pg_relation_is_updatable(c.oid, false) & 4) = 4 + OR EXISTS ( + SELECT 1 FROM pg_trigger t + WHERE t.tgrelid = c.oid + AND t.tgtype & (1 << 2) > 0 -- INSTEAD OF + AND t.tgtype & (1 << 4) > 0 -- UPDATE event + AND NOT t.tgisinternal + ) + ) AS is_update_enabled, obj_description(c.oid) AS comment FROM pg_class c diff --git a/src/lib/types.ts b/src/lib/types.ts index 26b3bc78..3f7837cf 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -460,6 +460,8 @@ export const postgresViewSchema = Type.Object({ schema: Type.String(), name: Type.String(), is_updatable: Type.Boolean(), + is_insert_enabled: Type.Boolean(), + is_update_enabled: Type.Boolean(), comment: Type.Union([Type.String(), Type.Null()]), columns: Type.Optional(Type.Array(postgresColumnSchema)), }) diff --git a/src/server/templates/typescript.ts b/src/server/templates/typescript.ts index 352c4ddc..d789b217 100644 --- a/src/server/templates/typescript.ts +++ b/src/server/templates/typescript.ts @@ -150,6 +150,8 @@ export const apply = async ({ view: { ...materializedView, is_updatable: false, + is_insert_enabled: false, + is_update_enabled: false, }, relationships: getRelationships(materializedView, relationships), }) @@ -623,7 +625,7 @@ export type Database = { ]} } ${ - view.is_updatable + view.is_insert_enabled ? `Insert: { ${columnsByTableId[view.id].map((column) => { if (!column.is_updatable) { @@ -640,8 +642,12 @@ export type Database = { { types, schemas, tables, views } ) })} - } - Update: { + }` + : '' + } + ${ + view.is_update_enabled + ? `Update: { ${columnsByTableId[view.id].map((column) => { if (!column.is_updatable) { return `${JSON.stringify(column.name)}?: never` diff --git a/test/db/00-init.sql b/test/db/00-init.sql index c30e1f4a..801650e5 100644 --- a/test/db/00-init.sql +++ b/test/db/00-init.sql @@ -500,4 +500,39 @@ LANGUAGE SQL STABLE AS $$ SELECT interval_test_row.duration_required * 2; -$$; \ No newline at end of file +$$; + +CREATE TABLE IF NOT EXISTS public.profile_type ( + id int2 NOT NULL PRIMARY KEY, + name text NOT NULL +); + +CREATE TABLE IF NOT EXISTS public.profile ( + id uuid NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + username text UNIQUE, + profile_type_id int2 REFERENCES public.profile_type(id) +); + +CREATE OR REPLACE VIEW public.profile_view ("id", "username", "profileType") +AS SELECT p.id, p.username, pt.name +FROM public.profile AS p +JOIN public.profile_type AS pt ON p.profile_type_id = pt.id; + +CREATE OR REPLACE FUNCTION public.profile_view_instead_of_trigger() +RETURNS TRIGGER +LANGUAGE plpgsql +AS $$ +BEGIN + IF TG_OP = 'INSERT' THEN + INSERT INTO public.profile (id, username) VALUES (NEW.id, NEW.username); + ELSIF TG_OP = 'UPDATE' THEN + UPDATE public.profile SET username = NEW.username WHERE id = OLD.id; + END IF; + RETURN NEW; +END; +$$; + +CREATE OR REPLACE TRIGGER profile_view_instead_of_trigger + INSTEAD OF INSERT OR UPDATE ON public.profile_view + FOR EACH ROW + EXECUTE FUNCTION public.profile_view_instead_of_trigger(); \ No newline at end of file diff --git a/test/lib/triggers.ts b/test/lib/triggers.ts index beb5d63d..8b29c241 100644 --- a/test/lib/triggers.ts +++ b/test/lib/triggers.ts @@ -229,6 +229,22 @@ create schema s2; create table s2.t(); create trigger tr before insert on s2.t e const triggers = res.data?.map(({ id, table_id, ...trigger }) => trigger) expect(triggers).toMatchInlineSnapshot(` [ + { + "activation": "INSTEAD OF", + "condition": null, + "enabled_mode": "ORIGIN", + "events": [ + "INSERT", + "UPDATE", + ], + "function_args": [], + "function_name": "profile_view_instead_of_trigger", + "function_schema": "public", + "name": "profile_view_instead_of_trigger", + "orientation": "ROW", + "schema": "public", + "table": "profile_view", + }, { "activation": "BEFORE", "condition": null, @@ -292,6 +308,22 @@ EXECUTE FUNCTION "MySchema"."my_trigger_function"(); const triggers = res.data?.map(({ id, table_id, ...trigger }) => trigger) expect(triggers).toMatchInlineSnapshot(` [ + { + "activation": "INSTEAD OF", + "condition": null, + "enabled_mode": "ORIGIN", + "events": [ + "INSERT", + "UPDATE", + ], + "function_args": [], + "function_name": "profile_view_instead_of_trigger", + "function_schema": "public", + "name": "profile_view_instead_of_trigger", + "orientation": "ROW", + "schema": "public", + "table": "profile_view", + }, { "activation": "BEFORE", "condition": null, diff --git a/test/lib/views.ts b/test/lib/views.ts index e623e14b..08f714aa 100644 --- a/test/lib/views.ts +++ b/test/lib/views.ts @@ -4,8 +4,7 @@ import { pgMeta } from './utils' test('list', async () => { const res = await pgMeta.views.list() expect(res.data?.find(({ name }) => name === 'todos_view')).toMatchInlineSnapshot( - { id: expect.any(Number) }, - ` + { id: expect.any(Number) }, ` { "columns": [ { @@ -71,12 +70,13 @@ test('list', async () => { ], "comment": null, "id": Any, + "is_insert_enabled": true, "is_updatable": true, + "is_update_enabled": true, "name": "todos_view", "schema": "public", } - ` - ) + `) }) test('list without columns', async () => { @@ -84,24 +84,23 @@ test('list without columns', async () => { expect(res.data?.find(({ name }) => name === 'todos_view')).toMatchInlineSnapshot( { id: expect.any(Number), - }, - ` + }, ` { "comment": null, "id": Any, + "is_insert_enabled": true, "is_updatable": true, + "is_update_enabled": true, "name": "todos_view", "schema": "public", } - ` - ) + `) }) test('retrieve', async () => { const res = await pgMeta.views.retrieve({ schema: 'public', name: 'todos_view' }) expect(res).toMatchInlineSnapshot( - { data: { id: expect.any(Number) } }, - ` + { data: { id: expect.any(Number) } }, ` { "data": { "columns": [ @@ -168,12 +167,13 @@ test('retrieve', async () => { ], "comment": null, "id": Any, + "is_insert_enabled": true, "is_updatable": true, + "is_update_enabled": true, "name": "todos_view", "schema": "public", }, "error": null, } - ` - ) + `) }) diff --git a/test/server/typegen.ts b/test/server/typegen.ts index 50a0896b..e7d7d006 100644 --- a/test/server/typegen.ts +++ b/test/server/typegen.ts @@ -171,6 +171,46 @@ test('typegen: typescript', async () => { }, ] } + profile: { + Row: { + id: string + profile_type_id: number | null + username: string | null + } + Insert: { + id?: string + profile_type_id?: number | null + username?: string | null + } + Update: { + id?: string + profile_type_id?: number | null + username?: string | null + } + Relationships: [ + { + foreignKeyName: "profile_profile_type_id_fkey" + columns: ["profile_type_id"] + referencedRelation: "profile_type" + referencedColumns: ["id"] + }, + ] + } + profile_type: { + Row: { + id: number + name: string + } + Insert: { + id: number + name: string + } + Update: { + id?: number + name?: string + } + Relationships: [] + } table_with_other_tables_row_type: { Row: { col1: Database["public"]["Tables"]["user_details"]["Row"] | null @@ -387,6 +427,20 @@ test('typegen: typescript', async () => { } Relationships: [] } + profile_view: { + Row: { + id: string | null + profileType: string | null + username: string | null + } + + Update: { + id?: never + profileType?: never + username?: never + } + Relationships: [] + } todos_matview: { Row: { details: string | null @@ -398,6 +452,7 @@ test('typegen: typescript', async () => { "user-id": number } | null } + Relationships: [ { foreignKeyName: "todos_user-id_fkey" @@ -506,6 +561,7 @@ test('typegen: typescript', async () => { user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } + Relationships: [] } users_view: { @@ -539,6 +595,7 @@ test('typegen: typescript', async () => { second_id: number | null second_name: string | null } + Relationships: [] } } @@ -1372,6 +1429,47 @@ test('typegen w/ one-to-one relationships', async () => { }, ] } + profile: { + Row: { + id: string + profile_type_id: number | null + username: string | null + } + Insert: { + id?: string + profile_type_id?: number | null + username?: string | null + } + Update: { + id?: string + profile_type_id?: number | null + username?: string | null + } + Relationships: [ + { + foreignKeyName: "profile_profile_type_id_fkey" + columns: ["profile_type_id"] + isOneToOne: false + referencedRelation: "profile_type" + referencedColumns: ["id"] + }, + ] + } + profile_type: { + Row: { + id: number + name: string + } + Insert: { + id: number + name: string + } + Update: { + id?: number + name?: string + } + Relationships: [] + } table_with_other_tables_row_type: { Row: { col1: Database["public"]["Tables"]["user_details"]["Row"] | null @@ -1600,6 +1698,20 @@ test('typegen w/ one-to-one relationships', async () => { } Relationships: [] } + profile_view: { + Row: { + id: string | null + profileType: string | null + username: string | null + } + + Update: { + id?: never + profileType?: never + username?: never + } + Relationships: [] + } todos_matview: { Row: { details: string | null @@ -1611,6 +1723,7 @@ test('typegen w/ one-to-one relationships', async () => { "user-id": number } | null } + Relationships: [ { foreignKeyName: "todos_user-id_fkey" @@ -1731,6 +1844,7 @@ test('typegen w/ one-to-one relationships', async () => { user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } + Relationships: [] } users_view: { @@ -1764,6 +1878,7 @@ test('typegen w/ one-to-one relationships', async () => { second_id: number | null second_name: string | null } + Relationships: [] } } @@ -2597,6 +2712,47 @@ test('typegen: typescript w/ one-to-one relationships', async () => { }, ] } + profile: { + Row: { + id: string + profile_type_id: number | null + username: string | null + } + Insert: { + id?: string + profile_type_id?: number | null + username?: string | null + } + Update: { + id?: string + profile_type_id?: number | null + username?: string | null + } + Relationships: [ + { + foreignKeyName: "profile_profile_type_id_fkey" + columns: ["profile_type_id"] + isOneToOne: false + referencedRelation: "profile_type" + referencedColumns: ["id"] + }, + ] + } + profile_type: { + Row: { + id: number + name: string + } + Insert: { + id: number + name: string + } + Update: { + id?: number + name?: string + } + Relationships: [] + } table_with_other_tables_row_type: { Row: { col1: Database["public"]["Tables"]["user_details"]["Row"] | null @@ -2825,6 +2981,20 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } Relationships: [] } + profile_view: { + Row: { + id: string | null + profileType: string | null + username: string | null + } + + Update: { + id?: never + profileType?: never + username?: never + } + Relationships: [] + } todos_matview: { Row: { details: string | null @@ -2836,6 +3006,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { "user-id": number } | null } + Relationships: [ { foreignKeyName: "todos_user-id_fkey" @@ -2956,6 +3127,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } + Relationships: [] } users_view: { @@ -2989,6 +3161,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { second_id: number | null second_name: string | null } + Relationships: [] } } @@ -3827,6 +4000,47 @@ test('typegen: typescript w/ postgrestVersion', async () => { }, ] } + profile: { + Row: { + id: string + profile_type_id: number | null + username: string | null + } + Insert: { + id?: string + profile_type_id?: number | null + username?: string | null + } + Update: { + id?: string + profile_type_id?: number | null + username?: string | null + } + Relationships: [ + { + foreignKeyName: "profile_profile_type_id_fkey" + columns: ["profile_type_id"] + isOneToOne: false + referencedRelation: "profile_type" + referencedColumns: ["id"] + }, + ] + } + profile_type: { + Row: { + id: number + name: string + } + Insert: { + id: number + name: string + } + Update: { + id?: number + name?: string + } + Relationships: [] + } table_with_other_tables_row_type: { Row: { col1: Database["public"]["Tables"]["user_details"]["Row"] | null @@ -4055,6 +4269,20 @@ test('typegen: typescript w/ postgrestVersion', async () => { } Relationships: [] } + profile_view: { + Row: { + id: string | null + profileType: string | null + username: string | null + } + + Update: { + id?: never + profileType?: never + username?: never + } + Relationships: [] + } todos_matview: { Row: { details: string | null @@ -4066,6 +4294,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { "user-id": number } | null } + Relationships: [ { foreignKeyName: "todos_user-id_fkey" @@ -4186,6 +4415,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { user_name: string | null user_status: Database["public"]["Enums"]["user_status"] | null } + Relationships: [] } users_view: { @@ -4219,6 +4449,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { second_id: number | null second_name: string | null } + Relationships: [] } } @@ -5469,6 +5700,39 @@ test('typegen: go', async () => { Id *int64 \`json:"id"\` } + type PublicProfileTypeSelect struct { + Id int16 \`json:"id"\` + Name string \`json:"name"\` + } + + type PublicProfileTypeInsert struct { + Id int16 \`json:"id"\` + Name string \`json:"name"\` + } + + type PublicProfileTypeUpdate struct { + Id *int16 \`json:"id"\` + Name *string \`json:"name"\` + } + + type PublicProfileSelect struct { + Id string \`json:"id"\` + ProfileTypeId *int16 \`json:"profile_type_id"\` + Username *string \`json:"username"\` + } + + type PublicProfileInsert struct { + Id *string \`json:"id"\` + ProfileTypeId *int16 \`json:"profile_type_id"\` + Username *string \`json:"username"\` + } + + type PublicProfileUpdate struct { + Id *string \`json:"id"\` + ProfileTypeId *int16 \`json:"profile_type_id"\` + Username *string \`json:"username"\` + } + type PublicCategorySelect struct { Id int32 \`json:"id"\` Name string \`json:"name"\` @@ -5544,6 +5808,12 @@ test('typegen: go', async () => { SecondName *string \`json:"second_name"\` } + type PublicProfileViewSelect struct { + Id *string \`json:"id"\` + ProfileType *string \`json:"profileType"\` + Username *string \`json:"username"\` + } + type PublicTodosMatviewSelect struct { Details *string \`json:"details"\` Id *int64 \`json:"id"\` @@ -5822,6 +6092,60 @@ test('typegen: swift', async () => { case status = "status" } } + internal struct ProfileSelect: Codable, Hashable, Sendable { + internal let id: UUID + internal let profileTypeId: Int16? + internal let username: String? + internal enum CodingKeys: String, CodingKey { + case id = "id" + case profileTypeId = "profile_type_id" + case username = "username" + } + } + internal struct ProfileInsert: Codable, Hashable, Sendable { + internal let id: UUID? + internal let profileTypeId: Int16? + internal let username: String? + internal enum CodingKeys: String, CodingKey { + case id = "id" + case profileTypeId = "profile_type_id" + case username = "username" + } + } + internal struct ProfileUpdate: Codable, Hashable, Sendable { + internal let id: UUID? + internal let profileTypeId: Int16? + internal let username: String? + internal enum CodingKeys: String, CodingKey { + case id = "id" + case profileTypeId = "profile_type_id" + case username = "username" + } + } + internal struct ProfileTypeSelect: Codable, Hashable, Sendable { + internal let id: Int16 + internal let name: String + internal enum CodingKeys: String, CodingKey { + case id = "id" + case name = "name" + } + } + internal struct ProfileTypeInsert: Codable, Hashable, Sendable { + internal let id: Int16 + internal let name: String + internal enum CodingKeys: String, CodingKey { + case id = "id" + case name = "name" + } + } + internal struct ProfileTypeUpdate: Codable, Hashable, Sendable { + internal let id: Int16? + internal let name: String? + internal enum CodingKeys: String, CodingKey { + case id = "id" + case name = "name" + } + } internal struct TableWithOtherTablesRowTypeSelect: Codable, Hashable, Sendable { internal let col1: UserDetailsSelect? internal let col2: AViewSelect? @@ -6011,6 +6335,16 @@ test('typegen: swift', async () => { case id = "id" } } + internal struct ProfileViewSelect: Codable, Hashable, Sendable { + internal let id: UUID? + internal let profiletype: String? + internal let username: String? + internal enum CodingKeys: String, CodingKey { + case id = "id" + case profiletype = "profileType" + case username = "username" + } + } internal struct TodosMatviewSelect: Codable, Hashable, Sendable { internal let details: String? internal let id: Int64? @@ -6353,6 +6687,60 @@ test('typegen: swift w/ public access control', async () => { case status = "status" } } + public struct ProfileSelect: Codable, Hashable, Sendable { + public let id: UUID + public let profileTypeId: Int16? + public let username: String? + public enum CodingKeys: String, CodingKey { + case id = "id" + case profileTypeId = "profile_type_id" + case username = "username" + } + } + public struct ProfileInsert: Codable, Hashable, Sendable { + public let id: UUID? + public let profileTypeId: Int16? + public let username: String? + public enum CodingKeys: String, CodingKey { + case id = "id" + case profileTypeId = "profile_type_id" + case username = "username" + } + } + public struct ProfileUpdate: Codable, Hashable, Sendable { + public let id: UUID? + public let profileTypeId: Int16? + public let username: String? + public enum CodingKeys: String, CodingKey { + case id = "id" + case profileTypeId = "profile_type_id" + case username = "username" + } + } + public struct ProfileTypeSelect: Codable, Hashable, Sendable { + public let id: Int16 + public let name: String + public enum CodingKeys: String, CodingKey { + case id = "id" + case name = "name" + } + } + public struct ProfileTypeInsert: Codable, Hashable, Sendable { + public let id: Int16 + public let name: String + public enum CodingKeys: String, CodingKey { + case id = "id" + case name = "name" + } + } + public struct ProfileTypeUpdate: Codable, Hashable, Sendable { + public let id: Int16? + public let name: String? + public enum CodingKeys: String, CodingKey { + case id = "id" + case name = "name" + } + } public struct TableWithOtherTablesRowTypeSelect: Codable, Hashable, Sendable { public let col1: UserDetailsSelect? public let col2: AViewSelect? @@ -6542,6 +6930,16 @@ test('typegen: swift w/ public access control', async () => { case id = "id" } } + public struct ProfileViewSelect: Codable, Hashable, Sendable { + public let id: UUID? + public let profiletype: String? + public let username: String? + public enum CodingKeys: String, CodingKey { + case id = "id" + case profiletype = "profileType" + case username = "username" + } + } public struct TodosMatviewSelect: Codable, Hashable, Sendable { public let details: String? public let id: Int64? @@ -6814,6 +7212,33 @@ test('typegen: python', async () => { duration_required: NotRequired[Annotated[str, Field(alias="duration_required")]] id: NotRequired[Annotated[int, Field(alias="id")]] + class PublicProfileType(BaseModel): + id: int = Field(alias="id") + name: str = Field(alias="name") + + class PublicProfileTypeInsert(TypedDict): + id: Annotated[int, Field(alias="id")] + name: Annotated[str, Field(alias="name")] + + class PublicProfileTypeUpdate(TypedDict): + id: NotRequired[Annotated[int, Field(alias="id")]] + name: NotRequired[Annotated[str, Field(alias="name")]] + + class PublicProfile(BaseModel): + id: uuid.UUID = Field(alias="id") + profile_type_id: Optional[int] = Field(alias="profile_type_id") + username: Optional[str] = Field(alias="username") + + class PublicProfileInsert(TypedDict): + id: NotRequired[Annotated[uuid.UUID, Field(alias="id")]] + profile_type_id: NotRequired[Annotated[Optional[int], Field(alias="profile_type_id")]] + username: NotRequired[Annotated[Optional[str], Field(alias="username")]] + + class PublicProfileUpdate(TypedDict): + id: NotRequired[Annotated[uuid.UUID, Field(alias="id")]] + profile_type_id: NotRequired[Annotated[Optional[int], Field(alias="profile_type_id")]] + username: NotRequired[Annotated[Optional[str], Field(alias="username")]] + class PublicCategory(BaseModel): id: int = Field(alias="id") name: str = Field(alias="name") @@ -6878,6 +7303,11 @@ test('typegen: python', async () => { second_id: Optional[int] = Field(alias="second_id") second_name: Optional[str] = Field(alias="second_name") + class PublicProfileView(BaseModel): + id: Optional[uuid.UUID] = Field(alias="id") + profiletype: Optional[str] = Field(alias="profileType") + username: Optional[str] = Field(alias="username") + class PublicTodosMatview(BaseModel): details: Optional[str] = Field(alias="details") id: Optional[int] = Field(alias="id") From 048a1fc8302c6967ed29f83dfa6e5ab0a0cf3e4c Mon Sep 17 00:00:00 2001 From: Nancy <9d.24.nancy.sangani@gmail.com> Date: Sat, 11 Apr 2026 18:59:17 +0530 Subject: [PATCH 2/2] style: fix prettier formatting in views.ts --- test/lib/views.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/lib/views.ts b/test/lib/views.ts index 08f714aa..f659a364 100644 --- a/test/lib/views.ts +++ b/test/lib/views.ts @@ -4,7 +4,8 @@ import { pgMeta } from './utils' test('list', async () => { const res = await pgMeta.views.list() expect(res.data?.find(({ name }) => name === 'todos_view')).toMatchInlineSnapshot( - { id: expect.any(Number) }, ` + { id: expect.any(Number) }, + ` { "columns": [ { @@ -76,7 +77,8 @@ test('list', async () => { "name": "todos_view", "schema": "public", } - `) + ` + ) }) test('list without columns', async () => { @@ -84,7 +86,8 @@ test('list without columns', async () => { expect(res.data?.find(({ name }) => name === 'todos_view')).toMatchInlineSnapshot( { id: expect.any(Number), - }, ` + }, + ` { "comment": null, "id": Any, @@ -94,13 +97,15 @@ test('list without columns', async () => { "name": "todos_view", "schema": "public", } - `) + ` + ) }) test('retrieve', async () => { const res = await pgMeta.views.retrieve({ schema: 'public', name: 'todos_view' }) expect(res).toMatchInlineSnapshot( - { data: { id: expect.any(Number) } }, ` + { data: { id: expect.any(Number) } }, + ` { "data": { "columns": [ @@ -175,5 +180,6 @@ test('retrieve', async () => { }, "error": null, } - `) + ` + ) })