Skip to content

Commit 1a52837

Browse files
committed
feat: generate types for foreign tables
1 parent dd47c6d commit 1a52837

File tree

7 files changed

+141
-70
lines changed

7 files changed

+141
-70
lines changed

src/lib/PostgresMetaForeignTables.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { literal } from 'pg-format'
2-
import { coalesceRowsToArray } from './helpers.js'
2+
import { coalesceRowsToArray, filterByList } from './helpers.js'
33
import { columnsSql, foreignTablesSql } from './sql/index.js'
44
import { PostgresMetaResult, PostgresForeignTable } from './types.js'
55

@@ -11,25 +11,37 @@ export default class PostgresMetaForeignTables {
1111
}
1212

1313
async list(options: {
14+
includedSchemas?: string[]
15+
excludedSchemas?: string[]
1416
limit?: number
1517
offset?: number
1618
includeColumns: false
1719
}): Promise<PostgresMetaResult<(PostgresForeignTable & { columns: never })[]>>
1820
async list(options?: {
21+
includedSchemas?: string[]
22+
excludedSchemas?: string[]
1923
limit?: number
2024
offset?: number
2125
includeColumns?: boolean
2226
}): Promise<PostgresMetaResult<(PostgresForeignTable & { columns: unknown[] })[]>>
2327
async list({
28+
includedSchemas,
29+
excludedSchemas,
2430
limit,
2531
offset,
2632
includeColumns = true,
2733
}: {
34+
includedSchemas?: string[]
35+
excludedSchemas?: string[]
2836
limit?: number
2937
offset?: number
3038
includeColumns?: boolean
3139
} = {}): Promise<PostgresMetaResult<PostgresForeignTable[]>> {
3240
let sql = generateEnrichedForeignTablesSql({ includeColumns })
41+
const filter = filterByList(includedSchemas, excludedSchemas)
42+
if (filter) {
43+
sql += ` where schema ${filter}`
44+
}
3345
if (limit) {
3446
sql += ` limit ${limit}`
3547
}

src/lib/generators.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import PostgresMeta from './PostgresMeta.js'
22
import {
33
PostgresColumn,
4+
PostgresForeignTable,
45
PostgresFunction,
56
PostgresMaterializedView,
67
PostgresRelationship,
@@ -14,6 +15,7 @@ import { PostgresMetaResult } from './types.js'
1415
export type GeneratorMetadata = {
1516
schemas: PostgresSchema[]
1617
tables: Omit<PostgresTable, 'columns'>[]
18+
foreignTables: Omit<PostgresForeignTable, 'columns'>[]
1719
views: Omit<PostgresView, 'columns'>[]
1820
materializedViews: Omit<PostgresMaterializedView, 'columns'>[]
1921
columns: PostgresColumn[]
@@ -46,6 +48,15 @@ export async function getGeneratorMetadata(
4648
return { data: null, error: tablesError }
4749
}
4850

51+
const { data: foreignTables, error: foreignTablesError } = await pgMeta.foreignTables.list({
52+
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
53+
excludedSchemas,
54+
includeColumns: false,
55+
})
56+
if (foreignTablesError) {
57+
return { data: null, error: foreignTablesError }
58+
}
59+
4960
const { data: views, error: viewsError } = await pgMeta.views.list({
5061
includedSchemas: includedSchemas.length > 0 ? includedSchemas : undefined,
5162
excludedSchemas,
@@ -104,6 +115,7 @@ export async function getGeneratorMetadata(
104115
(includedSchemas.length === 0 || includedSchemas.includes(name))
105116
),
106117
tables,
118+
foreignTables,
107119
views,
108120
materializedViews,
109121
columns,

src/server/server.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ if (EXPORT_DOCS) {
4141
const [
4242
{ data: schemas, error: schemasError },
4343
{ data: tables, error: tablesError },
44+
{ data: foreignTables, error: foreignTablesError },
4445
{ data: views, error: viewsError },
4546
{ data: materializedViews, error: materializedViewsError },
4647
{ data: columns, error: columnsError },
@@ -54,6 +55,11 @@ if (EXPORT_DOCS) {
5455
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
5556
includeColumns: false,
5657
}),
58+
pgMeta.foreignTables.list({
59+
includedSchemas:
60+
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
61+
includeColumns: false,
62+
}),
5763
pgMeta.views.list({
5864
includedSchemas:
5965
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
@@ -86,6 +92,9 @@ if (EXPORT_DOCS) {
8692
if (tablesError) {
8793
throw new Error(tablesError.message)
8894
}
95+
if (foreignTablesError) {
96+
throw new Error(foreignTablesError.message)
97+
}
8998
if (viewsError) {
9099
throw new Error(viewsError.message)
91100
}
@@ -113,6 +122,7 @@ if (EXPORT_DOCS) {
113122
GENERATE_TYPES_INCLUDED_SCHEMAS.includes(name)
114123
),
115124
tables: tables!,
125+
foreignTables: foreignTables!,
116126
views: views!,
117127
materializedViews: materializedViews!,
118128
columns: columns!,

src/server/templates/typescript.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { GeneratorMetadata } from '../../lib/generators.js'
1212
export const apply = async ({
1313
schemas,
1414
tables,
15+
foreignTables,
1516
views,
1617
materializedViews,
1718
columns,
@@ -23,7 +24,7 @@ export const apply = async ({
2324
detectOneToOneRelationships: boolean
2425
}): Promise<string> => {
2526
const columnsByTableId = Object.fromEntries<PostgresColumn[]>(
26-
[...tables, ...views, ...materializedViews].map((t) => [t.id, []])
27+
[...tables, ...foreignTables, ...views, ...materializedViews].map((t) => [t.id, []])
2728
)
2829
columns
2930
.filter((c) => c.table_id in columnsByTableId)
@@ -37,7 +38,7 @@ export type Database = {
3738
${schemas
3839
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
3940
.map((schema) => {
40-
const schemaTables = tables
41+
const schemaTables = [...tables, ...foreignTables]
4142
.filter((table) => table.schema === schema.name)
4243
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
4344
const schemaViews = [...views, ...materializedViews]

test/db/00-init.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ create extension postgres_fdw;
8787
create server foreign_server foreign data wrapper postgres_fdw options (host 'localhost', port '5432', dbname 'postgres');
8888
create user mapping for postgres server foreign_server options (user 'postgres', password 'postgres');
8989
create foreign table foreign_table (
90-
id int8,
90+
id int8 not null,
9191
name text,
9292
status user_status
9393
) server foreign_server options (schema_name 'public', table_name 'users');

test/lib/foreign-tables.ts

Lines changed: 66 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -22,71 +22,71 @@ test('list', async () => {
2222
const res = await pgMeta.foreignTables.list()
2323
expect(cleanNondetFromResponse(res).data?.find(({ name }) => name === 'foreign_table'))
2424
.toMatchInlineSnapshot(`
25-
{
26-
"columns": [
27-
{
28-
"check": null,
29-
"comment": null,
30-
"data_type": "bigint",
31-
"default_value": null,
32-
"enums": [],
33-
"format": "int8",
34-
"identity_generation": null,
35-
"is_generated": false,
36-
"is_identity": false,
37-
"is_nullable": true,
38-
"is_unique": false,
39-
"is_updatable": true,
40-
"name": "id",
41-
"ordinal_position": 1,
42-
"schema": "public",
43-
"table": "foreign_table",
44-
},
45-
{
46-
"check": null,
47-
"comment": null,
48-
"data_type": "text",
49-
"default_value": null,
50-
"enums": [],
51-
"format": "text",
52-
"identity_generation": null,
53-
"is_generated": false,
54-
"is_identity": false,
55-
"is_nullable": true,
56-
"is_unique": false,
57-
"is_updatable": true,
58-
"name": "name",
59-
"ordinal_position": 2,
60-
"schema": "public",
61-
"table": "foreign_table",
62-
},
63-
{
64-
"check": null,
65-
"comment": null,
66-
"data_type": "USER-DEFINED",
67-
"default_value": null,
68-
"enums": [
69-
"ACTIVE",
70-
"INACTIVE",
71-
],
72-
"format": "user_status",
73-
"identity_generation": null,
74-
"is_generated": false,
75-
"is_identity": false,
76-
"is_nullable": true,
77-
"is_unique": false,
78-
"is_updatable": true,
79-
"name": "status",
80-
"ordinal_position": 3,
81-
"schema": "public",
82-
"table": "foreign_table",
83-
},
84-
],
85-
"comment": null,
86-
"name": "foreign_table",
87-
"schema": "public",
88-
}
89-
`)
25+
{
26+
"columns": [
27+
{
28+
"check": null,
29+
"comment": null,
30+
"data_type": "bigint",
31+
"default_value": null,
32+
"enums": [],
33+
"format": "int8",
34+
"identity_generation": null,
35+
"is_generated": false,
36+
"is_identity": false,
37+
"is_nullable": false,
38+
"is_unique": false,
39+
"is_updatable": true,
40+
"name": "id",
41+
"ordinal_position": 1,
42+
"schema": "public",
43+
"table": "foreign_table",
44+
},
45+
{
46+
"check": null,
47+
"comment": null,
48+
"data_type": "text",
49+
"default_value": null,
50+
"enums": [],
51+
"format": "text",
52+
"identity_generation": null,
53+
"is_generated": false,
54+
"is_identity": false,
55+
"is_nullable": true,
56+
"is_unique": false,
57+
"is_updatable": true,
58+
"name": "name",
59+
"ordinal_position": 2,
60+
"schema": "public",
61+
"table": "foreign_table",
62+
},
63+
{
64+
"check": null,
65+
"comment": null,
66+
"data_type": "USER-DEFINED",
67+
"default_value": null,
68+
"enums": [
69+
"ACTIVE",
70+
"INACTIVE",
71+
],
72+
"format": "user_status",
73+
"identity_generation": null,
74+
"is_generated": false,
75+
"is_identity": false,
76+
"is_nullable": true,
77+
"is_unique": false,
78+
"is_updatable": true,
79+
"name": "status",
80+
"ordinal_position": 3,
81+
"schema": "public",
82+
"table": "foreign_table",
83+
},
84+
],
85+
"comment": null,
86+
"name": "foreign_table",
87+
"schema": "public",
88+
}
89+
`)
9090
})
9191

9292
test('list without columns', async () => {
@@ -117,7 +117,7 @@ test('retrieve', async () => {
117117
"identity_generation": null,
118118
"is_generated": false,
119119
"is_identity": false,
120-
"is_nullable": true,
120+
"is_nullable": false,
121121
"is_unique": false,
122122
"is_updatable": true,
123123
"name": "id",

test/server/typegen.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,24 @@ test('typegen', async () => {
3636
Update: {}
3737
Relationships: []
3838
}
39+
foreign_table: {
40+
Row: {
41+
id: number
42+
name: string | null
43+
status: Database["public"]["Enums"]["user_status"] | null
44+
}
45+
Insert: {
46+
id: number
47+
name?: string | null
48+
status?: Database["public"]["Enums"]["user_status"] | null
49+
}
50+
Update: {
51+
id?: number
52+
name?: string | null
53+
status?: Database["public"]["Enums"]["user_status"] | null
54+
}
55+
Relationships: []
56+
}
3957
memes: {
4058
Row: {
4159
category: number | null
@@ -519,6 +537,24 @@ test('typegen w/ one-to-one relationships', async () => {
519537
Update: {}
520538
Relationships: []
521539
}
540+
foreign_table: {
541+
Row: {
542+
id: number
543+
name: string | null
544+
status: Database["public"]["Enums"]["user_status"] | null
545+
}
546+
Insert: {
547+
id: number
548+
name?: string | null
549+
status?: Database["public"]["Enums"]["user_status"] | null
550+
}
551+
Update: {
552+
id?: number
553+
name?: string | null
554+
status?: Database["public"]["Enums"]["user_status"] | null
555+
}
556+
Relationships: []
557+
}
522558
memes: {
523559
Row: {
524560
category: number | null

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy