Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tenant Scripting Enhancements #1964

Merged
merged 11 commits into from
Apr 8, 2024
38 changes: 29 additions & 9 deletions src/main/java/sirius/biz/jobs/batch/ImportBatchProcessFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,55 @@ public String queueName() {
/**
* Permits selecting the {@link sirius.biz.scripting.ScriptableEventDispatcher} to use for this import.
*/
public static final Parameter<String> DISPATCHER_PARAMETER;
public static final Parameter<String> DISPATCHER_PARAMETER = createDispatcherParameter();

static {
SelectStringParameter dispatcherParameter = new SelectStringParameter("eventDispatcher", "$ImportBatchProcessFactory.eventDispatcher");
@Part
private ScriptableEvents scriptableEvents;

/**
* Creates a new instance of the dispatcher parameter.
*
* @return a new instance of the dispatcher parameter
*/
public static Parameter<String> createDispatcherParameter() {
SelectStringParameter dispatcherParameter =
new SelectStringParameter("eventDispatcher", "$ImportBatchProcessFactory.eventDispatcher");
dispatcherParameter.markRequired();
dispatcherParameter.withDescription("$ImportBatchProcessFactory.eventDispatcher.help");
dispatcherParameter.withEntriesProvider(() -> {
Map<String, String> eventDispatchers = new LinkedHashMap<>();
ScriptableEvents scriptableEvents = Injector.context().getPart(ScriptableEvents.class);
if (scriptableEvents != null) {
scriptableEvents.fetchDispatchersForCurrentTenant()
.forEach(dispatcher -> eventDispatchers.put(dispatcher, dispatcher));
.forEach(dispatcher -> eventDispatchers.put(dispatcher, dispatcher));
}
return eventDispatchers;
});
dispatcherParameter.hideWhen((parameter, context) -> {
return parameter.getValues().size() == 1;
});

DISPATCHER_PARAMETER = dispatcherParameter.build();
return dispatcherParameter.build();
}

@Part
private ScriptableEvents scriptableEvents;
/**
* Determines if scriptable events are enabled for this factory.
* <p>
* Disabled by default. Override this method to enable scriptable events where needed.
*
* @return <tt>true</tt> if scriptable events should be enabled, <tt>false</tt> otherwise
*/
protected boolean enableScriptableEvents() {
return false;
}

@Override
protected abstract ImportJob createJob(ProcessContext process);

@Override
protected void collectParameters(Consumer<Parameter<?>> parameterCollector) {
if (scriptableEvents.fetchDispatchersForCurrentTenant().size() > 1) {
parameterCollector.accept(DISPATCHER_PARAMETER);
if (enableScriptableEvents()) {
parameterCollector.accept(createDispatcherParameter());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import sirius.kernel.di.std.AutoRegister;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;

Expand All @@ -37,8 +36,7 @@ public interface ScriptableEventDispatcherRepository {
* @param tenantId the tenant for which to fetch the dispatcher
* @param name the name of the dispatcher to fetch
* @return the dispatcher with the given name for the given tenant wrapped as optional or an empty optional if
* no such dispatcher exists. <b>NOTE:</b> if an empty name is given, the first dispatcher for the given tenant
* is used. This helps to simplify the usage of custom events.
* no such dispatcher exists.
*/
Optional<ScriptableEventDispatcher> fetchDispatcher(@Nonnull String tenantId, @Nullable String name);
Optional<ScriptableEventDispatcher> fetchDispatcher(@Nonnull String tenantId, @Nonnull String name);
}
8 changes: 4 additions & 4 deletions src/main/java/sirius/biz/scripting/ScriptableEvents.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package sirius.biz.scripting;

import sirius.kernel.commons.Strings;
import sirius.kernel.di.std.Part;
import sirius.kernel.di.std.Register;
import sirius.web.security.ScopeInfo;
Expand Down Expand Up @@ -53,11 +54,10 @@ public void handleEvent(ScriptableEvent event) {
*
* @param name the name of the dispatcher to fetch
* @return the dispatcher for the current tenant with the given name or a NOOP dispatcher if no such dispatcher
* exists. Note, if an empty <tt>name</tt> is given, the first available dispatcher for the current tenant is used.
* This way, if exactly one dispatcher is present, it will be used in all import processes etc.
* exists.
*/
public ScriptableEventDispatcher fetchDispatcherForCurrentTenant(@Nullable String name) {
if (dispatcherRepository == null) {
public ScriptableEventDispatcher fetchDispatcherForCurrentTenant(String name) {
if (dispatcherRepository == null || Strings.isEmpty(name)) {
andyHa marked this conversation as resolved.
Show resolved Hide resolved
return NOOP_DISPATCHER;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import sirius.pasta.noodle.sandbox.SandboxMode;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Optional;

Expand All @@ -55,6 +54,7 @@ public class MongoCustomEventDispatcherRepository implements ScriptableEventDisp
public List<String> fetchAvailableDispatchers(@Nonnull String tenantId) {
return mango.select(MongoCustomScript.class)
.eq(MongoCustomScript.TENANT, tenantId)
.eq(MongoCustomScript.DISABLED, false)
.orderAsc(MongoCustomScript.CODE)
.queryList()
.stream()
Expand All @@ -63,22 +63,13 @@ public List<String> fetchAvailableDispatchers(@Nonnull String tenantId) {
}

@Override
public Optional<ScriptableEventDispatcher> fetchDispatcher(@Nonnull String tenantId, @Nullable String name) {
if (Strings.isEmpty(name)) {
List<MongoCustomScript> mongoCustomScripts =
mango.select(MongoCustomScript.class).eq(MongoCustomScript.TENANT, tenantId).limit(2).queryList();
if (mongoCustomScripts.size() == 1) {
return compileAndLoad(mongoCustomScripts.getFirst());
} else {
return Optional.empty();
}
} else {
return mango.select(MongoCustomScript.class)
.eq(MongoCustomScript.TENANT, tenantId)
.eq(MongoCustomScript.CODE, name)
.first()
.flatMap(this::compileAndLoad);
}
public Optional<ScriptableEventDispatcher> fetchDispatcher(@Nonnull String tenantId, @Nonnull String name) {
return mango.select(MongoCustomScript.class)
.eq(MongoCustomScript.TENANT, tenantId)
.eq(MongoCustomScript.CODE, name)
.eq(MongoCustomScript.DISABLED, false)
.first()
.flatMap(this::compileAndLoad);
}

private Optional<ScriptableEventDispatcher> compileAndLoad(MongoCustomScript script) {
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/sirius/biz/scripting/mongo/MongoCustomScript.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
* Stores a custom scripting profile for a tenant.
*/
@Framework(MongoCustomEventDispatcherRepository.FRAMEWORK_SCRIPTING_MONGO)
@Index(name = "lookup", columns = {"tenant", "code"}, columnSettings = {Mango.INDEX_ASCENDING, Mango.INDEX_ASCENDING})
@Index(name = "script_lookup",
columns = {"tenant", "disabled", "code"},
columnSettings = {Mango.INDEX_ASCENDING, Mango.INDEX_ASCENDING, Mango.INDEX_ASCENDING})
public class MongoCustomScript extends MongoTenantAware {

/**
Expand All @@ -38,6 +40,11 @@ public class MongoCustomScript extends MongoTenantAware {
@AutoImport
private String code;

public static final Mapping DISABLED = Mapping.named("disabled");
@Autoloaded
@AutoImport
private boolean disabled;

/**
* Contains the actual scripting code.
*/
Expand Down Expand Up @@ -71,4 +78,12 @@ public String getScript() {
public void setScript(String script) {
this.script = script;
}

public boolean isDisabled() {
return disabled;
}

public void setDisabled(boolean inactive) {
this.disabled = inactive;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
import sirius.biz.web.BizController;
import sirius.biz.web.MongoPageHelper;
import sirius.db.mixing.query.QueryField;
import sirius.db.mongo.Mango;
import sirius.kernel.di.std.Part;
import sirius.kernel.di.std.Register;
import sirius.kernel.tokenizer.Position;
import sirius.pasta.noodle.compiler.CompilationContext;
Expand All @@ -36,9 +34,6 @@ public class MongoCustomScriptController extends BizController {

private static final String PARAM_SCRIPT = "script";

@Part
private Mango mango;

/**
* Lists all scripts available for the current tenant.
*
Expand All @@ -60,6 +55,7 @@ public void listScripts(WebContext webContext) {
* Modifies / manages the given script.
*
* @param webContext the request to handle
* @param id the ID of the script to manage
*/
@Routed("/scripting/scripts/:1")
@Permission(ScriptingController.PERMISSION_SCRIPTING)
Expand All @@ -75,6 +71,7 @@ public void editScript(WebContext webContext, String id) {
* Deletes the given script.
*
* @param webContext the request to handle
* @param id the ID of the script to delete
*/
@Routed("/scripting/scripts/:1/delete")
@Permission(ScriptingController.PERMISSION_SCRIPTING)
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/biz_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,10 @@ MongoCodeListExportJobFactory.label = Codeliste exportieren
MongoCodeListImportJobFactory.description = Importiert eine Codeliste aus einer CSV oder Excel Datei.
MongoCodeListImportJobFactory.label = Codeliste importieren
MongoCustomScript.code = Code
MongoCustomScript.disabled = Deaktiviert
MongoCustomScript.state = Status
MongoCustomScript.state.active = Aktiv
MongoCustomScript.state.inactive = Inaktiv
MongoCustomScript.plural = Kunden-Scripte
MongoCustomScript.script = Quelltext
MongoCustomScript.unnamedScript = Unbenanntes Script
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@

<t:editForm url="@apply('/scripting/scripts/%s', script.getIdAsString())">
<input id="scriptField" name="script" value="@script.getScript()" type="hidden"/>
<t:textfield class="col-12 required"
name="code"
value="@script.getCode()"
labelKey="MongoCustomScript.code"/>
<div class="row">
<t:textfield class="col-12 col-md-6 required"
name="code"
value="@script.getCode()"
labelKey="MongoCustomScript.code"/>
<t:booleanSelect class="col-12 col-md-6"
name="disabled" value="script.isDisabled()"
labelKey="MongoCustomScript.disabled"/>
</div>

<t:heading labelKey="MongoCustomScript.script"/>
<div class="d-flex flex-col">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,21 @@
<div class="card">
<div class="card-body">
<t:emptyCheck data="scripts">
<table class="table mb-4">
<thead>
<tr>
<th>@i18n("MongoCustomScript.code")</th>
<th scope="col" class="delete-btn-column"/>
</tr>
</thead>
<tbody>
<t:datacards>
<i:for type="sirius.biz.scripting.mongo.MongoCustomScript" var="script" items="scripts.getItems()">
<tr>
<td>
<a href="@apply('/scripting/scripts/%s', script.getIdAsString())">@script.getCode()</a>
</td>
<td class="delete-btn-column">
<t:deleteButton
url="@apply('/scripting/scripts/%s/delete', script.getIdAsString())"
page="scripts"/>
</td>
</tr>
<t:datacard title="@script.getCode()"
link="@apply('/scripting/scripts/%s', script.getIdAsString())">
<i:block name="footer">
<i:if test="script.isDisabled()">
<t:tag color="red">@i18n("MongoCustomScript.state.inactive")</t:tag>
<i:else>
<t:tag color="green">@i18n("MongoCustomScript.state.active")</t:tag>
</i:else>
</i:if>
</i:block>
</t:datacard>
</i:for>
</tbody>
</table>
</t:datacards>
<t:pagination page="scripts" baseUrl="/scripting/scripts"/>
</t:emptyCheck>
</div>
Expand Down