Compare commits

..

3 Commits

Author SHA1 Message Date
dgtlmoon
a0ab1ab6be Update README.md 2025-08-23 23:56:35 +02:00
dgtlmoon
f4f716fffa More openAPI fixes 2025-08-23 23:53:06 +02:00
dgtlmoon
6e6136aaa7 Lets go openapi spec 2025-08-23 23:32:53 +02:00
12 changed files with 190 additions and 284 deletions

View File

@@ -15,10 +15,6 @@ jobs:
ruff check . --select E9,F63,F7,F82 ruff check . --select E9,F63,F7,F82
# Complete check with errors treated as warnings # Complete check with errors treated as warnings
ruff check . --exit-zero ruff check . --exit-zero
- name: Validate OpenAPI spec
run: |
pip install openapi-spec-validator
python3 -c "from openapi_spec_validator import validate_spec; import yaml; validate_spec(yaml.safe_load(open('docs/api-spec.yaml')))"
test-application-3-10: test-application-3-10:
needs: lint-code needs: lint-code

View File

@@ -3,7 +3,7 @@ from changedetectionio.strtobool import strtobool
from flask_restful import abort, Resource from flask_restful import abort, Resource
from flask import request from flask import request
import validators import validators
from . import auth, validate_openapi_request from . import auth
class Import(Resource): class Import(Resource):
@@ -12,7 +12,6 @@ class Import(Resource):
self.datastore = kwargs['datastore'] self.datastore = kwargs['datastore']
@auth.check_token @auth.check_token
@validate_openapi_request('importWatches')
def post(self): def post(self):
"""Import a list of watched URLs.""" """Import a list of watched URLs."""

View File

@@ -1,7 +1,9 @@
from flask_expects_json import expects_json from flask_expects_json import expects_json
from flask_restful import Resource, abort from flask_restful import Resource
from . import auth
from flask_restful import abort, Resource
from flask import request from flask import request
from . import auth, validate_openapi_request from . import auth
from . import schema_create_notification_urls, schema_delete_notification_urls from . import schema_create_notification_urls, schema_delete_notification_urls
class Notifications(Resource): class Notifications(Resource):
@@ -10,7 +12,6 @@ class Notifications(Resource):
self.datastore = kwargs['datastore'] self.datastore = kwargs['datastore']
@auth.check_token @auth.check_token
@validate_openapi_request('getNotifications')
def get(self): def get(self):
"""Return Notification URL List.""" """Return Notification URL List."""
@@ -21,7 +22,6 @@ class Notifications(Resource):
}, 200 }, 200
@auth.check_token @auth.check_token
@validate_openapi_request('addNotifications')
@expects_json(schema_create_notification_urls) @expects_json(schema_create_notification_urls)
def post(self): def post(self):
"""Create Notification URLs.""" """Create Notification URLs."""
@@ -49,7 +49,6 @@ class Notifications(Resource):
return {'notification_urls': added_urls}, 201 return {'notification_urls': added_urls}, 201
@auth.check_token @auth.check_token
@validate_openapi_request('replaceNotifications')
@expects_json(schema_create_notification_urls) @expects_json(schema_create_notification_urls)
def put(self): def put(self):
"""Replace Notification URLs.""" """Replace Notification URLs."""
@@ -72,7 +71,6 @@ class Notifications(Resource):
return {'notification_urls': clean_urls}, 200 return {'notification_urls': clean_urls}, 200
@auth.check_token @auth.check_token
@validate_openapi_request('deleteNotifications')
@expects_json(schema_delete_notification_urls) @expects_json(schema_delete_notification_urls)
def delete(self): def delete(self):
"""Delete Notification URLs.""" """Delete Notification URLs."""

View File

@@ -1,6 +1,6 @@
from flask_restful import Resource, abort from flask_restful import Resource, abort
from flask import request from flask import request
from . import auth, validate_openapi_request from . import auth
class Search(Resource): class Search(Resource):
def __init__(self, **kwargs): def __init__(self, **kwargs):
@@ -8,7 +8,6 @@ class Search(Resource):
self.datastore = kwargs['datastore'] self.datastore = kwargs['datastore']
@auth.check_token @auth.check_token
@validate_openapi_request('searchWatches')
def get(self): def get(self):
"""Search for watches by URL or title text.""" """Search for watches by URL or title text."""
query = request.args.get('q', '').strip() query = request.args.get('q', '').strip()

