Skip to content

feat(pipeline): Add OrganizationPipelineEndpoint for API-driven pipelines#111539

Merged
evanpurkhiser merged 2 commits into
masterfrom
evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines
Mar 27, 2026
Merged

feat(pipeline): Add OrganizationPipelineEndpoint for API-driven pipelines#111539
evanpurkhiser merged 2 commits into
masterfrom
evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines

Conversation

@evanpurkhiser

@evanpurkhiser evanpurkhiser commented Mar 25, 2026

Copy link
Copy Markdown
Member

Add a new REST endpoint that allows driving integration pipelines via
JSON API requests instead of the legacy redirect-based flow. The
endpoint supports initializing a pipeline (POST with action=initialize),
retrieving the current step info (GET), and advancing through steps
(POST with step-specific data).

Uses ControlSiloOrganizationEndpoint as the base class since integration
models live in the control silo, requiring the RPC service layer to
resolve organizations from slugs.

The endpoint rejects pipelines that don't support API mode with a 400,
and returns structured errors for invalid providers, missing sessions,
and unsupported pipeline names.

Refs VDY-36

Requires #111454

@evanpurkhiser evanpurkhiser requested review from a team as code owners March 25, 2026 17:42
@linear-code

linear-code Bot commented Mar 25, 2026

Copy link
Copy Markdown

@evanpurkhiser evanpurkhiser requested review from a team and removed request for a team March 25, 2026 17:42
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Mar 25, 2026
Comment thread src/sentry/api/endpoints/organization_pipeline.py Outdated
except NotRegistered:
return Response({"detail": f"Unknown provider: {provider_id}"}, status=404)
except IntegrationPipelineError as e:
return Response({"detail": str(e)}, status=404 if e.not_found else 400)

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 3 months ago

In general, to fix this problem we should avoid returning raw exception messages to the client. Instead, we should: (1) log the exception (with stack trace) on the server for debugging; and (2) send a generic, non-sensitive error message in the HTTP response. If we still need to differentiate between “not found” and “bad request”, we can keep using e.not_found for the status code while hiding the detailed message.

In this specific file, the minimal and safest change is to modify the except IntegrationPipelineError as e: block in _initialize_pipeline. We should import logger is already defined (logging.getLogger(__name__)), so we can use that to log the error. Then change the response JSON so that it no longer includes str(e); instead, use a generic message such as "Failed to initialize integration pipeline." while preserving the existing status code decision. No other logic needs to change, and no new external dependencies are required.

Concretely:

  • In src/sentry/api/endpoints/organization_pipeline.py, at the except IntegrationPipelineError as e: clause (around lines 116–117), insert a call like logger.exception("Failed to initialize integration pipeline for provider %s", provider_id) to log the full exception and stack trace.
  • Replace {"detail": str(e)} with a fixed string, e.g. {"detail": "Failed to initialize integration pipeline."}.
  • Keep using status=404 if e.not_found else 400 to preserve the existing behavior regarding HTTP status codes.
Suggested changeset 1
src/sentry/api/endpoints/organization_pipeline.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/sentry/api/endpoints/organization_pipeline.py b/src/sentry/api/endpoints/organization_pipeline.py
--- a/src/sentry/api/endpoints/organization_pipeline.py
+++ b/src/sentry/api/endpoints/organization_pipeline.py
@@ -114,7 +114,13 @@
         except NotRegistered:
             return Response({"detail": f"Unknown provider: {provider_id}"}, status=404)
         except IntegrationPipelineError as e:
-            return Response({"detail": str(e)}, status=404 if e.not_found else 400)
+            logger.exception(
+                "Failed to initialize integration pipeline for provider %s", provider_id
+            )
+            return Response(
+                {"detail": "Failed to initialize integration pipeline."},
+                status=404 if e.not_found else 400,
+            )
 
         if not pipeline.is_api_ready():
             return Response({"detail": "Pipeline does not support API mode."}, status=400)
EOF
@@ -114,7 +114,13 @@
except NotRegistered:
return Response({"detail": f"Unknown provider: {provider_id}"}, status=404)
except IntegrationPipelineError as e:
return Response({"detail": str(e)}, status=404 if e.not_found else 400)
logger.exception(
"Failed to initialize integration pipeline for provider %s", provider_id
)
return Response(
{"detail": "Failed to initialize integration pipeline."},
status=404 if e.not_found else 400,
)

