From 9c760c7645c02def0afb182b6bb7d0d40efeabee Mon Sep 17 00:00:00 2001 From: Nicholas Yager Date: Sun, 25 Jun 2023 18:43:03 -0400 Subject: [PATCH 1/4] feat(cli): Changed CLI parameters for Ownership to be explicit I've updated how CLI arguments for owners work. Specifically, I've changed to arguments to no longer use user input to know the field name. Instad, there are new `--owner-name`, `--owner-email`, and `--owner-properties` fields. The `--owner-properties` field is used to supply a YML string that is parsed for extra properities in the object. BREAKING CHANGE: Creates a breaking change in CLI arguments relative to current version Resolves: #65 --- dbt_meshify/main.py | 54 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/dbt_meshify/main.py b/dbt_meshify/main.py index 3ea3b5b..60654ef 100644 --- a/dbt_meshify/main.py +++ b/dbt_meshify/main.py @@ -1,8 +1,10 @@ +import functools import os from pathlib import Path from typing import Any, Dict, List, Optional, Tuple import click +import yaml from dbt.contracts.graph.unparsed import Owner from .dbt_projects import DbtProject, DbtProjectHolder, DbtSubProject @@ -36,12 +38,19 @@ help="The dbt selection syntax specifying the resources to include in the operation", ) -owner = click.option( - "--owner", - nargs=2, - multiple=True, - type=click.Tuple([str, str]), - help="A tuple of Owner information for the group. For example " "`--owner name example`", +owner_name = click.option( + "--owner-name", + help="The group Owner's name.", +) + +owner_email = click.option( + "--owner-email", + help="The group Owner's email address.", +) + +owner_properties = click.option( + "--owner-properties", + help="Additional properties to assign to a group Owner.", ) selector = click.option( @@ -51,6 +60,20 @@ ) +def owner(func): + """Add click options and argument validation for creating Owner objects.""" + + @functools.wraps(func) + def wrapper_decorator(*args, **kwargs): + print("Validating owner configs.") + # Do something before + value = func(*args, **kwargs) + # Do something after + return value + + return wrapper_decorator + + # define cli group @click.group() def cli(): @@ -187,14 +210,19 @@ def add_version(select, exclude, project_path, selector, prerelease, defined_in) @select @selector @click.argument("name") +@owner_name +@owner_email +@owner_properties @owner @group_yml_path def create_group( name, project_path: os.PathLike, - owner: List[Tuple[str, str]], group_yml_path: os.PathLike, select: str, + owner_name: Optional[str] = None, + owner_email: Optional[str] = None, + owner_properties: str = '{}', exclude: Optional[str] = None, selector: Optional[str] = None, ): @@ -216,7 +244,9 @@ def create_group( "The provided group-yml-path is not contained within the provided dbt project." ) - owner: Owner = Owner(**{key: value for key, value in owner}) + owner: Owner = Owner( + name=owner_name, email=owner_email, _extra=yaml.safe_load(owner_properties) + ) grouper = ResourceGrouper(project) grouper.add_group( @@ -235,16 +265,20 @@ def create_group( @select @selector @click.argument("name") -@owner +@owner_name +@owner_email +@owner_properties @group_yml_path @click.pass_context def group( ctx, name, project_path: os.PathLike, - owner: List[Tuple[str, str]], group_yml_path: os.PathLike, select: str, + owner_name: Optional[str] = None, + owner_email: Optional[str] = None, + owner_properties: Optional[str] = None, exclude: Optional[str] = None, selector: Optional[str] = None, ): From 492d50ac939a2e857868d893d5ece1eedf4be821 Mon Sep 17 00:00:00 2001 From: Nicholas Yager Date: Sun, 25 Jun 2023 19:06:34 -0400 Subject: [PATCH 2/4] feat(cli): Add validation for new cli options. Move click definitions to cli.py Resolves: #65 --- dbt_meshify/cli.py | 67 +++++++++++++++++++++++++++++++++++- dbt_meshify/main.py | 82 +++++++++------------------------------------ 2 files changed, 81 insertions(+), 68 deletions(-) diff --git a/dbt_meshify/cli.py b/dbt_meshify/cli.py index a813810..cf0ca4d 100644 --- a/dbt_meshify/cli.py +++ b/dbt_meshify/cli.py @@ -1 +1,66 @@ -# TODO add the logic for each meshify command to independent tasks here to make cli thinner +import functools + +import click + +# define common parameters +project_path = click.option( + "--project-path", + type=click.Path(exists=True), + default=".", + help="The path to the dbt project to operate on. Defaults to the current directory.", +) + +exclude = click.option( + "--exclude", + "-e", + default=None, + help="The dbt selection syntax specifying the resources to exclude in the operation", +) + +group_yml_path = click.option( + "--group-yml-path", + type=click.Path(exists=False), + help="An optional path to store the new group YAML definition.", +) + +select = click.option( + "--select", + "-s", + default=None, + help="The dbt selection syntax specifying the resources to include in the operation", +) + +selector = click.option( + "--selector", + default=None, + help="The name of the YML selector specifying the resources to include in the operation", +) + +owner_name = click.option( + "--owner-name", + help="The group Owner's name.", +) + +owner_email = click.option( + "--owner-email", + help="The group Owner's email address.", +) + +owner_properties = click.option( + "--owner-properties", + help="Additional properties to assign to a group Owner.", +) + + +def owner(func): + """Add click options and argument validation for creating Owner objects.""" + + @functools.wraps(func) + def wrapper_decorator(*args, **kwargs): + if kwargs.get('owner_name') is None and kwargs.get('owner_email') is None: + raise click.UsageError( + "Groups require an Owner to be defined using --owner-name and/or --owner-email." + ) + return func(*args, **kwargs) + + return wrapper_decorator diff --git a/dbt_meshify/main.py b/dbt_meshify/main.py index 60654ef..49caff7 100644 --- a/dbt_meshify/main.py +++ b/dbt_meshify/main.py @@ -1,78 +1,25 @@ -import functools import os from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple +from typing import Optional import click import yaml from dbt.contracts.graph.unparsed import Owner +from .cli import ( + exclude, + group_yml_path, + owner, + owner_email, + owner_name, + owner_properties, + project_path, + select, + selector, +) from .dbt_projects import DbtProject, DbtProjectHolder, DbtSubProject from .storage.yaml_editors import DbtMeshModelConstructor -# define common parameters -project_path = click.option( - "--project-path", - type=click.Path(exists=True), - default=".", - help="The path to the dbt project to operate on. Defaults to the current directory.", -) - -exclude = click.option( - "--exclude", - "-e", - default=None, - help="The dbt selection syntax specifying the resources to exclude in the operation", -) - -group_yml_path = click.option( - "--group-yml-path", - type=click.Path(exists=False), - help="An optional path to store the new group YAML definition.", -) - -select = click.option( - "--select", - "-s", - default=None, - help="The dbt selection syntax specifying the resources to include in the operation", -) - -owner_name = click.option( - "--owner-name", - help="The group Owner's name.", -) - -owner_email = click.option( - "--owner-email", - help="The group Owner's email address.", -) - -owner_properties = click.option( - "--owner-properties", - help="Additional properties to assign to a group Owner.", -) - -selector = click.option( - "--selector", - default=None, - help="The name of the YML selector specifying the resources to include in the operation", -) - - -def owner(func): - """Add click options and argument validation for creating Owner objects.""" - - @functools.wraps(func) - def wrapper_decorator(*args, **kwargs): - print("Validating owner configs.") - # Do something before - value = func(*args, **kwargs) - # Do something after - return value - - return wrapper_decorator - # define cli group @click.group() @@ -244,14 +191,14 @@ def create_group( "The provided group-yml-path is not contained within the provided dbt project." ) - owner: Owner = Owner( + group_owner: Owner = Owner( name=owner_name, email=owner_email, _extra=yaml.safe_load(owner_properties) ) grouper = ResourceGrouper(project) grouper.add_group( name=name, - owner=owner, + owner=group_owner, select=select, exclude=exclude, selector=selector, @@ -268,6 +215,7 @@ def create_group( @owner_name @owner_email @owner_properties +@owner @group_yml_path @click.pass_context def group( From 070f2ef83dbe218667bbe6149f3929b51208c2d6 Mon Sep 17 00:00:00 2001 From: Nicholas Yager Date: Sun, 25 Jun 2023 19:19:35 -0400 Subject: [PATCH 3/4] test: Update tests to work with new argument structure --- dbt_meshify/main.py | 4 ++-- tests/integration/test_create_group_command.py | 10 ++++------ tests/integration/test_group_command.py | 3 +-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dbt_meshify/main.py b/dbt_meshify/main.py index 49caff7..46fe647 100644 --- a/dbt_meshify/main.py +++ b/dbt_meshify/main.py @@ -169,7 +169,7 @@ def create_group( select: str, owner_name: Optional[str] = None, owner_email: Optional[str] = None, - owner_properties: str = '{}', + owner_properties: Optional[str] = None, exclude: Optional[str] = None, selector: Optional[str] = None, ): @@ -192,7 +192,7 @@ def create_group( ) group_owner: Owner = Owner( - name=owner_name, email=owner_email, _extra=yaml.safe_load(owner_properties) + name=owner_name, email=owner_email, _extra=yaml.safe_load(owner_properties or '{}') ) grouper = ResourceGrouper(project) diff --git a/tests/integration/test_create_group_command.py b/tests/integration/test_create_group_command.py index 7c15573..e62a3e4 100644 --- a/tests/integration/test_create_group_command.py +++ b/tests/integration/test_create_group_command.py @@ -51,11 +51,9 @@ def test_create_group_command(model_yml, start_group_yml, end_group_yml): "shared_model", "--project-path", proj_path_string, - "--owner", - "name", + "--owner-name", "Shaina Fake", - "--owner", - "email", + "--owner-email", "fake@example.com", ], ) @@ -97,10 +95,10 @@ def test_group_group_owner_properties(name, email, end_group_yml): args = ["test_group", "--select", "shared_model", "--project-path", proj_path_string] if name: - args += ["--owner", "name", name] + args += ["--owner-name", name] if email: - args += ["--owner", "email", email] + args += ["--owner-email", email] runner = CliRunner() result = runner.invoke(create_group, args) diff --git a/tests/integration/test_group_command.py b/tests/integration/test_group_command.py index 1858719..565b323 100644 --- a/tests/integration/test_group_command.py +++ b/tests/integration/test_group_command.py @@ -36,8 +36,7 @@ def test_group_command(select, expected_public_contracted_models): group, [ "test_group", - "--owner", - "name", + "--owner-name", "Teenage Mutant Jinja Turtles", "--select", select, From f20c0eff3dc5c2d3fe326669be7fdc746f12f33f Mon Sep 17 00:00:00 2001 From: Nicholas Yager Date: Sun, 25 Jun 2023 19:57:06 -0400 Subject: [PATCH 4/4] docs(cli): Update docs to reflect new owner CLI design --- README.md | 2 +- docs/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 363ab9c..023986f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ pip install dbt-meshify # create a group of all models tagged with "finance" # leaf nodes and nodes with cross-group dependencies will be `public` # public nodes will also have contracts added to them -dbt-meshify group finance --owner name Monopoly Man -s +tag:finance +dbt-meshify group finance --owner-name "Monopoly Man" -s +tag:finance # optionally use the add-version operation to add a new version to a model dbt-meshify operation add-version -s fct_orders diff --git a/docs/index.md b/docs/index.md index 82eccae..5c4192c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -30,7 +30,7 @@ Here's how that might look for the process of creating a separate `finance` subp # create a group of all models tagged with "finance" # leaf nodes and nodes with cross-group dependencies will be `public` # public nodes will also have contracts added to them -dbt-meshify group finance --owner name Monopoly Man -s +tag:finance +dbt-meshify group finance --owner-name "Monopoly Man" -s +tag:finance # optionally use the add-version operation to add a new version to a model dbt-meshify operation add-version -s fct_orders