Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/added-20260210-123451.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: added
body: Update import command interface to support ci/cd flow
time: 2026-02-10T12:34:51.783593963Z
custom:
Author: aviatco
AuthorLink: https://github.com/aviatco
4 changes: 3 additions & 1 deletion src/fabric_cli/commands/fs/fab_fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ def get_command(args: Namespace) -> None:
@set_command_context()
def import_command(args: Namespace) -> None:
context = handle_context.get_command_context(args.path, raise_error=False)
context.check_command_support(Command.FS_IMPORT)
if hasattr(args, 'path') and args.path:
# In CICD flow - no path elements involved, skip command support check
Comment thread
aviatco marked this conversation as resolved.
context.check_command_support(Command.FS_IMPORT)
fs_import.exec_command(args, context)


Expand Down
17 changes: 13 additions & 4 deletions src/fabric_cli/commands/fs/fab_fs_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@

from argparse import Namespace

from fabric_cli.commands.fs.impor import fab_fs_import_item as import_item
from fabric_cli.commands.fs.impor import fab_fs_import_direct_api as import_direct_api
from fabric_cli.commands.fs.impor import fab_fs_import_cicd as import_cicd
from fabric_cli.core.hiearchy.fab_hiearchy import FabricElement, Item
from fabric_cli.utils import fab_util as utils


def exec_command(args: Namespace, context: FabricElement) -> None:
args.input = utils.process_nargs(args.input)
if hasattr(args, 'validate_args'):
args.validate_args(args)

if isinstance(context, Item):
import_item.import_single_item(context, args)
# Direct API flow
if args.path and args.input:
args.input = utils.process_nargs(args.input)
if isinstance(context, Item):
import_direct_api.import_single_item(context, args)

# CI/CD flow
elif args.config_file:
import_cicd.import_with_config_file(args)
Comment thread
aviatco marked this conversation as resolved.
4 changes: 4 additions & 0 deletions src/fabric_cli/commands/fs/impor/fab_fs_import_cicd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from argparse import Namespace

def import_with_config_file(args: Namespace) -> None:
"""Import using config file and environment parameters - delegates to CICD library."""
Comment thread
aviatco marked this conversation as resolved.
1 change: 1 addition & 0 deletions src/fabric_cli/core/fab_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@
ERROR_UNIVERSAL_SECURITY_DISABLED = "UniversalSecurityDisabled"
ERROR_SPN_AUTH_MISSING = "ServicePrincipalAuthMissing"
ERROR_JOB_FAILED = "JobFailed"
ERROR_INVALID_IMPORT_ARGUMENTS = "InvalidImportArguments"

# Exit codes
EXIT_CODE_SUCCESS = 0
Expand Down
6 changes: 4 additions & 2 deletions src/fabric_cli/core/hiearchy/fab_base_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ def check_command_support(self, commmand: Command) -> bool:
is_supported = self.item_type in cmd.get_supported_items(commmand)
if is_unsupported:
raise FabricCLIError(
ErrorMessages.Hierarchy.command_not_supported(commmand.value),
ErrorMessages.Hierarchy.command_not_supported(
commmand.value, str(self.item_type)),
fab_constant.ERROR_UNSUPPORTED_COMMAND,
)
if is_supported:
Expand All @@ -109,7 +110,8 @@ def check_command_support(self, commmand: Command) -> bool:
super().check_command_support(commmand)
except FabricCLIError:
raise FabricCLIError(
ErrorMessages.Hierarchy.command_not_supported(commmand.value),
ErrorMessages.Hierarchy.command_not_supported(
commmand.value, str(self.item_type)),
fab_constant.ERROR_UNSUPPORTED_COMMAND,
)
return True
3 changes: 2 additions & 1 deletion src/fabric_cli/core/hiearchy/fab_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ def check_command_support(self, commmand: Command) -> bool:
is_supported = self.type in cmd.get_supported_elements(commmand)
if not is_supported:
raise FabricCLIError(
ErrorMessages.Hierarchy.command_not_supported(commmand.value),
ErrorMessages.Hierarchy.command_not_supported(
commmand.value, self.type.value),
fab_constant.ERROR_UNSUPPORTED_COMMAND,
)
return True
Expand Down
12 changes: 12 additions & 0 deletions src/fabric_cli/errors/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,18 @@ def gateway_property_not_supported_for_type(
property_name: str, gateway_type: str
) -> str:
return f"Setting '{property_name}' is not supported for Gateway type '{gateway_type}'"

