mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-12-26 11:08:13 +01:00
Improve efficiency of action log queries (#3917)
This commit is contained in:
parent
d748817351
commit
9e7a3d26e4
@ -26,6 +26,7 @@
|
||||
package net.luckperms.api;
|
||||
|
||||
import net.luckperms.api.actionlog.ActionLogger;
|
||||
import net.luckperms.api.actionlog.filter.ActionFilterFactory;
|
||||
import net.luckperms.api.context.ContextCalculator;
|
||||
import net.luckperms.api.context.ContextManager;
|
||||
import net.luckperms.api.event.EventBus;
|
||||
@ -267,4 +268,14 @@ public interface LuckPerms {
|
||||
@Internal
|
||||
@NonNull NodeMatcherFactory getNodeMatcherFactory();
|
||||
|
||||
/**
|
||||
* Gets the {@link ActionFilterFactory}.
|
||||
*
|
||||
* @return the action filter factory
|
||||
* @since 5.5
|
||||
*/
|
||||
@Internal
|
||||
@NonNull
|
||||
ActionFilterFactory getActionFilterFactory();
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ package net.luckperms.api.actionlog;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.jetbrains.annotations.ApiStatus.NonExtendable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
@ -35,7 +36,10 @@ import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a logged action.
|
||||
*
|
||||
* <p>API users should not implement this interface directly.</p>
|
||||
*/
|
||||
@NonExtendable
|
||||
public interface Action extends Comparable<Action> {
|
||||
|
||||
/**
|
||||
@ -81,6 +85,7 @@ public interface Action extends Comparable<Action> {
|
||||
/**
|
||||
* Represents the source of an action.
|
||||
*/
|
||||
@NonExtendable
|
||||
interface Source {
|
||||
|
||||
/**
|
||||
@ -102,6 +107,7 @@ public interface Action extends Comparable<Action> {
|
||||
/**
|
||||
* Represents the target of an action.
|
||||
*/
|
||||
@NonExtendable
|
||||
interface Target {
|
||||
|
||||
/**
|
||||
@ -126,7 +132,7 @@ public interface Action extends Comparable<Action> {
|
||||
@NonNull Type getType();
|
||||
|
||||
/**
|
||||
* Represents the type of a {@link Target}.
|
||||
* Represents the type of {@link Target}.
|
||||
*/
|
||||
enum Type {
|
||||
USER, GROUP, TRACK
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package net.luckperms.api.actionlog;
|
||||
|
||||
import net.luckperms.api.actionlog.filter.ActionFilter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
@ -40,7 +41,11 @@ import java.util.UUID;
|
||||
* You can add to the log using the {@link ActionLogger}, and then request an updated copy.</p>
|
||||
*
|
||||
* <p>All methods are thread safe, and return immutable and thread safe collections.</p>
|
||||
*
|
||||
* @deprecated Use {@link ActionLogger#queryActions(ActionFilter)} or
|
||||
* {@link ActionLogger#queryActions(ActionFilter, int, int)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ActionLog {
|
||||
|
||||
/**
|
||||
|
@ -25,9 +25,11 @@
|
||||
|
||||
package net.luckperms.api.actionlog;
|
||||
|
||||
import net.luckperms.api.messaging.MessagingService;
|
||||
import net.luckperms.api.actionlog.filter.ActionFilter;
|
||||
import net.luckperms.api.util.Page;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@ -46,43 +48,78 @@ public interface ActionLogger {
|
||||
* Gets a {@link ActionLog} instance from the plugin storage.
|
||||
*
|
||||
* @return a log instance
|
||||
* @deprecated Use {@link #queryActions(ActionFilter)} or {@link #queryActions(ActionFilter, int, int)} instead. These methods
|
||||
* are more efficient (they don't load the full action log into memory) and allow for pagination.
|
||||
*/
|
||||
@Deprecated
|
||||
@NonNull CompletableFuture<ActionLog> getLog();
|
||||
|
||||
/**
|
||||
* Submits a log entry to the plugin to be handled.
|
||||
* Gets all actions from the action log matching the given {@code filter}.
|
||||
*
|
||||
* <p>This method submits the log to the storage provider and broadcasts
|
||||
* it.</p>
|
||||
* <p>If the filter is {@code null}, all actions will be returned.</p>
|
||||
*
|
||||
* <p>It is therefore roughly equivalent to calling
|
||||
* {@link #submitToStorage(Action)} and {@link #broadcastAction(Action)},
|
||||
* however, using this method is preferred to making the calls individually.</p>
|
||||
* <p>Unlike {@link #queryActions(ActionFilter, int, int)}, this method does not implement any pagination and will return
|
||||
* all entries at once.</p>
|
||||
*
|
||||
* <p>If you want to submit a log entry but don't know which method to pick,
|
||||
* @param filter the filter, optional
|
||||
* @return the actions
|
||||
* @since 5.5
|
||||
*/
|
||||
@NonNull CompletableFuture<List<Action>> queryActions(@NonNull ActionFilter filter);
|
||||
|
||||
/**
|
||||
* Gets a page of actions from the action log matching the given {@code filter}.
|
||||
*
|
||||
* <p>If the filter is {@code null}, all actions will be returned.</p>
|
||||
*
|
||||
* @param filter the filter, optional
|
||||
* @param pageSize the size of the page
|
||||
* @param pageNumber the page number
|
||||
* @return the page of actions
|
||||
* @since 5.5
|
||||
*/
|
||||
@NonNull CompletableFuture<Page<Action>> queryActions(@NonNull ActionFilter filter, int pageSize, int pageNumber);
|
||||
|
||||
/**
|
||||
* Submits a logged action to LuckPerms.
|
||||
*
|
||||
* <p>This method submits the action to the storage provider to be persisted in the action log.
|
||||
* It also broadcasts it to administrator players on the current instance and to admins on other
|
||||
* connected servers if a messaging service is configured.</p>
|
||||
*
|
||||
* <p>It is roughly equivalent to calling
|
||||
* {@link #submitToStorage(Action)} followed by {@link #broadcastAction(Action)},
|
||||
* however using this method is preferred to making the calls individually.</p>
|
||||
*
|
||||
* <p>If you want to submit an action log entry but don't know which method to pick,
|
||||
* use this one.</p>
|
||||
*
|
||||
* @param entry the entry to submit
|
||||
* @return a future which will complete when the action is done
|
||||
* @return a future which will complete when the action is submitted
|
||||
*/
|
||||
@NonNull CompletableFuture<Void> submit(@NonNull Action entry);
|
||||
|
||||
/**
|
||||
* Submits a log entry to the plugins storage handler.
|
||||
* Submits a logged action to LuckPerms and persists it in the storage backend.
|
||||
*
|
||||
* <p>This method does not broadcast the action or send it through the messaging service.</p>
|
||||
*
|
||||
* @param entry the entry to submit
|
||||
* @return a future which will complete when the action is done
|
||||
* @return a future which will complete when the action is submitted
|
||||
*/
|
||||
@NonNull CompletableFuture<Void> submitToStorage(@NonNull Action entry);
|
||||
|
||||
/**
|
||||
* Submits a log entry to the plugins log broadcasting handler.
|
||||
* Submits a logged action to LuckPerms and broadcasts it to administrators.
|
||||
*
|
||||
* <p>If enabled, this method will also dispatch the log entry via the
|
||||
* plugins {@link MessagingService}.</p>
|
||||
* <p>The broadcast is made to administrator players on the current instance
|
||||
* and to admins on other connected servers if a messaging service is configured.</p>
|
||||
*
|
||||
* <p>This method does not save the action to the plugin storage backend.</p>
|
||||
*
|
||||
* @param entry the entry to submit
|
||||
* @return a future which will complete when the action is done
|
||||
* @return a future which will complete when the action is broadcasted
|
||||
*/
|
||||
@NonNull CompletableFuture<Void> broadcastAction(@NonNull Action entry);
|
||||
|
||||
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.luckperms.api.actionlog.filter;
|
||||
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import org.jetbrains.annotations.ApiStatus.NonExtendable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A predicate filter which matches certain {@link Action}s.
|
||||
*
|
||||
* <p>API users should not implement this interface directly.</p>
|
||||
*
|
||||
* @since 5.5
|
||||
*/
|
||||
@NonExtendable
|
||||
public interface ActionFilter extends Predicate<Action> {
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches any action.
|
||||
*
|
||||
* @return the matcher
|
||||
*/
|
||||
static ActionFilter any() {
|
||||
return LuckPermsProvider.get().getActionFilterFactory().any();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions with a specific source user.
|
||||
*
|
||||
* @param uniqueId the source user unique id
|
||||
* @return the matcher
|
||||
*/
|
||||
static ActionFilter source(UUID uniqueId) {
|
||||
return LuckPermsProvider.get().getActionFilterFactory().source(uniqueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions which target a specific user.
|
||||
*
|
||||
* @param uniqueId the target user unique id
|
||||
* @return the matcher
|
||||
*/
|
||||
static ActionFilter user(UUID uniqueId) {
|
||||
return LuckPermsProvider.get().getActionFilterFactory().user(uniqueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions which target a specific group.
|
||||
*
|
||||
* @param name the target group name
|
||||
* @return the matcher
|
||||
*/
|
||||
static ActionFilter group(String name) {
|
||||
return LuckPermsProvider.get().getActionFilterFactory().group(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions which target a specific track.
|
||||
*
|
||||
* @param name the target track name
|
||||
* @return the matcher
|
||||
*/
|
||||
static ActionFilter track(String name) {
|
||||
return LuckPermsProvider.get().getActionFilterFactory().track(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions which contain a specific search query in the source name,
|
||||
* target name or description.
|
||||
*
|
||||
* @param query the search query
|
||||
* @return the matcher
|
||||
*/
|
||||
static ActionFilter search(String query) {
|
||||
return LuckPermsProvider.get().getActionFilterFactory().search(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if the given {@link Action} matches the filter.
|
||||
*
|
||||
* @param action the action to test
|
||||
* @return true if the action matched
|
||||
*/
|
||||
@Override
|
||||
boolean test(Action action);
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.luckperms.api.actionlog.filter;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus.Internal;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A factory which creates {@link ActionFilter}s.
|
||||
*
|
||||
* @since 5.5
|
||||
*/
|
||||
@Internal
|
||||
public interface ActionFilterFactory {
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches any action.
|
||||
*
|
||||
* @return the matcher
|
||||
*/
|
||||
ActionFilter any();
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions with a specific source user.
|
||||
*
|
||||
* @param uniqueId the source user unique id
|
||||
* @return the matcher
|
||||
*/
|
||||
ActionFilter source(UUID uniqueId);
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions which target a specific user.
|
||||
*
|
||||
* @param uniqueId the target user unique id
|
||||
* @return the matcher
|
||||
*/
|
||||
ActionFilter user(UUID uniqueId);
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions which target a specific group.
|
||||
*
|
||||
* @param name the target group name
|
||||
* @return the matcher
|
||||
*/
|
||||
ActionFilter group(String name);
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions which target a specific track.
|
||||
*
|
||||
* @param name the target track name
|
||||
* @return the matcher
|
||||
*/
|
||||
ActionFilter track(String name);
|
||||
|
||||
/**
|
||||
* Gets an {@link ActionFilter} which matches actions which contain a specific search query in the source name,
|
||||
* target name or description.
|
||||
*
|
||||
* @param query the search query
|
||||
* @return the matcher
|
||||
*/
|
||||
ActionFilter search(String query);
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link net.luckperms.api.actionlog.Action} filters.
|
||||
*/
|
||||
package net.luckperms.api.actionlog.filter;
|
@ -26,7 +26,6 @@
|
||||
package net.luckperms.api.event.sync;
|
||||
|
||||
import net.luckperms.api.event.LuckPermsEvent;
|
||||
import net.luckperms.api.event.type.Cancellable;
|
||||
import net.luckperms.api.event.util.Param;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
53
api/src/main/java/net/luckperms/api/util/Page.java
Normal file
53
api/src/main/java/net/luckperms/api/util/Page.java
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.luckperms.api.util;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a page of entries.
|
||||
*
|
||||
* @since 5.5
|
||||
*/
|
||||
public interface Page<T> {
|
||||
|
||||
/**
|
||||
* Gets the entries on this page.
|
||||
*
|
||||
* @return the entries
|
||||
*/
|
||||
@NonNull List<T> entries();
|
||||
|
||||
/**
|
||||
* Gets the total/overall number of entries (not just the number of entries on this page).
|
||||
*
|
||||
* @return the total number of entries
|
||||
*/
|
||||
int overallSize();
|
||||
|
||||
}
|
@ -37,7 +37,6 @@ import org.bukkit.event.server.PluginEnableEvent;
|
||||
import org.bukkit.event.server.RemoteServerCommandEvent;
|
||||
import org.bukkit.event.server.ServerCommandEvent;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class BukkitPlatformListener implements Listener {
|
||||
|
@ -4,7 +4,11 @@ plugins {
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform {}
|
||||
useJUnitPlatform {
|
||||
if (!project.hasProperty('dockerTests')) {
|
||||
excludeTags 'docker'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
@ -15,9 +19,14 @@ dependencies {
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1'
|
||||
testImplementation "org.testcontainers:junit-jupiter:1.19.8"
|
||||
testImplementation 'org.mockito:mockito-core:5.11.0'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
|
||||
testImplementation 'com.h2database:h2:2.1.214'
|
||||
testImplementation 'org.mongodb:mongodb-driver-legacy:4.5.0'
|
||||
testImplementation 'org.spongepowered:configurate-yaml:3.7.2'
|
||||
testImplementation 'org.spongepowered:configurate-hocon:3.7.2'
|
||||
testImplementation 'me.lucko.configurate:configurate-toml:3.7'
|
||||
|
||||
api project(':api')
|
||||
api 'org.checkerframework:checker-qual:3.12.0'
|
||||
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.actionlog;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import me.lucko.luckperms.common.util.ImmutableCollectors;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Log {
|
||||
private static final Log EMPTY = new Log(ImmutableList.of());
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static Log empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
private final SortedSet<LoggedAction> content;
|
||||
|
||||
Log(List<LoggedAction> content) {
|
||||
this.content = ImmutableSortedSet.copyOf(content);
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getContent(UUID actor) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getSource().getUniqueId().equals(actor))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getUserHistory(UUID uniqueId) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.USER)
|
||||
.filter(e -> e.getTarget().getUniqueId().isPresent() && e.getTarget().getUniqueId().get().equals(uniqueId))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getGroupHistory(String name) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.GROUP)
|
||||
.filter(e -> e.getTarget().getName().equals(name))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getTrackHistory(String name) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.TRACK)
|
||||
.filter(e -> e.getTarget().getName().equals(name))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public SortedSet<LoggedAction> getSearch(String query) {
|
||||
return this.content.stream()
|
||||
.filter(e -> e.matchesSearch(query))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final List<LoggedAction> content = new ArrayList<>();
|
||||
|
||||
public Builder add(LoggedAction e) {
|
||||
this.content.add(e);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Log build() {
|
||||
if (this.content.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
return new Log(this.content);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -29,12 +29,14 @@ import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.commands.log.LogNotify;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.messaging.InternalMessagingService;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import net.luckperms.api.event.log.LogBroadcastEvent;
|
||||
import net.luckperms.api.event.log.LogNotifyEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LogDispatcher {
|
||||
@ -64,7 +66,12 @@ public class LogDispatcher {
|
||||
return !this.plugin.getEventDispatcher().dispatchLogBroadcast(cancelled, entry, origin);
|
||||
}
|
||||
|
||||
private void broadcast(LoggedAction entry, LogNotifyEvent.Origin origin, Sender sender) {
|
||||
// broadcast the entry to online players
|
||||
private void broadcast(LoggedAction entry, LogBroadcastEvent.Origin broadcastOrigin, LogNotifyEvent.Origin origin, Sender sender) {
|
||||
if (!shouldBroadcast(entry, broadcastOrigin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.getOnlineSenders()
|
||||
.filter(CommandPermission.LOG_NOTIFY::isAuthorized)
|
||||
.filter(s -> {
|
||||
@ -74,41 +81,46 @@ public class LogDispatcher {
|
||||
.forEach(s -> Message.LOG.send(s, entry));
|
||||
}
|
||||
|
||||
public void dispatch(LoggedAction entry, Sender sender) {
|
||||
// log the entry to storage
|
||||
public CompletableFuture<Void> logToStorage(LoggedAction entry) {
|
||||
if (!this.plugin.getEventDispatcher().dispatchLogPublish(false, entry)) {
|
||||
this.plugin.getStorage().logAction(entry);
|
||||
return this.plugin.getStorage().logAction(entry);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
|
||||
this.plugin.getMessagingService().ifPresent(service -> service.pushLog(entry));
|
||||
|
||||
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.LOCAL)) {
|
||||
broadcast(entry, LogNotifyEvent.Origin.LOCAL, sender);
|
||||
// log the entry to messaging
|
||||
public CompletableFuture<Void> logToMessaging(LoggedAction entry) {
|
||||
InternalMessagingService messagingService = this.plugin.getMessagingService().orElse(null);
|
||||
if (messagingService != null) {
|
||||
return messagingService.pushLog(entry);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
|
||||
// log the entry to storage and messaging, and broadcast it to online players
|
||||
private CompletableFuture<Void> dispatch(LoggedAction entry, Sender sender, LogBroadcastEvent.Origin broadcastOrigin, LogNotifyEvent.Origin origin) {
|
||||
CompletableFuture<Void> storageFuture = logToStorage(entry);
|
||||
CompletableFuture<Void> messagingFuture = logToMessaging(entry);
|
||||
broadcast(entry, broadcastOrigin, origin, sender);
|
||||
return CompletableFuture.allOf(storageFuture, messagingFuture);
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> dispatch(LoggedAction entry, Sender sender) {
|
||||
return dispatch(entry, sender, LogBroadcastEvent.Origin.LOCAL, LogNotifyEvent.Origin.LOCAL);
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> dispatchFromApi(LoggedAction entry) {
|
||||
return dispatch(entry, null, LogBroadcastEvent.Origin.LOCAL_API, LogNotifyEvent.Origin.LOCAL_API);
|
||||
}
|
||||
|
||||
public void broadcastFromApi(LoggedAction entry) {
|
||||
this.plugin.getMessagingService().ifPresent(extendedMessagingService -> extendedMessagingService.pushLog(entry));
|
||||
|
||||
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.LOCAL_API)) {
|
||||
broadcast(entry, LogNotifyEvent.Origin.LOCAL_API, null);
|
||||
}
|
||||
broadcast(entry, LogBroadcastEvent.Origin.LOCAL_API, LogNotifyEvent.Origin.LOCAL_API, null);
|
||||
}
|
||||
|
||||
public void dispatchFromApi(LoggedAction entry) {
|
||||
if (!this.plugin.getEventDispatcher().dispatchLogPublish(false, entry)) {
|
||||
try {
|
||||
this.plugin.getStorage().logAction(entry).get();
|
||||
} catch (Exception e) {
|
||||
this.plugin.getLogger().warn("Error whilst storing action", e);
|
||||
}
|
||||
}
|
||||
|
||||
broadcastFromApi(entry);
|
||||
}
|
||||
|
||||
public void dispatchFromRemote(LoggedAction entry) {
|
||||
if (shouldBroadcast(entry, LogBroadcastEvent.Origin.REMOTE)) {
|
||||
broadcast(entry, LogNotifyEvent.Origin.REMOTE, null);
|
||||
}
|
||||
public void broadcastFromRemote(LoggedAction entry) {
|
||||
broadcast(entry, LogBroadcastEvent.Origin.REMOTE, LogNotifyEvent.Origin.REMOTE, null);
|
||||
}
|
||||
}
|
||||
|
@ -23,54 +23,50 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.util;
|
||||
package me.lucko.luckperms.common.actionlog;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A simple pagination utility
|
||||
*
|
||||
* @param <T> the element type
|
||||
*/
|
||||
public class Paginated<T> {
|
||||
private final List<T> content;
|
||||
|
||||
public Paginated(Collection<T> content) {
|
||||
this.content = ImmutableList.copyOf(content);
|
||||
public class LogPage {
|
||||
public static LogPage of(List<LoggedAction> content, @Nullable PageParameters params, int totalEntries) {
|
||||
return new LogPage(content, params, totalEntries);
|
||||
}
|
||||
|
||||
public List<T> getContent() {
|
||||
private final List<LoggedAction> content;
|
||||
private final @Nullable PageParameters params;
|
||||
private final int totalEntries;
|
||||
|
||||
LogPage(List<LoggedAction> content, @Nullable PageParameters params, int totalEntries) {
|
||||
this.content = ImmutableList.copyOf(content);
|
||||
this.params = params;
|
||||
this.totalEntries = totalEntries;
|
||||
}
|
||||
|
||||
public List<LoggedAction> getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
public int getMaxPages(int entriesPerPage) {
|
||||
return (int) Math.ceil((double) this.content.size() / (double) entriesPerPage);
|
||||
public List<Entry<LoggedAction>> getNumberedContent() {
|
||||
int startIndex = this.params != null
|
||||
? this.params.pageSize() * (this.params.pageNumber() - 1)
|
||||
: 0;
|
||||
|
||||
List<Entry<LoggedAction>> numberedContent = new ArrayList<>();
|
||||
for (int i = 0; i < this.content.size(); i++) {
|
||||
int index = startIndex + i + 1;
|
||||
numberedContent.add(new Entry<>(index, this.content.get(i)));
|
||||
}
|
||||
return numberedContent;
|
||||
}
|
||||
|
||||
public List<Entry<T>> getPage(int pageNo, int pageSize) {
|
||||
if (pageNo < 1) {
|
||||
throw new IllegalArgumentException("pageNo cannot be less than 1: " + pageNo);
|
||||
}
|
||||
|
||||
int first = (pageNo - 1) * pageSize;
|
||||
if (this.content.size() <= first) {
|
||||
throw new IllegalStateException("Content does not contain that many elements. (requested page: " + pageNo +
|
||||
", page size: " + pageSize + ", page first index: " + first + ", content size: " + this.content.size() + ")");
|
||||
}
|
||||
|
||||
int last = first + pageSize - 1;
|
||||
List<Entry<T>> out = new ArrayList<>(pageSize);
|
||||
|
||||
for (int i = first; i <= last && i < this.content.size(); i++) {
|
||||
out.add(new Entry<>(i + 1, this.content.get(i)));
|
||||
}
|
||||
|
||||
return out;
|
||||
public int getTotalEntries() {
|
||||
return this.totalEntries;
|
||||
}
|
||||
|
||||
public static final class Entry<T> {
|
@ -26,6 +26,7 @@
|
||||
package me.lucko.luckperms.common.actionlog;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.HolderType;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
@ -44,10 +45,10 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* An implementation of {@link Action} and {@link Action.Builder},
|
||||
@ -123,15 +124,11 @@ public class LoggedAction implements Action {
|
||||
return ActionComparator.INSTANCE.compare(this, other);
|
||||
}
|
||||
|
||||
public boolean matchesSearch(String query) {
|
||||
query = Objects.requireNonNull(query, "query").toLowerCase(Locale.ROOT);
|
||||
return this.source.name.toLowerCase(Locale.ROOT).contains(query) ||
|
||||
this.target.name.toLowerCase(Locale.ROOT).contains(query) ||
|
||||
this.description.toLowerCase(Locale.ROOT).contains(query);
|
||||
}
|
||||
|
||||
public void submit(LuckPermsPlugin plugin, Sender sender) {
|
||||
plugin.getLogDispatcher().dispatch(this, sender);
|
||||
CompletableFuture<Void> future = plugin.getLogDispatcher().dispatch(this, sender);
|
||||
if (plugin.getConfiguration().get(ConfigKeys.LOG_SYNCHRONOUSLY_IN_COMMANDS)) {
|
||||
future.join();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -393,14 +390,14 @@ public class LoggedAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
public static char getTypeCharacter(Target.Type type) {
|
||||
public static String getTypeString(Target.Type type) {
|
||||
switch (type) {
|
||||
case USER:
|
||||
return 'U';
|
||||
return "U";
|
||||
case GROUP:
|
||||
return 'G';
|
||||
return "G";
|
||||
case TRACK:
|
||||
return 'T';
|
||||
return "T";
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.actionlog.filter;
|
||||
|
||||
import me.lucko.luckperms.common.filter.FilterField;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ActionFields {
|
||||
|
||||
public static final FilterField<Action, UUID> SOURCE_UNIQUE_ID = FilterField.named(
|
||||
"SOURCE_UNIQUE_ID",
|
||||
action -> action.getSource().getUniqueId()
|
||||
);
|
||||
public static final FilterField<Action, String> SOURCE_NAME = FilterField.named(
|
||||
"SOURCE_NAME",
|
||||
action -> action.getSource().getName()
|
||||
);
|
||||
public static final FilterField<Action, Action.Target.Type> TARGET_TYPE = FilterField.named(
|
||||
"TARGET_TYPE",
|
||||
action -> action.getTarget().getType()
|
||||
);
|
||||
public static final FilterField<Action, UUID> TARGET_UNIQUE_ID = FilterField.named(
|
||||
"TARGET_UNIQUE_ID",
|
||||
action -> action.getTarget().getUniqueId().orElse(null)
|
||||
);
|
||||
public static final FilterField<Action, String> TARGET_NAME = FilterField.named(
|
||||
"TARGET_NAME",
|
||||
action -> action.getTarget().getName()
|
||||
);
|
||||
public static final FilterField<Action, String> DESCRIPTION = FilterField.named(
|
||||
"DESCRIPTION",
|
||||
action -> action.getDescription()
|
||||
);
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.actionlog.filter;
|
||||
|
||||
import me.lucko.luckperms.common.filter.FilterField;
|
||||
import me.lucko.luckperms.common.filter.mongo.FilterMongoBuilder;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ActionFilterMongoBuilder extends FilterMongoBuilder<Action> {
|
||||
public static final ActionFilterMongoBuilder INSTANCE = new ActionFilterMongoBuilder();
|
||||
|
||||
private ActionFilterMongoBuilder() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mapFieldName(FilterField<Action, ?> field) {
|
||||
if (field == ActionFields.SOURCE_UNIQUE_ID) {
|
||||
return "source.uniqueId";
|
||||
} else if (field == ActionFields.SOURCE_NAME) {
|
||||
return "source.name";
|
||||
} else if (field == ActionFields.TARGET_TYPE) {
|
||||
return "target.type";
|
||||
} else if (field == ActionFields.TARGET_UNIQUE_ID) {
|
||||
return "target.uniqueId";
|
||||
} else if (field == ActionFields.TARGET_NAME) {
|
||||
return "target.name";
|
||||
} else if (field == ActionFields.DESCRIPTION) {
|
||||
return "description";
|
||||
}
|
||||
throw new AssertionError(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object mapConstraintValue(Object value) {
|
||||
if (value instanceof String | value instanceof UUID) {
|
||||
return value;
|
||||
} else if (value instanceof Action.Target.Type) {
|
||||
return ((Action.Target.Type) value).name();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to map value with type: " + value.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.actionlog.filter;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.filter.FilterField;
|
||||
import me.lucko.luckperms.common.filter.sql.FilterSqlBuilder;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ActionFilterSqlBuilder extends FilterSqlBuilder<Action> {
|
||||
|
||||
@Override
|
||||
public void visitFieldName(FilterField<Action, ?> field) {
|
||||
if (field == ActionFields.SOURCE_UNIQUE_ID) {
|
||||
this.builder.append("actor_uuid");
|
||||
} else if (field == ActionFields.SOURCE_NAME) {
|
||||
this.builder.append("actor_name");
|
||||
} else if (field == ActionFields.TARGET_TYPE) {
|
||||
this.builder.append("type");
|
||||
} else if (field == ActionFields.TARGET_UNIQUE_ID) {
|
||||
this.builder.append("acted_uuid");
|
||||
} else if (field == ActionFields.TARGET_NAME) {
|
||||
this.builder.append("acted_name");
|
||||
} else if (field == ActionFields.DESCRIPTION) {
|
||||
this.builder.append("action");
|
||||
} else {
|
||||
throw new AssertionError(field);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstraintValue(Object value) {
|
||||
if (value instanceof String) {
|
||||
this.builder.variable(((String) value));
|
||||
} else if (value instanceof UUID) {
|
||||
this.builder.variable(value.toString());
|
||||
} else if (value instanceof Action.Target.Type) {
|
||||
this.builder.variable(LoggedAction.getTypeString((Action.Target.Type) value));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to write value with type: " + value.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.actionlog.filter;
|
||||
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.filter.ConstraintFactory;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.actionlog.Action.Target;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class ActionFilters {
|
||||
private ActionFilters() {}
|
||||
|
||||
// all actions
|
||||
public static FilterList<Action> all() {
|
||||
return FilterList.empty();
|
||||
}
|
||||
|
||||
// all actions performed by a given source (actor)
|
||||
public static FilterList<Action> source(UUID uniqueId) {
|
||||
return FilterList.and(
|
||||
ActionFields.SOURCE_UNIQUE_ID.isEqualTo(uniqueId, ConstraintFactory.UUIDS)
|
||||
);
|
||||
}
|
||||
|
||||
// all actions affecting a given user
|
||||
public static FilterList<Action> user(UUID uniqueId) {
|
||||
return FilterList.and(
|
||||
ActionFields.TARGET_TYPE.isEqualTo(Target.Type.USER, TARGET_TYPE_CONSTRAINT_FACTORY),
|
||||
ActionFields.TARGET_UNIQUE_ID.isEqualTo(uniqueId, ConstraintFactory.UUIDS)
|
||||
);
|
||||
}
|
||||
|
||||
// all actions affecting a given group
|
||||
public static FilterList<Action> group(String name) {
|
||||
return FilterList.and(
|
||||
ActionFields.TARGET_TYPE.isEqualTo(Target.Type.GROUP, TARGET_TYPE_CONSTRAINT_FACTORY),
|
||||
ActionFields.TARGET_NAME.isEqualTo(name, ConstraintFactory.STRINGS)
|
||||
);
|
||||
}
|
||||
|
||||
// all actions affecting a given track
|
||||
public static FilterList<Action> track(String name) {
|
||||
return FilterList.and(
|
||||
ActionFields.TARGET_TYPE.isEqualTo(Target.Type.TRACK, TARGET_TYPE_CONSTRAINT_FACTORY),
|
||||
ActionFields.TARGET_NAME.isEqualTo(name, ConstraintFactory.STRINGS)
|
||||
);
|
||||
}
|
||||
|
||||
// all actions matching the given search query
|
||||
public static FilterList<Action> search(String query) {
|
||||
return FilterList.or(
|
||||
ActionFields.SOURCE_NAME.isSimilarTo("%" + query + "%", ConstraintFactory.STRINGS),
|
||||
ActionFields.TARGET_NAME.isSimilarTo("%" + query + "%", ConstraintFactory.STRINGS),
|
||||
ActionFields.DESCRIPTION.isSimilarTo("%" + query + "%", ConstraintFactory.STRINGS)
|
||||
);
|
||||
}
|
||||
|
||||
private static final ConstraintFactory<Target.Type> TARGET_TYPE_CONSTRAINT_FACTORY = new ConstraintFactory<Target.Type>() {
|
||||
@Override
|
||||
public Predicate<Target.Type> equal(Target.Type value) {
|
||||
return value::equals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<Target.Type> notEqual(Target.Type value) {
|
||||
return string -> !value.equals(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<Target.Type> similar(Target.Type value) {
|
||||
Pattern pattern = Comparison.compilePatternForLikeSyntax(value.toString());
|
||||
return type -> pattern.matcher(type.toString()).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<Target.Type> notSimilar(Target.Type value) {
|
||||
Pattern pattern = Comparison.compilePatternForLikeSyntax(value.toString());
|
||||
return type -> !pattern.matcher(type.toString()).matches();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
|
||||
package me.lucko.luckperms.common.api;
|
||||
|
||||
import me.lucko.luckperms.common.api.implementation.ApiActionFilterFactory;
|
||||
import me.lucko.luckperms.common.api.implementation.ApiActionLogger;
|
||||
import me.lucko.luckperms.common.api.implementation.ApiContextManager;
|
||||
import me.lucko.luckperms.common.api.implementation.ApiGroupManager;
|
||||
@ -47,6 +48,7 @@ import me.lucko.luckperms.common.plugin.logging.PluginLogger;
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.actionlog.ActionLogger;
|
||||
import net.luckperms.api.actionlog.filter.ActionFilterFactory;
|
||||
import net.luckperms.api.context.ContextManager;
|
||||
import net.luckperms.api.messaging.MessagingService;
|
||||
import net.luckperms.api.messenger.MessengerProvider;
|
||||
@ -225,4 +227,8 @@ public class LuckPermsApiProvider implements LuckPerms {
|
||||
return ApiNodeMatcherFactory.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ActionFilterFactory getActionFilterFactory() {
|
||||
return ApiActionFilterFactory.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.api.implementation;
|
||||
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.actionlog.filter.ActionFilter;
|
||||
|
||||
public class ApiActionFilter implements ActionFilter {
|
||||
private final FilterList<Action> filter;
|
||||
|
||||
public ApiActionFilter(FilterList<Action> filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Action action) {
|
||||
return this.filter.evaluate(action);
|
||||
}
|
||||
|
||||
public FilterList<Action> getFilter() {
|
||||
return this.filter;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.api.implementation;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import net.luckperms.api.actionlog.filter.ActionFilter;
|
||||
import net.luckperms.api.actionlog.filter.ActionFilterFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ApiActionFilterFactory implements ActionFilterFactory {
|
||||
public static final ApiActionFilterFactory INSTANCE = new ApiActionFilterFactory();
|
||||
|
||||
private ApiActionFilterFactory() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFilter any() {
|
||||
return new ApiActionFilter(ActionFilters.all());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFilter source(UUID uniqueId) {
|
||||
Objects.requireNonNull(uniqueId, "uniqueId");
|
||||
return new ApiActionFilter(ActionFilters.source(uniqueId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFilter user(UUID uniqueId) {
|
||||
Objects.requireNonNull(uniqueId, "uniqueId");
|
||||
return new ApiActionFilter(ActionFilters.user(uniqueId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFilter group(String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
return new ApiActionFilter(ActionFilters.group(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFilter track(String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
return new ApiActionFilter(ActionFilters.track(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFilter search(String query) {
|
||||
Objects.requireNonNull(query, "query");
|
||||
return new ApiActionFilter(ActionFilters.search(query));
|
||||
}
|
||||
|
||||
}
|
@ -25,50 +25,63 @@
|
||||
|
||||
package me.lucko.luckperms.common.api.implementation;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.api.ApiUtils;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.util.ImmutableCollectors;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.actionlog.ActionLog;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.SortedSet;
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Deprecated
|
||||
public class ApiActionLog implements ActionLog {
|
||||
private final Log handle;
|
||||
private final SortedSet<Action> content;
|
||||
|
||||
public ApiActionLog(Log handle) {
|
||||
this.handle = handle;
|
||||
public ApiActionLog(List<LoggedAction> content) {
|
||||
this.content = ImmutableSortedSet.copyOf(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SortedSet<Action> getContent() {
|
||||
return (SortedSet) this.handle.getContent();
|
||||
return this.content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SortedSet<Action> getContent(@NonNull UUID actor) {
|
||||
Objects.requireNonNull(actor, "actor");
|
||||
return (SortedSet) this.handle.getContent(actor);
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getSource().getUniqueId().equals(actor))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SortedSet<Action> getUserHistory(@NonNull UUID uniqueId) {
|
||||
Objects.requireNonNull(uniqueId, "uuid");
|
||||
return (SortedSet) this.handle.getUserHistory(uniqueId);
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.USER)
|
||||
.filter(e -> e.getTarget().getUniqueId().isPresent() && e.getTarget().getUniqueId().get().equals(uniqueId))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SortedSet<Action> getGroupHistory(@NonNull String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
return (SortedSet) this.handle.getGroupHistory(ApiUtils.checkName(name));
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.GROUP)
|
||||
.filter(e -> e.getTarget().getName().equals(name))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SortedSet<Action> getTrackHistory(@NonNull String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
return (SortedSet) this.handle.getTrackHistory(ApiUtils.checkName(name));
|
||||
return this.content.stream()
|
||||
.filter(e -> e.getTarget().getType() == Action.Target.Type.TRACK)
|
||||
.filter(e -> e.getTarget().getName().equals(name))
|
||||
.collect(ImmutableCollectors.toSortedSet());
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,22 @@
|
||||
|
||||
package me.lucko.luckperms.common.api.implementation;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.LogDispatcher;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.actionlog.ActionLog;
|
||||
import net.luckperms.api.actionlog.ActionLogger;
|
||||
import net.luckperms.api.actionlog.filter.ActionFilter;
|
||||
import net.luckperms.api.util.Page;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ApiActionLogger implements ActionLogger {
|
||||
@ -47,22 +56,66 @@ public class ApiActionLogger implements ActionLogger {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public @NonNull CompletableFuture<ActionLog> getLog() {
|
||||
return this.plugin.getStorage().getLog().thenApply(ApiActionLog::new);
|
||||
return this.plugin.getStorage().getLogPage(ActionFilters.all(), null)
|
||||
.thenApply(result -> new ApiActionLog(result.getContent()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<List<Action>> queryActions(@NonNull ActionFilter filter) {
|
||||
return this.plugin.getStorage().getLogPage(getFilterList(filter), null).thenApply(ActionPage::new).thenApply(Page::entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Page<Action>> queryActions(@NonNull ActionFilter filter, int pageSize, int pageNumber) {
|
||||
return this.plugin.getStorage().getLogPage(getFilterList(filter), new PageParameters(pageSize, pageNumber)).thenApply(ActionPage::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Void> submit(@NonNull Action entry) {
|
||||
return CompletableFuture.runAsync(() -> this.plugin.getLogDispatcher().dispatchFromApi((LoggedAction) entry), this.plugin.getBootstrap().getScheduler().async());
|
||||
return this.plugin.getLogDispatcher().dispatchFromApi((LoggedAction) entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Void> submitToStorage(@NonNull Action entry) {
|
||||
return this.plugin.getStorage().logAction(entry);
|
||||
return this.plugin.getLogDispatcher().logToStorage((LoggedAction) entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull CompletableFuture<Void> broadcastAction(@NonNull Action entry) {
|
||||
return CompletableFuture.runAsync(() -> this.plugin.getLogDispatcher().broadcastFromApi((LoggedAction) entry), this.plugin.getBootstrap().getScheduler().async());
|
||||
LogDispatcher dispatcher = this.plugin.getLogDispatcher();
|
||||
|
||||
CompletableFuture<Void> messagingFuture = dispatcher.logToStorage(((LoggedAction) entry));
|
||||
dispatcher.broadcastFromApi(((LoggedAction) entry));
|
||||
return messagingFuture;
|
||||
}
|
||||
|
||||
private static FilterList<Action> getFilterList(ActionFilter filter) {
|
||||
Objects.requireNonNull(filter, "filter");
|
||||
if (filter instanceof ApiActionFilter) {
|
||||
return ((ApiActionFilter) filter).getFilter();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown filter type: " + filter.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ActionPage implements Page<Action> {
|
||||
private final LogPage page;
|
||||
|
||||
private ActionPage(LogPage page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public @NonNull List<Action> entries() {
|
||||
return (List) this.page.getContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int overallSize() {
|
||||
return this.page.getTotalEntries();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,14 +25,13 @@
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.action.Action;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.Query;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.BulkUpdateAction;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.model.HolderType;
|
||||
import net.luckperms.api.node.Node;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@ -46,35 +45,30 @@ public final class BulkUpdate {
|
||||
private final DataType dataType;
|
||||
|
||||
// the action to apply to the data which matches the constraints
|
||||
private final Action action;
|
||||
private final BulkUpdateAction action;
|
||||
|
||||
// a set of constraints which data must match to be acted upon
|
||||
private final List<Query> queries;
|
||||
// a set of filters which data must match to be acted upon
|
||||
private final FilterList<Node> filters;
|
||||
|
||||
// update statistics of the operation (number of nodes, users and groups affected)
|
||||
private final BulkUpdateStatistics statistics = new BulkUpdateStatistics();
|
||||
private final boolean trackStatistics;
|
||||
|
||||
public BulkUpdate(DataType dataType, Action action, List<Query> queries, boolean trackStatistics) {
|
||||
public BulkUpdate(DataType dataType, BulkUpdateAction action, FilterList<Node> filters, boolean trackStatistics) {
|
||||
this.dataType = dataType;
|
||||
this.action = action;
|
||||
this.queries = queries;
|
||||
this.filters = filters;
|
||||
this.trackStatistics = trackStatistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a Node instance satisfies the constrints of this query
|
||||
* Check to see if a Node instance satisfies the constraints of this query
|
||||
*
|
||||
* @param node the node to check
|
||||
* @return true if satisfied
|
||||
*/
|
||||
public boolean satisfiesConstraints(Node node) {
|
||||
for (Query query : this.queries) {
|
||||
if (!query.isSatisfiedBy(node)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
public boolean satisfiesFilters(Node node) {
|
||||
return this.filters.evaluate(node);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +78,7 @@ public final class BulkUpdate {
|
||||
* @return the transformed node, or null if the node should be deleted
|
||||
*/
|
||||
private Node apply(Node node) {
|
||||
if (!satisfiesConstraints(node)) {
|
||||
if (!satisfiesFilters(node)) {
|
||||
return node; // make no change
|
||||
}
|
||||
|
||||
@ -129,63 +123,16 @@ public final class BulkUpdate {
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this {@link BulkUpdate} to SQL syntax
|
||||
*
|
||||
* @return this query in SQL form
|
||||
*/
|
||||
public PreparedStatementBuilder buildAsSql() {
|
||||
// DELETE FROM {table} WHERE ...
|
||||
// UPDATE {table} SET ... WHERE ...
|
||||
|
||||
PreparedStatementBuilder builder = new PreparedStatementBuilder();
|
||||
|
||||
// add the action
|
||||
// (DELETE FROM or UPDATE)
|
||||
this.action.appendSql(builder);
|
||||
|
||||
return appendConstraintsAsSql(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the constraints of this {@link BulkUpdate} to the provided statement builder in SQL syntax
|
||||
*
|
||||
* @param builder the statement builder to append the constraints to
|
||||
* @return the same statement builder provided as input
|
||||
*/
|
||||
public PreparedStatementBuilder appendConstraintsAsSql(PreparedStatementBuilder builder) {
|
||||
|
||||
// if there are no constraints, just return without a WHERE clause
|
||||
if (this.queries.isEmpty()) {
|
||||
return builder;
|
||||
}
|
||||
|
||||
// append constraints
|
||||
builder.append(" WHERE");
|
||||
for (int i = 0; i < this.queries.size(); i++) {
|
||||
Query query = this.queries.get(i);
|
||||
|
||||
builder.append(" ");
|
||||
if (i != 0) {
|
||||
builder.append("AND ");
|
||||
}
|
||||
|
||||
query.appendSql(builder);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public DataType getDataType() {
|
||||
return this.dataType;
|
||||
}
|
||||
|
||||
public Action getAction() {
|
||||
public BulkUpdateAction getAction() {
|
||||
return this.action;
|
||||
}
|
||||
|
||||
public List<Query> getQueries() {
|
||||
return this.queries;
|
||||
public FilterList<Node> getFilters() {
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
public boolean isTrackingStatistics() {
|
||||
@ -204,12 +151,12 @@ public final class BulkUpdate {
|
||||
|
||||
return this.getDataType() == that.getDataType() &&
|
||||
Objects.equals(this.getAction(), that.getAction()) &&
|
||||
Objects.equals(this.getQueries(), that.getQueries());
|
||||
Objects.equals(this.getFilters(), that.getFilters());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getDataType(), getAction(), getQueries(), isTrackingStatistics());
|
||||
return Objects.hash(getDataType(), getAction(), getFilters(), isTrackingStatistics());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -217,7 +164,7 @@ public final class BulkUpdate {
|
||||
return "BulkUpdate(" +
|
||||
"dataType=" + this.getDataType() + ", " +
|
||||
"action=" + this.getAction() + ", " +
|
||||
"constraints=" + this.getQueries() + ", " +
|
||||
"constraints=" + this.getFilters() + ", " +
|
||||
"trackStatistics=" + this.isTrackingStatistics() + ")";
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,12 @@
|
||||
package me.lucko.luckperms.common.bulkupdate;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.Action;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.Query;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.BulkUpdateAction;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.filter.ConstraintFactory;
|
||||
import me.lucko.luckperms.common.filter.Filter;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import net.luckperms.api.node.Node;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
@ -45,18 +49,18 @@ public class BulkUpdateBuilder {
|
||||
private DataType dataType = DataType.ALL;
|
||||
|
||||
// the action to apply to the data which matches the constraints
|
||||
private Action action = null;
|
||||
private BulkUpdateAction action = null;
|
||||
|
||||
// should the operation count the number of affected nodes, users and groups
|
||||
private boolean trackStatistics = false;
|
||||
|
||||
// a set of constraints which data must match to be acted upon
|
||||
private final Set<Query> queries = new LinkedHashSet<>();
|
||||
// a set of filters which data must match to be acted upon
|
||||
private final Set<Filter<Node, String>> filters = new LinkedHashSet<>();
|
||||
|
||||
private BulkUpdateBuilder() {
|
||||
}
|
||||
|
||||
public BulkUpdateBuilder action(Action action) {
|
||||
public BulkUpdateBuilder action(BulkUpdateAction action) {
|
||||
this.action = action;
|
||||
return this;
|
||||
}
|
||||
@ -71,8 +75,8 @@ public class BulkUpdateBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public BulkUpdateBuilder query(Query query) {
|
||||
this.queries.add(query);
|
||||
public BulkUpdateBuilder filter(BulkUpdateField field, Comparison comparison, String value) {
|
||||
this.filters.add(new Filter<>(field, ConstraintFactory.STRINGS.build(comparison, value)));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -81,7 +85,8 @@ public class BulkUpdateBuilder {
|
||||
throw new IllegalStateException("no action specified");
|
||||
}
|
||||
|
||||
return new BulkUpdate(this.dataType, this.action, ImmutableList.copyOf(this.queries), this.trackStatistics);
|
||||
FilterList<Node> filters = new FilterList<>(FilterList.LogicalOperator.AND, ImmutableList.copyOf(this.filters));
|
||||
return new BulkUpdate(this.dataType, this.action, filters, this.trackStatistics);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -89,7 +94,7 @@ public class BulkUpdateBuilder {
|
||||
return "BulkUpdateBuilder(" +
|
||||
"dataType=" + this.dataType + ", " +
|
||||
"action=" + this.action + ", " +
|
||||
"constraints=" + this.queries + ", " +
|
||||
"constraints=" + this.filters + ", " +
|
||||
"trackStatistics=" + this.trackStatistics + ")";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate;
|
||||
|
||||
import me.lucko.luckperms.common.filter.FilterField;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.node.Node;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Represents a field being used in a bulk update
|
||||
*/
|
||||
public enum BulkUpdateField implements FilterField<Node, String> {
|
||||
|
||||
PERMISSION {
|
||||
@Override
|
||||
public String getValue(Node node) {
|
||||
return node.getKey();
|
||||
}
|
||||
},
|
||||
|
||||
SERVER {
|
||||
@Override
|
||||
public String getValue(Node node) {
|
||||
return node.getContexts().getAnyValue(DefaultContextKeys.SERVER_KEY).orElse("global");
|
||||
}
|
||||
},
|
||||
|
||||
WORLD {
|
||||
@Override
|
||||
public String getValue(Node node) {
|
||||
return node.getContexts().getAnyValue(DefaultContextKeys.WORLD_KEY).orElse("global");
|
||||
}
|
||||
};
|
||||
|
||||
public static BulkUpdateField of(String s) {
|
||||
try {
|
||||
return valueOf(s.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.action.BulkUpdateAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
|
||||
import me.lucko.luckperms.common.filter.FilterField;
|
||||
import me.lucko.luckperms.common.filter.sql.FilterSqlBuilder;
|
||||
import net.luckperms.api.node.Node;
|
||||
|
||||
public class BulkUpdateSqlBuilder extends FilterSqlBuilder<Node> {
|
||||
|
||||
public void visit(BulkUpdate update) {
|
||||
visit(update.getAction());
|
||||
visit(update.getFilters());
|
||||
}
|
||||
|
||||
public void visit(BulkUpdateAction action) {
|
||||
if (action instanceof UpdateAction) {
|
||||
visit(((UpdateAction) action));
|
||||
} else if (action instanceof DeleteAction) {
|
||||
visit(((DeleteAction) action));
|
||||
} else {
|
||||
throw new UnsupportedOperationException(action.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(UpdateAction action) {
|
||||
this.builder.append("UPDATE {table} SET ");
|
||||
visitFieldName(action.getField());
|
||||
this.builder.append("=");
|
||||
this.builder.variable(action.getNewValue());
|
||||
}
|
||||
|
||||
public void visit(DeleteAction action) {
|
||||
this.builder.append("DELETE FROM {table}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldName(FilterField<Node, ?> field) {
|
||||
if (field == BulkUpdateField.PERMISSION) {
|
||||
this.builder.append("permission");
|
||||
} else if (field == BulkUpdateField.SERVER) {
|
||||
this.builder.append("server");
|
||||
} else if (field == BulkUpdateField.WORLD) {
|
||||
this.builder.append("world");
|
||||
} else {
|
||||
throw new AssertionError(field);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -25,13 +25,12 @@
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.action;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
|
||||
import net.luckperms.api.node.Node;
|
||||
|
||||
/**
|
||||
* Represents an action to be applied to a given node.
|
||||
*/
|
||||
public interface Action {
|
||||
public interface BulkUpdateAction {
|
||||
|
||||
/**
|
||||
* Gets the name of this action
|
||||
@ -44,17 +43,8 @@ public interface Action {
|
||||
* Applies this action to the given NodeModel, and returns the result.
|
||||
*
|
||||
* @param from the node to base changes from
|
||||
* @return the new nodemodel instance, or null if the node should be deleted.
|
||||
* @return the new node instance, or null if the node should be deleted.
|
||||
*/
|
||||
Node apply(Node from);
|
||||
|
||||
/**
|
||||
* Gets this action in SQL form.
|
||||
*
|
||||
* Will include a placeholder for the table, as "{table}".
|
||||
*
|
||||
* @param builder the statement builder
|
||||
*/
|
||||
void appendSql(PreparedStatementBuilder builder);
|
||||
|
||||
}
|
@ -25,10 +25,9 @@
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.action;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
|
||||
import net.luckperms.api.node.Node;
|
||||
|
||||
public class DeleteAction implements Action {
|
||||
public class DeleteAction implements BulkUpdateAction {
|
||||
|
||||
public static DeleteAction create() {
|
||||
return new DeleteAction();
|
||||
@ -46,9 +45,4 @@ public class DeleteAction implements Action {
|
||||
public Node apply(Node from) {
|
||||
return null; // this action just deletes nodes, so return null
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSql(PreparedStatementBuilder builder) {
|
||||
builder.append("DELETE FROM {table}");
|
||||
}
|
||||
}
|
||||
|
@ -25,28 +25,27 @@
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.action;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateField;
|
||||
import me.lucko.luckperms.common.node.factory.NodeBuilders;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
import net.luckperms.api.node.Node;
|
||||
|
||||
public class UpdateAction implements Action {
|
||||
public class UpdateAction implements BulkUpdateAction {
|
||||
|
||||
public static UpdateAction of(QueryField field, String value) {
|
||||
public static UpdateAction of(BulkUpdateField field, String value) {
|
||||
return new UpdateAction(field, value);
|
||||
}
|
||||
|
||||
// the field we're updating
|
||||
private final QueryField field;
|
||||
private final BulkUpdateField field;
|
||||
|
||||
// the new value of the field
|
||||
private final String value;
|
||||
private final String newValue;
|
||||
|
||||
private UpdateAction(QueryField field, String value) {
|
||||
private UpdateAction(BulkUpdateField field, String newValue) {
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
this.newValue = newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -54,11 +53,19 @@ public class UpdateAction implements Action {
|
||||
return "update";
|
||||
}
|
||||
|
||||
public BulkUpdateField getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
public String getNewValue() {
|
||||
return this.newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node apply(Node from) {
|
||||
switch (this.field) {
|
||||
case PERMISSION:
|
||||
return NodeBuilders.determineMostApplicable(this.value)
|
||||
return NodeBuilders.determineMostApplicable(this.newValue)
|
||||
.value(from.getValue())
|
||||
.expiry(from.getExpiry())
|
||||
.context(from.getContexts())
|
||||
@ -66,8 +73,8 @@ public class UpdateAction implements Action {
|
||||
case SERVER: {
|
||||
MutableContextSet contexts = from.getContexts().mutableCopy();
|
||||
contexts.removeAll(DefaultContextKeys.SERVER_KEY);
|
||||
if (!this.value.equals("global")) {
|
||||
contexts.add(DefaultContextKeys.SERVER_KEY, this.value);
|
||||
if (!this.newValue.equals("global")) {
|
||||
contexts.add(DefaultContextKeys.SERVER_KEY, this.newValue);
|
||||
}
|
||||
|
||||
return from.toBuilder()
|
||||
@ -77,8 +84,8 @@ public class UpdateAction implements Action {
|
||||
case WORLD: {
|
||||
MutableContextSet contexts = from.getContexts().mutableCopy();
|
||||
contexts.removeAll(DefaultContextKeys.WORLD_KEY);
|
||||
if (!this.value.equals("global")) {
|
||||
contexts.add(DefaultContextKeys.WORLD_KEY, this.value);
|
||||
if (!this.newValue.equals("global")) {
|
||||
contexts.add(DefaultContextKeys.WORLD_KEY, this.newValue);
|
||||
}
|
||||
|
||||
return from.toBuilder()
|
||||
@ -89,10 +96,4 @@ public class UpdateAction implements Action {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSql(PreparedStatementBuilder builder) {
|
||||
builder.append("UPDATE {table} SET " + this.field.getSqlName() + "=");
|
||||
builder.variable(this.value);
|
||||
}
|
||||
}
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.comparison;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* An enumeration of standard {@link Comparison}s.
|
||||
*/
|
||||
public enum StandardComparison implements Comparison {
|
||||
|
||||
EQUAL("==", "=") {
|
||||
@Override
|
||||
public CompiledExpression compile(String expression) {
|
||||
return expression::equalsIgnoreCase;
|
||||
}
|
||||
},
|
||||
|
||||
NOT_EQUAL("!=", "!=") {
|
||||
@Override
|
||||
public CompiledExpression compile(String expression) {
|
||||
return string -> !expression.equalsIgnoreCase(string);
|
||||
}
|
||||
},
|
||||
|
||||
SIMILAR("~~", "LIKE") {
|
||||
@Override
|
||||
public CompiledExpression compile(String expression) {
|
||||
Pattern pattern = StandardComparison.compilePatternForLikeSyntax(expression);
|
||||
return string -> pattern.matcher(string).matches();
|
||||
}
|
||||
},
|
||||
|
||||
NOT_SIMILAR("!~", "NOT LIKE") {
|
||||
@Override
|
||||
public CompiledExpression compile(String expression) {
|
||||
Pattern pattern = StandardComparison.compilePatternForLikeSyntax(expression);
|
||||
return string -> !pattern.matcher(string).matches();
|
||||
}
|
||||
};
|
||||
|
||||
public static final String WILDCARD = "%";
|
||||
public static final String WILDCARD_ONE = "_";
|
||||
|
||||
private final String symbol;
|
||||
private final String asSql;
|
||||
|
||||
StandardComparison(String symbol, String asSql) {
|
||||
this.symbol = symbol;
|
||||
this.asSql = asSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return this.symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSql(PreparedStatementBuilder builder) {
|
||||
builder.append(this.asSql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.symbol;
|
||||
}
|
||||
|
||||
public static StandardComparison parseComparison(String s) {
|
||||
for (StandardComparison t : values()) {
|
||||
if (t.getSymbol().equals(s)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Pattern compilePatternForLikeSyntax(String expression) {
|
||||
expression = expression.replace(".", "\\.");
|
||||
|
||||
// convert from SQL LIKE syntax to regex
|
||||
expression = expression.replace(WILDCARD_ONE, ".");
|
||||
expression = expression.replace(WILDCARD, ".*");
|
||||
|
||||
return Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.query;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
|
||||
import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.node.Node;
|
||||
|
||||
/**
|
||||
* Represents a query component
|
||||
*/
|
||||
public class Query {
|
||||
|
||||
public static Query of(QueryField field, Constraint constraint) {
|
||||
return new Query(field, constraint);
|
||||
}
|
||||
|
||||
// the field this query is comparing against
|
||||
private final QueryField field;
|
||||
|
||||
// the constraint
|
||||
private final Constraint constraint;
|
||||
|
||||
private Query(QueryField field, Constraint constraint) {
|
||||
this.field = field;
|
||||
this.constraint = constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the given node satisfies this query
|
||||
*
|
||||
* @param node the node
|
||||
* @return true if satisfied
|
||||
*/
|
||||
public boolean isSatisfiedBy(Node node) {
|
||||
switch (this.field) {
|
||||
case PERMISSION:
|
||||
return this.constraint.eval(node.getKey());
|
||||
case SERVER:
|
||||
return this.constraint.eval(node.getContexts().getAnyValue(DefaultContextKeys.SERVER_KEY).orElse("global"));
|
||||
case WORLD:
|
||||
return this.constraint.eval(node.getContexts().getAnyValue(DefaultContextKeys.WORLD_KEY).orElse("global"));
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public void appendSql(PreparedStatementBuilder builder) {
|
||||
this.constraint.appendSql(builder, this.field.getSqlName());
|
||||
}
|
||||
|
||||
public QueryField getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
public Constraint getConstraint() {
|
||||
return this.constraint;
|
||||
}
|
||||
}
|
@ -86,39 +86,44 @@ public abstract class ParentCommand<T, I> extends Command<Void> {
|
||||
return;
|
||||
}
|
||||
|
||||
final String targetArgument = args.get(0);
|
||||
I targetId = null;
|
||||
if (this.type == Type.TAKES_ARGUMENT_FOR_TARGET) {
|
||||
targetId = parseTarget(targetArgument, plugin, sender);
|
||||
if (this.type == Type.TARGETED) {
|
||||
final String targetArgument = args.get(0);
|
||||
I targetId = parseTarget(targetArgument, plugin, sender);
|
||||
if (targetId == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ReentrantLock lock = getLockForTarget(targetId);
|
||||
lock.lock();
|
||||
try {
|
||||
T target = getTarget(targetId, plugin, sender);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReentrantLock lock = getLockForTarget(targetId);
|
||||
lock.lock();
|
||||
try {
|
||||
sub.execute(plugin, sender, target, args.subList(this.type.minArgs, args.size()), label);
|
||||
T target = getTarget(targetId, plugin, sender);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sub.execute(plugin, sender, target, args.subList(this.type.minArgs, args.size()), label);
|
||||
} catch (CommandException e) {
|
||||
e.handle(sender, label, sub);
|
||||
}
|
||||
|
||||
cleanup(target, plugin);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
sub.execute(plugin, sender, null, args.subList(this.type.minArgs, args.size()), label);
|
||||
} catch (CommandException e) {
|
||||
e.handle(sender, label, sub);
|
||||
}
|
||||
|
||||
cleanup(target, plugin);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(LuckPermsPlugin plugin, Sender sender, ArgumentList args) {
|
||||
switch (this.type) {
|
||||
case TAKES_ARGUMENT_FOR_TARGET:
|
||||
case TARGETED:
|
||||
return TabCompleter.create()
|
||||
.at(0, CompletionSupplier.startsWith(() -> getTargets(plugin).stream()))
|
||||
.at(1, CompletionSupplier.startsWith(() -> getChildren().stream()
|
||||
@ -133,7 +138,7 @@ public abstract class ParentCommand<T, I> extends Command<Void> {
|
||||
.orElse(Collections.emptyList())
|
||||
)
|
||||
.complete(args);
|
||||
case NO_TARGET_ARGUMENT:
|
||||
case NOT_TARGETED:
|
||||
return TabCompleter.create()
|
||||
.at(0, CompletionSupplier.startsWith(() -> getChildren().stream()
|
||||
.filter(s -> s.isAuthorized(sender))
|
||||
@ -178,21 +183,31 @@ public abstract class ParentCommand<T, I> extends Command<Void> {
|
||||
return getChildren().stream().anyMatch(sc -> sc.isAuthorized(sender));
|
||||
}
|
||||
|
||||
protected abstract List<String> getTargets(LuckPermsPlugin plugin);
|
||||
protected List<String> getTargets(LuckPermsPlugin plugin) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected abstract I parseTarget(String target, LuckPermsPlugin plugin, Sender sender);
|
||||
protected I parseTarget(String target, LuckPermsPlugin plugin, Sender sender) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected abstract ReentrantLock getLockForTarget(I target);
|
||||
protected ReentrantLock getLockForTarget(I target) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected abstract T getTarget(I target, LuckPermsPlugin plugin, Sender sender);
|
||||
protected T getTarget(I target, LuckPermsPlugin plugin, Sender sender) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected abstract void cleanup(T t, LuckPermsPlugin plugin);
|
||||
protected void cleanup(T t, LuckPermsPlugin plugin) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
// e.g. /lp log sub-command....
|
||||
NO_TARGET_ARGUMENT(0),
|
||||
NOT_TARGETED(0),
|
||||
// e.g. /lp user <USER> sub-command....
|
||||
TAKES_ARGUMENT_FOR_TARGET(1);
|
||||
TARGETED(1);
|
||||
|
||||
private final int cmdIndex;
|
||||
private final int minArgs;
|
||||
|
@ -28,12 +28,9 @@ package me.lucko.luckperms.common.commands.group;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateField;
|
||||
import me.lucko.luckperms.common.bulkupdate.DataType;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.Query;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
|
||||
import me.lucko.luckperms.common.command.abstraction.SingleCommand;
|
||||
import me.lucko.luckperms.common.command.access.ArgumentPermissions;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
@ -43,6 +40,7 @@ import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompletions;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.messaging.InternalMessagingService;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
@ -115,7 +113,7 @@ public class DeleteGroup extends SingleCommand {
|
||||
.trackStatistics(false)
|
||||
.dataType(DataType.ALL)
|
||||
.action(DeleteAction.create())
|
||||
.query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, Inheritance.key(groupName))))
|
||||
.filter(BulkUpdateField.PERMISSION, Comparison.EQUAL, Inheritance.key(groupName))
|
||||
.build();
|
||||
plugin.getStorage().applyBulkUpdate(operation).whenCompleteAsync((v, ex) -> {
|
||||
if (ex != null) {
|
||||
|
@ -60,7 +60,7 @@ public class GroupParentCommand extends ParentCommand<Group, String> {
|
||||
.build(key -> new ReentrantLock());
|
||||
|
||||
public GroupParentCommand() {
|
||||
super(CommandSpec.GROUP, "Group", Type.TAKES_ARGUMENT_FOR_TARGET, ImmutableList.<Command<Group>>builder()
|
||||
super(CommandSpec.GROUP, "Group", Type.TARGETED, ImmutableList.<Command<Group>>builder()
|
||||
.add(new GroupInfo())
|
||||
.add(new CommandPermission<>(HolderType.GROUP))
|
||||
.add(new CommandParent<>(HolderType.GROUP))
|
||||
|
@ -28,11 +28,8 @@ package me.lucko.luckperms.common.commands.group;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateField;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.Query;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.ArgumentPermissions;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
@ -41,6 +38,7 @@ import me.lucko.luckperms.common.command.tabcomplete.CompletionSupplier;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.command.utils.StorageAssistant;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.node.types.Inheritance;
|
||||
@ -112,8 +110,8 @@ public class GroupRename extends ChildCommand<Group> {
|
||||
BulkUpdate operation = BulkUpdateBuilder.create()
|
||||
.trackStatistics(false)
|
||||
.dataType(me.lucko.luckperms.common.bulkupdate.DataType.ALL)
|
||||
.action(UpdateAction.of(QueryField.PERMISSION, Inheritance.key(newGroupName)))
|
||||
.query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, Inheritance.key(target.getName()))))
|
||||
.action(UpdateAction.of(BulkUpdateField.PERMISSION, Inheritance.key(newGroupName)))
|
||||
.filter(BulkUpdateField.PERMISSION, Comparison.EQUAL, Inheritance.key(target.getName()))
|
||||
.build();
|
||||
return plugin.getStorage().applyBulkUpdate(operation);
|
||||
} else {
|
||||
|
@ -25,25 +25,26 @@
|
||||
|
||||
package me.lucko.luckperms.common.commands.log;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompletions;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.storage.misc.DataConstraints;
|
||||
import me.lucko.luckperms.common.util.Paginated;
|
||||
import me.lucko.luckperms.common.util.Predicates;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LogGroupHistory extends ChildCommand<Log> {
|
||||
public class LogGroupHistory extends ChildCommand<Void> {
|
||||
private static final int ENTRIES_PER_PAGE = 10;
|
||||
|
||||
public LogGroupHistory() {
|
||||
@ -51,44 +52,34 @@ public class LogGroupHistory extends ChildCommand<Log> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
|
||||
String group = args.get(0).toLowerCase(Locale.ROOT);
|
||||
if (!DataConstraints.GROUP_NAME_TEST.test(group)) {
|
||||
Message.GROUP_INVALID_ENTRY.send(sender, group);
|
||||
return;
|
||||
}
|
||||
|
||||
Paginated<LoggedAction> content = new Paginated<>(log.getGroupHistory(group));
|
||||
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, args.getIntOrDefault(1, 1));
|
||||
LogPage log = plugin.getStorage().getLogPage(ActionFilters.group(group), pageParams).join();
|
||||
|
||||
int page = args.getIntOrDefault(1, Integer.MIN_VALUE);
|
||||
if (page != Integer.MIN_VALUE) {
|
||||
showLog(page, sender, content);
|
||||
} else {
|
||||
showLog(content.getMaxPages(ENTRIES_PER_PAGE), sender, content);
|
||||
}
|
||||
}
|
||||
int page = pageParams.pageNumber();
|
||||
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
|
||||
|
||||
private static void showLog(int page, Sender sender, Paginated<LoggedAction> log) {
|
||||
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
|
||||
if (maxPage == 0) {
|
||||
if (log.getTotalEntries() == 0) {
|
||||
Message.LOG_NO_ENTRIES.send(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (page == Integer.MIN_VALUE) {
|
||||
page = maxPage;
|
||||
}
|
||||
|
||||
if (page < 1 || page > maxPage) {
|
||||
Message.LOG_INVALID_PAGE_RANGE.send(sender, maxPage);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
|
||||
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
|
||||
String name = entries.stream().findAny().get().value().getTarget().getName();
|
||||
Message.LOG_HISTORY_GROUP_HEADER.send(sender, name, page, maxPage);
|
||||
|
||||
for (Paginated.Entry<LoggedAction> e : entries) {
|
||||
for (LogPage.Entry<LoggedAction> e : entries) {
|
||||
Message.LOG_ENTRY.send(sender, e.position(), e.value());
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package me.lucko.luckperms.common.commands.log;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
@ -43,7 +42,7 @@ import net.luckperms.api.node.Node;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LogNotify extends ChildCommand<Log> {
|
||||
public class LogNotify extends ChildCommand<Void> {
|
||||
private static final String IGNORE_NODE = "luckperms.log.notify.ignoring";
|
||||
|
||||
public LogNotify() {
|
||||
@ -83,7 +82,7 @@ public class LogNotify extends ChildCommand<Log> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
|
||||
if (sender.isConsole()) {
|
||||
Message.LOG_NOTIFY_CONSOLE.send(sender);
|
||||
return;
|
||||
|
@ -26,22 +26,13 @@
|
||||
package me.lucko.luckperms.common.commands.log;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.command.abstraction.Command;
|
||||
import me.lucko.luckperms.common.command.abstraction.ParentCommand;
|
||||
import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class LogParentCommand extends ParentCommand<Log, Void> {
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public class LogParentCommand extends ParentCommand<Void, Void> {
|
||||
public LogParentCommand() {
|
||||
super(CommandSpec.LOG, "Log", Type.NO_TARGET_ARGUMENT, ImmutableList.<Command<Log>>builder()
|
||||
super(CommandSpec.LOG, "Log", Type.NOT_TARGETED, ImmutableList.<Command<Void>>builder()
|
||||
.add(new LogRecent())
|
||||
.add(new LogSearch())
|
||||
.add(new LogNotify())
|
||||
@ -51,38 +42,4 @@ public class LogParentCommand extends ParentCommand<Log, Void> {
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReentrantLock getLockForTarget(Void target) {
|
||||
return this.lock; // all commands target the same log, so we share a lock between all "targets"
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Log getTarget(Void target, LuckPermsPlugin plugin, Sender sender) {
|
||||
Log log = plugin.getStorage().getLog().join();
|
||||
|
||||
if (log == null) {
|
||||
Message.LOG_LOAD_ERROR.send(sender);
|
||||
}
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup(Log log, LuckPermsPlugin plugin) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTargets(LuckPermsPlugin plugin) {
|
||||
// should never be called if we specify Type.NO_TARGET_ARGUMENT in the constructor
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void parseTarget(String target, LuckPermsPlugin plugin, Sender sender) {
|
||||
// should never be called if we specify Type.NO_TARGET_ARGUMENT in the constructor
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,22 +25,23 @@
|
||||
|
||||
package me.lucko.luckperms.common.commands.log;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.util.Paginated;
|
||||
import me.lucko.luckperms.common.util.Predicates;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LogRecent extends ChildCommand<Log> {
|
||||
public class LogRecent extends ChildCommand<Void> {
|
||||
private static final int ENTRIES_PER_PAGE = 10;
|
||||
|
||||
public LogRecent() {
|
||||
@ -48,39 +49,32 @@ public class LogRecent extends ChildCommand<Log> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
|
||||
if (args.isEmpty()) {
|
||||
// No page or user
|
||||
Paginated<LoggedAction> content = new Paginated<>(log.getContent());
|
||||
showLog(content.getMaxPages(ENTRIES_PER_PAGE), false, sender, content);
|
||||
return;
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
|
||||
int page = 1;
|
||||
UUID uuid = null;
|
||||
|
||||
if (!args.isEmpty()) {
|
||||
int pageNo = args.getIntOrDefault(0, Integer.MIN_VALUE);
|
||||
if (pageNo != Integer.MIN_VALUE) {
|
||||
page = pageNo;
|
||||
} else {
|
||||
uuid = args.getUserTarget(0, plugin, sender);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
pageNo = args.getIntOrDefault(1, Integer.MIN_VALUE);
|
||||
if (pageNo != Integer.MIN_VALUE) {
|
||||
page = pageNo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int page = args.getIntOrDefault(0, Integer.MIN_VALUE);
|
||||
if (page != Integer.MIN_VALUE) {
|
||||
Paginated<LoggedAction> content = new Paginated<>(log.getContent());
|
||||
showLog(page, false, sender, content);
|
||||
return;
|
||||
}
|
||||
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, page);
|
||||
LogPage log = plugin.getStorage().getLogPage(uuid == null ? ActionFilters.all() : ActionFilters.source(uuid), pageParams).join();
|
||||
|
||||
// User and possibly page
|
||||
UUID uuid = args.getUserTarget(0, plugin, sender);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Paginated<LoggedAction> content = new Paginated<>(log.getContent(uuid));
|
||||
page = args.getIntOrDefault(1, Integer.MIN_VALUE);
|
||||
if (page != Integer.MIN_VALUE) {
|
||||
showLog(page, true, sender, content);
|
||||
} else {
|
||||
showLog(content.getMaxPages(ENTRIES_PER_PAGE), true, sender, content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void showLog(int page, boolean specificUser, Sender sender, Paginated<LoggedAction> log) {
|
||||
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
|
||||
if (maxPage == 0) {
|
||||
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
|
||||
if (log.getTotalEntries() == 0) {
|
||||
Message.LOG_NO_ENTRIES.send(sender);
|
||||
return;
|
||||
}
|
||||
@ -90,8 +84,8 @@ public class LogRecent extends ChildCommand<Log> {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
|
||||
if (specificUser) {
|
||||
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
|
||||
if (uuid != null) {
|
||||
String name = entries.stream().findAny().get().value().getSource().getName();
|
||||
if (name.contains("@")) {
|
||||
name = name.split("@")[0];
|
||||
@ -101,8 +95,9 @@ public class LogRecent extends ChildCommand<Log> {
|
||||
Message.LOG_RECENT_HEADER.send(sender, page, maxPage);
|
||||
}
|
||||
|
||||
for (Paginated.Entry<LoggedAction> e : entries) {
|
||||
for (LogPage.Entry<LoggedAction> e : entries) {
|
||||
Message.LOG_ENTRY.send(sender, e.position(), e.value());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,21 +25,22 @@
|
||||
|
||||
package me.lucko.luckperms.common.commands.log;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.util.Paginated;
|
||||
import me.lucko.luckperms.common.util.Predicates;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LogSearch extends ChildCommand<Log> {
|
||||
public class LogSearch extends ChildCommand<Void> {
|
||||
private static final int ENTRIES_PER_PAGE = 10;
|
||||
|
||||
public LogSearch() {
|
||||
@ -47,8 +48,8 @@ public class LogSearch extends ChildCommand<Log> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
|
||||
int page = Integer.MIN_VALUE;
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
|
||||
int page = 1;
|
||||
if (args.size() > 1) {
|
||||
try {
|
||||
page = Integer.parseInt(args.get(args.size() - 1));
|
||||
@ -59,36 +60,26 @@ public class LogSearch extends ChildCommand<Log> {
|
||||
}
|
||||
|
||||
final String query = String.join(" ", args);
|
||||
Paginated<LoggedAction> content = new Paginated<>(log.getSearch(query));
|
||||
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, page);
|
||||
LogPage log = plugin.getStorage().getLogPage(ActionFilters.search(query), pageParams).join();
|
||||
|
||||
if (page != Integer.MIN_VALUE) {
|
||||
showLog(page, query, sender, content);
|
||||
} else {
|
||||
showLog(content.getMaxPages(ENTRIES_PER_PAGE), query, sender, content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void showLog(int page, String query, Sender sender, Paginated<LoggedAction> log) {
|
||||
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
|
||||
if (maxPage == 0) {
|
||||
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
|
||||
if (log.getTotalEntries() == 0) {
|
||||
Message.LOG_NO_ENTRIES.send(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (page == Integer.MIN_VALUE) {
|
||||
page = maxPage;
|
||||
}
|
||||
|
||||
if (page < 1 || page > maxPage) {
|
||||
Message.LOG_INVALID_PAGE_RANGE.send(sender, maxPage);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
|
||||
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
|
||||
Message.LOG_SEARCH_HEADER.send(sender, query, page, maxPage);
|
||||
|
||||
for (Paginated.Entry<LoggedAction> e : entries) {
|
||||
for (LogPage.Entry<LoggedAction> e : entries) {
|
||||
Message.LOG_ENTRY.send(sender, e.position(), e.value());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,25 +25,26 @@
|
||||
|
||||
package me.lucko.luckperms.common.commands.log;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompletions;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.storage.misc.DataConstraints;
|
||||
import me.lucko.luckperms.common.util.Paginated;
|
||||
import me.lucko.luckperms.common.util.Predicates;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LogTrackHistory extends ChildCommand<Log> {
|
||||
public class LogTrackHistory extends ChildCommand<Void> {
|
||||
private static final int ENTRIES_PER_PAGE = 10;
|
||||
|
||||
public LogTrackHistory() {
|
||||
@ -51,44 +52,33 @@ public class LogTrackHistory extends ChildCommand<Log> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
|
||||
String track = args.get(0).toLowerCase(Locale.ROOT);
|
||||
if (!DataConstraints.TRACK_NAME_TEST.test(track)) {
|
||||
Message.TRACK_INVALID_ENTRY.send(sender, track);
|
||||
return;
|
||||
}
|
||||
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, args.getIntOrDefault(1, 1));
|
||||
LogPage log = plugin.getStorage().getLogPage(ActionFilters.track(track), pageParams).join();
|
||||
|
||||
Paginated<LoggedAction> content = new Paginated<>(log.getTrackHistory(track));
|
||||
int page = pageParams.pageNumber();
|
||||
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
|
||||
|
||||
int page = args.getIntOrDefault(1, Integer.MIN_VALUE);
|
||||
if (page != Integer.MIN_VALUE) {
|
||||
showLog(page, sender, content);
|
||||
} else {
|
||||
showLog(content.getMaxPages(ENTRIES_PER_PAGE), sender, content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void showLog(int page, Sender sender, Paginated<LoggedAction> log) {
|
||||
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
|
||||
if (maxPage == 0) {
|
||||
if (log.getTotalEntries() == 0) {
|
||||
Message.LOG_NO_ENTRIES.send(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (page == Integer.MIN_VALUE) {
|
||||
page = maxPage;
|
||||
}
|
||||
|
||||
if (page < 1 || page > maxPage) {
|
||||
Message.LOG_INVALID_PAGE_RANGE.send(sender, maxPage);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
|
||||
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
|
||||
String name = entries.stream().findAny().get().value().getTarget().getName();
|
||||
Message.LOG_HISTORY_TRACK_HEADER.send(sender, name, page, maxPage);
|
||||
|
||||
for (Paginated.Entry<LoggedAction> e : entries) {
|
||||
for (LogPage.Entry<LoggedAction> e : entries) {
|
||||
Message.LOG_ENTRY.send(sender, e.position(), e.value());
|
||||
}
|
||||
}
|
||||
|
@ -25,22 +25,23 @@
|
||||
|
||||
package me.lucko.luckperms.common.commands.log;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.command.abstraction.ChildCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.util.Paginated;
|
||||
import me.lucko.luckperms.common.util.Predicates;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LogUserHistory extends ChildCommand<Log> {
|
||||
public class LogUserHistory extends ChildCommand<Void> {
|
||||
private static final int ENTRIES_PER_PAGE = 10;
|
||||
|
||||
public LogUserHistory() {
|
||||
@ -48,25 +49,19 @@ public class LogUserHistory extends ChildCommand<Log> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Log log, ArgumentList args, String label) {
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, Void ignored, ArgumentList args, String label) {
|
||||
UUID uuid = args.getUserTarget(0, plugin, sender);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Paginated<LoggedAction> content = new Paginated<>(log.getUserHistory(uuid));
|
||||
PageParameters pageParams = new PageParameters(ENTRIES_PER_PAGE, args.getIntOrDefault(1, 1));
|
||||
LogPage log = plugin.getStorage().getLogPage(ActionFilters.user(uuid), pageParams).join();
|
||||
|
||||
int page = args.getIntOrDefault(1, Integer.MIN_VALUE);
|
||||
if (page != Integer.MIN_VALUE) {
|
||||
showLog(page, sender, content);
|
||||
} else {
|
||||
showLog(content.getMaxPages(ENTRIES_PER_PAGE), sender, content);
|
||||
}
|
||||
}
|
||||
int page = pageParams.pageNumber();
|
||||
int maxPage = pageParams.getMaxPage(log.getTotalEntries());
|
||||
|
||||
private static void showLog(int page, Sender sender, Paginated<LoggedAction> log) {
|
||||
int maxPage = log.getMaxPages(ENTRIES_PER_PAGE);
|
||||
if (maxPage == 0) {
|
||||
if (log.getTotalEntries() == 0) {
|
||||
Message.LOG_NO_ENTRIES.send(sender);
|
||||
return;
|
||||
}
|
||||
@ -76,11 +71,11 @@ public class LogUserHistory extends ChildCommand<Log> {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Paginated.Entry<LoggedAction>> entries = log.getPage(page, ENTRIES_PER_PAGE);
|
||||
List<LogPage.Entry<LoggedAction>> entries = log.getNumberedContent();
|
||||
String name = entries.stream().findAny().get().value().getTarget().getName();
|
||||
Message.LOG_HISTORY_USER_HEADER.send(sender, name, page, maxPage);
|
||||
|
||||
for (Paginated.Entry<LoggedAction> e : entries) {
|
||||
for (LogPage.Entry<LoggedAction> e : entries) {
|
||||
Message.LOG_ENTRY.send(sender, e.position(), e.value());
|
||||
}
|
||||
}
|
||||
|
@ -28,15 +28,12 @@ package me.lucko.luckperms.common.commands.misc;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateField;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateSqlBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateStatistics;
|
||||
import me.lucko.luckperms.common.bulkupdate.DataType;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Comparison;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.Query;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
|
||||
import me.lucko.luckperms.common.command.abstraction.CommandException;
|
||||
import me.lucko.luckperms.common.command.abstraction.SingleCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
@ -44,6 +41,7 @@ import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentException;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
@ -112,7 +110,7 @@ public class BulkUpdateCommand extends SingleCommand {
|
||||
}
|
||||
|
||||
String field = args.remove(0);
|
||||
QueryField queryField = QueryField.of(field);
|
||||
BulkUpdateField queryField = BulkUpdateField.of(field);
|
||||
if (queryField == null) {
|
||||
throw new ArgumentException.DetailedUsage();
|
||||
}
|
||||
@ -131,20 +129,20 @@ public class BulkUpdateCommand extends SingleCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
QueryField field = QueryField.of(parts[0]);
|
||||
BulkUpdateField field = BulkUpdateField.of(parts[0]);
|
||||
if (field == null) {
|
||||
Message.BULK_UPDATE_INVALID_CONSTRAINT.send(sender, constraint);
|
||||
return;
|
||||
}
|
||||
|
||||
Comparison comparison = StandardComparison.parseComparison(parts[1]);
|
||||
Comparison comparison = Comparison.parse(parts[1]);
|
||||
if (comparison == null) {
|
||||
Message.BULK_UPDATE_INVALID_COMPARISON.send(sender, parts[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
String expr = parts[2];
|
||||
bulkUpdateBuilder.query(Query.of(field, Constraint.of(comparison, expr)));
|
||||
bulkUpdateBuilder.filter(field, comparison, expr);
|
||||
}
|
||||
|
||||
BulkUpdate bulkUpdate = bulkUpdateBuilder.build();
|
||||
@ -155,7 +153,11 @@ public class BulkUpdateCommand extends SingleCommand {
|
||||
String id = String.format("%04d", ThreadLocalRandom.current().nextInt(10000));
|
||||
this.pendingOperations.put(id, bulkUpdate);
|
||||
|
||||
Message.BULK_UPDATE_QUEUED.send(sender, bulkUpdate.buildAsSql().toReadableString().replace("{table}", bulkUpdate.getDataType().getName()));
|
||||
BulkUpdateSqlBuilder sqlBuilder = new BulkUpdateSqlBuilder();
|
||||
sqlBuilder.visit(bulkUpdate);
|
||||
String readableSql = sqlBuilder.builder().toReadableString().replace("{table}", bulkUpdate.getDataType().getName());
|
||||
|
||||
Message.BULK_UPDATE_QUEUED.send(sender, readableSql);
|
||||
Message.BULK_UPDATE_CONFIRM.send(sender, label, id);
|
||||
}
|
||||
}
|
||||
|
@ -26,9 +26,6 @@
|
||||
package me.lucko.luckperms.common.commands.misc;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Comparison;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
|
||||
import me.lucko.luckperms.common.cache.LoadingMap;
|
||||
import me.lucko.luckperms.common.command.abstraction.SingleCommand;
|
||||
import me.lucko.luckperms.common.command.access.CommandPermission;
|
||||
@ -36,6 +33,7 @@ import me.lucko.luckperms.common.command.spec.CommandSpec;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompleter;
|
||||
import me.lucko.luckperms.common.command.tabcomplete.TabCompletions;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentList;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.model.HolderType;
|
||||
import me.lucko.luckperms.common.node.comparator.NodeEntryComparator;
|
||||
@ -62,13 +60,13 @@ public class SearchCommand extends SingleCommand {
|
||||
|
||||
@Override
|
||||
public void execute(LuckPermsPlugin plugin, Sender sender, ArgumentList args, String label) {
|
||||
Comparison comparison = StandardComparison.parseComparison(args.get(0));
|
||||
Comparison comparison = Comparison.parse(args.get(0));
|
||||
if (comparison == null) {
|
||||
comparison = StandardComparison.EQUAL;
|
||||
comparison = Comparison.EQUAL;
|
||||
args.add(0, "==");
|
||||
}
|
||||
|
||||
ConstraintNodeMatcher<Node> matcher = StandardNodeMatchers.of(Constraint.of(comparison, args.get(1)));
|
||||
ConstraintNodeMatcher<Node> matcher = StandardNodeMatchers.key(args.get(1), comparison);
|
||||
int page = args.getIntOrDefault(2, 1);
|
||||
|
||||
Message.SEARCH_SEARCHING.send(sender, matcher.toString());
|
||||
@ -120,7 +118,7 @@ public class SearchCommand extends SingleCommand {
|
||||
headerMessage.send(sender, page, pages.size(), results.size());
|
||||
|
||||
for (Map.Entry<String, NodeEntry<T, Node>> ent : mappedContent) {
|
||||
Message.SEARCH_NODE_ENTRY.send(sender, comparison != StandardComparison.EQUAL, ent.getValue().getNode(), ent.getKey(), holderType, label, sender.getPlugin());
|
||||
Message.SEARCH_NODE_ENTRY.send(sender, comparison != Comparison.EQUAL, ent.getValue().getNode(), ent.getKey(), holderType, label, sender.getPlugin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public class TrackParentCommand extends ParentCommand<Track, String> {
|
||||
.build(key -> new ReentrantLock());
|
||||
|
||||
public TrackParentCommand() {
|
||||
super(CommandSpec.TRACK, "Track", Type.TAKES_ARGUMENT_FOR_TARGET, ImmutableList.<Command<Track>>builder()
|
||||
super(CommandSpec.TRACK, "Track", Type.TARGETED, ImmutableList.<Command<Track>>builder()
|
||||
.add(new TrackInfo())
|
||||
.add(new TrackEditor())
|
||||
.add(new TrackAppend())
|
||||
|
@ -62,7 +62,7 @@ public class UserParentCommand extends ParentCommand<User, UserIdentifier> {
|
||||
.build(key -> new ReentrantLock());
|
||||
|
||||
public UserParentCommand() {
|
||||
super(CommandSpec.USER, "User", Type.TAKES_ARGUMENT_FOR_TARGET, ImmutableList.<Command<User>>builder()
|
||||
super(CommandSpec.USER, "User", Type.TARGETED, ImmutableList.<Command<User>>builder()
|
||||
.add(new UserInfo())
|
||||
.add(new CommandPermission<>(HolderType.USER))
|
||||
.add(new CommandParent<>(HolderType.USER))
|
||||
|
@ -443,6 +443,11 @@ public final class ConfigKeys {
|
||||
.collect(ImmutableCollectors.toList());
|
||||
});
|
||||
|
||||
/**
|
||||
* If log should be posted synchronously to storage/messaging in commands
|
||||
*/
|
||||
public static final ConfigKey<Boolean> LOG_SYNCHRONOUSLY_IN_COMMANDS = booleanKey("log-synchronously-in-commands", false);
|
||||
|
||||
/**
|
||||
* If LuckPerms should automatically install translation bundles and periodically update them.
|
||||
*/
|
||||
|
@ -23,48 +23,60 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.comparison;
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A method of comparing two strings
|
||||
*/
|
||||
public interface Comparison {
|
||||
public enum Comparison {
|
||||
|
||||
EQUAL("=="),
|
||||
NOT_EQUAL("!="),
|
||||
SIMILAR("~~"),
|
||||
NOT_SIMILAR("!~");
|
||||
|
||||
public static final String WILDCARD = "%";
|
||||
public static final String WILDCARD_ONE = "_";
|
||||
|
||||
private final String symbol;
|
||||
|
||||
Comparison(String symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the symbol which represents this comparison
|
||||
*
|
||||
* @return the comparison symbol
|
||||
*/
|
||||
String getSymbol();
|
||||
public String getSymbol() {
|
||||
return this.symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link CompiledExpression} for the given expression
|
||||
*
|
||||
* @param expression the expression
|
||||
* @return the compiled expression
|
||||
*/
|
||||
CompiledExpression compile(String expression);
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comparison operator in SQL form
|
||||
*/
|
||||
void appendSql(PreparedStatementBuilder builder);
|
||||
public static Comparison parse(String s) {
|
||||
for (Comparison t : values()) {
|
||||
if (t.getSymbol().equals(s)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of {@link Comparison} which is bound to an expression.
|
||||
*/
|
||||
interface CompiledExpression {
|
||||
public static Pattern compilePatternForLikeSyntax(String expression) {
|
||||
expression = expression.replace(".", "\\.");
|
||||
|
||||
/**
|
||||
* Tests the expression against a given string, according to the
|
||||
* rules of the parent {@link Comparison}.
|
||||
*
|
||||
* @param string the string
|
||||
* @return if there was a match
|
||||
*/
|
||||
boolean test(String string);
|
||||
// convert from SQL LIKE syntax to regex
|
||||
expression = expression.replace(WILDCARD_ONE, ".");
|
||||
expression = expression.replace(WILDCARD, ".*");
|
||||
|
||||
return Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
}
|
@ -23,24 +23,28 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.comparison;
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class Constraint {
|
||||
|
||||
public static Constraint of(Comparison comparison, String expression) {
|
||||
return new Constraint(comparison, expression);
|
||||
}
|
||||
public class Constraint<T> {
|
||||
|
||||
private final Comparison comparison;
|
||||
private final String expressionValue;
|
||||
private final Comparison.CompiledExpression compiledExpression;
|
||||
private final T value;
|
||||
private final Predicate<T> predicate;
|
||||
|
||||
private Constraint(Comparison comparison, String expression) {
|
||||
Constraint(Comparison comparison, T value, Predicate<T> predicate) {
|
||||
this.comparison = comparison;
|
||||
this.expressionValue = expression;
|
||||
this.compiledExpression = this.comparison.compile(this.expressionValue);
|
||||
this.value = value;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
public Comparison comparison() {
|
||||
return this.comparison;
|
||||
}
|
||||
|
||||
public T value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,20 +53,12 @@ public class Constraint {
|
||||
* @param value the value
|
||||
* @return true if satisfied
|
||||
*/
|
||||
public boolean eval(String value) {
|
||||
return this.compiledExpression.test(value);
|
||||
}
|
||||
|
||||
public void appendSql(PreparedStatementBuilder builder, String field) {
|
||||
// e.g. field LIKE ?
|
||||
builder.append(field).append(' ');
|
||||
this.comparison.appendSql(builder);
|
||||
builder.append(' ');
|
||||
builder.variable(this.expressionValue);
|
||||
public boolean evaluate(T value) {
|
||||
return this.predicate.test(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.comparison + " " + this.expressionValue;
|
||||
return this.comparison + " " + this.value;
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public interface ConstraintFactory<T> {
|
||||
|
||||
Predicate<T> equal(T value);
|
||||
Predicate<T> notEqual(T value);
|
||||
Predicate<T> similar(T value);
|
||||
Predicate<T> notSimilar(T value);
|
||||
|
||||
default Constraint<T> build(Comparison comparison, T value) {
|
||||
switch (comparison) {
|
||||
case EQUAL:
|
||||
return new Constraint<>(comparison, value, equal(value));
|
||||
case NOT_EQUAL:
|
||||
return new Constraint<>(comparison, value, notEqual(value));
|
||||
case SIMILAR:
|
||||
return new Constraint<>(comparison, value, similar(value));
|
||||
case NOT_SIMILAR:
|
||||
return new Constraint<>(comparison, value, notSimilar(value));
|
||||
default:
|
||||
throw new AssertionError(comparison);
|
||||
}
|
||||
}
|
||||
|
||||
ConstraintFactory<String> STRINGS = new ConstraintFactory<String>() {
|
||||
@Override
|
||||
public Predicate<String> equal(String value) {
|
||||
return value::equalsIgnoreCase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<String> notEqual(String value) {
|
||||
return string -> !value.equalsIgnoreCase(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<String> similar(String value) {
|
||||
Pattern pattern = Comparison.compilePatternForLikeSyntax(value);
|
||||
return string -> pattern.matcher(string).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<String> notSimilar(String value) {
|
||||
Pattern pattern = Comparison.compilePatternForLikeSyntax(value);
|
||||
return string -> !pattern.matcher(string).matches();
|
||||
}
|
||||
};
|
||||
|
||||
ConstraintFactory<UUID> UUIDS = new ConstraintFactory<UUID>() {
|
||||
@Override
|
||||
public Predicate<UUID> equal(UUID value) {
|
||||
return value::equals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<UUID> notEqual(UUID value) {
|
||||
return string -> !value.equals(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<UUID> similar(UUID value) {
|
||||
Pattern pattern = Comparison.compilePatternForLikeSyntax(value.toString());
|
||||
return uuid -> pattern.matcher(uuid.toString()).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<UUID> notSimilar(UUID value) {
|
||||
Pattern pattern = Comparison.compilePatternForLikeSyntax(value.toString());
|
||||
return uuid -> !pattern.matcher(uuid.toString()).matches();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
public class Filter<T, FT> {
|
||||
private final FilterField<T, FT> field;
|
||||
private final Constraint<FT> constraint;
|
||||
|
||||
public Filter(FilterField<T, FT> field, Constraint<FT> constraint) {
|
||||
this.field = field;
|
||||
this.constraint = constraint;
|
||||
}
|
||||
|
||||
public final FilterField<T, FT> field() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
public final Constraint<FT> constraint() {
|
||||
return this.constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the given value satisfies this filter
|
||||
*
|
||||
* @param value the value
|
||||
* @return true if satisfied
|
||||
*/
|
||||
public boolean evaluate(T value) {
|
||||
return this.constraint.evaluate(this.field.getValue(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.field + " " + this.constraint;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Represents a field that can be filtered on.
|
||||
*
|
||||
* @param <T> the type the field is on
|
||||
*/
|
||||
public interface FilterField<T, FT> {
|
||||
|
||||
static <T, FT> FilterField<T, FT> named(String name, Function<T, FT> func) {
|
||||
return new FilterField<T, FT>() {
|
||||
@Override
|
||||
public FT getValue(T object) {
|
||||
return func.apply(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of this field on the given object.
|
||||
*
|
||||
* @param object the object
|
||||
* @return the field value as a string
|
||||
*/
|
||||
FT getValue(T object);
|
||||
|
||||
default Filter<T, FT> isEqualTo(FT value, ConstraintFactory<FT> factory) {
|
||||
return new Filter<>(this, factory.build(Comparison.EQUAL, value));
|
||||
}
|
||||
|
||||
default Filter<T, FT> isNotEqualTo(FT value, ConstraintFactory<FT> factory) {
|
||||
return new Filter<>(this, factory.build(Comparison.NOT_EQUAL, value));
|
||||
}
|
||||
|
||||
default Filter<T, FT> isSimilarTo(FT value, ConstraintFactory<FT> factory) {
|
||||
return new Filter<>(this, factory.build(Comparison.SIMILAR, value));
|
||||
}
|
||||
|
||||
default Filter<T, FT> isNotSimilarTo(FT value, ConstraintFactory<FT> factory) {
|
||||
return new Filter<>(this, factory.build(Comparison.NOT_SIMILAR, value));
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import com.google.common.collect.ForwardingList;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FilterList<T> extends ForwardingList<Filter<T, ?>> {
|
||||
|
||||
public static <T> FilterList<T> empty() {
|
||||
return new FilterList<>(LogicalOperator.AND, ImmutableList.of());
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> FilterList<T> and(Filter<T, ?>... filters) {
|
||||
return new FilterList<>(LogicalOperator.AND, ImmutableList.copyOf(filters));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> FilterList<T> or(Filter<T, ?>... filters) {
|
||||
return new FilterList<>(LogicalOperator.OR, ImmutableList.copyOf(filters));
|
||||
}
|
||||
|
||||
private final LogicalOperator operator;
|
||||
private final List<Filter<T, ?>> filters;
|
||||
|
||||
public FilterList(LogicalOperator operator, List<Filter<T, ?>> filters) {
|
||||
this.operator = operator;
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public LogicalOperator operator() {
|
||||
return this.operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Filter<T, ?>> delegate() {
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a value satisfies all (AND) or any (OR) filters in the list
|
||||
*
|
||||
* @param value the value to check
|
||||
* @return true if satisfied
|
||||
*/
|
||||
public boolean evaluate(T value) {
|
||||
return this.operator.match(this.filters, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String operator = this.operator.name().toLowerCase(Locale.ROOT);
|
||||
return this.filters.stream().map(Filter::toString).collect(Collectors.joining(" " + operator + " "));
|
||||
}
|
||||
|
||||
public enum LogicalOperator {
|
||||
AND {
|
||||
@Override
|
||||
public <T> boolean match(List<? extends Filter<T, ?>> filters, T value) {
|
||||
return filters.stream().allMatch(filter -> filter.evaluate(value)); // true if empty
|
||||
}
|
||||
},
|
||||
OR {
|
||||
@Override
|
||||
public <T> boolean match(List<? extends Filter<T, ?>> filters, T value) {
|
||||
return filters.stream().anyMatch(filter -> filter.evaluate(value)); // false if empty
|
||||
}
|
||||
};
|
||||
|
||||
public abstract <T> boolean match(List<? extends Filter<T, ?>> filters, T value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class PageParameters {
|
||||
|
||||
private final int pageSize;
|
||||
private final int pageNumber;
|
||||
|
||||
public PageParameters(int pageSize, int pageNumber) {
|
||||
if (pageSize < 1) {
|
||||
throw new IllegalArgumentException("pageSize cannot be less than 1: " + pageSize);
|
||||
}
|
||||
if (pageNumber < 1) {
|
||||
throw new IllegalArgumentException("pageNumber cannot be less than 1: " + pageNumber);
|
||||
}
|
||||
|
||||
this.pageSize = pageSize;
|
||||
this.pageNumber = pageNumber;
|
||||
}
|
||||
|
||||
public int pageSize() {
|
||||
return this.pageSize;
|
||||
}
|
||||
|
||||
public int pageNumber() {
|
||||
return this.pageNumber;
|
||||
}
|
||||
|
||||
public <T> List<T> paginate(List<T> input) {
|
||||
int fromIndex = this.pageSize * (this.pageNumber - 1);
|
||||
if (fromIndex >= input.size()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
int toIndex = Math.min(fromIndex + this.pageSize, input.size());
|
||||
return input.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
public <T> Stream<T> paginate(Stream<T> input) {
|
||||
return input.skip((long) this.pageSize * (this.pageNumber - 1)).limit(this.pageSize);
|
||||
}
|
||||
|
||||
public int getMaxPage(int totalEntries) {
|
||||
if (totalEntries == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (totalEntries + this.pageSize - 1) / this.pageSize;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter.mongo;
|
||||
|
||||
import com.mongodb.client.FindIterable;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.filter.Constraint;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ConstraintMongoBuilder {
|
||||
public static final ConstraintMongoBuilder INSTANCE = new ConstraintMongoBuilder();
|
||||
|
||||
protected ConstraintMongoBuilder() {
|
||||
|
||||
}
|
||||
|
||||
public Object mapConstraintValue(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Bson make(Constraint<?> constraint, String fieldName) {
|
||||
Comparison comparison = constraint.comparison();
|
||||
Object value = mapConstraintValue(constraint.value());
|
||||
|
||||
switch (comparison) {
|
||||
case EQUAL:
|
||||
return Filters.eq(fieldName, value);
|
||||
case NOT_EQUAL:
|
||||
return Filters.ne(fieldName, value);
|
||||
case SIMILAR: {
|
||||
if (!(value instanceof String)) {
|
||||
throw new IllegalArgumentException("Unable to create SIMILAR comparison for non-string type: " + value.getClass().getName());
|
||||
}
|
||||
Pattern pattern = Comparison.compilePatternForLikeSyntax((String) value);
|
||||
return Filters.regex(fieldName, pattern);
|
||||
}
|
||||
case NOT_SIMILAR: {
|
||||
if (!(value instanceof String)) {
|
||||
throw new IllegalArgumentException("Unable to create NOT_SIMILAR comparison for non-string type: " + value.getClass().getName());
|
||||
}
|
||||
Pattern pattern = Comparison.compilePatternForLikeSyntax((String) value);
|
||||
return Filters.not(Filters.regex(fieldName, pattern));
|
||||
}
|
||||
default:
|
||||
throw new AssertionError(comparison);
|
||||
}
|
||||
}
|
||||
|
||||
public static <R> FindIterable<R> page(@Nullable PageParameters params, FindIterable<R> iterable) {
|
||||
if (params == null) {
|
||||
return iterable;
|
||||
}
|
||||
|
||||
int pageSize = params.pageSize();
|
||||
int pageNumber = params.pageNumber();
|
||||
return iterable.limit(pageSize).skip((pageNumber - 1) * pageSize);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter.mongo;
|
||||
|
||||
import com.mongodb.client.model.Filters;
|
||||
import me.lucko.luckperms.common.filter.Filter;
|
||||
import me.lucko.luckperms.common.filter.FilterField;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import org.bson.conversions.Bson;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class FilterMongoBuilder<T> extends ConstraintMongoBuilder {
|
||||
|
||||
public abstract String mapFieldName(FilterField<T, ?> field);
|
||||
|
||||
public Bson make(Filter<T, ?> filter) {
|
||||
return make(filter.constraint(), mapFieldName(filter.field()));
|
||||
}
|
||||
|
||||
public Bson make(FilterList.LogicalOperator combineOperator, List<? extends Filter<T, ?>> filters) {
|
||||
if (filters.isEmpty()) {
|
||||
return Filters.empty();
|
||||
}
|
||||
|
||||
List<Bson> bsonFilters = filters.stream().map(this::make).collect(Collectors.toList());
|
||||
switch (combineOperator) {
|
||||
case AND:
|
||||
return Filters.and(bsonFilters);
|
||||
case OR:
|
||||
return Filters.or(bsonFilters);
|
||||
default:
|
||||
throw new AssertionError(combineOperator);
|
||||
}
|
||||
}
|
||||
|
||||
public Bson make(FilterList<T> filters) {
|
||||
return make(filters.operator(), filters);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter.sql;
|
||||
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.filter.Constraint;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.storage.implementation.sql.builder.AbstractSqlBuilder;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class ConstraintSqlBuilder extends AbstractSqlBuilder {
|
||||
|
||||
public void visitConstraintValue(Object value) {
|
||||
if (value instanceof String) {
|
||||
this.builder.variable(((String) value));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to write value with type: " + value.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(Constraint<?> constraint) {
|
||||
// '= value'
|
||||
// '!= value'
|
||||
// 'LIKE value'
|
||||
// 'NOT LIKE value'
|
||||
|
||||
visit(constraint.comparison());
|
||||
this.builder.append(' ');
|
||||
visitConstraintValue(constraint.value());
|
||||
}
|
||||
|
||||
public void visit(Comparison comparison) {
|
||||
switch (comparison) {
|
||||
case EQUAL:
|
||||
this.builder.append("=");
|
||||
break;
|
||||
case NOT_EQUAL:
|
||||
this.builder.append("!=");
|
||||
break;
|
||||
case SIMILAR:
|
||||
this.builder.append("LIKE");
|
||||
break;
|
||||
case NOT_SIMILAR:
|
||||
this.builder.append("NOT LIKE");
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError(comparison);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(@Nullable PageParameters params) {
|
||||
if (params == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int pageSize = params.pageSize();
|
||||
int pageNumber = params.pageNumber();
|
||||
this.builder.append(" LIMIT " + pageSize + " OFFSET " + (pageNumber - 1) * pageSize);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter.sql;
|
||||
|
||||
import me.lucko.luckperms.common.filter.Filter;
|
||||
import me.lucko.luckperms.common.filter.FilterField;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class FilterSqlBuilder<T> extends ConstraintSqlBuilder {
|
||||
|
||||
public abstract void visitFieldName(FilterField<T, ?> field);
|
||||
|
||||
public void visit(Filter<T, ?> filter) {
|
||||
// 'field = value'
|
||||
// 'field != value'
|
||||
// 'field LIKE value'
|
||||
// 'field NOT LIKE value'
|
||||
|
||||
visitFieldName(filter.field());
|
||||
this.builder.append(' ');
|
||||
visit(filter.constraint());
|
||||
}
|
||||
|
||||
public void visit(FilterList.LogicalOperator combineOperator, List<? extends Filter<T, ?>> filters) {
|
||||
if (filters.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String combineString;
|
||||
switch (combineOperator) {
|
||||
case AND:
|
||||
combineString = "AND ";
|
||||
break;
|
||||
case OR:
|
||||
combineString = "OR ";
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError(combineOperator);
|
||||
}
|
||||
|
||||
this.builder.append(" WHERE");
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
Filter<T, ?> filter = filters.get(i);
|
||||
this.builder.append(" ");
|
||||
if (i != 0) {
|
||||
this.builder.append(combineString);
|
||||
}
|
||||
visit(filter);
|
||||
}
|
||||
}
|
||||
|
||||
public void visit(FilterList<T> filters) {
|
||||
visit(filters.operator(), filters);
|
||||
}
|
||||
|
||||
}
|
@ -300,7 +300,7 @@ public interface Message {
|
||||
.append(text()
|
||||
.color(DARK_GRAY)
|
||||
.append(text('['))
|
||||
.append(text(LoggedAction.getTypeCharacter(action.getTarget().getType()), GREEN))
|
||||
.append(text(LoggedAction.getTypeString(action.getTarget().getType()), GREEN))
|
||||
.append(text(']'))
|
||||
)
|
||||
.append(space())
|
||||
@ -3778,7 +3778,7 @@ public interface Message {
|
||||
.append(text()
|
||||
.color(DARK_GRAY)
|
||||
.append(text('['))
|
||||
.append(text(LoggedAction.getTypeCharacter(action.getTarget().getType()), GREEN))
|
||||
.append(text(LoggedAction.getTypeString(action.getTarget().getType()), GREEN))
|
||||
.append(text(']'))
|
||||
)
|
||||
.append(space())
|
||||
|
@ -31,6 +31,8 @@ import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.messenger.Messenger;
|
||||
import net.luckperms.api.messenger.MessengerProvider;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface InternalMessagingService {
|
||||
|
||||
/**
|
||||
@ -60,21 +62,21 @@ public interface InternalMessagingService {
|
||||
* Uses the messaging service to inform other servers about a general
|
||||
* change.
|
||||
*/
|
||||
void pushUpdate();
|
||||
CompletableFuture<Void> pushUpdate();
|
||||
|
||||
/**
|
||||
* Pushes an update for a specific user.
|
||||
*
|
||||
* @param user the user
|
||||
*/
|
||||
void pushUserUpdate(User user);
|
||||
CompletableFuture<Void> pushUserUpdate(User user);
|
||||
|
||||
/**
|
||||
* Pushes a log entry to connected servers.
|
||||
*
|
||||
* @param logEntry the log entry
|
||||
*/
|
||||
void pushLog(Action logEntry);
|
||||
CompletableFuture<Void> pushLog(Action logEntry);
|
||||
|
||||
/**
|
||||
* Pushes a custom payload to connected servers.
|
||||
@ -82,6 +84,6 @@ public interface InternalMessagingService {
|
||||
* @param channelId the channel id
|
||||
* @param payload the payload
|
||||
*/
|
||||
void pushCustomPayload(String channelId, String payload);
|
||||
CompletableFuture<Void> pushCustomPayload(String channelId, String payload);
|
||||
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import me.lucko.luckperms.common.messaging.message.UpdateMessageImpl;
|
||||
import me.lucko.luckperms.common.messaging.message.UserUpdateMessageImpl;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.util.AsyncInterface;
|
||||
import me.lucko.luckperms.common.util.ExpiringSet;
|
||||
import me.lucko.luckperms.common.util.gson.GsonProvider;
|
||||
import me.lucko.luckperms.common.util.gson.JObject;
|
||||
@ -54,9 +55,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LuckPermsMessagingService implements InternalMessagingService, IncomingMessageConsumer {
|
||||
public class LuckPermsMessagingService extends AsyncInterface implements InternalMessagingService, IncomingMessageConsumer {
|
||||
private final LuckPermsPlugin plugin;
|
||||
private final ExpiringSet<UUID> receivedMessages;
|
||||
private final PushUpdateBuffer updateBuffer;
|
||||
@ -65,6 +67,7 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
|
||||
private final Messenger messenger;
|
||||
|
||||
public LuckPermsMessagingService(LuckPermsPlugin plugin, MessengerProvider messengerProvider) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
|
||||
this.messengerProvider = messengerProvider;
|
||||
@ -107,8 +110,8 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushUpdate() {
|
||||
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
|
||||
public CompletableFuture<Void> pushUpdate() {
|
||||
return future(() -> {
|
||||
UUID requestId = generatePingId();
|
||||
this.plugin.getLogger().info("[Messaging] Sending ping with id: " + requestId);
|
||||
this.messenger.sendOutgoingMessage(new UpdateMessageImpl(requestId));
|
||||
@ -116,8 +119,8 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushUserUpdate(User user) {
|
||||
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
|
||||
public CompletableFuture<Void> pushUserUpdate(User user) {
|
||||
return future(() -> {
|
||||
UUID requestId = generatePingId();
|
||||
this.plugin.getLogger().info("[Messaging] Sending user ping for '" + user.getPlainDisplayName() + "' with id: " + requestId);
|
||||
this.messenger.sendOutgoingMessage(new UserUpdateMessageImpl(requestId, user.getUniqueId()));
|
||||
@ -125,8 +128,8 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushLog(Action logEntry) {
|
||||
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
|
||||
public CompletableFuture<Void> pushLog(Action logEntry) {
|
||||
return future(() -> {
|
||||
UUID requestId = generatePingId();
|
||||
|
||||
if (this.plugin.getEventDispatcher().dispatchLogNetworkPublish(!this.plugin.getConfiguration().get(ConfigKeys.PUSH_LOG_ENTRIES), requestId, logEntry)) {
|
||||
@ -139,8 +142,8 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushCustomPayload(String channelId, String payload) {
|
||||
this.plugin.getBootstrap().getScheduler().executeAsync(() -> {
|
||||
public CompletableFuture<Void> pushCustomPayload(String channelId, String payload) {
|
||||
return future(() -> {
|
||||
UUID requestId = generatePingId();
|
||||
this.messenger.sendOutgoingMessage(new CustomMessageImpl(requestId, channelId, payload));
|
||||
});
|
||||
@ -283,7 +286,7 @@ public class LuckPermsMessagingService implements InternalMessagingService, Inco
|
||||
ActionLogMessage msg = (ActionLogMessage) message;
|
||||
|
||||
this.plugin.getEventDispatcher().dispatchLogReceive(msg.getId(), msg.getAction());
|
||||
this.plugin.getLogDispatcher().dispatchFromRemote((LoggedAction) msg.getAction());
|
||||
this.plugin.getLogDispatcher().broadcastFromRemote((LoggedAction) msg.getAction());
|
||||
|
||||
} else if (message instanceof CustomMessage) {
|
||||
CustomMessage msg = (CustomMessage) message;
|
||||
|
@ -25,7 +25,9 @@
|
||||
|
||||
package me.lucko.luckperms.common.node.matcher;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.filter.Constraint;
|
||||
import me.lucko.luckperms.common.filter.ConstraintFactory;
|
||||
import net.luckperms.api.node.Node;
|
||||
import net.luckperms.api.node.matcher.NodeMatcher;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@ -35,20 +37,20 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
* Abstract implementation of {@link NodeMatcher} backed by a {@link Constraint}.
|
||||
*/
|
||||
public abstract class ConstraintNodeMatcher<T extends Node> implements NodeMatcher<T> {
|
||||
private final Constraint constraint;
|
||||
private final Constraint<String> constraint;
|
||||
|
||||
protected ConstraintNodeMatcher(Constraint constraint) {
|
||||
this.constraint = constraint;
|
||||
protected ConstraintNodeMatcher(Comparison comparison, String value) {
|
||||
this.constraint = ConstraintFactory.STRINGS.build(comparison, value);
|
||||
}
|
||||
|
||||
public Constraint getConstraint() {
|
||||
public Constraint<String> getConstraint() {
|
||||
return this.constraint;
|
||||
}
|
||||
|
||||
public abstract @Nullable T filterConstraintMatch(@NonNull Node node);
|
||||
|
||||
public @Nullable T match(Node node) {
|
||||
return getConstraint().eval(node.getKey()) ? filterConstraintMatch(node) : null;
|
||||
return getConstraint().evaluate(node.getKey()) ? filterConstraintMatch(node) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,8 +25,7 @@
|
||||
|
||||
package me.lucko.luckperms.common.node.matcher;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.node.AbstractNode;
|
||||
import me.lucko.luckperms.common.node.types.DisplayName;
|
||||
import me.lucko.luckperms.common.node.types.Inheritance;
|
||||
@ -45,12 +44,12 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
public final class StandardNodeMatchers {
|
||||
private StandardNodeMatchers() {}
|
||||
|
||||
public static ConstraintNodeMatcher<Node> of(Constraint constraint) {
|
||||
return new Generic(constraint);
|
||||
public static ConstraintNodeMatcher<Node> key(String value, Comparison comparison) {
|
||||
return new Generic(comparison, value);
|
||||
}
|
||||
|
||||
public static ConstraintNodeMatcher<Node> key(String key) {
|
||||
return new Generic(Constraint.of(StandardComparison.EQUAL, key));
|
||||
return new Generic(Comparison.EQUAL, key);
|
||||
}
|
||||
|
||||
public static <T extends Node> ConstraintNodeMatcher<T> key(T node) {
|
||||
@ -58,7 +57,7 @@ public final class StandardNodeMatchers {
|
||||
}
|
||||
|
||||
public static ConstraintNodeMatcher<Node> keyStartsWith(String startsWith) {
|
||||
return new Generic(Constraint.of(StandardComparison.SIMILAR, startsWith + StandardComparison.WILDCARD));
|
||||
return new Generic(Comparison.SIMILAR, startsWith + Comparison.WILDCARD);
|
||||
}
|
||||
|
||||
public static <T extends Node> ConstraintNodeMatcher<T> equals(T other, NodeEqualityPredicate equalityPredicate) {
|
||||
@ -74,8 +73,8 @@ public final class StandardNodeMatchers {
|
||||
}
|
||||
|
||||
private static class Generic extends ConstraintNodeMatcher<Node> {
|
||||
Generic(Constraint constraint) {
|
||||
super(constraint);
|
||||
Generic(Comparison comparison, String value) {
|
||||
super(comparison, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -89,7 +88,7 @@ public final class StandardNodeMatchers {
|
||||
private final NodeEqualityPredicate equalityPredicate;
|
||||
|
||||
NodeEquals(T node, NodeEqualityPredicate equalityPredicate) {
|
||||
super(Constraint.of(StandardComparison.EQUAL, node.getKey()));
|
||||
super(Comparison.EQUAL, node.getKey());
|
||||
this.node = node;
|
||||
this.equalityPredicate = equalityPredicate;
|
||||
}
|
||||
@ -106,7 +105,7 @@ public final class StandardNodeMatchers {
|
||||
|
||||
private static final class MetaKeyEquals extends ConstraintNodeMatcher<MetaNode> {
|
||||
MetaKeyEquals(String metaKey) {
|
||||
super(Constraint.of(StandardComparison.SIMILAR, Meta.key(metaKey, StandardComparison.WILDCARD)));
|
||||
super(Comparison.SIMILAR, Meta.key(metaKey, Comparison.WILDCARD));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -118,8 +117,8 @@ public final class StandardNodeMatchers {
|
||||
private static final class TypeEquals<T extends Node> extends ConstraintNodeMatcher<T> {
|
||||
private final NodeType<? extends T> type;
|
||||
|
||||
protected TypeEquals(NodeType<? extends T> type) {
|
||||
super(getConstraintForType(type));
|
||||
TypeEquals(NodeType<? extends T> type) {
|
||||
super(Comparison.SIMILAR, getSimilarToComparisonValue(type));
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@ -128,21 +127,21 @@ public final class StandardNodeMatchers {
|
||||
return this.type.tryCast(node).orElse(null);
|
||||
}
|
||||
|
||||
private static Constraint getConstraintForType(NodeType<?> type) {
|
||||
private static String getSimilarToComparisonValue(NodeType<?> type) {
|
||||
if (type == NodeType.REGEX_PERMISSION) {
|
||||
return Constraint.of(StandardComparison.SIMILAR, RegexPermission.key(StandardComparison.WILDCARD));
|
||||
return RegexPermission.key(Comparison.WILDCARD);
|
||||
} else if (type == NodeType.INHERITANCE) {
|
||||
return Constraint.of(StandardComparison.SIMILAR, Inheritance.key(StandardComparison.WILDCARD));
|
||||
return Inheritance.key(Comparison.WILDCARD);
|
||||
} else if (type == NodeType.PREFIX) {
|
||||
return Constraint.of(StandardComparison.SIMILAR, Prefix.NODE_MARKER + StandardComparison.WILDCARD + AbstractNode.NODE_SEPARATOR + StandardComparison.WILDCARD);
|
||||
return Prefix.NODE_MARKER + Comparison.WILDCARD + AbstractNode.NODE_SEPARATOR + Comparison.WILDCARD;
|
||||
} else if (type == NodeType.SUFFIX) {
|
||||
return Constraint.of(StandardComparison.SIMILAR, Suffix.NODE_MARKER + StandardComparison.WILDCARD + AbstractNode.NODE_SEPARATOR + StandardComparison.WILDCARD);
|
||||
return Suffix.NODE_MARKER + Comparison.WILDCARD + AbstractNode.NODE_SEPARATOR + Comparison.WILDCARD;
|
||||
} else if (type == NodeType.META) {
|
||||
return Constraint.of(StandardComparison.SIMILAR, Meta.key(StandardComparison.WILDCARD, StandardComparison.WILDCARD));
|
||||
return Meta.key(Comparison.WILDCARD, Comparison.WILDCARD);
|
||||
} else if (type == NodeType.WEIGHT) {
|
||||
return Constraint.of(StandardComparison.SIMILAR, Weight.NODE_MARKER + StandardComparison.WILDCARD);
|
||||
return Weight.NODE_MARKER + Comparison.WILDCARD;
|
||||
} else if (type == NodeType.DISPLAY_NAME) {
|
||||
return Constraint.of(StandardComparison.SIMILAR, DisplayName.key(StandardComparison.WILDCARD));
|
||||
return DisplayName.key(Comparison.WILDCARD);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unable to create a NodeMatcher for NodeType " + type.name());
|
||||
|
@ -78,7 +78,7 @@ public final class AbstractSender<T> implements Sender {
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
if (isConsole()) {
|
||||
if (this.factory.shouldSplitNewlines(this.sender)) {
|
||||
for (Component line : splitNewlines(message)) {
|
||||
this.factory.sendMessage(this.sender, line);
|
||||
}
|
||||
@ -89,12 +89,12 @@ public final class AbstractSender<T> implements Sender {
|
||||
|
||||
@Override
|
||||
public Tristate getPermissionValue(String permission) {
|
||||
return (isConsole() && this.factory.consoleHasAllPermissions()) ? Tristate.TRUE : this.factory.getPermissionValue(this.sender, permission);
|
||||
return isConsole() ? Tristate.TRUE : this.factory.getPermissionValue(this.sender, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return (isConsole() && this.factory.consoleHasAllPermissions()) || this.factory.hasPermission(this.sender, permission);
|
||||
return isConsole() || this.factory.hasPermission(this.sender, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,8 +63,8 @@ public abstract class SenderFactory<P extends LuckPermsPlugin, T> implements Aut
|
||||
|
||||
protected abstract boolean isConsole(T sender);
|
||||
|
||||
protected boolean consoleHasAllPermissions() {
|
||||
return true;
|
||||
protected boolean shouldSplitNewlines(T sender) {
|
||||
return isConsole(sender);
|
||||
}
|
||||
|
||||
public final Sender wrap(T sender) {
|
||||
|
@ -26,8 +26,10 @@
|
||||
package me.lucko.luckperms.common.storage;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
@ -36,12 +38,13 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.implementation.StorageImplementation;
|
||||
import me.lucko.luckperms.common.storage.implementation.split.SplitStorage;
|
||||
import me.lucko.luckperms.common.storage.misc.NodeEntry;
|
||||
import me.lucko.luckperms.common.util.Throwing;
|
||||
import me.lucko.luckperms.common.util.AsyncInterface;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.event.cause.CreationCause;
|
||||
import net.luckperms.api.event.cause.DeletionCause;
|
||||
import net.luckperms.api.model.PlayerSaveResult;
|
||||
import net.luckperms.api.node.Node;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -51,18 +54,17 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
/**
|
||||
* Provides a {@link CompletableFuture} based API for interacting with a {@link StorageImplementation}.
|
||||
*/
|
||||
public class Storage {
|
||||
public class Storage extends AsyncInterface {
|
||||
private final LuckPermsPlugin plugin;
|
||||
private final StorageImplementation implementation;
|
||||
|
||||
public Storage(LuckPermsPlugin plugin, StorageImplementation implementation) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
this.implementation = implementation;
|
||||
}
|
||||
@ -79,32 +81,6 @@ public class Storage {
|
||||
}
|
||||
}
|
||||
|
||||
private <T> CompletableFuture<T> future(Callable<T> supplier) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return supplier.call();
|
||||
} catch (Exception e) {
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, this.plugin.getBootstrap().getScheduler().async());
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> future(Throwing.Runnable runnable) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, this.plugin.getBootstrap().getScheduler().async());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.implementation.getImplementationName();
|
||||
}
|
||||
@ -133,8 +109,8 @@ public class Storage {
|
||||
return future(() -> this.implementation.logAction(entry));
|
||||
}
|
||||
|
||||
public CompletableFuture<Log> getLog() {
|
||||
return future(this.implementation::getLog);
|
||||
public CompletableFuture<LogPage> getLogPage(FilterList<Action> filters, @Nullable PageParameters page) {
|
||||
return future(() -> this.implementation.getLogPage(filters, page));
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
|
@ -25,8 +25,10 @@
|
||||
|
||||
package me.lucko.luckperms.common.storage.implementation;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
@ -58,7 +60,7 @@ public interface StorageImplementation {
|
||||
|
||||
void logAction(Action entry) throws Exception;
|
||||
|
||||
Log getLog() throws Exception;
|
||||
LogPage getLogPage(FilterList<Action> filters, @Nullable PageParameters page) throws Exception;
|
||||
|
||||
void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception;
|
||||
|
||||
|
@ -26,10 +26,12 @@
|
||||
package me.lucko.luckperms.common.storage.implementation.file;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.context.ImmutableContextSetImpl;
|
||||
import me.lucko.luckperms.common.context.serializer.ContextSetConfigurateSerializer;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.HolderType;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
@ -59,6 +61,7 @@ import net.luckperms.api.node.types.InheritanceNode;
|
||||
import net.luckperms.api.node.types.MetaNode;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.Types;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
@ -168,8 +171,8 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
}
|
||||
|
||||
@Override
|
||||
public Log getLog() throws IOException {
|
||||
return this.actionLogger.getLog();
|
||||
public LogPage getLogPage(FilterList<Action> filters, @Nullable PageParameters page) throws Exception {
|
||||
return this.actionLogger.getLogPage(filters, page);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,11 +29,15 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import me.lucko.luckperms.common.actionlog.ActionJsonSerializer;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.cache.BufferedRequest;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.util.gson.GsonProvider;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@ -42,11 +46,14 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FileActionLogger {
|
||||
|
||||
@ -130,31 +137,43 @@ public class FileActionLogger {
|
||||
}
|
||||
}
|
||||
|
||||
public Log getLog() throws IOException {
|
||||
private Stream<LoggedAction> loadLog(FilterList<Action> filters) throws IOException {
|
||||
// if there is log content waiting to be written, flush immediately before trying to read
|
||||
if (this.saveBuffer.isEnqueued()) {
|
||||
this.saveBuffer.requestDirectly();
|
||||
}
|
||||
|
||||
if (!Files.exists(this.contentFile)) {
|
||||
return Log.empty();
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
Log.Builder log = Log.builder();
|
||||
|
||||
Stream.Builder<LoggedAction> builder = Stream.builder();
|
||||
try (BufferedReader reader = Files.newBufferedReader(this.contentFile, StandardCharsets.UTF_8)) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
try {
|
||||
JsonElement parsed = GsonProvider.parser().parse(line);
|
||||
log.add(ActionJsonSerializer.deserialize(parsed));
|
||||
LoggedAction action = ActionJsonSerializer.deserialize(parsed);
|
||||
if (filters.evaluate(action)) {
|
||||
builder.add(action);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
return log.build();
|
||||
public LogPage getLogPage(FilterList<Action> filters, @Nullable PageParameters page) throws IOException {
|
||||
List<LoggedAction> filtered = loadLog(filters)
|
||||
.sorted(Comparator.comparing(LoggedAction::getTimestamp))
|
||||
.collect(Collectors.toList())
|
||||
.reversed();
|
||||
|
||||
int size = filtered.size();
|
||||
List<LoggedAction> paginated = page != null ? page.paginate(filtered) : filtered;
|
||||
return LogPage.of(paginated, page, size);
|
||||
}
|
||||
|
||||
private final class SaveBuffer extends BufferedRequest<Void> {
|
||||
|
@ -36,10 +36,15 @@ import com.mongodb.client.MongoCursor;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.ReplaceOptions;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import com.mongodb.client.model.Sorts;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilterMongoBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.context.MutableContextSetImpl;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.filter.mongo.ConstraintMongoBuilder;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.HolderType;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
@ -64,6 +69,8 @@ import net.luckperms.api.node.Node;
|
||||
import net.luckperms.api.node.NodeBuilder;
|
||||
import org.bson.Document;
|
||||
import org.bson.UuidRepresentation;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
@ -168,79 +175,24 @@ public class MongoStorage implements StorageImplementation {
|
||||
@Override
|
||||
public void logAction(Action entry) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "action");
|
||||
|
||||
Document doc = new Document()
|
||||
.append("timestamp", entry.getTimestamp().getEpochSecond())
|
||||
.append("source", new Document()
|
||||
.append("uniqueId", entry.getSource().getUniqueId())
|
||||
.append("name", entry.getSource().getName())
|
||||
);
|
||||
|
||||
Document target = new Document()
|
||||
.append("type", entry.getTarget().getType().name())
|
||||
.append("name", entry.getTarget().getName());
|
||||
|
||||
if (entry.getTarget().getUniqueId().isPresent()) {
|
||||
target.append("uniqueId", entry.getTarget().getUniqueId().get());
|
||||
}
|
||||
|
||||
doc.append("target", target);
|
||||
doc.append("description", entry.getDescription());
|
||||
|
||||
c.insertOne(doc);
|
||||
c.insertOne(actionToDoc(entry));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Log getLog() {
|
||||
Log.Builder log = Log.builder();
|
||||
public LogPage getLogPage(FilterList<Action> filters, @Nullable PageParameters page) throws Exception {
|
||||
Bson filter = ActionFilterMongoBuilder.INSTANCE.make(filters);
|
||||
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "action");
|
||||
try (MongoCursor<Document> cursor = c.find().iterator()) {
|
||||
long count = c.countDocuments(filter);
|
||||
|
||||
List<LoggedAction> content = new ArrayList<>();
|
||||
try (MongoCursor<Document> cursor = ConstraintMongoBuilder.page(page, c.find(filter).sort(Sorts.descending("timestamp", "_id"))).iterator()) {
|
||||
while (cursor.hasNext()) {
|
||||
Document d = cursor.next();
|
||||
|
||||
if (d.containsKey("source")) {
|
||||
// new format
|
||||
Document source = d.get("source", Document.class);
|
||||
Document target = d.get("target", Document.class);
|
||||
|
||||
UUID targetUniqueId = null;
|
||||
if (target.containsKey("uniqueId")) {
|
||||
targetUniqueId = target.get("uniqueId", UUID.class);
|
||||
}
|
||||
|
||||
LoggedAction e = LoggedAction.build()
|
||||
.timestamp(Instant.ofEpochSecond(d.getLong("timestamp")))
|
||||
.source(source.get("uniqueId", UUID.class))
|
||||
.sourceName(source.getString("name"))
|
||||
.targetType(LoggedAction.parseType(target.getString("type")))
|
||||
.target(targetUniqueId)
|
||||
.targetName(target.getString("name"))
|
||||
.description(d.getString("description"))
|
||||
.build();
|
||||
|
||||
log.add(e);
|
||||
} else {
|
||||
// old format
|
||||
UUID actedUuid = null;
|
||||
if (d.containsKey("acted")) {
|
||||
actedUuid = d.get("acted", UUID.class);
|
||||
}
|
||||
|
||||
LoggedAction e = LoggedAction.build()
|
||||
.timestamp(Instant.ofEpochSecond(d.getLong("timestamp")))
|
||||
.source(d.get("actor", UUID.class))
|
||||
.sourceName(d.getString("actorName"))
|
||||
.targetType(LoggedAction.parseTypeCharacter(d.getString("type").charAt(0)))
|
||||
.target(actedUuid)
|
||||
.targetName(d.getString("actedName"))
|
||||
.description(d.getString("action"))
|
||||
.build();
|
||||
|
||||
log.add(e);
|
||||
}
|
||||
content.add(actionFromDoc(cursor.next()));
|
||||
}
|
||||
}
|
||||
return log.build();
|
||||
|
||||
return LogPage.of(content, page, (int) count);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -253,7 +205,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
UUID uuid = getDocumentId(d);
|
||||
Document results = processBulkUpdate(d, bulkUpdate, HolderType.USER);
|
||||
if (results != null) {
|
||||
c.replaceOne(new Document("_id", uuid), results);
|
||||
c.replaceOne(Filters.eq("_id", uuid), results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,7 +219,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
String holder = d.getString("_id");
|
||||
Document results = processBulkUpdate(d, bulkUpdate, HolderType.GROUP);
|
||||
if (results != null) {
|
||||
c.replaceOne(new Document("_id", holder), results);
|
||||
c.replaceOne(Filters.eq("_id", holder), results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -294,7 +246,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
public User loadUser(UUID uniqueId, String username) {
|
||||
User user = this.plugin.getUserManager().getOrMake(uniqueId, username);
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "users");
|
||||
try (MongoCursor<Document> cursor = c.find(new Document("_id", user.getUniqueId())).iterator()) {
|
||||
try (MongoCursor<Document> cursor = c.find(Filters.eq("_id", user.getUniqueId())).iterator()) {
|
||||
if (cursor.hasNext()) {
|
||||
// User exists, let's load.
|
||||
Document d = cursor.next();
|
||||
@ -309,7 +261,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
|
||||
boolean updatedUsername = user.getUsername().isPresent() && (name == null || !user.getUsername().get().equalsIgnoreCase(name));
|
||||
if (updatedUsername | user.auditTemporaryNodes()) {
|
||||
c.replaceOne(new Document("_id", user.getUniqueId()), userToDoc(user));
|
||||
c.replaceOne(Filters.eq("_id", user.getUniqueId()), userToDoc(user));
|
||||
}
|
||||
} else {
|
||||
if (this.plugin.getUserManager().isNonDefaultUser(user)) {
|
||||
@ -337,9 +289,9 @@ public class MongoStorage implements StorageImplementation {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "users");
|
||||
user.normalData().discardChanges();
|
||||
if (!this.plugin.getUserManager().isNonDefaultUser(user)) {
|
||||
c.deleteOne(new Document("_id", user.getUniqueId()));
|
||||
c.deleteOne(Filters.eq("_id", user.getUniqueId()));
|
||||
} else {
|
||||
c.replaceOne(new Document("_id", user.getUniqueId()), userToDoc(user), new ReplaceOptions().upsert(true));
|
||||
c.replaceOne(Filters.eq("_id", user.getUniqueId()), userToDoc(user), new ReplaceOptions().upsert(true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -363,7 +315,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
public <N extends Node> List<NodeEntry<UUID, N>> searchUserNodes(ConstraintNodeMatcher<N> constraint) throws Exception {
|
||||
List<NodeEntry<UUID, N>> held = new ArrayList<>();
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "users");
|
||||
try (MongoCursor<Document> cursor = c.find().iterator()) {
|
||||
try (MongoCursor<Document> cursor = c.find(Filters.elemMatch("permissions", ConstraintMongoBuilder.INSTANCE.make(constraint.getConstraint(), "key"))).iterator()) {
|
||||
while (cursor.hasNext()) {
|
||||
Document d = cursor.next();
|
||||
UUID holder = getDocumentId(d);
|
||||
@ -384,7 +336,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
public Group createAndLoadGroup(String name) {
|
||||
Group group = this.plugin.getGroupManager().getOrMake(name);
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "groups");
|
||||
try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) {
|
||||
try (MongoCursor<Document> cursor = c.find(Filters.eq("_id", group.getName())).iterator()) {
|
||||
if (cursor.hasNext()) {
|
||||
Document d = cursor.next();
|
||||
group.loadNodesFromStorage(nodesFromDoc(d));
|
||||
@ -398,7 +350,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
@Override
|
||||
public Optional<Group> loadGroup(String name) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "groups");
|
||||
try (MongoCursor<Document> cursor = c.find(new Document("_id", name)).iterator()) {
|
||||
try (MongoCursor<Document> cursor = c.find(Filters.eq("_id", name)).iterator()) {
|
||||
if (!cursor.hasNext()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -432,20 +384,20 @@ public class MongoStorage implements StorageImplementation {
|
||||
public void saveGroup(Group group) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "groups");
|
||||
group.normalData().discardChanges();
|
||||
c.replaceOne(new Document("_id", group.getName()), groupToDoc(group), new ReplaceOptions().upsert(true));
|
||||
c.replaceOne(Filters.eq("_id", group.getName()), groupToDoc(group), new ReplaceOptions().upsert(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteGroup(Group group) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "groups");
|
||||
c.deleteOne(new Document("_id", group.getName()));
|
||||
c.deleteOne(Filters.eq("_id", group.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <N extends Node> List<NodeEntry<String, N>> searchGroupNodes(ConstraintNodeMatcher<N> constraint) throws Exception {
|
||||
List<NodeEntry<String, N>> held = new ArrayList<>();
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "groups");
|
||||
try (MongoCursor<Document> cursor = c.find().iterator()) {
|
||||
try (MongoCursor<Document> cursor = c.find(Filters.elemMatch("permissions", ConstraintMongoBuilder.INSTANCE.make(constraint.getConstraint(), "key"))).iterator()) {
|
||||
while (cursor.hasNext()) {
|
||||
Document d = cursor.next();
|
||||
String holder = d.getString("_id");
|
||||
@ -466,7 +418,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
public Track createAndLoadTrack(String name) {
|
||||
Track track = this.plugin.getTrackManager().getOrMake(name);
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "tracks");
|
||||
try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) {
|
||||
try (MongoCursor<Document> cursor = c.find(Filters.eq("_id", track.getName())).iterator()) {
|
||||
if (!cursor.hasNext()) {
|
||||
c.insertOne(trackToDoc(track));
|
||||
} else {
|
||||
@ -481,7 +433,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
@Override
|
||||
public Optional<Track> loadTrack(String name) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "tracks");
|
||||
try (MongoCursor<Document> cursor = c.find(new Document("_id", name)).iterator()) {
|
||||
try (MongoCursor<Document> cursor = c.find(Filters.eq("_id", name)).iterator()) {
|
||||
if (!cursor.hasNext()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -515,13 +467,13 @@ public class MongoStorage implements StorageImplementation {
|
||||
@Override
|
||||
public void saveTrack(Track track) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "tracks");
|
||||
c.replaceOne(new Document("_id", track.getName()), trackToDoc(track));
|
||||
c.replaceOne(Filters.eq("_id", track.getName()), trackToDoc(track));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTrack(Track track) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "tracks");
|
||||
c.deleteOne(new Document("_id", track.getName()));
|
||||
c.deleteOne(Filters.eq("_id", track.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -534,13 +486,13 @@ public class MongoStorage implements StorageImplementation {
|
||||
|
||||
// do the insert
|
||||
if (!username.equalsIgnoreCase(oldUsername)) {
|
||||
c.replaceOne(new Document("_id", uniqueId), new Document("_id", uniqueId).append("name", username), new ReplaceOptions().upsert(true));
|
||||
c.replaceOne(Filters.eq("_id", uniqueId), new Document("_id", uniqueId).append("name", username), new ReplaceOptions().upsert(true));
|
||||
}
|
||||
|
||||
PlayerSaveResultImpl result = PlayerSaveResultImpl.determineBaseResult(username, oldUsername);
|
||||
|
||||
Set<UUID> conflicting = new HashSet<>();
|
||||
try (MongoCursor<Document> cursor = c.find(new Document("name", username)).iterator()) {
|
||||
try (MongoCursor<Document> cursor = c.find(Filters.eq("name", username)).iterator()) {
|
||||
while (cursor.hasNext()) {
|
||||
conflicting.add(getDocumentId(cursor.next()));
|
||||
}
|
||||
@ -565,7 +517,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
@Override
|
||||
public UUID getPlayerUniqueId(String username) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "uuid");
|
||||
Document doc = c.find(new Document("name", username.toLowerCase(Locale.ROOT))).first();
|
||||
Document doc = c.find(Filters.eq("name", username.toLowerCase(Locale.ROOT))).first();
|
||||
if (doc != null) {
|
||||
return getDocumentId(doc);
|
||||
}
|
||||
@ -575,7 +527,7 @@ public class MongoStorage implements StorageImplementation {
|
||||
@Override
|
||||
public String getPlayerName(UUID uniqueId) {
|
||||
MongoCollection<Document> c = this.database.getCollection(this.prefix + "uuid");
|
||||
Document doc = c.find(new Document("_id", uniqueId)).first();
|
||||
Document doc = c.find(Filters.eq("_id", uniqueId)).first();
|
||||
if (doc != null) {
|
||||
String username = doc.get("name", String.class);
|
||||
if (username != null && !username.equals("null")) {
|
||||
@ -635,10 +587,9 @@ public class MongoStorage implements StorageImplementation {
|
||||
}
|
||||
|
||||
private static Document nodeToDoc(Node node) {
|
||||
Document document = new Document();
|
||||
|
||||
document.append("key", node.getKey());
|
||||
document.append("value", node.getValue());
|
||||
Document document = new Document()
|
||||
.append("key", node.getKey())
|
||||
.append("value", node.getValue());
|
||||
|
||||
Instant expiry = node.getExpiry();
|
||||
if (expiry != null) {
|
||||
@ -699,4 +650,62 @@ public class MongoStorage implements StorageImplementation {
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Document actionToDoc(Action action) {
|
||||
Document source = new Document()
|
||||
.append("uniqueId", action.getSource().getUniqueId())
|
||||
.append("name", action.getSource().getName());
|
||||
|
||||
Document target = new Document()
|
||||
.append("type", action.getTarget().getType().name())
|
||||
.append("name", action.getTarget().getName());
|
||||
if (action.getTarget().getUniqueId().isPresent()) {
|
||||
target.append("uniqueId", action.getTarget().getUniqueId().get());
|
||||
}
|
||||
|
||||
return new Document()
|
||||
.append("timestamp", action.getTimestamp().getEpochSecond())
|
||||
.append("source", source)
|
||||
.append("target", target)
|
||||
.append("description", action.getDescription());
|
||||
}
|
||||
|
||||
private static LoggedAction actionFromDoc(Document d) {
|
||||
if (d.containsKey("source")) {
|
||||
// new format
|
||||
Document source = d.get("source", Document.class);
|
||||
Document target = d.get("target", Document.class);
|
||||
|
||||
UUID targetUniqueId = null;
|
||||
if (target.containsKey("uniqueId")) {
|
||||
targetUniqueId = target.get("uniqueId", UUID.class);
|
||||
}
|
||||
|
||||
return LoggedAction.build()
|
||||
.timestamp(Instant.ofEpochSecond(d.getLong("timestamp")))
|
||||
.source(source.get("uniqueId", UUID.class))
|
||||
.sourceName(source.getString("name"))
|
||||
.targetType(LoggedAction.parseType(target.getString("type")))
|
||||
.target(targetUniqueId)
|
||||
.targetName(target.getString("name"))
|
||||
.description(d.getString("description"))
|
||||
.build();
|
||||
} else {
|
||||
// old format
|
||||
UUID actedUuid = null;
|
||||
if (d.containsKey("acted")) {
|
||||
actedUuid = d.get("acted", UUID.class);
|
||||
}
|
||||
|
||||
return LoggedAction.build()
|
||||
.timestamp(Instant.ofEpochSecond(d.getLong("timestamp")))
|
||||
.source(d.get("actor", UUID.class))
|
||||
.sourceName(d.getString("actorName"))
|
||||
.targetType(LoggedAction.parseTypeCharacter(d.getString("type").charAt(0)))
|
||||
.target(actedUuid)
|
||||
.targetName(d.getString("actedName"))
|
||||
.description(d.getString("action"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,8 +26,10 @@
|
||||
package me.lucko.luckperms.common.storage.implementation.split;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
@ -40,6 +42,7 @@ import me.lucko.luckperms.common.storage.misc.NodeEntry;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.model.PlayerSaveResult;
|
||||
import net.luckperms.api.node.Node;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -118,8 +121,8 @@ public class SplitStorage implements StorageImplementation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Log getLog() throws Exception {
|
||||
return implFor(SplitStorageType.LOG).getLog();
|
||||
public LogPage getLogPage(FilterList<Action> filters, @Nullable PageParameters page) throws Exception {
|
||||
return implFor(SplitStorageType.LOG).getLogPage(filters, page);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,12 +28,16 @@ package me.lucko.luckperms.common.storage.implementation.sql;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilterSqlBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateSqlBuilder;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdateStatistics;
|
||||
import me.lucko.luckperms.common.bulkupdate.PreparedStatementBuilder;
|
||||
import me.lucko.luckperms.common.context.serializer.ContextSetJsonSerializer;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.filter.sql.ConstraintSqlBuilder;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
@ -54,6 +58,7 @@ import net.luckperms.api.context.DefaultContextKeys;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
import net.luckperms.api.model.PlayerSaveResult;
|
||||
import net.luckperms.api.node.Node;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -127,6 +132,7 @@ public class SqlStorage implements StorageImplementation {
|
||||
|
||||
private static final String ACTION_INSERT = "INSERT INTO '{prefix}actions' (time, actor_uuid, actor_name, type, acted_uuid, acted_name, action) VALUES(?, ?, ?, ?, ?, ?, ?)";
|
||||
private static final String ACTION_SELECT_ALL = "SELECT * FROM '{prefix}actions'";
|
||||
private static final String ACTION_COUNT = "SELECT COUNT(*) FROM '{prefix}actions'";
|
||||
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
@ -242,18 +248,38 @@ public class SqlStorage implements StorageImplementation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Log getLog() throws SQLException {
|
||||
final Log.Builder log = Log.builder();
|
||||
public LogPage getLogPage(FilterList<Action> filter, @Nullable PageParameters page) throws SQLException {
|
||||
int count = 0;
|
||||
List<LoggedAction> content = new ArrayList<>();
|
||||
|
||||
try (Connection c = this.connectionFactory.getConnection()) {
|
||||
try (PreparedStatement ps = c.prepareStatement(this.statementProcessor.apply(ACTION_SELECT_ALL))) {
|
||||
ActionFilterSqlBuilder countSqlBuilder = new ActionFilterSqlBuilder();
|
||||
countSqlBuilder.builder().append(ACTION_COUNT);
|
||||
countSqlBuilder.visit(filter);
|
||||
|
||||
try (PreparedStatement ps = countSqlBuilder.builder().build(c, this.statementProcessor)) {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
count = rs.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionFilterSqlBuilder sqlBuilder = new ActionFilterSqlBuilder();
|
||||
sqlBuilder.builder().append(ACTION_SELECT_ALL);
|
||||
sqlBuilder.visit(filter);
|
||||
sqlBuilder.builder().append(" ORDER BY time DESC, id DESC");
|
||||
sqlBuilder.visit(page);
|
||||
|
||||
try (PreparedStatement ps = sqlBuilder.builder().build(c, this.statementProcessor)) {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
log.add(readAction(rs));
|
||||
content.add(readAction(rs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return log.build();
|
||||
return LogPage.of(content, page, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -262,18 +288,20 @@ public class SqlStorage implements StorageImplementation {
|
||||
|
||||
try (Connection c = this.connectionFactory.getConnection()) {
|
||||
if (bulkUpdate.getDataType().isIncludingUsers()) {
|
||||
String table = this.statementProcessor.apply("{prefix}user_permissions");
|
||||
try (PreparedStatement ps = bulkUpdate.buildAsSql().build(c, q -> q.replace("{table}", table))) {
|
||||
Function<String, String> tableReplacement = s -> s.replace("{table}", "{prefix}user_permissions");
|
||||
|
||||
BulkUpdateSqlBuilder sqlBuilder = new BulkUpdateSqlBuilder();
|
||||
sqlBuilder.visit(bulkUpdate);
|
||||
|
||||
try (PreparedStatement ps = sqlBuilder.builder().build(c, this.statementProcessor.compose(tableReplacement))) {
|
||||
if (bulkUpdate.isTrackingStatistics()) {
|
||||
PreparedStatementBuilder builder = new PreparedStatementBuilder();
|
||||
builder.append(USER_PERMISSIONS_SELECT_DISTINCT);
|
||||
bulkUpdate.appendConstraintsAsSql(builder);
|
||||
BulkUpdateSqlBuilder statsSqlBuilder = new BulkUpdateSqlBuilder();
|
||||
statsSqlBuilder.builder().append(USER_PERMISSIONS_SELECT_DISTINCT);
|
||||
statsSqlBuilder.visit(bulkUpdate.getFilters());
|
||||
|
||||
try (PreparedStatement lookup = builder.build(c, this.statementProcessor)) {
|
||||
try (PreparedStatement lookup = statsSqlBuilder.builder().build(c, this.statementProcessor)) {
|
||||
try (ResultSet rs = lookup.executeQuery()) {
|
||||
Set<UUID> uuids = new HashSet<>();
|
||||
|
||||
while (rs.next()) {
|
||||
uuids.add(Uuids.fromString(rs.getString("uuid")));
|
||||
}
|
||||
@ -281,7 +309,9 @@ public class SqlStorage implements StorageImplementation {
|
||||
stats.incrementAffectedUsers(uuids.size());
|
||||
}
|
||||
}
|
||||
stats.incrementAffectedNodes(ps.executeUpdate());
|
||||
|
||||
int rowsAffected = ps.executeUpdate();
|
||||
stats.incrementAffectedNodes(rowsAffected);
|
||||
} else {
|
||||
ps.execute();
|
||||
}
|
||||
@ -289,18 +319,20 @@ public class SqlStorage implements StorageImplementation {
|
||||
}
|
||||
|
||||
if (bulkUpdate.getDataType().isIncludingGroups()) {
|
||||
String table = this.statementProcessor.apply("{prefix}group_permissions");
|
||||
try (PreparedStatement ps = bulkUpdate.buildAsSql().build(c, q -> q.replace("{table}", table))) {
|
||||
Function<String, String> tableReplacement = s -> s.replace("{table}", "{prefix}group_permissions");
|
||||
|
||||
BulkUpdateSqlBuilder sqlBuilder = new BulkUpdateSqlBuilder();
|
||||
sqlBuilder.visit(bulkUpdate);
|
||||
|
||||
try (PreparedStatement ps = sqlBuilder.builder().build(c, this.statementProcessor.compose(tableReplacement))) {
|
||||
if (bulkUpdate.isTrackingStatistics()) {
|
||||
PreparedStatementBuilder builder = new PreparedStatementBuilder();
|
||||
builder.append(GROUP_PERMISSIONS_SELECT_ALL);
|
||||
bulkUpdate.appendConstraintsAsSql(builder);
|
||||
BulkUpdateSqlBuilder statsSqlBuilder = new BulkUpdateSqlBuilder();
|
||||
statsSqlBuilder.builder().append(GROUP_PERMISSIONS_SELECT_ALL);
|
||||
statsSqlBuilder.visit(bulkUpdate.getFilters());
|
||||
|
||||
try (PreparedStatement lookup = builder.build(c, this.statementProcessor)) {
|
||||
try (PreparedStatement lookup = statsSqlBuilder.builder().build(c, this.statementProcessor)) {
|
||||
try (ResultSet rs = lookup.executeQuery()) {
|
||||
Set<String> groups = new HashSet<>();
|
||||
|
||||
while (rs.next()) {
|
||||
groups.add(rs.getString("name"));
|
||||
}
|
||||
@ -308,7 +340,9 @@ public class SqlStorage implements StorageImplementation {
|
||||
stats.incrementAffectedGroups(groups.size());
|
||||
}
|
||||
}
|
||||
stats.incrementAffectedNodes(ps.executeUpdate());
|
||||
|
||||
int rowsAffected = ps.executeUpdate();
|
||||
stats.incrementAffectedNodes(rowsAffected);
|
||||
} else {
|
||||
ps.execute();
|
||||
}
|
||||
@ -430,12 +464,14 @@ public class SqlStorage implements StorageImplementation {
|
||||
|
||||
@Override
|
||||
public <N extends Node> List<NodeEntry<UUID, N>> searchUserNodes(ConstraintNodeMatcher<N> constraint) throws SQLException {
|
||||
PreparedStatementBuilder builder = new PreparedStatementBuilder().append(USER_PERMISSIONS_SELECT_PERMISSION);
|
||||
constraint.getConstraint().appendSql(builder, "permission");
|
||||
ConstraintSqlBuilder sqlBuilder = new ConstraintSqlBuilder();
|
||||
sqlBuilder.builder().append(USER_PERMISSIONS_SELECT_PERMISSION);
|
||||
sqlBuilder.builder().append("permission ");
|
||||
sqlBuilder.visit(constraint.getConstraint());
|
||||
|
||||
List<NodeEntry<UUID, N>> held = new ArrayList<>();
|
||||
try (Connection c = this.connectionFactory.getConnection()) {
|
||||
try (PreparedStatement ps = builder.build(c, this.statementProcessor)) {
|
||||
try (PreparedStatement ps = sqlBuilder.builder().build(c, this.statementProcessor)) {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
UUID holder = UUID.fromString(rs.getString("uuid"));
|
||||
@ -534,12 +570,14 @@ public class SqlStorage implements StorageImplementation {
|
||||
|
||||
@Override
|
||||
public <N extends Node> List<NodeEntry<String, N>> searchGroupNodes(ConstraintNodeMatcher<N> constraint) throws SQLException {
|
||||
PreparedStatementBuilder builder = new PreparedStatementBuilder().append(GROUP_PERMISSIONS_SELECT_PERMISSION);
|
||||
constraint.getConstraint().appendSql(builder, "permission");
|
||||
ConstraintSqlBuilder sqlBuilder = new ConstraintSqlBuilder();
|
||||
sqlBuilder.builder().append(GROUP_PERMISSIONS_SELECT_PERMISSION);
|
||||
sqlBuilder.builder().append("permission ");
|
||||
sqlBuilder.visit(constraint.getConstraint());
|
||||
|
||||
List<NodeEntry<String, N>> held = new ArrayList<>();
|
||||
try (Connection c = this.connectionFactory.getConnection()) {
|
||||
try (PreparedStatement ps = builder.build(c, this.statementProcessor)) {
|
||||
try (PreparedStatement ps = sqlBuilder.builder().build(c, this.statementProcessor)) {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
String holder = rs.getString("name");
|
||||
@ -737,7 +775,7 @@ public class SqlStorage implements StorageImplementation {
|
||||
ps.setLong(1, action.getTimestamp().getEpochSecond());
|
||||
ps.setString(2, action.getSource().getUniqueId().toString());
|
||||
ps.setString(3, action.getSource().getName());
|
||||
ps.setString(4, Character.toString(LoggedAction.getTypeCharacter(action.getTarget().getType())));
|
||||
ps.setString(4, LoggedAction.getTypeString(action.getTarget().getType()));
|
||||
ps.setString(5, action.getTarget().getUniqueId().map(UUID::toString).orElse("null"));
|
||||
ps.setString(6, action.getTarget().getName());
|
||||
ps.setString(7, action.getDescription());
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage.implementation.sql.builder;
|
||||
|
||||
public class AbstractSqlBuilder {
|
||||
|
||||
protected final PreparedStatementBuilder builder = new PreparedStatementBuilder();
|
||||
|
||||
public PreparedStatementBuilder builder() {
|
||||
return this.builder;
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate;
|
||||
package me.lucko.luckperms.common.storage.implementation.sql.builder;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
@ -57,12 +57,21 @@ public class PreparedStatementBuilder {
|
||||
}
|
||||
|
||||
public PreparedStatement build(Connection connection, Function<String, String> mapping) throws SQLException {
|
||||
PreparedStatement statement = connection.prepareStatement(mapping.apply(this.sb.toString()));
|
||||
for (int i = 0; i < this.variables.size(); i++) {
|
||||
String var = this.variables.get(i);
|
||||
statement.setString(i + 1, var);
|
||||
PreparedStatement statement = null;
|
||||
try {
|
||||
statement = connection.prepareStatement(mapping.apply(this.sb.toString()));
|
||||
for (int i = 0; i < this.variables.size(); i++) {
|
||||
String var = this.variables.get(i);
|
||||
statement.setString(i + 1, var);
|
||||
}
|
||||
return statement;
|
||||
} catch (SQLException e) {
|
||||
// if an exception is thrown, any try-with-resources block above this call won't be able to close the statement
|
||||
if (statement != null) {
|
||||
statement.close();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
|
||||
public String toReadableString() {
|
||||
@ -72,4 +81,8 @@ public class PreparedStatementBuilder {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public String toQueryString() {
|
||||
return this.sb.toString();
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@
|
||||
|
||||
package me.lucko.luckperms.common.storage.misc;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -54,6 +56,10 @@ public class StorageCredentials {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public StorageCredentials(String address, String database, String username, String password) {
|
||||
this(address, database, username, password, 10, 10, 1800000, 0, 5000, ImmutableMap.of());
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return Objects.requireNonNull(this.address, "address");
|
||||
}
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.util;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
/**
|
||||
* Base class for an interface which can perform operations asynchronously and return {@link CompletableFuture}s
|
||||
*/
|
||||
public abstract class AsyncInterface {
|
||||
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
protected AsyncInterface(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
protected <T> CompletableFuture<T> future(Callable<T> supplier) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return supplier.call();
|
||||
} catch (Exception e) {
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, this.plugin.getBootstrap().getScheduler().async());
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> future(Throwing.Runnable runnable) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, this.plugin.getBootstrap().getScheduler().async());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.actionlog;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilterMongoBuilder;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import org.bson.BsonDocument;
|
||||
import org.bson.UuidRepresentation;
|
||||
import org.bson.codecs.configuration.CodecRegistries;
|
||||
import org.bson.codecs.configuration.CodecRegistry;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ActionFilterMongoTest {
|
||||
|
||||
private static Stream<Arguments> testQueries() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
ActionFilters.source(UUID.fromString("725d585e-4ff1-4f18-acca-6ac538364080")),
|
||||
// {"$and": [{"source.uniqueId": {"$binary": {"base64": "cl1YXk/xTxisymrFODZAgA==", "subType": "04"}}}]}
|
||||
"{\"$and\": [{\"source.uniqueId\": {\"$binary\": {\"base64\": \"cl1YXk/xTxisymrFODZAgA==\", \"subType\": \"04\"}}}]}"
|
||||
),
|
||||
Arguments.of(
|
||||
ActionFilters.user(UUID.fromString("725d585e-4ff1-4f18-acca-6ac538364080")),
|
||||
// {"$and": [{"target.type": "USER"}, {"target.uniqueId": {"$binary": {"base64": "cl1YXk/xTxisymrFODZAgA==", "subType": "04"}}}]}
|
||||
"{\"$and\": [{\"target.type\": \"USER\"}, {\"target.uniqueId\": {\"$binary\": {\"base64\": \"cl1YXk/xTxisymrFODZAgA==\", \"subType\": \"04\"}}}]}"
|
||||
),
|
||||
Arguments.of(
|
||||
ActionFilters.group("test"),
|
||||
// {"$and": [{"target.type": "GROUP"}, {"target.name": "test"}]}
|
||||
"{\"$and\": [{\"target.type\": \"GROUP\"}, {\"target.name\": \"test\"}]}"
|
||||
),
|
||||
Arguments.of(
|
||||
ActionFilters.track("test"),
|
||||
// {"$and": [{"target.type": "TRACK"}, {"target.name": "test"}]}
|
||||
"{\"$and\": [{\"target.type\": \"TRACK\"}, {\"target.name\": \"test\"}]}"
|
||||
),
|
||||
Arguments.of(
|
||||
ActionFilters.search("test"),
|
||||
// {"$or": [{"source.name": {"$regularExpression": {"pattern": ".*test.*", "options": "i"}}}, {"target.name": {"$regularExpression": {"pattern": ".*test.*", "options": "i"}}}, {"description": {"$regularExpression": {"pattern": ".*test.*", "options": "i"}}}]}
|
||||
"{\"$or\": [{\"source.name\": {\"$regularExpression\": {\"pattern\": \".*test.*\", \"options\": \"i\"}}}, {\"target.name\": {\"$regularExpression\": {\"pattern\": \".*test.*\", \"options\": \"i\"}}}, {\"description\": {\"$regularExpression\": {\"pattern\": \".*test.*\", \"options\": \"i\"}}}]}"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0}")
|
||||
@MethodSource
|
||||
public void testQueries(FilterList<Action> filters, String expectedQuery) {
|
||||
Bson bson = ActionFilterMongoBuilder.INSTANCE.make(filters);
|
||||
|
||||
CodecRegistry codec = CodecRegistries.withUuidRepresentation(Bson.DEFAULT_CODEC_REGISTRY, UuidRepresentation.STANDARD);
|
||||
String json = bson.toBsonDocument(BsonDocument.class, codec).toJson();
|
||||
|
||||
assertEquals(expectedQuery, json);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.actionlog;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilterSqlBuilder;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ActionFilterSqlTest {
|
||||
|
||||
private static Stream<Arguments> testFiltersSql() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
ActionFilters.source(UUID.fromString("725d585e-4ff1-4f18-acca-6ac538364080")),
|
||||
"WHERE actor_uuid = 725d585e-4ff1-4f18-acca-6ac538364080",
|
||||
"WHERE actor_uuid = ?"
|
||||
),
|
||||
Arguments.of(
|
||||
ActionFilters.user(UUID.fromString("725d585e-4ff1-4f18-acca-6ac538364080")),
|
||||
"WHERE type = U AND acted_uuid = 725d585e-4ff1-4f18-acca-6ac538364080",
|
||||
"WHERE type = ? AND acted_uuid = ?"
|
||||
),
|
||||
Arguments.of(
|
||||
ActionFilters.group("test"),
|
||||
"WHERE type = G AND acted_name = test",
|
||||
"WHERE type = ? AND acted_name = ?"
|
||||
),
|
||||
Arguments.of(
|
||||
ActionFilters.track("test"),
|
||||
"WHERE type = T AND acted_name = test",
|
||||
"WHERE type = ? AND acted_name = ?"
|
||||
),
|
||||
Arguments.of(
|
||||
ActionFilters.search("test"),
|
||||
"WHERE actor_name LIKE %test% OR acted_name LIKE %test% OR action LIKE %test%",
|
||||
"WHERE actor_name LIKE ? OR acted_name LIKE ? OR action LIKE ?"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {1}")
|
||||
@MethodSource
|
||||
public void testFiltersSql(FilterList<Action> filters, String expectedSql, String expectedSqlParams) {
|
||||
ActionFilterSqlBuilder sqlBuilder = new ActionFilterSqlBuilder();
|
||||
sqlBuilder.visit(filters);
|
||||
|
||||
assertEquals(" " + expectedSql, sqlBuilder.builder().toReadableString());
|
||||
assertEquals(" " + expectedSqlParams, sqlBuilder.builder().toQueryString());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.actionlog;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.filter.FilterList;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ActionFilterTest {
|
||||
|
||||
@Test
|
||||
public void testSource() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
FilterList<Action> filter = ActionFilters.source(uuid);
|
||||
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.USER)
|
||||
.target(UUID.randomUUID())
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertTrue(filter.evaluate(LoggedAction.build()
|
||||
.source(uuid)
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.USER)
|
||||
.target(UUID.randomUUID())
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUser() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
FilterList<Action> filter = ActionFilters.user(uuid);
|
||||
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.USER)
|
||||
.target(UUID.randomUUID())
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.target(uuid)
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertTrue(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.USER)
|
||||
.target(uuid)
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroup() {
|
||||
String name = "test";
|
||||
FilterList<Action> filter = ActionFilters.group(name);
|
||||
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.USER)
|
||||
.target(UUID.randomUUID())
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName("aaaaa")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.TRACK)
|
||||
.targetName(name)
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertTrue(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName(name)
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrack() {
|
||||
String name = "test";
|
||||
FilterList<Action> filter = ActionFilters.track(name);
|
||||
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.USER)
|
||||
.target(UUID.randomUUID())
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.TRACK)
|
||||
.targetName("aaaaa")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName(name)
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertTrue(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.TRACK)
|
||||
.targetName(name)
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch() {
|
||||
FilterList<Action> filter = ActionFilters.search("bar");
|
||||
|
||||
assertFalse(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
|
||||
assertTrue(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("foobarbaz")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName("Test Target")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertTrue(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName("foobarbaz")
|
||||
.description("test 123")
|
||||
.build())
|
||||
);
|
||||
assertTrue(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName("Test Target")
|
||||
.description("foo bar baz")
|
||||
.build())
|
||||
);
|
||||
assertTrue(filter.evaluate(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("bar")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName("bar")
|
||||
.description("bar")
|
||||
.build())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.action.BulkUpdateAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class BulkUpdateSqlTest {
|
||||
|
||||
private static Stream<Arguments> testSimpleActionSql() {
|
||||
return Stream.of(
|
||||
Arguments.of("DELETE FROM {table}", DeleteAction.create()),
|
||||
Arguments.of("UPDATE {table} SET permission=foo", UpdateAction.of(BulkUpdateField.PERMISSION, "foo")),
|
||||
Arguments.of("UPDATE {table} SET server=foo", UpdateAction.of(BulkUpdateField.SERVER, "foo")),
|
||||
Arguments.of("UPDATE {table} SET world=foo", UpdateAction.of(BulkUpdateField.WORLD, "foo"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0}")
|
||||
@MethodSource
|
||||
public void testSimpleActionSql(String expectedSql, BulkUpdateAction action) {
|
||||
BulkUpdate update = BulkUpdateBuilder.create()
|
||||
.action(action)
|
||||
.build();
|
||||
|
||||
BulkUpdateSqlBuilder sqlBuilder = new BulkUpdateSqlBuilder();
|
||||
sqlBuilder.visit(update);
|
||||
String sql = sqlBuilder.builder().toReadableString();
|
||||
|
||||
assertEquals(expectedSql, sql);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> testQueryFilterSql() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
"DELETE FROM {table} WHERE permission = foo",
|
||||
DeleteAction.create(),
|
||||
BulkUpdateField.PERMISSION,
|
||||
Comparison.EQUAL,
|
||||
"foo"
|
||||
),
|
||||
Arguments.of(
|
||||
"DELETE FROM {table} WHERE permission != foo",
|
||||
DeleteAction.create(),
|
||||
BulkUpdateField.PERMISSION,
|
||||
Comparison.NOT_EQUAL,
|
||||
"foo"
|
||||
),
|
||||
Arguments.of(
|
||||
"DELETE FROM {table} WHERE permission LIKE foo",
|
||||
DeleteAction.create(),
|
||||
BulkUpdateField.PERMISSION,
|
||||
Comparison.SIMILAR,
|
||||
"foo"
|
||||
),
|
||||
Arguments.of(
|
||||
"DELETE FROM {table} WHERE permission NOT LIKE foo",
|
||||
DeleteAction.create(),
|
||||
BulkUpdateField.PERMISSION,
|
||||
Comparison.NOT_SIMILAR,
|
||||
"foo"
|
||||
),
|
||||
Arguments.of(
|
||||
"UPDATE {table} SET server=foo WHERE world = bar",
|
||||
UpdateAction.of(BulkUpdateField.SERVER, "foo"),
|
||||
BulkUpdateField.WORLD,
|
||||
Comparison.EQUAL,
|
||||
"bar"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0}")
|
||||
@MethodSource
|
||||
public void testQueryFilterSql(String expectedSql, BulkUpdateAction action, BulkUpdateField field, Comparison comparison, String value) {
|
||||
BulkUpdate update = BulkUpdateBuilder.create()
|
||||
.action(action)
|
||||
.filter(field, comparison, value)
|
||||
.build();
|
||||
|
||||
BulkUpdateSqlBuilder sqlBuilder = new BulkUpdateSqlBuilder();
|
||||
sqlBuilder.visit(update);
|
||||
String sql = sqlBuilder.builder().toReadableString();
|
||||
|
||||
assertEquals(expectedSql, sql);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryFilterMultipleSql() {
|
||||
BulkUpdate update = BulkUpdateBuilder.create()
|
||||
.action(UpdateAction.of(BulkUpdateField.SERVER, "foo"))
|
||||
.filter(BulkUpdateField.WORLD, Comparison.EQUAL, "bar")
|
||||
.filter(BulkUpdateField.PERMISSION, Comparison.SIMILAR, "baz")
|
||||
.filter(BulkUpdateField.SERVER, Comparison.NOT_EQUAL, "aaa")
|
||||
.filter(BulkUpdateField.WORLD, Comparison.NOT_SIMILAR, "bbb")
|
||||
.build();
|
||||
|
||||
BulkUpdateSqlBuilder sqlBuilder = new BulkUpdateSqlBuilder();
|
||||
sqlBuilder.visit(update);
|
||||
assertEquals(
|
||||
"UPDATE {table} SET server=? WHERE world = ? AND permission LIKE ? AND server != ? AND world NOT LIKE ?",
|
||||
sqlBuilder.builder().toQueryString()
|
||||
);
|
||||
assertEquals(
|
||||
"UPDATE {table} SET server=foo WHERE world = bar AND permission LIKE baz AND server != aaa AND world NOT LIKE bbb",
|
||||
sqlBuilder.builder().toReadableString()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -26,25 +26,17 @@
|
||||
package me.lucko.luckperms.common.bulkupdate;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.Action;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.DeleteAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.action.UpdateAction;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.Constraint;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.Query;
|
||||
import me.lucko.luckperms.common.bulkupdate.query.QueryField;
|
||||
import me.lucko.luckperms.common.filter.Comparison;
|
||||
import me.lucko.luckperms.common.model.HolderType;
|
||||
import me.lucko.luckperms.common.node.types.Permission;
|
||||
import net.luckperms.api.node.Node;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ -53,9 +45,9 @@ public class BulkUpdateTest {
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
BulkUpdate update = BulkUpdateBuilder.create()
|
||||
.action(UpdateAction.of(QueryField.SERVER, "foo"))
|
||||
.query(Query.of(QueryField.WORLD, Constraint.of(StandardComparison.EQUAL, "bar")))
|
||||
.query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.SIMILAR, "hello%")))
|
||||
.action(UpdateAction.of(BulkUpdateField.SERVER, "foo"))
|
||||
.filter(BulkUpdateField.WORLD, Comparison.EQUAL, "bar")
|
||||
.filter(BulkUpdateField.PERMISSION, Comparison.SIMILAR, "hello%")
|
||||
.trackStatistics(true)
|
||||
.build();
|
||||
|
||||
@ -88,8 +80,8 @@ public class BulkUpdateTest {
|
||||
public void testDelete() {
|
||||
BulkUpdate update = BulkUpdateBuilder.create()
|
||||
.action(DeleteAction.create())
|
||||
.query(Query.of(QueryField.WORLD, Constraint.of(StandardComparison.EQUAL, "bar")))
|
||||
.query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.SIMILAR, "hello%")))
|
||||
.filter(BulkUpdateField.WORLD, Comparison.EQUAL, "bar")
|
||||
.filter(BulkUpdateField.PERMISSION, Comparison.SIMILAR, "hello%")
|
||||
.trackStatistics(true)
|
||||
.build();
|
||||
|
||||
@ -115,74 +107,4 @@ public class BulkUpdateTest {
|
||||
assertEquals(0, statistics.getAffectedGroups());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> testSimpleActionSql() {
|
||||
return Stream.of(
|
||||
Arguments.of("DELETE FROM {table}", DeleteAction.create()),
|
||||
Arguments.of("UPDATE {table} SET permission=foo", UpdateAction.of(QueryField.PERMISSION, "foo")),
|
||||
Arguments.of("UPDATE {table} SET server=foo", UpdateAction.of(QueryField.SERVER, "foo")),
|
||||
Arguments.of("UPDATE {table} SET world=foo", UpdateAction.of(QueryField.WORLD, "foo"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0}")
|
||||
@MethodSource
|
||||
public void testSimpleActionSql(String expectedSql, Action action) {
|
||||
BulkUpdate update = BulkUpdateBuilder.create()
|
||||
.action(action)
|
||||
.build();
|
||||
assertEquals(expectedSql, update.buildAsSql().toReadableString());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> testQueryFilterSql() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
"DELETE FROM {table} WHERE permission = foo",
|
||||
DeleteAction.create(),
|
||||
Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.EQUAL, "foo"))
|
||||
),
|
||||
Arguments.of(
|
||||
"DELETE FROM {table} WHERE permission != foo",
|
||||
DeleteAction.create(),
|
||||
Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.NOT_EQUAL, "foo"))
|
||||
),
|
||||
Arguments.of(
|
||||
"DELETE FROM {table} WHERE permission LIKE foo",
|
||||
DeleteAction.create(),
|
||||
Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.SIMILAR, "foo"))
|
||||
),
|
||||
Arguments.of(
|
||||
"DELETE FROM {table} WHERE permission NOT LIKE foo",
|
||||
DeleteAction.create(),
|
||||
Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.NOT_SIMILAR, "foo"))
|
||||
),
|
||||
Arguments.of(
|
||||
"UPDATE {table} SET server=foo WHERE world = bar",
|
||||
UpdateAction.of(QueryField.SERVER, "foo"),
|
||||
Query.of(QueryField.WORLD, Constraint.of(StandardComparison.EQUAL, "bar"))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0}")
|
||||
@MethodSource
|
||||
public void testQueryFilterSql(String expectedSql, Action action, Query query) {
|
||||
BulkUpdate update = BulkUpdateBuilder.create()
|
||||
.action(action)
|
||||
.query(query)
|
||||
.build();
|
||||
assertEquals(expectedSql, update.buildAsSql().toReadableString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryFilterMultipleSql() {
|
||||
BulkUpdate update = BulkUpdateBuilder.create()
|
||||
.action(UpdateAction.of(QueryField.SERVER, "foo"))
|
||||
.query(Query.of(QueryField.WORLD, Constraint.of(StandardComparison.EQUAL, "bar")))
|
||||
.query(Query.of(QueryField.PERMISSION, Constraint.of(StandardComparison.SIMILAR, "baz")))
|
||||
.build();
|
||||
|
||||
String expected = "UPDATE {table} SET server=foo WHERE world = bar AND permission LIKE baz";
|
||||
assertEquals(expected, update.buildAsSql().toReadableString());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,9 +23,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate;
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparison.StandardComparison;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
@ -43,8 +42,10 @@ public class ComparisonTest {
|
||||
"'', foo, false",
|
||||
})
|
||||
public void testEquals(String expression, String test, boolean expected) {
|
||||
assertEquals(expected, StandardComparison.EQUAL.compile(expression).test(test));
|
||||
assertEquals(!expected, StandardComparison.NOT_EQUAL.compile(expression).test(test));
|
||||
assertEquals(expected, ConstraintFactory.STRINGS.build(Comparison.EQUAL, expression).evaluate(test));
|
||||
assertEquals(expected, ConstraintFactory.STRINGS.build(Comparison.EQUAL, test).evaluate(expression));
|
||||
assertEquals(!expected, ConstraintFactory.STRINGS.build(Comparison.NOT_EQUAL, expression).evaluate(test));
|
||||
assertEquals(!expected, ConstraintFactory.STRINGS.build(Comparison.NOT_EQUAL, test).evaluate(expression));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0} {1}")
|
||||
@ -72,8 +73,8 @@ public class ComparisonTest {
|
||||
"_ar, bbar, false",
|
||||
})
|
||||
public void testSimilar(String expression, String test, boolean expected) {
|
||||
assertEquals(expected, StandardComparison.SIMILAR.compile(expression).test(test));
|
||||
assertEquals(!expected, StandardComparison.NOT_SIMILAR.compile(expression).test(test));
|
||||
assertEquals(expected, ConstraintFactory.STRINGS.build(Comparison.SIMILAR, expression).evaluate(test));
|
||||
assertEquals(!expected, ConstraintFactory.STRINGS.build(Comparison.NOT_SIMILAR, expression).evaluate(test));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import me.lucko.luckperms.common.filter.mongo.FilterMongoBuilder;
|
||||
import org.bson.BsonDocument;
|
||||
import org.bson.UuidRepresentation;
|
||||
import org.bson.codecs.configuration.CodecRegistries;
|
||||
import org.bson.codecs.configuration.CodecRegistry;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class FilterMongoTest {
|
||||
|
||||
private static Stream<Arguments> testQueries() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
FilterList.empty(),
|
||||
// {}
|
||||
"{}"
|
||||
),
|
||||
Arguments.of(
|
||||
FilterList.and(
|
||||
TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS)
|
||||
),
|
||||
// {"$and": [{"foo": "hello"}]}
|
||||
"{\"$and\": [{\"foo\": \"hello\"}]}"
|
||||
),
|
||||
Arguments.of(
|
||||
FilterList.and(
|
||||
TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS),
|
||||
TestField.BAR.isEqualTo("world", ConstraintFactory.STRINGS)
|
||||
),
|
||||
// {"$and": [{"foo": "hello"}, {"bar": "world"}]}
|
||||
"{\"$and\": [{\"foo\": \"hello\"}, {\"bar\": \"world\"}]}"
|
||||
),
|
||||
Arguments.of(
|
||||
FilterList.or(
|
||||
TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS)
|
||||
),
|
||||
// {"$or": [{"foo": "hello"}]}
|
||||
"{\"$or\": [{\"foo\": \"hello\"}]}"
|
||||
),
|
||||
Arguments.of(
|
||||
FilterList.or(
|
||||
TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS),
|
||||
TestField.BAR.isEqualTo("world", ConstraintFactory.STRINGS)
|
||||
),
|
||||
// {"$or": [{"foo": "hello"}, {"bar": "world"}]}
|
||||
"{\"$or\": [{\"foo\": \"hello\"}, {\"bar\": \"world\"}]}"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0}")
|
||||
@MethodSource
|
||||
public void testQueries(FilterList<Object> filters, String expectedQuery) {
|
||||
Bson bson = new TestFilterMongoBuilder().make(filters);
|
||||
|
||||
CodecRegistry codec = CodecRegistries.withUuidRepresentation(Bson.DEFAULT_CODEC_REGISTRY, UuidRepresentation.STANDARD);
|
||||
String json = bson.toBsonDocument(BsonDocument.class, codec).toJson();
|
||||
|
||||
assertEquals(expectedQuery, json);
|
||||
}
|
||||
|
||||
private enum TestField implements FilterField<Object, String> {
|
||||
FOO, BAR;
|
||||
|
||||
@Override
|
||||
public String getValue(Object object) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestFilterMongoBuilder extends FilterMongoBuilder<Object> {
|
||||
|
||||
@Override
|
||||
public String mapFieldName(FilterField<Object, ?> field) {
|
||||
return field.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import me.lucko.luckperms.common.filter.sql.FilterSqlBuilder;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class FilterSqlTest {
|
||||
|
||||
private static Stream<Arguments> testQueries() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
FilterList.empty(),
|
||||
"",
|
||||
""
|
||||
),
|
||||
Arguments.of(
|
||||
FilterList.and(
|
||||
TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS)
|
||||
),
|
||||
" WHERE foo = hello",
|
||||
" WHERE foo = ?"
|
||||
),
|
||||
Arguments.of(
|
||||
FilterList.and(
|
||||
TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS),
|
||||
TestField.BAR.isEqualTo("world", ConstraintFactory.STRINGS)
|
||||
),
|
||||
" WHERE foo = hello AND bar = world",
|
||||
" WHERE foo = ? AND bar = ?"
|
||||
),
|
||||
Arguments.of(
|
||||
FilterList.or(
|
||||
TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS)
|
||||
),
|
||||
" WHERE foo = hello",
|
||||
" WHERE foo = ?"
|
||||
),
|
||||
Arguments.of(
|
||||
FilterList.or(
|
||||
TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS),
|
||||
TestField.BAR.isEqualTo("world", ConstraintFactory.STRINGS)
|
||||
),
|
||||
" WHERE foo = hello OR bar = world",
|
||||
" WHERE foo = ? OR bar = ?"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0}")
|
||||
@MethodSource
|
||||
public void testQueries(FilterList<Object> filters, String expectedSql, String expectedSqlParams) {
|
||||
TestFilterMongoBuilder sqlBuilder = new TestFilterMongoBuilder();
|
||||
sqlBuilder.visit(filters);
|
||||
|
||||
System.out.println(sqlBuilder.builder().toReadableString());
|
||||
System.out.println(sqlBuilder.builder().toQueryString());
|
||||
|
||||
assertEquals(expectedSql, sqlBuilder.builder().toReadableString());
|
||||
assertEquals(expectedSqlParams, sqlBuilder.builder().toQueryString());
|
||||
}
|
||||
|
||||
private enum TestField implements FilterField<Object, String> {
|
||||
FOO, BAR;
|
||||
|
||||
@Override
|
||||
public String getValue(Object object) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestFilterMongoBuilder extends FilterSqlBuilder<Object> {
|
||||
|
||||
@Override
|
||||
public void visitFieldName(FilterField<Object, ?> field) {
|
||||
this.builder.append(field.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.filter;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class PageParametersTest {
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0} {1}")
|
||||
@CsvSource({
|
||||
"5, 150, 30",
|
||||
"151, 150, 1",
|
||||
"150, 150, 1",
|
||||
"149, 150, 2",
|
||||
"1, 1, 1",
|
||||
"1, 0, 0",
|
||||
"10, 0, 0",
|
||||
})
|
||||
public void testMaxPage(int pageSize, int total, int expectedMaxPage) {
|
||||
int maxPage = new PageParameters(pageSize, 1).getMaxPage(total);
|
||||
assertEquals(expectedMaxPage, maxPage);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import me.lucko.luckperms.common.actionlog.LogPage;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.actionlog.filter.ActionFilters;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.config.LuckPermsConfiguration;
|
||||
import me.lucko.luckperms.common.event.EventDispatcher;
|
||||
import me.lucko.luckperms.common.filter.PageParameters;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.PrimaryGroupHolder;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.model.manager.group.GroupManager;
|
||||
import me.lucko.luckperms.common.model.manager.group.StandardGroupManager;
|
||||
import me.lucko.luckperms.common.model.manager.user.StandardUserManager;
|
||||
import me.lucko.luckperms.common.model.manager.user.UserManager;
|
||||
import me.lucko.luckperms.common.node.types.Inheritance;
|
||||
import me.lucko.luckperms.common.node.types.Permission;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
|
||||
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
|
||||
import me.lucko.luckperms.common.storage.implementation.StorageImplementation;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.model.PlayerSaveResult;
|
||||
import net.luckperms.api.model.PlayerSaveResult.Outcome;
|
||||
import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.node.Node;
|
||||
import net.luckperms.api.node.types.InheritanceNode;
|
||||
import net.luckperms.api.node.types.PermissionNode;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.AdditionalAnswers.answer;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public abstract class AbstractStorageTest {
|
||||
|
||||
@Mock protected LuckPermsPlugin plugin;
|
||||
@Mock protected LuckPermsBootstrap bootstrap;
|
||||
@Mock protected LuckPermsConfiguration configuration;
|
||||
|
||||
private StorageImplementation storage;
|
||||
|
||||
@BeforeEach
|
||||
public final void setupMocksAndStorage() throws Exception {
|
||||
lenient().when(this.plugin.getBootstrap()).thenReturn(this.bootstrap);
|
||||
lenient().when(this.plugin.getConfiguration()).thenReturn(this.configuration);
|
||||
lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class));
|
||||
lenient().when(this.bootstrap.getScheduler()).thenReturn(mock(SchedulerAdapter.class));
|
||||
lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION)).thenReturn(PrimaryGroupHolder.AllParentsByWeight::new);
|
||||
lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION_METHOD)).thenReturn("parents-by-weight");
|
||||
lenient().when(this.bootstrap.getResourceStream(anyString()))
|
||||
.then(answer((String path) -> AbstractStorageTest.class.getClassLoader().getResourceAsStream(path)));
|
||||
lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class));
|
||||
|
||||
this.storage = makeStorage(this.plugin);
|
||||
this.storage.init();
|
||||
}
|
||||
|
||||
protected abstract StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception;
|
||||
|
||||
protected void cleanupResources() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public final void shutdownStorage() {
|
||||
this.storage.shutdown();
|
||||
cleanupResources();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionLog() throws Exception {
|
||||
UUID sourceUuid = UUID.randomUUID();
|
||||
UUID targetUuid = UUID.randomUUID();
|
||||
|
||||
Instant baseTime = Instant.now();
|
||||
|
||||
Function<Integer, LoggedAction> mockAction = i -> LoggedAction.build()
|
||||
.source(i % 2 == 0 ? sourceUuid : UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.USER)
|
||||
.target(targetUuid)
|
||||
.targetName("Test Target")
|
||||
.description("hello " + i)
|
||||
.timestamp(baseTime.plusSeconds(i))
|
||||
.build();
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
this.storage.logAction(mockAction.apply(-i));
|
||||
}
|
||||
for (int i = 0; i < 100; i++) {
|
||||
this.storage.logAction(mockAction.apply(i));
|
||||
}
|
||||
for (int i = 100; i < 200; i++) {
|
||||
this.storage.logAction(mockAction.apply(-i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
this.storage.logAction(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.GROUP)
|
||||
.targetName(i % 2 == 0 ? "test_group" : "dummy")
|
||||
.description("group test " + i)
|
||||
.timestamp(baseTime)
|
||||
.build());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
this.storage.logAction(LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.TRACK)
|
||||
.targetName(i % 2 == 0 ? "test_track" : "dummy")
|
||||
.description("track test " + i)
|
||||
.timestamp(baseTime)
|
||||
.build());
|
||||
}
|
||||
|
||||
LogPage page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(5, 1));
|
||||
assertEquals(ImmutableList.of(
|
||||
mockAction.apply(98),
|
||||
mockAction.apply(96),
|
||||
mockAction.apply(94),
|
||||
mockAction.apply(92),
|
||||
mockAction.apply(90)
|
||||
), page.getContent());
|
||||
List<Integer> positions = page.getNumberedContent().stream().map(LogPage.Entry::position).collect(Collectors.toList());
|
||||
assertEquals(ImmutableList.of(1, 2, 3, 4, 5), positions);
|
||||
assertEquals(150, page.getTotalEntries());
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(5, 3));
|
||||
assertEquals(ImmutableList.of(
|
||||
mockAction.apply(78),
|
||||
mockAction.apply(76),
|
||||
mockAction.apply(74),
|
||||
mockAction.apply(72),
|
||||
mockAction.apply(70)
|
||||
), page.getContent());
|
||||
positions = page.getNumberedContent().stream().map(LogPage.Entry::position).collect(Collectors.toList());
|
||||
assertEquals(ImmutableList.of(11, 12, 13, 14, 15), positions);
|
||||
assertEquals(150, page.getTotalEntries());
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(5, 31));
|
||||
assertEquals(150, page.getTotalEntries());
|
||||
assertEquals(0, page.getContent().size());
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(500, 1));
|
||||
assertEquals(150, page.getTotalEntries());
|
||||
assertEquals(150, page.getContent().size());
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.source(sourceUuid), null);
|
||||
assertEquals(150, page.getTotalEntries());
|
||||
assertEquals(150, page.getContent().size());
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.all(), null);
|
||||
assertEquals(320, page.getTotalEntries());
|
||||
assertEquals(320, page.getContent().size());
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.user(targetUuid), new PageParameters(5, 1));
|
||||
assertEquals(300, page.getTotalEntries());
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.group("test_group"), new PageParameters(10, 1));
|
||||
assertEquals(5, page.getContent().size());
|
||||
assertEquals(
|
||||
ImmutableList.of("group test 8", "group test 6", "group test 4", "group test 2", "group test 0"),
|
||||
page.getContent().stream().map(LoggedAction::getDescription).collect(Collectors.toList())
|
||||
);
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.track("test_track"), new PageParameters(10, 1));
|
||||
assertEquals(5, page.getContent().size());
|
||||
assertEquals(
|
||||
ImmutableList.of("track test 8", "track test 6", "track test 4", "track test 2", "track test 0"),
|
||||
page.getContent().stream().map(LoggedAction::getDescription).collect(Collectors.toList())
|
||||
);
|
||||
|
||||
page = this.storage.getLogPage(ActionFilters.search("hello"), new PageParameters(500, 1));
|
||||
assertEquals(300, page.getContent().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSavePlayerData() throws Exception {
|
||||
UUID uniqueId = UUID.randomUUID();
|
||||
|
||||
// clean insert
|
||||
PlayerSaveResult r1 = this.storage.savePlayerData(uniqueId, "Player1");
|
||||
assertEquals(ImmutableSet.of(Outcome.CLEAN_INSERT), r1.getOutcomes());
|
||||
assertNull(r1.getOtherUniqueIds());
|
||||
assertNull(r1.getPreviousUsername());
|
||||
|
||||
// no change expected
|
||||
PlayerSaveResult r2 = this.storage.savePlayerData(uniqueId, "Player1");
|
||||
assertEquals(ImmutableSet.of(Outcome.NO_CHANGE), r2.getOutcomes());
|
||||
assertNull(r2.getOtherUniqueIds());
|
||||
assertNull(r2.getPreviousUsername());
|
||||
|
||||
// changed username
|
||||
PlayerSaveResult r3 = this.storage.savePlayerData(uniqueId, "Player2");
|
||||
assertEquals(ImmutableSet.of(Outcome.USERNAME_UPDATED), r3.getOutcomes());
|
||||
assertNull(r3.getOtherUniqueIds());
|
||||
assertTrue("Player1".equalsIgnoreCase(r3.getPreviousUsername()));
|
||||
|
||||
// changed uuid
|
||||
UUID newUniqueId = UUID.randomUUID();
|
||||
PlayerSaveResult r4 = this.storage.savePlayerData(newUniqueId, "Player2");
|
||||
assertEquals(ImmutableSet.of(Outcome.CLEAN_INSERT, Outcome.OTHER_UNIQUE_IDS_PRESENT_FOR_USERNAME), r4.getOutcomes());
|
||||
assertNotNull(r4.getOtherUniqueIds());
|
||||
assertEquals(ImmutableSet.of(uniqueId), r4.getOtherUniqueIds());
|
||||
assertNull(r2.getPreviousUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPlayerUniqueIdAndName() throws Exception {
|
||||
UUID uniqueId = UUID.randomUUID();
|
||||
String username = "Player1";
|
||||
|
||||
this.storage.savePlayerData(uniqueId, username);
|
||||
|
||||
assertEquals(uniqueId, this.storage.getPlayerUniqueId("Player1"));
|
||||
assertTrue(username.equalsIgnoreCase(this.storage.getPlayerName(uniqueId)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPlayerUniqueIdAndNameNull() throws Exception {
|
||||
assertNull(this.storage.getPlayerUniqueId("Player1"));
|
||||
assertNull(this.storage.getPlayerName(UUID.randomUUID()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndLoadGroup() throws Exception {
|
||||
StandardGroupManager groupManager = new StandardGroupManager(this.plugin);
|
||||
|
||||
//noinspection unchecked,rawtypes
|
||||
lenient().when(this.plugin.getGroupManager()).thenReturn((GroupManager) groupManager);
|
||||
|
||||
Group group = this.storage.createAndLoadGroup("test");
|
||||
|
||||
group.normalData().add(Permission.builder()
|
||||
.permission("test.1")
|
||||
.withContext("server", "test")
|
||||
.build()
|
||||
);
|
||||
group.normalData().add(Permission.builder()
|
||||
.permission("test.2")
|
||||
.withContext("world", "test")
|
||||
.build()
|
||||
);
|
||||
group.normalData().add(Permission.builder()
|
||||
.permission("test.3")
|
||||
.expiry(1, TimeUnit.HOURS)
|
||||
.withContext("server", "test")
|
||||
.withContext("world", "test")
|
||||
.withContext("hello", "test")
|
||||
.build()
|
||||
);
|
||||
|
||||
Set<Node> nodes = group.normalData().asSet();
|
||||
assertEquals(3, nodes.size());
|
||||
|
||||
this.storage.saveGroup(group);
|
||||
groupManager.unload("test");
|
||||
|
||||
Group loaded = this.storage.loadGroup("test").orElse(null);
|
||||
assertNotNull(loaded);
|
||||
assertNotSame(group, loaded);
|
||||
assertEquals(nodes, loaded.normalData().asSet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndDeleteUser() throws Exception {
|
||||
StandardUserManager userManager = new StandardUserManager(this.plugin);
|
||||
|
||||
//noinspection unchecked,rawtypes
|
||||
when(this.plugin.getUserManager()).thenReturn((UserManager) userManager);
|
||||
|
||||
UUID exampleUniqueId = UUID.fromString("069a79f4-44e9-4726-a5be-fca90e38aaf5");
|
||||
String exampleUsername = "Notch";
|
||||
PermissionNode examplePermission = Permission.builder()
|
||||
.permission("test.1")
|
||||
.withContext("server", "test")
|
||||
.build();
|
||||
InheritanceNode defaultGroupNode = Inheritance.builder(GroupManager.DEFAULT_GROUP_NAME).build();
|
||||
|
||||
// create a default user, assert that is doesn't appear in unique users list
|
||||
this.storage.savePlayerData(exampleUniqueId, exampleUsername);
|
||||
assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId));
|
||||
|
||||
// give the user a node, assert that it does appear in unique users list
|
||||
User user = this.storage.loadUser(exampleUniqueId, exampleUsername);
|
||||
user.setNode(DataType.NORMAL, examplePermission, true);
|
||||
this.storage.saveUser(user);
|
||||
assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId));
|
||||
|
||||
// clear all nodes (reset to default) and assert that it does not appear in unique users list
|
||||
user.clearNodes(DataType.NORMAL, null, true);
|
||||
this.storage.saveUser(user);
|
||||
assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId));
|
||||
assertEquals(ImmutableSet.of(defaultGroupNode), user.normalData().asSet());
|
||||
|
||||
// give it a node again, assert that it shows as a unique user
|
||||
user.setNode(DataType.NORMAL, examplePermission, true);
|
||||
this.storage.saveUser(user);
|
||||
assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId));
|
||||
assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet());
|
||||
|
||||
// reload user data from the db and assert that it is unchanged
|
||||
user = this.storage.loadUser(exampleUniqueId, exampleUsername);
|
||||
assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.implementation.StorageImplementation;
|
||||
import me.lucko.luckperms.common.storage.implementation.file.CombinedConfigurateStorage;
|
||||
import me.lucko.luckperms.common.storage.implementation.file.SeparatedConfigurateStorage;
|
||||
import me.lucko.luckperms.common.storage.implementation.file.loader.HoconLoader;
|
||||
import me.lucko.luckperms.common.storage.implementation.file.loader.JsonLoader;
|
||||
import me.lucko.luckperms.common.storage.implementation.file.loader.TomlLoader;
|
||||
import me.lucko.luckperms.common.storage.implementation.file.loader.YamlLoader;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.mockito.Mockito.lenient;
|
||||
|
||||
public class ConfigurateStorageTest {
|
||||
|
||||
@Nested
|
||||
class SeparatedYaml extends AbstractStorageTest {
|
||||
@TempDir
|
||||
private Path directory;
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory);
|
||||
return new SeparatedConfigurateStorage(plugin, "YAML", new YamlLoader(), ".yml", "yaml-storage");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class SeparatedJson extends AbstractStorageTest {
|
||||
@TempDir
|
||||
private Path directory;
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory);
|
||||
return new SeparatedConfigurateStorage(plugin, "JSON", new JsonLoader(), ".json", "json-storage");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class SeparatedHocon extends AbstractStorageTest {
|
||||
@TempDir
|
||||
private Path directory;
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory);
|
||||
return new SeparatedConfigurateStorage(plugin, "HOCON", new HoconLoader(), ".conf", "hocon-storage");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class SeparatedToml extends AbstractStorageTest {
|
||||
@TempDir
|
||||
private Path directory;
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory);
|
||||
return new SeparatedConfigurateStorage(plugin, "TOML", new TomlLoader(), ".toml", "toml-storage");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class CombinedYaml extends AbstractStorageTest {
|
||||
@TempDir
|
||||
private Path directory;
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory);
|
||||
return new CombinedConfigurateStorage(plugin, "YAML", new YamlLoader(), ".yml", "yaml-storage");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class CombinedJson extends AbstractStorageTest {
|
||||
@TempDir
|
||||
private Path directory;
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory);
|
||||
return new CombinedConfigurateStorage(plugin, "JSON", new JsonLoader(), ".json", "json-storage");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class CombinedHocon extends AbstractStorageTest {
|
||||
@TempDir
|
||||
private Path directory;
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory);
|
||||
return new CombinedConfigurateStorage(plugin, "HOCON", new HoconLoader(), ".conf", "hocon-storage");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class CombinedToml extends AbstractStorageTest {
|
||||
@TempDir
|
||||
private Path directory;
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory);
|
||||
return new CombinedConfigurateStorage(plugin, "TOML", new TomlLoader(), ".toml", "toml-storage");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.implementation.StorageImplementation;
|
||||
import me.lucko.luckperms.common.storage.implementation.mongodb.MongoStorage;
|
||||
import me.lucko.luckperms.common.storage.misc.StorageCredentials;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
@Tag("docker")
|
||||
public class MongoStorageTest extends AbstractStorageTest {
|
||||
|
||||
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("mongo"))
|
||||
.withExposedPorts(27017);
|
||||
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
this.container.start();
|
||||
String host = this.container.getHost();
|
||||
Integer port = this.container.getFirstMappedPort();
|
||||
|
||||
StorageCredentials credentials = new StorageCredentials(
|
||||
host + ":" + port,
|
||||
"minecraft",
|
||||
"",
|
||||
""
|
||||
);
|
||||
return new MongoStorage(plugin, credentials, "", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanupResources() {
|
||||
this.container.stop();
|
||||
}
|
||||
}
|
@ -25,237 +25,22 @@
|
||||
|
||||
package me.lucko.luckperms.common.storage;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.actionlog.LoggedAction;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.config.LuckPermsConfiguration;
|
||||
import me.lucko.luckperms.common.event.EventDispatcher;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.PrimaryGroupHolder;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.model.manager.group.GroupManager;
|
||||
import me.lucko.luckperms.common.model.manager.group.StandardGroupManager;
|
||||
import me.lucko.luckperms.common.model.manager.user.StandardUserManager;
|
||||
import me.lucko.luckperms.common.model.manager.user.UserManager;
|
||||
import me.lucko.luckperms.common.node.types.Inheritance;
|
||||
import me.lucko.luckperms.common.node.types.Permission;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
|
||||
import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter;
|
||||
import me.lucko.luckperms.common.storage.implementation.StorageImplementation;
|
||||
import me.lucko.luckperms.common.storage.implementation.sql.SqlStorage;
|
||||
import me.lucko.luckperms.common.storage.implementation.sql.connection.ConnectionFactory;
|
||||
import me.lucko.luckperms.common.storage.implementation.sql.connection.file.NonClosableConnection;
|
||||
import net.luckperms.api.actionlog.Action;
|
||||
import net.luckperms.api.model.PlayerSaveResult;
|
||||
import net.luckperms.api.model.PlayerSaveResult.Outcome;
|
||||
import net.luckperms.api.model.data.DataType;
|
||||
import net.luckperms.api.node.Node;
|
||||
import net.luckperms.api.node.types.InheritanceNode;
|
||||
import net.luckperms.api.node.types.PermissionNode;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.AdditionalAnswers.answer;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
public class SqlStorageTest extends AbstractStorageTest {
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class SqlStorageTest {
|
||||
|
||||
@Mock private LuckPermsPlugin plugin;
|
||||
@Mock private LuckPermsBootstrap bootstrap;
|
||||
@Mock private LuckPermsConfiguration configuration;
|
||||
|
||||
private SqlStorage storage;
|
||||
|
||||
@BeforeEach
|
||||
public void setupMocksAndDatabase() throws Exception {
|
||||
lenient().when(this.plugin.getBootstrap()).thenReturn(this.bootstrap);
|
||||
lenient().when(this.plugin.getConfiguration()).thenReturn(this.configuration);
|
||||
lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class));
|
||||
lenient().when(this.bootstrap.getScheduler()).thenReturn(mock(SchedulerAdapter.class));
|
||||
lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION)).thenReturn(PrimaryGroupHolder.AllParentsByWeight::new);
|
||||
lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION_METHOD)).thenReturn("parents-by-weight");
|
||||
lenient().when(this.bootstrap.getResourceStream(anyString()))
|
||||
.then(answer((String path) -> SqlStorageTest.class.getClassLoader().getResourceAsStream(path)));
|
||||
lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class));
|
||||
|
||||
this.storage = new SqlStorage(this.plugin, new TestH2ConnectionFactory(), "luckperms_");
|
||||
this.storage.init();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void shutdownDatabase() {
|
||||
this.storage.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActionLog() throws Exception {
|
||||
LoggedAction action = LoggedAction.build()
|
||||
.source(UUID.randomUUID())
|
||||
.sourceName("Test Source")
|
||||
.targetType(Action.Target.Type.USER)
|
||||
.target(UUID.randomUUID())
|
||||
.targetName("Test Target")
|
||||
.description("hello 123 hello 123")
|
||||
.build();
|
||||
|
||||
this.storage.logAction(action);
|
||||
|
||||
Log log = this.storage.getLog();
|
||||
assertEquals(1, log.getContent().size());
|
||||
assertEquals(action, log.getContent().first());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSavePlayerData() throws Exception {
|
||||
UUID uniqueId = UUID.randomUUID();
|
||||
|
||||
// clean insert
|
||||
PlayerSaveResult r1 = this.storage.savePlayerData(uniqueId, "Player1");
|
||||
assertEquals(ImmutableSet.of(Outcome.CLEAN_INSERT), r1.getOutcomes());
|
||||
assertNull(r1.getOtherUniqueIds());
|
||||
assertNull(r1.getPreviousUsername());
|
||||
|
||||
// no change expected
|
||||
PlayerSaveResult r2 = this.storage.savePlayerData(uniqueId, "Player1");
|
||||
assertEquals(ImmutableSet.of(Outcome.NO_CHANGE), r2.getOutcomes());
|
||||
assertNull(r2.getOtherUniqueIds());
|
||||
assertNull(r2.getPreviousUsername());
|
||||
|
||||
// changed username
|
||||
PlayerSaveResult r3 = this.storage.savePlayerData(uniqueId, "Player2");
|
||||
assertEquals(ImmutableSet.of(Outcome.USERNAME_UPDATED), r3.getOutcomes());
|
||||
assertNull(r3.getOtherUniqueIds());
|
||||
assertTrue("Player1".equalsIgnoreCase(r3.getPreviousUsername()));
|
||||
|
||||
// changed uuid
|
||||
UUID newUniqueId = UUID.randomUUID();
|
||||
PlayerSaveResult r4 = this.storage.savePlayerData(newUniqueId, "Player2");
|
||||
assertEquals(ImmutableSet.of(Outcome.CLEAN_INSERT, Outcome.OTHER_UNIQUE_IDS_PRESENT_FOR_USERNAME), r4.getOutcomes());
|
||||
assertNotNull(r4.getOtherUniqueIds());
|
||||
assertEquals(ImmutableSet.of(uniqueId), r4.getOtherUniqueIds());
|
||||
assertNull(r2.getPreviousUsername());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPlayerUniqueIdAndName() throws Exception {
|
||||
UUID uniqueId = UUID.randomUUID();
|
||||
String username = "Player1";
|
||||
|
||||
this.storage.savePlayerData(uniqueId, username);
|
||||
|
||||
assertEquals(uniqueId, this.storage.getPlayerUniqueId("Player1"));
|
||||
assertTrue(username.equalsIgnoreCase(this.storage.getPlayerName(uniqueId)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPlayerUniqueIdAndNameNull() throws Exception {
|
||||
assertNull(this.storage.getPlayerUniqueId("Player1"));
|
||||
assertNull(this.storage.getPlayerName(UUID.randomUUID()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndLoadGroup() throws Exception {
|
||||
StandardGroupManager groupManager = new StandardGroupManager(this.plugin);
|
||||
|
||||
//noinspection unchecked,rawtypes
|
||||
lenient().when(this.plugin.getGroupManager()).thenReturn((GroupManager) groupManager);
|
||||
|
||||
Group group = this.storage.createAndLoadGroup("test");
|
||||
|
||||
group.normalData().add(Permission.builder()
|
||||
.permission("test.1")
|
||||
.withContext("server", "test")
|
||||
.build()
|
||||
);
|
||||
group.normalData().add(Permission.builder()
|
||||
.permission("test.2")
|
||||
.withContext("world", "test")
|
||||
.build()
|
||||
);
|
||||
group.normalData().add(Permission.builder()
|
||||
.permission("test.3")
|
||||
.expiry(1, TimeUnit.HOURS)
|
||||
.withContext("server", "test")
|
||||
.withContext("world", "test")
|
||||
.withContext("hello", "test")
|
||||
.build()
|
||||
);
|
||||
|
||||
Set<Node> nodes = group.normalData().asSet();
|
||||
assertEquals(3, nodes.size());
|
||||
|
||||
this.storage.saveGroup(group);
|
||||
groupManager.unload("test");
|
||||
|
||||
Group loaded = this.storage.loadGroup("test").orElse(null);
|
||||
assertNotNull(loaded);
|
||||
assertNotSame(group, loaded);
|
||||
assertEquals(nodes, loaded.normalData().asSet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndDeleteUser() throws SQLException {
|
||||
StandardUserManager userManager = new StandardUserManager(this.plugin);
|
||||
|
||||
//noinspection unchecked,rawtypes
|
||||
when(this.plugin.getUserManager()).thenReturn((UserManager) userManager);
|
||||
|
||||
UUID exampleUniqueId = UUID.fromString("069a79f4-44e9-4726-a5be-fca90e38aaf5");
|
||||
String exampleUsername = "Notch";
|
||||
PermissionNode examplePermission = Permission.builder()
|
||||
.permission("test.1")
|
||||
.withContext("server", "test")
|
||||
.build();
|
||||
InheritanceNode defaultGroupNode = Inheritance.builder(GroupManager.DEFAULT_GROUP_NAME).build();
|
||||
|
||||
// create a default user, assert that is doesn't appear in unique users list
|
||||
this.storage.savePlayerData(exampleUniqueId, exampleUsername);
|
||||
assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId));
|
||||
|
||||
// give the user a node, assert that it does appear in unique users list
|
||||
User user = this.storage.loadUser(exampleUniqueId, exampleUsername);
|
||||
user.setNode(DataType.NORMAL, examplePermission, true);
|
||||
this.storage.saveUser(user);
|
||||
assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId));
|
||||
|
||||
// clear all nodes (reset to default) and assert that it does not appear in unique users list
|
||||
user.clearNodes(DataType.NORMAL, null, true);
|
||||
this.storage.saveUser(user);
|
||||
assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId));
|
||||
assertEquals(ImmutableSet.of(defaultGroupNode), user.normalData().asSet());
|
||||
|
||||
// give it a node again, assert that it shows as a unique user
|
||||
user.setNode(DataType.NORMAL, examplePermission, true);
|
||||
this.storage.saveUser(user);
|
||||
assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId));
|
||||
assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet());
|
||||
|
||||
// reload user data from the db and assert that it is unchanged
|
||||
user = this.storage.loadUser(exampleUniqueId, exampleUsername);
|
||||
assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet());
|
||||
@Override
|
||||
protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception {
|
||||
return new SqlStorage(plugin, new TestH2ConnectionFactory(), "luckperms_");
|
||||
}
|
||||
|
||||
private static class TestH2ConnectionFactory implements ConnectionFactory {
|
||||
@ -300,5 +85,4 @@ public class SqlStorageTest {
|
||||
this.connection.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,104 +0,0 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class PaginatedTest {
|
||||
|
||||
private static final Paginated<String> EXAMPLE_PAGE = new Paginated<>(ImmutableList.of("one", "two", "three", "four", "five"));
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"3, 2",
|
||||
"1, 5",
|
||||
"1, 6"
|
||||
})
|
||||
public void testMaxPages(int expected, int entriesPerPage) {
|
||||
assertEquals(expected, EXAMPLE_PAGE.getMaxPages(entriesPerPage));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"1, 2",
|
||||
"2, 2",
|
||||
"3, 1"
|
||||
})
|
||||
public void testPageSize(int pageNo, int expectedSize) {
|
||||
List<Paginated.Entry<String>> page = EXAMPLE_PAGE.getPage(pageNo, 2);
|
||||
assertEquals(expectedSize, page.size());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> testPageContent() {
|
||||
return Stream.of(
|
||||
Arguments.of(1, ImmutableList.of(
|
||||
new Paginated.Entry<>(1, "one"),
|
||||
new Paginated.Entry<>(2, "two")
|
||||
)),
|
||||
Arguments.of(2, ImmutableList.of(
|
||||
new Paginated.Entry<>(3, "three"),
|
||||
new Paginated.Entry<>(4, "four")
|
||||
)),
|
||||
Arguments.of(3, ImmutableList.of(
|
||||
new Paginated.Entry<>(5, "five")
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
public void testPageContent(int pageNo, List<Paginated.Entry<String>> expectedContent) {
|
||||
assertEquals(expectedContent, EXAMPLE_PAGE.getPage(pageNo, 2));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"4, 2",
|
||||
})
|
||||
public void testFailState(int pageNo, int pageSize) {
|
||||
assertThrows(IllegalStateException.class, () -> EXAMPLE_PAGE.getPage(pageNo, pageSize));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"0, 2",
|
||||
"-1, 2"
|
||||
})
|
||||
public void testFailArgument(int pageNo, int pageSize) {
|
||||
assertThrows(IllegalArgumentException.class, () -> EXAMPLE_PAGE.getPage(pageNo, pageSize));
|
||||
}
|
||||
|
||||
}
|
@ -44,7 +44,6 @@ import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerLoginNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Uuids;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
@ -29,8 +29,6 @@ import com.google.common.collect.Iterables;
|
||||
import me.lucko.luckperms.common.messaging.pluginmsg.AbstractPluginMessageMessenger;
|
||||
import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask;
|
||||
import me.lucko.luckperms.fabric.LPFabricPlugin;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
|
||||
import net.luckperms.api.messenger.IncomingMessageConsumer;
|
||||
@ -39,7 +37,6 @@ import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
|
@ -32,8 +32,6 @@ import net.luckperms.api.query.QueryOptions;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Mixin interface for {@link ServerPlayerEntity} implementing {@link User} related
|
||||
* caches and functions.
|
||||
|
@ -33,8 +33,16 @@ import java.util.concurrent.CompletableFuture;
|
||||
*/
|
||||
public interface CommandExecutor {
|
||||
|
||||
CompletableFuture<Void> execute(String command);
|
||||
CompletableFuture<Void> execute(StandaloneSender player, String command);
|
||||
|
||||
List<String> tabComplete(String command);
|
||||
List<String> tabComplete(StandaloneSender player, String command);
|
||||
|
||||
default CompletableFuture<Void> execute(String command) {
|
||||
return execute(StandaloneUser.INSTANCE, command);
|
||||
}
|
||||
|
||||
default List<String> tabComplete(String command) {
|
||||
return tabComplete(StandaloneUser.INSTANCE, command);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,34 +23,26 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.query;
|
||||
package me.lucko.luckperms.standalone.app.integration;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a field being used in an update
|
||||
*/
|
||||
public enum QueryField {
|
||||
public interface StandaloneSender {
|
||||
String getName();
|
||||
|
||||
PERMISSION("permission"),
|
||||
SERVER("server"),
|
||||
WORLD("world");
|
||||
UUID getUniqueId();
|
||||
|
||||
private final String sqlName;
|
||||
void sendMessage(Component component);
|
||||
|
||||
public static QueryField of(String s) {
|
||||
try {
|
||||
return valueOf(s.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Tristate getPermissionValue(String permission);
|
||||
|
||||
QueryField(String sqlName) {
|
||||
this.sqlName = sqlName;
|
||||
}
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
public String getSqlName() {
|
||||
return this.sqlName;
|
||||
}
|
||||
boolean isConsole();
|
||||
|
||||
Locale getLocale();
|
||||
}
|
@ -28,58 +28,57 @@ package me.lucko.luckperms.standalone.app.integration;
|
||||
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.ansi.ANSIComponentSerializer;
|
||||
import net.kyori.ansi.ColorLevel;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Dummy/singleton player class used by the standalone plugin.
|
||||
*
|
||||
* <p>In various places (ContextManager, SenderFactory, ..) the platform "player" type is used
|
||||
* as a generic parameter. This class acts as this type for the standalone plugin.</p>
|
||||
* The sender instance used for the console / users executing commands
|
||||
* on a standalone instance of LuckPerms
|
||||
*/
|
||||
public class SingletonPlayer {
|
||||
public class StandaloneUser implements StandaloneSender {
|
||||
|
||||
/** Empty UUID used by the singleton player. */
|
||||
private static final UUID UUID = new UUID(0, 0);
|
||||
|
||||
/** A message sink that prints the component to stdout */
|
||||
private static final Consumer<Component> PRINT_TO_STDOUT = component -> LuckPermsApplication.LOGGER.info(ANSIComponentSerializer.ansi().serialize(component));
|
||||
public static final StandaloneUser INSTANCE = new StandaloneUser();
|
||||
|
||||
/** Singleton instance */
|
||||
public static final SingletonPlayer INSTANCE = new SingletonPlayer();
|
||||
|
||||
/** A set of message sinks that messages are delivered to */
|
||||
private final Set<Consumer<Component>> messageSinks;
|
||||
|
||||
private SingletonPlayer() {
|
||||
this.messageSinks = new CopyOnWriteArraySet<>();
|
||||
this.messageSinks.add(PRINT_TO_STDOUT);
|
||||
private StandaloneUser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "StandaloneUser";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return UUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component component) {
|
||||
for (Consumer<Component> sink : this.messageSinks) {
|
||||
sink.accept(component);
|
||||
}
|
||||
LuckPermsApplication.LOGGER.info(ANSIComponentSerializer.ansi().serialize(component));
|
||||
}
|
||||
|
||||
public void addMessageSink(Consumer<Component> sink) {
|
||||
this.messageSinks.add(sink);
|
||||
@Override
|
||||
public Tristate getPermissionValue(String permission) {
|
||||
return Tristate.TRUE;
|
||||
}
|
||||
|
||||
public void removeMessageSink(Consumer<Component> sink) {
|
||||
this.messageSinks.remove(sink);
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
}
|
@ -39,7 +39,7 @@ import me.lucko.luckperms.common.plugin.AbstractLuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.plugin.util.AbstractConnectionListener;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
||||
import me.lucko.luckperms.standalone.app.integration.SingletonPlayer;
|
||||
import me.lucko.luckperms.standalone.app.integration.StandaloneUser;
|
||||
import me.lucko.luckperms.standalone.stub.StandaloneContextManager;
|
||||
import me.lucko.luckperms.standalone.stub.StandaloneDummyConnectionListener;
|
||||
import me.lucko.luckperms.standalone.stub.StandaloneEventBus;
|
||||
@ -162,7 +162,7 @@ public class LPStandalonePlugin extends AbstractLuckPermsPlugin {
|
||||
|
||||
@Override
|
||||
public Sender getConsoleSender() {
|
||||
return getSenderFactory().wrap(SingletonPlayer.INSTANCE);
|
||||
return getSenderFactory().wrap(StandaloneUser.INSTANCE);
|
||||
}
|
||||
|
||||
public StandaloneSenderFactory getSenderFactory() {
|
||||
|
@ -29,7 +29,7 @@ import me.lucko.luckperms.common.command.CommandManager;
|
||||
import me.lucko.luckperms.common.command.utils.ArgumentTokenizer;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.standalone.app.integration.CommandExecutor;
|
||||
import me.lucko.luckperms.standalone.app.integration.SingletonPlayer;
|
||||
import me.lucko.luckperms.standalone.app.integration.StandaloneSender;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -43,15 +43,15 @@ public class StandaloneCommandManager extends CommandManager implements CommandE
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> execute(String command) {
|
||||
Sender wrapped = this.plugin.getSenderFactory().wrap(SingletonPlayer.INSTANCE);
|
||||
public CompletableFuture<Void> execute(StandaloneSender player, String command) {
|
||||
Sender wrapped = this.plugin.getSenderFactory().wrap(player);
|
||||
List<String> arguments = ArgumentTokenizer.EXECUTE.tokenizeInput(command);
|
||||
return executeCommand(wrapped, "lp", arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(String command) {
|
||||
Sender wrapped = this.plugin.getSenderFactory().wrap(SingletonPlayer.INSTANCE);
|
||||
public List<String> tabComplete(StandaloneSender player, String command) {
|
||||
Sender wrapped = this.plugin.getSenderFactory().wrap(player);
|
||||
List<String> arguments = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(command);
|
||||
return tabCompleteCommand(wrapped, arguments);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user