Skip to content

Commit

Permalink
v0.0.15
Browse files Browse the repository at this point in the history
* Bugfixes

---------

Co-authored-by: TJ Murphy <[email protected]>
  • Loading branch information
teej and teej authored Jan 7, 2024
1 parent 7d05e5f commit 879ae18
Show file tree
Hide file tree
Showing 60 changed files with 1,034 additions and 253 deletions.
69 changes: 69 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Test and Lint Python Package

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_call:
secrets:
TEST_SNOWFLAKE_ACCOUNT:
required: true
TEST_SNOWFLAKE_USER:
required: true
TEST_SNOWFLAKE_PASSWORD:
required: true

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Create a virtual environment
run: |
python -m venv .venv
- name: Install dependencies
run: |
source ./.venv/bin/activate
python -m pip install --upgrade pip
make install-dev
- name: Run checks (linter, code style, tests)
run: |
source ./.venv/bin/activate
make check
integration:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [snowflake-aws-enterprise, snowflake-aws-standard]
environment: ${{ matrix.environment }}
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.9
- name: Create a virtual environment
run: |
python -m venv .venv
- name: Install dependencies
run: |
source ./.venv/bin/activate
python -m pip install --upgrade pip
make install-dev
- name: Run integration tests
run: |
source ./.venv/bin/activate
make integration
env:
TEST_SNOWFLAKE_ACCOUNT: ${{ secrets.TEST_SNOWFLAKE_ACCOUNT }}
TEST_SNOWFLAKE_USER: ${{ secrets.TEST_SNOWFLAKE_USER }}
TEST_SNOWFLAKE_PASSWORD: ${{ secrets.TEST_SNOWFLAKE_PASSWORD }}
TEST_SNOWFLAKE_ROLE: ACCOUNTADMIN
31 changes: 31 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# How to install Titan

Snowflake app (preferred)

```SQL
-- AWS
CREATE STAGE titan_aws URL = 's3://titan-snowflake/';
EXECUTE IMMEDIATE FROM @titan_aws/install;

-- Google Cloud
CREATE STORAGE INTEGRATION titan_storage_int
TYPE = EXTERNAL_STAGE
STORAGE_PROVIDER = 'GCS'
ENABLED = TRUE
STORAGE_ALLOWED_LOCATIONS = ('gcs://titan-snowflake/')
;

CREATE STAGE titan_gcp
URL = 'gcs://titan-snowflake/'
STORAGE_INTEGRATION = titan_storage_int;

EXECUTE IMMEDIATE
FROM @titan_gcp/install;
```


Python package via pip

```sh
pip install https://github.com/teej/titan/archive/main.zip
```
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.PHONY: install install-dev test integration style check

install:
pip install -e .

install-dev:
pip install -e ".[dev]"

test:
python -m pytest

integration:
python -m pytest --snowflake

style:
python -m black .

check: style test
15 changes: 15 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pytest


def pytest_addoption(parser):
parser.addoption(
"--snowflake",
action="store_true",
default=False,
help="Runs tests that require a Snowflake connection",
)


def pytest_runtest_setup(item):
if "requires_snowflake" in item.keywords and not item.config.getoption("--snowflake"):
pytest.skip("need --snowflake option to run this test")
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ venvPath = ''
venv = ''

[tool.ruff]
line-length = 120
line-length = 120

[tool.pytest.ini_options]
addopts = "-n 4"
markers = "requires_snowflake"
filterwarnings = [
"ignore:.*urllib3.contrib.pyopenssl.*:DeprecationWarning"
]
12 changes: 12 additions & 0 deletions scripts/install
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

CREATE DATABASE titan;

