-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathschema.ts
More file actions
123 lines (110 loc) · 3.77 KB
/
schema.ts
File metadata and controls
123 lines (110 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { printSchema, getIntrospectionQuery, buildClientSchema } from 'graphql'
import { ConstructivePreset, makePgService } from 'graphile-settings'
import { makeSchema } from 'graphile-build'
import { buildConnectionString, getPgPool } from 'pg-cache'
import type { GraphileConfig } from 'graphile-config'
import * as http from 'node:http'
import * as https from 'node:https'
export type BuildSchemaOptions = {
database?: string;
schemas: string[];
graphile?: Partial<GraphileConfig.Preset>;
};
// Build GraphQL Schema SDL directly from Postgres using PostGraphile v5, without HTTP.
export async function buildSchemaSDL(opts: BuildSchemaOptions): Promise<string> {
const database = opts.database ?? 'constructive'
const schemas = Array.isArray(opts.schemas) ? opts.schemas : []
// Get pool config for connection string
const pool = getPgPool({ database })
const poolConfig = (pool as any).options || {}
const connectionString = buildConnectionString(
poolConfig.user || 'postgres',
poolConfig.password || '',
poolConfig.host || 'localhost',
poolConfig.port || 5432,
database,
)
// Build v5 preset
const preset: GraphileConfig.Preset = {
extends: [
ConstructivePreset,
...(opts.graphile?.extends ?? []),
],
...(opts.graphile?.disablePlugins && { disablePlugins: opts.graphile.disablePlugins }),
...(opts.graphile?.plugins && { plugins: opts.graphile.plugins }),
...(opts.graphile?.schema && { schema: opts.graphile.schema }),
pgServices: [
makePgService({
connectionString,
schemas,
}),
],
}
const { schema } = await makeSchema(preset)
return printSchema(schema)
}
// Fetch GraphQL Schema SDL from a running GraphQL endpoint via introspection.
// This centralizes GraphQL client usage in the server package to avoid duplicating deps in the CLI.
export async function fetchEndpointSchemaSDL(endpoint: string, opts?: { headerHost?: string, headers?: Record<string, string>, auth?: string }): Promise<string> {
const url = new URL(endpoint)
const requestUrl = url
const introspectionQuery = getIntrospectionQuery({ descriptions: true })
const postData = JSON.stringify({
query: introspectionQuery,
variables: null,
operationName: 'IntrospectionQuery',
})
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'Content-Length': String(Buffer.byteLength(postData)),
}
if (opts?.headerHost) {
headers['Host'] = opts.headerHost
}
if (opts?.auth) {
headers['Authorization'] = opts.auth
}
if (opts?.headers) {
for (const [key, value] of Object.entries(opts.headers)) {
headers[key] = value
}
}
const isHttps = requestUrl.protocol === 'https:'
const lib = isHttps ? https : http
const responseData: string = await new Promise((resolve, reject) => {
const req = lib.request({
hostname: requestUrl.hostname,
port: (requestUrl.port ? Number(requestUrl.port) : (isHttps ? 443 : 80)),
path: requestUrl.pathname,
method: 'POST',
headers,
}, (res) => {
let data = ''
res.on('data', (chunk) => { data += chunk })
res.on('end', () => {
if (res.statusCode && res.statusCode >= 400) {
reject(new Error(`HTTP ${res.statusCode} – ${data}`))
return
}
resolve(data)
})
})
req.on('error', (err) => reject(err))
req.write(postData)
req.end()
})
let json: any
try {
json = JSON.parse(responseData)
} catch (e) {
throw new Error(`Failed to parse response: ${responseData}`)
}
if (json.errors) {
throw new Error('Introspection returned errors')
}
if (!json.data) {
throw new Error('No data in introspection response')
}
const schema = buildClientSchema(json.data as any)
return printSchema(schema)
}