Skip to content

Commit

Permalink
Default config support (#74)
Browse files Browse the repository at this point in the history
* Refactor logging levels and Info class

Moved LOGGING_LEVELS and Info class definitions from cli module to utils module for better code organization. Also removed an unused import from cli/env.py.

* Add support for setting a default database configuration

Implemented a method to set default configurations for database services if none is provided. Updated CLI to include a command for setting default configurations and adjusted related utilities to utilize the new default setting functionality.

* Add config path option to handle custom config locations

Introduced a `config_path` option to support specifying custom configuration file paths across CLI commands and internal utilities. This enhancement ensures all related functions, tests, and utilities properly account for the new parameter, enabling greater flexibility and ease of use.

* Add utility functions for test configuration paths.

Created `testUtils.py` to centralize common test utilities and updated relevant test files to use these utilities. This refactor improves code maintainability by reducing redundancy across test cases. Additionally, enhanced error handling messages in `DbUtils.py` for better clarity.

* Add utility functions for test configuration paths.

Created `testUtils.py` to centralize common test utilities and updated relevant test files to use these utilities. This refactor improves code maintainability by reducing redundancy across test cases. Additionally, enhanced error handling messages in `DbUtils.py` for better clarity.

* Improve default database configuration handling

Enhance handling of default database configurations by adding informative messages and updating the logic to fetch and set default values. Extend test coverage to include scenarios for setting and querying default database configurations.

* Refactor database utilities and enhance CLI query execution

Refactored `DbUtils` to include a new `execute_query` function for better modularity. Enhanced CLI operations in `db.py` with `yaspin` spinner and warning suppression. Updated tests and configs to reflect the changes for cleaner and more readable code.

* Refactor database utilities and enhance CLI query execution

Refactored `DbUtils` to include a new `execute_query` function for better modularity. Enhanced CLI operations in `db.py` with `yaspin` spinner and warning suppression. Updated tests and configs to reflect the changes for cleaner and more readable code.

* Enhance test_configure with additional imports and config listing

Add necessary imports and include a call to `list_config` in the `test_configure_db` function to improve coverage. This ensures comprehensive testing of configuration settings and utilities.

* Refactor: Remove unnecessary blank line in test file

Eliminated an unneeded blank line in `test_configure.py` to enhance code readability and maintain consistency with the project's style guidelines. This change does not affect

* Update config references and improve command descriptions

Revised documentation and code to standardize the config file path as `~/.hckrcfg`. Enhanced command descriptions and examples for clarity.

* Improve documentation for default database configuration

Clarify instructions on how database configurations are applied when the -c/--config flag is not provided. This helps users understand the implications of the default settings in command executions.

* Refactor imports for readability in DbUtils and test_configure

Reorganized imports in DbUtils.py to improve readability and maintain consistency. Removed an unused import in test_configure.py for cleaner code.

* Refactor import statements in test_configure.py

Consolidate imports for better readability and maintainability. This change ensures that related import statements are grouped together logically.

* Capitalize first word of configure command description

Improve readability of CLI help text by capitalizing the first word in the description of the 'configure' command. This aligns it with the format used for other command descriptions.

* Capitalize first word of configure command description

Improve readability of CLI help text by capitalizing the first word in the description of the 'configure' command. This aligns it with the format used for other command descriptions.

* Update version to 0.5.0

Removed the "dev1" suffix from the version number to mark the transition from a development version to a stable release. This change ensures clarity in versioning for releases.

* Add click module and improve error formatting

Included the click module to manage command-line interface interactions. Enhanced the error message formatting to display errors in red for better readability. Updated the pyproject.toml to ensure consistent package management.

* Fix grammar in test comment

Corrected a minor grammatical error in a comment within the test_configure_set_default_db function. Changed "configure sqlite database" to "configure a sqlite database" to improve readability and clarity.

* Remove redundant code in test_configure.py

The call to list_config() was unnecessary and has been removed to clean up the test code. This streamlines the test and maintains its functionality, ensuring clarity and efficiency.
  • Loading branch information
pateash authored Sep 12, 2024
1 parent 45ec904 commit a498ca7
Show file tree
Hide file tree
Showing 22 changed files with 469 additions and 257 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,6 @@ _build/
out/
*.sqlite
!docs/source/commands/env

# config
.hckrcfg
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
[![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-black.svg)](https://sonarcloud.io/summary/new_code?id=hckr-cli_hckr)
[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=hckr-cli_hckr)](https://sonarcloud.io/summary/new_code?id=hckr-cli_hckr)


[//]: # ([![GitHub commit activity](https://img.shields.io/github/commit-activity/m/hckr-cli/hckr)](https://github.com/hckr-cli/hckr/graphs/commit-activity))

## Introduction
Expand Down
4 changes: 4 additions & 0 deletions dev/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ pre-commit install

## Homebrew formulae
Please find contributing guide for `Homebrew formulae` here [HOMEBREW.md](HOMEBREW.md)


## Senty integration -
[Sentry console](https://hckr-cli.sentry.io/projects/hckr/?project=4507910060572672)
2 changes: 1 addition & 1 deletion docs/source/commands/database/db.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.. important::
Before using these commands you need to add your database configuration in config (``.hckrcfg`` file), please refer :ref:`Configuring your databases <configuration>`
Before using these commands you need to add your database configuration in config (``~/.hckrcfg`` file), please refer :ref:`Configuring your databases <configuration>`


.. click:: hckr.cli.db:db
Expand Down
2 changes: 1 addition & 1 deletion docs/source/commands/database/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Database query
``hckr`` supports various of database commands.

.. important::
Before using these commands you need to add your database configuration in config (``.hckrcfg`` file), please refer :ref:`Configuring your database <configuration>`
Before using these commands you need to add your database configuration in config (``~/.hckrcfg`` file), please refer :ref:`Configuring your database <configuration>`

.. tip::
More commands will be added in future updates. Stay tuned!
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ dependencies = [
"snowflake-sqlalchemy", # For Snowflake,

# Error and debugging
"sentry-sdk"
"sentry-sdk",
]

[project.urls]
Expand Down
2 changes: 1 addition & 1 deletion src/hckr/__about__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024-present Ashish Patel <[email protected]>
#
# SPDX-License-Identifier: MIT
__version__ = "0.5.0.dev1"
__version__ = "0.5.0"
67 changes: 33 additions & 34 deletions src/hckr/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
from ..utils.MessageUtils import PError, PSuccess
from ..utils.config.ConfigUtils import (
init_config,
DEFAULT_CONFIG,
configMessage,
list_config,
set_config_value,
get_config_value,
common_config_options,
config_file_path_option,
)


Expand All @@ -23,21 +24,11 @@ def config(ctx):
pass


def common_config_options(func):
func = click.option(
"-c",
"--config",
help="Config instance, default: DEFAULT",
default=DEFAULT_CONFIG,
)(func)
return func


@config.command()
@common_config_options
@click.argument("key")
@click.argument("value")
def set(config, key, value):
def set(config, config_path, key, value):
"""
This command adds a new entry to the config file with key and value
Expand All @@ -59,14 +50,14 @@ def set(config, key, value):
"""

configMessage(config)
set_config_value(config, key, value)
set_config_value(config, config_path, key, value)
PSuccess(f"[{config}] {key} <- {value}")


@config.command()
@common_config_options
@click.argument("key")
def get(config, key):
def get(config, config_path, key):
"""
This command returns value for a key in a configuration
Expand All @@ -91,25 +82,36 @@ def get(config, key):

configMessage(config)
try:
value = get_config_value(config, key)
value = get_config_value(config, config_path, key)
PSuccess(f"[{config}] {key} = {value}")
except ValueError as e:
PError(f"{e}")


@config.command("list")
@config_file_path_option
def list_configs(config_path):
"""
This command show list all configurations and their key values
**Example Usage**:
* We can also see all configuration list command
.. code-block:: shell
$ hckr config list
**Command Reference**:
"""
list_config(config_path, _all=True)


@config.command()
@common_config_options
@click.option(
"-a",
"--all",
default=False,
is_flag=True,
help="Whether to shows a list of all configs (default: False)",
)
def list(config, all):
def show(config, config_path):
"""
This command show list of all keys available in given configuration,
we can also see values in all configurations by providing -a/--all flag
This command show list of all keys available in the given configuration,
**Example Usage**:
Expand All @@ -119,23 +121,20 @@ def list(config, all):
.. code-block:: shell
$ hckr config list
$ hckr config show
* Similarly, we can also get all values in a specific configuration using -c/--config flag
.. code-block:: shell
$ hckr config list -c MY_DATABASE
* Additionally, we can also see all configurations using -a/--all flag
.. code-block:: shell
$ hckr config list --all
$ hckr config show -c MY_DATABASE
**Command Reference**:
"""
list_config(config, all)
list_config(
config_path,
config,
)


@config.command()
Expand Down
59 changes: 52 additions & 7 deletions src/hckr/cli/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from ..utils.config.ConfigUtils import (
list_config,
set_config_value,
set_default_config,
config_file_path_option,
)
from ..utils.config.ConfigureUtils import configure_host, configure_creds
from ..utils.config.Constants import (
Expand All @@ -16,7 +18,7 @@


@click.group(
help="easy configurations for other commands (eg. db)",
help="Easy configurations for other commands (eg. db)",
context_settings={"help_option_names": ["-h", "--help"]},
)
@click.pass_context
Expand All @@ -27,6 +29,39 @@ def configure(ctx):
pass


@configure.command()
@config_file_path_option
@click.argument("service", type=click.Choice([str(ConfigType.DATABASE)]))
@click.argument("config_name")
def set_default(service, config_name, config_path):
"""Set the default configuration for a service configured via ``hckr configure``
This command configures database credentials based on the selected database type.
.. hint::
We currently support ``database`` default configuration which corresponds to ``hckr configure``,
**Example Usage**:
* Setting up your default database configuration in [DEFAULT] configuration
.. code-block:: shell
$ hckr configure set-default db MY_DB_CONFIG
.. note::
Please note that the ``MY_DB_CONFIG`` config must be configured before running this using ``hckr configure db`` command
.. important:: This command will add an entry in ``[DEFAULT]`` configuration like ``database = MY_DB_CONFIG`` and
if you run any database command like ``hckr db query <QUERY>`` without providing configuration using
``-c/--config`` flag this config will be used.
**Command Reference**:
"""

set_default_config(service, config_name, config_path)


@configure.command("db")
@click.option(
"--config-name",
Expand Down Expand Up @@ -54,8 +89,10 @@ def configure(ctx):
@click.option("--account", prompt=False, help="Snowflake Account Id")
@click.option("--warehouse", prompt=False, help="Snowflake warehouse")
@click.option("--role", prompt=False, help="Snowflake role")
@config_file_path_option
def configure_db(
config_name,
config_path,
database_type,
host,
port,
Expand Down Expand Up @@ -109,21 +146,29 @@ def configure_db(
**Command Reference**:
"""

set_config_value(config_name, CONFIG_TYPE, ConfigType.DATABASE)
set_config_value(config_name, config_path, CONFIG_TYPE, ConfigType.DATABASE)
selected_db_type = db_type_mapping[database_type]
set_config_value(config_name, DB_TYPE, selected_db_type)
set_config_value(config_name, config_path, DB_TYPE, selected_db_type)

configure_creds(config_name, password, selected_db_type, user)
configure_creds(config_name, config_path, password, selected_db_type, user)

if not database_name:
database_name = click.prompt("Enter the database name")
set_config_value(config_name, DB_NAME, database_name)
set_config_value(config_name, config_path, DB_NAME, database_name)

configure_host(
account, config_name, host, port, role, schema, selected_db_type, warehouse
config_name,
config_path,
account,
host,
port,
role,
schema,
selected_db_type,
warehouse,
)

PSuccess(
f"Database configuration saved successfully in config instance '{config_name}'"
)
list_config(config_name)
list_config(config_path, config_name)
60 changes: 16 additions & 44 deletions src/hckr/cli/db.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import warnings

import click
import pandas as pd
from sqlalchemy import create_engine, text
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.exc import SAWarning
from yaspin import yaspin # type: ignore

from hckr.cli.config import common_config_options
from hckr.utils.DataUtils import print_df_as_table
from hckr.utils.DbUtils import get_db_url
from hckr.utils.MessageUtils import PError, PInfo
from hckr.utils.DbUtils import get_db_url, execute_query
from hckr.utils.MessageUtils import PError

# Suppress the specific SQLAlchemy warning
warnings.filterwarnings("ignore", category=SAWarning, message=".*flatten.*")

# Your SQLAlchemy code


@click.group(
Expand Down Expand Up @@ -35,7 +40,7 @@ def db():
required=False,
)
@click.pass_context
def query(ctx, config, query, num_rows=None, num_cols=None):
def query(ctx, config, config_path, query, num_rows=None, num_cols=None):
"""
This command executes a SQL query on your configured database and show you result in a table format ( in
``SELECT/SHOW/DESC`` queries )
Expand Down Expand Up @@ -71,42 +76,9 @@ def query(ctx, config, query, num_rows=None, num_cols=None):
**Command Reference**:
"""
db_url = get_db_url(section=config)
db_url = get_db_url(section=config, config_path=config_path)
if not db_url:
PError("Database credentials are not properly configured.")

query = query.strip()
engine = create_engine(db_url)
try:
with engine.connect() as connection:
# Normalize and determine the type of query
normalized_query = query.lower()
is_data_returning_query = normalized_query.startswith(
("select", "desc", "describe", "show", "explain")
)
is_ddl_query = normalized_query.startswith(
("create", "alter", "drop", "truncate")
)

if is_data_returning_query:
# Execute and fetch results for queries that return data
df = pd.read_sql_query(text(query), connection)

# Optionally limit rows and columns if specified
if num_rows is not None:
df = df.head(num_rows)
if num_cols is not None:
df = df.iloc[:, :num_cols]

print_df_as_table(df, title=query)
return df
else:
# Execute DDL or non-data-returning DML queries
with connection.begin(): # this will automatically commit at the end
result = connection.execute(text(query))
if is_ddl_query:
PInfo(query, "Success")
else:
PInfo(query, f"[Success] Rows affected: {result.rowcount}")
except SQLAlchemyError as e:
PError(f"Error executing query: {e}")
with yaspin(text="Running query...", color="green", timer=True) as spinner:
execute_query(db_url, query, num_rows, num_cols)
spinner.ok("✔")
2 changes: 1 addition & 1 deletion src/hckr/utils/CliUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Info:

def __init__(self): # Note: This object must have an empty constructor.
"""Create a new instance."""
self.verbose: int = 0
self.verbose = 0


def check_latest_version():
Expand Down
Loading

0 comments on commit a498ca7

Please sign in to comment.