Skip to content
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
69 changes: 69 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Deploy Documentation
on:
push:
branches: [ 'main' ]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:
timeout-minutes: 20
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')"
env:
PG_MAJOR: 14
steps:
- uses: actions/checkout@v4
- uses: bleep-build/bleep-setup-action@0.0.1
- uses: coursier/cache-action@v6
with:
extraFiles: bleep.yaml

- name: Start up Postgres
run: docker compose up -d

- name: Generate docs with mdoc
run: |
bleep generate-docs

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: site/package-lock.json

- name: Install dependencies
run: npm ci
working-directory: ./site

- name: Build website
run: npm run build
working-directory: ./site

- name: Setup Pages
uses: actions/configure-pages@v3

- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: ./site/build

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ target
.vscode
site/docs
myproject/
frontpage-generated/
39 changes: 39 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,45 @@ npm run serve
- Check `bleep.yaml` for script configurations
- Use database introspection tools to verify schema

### Working with Frontpage Code Examples

The website's code examples are generated from real Typo code using the `frontpage` schema in the test database. This ensures that all examples shown on the website are accurate and compile correctly.

**Schema Location**: `init/data/frontpage/` contains:
- `schema.sql` - Database schema for frontpage examples
- `*.sql` - SQL files that generate Typo repositories and types

**Generation Process**:
1. **Modify Schema**: Edit files in `init/data/frontpage/` to change database structure or add new examples
2. **Restart Database**: Run `docker-compose down && docker-compose up -d` to apply schema changes
3. **Generate Code**: Run `bleep run GeneratedFrontpage` to generate fresh Scala code
4. **Update Website**: Manually copy the generated code from `frontpage-generated/` into website components in `site/src/components/FeatureShowcase/index.js`

**Important Notes**:
- The `frontpage-generated/` directory is gitignored - we don't check in generated code
- The frontpage schema uses real PostgreSQL features (domains, enums, foreign keys) to showcase Typo's capabilities
- Always verify examples compile by running `bleep generate-docs` after updating website code
- This is the only part of the documentation workflow that requires manual copying of generated code
- Website logo and favicon can be regenerated using `site/scripts/generate-logo.js` and `npm run generate-favicon`

**Example Workflow**:
```bash
# 1. Edit schema
vi init/data/frontpage/schema.sql

# 2. Restart database
docker-compose down && docker-compose up -d

# 3. Generate fresh code
bleep run GeneratedFrontpage

# 4. Copy relevant parts to website (manual step)
# Look in frontpage-generated/ for the code you need

# 5. Verify documentation builds
bleep generate-docs
```

## Troubleshooting

### Common Issues
Expand Down
3 changes: 3 additions & 0 deletions bleep.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ scripts:
generate-docs:
main: scripts.GenDocumentation
project: typo-scripts-doc
generate-frontpage:
main: scripts.GeneratedFrontpage
project: typo-scripts
generate-sources:
main: scripts.GeneratedSources
project: typo-scripts
Expand Down
9 changes: 9 additions & 0 deletions init/data/frontpage/complex-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Advanced parameter syntax example
SELECT p.*, a.city, e.salary
FROM frontpage.person p
JOIN frontpage.address a ON p.address_id = a.id
LEFT JOIN frontpage.employee e ON p.id = e.person_id
WHERE p.id = :person_id!
AND p.created_at >= :since!
AND a.country = :country:String?
AND (:max_salary? IS NULL OR e.salary <= :max_salary)
153 changes: 153 additions & 0 deletions init/data/frontpage/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
-- Schema for Typo front page examples
CREATE SCHEMA IF NOT EXISTS frontpage;

-- PostgreSQL domains example (create first)
CREATE DOMAIN frontpage.email AS TEXT CHECK (VALUE ~ '^[^@]+@[^@]+\.[^@]+$');

-- Enum type example
CREATE TYPE frontpage.user_status AS ENUM ('active', 'inactive', 'suspended');
CREATE TYPE frontpage.order_status AS ENUM ('pending', 'active', 'shipped', 'cancelled');
CREATE TYPE frontpage.user_role AS ENUM ('admin', 'manager', 'employee');

-- Basic example tables
CREATE TABLE frontpage.department (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
budget DECIMAL(12,2)
);

CREATE TABLE frontpage.user (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email frontpage.email NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
department_id UUID REFERENCES frontpage.department(id),
status frontpage.user_status DEFAULT 'active',
verified BOOLEAN DEFAULT false
);

-- Relationships example tables
CREATE TABLE frontpage.product (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
price DECIMAL(10,2) NOT NULL,
in_stock BOOLEAN DEFAULT true,
quantity INTEGER DEFAULT 0,
last_restocked TIMESTAMP,
last_modified TIMESTAMP DEFAULT NOW(),
tags TEXT[] DEFAULT '{}',
categories INTEGER[] DEFAULT '{}',
prices DECIMAL[] DEFAULT '{}',
attributes JSONB[] DEFAULT '{}'
);

CREATE TABLE frontpage.category (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL
);

CREATE TABLE frontpage.product_category (
product_id UUID REFERENCES frontpage.product(id),
category_id UUID REFERENCES frontpage.category(id),
PRIMARY KEY (product_id, category_id)
);