View File

@@ -1,5 +1,5 @@
from flask_restful import Resource from flask_restful import Resource
from . import auth, validate_openapi_request from . import auth
class SystemInfo(Resource): class SystemInfo(Resource):
@@ -9,7 +9,6 @@ class SystemInfo(Resource):
self.update_q = kwargs['update_q'] self.update_q = kwargs['update_q']
@auth.check_token @auth.check_token
@validate_openapi_request('getSystemInfo')
def get(self): def get(self):
"""Return system info.""" """Return system info."""
import time import time

View File

@@ -7,7 +7,7 @@ from flask import request
from . import auth from . import auth
# Import schemas from __init__.py # Import schemas from __init__.py
from . import schema_tag, schema_create_tag, schema_update_tag, validate_openapi_request from . import schema_tag, schema_create_tag, schema_update_tag
class Tag(Resource): class Tag(Resource):
@@ -19,7 +19,6 @@ class Tag(Resource):
# Get information about a single tag # Get information about a single tag
# curl http://localhost:5000/api/v1/tag/<string:uuid> # curl http://localhost:5000/api/v1/tag/<string:uuid>
@auth.check_token @auth.check_token
@validate_openapi_request('getTag')
def get(self, uuid): def get(self, uuid):
"""Get data for a single tag/group, toggle notification muting, or recheck all.""" """Get data for a single tag/group, toggle notification muting, or recheck all."""
from copy import deepcopy from copy import deepcopy
@@ -51,7 +50,6 @@ class Tag(Resource):
return tag return tag
@auth.check_token @auth.check_token
@validate_openapi_request('deleteTag')
def delete(self, uuid): def delete(self, uuid):
"""Delete a tag/group and remove it from all watches.""" """Delete a tag/group and remove it from all watches."""
if not self.datastore.data['settings']['application']['tags'].get(uuid): if not self.datastore.data['settings']['application']['tags'].get(uuid):
@@ -68,7 +66,6 @@ class Tag(Resource):
return 'OK', 204 return 'OK', 204
@auth.check_token @auth.check_token
@validate_openapi_request('updateTag')
@expects_json(schema_update_tag) @expects_json(schema_update_tag)
def put(self, uuid): def put(self, uuid):
"""Update tag information.""" """Update tag information."""
@@ -83,7 +80,6 @@ class Tag(Resource):
@auth.check_token @auth.check_token
@validate_openapi_request('createTag')
# Only cares for {'title': 'xxxx'} # Only cares for {'title': 'xxxx'}
def post(self): def post(self):
"""Create a single tag/group.""" """Create a single tag/group."""
@@ -104,7 +100,6 @@ class Tags(Resource):
self.datastore = kwargs['datastore'] self.datastore = kwargs['datastore']
@auth.check_token @auth.check_token
@validate_openapi_request('listTags')
def get(self): def get(self):
"""List tags/groups.""" """List tags/groups."""
result = {} result = {}

View File

