Skip to content

Commit

Permalink
include datalayer/service/backend docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
petrjasek committed Oct 17, 2016
1 parent 2b4b227 commit e8db629
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 21 deletions.
26 changes: 21 additions & 5 deletions docs/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,27 @@ Tasks that involve communication with external services (ingest update, publishi

It uses same app factory like API server.

Data Storage
------------
Main data storage is `mongoDB <https://www.mongodb.com/>`_, content items are also indexed using `elastic <https://www.elastic.co/products/elasticsearch>`_.
Data Layer
----------
In short - main data storage is `mongoDB <https://www.mongodb.com/>`_, content items are also indexed using `elastic <https://www.elastic.co/products/elasticsearch>`_. This logic is implemented via custom eve data layer, superdesk service layer and data backend.

.. autoclass:: superdesk.datalayer.SuperdeskDataLayer
:members:

.. autoclass:: superdesk.services.BaseService
:members:

.. autoclass:: superdesk.eve_backend.EveBackend
:members:

Media Storage
--------------
By default uploaded/ingested files are stored in `mongoDB GridFS <https://docs.mongodb.com/manual/core/gridfs/>`_,
but there are also other implementations, like Amazon S3 backend.
By default uploaded/ingested files are stored in `mongoDB GridFS <https://docs.mongodb.com/manual/core/gridfs/>`_.

.. autoclass:: superdesk.storage.desk_media_storage.SuperdeskGridFSMediaStorage
:members:

There is also Amazon S3 implementation, which is used when Amazon is configured via settings.

.. autoclass:: superdesk.storage.amazon.amazon_media_storage.AmazonMediaStorage
:members:
6 changes: 5 additions & 1 deletion superdesk/datalayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@


class SuperdeskDataLayer(DataLayer):
"""Superdesk Data Layer"""
"""Superdesk Data Layer.
Implements eve data layer interface, is used to make eve work with superdesk service layer.
It handles app initialization and later it forwards eve calls to respective service.
"""

serializers = {}
serializers.update(Mongo.serializers)
Expand Down
76 changes: 71 additions & 5 deletions superdesk/eve_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@


class EveBackend():
"""Superdesk data backend, handles mongodb/elastic data storage."""

def find_one(self, endpoint_name, req, **lookup):
"""Find single item.
:param endpoint_name: resource name
:param req: parsed request
:param lookup: additional filter
"""
backend = self._backend(endpoint_name)
item = backend.find_one(endpoint_name, req=req, **lookup)
search_backend = self._lookup_backend(endpoint_name, fallback=True)
Expand Down Expand Up @@ -69,6 +77,12 @@ def search(self, endpoint_name, source):
logger.warn('there is no search backend for %s' % endpoint_name)

def get(self, endpoint_name, req, lookup):
"""Get list of items.
:param endpoint_name: resource name
:param req: parsed request
:param lookup: additional filter
"""
backend = self._lookup_backend(endpoint_name, fallback=True)
cursor = backend.find(endpoint_name, req, lookup)
if not cursor.count():
Expand All @@ -79,11 +93,24 @@ def get(self, endpoint_name, req, lookup):
return backend.find(endpoint_name, req, lookup)

def get_from_mongo(self, endpoint_name, req, lookup):
"""Get list of items from mongo.
No matter if there is elastic configured, this will use mongo.
:param endpoint_name: resource name
:param req: parsed request
:param lookup: additional filter
"""
req.if_modified_since = None
backend = self._backend(endpoint_name)
return backend.find(endpoint_name, req, lookup)

def find_and_modify(self, endpoint_name, **kwargs):
"""Find and modify in mongo.
:param endpoint_name: resource name
:param kwargs: kwargs for pymongo ``find_and_modify``
"""
backend = self._backend(endpoint_name)

if kwargs.get('query'):
Expand All @@ -102,6 +129,11 @@ def create(self, endpoint_name, docs, **kwargs):
return ids

def create_in_mongo(self, endpoint_name, docs, **kwargs):
"""Create items in mongo.
:param endpoint_name: resource name
:param docs: list of docs to create
"""
for doc in docs:
doc.setdefault(config.ETAG, document_etag(doc))
self.set_default_dates(doc)
Expand All @@ -111,6 +143,11 @@ def create_in_mongo(self, endpoint_name, docs, **kwargs):
return ids

def create_in_search(self, endpoint_name, docs, **kwargs):
"""Create items in elastic.
:param endpoint_name: resource name
:param docs: list of docs
"""
search_backend = self._lookup_backend(endpoint_name)
if search_backend:
search_backend.insert(endpoint_name, docs, **kwargs)
Expand Down Expand Up @@ -172,11 +209,27 @@ def _change_request(self, endpoint_name, id, updates, original):
return updates

def replace(self, endpoint_name, id, document, original):
"""Replace an item.
:param endpoint_name: resource name
:param id: item id
:param document: next version of item
:param original: current version of document
"""
res = self.replace_in_mongo(endpoint_name, id, document, original)
self.replace_in_search(endpoint_name, id, document, original)
return res

def update_in_mongo(self, endpoint_name, id, updates, original):
"""Update item in mongo.
Modifies ``_updated`` timestamp and ``_etag``.
:param endpoint_name: resource name
:param id: item id
:param updates: updates to item to be saved
:param original: current version of the item
"""
updates.setdefault(config.LAST_UPDATED, utcnow())
if config.ETAG not in updates:
updated = original.copy()
Expand All @@ -188,23 +241,35 @@ def update_in_mongo(self, endpoint_name, id, updates, original):
return res if res is not None else updates

def replace_in_mongo(self, endpoint_name, id, document, original):
"""Replace item in mongo.
:param endpoint_name: resource name
:param id: item id
:param document: next version of item
:param original: current version of item
"""
backend = self._backend(endpoint_name)
res = backend.replace(endpoint_name, id, document, original)
return res

def replace_in_search(self, endpoint_name, id, document, original):
"""Replace item in elastic.
:param endpoint_name: resource name
:param id: item id
:param document: next version of item
:param original: current version of item
"""
search_backend = self._lookup_backend(endpoint_name)
if search_backend is not None:
search_backend.replace(endpoint_name, id, document)

def delete(self, endpoint_name, lookup):
"""
Delete method to delete by using mongo query syntax
"""Delete method to delete by using mongo query syntax.
:param endpoint_name: Name of the endpoint
:param lookup: User mongo query syntax. example 1. {'_id':123}, 2. {'item_id': {'$in': [123, 234]}}
:returns:
Returns the mongo remove command response. {'n': 12, 'ok': 1}
:param lookup: User mongo query syntax. example 1. ``{'_id':123}``, 2. ``{'item_id': {'$in': [123, 234]}}``
:returns: Returns the mongo remove command response. {'n': 12, 'ok': 1}
"""
backend = self._backend(endpoint_name)
search_backend = self._lookup_backend(endpoint_name)
Expand Down Expand Up @@ -250,6 +315,7 @@ def _lookup_backend(self, endpoint_name, fallback=False):
return backend

def set_default_dates(self, doc):
"""Helper to populate ``_created`` and ``_updated`` timestamps."""
now = utcnow()
doc.setdefault(config.DATE_CREATED, now)
doc.setdefault(config.LAST_UPDATED, now)
10 changes: 5 additions & 5 deletions superdesk/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#
# For the full copyright and license information, please see the
# AUTHORS and LICENSE files distributed with this source code, or
# at https://www.sourcefabric.org/superdesk/license
#. at https://www.sourcefabric.org/superdesk/license

import logging
from flask import current_app as app
Expand Down Expand Up @@ -84,7 +84,7 @@ def find_one(self, req, **lookup):
def find(self, where, **kwargs):
"""Find items in service collection using mongo query.
:param dict where
:param dict where:
"""
return self.backend.find(self.datasource, where, **kwargs)

Expand Down Expand Up @@ -149,21 +149,21 @@ def is_authorized(self, **kwargs):
"""Subclass should override if the resource handled by the service has intrinsic privileges.
:param kwargs: should have properties which help in authorizing the request
:return: False if unauthorized and True if authorized
:return: ``False`` if unauthorized and True if authorized
"""

return True

def search(self, source):
"""Search using search backend.
:param source
:param source: query source param
"""
return self.backend.search(self.datasource, source)

def remove_from_search(self, _id):
"""Remove item from search by its id.
:param _id
:param _id: item id
"""
return self.backend.remove_from_search(self.datasource, _id)
11 changes: 6 additions & 5 deletions superdesk/storage/amazon/amazon_media_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,14 @@ def url_for_media(self, media_id, content_type=None):
return url_generator(self.app, media_id)

def media_id(self, filename, content_type=None, version=True):
"""Get the `media_id` path for the `filename` given.
"""Get the ``media_id`` path for the given ``filename``.
if filename doesn't have an extension one is guessed,
and additional `version` option to have automatic version or not to have,
or to send a `string` one.
`AMAZON_S3_SUBFOLDER` configuration is used for
easement deploying multiple instance on the same bucket.
and additional *version* option to have automatic version or not to have,
or to send a `string` one.
``AMAZON_S3_SUBFOLDER`` configuration is used for
easement deploying multiple instance on the same bucket.
"""
if not self.app.config.get('AMAZON_SERVE_DIRECT_LINKS', False):
return str(bson.ObjectId())
Expand Down

0 comments on commit e8db629

Please sign in to comment.