From 3ced7735a2daa13dd7d0eb50cdf1f5b9b729139c Mon Sep 17 00:00:00 2001 From: Tigran Saluev Date: Sat, 23 Feb 2019 17:41:29 +0300 Subject: [PATCH] Add card entity --- backend/dev_settings.py | 3 ++ backend/server.py | 29 ++++++++++++++--- backend/storage/__init__.py | 0 backend/storage/card.py | 38 ++++++++++++++++++++++ backend/storage/card_impl.py | 63 ++++++++++++++++++++++++++++++++++++ backend/wiring.py | 24 ++++++++++++++ requirements.txt | 1 + 7 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 backend/storage/__init__.py create mode 100644 backend/storage/card.py create mode 100644 backend/storage/card_impl.py create mode 100644 backend/wiring.py diff --git a/backend/dev_settings.py b/backend/dev_settings.py index e69de29..ba25bb6 100644 --- a/backend/dev_settings.py +++ b/backend/dev_settings.py @@ -0,0 +1,3 @@ +MONGO_HOST = "mongo" +MONGO_PORT = 27017 +MONGO_DATABASE = "core" diff --git a/backend/server.py b/backend/server.py index 757749e..9bc703c 100644 --- a/backend/server.py +++ b/backend/server.py @@ -3,6 +3,13 @@ import flask import flask_cors +from backend.storage.card import CardNotFound +from backend.wiring import Wiring + + +env = os.environ.get("APP_ENV", "dev") +print(f"Starting application in {env} mode") + class HabrAppDemo(flask.Flask): @@ -11,11 +18,25 @@ def __init__(self, *args, **kwargs): flask_cors.CORS(self) + self.wiring = Wiring(env) -app = HabrAppDemo("habr-app-demo") + self.route("/api/v1/card/")(self.card) -env = os.environ.get("APP_ENV", "dev") -print(f"Starting application in {env} mode") -app.config.from_object(f"backend.{env}_settings") + def card(self, card_id_or_slug): + try: + card = self.wiring.card_dao.get_by_slug(card_id_or_slug) + except CardNotFound: + try: + card = self.wiring.card_dao.get_by_id(card_id_or_slug) + except (CardNotFound, ValueError): + return flask.abort(404) + return flask.jsonify({ + k: v + for k, v in card.__dict__.items() + if v is not None + }) + +app = HabrAppDemo("habr-app-demo") +app.config.from_object(f"backend.{env}_settings") app.secret_key = "nu privet habravchanin" diff --git a/backend/storage/__init__.py b/backend/storage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/storage/card.py b/backend/storage/card.py new file mode 100644 index 0000000..834e315 --- /dev/null +++ b/backend/storage/card.py @@ -0,0 +1,38 @@ +import abc +from typing import Iterable + + +class Card(object): + def __init__(self, id: str = None, slug: str = None, name: str = None, markdown: str = None, html: str = None): + self.id = id + self.slug = slug + self.name = name + self.markdown = markdown + self.html = html + + +class CardDAO(object, metaclass=abc.ABCMeta): + + @abc.abstractmethod + def create(self, card: Card) -> Card: + pass + + @abc.abstractmethod + def update(self, card: Card) -> Card: + pass + + @abc.abstractmethod + def get_all(self) -> Iterable[Card]: + pass + + @abc.abstractmethod + def get_by_id(self, card_id: str) -> Card: + pass + + @abc.abstractmethod + def get_by_slug(self, slug: str) -> Card: + pass + + +class CardNotFound(Exception): + pass diff --git a/backend/storage/card_impl.py b/backend/storage/card_impl.py new file mode 100644 index 0000000..c92382b --- /dev/null +++ b/backend/storage/card_impl.py @@ -0,0 +1,63 @@ +from typing import Iterable + +import bson +import bson.errors +from pymongo.collection import Collection +from pymongo.database import Database + +from backend.storage.card import Card, CardDAO, CardNotFound + + +class MongoCardDAO(CardDAO): + + def __init__(self, mongo_database: Database): + self.mongo_database = mongo_database + self.collection.create_index("slug", unique=True) + + @property + def collection(self) -> Collection: + return self.mongo_database["cards"] + + @classmethod + def to_bson(cls, card: Card): + result = { + k: v + for k, v in card.__dict__.items() + if v is not None + } + if "id" in result: + result["_id"] = bson.ObjectId(result.pop("id")) + return result + + @classmethod + def from_bson(cls, document) -> Card: + document["id"] = str(document.pop("_id")) + return Card(**document) + + def create(self, card: Card) -> Card: + card.id = str(self.collection.insert_one(self.to_bson(card)).inserted_id) + return card + + def update(self, card: Card) -> Card: + card_id = bson.ObjectId(card.id) + self.collection.update_one({"_id": card_id}, {"$set": self.to_bson(card)}) + return card + + def get_all(self) -> Iterable[Card]: + for document in self.collection.find(): + yield self.from_bson(document) + + def get_by_id(self, card_id: str) -> Card: + try: + return self._get_by_query({"_id": bson.ObjectId(card_id)}) + except bson.errors.InvalidId: + raise ValueError + + def get_by_slug(self, slug: str) -> Card: + return self._get_by_query({"slug": slug}) + + def _get_by_query(self, query) -> Card: + document = self.collection.find_one(query) + if document is None: + raise CardNotFound() + return self.from_bson(document) diff --git a/backend/wiring.py b/backend/wiring.py new file mode 100644 index 0000000..72118e9 --- /dev/null +++ b/backend/wiring.py @@ -0,0 +1,24 @@ +import os + +from pymongo import MongoClient +from pymongo.database import Database + +import backend.dev_settings +from backend.storage.card import CardDAO +from backend.storage.card_impl import MongoCardDAO + + +class Wiring(object): + + def __init__(self, env=None): + if env is None: + env = os.environ.get("APP_ENV", "dev") + self.settings = { + "dev": backend.dev_settings, + }[env] + + self.mongo_client: MongoClient = MongoClient( + host=self.settings.MONGO_HOST, + port=self.settings.MONGO_PORT) + self.mongo_database: Database = self.mongo_client[self.settings.MONGO_DATABASE] + self.card_dao: CardDAO = MongoCardDAO(self.mongo_database) diff --git a/requirements.txt b/requirements.txt index b805555..e703f16 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ flask flask-cors gevent gunicorn +pymongo