@@ -11,7 +11,7 @@ from . import auth
import copy import copy
# Import schemas from __init__.py # Import schemas from __init__.py
from . import schema, schema_create_watch, schema_update_watch, validate_openapi_request from . import schema, schema_create_watch, schema_update_watch
class Watch(Resource): class Watch(Resource):
@@ -25,7 +25,6 @@ class Watch(Resource):
# @todo - version2 - ?muted and ?paused should be able to be called together, return the watch struct not "OK" # @todo - version2 - ?muted and ?paused should be able to be called together, return the watch struct not "OK"
# ?recheck=true # ?recheck=true
@auth.check_token @auth.check_token
@validate_openapi_request('getWatch')
def get(self, uuid): def get(self, uuid):
"""Get information about a single watch, recheck, pause, or mute.""" """Get information about a single watch, recheck, pause, or mute."""
from copy import deepcopy from copy import deepcopy
@@ -58,7 +57,6 @@ class Watch(Resource):
return watch return watch
@auth.check_token @auth.check_token
@validate_openapi_request('deleteWatch')
def delete(self, uuid): def delete(self, uuid):
"""Delete a watch and related history.""" """Delete a watch and related history."""
if not self.datastore.data['watching'].get(uuid): if not self.datastore.data['watching'].get(uuid):
@@ -68,7 +66,6 @@ class Watch(Resource):
return 'OK', 204 return 'OK', 204
@auth.check_token @auth.check_token
@validate_openapi_request('updateWatch')
@expects_json(schema_update_watch) @expects_json(schema_update_watch)
def put(self, uuid): def put(self, uuid):
"""Update watch information.""" """Update watch information."""
@@ -94,7 +91,6 @@ class WatchHistory(Resource):
# Get a list of available history for a watch by UUID # Get a list of available history for a watch by UUID
# curl http://localhost:5000/api/v1/watch/<string:uuid>/history # curl http://localhost:5000/api/v1/watch/<string:uuid>/history
@auth.check_token @auth.check_token
@validate_openapi_request('getWatchHistory')
def get(self, uuid): def get(self, uuid):
"""Get a list of all historical snapshots available for a watch.""" """Get a list of all historical snapshots available for a watch."""
watch = self.datastore.data['watching'].get(uuid) watch = self.datastore.data['watching'].get(uuid)
@@ -109,7 +105,6 @@ class WatchSingleHistory(Resource):
self.datastore = kwargs['datastore'] self.datastore = kwargs['datastore']
@auth.check_token @auth.check_token
@validate_openapi_request('getWatchSnapshot')
def get(self, uuid, timestamp): def get(self, uuid, timestamp):
"""Get single snapshot from watch.""" """Get single snapshot from watch."""
watch = self.datastore.data['watching'].get(uuid) watch = self.datastore.data['watching'].get(uuid)
@@ -143,7 +138,6 @@ class WatchFavicon(Resource):
self.datastore = kwargs['datastore'] self.datastore = kwargs['datastore']
@auth.check_token @auth.check_token
@validate_openapi_request('getWatchFavicon')
def get(self, uuid): def get(self, uuid):
"""Get favicon for a watch.""" """Get favicon for a watch."""
watch = self.datastore.data['watching'].get(uuid) watch = self.datastore.data['watching'].get(uuid)
@@ -178,7 +172,6 @@ class CreateWatch(Resource):
self.update_q = kwargs['update_q'] self.update_q = kwargs['update_q']
@auth.check_token @auth.check_token
@validate_openapi_request('createWatch')
@expects_json(schema_create_watch) @expects_json(schema_create_watch)
def post(self): def post(self):
"""Create a single watch.""" """Create a single watch."""
@@ -214,7 +207,6 @@ class CreateWatch(Resource):
return "Invalid or unsupported URL", 400 return "Invalid or unsupported URL", 400
@auth.check_token @auth.check_token
@validate_openapi_request('listWatches')
def get(self): def get(self):
"""List watches.""" """List watches."""
list = {} list = {}

View File

@@ -1,9 +1,4 @@
import copy import copy
import yaml
import functools
from flask import request, abort
from openapi_core import OpenAPI
from openapi_core.contrib.flask import FlaskOpenAPIRequest
from . import api_schema from . import api_schema
from ..model import watch_base from ..model import watch_base
@@ -30,38 +25,6 @@ schema_create_notification_urls['required'] = ['notification_urls']
schema_delete_notification_urls = copy.deepcopy(schema_notification_urls) schema_delete_notification_urls = copy.deepcopy(schema_notification_urls)
schema_delete_notification_urls['required'] = ['notification_urls'] schema_delete_notification_urls['required'] = ['notification_urls']
# Load OpenAPI spec for validation
_openapi_spec = None
def get_openapi_spec():
global _openapi_spec
if _openapi_spec is None:
import os
spec_path = os.path.join(os.path.dirname(__file__), '../../docs/api-spec.yaml')
with open(spec_path, 'r') as f:
spec_dict = yaml.safe_load(f)
_openapi_spec = OpenAPI.from_dict(spec_dict)
return _openapi_spec
def validate_openapi_request(operation_id):
"""Decorator to validate incoming requests against OpenAPI spec."""
def decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
try:
spec = get_openapi_spec()
openapi_request = FlaskOpenAPIRequest(request)
result = spec.unmarshal_request(openapi_request, operation_id)
if result.errors:
abort(400, message=f"OpenAPI validation failed: {result.errors}")
return f(*args, **kwargs)
except Exception as e:
# If OpenAPI validation fails, log but don't break existing functionality
print(f"OpenAPI validation warning for {operation_id}: {e}")
return f(*args, **kwargs)
return wrapper
return decorator
# Import all API resources # Import all API resources
from .Watch import Watch, WatchHistory, WatchSingleHistory, CreateWatch, WatchFavicon from .Watch import Watch, WatchHistory, WatchSingleHistory, CreateWatch, WatchFavicon
from .Tags import Tags, Tag from .Tags import Tags, Tag

