Add delete functionality
This commit is contained in:
parent
fee5801db6
commit
58a94c365d
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue