Skip to content

Commit

Permalink
#62 - move plugin configurations to event level
Browse files Browse the repository at this point in the history
  • Loading branch information
cbellone committed Oct 2, 2015
1 parent f47a586 commit 0333e39
Showing 11 changed files with 151 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -92,10 +92,10 @@ public List<PluginConfigOption> loadPluginConfiguration() {
return pluginManager.loadAllConfigOptions();
}

@RequestMapping(value = "/configuration/plugin/update-bulk", method = POST)
public List<PluginConfigOption> updatePluginConfiguration(@RequestBody List<PluginConfigOptionModification> input) {
@RequestMapping(value = "/configuration/event/{eventId}/plugin/update-bulk", method = POST)
public List<PluginConfigOption> updatePluginConfiguration(@PathVariable int eventId, @RequestBody List<PluginConfigOptionModification> input) {
Objects.requireNonNull(input);
pluginManager.saveAllConfigOptions(input);
pluginManager.saveAllConfigOptions(eventId, input);
return loadPluginConfiguration();
}

42 changes: 27 additions & 15 deletions src/main/java/alfio/manager/plugin/PluginManager.java
Original file line number Diff line number Diff line change
@@ -27,72 +27,84 @@
import alfio.plugin.ReservationConfirmationPlugin;
import alfio.plugin.TicketAssignmentPlugin;
import alfio.plugin.WaitingQueueSubscriptionPlugin;
import alfio.repository.EventRepository;
import alfio.repository.plugin.PluginConfigurationRepository;
import alfio.repository.plugin.PluginLogRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.time.ZonedDateTime;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Stream;

@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class PluginManager implements ApplicationListener<ContextRefreshedEvent> {

private final List<Plugin> plugins;
private final PluginConfigurationRepository pluginConfigurationRepository;
private final PluginLogRepository pluginLogRepository;
private final EventRepository eventRepository;
private final ExecutorService executor = Executors.newCachedThreadPool();

@Autowired
public PluginManager(List<Plugin> plugins, PluginConfigurationRepository pluginConfigurationRepository, PluginLogRepository pluginLogRepository) {
public PluginManager(List<Plugin> plugins, PluginConfigurationRepository pluginConfigurationRepository, PluginLogRepository pluginLogRepository, EventRepository eventRepository) {
this.plugins = plugins;
this.pluginConfigurationRepository = pluginConfigurationRepository;
this.pluginLogRepository = pluginLogRepository;
this.eventRepository = eventRepository;
}

public void handleReservationConfirmation(TicketReservation reservation, int eventId) {
executor.submit(() -> filterPlugins(plugins, ReservationConfirmationPlugin.class).forEach(p -> p.onReservationConfirmation(reservation, eventId)));
executor.submit(() -> filterPlugins(plugins, eventId, ReservationConfirmationPlugin.class).forEach(p -> p.onReservationConfirmation(reservation, eventId)));
}

public void handleTicketAssignment(Ticket ticket) {
executor.submit(() -> filterPlugins(plugins, TicketAssignmentPlugin.class).forEach(p -> p.onTicketAssignment(ticket)));
executor.submit(() -> filterPlugins(plugins, ticket.getEventId(), TicketAssignmentPlugin.class).forEach(p -> p.onTicketAssignment(ticket)));
}

public void handleWaitingQueueSubscription(WaitingQueueSubscription waitingQueueSubscription) {
executor.submit(() -> filterPlugins(plugins, WaitingQueueSubscriptionPlugin.class).forEach(p -> p.onWaitingQueueSubscription(waitingQueueSubscription)));
executor.submit(() -> filterPlugins(plugins, waitingQueueSubscription.getEventId(), WaitingQueueSubscriptionPlugin.class).forEach(p -> p.onWaitingQueueSubscription(waitingQueueSubscription)));
}

public List<PluginConfigOption> loadAllConfigOptions() {
return pluginConfigurationRepository.loadAll();
}

public void saveAllConfigOptions(List<PluginConfigOptionModification> input) {
input.forEach(m -> pluginConfigurationRepository.update(m.getPluginId(), m.getName(), m.getValue()));
public void saveAllConfigOptions(int eventId, List<PluginConfigOptionModification> input) {
input.forEach(m -> pluginConfigurationRepository.update(m.getPluginId(), eventId, m.getName(), m.getValue()));
}

public List<PluginLog> loadAllLogMessages() {
return pluginLogRepository.loadAll();
}

private static <T extends Plugin> Stream<T> filterPlugins(List<Plugin> plugins, Class<T> type) {
private static <T extends Plugin> Stream<T> filterPlugins(List<Plugin> plugins, int eventId, Class<T> type) {
return plugins.stream()
.filter(type::isInstance)
.filter(Plugin::isEnabled)
.filter(p -> p.isEnabled(eventId))
.map(type::cast);
}

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
plugins.stream()
.filter(p -> !pluginConfigurationRepository.loadSingleOption(p.getId(), Plugin.ENABLED_CONF_NAME).isPresent())
.forEach(p -> {
pluginConfigurationRepository.insert(p.getId(), Plugin.ENABLED_CONF_NAME, "false", "Enabled", ComponentType.BOOLEAN);
p.install();
});
eventRepository.findAll()
.stream()
.filter(e -> e.getEnd().isBefore(ZonedDateTime.now(e.getZoneId())))
.forEach(e -> {
final int eventId = e.getId();
plugins.stream()
.filter(p -> !pluginConfigurationRepository.loadSingleOption(p.getId(), eventId, Plugin.ENABLED_CONF_NAME).isPresent())
.forEach(p -> {
pluginConfigurationRepository.insert(p.getId(), eventId, Plugin.ENABLED_CONF_NAME, "false", "Enabled", ComponentType.BOOLEAN);
p.install(eventId);
});
});
}
}
23 changes: 22 additions & 1 deletion src/main/java/alfio/manager/system/DataMigrator.java
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
import alfio.model.system.EventMigration;
import alfio.repository.EventRepository;
import alfio.repository.TicketRepository;
import alfio.repository.plugin.PluginConfigurationRepository;
import alfio.repository.system.ConfigurationRepository;
import alfio.repository.system.EventMigrationRepository;
import alfio.util.EventUtil;
@@ -41,6 +42,7 @@

import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@@ -63,6 +65,7 @@ public class DataMigrator {
private final ZonedDateTime buildTimestamp;
private final TransactionTemplate transactionTemplate;
private final ConfigurationRepository configurationRepository;
private final PluginConfigurationRepository pluginConfigurationRepository;
private final NamedParameterJdbcTemplate jdbc;

@Autowired
@@ -73,11 +76,12 @@ public DataMigrator(EventMigrationRepository eventMigrationRepository,
PlatformTransactionManager transactionManager,
TicketRepository ticketRepository,
ConfigurationRepository configurationRepository,
NamedParameterJdbcTemplate jdbc) {
PluginConfigurationRepository pluginConfigurationRepository, NamedParameterJdbcTemplate jdbc) {
this.eventMigrationRepository = eventMigrationRepository;
this.eventRepository = eventRepository;
this.ticketRepository = ticketRepository;
this.configurationRepository = configurationRepository;
this.pluginConfigurationRepository = pluginConfigurationRepository;
this.jdbc = jdbc;
this.currentVersion = parseVersion(currentVersion);
this.currentVersionAsString = currentVersion;
@@ -112,6 +116,7 @@ void migrateEventToCurrentVersion(Event event) {
optional.ifPresent(eventMigration -> eventMigrationRepository.lockEventMigrationForUpdate(eventMigration.getId()));
createMissingTickets(event);
fillDescriptions(event);
migratePluginConfig(event);
if(alreadyDefined) {
EventMigration eventMigration = optional.get();
int result = eventMigrationRepository.updateMigrationData(eventMigration.getId(), currentVersionAsString, buildTimestamp, EventMigration.Status.COMPLETE.name());
@@ -125,6 +130,22 @@ void migrateEventToCurrentVersion(Event event) {
}
}

void migratePluginConfig(Event event) {
transactionTemplate.execute(s -> {
pluginConfigurationRepository.loadByEventId(-1).forEach(p -> {
MapSqlParameterSource source = new MapSqlParameterSource("pluginId", p.getPluginId())
.addValue("eventId", event.getId())
.addValue("confName", p.getOptionName())
.addValue("confValue", p.getValue())
.addValue("description", p.getDescription())
.addValue("confType", p.getComponentType().name());
jdbc.update("insert into plugin_configuration(plugin_id, event_id, conf_name, conf_value, conf_description, conf_type) values(:pluginId, :eventId, :confName, :confValue, :description, :confType)", source);
});
jdbc.update("update plugin_configuration set event_id = -2 where event_id = -1", new EmptySqlParameterSource());
return null;
});
}

void fillReservationsLanguage() {
transactionTemplate.execute(s -> {
jdbc.queryForList("select id from tickets_reservation where user_language is null", new EmptySqlParameterSource(), String.class)
3 changes: 3 additions & 0 deletions src/main/java/alfio/model/plugin/PluginConfigOption.java
Original file line number Diff line number Diff line change
@@ -27,18 +27,21 @@
@Data
public class PluginConfigOption {
private final String pluginId;
private final int eventId;
private final String optionName;
private final String optionValue;
private final String description;
private final ComponentType componentType;


public PluginConfigOption(@Column("plugin_id") String pluginId,
@Column("event_id") int eventId,
@Column("conf_name") String optionName,
@Column("conf_value") String optionValue,
@Column("conf_description") String description,
@Column("conf_type") ComponentType componentType) {
this.pluginId = pluginId;
this.eventId = eventId;
this.optionName = optionName;
this.optionValue = optionValue;
this.description = description;
9 changes: 6 additions & 3 deletions src/main/java/alfio/plugin/Plugin.java
Original file line number Diff line number Diff line change
@@ -42,18 +42,21 @@ public interface Plugin {

/**
* Returns the state of this plugin: whether or not the admin activated it
* @param eventId the Event under examination
* @return {@code true} if enabled, {@code false} otherwise
*/
boolean isEnabled();
boolean isEnabled(int eventId);

/**
* Returns all the config options needed by the current plugin. This collection must contains at least one element
* @param eventId the id of the event
* @return the options, never {@code null}
*/
Collection<PluginConfigOption> getConfigOptions();
Collection<PluginConfigOption> getConfigOptions(int eventId);

/**
* Triggers the installation procedure. In this phase, the plugin can safely write its configuration on the database.
* @param eventId id of the event to be configured
*/
void install();
void install(int eventId);
}
8 changes: 4 additions & 4 deletions src/main/java/alfio/plugin/PluginDataStorageProvider.java
Original file line number Diff line number Diff line change
@@ -62,12 +62,12 @@ private PluginDataStorage(String pluginId,
this.tx = tx;
}

public Optional<String> getConfigValue(String name) {
return pluginConfigurationRepository.loadSingleOption(pluginId, name).map(PluginConfigOption::getOptionValue);
public Optional<String> getConfigValue(String name, int eventId) {
return pluginConfigurationRepository.loadSingleOption(pluginId, eventId, name).map(PluginConfigOption::getOptionValue);
}

public void insertConfigValue(String name, String value, String description, ComponentType componentType) {
pluginConfigurationRepository.insert(pluginId, name, value, description, componentType);
public void insertConfigValue(int eventId, String name, String value, String description, ComponentType componentType) {
pluginConfigurationRepository.insert(pluginId, eventId, name, value, description, componentType);
}

public void registerSuccess(String description, int eventId) {
22 changes: 11 additions & 11 deletions src/main/java/alfio/plugin/mailchimp/MailChimpPlugin.java
Original file line number Diff line number Diff line change
@@ -77,25 +77,25 @@ public String getName() {
}

@Override
public boolean isEnabled() {
return pluginDataStorage.getConfigValue(ENABLED_CONF_NAME).map(Boolean::parseBoolean).orElse(false);
public boolean isEnabled(int eventId) {
return pluginDataStorage.getConfigValue(ENABLED_CONF_NAME, eventId).map(Boolean::parseBoolean).orElse(false);
}

@Override
public Collection<PluginConfigOption> getConfigOptions() {
return Arrays.asList(new PluginConfigOption(getId(), DATA_CENTER, "", "The MailChimp data center used by your account (e.g. us6)", ComponentType.TEXT),
new PluginConfigOption(getId(), API_KEY, "", "the Mailchimp API Key", ComponentType.TEXT),
new PluginConfigOption(getId(), LIST_ID, "", "the list ID", ComponentType.TEXT));
public Collection<PluginConfigOption> getConfigOptions(int eventId) {
return Arrays.asList(new PluginConfigOption(getId(), eventId, DATA_CENTER, "", "The MailChimp data center used by your account (e.g. us6)", ComponentType.TEXT),
new PluginConfigOption(getId(), eventId, API_KEY, "", "the Mailchimp API Key", ComponentType.TEXT),
new PluginConfigOption(getId(), eventId, LIST_ID, "", "the list ID", ComponentType.TEXT));
}

@Override
public void install() {
getConfigOptions().stream().forEach(o -> pluginDataStorage.insertConfigValue(o.getOptionName(), o.getOptionValue(), o.getDescription(), o.getComponentType()));
public void install(int eventId) {
getConfigOptions(eventId).stream().forEach(o -> pluginDataStorage.insertConfigValue(eventId, o.getOptionName(), o.getOptionValue(), o.getDescription(), o.getComponentType()));
}

private Optional<String> getListAddress(int eventId, String email, String name, String language) {
Optional<String> dataCenter = pluginDataStorage.getConfigValue(DATA_CENTER);
Optional<String> listId = pluginDataStorage.getConfigValue(LIST_ID);
Optional<String> dataCenter = pluginDataStorage.getConfigValue(DATA_CENTER, eventId);
Optional<String> listId = pluginDataStorage.getConfigValue(LIST_ID, eventId);
if(dataCenter.isPresent() && listId.isPresent()) {
return Optional.of(String.format(LIST_ADDRESS, dataCenter.get(), listId.get()));
} else {
@@ -105,7 +105,7 @@ private Optional<String> getListAddress(int eventId, String email, String name,
}

private Optional<String> getApiKey(int eventId, String email, String name, String language) {
Optional<String> apiKey = pluginDataStorage.getConfigValue(API_KEY);
Optional<String> apiKey = pluginDataStorage.getConfigValue(API_KEY, eventId);
if(!apiKey.isPresent()) {
pluginDataStorage.registerFailure(String.format(FAILURE_MSG, email, name, language, "missing API Key"), eventId);
}
Original file line number Diff line number Diff line change
@@ -31,16 +31,19 @@ public interface PluginConfigurationRepository {
@Query("select * from plugin_configuration")
List<PluginConfigOption> loadAll();

@Query("select * from plugin_configuration where plugin_id = :pluginId")
List<PluginConfigOption> loadByPluginId(@Bind("pluginId") String pluginId);
@Query("select * from plugin_configuration where plugin_id = :pluginId and event_id = :eventId")
List<PluginConfigOption> loadByPluginIdAndEventId(@Bind("pluginId") String pluginId, @Bind("eventId") int eventId);

@Query("select * from plugin_configuration where plugin_id = :pluginId and conf_name = :name")
Optional<PluginConfigOption> loadSingleOption(@Bind("pluginId") String pluginId, @Bind("name") String name);
@Query("select * from plugin_configuration where plugin_id = :pluginId and event_id = :eventId and conf_name = :name")
Optional<PluginConfigOption> loadSingleOption(@Bind("pluginId") String pluginId, @Bind("eventId") int eventId, @Bind("name") String name);

@Query("insert into plugin_configuration(plugin_id, conf_name, conf_value, conf_description, conf_type) values (:pluginId, :name, :value, :description, :type)")
int insert(@Bind("pluginId") String pluginId, @Bind("name") String name, @Bind("value") String value, @Bind("description") String description, @Bind("type") ComponentType type);
@Query("insert into plugin_configuration(plugin_id, event_id, conf_name, conf_value, conf_description, conf_type) values (:pluginId, :eventId, :name, :value, :description, :type)")
int insert(@Bind("pluginId") String pluginId, @Bind("eventId") int eventId, @Bind("name") String name, @Bind("value") String value, @Bind("description") String description, @Bind("type") ComponentType type);

@Query("update plugin_configuration set conf_value = :value where plugin_id = :pluginId and conf_name = :name")
int update(@Bind("pluginId") String pluginId, @Bind("name") String name, @Bind("value") String value);
@Query("update plugin_configuration set conf_value = :value where plugin_id = :pluginId and event_id = :eventId and conf_name = :name")
int update(@Bind("pluginId") String pluginId, @Bind("eventId") int eventId, @Bind("name") String name, @Bind("value") String value);

@Query("select * from plugin_configuration where event_id = :eventId")
List<PluginConfigOption> loadByEventId(@Bind("eventId") int eventId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--
-- This file is part of alf.io.
--
-- alf.io is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- alf.io is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with alf.io. If not, see <http://www.gnu.org/licenses/>.
--

--set the default to -1 in order to mark the existing plugin configuration and migrate it later
alter table plugin_configuration add COLUMN event_id INTEGER DEFAULT -1 NOT null;
alter table PLUGIN_CONFIGURATION add constraint "unique_plugin_conf_event_id" unique(plugin_id, event_id, conf_name);
alter table PLUGIN_CONFIGURATION drop constraint "unique_plugin_conf";
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--
-- This file is part of alf.io.
--
-- alf.io is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- alf.io is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with alf.io. If not, see <http://www.gnu.org/licenses/>.
--

--set the default to -1 in order to mark the existing plugin configuration and migrate it later
alter table plugin_configuration add COLUMN event_id integer DEFAULT -1 NOT null;
alter table PLUGIN_CONFIGURATION drop constraint "unique_plugin_conf";
alter table PLUGIN_CONFIGURATION add constraint "unique_plugin_conf" unique(plugin_id, event_id, conf_name);
Loading

0 comments on commit 0333e39

Please sign in to comment.