MongoDB chat storage backend example¶
Example Service¶
import argparse
import uuid
from aiohttp import web
from motor import motor_asyncio
import pymongo
HTTP_TIMEOUT = 10
def main():
parser = argparse.ArgumentParser(
description='MongoDB based storage service.',
)
parser.add_argument('--port', type=int, default=8080)
parser.add_argument(
'--mongo-uri',
default='mongodb://localhost:27017/',
help='mongodb connection uri',
)
args = parser.parse_args()
web.run_app(create_app(args), port=args.port)
async def create_app(args):
connection = motor_asyncio.AsyncIOMotorClient(args.mongo_uri)
collection = connection['chat_db']['messages']
routes = web.RouteTableDef()
@routes.get('/ping')
async def handle_ping(request):
return web.Response(text='OK.')
@routes.post('/messages/send')
async def post(request):
doc = await request.json()
doc_id = uuid.uuid4().hex
await collection.update_one(
{'_id': doc_id},
{
'$set': {'username': doc['username'], 'text': doc['text']},
'$currentDate': {'created': True},
},
upsert=True,
)
return web.json_response({'id': doc_id})
@routes.post('/messages/retrieve')
async def get(request):
docs = await (
collection.find({}).sort('created', pymongo.DESCENDING).limit(20)
).to_list(20)
messages = [
{
'username': doc['username'],
'text': doc['text'],
'created': doc['created'].isoformat(),
}
for doc in docs
]
return web.json_response({'messages': messages})
app = web.Application()
app.add_routes(routes)
return app
if __name__ == '__main__':
main()
Conftest¶
import pathlib
import sys
import pytest
pytest_plugins = [
'testsuite.pytest_plugin',
'testsuite.databases.mongo.pytest_plugin',
]
MONGO_COLLECTIONS = {
'messages': {
'settings': {
'collection': 'messages',
'connection': 'example',
'database': 'chat_db',
},
'indexes': [{'key': 'created'}],
},
}
def pytest_addoption(parser):
group = parser.getgroup('Example service')
group.addoption(
'--example-service-port',
help='Bind example services to this port (default is %(default)s)',
default=8080,
type=int,
)
@pytest.fixture
async def example_service(
ensure_daemon_started,
# Service process holder
example_service_scope,
# Service dependencies
mongodb,
mockserver,
):
# Start service if not started yet
await ensure_daemon_started(example_service_scope)
@pytest.fixture
async def example_client(
create_service_client,
example_service_baseurl,
example_service,
):
# Create service client instance
return create_service_client(example_service_baseurl)
@pytest.fixture(scope='session')
def example_service_baseurl(pytestconfig):
return f'http://localhost:{pytestconfig.option.example_service_port}/'
@pytest.fixture(scope='session')
def example_root():
"""Path to example service root."""
return pathlib.Path(__file__).parent.parent
@pytest.fixture(scope='session')
async def example_service_scope(
pytestconfig,
create_daemon_scope,
example_service_baseurl,
example_root,
mongo_connection_info,
):
async with create_daemon_scope(
args=[
sys.executable,
str(example_root.joinpath('server.py')),
'--port',
str(pytestconfig.option.example_service_port),
'--mongo-uri',
mongo_connection_info.get_uri(),
],
ping_url=example_service_baseurl + 'ping',
) as scope:
yield scope
@pytest.fixture(scope='session')
def mongodb_settings():
return MONGO_COLLECTIONS
Test¶
async def test_messages_send(example_client, mongodb):
message = {
'username': 'Alice',
'text': 'Rabbits are so cute, love them ^_^',
}
response = await example_client.post('messages/send', json=message)
assert response.status_code == 200
body = response.json()
msg_id = body['id']
# read directly from mongodb
stored_message = mongodb['messages'].find_one({'_id': msg_id})
stored_message.pop('_id')
stored_message.pop('created')
assert stored_message == message
async def test_messages_retrieve(example_client):
# db_messages.json
read_response = await example_client.post('messages/retrieve')
assert read_response.status_code == 200
doc = read_response.json()
assert doc == {
'messages': [
{
'username': 'Alice',
'created': '2020-01-01T12:02:00',
'text': 'Hi Bob!',
},
{
'username': 'Bob',
'created': '2020-01-01T12:01:00',
'text': 'Hi, my name is Bob!',
},
],
}