aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWojtek Kosior <koszko@koszko.org>2022-08-23 13:34:30 +0200
committerWojtek Kosior <koszko@koszko.org>2022-09-28 12:54:52 +0200
commite255c2f353ad16d3ed0460dabe84a11b119902da (patch)
tree4e7ca46d957a898cc7ffff7924fa16411560239f
parent7dc7b9c2c2d4b169ad545606e2fc7ef752a5d5a2 (diff)
downloadhaketilo-hydrilla-e255c2f353ad16d3ed0460dabe84a11b119902da.tar.gz
haketilo-hydrilla-e255c2f353ad16d3ed0460dabe84a11b119902da.zip
make it possible to change repo's name and URL using web UI
-rw-r--r--src/hydrilla/proxy/state_impl/repos.py72
-rw-r--r--src/hydrilla/proxy/web_ui/repos.py68
-rw-r--r--src/hydrilla/proxy/web_ui/templates/repos.html.jinja2
-rw-r--r--src/hydrilla/proxy/web_ui/templates/repos__show_single.html.jinja116
4 files changed, 164 insertions, 94 deletions
diff --git a/src/hydrilla/proxy/state_impl/repos.py b/src/hydrilla/proxy/state_impl/repos.py
index ae3b70c..2670ae9 100644
--- a/src/hydrilla/proxy/state_impl/repos.py
+++ b/src/hydrilla/proxy/state_impl/repos.py
@@ -54,6 +54,28 @@ from . import base
from . import _operations
+repo_name_regex = re.compile(r'''
+^
+(?:
+ []a-zA-Z0-9()<>^&$.!,?@#|;:%"'*{}[/_=+-]+ # allowed non-whitespace characters
+
+ (?: # optional additional words separated by single spaces
+ [ ]
+ []a-zA-Z0-9()<>^&$.!,?@#|;:%"'*{}[/_=+-]+
+ )*
+)
+$
+''', re.VERBOSE)
+
+def sanitize_repo_name(name: str) -> str:
+ name = name.strip()
+
+ if repo_name_regex.match(name) is None:
+ raise st.RepoNameInvalid()
+
+ return name
+
+
def sanitize_repo_url(url: str) -> str:
try:
parsed = urlparse(url)
@@ -220,20 +242,39 @@ class ConcreteRepoRef(st.RepoRef):
url: t.Optional[str] = None
) -> None:
if name is not None:
- raise NotImplementedError()
+ if name.isspace():
+ raise st.RepoNameInvalid()
- if url is None:
- return
+ name = sanitize_repo_name(name)
- url = sanitize_repo_url(url)
+ if url is not None:
+ if url.isspace():
+ raise st.RepoUrlInvalid()
+
+ url = sanitize_repo_url(url)
+
+ if name is None and url is None:
+ return
with self.state.cursor(transaction=True) as cursor:
ensure_repo_not_deleted(cursor, self.id)
- cursor.execute(
- 'UPDATE repos SET url = ? WHERE repo_id = ?;',
- (url, self.id)
- )
+ if url is not None:
+ cursor.execute(
+ 'UPDATE repos SET url = ? WHERE repo_id = ?;',
+ (url, self.id)
+ )
+
+ if name is not None:
+ try:
+ cursor.execute(
+ 'UPDATE repos SET name = ? WHERE repo_id = ?;',
+ (name, self.id)
+ )
+ except sqlite3.IntegrityError:
+ raise st.RepoNameTaken()
+
+ self.state.recompute_dependencies()
def refresh(self) -> st.RepoIterationRef:
with self.state.cursor(transaction=True) as cursor:
@@ -256,7 +297,7 @@ class ConcreteRepoRef(st.RepoRef):
RemoteFileResolver(repo_url)
)
- self.state.recompute_dependencies()
+ self.state.rebuild_structures()
cursor.execute(
'''
@@ -297,19 +338,6 @@ class ConcreteRepoRef(st.RepoRef):
return make_repo_display_info(self, *row)
-repo_name_regex = re.compile(r'''
-^
-(?:
- []a-zA-Z0-9()<>^&$.!,?@#|;:%"'*{}[/_=+-]+ # allowed non-whitespace characters
-
- (?: # optional additional words separated by single spaces
- [ ]
- []a-zA-Z0-9()<>^&$.!,?@#|;:%"'*{}[/_=+-]+
- )*
-)
-$
-''', re.VERBOSE)
-
@dc.dataclass(frozen=True)
class ConcreteRepoStore(st.RepoStore):
state: base.HaketiloStateWithFields
diff --git a/src/hydrilla/proxy/web_ui/repos.py b/src/hydrilla/proxy/web_ui/repos.py
index 166cf53..629e399 100644
--- a/src/hydrilla/proxy/web_ui/repos.py
+++ b/src/hydrilla/proxy/web_ui/repos.py
@@ -43,7 +43,7 @@ from . import _app
bp = flask.Blueprint('repos', __package__)
@bp.route('/repos/add', methods=['GET'])
-def add_repo_get(errors: t.Mapping[str, bool] = {}) -> werkzeug.Response:
+def add_repo(errors: t.Mapping[str, bool] = {}) -> werkzeug.Response:
html = flask.render_template('repos__add.html.jinja', **errors)
return flask.make_response(html, 200)
@@ -51,7 +51,7 @@ def add_repo_get(errors: t.Mapping[str, bool] = {}) -> werkzeug.Response:
def add_repo_post() -> werkzeug.Response:
form_data = flask.request.form
if 'name' not in form_data or 'url' not in form_data:
- return add_repo_get()
+ return add_repo()
try:
new_repo_ref = _app.get_haketilo_state().repo_store().add(
@@ -59,11 +59,11 @@ def add_repo_post() -> werkzeug.Response:
url = form_data['url']
)
except st.RepoNameInvalid:
- return add_repo_get({'repo_name_invalid': True})
+ return add_repo({'repo_name_invalid': True})
except st.RepoNameTaken:
- return add_repo_get({'repo_name_taken': True})
+ return add_repo({'repo_name_taken': True})
except st.RepoUrlInvalid:
- return add_repo_get({'repo_url_invalid': True})
+ return add_repo({'repo_url_invalid': True})
return flask.redirect(flask.url_for('.show_repo', repo_id=new_repo_ref.id))
@@ -81,56 +81,52 @@ def repos() -> werkzeug.Response:
return flask.make_response(html, 200)
@bp.route('/repos/view/<string:repo_id>')
-def show_repo(repo_id: str) -> werkzeug.Response:
+def show_repo(repo_id: str, errors: t.Mapping[str, bool] = {}) \
+ -> werkzeug.Response:
try:
store = _app.get_haketilo_state().repo_store()
display_info = store.get(repo_id).get_display_info()
html = flask.render_template(
'repos__show_single.html.jinja',
- display_info = display_info
+ display_info = display_info,
+ **errors
)
return flask.make_response(html, 200)
except st.MissingItemError:
flask.abort(404)
-def sanitize_altered_repo_id(repo_id: str) -> str:
+@bp.route('/repos/view/<string:repo_id>', methods=['POST'])
+def alter_repo(repo_id: str) -> werkzeug.Response:
repo_id = str(int(repo_id))
if repo_id == '1':
# Protect local semi-repo.
flask.abort(403)
- return repo_id
+ repo_ref = _app.get_haketilo_state().repo_store().get(repo_id)
-@bp.route('/repos/update_url/<string:repo_id>', methods=['POST'])
-def update_repo_url(repo_id: str) -> werkzeug.Response:
- repo_id = sanitize_altered_repo_id(repo_id)
-
- try:
- repo_ref = _app.get_haketilo_state().repo_store().get(repo_id)
- repo_ref.update()
- except st.MissingItemError:
- flask.abort(404)
-
- return flask.redirect(flask.url_for('.show_repo', repo_id=repo_id))
-
-@bp.route('/repos/remove/<string:repo_id>', methods=['POST'])
-def remove_repo(repo_id: str):
- repo_id = sanitize_altered_repo_id(repo_id)
-
- try:
- _app.get_haketilo_state().repo_store().get(repo_id).remove()
- except st.MissingItemError:
- flask.abort(404)
-
- return flask.redirect(flask.url_for('.repos'))
-
-@bp.route('/repos/refresh/<string:repo_id>', methods=['POST'])
-def refresh_repo(repo_id: str):
- repo_id = sanitize_altered_repo_id(repo_id)
+ form_data = flask.request.form
+ action = form_data['action']
try:
- _app.get_haketilo_state().repo_store().get(repo_id).refresh()
+ if action == 'remove_repo':
+ repo_ref.remove()
+ return flask.redirect(flask.url_for('.repos'))
+ elif action == 'refresh_repo':
+ repo_ref.refresh()
+ elif action == 'update_repo_data':
+ repo_ref.update(
+ url = form_data.get('url'),
+ name = form_data.get('name')
+ )
+ else:
+ raise ValueError()
+ except st.RepoNameInvalid:
+ return show_repo(repo_id, {'repo_name_invalid': True})
+ except st.RepoNameTaken:
+ return show_repo(repo_id, {'repo_name_taken': True})
+ except st.RepoUrlInvalid:
+ return show_repo(repo_id, {'repo_url_invalid': True})
except st.MissingItemError:
flask.abort(404)
diff --git a/src/hydrilla/proxy/web_ui/templates/repos.html.jinja b/src/hydrilla/proxy/web_ui/templates/repos.html.jinja
index 3e61a83..54465a1 100644
--- a/src/hydrilla/proxy/web_ui/templates/repos.html.jinja
+++ b/src/hydrilla/proxy/web_ui/templates/repos.html.jinja
@@ -29,7 +29,7 @@ in a proprietary work, I am not going to enforce this in court.
{% block main %}
<h3>{{ _('web_ui.repos.heading') }}</h3>
<div>
- <a href="{{ url_for('.add_repo_get') }}" class="green-button">
+ <a href="{{ url_for('.add_repo') }}" class="green-button">
{{ _('web_ui.repos.add_repo_button') }}
</a>
</div>
diff --git a/src/hydrilla/proxy/web_ui/templates/repos__show_single.html.jinja b/src/hydrilla/proxy/web_ui/templates/repos__show_single.html.jinja
index 96100ce..5783a20 100644
--- a/src/hydrilla/proxy/web_ui/templates/repos__show_single.html.jinja
+++ b/src/hydrilla/proxy/web_ui/templates/repos__show_single.html.jinja
@@ -27,46 +27,93 @@ in a proprietary work, I am not going to enforce this in court.
{% include 'include/checkbox_tricks_style.css.jinja' %}
{% endblock %}
{% block main %}
- {% set repo_id = display_info.ref.id -%}
- <h3>
- {% if display_info.is_local_semirepo -%}
- {{ _('web_ui.repos.local_packages_semirepo') }}
- {% else -%}
+ {%- set repo_id = display_info.ref.id %}
+ {%- if display_info.is_local_semirepo %}
+ <h3>{{ _('web_ui.repos.local_packages_semirepo') }}</h3>
+ {% else -%}
+ <h3>
{{ _('web_ui.repos.single.heading.name_{}').format(display_info.name) }}
- {% endif -%}
- </h3>
- {% if display_info.deleted and not display_info.is_local_semirepo -%}
- <div>
- {{ _('web_ui.repos.single.repo_is_deleted') }}
- </div>
- {% elif not display_info.deleted -%}
- <input id="show_url_edit_form" type="checkbox" class="chbx-tricks-show-hide"
- checked="">
+ </h3>
+ {%- if repo_name_invalid is defined or repo_name_taken is defined %}
+ {%- set checked_attr = '' %}
+ {%- else %}
+ {%- set checked_attr = 'checked=""' %}
+ {%- endif %}
+ {%- if not display_info.deleted %}
+ <input id="hide_name_edit_form" type="checkbox"
+ class="chbx-tricks-show-hide" {{ checked_attr }}>
<div>
- <div>
- {{ display_info.url }}
- </div>
- <div>
- <label for="show_url_edit_form" class="green-button">
- {{ _('web_ui.repos.single.update_url_button') }}
- </label>
- </div>
+ <label for="hide_name_edit_form" class="green-button">
+ {{ _('web_ui.repos.single.update_name_button') }}
+ </label>
</div>
- {% set action_url = url_for('.update_repo_url', repo_id=repo_id) -%}
- <form method="POST" action="{{ action_url }}">
- <input type="hidden" name="action" value="update_url">
+ <form method="POST">
+ <input type="hidden" name="action" value="update_repo_data">
+ {%- if repo_name_invalid is defined %}
+ <aside class="error-note">
+ {{ _('web_ui.repos.add.repo_name_invalid') }}
+ </aside>
+ {%- endif %}
+ {%- if repo_name_taken is defined %}
+ <aside class="error-note">
+ {{ _('web_ui.repos.add.repo_name_taken') }}
+ </aside>
+ {%- endif %}
<div>
- <input name="url" value="{{ display_info.url }}">
+ <input name="name" value="{{ display_info.name }}" required="">
</div>
<div>
<button class="green-button">
- {{ _('web_ui.repos.single.commit_update_url_button') }}
+ {{ _('web_ui.repos.single.commit_update_name_button') }}
</button>
- <label for="show_url_edit_form" class="green-button">
- {{ _('web_ui.repos.single.abort_update_url_button') }}
+ <label for="hide_url_edit_form" class="green-button">
+ {{ _('web_ui.repos.single.abort_update_name_button') }}
</label>
</div>
</form>
+ {%- endif %}{# not display_info.deleted #}
+ {%- endif %}{# display_info.is_local_semirepo (else) #}
+ {% if display_info.deleted and not display_info.is_local_semirepo -%}
+ <div>
+ {{ _('web_ui.repos.single.repo_is_deleted') }}
+ </div>
+ {%- elif not display_info.deleted %}
+ {%- if repo_url_invalid is defined %}
+ {%- set checked_attr = '' %}
+ {%- else %}
+ {%- set checked_attr = 'checked=""' %}
+ {%- endif %}
+ <input id="hide_url_edit_form" type="checkbox" class="chbx-tricks-show-hide"
+ {{ checked_attr }}>
+ <div>
+ <div>
+ {{ display_info.url }}
+ </div>
+ <div>
+ <label for="hide_url_edit_form" class="green-button">
+ {{ _('web_ui.repos.single.update_url_button') }}
+ </label>
+ </div>
+ </div>
+ <form method="POST">
+ <input type="hidden" name="action" value="update_repo_data">
+ {%- if repo_url_invalid is defined %}
+ <aside class="error-note">
+ {{ _('web_ui.repos.add.repo_url_invalid') }}
+ </aside>
+ {%- endif %}
+ <div>
+ <input name="url" value="{{ display_info.url }}" required="">
+ </div>
+ <div>
+ <button class="green-button">
+ {{ _('web_ui.repos.single.commit_update_url_button') }}
+ </button>
+ <label for="hide_url_edit_form" class="green-button">
+ {{ _('web_ui.repos.single.abort_update_url_button') }}
+ </label>
+ </div>
+ </form>
<div>
{% if display_info.last_refreshed is none -%}
{{ _('web_ui.repos.single.repo_never_refreshed') }}
@@ -76,15 +123,14 @@ in a proprietary work, I am not going to enforce this in court.
.format(display_info.last_refreshed.strftime('%F %H:%M'))
}}
{% endif -%}
- {% set action_url = url_for('.refresh_repo', repo_id=repo_id) -%}
- <form method="POST" action="{{ action_url }}">
+ <form method="POST">
<input type="hidden" name="action" value="refresh_repo">
<button class="green-button">
{{ _('web_ui.repos.single.refresh_now_button') }}
</button>
</form>
</div>
- {% endif -%}
+ {% endif -%}{# not display_info.deleted (elif) #}
<div>
{{
_('web_ui.repos.item_count_{mappings}_{resources}')
@@ -94,9 +140,9 @@ in a proprietary work, I am not going to enforce this in court.
)
}}
</div>
- {% if not display_info.is_local_semirepo -%}
- {% set action_url = url_for('.remove_repo', repo_id=repo_id) -%}
- <form method="POST" action="{{ action_url }}">
+ {% if not display_info.is_local_semirepo and not display_info.deleted -%}
+ <form method="POST">
+ <input type="hidden" name="action" value="remove_repo">
<button class="green-button">
{{ _('web_ui.repos.single.remove_button') }}
</button>