feature: add db:create command (#8760)

This commit is contained in:
Harminder Virk
2024-08-26 15:04:52 +05:30
committed by GitHub
parent cd4063de74
commit 7c2cfc132a
9 changed files with 396 additions and 1 deletions
@@ -51,6 +51,26 @@ export class EnvEditor {
)
}
/**
* Returns the value for an existing key from the
* ".env" file
*/
get(key: string): string | null {
const envFile = this.#files.find((file) => file.filePath.endsWith(".env"))
if (!envFile) {
return null
}
const matchingLine = envFile.contents.find((line) =>
line.startsWith(`${key}=`)
)
if (!matchingLine) {
return null
}
const [_, ...rest] = matchingLine.split("=")
return rest.join("=")
}
/**
* Set key-value pair to the dot-env files.
*
+1
View File
@@ -26,5 +26,6 @@ export * from "./shipping"
export * from "./totals"
export * from "./totals/big-number"
export * from "./user"
export * from "./pg"
export const MedusaModuleType = Symbol.for("MedusaModule")
+52
View File
@@ -0,0 +1,52 @@
/*
|--------------------------------------------------------------------------
| PostgreSQL utilities
|--------------------------------------------------------------------------
|
| @note
| These utlities does not rely on an MedusaJS application and neither
| uses the Mikro ORM.
| The goal here is to run DB operations without booting the application.
|
| For example:
| Creating a database from CLI, or checking if a database exists
|
*/
import { Client, type ClientConfig } from "pg"
import { parse } from "pg-connection-string"
/**
* Parsers the database connection string into an object
* of postgreSQL options
*/
export function parseConnectionString(connectionString: string) {
return parse(connectionString)
}
/**
* Creates a PostgreSQL database client using the connection
* string or database options
*/
export function createClient(options: string | ClientConfig) {
return new Client(options)
}
/**
* Creates a database using the client. Make sure to call
* `client.connect` before using this utility.
*/
export async function createDb(client: Client, databaseName: string) {
await client.query(`CREATE DATABASE "${databaseName}"`)
}
/**
* Checks if a database exists using the Client. Make sure to call
* `client.connect` before using this utility.
*/
export async function dbExists(client: Client, databaseName: string) {
const result = await client.query(
`SELECT datname FROM pg_catalog.pg_database WHERE datname='${databaseName}';`
)
return !!result.rowCount
}
@@ -0,0 +1,118 @@
import { dropDatabase } from "pg-god"
import {
createClient,
parseConnectionString,
dbExists,
createDb,
} from "../../index"
const DB_HOST = process.env.DB_HOST ?? "localhost"
const DB_USERNAME = process.env.DB_USERNAME ?? ""
const DB_PASSWORD = process.env.DB_PASSWORD ?? ""
export const pgGodCredentials = {
user: DB_USERNAME,
password: DB_PASSWORD,
host: DB_HOST,
}
describe("Pg | createClient", () => {
test("create client connection from connection options", async () => {
const client = createClient(pgGodCredentials)
await client.connect()
await client.end()
})
test("create client connection from connectionString without db name", async () => {
const client = createClient(
`postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}`
)
await client.connect()
await client.end()
})
test("create client connection from connectionString with db name", async () => {
const client = createClient(
`postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/foo`
)
await expect(() => client.connect()).rejects.toMatchInlineSnapshot(
`[error: database "foo" does not exist]`
)
})
})
describe("Pg | parseConnectionString", () => {
test("parse connection string without db name", async () => {
const options = parseConnectionString(
`postgres://${DB_USERNAME}:@${DB_HOST}`
)
expect(options).toEqual({
user: DB_USERNAME,
password: "",
host: DB_HOST,
port: "",
database: null,
})
})
test("parse connection string with db name", async () => {
const options = parseConnectionString(
`postgres://${DB_USERNAME}:@${DB_HOST}/foo`
)
expect(options).toEqual({
user: DB_USERNAME,
password: "",
host: DB_HOST,
port: "",
database: "foo",
})
})
})
describe("Pg | dbExists", () => {
beforeEach(async () => {
await dropDatabase({ databaseName: "foo" }, pgGodCredentials)
})
afterAll(async () => {
await dropDatabase({ databaseName: "foo" }, pgGodCredentials)
})
test("return false when db does not exist", async () => {
const options = parseConnectionString(
`postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/foo`
)
const client = createClient({
host: options.host!,
port: options.port ? Number(options.port) : undefined,
user: options.user,
password: options.password,
})
await client.connect()
const exists = await dbExists(client, options.database!)
await client.end()
expect(exists).toBe(false)
})
test("return true when db exist", async () => {
const options = parseConnectionString(
`postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/foo`
)
const client = createClient({
host: options.host!,
port: options.port ? Number(options.port) : undefined,
user: options.user,
password: options.password,
})
await client.connect()
await createDb(client, options.database!)
const exists = await dbExists(client, options.database!)
await client.end()
expect(exists).toBe(true)
})
})