aboutsummaryrefslogtreecommitdiff
path: root/src/web/views/api
diff options
context:
space:
mode:
authorCédric Bonhomme <cedric@cedricbonhomme.org>2020-02-26 11:27:31 +0100
committerCédric Bonhomme <cedric@cedricbonhomme.org>2020-02-26 11:27:31 +0100
commit62b3afeeedfe054345f86093e2d243e956c1e3c9 (patch)
treebbd58f5c8c07f5d87b1c1cca73fa1d5af6178f48 /src/web/views/api
parentUpdated Python dependencies. (diff)
downloadnewspipe-62b3afeeedfe054345f86093e2d243e956c1e3c9.tar.gz
newspipe-62b3afeeedfe054345f86093e2d243e956c1e3c9.tar.bz2
newspipe-62b3afeeedfe054345f86093e2d243e956c1e3c9.zip
The project is now using Poetry.
Diffstat (limited to 'src/web/views/api')
-rw-r--r--src/web/views/api/__init__.py0
-rw-r--r--src/web/views/api/v2/__init__.py3
-rw-r--r--src/web/views/api/v2/article.py53
-rw-r--r--src/web/views/api/v2/category.py27
-rw-r--r--src/web/views/api/v2/common.py222
-rw-r--r--src/web/views/api/v2/feed.py47
-rw-r--r--src/web/views/api/v3/__init__.py3
-rw-r--r--src/web/views/api/v3/article.py84
-rw-r--r--src/web/views/api/v3/common.py109
-rw-r--r--src/web/views/api/v3/feed.py58
10 files changed, 0 insertions, 606 deletions
diff --git a/src/web/views/api/__init__.py b/src/web/views/api/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/src/web/views/api/__init__.py
+++ /dev/null
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/<int:obj_id>', 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/<int:obj_id>',
- 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/<id>
- -> to retrieve one
- POST resource
- -> to create one
- PUT resource/<id>
- -> to update one
- DELETE resource/<id>
- -> 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/<int:obj_id>', endpoint='feed.json')
-api.add_resource(FeedsAPI, '/feeds', endpoint='feeds.json')
-api.add_resource(FetchableFeedAPI, '/feeds/fetchable',
- endpoint='fetchable_feed.json')
diff --git a/src/web/views/api/v3/__init__.py b/src/web/views/api/v3/__init__.py
deleted file mode 100644
index 76aa1f19..00000000
--- a/src/web/views/api/v3/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from web.views.api.v3 import article
-
-__all__ = ['article']
diff --git a/src/web/views/api/v3/article.py b/src/web/views/api/v3/article.py
deleted file mode 100644
index 4cf35648..00000000
--- a/src/web/views/api/v3/article.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Newspipe - A Web based news aggregator.
-# Copyright (C) 2010-2018 Cédric Bonhomme - https://www.cedricbonhomme.org
-#
-# For more information : http://gitlab.com/newspipe/newspipe
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>
-
-__author__ = "Cedric Bonhomme"
-__version__ = "$Revision: 0.1 $"
-__date__ = "$Date: 2016/04/29 $"
-__revision__ = "$Date: 2016/04/29 $"
-__copyright__ = "Copyright (c) Cedric Bonhomme"
-__license__ = "GPLv3"
-
-from flask_login import current_user
-from werkzeug.exceptions import NotFound
-from flask_restless import ProcessingException
-from web import models
-from bootstrap import application, manager
-from web.controllers import ArticleController, FeedController
-from web.views.api.v3.common import AbstractProcessor
-from web.views.api.v3.common import url_prefix, auth_func
-
-class ArticleProcessor(AbstractProcessor):
- """Concrete processors for the Article Web service.
- """
-
- def get_single_preprocessor(self, instance_id=None, **kw):
- try:
- article = ArticleController(current_user.id).get(id=instance_id)
- except NotFound:
- raise ProcessingException(description='No such article.', code=404)
- self.is_authorized(current_user, article)
-
- def post_preprocessor(self, data=None, **kw):
- data["user_id"] = current_user.id
-
- try:
- feed = FeedController(current_user.id).get(id=data["feed_id"])
- except NotFound:
- raise ProcessingException(description='No such feed.', code=404)
- self.is_authorized(current_user, feed)
-
- data["category_id"] = feed.category_id
-
- def delete_preprocessor(self, instance_id=None, **kw):
- try:
- article = ArticleController(current_user.id).get(id=instance_id)
- except NotFound:
- raise ProcessingException(description='No such article.', code=404)
- self.is_authorized(current_user, article)
-
-article_processor = ArticleProcessor()
-
-blueprint_article = manager.create_api_blueprint(models.Article,
- url_prefix=url_prefix,
- methods=['GET', 'POST', 'PUT', 'DELETE'],
- preprocessors=dict(GET_SINGLE=[auth_func,
- article_processor.get_single_preprocessor],
- GET_MANY=[auth_func,
- article_processor.get_many_preprocessor],
- POST=[auth_func,
- article_processor.post_preprocessor],
- PUT_SINGLE=[auth_func,
- article_processor.put_single_preprocessor],
- PUT_MANY=[auth_func,
- article_processor.put_many_preprocessor],
- DELETE=[auth_func,
- article_processor.delete_preprocessor]))
-application.register_blueprint(blueprint_article)
diff --git a/src/web/views/api/v3/common.py b/src/web/views/api/v3/common.py
deleted file mode 100644
index d5e94a3f..00000000
--- a/src/web/views/api/v3/common.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Newspipe - A Web based news aggregator.
-# Copyright (C) 2010-2018 Cédric Bonhomme - https://www.cedricbonhomme.org
-#
-# For more information : http://gitlab.com/newspipe/newspipe
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>
-
-__author__ = "Cedric Bonhomme"
-__version__ = "$Revision: 0.1 $"
-__date__ = "$Date: 2016/04/29 $"
-__revision__ = "$Date: 2016/04/29 $"
-__copyright__ = "Copyright (c) Cedric Bonhomme"
-__license__ = "GPLv3"
-
-from flask import request
-from flask_login import current_user
-from flask_restless import ProcessingException
-from werkzeug.exceptions import NotFound
-from web.controllers import ArticleController, UserController
-from web.views.common import login_user_bundle
-
-url_prefix = '/api/v3'
-
-def auth_func(*args, **kw):
- if request.authorization:
- ucontr = UserController()
- try:
- user = ucontr.get(nickname=request.authorization.username)
- except NotFound:
- raise ProcessingException("Couldn't authenticate your user",
- code=401)
- if not ucontr.check_password(user, request.authorization.password):
- raise ProcessingException("Couldn't authenticate your user",
- code=401)
- if not user.is_active:
- raise ProcessingException("User is deactivated", code=401)
- login_user_bundle(user)
- if not current_user.is_authenticated:
- raise ProcessingException(description='Not authenticated!', code=401)
-
-class AbstractProcessor():
- """Abstract processors for the Web services.
- """
-
- def is_authorized(self, user, obj):
- if user.id != obj.user_id:
- raise ProcessingException(description='Not Authorized', code=401)
-
- def get_single_preprocessor(self, instance_id=None, **kw):
- # Check if the user is authorized to modify the specified
- # instance of the model.
- pass
-
- def get_many_preprocessor(self, search_params=None, **kw):
- """Accepts a single argument, `search_params`, which is a dictionary
- containing the search parameters for the request.
- """
- filt = dict(name="user_id",
- op="eq",
- val=current_user.id)
-
- # Check if there are any filters there already.
- if "filters" not in search_params:
- search_params["filters"] = []
-
- search_params["filters"].append(filt)
-
- def post_preprocessor(self, data=None, **kw):
- pass
-
- def put_single_preprocessor(instance_id=None, data=None, **kw):
- """Accepts two arguments, `instance_id`, the primary key of the
- instance of the model to patch, and `data`, the dictionary of fields
- to change on the instance.
- """
- pass
-
- def put_many_preprocessor(search_params=None, data=None, **kw):
- """Accepts two arguments: `search_params`, which is a dictionary
- containing the search parameters for the request, and `data`, which
- is a dictionary representing the fields to change on the matching
- instances and the values to which they will be set.
- """
- filt = dict(name="user_id",
- op="eq",
- val=current_user.id)
-
- # Check if there are any filters there already.
- if "filters" not in search_params:
- search_params["filters"] = []
-
- search_params["filters"].append(filt)
-
- def delete_preprocessor(self, instance_id=None, **kw):
- pass
diff --git a/src/web/views/api/v3/feed.py b/src/web/views/api/v3/feed.py
deleted file mode 100644
index 2cbbafd9..00000000
--- a/src/web/views/api/v3/feed.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# Newspipe - A Web based news aggregator.
-# Copyright (C) 2010-2018 Cédric Bonhomme - https://www.cedricbonhomme.org
-#
-# For more information : http://gitlab.com/newspipe/newspipe
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>
-
-__author__ = "Cedric Bonhomme"
-__version__ = "$Revision: 0.1 $"
-__date__ = "$Date: 2016/04/29 $"
-__revision__ = "$Date: 2016/04/29 $"
-__copyright__ = "Copyright (c) Cedric Bonhomme"
-__license__ = "GPLv3"
-
-from flask_login import current_user
-from web import models
-from bootstrap import application, manager
-from web.controllers import FeedController
-from web.views.api.v3.common import AbstractProcessor
-from web.views.api.v3.common import url_prefix, auth_func
-
-class FeedProcessor(AbstractProcessor):
- """Concrete processors for the Feed Web service.
- """
-
- def get_single_preprocessor(self, instance_id=None, **kw):
- # Check if the user is authorized to modify the specified
- # instance of the model.
- feed = FeedController(current_user.id).get(id=instance_id)
- self.is_authorized(current_user, feed)
-
-feed_processor = FeedProcessor()
-
-blueprint_feed = manager.create_api_blueprint(models.Feed,
- url_prefix=url_prefix,
- methods=['GET', 'POST', 'PUT', 'DELETE'],
- preprocessors=dict(GET_SINGLE=[auth_func,
- feed_processor.get_single_preprocessor],
- GET_MANY=[auth_func,
- feed_processor.get_many_preprocessor],
- PUT_SINGLE=[auth_func],
- POST=[auth_func],
- DELETE=[auth_func]))
-application.register_blueprint(blueprint_feed)
bgstack15