Skip to content

Commit

Permalink
fix(BugFixes) Fix error handling for invalid XLS forms TASK-1353 (#5403)
Browse files Browse the repository at this point in the history
Fix the error handling for invalid survey JSON content when deploying


### 📖 Description
This PR adds error handling for the case when Pyxform fails when
attempting to deploy a survey with invalid content. A unit test was
included to ensure this works ok.


### 👀 Preview steps
1. Create a new survey using a bad xls form (see the notion page for a
test file with bad format)
2. Attempt to deploy the form
3. The frontend should show a validation error with a hint of how to fix
it
4. Edit and save the form (labels are added automatically when you enter
the form editor, so there's no need to do anything in this case, just
edit and hit the save button)
5. Attempt to deploy again
6. It should be deployed without issues
  • Loading branch information
Guitlle authored Jan 5, 2025
1 parent 3a15b73 commit 7654a8b
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 3 deletions.
5 changes: 3 additions & 2 deletions jsapp/js/actions.es6
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,10 @@ actions.resources.deployAsset.failed.listen(function(data, redeployment){
// failed to retrieve a valid response from the server
// setContent() removes the input box, but the value is retained
var msg;
if (data.status == 500 && data.responseJSON && data.responseJSON.error) {
const has_error_code = data.status == 500 || data.status == 400;
if (has_error_code && data.responseJSON && data.responseJSON.error) {
msg = `<pre>${data.responseJSON.error}</pre>`;
} else if (data.status == 500 && data.responseText) {
} else if (has_error_code && data.responseText) {
msg = `<pre>${data.responseText}</pre>`;
} else {
msg = t('please check your connection and try again.');
Expand Down
6 changes: 5 additions & 1 deletion kpi/serializers/v2/deployment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding: utf-8
from django.conf import settings
from pyxform.errors import PyXFormError
from rest_framework import serializers

from .asset import AssetSerializer
Expand Down Expand Up @@ -32,7 +33,10 @@ def create(self, validated_data):

# `asset.deploy()` deploys the latest version and updates that versions'
# 'deployed' boolean value
asset.deploy(backend=backend_id, active=validated_data.get('active', False))
try:
asset.deploy(backend=backend_id, active=validated_data.get('active', False))
except PyXFormError as e:
raise serializers.ValidationError({'error': (f'ODK Validation Error: {e}')})
return asset.deployment

def update(self, instance, validated_data):
Expand Down
86 changes: 86 additions & 0 deletions kpi/tests/api/v2/test_api_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1726,6 +1726,92 @@ def test_asset_deployment(self):
== AssetDeploymentStatus.DEPLOYED.value
)

def test_asset_deployment_validation_error(self):
bad_content = {
'schema': '1',
'survey': [
{
'name': 'start',
'type': 'start',
'$kuid': 'O23MoETkI',
'$xpath': 'start',
'$autoname': 'start',
},
{
'name': 'end',
'type': 'end',
'$kuid': '9rvIvjnrP',
'$xpath': 'end',
'$autoname': 'end',
},
{
'name': 'Enter_a_float_number',
'type': 'decimal',
'$kuid': 'e2v7ZTcDw',
'label': ['Enter a float number'],
'$xpath': 'Enter_a_float_number',
'required': False,
'$autoname': 'Enter_a_float_number',
},
{
'name': 'What_s_your_name',
'type': 'text',
'$kuid': 'w8nnstZ1r',
'label': ["What's your name?"],
'$xpath': 'What_s_your_name',
'required': False,
'$autoname': 'What_s_your_name',
},
{
'name': 'Enter_an_int_number',
'type': 'integer',
'$kuid': 'hpw7EKED0',
'$xpath': 'Enter_an_int_number',
'required': False,
'$autoname': 'Enter_an_int_number',
},
{
'name': 'Enter_a_time',
'type': 'time',
'$kuid': 'rwf9XqdlC',
'label': ['Enter a time'],
'$xpath': 'Enter_a_time',
'required': False,
'$autoname': 'Enter_a_time',
},
],
'choices': [],
'settings': {},
}
assets_url = reverse(self._get_endpoint('asset-list'))
asset_response = self.client.post(
assets_url,
{'content': bad_content, 'asset_type': 'survey'},
format='json',
)
asset = Asset.objects.get(uid=asset_response.data.get('uid'))

deployment_url = reverse(
self._get_endpoint('asset-deployment'), kwargs={'uid': asset.uid}
)

deploy_response = self.client.post(
deployment_url,
{
'backend': 'mock',
'active': True,
},
)

self.assertEqual(deploy_response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
deploy_response.data['error'],
(
'ODK Validation Error: '
"The survey element named 'Enter_an_int_number' has no label or hint."
),
)

def test_asset_redeployment(self):
self.test_asset_deployment()

Expand Down

0 comments on commit 7654a8b

Please sign in to comment.