Convert more routes to use body fields instead of query params

This commit is contained in:
Colin McDonald 2016-06-23 00:56:04 -04:00
parent c00b424fe8
commit b291e9e13b
14 changed files with 80 additions and 69 deletions

View File

@ -1,6 +1,7 @@
package net.frozenorb.apiv3.route.auditLog; package net.frozenorb.apiv3.route.auditLog;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
@ -18,7 +19,8 @@ public final class POSTAuditLog implements Handler<RoutingContext> {
} else if (user == null) { } else if (user == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id")); ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("id"));
} else { } else {
String userIp = ctx.request().getParam("userIp"); JsonObject requestBody = ctx.getBodyAsJson();
String userIp = requestBody.getString("userIp");
if (!IpUtils.isValidIp(userIp)) { if (!IpUtils.isValidIp(userIp)) {
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
@ -28,9 +30,9 @@ public final class POSTAuditLog implements Handler<RoutingContext> {
AuditLogActionType type; AuditLogActionType type;
try { try {
type = AuditLogActionType.valueOf(ctx.request().getParam("type")); type = AuditLogActionType.valueOf(requestBody.getString("type"));
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
ErrorUtils.respondNotFound(ctx, "Audit log action type", ctx.request().getParam("type")); ErrorUtils.respondNotFound(ctx, "Audit log action type", requestBody.getString("type"));
return; return;
} }

View File

@ -2,6 +2,7 @@ package net.frozenorb.apiv3.route.emailToken;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
@ -31,7 +32,8 @@ public final class POSTEmailTokensConfirm implements Handler<RoutingContext> {
return; return;
} }
String password = ctx.request().getParam("password"); JsonObject requestBody = ctx.getBodyAsJson();
String password = requestBody.getString("password");
if (password.length() < 8) { if (password.length() < 8) {
ErrorUtils.respondGeneric(ctx, 200, "Your password is too short."); ErrorUtils.respondGeneric(ctx, 200, "Your password is too short.");

View File

@ -2,6 +2,7 @@ package net.frozenorb.apiv3.route.grants;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
@ -25,14 +26,15 @@ public final class DELETEGrantsId implements Handler<RoutingContext> {
return; return;
} }
User removedBy = User.findByIdSync(ctx.request().getParam("removedBy")); JsonObject requestBody = ctx.getBodyAsJson();
User removedBy = User.findByIdSync(requestBody.getString("removedBy"));
if (removedBy == null) { if (removedBy == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); ErrorUtils.respondNotFound(ctx, "User", requestBody.getString("removedBy"));
return; return;
} }
String reason = ctx.request().getParam("reason"); String reason = requestBody.getString("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
ErrorUtils.respondRequiredInput(ctx, "reason"); ErrorUtils.respondRequiredInput(ctx, "reason");

View File

@ -2,6 +2,7 @@ package net.frozenorb.apiv3.route.ipBans;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
@ -25,14 +26,15 @@ public final class DELETEIpBan implements Handler<RoutingContext> {
return; return;
} }
User removedBy = User.findByIdSync(ctx.request().getParam("removedBy")); JsonObject requestBody = ctx.getBodyAsJson();
User removedBy = User.findByIdSync(requestBody.getString("removedBy"));
if (removedBy == null) { if (removedBy == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); ErrorUtils.respondNotFound(ctx, "User", requestBody.getString("removedBy"));
return; return;
} }
String reason = ctx.request().getParam("reason"); String reason = requestBody.getString("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
ErrorUtils.respondRequiredInput(ctx, "reason"); ErrorUtils.respondRequiredInput(ctx, "reason");

View File

@ -1,6 +1,7 @@
package net.frozenorb.apiv3.route.ipBans; package net.frozenorb.apiv3.route.ipBans;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.IpBan; import net.frozenorb.apiv3.model.IpBan;
@ -13,14 +14,15 @@ import java.time.Instant;
public final class POSTIpBans implements Handler<RoutingContext> { public final class POSTIpBans implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
String userIp = ctx.request().getParam("id"); JsonObject requestBody = ctx.getBodyAsJson();
String userIp = requestBody.getString("id");
if (!IpUtils.isValidIp(userIp)) { if (!IpUtils.isValidIp(userIp)) {
ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid."); ErrorUtils.respondInvalidInput(ctx, "Ip address \"" + userIp + "\" is not valid.");
return; return;
} }
String reason = ctx.request().getParam("reason"); String reason = requestBody.getString("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
ErrorUtils.respondRequiredInput(ctx, "reason"); ErrorUtils.respondRequiredInput(ctx, "reason");
@ -29,10 +31,8 @@ public final class POSTIpBans implements Handler<RoutingContext> {
Instant expiresAt = null; Instant expiresAt = null;
try { if (requestBody.containsKey("expiresAt") && requestBody.getLong("expiresAt") != -1) {
expiresAt = Instant.ofEpochMilli(Long.parseLong(ctx.request().getParam("expiresAt"))); expiresAt = Instant.ofEpochMilli(requestBody.getLong("expiresAt"));
} catch (NumberFormatException ignored) {
// Just leave it null, we don't need an expiration date.
} }
if (expiresAt != null && expiresAt.isBefore(Instant.now())) { if (expiresAt != null && expiresAt.isBefore(Instant.now())) {
@ -41,7 +41,7 @@ public final class POSTIpBans implements Handler<RoutingContext> {
} }
// We purposely don't do a null check, ip bans don't have to have a source. // We purposely don't do a null check, ip bans don't have to have a source.
User addedBy = User.findByIdSync(ctx.request().getParam("addedBy")); User addedBy = User.findByIdSync(requestBody.getString("addedBy"));
IpBan ipBan = new IpBan(userIp, reason, expiresAt, addedBy, ctx.get("actor")); IpBan ipBan = new IpBan(userIp, reason, expiresAt, addedBy, ctx.get("actor"));
ipBan.insert(); ipBan.insert();

View File

@ -1,6 +1,7 @@
package net.frozenorb.apiv3.route.notificationTemplates; package net.frozenorb.apiv3.route.notificationTemplates;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.NotificationTemplate;
@ -8,9 +9,10 @@ import net.frozenorb.apiv3.model.NotificationTemplate;
public final class POSTNotificationTemplates implements Handler<RoutingContext> { public final class POSTNotificationTemplates implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
String id = ctx.request().getParam("id"); JsonObject requestBody = ctx.getBodyAsJson();
String subject = ctx.request().getParam("subject"); String id = requestBody.getString("id");
String body = ctx.request().getParam("body"); String subject = requestBody.getString("subject");
String body = requestBody.getString("body");
NotificationTemplate notificationTemplate = new NotificationTemplate(id, subject, body); NotificationTemplate notificationTemplate = new NotificationTemplate(id, subject, body);
notificationTemplate.insert(); notificationTemplate.insert();

View File

@ -2,6 +2,7 @@ package net.frozenorb.apiv3.route.punishments;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
@ -25,14 +26,15 @@ public final class DELETEPunishments implements Handler<RoutingContext> {
return; return;
} }
User removedBy = User.findByIdSync(ctx.request().getParam("removedBy")); JsonObject requestBody = ctx.getBodyAsJson();
User removedBy = User.findByIdSync(requestBody.getString("removedBy"));
if (removedBy == null) { if (removedBy == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); ErrorUtils.respondNotFound(ctx, "User", requestBody.getString("removedBy"));
return; return;
} }
String reason = ctx.request().getParam("reason"); String reason = requestBody.getString("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
ErrorUtils.respondRequiredInput(ctx, "reason"); ErrorUtils.respondRequiredInput(ctx, "reason");

View File

@ -3,6 +3,7 @@ package net.frozenorb.apiv3.route.punishments;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.auditLog.AuditLog; import net.frozenorb.apiv3.auditLog.AuditLog;
@ -23,15 +24,16 @@ public final class DELETEUserActivePunishment implements Handler<RoutingContext>
return; return;
} }
Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(ctx.request().getParam("type").toUpperCase()); JsonObject requestBody = ctx.getBodyAsJson();
User removedBy = User.findByIdSync(ctx.request().getParam("removedBy")); Punishment.PunishmentType type = Punishment.PunishmentType.valueOf(requestBody.getString("type").toUpperCase());
User removedBy = User.findByIdSync(requestBody.getString("removedBy"));
if (removedBy == null) { if (removedBy == null) {
ErrorUtils.respondNotFound(ctx, "User", ctx.request().getParam("removedBy")); ErrorUtils.respondNotFound(ctx, "User", requestBody.getString("removedBy"));
return; return;
} }
String reason = ctx.request().getParam("reason"); String reason = requestBody.getString("reason");
if (reason == null || reason.trim().isEmpty()) { if (reason == null || reason.trim().isEmpty()) {
ErrorUtils.respondRequiredInput(ctx, "reason"); ErrorUtils.respondRequiredInput(ctx, "reason");

View File

@ -1,6 +1,7 @@
package net.frozenorb.apiv3.route.ranks; package net.frozenorb.apiv3.route.ranks;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.Rank; import net.frozenorb.apiv3.model.Rank;
@ -8,12 +9,13 @@ import net.frozenorb.apiv3.model.Rank;
public final class POSTRanks implements Handler<RoutingContext> { public final class POSTRanks implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
String id = ctx.request().getParam("id"); JsonObject requestBody = ctx.getBodyAsJson();
int weight = Integer.parseInt(ctx.request().getParam("weight")); String id = requestBody.getString("id");
String displayName = ctx.request().getParam("displayName"); int weight = requestBody.getInteger("weight");
String gameColor = ctx.request().getParam("gameColor"); String displayName = requestBody.getString("displayName");
String websiteColor = ctx.request().getParam("websiteColor"); String gameColor = requestBody.getString("gameColor");
boolean staffRank = Boolean.parseBoolean(ctx.request().getParam("staffRank")); String websiteColor = requestBody.getString("websiteColor");
boolean staffRank = requestBody.getBoolean("staffRank");
Rank rank = new Rank(id, weight, displayName, gameColor, websiteColor, staffRank); Rank rank = new Rank(id, weight, displayName, gameColor, websiteColor, staffRank);
rank.insert(); rank.insert();

View File

@ -1,6 +1,7 @@
package net.frozenorb.apiv3.route.serverGroups; package net.frozenorb.apiv3.route.serverGroups;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.ServerGroup; import net.frozenorb.apiv3.model.ServerGroup;
@ -8,8 +9,9 @@ import net.frozenorb.apiv3.model.ServerGroup;
public final class POSTServerGroups implements Handler<RoutingContext> { public final class POSTServerGroups implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
String id = ctx.request().getParam("id"); JsonObject requestBody = ctx.getBodyAsJson();
String image = ctx.request().getParam("image"); String id = requestBody.getString("id");
String image = requestBody.getString("image");
ServerGroup serverGroup = new ServerGroup(id, image); ServerGroup serverGroup = new ServerGroup(id, image);
serverGroup.insert(); serverGroup.insert();

View File

@ -1,6 +1,7 @@
package net.frozenorb.apiv3.route.servers; package net.frozenorb.apiv3.route.servers;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.Server; import net.frozenorb.apiv3.model.Server;
@ -13,13 +14,14 @@ import java.util.UUID;
public final class POSTServers implements Handler<RoutingContext> { public final class POSTServers implements Handler<RoutingContext> {
public void handle(RoutingContext ctx) { public void handle(RoutingContext ctx) {
String id = ctx.request().getParam("id"); JsonObject requestBody = ctx.getBodyAsJson();
String displayName = ctx.request().getParam("displayName"); String id = requestBody.getString("id");
ServerGroup group = ServerGroup.findById(ctx.request().getParam("group")); String displayName = requestBody.getString("displayName");
String ip = ctx.request().getParam("ip"); ServerGroup group = ServerGroup.findById(requestBody.getString("group"));
String ip = requestBody.getString("ip");
if (group == null) { if (group == null) {
ErrorUtils.respondNotFound(ctx, "Server group", ctx.request().getParam("group")); ErrorUtils.respondNotFound(ctx, "Server group", requestBody.getString("group"));
return; return;
} }

View File

@ -2,6 +2,7 @@ package net.frozenorb.apiv3.route.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
@ -18,12 +19,14 @@ public final class POSTUserChangePassword implements Handler<RoutingContext> {
return; return;
} }
JsonObject requestBody = ctx.getBodyAsJson();
if (user.getPassword() == null) { if (user.getPassword() == null) {
ErrorUtils.respondInvalidInput(ctx, "User provided does not have password set."); ErrorUtils.respondInvalidInput(ctx, "User provided does not have password set.");
return; return;
} }
boolean authorized = user.checkPassword(ctx.request().getParam("currentPassword")); boolean authorized = user.checkPassword(requestBody.getString("currentPassword"));
if (!authorized) { if (!authorized) {
ErrorUtils.respondInvalidInput(ctx, "Current password is not correct."); ErrorUtils.respondInvalidInput(ctx, "Current password is not correct.");
@ -38,7 +41,7 @@ public final class POSTUserChangePassword implements Handler<RoutingContext> {
// TODO // TODO
} }
String newPassword = ctx.request().getParam("newPassword"); String newPassword = requestBody.getString("newPassword");
if (newPassword.length() < 8) { if (newPassword.length() < 8) {
ErrorUtils.respondGeneric(ctx, 200, "Your password is too short."); ErrorUtils.respondGeneric(ctx, 200, "Your password is too short.");

View File

@ -1,11 +1,11 @@
package net.frozenorb.apiv3.route.users; package net.frozenorb.apiv3.route.users;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actor.Actor; import net.frozenorb.apiv3.actor.Actor;
import net.frozenorb.apiv3.actor.ActorType; import net.frozenorb.apiv3.actor.ActorType;
import net.frozenorb.apiv3.actor.actors.ServerActor;
import net.frozenorb.apiv3.model.IpLogEntry; import net.frozenorb.apiv3.model.IpLogEntry;
import net.frozenorb.apiv3.model.Server; import net.frozenorb.apiv3.model.Server;
import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.model.User;
@ -25,23 +25,19 @@ public final class POSTUserLogin implements Handler<RoutingContext> {
return; return;
} }
JsonObject requestBody = ctx.getBodyAsJson();
User user = User.findByIdSync(uuid); User user = User.findByIdSync(uuid);
String username = ctx.request().getParam("username"); String username = requestBody.getString("username");
String userIp = ctx.request().getParam("userIp"); String userIp = requestBody.getString("userIp");
Actor actor = ctx.get("actor"); Actor actor = ctx.get("actor");
Server server;
if (actor.getType() == ActorType.SERVER) { if (actor.getType() != ActorType.SERVER) {
server = ((ServerActor) actor).getServer(); ErrorUtils.respondGeneric(ctx, 400, "This action can only be performed when requested by a server.");
} else { return;
server = Server.findById(ctx.request().getParam("server"));
if (server == null) {
ErrorUtils.respondNotFound(ctx, "Server", ctx.request().getParam("server"));
return;
}
} }
Server server = Server.findById(actor.getName());
if (!IpUtils.isValidIp(userIp)) { if (!IpUtils.isValidIp(userIp)) {
ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid."); ErrorUtils.respondInvalidInput(ctx, "IP address \"" + userIp + "\" is not valid.");
return; return;

View File

@ -2,6 +2,7 @@ package net.frozenorb.apiv3.route.users;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.vertx.core.Handler; import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.NotificationTemplate;
@ -9,7 +10,6 @@ import net.frozenorb.apiv3.model.User;
import net.frozenorb.apiv3.unsorted.Notification; import net.frozenorb.apiv3.unsorted.Notification;
import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.ErrorUtils;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
public final class POSTUserNotify implements Handler<RoutingContext> { public final class POSTUserNotify implements Handler<RoutingContext> {
@ -27,24 +27,16 @@ public final class POSTUserNotify implements Handler<RoutingContext> {
return; return;
} }
NotificationTemplate template = NotificationTemplate.findByIdSync(ctx.request().getParam("template")); JsonObject requestBody = ctx.getBodyAsJson();
NotificationTemplate template = NotificationTemplate.findByIdSync(requestBody.getString("template"));
if (template == null) { if (template == null) {
ErrorUtils.respondNotFound(ctx, "Notification template", ctx.request().getParam("template")); ErrorUtils.respondNotFound(ctx, "Notification template", requestBody.getString("template"));
return; return;
} }
Map<String, Object> subjectReplacements = new HashMap<>(); Map<String, Object> subjectReplacements = requestBody.getJsonObject("subjectReplacements").getMap();
Map<String, Object> bodyReplacements = new HashMap<>(); Map<String, Object> bodyReplacements = requestBody.getJsonObject("subjectReplacements").getMap();
//TODO: Probably make this use the body as json
/*req.queryMap("subject").toMap().forEach((key, values) -> {
subjectReplacements.put(key, values[0]);
});
req.queryMap("body").toMap().forEach((key, values) -> {
bodyReplacements.put(key, values[0]);
});*/
Notification notification = new Notification(template, subjectReplacements, bodyReplacements); Notification notification = new Notification(template, subjectReplacements, bodyReplacements);