From b01ee24d551ef74b5a4207c3be74317f054ff35f Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Thu, 19 May 2022 15:14:36 +0200 Subject: [PATCH] POC --- changedetectionio/__init__.py | 24 +++++++++++- changedetectionio/api_v1.py | 72 +++++++++++++++++++++++++++++++++++ changedetectionio/store.py | 7 +++- requirements.txt | 1 + 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 changedetectionio/api_v1.py diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py index 9df27eb2..7f8c1a69 100644 --- a/changedetectionio/__init__.py +++ b/changedetectionio/__init__.py @@ -36,9 +36,11 @@ from flask import ( url_for, ) from flask_login import login_required +from flask_restful import reqparse, abort, Api, Resource + from flask_wtf import CSRFProtect -from changedetectionio import html_tools +from changedetectionio import api_v1, html_tools __version__ = '0.39.13.1' @@ -78,6 +80,8 @@ csrf.init_app(app) notification_debug_log=[] +watch_api = Api(app, decorators=[csrf.exempt]) + def init_app_secret(datastore_path): secret = "" @@ -179,6 +183,24 @@ def changedetection_app(config=None, datastore_o=None): login_manager.login_view = 'login' app.secret_key = init_app_secret(config['datastore_path']) + + watch_api.add_resource(api_v1.WatchSingleHistory, + '/api/v1/watch//history/', + resource_class_kwargs={'datastore': datastore}) + + watch_api.add_resource(api_v1.WatchHistory, + '/api/v1/watch//history', + resource_class_kwargs={'datastore': datastore}) + + watch_api.add_resource(api_v1.CreateWatch, '/api/v1/watch', + resource_class_kwargs={'datastore': datastore}) + + watch_api.add_resource(api_v1.Watch, '/api/v1/watch/', resource_class_kwargs={'datastore': datastore}) + + + + + # Setup cors headers to allow all domains # https://flask-cors.readthedocs.io/en/latest/ # CORS(app) diff --git a/changedetectionio/api_v1.py b/changedetectionio/api_v1.py new file mode 100644 index 00000000..65906545 --- /dev/null +++ b/changedetectionio/api_v1.py @@ -0,0 +1,72 @@ +from flask_restful import reqparse, abort, Api, Resource +from flask import request +# https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + +class Watch(Resource): + def __init__(self, **kwargs): + # datastore is a black box dependency + self.datastore = kwargs['datastore'] + + def get(self, uuid): + watch = self.datastore.data['watching'].get(uuid) + if not watch: + abort(404, message='No watch exists with the UUID of {}'.format(uuid)) + + return watch + + def delete(self, uuid): + if not self.datastore.data['watching'].get(uuid): + abort(400, message='No watch exists with the UUID of {}'.format(uuid)) + + self.datastore.delete(uuid) + return '', 204 + + + + +class WatchHistory(Resource): + def __init__(self, **kwargs): + # datastore is a black box dependency + self.datastore = kwargs['datastore'] + + def get(self, uuid, timestamp): + watch = self.datastore.data['watching'].get(uuid) + if not watch: + abort(404, message='No watch exists with the UUID of {}'.format(uuid)) + + def delete(self, timestamp): + # Delete all history by timestamp or 'all' + return '', 204 + return watch + +class WatchSingleHistory(Resource): + def __init__(self, **kwargs): + # datastore is a black box dependency + self.datastore = kwargs['datastore'] + + def get(self, uuid, timestamp): + watch = self.datastore.data['watching'].get(uuid) + if not watch: + abort(404, message='No watch exists with the UUID of {}'.format(uuid)) + + return watch + + def delete(self, uuid, timestamp): + if not self.datastore.data['watching'].get(uuid): + abort(400, message='No watch exists with the UUID of {}'.format(uuid)) + + self.datastore.delete(uuid) + return '', 204 + +class CreateWatch(Resource): + def __init__(self, **kwargs): + # datastore is a black box dependency + self.datastore = kwargs['datastore'] + + def post(self): + # "Fields" for validation? + tag = request.form.get('tag', '') + new_uuid = self.datastore.add_watch(url=request.form.get('url').strip(), tag=tag) + return new_uuid, 201 + + diff --git a/changedetectionio/store.py b/changedetectionio/store.py index 5c41f8ca..848ff490 100644 --- a/changedetectionio/store.py +++ b/changedetectionio/store.py @@ -211,7 +211,8 @@ class ChangeDetectionStore: def get_all_tags(self): tags = [] for uuid, watch in self.data['watching'].items(): - + if watch['tag'] is None: + continue # Support for comma separated list of tags. for tag in watch['tag'].split(','): tag = tag.strip() @@ -280,6 +281,10 @@ class ChangeDetectionStore: def add_watch(self, url, tag="", extras=None, write_to_disk_now=True): if extras is None: extras = {} + # should always be str + if tag is None or not tag: + tag='' + # Incase these are copied across, assume it's a reference and deepcopy() apply_extras = deepcopy(extras) diff --git a/requirements.txt b/requirements.txt index 468dca88..9ce85a48 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ timeago ~=1.0 inscriptis ~= 2.2 feedgen ~= 0.9 flask-login ~= 0.5 +flask_restful pytz # Set these versions together to avoid a RequestsDependencyWarning