def test_StoreAction():
    fake_in_memory_store = ValidationsStore(root_directory=None,
                                            store_backend={
                                                "class_name":
                                                "InMemoryStoreBackend",
                                            })
    stores = {"fake_in_memory_store": fake_in_memory_store}

    # NOTE: This is a hack meant to last until we implement runtime_configs
    class Object(object):
        pass

    data_context = Object()
    data_context.stores = stores

    action = StoreAction(
        data_context=data_context,
        target_store_name="fake_in_memory_store",
    )
    assert fake_in_memory_store.list_keys() == []

    vr_id = "ValidationResultIdentifier.my_db.default_generator.my_table.default_expectations.prod_20190801"
    action.run(validation_result_suite_identifier=ValidationResultIdentifier(
        from_string=vr_id),
               validation_result_suite={},
               data_asset=None)

    assert len(fake_in_memory_store.list_keys()) == 1
    assert fake_in_memory_store.list_keys()[0].to_string(
    ) == "ValidationResultIdentifier.my_db.default_generator.my_table.default_expectations.prod_20190801"
    assert fake_in_memory_store.get(
        ValidationResultIdentifier(
            from_string=
            "ValidationResultIdentifier.my_db.default_generator.my_table.default_expectations.prod_20190801"
        )) == {}
Пример #2
0
def test_ValidationResultIdentifier_to_tuple(expectation_suite_identifier):
    validation_result_identifier = ValidationResultIdentifier(
        expectation_suite_identifier, "my_run_id", "my_batch_identifier")
    assert validation_result_identifier.to_tuple() == ("my", "expectation",
                                                       "suite", "name",
                                                       "my_run_id",
                                                       "my_batch_identifier")
    assert validation_result_identifier.to_fixed_length_tuple() == (
        "my.expectation.suite.name", "my_run_id", "my_batch_identifier")

    validation_result_identifier_no_run_id = ValidationResultIdentifier(
        expectation_suite_identifier, None, "my_batch_identifier")
    assert validation_result_identifier_no_run_id.to_tuple() == (
        "my", "expectation", "suite", "name", "__none__",
        "my_batch_identifier")
    assert validation_result_identifier_no_run_id.to_fixed_length_tuple() == (
        "my.expectation.suite.name", "__none__", "my_batch_identifier")

    validation_result_identifier_no_batch_identifier = ValidationResultIdentifier(
        expectation_suite_identifier, "my_run_id", None)
    assert validation_result_identifier_no_batch_identifier.to_tuple() == (
        "my", "expectation", "suite", "name", "my_run_id", "__none__")
    assert validation_result_identifier_no_batch_identifier.to_fixed_length_tuple(
    ) == ("my.expectation.suite.name", "my_run_id", "__none__")

    validation_result_identifier_no_run_id_no_batch_identifier = ValidationResultIdentifier(
        expectation_suite_identifier, None, None)
    assert validation_result_identifier_no_run_id_no_batch_identifier.to_tuple(
    ) == ("my", "expectation", "suite", "name", "__none__", "__none__")
    assert validation_result_identifier_no_run_id_no_batch_identifier.to_fixed_length_tuple(
    ) == ("my.expectation.suite.name", "__none__", "__none__")
def test_StoreAction():
    fake_in_memory_store = ValidationsStore(
        store_backend={
            "class_name": "InMemoryStoreBackend",
        }
    )
    stores = {"fake_in_memory_store": fake_in_memory_store}

    class Object:
        ge_cloud_mode = False

    data_context = Object()
    data_context.stores = stores

    action = StoreValidationResultAction(
        data_context=data_context,
        target_store_name="fake_in_memory_store",
    )
    assert fake_in_memory_store.list_keys() == []

    action.run(
        validation_result_suite_identifier=ValidationResultIdentifier(
            expectation_suite_identifier=ExpectationSuiteIdentifier(
                expectation_suite_name="default_expectations"
            ),
            run_id=RunIdentifier(run_name="prod_20190801"),
            batch_identifier="1234",
        ),
        validation_result_suite=ExpectationSuiteValidationResult(
            success=False, results=[]
        ),
        data_asset=None,
    )

    expected_run_id = RunIdentifier(
        run_name="prod_20190801", run_time="20190926T134241.000000Z"
    )

    assert len(fake_in_memory_store.list_keys()) == 1
    stored_identifier = fake_in_memory_store.list_keys()[0]
    assert stored_identifier.batch_identifier == "1234"
    assert (
        stored_identifier.expectation_suite_identifier.expectation_suite_name
        == "default_expectations"
    )
    assert stored_identifier.run_id == expected_run_id

    assert fake_in_memory_store.get(
        ValidationResultIdentifier(
            expectation_suite_identifier=ExpectationSuiteIdentifier(
                expectation_suite_name="default_expectations"
            ),
            run_id=expected_run_id,
            batch_identifier="1234",
        )
    ) == ExpectationSuiteValidationResult(success=False, results=[])
