feat: add admin apis (CM-1235)#4211
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new set of Auth0-protected public v1 API endpoints plus supporting DAL methods to manage OSSPCKGS “stewardship” lifecycle actions (open, assign steward, escalate, update status) while writing audit entries to stewardship_activity.
Changes:
- Added DAL helpers for stewardship CRUD-ish workflows and exported shared constants for request validation.
- Added four new
/api/public/v1/stewardships/*handlers with Zod validation and wired them into the v1 router. - Added routing for the new stewardship endpoints under the existing Auth0
oauth2Middleware.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| services/libs/data-access-layer/src/osspckgs/stewardships.ts | Adds DAL APIs for opening, assigning, escalating, and status updates + shared enum constants. |
| backend/src/api/public/v1/stewardships/openStewardship.ts | New POST handler to open a package for stewardship via normalized PURL. |
| backend/src/api/public/v1/stewardships/assignSteward.ts | New PUT handler to soft-delete + insert a steward assignment. |
| backend/src/api/public/v1/stewardships/escalate.ts | New PUT handler to escalate a stewardship with a resolution path. |
| backend/src/api/public/v1/stewardships/updateStatus.ts | New PUT handler to update stewardship status with conditional inactiveReason validation. |
| backend/src/api/public/v1/stewardships/schemas.ts | Adds shared param schema for numeric stewardship id. |
| backend/src/api/public/v1/stewardships/index.ts | Adds stewardships router + rate limiting (scope checks currently commented out). |
| backend/src/api/public/v1/index.ts | Mounts the new /stewardships router under v1 with Auth0 middleware. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
8af239c to
3f87f89
Compare
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
e1b707e to
03abe99
Compare
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 01f5fee. Configure here.
| WHERE id = $(stewardshipId) | ||
| AND status IN ('unassigned', 'open') | ||
| RETURNING id, package_id, status, origin, version, opened_at, | ||
| last_status_at, inactive_reason, resolution_path, status_note, created_at, updated_at |
There was a problem hiding this comment.
Invalid stewardship columns in SQL
High Severity
When moveToAssessing is true, assignSteward runs an UPDATE that sets and returns resolution_path and status_note on stewardships, but those columns are not defined in the stewardship migration schema, so PostgreSQL rejects the query and the assign endpoint fails for that flow.
Reviewed by Cursor Bugbot for commit 01f5fee. Configure here.
| `, | ||
| { stewardshipId, actorUserId: data.assignedBy, metadata: JSON.stringify({ status: 'assessing' }) }, | ||
| ) | ||
| if (updated) finalStewardship = mapStewardshipRow(updated) |
There was a problem hiding this comment.
moveToAssessing succeeds without status change
Medium Severity
With moveToAssessing true, the status UPDATE only applies when status is unassigned or open. If the stewardship is already past that (e.g. active), the handler still returns 200 with stewards updated but the prior status unchanged, so clients believe an atomic move to assessing occurred when it did not.
Reviewed by Cursor Bugbot for commit 01f5fee. Configure here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
backend/src/api/public/v1/packages/openapi.yaml:443
- The field-level description for
stewardsstill says "Null in v1", butGET /packages/detailcan now return an array (possibly empty) when a stewardship exists.
stewards:
description: Assigned stewards or null. Null in v1.
oneOf:
- type: array
items:
$ref: '#/components/schemas/Steward'
- type: 'null'
| SET status = 'assessing', | ||
| last_status_at = NOW(), | ||
| resolution_path = NULL, | ||
| status_note = NULL, | ||
| updated_at = NOW() | ||
| WHERE id = $(stewardshipId) | ||
| AND status IN ('unassigned', 'open') | ||
| RETURNING id, package_id, status, origin, version, opened_at, | ||
| last_status_at, inactive_reason, resolution_path, status_note, created_at, updated_at | ||
| ), |
| SET status = $(status), | ||
| last_status_at = NOW(), | ||
| inactive_reason = CASE WHEN $(status) = 'inactive' THEN $(inactiveReason) ELSE NULL END, | ||
| updated_at = NOW() |
| import { Router } from 'express' | ||
|
|
||
| import { createRateLimiter } from '@/api/apiRateLimiter' | ||
| // TODO: restore once write:stewardships is added to Auth0 staging tenant | ||
| // import { requireScopes } from '@/api/public/middlewares/requireScopes' | ||
| import { safeWrap } from '@/middlewares/errorMiddleware' | ||
|
|
||
| // import { SCOPES } from '@/security/scopes' | ||
| import { assignStewardHandler } from './assignSteward' | ||
| import { escalateHandler } from './escalate' | ||
| import { openStewardship } from './openStewardship' | ||
| import { updateStatusHandler } from './updateStatus' | ||
|
|
||
| const rateLimiter = createRateLimiter({ max: 60, windowMs: 60 * 1000 }) | ||
|
|
||
| export function stewardshipsRouter(): Router { | ||
| const router = Router() | ||
|
|
||
| router.use(rateLimiter) |
| inactiveReason: | ||
| type: | ||
| - string | ||
| - 'null' | ||
| enum: | ||
| - quarterly_cadence_missed | ||
| - stepped_down | ||
| - no_longer_critical | ||
| - 'null' |
| stewardship: | ||
| type: object | ||
| description: Stewardship state. In v1 always unassigned with no stewards or activity. | ||
| properties: |


Summary
Expose four admin-facing endpoints to support stewardship lifecycle management in the OSSPREY Program dashboard. Admins can now open a package for stewardship, assign individual stewards, escalate with a structured resolution path, and update stewardship status — all with audit logging to
stewardship_activity.Changes
POST /api/public/v1/stewardships— opens a package for stewardship (upserts status →open); body takes a PURL, normalised before DB lookupPUT /api/public/v1/stewardships/:id/steward— assigns a steward (leadorco_steward) via soft-delete + insert pattern; entire operation is wrapped in a transaction to keep the audit log consistentPUT /api/public/v1/stewardships/:id/escalate— sets status →escalatedand records one of 6 resolution paths instewardship_activitymetadataPUT /api/public/v1/stewardships/:id/status— updates stewardship status (assessing,active,needs_attention,blocked,inactive);inactiveReasonis required when settinginactiveand is preserved on other transitions (not overwritten to null)stewardships.ts) — addedgetStewardshipById,openStewardshipByPurl,assignSteward,escalateStewardship,updateStewardshipStatus; exportedESCALATION_RESOLUTION_PATHS,INACTIVE_REASONS, andSTEWARDSHIP_UPDATABLE_STATUSESas shared constantsTODOfollowing the same pattern as existing packages endpoints (restore oncewrite:stewardshipsis provisioned on the Auth0 staging tenant)Type of change
JIRA ticket
ticket
Note
High Risk
New write endpoints mutate stewardship state and assignments with scope enforcement disabled until Auth0 provisions
write:stewardships, widening who can change production-like data if tokens leak.Overview
Adds OAuth-protected write APIs under
/api/v1/stewardshipsfor OSSPREY admin workflows: open by PURL, assign stewards (optionalmoveToAssessing), escalate with a resolution path, and update status (with requiredinactiveReasonwhen inactive). Handlers delegate to new DAL functions that upsert/updatestewardships, managestewardship_stewards, and appendstewardship_activityaudit rows.Read-side changes expose
stewardshipIdon package list/detail and populate package detail stewardship with real stewards andlastActivityAtviagetStewardshipSummary. Package OpenAPI documents the new IDs; a separate stewardships OpenAPI describes the mutations.write:stewardshipsscope checks are commented out (same pattern as existing package stewardship reads) until Auth0 staging is provisioned.Reviewed by Cursor Bugbot for commit 01f5fee. Bugbot is set up for automated code reviews on this repo. Configure here.