admin: add snashots of test users (#89640)
gitea/wcs/pipeline/head This commit looks good Details

This commit is contained in:
Valentin Deniaud 2024-04-17 11:53:53 +02:00
parent 70805be37d
commit 45cc6f7d5c
6 changed files with 103 additions and 20 deletions

View File

@ -1721,3 +1721,53 @@ def test_tests_test_users_management(pub):
assert 'Some already existing users were not imported.' in resp.text
assert 'User test 2' in resp.text
assert pub.user_class.count([NotNull('test_uuid')]) == 2
def test_tests_test_users_history_page(pub):
create_superuser(pub)
test_user = pub.user_class(name='Test User')
test_user.email = 'jane@example.com'
test_user.test_uuid = '42'
test_user.form_data = {
'1': 'Jane',
'2': 'Doe',
'3': 'jane@example.com',
}
# create one snapshot
test_user.store()
# create second snapshot
test_user.name = 'Modified User'
test_user.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/test-users/%s/' % test_user.id)
resp = resp.click('History')
assert [x.attrib['class'] for x in resp.pyquery.find('.snapshots-list tr')] == [
'new-day',
'collapsed',
]
# export snapshot
resp_export = resp.click('Export', index=1)
assert resp_export.content_type == 'application/x-wcs-snapshot'
assert '>Test User<' in resp_export.text
# view snapshot
resp = resp.click('View', index=1)
assert resp.form['name'].value == 'Test User'
assert '>Submit<' not in resp.text
# restore
assert pub.user_class.count([NotNull('test_uuid')]) == 1
resp = resp.click('Restore version')
assert 'Restore as a new item' not in resp.text
resp = resp.form.submit('submit').follow()
assert pub.user_class.count([NotNull('test_uuid')]) == 1
test_user = pub.user_class.get(test_user.id)
assert test_user.name == 'Test User'

View File

@ -31,7 +31,7 @@ from wcs.admin.workflow_tests import WorkflowTestsDirectory
from wcs.api import posted_json_data_to_formdata_data
from wcs.backoffice.management import FormBackofficeEditPage, FormBackOfficeStatusPage
from wcs.backoffice.pagination import pagination_links
from wcs.backoffice.snapshots import SnapshotsDirectory
from wcs.backoffice.snapshots import SnapshotDirectory, SnapshotsDirectory
from wcs.carddef import CardDef
from wcs.formdef import FormDef
from wcs.forms.common import FormStatusPage
@ -993,18 +993,29 @@ class WebserviceResponseDirectory(Directory):
return redirect('.')
class TestUserPage(Directory):
_q_exports = ['', 'delete', 'export']
class TestUserSnapshotDirectory(SnapshotDirectory):
allow_restore_as_new = False
def __init__(self, component):
class TestUserPage(Directory):
_q_exports = ['', 'delete', 'export', ('history', 'snapshots_dir')]
def __init__(self, component, instance=None):
try:
self.user = get_publisher().user_class.get(component)
self.user = instance or get_publisher().user_class.get(component)
except IndexError:
raise TraversalError()
if not self.user.test_uuid:
raise TraversalError()
self.snapshots_dir = SnapshotsDirectory(self.user)
self.snapshots_dir.snapshot_directory_class = TestUserSnapshotDirectory
def _q_traverse(self, path):
get_response().breadcrumb.append((str(self.user.id) + '/', self.user.name))
return super()._q_traverse(path)
def _q_index(self):
form = Form(enctype='multipart/form-data')
@ -1028,7 +1039,8 @@ class TestUserPage(Directory):
)
formdef.add_fields_to_form(form, form_data=self.user.form_data)
form.add_submit('submit', _('Submit'))
if not self.user.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
form.add_media()
@ -1048,17 +1060,24 @@ class TestUserPage(Directory):
else:
self.user.name = form.get_widget('name').parse()
self.user.roles = form.get_widget('roles').parse()
self.user.store()
self.user.store(comment=_('Change in attribute values'))
return redirect('..')
get_response().breadcrumb.append(('edit', _('Edit test user')))
if self.user.is_readonly():
r = TemplateIO(html=True)
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This user is readonly.')
r += utils.snapshot_info_block(self.user.snapshot_object)
get_response().filter['sidebar'] = r.getvalue()
r = TemplateIO(html=True)
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s</h2>') % (_('Edit test user'))
r += htmltext('<span class="actions">')
r += htmltext('<a href="export">%s</a>') % _('Export')
r += htmltext('</span>')
if not self.user.is_readonly():
r += htmltext('<span class="actions">')
r += htmltext('<a href="export">%s</a>') % _('Export')
r += htmltext('<a href="history/">%s</a>') % _('History')
r += htmltext('</span>')
r += htmltext('</div>')
r += form.render()
return r.getvalue()
@ -1151,7 +1170,7 @@ class TestUsersDirectory(Directory):
if not form.has_errors():
user.name = form.get_widget('name').parse()
user.store()
user.store(comment=_('Creation'))
return redirect('.')
get_response().breadcrumb.append(('new', _('New')))
@ -1221,7 +1240,7 @@ class TestUsersDirectory(Directory):
users.append(user)
for user in users:
user.store()
user.store(comment=_('Creation (from import)'))
if users_were_ignored:
get_session().message = ('warning', _('Some already existing users were not imported.'))

