Skip to content

Commit

Permalink
SNOW-1623352 Add option to pass app installation hash (#1441)
Browse files Browse the repository at this point in the history
Adds `--consumer-app-hash` to the `snow app events` command to distinguish between multiple app installations in the same account.
  • Loading branch information
sfc-gh-fcampbell authored Aug 13, 2024
1 parent ffd30d4 commit 53c31d7
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 53 deletions.
6 changes: 6 additions & 0 deletions src/snowflake/cli/_plugins/nativeapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ def app_events(
default="",
help="The name of the consumer account in the organization.",
),
consumer_app_hash: str = typer.Option(
default="",
help="The SHA-1 hash of the consumer application name",
),
first: int = typer.Option(
default=-1,
show_default=False,
Expand Down Expand Up @@ -526,6 +530,7 @@ def app_events(
scopes=scopes,
consumer_org=consumer_org,
consumer_account=consumer_account,
consumer_app_hash=consumer_app_hash,
)
)
# Append a newline at the end to make the CLI output clean when we hit Ctrl-C
Expand All @@ -542,6 +547,7 @@ def app_events(
last=last,
consumer_org=consumer_org,
consumer_account=consumer_account,
consumer_app_hash=consumer_app_hash,
)
)

Expand Down
30 changes: 25 additions & 5 deletions src/snowflake/cli/_plugins/nativeapp/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,7 @@ def get_events(
scopes: list[str] | None = None,
consumer_org: str = "",
consumer_account: str = "",
consumer_app_hash: str = "",
first: int = -1,
last: int = -1,
) -> list[dict]:
Expand All @@ -818,15 +819,24 @@ def get_events(
package_name = unquote_identifier(self.package_name)
org_name = unquote_identifier(consumer_org)
account_name = unquote_identifier(consumer_account)
app_clause = (
f"resource_attributes:\"snow.database.name\" = '{app_name}'"
if not (consumer_org and consumer_account)
else (

# Filter on record attributes
if consumer_org and consumer_account:
# Look for events shared from a consumer account
app_clause = (
f"resource_attributes:\"snow.application.package.name\" = '{package_name}' "
f"and resource_attributes:\"snow.application.consumer.organization\" = '{org_name}' "
f"and resource_attributes:\"snow.application.consumer.name\" = '{account_name}'"
)
)
if consumer_app_hash:
# If the user has specified a hash of a specific app installation
# in the consumer account, filter events to that installation only
app_clause += f" and resource_attributes:\"snow.database.hash\" = '{consumer_app_hash.lower()}'"
else:
# Otherwise look for events from an app installed in the same account as the package
app_clause = f"resource_attributes:\"snow.database.name\" = '{app_name}'"

# Filter on event time
if isinstance(since, datetime):
since_clause = f"and timestamp >= '{since}'"
elif isinstance(since, str) and since:
Expand All @@ -839,16 +849,23 @@ def get_events(
until_clause = f"and timestamp <= sysdate() - interval '{until}'"
else:
until_clause = ""

# Filter on event type (log, span, span_event)
type_in_values = ",".join(f"'{v}'" for v in record_types)
types_clause = (
f"and record_type in ({type_in_values})" if type_in_values else ""
)

# Filter on event scope (e.g. the logger name)
scope_in_values = ",".join(f"'{v}'" for v in scopes)
scopes_clause = (
f"and scope:name in ({scope_in_values})" if scope_in_values else ""
)

# Limit event count
first_clause = f"limit {first}" if first >= 0 else ""
last_clause = f"limit {last}" if last >= 0 else ""

query = dedent(
f"""\
select * from (
Expand Down Expand Up @@ -878,6 +895,7 @@ def stream_events(
scopes: list[str] | None = None,
consumer_org: str = "",
consumer_account: str = "",
consumer_app_hash: str = "",
last: int = -1,
) -> Generator[dict, None, None]:
try:
Expand All @@ -887,6 +905,7 @@ def stream_events(
scopes=scopes,
consumer_org=consumer_org,
consumer_account=consumer_account,
consumer_app_hash=consumer_app_hash,
last=last,
)
yield from events # Yield the initial batch of events
Expand All @@ -901,6 +920,7 @@ def stream_events(
scopes=scopes,
consumer_org=consumer_org,
consumer_account=consumer_account,
consumer_app_hash=consumer_app_hash,
)
if not events:
continue
Expand Down
96 changes: 50 additions & 46 deletions tests/__snapshots__/test_help_messages.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -260,52 +260,56 @@
nd-events

+- Options --------------------------------------------------------------------+
| --since TEXT Fetch events that are |
| newer than this time ago, |
| in Snowflake interval |
| syntax. |
| --until TEXT Fetch events that are |
| older than this time ago, |
| in Snowflake interval |
| syntax. |
| --type [log|span|span_event] Restrict results to |
| specific record type. Can |
| be specified multiple |
| times. |
| --scope TEXT Restrict results to a |
| specific scope name. Can |
| be specified multiple |
| times. |
| --consumer-org TEXT The name of the consumer |
| organization. |
| --consumer-account TEXT The name of the consumer |
| account in the |
| organization. |
| --first INTEGER Fetch only the first N |
| events. Cannot be used |
| with --last. |
| --last INTEGER Fetch only the last N |
| events. Cannot be used |
| with --first. |
| --follow -f Continue polling for |
| events. Implies --last 20 |
| unless overridden or the |
| --since flag is used. |
| --follow-interval INTEGER Polling interval in |
| seconds when using the |
| --follow flag. |
| [default: 10] |
| --project -p TEXT Path where Snowflake |
| project resides. Defaults |
| to current working |
| directory. |
| --env TEXT String in format of |
| key=value. Overrides |
| variables from env |
| section used for |
| templating. |
| --help -h Show this message and |
| exit. |
| --since TEXT Fetch events that are |
| newer than this time |
| ago, in Snowflake |
| interval syntax. |
| --until TEXT Fetch events that are |
| older than this time |
| ago, in Snowflake |
| interval syntax. |
| --type [log|span|span_event] Restrict results to |
| specific record type. |
| Can be specified |
| multiple times. |
| --scope TEXT Restrict results to a |
| specific scope name. Can |
| be specified multiple |
| times. |
| --consumer-org TEXT The name of the consumer |
| organization. |
| --consumer-account TEXT The name of the consumer |
| account in the |
| organization. |
| --consumer-app-hash TEXT The SHA-1 hash of the |
| consumer application |
| name |
| --first INTEGER Fetch only the first N |
| events. Cannot be used |
| with --last. |
| --last INTEGER Fetch only the last N |
| events. Cannot be used |
| with --first. |
| --follow -f Continue polling for |
| events. Implies --last |
| 20 unless overridden or |
| the --since flag is |
| used. |
| --follow-interval INTEGER Polling interval in |
| seconds when using the |
| --follow flag. |
| [default: 10] |
| --project -p TEXT Path where Snowflake |
| project resides. |
| Defaults to current |
| working directory. |
| --env TEXT String in format of |
| key=value. Overrides |
| variables from env |
| section used for |
| templating. |
| --help -h Show this message and |
| exit. |
+------------------------------------------------------------------------------+
+- Connection configuration ---------------------------------------------------+
| --connection,--environment -c TEXT Name of the connection, as defined |
Expand Down
18 changes: 16 additions & 2 deletions tests/nativeapp/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1387,18 +1387,30 @@ def test_account_event_table_not_set_up(mock_execute, temp_dir, mock_cursor):
],
)
@pytest.mark.parametrize(
["consumer_org", "consumer_account", "expected_app_clause"],
["consumer_org", "consumer_account", "consumer_app_hash", "expected_app_clause"],
[
("", "", f"resource_attributes:\"snow.database.name\" = 'MYAPP'"),
("", "", "", f"resource_attributes:\"snow.database.name\" = 'MYAPP'"),
(
"testorg",
"testacc",
"",
(
f"resource_attributes:\"snow.application.package.name\" = 'APP_PKG' "
f"and resource_attributes:\"snow.application.consumer.organization\" = 'TESTORG' "
f"and resource_attributes:\"snow.application.consumer.name\" = 'TESTACC'"
),
),
(
"testorg",
"testacc",
"428cdba48b74dfbbb333d5ea2cc51a78ecc56ce2",
(
f"resource_attributes:\"snow.application.package.name\" = 'APP_PKG' "
f"and resource_attributes:\"snow.application.consumer.organization\" = 'TESTORG' "
f"and resource_attributes:\"snow.application.consumer.name\" = 'TESTACC' "
f"and resource_attributes:\"snow.database.hash\" = '428cdba48b74dfbbb333d5ea2cc51a78ecc56ce2'"
),
),
],
)
@pytest.mark.parametrize(
Expand Down Expand Up @@ -1436,6 +1448,7 @@ def test_get_events(
expected_types_clause,
consumer_org,
consumer_account,
consumer_app_hash,
expected_app_clause,
scopes,
expected_scopes_clause,
Expand Down Expand Up @@ -1488,6 +1501,7 @@ def get_events():
scopes=scopes,
consumer_org=consumer_org,
consumer_account=consumer_account,
consumer_app_hash=consumer_app_hash,
first=first,
last=last,
)
Expand Down

0 comments on commit 53c31d7

Please sign in to comment.