Add listing
This commit is contained in:
parent
8dd63e33f0
commit
fee5801db6
|
@ -46,8 +46,8 @@ class Slinky:
|
||||||
Class for Slinky
|
Class for Slinky
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, url: str) -> None:
|
def __init__(self, db_url: str) -> None:
|
||||||
self.db = db.ShortcodeDB(url) # pylint: disable=invalid-name
|
self.db = db.ShortcodeDB(db_url) # pylint: disable=invalid-name
|
||||||
self.session = self.db.session()
|
self.session = self.db.session()
|
||||||
|
|
||||||
def add(
|
def add(
|
||||||
|
@ -118,3 +118,15 @@ class Slinky:
|
||||||
{db.ShortURL.fixed_views: db.ShortURL.fixed_views - 1}
|
{db.ShortURL.fixed_views: db.ShortURL.fixed_views - 1}
|
||||||
)
|
)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
|
def get_all(self) -> list[Shortcode]:
|
||||||
|
"""
|
||||||
|
Return a Shortcode object for a given shortcode
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shortcode (str): the shortcode to look up
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Shortcode: full Shortcode object for the given shortcode
|
||||||
|
"""
|
||||||
|
return list(self.session.query(db.ShortURL).all())
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{% include '_head.html' %}
|
{% include '_head.html' -%}
|
||||||
|
|
||||||
<!-- Begin page content -->
|
<!-- Begin page content -->
|
||||||
<main class="container">
|
<main class="container">
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<form action="/_/add" method="post">
|
<form action="/_/add" method="post">
|
||||||
{{ form.url.label }} {{ form.url }}<br />
|
{{ form.url.label }} {{ form.url }}<br />
|
||||||
{{ form.length.label }} {{ form.length }}<br />
|
{{ form.length.label }} {{ form.length }}<br />
|
||||||
{{ form.fixed_views.label }} {{ form.fixed_views }} (0 = unlimited)<br />
|
{{ form.fixed_views.label }} {{ form.fixed_views }} (-1 for unlimited)<br />
|
||||||
{{ form.expiry.label}} {{ form.expiry(class='datepicker') }} (leave as default for unlimited)<br />
|
{{ form.expiry.label}} {{ form.expiry(class='datepicker') }} (leave as default for unlimited)<br />
|
||||||
|
|
||||||
<button id="submit" class="btn btn-primary" type="submit" onclick="waiting();" style="margin: 1em 0;">
|
<button id="submit" class="btn btn-primary" type="submit" onclick="waiting();" style="margin: 1em 0;">
|
||||||
|
@ -36,4 +36,4 @@
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{% include '_tail.html' %}
|
{% include '_tail.html' -%}
|
||||||
|
|
35
slinky/templates/list.html
Normal file
35
slinky/templates/list.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{% include '_head.html' -%}
|
||||||
|
|
||||||
|
<!-- Begin page content -->
|
||||||
|
<main class="container">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="mt-5">Add a shortcode</h1>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div id="content">
|
||||||
|
{% if shortcodes -%}
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Shortcode</th>
|
||||||
|
<th scope="col">URL</th>
|
||||||
|
<th scope="col">Remaining views</th>
|
||||||
|
<th scope="col">Expiry date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for shortcode in shortcodes -%}
|
||||||
|
<tr>
|
||||||
|
<td>{{ shortcode.shortcode }}</td>
|
||||||
|
<td><a href="{{shortcode.url}}">{{ shortcode.url }}</a></td>
|
||||||
|
<td>{{ shortcode.fixed_views if shortcode.fixed_views >= 0 else 'Unlimited' }}</td>
|
||||||
|
<td>{{ shortcode.expiry if shortcode.expiry != '9999-12-31 23:59:59.999999' else 'None' }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor -%}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif -%}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{% include '_tail.html' -%}
|
|
@ -65,7 +65,7 @@ def try_path_as_shortcode(path: str) -> Response:
|
||||||
Try the initial path as a shortcode, redirect if found
|
Try the initial path as a shortcode, redirect if found
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Optional[Response]: redirect if found, otherwise continue on
|
Response: redirect if found, otherwise 404
|
||||||
"""
|
"""
|
||||||
should_redirect = True
|
should_redirect = True
|
||||||
slinky = Slinky(cfg['db'])
|
slinky = Slinky(cfg['db'])
|
||||||
|
@ -87,12 +87,12 @@ def try_path_as_shortcode(path: str) -> Response:
|
||||||
|
|
||||||
|
|
||||||
@slinky_webapp.route('/_/add', methods=['GET', 'POST'])
|
@slinky_webapp.route('/_/add', methods=['GET', 'POST'])
|
||||||
def add() -> str:
|
def add() -> Response:
|
||||||
"""
|
"""
|
||||||
Create and add a new shorturl
|
Create and add a new shorturl
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: shortcode for the URL
|
str: HTTP response
|
||||||
"""
|
"""
|
||||||
shortcode = ''
|
shortcode = ''
|
||||||
url = ''
|
url = ''
|
||||||
|
@ -107,11 +107,27 @@ def add() -> str:
|
||||||
|
|
||||||
if url:
|
if url:
|
||||||
slinky = Slinky(cfg['db'])
|
slinky = Slinky(cfg['db'])
|
||||||
while True:
|
for attempts in range(50):
|
||||||
try:
|
try:
|
||||||
shortcode = slinky.add(url, length, fixed_views, expiry)
|
shortcode = slinky.add(url, length, fixed_views, expiry)
|
||||||
break
|
break
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logging.warning('Shortcode already exists. Retrying.')
|
logging.warning(
|
||||||
|
'Shortcode already exists. Retrying (%d/50).', attempts
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return Response('Could not create a unique shortcode', 500)
|
||||||
|
|
||||||
return render_template('add.html', form=form, shortcode=shortcode)
|
return render_template('add.html', form=form, shortcode=shortcode)
|
||||||
|
|
||||||
|
|
||||||
|
@slinky_webapp.route('/_/list', methods=['GET', 'POST'])
|
||||||
|
def lister() -> str:
|
||||||
|
"""
|
||||||
|
Create and add a new shorturl
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: shortcode for the URL
|
||||||
|
"""
|
||||||
|
slinky = Slinky(cfg['db'])
|
||||||
|
return render_template('list.html', shortcodes=slinky.get_all())
|
||||||
|
|
|
@ -89,6 +89,9 @@
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/_/add">Add</a>
|
<a class="nav-link" href="/_/add">Add</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/_/list">List</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -56,3 +56,15 @@ class TestSlinky(TestCase):
|
||||||
Slinky(self.test_db).add,
|
Slinky(self.test_db).add,
|
||||||
'https://www.example.com',
|
'https://www.example.com',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_get_all(self) -> None:
|
||||||
|
"""
|
||||||
|
Ensure multiple results are returned and that they return all fields
|
||||||
|
"""
|
||||||
|
shortcodes = Slinky(self.test_db).get_all()
|
||||||
|
|
||||||
|
self.assertGreater(len(shortcodes), 1)
|
||||||
|
assert hasattr(shortcodes[0], 'shortcode')
|
||||||
|
assert hasattr(shortcodes[0], 'url')
|
||||||
|
assert hasattr(shortcodes[0], 'fixed_views')
|
||||||
|
assert hasattr(shortcodes[0], 'expiry')
|
||||||
|
|
|
@ -6,6 +6,7 @@ from unittest import TestCase, mock
|
||||||
|
|
||||||
from slinky import web
|
from slinky import web
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.dict('slinky.web.cfg', {'db': 'sqlite:///tests/test.db'})
|
@mock.patch.dict('slinky.web.cfg', {'db': 'sqlite:///tests/test.db'})
|
||||||
class TestWeb(TestCase):
|
class TestWeb(TestCase):
|
||||||
"""
|
"""
|
||||||
|
@ -33,3 +34,20 @@ class TestWeb(TestCase):
|
||||||
"""
|
"""
|
||||||
response = web.try_path_as_shortcode('egif')
|
response = web.try_path_as_shortcode('egif')
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
@mock.patch(
|
||||||
|
'slinky.web.ShortURLForm',
|
||||||
|
return_value=mock.Mock(
|
||||||
|
shortcode=mock.Mock(data=''),
|
||||||
|
url=mock.Mock(data='https://example.com'),
|
||||||
|
fixed_views=mock.Mock(data=0),
|
||||||
|
expiry=mock.Mock(data='1970-01-01 00:00:00.000000'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@mock.patch('slinky.random_string', return_value='egie')
|
||||||
|
def test_no_unique_shortcode(self, *_: Any) -> None:
|
||||||
|
"""
|
||||||
|
Ensure non-unique shortcodes return a 500 error
|
||||||
|
"""
|
||||||
|
response = web.add()
|
||||||
|
self.assertEqual(response.status_code, 500)
|
||||||
|
|
Loading…
Reference in a new issue