Hooks - Validation and Caching

This commit is contained in:
avikganguly01
2014-10-16 18:18:47 +05:30
parent 3847186f2b
commit 84c321cfe8
11 changed files with 212 additions and 17 deletions
@@ -41,6 +41,7 @@ public class WebXmlConfiguration {
jerseyServletRegistration.addInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters",
ResponseCorsFilter.class.getName());
jerseyServletRegistration.addInitParameter("com.sun.jersey.config.feature.DisableWADL", "true");
// debugging for development:
// jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters",
// LoggingFilter.class.getName());
@@ -65,8 +65,10 @@ public class Hook extends AbstractAuditableCustom<AppUser, Long> {
final Set<HookResource> events) {
final String displayName = command
.stringValueOfParameterNamed(displayNameParamName);
final Boolean isActive = command
Boolean isActive = command
.booleanObjectValueOfParameterNamed(isActiveParamName);
if(isActive == null)
isActive = false;
return new Hook(template, displayName, isActive, config, events);
}
@@ -10,11 +10,11 @@ import java.util.List;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
import org.mifosplatform.infrastructure.hooks.domain.Hook;
import org.mifosplatform.infrastructure.hooks.domain.HookRepository;
import org.mifosplatform.infrastructure.hooks.event.HookEvent;
import org.mifosplatform.infrastructure.hooks.event.HookEventSource;
import org.mifosplatform.infrastructure.hooks.processor.HookProcessor;
import org.mifosplatform.infrastructure.hooks.processor.HookProcessorProvider;
import org.mifosplatform.infrastructure.hooks.service.HookReadPlatformService;
import org.mifosplatform.infrastructure.security.service.TenantDetailsService;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
@@ -24,14 +24,14 @@ import org.springframework.stereotype.Service;
public class MifosHookListener implements HookListener {
private final HookProcessorProvider hookProcessorProvider;
private final HookRepository hookRepository;
private final HookReadPlatformService hookReadPlatformService;
private final TenantDetailsService tenantDetailsService;
@Autowired
public MifosHookListener(final HookProcessorProvider hookProcessorProvider,
final HookRepository hookRepository,
final HookReadPlatformService hookReadPlatformService,
final TenantDetailsService tenantDetailsService) {
this.hookRepository = hookRepository;
this.hookReadPlatformService = hookReadPlatformService;
this.hookProcessorProvider = hookProcessorProvider;
this.tenantDetailsService = tenantDetailsService;
}
@@ -51,10 +51,11 @@ public class MifosHookListener implements HookListener {
final String entityName = hookEventSource.getEntityName();
final String actionName = hookEventSource.getActionName();
final String payload = event.getPayload();
final List<Hook> hooks = this.hookReadPlatformService
.retrieveHooksByEvent(hookEventSource.getEntityName(),
hookEventSource.getActionName());
final List<Hook> hooks = this.hookRepository
.findAllHooksListeningToEvent(hookEventSource.getEntityName(),
hookEventSource.getActionName());
for (final Hook hook : hooks) {
final HookProcessor processor = this.hookProcessorProvider
.getProcessor(hook);
@@ -57,7 +57,7 @@ public class WebHookProcessor implements HookProcessor {
private void sendRequest(final String url, final String contentType,
final String payload, final String entityName,
final String actionName, final String tenantIdentifier,
final String authToken) {
@SuppressWarnings("unused") final String authToken) {
final String mifosEndpointUrl = System.getProperty("baseUrl");
final WebHookService service = ProcessorHelper
@@ -70,13 +70,11 @@ public class WebHookProcessor implements HookProcessor {
|| contentType.contains("json")) {
final JsonObject json = new JsonParser().parse(payload)
.getAsJsonObject();
json.addProperty("mifosToken", authToken);
service.sendJsonRequest(entityName, actionName, tenantIdentifier,
mifosEndpointUrl, json, callback);
} else {
Map<String, String> map = new HashMap<>();
map = new Gson().fromJson(payload, map.getClass());
map.put("mifosToken", authToken);
service.sendFormRequest(entityName, actionName, tenantIdentifier,
mifosEndpointUrl, map, callback);
}
@@ -14,6 +14,7 @@ import retrofit.client.Response;
import retrofit.http.Body;
import retrofit.http.FieldMap;
import retrofit.http.FormUrlEncoded;
import retrofit.http.GET;
import retrofit.http.Header;
import retrofit.http.POST;
@@ -27,6 +28,10 @@ public interface WebHookService {
final static String ENDPOINT_HEADER = "X-Mifos-Endpoint";
final static String API_KEY_HEADER = "X-Mifos-API-Key";
// Ping
@GET("/")
Response sendEmptyRequest();
// Template - Web
@POST("/")
void sendJsonRequest(@Header(ENTITY_HEADER) String entityHeader,
@@ -6,8 +6,10 @@
package org.mifosplatform.infrastructure.hooks.service;
import java.util.Collection;
import java.util.List;
import org.mifosplatform.infrastructure.hooks.data.HookData;
import org.mifosplatform.infrastructure.hooks.domain.Hook;
public interface HookReadPlatformService {
@@ -15,5 +17,7 @@ public interface HookReadPlatformService {
HookData retrieveHook(Long hookId);
HookData retrieveNewHookDetails(String templateName);
List<Hook> retrieveHooksByEvent(final String actionName, final String entityName);
HookData retrieveNewHookDetails(String templateName);
}
@@ -19,9 +19,12 @@ import org.mifosplatform.infrastructure.hooks.data.Field;
import org.mifosplatform.infrastructure.hooks.data.Grouping;
import org.mifosplatform.infrastructure.hooks.data.HookData;
import org.mifosplatform.infrastructure.hooks.data.HookTemplateData;
import org.mifosplatform.infrastructure.hooks.domain.Hook;
import org.mifosplatform.infrastructure.hooks.domain.HookRepository;
import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@@ -31,12 +34,15 @@ import org.springframework.stereotype.Service;
public class HookReadPlatformServiceImpl implements HookReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final HookRepository hookRepository;
private final PlatformSecurityContext context;
@Autowired
public HookReadPlatformServiceImpl(final PlatformSecurityContext context,
final HookRepository hookRepository,
final RoutingDataSource dataSource) {
this.context = context;
this.hookRepository = hookRepository;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@@ -64,6 +70,15 @@ public class HookReadPlatformServiceImpl implements HookReadPlatformService {
}
@Override
@Cacheable(value = "hooks", key = "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('HK')")
public List<Hook> retrieveHooksByEvent(final String actionName,
final String entityName) {
return this.hookRepository.findAllHooksListeningToEvent(actionName,
entityName);
}
@Override
public HookData retrieveNewHookDetails(final String templateName) {
@@ -7,9 +7,11 @@ package org.mifosplatform.infrastructure.hooks.service;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.actionNameParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.configParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.contentTypeName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.entityNameParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.eventsParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.nameParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.payloadURLName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.webTemplateName;
import java.util.ArrayList;
@@ -36,13 +38,18 @@ import org.mifosplatform.infrastructure.hooks.domain.HookTemplateRepository;
import org.mifosplatform.infrastructure.hooks.domain.Schema;
import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException;
import org.mifosplatform.infrastructure.hooks.exception.HookTemplateNotFoundException;
import org.mifosplatform.infrastructure.hooks.processor.ProcessorHelper;
import org.mifosplatform.infrastructure.hooks.processor.WebHookService;
import org.mifosplatform.infrastructure.hooks.serialization.HookCommandFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import retrofit.RetrofitError;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@@ -72,6 +79,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
@Transactional
@Override
@CacheEvict(value = "hooks", allEntries = true)
public CommandProcessingResult createHook(final JsonCommand command) {
try {
@@ -90,7 +98,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
final Hook hook = Hook.fromJson(command, template, config,
allEvents);
validateHookRules(template, config);
validateHookRules(template, config, allEvents);
this.hookRepository.save(hook);
@@ -105,6 +113,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
@Transactional
@Override
@CacheEvict(value = "hooks", allEntries = true)
public CommandProcessingResult updateHook(final Long hookId,
final JsonCommand command) {
@@ -156,6 +165,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
@Transactional
@Override
@CacheEvict(value = "hooks", allEntries = true)
public CommandProcessingResult deleteHook(final Long hookId) {
this.context.authenticatedUser();
@@ -200,7 +210,9 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
for (final Entry<String, String> configEntry : hookConfig.entrySet()) {
for (final Schema field : fields) {
if (field.getFieldName().equalsIgnoreCase(configEntry.getKey())) {
final String fieldName = field.getFieldName();
if (fieldName.equalsIgnoreCase(configEntry.getKey())) {
final HookConfiguration config = HookConfiguration
.createNewWithoutHook(field.getFieldType(),
configEntry.getKey(),
@@ -237,7 +249,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
}
private void validateHookRules(final HookTemplate template,
final Set<HookConfiguration> config) {
final Set<HookConfiguration> config, Set<HookResource> events) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
@@ -250,6 +262,43 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
errorMessage);
}
for (final HookConfiguration conf : config) {
final String fieldValue = conf.getFieldValue();
if (conf.getFieldName().equals(contentTypeName)) {
if (!(fieldValue.equalsIgnoreCase("json") || fieldValue
.equalsIgnoreCase("form"))) {
final String errorMessage = "content.type.must.be.json.or.form";
baseDataValidator.reset()
.failWithCodeNoParameterAddedToErrorCode(
errorMessage);
}
}
if (conf.getFieldName().equals(payloadURLName)) {
try {
final WebHookService service = ProcessorHelper
.createWebHookService(fieldValue);
service.sendEmptyRequest();
} catch (RetrofitError re) {
// Swallow error if it's because of method not supported or
// if url throws 404 - required for integration test,
// url generated on 1st POST request
if (re.getResponse() == null) {
String errorMessage = "url.invalid";
baseDataValidator.reset()
.failWithCodeNoParameterAddedToErrorCode(
errorMessage);
}
}
}
}
if (events == null || events.isEmpty()) {
final String errorMessage = "registered.events.cannot.be.empty";
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
errorMessage);
}
final Set<Schema> fields = template.getSchema();
for (final Schema field : fields) {
if (!field.isOptional()) {
@@ -261,8 +310,8 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
}
if (!found) {
final String errorMessage = "required.config.field."
+ field.getFieldName() + ".not.provided";
baseDataValidator.reset()
+ "not.provided";
baseDataValidator.reset().value(field.getFieldName())
.failWithCodeNoParameterAddedToErrorCode(
errorMessage);
}
@@ -34,4 +34,6 @@
overflowToDisk="false" />
<cache name="codes" maxEntriesLocalHeap="10000" eternal="true"
overflowToDisk="false" />
<cache name="hooks" maxEntriesLocalHeap="10000" eternal="true"
overflowToDisk="false" />
</ehcache>