Change EppMetric.Builder to use @AutoValue.Builder

Getting rid of builder boilerplate makes my heart sing.  Since we can no
longer @Inject the Builder() constructor, this change adds a provider
in WhiteboxModule that calls a special builderForRequest() factory method,
which gets passed a request ID and Clock and preserves the existing
EppMetric magic that sets the start and end time for you.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=132714432
This commit is contained in:
nickfelt 2016-09-09 14:09:11 -07:00 committed by Ben McIlwain
parent ceb5c2117e
commit 2537e95de5
10 changed files with 108 additions and 107 deletions

View file

@ -18,6 +18,7 @@ import static google.registry.flows.EppXmlTransformer.unmarshal;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import google.registry.flows.FlowModule.EppExceptionInProviderException; import google.registry.flows.FlowModule.EppExceptionInProviderException;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
@ -55,7 +56,7 @@ public final class EppController {
boolean isDryRun, boolean isDryRun,
boolean isSuperuser, boolean isSuperuser,
byte[] inputXmlBytes) { byte[] inputXmlBytes) {
metricBuilder.setClientId(sessionMetadata.getClientId()); metricBuilder.setClientId(Optional.fromNullable(sessionMetadata.getClientId()));
metricBuilder.setPrivilegeLevel(isSuperuser ? "SUPERUSER" : "NORMAL"); metricBuilder.setPrivilegeLevel(isSuperuser ? "SUPERUSER" : "NORMAL");
try { try {
EppInput eppInput; EppInput eppInput;

View file

@ -21,6 +21,7 @@ import google.registry.flows.EppConsoleAction;
import google.registry.flows.EppTlsAction; import google.registry.flows.EppTlsAction;
import google.registry.flows.FlowComponent; import google.registry.flows.FlowComponent;
import google.registry.flows.TlsCredentials.EppTlsModule; import google.registry.flows.TlsCredentials.EppTlsModule;
import google.registry.monitoring.whitebox.WhiteboxModule;
import google.registry.rdap.RdapAutnumAction; import google.registry.rdap.RdapAutnumAction;
import google.registry.rdap.RdapDomainAction; import google.registry.rdap.RdapDomainAction;
import google.registry.rdap.RdapDomainSearchAction; import google.registry.rdap.RdapDomainSearchAction;
@ -50,6 +51,7 @@ import google.registry.whois.WhoisServer;
RdapModule.class, RdapModule.class,
RegistrarUserModule.class, RegistrarUserModule.class,
RequestModule.class, RequestModule.class,
WhiteboxModule.class,
WhoisModule.class, WhoisModule.class,
}) })
interface FrontendRequestComponent { interface FrontendRequestComponent {

View file

@ -24,6 +24,7 @@ java_library(
"//java/google/registry/keyring/api", "//java/google/registry/keyring/api",
"//java/google/registry/loadtest", "//java/google/registry/loadtest",
"//java/google/registry/mapreduce", "//java/google/registry/mapreduce",
"//java/google/registry/monitoring/whitebox",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/request:modules", "//java/google/registry/request:modules",
"//java/google/registry/tools/server", "//java/google/registry/tools/server",

View file

@ -22,6 +22,7 @@ import google.registry.flows.FlowComponent;
import google.registry.loadtest.LoadTestAction; import google.registry.loadtest.LoadTestAction;
import google.registry.loadtest.LoadTestModule; import google.registry.loadtest.LoadTestModule;
import google.registry.mapreduce.MapreduceModule; import google.registry.mapreduce.MapreduceModule;
import google.registry.monitoring.whitebox.WhiteboxModule;
import google.registry.request.RequestModule; import google.registry.request.RequestModule;
import google.registry.request.RequestScope; import google.registry.request.RequestScope;
import google.registry.tools.server.CreateGroupsAction; import google.registry.tools.server.CreateGroupsAction;
@ -54,6 +55,7 @@ import google.registry.tools.server.javascrap.RefreshAllDomainsAction;
MapreduceModule.class, MapreduceModule.class,
RequestModule.class, RequestModule.class,
ToolsServerModule.class, ToolsServerModule.class,
WhiteboxModule.class,
}) })
interface ToolsRequestComponent { interface ToolsRequestComponent {
BackfillAutorenewBillingFlagAction backfillAutorenewBillingFlagAction(); BackfillAutorenewBillingFlagAction backfillAutorenewBillingFlagAction();

View file

@ -14,21 +14,18 @@
package google.registry.monitoring.whitebox; package google.registry.monitoring.whitebox;
import static com.google.apphosting.api.ApiProxy.getCurrentEnvironment;
import static google.registry.bigquery.BigqueryUtils.toBigqueryTimestamp; import static google.registry.bigquery.BigqueryUtils.toBigqueryTimestamp;
import static org.joda.time.DateTimeZone.UTC;
import com.google.api.services.bigquery.model.TableFieldSchema; import com.google.api.services.bigquery.model.TableFieldSchema;
import com.google.auto.value.AutoValue; import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import google.registry.bigquery.BigqueryUtils.FieldType; import google.registry.bigquery.BigqueryUtils.FieldType;
import google.registry.model.eppoutput.Result.Code; import google.registry.model.eppoutput.Result.Code;
import google.registry.request.RequestScope; import google.registry.request.RequestScope;
import google.registry.util.Clock;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** /**
@ -53,30 +50,6 @@ public abstract class EppMetric implements BigQueryMetric {
new TableFieldSchema().setName("eppStatus").setType(FieldType.INTEGER.name()), new TableFieldSchema().setName("eppStatus").setType(FieldType.INTEGER.name()),
new TableFieldSchema().setName("attempts").setType(FieldType.INTEGER.name())); new TableFieldSchema().setName("attempts").setType(FieldType.INTEGER.name()));
private static final String REQUEST_LOG_ID = "com.google.appengine.runtime.request_log_id";
private static EppMetric create(
String requestId,
DateTime startTimestamp,
DateTime endTimestamp,
String commandName,
String clientId,
String privilegeLevel,
String eppTarget,
Code status,
int attempts) {
return new AutoValue_EppMetric(
requestId,
startTimestamp,
endTimestamp,
Optional.fromNullable(commandName),
Optional.fromNullable(clientId),
Optional.fromNullable(privilegeLevel),
Optional.fromNullable(eppTarget),
Optional.fromNullable(status),
attempts);
}
public abstract String getRequestId(); public abstract String getRequestId();
public abstract DateTime getStartTimestamp(); public abstract DateTime getStartTimestamp();
@ -141,90 +114,78 @@ public abstract class EppMetric implements BigQueryMetric {
} }
} }
/** A builder to create instances of {@link EppMetric}. */ /** Create an {@link EppMetric.Builder}. */
public static class Builder { public static Builder builder() {
return new AutoValue_EppMetric.Builder();
// Required values }
private final String requestId;
private final DateTime startTimestamp;
private int attempts = 0;
// Optional values
private String commandName;
private String clientId;
private String privilegeLevel;
private String eppTarget;
private Code status;
/** /**
* Create an {@link EppMetric.Builder}. * Create an {@link EppMetric.Builder} for a request context, with the given request ID and
* with start and end timestamps taken from the given clock.
* *
* <p>The start timestamp of metrics created via this instance's {@link Builder#build()} will be * <p>The start timestamp is recorded now, and the end timestamp at {@code build()}.
* the time that this builder was created.
*/ */
@Inject public static Builder builderForRequest(String requestId, Clock clock) {
public Builder() { return builder()
this(DateTime.now(UTC)); .setRequestId(requestId)
.setStartTimestamp(clock.nowUtc())
.setClock(clock);
} }
@VisibleForTesting /** A builder to create instances of {@link EppMetric}. */
Builder(DateTime startTimestamp) { @AutoValue.Builder
this.requestId = getCurrentEnvironment().getAttributes().get(REQUEST_LOG_ID).toString(); public abstract static class Builder {
this.startTimestamp = startTimestamp;
this.attempts = 0;
}
public Builder setCommandName(String value) { /** Builder-only counter of the number of attempts, to support {@link #incrementAttempts()}. */
commandName = value; private int attempts = 0;
return this;
}
public Builder setClientId(String value) { /** Builder-only clock to support automatic recording of endTimestamp on {@link #build()}. */
clientId = value; private Clock clock = null;
return this;
}
public Builder setPrivilegeLevel(String value) { abstract Builder setRequestId(String requestId);
privilegeLevel = value;
return this;
}
public Builder setEppTarget(String value) { abstract Builder setStartTimestamp(DateTime startTimestamp);
eppTarget = value;
return this;
}
public Builder setStatus(Code value) { abstract Builder setEndTimestamp(DateTime endTimestamp);
status = value;
return this; public abstract Builder setCommandName(String commandName);
}
public abstract Builder setClientId(String clientId);
public abstract Builder setClientId(Optional<String> clientId);
public abstract Builder setPrivilegeLevel(String privilegeLevel);
public abstract Builder setEppTarget(String eppTarget);
public abstract Builder setStatus(Code code);
abstract Builder setAttempts(Integer attempts);
public Builder incrementAttempts() { public Builder incrementAttempts() {
attempts++; attempts++;
return this; return this;
} }
@VisibleForTesting Builder setClock(Clock clock) {
EppMetric build(DateTime endTimestamp) { this.clock = clock;
return EppMetric.create( return this;
requestId,
startTimestamp,
endTimestamp,
commandName,
clientId,
privilegeLevel,
eppTarget,
status,
attempts);
} }
/** /**
* Build an instance of {@link EppMetric} using this builder. * Build an instance of {@link EppMetric} using this builder.
* *
* <p>The end timestamp of the metric will be the current time. * <p>If a clock was provided with {@code setClock()}, the end timestamp will be set to the
* current timestamp of the clock; otherwise end timestamp must have been previously set.
*/ */
public EppMetric build() { public EppMetric build() {
return build(DateTime.now(UTC)); setAttempts(attempts);
if (clock != null) {
setEndTimestamp(clock.nowUtc());
} }
return autoBuild();
}
abstract EppMetric autoBuild();
} }
} }

View file

@ -17,6 +17,7 @@ package google.registry.monitoring.whitebox;
import static google.registry.request.RequestParameters.extractRequiredParameter; import static google.registry.request.RequestParameters.extractRequiredParameter;
import com.google.api.services.bigquery.model.TableFieldSchema; import com.google.api.services.bigquery.model.TableFieldSchema;
import com.google.apphosting.api.ApiProxy;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import dagger.Module; import dagger.Module;
@ -24,7 +25,9 @@ import dagger.Provides;
import dagger.multibindings.IntoMap; import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey; import dagger.multibindings.StringKey;
import google.registry.request.Parameter; import google.registry.request.Parameter;
import google.registry.util.Clock;
import java.util.UUID; import java.util.UUID;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
/** /**
@ -33,6 +36,8 @@ import javax.servlet.http.HttpServletRequest;
@Module @Module
public class WhiteboxModule { public class WhiteboxModule {
private static final String REQUEST_LOG_ID = "com.google.appengine.runtime.request_log_id";
@Provides @Provides
@IntoMap @IntoMap
@StringKey(EppMetric.TABLE_ID) @StringKey(EppMetric.TABLE_ID)
@ -68,4 +73,17 @@ public class WhiteboxModule {
} }
}; };
} }
@Provides
@Named("requestLogId")
static String provideRequestLogId() {
return ApiProxy.getCurrentEnvironment().getAttributes().get(REQUEST_LOG_ID).toString();
}
/** Provides an EppMetric builder with the request ID and startTimestamp already initialized. */
@Provides
static EppMetric.Builder provideEppMetricBuilder(
@Named("requestLogId") String requestLogId, Clock clock) {
return EppMetric.builderForRequest(requestLogId, clock);
}
} }

