Add delete functionality

This commit is contained in:
Scott Wallace 2021-12-28 15:13:45 +00:00
parent fee5801db6
commit 58a94c365d
Signed by: scott
GPG key ID: AA742FDC5AFE2A72
6 changed files with 47 additions and 11 deletions

View file

@ -71,7 +71,7 @@ class Slinky:
""" """
shortcode = random_string(length=length) shortcode = random_string(length=length)
if self.get(shortcode).url: if self.get_by_shortcode(shortcode).url:
raise ValueError(f'Shortcode {shortcode} already exists') raise ValueError(f'Shortcode {shortcode} already exists')
dbentry = db.ShortURL( dbentry = db.ShortURL(
@ -85,7 +85,7 @@ class Slinky:
return shortcode return shortcode
def get(self, shortcode: str) -> Shortcode: def get_by_shortcode(self, shortcode: str) -> Shortcode:
""" """
Return a Shortcode object for a given shortcode Return a Shortcode object for a given shortcode
@ -130,3 +130,15 @@ class Slinky:
Shortcode: full Shortcode object for the given shortcode Shortcode: full Shortcode object for the given shortcode
""" """
return list(self.session.query(db.ShortURL).all()) return list(self.session.query(db.ShortURL).all())
def delete_by_shortcode(self, shortcode: str) -> None:
"""
Delete shortcode entry
Args:
shortcode (str): Shortcode of entry to delete
"""
entry = self.session.query(db.ShortURL).filter_by(shortcode=shortcode).first()
self.session.delete(entry)
self.session.commit()

View file

@ -28,7 +28,7 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{{request.host_url}}{{ shortcode }}</td> <td><a href="{{request.host_url}}{{ shortcode }}">{{request.host_url}}{{ shortcode }}</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View file

@ -15,6 +15,7 @@
<th scope="col">URL</th> <th scope="col">URL</th>
<th scope="col">Remaining views</th> <th scope="col">Remaining views</th>
<th scope="col">Expiry date</th> <th scope="col">Expiry date</th>
<th scope="col"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -24,6 +25,14 @@
<td><a href="{{shortcode.url}}">{{ shortcode.url }}</a></td> <td><a href="{{shortcode.url}}">{{ shortcode.url }}</a></td>
<td>{{ shortcode.fixed_views if shortcode.fixed_views >= 0 else 'Unlimited' }}</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> <td>{{ shortcode.expiry if shortcode.expiry != '9999-12-31 23:59:59.999999' else 'None' }}</td>
<td>
<form action="/_/list" method="post">
<input id="delete" type="hidden" name="delete" value="{{ shortcode.shortcode }}" />
<button id="submit" class="btn btn-danger" type="submit">
Delete
</button>
</form>
</td>
</tr> </tr>
{% endfor -%} {% endfor -%}
</tbody> </tbody>

View file

@ -8,7 +8,7 @@ from datetime import datetime
import yaml import yaml
from flask import Blueprint, Response, redirect, render_template from flask import Blueprint, Response, redirect, render_template
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import DateTimeLocalField, IntegerField, StringField from wtforms import DateTimeLocalField, HiddenField, IntegerField, StringField
from wtforms.validators import DataRequired, Length from wtforms.validators import DataRequired, Length
from slinky import Slinky from slinky import Slinky
@ -19,9 +19,17 @@ with open('config.yaml', encoding='utf-8-sig') as conffile:
cfg = yaml.safe_load(conffile) cfg = yaml.safe_load(conffile)
class ShortURLForm(FlaskForm): # type: ignore[misc] class DelForm(FlaskForm): # type: ignore[misc]
""" """
Web form definition Delete form definition
"""
delete = HiddenField('delete')
class AddForm(FlaskForm): # type: ignore[misc]
"""
Add form definition
""" """
url = StringField( url = StringField(
@ -69,7 +77,7 @@ def try_path_as_shortcode(path: str) -> Response:
""" """
should_redirect = True should_redirect = True
slinky = Slinky(cfg['db']) slinky = Slinky(cfg['db'])
shortcode = slinky.get(path) shortcode = slinky.get_by_shortcode(path)
if shortcode.url: if shortcode.url:
if shortcode.fixed_views == 0: if shortcode.fixed_views == 0:
logging.warning('Shortcode out of views') logging.warning('Shortcode out of views')
@ -97,7 +105,7 @@ def add() -> Response:
shortcode = '' shortcode = ''
url = '' url = ''
form = ShortURLForm(meta={'csrf': False}) form = AddForm(meta={'csrf': False})
if form.is_submitted(): if form.is_submitted():
url = form.url.data.strip() url = form.url.data.strip()
@ -129,5 +137,10 @@ def lister() -> str:
Returns: Returns:
str: shortcode for the URL str: shortcode for the URL
""" """
form = DelForm(meta={'csrf': False})
slinky = Slinky(cfg['db']) slinky = Slinky(cfg['db'])
return render_template('list.html', shortcodes=slinky.get_all())
if form.is_submitted():
slinky.delete_by_shortcode(form.delete.data.strip())
return render_template('list.html', form=form, shortcodes=slinky.get_all())

View file

@ -43,7 +43,9 @@ class TestSlinky(TestCase):
Ensure we can fetch a URL for a known shortcode Ensure we can fetch a URL for a known shortcode
""" """
self.assertEqual('https://example.com', Slinky(self.test_db).get('egie').url) self.assertEqual(
'https://example.com', Slinky(self.test_db).get_by_shortcode('egie').url
)
@mock.patch('sqlalchemy.orm.session.Session.add', return_value=None) @mock.patch('sqlalchemy.orm.session.Session.add', return_value=None)
@mock.patch('slinky.random_string', return_value='egie') @mock.patch('slinky.random_string', return_value='egie')

View file

@ -47,7 +47,7 @@ class TestWeb(TestCase):
@mock.patch('slinky.random_string', return_value='egie') @mock.patch('slinky.random_string', return_value='egie')
def test_no_unique_shortcode(self, *_: Any) -> None: def test_no_unique_shortcode(self, *_: Any) -> None:
""" """
Ensure non-unique shortcodes return a 500 error Ensure non-unique shortcode generation returns a 500 error
""" """
response = web.add() response = web.add()
self.assertEqual(response.status_code, 500) self.assertEqual(response.status_code, 500)