Пример #4
0
def test_ValidationsStore_with_TupleS3StoreBackend():
    bucket = "test_validation_store_bucket"
    prefix = "test/prefix"

    # create a bucket in Moto's mock AWS environment
    conn = boto3.resource("s3", region_name="us-east-1")
    conn.create_bucket(Bucket=bucket)

    # First, demonstrate that we pick up default configuration including from an S3TupleS3StoreBackend
    my_store = ValidationsStore(store_backend={
        "class_name": "TupleS3StoreBackend",
        "bucket": bucket,
        "prefix": prefix,
    })

    with pytest.raises(TypeError):
        my_store.get("not_a_ValidationResultIdentifier")

    ns_1 = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            expectation_suite_name="asset.quarantine", ),
        run_id="20191007T151224.1234Z_prod_100",
        batch_identifier="batch_id",
    )
    my_store.set(ns_1, ExpectationSuiteValidationResult(success=True))
    assert my_store.get(ns_1) == ExpectationSuiteValidationResult(
        success=True, statistics={}, results=[])

    ns_2 = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            expectation_suite_name="asset.quarantine", ),
        run_id="20191007T151224.1234Z_prod_200",
        batch_identifier="batch_id",
    )

    my_store.set(ns_2, ExpectationSuiteValidationResult(success=False))
    assert my_store.get(ns_2) == ExpectationSuiteValidationResult(
        success=False, statistics={}, results=[])

    # Verify that internals are working as expected, including the default filepath
    assert {
        s3_object_info["Key"]
        for s3_object_info in boto3.client("s3").list_objects_v2(
            Bucket=bucket, Prefix=prefix)["Contents"]
    } == {
        "test/prefix/asset/quarantine/20191007T151224.1234Z_prod_100/20190926T134241.000000Z/batch_id.json",
        "test/prefix/asset/quarantine/20191007T151224.1234Z_prod_200/20190926T134241.000000Z/batch_id.json",
    }

    print(my_store.list_keys())
    assert set(my_store.list_keys()) == {
        ns_1,
        ns_2,
    }
def test_ValidationsStore_with_DatabaseStoreBackend(sa):
    # Use sqlite so we don't require postgres for this test.
    connection_kwargs = {"drivername": "sqlite"}

    # First, demonstrate that we pick up default configuration
    my_store = ValidationsStore(
        store_backend={
            "class_name": "DatabaseStoreBackend",
            "credentials": connection_kwargs,
        }
    )

    with pytest.raises(TypeError):
        my_store.get("not_a_ValidationResultIdentifier")

    ns_1 = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            expectation_suite_name="asset.quarantine",
        ),
        run_id="20191007T151224.1234Z_prod_100",
        batch_identifier="batch_id",
    )
    my_store.set(ns_1, ExpectationSuiteValidationResult(success=True))
    assert my_store.get(ns_1) == ExpectationSuiteValidationResult(
        success=True, statistics={}, results=[]
    )

    ns_2 = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            expectation_suite_name="asset.quarantine",
        ),
        run_id="20191007T151224.1234Z_prod_200",
        batch_identifier="batch_id",
    )

    my_store.set(ns_2, ExpectationSuiteValidationResult(success=False))
    assert my_store.get(ns_2) == ExpectationSuiteValidationResult(
        success=False, statistics={}, results=[]
    )

    assert set(my_store.list_keys()) == {
        ns_1,
        ns_2,
    }

    """
    What does this test and why?
    A Store should be able to report it's store_backend_id
    which is set when the StoreBackend is instantiated.
    """
    # Check that store_backend_id exists can be read
    assert my_store.store_backend_id is not None
    # Check that store_backend_id is a valid UUID
    assert test_utils.validate_uuid4(my_store.store_backend_id)
Пример #6
0
def validation_result_suite_id():
    return ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            "asset.default"),
        run_id=RunIdentifier(run_name="test_100"),
        batch_identifier="1234",
    )
    def _run_actions(
        self,
        batch,
        expectation_suite_identifier,
        expectation_suite,
        batch_validation_result,
        run_id,
    ):
        """
        Runs all actions configured for this operator on the result of validating one
        batch against one expectation suite.

        If an action fails with an exception, the method does not continue.

        :param batch:
        :param expectation_suite:
        :param batch_validation_result:
        :param run_id:
        :return: a dictionary: {action name -> result returned by the action}
        """
        batch_actions_results = {}
        for action in self.action_list:
            # NOTE: Eugene: 2019-09-23: log the info about the batch and the expectation suite
            logger.debug(
                "Processing validation action with name {}".format(action["name"])
            )

            if isinstance(batch, Validator):
                batch_identifier = batch.active_batch_id
            else:
                batch_identifier = batch.batch_id

            validation_result_id = ValidationResultIdentifier(
                expectation_suite_identifier=expectation_suite_identifier,
                run_id=run_id,
                batch_identifier=batch_identifier,
            )
            try:
                action_result = self.actions[action["name"]].run(
                    validation_result_suite_identifier=validation_result_id,
                    validation_result_suite=batch_validation_result,
                    data_asset=batch,
                    payload=batch_actions_results,
                )

                # add action_result
                batch_actions_results[action["name"]] = (
                    {} if action_result is None else action_result
                )
                batch_actions_results[action["name"]]["class"] = action["action"][
                    "class_name"
                ]

            except Exception as e:
                logger.exception(
                    "Error running action with name {}".format(action["name"])
                )
                raise e

        return batch_actions_results
def test_SlackNotificationAction(data_context):
    renderer = {
        "module_name": "great_expectations.render.renderer.slack_renderer",
        "class_name": "SlackRenderer",
    }
    slack_webhook = "https://hooks.slack.com/services/test/slack/webhook"
    notify_on = "all"

    slack_action = SlackNotificationAction(
        data_context=data_context,
        renderer=renderer,
        slack_webhook=slack_webhook,
        notify_on=notify_on
    )

    validation_result_suite = ExpectationSuiteValidationResult(results=[], success=True,
                                                               statistics={'evaluated_expectations': 0,
                                                                           'successful_expectations': 0,
                                                                           'unsuccessful_expectations': 0,
                                                                           'success_percent': None},
                                                               meta={
                                                                   'great_expectations.__version__': 'v0.8.0__develop',
                                                                   'expectation_suite_name': 'asset.default',
                                                                   'run_id': 'test_100'})

    validation_result_suite_id = ValidationResultIdentifier(expectation_suite_identifier=ExpectationSuiteIdentifier(
        "asset.default"), run_id="test_100", batch_identifier="1234")

    # TODO: improve this test - currently it is verifying a failed call to Slack
    assert slack_action.run(
        validation_result_suite_identifier=validation_result_suite_id,
        validation_result_suite=validation_result_suite,
        data_asset=None
    ) == None
