Skip to content

Commit

Permalink
Merge branch 'main' into osalazar/SNOW-1449792
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-cgorrie authored Aug 23, 2024
2 parents 19688c0 + c7c986c commit 4601156
Show file tree
Hide file tree
Showing 30 changed files with 583 additions and 2 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ jobs:
run: |
args="${{ steps.tests_to_run.outputs.pytestArgs }}"
pythonpath="${{ steps.tests_to_run.outputs.pytestPaths }}"
if [ -z "${args}" ] || [ -z "${pythonpath}" ]; then
echo “Nothing to test”
else
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Some applications require other account-level setup before they can be properly
| [Object-level References](./object-level-references/) | A simple dashboard to show how to interact with the object-level references and bindings. |
| [Reference Usage](./reference-usage/) | How to share a provider table with a native application whose data is replicated to any consumer in the data cloud. |
| [SPCS Three-tier](./spcs-three-tier/) | A simple three-tiered web app that can be deployed in Snowpark Container Services. It queries the TPC-H 100 data set and returns the top sales clerks. |
| [Snowflake Cortex](./snowflake-cortex/) | A simple example on how to implement the Cortex Complete and to make it interact with user data. |
| [Tasks and Streams](./tasks-streams/) | How to execute a task and visualize changes using streams within a native application. |

