http-api, http-service, rl-client: bulk upload configuration changes
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.http.api.config;
|
package net.runelite.http.api.config;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -48,6 +49,7 @@ import okhttp3.Response;
|
|||||||
public class ConfigClient
|
public class ConfigClient
|
||||||
{
|
{
|
||||||
private static final MediaType TEXT_PLAIN = MediaType.parse("text/plain");
|
private static final MediaType TEXT_PLAIN = MediaType.parse("text/plain");
|
||||||
|
private static final Gson GSON = RuneLiteAPI.GSON;
|
||||||
|
|
||||||
private final OkHttpClient client;
|
private final OkHttpClient client;
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
@@ -114,6 +116,59 @@ public class ConfigClient
|
|||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Void> patch(Configuration configuration)
|
||||||
|
{
|
||||||
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
|
||||||
|
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
||||||
|
.addPathSegment("config")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
log.debug("Built URI: {}", url);
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.patch(RequestBody.create(RuneLiteAPI.JSON, GSON.toJson(configuration)))
|
||||||
|
.header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString())
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(new Callback()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call call, IOException e)
|
||||||
|
{
|
||||||
|
log.warn("Unable to synchronize configuration item", e);
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response)
|
||||||
|
{
|
||||||
|
if (response.code() != 200)
|
||||||
|
{
|
||||||
|
String body = "bad response";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
body = response.body().string();
|
||||||
|
}
|
||||||
|
catch (IOException ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn("failed to synchronize some of {} configuration values: {}", configuration.getConfig().size(), body);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.debug("Synchronized {} configuration values", configuration.getConfig().size());
|
||||||
|
}
|
||||||
|
response.close();
|
||||||
|
future.complete(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
public CompletableFuture<Void> unset(String key)
|
public CompletableFuture<Void> unset(String key)
|
||||||
{
|
{
|
||||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
|||||||
@@ -24,28 +24,15 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.http.api.config;
|
package net.runelite.http.api.config;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
public class ConfigEntry
|
public class ConfigEntry
|
||||||
{
|
{
|
||||||
private String key;
|
private String key;
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
public String getKey()
|
|
||||||
{
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKey(String key)
|
|
||||||
{
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue()
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String value)
|
|
||||||
{
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,18 +26,12 @@ package net.runelite.http.api.config;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
public class Configuration
|
public class Configuration
|
||||||
{
|
{
|
||||||
private List<ConfigEntry> config = new ArrayList<>();
|
private List<ConfigEntry> config = new ArrayList<>();
|
||||||
|
|
||||||
public Configuration(List<ConfigEntry> config)
|
|
||||||
{
|
|
||||||
this.config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ConfigEntry> getConfig()
|
|
||||||
{
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
package net.runelite.http.service.config;
|
package net.runelite.http.service.config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import net.runelite.http.api.config.Configuration;
|
import net.runelite.http.api.config.Configuration;
|
||||||
@@ -32,6 +33,7 @@ import net.runelite.http.service.account.AuthFilter;
|
|||||||
import net.runelite.http.service.account.beans.SessionEntry;
|
import net.runelite.http.service.account.beans.SessionEntry;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PatchMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -66,6 +68,29 @@ public class ConfigController
|
|||||||
return configService.get(session.getUser());
|
return configService.get(session.getUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PatchMapping
|
||||||
|
public List<String> patch(
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
@RequestBody Configuration changes
|
||||||
|
) throws IOException
|
||||||
|
{
|
||||||
|
SessionEntry session = authFilter.handle(request, response);
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> failures = configService.patch(session.getUser(), changes);
|
||||||
|
if (failures.size() != 0)
|
||||||
|
{
|
||||||
|
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(path = "/{key:.+}", method = PUT)
|
@RequestMapping(path = "/{key:.+}", method = PUT)
|
||||||
public void setKey(
|
public void setKey(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
package net.runelite.http.service.config;
|
package net.runelite.http.service.config;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
@@ -38,6 +39,7 @@ import static com.mongodb.client.model.Filters.eq;
|
|||||||
import com.mongodb.client.model.IndexOptions;
|
import com.mongodb.client.model.IndexOptions;
|
||||||
import com.mongodb.client.model.Indexes;
|
import com.mongodb.client.model.Indexes;
|
||||||
import com.mongodb.client.model.UpdateOptions;
|
import com.mongodb.client.model.UpdateOptions;
|
||||||
|
import static com.mongodb.client.model.Updates.combine;
|
||||||
import static com.mongodb.client.model.Updates.set;
|
import static com.mongodb.client.model.Updates.set;
|
||||||
import static com.mongodb.client.model.Updates.unset;
|
import static com.mongodb.client.model.Updates.unset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -50,6 +52,7 @@ import net.runelite.http.api.RuneLiteAPI;
|
|||||||
import net.runelite.http.api.config.ConfigEntry;
|
import net.runelite.http.api.config.ConfigEntry;
|
||||||
import net.runelite.http.api.config.Configuration;
|
import net.runelite.http.api.config.Configuration;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
|
import org.bson.conversions.Bson;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -131,31 +134,79 @@ public class ConfigService
|
|||||||
return new Configuration(config);
|
return new Configuration(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> patch(int userID, Configuration config)
|
||||||
|
{
|
||||||
|
List<String> failures = new ArrayList<>();
|
||||||
|
List<Bson> sets = new ArrayList<>(config.getConfig().size());
|
||||||
|
for (ConfigEntry entry : config.getConfig())
|
||||||
|
{
|
||||||
|
Bson s = setForKV(entry.getKey(), entry.getValue());
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
failures.add(entry.getKey());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sets.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sets.size() > 0)
|
||||||
|
{
|
||||||
|
mongoCollection.updateOne(
|
||||||
|
eq("_userId", userID),
|
||||||
|
combine(sets),
|
||||||
|
upsertUpdateOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Bson setForKV(String key, @Nullable String value)
|
||||||
|
{
|
||||||
|
if (key.startsWith("$") || key.startsWith("_"))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] split = key.split("\\.", 2);
|
||||||
|
if (split.length != 2)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String dbKey = split[0] + "." + split[1].replace('.', ':');
|
||||||
|
|
||||||
|
if (Strings.isNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return unset(dbKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validateJson(value))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object jsonValue = parseJsonString(value);
|
||||||
|
return set(dbKey, jsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean setKey(
|
public boolean setKey(
|
||||||
int userId,
|
int userId,
|
||||||
String key,
|
String key,
|
||||||
@Nullable String value
|
@Nullable String value
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (key.startsWith("$") || key.startsWith("_"))
|
Bson set = setForKV(key, value);
|
||||||
|
if (set == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] split = key.split("\\.", 2);
|
|
||||||
if (split.length != 2)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validateJson(value))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object jsonValue = parseJsonString(value);
|
|
||||||
mongoCollection.updateOne(eq("_userId", userId),
|
mongoCollection.updateOne(eq("_userId", userId),
|
||||||
set(split[0] + "." + split[1].replace('.', ':'), jsonValue),
|
set,
|
||||||
upsertUpdateOptions);
|
upsertUpdateOptions);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -165,19 +216,13 @@ public class ConfigService
|
|||||||
String key
|
String key
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (key.startsWith("$") || key.startsWith("_"))
|
Bson set = setForKV(key, null);
|
||||||
|
if (set == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] split = key.split("\\.", 2);
|
mongoCollection.updateOne(eq("_userId", userId), set);
|
||||||
if (split.length != 2)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mongoCollection.updateOne(eq("_userId", userId),
|
|
||||||
unset(split[0] + "." + split[1].replace('.', ':')));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -894,40 +894,32 @@ public class ConfigManager
|
|||||||
private CompletableFuture<Void> sendConfig()
|
private CompletableFuture<Void> sendConfig()
|
||||||
{
|
{
|
||||||
CompletableFuture<Void> future = null;
|
CompletableFuture<Void> future = null;
|
||||||
boolean changed;
|
|
||||||
synchronized (pendingChanges)
|
synchronized (pendingChanges)
|
||||||
{
|
{
|
||||||
|
if (pendingChanges.isEmpty())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (configClient != null)
|
if (configClient != null)
|
||||||
{
|
{
|
||||||
future = CompletableFuture.allOf(pendingChanges.entrySet().stream().map(entry ->
|
Configuration patch = new Configuration(pendingChanges.entrySet().stream()
|
||||||
{
|
.map(e -> new ConfigEntry(e.getKey(), e.getValue()))
|
||||||
String key = entry.getKey();
|
.collect(Collectors.toList()));
|
||||||
String value = entry.getValue();
|
|
||||||
|
|
||||||
if (Strings.isNullOrEmpty(value))
|
future = configClient.patch(patch);
|
||||||
{
|
|
||||||
return configClient.unset(key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return configClient.set(key, value);
|
|
||||||
}
|
|
||||||
}).toArray(CompletableFuture[]::new));
|
|
||||||
}
|
}
|
||||||
changed = !pendingChanges.isEmpty();
|
|
||||||
pendingChanges.clear();
|
pendingChanges.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed)
|
try
|
||||||
{
|
{
|
||||||
try
|
saveToFile(propertiesFile);
|
||||||
{
|
}
|
||||||
saveToFile(propertiesFile);
|
catch (IOException ex)
|
||||||
}
|
{
|
||||||
catch (IOException ex)
|
log.warn("unable to save configuration file", ex);
|
||||||
{
|
|
||||||
log.warn("unable to save configuration file", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
|
|||||||
Reference in New Issue
Block a user