Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/azure-cli/azure/cli/command_modules/appservice/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -10450,7 +10450,28 @@ def remove_continuous_webjob(cmd, resource_group_name, name, webjob_name, slot=N


def list_triggered_webjobs(cmd, resource_group_name, name, slot=None):
return _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'list_triggered_web_jobs', slot)
def _handle_409(ex):
try:
response_text = ex.response.text()
except TypeError:
response_text = ex.response.text
try:
parsed = json.loads(response_text)
# Live responses encode the JSON object as a JSON string; decode again if needed.
if isinstance(parsed, str):
parsed = json.loads(parsed)
message = parsed.get('error') or str(ex)
except (ValueError, AttributeError):
message = str(ex)
raise UnclassifiedUserFault(message)

try:
pager = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'list_triggered_web_jobs', slot)
return list(pager)
except HttpResponseError as ex:
if ex.status_code == 409:
_handle_409(ex)
raise


def run_triggered_webjob(cmd, resource_group_name, name, webjob_name, slot=None):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
MutuallyExclusiveArgumentError,
ArgumentUsageError,
AzureResponseError,
ResourceNotFoundError)
ResourceNotFoundError,
UnclassifiedUserFault)
from azure.cli.command_modules.appservice.custom import (set_deployment_user,
update_git_token, add_hostname,
update_site_configs,
Expand All @@ -41,7 +42,8 @@
list_startup_logs,
show_startup_log,
troubleshoot_status,
create_webapp)
create_webapp,
list_triggered_webjobs)

# pylint: disable=line-too-long
from azure.cli.core.profiles import ResourceType
Expand Down Expand Up @@ -2165,5 +2167,75 @@ def test_get_java_runtimes_from_container_settings_reads_mapping(self):
self.assertTrue(all(is_auto for _, _, is_auto in runtimes))


class TestListTriggeredWebjobs(unittest.TestCase):
def _build_http_response_error(self, status_code, body_text):
"""Build an HttpResponseError with the given status code and response body text."""
response_mock = mock.MagicMock()
response_mock.text.return_value = body_text
error = HttpResponseError(message="Operation returned an invalid status '{}'".format(status_code))
error.status_code = status_code
error.response = response_mock
Comment on lines +2175 to +2177
return error

@mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation')
def test_list_triggered_webjobs_409_surfaces_kudu_error(self, generic_op_mock):
"""list_triggered_webjobs raises UnclassifiedUserFault with the Kudu body on HTTP 409.

The live response body is a JSON-encoded string whose value is the JSON object,
e.g. the raw text is: '"{\"error\":\"The web app is not configured...\"}"'
"""
kudu_message = ("The web app is not configured to run the web job. "
"Please enable running web jobs before calling the API.")
# Simulate the production response shape: body is a JSON-encoded string of the JSON object.
import json as _json
body = _json.dumps(_json.dumps({"error": kudu_message}))
generic_op_mock.side_effect = self._build_http_response_error(409, body)

cmd = _get_test_cmd()
with self.assertRaises(UnclassifiedUserFault) as ctx:
list_triggered_webjobs(cmd, 'rg', 'myapp')

self.assertEqual(str(ctx.exception), kudu_message)

@mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation')
def test_list_triggered_webjobs_non_409_reraises(self, generic_op_mock):
"""list_triggered_webjobs re-raises HttpResponseError when status is not 409."""
generic_op_mock.side_effect = self._build_http_response_error(500, '{"error": "Internal Server Error"}')

cmd = _get_test_cmd()
with self.assertRaises(HttpResponseError):
list_triggered_webjobs(cmd, 'rg', 'myapp')

@mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation')
def test_list_triggered_webjobs_409_fallback_when_body_unparseable(self, generic_op_mock):
"""list_triggered_webjobs raises UnclassifiedUserFault even when body is not valid JSON."""
generic_op_mock.side_effect = self._build_http_response_error(409, 'not json at all')

cmd = _get_test_cmd()
with self.assertRaises(UnclassifiedUserFault):
list_triggered_webjobs(cmd, 'rg', 'myapp')

@mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation')
def test_list_triggered_webjobs_409_raised_during_pager_iteration(self, generic_op_mock):
"""list_triggered_webjobs catches 409 raised lazily when the pager is enumerated."""
kudu_message = ("The web app is not configured to run the web job. "
"Please enable running web jobs before calling the API.")
import json as _json
body = _json.dumps(_json.dumps({"error": kudu_message}))
ex = self._build_http_response_error(409, body)

def _raising_iter():
raise ex
yield # make this a generator (never reached)

generic_op_mock.return_value = _raising_iter()

cmd = _get_test_cmd()
with self.assertRaises(UnclassifiedUserFault) as ctx:
list_triggered_webjobs(cmd, 'rg', 'myapp')

self.assertEqual(str(ctx.exception), kudu_message)


if __name__ == '__main__':
unittest.main()
Loading