Skip to content

Commit e129a8a

Browse files
committed
Admin migration guide
1 parent 4643fff commit e129a8a

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

doc/admin-api-migration.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Admin REST API Migration Guide
2+
3+
The admin REST API response shapes have changed as part of the authorization system refactoring. All endpoints use the same URL paths and HTTP methods. Request bodies are unchanged. Only response JSON shapes differ.
4+
5+
## Unchanged endpoints
6+
7+
These endpoints have no response changes:
8+
9+
| Endpoint | Description |
10+
|----------|-------------|
11+
| `POST /tenants/:id` | Update tenant name |
12+
| `POST /tenants/:id/groups` | Create group under tenant |
13+
| `POST /groups/:id` | Update group name |
14+
| `POST /tenants/:id/permissions` | Add permission to tenant |
15+
| `DELETE /tenants/:id/permissions` | Remove permission from tenant |
16+
| `POST /groups/:id/permissions` | Add permission to group |
17+
| `DELETE /groups/:id/permissions` | Remove permission from group |
18+
| `POST /groups/:id/tenant` | Set group's parent tenant |
19+
| `POST /feeds/:id/group` | Set feed's parent group |
20+
| `POST /feed_versions/:id/permissions` | Add permission to feed version |
21+
| `DELETE /feed_versions/:id/permissions` | Remove permission from feed version |
22+
| `GET /users` | List users |
23+
| `GET /users/:id` | Get user |
24+
25+
## Changed endpoints
26+
27+
### `GET /me`
28+
29+
```jsonc
30+
// Before
31+
{
32+
"user": {"id": "ian", "name": "Ian", "email": "ian@example.com"},
33+
"groups": [{"id": 1, "name": "BA-group"}],
34+
"expanded_groups": [{"id": 1, "name": "BA-group"}, {"id": 2, "name": "CT-group"}],
35+
"external_data": {"gatekeeper": "..."},
36+
"roles": ["admin"]
37+
}
38+
39+
// After
40+
{
41+
"id": "ian",
42+
"name": "Ian",
43+
"email": "ian@example.com",
44+
"groups": [{"id": 1, "name": "BA-group"}],
45+
"expanded_groups": [{"id": 1, "name": "BA-group"}, {"id": 2, "name": "CT-group"}],
46+
"external_data": {"gatekeeper": "..."},
47+
"roles": ["admin"]
48+
}
49+
```
50+
51+
**Changes:** User fields (`id`, `name`, `email`) are at the top level instead of nested under `user`.
52+
53+
### `GET /tenants`, `GET /groups`, `GET /feeds`, `GET /feed_versions`
54+
55+
All list endpoints now return a flat array of `ObjectRef` instead of a type-specific wrapper.
56+
57+
```jsonc
58+
// Before (GET /tenants)
59+
{
60+
"tenants": [
61+
{"id": 1, "name": "tl-tenant"},
62+
{"id": 2, "name": "other-tenant"}
63+
]
64+
}
65+
66+
// After (GET /tenants)
67+
[
68+
{"type": "tenant", "id": 1},
69+
{"type": "tenant", "id": 2}
70+
]
71+
```
72+
73+
```jsonc
74+
// Before (GET /feeds)
75+
{
76+
"feeds": [
77+
{"id": 5, "onestop_id": "BA", "name": "BART"},
78+
{"id": 6, "onestop_id": "CT", "name": "Caltrain"}
79+
]
80+
}
81+
82+
// After (GET /feeds)
83+
[
84+
{"type": "feed", "id": 5},
85+
{"type": "feed", "id": 6}
86+
]
87+
```
88+
89+
**Changes:**
90+
- Response is a JSON array, not an object with a type-specific key
91+
- Each item is `{"type": "...", "id": N}` instead of a full entity object
92+
- Entity names and other fields (e.g., `onestop_id`) are no longer included in list responses
93+
- Use the permissions endpoint (`GET /:type/:id`) to get full details for a specific entity
94+
95+
### `GET /tenants/:id`, `GET /groups/:id`, `GET /feeds/:id`, `GET /feed_versions/:id`
96+
97+
All permissions endpoints now return a generic `ObjectPermissions` shape instead of type-specific responses.
98+
99+
```jsonc
100+
// Before (GET /tenants/1)
101+
{
102+
"tenant": {"id": 1, "name": "tl-tenant"},
103+
"groups": [{"id": 2, "name": "BA-group"}],
104+
"actions": {
105+
"can_view": true,
106+
"can_edit": true,
107+
"can_edit_members": true,
108+
"can_create_org": true,
109+
"can_delete_org": true
110+
},
111+
"users": {
112+
"admins": [{"type": "user", "id": "ian", "name": "Ian", "relation": "admin"}],
113+
"members": [{"type": "user", "id": "drew", "name": "Drew", "relation": "member"}]
114+
}
115+
}
116+
117+
// After (GET /tenants/1)
118+
{
119+
"ref": {"type": "tenant", "id": 1, "name": "tl-tenant"},
120+
"actions": {
121+
"can_view": true,
122+
"can_edit": true,
123+
"can_edit_members": true,
124+
"can_create_org": true,
125+
"can_delete_org": true
126+
},
127+
"subjects": [
128+
{"subject": {"type": "user", "name": "ian"}, "relation": "admin", "name": "Ian"},
129+
{"subject": {"type": "user", "name": "drew"}, "relation": "member", "name": "Drew"}
130+
],
131+
"children": [
132+
{"type": "org", "id": 2, "name": "BA-group"}
133+
]
134+
}
135+
```
136+
137+
```jsonc
138+
// Before (GET /groups/2)
139+
{
140+
"group": {"id": 2, "name": "BA-group"},
141+
"tenant": {"id": 1, "name": "tl-tenant"},
142+
"feeds": [{"id": 5, "onestop_id": "BA", "name": "BART"}],
143+
"actions": {"can_view": true, ...},
144+
"users": {
145+
"managers": [...],
146+
"editors": [...],
147+
"viewers": [...]
148+
}
149+
}
150+
151+
// After (GET /groups/2)
152+
{
153+
"ref": {"type": "org", "id": 2, "name": "BA-group"},
154+
"actions": {"can_view": true, ...},
155+
"subjects": [
156+
{"subject": {"type": "user", "name": "ian"}, "relation": "viewer", "name": "Ian"}
157+
],
158+
"parent": {"type": "tenant", "id": 1, "name": "tl-tenant"},
159+
"children": [
160+
{"type": "feed", "id": 5, "name": "BART"}
161+
]
162+
}
163+
```
164+
165+
**Changes:**
166+
167+
| Before | After |
168+
|--------|-------|
169+
| Entity at top level (`tenant`, `group`, `feed`, `feed_version`) | `ref` with `{type, id, name}` |
170+
| Parent as type-specific field (`tenant` on groups, `group` on feeds) | `parent` with `{type, id, name}` |
171+
| Children as type-specific field (`groups` on tenants, `feeds` on groups) | `children` array of `{type, id, name}` |
172+
| Users grouped by role (`admins`, `members`, `managers`, `editors`, `viewers`) | `subjects` flat array with `.relation` field |
173+
| Actions include `false` values via `omitempty` (absent = false) | Actions only include `true` values (absent = false) |
174+
| Feed-specific fields in children (`onestop_id`) | Only `name` available on children |
175+
176+
### Key differences summary
177+
178+
1. **Generic shape** — all entity types return the same JSON structure
179+
2. **`subjects` replaces role-grouped users** — filter by `.relation` client-side instead of accessing `.users.admins`, `.users.editors`, etc.
180+
3. **`parent`/`children` replace type-specific fields**`parent` is always a single `ObjectRef`, `children` is always an array
181+
4. **Actions only include granted permissions** — denied actions are absent from the map, not present as `false`
182+
5. **List endpoints return bare `ObjectRef` arrays** — no entity details; use the detail endpoint for names

0 commit comments

Comments
 (0)