From 62b3afeeedfe054345f86093e2d243e956c1e3c9 Mon Sep 17 00:00:00 2001 From: Cédric Bonhomme Date: Wed, 26 Feb 2020 11:27:31 +0100 Subject: The project is now using Poetry. --- src/web/views/api/v2/__init__.py | 3 - src/web/views/api/v2/article.py | 53 ---------- src/web/views/api/v2/category.py | 27 ----- src/web/views/api/v2/common.py | 222 --------------------------------------- src/web/views/api/v2/feed.py | 47 --------- 5 files changed, 352 deletions(-) delete mode 100644 src/web/views/api/v2/__init__.py delete mode 100644 src/web/views/api/v2/article.py delete mode 100644 src/web/views/api/v2/category.py delete mode 100644 src/web/views/api/v2/common.py delete mode 100644 src/web/views/api/v2/feed.py (limited to 'src/web/views/api/v2') diff --git a/src/web/views/api/v2/__init__.py b/src/web/views/api/v2/__init__.py deleted file mode 100644 index 46760261..00000000 --- a/src/web/views/api/v2/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from web.views.api.v2 import article, feed, category - -__all__ = ['article', 'feed', 'category'] diff --git a/src/web/views/api/v2/article.py b/src/web/views/api/v2/article.py deleted file mode 100644 index 2be286c6..00000000 --- a/src/web/views/api/v2/article.py +++ /dev/null @@ -1,53 +0,0 @@ -from conf import API_ROOT -import dateutil.parser -from datetime import datetime -from flask import current_app -from flask_restful import Api - -from web.views.common import api_permission -from web.controllers import ArticleController -from web.views.api.v2.common import (PyAggAbstractResource, - PyAggResourceNew, PyAggResourceExisting, PyAggResourceMulti) - - -class ArticleNewAPI(PyAggResourceNew): - controller_cls = ArticleController - - -class ArticleAPI(PyAggResourceExisting): - controller_cls = ArticleController - - -class ArticlesAPI(PyAggResourceMulti): - controller_cls = ArticleController - - -class ArticlesChallenge(PyAggAbstractResource): - controller_cls = ArticleController - attrs = {'ids': {'type': list, 'default': []}} - - @api_permission.require(http_exception=403) - def get(self): - parsed_args = self.reqparse_args(right='read') - # collecting all attrs for casting purpose - attrs = self.controller_cls._get_attrs_desc('admin') - for id_dict in parsed_args['ids']: - keys_to_ignore = [] - for key in id_dict: - if key not in attrs: - keys_to_ignore.append(key) - if issubclass(attrs[key]['type'], datetime): - id_dict[key] = dateutil.parser.parse(id_dict[key]) - for key in keys_to_ignore: - del id_dict[key] - - result = list(self.controller.challenge(parsed_args['ids'])) - return result or None, 200 if result else 204 - -api = Api(current_app, prefix=API_ROOT) - -api.add_resource(ArticleNewAPI, '/article', endpoint='article_new.json') -api.add_resource(ArticleAPI, '/article/', endpoint='article.json') -api.add_resource(ArticlesAPI, '/articles', endpoint='articles.json') -api.add_resource(ArticlesChallenge, '/articles/challenge', - endpoint='articles_challenge.json') diff --git a/src/web/views/api/v2/category.py b/src/web/views/api/v2/category.py deleted file mode 100644 index 70fda1ea..00000000 --- a/src/web/views/api/v2/category.py +++ /dev/null @@ -1,27 +0,0 @@ -from conf import API_ROOT -from flask import current_app -from flask_restful import Api - -from web.controllers.category import CategoryController -from web.views.api.v2.common import (PyAggResourceNew, - PyAggResourceExisting, - PyAggResourceMulti) - - -class CategoryNewAPI(PyAggResourceNew): - controller_cls = CategoryController - - -class CategoryAPI(PyAggResourceExisting): - controller_cls = CategoryController - - -class CategoriesAPI(PyAggResourceMulti): - controller_cls = CategoryController - - -api = Api(current_app, prefix=API_ROOT) -api.add_resource(CategoryNewAPI, '/category', endpoint='category_new.json') -api.add_resource(CategoryAPI, '/category/', - endpoint='category.json') -api.add_resource(CategoriesAPI, '/categories', endpoint='categories.json') diff --git a/src/web/views/api/v2/common.py b/src/web/views/api/v2/common.py deleted file mode 100644 index 8a53d7e6..00000000 --- a/src/web/views/api/v2/common.py +++ /dev/null @@ -1,222 +0,0 @@ -"""For a given resources, classes in the module intend to create the following -routes : - GET resource/ - -> to retrieve one - POST resource - -> to create one - PUT resource/ - -> to update one - DELETE resource/ - -> to delete one - - GET resources - -> to retrieve several - POST resources - -> to create several - PUT resources - -> to update several - DELETE resources - -> to delete several -""" -import ast -import logging -from functools import wraps -from werkzeug.exceptions import Unauthorized, BadRequest, Forbidden, NotFound -from flask import request -from flask_restful import Resource, reqparse -from flask_login import current_user - -from web.views.common import admin_permission, api_permission, \ - login_user_bundle, jsonify -from web.controllers import UserController - -logger = logging.getLogger(__name__) - - -def authenticate(func): - @wraps(func) - def wrapper(*args, **kwargs): - if request.authorization: - ucontr = UserController() - try: - user = ucontr.get(nickname=request.authorization.username) - except NotFound: - raise Forbidden("Couldn't authenticate your user") - if not ucontr.check_password(user, request.authorization.password): - raise Forbidden("Couldn't authenticate your user") - if not user.is_active: - raise Forbidden("User is deactivated") - login_user_bundle(user) - if current_user.is_authenticated: - return func(*args, **kwargs) - raise Unauthorized() - return wrapper - - -class PyAggAbstractResource(Resource): - method_decorators = [authenticate, jsonify] - controller_cls = None - attrs = None - - @property - def controller(self): - if admin_permission.can(): - return self.controller_cls() - return self.controller_cls(current_user.id) - - def reqparse_args(self, right, req=None, strict=False, default=True, - allow_empty=False): - """ - strict: bool - if True will throw 400 error if args are defined and not in request - default: bool - if True, won't return defaults - args: dict - the args to parse, if None, self.attrs will be used - """ - try: - if req: - in_values = req.json - else: - in_values = request.args or request.json or {} - if not in_values and allow_empty: - return {} - except BadRequest: - if allow_empty: - return {} - raise - parser = reqparse.RequestParser() - if self.attrs is not None: - attrs = self.attrs - elif admin_permission.can(): - attrs = self.controller_cls._get_attrs_desc('admin') - elif api_permission.can(): - attrs = self.controller_cls._get_attrs_desc('api', right) - else: - attrs = self.controller_cls._get_attrs_desc('base', right) - assert attrs, "No defined attrs for %s" % self.__class__.__name__ - - for attr_name, attr in attrs.items(): - if not default and attr_name not in in_values: - continue - else: - parser.add_argument(attr_name, location='json', - default=in_values[attr_name]) - return parser.parse_args(req=request.args, strict=strict) - - -class PyAggResourceNew(PyAggAbstractResource): - - @api_permission.require(http_exception=403) - def post(self): - """Create a single new object""" - return self.controller.create(**self.reqparse_args(right='write')), 201 - - -class PyAggResourceExisting(PyAggAbstractResource): - - def get(self, obj_id=None): - """Retrieve a single object""" - return self.controller.get(id=obj_id) - - def put(self, obj_id=None): - """update an object, new attrs should be passed in the payload""" - args = self.reqparse_args(right='write', default=False) - if not args: - raise BadRequest() - return self.controller.update({'id': obj_id}, args), 200 - - def delete(self, obj_id=None): - """delete a object""" - self.controller.delete(obj_id) - return None, 204 - - -class PyAggResourceMulti(PyAggAbstractResource): - - def get(self): - """retrieve several objects. filters can be set in the payload on the - different fields of the object, and a limit can be set in there as well - """ - args = {} - try: - limit = request.json.pop('limit', 10) - order_by = request.json.pop('order_by', None) - except Exception: - args = self.reqparse_args(right='read', default=False) - limit = request.args.get('limit', 10) - order_by = request.args.get('order_by', None) - query = self.controller.read(**args) - if order_by: - query = query.order_by(order_by) - if limit: - query = query.limit(limit) - return [res for res in query] - - @api_permission.require(http_exception=403) - def post(self): - """creating several objects. payload should be: - >>> payload - [{attr1: val1, attr2: val2}, {attr1: val1, attr2: val2}] - """ - status, fail_count, results = 200, 0, [] - - class Proxy: - pass - for attrs in request.json: - try: - Proxy.json = attrs - args = self.reqparse_args('write', req=Proxy, default=False) - obj = self.controller.create(**args) - results.append(obj) - except Exception as error: - fail_count += 1 - results.append(str(error)) - if fail_count == len(results): # all failed => 500 - status = 500 - elif fail_count: # some failed => 206 - status = 206 - return results, status - - def put(self): - """updating several objects. payload should be: - >>> payload - [[obj_id1, {attr1: val1, attr2: val2}] - [obj_id2, {attr1: val1, attr2: val2}]] - """ - status, results = 200, [] - - class Proxy: - pass - for obj_id, attrs in request.json: - try: - Proxy.json = attrs - args = self.reqparse_args('write', req=Proxy, default=False) - result = self.controller.update({'id': obj_id}, args) - if result: - results.append('ok') - else: - results.append('nok') - except Exception as error: - results.append(str(error)) - if results.count('ok') == 0: # all failed => 500 - status = 500 - elif results.count('ok') != len(results): # some failed => 206 - status = 206 - return results, status - - def delete(self): - """will delete several objects, - a list of their ids should be in the payload""" - status, results = 204, [] - for obj_id in request.json: - try: - self.controller.delete(obj_id) - results.append('ok') - except Exception as error: - status = 206 - results.append(error) - # if no operation succeeded, it's not partial anymore, returning err 500 - if status == 206 and results.count('ok') == 0: - status = 500 - return results, status diff --git a/src/web/views/api/v2/feed.py b/src/web/views/api/v2/feed.py deleted file mode 100644 index a0691277..00000000 --- a/src/web/views/api/v2/feed.py +++ /dev/null @@ -1,47 +0,0 @@ -from conf import API_ROOT -from flask import current_app -from flask_restful import Api - -from web.views.common import api_permission -from web.controllers.feed import (FeedController, - DEFAULT_MAX_ERROR, - DEFAULT_LIMIT) - -from web.views.api.v2.common import PyAggAbstractResource, \ - PyAggResourceNew, \ - PyAggResourceExisting, \ - PyAggResourceMulti - - -class FeedNewAPI(PyAggResourceNew): - controller_cls = FeedController - - -class FeedAPI(PyAggResourceExisting): - controller_cls = FeedController - - -class FeedsAPI(PyAggResourceMulti): - controller_cls = FeedController - - -class FetchableFeedAPI(PyAggAbstractResource): - controller_cls = FeedController - attrs = {'max_error': {'type': int, 'default': DEFAULT_MAX_ERROR}, - 'limit': {'type': int, 'default': DEFAULT_LIMIT}} - - @api_permission.require(http_exception=403) - def get(self): - args = self.reqparse_args(right='read', allow_empty=True) - result = [feed for feed - in self.controller.list_fetchable(**args)] - return result or None, 200 if result else 204 - - -api = Api(current_app, prefix=API_ROOT) - -api.add_resource(FeedNewAPI, '/feed', endpoint='feed_new.json') -api.add_resource(FeedAPI, '/feed/', endpoint='feed.json') -api.add_resource(FeedsAPI, '/feeds', endpoint='feeds.json') -api.add_resource(FetchableFeedAPI, '/feeds/fetchable', - endpoint='fetchable_feed.json') -- cgit