mirror of https://github.com/caronc/apprise
URL parameters preserved for attachment queries (#187)
parent
d1a15978af
commit
408810d6b4
|
@ -28,6 +28,7 @@ import time
|
|||
import mimetypes
|
||||
from ..URLBase import URLBase
|
||||
from ..utils import parse_bool
|
||||
from ..AppriseLocale import gettext_lazy as _
|
||||
|
||||
|
||||
class AttachBase(URLBase):
|
||||
|
@ -61,7 +62,35 @@ class AttachBase(URLBase):
|
|||
# 5 MB = 5242880 bytes
|
||||
max_file_size = 5242880
|
||||
|
||||
def __init__(self, name=None, mimetype=None, cache=True, **kwargs):
|
||||
# Here is where we define all of the arguments we accept on the url
|
||||
# such as: schema://whatever/?overflow=upstream&format=text
|
||||
# These act the same way as tokens except they are optional and/or
|
||||
# have default values set if mandatory. This rule must be followed
|
||||
template_args = {
|
||||
'cache': {
|
||||
'name': _('Cache Age'),
|
||||
'type': 'int',
|
||||
# We default to (600) which means we cache for 10 minutes
|
||||
'default': 600,
|
||||
},
|
||||
'mime': {
|
||||
'name': _('Forced Mime Type'),
|
||||
'type': 'string',
|
||||
},
|
||||
'name': {
|
||||
'name': _('Forced File Name'),
|
||||
'type': 'string',
|
||||
},
|
||||
'verify': {
|
||||
'name': _('Verify SSL'),
|
||||
# SSL Certificate Authority Verification
|
||||
'type': 'bool',
|
||||
# Provide a default
|
||||
'default': True,
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, name=None, mimetype=None, cache=None, **kwargs):
|
||||
"""
|
||||
Initialize some general logging and common server arguments that will
|
||||
keep things consistent when working with the configurations that
|
||||
|
@ -109,19 +138,27 @@ class AttachBase(URLBase):
|
|||
# Absolute path to attachment
|
||||
self.download_path = None
|
||||
|
||||
# Set our cache flag; it can be True or a (positive) integer
|
||||
try:
|
||||
self.cache = cache if isinstance(cache, bool) else int(cache)
|
||||
# Set our cache flag; it can be True, False, None, or a (positive)
|
||||
# integer... nothing else
|
||||
if cache is not None:
|
||||
try:
|
||||
self.cache = cache if isinstance(cache, bool) else int(cache)
|
||||
|
||||
except (TypeError, ValueError):
|
||||
err = 'An invalid cache value ({}) was specified.'.format(
|
||||
cache)
|
||||
self.logger.warning(err)
|
||||
raise TypeError(err)
|
||||
|
||||
# Some simple error checking
|
||||
if self.cache < 0:
|
||||
err = 'A negative cache value ({}) was specified.'.format(
|
||||
cache)
|
||||
self.logger.warning(err)
|
||||
raise TypeError(err)
|
||||
|
||||
except (ValueError, TypeError):
|
||||
err = 'An invalid cache value ({}) was specified.'.format(cache)
|
||||
self.logger.warning(err)
|
||||
raise TypeError(err)
|
||||
else:
|
||||
self.cache = None
|
||||
|
||||
# Validate mimetype if specified
|
||||
if self._mimetype:
|
||||
|
@ -211,12 +248,16 @@ class AttachBase(URLBase):
|
|||
Simply returns true if the object has downloaded and stored the
|
||||
attachment AND the attachment has not expired.
|
||||
"""
|
||||
|
||||
cache = self.template_args['cache']['default'] \
|
||||
if self.cache is None else self.cache
|
||||
|
||||
if self.download_path and os.path.isfile(self.download_path) \
|
||||
and self.cache:
|
||||
and cache:
|
||||
|
||||
# We have enough reason to look further into our cached content
|
||||
# and verify it has not expired.
|
||||
if self.cache is True:
|
||||
if cache is True:
|
||||
# return our fixed content as is; we will always cache it
|
||||
return True
|
||||
|
||||
|
@ -224,7 +265,7 @@ class AttachBase(URLBase):
|
|||
# content again.
|
||||
try:
|
||||
age_in_sec = time.time() - os.stat(self.download_path).st_mtime
|
||||
if age_in_sec <= self.cache:
|
||||
if age_in_sec <= cache:
|
||||
return True
|
||||
|
||||
except (OSError, IOError):
|
||||
|
|
|
@ -78,6 +78,11 @@ class AttachHTTP(AttachBase):
|
|||
# Where our content is written to upon a call to download.
|
||||
self._temp_file = None
|
||||
|
||||
# Our Query String Dictionary; we use this to track arguments
|
||||
# specified that aren't otherwise part of this class
|
||||
self.qsd = {k: v for k, v in kwargs.get('qsd', {}).items()
|
||||
if k not in self.template_args}
|
||||
|
||||
return
|
||||
|
||||
def download(self, **kwargs):
|
||||
|
@ -122,6 +127,7 @@ class AttachHTTP(AttachBase):
|
|||
url,
|
||||
headers=headers,
|
||||
auth=auth,
|
||||
params=self.qsd,
|
||||
verify=self.verify_certificate,
|
||||
timeout=self.connection_timeout_sec,
|
||||
stream=True) as r:
|
||||
|
@ -252,18 +258,21 @@ class AttachHTTP(AttachBase):
|
|||
Returns the URL built dynamically based on specified arguments.
|
||||
"""
|
||||
|
||||
# Prepare our cache value
|
||||
if isinstance(self.cache, bool) or not self.cache:
|
||||
cache = 'yes' if self.cache else 'no'
|
||||
else:
|
||||
cache = int(self.cache)
|
||||
|
||||
# Define any arguments set
|
||||
args = {
|
||||
'verify': 'yes' if self.verify_certificate else 'no',
|
||||
'cache': cache,
|
||||
}
|
||||
|
||||
# Prepare our cache value
|
||||
if self.cache is not None:
|
||||
if isinstance(self.cache, bool) or not self.cache:
|
||||
cache = 'yes' if self.cache else 'no'
|
||||
else:
|
||||
cache = int(self.cache)
|
||||
|
||||
# Set our cache value
|
||||
args['cache'] = cache
|
||||
|
||||
if self._mimetype:
|
||||
# A format was enforced
|
||||
args['mime'] = self._mimetype
|
||||
|
@ -275,6 +284,9 @@ class AttachHTTP(AttachBase):
|
|||
# Append our headers into our args
|
||||
args.update({'+{}'.format(k): v for k, v in self.headers.items()})
|
||||
|
||||
# Apply any remaining entries to our URL
|
||||
args.update(self.qsd)
|
||||
|
||||
# Determine Authentication
|
||||
auth = ''
|
||||
if self.user and self.password:
|
||||
|
@ -290,7 +302,7 @@ class AttachHTTP(AttachBase):
|
|||
|
||||
default_port = 443 if self.secure else 80
|
||||
|
||||
return '{schema}://{auth}{hostname}{port}{fullpath}/?{args}'.format(
|
||||
return '{schema}://{auth}{hostname}{port}{fullpath}?{args}'.format(
|
||||
schema=self.secure_protocol if self.secure else self.protocol,
|
||||
auth=auth,
|
||||
hostname=self.quote(self.host, safe=''),
|
||||
|
|
|
@ -78,14 +78,14 @@ def test_apprise_attachment():
|
|||
assert aa
|
||||
|
||||
# Add another entry already in it's AttachBase format
|
||||
response = AppriseAttachment.instantiate(path)
|
||||
response = AppriseAttachment.instantiate(path, cache=True)
|
||||
assert isinstance(response, AttachBase)
|
||||
assert aa.add(response, asset=AppriseAsset())
|
||||
|
||||
# There is now 2 attachments
|
||||
assert len(aa) == 2
|
||||
|
||||
# No cache set, so our cache defaults to True
|
||||
# Cache is initialized to True
|
||||
assert aa[1].cache is True
|
||||
|
||||
# Reset our object
|
||||
|
@ -173,6 +173,10 @@ def test_apprise_attachment():
|
|||
assert not aa.add(AppriseAttachment.instantiate(
|
||||
'file://{}?name=andanother.png&cache=-600'.format(path)))
|
||||
|
||||
# Invalid cache value
|
||||
assert not aa.add(AppriseAttachment.instantiate(
|
||||
'file://{}?name=andanother.png'.format(path), cache='invalid'))
|
||||
|
||||
# No length change
|
||||
assert len(aa) == 3
|
||||
|
||||
|
|
|
@ -73,6 +73,39 @@ def test_attach_http_parse_url():
|
|||
assert AttachHTTP.parse_url('http://') is None
|
||||
|
||||
|
||||
def test_attach_http_query_string_dictionary():
|
||||
"""
|
||||
API: AttachHTTP() Query String Dictionary
|
||||
|
||||
"""
|
||||
|
||||
# no qsd specified
|
||||
results = AttachHTTP.parse_url('http://localhost')
|
||||
assert isinstance(results, dict)
|
||||
|
||||
# Create our object
|
||||
obj = AttachHTTP(**results)
|
||||
assert isinstance(obj, AttachHTTP)
|
||||
|
||||
assert re.search(r'[?&]verify=yes', obj.url())
|
||||
|
||||
# Now lets create a URL with a custom Query String entry
|
||||
|
||||
# some custom qsd entries specified
|
||||
results = AttachHTTP.parse_url('http://localhost?dl=1&_var=test')
|
||||
assert isinstance(results, dict)
|
||||
|
||||
# Create our object
|
||||
obj = AttachHTTP(**results)
|
||||
assert isinstance(obj, AttachHTTP)
|
||||
|
||||
assert re.search(r'[?&]verify=yes', obj.url())
|
||||
|
||||
# But now test that our custom arguments have also been set
|
||||
assert re.search(r'[?&]dl=1', obj.url())
|
||||
assert re.search(r'[?&]_var=test', obj.url())
|
||||
|
||||
|
||||
@mock.patch('requests.get')
|
||||
def test_attach_http(mock_get):
|
||||
"""
|
||||
|
@ -147,8 +180,26 @@ def test_attach_http(mock_get):
|
|||
dummy_response = DummyResponse()
|
||||
mock_get.return_value = dummy_response
|
||||
|
||||
# Test custom url get parameters
|
||||
results = AttachHTTP.parse_url(
|
||||
'http://user:pass@localhost/apprise.gif?+key=value')
|
||||
'http://user:pass@localhost/apprise.gif?dl=1&cache=300')
|
||||
assert isinstance(results, dict)
|
||||
attachment = AttachHTTP(**results)
|
||||
assert isinstance(attachment.url(), six.string_types) is True
|
||||
|
||||
# Test that our extended variables are passed along
|
||||
assert mock_get.call_count == 0
|
||||
assert attachment
|
||||
assert mock_get.call_count == 1
|
||||
assert 'params' in mock_get.call_args_list[0][1]
|
||||
assert 'dl' in mock_get.call_args_list[0][1]['params']
|
||||
|
||||
# Verify that arguments that are reserved for apprise are not
|
||||
# passed along
|
||||
assert 'cache' not in mock_get.call_args_list[0][1]['params']
|
||||
|
||||
results = AttachHTTP.parse_url(
|
||||
'http://user:pass@localhost/apprise.gif?+key=value&cache=True')
|
||||
assert isinstance(results, dict)
|
||||
attachment = AttachHTTP(**results)
|
||||
assert isinstance(attachment.url(), six.string_types) is True
|
||||
|
|
Loading…
Reference in New Issue