CREATE TABLE frontpage.order (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES frontpage.user(id),
product_id UUID REFERENCES frontpage.product(id),
status frontpage.order_status DEFAULT 'pending',
total DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
shipped_at TIMESTAMP
);

CREATE TABLE frontpage.order_item (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
order_id UUID REFERENCES frontpage.order(id),
product_id UUID REFERENCES frontpage.product(id),
quantity INTEGER NOT NULL,
price DECIMAL(10,2) NOT NULL,
shipped_at TIMESTAMP
);

-- Customers table for joins
CREATE TABLE frontpage.customer (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES frontpage.user(id),
company_name TEXT,
credit_limit DECIMAL(10,2),
verified BOOLEAN DEFAULT false
);

-- Many-to-many example
CREATE TABLE frontpage.role (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL UNIQUE
);

CREATE TABLE frontpage.user_role (
user_id UUID REFERENCES frontpage.user(id),
role_id UUID REFERENCES frontpage.role(id),
assigned_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (user_id, role_id)
);

-- Composite key example
CREATE TABLE frontpage.permission (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL UNIQUE
);

CREATE TABLE frontpage.user_permission (
user_id UUID REFERENCES frontpage.user(id),
permission_id UUID REFERENCES frontpage.permission(id),
granted_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (user_id, permission_id)
);

-- Advanced PostgreSQL types example
CREATE TABLE frontpage.location (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
position POINT,
area POLYGON,
ip_range INET,
metadata JSONB DEFAULT '{}'
);

-- Testing examples
CREATE TABLE frontpage.company (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL
);

ALTER TABLE frontpage.department ADD COLUMN company_id UUID REFERENCES frontpage.company(id);
ALTER TABLE frontpage.user ADD COLUMN manager_id UUID REFERENCES frontpage.user(id);
ALTER TABLE frontpage.user ADD COLUMN role frontpage.user_role DEFAULT 'employee';

-- Complex join examples
CREATE TABLE frontpage.employee (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
person_id UUID UNIQUE NOT NULL,
salary DECIMAL(10,2)
);

CREATE TABLE frontpage.person (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
address_id UUID,
created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE frontpage.address (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
city TEXT NOT NULL,
country TEXT NOT NULL
);

ALTER TABLE frontpage.person ADD CONSTRAINT fk_address
FOREIGN KEY (address_id) REFERENCES frontpage.address(id);

ALTER TABLE frontpage.employee ADD CONSTRAINT fk_person
FOREIGN KEY (person_id) REFERENCES frontpage.person(id);
12 changes: 12 additions & 0 deletions init/data/frontpage/update-user-status.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- sql/update-user-status.sql
UPDATE frontpage.user
SET
status = :new_status:frontpage.user_status!,
created_at = NOW() -- using created_at as modified_at since we don't have modified_at column
WHERE id = :user_id!
AND status != :new_status
RETURNING
id,
name,
status,
created_at as "modified_at:java.time.LocalDateTime!"
15 changes: 15 additions & 0 deletions init/data/frontpage/user-analytics.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- sql/user-analytics.sql
SELECT
u.name,
u.email,
COUNT(o.id) as order_count,
SUM(o.total) as lifetime_value,
MAX(o.created_at) as last_order_date
FROM frontpage.user u
LEFT JOIN frontpage.order o ON u.id = o.user_id
WHERE u.created_at >= :start_date:LocalDate!
AND u.status = :status:frontpage.user_status?
GROUP BY u.id, u.name, u.email
HAVING SUM(o.total) > :min_value:BigDecimal!
ORDER BY lifetime_value DESC
LIMIT :limit:Int!
3 changes: 3 additions & 0 deletions init/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ psql -d Adventureworks < /docker-entrypoint-initdb.d/data/install.sql
# this should have had a database by itself, but let's be lazy for now
psql -d Adventureworks < /docker-entrypoint-initdb.d/data/test-tables.sql
psql -d Adventureworks < /docker-entrypoint-initdb.d/data/issue148.sql

# Front page examples
psql -d Adventureworks < /docker-entrypoint-initdb.d/data/frontpage/schema.sql
18 changes: 18 additions & 0 deletions site-in/customization/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ val options = Options(
| `rewriteDatabase` | Let's you perform arbitrary rewrites of database schema snapshot. you can add/remove rows, foreign keys and so on. |
| `openEnums` | Controls if you want to tag tables ids as [open string enums](../type-safety/open-string-enums.md) |

## Database Libraries

Typo supports multiple Scala database libraries, each with specific optimizations:

- **Anorm** (`DbLibName.Anorm`) - Lightweight SQL parser for Play Framework
- **Doobie** (`DbLibName.Doobie`) - Functional JDBC layer for Cats Effect
- **ZIO-JDBC** (`DbLibName.ZioJdbc`) - Type-safe JDBC wrapper for ZIO

Each library generates code optimized for that specific ecosystem, including appropriate return types, error handling, and integration patterns.

```scala
val options = Options(
pkg = "myapp.db",
dbLib = Some(DbLibName.Anorm), // Choose your library
// ... other options
)
```

## Development options

| Field Name | Effect |
Expand Down
Loading