diff --git a/sdk/python/feast/cli/projects.py b/sdk/python/feast/cli/projects.py index ea8d5b573a6..c552a058635 100644 --- a/sdk/python/feast/cli/projects.py +++ b/sdk/python/feast/cli/projects.py @@ -3,7 +3,7 @@ from feast import utils from feast.cli.cli_options import tagsOption -from feast.errors import FeastObjectNotFoundException +from feast.errors import FeastObjectNotFoundException, ProjectNotFoundException from feast.repo_operations import create_feature_store @@ -23,16 +23,16 @@ def project_describe(ctx: click.Context, name: str): Describe a project """ store = create_feature_store(ctx) - try: project = store.get_project(name) except FeastObjectNotFoundException as e: print(e) exit(1) - print( yaml.dump( - yaml.safe_load(str(project)), default_flow_style=False, sort_keys=False + yaml.safe_load(str(project)), + default_flow_style=False, + sort_keys=False, ) ) @@ -44,20 +44,52 @@ def project_current(ctx: click.Context): Returns the current project configured with FeatureStore object """ store = create_feature_store(ctx) - try: project = store.get_project(name=None) except FeastObjectNotFoundException as e: print(e) exit(1) - print( yaml.dump( - yaml.safe_load(str(project)), default_flow_style=False, sort_keys=False + yaml.safe_load(str(project)), + default_flow_style=False, + sort_keys=False, ) ) +@projects_cmd.command("delete") +@click.argument("name", type=click.STRING) +@click.option( + "-y", + "--yes", + is_flag=True, + default=False, + help="Skip confirmation prompt and delete immediately.", +) +@click.pass_context +def project_delete(ctx: click.Context, name: str, yes: bool): + """ + Delete a project and all its resources from the registry. + """ + store = create_feature_store(ctx) + + if not yes: + click.confirm( + f"Are you sure you want to delete project '{name}'? " + "This will remove all associated resources from the registry.", + abort=True, + ) + + try: + store.delete_project(name) + except (FeastObjectNotFoundException, ProjectNotFoundException) as e: + print(str(e)) + raise SystemExit(1) + + print(f"Project '{name}' deleted successfully.") + + @projects_cmd.command(name="list") @tagsOption @click.pass_context @@ -70,11 +102,12 @@ def project_list(ctx: click.Context, tags: list[str]): tags_filter = utils.tags_list_to_dict(tags) for project in store.list_projects(tags=tags_filter): table.append([project.name, project.description, project.tags, project.owner]) - from tabulate import tabulate print( tabulate( - table, headers=["NAME", "DESCRIPTION", "TAGS", "OWNER"], tablefmt="plain" + table, + headers=["NAME", "DESCRIPTION", "TAGS", "OWNER"], + tablefmt="plain", ) ) diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py index f95bbf10c03..bfc71db875e 100644 --- a/sdk/python/feast/feature_store.py +++ b/sdk/python/feast/feature_store.py @@ -3401,6 +3401,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: + ProjectNotFoundException: 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]: