Skip to content

Commit

Permalink
Updated Settings Modal (#19)
Browse files Browse the repository at this point in the history
* updated default setting value for tethys workspace
* added validation and warning for required settings
* updated and added new test for app dependencies
  • Loading branch information
ckrew authored Feb 1, 2024
1 parent 0d8862f commit 434d983
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 7 deletions.
8 changes: 7 additions & 1 deletion tethysapp/app_store/begin_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from .helpers import check_all_present, logger, send_notification
from .resource_helpers import get_resource, get_app_instance_from_path
from tethys_apps.base.workspace import TethysWorkspace


def handle_property_not_present(prop):
Expand Down Expand Up @@ -92,9 +93,14 @@ def detect_app_dependencies(app_name, channel_layer, notification_method=send_no
if custom_settings:
notification_method("Processing App's Custom Settings....", channel_layer)
for setting in custom_settings:
default = setting.default
if isinstance(default, TethysWorkspace):
default = default.path

setting = {"name": setting.name,
"description": setting.description,
"default": str(setting.default),
"required": setting.required,
"default": str(default),
}
custom_settings_json.append(setting)

Expand Down
19 changes: 16 additions & 3 deletions tethysapp/app_store/public/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const settingsHelper = {
if (settingsData) {
if (settingsData.length > 0) {
$("#skipConfigButton").click(function() {
$(".setting_warning").hide()
ws.send(
JSON.stringify({
data: {
Expand All @@ -42,11 +43,13 @@ const settingsHelper = {
let formDataElement = $("#custom-settings-container").children("form")
settingsData.forEach((setting) => {
let defaultValue = setting.default ? setting.default : ""
let requiredClass = setting.required ? "required_setting" : ""
let newElement = `
<div class="form-group">
<label for="${setting.name}">${setting.name}</label>
<input type="text" class="form-control" id="${setting.name}" value="${defaultValue}">
<label for="${setting.name}">${setting.name}${setting.required ? "*": ""}</label>
<input type="text" class="form-control ${requiredClass}" id="${setting.name}" value="${defaultValue}">
<p class="help-block">${setting.description}</p>
<div id="${setting.name}_warningMessage" style="display:none;margin-top:10px;margin-bottom:10px" class="p-3 mb-2 bg-warning text-white setting_warning">This setting is required and must be filled to submit settings</div>
</div>`
formDataElement.append(newElement)
})
Expand All @@ -55,18 +58,28 @@ const settingsHelper = {
`<button type="submit" class="btn btn-success">Submit</button>`
)
formDataElement.submit(function(event) {
$(".setting_warning").hide()
event.preventDefault()
let formData = { settings: {} }
let has_errors = false
if ("app_py_path" in completeMessage) {
formData["app_py_path"] = completeMessage["app_py_path"]
}
$("#custom-settings-container")
.children("form")
.find(".form-control")
.each(function() {
if ($(this).hasClass("required_setting") && $(this).val() == "") {
let setting_name = $(this)[0].id
$(`#${setting_name}_warningMessage`).show()
has_errors = true
}
formData.settings[$(this).attr("id")] = $(this).val()
})


if (has_errors) {
return
}
ws.send(
JSON.stringify({
data: formData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ <h5 class="modal-title" id="add-app-modal-label">Submit your application to the
<label for="notifEmail">Notification Email</label>
<input type="text" class="form-control" id="notifEmail" placeholder="Email Address to notify on build">
</div>
<div id="notifEmail_failMessage" style="display:none;margin-top:10px;" class="p-3 mb-2 bg-warning text-white">A github url must be provided</div>
<div id="notifEmail_failMessage" style="display:none;margin-top:10px;" class="p-3 mb-2 bg-warning text-white">A email for notifications must be provided</div>
<div id="{{ store.conda_channel }}_failMessage" style="display:none;margin-top:10px;" class="p-3 mb-2 bg-info text-white"></div>
<div class="form-group">
<label for="githubURL">GitHub URL</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ <h5 class="modal-title" id="custom-settings-modal-label">Define Custom Settings<
<div class="modal-body" id="custom-settings-container">
</div>
<div class="modal-footer">
<p>*Settings with an asterisk are required unless the configuration is skipped</p>
<div id="customSettingLoader" hidden>
<img width="100px" src="{% static 'app_store/images/loader.gif' %}" />
</div>
Expand Down
42 changes: 40 additions & 2 deletions tethysapp/app_store/tests/unit_tests/test_begin_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_detect_app_dependencies_pip_settings(mocker, tethysapp_base_with_applic
mock_subprocess.Popen().stdout.readline.side_effect = [""]
mock_tethysapp = mocker.patch('tethysapp.app_store.begin_install.tethysapp')
mock_app = MagicMock()
mock_setting = MagicMock(default=True, description="description")
mock_setting = MagicMock(default="some_value", description="description", required=True)
mock_setting.name = "name"
mock_app.custom_settings.return_value = [mock_setting]
mocker.patch('tethysapp.app_store.begin_install.get_app_instance_from_path', return_value=mock_app)
Expand All @@ -67,7 +67,45 @@ def test_detect_app_dependencies_pip_settings(mocker, tethysapp_base_with_applic
detect_app_dependencies(app_name, channel_layer, mock_ws)

expected_data_json = {
"data": [{"name": "name", "description": "description", "default": "True"}],
"data": [{"name": "name", "description": "description", "required": True, "default": "some_value"}],
"returnMethod": "set_custom_settings",
"jsHelperFunction": "processCustomSettings",
"app_py_path": str(tethysapp_base_with_application_files / "tethysapp")
}
mock_ws.assert_has_calls([
call("Running PIP install....", channel_layer),
call("PIP install completed", channel_layer),
call("Processing App's Custom Settings....", channel_layer),
call(expected_data_json, channel_layer)
])
assert mock_subprocess.Popen().stdout.readline.call_count == 1


def test_detect_app_dependencies_pip_settings_workspace_default(mocker, tethysapp_base_with_application_files,
tmp_path):
app_name = "test_app"
channel_layer = MagicMock()
mock_ws = MagicMock()
mocker.patch('tethysapp.app_store.begin_install.call')
mocker.patch('tethysapp.app_store.begin_install.cache')
mocker.patch('tethysapp.app_store.begin_install.importlib')
mock_subprocess = mocker.patch('tethysapp.app_store.begin_install.subprocess')
mock_subprocess.Popen().stdout.readline.side_effect = [""]
mock_tethysapp = mocker.patch('tethysapp.app_store.begin_install.tethysapp')
mock_app = MagicMock()
mock_workspace = MagicMock(path=str(tmp_path))
mocker.patch('tethysapp.app_store.begin_install.isinstance', return_value=True)
mock_setting = MagicMock(default=mock_workspace, description="description", required=True)
mock_setting.name = "name"
mock_app.custom_settings.return_value = [mock_setting]
mocker.patch('tethysapp.app_store.begin_install.get_app_instance_from_path', return_value=mock_app)

mock_tethysapp.__path__ = [str(tethysapp_base_with_application_files / "tethysapp")]

detect_app_dependencies(app_name, channel_layer, mock_ws)

expected_data_json = {
"data": [{"name": "name", "description": "description", "required": True, "default": str(tmp_path)}],
"returnMethod": "set_custom_settings",
"jsHelperFunction": "processCustomSettings",
"app_py_path": str(tethysapp_base_with_application_files / "tethysapp")
Expand Down

0 comments on commit 434d983

Please sign in to comment.