From f614044681d3c7053950736d740ac7cdcfb68069 Mon Sep 17 00:00:00 2001 From: mcilwain Date: Mon, 30 Jul 2018 14:21:14 -0700 Subject: [PATCH] Add an auto update time field to the Cursor entity Also adjusts the nomulus list_cursors command to output the value of this field. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=206646117 --- .../registry/model/UpdateAutoTimestamp.java | 5 +-- java/google/registry/model/common/Cursor.java | 8 +++++ .../registry/tools/ListCursorsCommand.java | 32 +++++++++++++------ .../registry/model/common/CursorTest.java | 1 + .../google/registry/model/testdata/schema.txt | 1 + .../tools/ListCursorsCommandTest.java | 29 +++++++++++++++-- 6 files changed, 62 insertions(+), 14 deletions(-) diff --git a/java/google/registry/model/UpdateAutoTimestamp.java b/java/google/registry/model/UpdateAutoTimestamp.java index f8f250997..50a0b5068 100644 --- a/java/google/registry/model/UpdateAutoTimestamp.java +++ b/java/google/registry/model/UpdateAutoTimestamp.java @@ -18,6 +18,7 @@ import static google.registry.util.DateTimeUtils.START_OF_TIME; import google.registry.model.translators.UpdateAutoTimestampTranslatorFactory; import java.util.Optional; +import javax.annotation.Nullable; import org.joda.time.DateTime; /** @@ -29,12 +30,12 @@ public class UpdateAutoTimestamp extends ImmutableObject { DateTime timestamp; - /** Returns the timestamp, or {@link #START_OF_TIME} if it's null. */ + /** Returns the timestamp, or {@link START_OF_TIME} if it's null. */ public DateTime getTimestamp() { return Optional.ofNullable(timestamp).orElse(START_OF_TIME); } - public static UpdateAutoTimestamp create(DateTime timestamp) { + public static UpdateAutoTimestamp create(@Nullable DateTime timestamp) { UpdateAutoTimestamp instance = new UpdateAutoTimestamp(); instance.timestamp = timestamp; return instance; diff --git a/java/google/registry/model/common/Cursor.java b/java/google/registry/model/common/Cursor.java index 66d150bf1..b9de19f0a 100644 --- a/java/google/registry/model/common/Cursor.java +++ b/java/google/registry/model/common/Cursor.java @@ -25,6 +25,7 @@ import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Parent; import google.registry.model.ImmutableObject; +import google.registry.model.UpdateAutoTimestamp; import google.registry.model.registry.Registry; import org.joda.time.DateTime; @@ -108,6 +109,13 @@ public class Cursor extends ImmutableObject { DateTime cursorTime = START_OF_TIME; + /** An automatically managed timestamp of when this object was last written to Datastore. */ + UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null); + + public DateTime getLastUpdateTime() { + return lastUpdateTime.getTimestamp(); + } + /** * Checks that the type of the scoped object (or null) matches the required type for the specified * cursor (or null, if the cursor is a global cursor). diff --git a/java/google/registry/tools/ListCursorsCommand.java b/java/google/registry/tools/ListCursorsCommand.java index 7f5ddbcc6..822243167 100644 --- a/java/google/registry/tools/ListCursorsCommand.java +++ b/java/google/registry/tools/ListCursorsCommand.java @@ -19,6 +19,7 @@ import static google.registry.model.ofy.ObjectifyService.ofy; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; +import com.google.common.base.Strings; import com.googlecode.objectify.Key; import google.registry.model.common.Cursor; import google.registry.model.common.Cursor.CursorType; @@ -27,6 +28,7 @@ import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldType; import google.registry.tools.Command.RemoteApiCommand; import java.util.Map; +import java.util.Optional; /** Lists {@link Cursor} timestamps used by locking rolling cursor tasks, like in RDE. */ @Parameters(separators = " =", commandDescription = "Lists cursor timestamps used by LRC tasks") @@ -45,6 +47,8 @@ final class ListCursorsCommand implements RemoteApiCommand { description = "Filter TLDs to only include those with RDE escrow enabled; defaults to false.") private boolean filterEscrowEnabled = false; + private static final String OUTPUT_FMT = "%-20s %-24s %-24s"; + @Override public void run() { Map> registries = @@ -55,18 +59,26 @@ final class ListCursorsCommand implements RemoteApiCommand { .filter(r -> !filterEscrowEnabled || r.getEscrowEnabled()) .collect(toImmutableMap(r -> r, r -> Cursor.createKey(cursorType, r))); Map, Cursor> cursors = ofy().load().keys(registries.values()); - registries - .entrySet() - .stream() - .map(e -> renderLine(e.getKey().getTldStr(), e.getValue(), cursors)) - .sorted() - .forEach(System.out::println); + if (!registries.isEmpty()) { + String header = String.format(OUTPUT_FMT, "TLD", "Cursor Time", "Last Update Time"); + System.out.printf("%s\n%s\n", header, Strings.repeat("-", header.length())); + registries + .entrySet() + .stream() + .map( + e -> + renderLine( + e.getKey().getTldStr(), Optional.ofNullable(cursors.get(e.getValue())))) + .sorted() + .forEach(System.out::println); + } } - private static String renderLine( - String tld, Key cursorKey, Map, Cursor> cursors) { + private static String renderLine(String tld, Optional cursor) { return String.format( - "%-25s%s", - cursors.containsKey(cursorKey) ? cursors.get(cursorKey).getCursorTime() : "(absent)", tld); + OUTPUT_FMT, + tld, + cursor.map(c -> c.getCursorTime().toString()).orElse("(absent)"), + cursor.map(c -> c.getLastUpdateTime().toString()).orElse("(absent)")); } } diff --git a/javatests/google/registry/model/common/CursorTest.java b/javatests/google/registry/model/common/CursorTest.java index 9ab51d767..68a74a71b 100644 --- a/javatests/google/registry/model/common/CursorTest.java +++ b/javatests/google/registry/model/common/CursorTest.java @@ -32,6 +32,7 @@ import org.junit.Test; /** Unit tests for {@link Cursor}. */ public class CursorTest extends EntityTestCase { + @Test public void testSuccess_persistScopedCursor() { createTld("tld"); diff --git a/javatests/google/registry/model/testdata/schema.txt b/javatests/google/registry/model/testdata/schema.txt index af11e4c3f..8f02b0328 100644 --- a/javatests/google/registry/model/testdata/schema.txt +++ b/javatests/google/registry/model/testdata/schema.txt @@ -73,6 +73,7 @@ class google.registry.model.billing.BillingEvent$Recurring { class google.registry.model.common.Cursor { @Id java.lang.String id; @Parent com.googlecode.objectify.Key parent; + google.registry.model.UpdateAutoTimestamp lastUpdateTime; org.joda.time.DateTime cursorTime; } class google.registry.model.common.EntityGroupRoot { diff --git a/javatests/google/registry/tools/ListCursorsCommandTest.java b/javatests/google/registry/tools/ListCursorsCommandTest.java index a5cb6a61b..c2369b7b8 100644 --- a/javatests/google/registry/tools/ListCursorsCommandTest.java +++ b/javatests/google/registry/tools/ListCursorsCommandTest.java @@ -22,13 +22,32 @@ import static google.registry.testing.JUnitBackports.assertThrows; import com.beust.jcommander.ParameterException; import google.registry.model.common.Cursor; import google.registry.model.common.Cursor.CursorType; +import google.registry.model.ofy.Ofy; import google.registry.model.registry.Registry; +import google.registry.testing.FakeClock; +import google.registry.testing.InjectRule; import org.joda.time.DateTime; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; /** Unit tests for {@link ListCursorsCommand}. */ public class ListCursorsCommandTest extends CommandTestCase { + private static final String HEADER_ONE = + "TLD Cursor Time Last Update Time"; + + private static final String HEADER_TWO = + "--------------------------------------------------------------------------"; + + @Rule public final InjectRule inject = new InjectRule(); + + @Before + public void before() { + inject.setStaticField( + Ofy.class, "clock", new FakeClock(DateTime.parse("1984-12-21T06:07:08.789Z"))); + } + @Test public void testListCursors_noTlds_printsNothing() throws Exception { runCommand("--type=BRDA"); @@ -42,7 +61,11 @@ public class ListCursorsCommandTest extends CommandTestCase Cursor.create(CursorType.BRDA, DateTime.parse("1984-12-18TZ"), Registry.get("bar"))); runCommand("--type=BRDA"); assertThat(getStdoutAsLines()) - .containsExactly("(absent) foo", "1984-12-18T00:00:00.000Z bar") + .containsExactly( + HEADER_ONE, + HEADER_TWO, + "bar 1984-12-18T00:00:00.000Z 1984-12-21T06:07:08.789Z", + "foo (absent) (absent)") .inOrder(); } @@ -63,6 +86,8 @@ public class ListCursorsCommandTest extends CommandTestCase createTlds("foo", "bar"); persistResource(Registry.get("bar").asBuilder().setEscrowEnabled(true).build()); runCommand("--type=BRDA", "--escrow_enabled"); - assertThat(getStdoutAsLines()).containsExactly("(absent) bar"); + assertThat(getStdoutAsLines()) + .containsExactly( + HEADER_ONE, HEADER_TWO, "bar (absent) (absent)"); } }