Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions converters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from .base import ContentConverterBase, RequestDecodeError, KeyProvider
from .json_converter import JsonConverter
from .xml_converter import XmlConverter
from .plain_text_converter import PlainTextConverter
from .ignore_converter import IgnoreConverter

CONVERTERS: dict[str, ContentConverterBase] = {
"json": JsonConverter(),
"xml": XmlConverter(),
"plain_text": PlainTextConverter(),
"ignore": IgnoreConverter(),
}

__all__ = [
"CONVERTERS",
"ContentConverterBase",
"RequestDecodeError",
"KeyProvider",
]
32 changes: 32 additions & 0 deletions converters/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from abc import ABCMeta, abstractmethod
from typing import Protocol
import requests


class KeyProvider(Protocol):
@property
@abstractmethod
def keys(self) -> str:
pass


class RequestDecodeError(ValueError):
def __init__(self, inner: Exception, *args: object) -> None:
super().__init__(*args)
self.inner = inner


class ContentConverterBase(metaclass=ABCMeta):
@property
@abstractmethod
def supports_keys(self) -> bool:
pass

@property
@abstractmethod
def mime_type(self) -> str | None:
pass

@abstractmethod
def get_button_text(self, provider: KeyProvider, response: requests.Response) -> str | None:
pass
12 changes: 12 additions & 0 deletions converters/ignore_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import requests
from .base import ContentConverterBase, KeyProvider


class IgnoreConverter(ContentConverterBase):
supports_keys = False
mime_type = None

def get_button_text(self, provider: KeyProvider, response: requests.Response):
_ = provider
_ = response
return None
25 changes: 25 additions & 0 deletions converters/json_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import json
import mimetypes
import requests
from .base import ContentConverterBase, KeyProvider, RequestDecodeError

class JsonConverter(ContentConverterBase):
supports_keys = True
mime_type = mimetypes.types_map[".json"]

def get_button_text(self, provider: KeyProvider, response: requests.Response):
j = None
try:
j = json.loads(response.text)
except json.decoder.JSONDecodeError as e:
raise RequestDecodeError(e) from e
return JsonConverter.get_value(j, provider.keys)

@staticmethod
def get_value(j: dict | None, keys: str):
if j:
for key in keys.split('.'):
j = j.get(key)
if not j:
return None
return j
14 changes: 14 additions & 0 deletions converters/plain_text_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import mimetypes
import requests
from .base import ContentConverterBase, KeyProvider

class PlainTextConverter(ContentConverterBase):
supports_keys = False
mime_type = mimetypes.types_map[".txt"]

def get_button_text(self, provider: KeyProvider, response: requests.Response):
_ = provider
text = response.text
if text:
text = text.strip()
return text
18 changes: 18 additions & 0 deletions converters/xml_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import mimetypes
import requests
from .base import ContentConverterBase, KeyProvider, RequestDecodeError
from defusedxml import ElementTree

class XmlConverter(ContentConverterBase):
supports_keys = True
mime_type = mimetypes.types_map[".xml"]
_ignore_namespaces = {"": "*"}

