Skip to content

Commit 7c6bf70

Browse files
committed
docs: update documentation for Episode feature and scraper improvements
1 parent fe38cda commit 7c6bf70

5 files changed

Lines changed: 120 additions & 24 deletions

File tree

CLAUDE.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ npm run hana # Deploy to HANA HDI container ("starwars")
3434
npm run load # Load Star Wars fixture data (hybrid/HANA profile)
3535
npm run load_sqlite # Load fixture data into SQLite
3636

37+
# Data scraping
38+
npm run scrape # full run, cache-first (fast — uses committed cache)
39+
npm run scrape:films # films-only, no episodes (reproduces original dataset)
40+
npm run scrape:bypass-cache # fetch fresh from Wookieepedia (requires confirmation)
41+
3742
# Docs generation
3843
npm run openapi # Generate OpenAPI docs → docs/
3944
npm run asyncapi # Generate AsyncAPI docs
@@ -62,13 +67,17 @@ cap/labs/ Hands-on exercises (lab-01 through lab-05)
6267

6368
### Domain Model (`cap/db/schema.cds`)
6469

65-
Six core entities: **Film**, **People**, **Planet**, **Species**, **Starship**, **Vehicle**. Many-to-many relationships use explicit junction entities (`Film2People`, `Film2Planets`, etc.) with redirected projections in services. All entities use `managed` + `cuid` from `@sap/cds/common`.
70+
Core entities: **Film**, **People**, **Planet**, **Species**, **Starship**, **Vehicle**, **Show**, **Episode**. Many-to-many relationships use explicit junction entities (`Film2People`, `Film2Planets`, `Episode2People`, `Episode2Planets`, etc.) with redirected projections in services. All entities use `managed` + `cuid` from `@sap/cds/common`.
71+
72+
`Episode` is a composition child of `Show` — episodes cascade-delete with their parent show. Five `Episode2*` junction tables (`Episode2People`, `Episode2Planets`, `Episode2Starships`, `Episode2Vehicles`, `Episode2Species`) link episodes to the core entity set.
73+
74+
`Show2Planets`, `Show2Starships`, `Show2Vehicles`, and `Show2Species` are CDS `define view` declarations that aggregate over the corresponding `Episode2*` tables rather than being physical tables.
6675

6776
Profile-specific extensions live in `cap/db/hana/`, `cap/db/sqlite/`, `cap/db/postgres/`. Always check these when making cross-profile changes.
6877

6978
### Service Layer (`cap/srv/`)
7079

71-
Six domain services (one per entity): `StarWarsFilm`, `StarWarsPeople`, `StarWarsPlanet`, `StarWarsSpecies`, `StarWarsStarship`, `StarWarsVehicle`. Protocols: OData v4 (primary), OData v2 (adapter), GraphQL (`/graphql`), REST.
80+
Six domain services (one per entity): `StarWarsFilm`, `StarWarsPeople`, `StarWarsPlanet`, `StarWarsSpecies`, `StarWarsStarship`, `StarWarsVehicle`. A seventh service, `StarWarsEpisode`, exposes `Episodes` and `Episode2*` junctions as read-only projections (no handler logic required). Protocols: OData v4 (primary), OData v2 (adapter), GraphQL (`/graphql`), REST.
7281

