Repository: dbt-labs/dbt-core

Issue:
[CT-928] `dbt run-operation` works in click



Discussion:

Resolving patch:
diff --git a/core/dbt/cli/main.py b/core/dbt/cli/main.py
--- a/core/dbt/cli/main.py
+++ b/core/dbt/cli/main.py
@@ -18,6 +18,7 @@
 from dbt.task.seed import SeedTask
 from dbt.task.list import ListTask
 from dbt.task.freshness import FreshnessTask
+from dbt.task.run_operation import RunOperationTask
 
 
 # CLI invocation
@@ -260,6 +261,8 @@ def deps(ctx, **kwargs):
 # dbt init
 @cli.command("init")
 @click.pass_context
+# for backwards compatibility, accept 'project_name' as an optional positional argument
+@click.argument("project_name", required=False)
 @p.profile
 @p.profiles_dir
 @p.project_dir
@@ -268,7 +271,7 @@ def deps(ctx, **kwargs):
 @p.vars
 @requires.preflight
 def init(ctx, **kwargs):
-    """Initialize a new DBT project."""
+    """Initialize a new dbt project."""
     click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {ctx.obj['flags']}")
     return None, True
 
@@ -364,6 +367,7 @@ def run(ctx, **kwargs):
 # dbt run operation
 @cli.command("run-operation")
 @click.pass_context
+@click.argument("macro")
 @p.args
 @p.profile
 @p.profiles_dir
@@ -371,10 +375,16 @@ def run(ctx, **kwargs):
 @p.target
 @p.vars
 @requires.preflight
+@requires.profile
+@requires.project
 def run_operation(ctx, **kwargs):
     """Run the named macro with any supplied arguments."""
-    click.echo(f"`{inspect.stack()[0][3]}` called\n flags: {ctx.obj['flags']}")
-    return None, True
+    config = RuntimeConfig.from_parts(ctx.obj["project"], ctx.obj["profile"], ctx.obj["flags"])
+    task = RunOperationTask(ctx.obj["flags"], config)
+
+    results = task.run()
+    success = task.interpret_results(results)
+    return results, success
 
 
 # dbt seed
diff --git a/core/dbt/task/run_operation.py b/core/dbt/task/run_operation.py
--- a/core/dbt/task/run_operation.py
+++ b/core/dbt/task/run_operation.py
@@ -1,5 +1,4 @@
 from datetime import datetime
-from typing import Dict, Any
 import traceback
 
 import agate
@@ -8,7 +7,6 @@
 
 import dbt.exceptions
 from dbt.adapters.factory import get_adapter
-from dbt.config.utils import parse_cli_vars
 from dbt.contracts.results import RunOperationResultsArtifact
 from dbt.exceptions import InternalException
 from dbt.events.functions import fire_event
@@ -29,14 +27,6 @@ def _get_macro_parts(self):
 
         return package_name, macro_name
 
-    def _get_kwargs(self) -> Dict[str, Any]:
-        # N.B. parse_cli_vars is embedded into the param when using click.
-        # replace this with:
-        # return self.args.args
-        # when this task is refactored for click
-        # or remove the function completely as it's basically a noop
-        return parse_cli_vars(self.args.args)
-
     def compile_manifest(self) -> None:
         if self.manifest is None:
             raise InternalException("manifest was None in compile_manifest")
@@ -45,7 +35,7 @@ def _run_unsafe(self) -> agate.Table:
         adapter = get_adapter(self.config)
 
         package_name, macro_name = self._get_macro_parts()
-        macro_kwargs = self._get_kwargs()
+        macro_kwargs = self.args.args
 
         with adapter.connection_named("macro_{}".format(macro_name)):
             adapter.clear_transaction()


Test patch:
diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py
--- a/tests/unit/test_cli.py
+++ b/tests/unit/test_cli.py
@@ -23,7 +23,8 @@ def run_test(commands):
     def test_unhidden_params_have_help_texts(self):
         def run_test(command):
             for param in command.params:
-                if not param.hidden:
+                # arguments can't have help text
+                if not isinstance(param, click.Argument) and not param.hidden:
                     assert param.help is not None
             if type(command) is click.Group:
                 for command in command.commands.values():