Skip to content
Open
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
58 changes: 21 additions & 37 deletions .github/workflows/typescript-prisma-integ-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,52 +77,56 @@ jobs:

- name: Build project
working-directory: ./typescript/prisma
env:
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
run: |
npm run build

- name: Run validator unit tests
working-directory: ./typescript/prisma
env:
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
run: |
npm run test -- --testPathPatterns=validate

- name: Run transformer unit tests
working-directory: ./typescript/prisma
env:
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
run: |
npm run test -- --testPathPatterns=transform

- name: Run workflow unit tests
working-directory: ./typescript/prisma
env:
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
run: |
npm run test -- --testPathPatterns=workflow

- name: Run CLI integration tests
working-directory: ./typescript/prisma
env:
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
run: |
npm run test -- --testPathPatterns=cli-integration

- name: Create auth helper
- name: Create psql auth helper
run: |
cat > /tmp/generate-prisma-url.sh << 'EOF'
configure_credentials() {
cat > /tmp/configure-psql.sh << 'EOF'
configure_psql() {
local user=$1

if [ "$user" = "admin" ]; then
local auth_type="admin-auth"
local schema="public"
else
local auth_type="auth"
local schema="$NON_ADMIN_SCHEMA"
fi

export PGHOST=$CLUSTER_ENDPOINT
export PGPASSWORD=$(aws dsql generate-db-connect-${auth_type}-token --region $REGION --hostname $PGHOST --expires-in 300)
export PGUSER=$user
export PGDATABASE="postgres"
export PGSSLMODE="verify-full"

# URL-encode password for consumption by Prisma.
prisma_pass=$(python -c "from urllib.parse import quote; print(quote('$PGPASSWORD', safe=''))")
export DATABASE_URL="postgresql://$PGUSER:$prisma_pass@$CLUSTER_ENDPOINT:5432/$PGDATABASE?sslmode=$PGSSLMODE&schema=$schema"
}
EOF

Expand All @@ -131,11 +135,7 @@ jobs:
env:
CLUSTER_USER: "admin"
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
source /tmp/generate-prisma-url.sh
configure_credentials $CLUSTER_USER

# Ensure clean state first.
npm run prisma:migrate-down

Expand All @@ -148,8 +148,8 @@ jobs:
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
source /tmp/generate-prisma-url.sh
configure_credentials $CLUSTER_USER
source /tmp/configure-psql.sh
configure_psql $CLUSTER_USER

# Add a new column to the vet model (after the name field)
sed -i '/model vet/,/^}/s/name String @db.VarChar(30)/name String @db.VarChar(30)\n phone String? @db.VarChar(20)/' prisma/veterinary-schema.prisma
Expand All @@ -160,9 +160,9 @@ jobs:
# Validate the modified schema first
npm run validate prisma/veterinary-schema.prisma

# Generate incremental migration using --from-url
# Generate incremental migration using --from-config-datasource (reads from prisma.config.ts)
# Use --force because Prisma may generate DROP CONSTRAINT for PKs
npm run dsql-migrate -- prisma/veterinary-schema.prisma -o prisma/migrations/incremental/migration.sql --from-url "$DATABASE_URL" --force
npm run dsql-migrate -- prisma/veterinary-schema.prisma -o prisma/migrations/incremental/migration.sql --from-config-datasource --force

# Show what was generated
cat prisma/migrations/incremental/migration.sql
Expand Down Expand Up @@ -190,7 +190,6 @@ jobs:
env:
CLUSTER_USER: "admin"
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
npm run sample

Expand All @@ -199,11 +198,7 @@ jobs:
env:
CLUSTER_USER: "admin"
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
source /tmp/generate-prisma-url.sh
configure_credentials $CLUSTER_USER

npm run prisma:migrate-down

- name: Create non-admin role
Expand All @@ -216,8 +211,8 @@ jobs:
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
source /tmp/generate-prisma-url.sh
configure_credentials $ADMIN_USER
source /tmp/configure-psql.sh
configure_psql $ADMIN_USER

# Ensure clean state first.
psql -c "REVOKE ALL PRIVILEGES ON SCHEMA \"$NON_ADMIN_SCHEMA\" FROM \"$NON_ADMIN_USER\"" || true
Expand All @@ -234,13 +229,8 @@ jobs:
working-directory: ./typescript/prisma
env:
CLUSTER_USER: "myuser"
NON_ADMIN_SCHEMA: "myschema"
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
source /tmp/generate-prisma-url.sh
configure_credentials $CLUSTER_USER

# Ensure clean state first.
npm run prisma:migrate-down

Expand All @@ -251,21 +241,15 @@ jobs:
env:
CLUSTER_USER: "myuser"
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
npm run sample

- name: Clean non-admin schema
working-directory: ./typescript/prisma
env:
CLUSTER_USER: "myuser"
NON_ADMIN_SCHEMA: "myschema"
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
source /tmp/generate-prisma-url.sh
configure_credentials $CLUSTER_USER

npm run prisma:migrate-down

- name: Clean non-admin role
Expand All @@ -278,8 +262,8 @@ jobs:
CLUSTER_ENDPOINT: ${{ needs.create-cluster.outputs.cluster-endpoint }}
REGION: ${{ needs.create-cluster.outputs.region }}
run: |
source /tmp/generate-prisma-url.sh
configure_credentials $ADMIN_USER
source /tmp/configure-psql.sh
configure_psql $ADMIN_USER

psql -c "REVOKE ALL PRIVILEGES ON SCHEMA \"$NON_ADMIN_SCHEMA\" FROM \"$NON_ADMIN_USER\""
psql -c "DROP SCHEMA \"$NON_ADMIN_SCHEMA\" CASCADE"
Expand Down
40 changes: 14 additions & 26 deletions typescript/prisma/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ For more control over each step, you can run the tools separately:
# Generate and transform in one step
npx prisma migrate diff \
--from-empty \
--to-schema-datamodel prisma/schema.prisma \
--to-schema prisma/schema.prisma \
--script | npm run dsql-transform > prisma/migrations/001_init/migration.sql
```

Expand Down Expand Up @@ -117,7 +117,7 @@ npm run dsql-transform raw.sql -o migration.sql
# Transform using pipes (recommended)
npx prisma migrate diff \
--from-empty \
--to-schema-datamodel prisma/schema.prisma \
--to-schema prisma/schema.prisma \
--script | npm run dsql-transform > migration.sql
```

Expand Down Expand Up @@ -170,15 +170,15 @@ Note: The foreign key constraint is automatically removed since DSQL doesn't sup

## Incremental Migrations

After your initial deployment, when you need to make schema changes (add columns, tables, indexes), use the `--from-url` option to generate a migration that only includes the differences:
After your initial deployment, when you need to make schema changes (add columns, tables, indexes), use the `--from-config-datasource` option to generate a migration that only includes the differences:

```bash
npm run dsql-migrate prisma/schema.prisma \
-o prisma/migrations/002_add_email/migration.sql \
--from-url "$DATABASE_URL"
--from-config-datasource
```

This compares your updated schema against the live database and generates only the necessary changes.
This compares your updated schema against the live database (using credentials from `prisma.config.ts`) and generates only the necessary changes.

### Migration Ordering

Expand Down Expand Up @@ -217,7 +217,7 @@ If the primary key isn't actually changing (Prisma is just being cautious), use
```bash
npm run dsql-migrate prisma/schema.prisma \
-o prisma/migrations/002_add_email/migration.sql \
--from-url "$DATABASE_URL" \
--from-config-datasource \
--force
```

Expand Down Expand Up @@ -289,7 +289,7 @@ npm install

### Set environment variables

Set environment variables for your cluster details:
Set environment variables for your cluster details. These are required for all Prisma commands:

```bash
# e.g. "admin"
Expand All @@ -299,30 +299,18 @@ export CLUSTER_USER="<your user>"
export CLUSTER_ENDPOINT="<your endpoint>"
```

### Database migrations

Before running the example, you need to apply database migrations to create the required tables. Prisma's migration
tool requires a `DATABASE_URL` environment variable with authentication credentials.
### Build the project

Generate an authentication token following the instructions in
the [Aurora DSQL authentication token guide](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/SECTION_authentication-token.html)
and set it as the `CLUSTER_PASSWORD` environment variable, then set up the database URL:
Build the TypeScript code:

```bash
# Set schema based on user type.
if [ "$CLUSTER_USER" = "admin" ]; then
export SCHEMA="public"
else
export SCHEMA="myschema"
fi

# URL-encode password for consumption by Prisma.
export ENCODED_PASSWORD=$(python -c "from urllib.parse import quote; print(quote('$CLUSTER_PASSWORD', safe=''))")

# Set up DATABASE_URL for Prisma migrations.
export DATABASE_URL="postgresql://$CLUSTER_USER:$ENCODED_PASSWORD@$CLUSTER_ENDPOINT:5432/postgres?sslmode=verify-full&schema=$SCHEMA"
npm run build
```

### Database migrations

Before running the example, you need to apply database migrations to create the required tables. The `prisma.config.ts` file handles IAM authentication automatically using your AWS credentials.

Apply the database migrations:

```bash
Expand Down
26 changes: 18 additions & 8 deletions typescript/prisma/helpers/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ Examples:
npm run dsql-migrate prisma/schema.prisma -o prisma/migrations/001_init/migration.sql

# Incremental migration (after schema changes)
npm run dsql-migrate prisma/schema.prisma -o prisma/migrations/002_changes/migration.sql --from-url "$DATABASE_URL"
npm run dsql-migrate prisma/schema.prisma -o prisma/migrations/002_changes/migration.sql --from-config-datasource

# Manual workflow
npm run validate prisma/schema.prisma
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script | npm run dsql-transform > migration.sql
npx prisma migrate diff --from-empty --to-schema prisma/schema.prisma --script | npm run dsql-transform > migration.sql
`;

async function main(): Promise<void> {
Expand Down Expand Up @@ -118,14 +118,15 @@ Examples:
npm run dsql-migrate prisma/schema.prisma -o prisma/migrations/001_init/migration.sql

# Incremental migration (after schema changes)
npm run dsql-migrate prisma/schema.prisma -o prisma/migrations/002_changes/migration.sql --from-url "$DATABASE_URL"
npm run dsql-migrate prisma/schema.prisma -o prisma/migrations/002_changes/migration.sql --from-config-datasource
`);
process.exit(0);
}

let schemaPath: string | null = null;
let outputFile: string | null = null;
let fromUrl: string | null = null;
let fromConfigDatasource = false;
let fromEmpty = false;
let includeHeader = true;
let force = false;
Expand All @@ -140,6 +141,8 @@ Examples:
console.error("Error: --from-url requires a URL argument");
process.exit(1);
}
} else if (args[i] === "--from-config-datasource") {
fromConfigDatasource = true;
} else if (args[i] === "--from-empty") {
fromEmpty = true;
} else if (args[i] === "--no-header") {
Expand Down Expand Up @@ -168,7 +171,7 @@ Examples:
}

// Default to --from-empty if no --from-* option specified
if (!fromUrl && !fromEmpty) {
if (!fromUrl && !fromConfigDatasource && !fromEmpty) {
fromEmpty = true;
}

Expand All @@ -193,11 +196,18 @@ Examples:
}

// Step 2: Generate migration using Prisma
const fromSource = fromUrl ? "database" : "empty";
const fromSource = fromUrl || fromConfigDatasource ? "database" : "empty";
console.log(`\nGenerating migration (from ${fromSource})...`);

const fromArg = fromUrl ? `--from-url "${fromUrl}"` : "--from-empty";
const prismaCmd = `npx prisma migrate diff ${fromArg} --to-schema-datamodel "${schemaPath}" --script`;
let fromArg: string;
if (fromUrl) {
fromArg = `--from-url "${fromUrl}"`;
} else if (fromConfigDatasource) {
fromArg = "--from-config-datasource";
} else {
fromArg = "--from-empty";
}
const prismaCmd = `npx prisma migrate diff ${fromArg} --to-schema "${schemaPath}" --script`;

let rawSql: string;
try {
Expand Down Expand Up @@ -282,7 +292,7 @@ Examples:
npm run dsql-transform raw.sql -o migration.sql

# Transform using pipes
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script | npm run dsql-transform > migration.sql
npx prisma migrate diff --from-empty --to-schema prisma/schema.prisma --script | npm run dsql-transform > migration.sql

# Without header comment
npm run dsql-transform raw.sql --no-header -o migration.sql
Expand Down
Loading
Loading