http-api, http-service, rl-client: bulk upload configuration changes

This commit is contained in:
Max Weber
2020-11-23 00:14:55 -07:00
parent 4309dcd53e
commit e769ee0a7b
6 changed files with 175 additions and 77 deletions

View File

@@ -24,6 +24,7 @@
*/
package net.runelite.http.api.config;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.io.InputStream;
@@ -48,6 +49,7 @@ import okhttp3.Response;
public class ConfigClient
{
private static final MediaType TEXT_PLAIN = MediaType.parse("text/plain");
private static final Gson GSON = RuneLiteAPI.GSON;
private final OkHttpClient client;
private final UUID uuid;
@@ -114,6 +116,59 @@ public class ConfigClient
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)
{
CompletableFuture<Void> future = new CompletableFuture<>();

View File

@@ -24,28 +24,15 @@
*/
package net.runelite.http.api.config;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConfigEntry
{
private String key;
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;
}
}

View File

@@ -26,18 +26,12 @@ package net.runelite.http.api.config;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Configuration
{
private List<ConfigEntry> config = new ArrayList<>();
public Configuration(List<ConfigEntry> config)
{
this.config = config;
}
public List<ConfigEntry> getConfig()
{
return config;
}
}

View File

@@ -25,6 +25,7 @@
package net.runelite.http.service.config;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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 org.springframework.beans.factory.annotation.Autowired;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -66,6 +68,29 @@ public class ConfigController
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)
public void setKey(
HttpServletRequest request,

View File

@@ -25,6 +25,7 @@
package net.runelite.http.service.config;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
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.Indexes;
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.unset;
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.Configuration;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -131,31 +134,79 @@ public class ConfigService
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(
int userId,
String key,
@Nullable String value
)
{
if (key.startsWith("$") || key.startsWith("_"))
Bson set = setForKV(key, value);
if (set == null)
{
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),
set(split[0] + "." + split[1].replace('.', ':'), jsonValue),
set,
upsertUpdateOptions);
return true;
}
@@ -165,19 +216,13 @@ public class ConfigService
String key
)
{
if (key.startsWith("$") || key.startsWith("_"))
Bson set = setForKV(key, null);
if (set == null)
{
return false;
}
String[] split = key.split("\\.", 2);
if (split.length != 2)
{
return false;
}
mongoCollection.updateOne(eq("_userId", userId),
unset(split[0] + "." + split[1].replace('.', ':')));
mongoCollection.updateOne(eq("_userId", userId), set);
return true;
}

View File

@@ -894,40 +894,32 @@ public class ConfigManager
private CompletableFuture<Void> sendConfig()
{
CompletableFuture<Void> future = null;
boolean changed;
synchronized (pendingChanges)
{
if (pendingChanges.isEmpty())
{
return null;
}
if (configClient != null)
{
future = CompletableFuture.allOf(pendingChanges.entrySet().stream().map(entry ->
{
String key = entry.getKey();
String value = entry.getValue();
Configuration patch = new Configuration(pendingChanges.entrySet().stream()
.map(e -> new ConfigEntry(e.getKey(), e.getValue()))
.collect(Collectors.toList()));
if (Strings.isNullOrEmpty(value))
{
return configClient.unset(key);
}
else
{
return configClient.set(key, value);
}
}).toArray(CompletableFuture[]::new));
future = configClient.patch(patch);
}
changed = !pendingChanges.isEmpty();
pendingChanges.clear();
}
if (changed)
try
{
try
{
saveToFile(propertiesFile);
}
catch (IOException ex)
{
log.warn("unable to save configuration file", ex);
}
saveToFile(propertiesFile);
}
catch (IOException ex)
{
log.warn("unable to save configuration file", ex);
}
return future;