Пример #9
0
def test_ValidationsStore_with_TupleFileSystemStoreBackend(tmp_path_factory):
    path = str(
        tmp_path_factory.mktemp(
            "test_ValidationResultStore_with_TupleFileSystemStoreBackend__dir")
    )
    project_path = str(tmp_path_factory.mktemp("my_dir"))

    my_store = ValidationsStore(
        store_backend={
            "module_name": "great_expectations.data_context.store",
            "class_name": "TupleFilesystemStoreBackend",
            "base_directory": "my_store/",
        },
        runtime_environment={"root_directory": path},
    )

    with pytest.raises(TypeError):
        my_store.get("not_a_ValidationResultIdentifier")

    ns_1 = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            "asset.quarantine"),
        run_id="prod-100",
        batch_identifier="batch_id",
    )
    my_store.set(ns_1, ExpectationSuiteValidationResult(success=True))
    assert my_store.get(ns_1) == ExpectationSuiteValidationResult(
        success=True, statistics={}, results=[])

    ns_2 = ValidationResultIdentifier.from_tuple((
        "asset",
        "quarantine",
        "prod-20",
        datetime.datetime.now(datetime.timezone.utc),
        "batch_id",
    ))
    my_store.set(ns_2, ExpectationSuiteValidationResult(success=False))
    assert my_store.get(ns_2) == ExpectationSuiteValidationResult(
        success=False, statistics={}, results=[])

    print(my_store.list_keys())
    assert set(my_store.list_keys()) == {
        ns_1,
        ns_2,
    }

    print(gen_directory_tree_str(path))
    assert (gen_directory_tree_str(path) == """\
test_ValidationResultStore_with_TupleFileSystemStoreBackend__dir0/
    my_store/
        asset/
            quarantine/
                prod-100/
                    20190926T134241.000000Z/
                        batch_id.json
                prod-20/
                    20190926T134241.000000Z/
                        batch_id.json
""")
Пример #10
0
def test_HtmlSiteStore_S3_backend():
    bucket = "test_validation_store_bucket"
    prefix = "test/prefix"

    # create a bucket in Moto's mock AWS environment
    conn = boto3.resource("s3", region_name="us-east-1")
    conn.create_bucket(Bucket=bucket)

    my_store = HtmlSiteStore(store_backend={
        "class_name": "TupleS3StoreBackend",
        "bucket": bucket,
        "prefix": prefix,
    })

    with pytest.raises(TypeError):
        my_store.get("not_a_ValidationResultIdentifier")

    ns_1 = SiteSectionIdentifier(
        site_section_name="validations",
        resource_identifier=ValidationResultIdentifier(
            expectation_suite_identifier=ExpectationSuiteIdentifier(
                expectation_suite_name="asset.quarantine", ),
            run_id="20191007T151224.1234Z_prod_100",
            batch_identifier="1234",
        ),
    )
    my_store.set(ns_1, "aaa")

    ns_2 = SiteSectionIdentifier(
        site_section_name="expectations",
        resource_identifier=ExpectationSuiteIdentifier(
            expectation_suite_name="asset.quarantine", ),
    )
    my_store.set(ns_2, "bbb")

    assert set(my_store.list_keys()) == {
        ns_1.resource_identifier,
        ns_2.resource_identifier,
    }

    # This is a special un-store-like method exposed by the HtmlSiteStore
    my_store.write_index_page("index_html_string_content")

    # Verify that internals are working as expected, including the default filepath
    # paths below should include the batch_parameters
    assert {
        s3_object_info["Key"]
        for s3_object_info in boto3.client("s3").list_objects_v2(
            Bucket=bucket, Prefix=prefix)["Contents"]
    } == {
        "test/prefix/index.html",
        "test/prefix/expectations/asset/quarantine.html",
        "test/prefix/validations/asset/quarantine/20191007T151224.1234Z_prod_100/20190926T134241.000000Z/1234.html",
    }

    index_content = (boto3.client("s3").get_object(
        Bucket=bucket,
        Key="test/prefix/index.html")["Body"].read().decode("utf-8"))
    assert index_content == "index_html_string_content"
def test_ValidationResultIdentifier__init__entirely_flat():
    ValidationResultIdentifier(
        "a",
        "b",
        "c",
        "warning",
        "testing-12345",
        coerce_types=True,
    )
def test_ValidationResultIdentifier__init__partially_flat():
    ValidationResultIdentifier(coerce_types=True,
                               **{
                                   "expectation_suite_identifier": {
                                       "data_asset_name": ("a", "b", "c"),
                                       "expectation_suite_name": "hello",
                                   },
                                   "run_id": "testing-12345",
                               })
def test_ValidationResultIdentifier__init__mostly_nested():
    ValidationResultIdentifier(coerce_types=True,
                               **{
                                   "expectation_suite_identifier": {
                                       "data_asset_name": ("a", "b", "c"),
                                       "expectation_suite_name": "warning",
                                   },
                                   "run_id": "testing-12345",
                               })
