Skip to content

Commit

Permalink
feat(postgres): sim support (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
Chriscbr authored Dec 4, 2023
1 parent ab709ed commit 5e76122
Show file tree
Hide file tree
Showing 7 changed files with 1,919 additions and 205 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion postgres/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

This library allows using postgres with Wing.

## Prerequisites

* [winglang](https://winglang.io)
* [Neon](https://neon.tech/) free-tier account (for deploying on AWS)

## Installation

Use `npm` to install this library:
Expand Down Expand Up @@ -50,7 +55,8 @@ When you deploy Terraform that uses the `Database` class, you will need to have
- [x] Support `tf-aws` platform using Neon
- [ ] Support `sim` platform
- [ ] Make all Neon databases share a Neon project to stay within the free tier
- [ ] Initialize secret value only `cloud.Secret` APIs
- [ ] Reuse postgres client across multiple queries by requiring users to call `connect()` / `end()` methods
- [ ] Initialize secret value through `cloud.Secret` APIs - https://github.com/winglang/wing/issues/2726
- [ ] Support [parameterized queries](https://node-postgres.com/features/queries#parameterized-query) for preventing SQL injection
- [ ] Customize [type parser](https://node-postgres.com/features/queries#types) for most popular postgres types / conversions to Wing types
- [ ] Have `query()` return both rows and a list of field names
Expand Down
10 changes: 4 additions & 6 deletions postgres/lib.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ bring expect;
bring util;
bring "./lib.w" as l;

if util.env("WING_TARGET") == "tf-aws" {
let db = new l.Database(name: "test", pgVersion: 15);
let db = new l.Database(name: "test", pgVersion: 15);

test "run a simple query" {
let result = db.query("SELECT 1 as one, 2 as two;");
expect.equal(result.at(0), {one: 1, two: 2});
}
test "run a simple query" {
let result = db.query("SELECT 1 as one, 2 as two;");
expect.equal(result.at(0), {one: 1, two: 2});
}
83 changes: 67 additions & 16 deletions postgres/lib.w
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
bring aws;
bring cloud;
bring containers;
bring http;
bring util;
bring "constructs" as constructs;
bring "cdktf" as cdktf;
bring "@rybickic/cdktf-provider-neon" as rawNeon;
bring "@cdktf/provider-aws" as tfaws;

struct Credentials {
host: str;
user: str;
password: str;
dbname: str;
}

pub struct DatabaseProps {
/**
* The database name.
Expand All @@ -22,7 +17,7 @@ pub struct DatabaseProps {
* The postgres version.
* @default 15
*/
pgVersion: num;
pgVersion: num?;
}

pub interface IDatabase {
Expand All @@ -33,7 +28,9 @@ pub class Database {
inner: IDatabase;
new(props: DatabaseProps) {
let target = util.env("WING_TARGET");
if target == "tf-aws" {
if target == "sim" {
this.inner = new DatabaseSim(props);
} elif target == "tf-aws" {
this.inner = new DatabaseNeon(props);
} else {
throw "Unsupported target: " + target;
Expand All @@ -45,7 +42,44 @@ pub class Database {
}
}

pub class DatabaseNeon impl IDatabase {
class DatabaseSim impl IDatabase {
url: str?;
new(props: DatabaseProps) {
let image = "postgres:{props.pgVersion ?? 15}";
let container = new containers.Workload(
name: "postgres",
image: image,
env: {
POSTGRES_PASSWORD: "password"
},
port: 5432,
public: true,
// TODO: implement readiness check?
);
this.url = container.publicUrl;
}

pub inflight query(query: str): Array<Map<Json>> {
let port = num.fromStr(http.parseUrl(this.url ?? "error").port);
return PgUtil._query(query, {
host: "localhost",
password: "password",
database: "postgres",
user: "postgres",
port: port,
ssl: false,
});
}
}

struct DbCredentials {
host: str;
user: str;
password: str;
database: str;
}

class DatabaseNeon impl IDatabase {
creds: cloud.Secret;

new(props: DatabaseProps) {
Expand All @@ -54,7 +88,7 @@ pub class DatabaseNeon impl IDatabase {
// TODO: share a project between multiple databases
let project = new rawNeon.project.Project(
name: props.name,
pgVersion: props.pgVersion,
pgVersion: props.pgVersion ?? 15,
);

let db = new rawNeon.database.Database(
Expand All @@ -74,7 +108,7 @@ pub class DatabaseNeon impl IDatabase {
host: project.databaseHost,
user: project.databaseUser,
password: project.databasePassword,
dbname: project.databaseName,
database: project.databaseName,
})
) as "NeonCredentialsVersion";
}
Expand All @@ -90,10 +124,27 @@ pub class DatabaseNeon impl IDatabase {
return new rawNeon.provider.NeonProvider() as singletonKey in stack;
}

extern "./pg.js" static inflight _query(query: str, creds: Credentials): Array<Map<Json>>;

pub inflight query(query: str): Array<Map<Json>> {
let creds = Credentials.fromJson(this.creds.valueJson());
return DatabaseNeon._query(query, creds);
let creds = DbCredentials.fromJson(this.creds.valueJson());
return PgUtil._query(query, {
host: creds.host,
user: creds.user,
password: creds.password,
database: creds.database,
ssl: true,
});
}
}

struct ConnectionOptions {
host: str;
port: num?; // default: 5432
user: str;
password: str;
database: str;
ssl: bool;
}

class PgUtil {
pub extern "./pg.js" static inflight _query(query: str, creds: ConnectionOptions): Array<Map<Json>>;
}
Loading

0 comments on commit 5e76122

Please sign in to comment.