# Grant Label to User

Grants an **existing** user (already in Neo4j + Auth0 + `art_relations`) access to an additional vendor label. Unlike `create-test-user`, there is no Auth0 invite, no MFA enrollment, no AWS secret and no Auth0 metadata patch — the user already exists, so this only adds the label access. It reuses the user's existing `contact_id` and opens the same two PRs (Neo4j cypher + `art_relations` SQL) so the grant survives the nightly QA refresh, with an optional immediate-apply step.

Use this as the day-to-day path for devs who repeatedly work on the same labels: grant your own user once, then switch into the label via the account switcher in the app instead of going through OA impersonation every time.

## What's different from `create-test-user`

| Aspect | `create-test-user` | `grant-label-to-user` |
| --- | --- | --- |
| Target user | Brand-new, invited from scratch | Existing user (already in Neo4j + Auth0 + `art_relations`) |
| Auth0 | Invite + Universal Login + MFA enroll | None (user already authenticates) |
| AWS Secrets | Stores credentials | None |
| Auth0 metadata | Patched with profile/contact IDs | None |
| `contact_id` | Allocates a new one | Reuses the user's existing one |
| Output | Up to 11 steps | 7 steps (two PRs + optional immediate apply) |

## Steps

| # | Step | Description |
| --- | --- | --- |
| 1 | Gather input | Interactive prompts or CLI flags (email, vendor, roles) |
| 2 | Find identity | Resolves the user's identity ID + Auth0 user ID in Neo4j, checks existing access to the target vendor |
| 3 | Determine contact ID | Reuses the user's existing `art_relations` `contact_id`, checks the MySQL side of access |
| 4 | Determine new IDs | Clones the QA-refresh repos and picks the next free `profileId` + `vend_contact.id` |
| 5 | Neo4j Cypher PR | Generates a Cypher script and pushes a PR on `python-neo4j-cypher-scheduler` |
| 6 | SQL PR | Generates `art_relations` SQL inserts and pushes a PR on `python-rds-utils` |
| 7 | Immediate apply | Optional: runs the same Cypher + SQL live so access works before the PRs merge |

If the user already has full access to the target vendor (both the Neo4j path and the MySQL `vend_contact` row), the tool reports it and exits without opening PRs or allocating IDs. If only one side exists (partial-state drift), it refuses and tells you to fix the drift manually rather than allocate a second profile.

## Prerequisites

- Node.js 22+ (see `.nvmrc`)
- Valid AWS session (use AWSUME)
- Access to GitHub repos: `theorchard/python-rds-utils`, `theorchard/python-neo4j-cypher-scheduler`
- The target user must already exist in QA (Neo4j identity + Auth0 + an `art_relations` contact). For a brand-new user, use `create-test-user` instead.

## Usage

```bash
# Interactive mode — prompts for email, vendor, and roles
pnpm grant-label-to-user

# Resume from saved state
pnpm grant-label-to-user --resume

# Non-interactive (no '--' separator with pnpm; Commander treats it as end-of-options)
pnpm grant-label-to-user --email user@sonymusic-pde.com \
    --vendor-id 18805 --vendor-name "Thirty Tigers" --roles catalog,analytics --yes
```

Step 7 asks whether to apply the changes to live QA now (Neo4j + RDS). Answer yes for instant access before the PRs merge; otherwise access lands after the next QA refresh once both PRs are merged. In `--yes` mode the immediate apply is skipped.

## CLI Flags

| Flag | Description | Default |
| --- | --- | --- |
| `-r, --resume` | Resume from previously saved state | — |
| `-y, --yes` | Non-interactive mode (requires `--email`, `--vendor-id`, `--vendor-name`) | `false` |
| `--email <email>` | Email of the existing user | _(prompted)_ |
| `--vendor-id <id>` | Target vendor ID | _(prompted)_ |
| `--vendor-name <name>` | Target vendor display name (used in the PR description) | _(prompted)_ |
| `--roles <roles>` | Comma-separated roles | `catalog` |

Available roles: `catalog`, `marketing`, `analytics`, `administrator`, `accounting`, `manage rights`, `advertising`.

If a role name contains a space (`manage rights`), quote the whole value so the shell doesn't split it: `--roles "catalog,manage rights"`.

## State Persistence

State is saved after every step to `tmp/grant-label-to-user/state.json`; resume with `--resume`. Cypher/SQL PR branches are named `AUT-grant-label-to-user-<idx>` with an auto-incrementing index.