CREATE PROCEDURE titan.public.install()
RETURNS OBJECT NOT NULL
LANGUAGE PYTHON
RUNTIME_VERSION = '3.9'
PACKAGES = ('snowflake-snowpark-python')
IMPORTS = ('@TITAN/titan-latest.zip')
HANDLER = 'titan.spi.install'
EXECUTE AS CALLER
;
10 changes: 7 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name="titan",
version="0.0.7",
version="0.0.15",
description="The easy way to automate data warehouse infrastructure",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
Expand All @@ -21,17 +21,21 @@
install_requires=[
"click==8.1.7",
"inflection==0.5.1",
"pydantic",
"pydantic>=2.0",
"pyparsing==3.0.9",
"pyyaml",
"snowflake-connector-python",
"snowflake-snowpark-python",
"pygithub==1.55",
],
extras_require={
"dev": [
"black",
"tabulate",
"pytest",
"pytest>=6.0",
"ruff",
"snowflake-cli-labs",
"pytest-xdist",
]
},
)
8 changes: 5 additions & 3 deletions tests/fixtures/account.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ create account myaccount1
first_name = Jane
last_name = Smith
email = '[email protected]'
edition = enterprise
region = aws_us_west_2;
-- edition = enterprise
-- region = aws_us_west_2
;

create account myaccount2
admin_name = admin
admin_password = 'TestPassword1'
email = '[email protected]'
edition = enterprise;
-- edition = enterprise
;
6 changes: 4 additions & 2 deletions tests/fixtures/grant.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ GRANT OPERATE ON WAREHOUSE report_wh TO ROLE analyst WITH GRANT OPTION;
GRANT ALL ON REPLICATION GROUP some_group TO ROLE somerole;

-- Schema Privileges
GRANT MODIFY, ADD SEARCH OPTIMIZATION, CREATE SNOWFLAKE.ML.FORECAST ON SCHEMA someschema TO ROLE somerole;
GRANT MODIFY ON SCHEMA someschema TO ROLE somerole;
GRANT ADD SEARCH OPTIMIZATION ON SCHEMA someschema TO ROLE somerole;
GRANT CREATE SNOWFLAKE.ML.FORECAST ON SCHEMA someschema TO ROLE somerole;
GRANT CREATE MATERIALIZED VIEW ON SCHEMA mydb.myschema TO ROLE myrole;

-- Schemas Privileges
Expand All @@ -28,6 +30,6 @@ GRANT SELECT ON ALL TABLES IN SCHEMA mydb.myschema to ROLE analyst;
GRANT USAGE ON ALL FUNCTIONS IN DATABASE somedb TO ROLE analyst;

-- Future Schema Objects Privileges
GRANT SELECT,INSERT ON FUTURE TABLES IN SCHEMA mydb.myschema TO ROLE somerole;
GRANT INSERT ON FUTURE TABLES IN SCHEMA mydb.myschema TO ROLE somerole;


5 changes: 0 additions & 5 deletions tests/fixtures/share.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,3 @@ FROM SHARE
WEATHERSOURCE_SNOWFLAKE_SNOWPARK_TILE_SNOWFLAKE_SECURE_SHARE_1651768630709
;

CREATE DATABASE
SNOWPARK_FOR_PYTHON__HANDSONLAB__WEATHER_DATA
FROM SHARE
WEATHERSOURCE.SNOWFLAKE_MANAGED$PUBLIC_GCP_US_CENTRAL1."WEATHERSOURCE_SNOWFLAKE_SNOWPARK_TILE_SNOWFLAKE_SECURE_SHARE_1651768630709"
;
18 changes: 18 additions & 0 deletions tests/fixtures/stored_procedure.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
create or replace procedure clean_table(table_name STRING)
returns int
language python
runtime_version = '3.8'
packages = ('snowflake-snowpark-python')
handler = 'clean_table_handler'
AS
$$
import snowflake.snowpark

def clean_table_handler(session: snowflake.snowpark.session.Session,
table_name: str) -> int:
table = session.table(table_name)
result = table.delete(~table['fruit'].rlike('[a-z]+'))
# equivalent to `DELETE FROM dirty_data WHERE fruit NOT RLIKE '[a-z]+';`

