Skip to content

Commit

Permalink
Add postgres:create shortcut command
Browse files Browse the repository at this point in the history
  • Loading branch information
antoineleclair committed Jun 27, 2024
1 parent a807b1d commit f7bdc99
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 6 deletions.
11 changes: 7 additions & 4 deletions src/auth-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface Handlers {
onMessage: (event: MessageEvent) => void
}

export function readEventSource(url: string, discoConfig: DiscoConfig, handlers: Handlers) {
export function readEventSource(url: string, discoConfig: DiscoConfig, handlers: Handlers): Promise<void> {
const params: EventSourceInitDict = {
headers: {
Accept: 'text/event-stream',
Expand All @@ -27,8 +27,11 @@ export function readEventSource(url: string, discoConfig: DiscoConfig, handlers:
// 'output' is our way of saying that we're sending a message
es.addEventListener('output', handlers.onMessage)
// sending 'end' is our way of signaling that we want to close the connection
es.addEventListener('end', () => {
es.close()
return new Promise((resolve) => {
es.addEventListener('end', () => {
es.close()
resolve()
})
})
}

Expand All @@ -46,7 +49,7 @@ export function request({
discoConfig: DiscoConfig
body?: unknown
expectedStatuses?: number[]
extraHeaders?: Record<string, string>,
extraHeaders?: Record<string, string>
bodyStream?: Readable
}) {
const params: RequestInit = {
Expand Down
138 changes: 138 additions & 0 deletions src/commands/postgres/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {Command, Flags} from '@oclif/core'
import {getDisco} from '../../config.js'
import {request, readEventSource} from '../../auth-request.js'

type PostgresInstance = {
name: string
version: string
created: string
}

type PostgresDatabase = {
created: string
name: string
}

export default class PostgresCreate extends Command {
static description = 'create a database for a project, ensuring addon and instance are installed'

static examples = ['<%= config.bin %> <%= command.id %>']

static flags = {
disco: Flags.string({required: false}),
project: Flags.string({required: true}),
'env-var': Flags.string({required: true, default: 'DATABASE_URL'}),
}

public async run(): Promise<void> {
const {flags} = await this.parse(PostgresCreate)
const discoConfig = getDisco(flags.disco || null)
let instances: PostgresInstance[] | undefined
let instanceName: string | undefined
{
// get list of instances
const url = `https://${discoConfig.host}/api/projects/postgres-addon/cgi/endpoints/instances`
const res = await request({
method: 'GET',
url,
discoConfig,
expectedStatuses: [200, 404],
extraHeaders: {
'X-Disco-Include-API-Key': 'true',
},
})
if (res.status === 200) {
const respBody = (await res.json()) as {instances: PostgresInstance[]}
instances = respBody.instances
} else {
// res.status == 404
this.log('Postgres addon not installed. Installing.')
const url = `https://${discoConfig.host}/api/projects`
const project = 'postgres-addon'
const body = {
name: project,
githubRepo: 'letsdiscodev/disco-addon-postgres',
}

const res = await request({method: 'POST', url, discoConfig, body, expectedStatuses: [201]})
const data = (await res.json()) as any
if (data.deployment) {
const url = `https://${discoConfig.host}/api/projects/${project}/deployments/${data.deployment.number}/output`
await readEventSource(url, discoConfig, {
onMessage(event: MessageEvent) {
process.stdout.write(JSON.parse(event.data).text)
},
})
}

instances = []
}
}

if (instances.length === 0) {
this.log('No Postgres instance yet. Adding one.')
const url = `https://${discoConfig.host}/api/projects/postgres-addon/cgi/endpoints/instances`
const res = await request({
method: 'POST',
url,
discoConfig,
expectedStatuses: [201],
extraHeaders: {
'X-Disco-Include-API-Key': 'true',
},
})
const respBody = (await res.json()) as {
instance: {name: string}
project: {name: string}
deployment: {number: number}
}
this.log(`Added instance ${respBody.instance.name}.`)
const deploymentUrl = `https://${discoConfig.host}/api/projects/${respBody.project.name}/deployments/${respBody.deployment.number}/output`

await readEventSource(deploymentUrl, discoConfig, {
onMessage(event: MessageEvent) {
process.stdout.write(JSON.parse(event.data).text)
},
})
instanceName = respBody.instance.name
} else {
instances.sort((a, b) => new Date(a.created).valueOf() - new Date(b.created).valueOf())
instanceName = instances.at(-1)?.name
this.log(`Using Postgres instance ${instanceName}.`)
}

let databaseName: string | undefined
{
this.log('Creating database.')
const url = `https://${discoConfig.host}/api/projects/postgres-addon/cgi/endpoints/instances/${instanceName}/databases`
const res = await request({
method: 'POST',
url,
discoConfig,
expectedStatuses: [201],
extraHeaders: {
'X-Disco-Include-API-Key': 'true',
},
})
const respBody = (await res.json()) as {database: PostgresDatabase}
this.log(respBody.database.name)
databaseName = respBody.database.name
}

{
this.log('Attaching database to project.')
const url = `https://${discoConfig.host}/api/projects/postgres-addon/cgi/endpoints/instances/${instanceName}/databases/${databaseName}/attach`
const reqBody = {envVar: flags['env-var'], project: flags.project}
await request({
method: 'POST',
url,
discoConfig,
body: reqBody,
expectedStatuses: [200],
extraHeaders: {
'X-Disco-Include-API-Key': 'true',
},
})
}
}
}
4 changes: 2 additions & 2 deletions src/commands/postgres/instances/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export default class PostgresInstancesAdd extends Command {
'X-Disco-Include-API-Key': 'true',
},
})
const respBody = (await res.json()) as {project: {name: string}; deployment: {number: number}}
this.log(`Added instance ${respBody.project.name}`)
const respBody = (await res.json()) as {instance: {name: string}, project: {name: string}; deployment: {number: number}}
this.log(`Added instance ${respBody.instance.name}`)
const deploymentUrl = `https://${discoConfig.host}/api/projects/${respBody.project.name}/deployments/${respBody.deployment.number}/output`

readEventSource(deploymentUrl, discoConfig, {
Expand Down

0 comments on commit f7bdc99

Please sign in to comment.