diff --git a/deepset_cloud_sdk/_service/pipeline_service.py b/deepset_cloud_sdk/_service/pipeline_service.py index 42d925d0..1e3469d5 100644 --- a/deepset_cloud_sdk/_service/pipeline_service.py +++ b/deepset_cloud_sdk/_service/pipeline_service.py @@ -398,11 +398,13 @@ async def _overwrite_pipeline(self, name: str, pipeline_yaml: str) -> Response: return await self._create_pipeline(name=name, pipeline_yaml=pipeline_yaml) version_body = version_response.json() - latest_version = version_body["data"][0] - version_id = latest_version["version_id"] - is_draft = latest_version.get("is_draft", False) + versions = version_body["data"] - if is_draft: + # The pipeline exists but may have no saved versions yet (e.g. created but never + # versioned) -- there's then no draft to patch, same as when the latest version + # simply isn't a draft. + if versions and versions[0].get("is_draft", False): + version_id = versions[0]["version_id"] # Patch existing draft version logger.debug(f"Patching existing draft version '{version_id}' of pipeline '{name}'.") return await self._api.patch( @@ -412,7 +414,7 @@ async def _overwrite_pipeline(self, name: str, pipeline_yaml: str) -> Response: ) # Create a new version - logger.debug(f"Latest version '{version_id}' of pipeline '{name}' is not a draft, creating new version.") + logger.debug(f"Pipeline '{name}' has no draft version, creating new version.") return await self._api.post( workspace_name=self._workspace_name, endpoint=f"pipelines/{name}/versions", diff --git a/tests/unit/service/test_pipeline_service.py b/tests/unit/service/test_pipeline_service.py index 5df9a1df..59bd297f 100644 --- a/tests/unit/service/test_pipeline_service.py +++ b/tests/unit/service/test_pipeline_service.py @@ -680,6 +680,52 @@ async def test_import_pipeline_with_overwrite_true_creates_new_version_when_not_ assert create_version_call.kwargs["endpoint"] == "pipelines/test_pipeline_overwrite/versions" assert "config_yaml" in create_version_call.kwargs["json"] + @pytest.mark.asyncio + async def test_import_pipeline_with_overwrite_true_creates_new_version_when_no_versions_exist( + self, pipeline_service: PipelineService, index_pipeline: Pipeline, mock_api: AsyncMock + ) -> None: + """Test importing a pipeline with overwrite=True creates a new version when the pipeline has no versions. + + A pipeline can exist (GET /versions returns 200) with an empty `data` list -- e.g. it was + created but never versioned. This must not raise an IndexError; it should create a new + version the same way it would if the latest version simply wasn't a draft. + """ + config = PipelineConfig( + name="test_pipeline_overwrite", + inputs=PipelineInputs(query=["retriever.query"]), + outputs=PipelineOutputs(documents="meta_ranker.documents"), + strict_validation=False, + overwrite=True, + ) + + # Mock successful validation response + validation_response = Mock(spec=Response) + validation_response.status_code = HTTPStatus.NO_CONTENT.value + + # Mock versions response: pipeline exists, but has zero saved versions + versions_response = Mock(status_code=HTTPStatus.OK.value) + versions_response.json.return_value = {"data": []} + + # Mock successful "create new version" response + new_version_response = Mock(spec=Response) + new_version_response.status_code = HTTPStatus.CREATED.value + + # First POST is validation, second POST is "create new version" + mock_api.post.side_effect = [validation_response, new_version_response] + mock_api.get.return_value = versions_response + + await pipeline_service.import_async(index_pipeline, config) + + # validation + GET versions + POST versions (new version) + assert mock_api.post.call_count == 2 + assert mock_api.get.call_count == 1 + assert mock_api.patch.call_count == 0 + + # Check create-version POST call + create_version_call = mock_api.post.call_args_list[1] + assert create_version_call.kwargs["endpoint"] == "pipelines/test_pipeline_overwrite/versions" + assert "config_yaml" in create_version_call.kwargs["json"] + @pytest.mark.asyncio async def test_import_pipeline_with_overwrite_fallback_to_create( self, pipeline_service: PipelineService, index_pipeline: Pipeline, mock_api: AsyncMock