mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-27 03:27:37 +01:00
Made it possible to share query results via url
This commit is contained in:
parent
c7ed844c76
commit
7baf1d7556
@ -22,6 +22,7 @@ import org.apache.commons.text.TextStringBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -144,6 +145,17 @@ public enum Html {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String decodeFromURL(String string) {
|
||||
try {
|
||||
return StringUtils.replace(
|
||||
URLDecoder.decode(string, "UTF-8"),
|
||||
" ", "+" // Decoding replaces + with spaces
|
||||
);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The HTML String
|
||||
*/
|
||||
|
@ -34,6 +34,7 @@ import com.djrapitops.plan.storage.database.queries.filter.Filter;
|
||||
import com.djrapitops.plan.storage.database.queries.filter.FilterQuery;
|
||||
import com.djrapitops.plan.storage.database.queries.filter.QueryFilters;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.playertable.QueryTablePlayersQuery;
|
||||
import com.djrapitops.plan.storage.json.JSONStorage;
|
||||
import com.djrapitops.plan.utilities.java.Maps;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
@ -52,6 +53,7 @@ public class QueryJSONResolver implements Resolver {
|
||||
|
||||
private final PlanConfig config;
|
||||
private final DBSystem dbSystem;
|
||||
private final JSONStorage jsonStorage;
|
||||
private final Locale locale;
|
||||
private final Formatters formatters;
|
||||
|
||||
@ -60,12 +62,14 @@ public class QueryJSONResolver implements Resolver {
|
||||
QueryFilters filters,
|
||||
PlanConfig config,
|
||||
DBSystem dbSystem,
|
||||
JSONStorage jsonStorage,
|
||||
Locale locale,
|
||||
Formatters formatters
|
||||
) {
|
||||
this.filters = filters;
|
||||
this.config = config;
|
||||
this.dbSystem = dbSystem;
|
||||
this.jsonStorage = jsonStorage;
|
||||
this.locale = locale;
|
||||
this.formatters = formatters;
|
||||
}
|
||||
@ -82,9 +86,25 @@ public class QueryJSONResolver implements Resolver {
|
||||
}
|
||||
|
||||
private Response getResponse(Request request) {
|
||||
// Attempt to find previously created result
|
||||
try {
|
||||
Optional<JSONStorage.StoredJSON> previousResults = request.getQuery().get("timestamp")
|
||||
.flatMap(queryTimestamp -> jsonStorage.fetchExactJson("query", Long.parseLong(queryTimestamp)));
|
||||
if (previousResults.isPresent()) {
|
||||
return Response.builder()
|
||||
.setMimeType(MimeType.JSON)
|
||||
.setJSONContent(previousResults.get().json)
|
||||
.build();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new BadRequestException("Could not parse 'timestamp' into a number. Remove parameter or fix it.");
|
||||
}
|
||||
|
||||
String q = request.getQuery().get("q").orElseThrow(() -> new BadRequestException("'q' parameter not set (expecting json array)"));
|
||||
String view = request.getQuery().get("view").orElseThrow(() -> new BadRequestException("'view' parameter not set (expecting json object {afterDate, afterTime, beforeDate, beforeTime})"));
|
||||
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
q = URLDecoder.decode(q, "UTF-8");
|
||||
List<FilterQuery> queries = FilterQuery.parse(q);
|
||||
@ -92,15 +112,19 @@ public class QueryJSONResolver implements Resolver {
|
||||
|
||||
Map<String, Object> json = Maps.builder(String.class, Object.class)
|
||||
.put("path", result.getResultPath())
|
||||
.put("view", new Gson().fromJson(view, FiltersJSONResolver.ViewJSON.class))
|
||||
.put("timestamp", timestamp)
|
||||
.build();
|
||||
if (!result.isEmpty()) {
|
||||
json.put("data", getDataFor(result.getResultUUIDs(), view));
|
||||
}
|
||||
|
||||
JSONStorage.StoredJSON stored = jsonStorage.storeJson("query", json, timestamp);
|
||||
|
||||
return Response.builder()
|
||||
.setMimeType(MimeType.JSON)
|
||||
.setJSONContent(json)
|
||||
.setJSONContent(stored.json)
|
||||
.build();
|
||||
|
||||
} catch (ParseException e) {
|
||||
throw new BadRequestException("'view' date format was incorrect (expecting afterDate dd/mm/yyyy, afterTime hh:mm, beforeDate dd/mm/yyyy, beforeTime hh:mm}): " + e.getMessage());
|
||||
} catch (IOException e) {
|
||||
|
@ -25,6 +25,8 @@ import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.settings.locale.Locale;
|
||||
import com.djrapitops.plan.settings.locale.LocaleSystem;
|
||||
import com.djrapitops.plan.storage.file.JarResource;
|
||||
import com.djrapitops.plan.storage.json.JSONFileStorage;
|
||||
import com.djrapitops.plan.storage.json.JSONStorage;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
|
||||
import dagger.Module;
|
||||
@ -96,4 +98,10 @@ public class SystemObjectProvidingModule {
|
||||
return dataService;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
JSONStorage provideJSONStorage(JSONFileStorage jsonFileStorage) {
|
||||
return jsonFileStorage;
|
||||
}
|
||||
|
||||
}
|
@ -149,4 +149,8 @@ public class PlanFiles implements SubSystem {
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public Path getJSONStorageDirectory() {
|
||||
return getDataDirectory().resolve("cached_json");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.storage.json;
|
||||
|
||||
import com.djrapitops.plan.storage.file.PlanFiles;
|
||||
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* In charge of storing json files on disk for later retrieval.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
@Singleton
|
||||
public class JSONFileStorage implements JSONStorage {
|
||||
|
||||
private final PluginLogger logger;
|
||||
|
||||
private final Path jsonDirectory;
|
||||
private final Pattern timestampRegex = Pattern.compile(".*-([0-9]*).json");
|
||||
|
||||
@Inject
|
||||
public JSONFileStorage(PlanFiles files, PluginLogger logger) {
|
||||
this.logger = logger;
|
||||
|
||||
jsonDirectory = files.getJSONStorageDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredJSON storeJson(String identifier, String json, long timestamp) {
|
||||
Path writingTo = jsonDirectory.resolve(identifier + '-' + timestamp + ".json");
|
||||
try {
|
||||
Files.createDirectories(jsonDirectory);
|
||||
Files.write(writingTo, json.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Could not write a file to " + writingTo.toFile().getAbsolutePath() + ": " + e.getMessage());
|
||||
}
|
||||
return new StoredJSON(json, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<StoredJSON> fetchJSON(String identifier) {
|
||||
File[] stored = jsonDirectory.toFile().listFiles();
|
||||
if (stored == null) return Optional.empty();
|
||||
for (File file : stored) {
|
||||
String fileName = file.getName();
|
||||
if (fileName.endsWith(".json") && fileName.startsWith(identifier)) {
|
||||
return Optional.ofNullable(readStoredJSON(file));
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private StoredJSON readStoredJSON(File from) {
|
||||
Matcher timestampMatch = timestampRegex.matcher(from.getName());
|
||||
if (timestampMatch.find()) {
|
||||
try (Stream<String> lines = Files.lines(from.toPath())) {
|
||||
long timestamp = Long.parseLong(timestampMatch.group(1));
|
||||
StringBuilder json = new StringBuilder();
|
||||
lines.forEach(json::append);
|
||||
return new StoredJSON(json.toString(), timestamp);
|
||||
} catch (IOException e) {
|
||||
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " file '" + from.getName() + "' could not be read: " + e.getMessage());
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " contained a file '" + from.getName() + "' with improperly formatted -timestamp (could not parse number). This file was not placed there by Plan!");
|
||||
}
|
||||
} else {
|
||||
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " contained a file '" + from.getName() + "' that has no -timestamp. This file was not placed there by Plan!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<StoredJSON> fetchExactJson(String identifier, long timestamp) {
|
||||
File found = jsonDirectory.resolve(identifier + "-" + timestamp + ".json").toFile();
|
||||
if (!found.exists()) return Optional.empty();
|
||||
return Optional.ofNullable(readStoredJSON(found));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<StoredJSON> fetchJsonMadeBefore(String identifier, long timestamp) {
|
||||
return fetchJSONWithTimestamp(identifier, timestamp, (timestampMatch, time) -> Long.parseLong(timestampMatch.group(1)) < time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<StoredJSON> fetchJsonMadeAfter(String identifier, long timestamp) {
|
||||
return fetchJSONWithTimestamp(identifier, timestamp, (timestampMatch, time) -> Long.parseLong(timestampMatch.group(1)) > time);
|
||||
}
|
||||
|
||||
private Optional<StoredJSON> fetchJSONWithTimestamp(String identifier, long timestamp, BiPredicate<Matcher, Long> timestampComparator) {
|
||||
File[] stored = jsonDirectory.toFile().listFiles();
|
||||
if (stored == null) return Optional.empty();
|
||||
for (File file : stored) {
|
||||
try {
|
||||
String fileName = file.getName();
|
||||
if (fileName.endsWith(".json") && fileName.startsWith(identifier)) {
|
||||
Matcher timestampMatch = timestampRegex.matcher(fileName);
|
||||
if (timestampMatch.find() && timestampComparator.test(timestampMatch, timestamp)) {
|
||||
return Optional.ofNullable(readStoredJSON(file));
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// Ignore this file, malformed timestamp
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.storage.json;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* In charge of storing json somewhere for later retrieval.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public interface JSONStorage {
|
||||
|
||||
default StoredJSON storeJson(String identifier, String json) {
|
||||
return storeJson(identifier, json, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
default StoredJSON storeJson(String identifier, Object json) {
|
||||
return storeJson(identifier, new Gson().toJson(json));
|
||||
}
|
||||
|
||||
StoredJSON storeJson(String identifier, String json, long timestamp);
|
||||
|
||||
default StoredJSON storeJson(String identifier, Object json, long timestamp) {
|
||||
return storeJson(identifier, new Gson().toJson(json), timestamp);
|
||||
}
|
||||
|
||||
Optional<StoredJSON> fetchJSON(String identifier);
|
||||
|
||||
Optional<StoredJSON> fetchExactJson(String identifier, long timestamp);
|
||||
|
||||
Optional<StoredJSON> fetchJsonMadeBefore(String identifier, long timestamp);
|
||||
|
||||
Optional<StoredJSON> fetchJsonMadeAfter(String identifier, long timestamp);
|
||||
|
||||
final class StoredJSON {
|
||||
public final String json;
|
||||
public final long timestamp;
|
||||
|
||||
public StoredJSON(String json, long timestamp) {
|
||||
this.json = json;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ let filterCount = 0;
|
||||
id: "DOM id",
|
||||
options...
|
||||
}*/
|
||||
let timestamp = undefined;
|
||||
let filterView = {
|
||||
afterDate: null,
|
||||
afterTime: null,
|
||||
@ -53,7 +54,7 @@ class MultipleChoiceFilter extends Filter {
|
||||
|
||||
toObject() {
|
||||
let selected = [];
|
||||
for (let option of document.querySelector('#' + filter.id + " select").selectedOptions) {
|
||||
for (let option of document.querySelector('#' + this.id + " select").selectedOptions) {
|
||||
selected.push(option.text);
|
||||
}
|
||||
selected = JSON.stringify(selected);
|
||||
@ -257,24 +258,50 @@ function setFilterOption(
|
||||
}
|
||||
}
|
||||
|
||||
let query = [];
|
||||
|
||||
function performQuery() {
|
||||
for (let filter of filterQuery) {
|
||||
query.push(filter.toObject());
|
||||
}
|
||||
runQuery();
|
||||
}
|
||||
|
||||
function getQueryAddress() {
|
||||
if (timestamp) return `./v1/query?timestamp=${timestamp}`;
|
||||
|
||||
const encodedQuery = encodeURIComponent(JSON.stringify(query));
|
||||
const encodedView = encodeURIComponent(JSON.stringify(filterView));
|
||||
return `./v1/query?q=${encodedQuery}&view=${encodedView}`;
|
||||
}
|
||||
|
||||
function runQuery() {
|
||||
const queryButton = document.querySelector('#query-button');
|
||||
queryButton.setAttribute('disabled', 'true');
|
||||
queryButton.classList.add('disabled');
|
||||
|
||||
const query = [];
|
||||
for (filter of filterQuery) {
|
||||
query.push(filter.toObject());
|
||||
}
|
||||
document.querySelector('#content .tab').innerHTML =
|
||||
`<div class="page-loader">
|
||||
<span class="loader"></span>
|
||||
<p class="loader-text">Loading..</p>
|
||||
</div>`;
|
||||
|
||||
const encodedQuery = encodeURIComponent(JSON.stringify(query));
|
||||
const encodedView = encodeURIComponent(JSON.stringify(filterView));
|
||||
jsonRequest(`./v1/query?q=${encodedQuery}&view=${encodedView}`, function (json, error) {
|
||||
console.log(filterQuery);
|
||||
if (json) console.log(json);
|
||||
if (error) console.error(error);
|
||||
const navButton = document.querySelector('.navbar-nav .nav-item');
|
||||
navButton.insertAdjacentElement('beforebegin',
|
||||
`<li class="nav-item nav-button"><a class="nav-link" href="./query">
|
||||
<i class="far fa-fw fa-undo"></i>
|
||||
<span>Make another query</span>
|
||||
</a></li>`);
|
||||
|
||||
renderDataResultScreen(json.data.players.data.length);
|
||||
jsonRequest(getQueryAddress(), function (json, error) {
|
||||
if (!json.data) {
|
||||
window.history.replaceState({}, '', `${location.pathname}?error=${error ? error : 'Query result expired'}`);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
renderDataResultScreen(json.data.players.data.length, json.view ? json.view : {});
|
||||
|
||||
window.history.replaceState({}, '', `${location.pathname}?timestamp=${json.timestamp}`);
|
||||
|
||||
$('.player-table').DataTable({
|
||||
responsive: true,
|
||||
@ -291,7 +318,11 @@ function performQuery() {
|
||||
});
|
||||
}
|
||||
|
||||
function renderDataResultScreen(resultCount) {
|
||||
function renderDataResultScreen(resultCount, view) {
|
||||
const afterDate = filterView.afterDate ? filterView.afterDate : view.afterDate;
|
||||
const beforeDate = filterView.beforeDate ? filterView.beforeDate : view.beforeDate;
|
||||
const afterTime = filterView.afterTime ? filterView.afterTime : view.afterTime;
|
||||
const beforeTime = filterView.beforeTime ? filterView.beforeTime : view.beforeTime;
|
||||
document.querySelector('#content .tab').innerHTML =
|
||||
`<div class="container-fluid mt-4">
|
||||
<!-- Page Heading -->
|
||||
@ -301,12 +332,12 @@ function renderDataResultScreen(resultCount) {
|
||||
<p class="mb-0 text-gray-800">(matched ${resultCount} players)</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12 col-lg-11">
|
||||
<div class="col-xs-12 col-sm-12 col-lg-12">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
||||
<h6 class="m-0 font-weight-bold col-black" title=" ${filterView.afterDate} ${filterView.afterTime} - ${filterView.beforeDate} ${filterView.beforeTime}"><i
|
||||
<h6 class="m-0 font-weight-bold col-black" title=" ${afterDate} ${afterTime} - ${beforeDate} ${beforeTime}"><i
|
||||
class="fas fa-fw fa-users col-black"></i>
|
||||
View: ${filterView.afterDate} - ${filterView.beforeDate}</h6>
|
||||
View: ${afterDate} - ${beforeDate}</h6>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-hover player-table dataTable">
|
||||
|
@ -78,7 +78,7 @@
|
||||
|
||||
<div class="row">
|
||||
<!-- Card -->
|
||||
<div class="col-xs-12 col-sm-12 col-lg-11">
|
||||
<div class="col-xs-12 col-sm-12 col-lg-12">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-body" id="data_player_info">
|
||||
<label class="mt-2 mb-0" for="viewFromDateField">Show a view</label>
|
||||
@ -322,22 +322,40 @@
|
||||
|
||||
<script id="mainScript">
|
||||
const filters = [];
|
||||
jsonRequest("./v1/filters", function (json, error) {
|
||||
filters.push(...json.filters);
|
||||
|
||||
filterView = json.view;
|
||||
if (location.search.includes("error=")) {
|
||||
const placeBefore = document.querySelector('.tab .row div');
|
||||
placeBefore.insertAdjacentElement('beforebegin',
|
||||
`<alert class="alert alert-danger alert-dismissible show">
|
||||
${new URLSearchParams(location.search).get("error")}
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</alert>`);
|
||||
}
|
||||
|
||||
document.getElementById('viewFromDateField').setAttribute('placeholder', json.view.afterDate);
|
||||
document.getElementById('viewFromTimeField').setAttribute('placeholder', json.view.afterTime);
|
||||
document.getElementById('viewToDateField').setAttribute('placeholder', json.view.beforeDate);
|
||||
document.getElementById('viewToTimeField').setAttribute('placeholder', json.view.beforeTime);
|
||||
if (location.search.includes('timestamp=')) {
|
||||
const parameters = new URLSearchParams(location.search);
|
||||
timestamp = parameters.get('timestamp');
|
||||
runQuery();
|
||||
} else {
|
||||
jsonRequest("./v1/filters", function (json, error) {
|
||||
filters.push(...json.filters);
|
||||
|
||||
let filterElements = '';
|
||||
for (let i = 0; i < filters.length; i++) {
|
||||
filterElements += createFilterSelector('#filters', i, filters[i]);
|
||||
}
|
||||
document.getElementById('filter-dropdown').innerHTML = filterElements;
|
||||
});
|
||||
filterView = json.view;
|
||||
|
||||
document.getElementById('viewFromDateField').setAttribute('placeholder', json.view.afterDate);
|
||||
document.getElementById('viewFromTimeField').setAttribute('placeholder', json.view.afterTime);
|
||||
document.getElementById('viewToDateField').setAttribute('placeholder', json.view.beforeDate);
|
||||
document.getElementById('viewToTimeField').setAttribute('placeholder', json.view.beforeTime);
|
||||
|
||||
let filterElements = '';
|
||||
for (let i = 0; i < filters.length; i++) {
|
||||
filterElements += createFilterSelector('#filters', i, filters[i]);
|
||||
}
|
||||
document.getElementById('filter-dropdown').innerHTML = filterElements;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
@ -18,6 +18,7 @@ package com.djrapitops.plan.storage.database;
|
||||
|
||||
import com.djrapitops.plan.delivery.DeliveryUtilities;
|
||||
import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.modules.FiltersModule;
|
||||
import com.djrapitops.plan.settings.ConfigSystem;
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.storage.file.PlanFiles;
|
||||
@ -34,6 +35,7 @@ import java.nio.file.Path;
|
||||
@Component(modules = {
|
||||
DBSystemModule.class,
|
||||
TestSystemObjectProvidingModule.class,
|
||||
FiltersModule.class,
|
||||
|
||||
TestAPFModule.class,
|
||||
PlanPluginModule.class,
|
||||
|
@ -20,6 +20,8 @@ import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.identification.ServerServerInfo;
|
||||
import com.djrapitops.plan.settings.BukkitConfigSystem;
|
||||
import com.djrapitops.plan.settings.ConfigSystem;
|
||||
import com.djrapitops.plan.storage.json.JSONFileStorage;
|
||||
import com.djrapitops.plan.storage.json.JSONStorage;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
|
||||
@ -37,4 +39,7 @@ public interface PlanPluginModule {
|
||||
@Binds
|
||||
ServerInfo bindServerInfo(ServerServerInfo serverServerInfo);
|
||||
|
||||
@Binds
|
||||
JSONStorage bindJSONStorage(JSONFileStorage jsonFileStorage);
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user