misc: display a technical error on form with removed fields (#61255)
This commit is contained in:
parent
04136fe6dc
commit
909f16eff3
|
@ -1639,3 +1639,25 @@ def test_workflow_display_form_with_block_add(pub):
|
|||
'blah_var_data_raw': {'data': [{'123': 'foo'}, {'123': 'bar'}], 'schema': {'123': 'string'}},
|
||||
'blah_var_str': 'blah',
|
||||
}
|
||||
|
||||
|
||||
def test_removed_block_in_form_page(pub):
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'form title'
|
||||
formdef.fields = [
|
||||
fields.BlockField(id='0', label='test', type='block:removed'),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
resp = get_app(pub).get(formdef.get_url(), status=500)
|
||||
assert 'A fatal error happened.' in resp.text
|
||||
|
||||
if pub.is_using_postgresql():
|
||||
assert pub.loggederror_class.count() == 1
|
||||
logged_error = pub.loggederror_class.select()[0]
|
||||
assert '(id:%s)' % logged_error.id in resp.text
|
||||
resp = get_app(pub).get(formdef.get_url(), status=500)
|
||||
assert '(id:%s)' % logged_error.id in resp.text
|
||||
logged_error = pub.loggederror_class.select()[0]
|
||||
assert logged_error.occurences_count == 2
|
||||
|
|
|
@ -91,6 +91,14 @@ class SetValueError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class MissingBlockFieldError(Exception):
|
||||
def __init__(self, block_slug):
|
||||
self.block_slug = block_slug
|
||||
|
||||
def __str__(self):
|
||||
return force_text(_('Missing block field: %s') % self.block_slug)
|
||||
|
||||
|
||||
class PrefillSelectionWidget(CompositeWidget):
|
||||
def __init__(self, name, value=None, field=None, **kwargs):
|
||||
CompositeWidget.__init__(self, name, value, **kwargs)
|
||||
|
@ -3478,6 +3486,13 @@ class BlockField(WidgetField):
|
|||
def get_dependencies(self):
|
||||
yield self.block
|
||||
|
||||
def add_to_form(self, form, value=None):
|
||||
try:
|
||||
self.block
|
||||
except KeyError:
|
||||
raise MissingBlockFieldError(self.type[6:])
|
||||
return super().add_to_form(form, value=value)
|
||||
|
||||
def fill_admin_form(self, form):
|
||||
super().fill_admin_form(form)
|
||||
if form.get_widget('prefill'):
|
||||
|
|
|
@ -34,7 +34,7 @@ from quixote.html import TemplateIO, htmltext
|
|||
from quixote.util import randbytes
|
||||
|
||||
from wcs.categories import Category
|
||||
from wcs.fields import SetValueError
|
||||
from wcs.fields import MissingBlockFieldError, SetValueError
|
||||
from wcs.formdata import Evolution, FormData
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.forms.common import FormStatusPage, FormTemplateMixin
|
||||
|
@ -528,8 +528,14 @@ class FormPage(Directory, FormTemplateMixin):
|
|||
form_data.update(computed_data)
|
||||
self.feed_current_data(magictoken)
|
||||
|
||||
with get_publisher().substitutions.temporary_feed(transient_formdata, force_mode='lazy'):
|
||||
form = self.create_form(page, displayed_fields, transient_formdata=transient_formdata)
|
||||
try:
|
||||
with get_publisher().substitutions.temporary_feed(transient_formdata, force_mode='lazy'):
|
||||
form = self.create_form(page, displayed_fields, transient_formdata=transient_formdata)
|
||||
except MissingBlockFieldError as e:
|
||||
logged_error = get_publisher().record_error(
|
||||
str(e), exception=e, notify=True, formdef=self.formdef
|
||||
)
|
||||
raise errors.InternalServerError(logged_error)
|
||||
|
||||
if submit_button is True:
|
||||
# submit_button at True means a non-submitting button has been
|
||||
|
|
|
@ -94,8 +94,7 @@ class LoggedError:
|
|||
error.tech_id = error.build_tech_id()
|
||||
error.occurences_count += 1
|
||||
error.latest_occurence_timestamp = now()
|
||||
error.store()
|
||||
return error
|
||||
return error.store()
|
||||
|
||||
def record_new_occurence(self, error):
|
||||
if not self.id:
|
||||
|
@ -112,7 +111,7 @@ class LoggedError:
|
|||
# exception should be the same (same tech_id), record just in case
|
||||
self.exception_class = error.exception_class
|
||||
self.exception_message = error.exception_message
|
||||
self.store()
|
||||
return self.store()
|
||||
|
||||
@classmethod
|
||||
def record_error(cls, error_summary, plain_error_msg, publisher, kind=None, *args, **kwargs):
|
||||
|
|
|
@ -430,7 +430,7 @@ class WcsPublisher(QommonPublisher):
|
|||
)
|
||||
if not notify or logged_exception and logged_exception.occurences_count > 1:
|
||||
# notify only first occurence
|
||||
return
|
||||
return logged_exception
|
||||
try:
|
||||
self.logger.log_internal_error(
|
||||
error_summary, plain_error_msg, logged_exception.tech_id if logged_exception else None
|
||||
|
@ -440,6 +440,7 @@ class WcsPublisher(QommonPublisher):
|
|||
# were configured to be mailed. (formerly socket.error)
|
||||
# Could also could happen on file descriptor exhaustion.
|
||||
pass
|
||||
return logged_exception
|
||||
|
||||
def apply_global_action_timeouts(self, **kwargs):
|
||||
from wcs.workflows import Workflow, WorkflowGlobalActionTimeoutTrigger
|
||||
|
|
|
@ -18,7 +18,7 @@ import urllib.parse
|
|||
|
||||
import quixote
|
||||
from quixote import get_publisher
|
||||
from quixote.errors import AccessError, TraversalError
|
||||
from quixote.errors import AccessError, PublishError, TraversalError
|
||||
from quixote.html import TemplateIO, htmltext
|
||||
|
||||
from . import _, template
|
||||
|
@ -77,20 +77,23 @@ class EmailError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class InternalServerError:
|
||||
class InternalServerError(PublishError):
|
||||
status_code = 500
|
||||
|
||||
def __init__(self, logged_error):
|
||||
super().__init__(_('Technical error'))
|
||||
self.logged_error = logged_error
|
||||
|
||||
def render(self):
|
||||
from . import _
|
||||
|
||||
template.html_top(_('Oops, the server borked severely'))
|
||||
template.html_top(_('Technical error'))
|
||||
r = TemplateIO(html=True)
|
||||
|
||||
r += htmltext('<p>')
|
||||
r += _('This is bad bad bad; perhaps you will have more luck if ' 'you retry in a few minutes ? ')
|
||||
r += _(
|
||||
'Alternatively you could harass the webmaster (who may have '
|
||||
'been emailed automatically with this incident but you can\'t '
|
||||
'be sure about this.'
|
||||
)
|
||||
r += _('A fatal error happened. It has been recorded and will be available to administrators.')
|
||||
if self.logged_error:
|
||||
r += ' (id:%s)' % self.logged_error.id
|
||||
r += htmltext('</p>')
|
||||
return r.getvalue()
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ class QommonPublisher(Publisher):
|
|||
|
||||
def format_publish_error(self, exc):
|
||||
get_response().filter = {}
|
||||
if isinstance(exc, errors.AccessError) and hasattr(exc, 'render'):
|
||||
if isinstance(exc, errors.PublishError) and hasattr(exc, 'render'):
|
||||
return exc.render()
|
||||
return errors.format_publish_error(exc)
|
||||
|
||||
|
|
|
@ -3582,6 +3582,7 @@ class LoggedError(SqlMixin, wcs.logged_errors.LoggedError):
|
|||
sql_dict = {x: getattr(self, x) for x, y in self._table_static_fields}
|
||||
|
||||
conn, cur = get_connection_and_cursor()
|
||||
error = self
|
||||
if not self.id:
|
||||
existing_errors = list(self.get_with_indexed_value('tech_id', self.tech_id))
|
||||
if not existing_errors:
|
||||
|
@ -3613,6 +3614,7 @@ class LoggedError(SqlMixin, wcs.logged_errors.LoggedError):
|
|||
assert cur.fetchone() is not None, 'LoggedError id not found'
|
||||
conn.commit()
|
||||
cur.close()
|
||||
return error
|
||||
|
||||
@classmethod
|
||||
def _row2ob(cls, row, **kwargs):
|
||||
|
|
Loading…
Reference in New Issue