Пример #14
0
def validation_result_suite_extended_id():
    return ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            "asset.default"),
        run_id=RunIdentifier(run_name="test_100",
                             run_time="Tue May 08 15:14:45 +0800 2012"),
        batch_identifier=BatchIdentifier(batch_identifier="1234",
                                         data_asset_name="asset"),
    )
Пример #15
0
def test_ValidationsStore_with_DatabaseStoreBackend(sa):
    # Use sqlite so we don't require postgres for this test.
    connection_kwargs = {"drivername": "sqlite"}

    # First, demonstrate that we pick up default configuration
    my_store = ValidationsStore(
        store_backend={
            "class_name": "DatabaseStoreBackend",
            "credentials": connection_kwargs,
        }
    )

    with pytest.raises(TypeError):
        my_store.get("not_a_ValidationResultIdentifier")

    ns_1 = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            expectation_suite_name="asset.quarantine",
        ),
        run_id="20191007T151224.1234Z_prod_100",
        batch_identifier="batch_id",
    )
    my_store.set(ns_1, ExpectationSuiteValidationResult(success=True))
    assert my_store.get(ns_1) == ExpectationSuiteValidationResult(
        success=True, statistics={}, results=[]
    )

    ns_2 = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            expectation_suite_name="asset.quarantine",
        ),
        run_id="20191007T151224.1234Z_prod_200",
        batch_identifier="batch_id",
    )

    my_store.set(ns_2, ExpectationSuiteValidationResult(success=False))
    assert my_store.get(ns_2) == ExpectationSuiteValidationResult(
        success=False, statistics={}, results=[]
    )

    assert set(my_store.list_keys()) == {
        ns_1,
        ns_2,
    }
Пример #16
0
 def _convert_tuple_to_resource_identifier(self, tuple_):
     if tuple_[0] == "expectations":
         resource_identifier = ExpectationSuiteIdentifier(*tuple_[1])
     elif tuple_[0] == "validations":
         resource_identifier = ValidationResultIdentifier(*tuple_[1])
     else:
         raise Exception("unknown section name: " + tuple_[0])
     new_identifier = SiteSectionIdentifier(
         site_section_name=tuple_[0],
         resource_identifier=resource_identifier)
     return new_identifier
def test_ValidationResultIdentifier__init__mostly_nested_with_typed_child():
    ValidationResultIdentifier(coerce_types=True,
                               **{
                                   "expectation_suite_identifier": {
                                       "data_asset_name":
                                       DataAssetIdentifier("a", "b", "c"),
                                       "expectation_suite_name":
                                       "quarantine",
                                   },
                                   "run_id": "testing-12345",
                               })
def ge_validation_result_suite_id() -> ValidationResultIdentifier:
    validation_result_suite_id = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            "asset.default"),
        run_id=RunIdentifier(
            run_name="test_100",
            run_time=datetime.fromtimestamp(1640701702, tz=timezone.utc),
        ),
        batch_identifier="010ef8c1cd417910b971f4468f024ec5",
    )

    return validation_result_suite_id
def test_SlackNotificationAction(data_context):

    renderer = {
        "module_name": "great_expectations.render.renderer.slack_renderer",
        "class_name": "SlackRenderer",
    }
    slack_webhook = "https://hooks.slack.com/services/test/slack/webhook"
    notify_on = "all"

    slack_action = SlackNotificationAction(data_context=data_context,
                                           renderer=renderer,
                                           slack_webhook=slack_webhook,
                                           notify_on=notify_on)

    validation_result_suite = {
        'results': [],
        'success': True,
        'statistics': {
            'evaluated_expectations': 0,
            'successful_expectations': 0,
            'unsuccessful_expectations': 0,
            'success_percent': None
        },
        'meta': {
            'great_expectations.__version__': 'v0.8.0__develop',
            'data_asset_name': {
                'datasource': 'x',
                'generator': 'y',
                'generator_asset': 'z'
            },
            'expectation_suite_name': 'default',
            'run_id': '2019-09-25T060538.829112Z'
        }
    }

    validation_result_suite_id = ValidationResultIdentifier(
        **{
            'expectation_suite_identifier': {
                'data_asset_name': {
                    'datasource': 'x',
                    'generator': 'y',
                    'generator_asset': 'z'
                },
                'expectation_suite_name': 'default'
            },
            'run_id': 'test_100'
        })

    #TODO: improve this test - currently it is verifying a failed call to Slack
    assert slack_action.run(
        validation_result_suite_identifier=validation_result_suite_id,
        validation_result_suite=validation_result_suite,
        data_asset=None) == None
def test_ValidationResultIdentifier__init__nested_except_the_top_layer():
    ValidationResultIdentifier(
        {
            "data_asset_name": {
                "datasource": "a",
                "generator": "b",
                "generator_asset": "c",
            },
            "expectation_suite_name": "hello",
        },
        "testing-12345",
        coerce_types=True)