@staticmethod
def import_required_param_missing(param_display_name: str, flow_name: str, flow_syntax: str) -> str:
return f"{param_display_name} is required for {flow_name} flow.\nPlease use: {flow_syntax}"

@staticmethod
def import_mixed_flows_error() -> str:
return "Argument mismatch: you cannot mix path/input-based arguments with config-file, env, or parameters options."

@staticmethod
def import_no_flow_specified_error() -> str:
return "You must choose one import method: (1) a path to destination with -i/--input, or (2) --config-file with --env. These options cannot be used together"

@staticmethod
def query_not_supported_for_set(query: str) -> str:
Expand Down
4 changes: 2 additions & 2 deletions src/fabric_cli/errors/hierarchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ def invalid_type(name: str) -> str:
return f"Invalid type '{name}'"

@staticmethod
def command_not_supported(command: str) -> str:
return f"not supported for command '{command}'"
def command_not_supported(command: str, item_type: str) -> str:
return f"Command '{command}' is not supported for item type '{item_type}'"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

not specifically for items, right? if that's the case, let's rephrase the text to when item_type is not None

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Here I just enhance the error message, so we know which item_type is not supported. it is specific to item type


@staticmethod
def item_type_not_valid(item_type: str) -> str:
Expand Down
55 changes: 47 additions & 8 deletions src/fabric_cli/parsers/fab_fs_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,10 +390,14 @@ def register_get_parser(subparsers: _SubParsersAction) -> None:
# Command for 'import'
def register_import_parser(subparsers: _SubParsersAction) -> None:
import_examples = [
"# import a notebook from a local directory",
"# import a notebook from a local directory (Direct API call flow)",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

from user perspective, i am not aware of such flow.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

so what do you suggests?
@HasanAboShally

"$ import imp.Notebook -i /tmp/nb1.Notebook\n",
"# import a pipeline from a local directory",
"$ import pip.Notebook -i /tmp/pip1.DataPipeline -f",
"# import a pipeline from a local directory (Direct API call flow)",
"$ import pip.Notebook -i /tmp/pip1.DataPipeline -f\n",
"# import using config file and environment (CI/CD flow - no path needed)",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

same.. this is transparent to users

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

same here @HasanAboShally

"$ import --config-file config.yml --env dev\n",
"# import with config file, environment, and CICD parameters (CI/CD flow)",
"$ import --config-file config.yml --env prod -P '[{\"param1\":\"value1\"}]' -f",
]

import_parser = subparsers.add_parser(
Expand All @@ -402,27 +406,62 @@ def register_import_parser(subparsers: _SubParsersAction) -> None:
fab_examples=import_examples,
fab_learnmore=["_"],
)

# Create mutually exclusive groups for the two import flows
# flow_group = import_parser.add_mutually_exclusive_group(required=True)

#CI/CD flow parameters
import_parser.add_argument(
"path", nargs="+", type=str, help="Directory path (item name to import)"
"--config-file",
metavar="PATH",
type=str,
help="Path to local config file. When used, --env is required. Only used in CI/CD flow.",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

local config file is overloaded term in the cli context. we should use some other distinct name

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

cicd local config file?
@HasanAboShally

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

local import config file

)

import_parser.add_argument(
"--env",
metavar="ENV_NAME",
type=str,
help="Environment name as defined in config file. Required when using --config-file. Only used in CI/CD flow.",
Comment thread
aviatco marked this conversation as resolved.
)