View File

@ -283,12 +283,15 @@ class SnapshotsDirectory(Directory):
snapshot = get_publisher().snapshot_class.get(component, ignore_errors=True)
if not snapshot or not snapshot.is_from_object(self.obj):
raise errors.TraversalError()
return SnapshotDirectory(self.obj, snapshot)
snapshot_directory_class = getattr(self, 'snapshot_directory_class', SnapshotDirectory)
return snapshot_directory_class(self.obj, snapshot)
class SnapshotDirectory(Directory):
_q_exports = ['', 'export', 'restore', 'view', 'inspect']
allow_restore_as_new = True
def __init__(self, instance, snapshot):
self.obj = instance
self.snapshot = snapshot
@ -320,14 +323,15 @@ class SnapshotDirectory(Directory):
from wcs.blocks import BlockdefImportError
form = Form(enctype='multipart/form-data')
action_options = (
('as-new', _('Restore as a new item'), 'as-new'),
('overwrite', _('Overwrite current content'), 'overwrite'),
)
action = form.add(
RadiobuttonsWidget,
'action',
options=(
('as-new', _('Restore as a new item'), 'as-new'),
('overwrite', _('Overwrite current content'), 'overwrite'),
),
value='as-new',
options=action_options if self.allow_restore_as_new else action_options[1:],
value='as-new' if self.allow_restore_as_new else 'overwrite',
)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))

View File

@ -590,6 +590,7 @@ class WcsPublisher(QommonPublisher):
from wcs.data_sources import NamedDataSource
from wcs.formdef import FormDef
from wcs.mail_templates import MailTemplate
from wcs.sql import SqlUser
from wcs.testdef import TestDef
from wcs.workflows import Workflow
from wcs.wscalls import NamedWsCall
@ -611,6 +612,7 @@ class WcsPublisher(QommonPublisher):
CommentTemplateCategory,
DataSourceCategory,
TestDef,
SqlUser,
):
if klass.xml_root_node == object_type:
return klass

View File

@ -2943,7 +2943,7 @@ class SqlUser(SqlMixin, wcs.users.User):
return super().select(clause=clause, **kwargs)
@invalidate_substitution_cache
def store(self):
def store(self, comment=None):
sql_dict = {
'name': self.name,
'ascii_name': self.ascii_name,
@ -3037,6 +3037,9 @@ class SqlUser(SqlMixin, wcs.users.User):
cur.close()
if self.test_uuid and get_publisher().snapshot_class:
get_publisher().snapshot_class.snap(instance=self, comment=comment)
@classmethod
def _row2ob(cls, row, **kwargs):
o = cls()

View File

@ -60,6 +60,8 @@ class User(XmlStorableObject):
{% if user_var_zipcode %} {{ user_var_zipcode }}{% endif %}
{% if user_var_city %} {{ user_var_city }}{% endif %}"""
backoffice_class = 'wcs.admin.tests.TestUserPage'
XML_NODES = [
# fields to be included in xml export
('name', 'str'),
@ -409,6 +411,9 @@ class User(XmlStorableObject):
if status != 200:
get_publisher().record_error(_('Failed to call keepalive API (status: %s)') % status)
def get_admin_url(self):
return '%s/forms/test-users/%s/' % (get_publisher().get_backoffice_url(), self.id)
Substitutions.register(
'session_user_display_name',