Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🗃️ pomelo: db schema and migration setup #12

Merged
merged 2 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ node_modules/
build/
dist/
web-build/
drizzle/
/android/
/ios/

Expand Down
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"pomelo": "bun run --cwd pomelo dev",
"test": "concurrently 'npm:test:*' --group -c auto",
"test:ts:expo": "tsc",
"test:ts:node": "tsc -p tsconfig.node.json",
Expand Down
49 changes: 49 additions & 0 deletions pomelo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Setup

Project uses Vercel serverless functions to provide endpoints for Pomelo integration.
All exposed endpoints are found under `/api`.

## Local Development

Install dependencies on parent directory (`mobile`)

```bash
bun i
```

Spin up dev server

```bash
bun pomelo
```

## Database

Project uses Vercel Postgres DB and drizzle as an ORM.
DB auth is handled by `@vercel/postgres` when running `vercel dev`

### Migrations

After changing the schema declaration in `db/schema.th` you can run

```bash
bun db:migrate
```

to apply schema changes directy to DB.

### Advanced

If needed, migration can be done in 2 steps, first generating the migration SQL query and then executing it.
After changing the schema declaration in `db/schema.th` run:

``` bash
bun db:migration:generate
```

The SQL migration file will be generated under `/drizzle` with a number and unique name.
To execute it, run:

``` bash
bun db:migration:push
```
6 changes: 6 additions & 0 deletions pomelo/database/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { sql } from "@vercel/postgres";
import { drizzle } from "drizzle-orm/vercel-postgres";

import * as schema from "./schema.js";

export default drizzle(sql, { schema });
17 changes: 17 additions & 0 deletions pomelo/database/migrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import debug from "debug";
import { migrate } from "drizzle-orm/vercel-postgres/migrator";

import database from "./index.js";

const log = debug("pomelo");

try {
log("🏗️ migration started");
await migrate(database, {
migrationsFolder: "drizzle",
});
log("✅ migration finished");
} catch (error) {
log("❌ migration failed", error);
throw error;
}
44 changes: 44 additions & 0 deletions pomelo/database/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { jsonb, pgEnum, pgTable, text, numeric } from "drizzle-orm/pg-core";

import { CARD_STATUS, OPERATION_COUNTRIES, USER_STATUS } from "../utils/types";

export const userStatusEnum = pgEnum("status", USER_STATUS);
export const countryEnum = pgEnum("operation_country", OPERATION_COUNTRIES);
export const cardStatusEnum = pgEnum("status", CARD_STATUS);
export const transactionStatusEnum = pgEnum("transaction_status", ["APPROVED", "REJECTED"]);

export const users = pgTable("users", {
id: text("id").primaryKey(),
client_id: text("client_id"),
email: text("email"),
status: userStatusEnum("status"),
operation_country: countryEnum("operation_country"),
name: text("name"),
surname: text("surname"),
payload: jsonb("payload").notNull(),
});

export const card = pgTable("cards", {
id: text("id").primaryKey(),
user_id: text("user_id")
.references(() => users.id)
.notNull(),
affinity_group_id: text("affinity_group_id").notNull(),
status: cardStatusEnum("status").notNull(),
last_four: text("last_four"),
payload: jsonb("payload").notNull(),
});

export const transaction = pgTable("transactions", {
id: text("id").primaryKey(),
card_id: text("card_id")
.references(() => card.id)
.notNull(),
user_id: text("user_id")
.references(() => users.id)
.notNull(),
settlement_amount: numeric("settlement_amount").notNull(),
txHash: text("txHash"),
transaction_status: transactionStatusEnum("transaction_status").notNull(),
payload: jsonb("payload").notNull(),
});
12 changes: 10 additions & 2 deletions pomelo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,27 @@
"name": "@exactly/pomelo",
"type": "module",
"scripts": {
"test": "tsc"
"test": "tsc",
"db:migration:generate": "drizzle-kit generate:pg --schema=./database/schema.ts",
"db:migration:push": "bun run database/migrate.ts",
"db:migrate": "bun db:migration:generate && bun db:migration:push",
"dev": "bun run --cwd .. vercel dev"
},
"engines": {
"node": ">=20.0.0"
},
"dependencies": {
"@vercel/node": "^3.0.11",
"@vercel/postgres": "^0.5.1",
"@wagmi/core": "2.0.0-beta.6",
"drizzle-orm": "^0.29.1",
"viem": "2.0.0-beta.15",
"zod": "^3.22.4"
"zod": "^3.22.4",
"debug": "^4.3.4"
},
"devDependencies": {
"@types/node": "^20.10.4",
"drizzle-kit": "^0.20.6",
"typescript": "^5.3.3",
"vercel": "^32.6.1"
}
Expand Down
4 changes: 2 additions & 2 deletions pomelo/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"compilerOptions": {
"lib": ["ESNext"],
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"noUncheckedIndexedAccess": true,
"resolveJsonModule": true,
"esModuleInterop": true,
Expand Down
10 changes: 7 additions & 3 deletions pomelo/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { z } from "zod";

const country = z.enum(["ARG", "BRA", "MEX", "COL", "PER", "CHL"]);
export const OPERATION_COUNTRIES = ["ARG", "BRA", "MEX", "COL", "PER", "CHL"] as const;
export const CARD_STATUS = ["ACTIVE", "BLOCKED", "DISABLED"] as const;
export const USER_STATUS = ["ACTIVE", "BLOCKED"] as const;

const country = z.enum(OPERATION_COUNTRIES);

const date = z.string();

Expand Down Expand Up @@ -49,7 +53,7 @@ export const user = z.object({
id: z.string().regex(/^usr-.*/),
client_id: z.string().regex(/^cli-.*/),
email: z.string().email(),
status: z.enum(["ACTIVE", "BLOCKED"]),
status: z.enum(USER_STATUS),
operation_country: country,
name: z.string().optional(),
surname: z.string().optional(),
Expand Down Expand Up @@ -80,7 +84,7 @@ export const card = z.object({
affinity_group_id: z.string().regex(/^afg-.*/),
card_type: z.enum(["VIRTUAL", "PHYSICAL"]),
product_type: z.enum(["PREPAID", "DEBIT", "CREDIT"]).optional(),
status: z.enum(["ACTIVE", "BLOCKED", "DISABLED"]).optional(),
status: z.enum(CARD_STATUS).optional(),
shipment_id: z
.string()
.regex(/^shi-.*/)
Expand Down