diff --git a/sdk/python/feast/cli/projects.py b/sdk/python/feast/cli/projects.py index ea8d5b573a6..e6a38071e43 100644 --- a/sdk/python/feast/cli/projects.py +++ b/sdk/python/feast/cli/projects.py @@ -58,6 +58,38 @@ def project_current(ctx: click.Context): ) +@projects_cmd.command("delete") +@click.argument("name", type=click.STRING) +@click.option( + "--yes", + "-y", + is_flag=True, + default=False, + help="Skip confirmation prompt.", +) +@click.pass_context +def project_delete(ctx: click.Context, name: str, yes: bool): + """ + Delete a project and all its registered objects. + """ + store = create_feature_store(ctx) + + try: + store.get_project(name) + except FeastObjectNotFoundException as e: + print(e) + exit(1) + + if not yes: + click.confirm( + f"This will permanently delete project '{name}' and all its registered objects. Are you sure?", + abort=True, + ) + + store.delete_project(name) + print(f"Project '{name}' successfully deleted.") + + @projects_cmd.command(name="list") @tagsOption @click.pass_context diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py index 42bcd2feca9..6d1be5fce71 100644 --- a/sdk/python/feast/feature_store.py +++ b/sdk/python/feast/feature_store.py @@ -3388,6 +3388,19 @@ def get_project(self, name: Optional[str]) -> Project: """ return self.registry.get_project(name or self.project) + def delete_project(self, name: str, commit: bool = True) -> None: + """ + Deletes a project from the registry. + + Args: + name: Name of the project to delete. + commit: Whether the change should be persisted immediately. + + Raises: + ProjectObjectNotFoundException: The project could not be found. + """ + return self.registry.delete_project(name, commit=commit) + def list_saved_datasets( self, allow_cache: bool = False, tags: Optional[dict[str, str]] = None ) -> List[SavedDataset]: