mirror of
https://github.com/Casvt/MIND.git
synced 2026-02-19 11:54:46 -05:00
258 lines
7.7 KiB
Python
258 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# autopep8: off
|
|
|
|
from os.path import dirname
|
|
from subprocess import run
|
|
from sys import path
|
|
from typing import List, Type, Union
|
|
|
|
path.insert(0, dirname(dirname(__file__)))
|
|
|
|
import frontend.api
|
|
from backend.base.custom_exceptions import (NotificationServiceNotFound,
|
|
ReminderNotFound, TemplateNotFound)
|
|
from backend.base.definitions import MindException, StartType
|
|
from backend.base.helpers import folder_path
|
|
from backend.internals.server import Server
|
|
from frontend.input_validation import API_DOCS, DataSource, InputVariable
|
|
|
|
# autopep8: on
|
|
|
|
API_PREFIX = Server.api_prefix
|
|
ADMIN_PREFIX = Server.admin_prefix
|
|
API_FILE = folder_path('docs', 'src', 'other_docs', 'api.md')
|
|
|
|
url_var_map = {
|
|
'int:n_id': NotificationServiceNotFound,
|
|
'int:r_id': ReminderNotFound,
|
|
'int:t_id': TemplateNotFound,
|
|
'int:s_id': ReminderNotFound
|
|
}
|
|
|
|
result = f"""# API
|
|
Below is the API documentation. Report an issue on [GitHub](https://github.com/Casvt/MIND/issues).
|
|
|
|
All endpoints have the `{API_PREFIX}` prefix. That means, for example, that `/auth/login` can be reached at `{API_PREFIX}/auth/login`.
|
|
|
|
## Authentication
|
|
|
|
Authentication is done using an API key.
|
|
To log in, make a POST request to the [`{API_PREFIX}/auth/login`](#authlogin) endpoint.
|
|
You'll receive an API key, which you can then use in your requests to authenticate.
|
|
Supply it via the url parameter `api_key`.
|
|
This API key is valid for one hour (though the admin can change this duration) after which the key expires, any further requests return 401 'APIKeyExpired' and you are required to log in again.
|
|
If no `api_key` is supplied or it is invalid, 401 `APIKeyInvalid` is returned.
|
|
|
|
For example:
|
|
```bash
|
|
curl -sSL 'http://192.168.2.15:8080{API_PREFIX}/reminders?api_key=ABCDEFG'
|
|
```
|
|
|
|
## Supplying data
|
|
|
|
Often, data needs to be supplied with a request:
|
|
|
|
- If the parameters need to be supplied via `url`, add them to the url as url parameters.
|
|
- If the parameters need to be supplied via `body`, add them to the body as a json object and supply the `Content-Type: application/json` header.
|
|
- If the parameters need to be supplied via `file`, send them as form data values and supply the `Content-Type: multipart/form-data` header.
|
|
|
|
For example:
|
|
```bash
|
|
# URL parameter
|
|
curl -sSL 'http://192.168.2.15:8080{API_PREFIX}/reminders/search?api_key=ABCDEFG&query=Fountain&sort_by=time_reversed'
|
|
|
|
# Body parameter
|
|
curl -sSLX POST \\
|
|
-H 'Content-Type: application/json' \\
|
|
-d '{{"title": "Test service", "url": "test://fake/url"}}' \\
|
|
'http://192.168.2.15:8080{API_PREFIX}/notificationservices?api_key=ABCDEFG'
|
|
|
|
# File parameter
|
|
curl -sSLX POST \\
|
|
-H 'Content-Type: multipart/form-data' \\
|
|
-F file=@/backups/MIND_backup.db \\
|
|
'http://192.168.2.15:8080{ADMIN_PREFIX}/database?api_key=ABCDEFG'
|
|
|
|
```
|
|
|
|
## Endpoints
|
|
The following is automatically generated. Please report any issues on [GitHub](https://github.com/Casvt/MIND/issues).
|
|
"""
|
|
|
|
|
|
def make_exception_instance(cls: Type[MindException]) -> MindException:
|
|
for args in (
|
|
(),
|
|
('1'),
|
|
('1', '2'),
|
|
('1', StartType.STARTUP)
|
|
):
|
|
try:
|
|
inst = cls(*args)
|
|
except (TypeError, AttributeError):
|
|
continue
|
|
else:
|
|
return inst
|
|
|
|
raise RuntimeError("Unsupported exception parameter")
|
|
|
|
|
|
def extract_url_var(endpoint: str) -> Union[str, None]:
|
|
split = endpoint.replace('<', '>').split('>')
|
|
return None if len(split) == 1 else split[1]
|
|
|
|
|
|
def rule_header(endpoint: str, requires_auth: bool, description: str) -> str:
|
|
return f"""### `{endpoint}`
|
|
|
|
| Requires being logged in | Description |
|
|
| ------------------------ | ----------- |
|
|
| {'Yes' if requires_auth else 'No'} | {description} |
|
|
"""
|
|
|
|
|
|
def url_var_note(endpoint: str, url_var: str) -> str:
|
|
return f"""
|
|
Replace `<{url_var}>` with the ID of the entry. For example: `{endpoint.replace(f'<{url_var}>', '2')}`.
|
|
"""
|
|
|
|
|
|
def method_body(name: str, description: str) -> str:
|
|
r = f"\n??? {name.upper()}\n"
|
|
|
|
if description:
|
|
r += f"\n {description}\n"
|
|
|
|
return r
|
|
|
|
|
|
def method_parameters(
|
|
var_type: str,
|
|
variables: List[Type[InputVariable]]
|
|
) -> str:
|
|
r = f"""
|
|
**Parameters ({var_type})**
|
|
|
|
| Name | Required | Data type | Description | Allowed values |
|
|
| ---- | -------- | --------- | ----------- | -------------- |
|
|
"""
|
|
|
|
for var in variables:
|
|
r += " | %s | %s | %s | %s | %s |\n" % (
|
|
var.name, 'Yes' if var.required else 'No',
|
|
','.join(v.value for v in var.data_type), var.description,
|
|
", ".join(str(v) for v in var.options) or "N/A"
|
|
)
|
|
|
|
return r
|
|
|
|
|
|
def return_codes(
|
|
method_name: str,
|
|
exceptions: List[MindException]
|
|
) -> str:
|
|
r = f"""
|
|
**Returns**
|
|
|
|
| Code | Error | Description |
|
|
| ---- | ----- | ----------- |
|
|
| {201 if method_name == 'post' else 200} | N/A | Success |
|
|
"""
|
|
|
|
for exception in exceptions:
|
|
r += " | %d | %s | %s |\n" % (
|
|
exception.api_response['code'], exception.api_response['error'],
|
|
exception.__doc__
|
|
)
|
|
|
|
return r
|
|
|
|
|
|
def create_result(base_string: str) -> str:
|
|
for endpoint, data in API_DOCS.items():
|
|
# Add header
|
|
base_string += rule_header(
|
|
endpoint,
|
|
data.requires_auth,
|
|
data.description
|
|
)
|
|
|
|
# Add note about url var
|
|
url_var = extract_url_var(endpoint)
|
|
if url_var:
|
|
base_string += url_var_note(endpoint, url_var)
|
|
|
|
# Add info for each method
|
|
for m_name, method in (
|
|
(m, data.methods[m])
|
|
for m in data.methods.used_methods()
|
|
):
|
|
if method is None:
|
|
continue
|
|
|
|
# Add basic method info
|
|
base_string += method_body(m_name, method.description)
|
|
|
|
# Add input variable info
|
|
var_types = {
|
|
'url': [
|
|
v for v in method.input_variables if v.source == DataSource.VALUES
|
|
],
|
|
'body': [
|
|
v for v in method.input_variables if v.source == DataSource.DATA
|
|
],
|
|
'file': [
|
|
v for v in method.input_variables if v.source == DataSource.FILES
|
|
]
|
|
}
|
|
for var_type, entries in var_types.items():
|
|
if entries:
|
|
base_string += method_parameters(var_type, entries)
|
|
|
|
url_exception = (
|
|
[url_var_map[url_var]]
|
|
if url_var in url_var_map else
|
|
[]
|
|
)
|
|
variable_exceptions = [
|
|
e
|
|
for v in method.input_variables
|
|
for e in v.related_exceptions
|
|
]
|
|
related_exceptions = sorted(
|
|
(
|
|
make_exception_instance(e)
|
|
for e in set(variable_exceptions + url_exception)
|
|
),
|
|
key=lambda e: (
|
|
e.api_response["code"],
|
|
e.api_response["error"]
|
|
)
|
|
)
|
|
base_string += return_codes(m_name, related_exceptions)
|
|
|
|
base_string += '\n'
|
|
return base_string.strip()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
result = create_result(result)
|
|
|
|
with open(API_FILE, 'r') as f:
|
|
current_content = f.read()
|
|
|
|
if current_content == result:
|
|
print('Nothing changed')
|
|
else:
|
|
with open(API_FILE, 'w+') as f:
|
|
f.write(result)
|
|
|
|
# run(["git", "config", "--global", "user.email", '"casvantijn@gmail.com"'])
|
|
# run(["git", "config", "--global", "user.name", '"CasVT"'])
|
|
# run(["git", "checkout", "Development"])
|
|
# run(["git", "add", API_FILE])
|
|
# run(["git", "commit", "-m", "Updated API docs"])
|
|
# run(["git", "push"])
|