From 9c7882957413df6b34cc091dd76026c1a3f0894a Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Mon, 12 Aug 2019 09:19:30 -0700 Subject: [PATCH 1/6] Blob Storage read done, missing write and delete --- .../botbuilder/azure/blob_storage.py | 74 +++++++++++++++++++ libraries/botbuilder-azure/setup.py | 1 + .../botbuilder/core/storage.py | 2 +- 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 libraries/botbuilder-azure/botbuilder/azure/blob_storage.py diff --git a/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py b/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py new file mode 100644 index 000000000..1289c5426 --- /dev/null +++ b/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py @@ -0,0 +1,74 @@ +import json +from typing import List + +from azure.storage.blob import BlockBlobService, Blob +from botbuilder.core import Storage, StoreItem + +# TODO: sanitize_blob_name + + +class BlobStorageSettings: + def __init__( + self, + container_name: str, + account_name: str = "", + account_key: str = "", + connection_string: str = "", + ): + self.container_name = container_name + self.account_name = account_name + self.account_key = account_key + self.connection_string = connection_string + + +class BlobStorage(Storage): + def __init__(self, settings: BlobStorageSettings): + if settings.connection_string: + client = BlockBlobService(connection_string=settings.connection_string) + elif settings.account_name and settings.account_key: + client = BlockBlobService( + account_name=settings.account_name, account_key=settings.account_key + ) + else: + raise Exception( + "Connection string should be provided if there are no account name and key" + ) + + self.client = client + self.settings = settings + + async def read(self, keys: List[str]): + if not keys: + raise Exception("Please provide at least one key to read from storage.") + + self.client.create_container(self.settings.container_name) + items = {} + + for key in keys: + if self.client.exists( + container_name=self.settings.container_name, blob_name=key + ): + items[key] = self.client.get_blob_to_text( + container_name=self.settings.container_name, blob_name=key + ) + + return items + + async def write(self, changes): + pass + + async def delete(self, keys: List[str]): + pass + + def _blob_to_store_item(self, blob: Blob) -> StoreItem: + item = json.loads(blob.content) + item["e_tag"] = blob.properties.etag + item["id"] = blob.name + return StoreItem(**item) + + def _store_item_to_str(self, item: StoreItem) -> str: + if hasattr(item, "e_tag"): + delattr(item, "e_tag") + if hasattr(item, "id"): + delattr(item, "id") + return str(item) diff --git a/libraries/botbuilder-azure/setup.py b/libraries/botbuilder-azure/setup.py index 2ebb15f3b..74c2ef81e 100644 --- a/libraries/botbuilder-azure/setup.py +++ b/libraries/botbuilder-azure/setup.py @@ -6,6 +6,7 @@ REQUIRES = [ "azure-cosmos>=3.0.0", + "azure-storage-blob>=2.1.0", "botbuilder-schema>=4.4.0b1", "botframework-connector>=4.4.0b1", ] diff --git a/libraries/botbuilder-core/botbuilder/core/storage.py b/libraries/botbuilder-core/botbuilder/core/storage.py index 0d5b50029..8c9a1f1ab 100644 --- a/libraries/botbuilder-core/botbuilder/core/storage.py +++ b/libraries/botbuilder-core/botbuilder/core/storage.py @@ -49,7 +49,7 @@ def __str__(self): output = ( "{" + ",".join( - [f" '{attr}': '{getattr(self, attr)}'" for attr in non_magic_attributes] + [f' "{attr}": "{getattr(self, attr)}"' for attr in non_magic_attributes] ) + " }" ) From bd901295679fe6da31d1f5c27ba4a6cd9eb27f0c Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Mon, 12 Aug 2019 12:06:04 -0700 Subject: [PATCH 2/6] Blob storage implemented --- .../botbuilder/azure/__init__.py | 10 +- .../botbuilder/azure/blob_storage.py | 51 ++-- .../tests/test_blob_storage.py | 226 ++++++++++++++++++ 3 files changed, 271 insertions(+), 16 deletions(-) create mode 100644 libraries/botbuilder-azure/tests/test_blob_storage.py diff --git a/libraries/botbuilder-azure/botbuilder/azure/__init__.py b/libraries/botbuilder-azure/botbuilder/azure/__init__.py index 1f66a5f4f..54dea209d 100644 --- a/libraries/botbuilder-azure/botbuilder/azure/__init__.py +++ b/libraries/botbuilder-azure/botbuilder/azure/__init__.py @@ -7,5 +7,13 @@ from .about import __version__ from .cosmosdb_storage import CosmosDbStorage, CosmosDbConfig, CosmosDbKeyEscape +from .blob_storage import BlobStorage, BlobStorageSettings -__all__ = ["CosmosDbStorage", "CosmosDbConfig", "CosmosDbKeyEscape", "__version__"] +__all__ = [ + "BlobStorage", + "BlobStorageSettings", + "CosmosDbStorage", + "CosmosDbConfig", + "CosmosDbKeyEscape", + "__version__", +] diff --git a/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py b/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py index 1289c5426..c25a2a286 100644 --- a/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py +++ b/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py @@ -1,7 +1,7 @@ import json -from typing import List +from typing import Dict, List -from azure.storage.blob import BlockBlobService, Blob +from azure.storage.blob import BlockBlobService, Blob, PublicAccess from botbuilder.core import Storage, StoreItem # TODO: sanitize_blob_name @@ -37,38 +37,59 @@ def __init__(self, settings: BlobStorageSettings): self.client = client self.settings = settings - async def read(self, keys: List[str]): + async def read(self, keys: List[str]) -> Dict[str, object]: if not keys: raise Exception("Please provide at least one key to read from storage.") self.client.create_container(self.settings.container_name) + self.client.set_container_acl( + self.settings.container_name, public_access=PublicAccess.Container + ) items = {} for key in keys: if self.client.exists( container_name=self.settings.container_name, blob_name=key ): - items[key] = self.client.get_blob_to_text( - container_name=self.settings.container_name, blob_name=key + items[key] = self._blob_to_store_item( + self.client.get_blob_to_text( + container_name=self.settings.container_name, blob_name=key + ) ) return items - async def write(self, changes): - pass + async def write(self, changes: Dict[str, StoreItem]): + self.client.create_container(self.settings.container_name) + self.client.set_container_acl( + self.settings.container_name, public_access=PublicAccess.Container + ) + + for name, item in changes: + e_tag = None if "e_tag" not in item else item["e_tag"] + self.client.create_blob_from_text( + container_name=self.settings.container_name, + blob_name=name, + text=str(item), + if_match=e_tag, + ) async def delete(self, keys: List[str]): - pass + if keys is None: + raise Exception("BlobStorage.delete: keys parameter can't be null") + + self.client.create_container(self.settings.container_name) + self.client.set_container_acl( + self.settings.container_name, public_access=PublicAccess.Container + ) + + for key in keys: + self.client.delete_blob( + container_name=self.settings.container_name, blob_name=key + ) def _blob_to_store_item(self, blob: Blob) -> StoreItem: item = json.loads(blob.content) item["e_tag"] = blob.properties.etag item["id"] = blob.name return StoreItem(**item) - - def _store_item_to_str(self, item: StoreItem) -> str: - if hasattr(item, "e_tag"): - delattr(item, "e_tag") - if hasattr(item, "id"): - delattr(item, "id") - return str(item) diff --git a/libraries/botbuilder-azure/tests/test_blob_storage.py b/libraries/botbuilder-azure/tests/test_blob_storage.py new file mode 100644 index 000000000..213838259 --- /dev/null +++ b/libraries/botbuilder-azure/tests/test_blob_storage.py @@ -0,0 +1,226 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import pytest +from botbuilder.core import StoreItem +from botbuilder.azure import BlobStorage, BlobStorageSettings + +# local blob emulator instance blob +BLOB_STORAGE_SETTINGS = BlobStorageSettings( + connection_string=( + "AccountName=devstoreaccount1;" + "AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr" + "/KBHBeksoGMGw==;DefaultEndpointsProtocol=http;" + "BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;" + "QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;" + "TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;" + ), + container_name="test", +) +EMULATOR_RUNNING = False + + +async def reset(): + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + storage.client.delete_container(container_name=BLOB_STORAGE_SETTINGS.container_name) + + +class SimpleStoreItem(StoreItem): + def __init__(self, counter=1, e_tag="*"): + super(SimpleStoreItem, self).__init__() + self.counter = counter + self.e_tag = e_tag + + +class TestCosmosDbStorage: + @pytest.mark.asyncio + async def test_cosmos_storage_init_should_error_without_cosmos_db_config(self): + try: + BlobStorage(BlobStorageSettings()) # pylint: disable=no-value-for-parameter + except Exception as error: + assert error + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_read_should_return_data_with_valid_key(self): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write({"user": SimpleStoreItem()}) + + data = await storage.read(["user"]) + assert "user" in data + assert data["user"].counter == 1 + assert len(data.keys()) == 1 + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_read_update_should_return_new_etag(self): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write({"test": SimpleStoreItem(counter=1)}) + data_result = await storage.read(["test"]) + data_result["test"].counter = 2 + await storage.write(data_result) + data_updated = await storage.read(["test"]) + assert data_updated["test"].counter == 2 + assert data_updated["test"].e_tag != data_result["test"].e_tag + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_read_with_invalid_key_should_return_empty_dict(self): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + data = await storage.read(["test"]) + + assert isinstance(data, dict) + assert not data.keys() + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_read_no_key_should_throw(self): + try: + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.read([]) + except Exception as error: + assert error + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_write_should_add_new_value(self): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write({"user": SimpleStoreItem(counter=1)}) + + data = await storage.read(["user"]) + assert "user" in data + assert data["user"].counter == 1 + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_write_should_overwrite_when_new_e_tag_is_an_asterisk( + self + ): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write({"user": SimpleStoreItem()}) + + await storage.write({"user": SimpleStoreItem(counter=10, e_tag="*")}) + data = await storage.read(["user"]) + assert data["user"].counter == 10 + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_write_batch_operation(self): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write( + { + "batch1": SimpleStoreItem(counter=1), + "batch2": SimpleStoreItem(counter=1), + "batch3": SimpleStoreItem(counter=1), + } + ) + data = await storage.read(["batch1", "batch2", "batch3"]) + assert len(data.keys()) == 3 + assert data["batch1"] + assert data["batch2"] + assert data["batch3"] + assert data["batch1"].counter == 1 + assert data["batch2"].counter == 1 + assert data["batch3"].counter == 1 + assert data["batch1"].e_tag + assert data["batch2"].e_tag + assert data["batch3"].e_tag + await storage.delete(["batch1", "batch2", "batch3"]) + data = await storage.read(["batch1", "batch2", "batch3"]) + assert not data.keys() + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_write_crazy_keys_work(self): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + crazy_key = '!@#$%^&*()_+??><":QASD~`' + await storage.write({crazy_key: SimpleStoreItem(counter=1)}) + data = await storage.read([crazy_key]) + assert len(data.keys()) == 1 + assert data[crazy_key] + assert data[crazy_key].counter == 1 + assert data[crazy_key].e_tag + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_delete_should_delete_according_cached_data(self): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write({"test": SimpleStoreItem()}) + try: + await storage.delete(["test"]) + except Exception as error: + raise error + else: + data = await storage.read(["test"]) + + assert isinstance(data, dict) + assert not data.keys() + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_delete_should_delete_multiple_values_when_given_multiple_valid_keys( + self + ): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write({"test": SimpleStoreItem(), "test2": SimpleStoreItem(2)}) + + await storage.delete(["test", "test2"]) + data = await storage.read(["test", "test2"]) + assert not data.keys() + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_delete_should_delete_values_when_given_multiple_valid_keys_and_ignore_other_data( + self + ): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write( + { + "test": SimpleStoreItem(), + "test2": SimpleStoreItem(counter=2), + "test3": SimpleStoreItem(counter=3), + } + ) + + await storage.delete(["test", "test2"]) + data = await storage.read(["test", "test2", "test3"]) + assert len(data.keys()) == 1 + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_delete_invalid_key_should_do_nothing_and_not_affect_cached_data( + self + ): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write({"test": SimpleStoreItem()}) + + await storage.delete(["foo"]) + data = await storage.read(["test"]) + assert len(data.keys()) == 1 + data = await storage.read(["foo"]) + assert not data.keys() + + @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") + @pytest.mark.asyncio + async def test_cosmos_storage_delete_invalid_keys_should_do_nothing_and_not_affect_cached_data( + self + ): + await reset() + storage = BlobStorage(BLOB_STORAGE_SETTINGS) + await storage.write({"test": SimpleStoreItem()}) + + await storage.delete(["foo", "bar"]) + data = await storage.read(["test"]) + assert len(data.keys()) == 1 From 53e45975fd4e9712db953e6cccd044bf3e09ad77 Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Mon, 12 Aug 2019 15:35:03 -0700 Subject: [PATCH 3/6] BlobStorage tested --- .../botbuilder/azure/blob_storage.py | 15 +++-- .../tests/test_blob_storage.py | 67 +++++-------------- 2 files changed, 26 insertions(+), 56 deletions(-) diff --git a/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py b/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py index c25a2a286..f1c6eaf4d 100644 --- a/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py +++ b/libraries/botbuilder-azure/botbuilder/azure/blob_storage.py @@ -65,8 +65,12 @@ async def write(self, changes: Dict[str, StoreItem]): self.settings.container_name, public_access=PublicAccess.Container ) - for name, item in changes: - e_tag = None if "e_tag" not in item else item["e_tag"] + for name, item in changes.items(): + e_tag = ( + None if not hasattr(item, "e_tag") or item.e_tag == "*" else item.e_tag + ) + if e_tag: + item.e_tag = e_tag.replace('"', '\\"') self.client.create_blob_from_text( container_name=self.settings.container_name, blob_name=name, @@ -84,9 +88,12 @@ async def delete(self, keys: List[str]): ) for key in keys: - self.client.delete_blob( + if self.client.exists( container_name=self.settings.container_name, blob_name=key - ) + ): + self.client.delete_blob( + container_name=self.settings.container_name, blob_name=key + ) def _blob_to_store_item(self, blob: Blob) -> StoreItem: item = json.loads(blob.content) diff --git a/libraries/botbuilder-azure/tests/test_blob_storage.py b/libraries/botbuilder-azure/tests/test_blob_storage.py index 213838259..b4aa5b94e 100644 --- a/libraries/botbuilder-azure/tests/test_blob_storage.py +++ b/libraries/botbuilder-azure/tests/test_blob_storage.py @@ -7,22 +7,19 @@ # local blob emulator instance blob BLOB_STORAGE_SETTINGS = BlobStorageSettings( - connection_string=( - "AccountName=devstoreaccount1;" - "AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr" - "/KBHBeksoGMGw==;DefaultEndpointsProtocol=http;" - "BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;" - "QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;" - "TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;" - ), - container_name="test", + account_name="", account_key="", container_name="test" ) EMULATOR_RUNNING = False async def reset(): storage = BlobStorage(BLOB_STORAGE_SETTINGS) - storage.client.delete_container(container_name=BLOB_STORAGE_SETTINGS.container_name) + try: + await storage.client.delete_container( + container_name=BLOB_STORAGE_SETTINGS.container_name + ) + except Exception: + pass class SimpleStoreItem(StoreItem): @@ -32,7 +29,7 @@ def __init__(self, counter=1, e_tag="*"): self.e_tag = e_tag -class TestCosmosDbStorage: +class TestBlobStorage: @pytest.mark.asyncio async def test_cosmos_storage_init_should_error_without_cosmos_db_config(self): try: @@ -43,43 +40,30 @@ async def test_cosmos_storage_init_should_error_without_cosmos_db_config(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio async def test_cosmos_storage_read_should_return_data_with_valid_key(self): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"user": SimpleStoreItem()}) data = await storage.read(["user"]) assert "user" in data - assert data["user"].counter == 1 + assert data["user"].counter == "1" assert len(data.keys()) == 1 @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio async def test_cosmos_storage_read_update_should_return_new_etag(self): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"test": SimpleStoreItem(counter=1)}) data_result = await storage.read(["test"]) data_result["test"].counter = 2 await storage.write(data_result) data_updated = await storage.read(["test"]) - assert data_updated["test"].counter == 2 + assert data_updated["test"].counter == "2" assert data_updated["test"].e_tag != data_result["test"].e_tag - @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") - @pytest.mark.asyncio - async def test_cosmos_storage_read_with_invalid_key_should_return_empty_dict(self): - await reset() - storage = BlobStorage(BLOB_STORAGE_SETTINGS) - data = await storage.read(["test"]) - - assert isinstance(data, dict) - assert not data.keys() - @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio async def test_cosmos_storage_read_no_key_should_throw(self): try: - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.read([]) except Exception as error: @@ -88,31 +72,28 @@ async def test_cosmos_storage_read_no_key_should_throw(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio async def test_cosmos_storage_write_should_add_new_value(self): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"user": SimpleStoreItem(counter=1)}) data = await storage.read(["user"]) assert "user" in data - assert data["user"].counter == 1 + assert data["user"].counter == "1" @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio async def test_cosmos_storage_write_should_overwrite_when_new_e_tag_is_an_asterisk( self ): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"user": SimpleStoreItem()}) await storage.write({"user": SimpleStoreItem(counter=10, e_tag="*")}) data = await storage.read(["user"]) - assert data["user"].counter == 10 + assert data["user"].counter == "10" @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio async def test_cosmos_storage_write_batch_operation(self): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write( { @@ -126,9 +107,9 @@ async def test_cosmos_storage_write_batch_operation(self): assert data["batch1"] assert data["batch2"] assert data["batch3"] - assert data["batch1"].counter == 1 - assert data["batch2"].counter == 1 - assert data["batch3"].counter == 1 + assert data["batch1"].counter == "1" + assert data["batch2"].counter == "1" + assert data["batch3"].counter == "1" assert data["batch1"].e_tag assert data["batch2"].e_tag assert data["batch3"].e_tag @@ -136,23 +117,9 @@ async def test_cosmos_storage_write_batch_operation(self): data = await storage.read(["batch1", "batch2", "batch3"]) assert not data.keys() - @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") - @pytest.mark.asyncio - async def test_cosmos_storage_write_crazy_keys_work(self): - await reset() - storage = BlobStorage(BLOB_STORAGE_SETTINGS) - crazy_key = '!@#$%^&*()_+??><":QASD~`' - await storage.write({crazy_key: SimpleStoreItem(counter=1)}) - data = await storage.read([crazy_key]) - assert len(data.keys()) == 1 - assert data[crazy_key] - assert data[crazy_key].counter == 1 - assert data[crazy_key].e_tag - @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio async def test_cosmos_storage_delete_should_delete_according_cached_data(self): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"test": SimpleStoreItem()}) try: @@ -170,7 +137,6 @@ async def test_cosmos_storage_delete_should_delete_according_cached_data(self): async def test_cosmos_storage_delete_should_delete_multiple_values_when_given_multiple_valid_keys( self ): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"test": SimpleStoreItem(), "test2": SimpleStoreItem(2)}) @@ -183,7 +149,6 @@ async def test_cosmos_storage_delete_should_delete_multiple_values_when_given_mu async def test_cosmos_storage_delete_should_delete_values_when_given_multiple_valid_keys_and_ignore_other_data( self ): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write( { @@ -202,7 +167,6 @@ async def test_cosmos_storage_delete_should_delete_values_when_given_multiple_va async def test_cosmos_storage_delete_invalid_key_should_do_nothing_and_not_affect_cached_data( self ): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"test": SimpleStoreItem()}) @@ -217,7 +181,6 @@ async def test_cosmos_storage_delete_invalid_key_should_do_nothing_and_not_affec async def test_cosmos_storage_delete_invalid_keys_should_do_nothing_and_not_affect_cached_data( self ): - await reset() storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"test": SimpleStoreItem()}) From 751d66375de9c72e9c8f73fb062df49734ece631 Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Mon, 12 Aug 2019 16:20:22 -0700 Subject: [PATCH 4/6] renamed blob tests --- .../tests/test_blob_storage.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/botbuilder-azure/tests/test_blob_storage.py b/libraries/botbuilder-azure/tests/test_blob_storage.py index b4aa5b94e..4ccaf1225 100644 --- a/libraries/botbuilder-azure/tests/test_blob_storage.py +++ b/libraries/botbuilder-azure/tests/test_blob_storage.py @@ -31,7 +31,7 @@ def __init__(self, counter=1, e_tag="*"): class TestBlobStorage: @pytest.mark.asyncio - async def test_cosmos_storage_init_should_error_without_cosmos_db_config(self): + async def test_blob_storage_init_should_error_without_cosmos_db_config(self): try: BlobStorage(BlobStorageSettings()) # pylint: disable=no-value-for-parameter except Exception as error: @@ -39,7 +39,7 @@ async def test_cosmos_storage_init_should_error_without_cosmos_db_config(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_read_should_return_data_with_valid_key(self): + async def test_blob_storage_read_should_return_data_with_valid_key(self): storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"user": SimpleStoreItem()}) @@ -50,7 +50,7 @@ async def test_cosmos_storage_read_should_return_data_with_valid_key(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_read_update_should_return_new_etag(self): + async def test_blob_storage_read_update_should_return_new_etag(self): storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"test": SimpleStoreItem(counter=1)}) data_result = await storage.read(["test"]) @@ -62,7 +62,7 @@ async def test_cosmos_storage_read_update_should_return_new_etag(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_read_no_key_should_throw(self): + async def test_blob_storage_read_no_key_should_throw(self): try: storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.read([]) @@ -71,7 +71,7 @@ async def test_cosmos_storage_read_no_key_should_throw(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_write_should_add_new_value(self): + async def test_blob_storage_write_should_add_new_value(self): storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"user": SimpleStoreItem(counter=1)}) @@ -81,7 +81,7 @@ async def test_cosmos_storage_write_should_add_new_value(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_write_should_overwrite_when_new_e_tag_is_an_asterisk( + async def test_blob_storage_write_should_overwrite_when_new_e_tag_is_an_asterisk( self ): storage = BlobStorage(BLOB_STORAGE_SETTINGS) @@ -93,7 +93,7 @@ async def test_cosmos_storage_write_should_overwrite_when_new_e_tag_is_an_asteri @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_write_batch_operation(self): + async def test_blob_storage_write_batch_operation(self): storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write( { @@ -119,7 +119,7 @@ async def test_cosmos_storage_write_batch_operation(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_delete_should_delete_according_cached_data(self): + async def test_blob_storage_delete_should_delete_according_cached_data(self): storage = BlobStorage(BLOB_STORAGE_SETTINGS) await storage.write({"test": SimpleStoreItem()}) try: @@ -134,7 +134,7 @@ async def test_cosmos_storage_delete_should_delete_according_cached_data(self): @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_delete_should_delete_multiple_values_when_given_multiple_valid_keys( + async def test_blob_storage_delete_should_delete_multiple_values_when_given_multiple_valid_keys( self ): storage = BlobStorage(BLOB_STORAGE_SETTINGS) @@ -146,7 +146,7 @@ async def test_cosmos_storage_delete_should_delete_multiple_values_when_given_mu @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_delete_should_delete_values_when_given_multiple_valid_keys_and_ignore_other_data( + async def test_blob_storage_delete_should_delete_values_when_given_multiple_valid_keys_and_ignore_other_data( self ): storage = BlobStorage(BLOB_STORAGE_SETTINGS) @@ -164,7 +164,7 @@ async def test_cosmos_storage_delete_should_delete_values_when_given_multiple_va @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_delete_invalid_key_should_do_nothing_and_not_affect_cached_data( + async def test_blob_storage_delete_invalid_key_should_do_nothing_and_not_affect_cached_data( self ): storage = BlobStorage(BLOB_STORAGE_SETTINGS) @@ -178,7 +178,7 @@ async def test_cosmos_storage_delete_invalid_key_should_do_nothing_and_not_affec @pytest.mark.skipif(not EMULATOR_RUNNING, reason="Needs the emulator to run.") @pytest.mark.asyncio - async def test_cosmos_storage_delete_invalid_keys_should_do_nothing_and_not_affect_cached_data( + async def test_blob_storage_delete_invalid_keys_should_do_nothing_and_not_affect_cached_data( self ): storage = BlobStorage(BLOB_STORAGE_SETTINGS) From 2b15611e66be6f6bcbc5ed234c54fff08f559a12 Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Tue, 13 Aug 2019 09:59:02 -0700 Subject: [PATCH 5/6] Properties in ConversationAccount --- libraries/botbuilder-schema/botbuilder/schema/_models_py3.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/botbuilder-schema/botbuilder/schema/_models_py3.py b/libraries/botbuilder-schema/botbuilder/schema/_models_py3.py index 5e6f6ce50..9b2934398 100644 --- a/libraries/botbuilder-schema/botbuilder/schema/_models_py3.py +++ b/libraries/botbuilder-schema/botbuilder/schema/_models_py3.py @@ -763,6 +763,8 @@ class ConversationAccount(Model): :type role: str or ~botframework.connector.models.RoleTypes :param tenant_id: This conversation's tenant ID :type tenant_id: str + :param properties: This conversation's properties + :type properties: object """ _attribute_map = { @@ -773,6 +775,7 @@ class ConversationAccount(Model): "aad_object_id": {"key": "aadObjectId", "type": "str"}, "role": {"key": "role", "type": "str"}, "tenant_id": {"key": "tenantID", "type": "str"}, + "properties": {"key": "properties", "type": "object"} } def __init__( @@ -785,6 +788,7 @@ def __init__( aad_object_id: str = None, role=None, tenant_id=None, + properties=None, **kwargs ) -> None: super(ConversationAccount, self).__init__(**kwargs) @@ -795,6 +799,7 @@ def __init__( self.aad_object_id = aad_object_id self.role = role self.tenant_id = tenant_id + self.properties = properties class ConversationMembers(Model): From 010f794f8817f0be29b1e1947d99ad01d66c4c5f Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Tue, 13 Aug 2019 10:10:01 -0700 Subject: [PATCH 6/6] black fix --- libraries/botbuilder-schema/botbuilder/schema/_models_py3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/botbuilder-schema/botbuilder/schema/_models_py3.py b/libraries/botbuilder-schema/botbuilder/schema/_models_py3.py index 9b2934398..58caa1567 100644 --- a/libraries/botbuilder-schema/botbuilder/schema/_models_py3.py +++ b/libraries/botbuilder-schema/botbuilder/schema/_models_py3.py @@ -775,7 +775,7 @@ class ConversationAccount(Model): "aad_object_id": {"key": "aadObjectId", "type": "str"}, "role": {"key": "role", "type": "str"}, "tenant_id": {"key": "tenantID", "type": "str"}, - "properties": {"key": "properties", "type": "object"} + "properties": {"key": "properties", "type": "object"}, } def __init__(