Bug fixes and additions to schedule page. Ready for alpha

This commit is contained in:
Josh Stark 2015-10-24 16:14:25 +01:00
parent 7636800e4f
commit 7bc79ff181
15 changed files with 144 additions and 42 deletions

View File

@ -1,5 +1,8 @@
package io.linuxserver.davos.dto;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class ActionDTO {
public Long id;
@ -9,4 +12,8 @@ public class ActionDTO {
public String f2;
public String f3;
public String f4;
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}

View File

@ -1,7 +1,14 @@
package io.linuxserver.davos.dto;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class FilterDTO {
public Long id;
public String value;
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}

View File

@ -4,6 +4,9 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import io.linuxserver.davos.transfer.ftp.FileTransferType;
import io.linuxserver.davos.transfer.ftp.TransferProtocol;
@ -24,4 +27,8 @@ public class ScheduleConfigurationDTO {
public FileTransferType transferType;
public List<FilterDTO> filters = new ArrayList<FilterDTO>();
public List<ActionDTO> actions = new ArrayList<ActionDTO>();
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}

View File

@ -5,8 +5,6 @@ import java.util.List;
import javax.annotation.Resource;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import io.linuxserver.davos.persistence.model.ScheduleConfigurationModel;
@ -15,8 +13,6 @@ import io.linuxserver.davos.persistence.repository.ScheduleConfigurationReposito
@Component
public class DefaultScheduleConfigurationDAO implements ScheduleConfigurationDAO {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultScheduleConfigurationDAO.class);
@Resource
private ScheduleConfigurationRepository configRepository;
@ -38,9 +34,7 @@ public class DefaultScheduleConfigurationDAO implements ScheduleConfigurationDAO
@Override
public void updateLastRun(Long configId, DateTime lastRun) {
LOGGER.debug("Updating model with new lastRun of {}", lastRun);
ScheduleConfigurationModel model = configRepository.findOne(configId);
LOGGER.debug("Got existing model, {}", model);
model.lastRun = lastRun.toDate();
configRepository.save(model);

View File

@ -10,11 +10,7 @@ import javax.persistence.ManyToOne;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class ActionModel {
@Id
@ -37,7 +33,7 @@ public class ActionModel {
public String f4;
@ManyToOne
@JoinColumn(name = "schedule_id")
@JoinColumn(name = "action_schedule_id")
public ScheduleConfigurationModel schedule;
@Override

View File

@ -10,11 +10,7 @@ import javax.persistence.ManyToOne;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class FilterModel {
@Id
@ -25,7 +21,7 @@ public class FilterModel {
public String value;
@ManyToOne
@JoinColumn(name = "schedule_id")
@JoinColumn(name = "filter_schedule_id")
public ScheduleConfigurationModel schedule;
@Override

View File

@ -7,13 +7,14 @@ import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import io.linuxserver.davos.transfer.ftp.FileTransferType;
import io.linuxserver.davos.transfer.ftp.TransferProtocol;
@ -61,10 +62,12 @@ public class ScheduleConfigurationModel {
@Column
public FileTransferType transferType;
@OneToMany(fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "schedule", cascade = CascadeType.ALL)
@OneToMany(orphanRemoval = true, mappedBy = "schedule", cascade = CascadeType.ALL)
@LazyCollection(LazyCollectionOption.FALSE)
public List<FilterModel> filters = new ArrayList<FilterModel>();
@OneToMany(fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "schedule", cascade = CascadeType.ALL)
@OneToMany(orphanRemoval = true, mappedBy = "schedule", cascade = CascadeType.ALL)
@LazyCollection(LazyCollectionOption.FALSE)
public List<ActionModel> actions = new ArrayList<ActionModel>();
@Override

View File

@ -26,6 +26,7 @@ public class RunnableSchedule implements Runnable {
LOGGER.info("Starting schedule {}", scheduleId);
ScheduleConfigurationModel model = configurationDAO.getConfig(scheduleId);
ScheduleConfiguration config = ScheduleConfigurationFactory.createConfig(model);
ScheduleWorkflow scheduleWorkflow = new ScheduleWorkflow(config);

View File

@ -37,7 +37,7 @@ public class HttpAPICallAction implements PostDownloadAction {
headers.add("Content-Type", contentType);
LOGGER.info("Sending message to generic API for {}", execution.fileName);
HttpEntity<String> httpEntity = new HttpEntity<String>(body, headers);
HttpEntity<String> httpEntity = new HttpEntity<String>(body.replaceAll("$filename", execution.fileName), headers);
LOGGER.debug("Sending message {} to generic API: {}", httpEntity, url);
restTemplate.exchange(url, method, httpEntity, Object.class);

View File

@ -1,5 +1,5 @@
#spring.datasource.url=jdbc:h2:file:~/testdb
#spring.datasource.username=sa
#spring.datasource.password=sa
#spring.datasource.driverClassName=org.h2.Driver
#spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:h2:file:/config/db/davos
spring.datasource.username=sa
spring.datasource.password=sa
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.hibernate.ddl-auto=update

View File

@ -8,7 +8,7 @@
</Console>
<RollingFile name="File" fileName="davos.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log">
filePattern="/config/logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log">
<PatternLayout>
<Pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} - $5p - [%c{1}] - %msg%n

View File

@ -111,4 +111,8 @@ a.collection-item.schedule {
.chip {
margin-bottom: 10px;
}
.action_type {
text-transform: capitalize;
}

View File

@ -32,11 +32,49 @@ var action = (function ($) {
'use strict';
var initialise;
var initialise, addMove, addPushbullet, addApi;
initialise = function () {
$('#select_download_action').on('change', function () {
var action = $(this).find('option:selected').val();
$(this).val('choose');
if (action === 'pushbullet') {
addPushbullet();
}
if (action === 'move') {
addMove();
}
if (action === 'api') {
addApi();
}
});
};
addMove = function () {
var newAction = $('<div class="row action action_move"><div class="input-field col s3 action_type">move</div><div class="input-field col s8"><input type="text" placeholder="Move To.." class="f1" /></div></div>');
$('#download_actions').append(newAction);
};
addPushbullet = function () {
var newAction = $('<div class="row action action_pushbullet"><div class="input-field col s3 action_type">pushbullet</div><div class="input-field col s8"><input type="text" placeholder="API Key" class="f1" /></div></div>');
$('#download_actions').append(newAction);
};
addApi = function () {
var newAction = $('<div class="row action action_api"><div class="input-field col s3 action_type">api</div><div class="input-field col s8"><input type="text" placeholder="URL" class="f1" /><br />' +
'<input type="text" placeholder="Method (e.g. POST, GET)" class="f2" /><br /><input type="text" placeholder="Content Type (e.g. application/json)" class="f3" /><br /><input type="text"' +
' placeholder="Message body" class="f4" /><br /></div></div>');
$('#download_actions').append(newAction);
};
return {
@ -49,13 +87,13 @@ var schedule = (function ($) {
'use strict';
var initialise, save, onSuccess, onError, cleanId, startSchedule, stopSchedule, startResponse, stopResponse;
var initialise, save, onSuccess, onError, cleanId, startSchedule, stopSchedule, startResponse, stopResponse, cleanActionFunction;
initialise = function () {
$('#start_schedule').on('click', startSchedule);
$('#stop_schedule').on('click', stopSchedule);
$('#save_schedule').on('click', function () {
var postData = {
@ -87,6 +125,21 @@ var schedule = (function ($) {
postData.filters.push(filter);
});
$('#download_actions .action').each(function () {
var action = {
id: cleanId($(this).attr('data-action-id')),
actionType: $(this).find('.action_type').text(),
f1: cleanActionFunction($(this).find('.f1')),
f2: cleanActionFunction($(this).find('.f2')),
f3: cleanActionFunction($(this).find('.f3')),
f4: cleanActionFunction($(this).find('.f4'))
};
postData.actions.push(action);
});
$.ajax({
method: "POST",
@ -100,6 +153,14 @@ var schedule = (function ($) {
});
};
cleanActionFunction = function (action) {
if (action)
return action.val();
return null;
};
cleanId = function (id) {
if ($.trim(id).length === 0) {
@ -116,7 +177,7 @@ var schedule = (function ($) {
url: "/api/v1/schedule/" + cleanId($('#schedule_id').val()) + "/start"
}).done(startResponse).fail(startResponse);
};
stopSchedule = function () {
$.ajax({
@ -128,7 +189,7 @@ var schedule = (function ($) {
startResponse = function (msg) {
Materialize.toast(msg.message, 3000, 'rounded');
};
stopResponse = function (msg) {
Materialize.toast(msg.message, 3000, 'rounded');
};

View File

@ -234,7 +234,7 @@
</li>
<li>
<div class="collapsible-header active"><i class="material-icons">code</i>Post-download Actions</div>
<div class="collapsible-body">
<div class="collapsible-body" id="download_actions">
<div class="row">
<div class="col s12">
<p class="subtext">
@ -246,22 +246,41 @@
<div class="row">
<div class="input-field col s4">
<select class="browser-default">
<option value="" disabled="disabled" selected="selected">Add action...</option>
<select id="select_download_action" class="browser-default">
<option value="choose" disabled="disabled" selected="selected">Add action...</option>
<option value="pushbullet">Pushbullet Notification</option>
<option value="move">Move downloaded file</option>
<option value="move">Move Downloaded File</option>
<option value="api">Generic HTTP API Call</option>
</select>
</div>
</div>
<div id="download_actions">
<div th:each="action : ${schedule.actions}" th:switch="${action.actionType}" th:attr="data-action-id=${action.id}, class='row action action_' + ${action.actionType}">
<div class="input-field col s3 action_type" th:text="${action.actionType}"></div>
<div class="row download_action">
<div th:case="'move'" class="action_name input-field col s8">
<input type="text" placeholder="Move To..." class="f1" th:attr="value=${action.f1}" />
</div>
<div th:case="'pushbullet'" class="action_name input-field col s8">
<input type="text" placeholder="API Key" class="f1" th:attr="value=${action.f1}" />
</div>
<div th:case="'api'" class="action_name input-field col s8">
<input type="text" placeholder="URL" class="f1" th:attr="value=${action.f1}" />
<br />
<input type="text" placeholder="Method (e.g. POST, GET)" class="f2" th:attr="value=${action.f2}" />
<br />
<input type="text" placeholder="Content Type (e.g. application/json)" class="f3" th:attr="value=${action.f3}" />
<br />
<input type="text" placeholder="Message body" class="f4" th:attr="value=${action.f4}" />
<br />
</div>
</div>
</div>
</li>
</ul>
@ -285,9 +304,10 @@
<script type="text/javascript" th:src="@{/js/jquery-2.1.4.min.js}"></script>
<script type="text/javascript" th:src="@{/js/materialize.min.js}"></script>
<script type="text/javascript" th:src="@{/js/davos.js}"></script>
<script type="text/javascript" th:inline="text">
var base = "[[@{/}]]"
</script>
</body>

View File

@ -35,7 +35,7 @@ public class HttpAPICallActionTest {
@Before
public void setUp() {
httpAPICallAction = new HttpAPICallAction("http://url", HttpMethod.POST, "application/json", "{\"hello\":\"world\"}");
httpAPICallAction = new HttpAPICallAction("http://url", HttpMethod.POST, "application/json", "{\"hello\":\"$filename\"}");
initMocks(this);
}
@ -52,7 +52,7 @@ public class HttpAPICallActionTest {
String body = entityCaptor.getValue().getBody();
assertThat(body).isEqualTo("{\"hello\":\"world\"}");
assertThat(body).isEqualTo("{\"hello\":\"file.txt\"}");
}
@Test
@ -73,18 +73,24 @@ public class HttpAPICallActionTest {
@Test
public void ifRestTemplateFailsThenDoNothing() {
PostDownloadExecution execution = new PostDownloadExecution();
execution.fileName = "filename";
when(mockRestTemplate.exchange(eq("http://url"), eq(HttpMethod.POST), any(HttpEntity.class), eq(Object.class)))
.thenThrow(new RestClientException(""));
httpAPICallAction.execute(new PostDownloadExecution());
httpAPICallAction.execute(execution);
}
@Test
public void ifRestTemplateFailsBecauseMessageIsUnreadbleThenDoNothing() {
PostDownloadExecution execution = new PostDownloadExecution();
execution.fileName = "filename";
when(mockRestTemplate.exchange(eq("http://url"), eq(HttpMethod.POST), any(HttpEntity.class), eq(Object.class)))
.thenThrow(new HttpMessageConversionException(""));
httpAPICallAction.execute(new PostDownloadExecution());
httpAPICallAction.execute(execution);
}
}