def get_button_text(self, provider: KeyProvider, response: requests.Response):
xml = ElementTree.fromstring(response.text)
xpath = provider.keys
element = xml.find(xpath, self._ignore_namespaces)
# `Element`s are false-ly, when they have no children, thus explicit check agains None
if element is not None and element.text:
return element.text
return None
14 changes: 13 additions & 1 deletion locales/de_DE.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
{
"plugin.name": "Requests",
"actions.post.url.title": "URL",
"actions.post.json.title": "JSON"
"actions.post.json.title": "JSON",
"actions.request.http_method.title": "HTTP-Methode",
"actions.request.body_type.title": "Body-Datentyp",
"actions.request.reply_type.title": "Antwort-Datentyp",
"actions.get.keys_entry.title": "Antwort-Schlüssel",
"convert.list_item.json": "JSON",
"convert.list_item.xml": "XML",
"convert.list_item.plain_text": "reiner Text",
"convert.list_item.ignore": "(ignorieren)",
"custom_config.keys_hint.label.json": "Trenne Schlüssel mit einem Punkt (Beispiel: key1.key2.key3)",
"custom_config.keys_hint.label.xml": "Schlüssel ist eine vereinfachte Version von XPath.\nNamespaces können bei Bedarf via '{..}' explizit angegeben, und mit '{}' explizit deaktiviert werden.\n(Beispiel: ./{http://example.com/some/namesapce}key1/key2)",
"custom_config.keys_hint.label.plain_text": "",
"custom_config.keys_hint.label.ignore": ""
}
14 changes: 13 additions & 1 deletion locales/en_US.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
{
"plugin.name": "Requests",
"actions.post.url.title": "URL",
"actions.post.json.title": "JSON"
"actions.post.json.title": "JSON",
"actions.request.http_method.title": "HTTP method",
"actions.request.body_type.title": "body data type",
"actions.request.reply_type.title": "reply data type",
"actions.get.keys_entry.title": "reply keys",
"convert.list_item.json": "JSON",
"convert.list_item.xml": "XML",
"convert.list_item.plain_text": "plain text",
"convert.list_item.ignore": "(ignore)",
"custom_config.keys_hint.label.json": "Separate keys with a period (example: key1.key2.key3)",
"custom_config.keys_hint.label.xml": "Keys are a simplified version of XPath.\nNamespaces can be set explizitlcy via '{..}', and disabled with '{}'.\n(Example: ./{http://example.com/some/namesapce}key1/key2)",
"custom_config.keys_hint.label.plain_text": "",
"custom_config.keys_hint.label.ignore": ""
}
35 changes: 24 additions & 11 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from src.backend.PluginManager.ActionHolder import ActionHolder
from src.backend.DeckManagement.InputIdentifier import Input
from src.backend.PluginManager.ActionInputSupport import ActionInputSupport
from .multi_request import MultiRequest

# Import gtk modules
import gi
Expand All @@ -31,7 +32,7 @@
class PostRequest(ActionBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def on_ready(self):
self.set_media(media_path=os.path.join(self.plugin_base.PATH, "assets", "http.png"), size=0.9)

Expand All @@ -46,12 +47,12 @@ def get_config_rows(self) -> list:
self.json_entry.connect("notify::text", self.on_json_changed)

return [self.url_entry, self.json_entry]

def on_url_changed(self, entry, *args):
settings = self.get_settings()
settings["url"] = entry.get_text()
self.set_settings(settings)

def on_json_changed(self, entry, *args):
settings = self.get_settings()
settings["json"] = entry.get_text()
Expand All @@ -78,9 +79,9 @@ def on_key_down(self):
class GetRequest(ActionBase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.n_ticks = 0

def on_ready(self):
self.set_media(media_path=os.path.join(self.plugin_base.PATH, "assets", "http.png"), size=0.8)

Expand All @@ -101,7 +102,7 @@ def get_config_rows(self) -> list:
self.auto_fetch_spinner.connect("notify::value", self.on_auto_fetch_changed)

return [self.url_entry, self.headers_entry, self.keys_entry, self.auto_fetch_spinner]

def on_url_changed(self, entry, *args):
settings = self.get_settings()
settings["url"] = entry.get_text()
Expand Down Expand Up @@ -162,22 +163,20 @@ def get_value(self, j, keys):
j = j.get(key)

return j

def get_custom_config_area(self):
return Gtk.Label(label="Separate keys with a period (example: key1.key2.key3)")

def on_tick(self):
auto_fetch = self.get_settings().get("auto_fetch", 0)
if auto_fetch <= 0:
self.n_ticks = 0
return

if self.n_ticks % auto_fetch == 0:
self.on_key_down()
self.n_ticks = 0
self.n_ticks += 1




class RequestsPlugin(PluginBase):
Expand Down Expand Up @@ -217,6 +216,20 @@ def __init__(self):
)
self.add_action_holder(self.get_request_holder)

self.multi_request_holder = ActionHolder(
plugin_base=self,
action_base=MultiRequest,
action_id_suffix="MultiRequest",
action_name="Multi Request",
icon=Gtk.Picture.new_for_filename(os.path.join(self.PATH, "assets", "http.png")),
action_support={
Input.Key: ActionInputSupport.SUPPORTED,
Input.Dial: ActionInputSupport.SUPPORTED,
Input.Touchscreen: ActionInputSupport.UNTESTED
}
)
self.add_action_holder(self.multi_request_holder)

# Register plugin
self.register(
plugin_name=self.lm.get("plugin.name"),
Expand Down
Loading