diff --git a/README.md b/README.md
index 03e76a5ea..395e3ac80 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@ Know when ...
- Festivals with changes
- Realestate listing changes
- COVID related news from government websites
+- Detect and monitor changes in JSON API responses
+- API monitoring and alerting
_Need an actual Chrome runner with Javascript support? see the experimental Javascript/Chrome support changedetection.io branch!_
@@ -88,9 +90,21 @@ Just some examples
+### JSON API Monitoring
+
+Detect changes and monitor data in JSON API's by using the built-in JSONPath selectors as a filter.
+
+
+
+
+This will re-parse the JSON and apply indent to the text, making it super easy to monitor and detect changes in JSON API results
+
+
+
+
### Proxy
-A proxy for ChangeDectection.io can be configured by setting environment the
+A proxy for ChangeDetection.io can be configured by setting environment the
`HTTP_PROXY`, `HTTPS_PROXY` variables, examples are also in the `docker-compose.yml`
`NO_PROXY` exclude list can be specified by following `"localhost,192.168.0.0/24"`
diff --git a/backend/fetch_site_status.py b/backend/fetch_site_status.py
index 12216e196..dcefab156 100644
--- a/backend/fetch_site_status.py
+++ b/backend/fetch_site_status.py
@@ -88,12 +88,27 @@ class perform_site_check():
html = r.text
- # CSS Filter, extract the HTML that matches and feed that into the existing inscriptis::get_text
+ is_html = True
css_filter_rule = self.datastore.data['watching'][uuid]['css_filter']
if css_filter_rule and len(css_filter_rule.strip()):
- html = html_tools.css_filter(css_filter=css_filter_rule, html_content=r.content)
+ if 'json:' in css_filter_rule:
+ # POC hack, @todo rename vars, see how it fits in with the javascript version
+ import json
+ from jsonpath_ng import jsonpath, parse
- stripped_text_from_html = get_text(html)
+ json_data = json.loads(html)
+ jsonpath_expression = parse(css_filter_rule.replace('json:',''))
+ match = jsonpath_expression.find(json_data)
+ stripped_text_from_html = json.dumps(match[0].value, indent=4)
+
+ is_html = False
+
+ else:
+ # CSS Filter, extract the HTML that matches and feed that into the existing inscriptis::get_text
+ html = html_tools.css_filter(css_filter=css_filter_rule, html_content=r.content)
+
+ if is_html:
+ stripped_text_from_html = get_text(html)
# Usually from networkIO/requests level
except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout) as e:
diff --git a/backend/forms.py b/backend/forms.py
index ea8fd9951..72bda4222 100644
--- a/backend/forms.py
+++ b/backend/forms.py
@@ -82,7 +82,7 @@ class StringDictKeyValue(StringField):
else:
self.data = {}
-class ListRegex(object):
+class ValidateListRegex(object):
"""
Validates that anything that looks like a regex passes as a regex
"""
@@ -102,6 +102,28 @@ class ListRegex(object):
message = field.gettext('RegEx \'%s\' is not a valid regular expression.')
raise ValidationError(message % (line))
+class ValidateCSSJSONInput(object):
+ """
+ Filter validation
+ @todo CSS validator ;)
+ """
+
+ def __init__(self, message=None):
+ self.message = message
+
+ def __call__(self, form, field):
+ if 'json:' in field.data:
+ from jsonpath_ng.exceptions import JsonPathParserError
+ from jsonpath_ng import jsonpath, parse
+
+ input = field.data.replace('json:', '')
+
+ try:
+ parse(input)
+ except JsonPathParserError as e:
+ message = field.gettext('\'%s\' is not a valid JSONPath expression. (%s)')
+ raise ValidationError(message % (input, str(e)))
+
class watchForm(Form):
# https://wtforms.readthedocs.io/en/2.3.x/fields/#module-wtforms.fields.html5
@@ -111,10 +133,10 @@ class watchForm(Form):
tag = StringField('Tag', [validators.Optional(), validators.Length(max=35)])
minutes_between_check = html5.IntegerField('Maximum time in minutes until recheck',
[validators.Optional(), validators.NumberRange(min=1)])
- css_filter = StringField('CSS Filter')
+ css_filter = StringField('CSS/JSON Filter', [ValidateCSSJSONInput()])
title = StringField('Title')
- ignore_text = StringListField('Ignore Text', [ListRegex()])
+ ignore_text = StringListField('Ignore Text', [ValidateListRegex()])
notification_urls = StringListField('Notification URL List')
headers = StringDictKeyValue('Request Headers')
trigger_check = BooleanField('Send test notification on save')
diff --git a/backend/templates/edit.html b/backend/templates/edit.html
index b62754771..cdeb139bb 100644
--- a/backend/templates/edit.html
+++ b/backend/templates/edit.html
@@ -23,9 +23,12 @@