Пример #21
0
def test_resource_key_passes_run_name_filter():
    resource_key = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier("test_suite"),
        run_id=RunIdentifier(run_name="foofooprofilingfoo"),
        batch_identifier="f14c3d2f6e8028c2db0c25edabdb0d61",
    )

    assert (resource_key_passes_run_name_filter(
        resource_key, run_name_filter={"equals": "profiling"}) is False)
    assert (resource_key_passes_run_name_filter(
        resource_key, run_name_filter={"equals": "foofooprofilingfoo"}) is
            True)

    assert (resource_key_passes_run_name_filter(
        resource_key, run_name_filter={"not_equals": "profiling"}) is True)
    assert (resource_key_passes_run_name_filter(
        resource_key, run_name_filter={"not_equals": "foofooprofilingfoo"}) is
            False)

    assert (resource_key_passes_run_name_filter(
        resource_key, run_name_filter={"includes": "profiling"}) is True)
    assert (resource_key_passes_run_name_filter(
        resource_key, run_name_filter={"includes": "foobar"}) is False)

    assert (resource_key_passes_run_name_filter(
        resource_key, run_name_filter={"not_includes": "foobar"}) is True)
    assert (resource_key_passes_run_name_filter(
        resource_key, run_name_filter={"not_includes": "profiling"}) is False)

    assert (resource_key_passes_run_name_filter(
        resource_key,
        run_name_filter={"matches_regex": "(foo){2}profiling("
                         "foo)+"},
    ) is True)
    assert (resource_key_passes_run_name_filter(
        resource_key,
        run_name_filter={"matches_regex": "(foo){3}profiling("
                         "foo)+"},
    ) is False)
    with pytest.warns(DeprecationWarning):
        assert (resource_key_passes_run_name_filter(
            resource_key, run_name_filter={"eq": "profiling"}) is False)
        assert (resource_key_passes_run_name_filter(
            resource_key, run_name_filter={"eq": "foofooprofilingfoo"}) is
                True)
    with pytest.warns(DeprecationWarning):
        assert (resource_key_passes_run_name_filter(
            resource_key, run_name_filter={"ne": "profiling"}) is True)
        assert (resource_key_passes_run_name_filter(
            resource_key, run_name_filter={"ne": "foofooprofilingfoo"}) is
                False)
def test_resource_identifier_to_string():

    assert ValidationResultIdentifier(
        coerce_types=True,
        **{
            "expectation_suite_identifier": {
                "data_asset_name": {
                    "datasource": "a",
                    "generator": "b",
                    "generator_asset": "c",
                },
                "expectation_suite_name": "hello",
            },
            "run_id": "testing-12345",
        }).to_string(
        ) == "ValidationResultIdentifier.a.b.c.hello.testing-12345"
def test_SlackNotificationAction(data_context_parameterized_expectation_suite):
    renderer = {
        "module_name": "great_expectations.render.renderer.slack_renderer",
        "class_name": "SlackRenderer",
    }
    slack_webhook = "https://hooks.slack.com/services/test/slack/webhook"
    notify_on = "all"

    slack_action = SlackNotificationAction(
        data_context=data_context_parameterized_expectation_suite,
        renderer=renderer,
        slack_webhook=slack_webhook,
        notify_on=notify_on,
    )

    validation_result_suite = ExpectationSuiteValidationResult(
        results=[],
        success=True,
        statistics={
            "evaluated_expectations": 0,
            "successful_expectations": 0,
            "unsuccessful_expectations": 0,
            "success_percent": None,
        },
        meta={
            "great_expectations_version": "v0.8.0__develop",
            "expectation_suite_name": "asset.default",
            "run_id": "test_100",
        },
    )

    validation_result_suite_id = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            "asset.default"),
        run_id="test_100",
        batch_identifier="1234",
    )

    # TODO: improve this test - currently it is verifying a failed call to Slack. It returns a "empty" payload
    assert slack_action.run(
        validation_result_suite_identifier=validation_result_suite_id,
        validation_result_suite=validation_result_suite,
        data_asset=None,
    ) == {
        "slack_notification_result": None
    }
def test_ValidationResultIdentifier__init__totally_nested():
    my_id = ValidationResultIdentifier(coerce_types=True,
                                       **{
                                           "expectation_suite_identifier": {
                                               "data_asset_name": {
                                                   "datasource": "a",
                                                   "generator": "b",
                                                   "generator_asset": "c",
                                               },
                                               "expectation_suite_name":
                                               "failure",
                                           },
                                           "run_id": "testing-12345",
                                       })

    assert my_id.to_string(
    ) == "ValidationResultIdentifier.a.b.c.failure.testing-12345"
def test_TupleGCSStoreBackend_base_public_path():
    """
    What does this test and why?

    the base_public_path parameter allows users to point to a custom DNS when hosting Data docs.

    This test will exercise the get_url_for_key method twice to see that we are getting the expected url,
    with or without base_public_path
    """
    bucket = "leakybucket"
    prefix = "this_is_a_test_prefix"
    project = "dummy-project"
    base_public_path = "http://www.test.com/"

    with patch("google.cloud.storage.Client",
               autospec=True) as mock_gcs_client:
        mock_client = mock_gcs_client.return_value
        mock_bucket = mock_client.get_bucket.return_value
        mock_blob = mock_bucket.blob.return_value

        my_store_with_base_public_path = TupleGCSStoreBackend(
            filepath_template=None,
            bucket=bucket,
            prefix=prefix,
            project=project,
            base_public_path=base_public_path,
        )

        my_store_with_base_public_path.set(("BBB", ),
                                           b"bbb",
                                           content_encoding=None,
                                           content_type="image/png")

    run_id = RunIdentifier("my_run_id", datetime.datetime.utcnow())
    key = ValidationResultIdentifier(
        ExpectationSuiteIdentifier(expectation_suite_name="my_suite_name"),
        run_id,
        "my_batch_id",
    )
    run_time_string = run_id.to_tuple()[1]

    url = my_store_with_base_public_path.get_public_url_for_key(key.to_tuple())
    assert (
        url == "http://www.test.com/leakybucket" +
        f"/this_is_a_test_prefix/my_suite_name/my_run_id/{run_time_string}/my_batch_id"
    )
