0
# Storage
1
2
Storage implementations for persisting bot state and data. Includes memory storage for development and testing, plus abstract base classes for implementing custom storage solutions.
3
4
## Capabilities
5
6
### Storage Base Class
7
8
Abstract base class that defines the interface for all storage implementations in the Bot Framework, providing methods for reading, writing, and deleting data with support for concurrency control through eTags.
9
10
```python { .api }
11
class Storage:
12
async def read(self, keys):
13
"""
14
Read items from storage.
15
16
Args:
17
keys (list): List of keys to read
18
19
Returns:
20
dict: Dictionary of key-value pairs for found items
21
"""
22
23
async def write(self, changes):
24
"""
25
Write items to storage.
26
27
Args:
28
changes (dict): Dictionary of key-value pairs to write
29
"""
30
31
async def delete(self, keys):
32
"""
33
Delete items from storage.
34
35
Args:
36
keys (list): List of keys to delete
37
"""
38
```
39
40
### MemoryStorage
41
42
In-memory storage implementation for development and testing. Data is stored in memory and will be lost when the application restarts.
43
44
```python { .api }
45
class MemoryStorage(Storage):
46
def __init__(self, dictionary: dict = None):
47
"""
48
Initialize memory storage.
49
50
Args:
51
dictionary (dict, optional): Optional initial dictionary
52
"""
53
54
async def read(self, keys):
55
"""
56
Read items from memory storage.
57
58
Args:
59
keys (list): List of keys to read
60
61
Returns:
62
dict: Dictionary of found items
63
"""
64
65
async def write(self, changes):
66
"""
67
Write items to memory storage.
68
69
Args:
70
changes (dict): Dictionary of changes to write
71
"""
72
73
async def delete(self, keys):
74
"""
75
Delete items from memory storage.
76
77
Args:
78
keys (list): List of keys to delete
79
"""
80
```
81
82
### StoreItem
83
84
Base class for items that can be stored in bot storage, providing eTag support for concurrency control and change detection.
85
86
```python { .api }
87
class StoreItem:
88
def __init__(self):
89
"""Initialize store item with default eTag."""
90
self.e_tag = "*"
91
```
92
93
### QueueStorage
94
95
Queue-based storage implementation that provides FIFO (First-In-First-Out) storage capabilities for scenarios requiring ordered data processing.
96
97
```python { .api }
98
class QueueStorage:
99
def __init__(self, queues: dict = None):
100
"""
101
Initialize queue storage.
102
103
Args:
104
queues (dict, optional): Optional initial queues dictionary
105
"""
106
107
async def queue_activity(self, queue_name: str, activity):
108
"""
109
Add activity to a queue.
110
111
Args:
112
queue_name (str): Name of the queue
113
activity: Activity to queue
114
"""
115
116
async def dequeue_activity(self, queue_name: str):
117
"""
118
Remove and return activity from queue.
119
120
Args:
121
queue_name (str): Name of the queue
122
123
Returns:
124
Activity or None: Dequeued activity or None if queue is empty
125
"""
126
127
async def get_queue_length(self, queue_name: str):
128
"""
129
Get length of a queue.
130
131
Args:
132
queue_name (str): Name of the queue
133
134
Returns:
135
int: Number of items in queue
136
"""
137
```
138
139
### Storage Utilities
140
141
Utility functions for working with storage implementations and data serialization.
142
143
```python { .api }
144
def calculate_change_hash(obj):
145
"""
146
Calculate hash for change detection.
147
148
Args:
149
obj: Object to calculate hash for
150
151
Returns:
152
str: Hash string for the object
153
"""
154
155
def sanitize_key(key: str):
156
"""
157
Sanitize storage key to ensure compatibility.
158
159
Args:
160
key (str): Key to sanitize
161
162
Returns:
163
str: Sanitized key
164
"""
165
```
166
167
## Usage Examples
168
169
### Basic Memory Storage Setup
170
171
```python
172
from botbuilder.core import MemoryStorage, ConversationState, UserState
173
174
# Create memory storage
175
memory_storage = MemoryStorage()
176
177
# Use with bot states
178
conversation_state = ConversationState(memory_storage)
179
user_state = UserState(memory_storage)
180
181
# Storage is now ready to use with your bot
182
```
183
184
### Direct Storage Operations
185
186
```python
187
from botbuilder.core import MemoryStorage
188
189
# Create storage
190
storage = MemoryStorage()
191
192
# Write data directly
193
changes = {
194
"user:123": {"name": "John", "age": 30},
195
"conversation:456": {"topic": "weather", "turn_count": 5}
196
}
197
await storage.write(changes)
198
199
# Read data back
200
keys = ["user:123", "conversation:456"]
201
data = await storage.read(keys)
202
203
print(f"User data: {data.get('user:123')}")
204
print(f"Conversation data: {data.get('conversation:456')}")
205
206
# Delete data
207
await storage.delete(["user:123"])
208
```
209
210
### Custom Storage Implementation
211
212
```python
213
from botbuilder.core import Storage
214
import json
215
import os
216
217
class FileStorage(Storage):
218
def __init__(self, directory: str):
219
self.directory = directory
220
os.makedirs(directory, exist_ok=True)
221
222
async def read(self, keys):
223
items = {}
224
for key in keys:
225
file_path = os.path.join(self.directory, f"{key}.json")
226
if os.path.exists(file_path):
227
with open(file_path, 'r') as f:
228
items[key] = json.load(f)
229
return items
230
231
async def write(self, changes):
232
for key, value in changes.items():
233
file_path = os.path.join(self.directory, f"{key}.json")
234
with open(file_path, 'w') as f:
235
json.dump(value, f, indent=2)
236
237
async def delete(self, keys):
238
for key in keys:
239
file_path = os.path.join(self.directory, f"{key}.json")
240
if os.path.exists(file_path):
241
os.remove(file_path)
242
243
# Usage
244
file_storage = FileStorage("./bot_data")
245
conversation_state = ConversationState(file_storage)
246
user_state = UserState(file_storage)
247
```
248
249
### Storage with eTag Support
250
251
```python
252
from botbuilder.core import StoreItem, MemoryStorage
253
254
class UserProfile(StoreItem):
255
def __init__(self):
256
super().__init__()
257
self.name = None
258
self.email = None
259
self.preferences = {}
260
261
# Usage with eTag handling
262
storage = MemoryStorage()
263
264
# Create and store user profile
265
user_profile = UserProfile()
266
user_profile.name = "Alice"
267
user_profile.email = "alice@example.com"
268
269
await storage.write({"user:alice": user_profile})
270
271
# Read back with eTag preserved
272
data = await storage.read(["user:alice"])
273
retrieved_profile = data["user:alice"]
274
275
print(f"Profile eTag: {retrieved_profile.e_tag}")
276
277
# Modify and save (eTag will be updated automatically)
278
retrieved_profile.preferences["theme"] = "dark"
279
await storage.write({"user:alice": retrieved_profile})
280
```
281
282
### Queue Storage Usage
283
284
```python
285
from botbuilder.core import QueueStorage, Activity
286
287
# Create queue storage
288
queue_storage = QueueStorage()
289
290
# Queue some activities
291
activity1 = Activity(type="message", text="First message")
292
activity2 = Activity(type="message", text="Second message")
293
294
await queue_storage.queue_activity("pending", activity1)
295
await queue_storage.queue_activity("pending", activity2)
296
297
# Check queue length
298
length = await queue_storage.get_queue_length("pending")
299
print(f"Queue length: {length}")
300
301
# Process queue
302
while await queue_storage.get_queue_length("pending") > 0:
303
activity = await queue_storage.dequeue_activity("pending")
304
print(f"Processing: {activity.text}")
305
```
306
307
### Storage Error Handling
308
309
```python
310
async def safe_storage_operation(storage, key, data):
311
try:
312
await storage.write({key: data})
313
print(f"Successfully stored data for key: {key}")
314
except Exception as e:
315
print(f"Storage error for key {key}: {e}")
316
# Implement retry logic or fallback storage
317
await fallback_storage_operation(key, data)
318
319
async def fallback_storage_operation(key, data):
320
# Fallback to a different storage mechanism
321
fallback_storage = MemoryStorage()
322
await fallback_storage.write({key: data})
323
print(f"Data stored in fallback storage for key: {key}")
324
```
325
326
### Batch Storage Operations
327
328
```python
329
async def batch_user_update(storage, user_updates):
330
"""Update multiple users in a single storage operation."""
331
332
# Prepare batch changes
333
changes = {}
334
for user_id, update_data in user_updates.items():
335
key = f"user:{user_id}"
336
changes[key] = update_data
337
338
# Write all changes at once
339
await storage.write(changes)
340
print(f"Updated {len(changes)} users in batch")
341
342
# Usage
343
user_updates = {
344
"123": {"name": "John", "last_seen": "2023-01-01"},
345
"456": {"name": "Jane", "last_seen": "2023-01-02"},
346
"789": {"name": "Bob", "last_seen": "2023-01-03"}
347
}
348
349
await batch_user_update(storage, user_updates)
350
```
351
352
### Storage Key Management
353
354
```python
355
from botbuilder.core import sanitize_key
356
357
def create_storage_key(prefix: str, identifier: str, suffix: str = None):
358
"""Create a properly formatted storage key."""
359
parts = [prefix, identifier]
360
if suffix:
361
parts.append(suffix)
362
363
key = ":".join(parts)
364
return sanitize_key(key)
365
366
# Usage
367
user_key = create_storage_key("user", "john.doe@example.com")
368
conversation_key = create_storage_key("conversation", "teams-channel-123", "thread-456")
369
370
print(f"User key: {user_key}")
371
print(f"Conversation key: {conversation_key}")
372
```
373
374
## Types
375
376
```python { .api }
377
class StorageKeyFactory:
378
"""Factory for creating consistent storage keys."""
379
380
@staticmethod
381
def user_key(user_id: str):
382
"""Create user storage key."""
383
return f"user:{user_id}"
384
385
@staticmethod
386
def conversation_key(conversation_id: str):
387
"""Create conversation storage key."""
388
return f"conversation:{conversation_id}"
389
390
@staticmethod
391
def private_conversation_key(user_id: str, conversation_id: str):
392
"""Create private conversation storage key."""
393
return f"private:{user_id}:{conversation_id}"
394
```