ref(api): promote org-scoped project creation endpoint to public#116333
Conversation
|
🚨 Warning: This pull request contains Frontend and Backend changes! It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently. Have questions? Please ask in the |
📊 Type Coverage Diff✅ No new type safety issues introduced. Coverage: 93.58% |
6cd9e2a to
334ae5d
Compare
420a530 to
2897b36
Compare
6b8576d to
ecbe60f
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ecbe60f. Configure here.
ecbe60f to
dc65d45
Compare
POST /api/0/organizations/{org}/experimental/projects/ has been stable
since Jan 2024 and is what the onboarding UI uses to let org members
(who lack project:write) create a project. The /experimental/ path
segment was a one-off with no codebase convention behind it, and
EXPERIMENTAL status excluded it from the OpenAPI spec and @sentry/api.
Move the POST handler into OrganizationProjectsEndpoint at
POST /organizations/{org}/projects/ (the slot was free — only GET
existed there), promote to PUBLIC, add @extend_schema, and delete the
experimental file. The team-scoped POST /teams/{org}/{team}/projects/
is unchanged — it remains the right call when a team is already known.
Changes:
- Add OrganizationProjectsPermission (StaffPermissionMixin +
OrganizationPermission, POST scope lowered to project:read to
preserve member access)
- Add post() to OrganizationProjectsEndpoint with full OpenAPI docs
- Move constants and helpers from organization_projects_experiment.py;
import AuditData from team_projects.py instead of duplicating it
- Remove experimental URL from urls.py
- Remove experimental entry from api_publish_status_allowlist
- Remove /projects/ from api_ownership_allowlist (now owned by FOUNDATIONS)
- Update CODEOWNERS and coverage baseline to new file paths
- Rename test file and update all imports/patch paths
dc65d45 to
d421458
Compare
…projects/ (#116388) <!-- Describe your PR here. --> Backend PR #116333 promoted `POST /organizations/{org}/experimental/projects/` to `POST /organizations/{org}/projects/` (PUBLIC). That PR keeps the old `/experimental/` path alive as a backward-compat alias so the frontend doesn't break between deploys. This PR drops the `/experimental/` path from the two hooks that call it and updates the test mocks and generated URL registry to match. **Changed hooks:** - `useCreateProject` — used by the main onboarding flow and the create-project settings page - `useCreateProjectFromWizard` — used by the setup wizard **Also adds** a unit test for `useCreateProject` that explicitly asserts the URL routing logic (no team slug → org endpoint, team slug present → teams endpoint). That coverage didn't exist before. Depends on #116333. ### Legal Boilerplate Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.

POST /organizations/{org}/experimental/projects/has been stable since Jan 2024 — it is what the onboarding UI calls when a member (noproject:write) creates a project. Two problems: the/experimental/path is a one-off with no codebase pattern, andEXPERIMENTALstatus excludes it from the OpenAPI spec, so@sentry/apiand the CLI have never been able to use it.POST /organizations/{org}/projects/was an empty slot (only GET existed). This fills it.Before
After
The behavior is identical — auto-creates
team-{username}for the caller, requires onlyproject:read. The team-scoped endpoint is untouched.The
/experimental/alias is kept alive as a backward-compat shim (OrganizationProjectsExperimentalCompatEndpoint) so the frontend doesn't break between this deploy and the frontend PR landing. It's POST-only (http_method_names = ["post", "options"]), markedEXPERIMENTALso it stays out of the spec, and carries a TODO to remove it once the frontend PR merges.Also fixes a pre-existing data consistency bug in the moved code:
project.add_team(team)was called outside thetransaction.atomic()block, meaning a failure there would leave a team and project in the DB without being linked. Moved it inside the transaction.Housekeeping:
organization_projects_experiment.pydeleted, constants/helpers folded intoorganization_projects.py, both apidocs allowlists updated, CODEOWNERS updated, test file renamedtest_organization_projects_create.py+ new test for therole=memberhappy path.Frontend follow-up (separate PR): drop
/experimental/fromuseCreateProject.tsanduseCreateProjectFromWizard.tsx, then a cleanup PR removes the alias.Legal Boilerplate
Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.