Add MR to expand billing events into OneTimes

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=124837292
This commit is contained in:
Chris Tingue 2016-06-14 07:52:13 -07:00 committed by Justine Tunney
parent 7cf4ddce97
commit 2a83d122ef
6 changed files with 69 additions and 8 deletions

View file

@ -265,6 +265,12 @@
<url-pattern>/_dr/task/dnsRefreshForHostRename</url-pattern>
</servlet-mapping>
<!-- Mapreduce to expand recurring billing events into OneTimes. -->
<servlet-mapping>
<servlet-name>backend-servlet</servlet-name>
<url-pattern>/_dr/task/expandRecurringBillingEvents</url-pattern>
</servlet-mapping>
<!-- Security config -->
<security-constraint>
<web-resource-collection>

View file

@ -20,8 +20,6 @@ import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
@ -69,7 +67,12 @@ public class Cursor extends ImmutableObject {
*/
RDE_UPLOAD_SFTP(Registry.class),
/** Cursor for ensuring rolling transactional isolation of recurring billing expansion. */
/**
* Cursor for ensuring rolling transactional isolation of recurring billing expansion. The
* value of this cursor represents the exclusive upper bound on the range of billing times
* for which Recurring billing events have been expanded (i.e. the inclusive first billing time
* for the next expansion job).
*/
RECURRING_BILLING(EntityGroupRoot.class);
/** See the definition of scope on {@link #getScopeClass}. */
@ -120,16 +123,14 @@ public class Cursor extends ImmutableObject {
}
/** Creates a unique key for a given scope and cursor type. */
@VisibleForTesting
static Key<Cursor> createKey(CursorType cursorType, ImmutableObject scope) {
public static Key<Cursor> createKey(CursorType cursorType, ImmutableObject scope) {
Key<? extends ImmutableObject> scopeKey = Key.create(scope);
checkValidCursorTypeForScope(cursorType, scopeKey);
return Key.create(getCrossTldKey(), Cursor.class, generateId(cursorType, scopeKey));
}
/** Creates a unique key for a given global cursor type. */
@VisibleForTesting
static Key<Cursor> createGlobalKey(CursorType cursorType) {
public static Key<Cursor> createGlobalKey(CursorType cursorType) {
checkArgument(
cursorType.getScopeClass().equals(EntityGroupRoot.class),
"Cursor type is not a global cursor.");

View file

@ -14,11 +14,11 @@ java_library(
"//java/com/google/common/net",
"//java/google/registry/backup",
"//java/google/registry/bigquery",
"//java/google/registry/billing",
"//java/google/registry/config",
"//java/google/registry/cron",
"//java/google/registry/dns",
"//java/google/registry/dns/writer/api",
"//java/google/registry/dns/writer/dnsupdate",
"//java/google/registry/export",
"//java/google/registry/export/sheet",
"//java/google/registry/flows",
@ -35,6 +35,7 @@ java_library(
"//java/google/registry/util",
"//third_party/java/bouncycastle",
"//third_party/java/dagger",
"//third_party/java/joda_time",
"//third_party/java/jsr305_annotations",
"//third_party/java/jsr330_inject",
"//third_party/java/servlet/servlet_api",

View file

@ -15,14 +15,20 @@
package google.registry.module.backend;
import static google.registry.model.registry.Registries.assertTldExists;
import static google.registry.request.RequestParameters.extractOptionalDatetimeParameter;
import static google.registry.request.RequestParameters.extractRequiredParameter;
import com.google.common.base.Optional;
import dagger.Module;
import dagger.Provides;
import google.registry.billing.ExpandRecurringBillingEventsAction;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import org.joda.time.DateTime;
import javax.servlet.http.HttpServletRequest;
/**
@ -36,4 +42,11 @@ public class BackendModule {
static String provideTld(HttpServletRequest req) {
return assertTldExists(extractRequiredParameter(req, RequestParameters.PARAM_TLD));
}
@Provides
@Parameter("cursorTime")
static Optional<DateTime> provideCursorTime(HttpServletRequest req) {
return extractOptionalDatetimeParameter(
req, ExpandRecurringBillingEventsAction.PARAM_CURSOR_TIME);
}
}

View file

@ -160,6 +160,25 @@ public final class RequestParameters {
}
}
/**
* Returns first request parameter associated with {@code name} parsed as an
* <a href="https://goo.gl/pk5Q2k">ISO 8601</a> timestamp, e.g. {@code 1984-12-18TZ},
* {@code 2000-01-01T16:20:00Z}.
*
* @throws BadRequestException if request parameter is present but not a valid {@link DateTime}.
*/
public static Optional<DateTime> extractOptionalDatetimeParameter(
HttpServletRequest req, String name) {
String stringParam = req.getParameter(name);
try {
return isNullOrEmpty(stringParam)
? Optional.<DateTime>absent()
: Optional.of(DateTime.parse(stringParam));
} catch (IllegalArgumentException e) {
throw new BadRequestException("Bad ISO 8601 timestamp: " + name);
}
}
/**
* Returns first request parameter associated with {@code name} parsed as an optional
* {@link InetAddress} (which might be IPv6).

View file

@ -17,6 +17,7 @@ package google.registry.request;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.request.RequestParameters.extractBooleanParameter;
import static google.registry.request.RequestParameters.extractEnumParameter;
import static google.registry.request.RequestParameters.extractOptionalDatetimeParameter;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredDatetimeParameter;
import static google.registry.request.RequestParameters.extractRequiredMaybeEmptyParameter;
@ -184,6 +185,26 @@ public class RequestParametersTest {
extractRequiredDatetimeParameter(req, "timeParam");
}
@Test
public void testExtractOptionalDatetimeParameter_correctValue_works() throws Exception {
when(req.getParameter("timeParam")).thenReturn("2015-08-27T13:25:34.123Z");
assertThat(extractOptionalDatetimeParameter(req, "timeParam"))
.hasValue(DateTime.parse("2015-08-27T13:25:34.123Z"));
}
@Test
public void testExtractOptionalDatetimeParameter_badValue_throwsBadRequest() throws Exception {
when(req.getParameter("timeParam")).thenReturn("Tuesday at three o'clock");
thrown.expect(BadRequestException.class, "timeParam");
extractOptionalDatetimeParameter(req, "timeParam");
}
@Test
public void testExtractOptionalDatetimeParameter_empty_returnsAbsent() throws Exception {
when(req.getParameter("timeParam")).thenReturn("");
assertThat(extractOptionalDatetimeParameter(req, "timeParam")).isAbsent();
}
@Test
public void testExtractRequiredDatetimeParameter_noValue_throwsBadRequest() throws Exception {
thrown.expect(BadRequestException.class, "timeParam");