View file

@ -32,8 +32,10 @@ import google.registry.monitoring.whitebox.EppMetric;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.ShardableTestCase; import google.registry.testing.ShardableTestCase;
import google.registry.util.Clock;
import google.registry.util.SystemClock; import google.registry.util.SystemClock;
import google.registry.xml.ValidationMode; import google.registry.xml.ValidationMode;
import org.joda.time.DateTime;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -61,6 +63,10 @@ public class EppControllerTest extends ShardableTestCase {
@Mock EppResponse eppResponse; @Mock EppResponse eppResponse;
@Mock Result result; @Mock Result result;
private static final DateTime startTime = DateTime.parse("2016-09-01T00:00:00Z");
private final Clock clock = new FakeClock(startTime);
private EppController eppController; private EppController eppController;
@Before @Before
@ -77,9 +83,9 @@ public class EppControllerTest extends ShardableTestCase {
when(result.getCode()).thenReturn(Code.SuccessWithNoMessages); when(result.getCode()).thenReturn(Code.SuccessWithNoMessages);
eppController = new EppController(); eppController = new EppController();
eppController.metricBuilder = new EppMetric.Builder(); eppController.metricBuilder = EppMetric.builderForRequest("request-id-1", clock);
eppController.bigQueryMetricsEnqueuer = metricsEnqueuer; eppController.bigQueryMetricsEnqueuer = metricsEnqueuer;
eppController.clock = new FakeClock(); eppController.clock = clock;
eppController.flowComponentBuilder = flowComponentBuilder; eppController.flowComponentBuilder = flowComponentBuilder;
eppController.eppMetrics = eppMetrics; eppController.eppMetrics = eppMetrics;
} }
@ -105,6 +111,9 @@ public class EppControllerTest extends ShardableTestCase {
verify(metricsEnqueuer).export(metricCaptor.capture()); verify(metricsEnqueuer).export(metricCaptor.capture());
EppMetric metric = metricCaptor.getValue(); EppMetric metric = metricCaptor.getValue();
assertThat(metric.getRequestId()).isEqualTo("request-id-1");
assertThat(metric.getStartTimestamp()).isEqualTo(startTime);
assertThat(metric.getEndTimestamp()).isEqualTo(clock.nowUtc());
assertThat(metric.getClientId()).hasValue("some-client"); assertThat(metric.getClientId()).hasValue("some-client");
assertThat(metric.getPrivilegeLevel()).hasValue("NORMAL"); assertThat(metric.getPrivilegeLevel()).hasValue("NORMAL");
assertThat(metric.getStatus()).hasValue(Code.SyntaxError); assertThat(metric.getStatus()).hasValue(Code.SyntaxError);
@ -126,6 +135,9 @@ public class EppControllerTest extends ShardableTestCase {
verify(metricsEnqueuer).export(metricCaptor.capture()); verify(metricsEnqueuer).export(metricCaptor.capture());
EppMetric metric = metricCaptor.getValue(); EppMetric metric = metricCaptor.getValue();
assertThat(metric.getRequestId()).isEqualTo("request-id-1");
assertThat(metric.getStartTimestamp()).isEqualTo(startTime);
assertThat(metric.getEndTimestamp()).isEqualTo(clock.nowUtc());
assertThat(metric.getClientId()).hasValue("some-client"); assertThat(metric.getClientId()).hasValue("some-client");
assertThat(metric.getPrivilegeLevel()).hasValue("SUPERUSER"); assertThat(metric.getPrivilegeLevel()).hasValue("SUPERUSER");
assertThat(metric.getStatus()).hasValue(Code.SuccessWithNoMessages); assertThat(metric.getStatus()).hasValue(Code.SuccessWithNoMessages);

View file

@ -42,13 +42,13 @@ interface EppTestComponent {
@Module @Module
static class FakesAndMocksModule { static class FakesAndMocksModule {
final FakeClock clock; final FakeClock clock;
final EppMetric.Builder metrics; final EppMetric.Builder metricBuilder;
final BigQueryMetricsEnqueuer metricsEnqueuer; final BigQueryMetricsEnqueuer metricsEnqueuer;
final ModulesService modulesService; final ModulesService modulesService;
FakesAndMocksModule(FakeClock clock) { FakesAndMocksModule(FakeClock clock) {
this.clock = clock; this.clock = clock;
this.metrics = new EppMetric.Builder(); this.metricBuilder = EppMetric.builderForRequest("request-id-1", clock);
this.modulesService = mock(ModulesService.class); this.modulesService = mock(ModulesService.class);
this.metricsEnqueuer = mock(BigQueryMetricsEnqueuer.class); this.metricsEnqueuer = mock(BigQueryMetricsEnqueuer.class);
} }
@ -60,7 +60,7 @@ interface EppTestComponent {
@Provides @Provides
EppMetric.Builder provideMetrics() { EppMetric.Builder provideMetrics() {
return metrics; return metricBuilder;
} }
@Provides @Provides

View file

@ -90,7 +90,7 @@ public class FlowRunnerTest extends ShardableTestCase {
flowRunner.isDryRun = false; flowRunner.isDryRun = false;
flowRunner.isSuperuser = false; flowRunner.isSuperuser = false;
flowRunner.isTransactional = false; flowRunner.isTransactional = false;
flowRunner.metric = new EppMetric.Builder(); flowRunner.metric = EppMetric.builderForRequest("request-id-1", flowRunner.clock);
flowRunner.sessionMetadata = flowRunner.sessionMetadata =
new StatelessRequestSessionMetadata("TheRegistrar", ImmutableSet.<String>of()); new StatelessRequestSessionMetadata("TheRegistrar", ImmutableSet.<String>of());
flowRunner.trid = Trid.create("client-123", "server-456"); flowRunner.trid = Trid.create("client-123", "server-456");

View file

@ -36,21 +36,22 @@ public class EppMetricTest {
@Test @Test
public void testGetBigQueryRowEncoding_encodesCorrectly() throws Exception { public void testGetBigQueryRowEncoding_encodesCorrectly() throws Exception {
EppMetric metric = EppMetric metric =
new EppMetric.Builder(new DateTime(1337)) EppMetric.builder()
.setEppTarget("target") .setRequestId("request-id-1")
.setPrivilegeLevel("level") .setStartTimestamp(new DateTime(1337))
.setEndTimestamp(new DateTime(1338))
.setCommandName("command") .setCommandName("command")
.setClientId("client") .setClientId("client")
.setPrivilegeLevel("level")
.setEppTarget("target")
.setStatus(Code.CommandUseError) .setStatus(Code.CommandUseError)
.incrementAttempts() .incrementAttempts()
.build(new DateTime(1338)); .build();
// The request_id is randomly generated and hard to mock without a lot of supporting code
// so we just use the tested metric's request_id verbatim.
assertThat(metric.getBigQueryRowEncoding()) assertThat(metric.getBigQueryRowEncoding())
.containsExactlyEntriesIn( .containsExactlyEntriesIn(
new ImmutableMap.Builder<String, String>() new ImmutableMap.Builder<String, String>()
.put("requestId", metric.getRequestId()) .put("requestId", "request-id-1")
.put("startTime", "1.337000") .put("startTime", "1.337000")
.put("endTime", "1.338000") .put("endTime", "1.338000")
.put("commandName", "command") .put("commandName", "command")
@ -65,14 +66,17 @@ public class EppMetricTest {
@Test @Test
public void testGetBigQueryRowEncoding_hasAllSchemaFields() throws Exception { public void testGetBigQueryRowEncoding_hasAllSchemaFields() throws Exception {
EppMetric metric = EppMetric metric =
new EppMetric.Builder(new DateTime(1337)) EppMetric.builder()
.setEppTarget("target") .setRequestId("request-id-1")
.setPrivilegeLevel("level") .setStartTimestamp(new DateTime(1337))
.setEndTimestamp(new DateTime(1338))
.setCommandName("command") .setCommandName("command")
.setClientId("client") .setClientId("client")
.setPrivilegeLevel("level")
.setEppTarget("target")
.setStatus(Code.CommandUseError) .setStatus(Code.CommandUseError)
.incrementAttempts() .incrementAttempts()
.build(new DateTime(1338)); .build();
ImmutableSet.Builder<String> schemaFieldNames = new ImmutableSet.Builder<>(); ImmutableSet.Builder<String> schemaFieldNames = new ImmutableSet.Builder<>();
for (TableFieldSchema schemaField : metric.getSchemaFields()) { for (TableFieldSchema schemaField : metric.getSchemaFields()) {
schemaFieldNames.add(schemaField.getName()); schemaFieldNames.add(schemaField.getName());