0001-misc-store-LoggedErrors-in-SQL-48925.patch
tests/backoffice_pages/test_all.py | ||
---|---|---|
39 | 39 |
from wcs.carddef import CardDef |
40 | 40 |
from wcs.categories import Category |
41 | 41 |
from wcs.formdef import FormDef |
42 |
from wcs.logged_errors import LoggedError |
|
43 | 42 |
from wcs import fields |
44 | 43 |
from wcs.wscalls import NamedWsCall |
45 | 44 | |
... | ... | |
2220 | 2219 |
@pytest.mark.parametrize('notify_on_errors', [True, False]) |
2221 | 2220 |
@pytest.mark.parametrize('record_on_errors', [True, False]) |
2222 | 2221 |
def test_backoffice_wscall_on_error(http_requests, pub, emails, notify_on_errors, record_on_errors): |
2222 |
if not pub.is_using_postgresql(): |
|
2223 |
pytest.skip('this requires SQL') |
|
2224 |
return |
|
2225 | ||
2223 | 2226 |
pub.cfg['debug'] = {'error_email': 'errors@localhost.invalid'} |
2224 | 2227 |
pub.write_cfg() |
2225 | 2228 | |
2226 | 2229 |
create_user(pub) |
2227 | 2230 |
create_environment(pub) |
2231 |
pub.loggederror_class.wipe() |
|
2228 | 2232 |
formdef = FormDef.get_by_urlname('form-title') |
2229 | 2233 |
form_class = formdef.data_class() |
2230 | 2234 | |
... | ... | |
2277 | 2281 |
else: |
2278 | 2282 |
assert emails.count() == 0 |
2279 | 2283 | |
2280 |
# check LoggedError
|
|
2284 |
# check pub.loggederror_class
|
|
2281 | 2285 |
if record_on_errors: |
2282 |
assert LoggedError.count() == 1
|
|
2283 |
LoggedError.wipe()
|
|
2286 |
assert pub.loggederror_class.count() == 1
|
|
2287 |
pub.loggederror_class.wipe()
|
|
2284 | 2288 |
else: |
2285 |
assert LoggedError.count() == 0
|
|
2289 |
assert pub.loggederror_class.count() == 0
|
|
2286 | 2290 | |
2287 | 2291 | |
2288 | 2292 |
def test_backoffice_wscall_attachment(http_requests, pub): |
... | ... | |
4672 | 4676 | |
4673 | 4677 | |
4674 | 4678 |
def test_backoffice_logged_errors(pub): |
4679 |
if not pub.is_using_postgresql(): |
|
4680 |
pytest.skip('this requires SQL') |
|
4681 |
return |
|
4682 | ||
4675 | 4683 |
Workflow.wipe() |
4676 | 4684 |
workflow = Workflow.get_default_workflow() |
4677 | 4685 |
workflow.id = '12' |
... | ... | |
4693 | 4701 |
formdef.fields = [] |
4694 | 4702 |
formdef.store() |
4695 | 4703 | |
4696 |
LoggedError.wipe()
|
|
4704 |
pub.loggederror_class.wipe()
|
|
4697 | 4705 | |
4698 | 4706 |
create_superuser(pub) |
4699 | 4707 |
app = login(get_app(pub)) |
... | ... | |
4706 | 4714 |
resp = app.get('/test/') |
4707 | 4715 |
resp = resp.form.submit('submit').follow() |
4708 | 4716 |
resp = resp.form.submit('submit') |
4709 |
assert LoggedError.count() == 1
|
|
4717 |
assert pub.loggederror_class.count() == 1
|
|
4710 | 4718 | |
4711 | 4719 |
app = login(get_app(pub)) |
4712 | 4720 |
resp = app.get('/backoffice/forms/%s/' % formdef.id) |
... | ... | |
4724 | 4732 |
assert not 'Acked' in resp.text |
4725 | 4733 |
resp = resp.click('Ack').follow() |
4726 | 4734 |
assert 'Acked' in resp.text |
4727 |
assert LoggedError.select()[0].acked is True
|
|
4735 |
assert pub.loggederror_class.select()[0].acked is True
|
|
4728 | 4736 |
resp = resp.click('Delete').follow() |
4729 |
assert LoggedError.count() == 0
|
|
4737 |
assert pub.loggederror_class.count() == 0
|
|
4730 | 4738 | |
4731 | 4739 |
pub.cfg.update({'debug': {'error_email': None}}) |
4732 | 4740 |
pub.write_cfg() |
... | ... | |
4735 | 4743 |
resp = app.get('/test/') |
4736 | 4744 |
resp = resp.form.submit('submit').follow() |
4737 | 4745 |
resp = resp.form.submit('submit') |
4738 |
assert LoggedError.count() == 1
|
|
4746 |
assert pub.loggederror_class.count() == 1
|
|
4739 | 4747 | |
4740 | 4748 |
app = login(get_app(pub)) |
4741 | 4749 |
resp = app.get('/backoffice/workflows/%s/' % workflow.id) |
... | ... | |
4753 | 4761 |
resp = app.get('/test/') |
4754 | 4762 |
resp = resp.form.submit('submit').follow() |
4755 | 4763 |
resp = resp.form.submit('submit') |
4756 |
assert LoggedError.count() == 2
|
|
4764 |
assert pub.loggederror_class.count() == 2
|
|
4757 | 4765 | |
4758 | 4766 |
app = login(get_app(pub)) |
4759 | 4767 |
resp = app.get('/backoffice/workflows/%s/' % workflow.id) |
... | ... | |
5289 | 5297 |
source_formdef.store() |
5290 | 5298 |
source_formdef.data_class().wipe() |
5291 | 5299 |
target_formdef.data_class().wipe() |
5292 |
LoggedError.wipe() |
|
5300 |
if pub.is_using_postgresql(): |
|
5301 |
pub.loggederror_class.wipe() |
|
5293 | 5302 |
return locals() |
5294 | 5303 | |
5295 | 5304 | |
5296 |
def test_backoffice_create_formdata_backoffice_submission(create_formdata): |
|
5305 |
def test_backoffice_create_formdata_backoffice_submission(pub, create_formdata):
|
|
5297 | 5306 |
# create submitting user |
5298 | 5307 |
user = create_formdata['pub'].user_class() |
5299 | 5308 |
user.name = 'Jean Darmette' |
... | ... | |
5324 | 5333 |
assert target_data_class.count() == 0 |
5325 | 5334 |
# resubmit it through backoffice submission |
5326 | 5335 |
resp = resp.form.submit(name='button_resubmit') |
5327 |
assert LoggedError.count() == 0 |
|
5336 |
if pub.is_using_postgresql(): |
|
5337 |
assert pub.loggederror_class.count() == 0 |
|
5328 | 5338 |
assert target_data_class.count() == 1 |
5329 | 5339 |
target_formdata = target_data_class.select()[0] |
5330 | 5340 | |
... | ... | |
5353 | 5363 |
assert pq('.field-type-file .value').text() == 'bar' |
5354 | 5364 | |
5355 | 5365 | |
5356 |
def test_linked_forms_variables(create_formdata): |
|
5366 |
def test_linked_forms_variables(pub, create_formdata):
|
|
5357 | 5367 |
# create source formdata |
5358 | 5368 |
formdata = create_formdata['source_formdef'].data_class()() |
5359 | 5369 |
upload = PicklableUpload('/foo/bar', content_type='text/plain') |
... | ... | |
5370 | 5380 |
formdata.perform_workflow() |
5371 | 5381 |
formdata.store() |
5372 | 5382 | |
5373 |
get_publisher().substitutions.reset()
|
|
5374 |
get_publisher().substitutions.feed(formdata)
|
|
5375 |
substvars = get_publisher().substitutions.get_context_variables(mode='lazy')
|
|
5383 |
pub.substitutions.reset()
|
|
5384 |
pub.substitutions.feed(formdata)
|
|
5385 |
substvars = pub.substitutions.get_context_variables(mode='lazy')
|
|
5376 | 5386 |
assert str(substvars['form_links_resubmitted_form_var_foo_string']) == 'coucou' |
5377 | 5387 |
assert 'form_links_resubmitted_form_var_foo_string' in substvars.get_flat_keys() |
5378 | 5388 | |
... | ... | |
5391 | 5401 |
assert 'form_links_resubmitted_form_var_foo_string' not in resp |
5392 | 5402 | |
5393 | 5403 | |
5394 |
def test_backoffice_create_formdata_map_fields_by_varname(create_formdata): |
|
5404 |
def test_backoffice_create_formdata_map_fields_by_varname(pub, create_formdata):
|
|
5395 | 5405 |
create_formdata['create_formdata'].map_fields_by_varname = True |
5396 | 5406 |
create_formdata['create_formdata'].mappings = [] |
5397 | 5407 |
create_formdata['wf'].store() |
... | ... | |
5441 | 5451 |
assert target_data_class.count() == 0 |
5442 | 5452 |
# resubmit it through backoffice submission |
5443 | 5453 |
resp = resp.form.submit(name='button_resubmit') |
5444 |
assert LoggedError.count() == 0 |
|
5454 |
if pub.is_using_postgresql(): |
|
5455 |
assert pub.loggederror_class.count() == 0 |
|
5445 | 5456 |
assert target_data_class.count() == 1 |
5446 | 5457 |
target_formdata = target_data_class.select()[0] |
5447 | 5458 |
tests/backoffice_pages/test_carddata.py | ||
---|---|---|
10 | 10 |
from wcs.carddef import CardDef |
11 | 11 |
from wcs.categories import CardDefCategory |
12 | 12 |
from wcs.formdef import FormDef |
13 |
from wcs.logged_errors import LoggedError |
|
14 | 13 |
from wcs.qommon.http_request import HTTPRequest |
15 | 14 |
from wcs.wf.wscall import WebserviceCallStatusItem |
16 | 15 |
from wcs.workflows import ChoiceWorkflowStatusItem, Workflow |
... | ... | |
417 | 416 | |
418 | 417 | |
419 | 418 |
def test_backoffice_cards_wscall_failure_display(http_requests, pub, studio): |
420 |
LoggedError.wipe() |
|
419 |
if not pub.is_using_postgresql(): |
|
420 |
pytest.skip('this requires SQL') |
|
421 |
return |
|
422 | ||
423 |
pub.loggederror_class.wipe() |
|
421 | 424 |
user = create_user(pub) |
422 | 425 | |
423 | 426 |
Workflow.wipe() |
... | ... | |
473 | 476 |
resp = resp.follow() |
474 | 477 |
assert 'Error during webservice call' in resp.text |
475 | 478 | |
476 |
assert LoggedError.count() == 1
|
|
477 |
assert LoggedError.select()[0].get_formdata().data == {'1': 'plop'}
|
|
479 |
assert pub.loggederror_class.count() == 1
|
|
480 |
assert pub.loggederror_class.select()[0].get_formdata().data == {'1': 'plop'}
|
|
478 | 481 | |
479 | 482 | |
480 | 483 |
def test_block_card_item_link(pub, studio, blocks_feature): |
tests/form_pages/test_all.py | ||
---|---|---|
50 | 50 |
from wcs.data_sources import NamedDataSource |
51 | 51 |
from wcs.wscalls import NamedWsCall |
52 | 52 |
from wcs import fields |
53 |
from wcs.logged_errors import LoggedError |
|
54 | 53 |
from wcs.forms.root import PublicFormStatusPage |
55 | 54 | |
56 | 55 |
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub |
... | ... | |
2321 | 2320 |
assert formdef.data_class().get(data_id).status == 'wf-%s' % st2.id |
2322 | 2321 | |
2323 | 2322 |
# jump to a nonexistent status == do not jump, but add a LoggedError |
2324 |
LoggedError.wipe() |
|
2325 |
assert LoggedError.count() == 0 |
|
2323 |
if pub.is_using_postgresql(): |
|
2324 |
pub.loggederror_class.wipe() |
|
2325 |
assert pub.loggederror_class.count() == 0 |
|
2326 | 2326 |
editable.status = 'deleted_status_id' |
2327 | 2327 |
workflow.store() |
2328 | 2328 |
# go back to st1 |
... | ... | |
2338 | 2338 |
resp = resp.forms[0].submit('submit') |
2339 | 2339 |
resp = resp.follow() |
2340 | 2340 |
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id # stay on st1 |
2341 |
assert LoggedError.count() == 1 |
|
2342 |
logged_error = LoggedError.select()[0] |
|
2343 |
assert logged_error.formdata_id == str(formdata.id) |
|
2344 |
assert logged_error.formdef_id == formdef.id |
|
2345 |
assert logged_error.workflow_id == workflow.id |
|
2346 |
assert logged_error.status_id == st1.id |
|
2347 |
assert logged_error.status_item_id == editable.id |
|
2348 |
assert logged_error.occurences_count == 1 |
|
2341 |
if pub.is_using_postgresql(): |
|
2342 |
assert pub.loggederror_class.count() == 1 |
|
2343 |
logged_error = pub.loggederror_class.select()[0] |
|
2344 |
assert logged_error.formdata_id == str(formdata.id) |
|
2345 |
assert logged_error.formdef_id == formdef.id |
|
2346 |
assert logged_error.workflow_id == workflow.id |
|
2347 |
assert logged_error.status_id == st1.id |
|
2348 |
assert logged_error.status_item_id == editable.id |
|
2349 |
assert logged_error.occurences_count == 1 |
|
2349 | 2350 | |
2350 | 2351 |
# do it again: increment logged_error.occurences_count |
2351 | 2352 |
page = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % data_id) |
... | ... | |
2356 | 2357 |
resp = resp.forms[0].submit('submit') |
2357 | 2358 |
resp = resp.follow() |
2358 | 2359 |
assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id # stay on st1 |
2359 |
assert LoggedError.count() == 1 |
|
2360 |
logged_error = LoggedError.select()[0] |
|
2361 |
assert logged_error.occurences_count == 2 |
|
2360 |
if pub.is_using_postgresql(): |
|
2361 |
assert pub.loggederror_class.count() == 1 |
|
2362 |
logged_error = pub.loggederror_class.select()[0] |
|
2363 |
assert logged_error.occurences_count == 2 |
|
2362 | 2364 | |
2363 | 2365 | |
2364 | 2366 |
def test_form_edit_autocomplete_list(pub): |
... | ... | |
6714 | 6716 | |
6715 | 6717 | |
6716 | 6718 |
def test_logged_errors(pub): |
6719 |
if not pub.is_using_postgresql(): |
|
6720 |
pytest.skip('this requires SQL') |
|
6721 |
return |
|
6722 | ||
6717 | 6723 |
Workflow.wipe() |
6718 | 6724 |
workflow = Workflow.get_default_workflow() |
6719 | 6725 |
workflow.id = '12' |
... | ... | |
6735 | 6741 |
formdef.fields = [] |
6736 | 6742 |
formdef.store() |
6737 | 6743 | |
6738 |
LoggedError.wipe()
|
|
6744 |
pub.loggederror_class.wipe()
|
|
6739 | 6745 | |
6740 | 6746 |
app = get_app(pub) |
6741 | 6747 |
resp = app.get('/test/') |
6742 | 6748 |
resp = resp.form.submit('submit').follow() |
6743 | 6749 |
resp = resp.form.submit('submit') |
6744 |
assert LoggedError.count() == 1
|
|
6750 |
assert pub.loggederror_class.count() == 1
|
|
6745 | 6751 | |
6746 | 6752 |
resp = app.get('/test/') |
6747 | 6753 |
resp = resp.form.submit('submit').follow() |
6748 | 6754 |
resp = resp.form.submit('submit') |
6749 |
assert LoggedError.count() == 1
|
|
6755 |
assert pub.loggederror_class.count() == 1
|
|
6750 | 6756 | |
6751 |
error = LoggedError.get_on_index(
|
|
6752 |
'34-12-just_submitted-_jump-failed-to-evaluate-condition-ZeroDivisionError-integer-division-or-modulo-by-zero',
|
|
6753 |
'tech_id')
|
|
6757 |
error = list(pub.loggederror_class.get_with_indexed_value(
|
|
6758 |
'tech_id',
|
|
6759 |
'34-12-just_submitted-_jump-failed-to-evaluate-condition-ZeroDivisionError-integer-division-or-modulo-by-zero'))[0]
|
|
6754 | 6760 |
assert error.occurences_count == 2 |
6755 | 6761 | |
6756 |
assert len(LoggedError.get_ids_with_indexed_value('formdef_id', '34')) == 1
|
|
6757 |
assert len(LoggedError.get_ids_with_indexed_value('formdef_id', 'X')) == 0
|
|
6762 |
assert len(list(pub.loggederror_class.get_with_indexed_value('formdef_id', '34'))) == 1
|
|
6763 |
assert len(list(pub.loggederror_class.get_with_indexed_value('formdef_id', 'X'))) == 0
|
|
6758 | 6764 | |
6759 |
assert len(LoggedError.get_ids_with_indexed_value('workflow_id', '12')) == 1
|
|
6760 |
assert len(LoggedError.get_ids_with_indexed_value('workflow_id', 'X')) == 0
|
|
6765 |
assert len(list(pub.loggederror_class.get_with_indexed_value('workflow_id', '12'))) == 1
|
|
6766 |
assert len(list(pub.loggederror_class.get_with_indexed_value('workflow_id', 'X'))) == 0
|
|
6761 | 6767 | |
6762 | 6768 | |
6763 | 6769 |
def test_formdata_named_wscall(http_requests, pub): |
... | ... | |
8805 | 8811 |
assert pq('.linked .foo_string').text() == 'zob' |
8806 | 8812 | |
8807 | 8813 | |
8808 |
def test_create_formdata_empty_item_ds_with_id_parameter(create_formdata): |
|
8809 |
LoggedError.wipe() |
|
8814 |
def test_create_formdata_empty_item_ds_with_id_parameter(pub, create_formdata): |
|
8815 |
if pub.is_using_postgresql(): |
|
8816 |
pub.loggederror_class.wipe() |
|
8810 | 8817 |
NamedDataSource.wipe() |
8811 | 8818 |
data_source = NamedDataSource(name='foobar') |
8812 | 8819 |
data_source.data_source = { |
... | ... | |
8834 | 8841 |
resp = resp.form.submit('submit') # -> submission |
8835 | 8842 |
resp = resp.follow() |
8836 | 8843 |
assert create_formdata['target_formdef'].data_class().count() == 0 |
8837 |
assert LoggedError.count() == 0 |
|
8844 |
if pub.is_using_postgresql(): |
|
8845 |
assert pub.loggederror_class.count() == 0 |
|
8838 | 8846 |
resp = resp.form.submit('button_resubmit') |
8839 |
assert LoggedError.count() == 0 |
|
8847 |
if pub.is_using_postgresql(): |
|
8848 |
assert pub.loggederror_class.count() == 0 |
|
8840 | 8849 | |
8841 | 8850 | |
8842 | 8851 |
def test_js_libraries(pub): |
tests/test_mail_templates.py | ||
---|---|---|
8 | 8 |
from quixote import cleanup |
9 | 9 |
from wcs.formdef import FormDef |
10 | 10 |
from wcs.fields import FileField |
11 |
from wcs.logged_errors import LoggedError |
|
12 | 11 |
from wcs.mail_templates import MailTemplate |
13 | 12 |
from wcs.workflows import Workflow, SendmailWorkflowStatusItem |
14 | 13 |
from wcs.qommon.http_request import HTTPRequest |
... | ... | |
193 | 192 |
assert workflow.possible_status[0].items[0].mail_template == 'test-mail-template' |
194 | 193 | |
195 | 194 | |
196 |
def test_workflow_send_mail_template(pub, superuser, mail_templates_option, emails): |
|
195 |
def test_workflow_send_mail_template_with_sql(superuser, mail_templates_option, emails): |
|
196 |
pub = create_temporary_pub(sql_mode=True) |
|
197 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
198 |
pub.set_app_dir(req) |
|
199 |
pub._set_request(req) |
|
200 | ||
197 | 201 |
Workflow.wipe() |
198 | 202 |
MailTemplate.wipe() |
199 | 203 | |
... | ... | |
231 | 235 |
# check nothing is sent and an error is logged if the mail template is |
232 | 236 |
# missing |
233 | 237 |
emails.empty() |
234 |
LoggedError.wipe()
|
|
238 |
pub.loggederror_class.wipe()
|
|
235 | 239 |
MailTemplate.wipe() |
236 | 240 |
item.perform(formdata) |
237 | 241 |
pub.get_request().response.process_after_jobs() |
238 | 242 |
assert emails.count() == 0 |
239 |
assert LoggedError.count() == 1
|
|
240 |
logged_error = LoggedError.select()[0]
|
|
243 |
assert pub.loggederror_class.count() == 1
|
|
244 |
logged_error = pub.loggederror_class.select()[0]
|
|
241 | 245 |
assert logged_error.summary == 'reference to invalid mail template test-mail-template in status Status1' |
242 | 246 | |
243 | 247 |
tests/test_workflows.py | ||
---|---|---|
32 | 32 |
ItemsField, CommentField, EmailField, PageField, TitleField, |
33 | 33 |
SubtitleField, TextField, BoolField, TableField) |
34 | 34 |
from wcs.formdata import Evolution |
35 |
from wcs.logged_errors import LoggedError |
|
36 | 35 |
from wcs.roles import Role |
37 | 36 |
from wcs.workflows import (Workflow, WorkflowStatusItem, |
38 | 37 |
SendmailWorkflowStatusItem, SendSMSWorkflowStatusItem, |
... | ... | |
314 | 313 |
assert item.must_jump(formdata) is False |
315 | 314 | |
316 | 315 | |
317 |
def test_jump_bad_python_condition(pub): |
|
316 |
def test_jump_bad_python_condition(two_pubs): |
|
317 |
if not two_pubs.is_using_postgresql(): |
|
318 |
pytest.skip('this requires SQL') |
|
319 |
return |
|
320 | ||
318 | 321 |
FormDef.wipe() |
319 | 322 |
formdef = FormDef() |
320 | 323 |
formdef.name = 'foobar' |
321 | 324 |
formdef.store() |
322 |
pub.substitutions.feed(formdef)
|
|
325 |
two_pubs.substitutions.feed(formdef)
|
|
323 | 326 |
formdef.data_class().wipe() |
324 | 327 |
formdata = formdef.data_class()() |
325 | 328 |
item = JumpWorkflowStatusItem() |
326 | 329 | |
327 |
LoggedError.wipe()
|
|
330 |
two_pubs.loggederror_class.wipe()
|
|
328 | 331 |
item.condition = {'type': 'python', 'value': 'form_var_foobar == 0'} |
329 | 332 |
assert item.must_jump(formdata) is False |
330 |
assert LoggedError.count() == 1
|
|
331 |
logged_error = LoggedError.select()[0]
|
|
333 |
assert two_pubs.loggederror_class.count() == 1
|
|
334 |
logged_error = two_pubs.loggederror_class.select()[0]
|
|
332 | 335 |
assert logged_error.summary == 'Failed to evaluate condition' |
333 | 336 |
assert logged_error.exception_class == 'NameError' |
334 | 337 |
assert logged_error.exception_message == "name 'form_var_foobar' is not defined" |
335 | 338 |
assert logged_error.expression == 'form_var_foobar == 0' |
336 | 339 |
assert logged_error.expression_type == 'python' |
337 | 340 | |
338 |
LoggedError.wipe()
|
|
341 |
two_pubs.loggederror_class.wipe()
|
|
339 | 342 |
item.condition = {'type': 'python', 'value': '~ invalid ~'} |
340 | 343 |
assert item.must_jump(formdata) is False |
341 |
assert LoggedError.count() == 1
|
|
342 |
logged_error = LoggedError.select()[0]
|
|
344 |
assert two_pubs.loggederror_class.count() == 1
|
|
345 |
logged_error = two_pubs.loggederror_class.select()[0]
|
|
343 | 346 |
assert logged_error.summary == 'Failed to evaluate condition' |
344 | 347 |
assert logged_error.exception_class == 'SyntaxError' |
345 | 348 |
assert logged_error.exception_message == 'unexpected EOF while parsing (<string>, line 1)' |
... | ... | |
347 | 350 |
assert logged_error.expression_type == 'python' |
348 | 351 | |
349 | 352 | |
350 |
def test_jump_django_conditions(pub):
|
|
353 |
def test_jump_django_conditions(two_pubs):
|
|
351 | 354 |
FormDef.wipe() |
352 | 355 |
formdef = FormDef() |
353 | 356 |
formdef.name = 'foobar' |
... | ... | |
355 | 358 |
formdef.store() |
356 | 359 |
formdata = formdef.data_class()() |
357 | 360 |
formdata.data = {'1': 'hello'} |
358 |
pub.substitutions.feed(formdata)
|
|
361 |
two_pubs.substitutions.feed(formdata)
|
|
359 | 362 |
item = JumpWorkflowStatusItem() |
360 | 363 | |
361 |
LoggedError.wipe() |
|
364 |
if two_pubs.is_using_postgresql(): |
|
365 |
two_pubs.loggederror_class.wipe() |
|
362 | 366 |
item.condition = {'type': 'django', 'value': '1 < 2'} |
363 | 367 |
assert item.must_jump(formdata) is True |
364 | 368 | |
... | ... | |
371 | 375 |
item.condition = {'type': 'django', 'value': 'form_var_foo|first|upper == "X"'} |
372 | 376 |
assert item.must_jump(formdata) is False |
373 | 377 | |
374 |
assert LoggedError.count() == 0 |
|
378 |
if two_pubs.is_using_postgresql(): |
|
379 |
assert two_pubs.loggederror_class.count() == 0 |
|
375 | 380 | |
376 | 381 |
item.condition = {'type': 'django', 'value': '~ invalid ~'} |
377 | 382 |
assert item.must_jump(formdata) is False |
378 |
assert LoggedError.count() == 1 |
|
379 |
logged_error = LoggedError.select()[0] |
|
380 |
assert logged_error.summary == 'Failed to evaluate condition' |
|
381 |
assert logged_error.exception_class == 'TemplateSyntaxError' |
|
382 |
assert logged_error.exception_message == "Could not parse the remainder: '~' from '~'" |
|
383 |
assert logged_error.expression == '~ invalid ~' |
|
384 |
assert logged_error.expression_type == 'django' |
|
383 |
if two_pubs.is_using_postgresql(): |
|
384 |
assert two_pubs.loggederror_class.count() == 1 |
|
385 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
386 |
assert logged_error.summary == 'Failed to evaluate condition' |
|
387 |
assert logged_error.exception_class == 'TemplateSyntaxError' |
|
388 |
assert logged_error.exception_message == "Could not parse the remainder: '~' from '~'" |
|
389 |
assert logged_error.expression == '~ invalid ~' |
|
390 |
assert logged_error.expression_type == 'django' |
|
385 | 391 | |
386 | 392 | |
387 | 393 |
def test_check_auth(pub): |
... | ... | |
2268 | 2274 | |
2269 | 2275 | |
2270 | 2276 |
def test_jump_missing_previous_mark(two_pubs): |
2277 |
if not two_pubs.is_using_postgresql(): |
|
2278 |
pytest.skip('this requires SQL') |
|
2279 |
return |
|
2280 | ||
2271 | 2281 |
workflow = Workflow(name='jump-mark') |
2272 | 2282 |
st1 = workflow.add_status('Status1', 'st1') |
2273 | 2283 | |
... | ... | |
2292 | 2302 |
formdata.store() |
2293 | 2303 | |
2294 | 2304 |
time.sleep(0.3) |
2295 |
LoggedError.wipe()
|
|
2305 |
two_pubs.loggederror_class.wipe()
|
|
2296 | 2306 |
_apply_timeouts(two_pubs) |
2297 |
assert LoggedError.count() == 1
|
|
2307 |
assert two_pubs.loggederror_class.count() == 1
|
|
2298 | 2308 | |
2299 | 2309 | |
2300 | 2310 |
def test_sms(pub, sms_mocking): |
... | ... | |
3720 | 3730 |
def test_set_backoffice_field(http_requests, two_pubs): |
3721 | 3731 |
Workflow.wipe() |
3722 | 3732 |
FormDef.wipe() |
3723 |
LoggedError.wipe() |
|
3733 |
if two_pubs.is_using_postgresql(): |
|
3734 |
two_pubs.loggederror_class.wipe() |
|
3724 | 3735 |
wf = Workflow(name='xxx') |
3725 | 3736 |
wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) |
3726 | 3737 |
wf.backoffice_fields_formdef.fields = [ |
... | ... | |
3819 | 3830 |
formdata = formdef.data_class().get(formdata.id) |
3820 | 3831 |
assert formdata.data['bo1'] == '' |
3821 | 3832 | |
3822 |
assert LoggedError.count() == 0 |
|
3833 |
if two_pubs.is_using_postgresql(): |
|
3834 |
assert two_pubs.loggederror_class.count() == 0 |
|
3823 | 3835 | |
3824 | 3836 |
item.fields = [{'field_id': 'bo1', 'value': '= ~ invalid python ~'}] |
3825 | 3837 |
item.perform(formdata) |
3826 | 3838 |
formdata = formdef.data_class().get(formdata.id) |
3827 |
assert LoggedError.count() == 1 |
|
3828 |
logged_error = LoggedError.select()[0] |
|
3829 |
assert logged_error.summary == 'Failed to compute Python expression' |
|
3830 |
assert logged_error.formdata_id == str(formdata.id) |
|
3831 |
assert logged_error.expression == ' ~ invalid python ~' |
|
3832 |
assert logged_error.expression_type == 'python' |
|
3833 |
assert logged_error.exception_class == 'SyntaxError' |
|
3834 |
assert logged_error.exception_message == 'invalid syntax (<string>, line 1)' |
|
3839 |
if two_pubs.is_using_postgresql(): |
|
3840 |
assert two_pubs.loggederror_class.count() == 1 |
|
3841 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
3842 |
assert logged_error.summary == 'Failed to compute Python expression' |
|
3843 |
assert logged_error.formdata_id == str(formdata.id) |
|
3844 |
assert logged_error.expression == ' ~ invalid python ~' |
|
3845 |
assert logged_error.expression_type == 'python' |
|
3846 |
assert logged_error.exception_class == 'SyntaxError' |
|
3847 |
assert logged_error.exception_message == 'invalid syntax (<string>, line 1)' |
|
3835 | 3848 | |
3836 |
LoggedError.wipe() |
|
3849 |
if two_pubs.is_using_postgresql(): |
|
3850 |
two_pubs.loggederror_class.wipe() |
|
3837 | 3851 |
item.fields = [{'field_id': 'bo1', 'value': '{% if bad django %}'}] |
3838 | 3852 |
item.perform(formdata) |
3839 | 3853 |
formdata = formdef.data_class().get(formdata.id) |
3840 |
assert LoggedError.count() == 1 |
|
3841 |
logged_error = LoggedError.select()[0] |
|
3842 |
assert logged_error.summary == 'Failed to compute template' |
|
3843 |
assert logged_error.formdata_id == str(formdata.id) |
|
3844 |
assert logged_error.expression == '{% if bad django %}' |
|
3845 |
assert logged_error.expression_type == 'template' |
|
3846 |
assert logged_error.exception_class == 'TemplateError' |
|
3847 |
assert logged_error.exception_message.startswith('syntax error in Django template') |
|
3854 |
if two_pubs.is_using_postgresql(): |
|
3855 |
assert two_pubs.loggederror_class.count() == 1 |
|
3856 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
3857 |
assert logged_error.summary == 'Failed to compute template' |
|
3858 |
assert logged_error.formdata_id == str(formdata.id) |
|
3859 |
assert logged_error.expression == '{% if bad django %}' |
|
3860 |
assert logged_error.expression_type == 'template' |
|
3861 |
assert logged_error.exception_class == 'TemplateError' |
|
3862 |
assert logged_error.exception_message.startswith('syntax error in Django template') |
|
3848 | 3863 | |
3849 | 3864 | |
3850 | 3865 |
def test_set_backoffice_field_file(http_requests, two_pubs): |
... | ... | |
4011 | 4026 |
item.parent = st1 |
4012 | 4027 |
item.fields = [{'field_id': 'bo1', 'value': value}] |
4013 | 4028 | |
4014 |
LoggedError.wipe() |
|
4029 |
if two_pubs.is_using_postgresql(): |
|
4030 |
two_pubs.loggederror_class.wipe() |
|
4015 | 4031 |
item.perform(formdata) |
4016 | 4032 | |
4017 | 4033 |
formdata = formdef.data_class().get(formdata.id) |
4018 | 4034 |
assert formdata.data['bo1'].base_filename == 'hello.txt' |
4019 | 4035 |
assert formdata.data['bo1'].get_content() == b'HELLO WORLD' |
4020 |
assert LoggedError.count() == 1 |
|
4021 |
logged_error = LoggedError.select()[0] |
|
4022 |
assert logged_error.summary.startswith('Failed to convert') |
|
4023 |
assert logged_error.formdata_id == str(formdata.id) |
|
4024 |
assert logged_error.exception_class == 'ValueError' |
|
4036 |
if two_pubs.is_using_postgresql(): |
|
4037 |
assert two_pubs.loggederror_class.count() == 1 |
|
4038 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
4039 |
assert logged_error.summary.startswith('Failed to convert') |
|
4040 |
assert logged_error.formdata_id == str(formdata.id) |
|
4041 |
assert logged_error.exception_class == 'ValueError' |
|
4025 | 4042 | |
4026 | 4043 |
# check wrong field |
4027 | 4044 |
item = SetBackofficeFieldsWorkflowStatusItem() |
... | ... | |
4318 | 4335 |
def test_set_backoffice_field_date(two_pubs): |
4319 | 4336 |
Workflow.wipe() |
4320 | 4337 |
FormDef.wipe() |
4321 |
LoggedError.wipe() |
|
4338 |
if two_pubs.is_using_postgresql(): |
|
4339 |
two_pubs.loggederror_class.wipe() |
|
4322 | 4340 |
wf = Workflow(name='xxx') |
4323 | 4341 |
wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) |
4324 | 4342 |
st1 = wf.add_status('Status1') |
... | ... | |
4366 | 4384 |
assert datetime.date(*formdata.data['bo1'][:3]) == datetime.date(2017, 3, 23) |
4367 | 4385 | |
4368 | 4386 |
# invalid values => do nothing |
4369 |
assert LoggedError.count() == 0 |
|
4387 |
if two_pubs.is_using_postgresql(): |
|
4388 |
assert two_pubs.loggederror_class.count() == 0 |
|
4370 | 4389 |
for value in ('plop', '={}', '=[]'): |
4371 | 4390 |
item = SetBackofficeFieldsWorkflowStatusItem() |
4372 | 4391 |
item.parent = st1 |
4373 | 4392 |
item.fields = [{'field_id': 'bo1', 'value': value}] |
4374 | 4393 | |
4375 |
LoggedError.wipe() |
|
4394 |
if two_pubs.is_using_postgresql(): |
|
4395 |
two_pubs.loggederror_class.wipe() |
|
4376 | 4396 |
item.perform(formdata) |
4377 | 4397 |
formdata = formdef.data_class().get(formdata.id) |
4378 | 4398 |
assert datetime.date(*formdata.data['bo1'][:3]) == datetime.date(2017, 3, 23) |
4379 |
assert LoggedError.count() == 1 |
|
4380 |
assert LoggedError.select()[0].summary.startswith('Failed to convert') |
|
4399 |
if two_pubs.is_using_postgresql(): |
|
4400 |
assert two_pubs.loggederror_class.count() == 1 |
|
4401 |
assert two_pubs.loggederror_class.select()[0].summary.startswith('Failed to convert') |
|
4381 | 4402 | |
4382 | 4403 |
# None : empty date |
4383 | 4404 |
item = SetBackofficeFieldsWorkflowStatusItem() |
... | ... | |
4392 | 4413 |
def test_set_backoffice_field_boolean(two_pubs): |
4393 | 4414 |
Workflow.wipe() |
4394 | 4415 |
FormDef.wipe() |
4395 |
LoggedError.wipe() |
|
4396 | 4416 |
wf = Workflow(name='xxx') |
4397 | 4417 |
wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(wf) |
4398 | 4418 |
st1 = wf.add_status('Status1') |
... | ... | |
4547 | 4567 | |
4548 | 4568 | |
4549 | 4569 |
def test_workflow_action_condition(two_pubs): |
4550 |
pub = two_pubs |
|
4551 |
pub._set_request(None) # to avoid after jobs |
|
4570 |
two_pubs._set_request(None) # to avoid after jobs |
|
4552 | 4571 |
workflow = Workflow(name='jump condition migration') |
4553 | 4572 |
st1 = workflow.add_status('Status1', 'st1') |
4554 | 4573 |
workflow.store() |
... | ... | |
4556 | 4575 |
role = Role(name='bar1') |
4557 | 4576 |
role.store() |
4558 | 4577 | |
4559 |
user = pub.user_class()
|
|
4578 |
user = two_pubs.user_class()
|
|
4560 | 4579 |
user.roles = [role.id] |
4561 | 4580 |
user.store() |
4562 | 4581 | |
... | ... | |
4591 | 4610 |
choice.condition = {'type': 'python', 'value': 'form_var_foo == "foo"'} |
4592 | 4611 |
workflow.store() |
4593 | 4612 | |
4594 |
with pub.substitutions.temporary_feed(formdata1):
|
|
4613 |
with two_pubs.substitutions.temporary_feed(formdata1):
|
|
4595 | 4614 |
assert FormDef.get(formdef.id).data_class().get(formdata1.id).get_actions_roles() == set([role.id]) |
4596 |
with pub.substitutions.temporary_feed(formdata2):
|
|
4615 |
with two_pubs.substitutions.temporary_feed(formdata2):
|
|
4597 | 4616 |
assert FormDef.get(formdef.id).data_class().get(formdata2.id).get_actions_roles() == set() |
4598 | 4617 | |
4599 | 4618 |
assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 1 |
... | ... | |
4608 | 4627 |
assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 2 |
4609 | 4628 | |
4610 | 4629 |
# bad condition |
4611 |
LoggedError.wipe() |
|
4630 |
if two_pubs.is_using_postgresql(): |
|
4631 |
two_pubs.loggederror_class.wipe() |
|
4612 | 4632 |
choice.condition = {'type': 'python', 'value': 'foobar == barfoo'} |
4613 | 4633 |
workflow.store() |
4614 | 4634 |
assert len(FormDef.get(formdef.id).data_class().get_actionable_ids([role.id])) == 0 |
4615 |
assert LoggedError.count() == 1 |
|
4616 |
logged_error = LoggedError.select()[0] |
|
4617 |
assert logged_error.occurences_count > 1 # should be 2... == 12 with pickle, 4 with sql |
|
4618 |
assert logged_error.summary == 'Failed to evaluate condition' |
|
4619 |
assert logged_error.exception_class == 'NameError' |
|
4620 |
assert logged_error.exception_message == "name 'foobar' is not defined" |
|
4621 |
assert logged_error.expression == 'foobar == barfoo' |
|
4622 |
assert logged_error.expression_type == 'python' |
|
4635 |
if two_pubs.is_using_postgresql(): |
|
4636 |
assert two_pubs.loggederror_class.count() == 1 |
|
4637 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
4638 |
assert logged_error.occurences_count > 1 # should be 2... == 12 with pickle, 4 with sql |
|
4639 |
assert logged_error.summary == 'Failed to evaluate condition' |
|
4640 |
assert logged_error.exception_class == 'NameError' |
|
4641 |
assert logged_error.exception_message == "name 'foobar' is not defined" |
|
4642 |
assert logged_error.expression == 'foobar == barfoo' |
|
4643 |
assert logged_error.expression_type == 'python' |
|
4623 | 4644 | |
4624 | 4645 | |
4625 | 4646 |
def test_notifications(pub, http_requests): |
... | ... | |
4749 | 4770 |
formdef.fields = [] |
4750 | 4771 |
formdef.workflow_id = workflow.id |
4751 | 4772 |
formdef.store() |
4773 |
formdef.data_class().wipe() |
|
4752 | 4774 | |
4753 | 4775 |
for i in range(5): |
4754 | 4776 |
formdata = formdef.data_class()() |
... | ... | |
4786 | 4808 |
assert 'http://example.net/foobar/%s/status (New)' % formdata.id in emails.emails['New arrivals']['payload'] |
4787 | 4809 | |
4788 | 4810 | |
4789 |
def test_create_formdata(pub):
|
|
4811 |
def test_create_formdata(two_pubs):
|
|
4790 | 4812 |
FormDef.wipe() |
4791 |
LoggedError.wipe() |
|
4792 |
pub.tracking_code_class.wipe() |
|
4813 |
if two_pubs.is_using_postgresql(): |
|
4814 |
two_pubs.loggederror_class.wipe() |
|
4815 |
two_pubs.tracking_code_class.wipe() |
|
4793 | 4816 | |
4794 | 4817 |
target_formdef = FormDef() |
4795 | 4818 |
target_formdef.name = 'target form' |
... | ... | |
4824 | 4847 |
formdata.just_created() |
4825 | 4848 | |
4826 | 4849 |
assert target_formdef.data_class().count() == 0 |
4827 |
assert LoggedError.count() == 0 |
|
4850 |
if two_pubs.is_using_postgresql(): |
|
4851 |
assert two_pubs.loggederror_class.count() == 0 |
|
4828 | 4852 |
# check unconfigure action do nothing |
4829 | 4853 |
formdata.perform_workflow() |
4830 | 4854 |
assert target_formdef.data_class().count() == 0 |
... | ... | |
4835 | 4859 |
formdata.perform_workflow() |
4836 | 4860 |
assert target_formdef.data_class().count() == 1 |
4837 | 4861 | |
4838 |
errors = LoggedError.select() |
|
4839 |
assert len(errors) == 2 |
|
4840 |
assert any('form_var_toto_string' in (error.exception_message or '') for error in errors) |
|
4841 |
assert any('Missing field' in error.summary for error in errors) |
|
4862 |
if two_pubs.is_using_postgresql(): |
|
4863 |
errors = two_pubs.loggederror_class.select() |
|
4864 |
assert len(errors) == 2 |
|
4865 |
assert any('form_var_toto_string' in (error.exception_message or '') for error in errors) |
|
4866 |
assert any('Missing field' in error.summary for error in errors) |
|
4842 | 4867 | |
4843 | 4868 |
# no tracking code has been created |
4844 | 4869 |
created_formdata = target_formdef.data_class().select()[0] |
4845 | 4870 |
assert created_formdata.tracking_code is None |
4846 |
assert pub.tracking_code_class.count() == 0
|
|
4871 |
assert two_pubs.tracking_code_class.count() == 0
|
|
4847 | 4872 |
# now we want one |
4848 | 4873 |
target_formdef.enable_tracking_codes = True |
4849 | 4874 |
target_formdef.store() |
... | ... | |
4853 | 4878 |
assert target_formdef.data_class().count() == 1 |
4854 | 4879 |
created_formdata = target_formdef.data_class().select()[0] |
4855 | 4880 |
assert created_formdata.tracking_code is not None |
4856 |
assert pub.tracking_code_class.count() == 1
|
|
4857 |
assert pub.tracking_code_class.select()[0].formdef_id == target_formdef.id
|
|
4858 |
assert pub.tracking_code_class.select()[0].formdata_id == created_formdata.id
|
|
4881 |
assert two_pubs.tracking_code_class.count() == 1
|
|
4882 |
assert two_pubs.tracking_code_class.select()[0].formdef_id == target_formdef.id
|
|
4883 |
assert two_pubs.tracking_code_class.select()[0].formdata_id == str(created_formdata.id)
|
|
4859 | 4884 | |
4860 | 4885 |
create.condition = {'type': 'python', 'value': '1 == 2'} |
4861 | 4886 |
wf.store() |
... | ... | |
4866 | 4891 |
assert target_formdef.data_class().count() == 0 |
4867 | 4892 | |
4868 | 4893 | |
4869 |
def test_create_carddata(pub):
|
|
4894 |
def test_create_carddata(two_pubs):
|
|
4870 | 4895 |
CardDef.wipe() |
4871 | 4896 |
FormDef.wipe() |
4872 |
LoggedError.wipe() |
|
4897 |
if two_pubs.is_using_postgresql(): |
|
4898 |
two_pubs.loggederror_class.wipe() |
|
4873 | 4899 | |
4874 | 4900 |
carddef = CardDef() |
4875 | 4901 |
carddef.name = 'My card' |
... | ... | |
4921 | 4947 | |
4922 | 4948 |
assert carddef.data_class().count() == 1 |
4923 | 4949 | |
4924 |
errors = LoggedError.select() |
|
4925 |
assert len(errors) == 2 |
|
4926 |
assert any('form_var_undefined' in (error.exception_message or '') for error in errors) |
|
4927 |
assert any('invalid date value' in (error.exception_message or '') for error in errors) |
|
4950 |
if two_pubs.is_using_postgresql(): |
|
4951 |
errors = two_pubs.loggederror_class.select() |
|
4952 |
assert len(errors) == 2 |
|
4953 |
assert any('form_var_undefined' in (error.exception_message or '') for error in errors) |
|
4954 |
assert any('invalid date value' in (error.exception_message or '') for error in errors) |
|
4928 | 4955 | |
4929 | 4956 |
formdata = formdef.data_class()() |
4930 | 4957 |
today = datetime.date.today() |
4931 | 4958 | |
4932 | 4959 |
formdata.data = {'1': 'item1', |
4933 | 4960 |
'1_display': 'item1', |
4934 |
'2': today} |
|
4961 |
'2': today.timetuple()}
|
|
4935 | 4962 |
formdata.just_created() |
4936 | 4963 |
formdata.perform_workflow() |
4937 | 4964 | |
... | ... | |
4949 | 4976 |
assert carddef.data_class().count() == 0 |
4950 | 4977 | |
4951 | 4978 | |
4952 |
def test_call_external_workflow_with_evolution_linked_object(pub):
|
|
4979 |
def test_call_external_workflow_with_evolution_linked_object(two_pubs):
|
|
4953 | 4980 |
FormDef.wipe() |
4954 | 4981 |
CardDef.wipe() |
4955 |
LoggedError.wipe() |
|
4982 |
if two_pubs.is_using_postgresql(): |
|
4983 |
two_pubs.loggederror_class.wipe() |
|
4956 | 4984 | |
4957 | 4985 |
external_wf = Workflow(name='External Workflow') |
4958 | 4986 |
st1 = external_wf.add_status(name='New') |
... | ... | |
5031 | 5059 | |
5032 | 5060 |
# remove external formdata |
5033 | 5061 |
perform_items([action], formdata) |
5034 |
assert LoggedError.count() == 0 |
|
5062 |
if two_pubs.is_using_postgresql(): |
|
5063 |
assert two_pubs.loggederror_class.count() == 0 |
|
5035 | 5064 |
assert external_formdef.data_class().count() == 0 |
5036 | 5065 |
assert external_carddef.data_class().count() == 1 |
5037 | 5066 | |
5038 | 5067 |
# formdata is already deleted: cannot find it again |
5039 | 5068 |
perform_items([action], formdata) |
5040 |
assert LoggedError.count() == 1 |
|
5041 |
logged_error = LoggedError.select()[0] |
|
5042 |
assert logged_error.summary == 'Could not find linked "External Form" object by id %s' % external_formdata.id |
|
5043 |
assert logged_error.exception_class == 'KeyError' |
|
5044 |
assert logged_error.status_item_id == action.id |
|
5069 |
if two_pubs.is_using_postgresql(): |
|
5070 |
assert two_pubs.loggederror_class.count() == 1 |
|
5071 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
5072 |
assert logged_error.summary == 'Could not find linked "External Form" object by id %s' % external_formdata.id |
|
5073 |
assert logged_error.exception_class == 'KeyError' |
|
5074 |
assert logged_error.status_item_id == action.id |
|
5045 | 5075 | |
5046 | 5076 |
# try remove an unexisting carddef: do nothing |
5047 | 5077 |
unused_carddef = CardDef() |
... | ... | |
5049 | 5079 |
unused_carddef.fields = [] |
5050 | 5080 |
unused_carddef.workflow = external_wf |
5051 | 5081 |
unused_carddef.store() |
5052 |
LoggedError.wipe() |
|
5082 |
if two_pubs.is_using_postgresql(): |
|
5083 |
two_pubs.loggederror_class.wipe() |
|
5053 | 5084 |
action.slug = 'carddef:%s' % unused_carddef.url_name |
5054 | 5085 |
wf.store() |
5055 | 5086 |
perform_items([action], formdata) |
5056 |
assert LoggedError.count() == 0 |
|
5087 |
if two_pubs.is_using_postgresql(): |
|
5088 |
assert two_pubs.loggederror_class.count() == 0 |
|
5057 | 5089 |
assert external_formdef.data_class().count() == 0 |
5058 | 5090 |
assert external_carddef.data_class().count() == 1 |
5059 | 5091 |
# remove the right carddef |
5060 | 5092 |
action.slug = 'carddef:%s' % external_carddef.url_name |
5061 | 5093 |
wf.store() |
5062 | 5094 |
perform_items([action], formdata) |
5063 |
assert LoggedError.count() == 0 |
|
5095 |
if two_pubs.is_using_postgresql(): |
|
5096 |
assert two_pubs.loggederror_class.count() == 0 |
|
5064 | 5097 |
assert external_formdef.data_class().count() == 0 |
5065 | 5098 |
assert external_carddef.data_class().count() == 0 |
5066 | 5099 | |
5067 | 5100 | |
5068 |
def test_call_external_workflow_with_data_sourced_object(pub):
|
|
5101 |
def test_call_external_workflow_with_data_sourced_object(two_pubs):
|
|
5069 | 5102 |
FormDef.wipe() |
5070 | 5103 |
CardDef.wipe() |
5071 |
LoggedError.wipe() |
|
5104 |
if two_pubs.is_using_postgresql(): |
|
5105 |
two_pubs.loggederror_class.wipe() |
|
5072 | 5106 | |
5073 | 5107 |
carddef_wf = Workflow(name='Carddef Workflow') |
5074 | 5108 |
carddef_wf.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(carddef_wf) |
... | ... | |
5129 | 5163 |
formdef.workflow = wf |
5130 | 5164 |
formdef.store() |
5131 | 5165 | |
5132 |
assert LoggedError.count() == 0 |
|
5166 |
if two_pubs.is_using_postgresql(): |
|
5167 |
assert two_pubs.loggederror_class.count() == 0 |
|
5133 | 5168 |
assert carddef.data_class().count() == 1 |
5134 | 5169 | |
5135 | 5170 |
formdata = formdef.data_class()() |
... | ... | |
5139 | 5174 |
formdata.perform_workflow() |
5140 | 5175 | |
5141 | 5176 |
perform_items([update_action], formdata) |
5142 |
assert LoggedError.count() == 0 |
|
5177 |
if two_pubs.is_using_postgresql(): |
|
5178 |
assert two_pubs.loggederror_class.count() == 0 |
|
5143 | 5179 |
assert carddef.data_class().count() == 1 |
5144 | 5180 |
data = carddef.data_class().select()[0] |
5145 | 5181 |
assert data.data['bo0'] == '1' |
... | ... | |
5149 | 5185 |
assert data.data['bo0'] == '2' |
5150 | 5186 | |
5151 | 5187 |
perform_items([delete_action], formdata) |
5152 |
assert LoggedError.count() == 0 |
|
5188 |
if two_pubs.is_using_postgresql(): |
|
5189 |
assert two_pubs.loggederror_class.count() == 0 |
|
5153 | 5190 |
assert carddef.data_class().count() == 0 |
5154 | 5191 | |
5155 | 5192 |
perform_items([delete_action], formdata) |
5156 |
assert LoggedError.count() == 1 |
|
5157 |
logged_error = LoggedError.select()[0] |
|
5158 |
assert logged_error.summary == 'Could not find linked "Data" object by id %s' % carddata.id |
|
5159 |
assert logged_error.exception_class == 'KeyError' |
|
5193 |
if two_pubs.is_using_postgresql(): |
|
5194 |
assert two_pubs.loggederror_class.count() == 1 |
|
5195 |
logged_error = two_pubs.loggederror_class.select()[0] |
|
5196 |
assert logged_error.summary == 'Could not find linked "Data" object by id %s' % carddata.id |
|
5197 |
assert logged_error.exception_class == 'KeyError' |
|
5160 | 5198 | |
5161 | 5199 | |
5162 | 5200 |
def test_call_external_workflow_with_parent_object(pub): |
5163 | 5201 |
FormDef.wipe() |
5164 | 5202 |
CardDef.wipe() |
5165 |
LoggedError.wipe() |
|
5166 | 5203 | |
5167 | 5204 |
# carddef workflow, with global action to increment a counter in its |
5168 | 5205 |
# backoffice fields. |
... | ... | |
5249 | 5286 |
def test_call_external_workflow_use_caller_variable(pub): |
5250 | 5287 |
FormDef.wipe() |
5251 | 5288 |
CardDef.wipe() |
5252 |
LoggedError.wipe() |
|
5253 | 5289 | |
5254 | 5290 |
# carddef workflow, with global action to set a value in a backoffice field |
5255 | 5291 |
carddef_wf = Workflow(name='Carddef Workflow') |
... | ... | |
5324 | 5360 |
def test_edit_carddata_with_data_sourced_object(pub): |
5325 | 5361 |
FormDef.wipe() |
5326 | 5362 |
CardDef.wipe() |
5327 |
LoggedError.wipe() |
|
5328 | 5363 | |
5329 | 5364 |
datasource = { |
5330 | 5365 |
'type': 'formula', |
... | ... | |
5437 | 5472 |
def test_edit_carddata_with_linked_object(pub): |
5438 | 5473 |
FormDef.wipe() |
5439 | 5474 |
CardDef.wipe() |
5440 |
LoggedError.wipe() |
|
5441 | 5475 | |
5442 | 5476 |
carddef = CardDef() |
5443 | 5477 |
carddef.name = 'Parent' |
tests/test_wscall.py | ||
---|---|---|
3 | 3 | |
4 | 4 |
from wcs import fields |
5 | 5 |
from wcs.formdef import FormDef |
6 |
from wcs.logged_errors import LoggedError |
|
7 | 6 |
from wcs.qommon.http_request import HTTPRequest |
8 | 7 |
from wcs.qommon.template import Template |
9 | 8 |
from wcs.wscalls import NamedWsCall |
... | ... | |
27 | 26 | |
28 | 27 |
def test_named_wscall(pub): |
29 | 28 |
# create object |
29 |
NamedWsCall.wipe() |
|
30 | 30 |
wscall = NamedWsCall() |
31 | 31 |
wscall.name = 'Hello' |
32 | 32 |
wscall.request = {'url': 'http://example.net', 'qs_data': {'a': 'b'}} |
... | ... | |
172 | 172 | |
173 | 173 |
@pytest.mark.parametrize('notify_on_errors', [True, False]) |
174 | 174 |
@pytest.mark.parametrize('record_on_errors', [True, False]) |
175 |
def test_webservice_on_error(http_requests, pub, emails, notify_on_errors, record_on_errors): |
|
175 |
def test_webservice_on_error_with_sql(http_requests, emails, notify_on_errors, record_on_errors): |
|
176 |
pub = create_temporary_pub(sql_mode=True) |
|
176 | 177 |
pub.cfg['debug'] = {'error_email': 'errors@localhost.invalid'} |
177 | 178 |
pub.write_cfg() |
178 | 179 | |
179 | 180 |
NamedWsCall.wipe() |
180 |
LoggedError.wipe()
|
|
181 |
pub.loggederror_class.wipe()
|
|
181 | 182 |
FormDef.wipe() |
182 | 183 | |
183 | 184 |
wscall = NamedWsCall() |
... | ... | |
200 | 201 |
resp = get_app(pub).get('/foobar/') |
201 | 202 |
assert 'Foo Bar ' in resp.text |
202 | 203 |
assert emails.count() == 0 |
203 |
assert LoggedError.count() == 0
|
|
204 |
assert pub.loggederror_class.count() == 0
|
|
204 | 205 | |
205 | 206 |
for url_part in ['400', '400-json', '404', '404-json', '500', 'json-err1', 'json-errheader1']: |
206 | 207 |
status_code = 200 |
... | ... | |
217 | 218 |
else: |
218 | 219 |
assert emails.count() == 0 |
219 | 220 |
if record_on_errors: |
220 |
assert LoggedError.count() == 1
|
|
221 |
LoggedError.wipe()
|
|
221 |
assert pub.loggederror_class.count() == 1
|
|
222 |
pub.loggederror_class.wipe()
|
|
222 | 223 |
else: |
223 |
assert LoggedError.count() == 0 |
|
224 |
assert pub.loggederror_class.count() == 0 |
tests/utilities.py | ||
---|---|---|
4 | 4 |
import tempfile |
5 | 5 |
import random |
6 | 6 |
import psycopg2 |
7 |
import pytest |
|
8 | 7 |
import shutil |
9 | 8 |
import sys |
10 |
import threading |
|
11 | 9 | |
12 | 10 |
from wcs import sql, sessions, custom_views |
13 | 11 | |
... | ... | |
20 | 18 |
from wcs.qommon import force_str |
21 | 19 |
import wcs |
22 | 20 |
import wcs.wsgi |
23 |
from wcs import publisher, compat |
|
24 |
from wcs.qommon.http_request import HTTPRequest |
|
21 |
from wcs import compat |
|
25 | 22 |
from wcs.users import User |
26 | 23 |
from wcs.tracking_code import TrackingCode |
27 | 24 |
import wcs.qommon.emails |
... | ... | |
81 | 78 |
pub.session_class = sql.Session |
82 | 79 |
pub.custom_view_class = sql.CustomView |
83 | 80 |
pub.snapshot_class = sql.Snapshot |
81 |
pub.loggederror_class = sql.LoggedError |
|
84 | 82 |
pub.is_using_postgresql = lambda: True |
85 | 83 |
else: |
86 | 84 |
pub.user_class = User |
87 | 85 |
pub.tracking_code_class = TrackingCode |
88 | 86 |
pub.session_class = sessions.BasicSession |
89 | 87 |
pub.custom_view_class = custom_views.CustomView |
90 |
pub.snapshot_class = None |
|
91 | 88 |
pub.is_using_postgresql = lambda: False |
92 | 89 | |
93 | 90 |
pub.session_manager_class = sessions.StorageSessionManager |
... | ... | |
172 | 169 |
sql.do_session_table() |
173 | 170 |
sql.do_custom_views_table() |
174 | 171 |
sql.do_snapshots_table() |
172 |
sql.do_loggederrors_table() |
|
175 | 173 |
sql.do_meta_table() |
176 | 174 | |
177 | 175 |
conn.close() |
wcs/admin/logged_errors.py | ||
---|---|---|
24 | 24 |
from wcs.qommon import errors, get_cfg |
25 | 25 |
from wcs.qommon.misc import localstrftime |
26 | 26 | |
27 |
from wcs.logged_errors import LoggedError |
|
28 | ||
29 | 27 | |
30 | 28 |
class LoggedErrorDirectory(Directory): |
31 | 29 |
_q_exports = ['', 'delete', 'ack'] |
... | ... | |
107 | 105 |
return redirect('.') |
108 | 106 | |
109 | 107 |
def delete(self): |
110 |
self.error.remove_self()
|
|
108 |
get_publisher().loggederror_class.remove_object(self.error.id)
|
|
111 | 109 |
return redirect('..') |
112 | 110 | |
113 | 111 | |
... | ... | |
117 | 115 |
@classmethod |
118 | 116 |
def get_errors(cls, formdef_id=None, workflow_id=None): |
119 | 117 |
errors = [] |
118 |
if not get_publisher().loggederror_class: |
|
119 |
return errors |
|
120 | 120 |
if formdef_id: |
121 |
errors = LoggedError.get_with_indexed_value('formdef_id', formdef_id)
|
|
121 |
errors = get_publisher().loggederror_class.get_with_indexed_value('formdef_id', formdef_id)
|
|
122 | 122 |
elif workflow_id: |
123 |
errors = LoggedError.get_with_indexed_value('workflow_id', workflow_id)
|
|
123 |
errors = get_publisher().loggederror_class.get_with_indexed_value('workflow_id', workflow_id)
|
|
124 | 124 |
return list(errors) |
125 | 125 | |
126 | 126 |
@classmethod |
... | ... | |
167 | 167 | |
168 | 168 |
def _q_lookup(self, component): |
169 | 169 |
try: |
170 |
error = LoggedError.get(component)
|
|
170 |
error = get_publisher().loggederror_class.get(component)
|
|
171 | 171 |
except KeyError: |
172 | 172 |
raise errors.TraversalError() |
173 | 173 |
get_response().breadcrumb.append(('logged-errors/', _('Logged Errors'))) |
wcs/conditions.py | ||
---|---|---|
56 | 56 |
if self.log_errors: |
57 | 57 |
get_logger().warning('failed to evaluate %r (%r)', self, e) |
58 | 58 |
if self.record_errors: |
59 |
from wcs.logged_errors import LoggedError |
|
60 | 59 |
summary = _('Failed to evaluate condition') |
61 |
LoggedError.record(summary, |
|
62 |
formdata=self.context.get('formdata'), |
|
63 |
status_item=self.context.get('status_item'), |
|
64 |
expression=self.value, expression_type=self.type, |
|
65 |
exception=e) |
|
60 |
get_publisher().record_loggederror( |
|
61 |
summary, |
|
62 |
formdata=self.context.get('formdata'), |
|
63 |
status_item=self.context.get('status_item'), |
|
64 |
expression=self.value, expression_type=self.type, |
|
65 |
exception=e) |
|
66 | 66 |
raise RuntimeError() |
67 | 67 | |
68 | 68 |
def evaluate_python(self, local_variables): |
wcs/data_sources.py | ||
---|---|---|
512 | 512 |
url += param_name + '=' + urllib.quote(param_value) |
513 | 513 | |
514 | 514 |
def find_item(items, name, value): |
515 |
from wcs.logged_errors import LoggedError |
|
516 | 515 |
for item in items: |
517 | 516 |
if str(item.get(name)) == str(value): |
518 | 517 |
return item |
519 | 518 |
# not found |
520 |
LoggedError.record(_('Could not find element by id "%s"') % value)
|
|
519 |
get_publisher().record_loggederror(_('Could not find element by id "%s"') % value)
|
|
521 | 520 |
return None |
522 | 521 | |
523 | 522 |
request = get_request() |
wcs/fields.py | ||
---|---|---|
387 | 387 |
except TemplateError: |
388 | 388 |
return ('', explicit_lock) |
389 | 389 |
except AttributeError as e: |
390 |
from wcs.logged_errors import LoggedError |
|
391 |
LoggedError.record(_('Failed to evaluate prefill on field "%s"') % self.label, formdef=getattr(self, 'formdef', None), exception=e) |
|
390 |
get_publisher().record_loggederror( |
|
391 |
_('Failed to evaluate prefill on field "%s"') % self.label, |
|
392 |
formdef=getattr(self, 'formdef', None), |
|
393 |
exception=e) |
|
392 | 394 |
return ('', explicit_lock) |
393 | 395 | |
394 | 396 |
elif t == 'user' and user: |
wcs/formdata.py | ||
---|---|---|
601 | 601 |
if status_id == '_previous': |
602 | 602 |
previous_status = self.pop_previous_marked_status() |
603 | 603 |
if not previous_status: |
604 |
from wcs.logged_errors import LoggedError |
|
605 | 604 |
summary = _('Failed to compute previous status') |
606 |
LoggedError.record(summary, formdata=self)
|
|
605 |
get_publisher().record_loggederror(summary, formdata=self)
|
|
607 | 606 |
return |
608 | 607 |
status_id = previous_status.id |
609 | 608 |
status = 'wf-%s' % status_id |
... | ... | |
1211 | 1210 |
def iter_target_datas(self, objectdef=None, object_type=None, status_item=None): |
1212 | 1211 |
# objectdef, object_type and status_item are provided when called from a workflow action |
1213 | 1212 |
from wcs.wf.create_formdata import LinkedFormdataEvolutionPart |
1214 |
from wcs.logged_errors import LoggedError |
|
1215 | 1213 |
from .carddef import CardDef |
1216 | 1214 |
from .formdef import FormDef |
1217 | 1215 | |
... | ... | |
1265 | 1263 |
yield objectdef.data_class().get(target_id) |
1266 | 1264 |
except KeyError as e: |
1267 | 1265 |
# use custom error message depending on target type |
1268 |
LoggedError.record(_('Could not find linked "%(object_name)s" object by id %(object_id)s') % { |
|
1269 |
'object_name': objectdef.name, 'object_id': target_id}, |
|
1266 |
get_publisher().record_loggederror( |
|
1267 |
_('Could not find linked "%(object_name)s" object by id %(object_id)s') % { |
|
1268 |
'object_name': objectdef.name, 'object_id': target_id}, |
|
1270 | 1269 |
formdata=self, status_item=status_item, exception=e) |
1271 | 1270 |
else: |
1272 | 1271 |
# inspect page |
wcs/logged_errors.py | ||
---|---|---|
17 | 17 |
import datetime |
18 | 18 | |
19 | 19 |
from .qommon.misc import simplify |
20 |
from .qommon.xml_storage import XmlStorableObject |
|
21 | 20 |
from wcs.carddef import CardDef |
22 | 21 |
from wcs.formdef import FormDef |
23 | 22 |
from wcs.workflows import Workflow |
24 | 23 | |
25 | 24 | |
26 |
class LoggedError(XmlStorableObject):
|
|
25 |
class LoggedError(object):
|
|
27 | 26 |
_names = 'logged-errors' |
28 |
xml_root_node = 'error' |
|
29 |
_indexes = ['tech_id'] |
|
30 |
_hashed_indexes = ['formdef_id', 'workflow_id'] |
|
31 | 27 | |
28 |
id = None |
|
29 |
tech_id = None |
|
32 | 30 |
summary = None |
33 | 31 |
formdef_class = 'FormDef' |
34 | 32 |
formdata_id = None |
... | ... | |
46 | 44 |
latest_occurence_timestamp = None |
47 | 45 |
acked = False |
48 | 46 | |
49 |
# declarations for serialization |
|
50 |
XML_NODES = [ |
|
51 |
('summary', 'str'), ('traceback', 'str'), |
|
52 |
('exception_class', 'str'), ('exception_message', 'str'), |
|
53 |
('expression', 'str'), ('expression_type', 'str'), |
|
54 |
('formdata_id', 'str'), ('formdef_id', 'str'), ('workflow_id', 'str'), |
|
55 |
('formdef_class', 'str'), |
|
56 |
('status_id', 'str'), ('status_item_id', 'str'), |
|
57 |
('occurences_count', 'int'), |
|
58 |
('first_occurence_timestamp', 'datetime'), |
|
59 |
('latest_occurence_timestamp', 'datetime'), |
|
60 |
('acked', 'bool')] |
|
61 | ||
62 | 47 |
@classmethod |
63 | 48 |
def record(cls, error_summary, plain_error_msg=None, formdata=None, |
64 | 49 |
formdef=None, workflow=None, status=None, status_item=None, |
... | ... | |
97 | 82 |
error.status_id = status.id |
98 | 83 | |
99 | 84 |
error.first_occurence_timestamp = datetime.datetime.now() |
100 |
error.id = '%s-%s' % ( |
|
101 |
error.first_occurence_timestamp.strftime('%Y%m%d-%H%M%S'), |
|
102 |
error.tech_id, |
|
103 |
) |
|
104 |
existing_error = cls.get_on_index(error.tech_id, 'tech_id', ignore_errors=True) |
|
105 |
if existing_error: |
|
106 |
error = existing_error |
|
85 |
error.tech_id = error.build_tech_id() |
|
86 |
existing_errors = list(cls.get_with_indexed_value('tech_id', error.tech_id)) |
|
87 |
if existing_errors: |
|
88 |
error = existing_errors[0] |
|
107 | 89 |
error.occurences_count += 1 |
108 | 90 |
error.latest_occurence_timestamp = datetime.datetime.now() |
109 | 91 |
error.store() |
... | ... | |
113 | 95 |
def record_exception(cls, error_summary, plain_error_msg, publisher): |
114 | 96 |
try: |
115 | 97 |
context = publisher.substitutions.get_context_variables() |
116 |
except Exception as e:
|
|
98 |
except Exception: |
|
117 | 99 |
return |
118 | 100 |
formdata_id = context.get('form_number_raw') |
119 | 101 |
formdef_urlname = context.get('form_slug') |
... | ... | |
126 | 108 |
klass = CardDef |
127 | 109 |
formdef = klass.get_by_urlname(formdef_urlname) |
128 | 110 |
formdata = formdef.data_class().get(formdata_id, ignore_errors=True) |
129 |
return cls.record(error_summary, plain_error_msg, formdata=formdata, |
|
130 |
formdef=formdef, workflow=formdef.workflow) |
|
111 |
return cls.record( |
|
112 |
error_summary, plain_error_msg, formdata=formdata, |
|
113 |
formdef=formdef, workflow=formdef.workflow) |
|
131 | 114 | |
132 |
@property |
|
133 |
def tech_id(self): |
|
115 |
def build_tech_id(self): |
|
134 | 116 |
tech_id = '%s-%s-' % (self.formdef_id, self.workflow_id) |
135 | 117 |
if self.status_id: |
136 | 118 |
tech_id += '%s-' % self.status_id |
wcs/publisher.py | ||
---|---|---|
52 | 52 | |
53 | 53 |
from .users import User |
54 | 54 |
from .tracking_code import TrackingCode |
55 |
from .logged_errors import LoggedError |
|
56 | 55 | |
57 | 56 |
import pickle |
58 | 57 | |
... | ... | |
153 | 152 |
self.session_class = sql.Session |
154 | 153 |
self.custom_view_class = sql.CustomView |
155 | 154 |
self.snapshot_class = sql.Snapshot |
155 |
self.loggederror_class = sql.LoggedError |
|
156 | 156 |
sql.get_connection(new=True) |
157 | 157 |
else: |
158 | 158 |
self.user_class = User |
... | ... | |
160 | 160 |
self.session_class = sessions.BasicSession |
161 | 161 |
self.custom_view_class = custom_views.CustomView |
162 | 162 |
self.snapshot_class = None |
163 |
self.loggederror_class = None |
|
163 | 164 | |
164 | 165 |
self.session_manager_class = sessions.StorageSessionManager |
165 | 166 |
self.set_session_manager(self.session_manager_class(session_class=self.session_class)) |
... | ... | |
312 | 313 |
sql.do_tracking_code_table() |
313 | 314 |
sql.do_custom_views_table() |
314 | 315 |
sql.do_snapshots_table() |
316 |
sql.do_loggederrors_table() |
|
315 | 317 |
sql.do_meta_table() |
316 | 318 |
from .formdef import FormDef |
317 | 319 |
from .carddef import CardDef |
... | ... | |
340 | 342 | |
341 | 343 |
def log_internal_error(self, error_summary, plain_error_msg, record=False, notify=True): |
342 | 344 |
tech_id = None |
343 |
if record: |
|
344 |
logged_exception = LoggedError.record_exception(
|
|
345 |
if record and self.loggederror_class:
|
|
346 |
logged_exception = self.loggederror_class.record_exception(
|
|
345 | 347 |
error_summary, plain_error_msg, publisher=self) |
346 | 348 |
if logged_exception: |
347 | 349 |
tech_id = logged_exception.tech_id |
... | ... | |
357 | 359 |
# this could happen on file descriptor exhaustion |
358 | 360 |
pass |
359 | 361 | |
362 |
def record_loggederror(self, *args, **kwargs): |
|
363 |
if self.loggederror_class: |
|
364 |
self.loggederror_class.record(*args, **kwargs) |
|
365 | ||
360 | 366 |
def apply_global_action_timeouts(self): |
361 | 367 |
from wcs.workflows import Workflow, WorkflowGlobalActionTimeoutTrigger |
362 | 368 |
for workflow in Workflow.select(): |
wcs/sql.py | ||
---|---|---|
45 | 45 |
import wcs.carddata |
46 | 46 |
import wcs.custom_views |
47 | 47 |
import wcs.formdata |
48 |
import wcs.logged_errors |
|
48 | 49 |
import wcs.snapshots |
49 | 50 |
import wcs.tracking_code |
50 | 51 |
import wcs.users |
... | ... | |
859 | 860 |
cur.close() |
860 | 861 | |
861 | 862 | |
863 |
def do_loggederrors_table(concurrently=False): |
|
864 |
conn, cur = get_connection_and_cursor() |
|
865 |
table_name = 'loggederrors' |
|
866 | ||
867 |
cur.execute('''SELECT COUNT(*) FROM information_schema.tables |
|
868 |
WHERE table_schema = 'public' |
|
869 |
AND table_name = %s''', (table_name,)) |
|
870 |
if cur.fetchone()[0] == 0: |
|
871 |
cur.execute('''CREATE TABLE %s (id SERIAL PRIMARY KEY, |
|
872 |
tech_id VARCHAR UNIQUE, |
|
873 |
summary VARCHAR, |
|
874 |
formdef_class VARCHAR, |
|
875 |
formdata_id VARCHAR, |
|
876 |
formdef_id VARCHAR, |
|
877 |
workflow_id VARCHAR, |
|
878 |
status_id VARCHAR, |
|
879 |
status_item_id VARCHAR, |
|
880 |
expression VARCHAR, |
|
881 |
expression_type VARCHAR, |
|
882 |
traceback TEXT, |
|
883 |
exception_class VARCHAR, |
|
884 |
exception_message VARCHAR, |
|
885 |
occurences_count INTEGER, |
|
886 |
first_occurence_timestamp TIMESTAMP WITH TIME ZONE, |
|
887 |
latest_occurence_timestamp TIMESTAMP WITH TIME ZONE, |
|
888 |
acked BOOLEAN |
|
889 |
)''' % table_name) |
|
890 |
cur.execute('''SELECT column_name FROM information_schema.columns |
|
891 |
WHERE table_schema = 'public' |
|
892 |
AND table_name = %s''', (table_name,)) |
|
893 |
existing_fields = set([x[0] for x in cur.fetchall()]) |
|
894 | ||
895 |
needed_fields = set([x[0] for x in LoggedError._table_static_fields]) |
|
896 | ||
897 |
# delete obsolete fields |
|
898 |
for field in (existing_fields - needed_fields): |
|
899 |
cur.execute('''ALTER TABLE %s DROP COLUMN %s''' % (table_name, field)) |
|
900 | ||
901 |
create_index = 'CREATE INDEX' |
|
902 |
if concurrently: |
|
903 |
create_index = 'CREATE INDEX CONCURRENTLY' |
|
904 | ||
905 |
# build indexes |
|
906 |
existing_indexes = set() |
|
907 |
cur.execute('''SELECT indexname |
|
908 |
FROM pg_indexes |
|
909 |
WHERE schemaname = 'public' |
|
910 |
AND tablename = %s''', (table_name,)) |
|
911 |
existing_indexes = set([x[0] for x in cur.fetchall()]) |
|
912 | ||
913 |
for attr in ('formdef_id', 'workflow_id'): |
|
914 |
if not table_name + '_' + attr + '_idx' in existing_indexes: |
|
915 |
cur.execute('%(create_index)s %(table_name)s_%(attr)s_idx ON %(table_name)s (%(attr)s)' % { |
|
916 |
'create_index': create_index, |
|
917 |
'table_name': table_name, |
|
918 |
'attr': attr}) |
|
919 | ||
920 |
conn.commit() |
|
921 |
cur.close() |
|
922 | ||
923 | ||
862 | 924 |
@guard_postgres |
863 | 925 |
def do_meta_table(conn=None, cur=None, insert_current_sql_level=True): |
864 | 926 |
own_conn = False |
... | ... | |
2458 | 2520 |
return cls.get(row[0]) |
2459 | 2521 | |
2460 | 2522 | |
2523 |
class LoggedError(SqlMixin, wcs.logged_errors.LoggedError): |
|
2524 |
_table_name = 'loggederrors' |
|
2525 |
_table_static_fields = [ |
|
2526 |
('id', 'serial'), |
|
2527 |
('tech_id', 'varchar'), |
|
2528 |
('summary', 'varchar'), |
|
2529 |
('formdef_class', 'varchar'), |
|
2530 |
('formdata_id', 'varchar'), |
|
2531 |
('formdef_id', 'varchar'), |
|
2532 |
('workflow_id', 'varchar'), |
|
2533 |
('status_id', 'varchar'), |
|
2534 |
('status_item_id', 'varchar'), |
|
2535 |
('expression', 'varchar'), |
|
2536 |
('expression_type', 'varchar'), |
|
2537 |
('traceback', 'text'), |
|
2538 |
('exception_class', 'varchar'), |
|
2539 |
('exception_message', 'varchar'), |
|
2540 |
('occurences_count', 'integer'), |
|
2541 |
('first_occurence_timestamp', 'timestamptz'), |
|
2542 |
('latest_occurence_timestamp', 'timestamptz'), |
|
2543 |
('acked', 'boolean'), |
|
2544 |
] |
|
2545 | ||
2546 |
_numerical_id = False |
|
2547 | ||
2548 |
@guard_postgres |
|
2549 |
@invalidate_substitution_cache |
|
2550 |
def store(self): |
|
2551 |
sql_dict = {x: getattr(self, x) for x, y in self._table_static_fields} |
|
2552 | ||
2553 |
conn, cur = get_connection_and_cursor() |
|
2554 |
if not self.id: |
|
2555 |
column_names = [x for x in sql_dict.keys() if x != 'id'] |
|
2556 |
sql_statement = '''INSERT INTO %s (%s) |
|
2557 |
VALUES (%s) |
|
2558 |
RETURNING id''' % ( |
|
2559 |
self._table_name, |
|
2560 |
', '.join(column_names), |
|
2561 |
', '.join(['%%(%s)s' % x for x in column_names])) |
|
2562 |
cur.execute(sql_statement, sql_dict) |
|
2563 |
self.id = cur.fetchone()[0] |
|
2564 |
else: |
|
2565 |
column_names = sql_dict.keys() |
|
2566 |
sql_statement = '''UPDATE %s SET %s WHERE id = %%(id)s RETURNING id''' % ( |
|
2567 |
self._table_name, |
|
2568 |
', '.join(['%s = %%(%s)s' % (x, x) for x in column_names])) |
|
2569 |
cur.execute(sql_statement, sql_dict) |
|
2570 |
if cur.fetchone() is None: |
|
2571 |
raise AssertionError() |
|
2572 | ||
2573 |
conn.commit() |
|
2574 |
cur.close() |
|
2575 | ||
2576 |
@classmethod |
|
2577 |
def _row2ob(cls, row, **kwargs): |
|
2578 |
o = cls() |
|
2579 |
for field, value in zip(cls._table_static_fields, tuple(row)): |
|
2580 |
if field[1] in ('varchar', 'text'): |
|
2581 |
setattr(o, field[0], str_encode(value)) |
|
2582 |
else: |
|
2583 |
setattr(o, field[0], value) |
|
2584 |
return o |
|
2585 | ||
2586 |
@classmethod |
|
2587 |
def get_data_fields(cls): |
|
2588 |
return [] |
|
2589 | ||
2590 |
@classmethod |
|
2591 |
@guard_postgres |
|
2592 |
def fix_sequences(cls): |
|
2593 |
conn, cur = get_connection_and_cursor() |
|
2594 | ||
2595 |
sql_statement = '''select max(id) from %s''' % cls._table_name |
|
2596 |
cur.execute(sql_statement) |
|
2597 |
max_id = cur.fetchone()[0] |
|
2598 |
if max_id is not None: |
|
2599 |
sql_statement = '''ALTER SEQUENCE %s_id_seq RESTART %s''' % ( |
|
2600 |
cls._table_name, max_id+1) |
|
2601 |
cur.execute(sql_statement) |
|
2602 | ||
2603 |
conn.commit() |
|
2604 |
cur.close() |
|
2605 | ||
2606 | ||
2461 | 2607 |
class classproperty(object): |
2462 | 2608 |
def __init__(self, f): |
2463 | 2609 |
self.f = f |
... | ... | |
2691 | 2837 |
# latest migration, number + description (description is not used |
2692 | 2838 |
# programmaticaly but will make sure git conflicts if two migrations are |
2693 | 2839 |
# separately added with the same number) |
2694 |
SQL_LEVEL = (46, 'add index on formdata(status) - fix')
|
|
2840 |
SQL_LEVEL = (47, 'use SQL to store LoggedError')
|
|
2695 | 2841 | |
2696 | 2842 | |
2697 | 2843 |
def migrate_global_views(conn, cur): |
... | ... | |
2846 | 2992 |
if sql_level < 42: |
2847 | 2993 |
# 42: create snapshots table |
2848 | 2994 |
do_snapshots_table() |
2995 |
if sql_level < 47: |
|
2996 |
# 47: store LoggedErrors in SQL |
|
2997 |
do_loggederrors_table() |
|
2849 | 2998 | |
2850 | 2999 |
cur.execute('''UPDATE wcs_meta SET value = %s WHERE key = %s''', ( |
2851 | 3000 |
str(SQL_LEVEL[0]), 'sql_level')) |
wcs/wf/backoffice_fields.py | ||
---|---|---|
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 |
import xml.etree.ElementTree as ET |
18 | ||
19 |
from quixote import get_publisher |
|
18 | 20 |
from quixote.html import htmltext |
19 | 21 | |
20 | 22 |
from ..qommon import _, N_ |
... | ... | |
133 | 135 |
try: |
134 | 136 |
new_value = formdef_field.convert_value_from_anything(new_value) |
135 | 137 |
except ValueError as e: |
136 |
from wcs.logged_errors import LoggedError |
|
137 | 138 |
summary = _('Failed to convert %(class)s value to %(kind)s field (%(id)s)') % { |
138 | 139 |
'class': type(new_value), |
139 | 140 |
'kind': _(getattr(formdef_field, 'description', 'unknown')), |
140 | 141 |
'id': field['field_id'], |
141 | 142 |
} |
142 | 143 |
expression_dict = self.get_expression(field['value']) |
143 |
LoggedError.record(summary, formdata=formdata, status_item=self, |
|
144 |
expression=expression_dict['value'], |
|
145 |
expression_type=expression_dict['type'], |
|
146 |
exception=e) |
|
144 |
get_publisher().record_loggederror( |
|
145 |
summary, formdata=formdata, status_item=self, |
|
146 |
expression=expression_dict['value'], |
|
147 |
expression_type=expression_dict['type'], |
|
148 |
exception=e) |
|
147 | 149 |
continue |
148 | 150 | |
149 | 151 |
formdata.data['%s' % field['field_id']] = new_value |
wcs/wf/create_formdata.py | ||
---|---|---|
27 | 27 |
SingleSelectWidget, ComputedExpressionWidget, |
28 | 28 |
CheckboxWidget, VarnameWidget, HtmlWidget) |
29 | 29 | |
30 |
from wcs.logged_errors import LoggedError |
|
31 | 30 |
from wcs.workflows import WorkflowStatusItem, register_item_class |
32 | 31 |
from wcs.formdef import FormDef |
33 | 32 | |
... | ... | |
391 | 390 |
field=dest_field, |
392 | 391 |
value=src.data.get(field.id)) |
393 | 392 |
except Exception as e: |
394 |
LoggedError.record(_('Could not copy field by varname for "%s"') % field.varname, |
|
395 |
formdata=src, status_item=self, exception=e) |
|
393 |
get_publisher().record_loggederror( |
|
394 |
_('Could not copy field by varname for "%s"') % field.varname, |
|
395 |
formdata=src, status_item=self, exception=e) |
|
396 | 396 | |
397 | 397 |
# field.id can be serialized to xml, so we must always convert them to |
398 | 398 |
# str when matching |
... | ... | |
418 | 418 |
value=value) |
419 | 419 |
except Exception as e: |
420 | 420 |
expression = self.get_expression(mapping.expression) |
421 |
LoggedError.record(_('Could not assign value to field "%s"') % dest_field.label, |
|
422 |
formdata=src, status_item=self, |
|
423 |
expression=expression['value'], expression_type=expression['type'], |
|
424 |
exception=e) |
|
421 |
get_publisher().record_loggederror( |
|
422 |
_('Could not assign value to field "%s"') % dest_field.label, |
|
423 |
formdata=src, status_item=self, |
|
424 |
expression=expression['value'], expression_type=expression['type'], |
|
425 |
exception=e) |
|
425 | 426 | |
426 | 427 |
if missing_fields: |
427 | 428 |
summary = _('Missing field %r') % missing_fields |
428 |
LoggedError.record(summary, formdata=src, status_item=self)
|
|
429 |
get_publisher().record_loggederror(summary, formdata=src, status_item=self)
|
|
429 | 430 | |
430 | 431 |
def _set_value(self, formdata, field, value): |
431 | 432 |
if field.convert_value_from_anything: |
wcs/wf/external_workflow.py | ||
---|---|---|
19 | 19 |
from wcs.qommon import _ |
20 | 20 |
from wcs.qommon.form import SingleSelectWidget |
21 | 21 | |
22 |
from wcs.logged_errors import LoggedError |
|
23 | 22 |
from wcs.workflows import WorkflowStatusItem, perform_items, register_item_class |
24 | 23 |
from wcs.workflows import WorkflowGlobalActionWebserviceTrigger, Workflow |
25 | 24 |
from wcs.carddef import CardDef |
... | ... | |
126 | 125 | |
127 | 126 |
trigger = self.get_trigger(objectdef.workflow) |
128 | 127 |
if not trigger: |
129 |
LoggedError.record(_('No trigger with id "%s" found in workflow') % self.trigger_id, |
|
130 |
formdata=formdata, status_item=self) |
|
128 |
get_publisher().record_loggederror( |
|
129 |
_('No trigger with id "%s" found in workflow') % self.trigger_id, |
|
130 |
formdata=formdata, status_item=self) |
|
131 | 131 |
return |
132 | 132 | |
133 | 133 |
class CallerSource: |
wcs/workflows.py | ||
---|---|---|
1982 | 1982 |
vars.update(context or {}) |
1983 | 1983 | |
1984 | 1984 |
def log_exception(exception): |
1985 |
from wcs.logged_errors import LoggedError |
|
1986 | 1985 |
if expression['type'] == 'template': |
1987 | 1986 |
summary = _('Failed to compute template') |
1988 | 1987 |
else: |
1989 | 1988 |
summary = _('Failed to compute Python expression') |
1990 |
LoggedError.record(summary, formdata=formdata, status_item=status_item, |
|
1991 |
expression=expression['value'], |
|
1992 |
expression_type=expression['type'], |
|
1993 |
exception=exception) |
|
1989 |
get_publisher().record_loggederror( |
|
1990 |
summary, formdata=formdata, status_item=status_item, |
|
1991 |
expression=expression['value'], |
|
1992 |
expression_type=expression['type'], |
|
1993 |
exception=exception) |
|
1994 | 1994 | |
1995 | 1995 |
if expression['type'] == 'template': |
1996 | 1996 |
try: |
... | ... | |
2043 | 2043 | |
2044 | 2044 |
targets = [x for x in self.parent.parent.possible_status if x.id == self.status] |
2045 | 2045 |
if not targets and formdata: # do not log in presentation context: formdata is needed |
2046 |
from wcs.logged_errors import LoggedError |
|
2047 | 2046 |
message = _('reference to invalid status %(target)s in status %(status)s, ' |
2048 | 2047 |
'action %(status_item)s') % { |
2049 | 2048 |
'target': self.status, |
2050 | 2049 |
'status': self.parent.name, |
2051 | 2050 |
'status_item': _(self.description) |
2052 | 2051 |
} |
2053 |
LoggedError.record(message, formdata=formdata, status_item=self)
|
|
2052 |
get_publisher().record_loggederror(message, formdata=formdata, status_item=self)
|
|
2054 | 2053 | |
2055 | 2054 |
return targets |
2056 | 2055 | |
... | ... | |
2677 | 2676 |
subject = mail_template.subject |
2678 | 2677 |
extra_attachments = mail_template.attachments |
2679 | 2678 |
else: |
2680 |
from wcs.logged_errors import LoggedError |
|
2681 | 2679 |
message = _('reference to invalid mail template %(mail_template)s in status %(status)s') % { |
2682 | 2680 |
'status': self.parent.name, |
2683 | 2681 |
'mail_template': self.mail_template, |
2684 | 2682 |
} |
2685 |
LoggedError.record(message, formdata=formdata, status_item=self)
|
|
2683 |
get_publisher().record_loggederror(message, formdata=formdata, status_item=self)
|
|
2686 | 2684 |
return |
2687 | 2685 | |
2688 | 2686 |
try: |
2689 |
- |