return result.rows_deleted
$$;
31 changes: 16 additions & 15 deletions tests/integration/data_provider/test_fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,24 +78,24 @@
"name": name,
"owner": TEST_ROLE,
"data_retention_time_in_days": 1,
"max_data_extension_time_in_days": 1,
"max_data_extension_time_in_days": 14,
"transient": True,
"with_managed_access": False,
},
},
{
"resource_key": "shared_database",
"setup_sql": [
"CALL SYSTEM$ACCEPT_LEGAL_TERMS('DATA_EXCHANGE_LISTING', 'GZSOZ1LLE9')",
"""CREATE DATABASE {name} FROM SHARE WEATHERSOURCE.SNOWFLAKE_MANAGED$PUBLIC_GCP_US_CENTRAL1."WEATHERSOURCE_SNOWFLAKE_SNOWPARK_TILE_SNOWFLAKE_SECURE_SHARE_1651768630709" """,
],
"teardown_sql": "DROP DATABASE {name}",
"data": lambda name: {
"name": name,
"owner": TEST_ROLE,
"from_share": "WEATHERSOURCE.SNOWFLAKE_MANAGED$PUBLIC_GCP_US_CENTRAL1.WEATHERSOURCE_SNOWFLAKE_SNOWPARK_TILE_SNOWFLAKE_SECURE_SHARE_1651768630709",
"managed_access": False,
},
},
# {
# "resource_key": "shared_database",
# "setup_sql": [
# "CALL SYSTEM$ACCEPT_LEGAL_TERMS('DATA_EXCHANGE_LISTING', 'GZSOZ1LLE9')",
# "CREATE DATABASE {name} FROM SHARE WEATHERSOURCE_SNOWFLAKE_SNOWPARK_TILE_SNOWFLAKE_SECURE_SHARE_1651768630709",
# ],
# "teardown_sql": "DROP DATABASE {name}",
# "data": lambda name: {
# "name": name,
# "owner": TEST_ROLE,
# "from_share": "WEATHERSOURCE_SNOWFLAKE_SNOWPARK_TILE_SNOWFLAKE_SECURE_SHARE_1651768630709",
# },
# },
{
"resource_key": "table",
"setup_sql": "CREATE TABLE {name} (id INT)",
Expand Down Expand Up @@ -172,6 +172,7 @@ def resource(request, cursor, suffix, test_db):
cursor.execute(teardown_sql.format(name=resource_name))


@pytest.mark.requires_snowflake
def test_fetch_resource(resource, db_session, test_db):
fetch = getattr(data_provider, resource["fetch_method"])
fqn = _generate_fqn(resource, test_db)
Expand Down
1 change: 1 addition & 0 deletions tests/integration/test_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def resource(request, suffix, marked_for_cleanup):
yield res


@pytest.mark.requires_snowflake
def test_create_drop(resource, test_db, cursor):
cursor.execute(f"USE DATABASE {test_db}")
cursor.execute(resource.create_sql())
Expand Down
31 changes: 16 additions & 15 deletions tests/integration/test_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,20 @@ def role(suffix, cursor, marked_for_cleanup):
return role


def test_role_permissions(cursor, user, role):
session = cursor.connection
user_grant = RoleGrant(role=role, to_user=user, owner="ACCOUNTADMIN")
sysadmin_grant = RoleGrant(role=role, to_role="SYSADMIN", owner="ACCOUNTADMIN")
# @pytest.mark.requires_snowflake
# def test_role_permissions(cursor, user, role):
# session = cursor.connection
# user_grant = RoleGrant(role=role, to_user=user, owner="ACCOUNTADMIN")
# sysadmin_grant = RoleGrant(role=role, to_role="SYSADMIN", owner="ACCOUNTADMIN")

bp = Blueprint(name="test", account=os.environ["SNOWFLAKE_ACCOUNT"])
bp.add(
role,
user_grant,
sysadmin_grant,
)
changes = bp.plan(session)
assert len(changes) == 2
bp.apply(session, changes)
drift = bp.plan(session)
assert len(drift) == 0
# bp = Blueprint(name="test", account=os.environ["SNOWFLAKE_ACCOUNT"])
# bp.add(
# role,
# user_grant,
# sysadmin_grant,
# )
# changes = bp.plan(session)
# assert len(changes) == 2
# bp.apply(session, changes)
# drift = bp.plan(session)
# assert len(drift) == 0
Loading

0 comments on commit 879ae18

Please sign in to comment.