Repository: meltano/meltano

Issue:
bug: `meltano state list` with pattern - no such option
### Meltano Version

2.3.0

### Python Version

3.8

### Bug scope

CLI (options, error messages, logging, etc.)

### Operating System

Mac

### Description

It looks like the `--pattern` argument thats in the docs https://docs.meltano.com/reference/command-line-interface#list isnt available on the CLI.

```
(meltano) Patricks-MBP:data pnadolny$ meltano --version
meltano, version 2.3.0

(meltano) Patricks-MBP:data pnadolny$ meltano state list --pattern '*tap-gitlab*'
2022-07-25T21:31:25.438941Z [info     ] Environment 'userdev' is active
Usage: meltano state list [OPTIONS] [PATTERN]
Try 'meltano state list --help' for help.

Error: No such option: --pattern
```

### Code

_No response_


Discussion:
FYI @aaronsteers I'm adding this to next week's iteration to fix.
@tayloramurphy - Before we start dev work on this, I just want to confirm the spec regarding _how_ wildcards should be implemented.

Is it a correct expectation that we'll use the same parser that is used by `meltano select` syntax syntax?

The docs for `meltano state list` currently only mention `*` as wildcard, but the `select` rules are documented as:

> [Unix shell-style wildcards](https://en.wikipedia.org/wiki/Glob_(programming)#Syntax) can be used in selection patterns to match multiple entities or attributes at once:
> 
> *: matches any sequence of characters
> ?: matches one character
> [abc]: matches either a, b, or c
> [!abc]: matches any character but a, b, or c

Sidebar: `select` docs don't give instructions on how to escape characters, although it would not be common to have any of these characters in a state ID, stream name, or property name, so perhaps escaping will not be necessary. (Mentioned characters are [`*`, `!`, `?`, `[`, `]`].)

Simplest solution is probably just to implement the same parser as in `meltano select` (refactoring into the `utils` library if needed for reuse?) and then for docs, we can just say something like:

```diff
- The `--pattern` option allows filtering returned state IDs by using * as a wildcard.
+ The `--pattern` option allows filtering returned state IDs by wildcards. Pattern matching follows [meltano select](https://docs.meltano.com/reference/command-line-interface#select) syntax.
```

@kgpayne - Let me know (in accordance also with Taylor's response to the above), if you feel comfortable picking this up next week. Thanks!

I expect a weight between `2` and `4` and I for now I have placed a more conservative `4` for estimation.

I don't know how robust our implementation is currently, and my scoping takes into account that I believe the higher priority here should be code reuse (if at all possible), rather than trying to create from scratch a perfect/best pattern matching spec.
> Simplest solution is probably just to implement the same parser as in meltano select (refactoring into the utils library if needed for reuse?) and then for docs, we can just say something like:

@aaronsteers @kgpayne If it's actually simpler to use the more expressive matching patterns, then yeah I'm great with that. At a minimum I just want it to match what's in the docs, but if we get enhanced matching that's a nice bonus.
Related: 
- https://github.com/meltano/meltano/discussions/6270

Resolving patch:
diff --git a/src/meltano/cli/state.py b/src/meltano/cli/state.py
--- a/src/meltano/cli/state.py
+++ b/src/meltano/cli/state.py
@@ -111,7 +111,7 @@ def meltano_state(project: Project, ctx: click.Context):
 
 
 @meltano_state.command(cls=InstrumentedCmd, name="list")
-@click.argument("pattern", required=False)
+@click.option("--pattern", type=str, help="Filter state IDs by pattern.")
 @click.pass_context
 @pass_project()
 def list_state(


Test patch:
diff --git a/tests/meltano/cli/test_state.py b/tests/meltano/cli/test_state.py
--- a/tests/meltano/cli/test_state.py
+++ b/tests/meltano/cli/test_state.py
@@ -87,7 +87,7 @@ def test_list_pattern(
     ):
         with mock.patch("meltano.cli.state.StateService", return_value=state_service):
             for (pattern, expected_result) in patterns_with_expected_results:
-                result = cli_runner.invoke(cli, ["state", "list", pattern])
+                result = cli_runner.invoke(cli, ["state", "list", "--pattern", pattern])
                 assert_cli_runner(result)
                 assert self.get_result_set(result) == expected_result