Inject a DomainPricingLogic into ExpandRecurringBillingEventsAction (#1648)

* Inject a DomainPricingLogic into ExpandRecurringBillingEventsAction

This will be used in other PRs to set the renewal price correctly based on the
renewal price behavior of the BillingRecurrence event.

Note that, in order for this to work, a not-null constraint has been lifted on
the EPP flow state when the DomainPricingCustomLogic is being constructed, as
the pricing here will occur in a backend action outside the context of any EPP
flow.
This commit is contained in:
Ben McIlwain 2022-05-27 11:46:36 -04:00 committed by GitHub
parent 1e6e8c79f3
commit 1888b2c9a1
10 changed files with 71 additions and 30 deletions

View file

@ -44,6 +44,7 @@ import com.google.common.collect.Range;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.flows.domain.DomainPricingLogic;
import google.registry.mapreduce.MapreduceRunner; import google.registry.mapreduce.MapreduceRunner;
import google.registry.mapreduce.inputs.NullInput; import google.registry.mapreduce.inputs.NullInput;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
@ -98,6 +99,8 @@ public class ExpandRecurringBillingEventsAction implements Runnable {
@Inject @Parameter(PARAM_DRY_RUN) boolean isDryRun; @Inject @Parameter(PARAM_DRY_RUN) boolean isDryRun;
@Inject @Parameter(PARAM_CURSOR_TIME) Optional<DateTime> cursorTimeParam; @Inject @Parameter(PARAM_CURSOR_TIME) Optional<DateTime> cursorTimeParam;
@Inject DomainPricingLogic domainPricingLogic;
@Inject Response response; @Inject Response response;
@Inject ExpandRecurringBillingEventsAction() {} @Inject ExpandRecurringBillingEventsAction() {}

View file

@ -17,6 +17,8 @@ package google.registry.flows.custom;
import google.registry.flows.FlowMetadata; import google.registry.flows.FlowMetadata;
import google.registry.flows.SessionMetadata; import google.registry.flows.SessionMetadata;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
import java.util.Optional;
import javax.annotation.Nullable;
/** /**
* An abstract base class for all flow custom logic that stores the flow's {@link EppInput} and * An abstract base class for all flow custom logic that stores the flow's {@link EppInput} and
@ -24,26 +26,38 @@ import google.registry.model.eppinput.EppInput;
*/ */
public abstract class BaseFlowCustomLogic { public abstract class BaseFlowCustomLogic {
private final EppInput eppInput; @Nullable private final EppInput eppInput;
private final SessionMetadata sessionMetadata; @Nullable private final SessionMetadata sessionMetadata;
private final FlowMetadata flowMetadata; @Nullable private final FlowMetadata flowMetadata;
/**
* Constructs a BaseFlowCustomLogic for the specified EPP flow state.
*
* <p>Note that it is possible for the EPP flow state to be absent, which happens when the custom
* logic is running outside the context of an EPP flow (e.g. {@link DomainPricingCustomLogic} in
* backend actions).
*/
protected BaseFlowCustomLogic( protected BaseFlowCustomLogic(
EppInput eppInput, SessionMetadata sessionMetadata, FlowMetadata flowMetadata) { @Nullable EppInput eppInput,
@Nullable SessionMetadata sessionMetadata,
@Nullable FlowMetadata flowMetadata) {
this.eppInput = eppInput; this.eppInput = eppInput;
this.sessionMetadata = sessionMetadata; this.sessionMetadata = sessionMetadata;
this.flowMetadata = flowMetadata; this.flowMetadata = flowMetadata;
} }
protected EppInput getEppInput() { /** Returns the {@link EppInput}, which may be empty outside a flow context. */
return eppInput; protected Optional<EppInput> getEppInput() {
return Optional.ofNullable(eppInput);
} }
protected SessionMetadata getSessionMetadata() { /** Returns the {@link SessionMetadata}, which may be empty outside a flow context. */
return sessionMetadata; protected Optional<SessionMetadata> getSessionMetadata() {
return Optional.ofNullable(sessionMetadata);
} }
protected FlowMetadata getFlowMetadata() { /** Returns the {@link FlowMetadata}, which may be empty outside a flow context. */
return flowMetadata; protected Optional<FlowMetadata> getFlowMetadata() {
return Optional.ofNullable(flowMetadata);
} }
} }

View file

@ -18,6 +18,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.flows.FlowMetadata; import google.registry.flows.FlowMetadata;
import google.registry.flows.SessionMetadata; import google.registry.flows.SessionMetadata;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
import java.util.Optional;
/** /**
* A no-op base custom logic factory. * A no-op base custom logic factory.
@ -63,7 +64,10 @@ public class CustomLogicFactory {
} }
public DomainPricingCustomLogic forDomainPricing( public DomainPricingCustomLogic forDomainPricing(
EppInput eppInput, SessionMetadata sessionMetadata, FlowMetadata flowMetadata) { Optional<EppInput> eppInput,
return new DomainPricingCustomLogic(eppInput, sessionMetadata, flowMetadata); Optional<SessionMetadata> sessionMetadata,
Optional<FlowMetadata> flowMetadata) {
return new DomainPricingCustomLogic(
eppInput.orElse(null), sessionMetadata.orElse(null), flowMetadata.orElse(null));
} }
} }

View file

@ -14,15 +14,17 @@
package google.registry.flows.custom; package google.registry.flows.custom;
import dagger.BindsOptionalOf;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import google.registry.flows.FlowMetadata; import google.registry.flows.FlowMetadata;
import google.registry.flows.SessionMetadata; import google.registry.flows.SessionMetadata;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
import java.util.Optional;
/** Dagger module to provide instances of custom logic classes for EPP flows. */ /** Dagger module to provide instances of custom logic classes for EPP flows. */
@Module @Module
public class CustomLogicModule { public abstract class CustomLogicModule {
@Provides @Provides
static DomainCreateFlowCustomLogic provideDomainCreateFlowCustomLogic( static DomainCreateFlowCustomLogic provideDomainCreateFlowCustomLogic(
@ -81,9 +83,20 @@ public class CustomLogicModule {
@Provides @Provides
static DomainPricingCustomLogic provideDomainPricingCustomLogic( static DomainPricingCustomLogic provideDomainPricingCustomLogic(
CustomLogicFactory factory, CustomLogicFactory factory,
EppInput eppInput, Optional<EppInput> eppInput,
SessionMetadata sessionMetadata, Optional<SessionMetadata> sessionMetadata,
FlowMetadata flowMetadata) { Optional<FlowMetadata> flowMetadata) {
// Note that, for DomainPricingCustomLogic, the EPP flow state won't be present outside the
// context of an EPP flow.
return factory.forDomainPricing(eppInput, sessionMetadata, flowMetadata); return factory.forDomainPricing(eppInput, sessionMetadata, flowMetadata);
} }
@BindsOptionalOf
abstract EppInput optionalEppInput();
@BindsOptionalOf
abstract SessionMetadata optionalSessionMetadata();
@BindsOptionalOf
abstract FlowMetadata optionalFlowMetadata();
} }

View file

@ -24,6 +24,7 @@ import google.registry.flows.domain.FeesAndCredits;
import google.registry.model.ImmutableObject; import google.registry.model.ImmutableObject;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
import google.registry.model.tld.Registry; import google.registry.model.tld.Registry;
import javax.annotation.Nullable;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** /**
@ -34,7 +35,9 @@ import org.joda.time.DateTime;
public class DomainPricingCustomLogic extends BaseFlowCustomLogic { public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
public DomainPricingCustomLogic( public DomainPricingCustomLogic(
EppInput eppInput, SessionMetadata sessionMetadata, FlowMetadata flowMetadata) { @Nullable EppInput eppInput,
@Nullable SessionMetadata sessionMetadata,
@Nullable FlowMetadata flowMetadata) {
super(eppInput, sessionMetadata, flowMetadata); super(eppInput, sessionMetadata, flowMetadata);
} }

View file

@ -23,7 +23,6 @@ import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.CommandUseErrorException; import google.registry.flows.EppException.CommandUseErrorException;
import google.registry.flows.FlowScope;
import google.registry.flows.custom.DomainPricingCustomLogic; import google.registry.flows.custom.DomainPricingCustomLogic;
import google.registry.flows.custom.DomainPricingCustomLogic.CreatePriceParameters; import google.registry.flows.custom.DomainPricingCustomLogic.CreatePriceParameters;
import google.registry.flows.custom.DomainPricingCustomLogic.RenewPriceParameters; import google.registry.flows.custom.DomainPricingCustomLogic.RenewPriceParameters;
@ -50,7 +49,6 @@ import org.joda.time.DateTime;
* providing a {@link DomainPricingCustomLogic} implementation that operates on cross-TLD or per-TLD * providing a {@link DomainPricingCustomLogic} implementation that operates on cross-TLD or per-TLD
* logic. * logic.
*/ */
@FlowScope
public final class DomainPricingLogic { public final class DomainPricingLogic {
@Inject DomainPricingCustomLogic customLogic; @Inject DomainPricingCustomLogic customLogic;

View file

@ -61,6 +61,7 @@ import google.registry.export.UploadDatastoreBackupAction;
import google.registry.export.sheet.SheetModule; import google.registry.export.sheet.SheetModule;
import google.registry.export.sheet.SyncRegistrarsSheetAction; import google.registry.export.sheet.SyncRegistrarsSheetAction;
import google.registry.flows.FlowComponent; import google.registry.flows.FlowComponent;
import google.registry.flows.custom.CustomLogicModule;
import google.registry.mapreduce.MapreduceModule; import google.registry.mapreduce.MapreduceModule;
import google.registry.model.replay.ReplicateToDatastoreAction; import google.registry.model.replay.ReplicateToDatastoreAction;
import google.registry.monitoring.whitebox.WhiteboxModule; import google.registry.monitoring.whitebox.WhiteboxModule;
@ -103,6 +104,7 @@ import google.registry.tools.javascrap.CreateSyntheticHistoryEntriesAction;
BillingModule.class, BillingModule.class,
CloudDnsWriterModule.class, CloudDnsWriterModule.class,
CronModule.class, CronModule.class,
CustomLogicModule.class,
DnsCountQueryCoordinatorModule.class, DnsCountQueryCoordinatorModule.class,
DnsModule.class, DnsModule.class,
DnsUpdateConfigModule.class, DnsUpdateConfigModule.class,

View file

@ -48,14 +48,14 @@ import google.registry.tools.server.VerifyOteAction;
@RequestScope @RequestScope
@Subcomponent( @Subcomponent(
modules = { modules = {
BackupModule.class, BackupModule.class,
DnsModule.class, DnsModule.class,
EppToolModule.class, EppToolModule.class,
LoadTestModule.class, LoadTestModule.class,
MapreduceModule.class, MapreduceModule.class,
RequestModule.class, RequestModule.class,
ToolsServerModule.class, ToolsServerModule.class,
WhiteboxModule.class, WhiteboxModule.class,
}) })
interface ToolsRequestComponent { interface ToolsRequestComponent {
CreateGroupsAction createGroupsAction(); CreateGroupsAction createGroupsAction();

View file

@ -17,6 +17,7 @@ package google.registry.flows.custom;
import google.registry.flows.FlowMetadata; import google.registry.flows.FlowMetadata;
import google.registry.flows.SessionMetadata; import google.registry.flows.SessionMetadata;
import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.EppInput;
import java.util.Optional;
/** A custom logic factory for testing. */ /** A custom logic factory for testing. */
public class TestCustomLogicFactory extends CustomLogicFactory { public class TestCustomLogicFactory extends CustomLogicFactory {
@ -29,7 +30,10 @@ public class TestCustomLogicFactory extends CustomLogicFactory {
@Override @Override
public DomainPricingCustomLogic forDomainPricing( public DomainPricingCustomLogic forDomainPricing(
EppInput eppInput, SessionMetadata sessionMetadata, FlowMetadata flowMetadata) { Optional<EppInput> eppInput,
return new TestDomainPricingCustomLogic(eppInput, sessionMetadata, flowMetadata); Optional<SessionMetadata> sessionMetadata,
Optional<FlowMetadata> flowMetadata) {
return new TestDomainPricingCustomLogic(
eppInput.orElse(null), sessionMetadata.orElse(null), flowMetadata.orElse(null));
} }
} }

View file

@ -36,7 +36,7 @@ public class TestDomainCreateFlowCustomLogic extends DomainCreateFlowCustomLogic
new PollMessage.OneTime.Builder() new PollMessage.OneTime.Builder()
.setParent(parameters.historyEntry()) .setParent(parameters.historyEntry())
.setEventTime(tm().getTransactionTime()) .setEventTime(tm().getTransactionTime())
.setRegistrarId(getSessionMetadata().getRegistrarId()) .setRegistrarId(getSessionMetadata().get().getRegistrarId())
.setMsg("Custom logic was triggered") .setMsg("Custom logic was triggered")
.build(); .build();
return EntityChanges.newBuilder() return EntityChanges.newBuilder()