## Contributing
Expand Down
2 changes: 2 additions & 0 deletions hybrid-tables/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
snowflake.local.yml
output/**
70 changes: 70 additions & 0 deletions hybrid-tables/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Hybrid Tables

This Snowflake Native Application sample demonstrates how to use hybrid tables when user needs to enforce a primary key constraint.

The `internal.dictionary` table simulates the behavior of a `Dictionary/Hash Table` data structure by allowing users add just key/value pairs that don't exist yet in the table.

```sql
CREATE HYBRID TABLE IF NOT EXISTS internal.dictionary
(
key VARCHAR,
value VARCHAR,
CONSTRAINT pkey PRIMARY KEY (key)
);
```

To add new values in the `dictionary` table, there is a Stored Procedure that returns an `OK` status if the columns where added successfully or a json with the error if the new row could not be added.

```sql
CREATE OR REPLACE PROCEDURE core.add_key_value(KEY VARCHAR, VALUE VARCHAR)
RETURNS VARIANT
LANGUAGE SQL
AS
$$
BEGIN
INSERT INTO internal.dictionary VALUES (:KEY, :VALUE);
RETURN OBJECT_CONSTRUCT('STATUS', 'OK');
EXCEPTION
WHEN STATEMENT_ERROR THEN
RETURN OBJECT_CONSTRUCT('STATUS', 'FAILED',
'SQLCODE', SQLCODE,
'SQLERRM', SQLERRM,
'SQLSTATE', SQLSTATE);
END;
$$;
```

## Development

### Setting up / Updating the Environment

Run the following command to create or update Conda environment. This includes tools like Snowflake CLI and testing packages:

```sh
conda env update -f local_test_env.yml
```
To activate the environment, run the following command:

```sh
conda activate hybrid-tables-testing
```

### Automated Testing

With the conda environment activated, you can test the app as follows:

```sh
pytest
```

### Manual Testing / Deployment to Snowflake

You can deploy the application in dev mode as follows:

```sh
snow app run
```

## Additional Resources

- [Hybrid Tables](https://docs.snowflake.com/en/user-guide/tables-hybrid)
20 changes: 20 additions & 0 deletions hybrid-tables/app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Welcome to Hybrid Tables + Native Apps!

In this Snowflake Native App, you will be able to explore the usage of `Hybrid Tables` within Native Apps.

For more information about a Snowflake Native App, please read the [official Snowflake documentation](https://docs.snowflake.com/en/developer-guide/native-apps/native-apps-about) which goes in depth about many additional functionalities of this framework.

## Using the application after installation
To interact with the application after it has successfully installed in your account, switch to the application owner role first.

### Calling a stored procedure

```
CALL <your_application_name>.<schema_name>.<stored_procedure_name_with_args>;
```

### Calling a function

```
SELECT <your_application_name>.<schema_name>.<udf_with_args>;
```
11 changes: 11 additions & 0 deletions hybrid-tables/app/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This is a manifest.yml file, a required component of creating a Snowflake Native App.
# This file defines properties required by the application package, including the location of the setup script and version definitions.
# Refer to https://docs.snowflake.com/en/developer-guide/native-apps/creating-manifest for a detailed understanding of this file.

manifest_version: 1

artifacts:
setup_script: setup_script.sql
default_streamlit: core.ui
extension_code: true
readme: README.md
43 changes: 43 additions & 0 deletions hybrid-tables/app/setup_script.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
-- This is the setup script that runs while installing a Snowflake Native App in a consumer account.
-- To write this script, you can familiarize yourself with some of the following concepts:
-- Application Roles
-- Versioned Schemas
-- UDFs/Procs
-- Extension Code
-- Refer to https://docs.snowflake.com/en/developer-guide/native-apps/creating-setup-script for a detailed understanding of this file.

CREATE APPLICATION ROLE IF NOT EXISTS app_public;
CREATE OR ALTER VERSIONED SCHEMA core;
CREATE SCHEMA IF NOT EXISTS internal;
GRANT USAGE ON SCHEMA core TO APPLICATION ROLE app_public;
GRANT USAGE ON SCHEMA internal TO APPLICATION ROLE app_public;

CREATE HYBRID TABLE IF NOT EXISTS internal.dictionary
(
key VARCHAR,
value VARCHAR,
CONSTRAINT pkey PRIMARY KEY (key)
);

CREATE OR REPLACE PROCEDURE core.add_key_value(KEY VARCHAR, VALUE VARCHAR)
RETURNS VARIANT
LANGUAGE SQL
AS
$$
BEGIN
INSERT INTO internal.dictionary VALUES (:KEY, :VALUE);
RETURN OBJECT_CONSTRUCT('STATUS', 'OK');
EXCEPTION
WHEN STATEMENT_ERROR THEN
RETURN OBJECT_CONSTRUCT('STATUS', 'FAILED',
'SQLCODE', SQLCODE,
'SQLERRM', SQLERRM,
'SQLSTATE', SQLSTATE);
END;
$$;

CREATE OR REPLACE STREAMLIT core.ui
FROM '/streamlit/'
MAIN_FILE = 'ui.py';

GRANT USAGE ON STREAMLIT core.ui TO APPLICATION ROLE app_public;
12 changes: 12 additions & 0 deletions hybrid-tables/local_test_env.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This file is used to install packages for local testing
name: hybrid-tables-testing
channels:
- snowflake
dependencies:
- python=3.8
- pip
- pip:
- snowflake-snowpark-python>=1.15.0
- pytest
- streamlit>=1.26.0

2 changes: 2 additions & 0 deletions hybrid-tables/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
pythonpath=python/src
8 changes: 8 additions & 0 deletions hybrid-tables/python/src/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file is used to install packages used by the Streamlit App.
# For more details, refer to https://docs.snowflake.com/en/developer-guide/streamlit/create-streamlit-sql#label-streamlit-install-packages-manual

channels:
- snowflake
dependencies:
- streamlit=1.26.0
- snowflake-native-apps-permission
30 changes: 30 additions & 0 deletions hybrid-tables/python/src/ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from snowflake.snowpark import Session
import json
import streamlit as st

class UI:
def __init__(self, session: Session) -> None:
self.session = session

def run(self):
with st.form('key_value_form', clear_on_submit=True):
col1, col2 = st.columns(2)
key = col1.text_input('Add a key', key='Key')
value = col2.text_input('Add a value for the key', key='Value')
if st.form_submit_button('Add'):
if key == '' or value == '':
st.error("Key-value pairs should not be empty")
else:
self.add(key, value)

st.dataframe(self.session.table('internal.dictionary').to_pandas(), use_container_width=True)

def add(self, key: str, value: str):
result = json.loads(self.session.call('core.add_key_value', key, value))
if "SQLCODE" in result:
error = f"Primary key violation on Hybrid Table. **{key}** already exists." if result["SQLCODE"] == 200001 else result["SQLERRM"]
st.error(error, icon="🚨")

if __name__ == '__main__':
ui = UI(Session.builder.getOrCreate())
ui.run()
40 changes: 40 additions & 0 deletions hybrid-tables/python/test/test_ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import pytest
from unittest.mock import patch, MagicMock
from snowflake.snowpark import Session
from streamlit.testing.v1 import AppTest
from ui import UI

@pytest.fixture()
def session():
session = Session.builder.config('local_testing', True).create()
yield session
session.close()

@patch('snowflake.snowpark.session.Session.table')
def test_run(table: MagicMock, session):
# arrange
def script(session):
from ui import UI
sut = UI(session)
return sut.run()

table.return_value = session.create_dataframe([{"key": "mykey", "value": "myvalue"}])

# act
at = AppTest.from_function(script, kwargs={ "session": session }).run()

# assert
assert len(at.dataframe[0].value.index) == 1

@patch('snowflake.snowpark.session.Session.call')
def test_add(call: MagicMock, session):
# arrange
call.return_value = '{ "STATUS": "OK" }'
sut = UI(session)

# act
sut.add('', '')

# assert
call.assert_called_once()

11 changes: 11 additions & 0 deletions hybrid-tables/snowflake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This is a project definition file, a required component if you intend to use Snowflake CLI in a project directory such as this template.

definition_version: 1
native_app:
name: hybrid_tables
source_stage: app_src.stage
artifacts:
- src: app/*
dest: ./
- src: python/src/*
dest: streamlit/
2 changes: 1 addition & 1 deletion object-level-references/app/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ version:
artifacts:
setup_script: setup_script.sql
readme: README.md
default_streamlit: core.dashboard
default_streamlit: core.ui
extension_code: true

references:
Expand Down
1 change: 1 addition & 0 deletions shared_python_ci_env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ dependencies:
- snowflake-cli-labs>=2.0.0
- pytest
- streamlit>=1.26.0
- snowflake-ml-python

4 changes: 4 additions & 0 deletions snowflake-cortex/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
snowflake.local.yml
output/
**/__pycache__/
**/.pytest_cache/
29 changes: 29 additions & 0 deletions snowflake-cortex/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Snowflake Cortex

