# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

`ows-product` is an Orchard (OWS) Flask microservice that serves product information (releases,
UPCs, localizations, tracks, artists) backed by the Art Relations (AR) MySQL database. It runs under
uWSGI in production and is part of the PDEGO `ows-*` service family.

## Commands

Setup creates a `venv/` (Python 3.13) — most `make` targets call `venv/bin/...` directly.

| Task | Command |
|---|---|
| Install deps + venv | `make pip_dev` |
| Run dev server (port 5000) | `make dev` (requires `.env` — `cp .env.shadow .env`) |
| Lint | `make lint` (`ruff check product/ tests/`) |
| Unit tests | `make test_unit` (everything under `tests/` except `tests/integration`) |
| Integration tests | `make test_integration` |
| Single test | `venv/bin/py.test tests/path/to/test_file.py::test_name` |
| Dockerized unit+lint / integration | `make docker_unit_lint` / `make docker_test_integration` |

Note: the README mentions `flake8`, but linting has been migrated to `ruff` (config in
`pyproject.toml`: line length 100, rules `E,F`). Unit tests run with `Environment=test`, which
swaps the DB to in-memory SQLite (`StaticPool`) — see `product/config.py`.

## Architecture

Request flow is a strict three-layer pipeline:

```
product/handlers.py   → HTTP routing + auth gating; returns oto Response via flaskify()
product/logic/*.py    → business logic; the only layer that orchestrates models
product/models/*.py   → data access (SQLAlchemy Core against AR MySQL, PynamoDB for DynamoDB)
```

- **App wiring**: `application.py` imports `product.api` (creates the Flask `app`, runs
  `flask_request.setup`, instantiates `OwsClient` + `PdpAuthorizationBackend`) and
  `product.handlers` (registers every `@app.route` — **all routes live in `handlers.py`**).
- **Layering rule**: handlers never touch models directly; they call `logic/`, which calls
  `models/`. Handlers and logic communicate via `oto.response.Response` objects; `flaskify()`
  converts them to Flask responses at the handler boundary.
- **Models**: most are SQLAlchemy Core table modules against the AR MySQL DB via
  `product/connectors/mysql.py` (`db_session`). Raw multi-table reads live in `product/models/sql/`.
  DynamoDB-backed models use `product/connectors/dynamodb.py`.
- **Config & secrets**: `product/config.py` is the single source of env/DB/feature config. Outside
  `dev`/`test` it pulls credentials from AWS Secrets Manager (`FlaskSecretsManager`). `ENVIRONMENT`
  comes from the `Environment` env var.
- **Feature flags**: Split.io via `pythonfeatures`, accessed through
  `product/utils/feature_control_util.py` (flag names in `product/constants/features.py`).

## Authorization (in active migration to Permissions Platform)

This service is mid-migration from legacy auth to PP (`python-pdp-sdk`). **Read `AUTH.md` (repo
root) before touching anything auth-related** — it is the authoritative scan of every endpoint's
current posture and the phased PP migration plan.

Key facts (full detail in `AUTH.md`):
- **Three auth mechanisms coexist**: (1) grass headers via `owsrequest`
  (`flask_request.verify_grass_access` / `verify_grass_headers` / `verify_grass_ownership`),
  (2) `product/access_rules.yml` middleware, and (3) PP (`product/auth.py`).
- **Access-rules middleware is globally disabled** — `flask_request.setup(..., verify_access=False)`
  in `product/api.py`, so `access_rules.yml` is dormant and does not enforce.
- **PP is wired but not yet invoked by any handler.** `PdpAuthorizationBackend` is instantiated in
  `product/api.py`; `product/auth.py` holds the migration helpers (`assert_authorization`,
  `is_authorized_for_tenant`, `assert_authorization_for_tenant`, `get_tenant`). Adding PP enforcement
  means calling these from handlers, per `AUTH.md`'s per-endpoint plan.
- `get_tenant()` resolves a tenant from `product_id` only; non-product-keyed endpoints (UPC, track,
  ISRC, language, account) need a new resolver before PP can enforce.
- Endpoint → resource_type / action / proposed_roles mappings for PP live in Notion:
  [Content microservices: endpoints → resource / action / PP authorization](https://app.notion.com/p/121453445b6f46eaa8fdc02dc8789ad5?v=a4f766c448634863b267c6b37443c242)
  (filter `microservice = ows-product`).

## Tests

- `tests/` mirrors the package (`handlers/`, `logic/`, `models/`, `validation/`); integration tests
  live in `tests/integration/` and hit a real deployed environment (QA) with bearer-token personas
  defined in `tests/integration/conftest.py` (e.g. `ows_product_user_headers`,
  `ows_product_content_user_headers`, `ows_product_pp_user_headers`,
  `ows_product_pp_no_access_user_headers`, `unauthorized_headers`).
- Recent work has been adding integration "baseline" tests that capture current access behavior per
  endpoint before PP migration (see git history, PP-13xx / PP-1518).