View File

@@ -203,7 +203,7 @@ nav
<div class="tab-pane-inner" id="api"> <div class="tab-pane-inner" id="api">
<h4>API Access</h4> <h4>API Access</h4>
<p>Drive your changedetection.io via API, More about <a href="https://changedetection.io/docs/api_v1/index.html">API access and examples here</a>.</p> <p>Drive your changedetection.io via API, More about <a href="https://github.com/dgtlmoon/changedetection.io/wiki/API-Reference">API access here</a></p>
<div class="pure-control-group"> <div class="pure-control-group">
{{ render_checkbox_field(form.application.form.api_access_token_enabled) }} {{ render_checkbox_field(form.application.form.api_access_token_enabled) }}

View File

@@ -28,7 +28,7 @@ info:
For example: `x-api-key: YOUR_API_KEY` For example: `x-api-key: YOUR_API_KEY`
version: 0.1.0 version: 1.0.0
contact: contact:
name: ChangeDetection.io name: ChangeDetection.io
url: https://github.com/dgtlmoon/changedetection.io url: https://github.com/dgtlmoon/changedetection.io
@@ -90,8 +90,6 @@ tags:
notification services including email, Discord, Slack, webhooks, and many other popular platforms. notification services including email, Discord, Slack, webhooks, and many other popular platforms.
These settings serve as defaults that can be overridden at the individual watch or tag level. These settings serve as defaults that can be overridden at the individual watch or tag level.
The notification syntax uses [https://github.com/caronc/apprise](https://github.com/caronc/apprise).
- name: Search - name: Search
description: | description: |
Search and filter your watches by URL patterns, titles, or tags. Useful for quickly finding specific Search and filter your watches by URL patterns, titles, or tags. Useful for quickly finding specific
@@ -125,7 +123,7 @@ components:
uuid: uuid:
type: string type: string
format: uuid format: uuid
description: Unique identifier for the web page change monitor (watch) description: Unique identifier for the watch
readOnly: true readOnly: true
url: url:
type: string type: string
@@ -134,11 +132,11 @@ components:
maxLength: 5000 maxLength: 5000
title: title:
type: string type: string
description: Custom title for the web page change monitor (watch) description: Custom title for the watch
maxLength: 5000 maxLength: 5000
tag: tag:
type: string type: string
description: Tag UUID to associate with this web page change monitor (watch) description: Tag UUID to associate with this watch
maxLength: 5000 maxLength: 5000
tags: tags:
type: array type: array
@@ -147,7 +145,7 @@ components:
description: Array of tag UUIDs description: Array of tag UUIDs
paused: paused:
type: boolean type: boolean
description: Whether the web page change monitor (watch) is paused description: Whether the watch is paused
muted: muted:
type: boolean type: boolean
description: Whether notifications are muted description: Whether notifications are muted
@@ -197,7 +195,7 @@ components:
type: array type: array
items: items:
type: string type: string
description: Notification URLs for this web page change monitor (watch) description: Notification URLs for this watch
notification_title: notification_title:
type: string type: string
description: Custom notification title description: Custom notification title
@@ -267,7 +265,7 @@ components:
type: array type: array
items: items:
type: string type: string
description: Default notification URLs for web page change monitors (watches) with this tag description: Default notification URLs for watches with this tag
notification_muted: notification_muted:
type: boolean type: boolean
description: Whether notifications are muted for this tag description: Whether notifications are muted for this tag
@@ -291,7 +289,7 @@ components:
properties: properties:
watch_count: watch_count:
type: integer type: integer
description: Total number of web page change monitors (watches) description: Total number of watches
tag_count: tag_count:
type: integer type: integer
description: Total number of tags description: Total number of tags
@@ -309,7 +307,7 @@ components:
type: object type: object
additionalProperties: additionalProperties:
$ref: '#/components/schemas/Watch' $ref: '#/components/schemas/Watch'
description: Dictionary of matching web page change monitors (watches) keyed by UUID description: Dictionary of matching watches keyed by UUID
WatchHistory: WatchHistory:
type: object type: object
@@ -328,10 +326,9 @@ components:
paths: paths:
/watch: /watch:
get: get:
operationId: listWatches
tags: [Watch Management] tags: [Watch Management]
summary: List all watches summary: List all watches
description: Return concise list of available web page change monitors (watches) and basic info description: Return concise list of available watches and basic info
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -391,10 +388,9 @@ paths:
last_checked: 1640998800 last_checked: 1640998800
last_changed: 1640995200 last_changed: 1640995200
post: post:
operationId: createWatch
tags: [Watch Management] tags: [Watch Management]
summary: Create a new watch summary: Create a new watch
description: Create a single web page change monitor (watch). Requires at least 'url' to be set. description: Create a single watch. Requires at least 'url' to be set.
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -440,7 +436,7 @@ paths:
hours: 1 hours: 1
responses: responses:
'200': '200':
description: Web page change monitor (watch) created successfully description: Watch created successfully
content: content:
text/plain: text/plain:
schema: schema:
@@ -455,10 +451,10 @@ paths:
/watch/{uuid}: /watch/{uuid}:
get: get:
operationId: getWatch operationId: getSingleWatch
tags: [Watch Management] tags: [Watch Management]
summary: Get single watch summary: Get single watch
description: Retrieve web page change monitor (watch) information and set muted/paused status. Returns the FULL Watch JSON. description: Retrieve watch information and set muted/paused status. Returns the FULL Watch JSON.
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -476,13 +472,13 @@ paths:
- name: uuid - name: uuid
in: path in: path
required: true required: true
description: Web page change monitor (watch) unique ID description: Watch unique ID
schema: schema:
type: string type: string
format: uuid format: uuid
- name: recheck - name: recheck
in: query in: query
description: Recheck this web page change monitor (watch) description: Recheck this watch
schema: schema:
type: string type: string
enum: ["1", "true"] enum: ["1", "true"]
@@ -510,7 +506,7 @@ paths:
type: string type: string
example: "OK" example: "OK"
'404': '404':
description: Web page change monitor (watch) not found description: Watch not found
content: content:
application/json: application/json:
schema: schema:
@@ -520,7 +516,7 @@ paths:
operationId: updateWatch operationId: updateWatch
tags: [Watch Management] tags: [Watch Management]
summary: Update watch summary: Update watch
description: Update an existing web page change monitor (watch) using JSON. Accepts the same structure as returned in [get single watch information](#operation/getWatch). description: Update an existing watch using JSON. Accepts the same structure as returned in [get single watch information](#operation/getSingleWatch).
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -553,7 +549,7 @@ paths:
- name: uuid - name: uuid
in: path in: path
required: true required: true
description: Web page change monitor (watch) unique ID description: Watch unique ID
schema: schema:
type: string type: string
format: uuid format: uuid
@@ -565,7 +561,7 @@ paths:
$ref: '#/components/schemas/Watch' $ref: '#/components/schemas/Watch'
responses: responses:
'200': '200':
description: Web page change monitor (watch) updated successfully description: Watch updated successfully
content: content:
text/plain: text/plain:
schema: schema:
@@ -575,10 +571,9 @@ paths:
description: Server error description: Server error
delete: delete:
operationId: deleteWatch
tags: [Watch Management] tags: [Watch Management]
summary: Delete watch summary: Delete watch
description: Delete a web page change monitor (watch) and all related history description: Delete a watch and all related history
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -596,13 +591,13 @@ paths:
- name: uuid - name: uuid
in: path in: path
required: true required: true
description: Web page change monitor (watch) unique ID description: Watch unique ID
schema: schema:
type: string type: string
format: uuid format: uuid
responses: responses:
'200': '200':
description: Web page change monitor (watch) deleted successfully description: Watch deleted successfully
content: content:
text/plain: text/plain:
schema: schema:
@@ -611,10 +606,9 @@ paths:
/watch/{uuid}/history: /watch/{uuid}/history:
get: get:
operationId: getWatchHistory
tags: [Watch History] tags: [Watch History]
summary: Get watch history summary: Get watch history
description: Get a list of all historical snapshots available for a web page change monitor (watch) description: Get a list of all historical snapshots available for a watch
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -632,7 +626,7 @@ paths:
- name: uuid - name: uuid
in: path in: path
required: true required: true
description: Web page change monitor (watch) unique ID description: Watch unique ID
schema: schema:
type: string type: string
format: uuid format: uuid
@@ -647,14 +641,13 @@ paths:
"1640995200": "/path/to/snapshot1.txt" "1640995200": "/path/to/snapshot1.txt"
"1640998800": "/path/to/snapshot2.txt" "1640998800": "/path/to/snapshot2.txt"
'404': '404':
description: Web page change monitor (watch) not found description: Watch not found
/watch/{uuid}/history/{timestamp}: /watch/{uuid}/history/{timestamp}:
get: get:
operationId: getWatchSnapshot
tags: [Snapshots] tags: [Snapshots]
summary: Get single snapshot summary: Get single snapshot
description: Get single snapshot from web page change monitor (watch). Use 'latest' for the most recent snapshot. description: Get single snapshot from watch. Use 'latest' for the most recent snapshot.
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -673,7 +666,7 @@ paths:
- name: uuid - name: uuid
in: path in: path
required: true required: true
description: Web page change monitor (watch) unique ID description: Watch unique ID
schema: schema:
type: string type: string
format: uuid format: uuid
@@ -704,10 +697,9 @@ paths:
/watch/{uuid}/favicon: /watch/{uuid}/favicon:
get: get:
operationId: getWatchFavicon
tags: [Favicon] tags: [Favicon]
summary: Get watch favicon summary: Get watch favicon
description: Get the favicon for a web page change monitor (watch) as displayed in the watch overview list. description: Get the favicon for a watch as displayed in the watch overview list.
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -727,7 +719,7 @@ paths:
- name: uuid - name: uuid
in: path in: path
required: true required: true
description: Web page change monitor (watch) unique ID description: Watch unique ID
schema: schema:
type: string type: string
format: uuid format: uuid
@@ -744,7 +736,6 @@ paths:
/tags: /tags:
get: get:
operationId: listTags
tags: [Group / Tag Management] tags: [Group / Tag Management]
summary: List all tags summary: List all tags
description: Return list of available tags/groups description: Return list of available tags/groups
@@ -781,62 +772,11 @@ paths:
notification_urls: ["discord://webhook_id/webhook_token"] notification_urls: ["discord://webhook_id/webhook_token"]
notification_muted: false notification_muted: false
/tag:
post:
operationId: createTag
tags: [Group / Tag Management]
summary: Create tag
description: Create a single tag/group
x-code-samples:
- lang: 'curl'
source: |
curl -X POST "http://localhost:5000/api/v1/tag" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Important Sites"
}'
- lang: 'Python'
source: |
import requests
headers = {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
}
data = {'title': 'Important Sites'}
response = requests.post('http://localhost:5000/api/v1/tag',
headers=headers, json=data)
print(response.json())
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
example:
title: "Important Sites"
responses:
'201':
description: Tag created successfully
content:
application/json:
schema:
type: object
properties:
uuid:
type: string
format: uuid
description: UUID of the created tag
'400':
description: Invalid or unsupported tag
/tag/{uuid}: /tag/{uuid}:
get: get:
operationId: getTag
tags: [Group / Tag Management] tags: [Group / Tag Management]
summary: Get single tag summary: Get single tag
description: Retrieve tag information, set notification_muted status, recheck all web page change monitors (watches) in tag. description: Retrieve tag information, set notification_muted status, recheck all in tag.
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -866,7 +806,7 @@ paths:
enum: [muted, unmuted] enum: [muted, unmuted]
- name: recheck - name: recheck
in: query in: query
description: Queue all web page change monitors (watches) with this tag for recheck description: Queue all watches with this tag for recheck
schema: schema:
type: string type: string
enum: ["true"] enum: ["true"]
@@ -885,7 +825,6 @@ paths:
description: Tag not found description: Tag not found
put: put:
operationId: updateTag
tags: [Group / Tag Management] tags: [Group / Tag Management]
summary: Update tag summary: Update tag
description: Update an existing tag using JSON description: Update an existing tag using JSON
@@ -936,10 +875,9 @@ paths:
description: Server error description: Server error
delete: delete:
operationId: deleteTag
tags: [Group / Tag Management] tags: [Group / Tag Management]
summary: Delete tag summary: Delete tag
description: Delete a tag/group and remove it from all web page change monitors (watches) description: Delete a tag/group and remove it from all watches
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -965,10 +903,48 @@ paths:
'200': '200':
description: Tag deleted successfully description: Tag deleted successfully
post:
tags: [Group / Tag Management]
summary: Create tag
description: Create a single tag/group
x-code-samples:
- lang: 'curl'
source: |
curl -X POST "http://localhost:5000/api/v1/tag/550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Important Sites"
}'
- lang: 'Python'
source: |
import requests
headers = {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
}
tag_uuid = '550e8400-e29b-41d4-a716-446655440000'
data = {'title': 'Important Sites'}
response = requests.post(f'http://localhost:5000/api/v1/tag/{tag_uuid}',
headers=headers, json=data)
print(response.text)
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Tag'
example:
title: "Important Sites"
responses:
'200':
description: Tag created successfully
'500':
description: Server error
/notifications: /notifications:
get: get:
operationId: getNotifications
tags: [Notifications] tags: [Notifications]
summary: Get notification URLs summary: Get notification URLs
description: Return the notification URL list from the configuration description: Return the notification URL list from the configuration
@@ -993,7 +969,6 @@ paths:
$ref: '#/components/schemas/NotificationUrls' $ref: '#/components/schemas/NotificationUrls'
post: post:
operationId: addNotifications
tags: [Notifications] tags: [Notifications]
summary: Add notification URLs summary: Add notification URLs
description: Add one or more notification URLs to the configuration description: Add one or more notification URLs to the configuration
@@ -1047,7 +1022,6 @@ paths:
description: Invalid input description: Invalid input
put: put:
operationId: replaceNotifications
tags: [Notifications] tags: [Notifications]
summary: Replace notification URLs summary: Replace notification URLs
description: Replace all notification URLs with the provided list (can be empty) description: Replace all notification URLs with the provided list (can be empty)
@@ -1095,7 +1069,6 @@ paths:
description: Invalid input description: Invalid input
delete: delete:
operationId: deleteNotifications
tags: [Notifications] tags: [Notifications]
summary: Delete notification URLs summary: Delete notification URLs
description: Delete one or more notification URLs from the configuration description: Delete one or more notification URLs from the configuration
@@ -1140,10 +1113,9 @@ paths:
/search: /search:
get: get:
operationId: searchWatches
tags: [Search] tags: [Search]
summary: Search watches summary: Search watches
description: Search web page change monitors (watches) by URL or title text description: Search watches by URL or title text
x-code-samples: x-code-samples:
- lang: 'curl' - lang: 'curl'
source: | source: |
@@ -1195,7 +1167,6 @@ paths:
/import: /import:
post: post:
operationId: importWatches
tags: [Import] tags: [Import]
summary: Import watch URLs summary: Import watch URLs
description: Import a list of URLs to monitor. Accepts line-separated URLs in request body. description: Import a list of URLs to monitor. Accepts line-separated URLs in request body.
@@ -1221,17 +1192,17 @@ paths:
parameters: parameters:
- name: tag_uuids - name: tag_uuids
in: query in: query
description: Tag UUID to apply to imported web page change monitors (watches) description: Tag UUID to apply to imported watches
schema: schema:
type: string type: string
- name: tag - name: tag
in: query in: query
description: Tag name to apply to imported web page change monitors (watches) description: Tag name to apply to imported watches
schema: schema:
type: string type: string
- name: proxy - name: proxy
in: query in: query
description: Proxy key to use for imported web page change monitors (watches) description: Proxy key to use for imported watches
schema: schema:
type: string type: string
- name: dedupe - name: dedupe
@@ -1266,7 +1237,6 @@ paths:
/systeminfo: /systeminfo:
get: get:
operationId: getSystemInfo
tags: [System Information] tags: [System Information]
summary: Get system information summary: Get system information
description: Return information about the current system state description: Return information about the current system state

File diff suppressed because one or more lines are too long

View File

@@ -89,9 +89,6 @@ pytest-flask ~=1.2
# Anything 4.0 and up but not 5.0 # Anything 4.0 and up but not 5.0
jsonschema ~= 4.0 jsonschema ~= 4.0
# OpenAPI validation support
openapi-core[flask] >= 0.19.0
loguru loguru