import_parser.add_argument(
"-P",
"--params",
metavar="JSON",
type=str,
help="Optional parameters for CICD in JSON format (e.g., '[{\"p1\":\"v1\",\"p2\":\"v2\"}]'). Only used in CI/CD flow.",
)

# Direct API flow parameters
import_parser.add_argument(
"path",
nargs="*", # Changed from "+" to "*" to make it optional
type=str,
help="Directory path (item name to import). When used, -i/--input is required. Only used in Direct API call flow."
)

import_parser.add_argument(
"-i",
"--input",
nargs="+",
required=True,
help="Input path for import",
help="Input path for import. When used, path argument is required. Only used in Direct API call flow.",
)

import_parser.add_argument(
"--format",
metavar="",
help="Input format. Optional, supported for notebooks (.ipynb, .py)",
help="Input format. Optional, supported for notebooks (.ipynb, .py). Only used in Direct API call flow.",
)

import_parser.add_argument(
"-f", "--force", required=False, action="store_true", help="Force. Optional"
)

from fabric_cli.parsers.fab_parser_validators import validate_import_args

import_parser.set_defaults(func=fs.import_command, validate_args=lambda args: validate_import_args(args))
Comment thread
aviatco marked this conversation as resolved.
import_parser.usage = f"{utils_error_parser.get_usage_prog(import_parser)}"
import_parser.set_defaults(func=fs.import_command)


# Command for 'set'
Expand Down
49 changes: 48 additions & 1 deletion src/fabric_cli/parsers/fab_parser_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
# Licensed under the MIT License.

import argparse
from fabric_cli.core import fab_constant
from fabric_cli.core.fab_exceptions import FabricCLIError
from fabric_cli.errors import ErrorMessages


def validate_positive_int(value):
Expand All @@ -12,4 +15,48 @@ def validate_positive_int(value):
raise argparse.ArgumentTypeError(f"'{value}' must be a positive integer")
return ivalue
except ValueError:
raise argparse.ArgumentTypeError(f"'{value}' is not a valid integer")
raise argparse.ArgumentTypeError(f"'{value}' is not a valid integer")


def validate_import_args(args):
"""
Validate import command arguments based on two distinct flows:

Flow 1 (Direct API): path + input (--format optional)
Flow 2 (CICD): config-file + env (-P optional)
"""

def _check_required_param(args, attr_name, param_display_name, flow_name, flow_syntax):
"""Check if required parameter is provided for the specified flow."""
param_value = getattr(args, attr_name, None)
if not param_value:
error_message = ErrorMessages.Common.import_required_param_missing(param_display_name, flow_name, flow_syntax)
raise FabricCLIError(
error_message, fab_constant.ERROR_INVALID_IMPORT_ARGUMENTS)

is_direct_api_flow = args.path or args.input or args.format
is_cicd_flow = args.config_file or args.env or args.params

if is_direct_api_flow and is_cicd_flow:
error_message = ErrorMessages.Common.import_mixed_flows_error()
raise FabricCLIError(
error_message, fab_constant.ERROR_INVALID_IMPORT_ARGUMENTS)

if is_direct_api_flow:
flow_syntax = "fab import <path> -i <input_path> [--format <format>]"
_check_required_param(args, 'path', 'path', 'Direct API', flow_syntax)
_check_required_param(args, 'input', '-i/--input',
'Direct API', flow_syntax)
Comment thread
aviatco marked this conversation as resolved.

elif is_cicd_flow:
flow_syntax = "fab import --config-file <config_path> --env <env_name> [-P <params>]"
_check_required_param(args, 'config_file',
'--config-file', 'CICD', flow_syntax)
_check_required_param(args, 'env', '--env', 'CICD', flow_syntax)

else:
error_message = ErrorMessages.Common.import_no_flow_specified_error()
raise FabricCLIError(
error_message, fab_constant.ERROR_INVALID_IMPORT_ARGUMENTS)

return args
Loading