This simple Native App shows how to use Cortex Complete and make it interact with user data.

For this use case, the dataset used is rather small: only 10 entries. This is because the language model used in the Cortex function restricts input data size. You can change the language model used to a bigger / different one, according to your needs.
For more information about it please visit **[this page](https://docs.snowflake.com/en/user-guide/snowflake-cortex/llm-functions#cost-considerations)**.

## Data preparation

To run this example first execute this command, that is going to create a database and table with information about songs charts:
```sh
snow sql -f 'prepare/provider_data.sql'
```
## App execution

Then run `snow app run` on your terminal.

## App and Data Deletion
To delete the database and the app run

```sh
snow sql -q 'DROP DATABASE SONGS_CORTEX_DB;'
snow app teardown
```

## Further reading

For more information about the different ways to use snowflake AI capabilities visit this page:
**[Snowflake AI and ML documentation](https://docs.snowflake.com/en/guides-overview-ai-features)**
3 changes: 3 additions & 0 deletions snowflake-cortex/app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Snowflake Native App - Snowflake Cortex

This is a sample Snowflake Native App that shows the use of Snowflake Cortex inside Native Apps.
12 changes: 12 additions & 0 deletions snowflake-cortex/app/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# For more information on creating manifest, go to https://docs.snowflake.com/en/developer-guide/native-apps/creating-manifest
manifest_version: 1

artifacts:
setup_script: setup_script.sql
readme: README.md
default_streamlit: core.ui
extension_code: true

privileges:
- IMPORTED PRIVILEGES ON SNOWFLAKE DB:
description: "Imported privileges to use cortex DB"
21 changes: 21 additions & 0 deletions snowflake-cortex/app/setup_script.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- This is the setup script that runs while installing a Snowflake Native App in a consumer account.
-- For more information on how to create setup file, visit https://docs.snowflake.com/en/developer-guide/native-apps/creating-setup-script

-- A general guideline to building this script looks like:
-- 1. Create application roles
CREATE APPLICATION ROLE IF NOT EXISTS app_public;

-- 2. Create a versioned schema to hold those UDFs/Stored Procedures
CREATE OR ALTER VERSIONED SCHEMA core;
GRANT USAGE ON SCHEMA core TO APPLICATION ROLE app_public;

-- 3. Create a streamlit object using the code you wrote in you wrote in src/module-ui, as shown below.
-- The `from` value is derived from the stage path described in snowflake.yml
CREATE OR REPLACE STREAMLIT core.ui
FROM '/streamlit/'
MAIN_FILE = 'dashboard.py';

-- 4. Grant appropriate privileges over these objects to your application roles.
GRANT USAGE ON STREAMLIT core.ui TO APPLICATION ROLE app_public;

-- A detailed explanation can be found at https://docs.snowflake.com/en/developer-guide/native-apps/adding-streamlit
15 changes: 15 additions & 0 deletions snowflake-cortex/local_test_env.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file is used to install packages for local testing
name: snowflake-cortex-testing
channels:
- snowflake
dependencies:
- python=3.8
- pip
- pip:
- snowflake-native-apps-permission-stub
- snowflake-snowpark-python>=1.15.0
- snowflake-cli-labs>=2.0.0
- pytest
- streamlit>=1.26.0
- snowflake-ml-python

Loading

0 comments on commit 4601156

Please sign in to comment.