def test_parse_string_to_data_context_resource_identifier():

    assert parse_string_to_data_context_resource_identifier(
        "DataAssetIdentifier.A.B.C") == DataAssetIdentifier("A", "B", "C")

    assert parse_string_to_data_context_resource_identifier(
        "ValidationResultIdentifier.a.b.c.hello.testing-12345"
    ) == ValidationResultIdentifier(coerce_types=True,
                                    **{
                                        "expectation_suite_identifier": {
                                            "data_asset_name": {
                                                "datasource": "a",
                                                "generator": "b",
                                                "generator_asset": "c",
                                            },
                                            "expectation_suite_name": "hello",
                                        },
                                        "run_id": "testing-12345"
                                    })
def test_MicrosoftTeams_validation_results_with_datadocs():
    validation_result_suite = ExpectationSuiteValidationResult(
        results=[],
        success=True,
        statistics={
            "evaluated_expectations": 0,
            "successful_expectations": 0,
            "unsuccessful_expectations": 0,
            "success_percent": None,
        },
        meta={
            "great_expectations_version": "v0.8.0__develop",
            "expectation_suite_name": "asset.default",
            "run_id": "test_100",
        },
    )

    validation_result_suite_identifier = ValidationResultIdentifier(
        expectation_suite_identifier=ExpectationSuiteIdentifier(
            "asset.default"),
        run_id=RunIdentifier(run_name="test_100",
                             run_time="Tue May 08 15:14:45 +0800 2012"),
        batch_identifier=BatchIdentifier(batch_identifier="1234",
                                         data_asset_name="asset"),
    )

    data_docs_pages = {"local_site": "file:///localsite/index.html"}

    rendered_output = MicrosoftTeamsRenderer().render(
        validation_result_suite, validation_result_suite_identifier,
        data_docs_pages)

    expected_output = {
        "attachments": [{
            "content": {
                "$schema":
                "http://adaptivecards.io/schemas/adaptive-card.json",
                "actions": [{
                    "title": "Open data docs",
                    "type": "Action.OpenUrl",
                    "url": "file:///localsite/index.html",
                }],
                "body": [
                    {
                        "height":
                        "auto",
                        "items": [{
                            "columns": [{
                                "items": [
                                    {
                                        "size": "large",
                                        "text": "Validation "
                                        "results",
                                        "type": "TextBlock",
                                        "weight": "bolder",
                                        "wrap": True,
                                    },
                                    {
                                        "isSubtle": True,
                                        "spacing": "none",
                                        "text": "May "
                                        "08 "
                                        "2012 "
                                        "07:14:45",
                                        "type": "TextBlock",
                                        "wrap": True,
                                    },
                                ],
                                "type":
                                "Column",
                                "width":
                                "stretch",
                            }],
                            "type":
                            "ColumnSet",
                        }],
                        "separator":
                        True,
                        "type":
                        "Container",
                    },
                    {
                        "height":
                        "auto",
                        "items": [
                            {
                                "color": "good",
                                "horizontalAlignment": "left",
                                "text": "**Batch validation "
                                "status:** Success "
                                "!!!",
                                "type": "TextBlock",
                            },
                            {
                                "horizontalAlignment": "left",
                                "text": "**Data asset "
                                "name:** asset",
                                "type": "TextBlock",
                            },
                            {
                                "horizontalAlignment": "left",
                                "text": "**Expectation "
                                "suite name:** "
                                "asset.default",
                                "type": "TextBlock",
                            },
                            {
                                "horizontalAlignment": "left",
                                "text": "**Run name:** "
                                "test_100",
                                "type": "TextBlock",
                            },
                            {
                                "horizontalAlignment": "left",
                                "text": "**Batch ID:** 1234",
                                "type": "TextBlock",
                            },
                            {
                                "horizontalAlignment":
                                "left",
                                "text":
                                "**Summary:** *0* "
                                "of *0* "
                                "expectations were "
                                "met",
                                "type":
                                "TextBlock",
                            },
                        ],
                        "separator":
                        True,
                        "type":
                        "Container",
                    },
                ],
                "type":
                "AdaptiveCard",
                "version":
                "1.0",
            },
            "contentType":
            "application/vnd.microsoft.card.adaptive",
        }],
        "type":
        "message",
    }

    assert rendered_output == expected_output
    def run(
        self,
        assets_to_validate,
        run_id=None,
        evaluation_parameters=None,
        run_name=None,
        run_time=None,
        result_format=None,
    ):
        assert not (run_id and run_name) and not (
            run_id and run_time
        ), "Please provide either a run_id or run_name and/or run_time."
        if isinstance(run_id, str) and not run_name:
            warnings.warn(
                "String run_ids will be deprecated in the future. Please provide a run_id of type "
                "RunIdentifier(run_name=None, run_time=None), or a dictionary containing run_name "
                "and run_time (both optional). Instead of providing a run_id, you may also provide"
                "run_name and run_time separately.",
                DeprecationWarning,
            )
            try:
                run_time = parse(run_id)
            except (ParserError, TypeError):
                pass
            run_id = RunIdentifier(run_name=run_id, run_time=run_time)
        elif isinstance(run_id, dict):
            run_id = RunIdentifier(**run_id)
        elif not isinstance(run_id, RunIdentifier):
            run_id = RunIdentifier(run_name=run_name, run_time=run_time)

        run_results = {}

        for item in assets_to_validate:
            run_result_obj = {}
            batch = self._build_batch_from_item(item)
            expectation_suite_identifier = ExpectationSuiteIdentifier(
                expectation_suite_name=batch._expectation_suite.expectation_suite_name
            )
            validation_result_id = ValidationResultIdentifier(
                batch_identifier=batch.batch_id,
                expectation_suite_identifier=expectation_suite_identifier,
                run_id=run_id,
            )
            batch_validation_result = batch.validate(
                run_id=run_id,
                result_format=result_format if result_format else self.result_format,
                evaluation_parameters=evaluation_parameters,
            )
            run_result_obj["validation_result"] = batch_validation_result
            batch_actions_results = self._run_actions(
                batch,
                expectation_suite_identifier,
                batch._expectation_suite,
                batch_validation_result,
                run_id,
            )
            run_result_obj["actions_results"] = batch_actions_results
            run_results[validation_result_id] = run_result_obj

        return ValidationOperatorResult(
            run_id=run_id,
            run_results=run_results,
            validation_operator_config=self.validation_operator_config,
            evaluation_parameters=evaluation_parameters,
        )
    def run(
        self,
        assets_to_validate,
        run_id=None,
        base_expectation_suite_name=None,
        evaluation_parameters=None,
        run_name=None,
        run_time=None,
        result_format=None,
    ):
        assert not (run_id and run_name) and not (
            run_id and run_time
        ), "Please provide either a run_id or run_name and/or run_time."
        if isinstance(run_id, str) and not run_name:
            warnings.warn(
                "String run_ids will be deprecated in the future. Please provide a run_id of type "
                "RunIdentifier(run_name=None, run_time=None), or a dictionary containing run_name "
                "and run_time (both optional). Instead of providing a run_id, you may also provide"
                "run_name and run_time separately.",
                DeprecationWarning,
            )
            try:
                run_time = parse(run_id)
            except (ParserError, TypeError):
                pass
            run_id = RunIdentifier(run_name=run_id, run_time=run_time)
        elif isinstance(run_id, dict):
            run_id = RunIdentifier(**run_id)
        elif not isinstance(run_id, RunIdentifier):
            run_id = RunIdentifier(run_name=run_name, run_time=run_time)

        if base_expectation_suite_name is None:
            if self.base_expectation_suite_name is None:
                raise ValueError(
                    "base_expectation_suite_name must be configured in the validation operator or passed at runtime"
                )
            base_expectation_suite_name = self.base_expectation_suite_name

        run_results = {}

        for item in assets_to_validate:
            batch = self._build_batch_from_item(item)

            batch_id = batch.batch_id
            run_id = run_id

            assert not batch_id is None
            assert not run_id is None

            failure_expectation_suite_identifier = ExpectationSuiteIdentifier(
                expectation_suite_name=base_expectation_suite_name
                + self.expectation_suite_name_suffixes[0]
            )

            failure_validation_result_id = ValidationResultIdentifier(
                expectation_suite_identifier=failure_expectation_suite_identifier,
                run_id=run_id,
                batch_identifier=batch_id,
            )

            failure_expectation_suite = None
            try:
                failure_expectation_suite = self.data_context.stores[
                    self.data_context.expectations_store_name
                ].get(failure_expectation_suite_identifier)

            # NOTE : Abe 2019/09/17 : I'm concerned that this may be too permissive, since
            # it will catch any error in the Store, not just KeyErrors. In the longer term, a better
            # solution will be to have the Stores catch other known errors and raise KeyErrors,
            # so that methods like this can catch and handle a single error type.
            except Exception:
                logger.debug(
                    "Failure expectation suite not found: {}".format(
                        failure_expectation_suite_identifier
                    )
                )

            if failure_expectation_suite:
                failure_run_result_obj = {"expectation_suite_severity_level": "failure"}
                failure_validation_result = batch.validate(
                    failure_expectation_suite,
                    result_format=result_format
                    if result_format
                    else self.result_format,
                    evaluation_parameters=evaluation_parameters,
                )
                failure_run_result_obj["validation_result"] = failure_validation_result
                failure_actions_results = self._run_actions(
                    batch,
                    failure_expectation_suite_identifier,
                    failure_expectation_suite,
                    failure_validation_result,
                    run_id,
                )
                failure_run_result_obj["actions_results"] = failure_actions_results
                run_results[failure_validation_result_id] = failure_run_result_obj

                if not failure_validation_result.success and self.stop_on_first_error:
                    break

            warning_expectation_suite_identifier = ExpectationSuiteIdentifier(
                expectation_suite_name=base_expectation_suite_name
                + self.expectation_suite_name_suffixes[1]
            )

            warning_validation_result_id = ValidationResultIdentifier(
                expectation_suite_identifier=warning_expectation_suite_identifier,
                run_id=run_id,
                batch_identifier=batch.batch_id,
            )

            warning_expectation_suite = None
            try:
                warning_expectation_suite = self.data_context.stores[
                    self.data_context.expectations_store_name
                ].get(warning_expectation_suite_identifier)
            except Exception:
                logger.debug(
                    "Warning expectation suite not found: {}".format(
                        warning_expectation_suite_identifier
                    )
                )

            if warning_expectation_suite:
                warning_run_result_obj = {"expectation_suite_severity_level": "warning"}
                warning_validation_result = batch.validate(
                    warning_expectation_suite,
                    result_format=result_format
                    if result_format
                    else self.result_format,
                    evaluation_parameters=evaluation_parameters,
                )
                warning_run_result_obj["validation_result"] = warning_validation_result
                warning_actions_results = self._run_actions(
                    batch,
                    warning_expectation_suite_identifier,
                    warning_expectation_suite,
                    warning_validation_result,
                    run_id,
                )
                warning_run_result_obj["actions_results"] = warning_actions_results
                run_results[warning_validation_result_id] = warning_run_result_obj

        validation_operator_result = ValidationOperatorResult(
            run_id=run_id,
            run_results=run_results,
            validation_operator_config=self.validation_operator_config,
            evaluation_parameters=evaluation_parameters,
            success=all(
                [
                    run_result_obj["validation_result"].success
                    for run_result_obj in run_results.values()
                ]
            ),
        )

        if self.slack_webhook:
            if (
                self.notify_on == "all"
                or self.notify_on == "success"
                and validation_operator_result.success
                or self.notify_on == "failure"
                and not validation_operator_result.success
            ):
                slack_query = self._build_slack_query(
                    validation_operator_result=validation_operator_result
                )
                send_slack_notification(
                    query=slack_query, slack_webhook=self.slack_webhook
                )

        return validation_operator_result