7382
**Critical file separation rule:**
7483
- Service contracts → `*-service.cds`
@@ -123,6 +132,6 @@ GitHub Actions auto-deploys to GitHub Pages on push to `main` when files under `
123132

124133
- **CDS modeling**: Preserve namespace and `managed`/`cuid` patterns. Prefer explicit many-to-many junction entities. Keep `Common.ValueList` and `UI.*` patterns consistent.
125134
- **Breaking changes**: Avoid renames/removals without migration intent. See `docs/value-help-migration.md` for a past breaking-change example.
126-
- **Data loading**: Use `convertDataLite.js` for SQLite (avoids `SQLITE_BUSY`); use `convertData.js` for HANA (parallel chunk loading).
135+
- **Data loading**: Use `convertData.js` for all profiles (handles parallel chunk loading; SQLite-safe). The `load_sqlite` npm script invokes this directly.
127136
- **Generated folders**: `cap/gen/` is auto-generated. After HANA deployment, re-run `npm run build` if `gen/srv` is cleared.
128137
- **cds-mcp**: When editing CDS files, resolve entity/field definitions with `cds-mcp` before modifying models, and check CAP docs via `cds-mcp` before proposing CDS syntax or API usage.

README.md

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,48 @@ The original project was a data set and data model based in Python that exposed
1010

1111
The projects described above have fallen out of maintenance but still offered the opportunity for a fun yet challenging learning experience from a non-trivial data model. The many bi-directional, many-to-many relationships with the data provides a good basis for an SAP Cloud Application Programming Model and Fiori Draft UI sample.
1212

13+
This project extends the original data set with **TV Shows and streaming series** (The Mandalorian, The Clone Wars, Andor, etc.) scraped live from Wookieepedia, and introduces unified `Media` views that query across both films and shows.
14+
1315
### Data Model
1416

1517
#### Films
1618

1719
![film diagram](images/Film.png)
1820

21+
#### Shows (TV Series & Streaming)
22+
23+
The `Show` entity represents all canon Star Wars TV and streaming productions. It shares the same relationship structure as `Film` — each show can be linked to characters, planets, starships, vehicles, and species.
24+
25+
| Field | Type | Notes |
26+
| --- | --- | --- |
27+
| `title` | String | `@mandatory` |
28+
| `show_type` | Enum | `LIVE_ACTION_SERIES`, `ANIMATED_SERIES`, `ANIMATED_FILM`, `ANTHOLOGY` |
29+
| `seasons` | Integer | Number of seasons |
30+
| `episode_count` | Integer | Total episode count |
31+
| `network` | String | Broadcaster / streaming platform |
32+
| `director` | String | Lead director(s) |
33+
| `producer` | String | Lead producer(s) |
34+
| `release_date` | Date | First air date |
35+
36+
Each show owns a composition of `Episode` records (cascade-delete). The `Episode` entity captures per-episode metadata: `season_number`, `episode_number`, `title`, `air_date`, `director`, `writer`, `runtime` (minutes), and `timeline` (in-universe date, e.g. "19 BBY").
37+
38+
Junction tables: `Show2People` (physical), `Episode2People`, `Episode2Planets`, `Episode2Starships`, `Episode2Vehicles`, `Episode2Species`
39+
40+
`Show2Planets`, `Show2Starships`, `Show2Vehicles`, and `Show2Species` are **CDS `define view`** declarations that aggregate over the corresponding `Episode2*` tables. They are not physical tables — show-level relationship data is derived from episode-level scraping.
41+
42+
#### Unified Media Views
43+
44+
Six read-only `UNION ALL` views join `Film` and `Show` data across a single query surface. The relationship views each include a branch for `Episode2*` data so that episode-level associations are surfaced at the media level:
45+
46+
| View | Returns |
47+
| --- | --- |
48+
| `Media` | All films and shows with a `media_type` discriminator (`'FILM'` / `'SHOW'`) |
49+
| `MediaCharacters` | All film+show+episode → character relationships |
50+
| `MediaPlanets` | All film+show+episode → planet relationships |
51+
| `MediaSpecies` | All film+show+episode → species relationships |
52+
| `MediaStarships` | All film+show+episode → starship relationships |
53+
| `MediaVehicles` | All film+show+episode → vehicle relationships |
54+
1955
#### People
2056

2157
![people diagram](images/People.png)
@@ -43,15 +79,45 @@ The projects described above have fallen out of maintenance but still offered th
4379

4480
## Download and Installation
4581

46-
The original data model and data source files are in in the [oldPython\resources](./oldPython/resources/) folder.
82+
The rest of the operations can be performed within the [cap](./cap/) folder and there are scripts in the [package.json](./cap/package.json#L20) file for major operations.
83+
84+
You can use `npm run build` to perform the cds build and should be run before deployment to HANA or whenever you make changes to the data model.
4785

48-
The rest of the operations can be performed within the [cap](./cap/) folder and there are scripts in the [package.json](./cap/package.json#L20) file major operations.
86+
You can run `npm run hana` to deploy the content to your HANA database. Just be sure from the terminal that you are logged into the cf/xs cli and targeting the Account/Org/Space where you want the content to live. By default this command will create an HDI Container instance named **starwars**. **Note**: due to some strange circumstances in the latest versions of CAP it seems the `/gen/srv` folder is getting cleared after any deployment to HANA. Therefore just execute a `cds build` or `npm run build` after any deployment to restore the `/gen` folder until we find the root cause of this issue.
87+
88+
### Data Pipeline
89+
90+
Star Wars data is sourced from [Wookieepedia](https://starwars.fandom.com/wiki/Wookieepedia) via a rate-limited MediaWiki API scraper. The pipeline is two steps:
91+
92+
**Step 1 — Scrape** (optional — committed cache makes this instantaneous):
93+
94+
```bash
95+
cd cap
96+
npm run scrape # cache-first run — uses committed cache, completes in seconds
97+
npm run scrape:films # films-only run (no episodes); reproduces the original SWAPI dataset
98+
npm run scrape:bypass-cache # fetch fresh data from Wookieepedia (prompts for confirmation)
99+
```
100+
101+
The scraper crawls three levels deep: Show page → Season page → Episode page. It writes nine JSON files to `scripts/data/raw/`:
102+
`films.json`, `shows.json`, `episodes.json`, `people.json`, `planets.json`, `species.json`, `starships.json`, `vehicles.json`, `relationships.json`
103+
104+
Cache files (`scripts/data/cache/`) are committed to the repository (~6,469 files), so clones can run `npm run scrape` without any network access. Use `--bypass-cache` only when you need genuinely fresh Wookieepedia data.
105+
106+
**Step 2 — Load** into your target database:
107+
108+
```bash
109+
npm run load_sqlite # SQLite (local development)
110+
npm run load # SAP HANA Cloud (hybrid profile — requires .cdsrc-private.json)
111+
npm run load_pg # PostgreSQL
112+
```
49113

50-
You can use `npm run build` to perform the cds build and should be ran before deployment to HANA or whenever you make changes to the data model.
114+
The loading script is [convertData.js](./cap/convertData.js). It reads the raw JSON files and upserts all entities and junction records using CAP CQL.
51115

52-
You can run `npm run hana` to deploy the content to your HANA database. Just be sure from the terminal that you are logged into the cf/xs cli and targeting the Account/Org/Space where you want the content to live. By default this command will create an HDI Container instance named **starwars**. **Note**: due to some strange circumstances in the latest versions of CAP it seems the `/gen/srv` folder is getting cleared after any deployment to HANA. Therefore just execute a `cds build` or `npm run build` after any deployment to restore the `/gen` folder until we find the root cause of this issue.
116+
**Scraped data included:**
53117

54-
You can run the command `npm run load`. This command will read the original JSON data files from the source project and load them into your HANA database using Cloud Application Programming Model [CQL](https://cap.cloud.sap/docs/cds/cql). The loading script is [convertData.js](./cap/convertData.js)
118+
* **11 canon films** — Episodes I–IX, Rogue One, Solo (with directors, producers, release dates)
119+
* **15 shows** — 8 live-action series + 6 animated series (The Mandalorian, The Clone Wars, Andor, Rebels, etc.)
120+
* **772 episodes** — per-episode metadata and entity relationships (characters, planets, starships, vehicles, species)
55121

56122
The command `npm start` or `cds run` will start the service running locally. It will open the standard CAP test page where you can explore the OData Services or the Fiori UI.
57123

cap/README.md

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,25 @@ A learning-focused SAP Cloud Application Programming Model (CAP) sample built on
99
## Project Structure
1010

1111
| Path | Purpose |
12-
|------|---------|
12+
| --- | --- |
1313
| `db/schema.cds` | Domain model – entities, types, associations |
1414
| `db/hana/` `db/sqlite/` `db/postgres/` | Profile-specific model extensions |
1515
| `srv/*-service.cds` | Service definitions – projections, actions, functions |
1616
| `srv/*-fiori.cds` | Fiori/UI5 annotations (kept separate from service contracts) |
17+
| `srv/episode-service.cds` | Read-only `StarWarsEpisode` service (OData v4, GraphQL, REST) |
18+
| `srv/episode-fiori.cds` | Fiori annotations for the Episodes list |
1719
| `srv/services-auth.cds` | Centralized authorization – `@requires` / `@restrict` |
1820
| `srv/*.js` | Service handlers – `before` / `on` / `after` hooks |
1921
| `test/` | Automated tests by layer (model, service, handler) |
2022
| `docs/` | Architecture, learning path, cheat sheet, pitfalls |
2123
| `labs/` | Hands-on exercises with guided steps |
2224
| `app/` | UI frontends and Fiori Launchpad previews |
25+
| `../scripts/scraper/` | Wookieepedia scraper — rate-limited MediaWiki API crawler |
26+
| `../scripts/data/raw/` | Scraped JSON output loaded by `convertData.js` |
2327

2428
## Architecture at a Glance
2529

26-
```
30+
```text
2731
Client (Fiori / REST / GraphQL)
2832
2933
@@ -59,32 +63,37 @@ See [docs/cap-architecture.md](docs/cap-architecture.md) for the full annotated
5963
## Learning Tracks
6064

6165
### 🟢 Beginner Track
66+
6267
*Goal: understand CDS modeling and how CAP exposes it automatically.*
6368

6469
| # | Task | Where to look |
65-
|---|------|--------------|
70+
| --- | --- | --- |
6671
| 1 | Read `db/schema.cds` — find entities, types, associations | [db/schema.cds](db/schema.cds) |
6772
| 2 | Start the app (`npm run sqlite`) and browse `/odata/v4/StarWarsPeople/People` | [people-service.cds](srv/people-service.cds) |
6873
| 3 | Open Swagger UI at `http://localhost:4004/api-docs` | [server.js](srv/server.js) |
6974
| 4 | Understand how `@mandatory`, `@assert.range`, `@assert.format` work | [db/schema.cds](db/schema.cds) |
70-
| 5 | Complete **Lab 01** (add a new entity) | [labs/lab-01-model/](labs/lab-01-model/README.md) |
75+
| 5 | Find the `Episode` entity and its parent `Show` — notice the `Composition of many` | [db/schema.cds](db/schema.cds) |
76+
| 6 | Complete **Lab 01** (add a new entity) | [labs/lab-01-model/](labs/lab-01-model/README.md) |
7177

7278
### 🟡 Intermediate Track
79+
7380
*Goal: understand service handlers, events, and custom operations.*
7481

7582
| # | Task | Where to look |
76-
|---|------|--------------|
83+
| --- | --- | --- |
7784
| 1 | Trace the full `before → on → after` handler lifecycle for People | [people-service.js](srv/people-service.js) |
7885
| 2 | Call the `rename` bound action via HTTP | [people-service.cds](srv/people-service.cds) |
7986
| 3 | Call the `countByGender` unbound function | [people-service.cds](srv/people-service.cds) |
8087
| 4 | Understand how notifications + domain events are emitted | [people-service.js](srv/people-service.js) |
81-
| 5 | Complete **Lab 03** (add your own hook) | [labs/lab-03-handler/](labs/lab-03-handler/README.md) |
88+
| 5 | Compare `StarWarsEpisode` (read-only, no `.js` handler) with `StarWarsPeople` (full lifecycle) | [episode-service.cds](srv/episode-service.cds) |
89+
| 6 | Complete **Lab 03** (add your own hook) | [labs/lab-03-handler/](labs/lab-03-handler/README.md) |
8290

8391
### 🔴 Advanced Track
92+
8493
*Goal: master authorization, testing by layer, and multi-profile deployment.*
8594

8695
| # | Task | Where to look |
87-
|---|------|--------------|
96+
| --- | --- | --- |
8897
| 1 | Study the role matrix (`Viewer / Editor / Admin`) | [services-auth.cds](srv/services-auth.cds) |
8998
| 2 | Read handler-level tests and understand what each layer tests | [test/handler.test.js](test/handler.test.js) |
9099
| 3 | Compare SQLite vs HANA vs PG behavior | [docs/profile-comparison.md](docs/profile-comparison.md) |
@@ -105,7 +114,10 @@ npm run sqlite
105114
# Run all tests
106115
npm run test
107116

108-
# Load Star Wars fixture data (SQLite profile)
117+
# Regenerate scraped Star Wars data (cache-first — fast from committed cache files)
118+
npm run scrape
119+
120+
# Load Star Wars data into SQLite
109121
npm run load_sqlite
110122
```
111123

@@ -116,7 +128,7 @@ Browse to <http://localhost:4004> to see the service index, Swagger UI, and Fior
116128
## Runtime Profiles
117129

118130
| Profile | Command | Database | Use when |
119-
|---------|---------|----------|----------|
131+
| --- | --- | --- | --- |
120132
| `sqlite` | `npm run sqlite` | SQLite (file) | Local development, no external services |
121133
| `hybrid` | `npm run watch` | SAP HANA HDI | Integrated local dev with HANA |
122134
| `pg` | `npm run pg` | PostgreSQL | PostgreSQL-based environments |
@@ -128,9 +140,9 @@ See [docs/profile-comparison.md](docs/profile-comparison.md) for a full comparis
128140

129141
## CDS Configuration Notes
130142

131-
- `cds.requires.queue: true` — enables the persistent outbox queue for reliable event delivery.
132-
- `cds.requires.middlewares: true` — enables the full CAP middleware chain (CORS, authentication, etc.).
133-
- PostgreSQL credentials are intentionally **not hardcoded**; provide them via `.env` (see `cap/.env`).
143+
* `cds.requires.queue: true` — enables the persistent outbox queue for reliable event delivery.
144+
* `cds.requires.middlewares: true` — enables the full CAP middleware chain (CORS, authentication, etc.).
145+
* PostgreSQL credentials are intentionally **not hardcoded**; provide them via `.env` (see `cap/.env`).
134146

135147
---
136148

@@ -147,7 +159,7 @@ npm run test:migration # data conversion / migration tests
147159
## Learning Resources
148160

149161
| Resource | Link |
150-
|---------|------|
162+
| --- | --- |
151163
| CAP Architecture (this repo) | [docs/cap-architecture.md](docs/cap-architecture.md) |
152164
| Learning Path | [docs/learning-path.md](docs/learning-path.md) |
153165
| CAP Cheat Sheet | [docs/cap-cheat-sheet.md](docs/cap-cheat-sheet.md) |
@@ -160,5 +172,5 @@ npm run test:migration # data conversion / migration tests
160172

161173
## Upgrade Notes
162174

163-
- **Breaking change**: legacy value-help helper endpoints were removed in favor of `*Values` entities.
175+
* **Breaking change**: legacy value-help helper endpoints were removed in favor of `*Values` entities.
164176
Update using [docs/value-help-migration.md](docs/value-help-migration.md).

cap/docs/cap-architecture.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,17 @@ db/schema.cds
7878
├── Species
7979
├── Starship
8080
├── Vehicles
81+
├── Show (draft-enabled)
82+
│ └── Episode (composition child — cascade delete)
8183
├── Film2People (M:N junction with @assert.unique)
82-
└── ... (other junctions + views)
84+
├── Episode2People, Episode2Planets, Episode2Starships,
85+
│ Episode2Vehicles, Episode2Species (M:N junctions)
86+
└── Show2Planets, Show2Starships, Show2Vehicles, Show2Species
87+
(CDS define view over Episode2* — not physical tables)
8388
```
8489

90+
`define view` in CDS creates a SQL view rather than a physical table. It is the right choice when the data can be fully derived from another entity — there is no value in storing it redundantly. `Show2Planets` (and its siblings) are a concrete example: Wookieepedia show pages list no per-show relationships, but each episode page lists the planets it features. By defining the show-level view as an aggregation over `Episode2Planets`, show-level data is always correct and requires no separate load step.
91+
8592
### Service Layer (`srv/*-service.cds`)
8693

8794
Services **project** domain entities into API-facing views. The key insight:

cap/docs/learning-path.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Five progressive milestones, each building on the previous one. Every milestone
2222

2323
3. Trace how `Film2People` links `Film` and `People` (M:N relationship).
2424

25+
4. Find the `define view` declarations for `Show2Planets`, `Show2Starships`, `Show2Vehicles`, and `Show2Species`. Explain why they are views instead of physical junction tables. (Hint: where does the underlying data actually come from?)
26+
2527
**Key files:** `db/schema.cds`
2628

2729
**Check your understanding:** Run `npm run test` — all model tests should pass.
@@ -55,7 +57,7 @@ Five progressive milestones, each building on the previous one. Every milestone
5557

5658
**Key files:** `srv/people-service.cds`, `srv/film-service.cds`
5759

58-
**Check your understanding:** Open Swagger UI at `http://localhost:4004/api-docs` and explore the generated spec.
60+
**Check your understanding:** Open Swagger UI at `http://localhost:4004/api-docs` and explore the generated spec. Then compare `StarWarsEpisode` (`srv/episode-service.cds`) — a read-only service with no `.js` handler at all — with `StarWarsPeople`. Notice that CAP's generic provider handles all CRUD automatically when there is no `on` handler registered.
5961

6062
---
6163

0 commit comments

Comments
 (0)