Add listing

This commit is contained in:
Scott Wallace 2021-12-28 13:51:57 +00:00
parent 8dd63e33f0
commit fee5801db6
Signed by: scott
GPG key ID: AA742FDC5AFE2A72
7 changed files with 106 additions and 10 deletions

View file

@ -46,8 +46,8 @@ class Slinky:
Class for Slinky
"""
def __init__(self, url: str) -> None:
self.db = db.ShortcodeDB(url) # pylint: disable=invalid-name
def __init__(self, db_url: str) -> None:
self.db = db.ShortcodeDB(db_url) # pylint: disable=invalid-name
self.session = self.db.session()
def add(
@ -118,3 +118,15 @@ class Slinky:
{db.ShortURL.fixed_views: db.ShortURL.fixed_views - 1}
)
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())

View file

@ -1,4 +1,4 @@
{% include '_head.html' %}
{% include '_head.html' -%}
<!-- Begin page content -->
<main class="container">
@ -9,7 +9,7 @@
<form action="/_/add" method="post">
{{ form.url.label }} {{ form.url }}<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 />
<button id="submit" class="btn btn-primary" type="submit" onclick="waiting();" style="margin: 1em 0;">
@ -36,4 +36,4 @@
</div>
</main>
{% include '_tail.html' %}
{% include '_tail.html' -%}

View 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' -%}

View file

@ -65,7 +65,7 @@ def try_path_as_shortcode(path: str) -> Response:
Try the initial path as a shortcode, redirect if found
Returns:
Optional[Response]: redirect if found, otherwise continue on
Response: redirect if found, otherwise 404
"""
should_redirect = True
slinky = Slinky(cfg['db'])
@ -87,12 +87,12 @@ def try_path_as_shortcode(path: str) -> Response:
@slinky_webapp.route('/_/add', methods=['GET', 'POST'])
def add() -> str:
def add() -> Response:
"""
Create and add a new shorturl
Returns:
str: shortcode for the URL
str: HTTP response
"""
shortcode = ''
url = ''
@ -107,11 +107,27 @@ def add() -> str:
if url:
slinky = Slinky(cfg['db'])
while True:
for attempts in range(50):
try:
shortcode = slinky.add(url, length, fixed_views, expiry)
break
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)
@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())

View file

@ -89,6 +89,9 @@
<li class="nav-item">
<a class="nav-link" href="/_/add">Add</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/_/list">List</a>
</li>
</ul>
</div>
</div>

View file

@ -56,3 +56,15 @@ class TestSlinky(TestCase):
Slinky(self.test_db).add,
'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')

View file

@ -6,6 +6,7 @@ from unittest import TestCase, mock
from slinky import web
@mock.patch.dict('slinky.web.cfg', {'db': 'sqlite:///tests/test.db'})
class TestWeb(TestCase):
"""
@ -33,3 +34,20 @@ class TestWeb(TestCase):
"""
response = web.try_path_as_shortcode('egif')
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)