def test_TupleGCSStoreBackend():
    # pytest.importorskip("google-cloud-storage")
    """
    What does this test test and why?

    Since no package like moto exists for GCP services, we mock the GCS client
    and assert that the store backend makes the right calls for set, get, and list.

    TODO : One option may be to have a GCS Store in Docker, which can be use to "actually" run these tests.
    """

    bucket = "leakybucket"
    prefix = "this_is_a_test_prefix"
    project = "dummy-project"
    base_public_path = "http://www.test.com/"

    with patch("google.cloud.storage.Client",
               autospec=True) as mock_gcs_client:

        mock_client = mock_gcs_client.return_value
        mock_bucket = mock_client.get_bucket.return_value
        mock_blob = mock_bucket.blob.return_value

        my_store = TupleGCSStoreBackend(
            filepath_template="my_file_{0}",
            bucket=bucket,
            prefix=prefix,
            project=project,
        )

        my_store.set(("AAA", ), "aaa", content_type="text/html")

        mock_gcs_client.assert_called_with("dummy-project")
        mock_client.get_bucket.assert_called_with("leakybucket")
        mock_bucket.blob.assert_called_with(
            "this_is_a_test_prefix/my_file_AAA")
        # mock_bucket.blob.assert_any_call("this_is_a_test_prefix/.ge_store_backend_id")
        mock_blob.upload_from_string.assert_called_with(
            b"aaa", content_type="text/html")

    with patch("google.cloud.storage.Client",
               autospec=True) as mock_gcs_client:
        mock_client = mock_gcs_client.return_value
        mock_bucket = mock_client.get_bucket.return_value
        mock_blob = mock_bucket.blob.return_value

        my_store_with_no_filepath_template = TupleGCSStoreBackend(
            filepath_template=None,
            bucket=bucket,
            prefix=prefix,
            project=project)

        my_store_with_no_filepath_template.set(("AAA", ),
                                               b"aaa",
                                               content_encoding=None,
                                               content_type="image/png")

        mock_gcs_client.assert_called_with("dummy-project")
        mock_client.get_bucket.assert_called_with("leakybucket")
        mock_bucket.blob.assert_called_with("this_is_a_test_prefix/AAA")
        # mock_bucket.blob.assert_any_call("this_is_a_test_prefix/.ge_store_backend_id")
        mock_blob.upload_from_string.assert_called_with(
            b"aaa", content_type="image/png")

    with patch("google.cloud.storage.Client",
               autospec=True) as mock_gcs_client:

        mock_client = mock_gcs_client.return_value
        mock_bucket = mock_client.get_bucket.return_value
        mock_blob = mock_bucket.get_blob.return_value
        mock_str = mock_blob.download_as_string.return_value

        my_store.get(("BBB", ))

        mock_gcs_client.assert_called_once_with("dummy-project")
        mock_client.get_bucket.assert_called_once_with("leakybucket")
        mock_bucket.get_blob.assert_called_once_with(
            "this_is_a_test_prefix/my_file_BBB")
        mock_blob.download_as_string.assert_called_once()
        mock_str.decode.assert_called_once_with("utf-8")

    with patch("google.cloud.storage.Client",
               autospec=True) as mock_gcs_client:

        mock_client = mock_gcs_client.return_value

        my_store.list_keys()

        mock_client.list_blobs.assert_called_once_with(
            "leakybucket", prefix="this_is_a_test_prefix")

        my_store.remove_key("leakybucket")

        from google.cloud.exceptions import NotFound

        try:
            mock_client.get_bucket.assert_called_once_with("leakybucket")
        except NotFound:
            pass

    with patch("google.cloud.storage.Client",
               autospec=True) as mock_gcs_client:
        mock_gcs_client.side_effect = InvalidKeyError(
            "Hi I am an InvalidKeyError")
        with pytest.raises(InvalidKeyError):
            my_store.get(("non_existent_key", ))

    run_id = RunIdentifier("my_run_id", datetime.datetime.utcnow())
    key = ValidationResultIdentifier(
        ExpectationSuiteIdentifier(expectation_suite_name="my_suite_name"),
        run_id,
        "my_batch_id",
    )
    run_time_string = run_id.to_tuple()[1]

    url = my_store_with_no_filepath_template.get_url_for_key(key.to_tuple())
    assert (
        url == "https://storage.googleapis.com/leakybucket" +
        f"/this_is_a_test_prefix/my_suite_name/my_run_id/{run_time_string}/my_batch_id"
    )