package se.bjurr.prnfb.service;
import static com.atlassian.bitbucket.permission.Permission.ADMIN;
import static com.google.common.base.Joiner.on;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Iterables.tryFind;
import static com.google.common.collect.Lists.newArrayList;
import static se.bjurr.prnfb.settings.PrnfbNotificationBuilder.prnfbNotificationBuilder;
import static se.bjurr.prnfb.settings.PrnfbSettingsBuilder.prnfbSettingsBuilder;
import static se.bjurr.prnfb.settings.PrnfbSettingsDataBuilder.prnfbSettingsDataBuilder;
import java.util.List;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.atlassian.bitbucket.pull.PullRequestState;
import com.atlassian.bitbucket.user.SecurityService;
import com.atlassian.bitbucket.util.Operation;
import com.atlassian.sal.api.pluginsettings.PluginSettings;
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
import com.atlassian.sal.api.transaction.TransactionCallback;
import com.atlassian.sal.api.transaction.TransactionTemplate;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.gson.Gson;
import se.bjurr.prnfb.listener.PrnfbPullRequestAction;
import se.bjurr.prnfb.presentation.dto.ON_OR_OFF;
import se.bjurr.prnfb.settings.HasUuid;
import se.bjurr.prnfb.settings.PrnfbButton;
import se.bjurr.prnfb.settings.PrnfbNotification;
import se.bjurr.prnfb.settings.PrnfbNotificationBuilder;
import se.bjurr.prnfb.settings.PrnfbSettings;
import se.bjurr.prnfb.settings.PrnfbSettingsData;
import se.bjurr.prnfb.settings.TRIGGER_IF_MERGE;
import se.bjurr.prnfb.settings.USER_LEVEL;
import se.bjurr.prnfb.settings.ValidationException;
import se.bjurr.prnfb.settings.legacy.AdminFormValues.BUTTON_VISIBILITY;
import se.bjurr.prnfb.settings.legacy.Header;
import se.bjurr.prnfb.settings.legacy.SettingsStorage;
public class SettingsService {
public static final String STORAGE_KEY = "se.bjurr.prnfb.pull-request-notifier-for-bitbucket-3";
private static Gson gson = new Gson();
private final Logger logger = LoggerFactory.getLogger(SettingsService.class);
private final PluginSettings pluginSettings;
private final SecurityService securityService;
private final TransactionTemplate transactionTemplate;
public SettingsService(
PluginSettingsFactory pluginSettingsFactory,
TransactionTemplate transactionTemplate,
SecurityService securityService) {
this.pluginSettings = pluginSettingsFactory.createGlobalSettings();
this.transactionTemplate = transactionTemplate;
this.securityService = securityService;
}
public PrnfbButton addOrUpdateButton(PrnfbButton prnfbButton) {
return inSynchronizedTransaction(
new TransactionCallback<PrnfbButton>() {
@Override
public PrnfbButton doInTransaction() {
return doAddOrUpdateButton(prnfbButton);
}
});
}
public PrnfbNotification addOrUpdateNotification(PrnfbNotification prnfbNotification)
throws ValidationException {
return inSynchronizedTransaction(
new TransactionCallback<PrnfbNotification>() {
@Override
public PrnfbNotification doInTransaction() {
try {
return doAddOrUpdateNotification(prnfbNotification);
} catch (ValidationException e) {
propagate(e);
}
return null;
}
});
}
public void deleteButton(UUID uuid) {
inSynchronizedTransaction(
new TransactionCallback<Void>() {
@Override
public Void doInTransaction() {
doDeleteButton(uuid);
return null;
}
});
}
public void deleteNotification(UUID uuid) {
inSynchronizedTransaction(
new TransactionCallback<Void>() {
@Override
public Void doInTransaction() {
doDeleteNotification(uuid);
return null;
}
});
}
public Optional<PrnfbButton> findButton(UUID uuid) {
return tryFind(getPrnfbSettings().getButtons(), withUuid(uuid));
}
public Optional<PrnfbNotification> findNotification(UUID notificationUuid) {
return tryFind(getPrnfbSettings().getNotifications(), withUuid(notificationUuid));
}
public PrnfbButton getButton(UUID buttionUuid) {
Optional<PrnfbButton> foundOpt = findButton(buttionUuid);
if (!foundOpt.isPresent()) {
throw new RuntimeException(buttionUuid + " not fond in:\n" + on('\n').join(getButtons()));
}
return foundOpt.get();
}
public List<PrnfbButton> getButtons() {
return getPrnfbSettings().getButtons();
}
public List<PrnfbButton> getButtons(String projectKey) {
List<PrnfbButton> found = newArrayList();
for (PrnfbButton candidate : getPrnfbSettings().getButtons()) {
if (candidate.getProjectKey().isPresent()
&& candidate.getProjectKey().get().equals(projectKey)) {
found.add(candidate);
}
}
return found;
}
public List<PrnfbButton> getButtons(String projectKey, String repositorySlug) {
List<PrnfbButton> found = newArrayList();
for (PrnfbButton candidate : getPrnfbSettings().getButtons()) {
if (candidate.getProjectKey().isPresent()
&& candidate.getProjectKey().get().equals(projectKey) //
&& candidate.getRepositorySlug().isPresent()
&& candidate.getRepositorySlug().get().equals(repositorySlug)) {
found.add(candidate);
}
}
return found;
}
public PrnfbNotification getNotification(UUID notificationUuid) {
return find(getPrnfbSettings().getNotifications(), withUuid(notificationUuid));
}
public List<PrnfbNotification> getNotifications() {
return getPrnfbSettings().getNotifications();
}
public List<PrnfbNotification> getNotifications(String projectKey) {
List<PrnfbNotification> found = newArrayList();
for (PrnfbNotification candidate : getPrnfbSettings().getNotifications()) {
if (candidate.getProjectKey().isPresent()
&& candidate.getProjectKey().get().equals(projectKey)) {
found.add(candidate);
}
}
return found;
}
public List<PrnfbNotification> getNotifications(String projectKey, String repositorySlug) {
List<PrnfbNotification> found = newArrayList();
for (PrnfbNotification candidate : getPrnfbSettings().getNotifications()) {
if (candidate.getProjectKey().isPresent()
&& candidate.getProjectKey().get().equals(projectKey) //
&& candidate.getRepositorySlug().isPresent()
&& candidate.getRepositorySlug().get().equals(repositorySlug)) {
found.add(candidate);
}
}
return found;
}
@VisibleForTesting
public PrnfbSettings getPrnfbSettings() {
return inSynchronizedTransaction(
new TransactionCallback<PrnfbSettings>() {
@Override
public PrnfbSettings doInTransaction() {
return doGetPrnfbSettings();
}
});
}
public PrnfbSettingsData getPrnfbSettingsData() {
return getPrnfbSettings().getPrnfbSettingsData();
}
public void setPrnfbSettingsData(PrnfbSettingsData prnfbSettingsData) {
inSynchronizedTransaction(
new TransactionCallback<Void>() {
@Override
public Void doInTransaction() {
PrnfbSettings oldSettings = doGetPrnfbSettings();
PrnfbSettings newPrnfbSettings =
prnfbSettingsBuilder(oldSettings) //
.setPrnfbSettingsData(prnfbSettingsData) //
.build();
doSetPrnfbSettings(newPrnfbSettings);
return null;
}
});
}
private PrnfbButton doAddOrUpdateButton(PrnfbButton prnfbButton) {
if (findButton(prnfbButton.getUuid()).isPresent()) {
doDeleteButton(prnfbButton.getUuid());
}
PrnfbSettings originalSettings = doGetPrnfbSettings();
PrnfbSettings updated =
prnfbSettingsBuilder(originalSettings) //
.withButton(prnfbButton) //
.build();
doSetPrnfbSettings(updated);
return prnfbButton;
}
private PrnfbNotification doAddOrUpdateNotification(PrnfbNotification prnfbNotification)
throws ValidationException {
if (findNotification(prnfbNotification.getUuid()).isPresent()) {
doDeleteNotification(prnfbNotification.getUuid());
}
PrnfbSettings originalSettings = doGetPrnfbSettings();
PrnfbSettings updated =
prnfbSettingsBuilder(originalSettings) //
.withNotification(prnfbNotification) //
.build();
doSetPrnfbSettings(updated);
return prnfbNotification;
}
private void doDeleteButton(UUID uuid) {
PrnfbSettings originalSettings = doGetPrnfbSettings();
List<PrnfbButton> keep =
newArrayList(filter(originalSettings.getButtons(), not(withUuid(uuid))));
PrnfbSettings withoutDeleted =
prnfbSettingsBuilder(originalSettings) //
.setButtons(keep) //
.build();
doSetPrnfbSettings(withoutDeleted);
}
private void doDeleteNotification(UUID uuid) {
PrnfbSettings originalSettings = doGetPrnfbSettings();
List<PrnfbNotification> keep =
newArrayList(filter(originalSettings.getNotifications(), not(withUuid(uuid))));
PrnfbSettings withoutDeleted =
prnfbSettingsBuilder(originalSettings) //
.setNotifications(keep) //
.build();
doSetPrnfbSettings(withoutDeleted);
}
private PrnfbSettings doGetPrnfbSettings() {
Object storedSettings = this.pluginSettings.get(STORAGE_KEY);
if (storedSettings == null) {
this.logger.info("No settings found for " + STORAGE_KEY + ", looking for legacy settings.");
if (this.pluginSettings.get(se.bjurr.prnfb.settings.legacy.SettingsStorage.STORAGE_KEY)
!= null
|| this.pluginSettings.get(
se.bjurr.prnfb.settings.legacy.SettingsStorage.STORAGE_KEY_PRNFS)
!= null) {
try {
this.logger.info("Using legacy settings.");
se.bjurr.prnfb.settings.legacy.PrnfbSettings legacySettings =
SettingsStorage.getPrnfbSettings(this.pluginSettings);
PrnfbSettings fromLegacy = settingsFromLegacy(legacySettings);
doSetPrnfbSettings(fromLegacy);
storedSettings = this.pluginSettings.get(STORAGE_KEY);
} catch (Exception e) {
this.logger.error("", e);
}
} else {
this.logger.info("Creating new default settings.");
return prnfbSettingsBuilder() //
.setPrnfbSettingsData( //
prnfbSettingsDataBuilder() //
.setAdminRestriction(USER_LEVEL.ADMIN) //
.build()) //
.build();
}
}
return gson.fromJson(storedSettings.toString(), PrnfbSettings.class);
}
private void doSetPrnfbSettings(PrnfbSettings PrnfbSettings) {
String data = gson.toJson(PrnfbSettings);
this.pluginSettings.put(STORAGE_KEY, data);
}
private synchronized <T> T inSynchronizedTransaction(TransactionCallback<T> transactionCallback) {
return this.securityService //
.withPermission(ADMIN, "Getting config") //
.call(
new Operation<T, RuntimeException>() {
@Override
public T perform() throws RuntimeException {
return SettingsService.this.transactionTemplate.execute(transactionCallback);
}
});
}
private PrnfbSettings settingsFromLegacy(
se.bjurr.prnfb.settings.legacy.PrnfbSettings oldSettings) {
String ks = oldSettings.getKeyStore().orNull();
String ksp = oldSettings.getKeyStorePassword().orNull();
String kst = oldSettings.getKeyStoreType();
USER_LEVEL adminRestr = USER_LEVEL.SYSTEM_ADMIN;
if (oldSettings.isAdminsAllowed()) {
adminRestr = USER_LEVEL.ADMIN;
}
if (oldSettings.isUsersAllowed()) {
adminRestr = USER_LEVEL.EVERYONE;
}
boolean shouldAcceptAnyCertificate = false;
List<PrnfbButton> newButtons = newArrayList();
for (se.bjurr.prnfb.settings.legacy.PrnfbButton oldButton : oldSettings.getButtons()) {
USER_LEVEL userLevel = USER_LEVEL.SYSTEM_ADMIN;
if (oldButton.getVisibility() == BUTTON_VISIBILITY.ADMIN) {
userLevel = USER_LEVEL.ADMIN;
}
if (oldButton.getVisibility() == BUTTON_VISIBILITY.EVERYONE) {
userLevel = USER_LEVEL.EVERYONE;
}
newButtons.add(
new PrnfbButton(
UUID.randomUUID(),
oldButton.getTitle(),
userLevel,
ON_OR_OFF.off,
null,
null,
"confirmationText",
null));
}
List<PrnfbNotification> newNotifications = newArrayList();
for (se.bjurr.prnfb.settings.legacy.PrnfbNotification oldNotification :
oldSettings.getNotifications()) {
try {
PrnfbNotificationBuilder builder =
prnfbNotificationBuilder() //
.withFilterRegexp(oldNotification.getFilterRegexp().orNull()) //
.withFilterString(oldNotification.getFilterString().orNull()) //
.withInjectionUrl(oldNotification.getInjectionUrl().orNull()) //
.withInjectionUrlRegexp(oldNotification.getInjectionUrlRegexp().orNull()) //
.withMethod(oldNotification.getMethod()) //
.withName(oldNotification.getName()) //
.withPassword(oldNotification.getPassword().orNull()) //
.withPostContent(oldNotification.getPostContent().orNull()) //
.withProxyPassword(oldNotification.getProxyPassword().orNull()) //
.withProxyPort(oldNotification.getProxyPort()) //
.withProxyServer(oldNotification.getProxyServer().orNull()) //
.withProxyUser(oldNotification.getProxyUser().orNull()) //
.withTriggerIfCanMerge(
TRIGGER_IF_MERGE.valueOf(oldNotification.getTriggerIfCanMerge().name())) //
.withUrl(oldNotification.getUrl()) //
.withUser(oldNotification.getUser().orNull());
for (Header h : oldNotification.getHeaders()) {
builder.withHeader(h.getName(), h.getValue());
}
for (PullRequestState t : oldNotification.getTriggerIgnoreStateList()) {
builder.withTriggerIgnoreState(t);
}
for (PrnfbPullRequestAction t : oldNotification.getTriggers()) {
builder.withTrigger(t);
}
newNotifications.add(builder.build());
} catch (ValidationException e) {
this.logger.error("", e);
}
}
return prnfbSettingsBuilder() //
.setPrnfbSettingsData( //
prnfbSettingsDataBuilder() //
.setAdminRestriction(adminRestr) //
.setKeyStore(ks) //
.setKeyStorePassword(ksp) //
.setKeyStoreType(kst) //
.setShouldAcceptAnyCertificate(shouldAcceptAnyCertificate) //
.build()) //
.setButtons(newButtons) //
.setNotifications(newNotifications) //
.build();
}
private Predicate<HasUuid> withUuid(UUID uuid) {
return new Predicate<HasUuid>() {
@Override
public boolean apply(HasUuid input) {
return input.getUuid().equals(uuid);
}
};
}
}