Skip to content

Commit

Permalink
#349 wire up async script execution
Browse files Browse the repository at this point in the history
  • Loading branch information
syjer committed Jan 3, 2018
1 parent 4f8f850 commit 29cda57
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 51 deletions.
18 changes: 9 additions & 9 deletions src/main/java/alfio/controller/api/admin/ScriptController.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ public List<ScriptSupport> listAll(Principal principal) {

/*
function getScriptMetadata() {
return {async:true, events: ['TEST']};
return {async:true, events: ['RESERVATION_CONFIRMATION']};
}
function executeScript() {
function executeScript(event) {
log.warn('hello from script with event: ' + event);
}
*/
@RequestMapping(value = "/scripting", method = RequestMethod.POST)
Expand All @@ -60,16 +60,16 @@ public void createOrUpdate(@RequestBody Script script, Principal principal) {
}


@RequestMapping(value = "/scripting/{path}", method = RequestMethod.DELETE)
public void delete(@PathVariable("path") String path, Principal principal) {
@RequestMapping(value = "/scripting/{path}/{name}", method = RequestMethod.DELETE)
public void delete(@PathVariable("path") String path, @PathVariable("name") String name, Principal principal) {
ensureAdmin(principal);
scriptingService.delete(path);
scriptingService.delete(path, name);
}

@RequestMapping(value = "/scripting/{path}/toggle/{enable}", method = RequestMethod.POST)
public void toggle(@PathVariable("path") String path, @PathVariable("enable") boolean enable, Principal principal) {
@RequestMapping(value = "/scripting/{path}/{name}/toggle/{enable}", method = RequestMethod.POST)
public void toggle(@PathVariable("path") String path, @PathVariable("name") String name, @PathVariable("enable") boolean enable, Principal principal) {
ensureAdmin(principal);
scriptingService.toggle(path, enable);
scriptingService.toggle(path, name, enable);
}

private void ensureAdmin(Principal principal) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/alfio/manager/ExtensionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private void asyncCall(ExtensionEvent event, int eventId, int organizationId, Ma


public static String toPath(int organizationId, int eventId) {
return organizationId + "/" + eventId;
return organizationId + "." + eventId;
}

}
16 changes: 16 additions & 0 deletions src/main/java/alfio/model/ScriptSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,20 @@ public ScriptSupport(@Column("path") String path,
this.async = async;
this.script = script;
}


@Getter
public static class ScriptPathNameHash {
private final String path;
private final String name;
private final String hash;

public ScriptPathNameHash(@Column("path") String path,
@Column("name") String name,
@Column("hash") String hash) {
this.path = path;
this.name = name;
this.hash = hash;
}
}
}
41 changes: 27 additions & 14 deletions src/main/java/alfio/repository/ScriptRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import ch.digitalfondue.npjt.QueryRepository;

import java.util.List;
import java.util.Set;

@QueryRepository
public interface ScriptRepository {
Expand All @@ -36,25 +37,37 @@ int insert(@Bind("path") String path,
@Bind("async") boolean async,
@Bind("script") String script);

@Query("update script_support set enabled = :enabled where path = :path")
int toggle(@Bind("path") String path, @Bind("enabled") boolean enabled);
@Query("update script_support set enabled = :enabled where path = :path and name = :name")
int toggle(@Bind("path") String path, @Bind("name") String name, @Bind("enabled") boolean enabled);

@Query("insert into script_event(path_fk, event) values " +
" (:path, :event)")
int insert(@Bind("path") String path, @Bind("event") String event);
@Query("insert into script_event(path_fk, name_fk, event) values " +
" (:path, :name, :event)")
int insertEvent(@Bind("path") String path, @Bind("name") String name, @Bind("event") String event);

@Query("select count(*) from script_support where path = :path")
int hasPath(@Bind("path") String path);
@Query("select count(*) from script_support where path = :path and name = :name")
int hasPath(@Bind("path") String path, @Bind("name") String name);

@Query("select script from script_support where path = :path")
String getScript(@Bind("path") String path);
@Query("select script from script_support where path = :path and name = :name")
String getScript(@Bind("path") String path, @Bind("name") String name);

@Query("delete from script_event where path_fk = :path")
int deleteEventsForPath(@Bind("path") String path);
@Query("delete from script_event where path_fk = :path and name_fk = :name")
int deleteEventsForPath(@Bind("path") String path, @Bind("name") String name);

@Query("delete from script_support where path = :path")
int deleteScriptForPath(@Bind("path") String path);
@Query("delete from script_support where path = :path and name = :name")
int deleteScriptForPath(@Bind("path") String path, @Bind("name") String name);

@Query("select * from script_support order by path")
@Query("select * from script_support order by name, path")
List<ScriptSupport> listAll();


@Query("select path, name, hash from " +
" (select a1.* from " +
" (select path, name, hash from script_support where enabled = true and async = :async and (path in (:possiblePaths))) a1 " +
" left outer join (select path, name from script_support where enabled = true and async = :async and (path in (:possiblePaths))) a2 on " +
" (a1.name = a2.name) and length(a1.path) < length(a2.path) where a2.path is null) " +
" " +
" inner join script_event on path_fk = path and name_fk = name where event = :event")
List<ScriptSupport.ScriptPathNameHash> findActive(@Bind("possiblePaths") Set<String> possiblePaths,
@Bind("async") boolean async,
@Bind("event") String event);
}
5 changes: 5 additions & 0 deletions src/main/java/alfio/scripting/ScriptingExecutionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@

package alfio.scripting;

import alfio.util.Json;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.script.*;
import java.util.Collections;
Expand Down Expand Up @@ -93,6 +95,9 @@ private static Object executeScript(String name, CompiledScript script, Map<Stri
}
ScriptContext newContext = new SimpleScriptContext();
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
engineScope.put("log", log);
engineScope.put("GSON", Json.GSON);
engineScope.put("restTemplate", new RestTemplate());
engineScope.putAll(params);
return script.eval(newContext);
} catch (ScriptException ex) {
Expand Down
58 changes: 33 additions & 25 deletions src/main/java/alfio/scripting/ScriptingService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
package alfio.scripting;

import alfio.model.ScriptSupport;
import alfio.model.ScriptSupport.ScriptPathNameHash;
import alfio.repository.ScriptRepository;
import alfio.util.Json;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -54,67 +54,75 @@ public void createOrUpdate(Script script) {

ScriptMetadata scriptMetadata = new ScriptMetadata(async, events);

if(scriptRepository.hasPath(script.getPath()) > 0) {
scriptRepository.deleteEventsForPath(script.getPath());
scriptRepository.deleteScriptForPath(script.getPath());
if(scriptRepository.hasPath(script.getPath(), script.getName()) > 0) {
scriptRepository.deleteEventsForPath(script.getPath(), script.getName());
scriptRepository.deleteScriptForPath(script.getPath(), script.getName());
}

scriptRepository.insert(script.getPath(), script.getName(), hash, script.isEnabled(), scriptMetadata.async, script.getScript());
for(String event : scriptMetadata.events) {
scriptRepository.insert(script.getPath(), event);
scriptRepository.insertEvent(script.getPath(), script.getName(), event);
}
}

@Transactional
public void toggle(String path, boolean status) {
scriptRepository.toggle(path, status);
public void toggle(String path, String name, boolean status) {
scriptRepository.toggle(path, name, status);
}

@Transactional
public void delete(String path) {
scriptRepository.deleteEventsForPath(path);
scriptRepository.deleteScriptForPath(path);
public void delete(String path, String name) {
scriptRepository.deleteEventsForPath(path, name);
scriptRepository.deleteScriptForPath(path, name);
}

public String getScript(String path) {
return scriptRepository.getScript(path);
public String getScript(String path, String name) {
return scriptRepository.getScript(path, name);
}

public <T> T executeScriptsForEvent(String event, String basePath, Map<String, Object> payload) {
List<Triple<String, String, String>> activePaths = getActiveScriptsForEvent(event, basePath, false);
List<ScriptPathNameHash> activePaths = getActiveScriptsForEvent(event, basePath, false);
T res = null;
Map<String, Object> input = new HashMap<>(payload);
input.put("event", event);
for(Triple<String, String, String> activePath : activePaths) {
String path = activePath.getLeft();
res = scriptingExecutionService.executeScript(path, activePath.getMiddle(), activePath.getRight(),
() -> getScript(path)+"\n;executeScript(event);", input);
for(ScriptPathNameHash activePath : activePaths) {
String path = activePath.getPath();
String name = activePath.getName();
res = scriptingExecutionService.executeScript(path, name, activePath.getHash(),
() -> getScript(path, name)+"\n;executeScript(event);", input);
input.put("output", res);
}
return res;
}

public void executeScriptAsync(String event, String basePath, Map<String, Object> payload) {
List<Triple<String, String, String>> activePaths = getActiveScriptsForEvent(event, basePath, true);
List<ScriptPathNameHash> activePaths = getActiveScriptsForEvent(event, basePath, true);
Map<String, Object> input = new HashMap<>(payload);
input.put("event", event);
for(Triple<String, String, String> activePath : activePaths) {
String path = activePath.getLeft();
scriptingExecutionService.executeScriptAsync(path, activePath.getMiddle(), activePath.getRight(), () -> getScript(path)+"\n;executeScript(event);", input);
for(ScriptPathNameHash activePath : activePaths) {
String path = activePath.getPath();
String name = activePath.getName();
scriptingExecutionService.executeScriptAsync(path, name, activePath.getHash(), () -> getScript(path, name)+"\n;executeScript(event);", input);
}
}

private List<Triple<String, String, String>> getActiveScriptsForEvent(String event, String basePath, boolean async) {
private List<ScriptPathNameHash> getActiveScriptsForEvent(String event, String basePath, boolean async) {
// fetch all active scripts
// to handle override:
// if there are active two scripts with the same name
// with path:
// - org/event
// - org.event
// - org
// the one with the longest path win

//generate all the paths
Set<String> paths = new TreeSet<>();
String[] splitted = basePath.split("\\.");
for (int i = 0; i < splitted.length; i++) {
paths.add(StringUtils.join(Arrays.copyOfRange(splitted, 0, i + 1), '.'));
}

return Collections.emptyList();
return scriptRepository.findActive(paths, async, event);
}

public List<ScriptSupport> listAll() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ create table script_support (
async boolean not null,
script clob not null
);
alter table script_support add constraint "unique_script_support" unique(path, name);

create table script_event (
path_fk varchar(256) not null,
name_fk varchar(256) not null,
event varchar(256) not null
);
);

alter table script_event add constraint "unique_script_event" unique(path_fk, name_fk);

--TODO: add fk
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

function deleteScript(script) {
if(window.confirm('Delete script ' + script.name+'?')) {
$http.delete('/admin/api/scripting/' + script.path).then(function () {
$http.delete('/admin/api/scripting/' + script.path + '/' + script.name).then(function () {
load();
});
}
Expand Down

0 comments on commit 29cda57

Please sign in to comment.