if not pipeline.is_api_ready():
return Response({"detail": "Pipeline does not support API mode."}, status=400)
Copilot is powered by AI and may make mistakes. Always verify output.
Comment thread src/sentry/api/endpoints/organization_pipeline.py
@evanpurkhiser evanpurkhiser force-pushed the evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines branch from 7d65b9f to 0493c31 Compare March 25, 2026 22:15
Comment thread src/sentry/api/endpoints/organization_pipeline.py Outdated
Comment thread tests/sentry/api/endpoints/test_organization_pipeline.py Outdated
Comment thread src/sentry/api/endpoints/organization_pipeline.py
Comment thread src/sentry/api/endpoints/organization_pipeline.py
Comment thread src/sentry/api/endpoints/organization_pipeline.py Outdated
Comment thread src/sentry/api/endpoints/organization_pipeline.py Outdated
except StopIteration:
return Response({"detail": "Invalid pipeline type"}, status=404)

pipeline = pipeline_cls.get_for_request(request._request)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand right, we have only one state for integration pipelines and one state for auth pipelines. So if a user stats an integration pipeline (say github), then part way through abandons it and starts another (say gitlab), is there any way that cause this to have problems?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would be a problem even today actually.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would only happen if they did the slack setup pipeline, then later the identity slack pipeline though

Comment thread src/sentry/api/endpoints/organization_pipeline.py
Comment thread src/sentry/api/endpoints/organization_pipeline.py
Comment thread src/sentry/api/endpoints/organization_pipeline.py
return [DummyStep()]


class NonApiPipeline(Pipeline[Never, PipelineSessionStore]):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't totally understand what Never represents here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first parameter is the Model associated to the pipeline. This one doesn't have a model so it's Never.

Comment thread tests/sentry/api/endpoints/test_organization_pipeline.py Outdated
@evanpurkhiser evanpurkhiser force-pushed the evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines branch from 0493c31 to ab46a65 Compare March 26, 2026 21:01
@getsantry getsantry Bot requested a review from a team as a code owner March 26, 2026 21:04
@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Mar 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚨 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 #discuss-dev-infra channel.

Comment thread src/sentry/api/endpoints/organization_pipeline.py
@evanpurkhiser evanpurkhiser force-pushed the evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines branch from e61ed52 to b13b88d Compare March 26, 2026 21:31
@evanpurkhiser evanpurkhiser requested a review from a team as a code owner March 26, 2026 21:31

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 4 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment thread src/sentry/api/endpoints/organization_pipeline.py
Comment thread src/sentry/pipeline/base.py
@evanpurkhiser evanpurkhiser force-pushed the evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines branch from 4b7b022 to 7be2894 Compare March 26, 2026 21:44
@evanpurkhiser evanpurkhiser force-pushed the evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines branch from 8cbf377 to 7be2894 Compare March 26, 2026 22:28
@evanpurkhiser evanpurkhiser force-pushed the evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines branch from 6595ded to 5e884b8 Compare March 26, 2026 22:44
Comment thread src/sentry/api/endpoints/organization_pipeline.py
…ines

Add a new REST endpoint that allows driving integration pipelines via
JSON API requests instead of the legacy redirect-based flow. The
endpoint supports initializing a pipeline (POST with action=initialize),
retrieving the current step info (GET), and advancing through steps
(POST with step-specific data).

Uses ControlSiloOrganizationEndpoint as the base class since integration
models live in the control silo, requiring the RPC service layer to
resolve organizations from slugs.

The endpoint rejects pipelines that don't support API mode with a 400,
and returns structured errors for invalid providers, missing sessions,
and unsupported pipeline names.

Refs VDY-36
@evanpurkhiser evanpurkhiser force-pushed the evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines branch from 8415c30 to 95f784e Compare March 27, 2026 15:50
Comment thread src/sentry/api/endpoints/organization_pipeline.py
@evanpurkhiser evanpurkhiser merged commit 1e1d235 into master Mar 27, 2026
66 of 69 checks passed
@evanpurkhiser evanpurkhiser deleted the evanpurkhiser/feat-pipeline-add-organizationpipelineendpoint-for-api-driven-pipelines branch March 27, 2026 17:35
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 12, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants