From 9f551eb552aa566b995e8fc38fa115c4f5969728 Mon Sep 17 00:00:00 2001 From: gbrodman Date: Thu, 17 Aug 2023 15:51:21 -0400 Subject: [PATCH] Refactor the way that the console BE parses POST bodies (#2113) This includes two changes: 1. Creating a base string-type adapter for use parsing to/from JSON classes that are represented as simple strings 2. Changing the object-provider methods so that the POST bodies should contain precisely the expected object(s) and nothing else. This way, it's easier for the frontend and backend to agree that, for instance, one POST endpoint might accept exactly a Registrar object, or a list of Contact objects. --- .../app/shared/services/backend.service.ts | 11 ++--- .../model/adapters/CurrencyJsonAdapter.java | 17 ++------ .../registry/request/RequestModule.java | 9 ++-- .../registrar/RegistrarConsoleModule.java | 21 +++------- .../server/console/RegistrarsActionTest.java | 4 +- .../console/settings/ContactActionTest.java | 3 +- .../console/settings/SecurityActionTest.java | 9 ++-- .../registry/util/CidrAddressBlock.java | 17 +------- .../registry/util/DateTimeTypeAdapter.java | 17 +------- .../registry/util/StringBaseTypeAdapter.java | 41 +++++++++++++++++++ 10 files changed, 69 insertions(+), 80 deletions(-) create mode 100644 util/src/main/java/google/registry/util/StringBaseTypeAdapter.java diff --git a/console-webapp/src/app/shared/services/backend.service.ts b/console-webapp/src/app/shared/services/backend.service.ts index 440653d0d..d02cebcbd 100644 --- a/console-webapp/src/app/shared/services/backend.service.ts +++ b/console-webapp/src/app/shared/services/backend.service.ts @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; -import { Observable, catchError, of } from 'rxjs'; -import { Contact } from '../../settings/contact/contact.service'; +import { Injectable } from '@angular/core'; +import { catchError, Observable, of } from 'rxjs'; import { SecuritySettingsBackendModel } from 'src/app/settings/security/security.service'; +import { Contact } from '../../settings/contact/contact.service'; + @Injectable() export class BackendService { constructor(private http: HttpClient) {} @@ -55,7 +56,7 @@ export class BackendService { ): Observable { return this.http.post( `/console-api/settings/contacts?registrarId=${registrarId}`, - { contacts } + contacts ); } @@ -85,7 +86,7 @@ export class BackendService { ): Observable { return this.http.post( `/console-api/settings/security?registrarId=${registrarId}`, - { registrar: securitySettings } + securitySettings ); } } diff --git a/core/src/main/java/google/registry/model/adapters/CurrencyJsonAdapter.java b/core/src/main/java/google/registry/model/adapters/CurrencyJsonAdapter.java index 0efeb8044..55a35e5db 100644 --- a/core/src/main/java/google/registry/model/adapters/CurrencyJsonAdapter.java +++ b/core/src/main/java/google/registry/model/adapters/CurrencyJsonAdapter.java @@ -14,26 +14,17 @@ package google.registry.model.adapters; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; import google.registry.model.adapters.CurrencyUnitAdapter.UnknownCurrencyException; +import google.registry.util.StringBaseTypeAdapter; import java.io.IOException; import org.joda.money.CurrencyUnit; -public class CurrencyJsonAdapter extends TypeAdapter { +public class CurrencyJsonAdapter extends StringBaseTypeAdapter { @Override - public void write(JsonWriter out, CurrencyUnit value) throws IOException { - String currency = CurrencyUnitAdapter.convertFromCurrency(value); - out.value(currency); - } - - @Override - public CurrencyUnit read(JsonReader in) throws IOException { - String currency = in.nextString(); + protected CurrencyUnit fromString(String stringValue) throws IOException { try { - return CurrencyUnitAdapter.convertFromString(currency); + return CurrencyUnitAdapter.convertFromString(stringValue); } catch (UnknownCurrencyException e) { throw new IOException("Unknown currency"); } diff --git a/core/src/main/java/google/registry/request/RequestModule.java b/core/src/main/java/google/registry/request/RequestModule.java index 5406f49a4..6a56fb945 100644 --- a/core/src/main/java/google/registry/request/RequestModule.java +++ b/core/src/main/java/google/registry/request/RequestModule.java @@ -32,7 +32,7 @@ import com.google.common.io.CharStreams; import com.google.common.net.MediaType; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; +import com.google.gson.JsonElement; import com.google.protobuf.ByteString; import dagger.Module; import dagger.Provides; @@ -263,11 +263,10 @@ public final class RequestModule { @Provides @OptionalJsonPayload - public static Optional provideJsonBody(HttpServletRequest req, Gson gson) { + public static Optional provideJsonBody(HttpServletRequest req, Gson gson) { try { - JsonObject body = gson.fromJson(req.getReader(), JsonObject.class); - return Optional.of(body); - } catch (Exception e) { + return Optional.of(gson.fromJson(req.getReader(), JsonElement.class)); + } catch (IOException e) { return Optional.empty(); } } diff --git a/core/src/main/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java b/core/src/main/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java index 429421bc4..2fe928fa3 100644 --- a/core/src/main/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java +++ b/core/src/main/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java @@ -21,7 +21,7 @@ import static google.registry.request.RequestParameters.extractRequiredParameter import com.google.common.collect.ImmutableSet; import com.google.gson.Gson; -import com.google.gson.JsonObject; +import com.google.gson.JsonElement; import dagger.Module; import dagger.Provides; import google.registry.model.registrar.Registrar; @@ -172,15 +172,8 @@ public final class RegistrarConsoleModule { @Provides @Parameter("contacts") public static Optional> provideContacts( - Gson gson, @OptionalJsonPayload Optional payload) { - - if (payload.isPresent() && payload.get().has("contacts")) { - return Optional.of( - ImmutableSet.copyOf( - gson.fromJson(payload.get().get("contacts").getAsJsonArray(), RegistrarPoc[].class))); - } - - return Optional.empty(); + Gson gson, @OptionalJsonPayload Optional payload) { + return payload.map(s -> ImmutableSet.copyOf(gson.fromJson(s, RegistrarPoc[].class))); } @Provides @@ -192,11 +185,7 @@ public final class RegistrarConsoleModule { @Provides @Parameter("registrar") public static Optional provideRegistrar( - Gson gson, @OptionalJsonPayload Optional payload) { - if (payload.isPresent() && payload.get().has("registrar")) { - return Optional.of(gson.fromJson(payload.get().get("registrar"), Registrar.class)); - } - - return Optional.empty(); + Gson gson, @OptionalJsonPayload Optional payload) { + return payload.map(s -> gson.fromJson(s, Registrar.class)); } } diff --git a/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java b/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java index 8aaf16f03..eafad284e 100644 --- a/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java @@ -251,9 +251,7 @@ class RegistrarsActionTest { passcodeGenerator); } else { try { - doReturn( - new BufferedReader( - new StringReader("{\"registrar\":" + registrarParamMap.toString() + "}"))) + doReturn(new BufferedReader(new StringReader(registrarParamMap.toString()))) .when(request) .getReader(); } catch (IOException e) { diff --git a/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java index ff0d39787..923f395c5 100644 --- a/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java @@ -238,8 +238,7 @@ class ContactActionTest { if (method.equals(Action.Method.GET)) { return new ContactAction(request, authResult, response, GSON, registrarId, Optional.empty()); } else { - when(request.getReader()) - .thenReturn(new BufferedReader(new StringReader("{\"contacts\":" + contacts + "}"))); + when(request.getReader()).thenReturn(new BufferedReader(new StringReader(contacts))); Optional> maybeContacts = RegistrarConsoleModule.provideContacts( GSON, RequestModule.provideJsonBody(request, GSON)); diff --git a/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java index dd70a5af8..bde45c304 100644 --- a/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java @@ -116,12 +116,9 @@ class SecurityActionTest { private SecurityAction createAction(AuthResult authResult, String registrarId) throws IOException { - doReturn(new BufferedReader(new StringReader("{\"registrar\":" + jsonRegistrar1 + "}"))) - .when(request) - .getReader(); - Optional maybeRegistrar = - RegistrarConsoleModule.provideRegistrar( - GSON, RequestModule.provideJsonBody(request, GSON)); + doReturn(new BufferedReader(new StringReader(jsonRegistrar1))).when(request).getReader(); + Optional maybeRegistrar = + RegistrarConsoleModule.provideRegistrar(GSON, RequestModule.provideJsonBody(request, GSON)); return new SecurityAction( authResult, response, diff --git a/util/src/main/java/google/registry/util/CidrAddressBlock.java b/util/src/main/java/google/registry/util/CidrAddressBlock.java index 2c569acf2..d46e37122 100644 --- a/util/src/main/java/google/registry/util/CidrAddressBlock.java +++ b/util/src/main/java/google/registry/util/CidrAddressBlock.java @@ -19,10 +19,6 @@ import static com.google.common.base.Preconditions.checkArgument; import com.google.common.collect.AbstractSequentialIterator; import com.google.common.flogger.FluentLogger; import com.google.common.net.InetAddresses; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; import java.io.Serializable; import java.net.InetAddress; import java.net.UnknownHostException; @@ -481,19 +477,10 @@ public class CidrAddressBlock implements Iterable, Serializable { return getCidrString(ip, netmask); } - public static class CidrAddressBlockAdapter extends TypeAdapter { + public static class CidrAddressBlockAdapter extends StringBaseTypeAdapter { @Override - public CidrAddressBlock read(JsonReader reader) throws IOException { - String stringValue = reader.nextString(); - if (stringValue.equals("null")) { - return null; - } + protected CidrAddressBlock fromString(String stringValue) { return new CidrAddressBlock(stringValue); } - - @Override - public void write(JsonWriter writer, CidrAddressBlock cidrAddressBlock) throws IOException { - writer.value(cidrAddressBlock.toString()); - } } } diff --git a/util/src/main/java/google/registry/util/DateTimeTypeAdapter.java b/util/src/main/java/google/registry/util/DateTimeTypeAdapter.java index 4c922baa8..b3025a2bd 100644 --- a/util/src/main/java/google/registry/util/DateTimeTypeAdapter.java +++ b/util/src/main/java/google/registry/util/DateTimeTypeAdapter.java @@ -14,28 +14,15 @@ package google.registry.util; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; import java.io.IOException; -import java.util.Objects; import org.joda.time.DateTime; import org.joda.time.format.ISODateTimeFormat; /** GSON type adapter for Joda {@link DateTime} objects. */ -public class DateTimeTypeAdapter extends TypeAdapter { +public class DateTimeTypeAdapter extends StringBaseTypeAdapter { @Override - public void write(JsonWriter out, DateTime value) throws IOException { - out.value(Objects.toString(value)); - } - - @Override - public DateTime read(JsonReader in) throws IOException { - String stringValue = in.nextString(); - if (stringValue.equals("null")) { - return null; - } + protected DateTime fromString(String stringValue) throws IOException { return ISODateTimeFormat.dateTime().withZoneUTC().parseDateTime(stringValue); } } diff --git a/util/src/main/java/google/registry/util/StringBaseTypeAdapter.java b/util/src/main/java/google/registry/util/StringBaseTypeAdapter.java new file mode 100644 index 000000000..9054a731a --- /dev/null +++ b/util/src/main/java/google/registry/util/StringBaseTypeAdapter.java @@ -0,0 +1,41 @@ +// Copyright 2023 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.util; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.util.Objects; + +/** Abstract class for {@link TypeAdapter}s that can convert directly to/from strings. */ +public abstract class StringBaseTypeAdapter extends TypeAdapter { + + @Override + public T read(JsonReader reader) throws IOException { + String stringValue = reader.nextString(); + if (stringValue.equals("null")) { + return null; + } + return fromString(stringValue); + } + + @Override + public void write(JsonWriter writer, T t) throws IOException { + writer.value(Objects.toString(t)); + } + + protected abstract T fromString(String stringValue) throws IOException; +}