Skip to content

Commit 4b1f52f

Browse files
authored
PIR: Add broker step action handler logic (#8282)
Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1213886961787939?focus=true ### Description Adds logic for the new GenerateEmail action. ### Steps to test this PR Will be testable later on the top stacked PR ### UI changes No UI changes <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes PIR broker automation around email generation for opt-out/scan flows; scope is limited to the state engine handler and side-effect shape, with unit test coverage. > > **Overview** > Adds **broker step handling** for the new **`GenerateEmail`** action: when that action runs, the actions runner moves to **`PirStage.EMAIL_GENERATE`** and emits **`GetEmailForProfile`** (same path as opt-out/email-confirmation steps that need an email but have none). > > **`GetEmailForProfile`** is narrowed to **`actionId`** and **`brokerName`** only—**`extractedProfile`** and **`profileQuery`** are no longer passed on the side effect (call sites and tests updated accordingly). > > Unit tests cover **`GenerateEmail`** on scan, opt-out, and email-confirmation steps. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e6537d7. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 38cfae9 commit 4b1f52f

4 files changed

Lines changed: 119 additions & 21 deletions

File tree

pir/pir-impl/src/main/java/com/duckduckgo/pir/impl/common/actions/ExecuteBrokerStepActionEventHandler.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import com.duckduckgo.pir.impl.scripts.models.BrokerAction.Click
4646
import com.duckduckgo.pir.impl.scripts.models.BrokerAction.EmailConfirmation
4747
import com.duckduckgo.pir.impl.scripts.models.BrokerAction.Expectation
4848
import com.duckduckgo.pir.impl.scripts.models.BrokerAction.FillForm
49+
import com.duckduckgo.pir.impl.scripts.models.BrokerAction.GenerateEmail
4950
import com.duckduckgo.pir.impl.scripts.models.BrokerAction.GetCaptchaInfo
5051
import com.duckduckgo.pir.impl.scripts.models.BrokerAction.SolveCaptcha
5152
import com.duckduckgo.pir.impl.scripts.models.DataSource.EXTRACTED_PROFILE
@@ -97,18 +98,6 @@ class ExecuteBrokerStepActionEventHandler @Inject constructor(
9798
actionToExecute.needsEmail &&
9899
!hasEmail(currentBrokerStep)
99100
) {
100-
val extractedProfile = when (currentBrokerStep) {
101-
is OptOutStep -> {
102-
currentBrokerStep.profileToOptOut
103-
}
104-
105-
is EmailConfirmationStep -> {
106-
currentBrokerStep.profileToOptOut
107-
}
108-
109-
else -> null // Invalid state
110-
}
111-
112101
Next(
113102
nextState = state.copy(
114103
stageStatus = PirStageStatus(
@@ -120,8 +109,19 @@ class ExecuteBrokerStepActionEventHandler @Inject constructor(
120109
GetEmailForProfile(
121110
actionId = actionToExecute.id,
122111
brokerName = currentBrokerStep.broker.name,
123-
extractedProfile = extractedProfile!!,
124-
profileQuery = state.profileQuery,
112+
),
113+
)
114+
} else if (actionToExecute is GenerateEmail) {
115+
Next(
116+
nextState = state.copy(
117+
stageStatus = PirStageStatus(
118+
currentStage = PirStage.EMAIL_GENERATE,
119+
stageStartMs = currentTimeProvider.currentTimeMillis(),
120+
),
121+
),
122+
sideEffect = GetEmailForProfile(
123+
actionId = actionToExecute.id,
124+
brokerName = currentBrokerStep.broker.name,
125125
),
126126
)
127127
} else {

pir/pir-impl/src/main/java/com/duckduckgo/pir/impl/common/actions/PirActionsRunnerStateEngine.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package com.duckduckgo.pir.impl.common.actions
1818

1919
import com.duckduckgo.pir.impl.common.BrokerStepsParser.BrokerStep
2020
import com.duckduckgo.pir.impl.common.PirJob.RunType
21-
import com.duckduckgo.pir.impl.models.ExtractedProfile
2221
import com.duckduckgo.pir.impl.models.ProfileQuery
2322
import com.duckduckgo.pir.impl.pixels.PirStage
2423
import com.duckduckgo.pir.impl.scripts.models.BrokerAction
@@ -175,8 +174,6 @@ interface PirActionsRunnerStateEngine {
175174
data class GetEmailForProfile(
176175
override val actionId: String,
177176
val brokerName: String,
178-
val extractedProfile: ExtractedProfile,
179-
val profileQuery: ProfileQuery?,
180177
) : SideEffect(),
181178
BrokerActionSideEffect
182179

pir/pir-impl/src/test/kotlin/com/duckduckgo/pir/impl/common/actions/ExecuteBrokerStepActionEventHandlerTest.kt

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,6 @@ class ExecuteBrokerStepActionEventHandlerTest {
224224
val sideEffect = result.sideEffect as GetEmailForProfile
225225
assertEquals("action-1", sideEffect.actionId)
226226
assertEquals(testBrokerName, sideEffect.brokerName)
227-
assertEquals(testExtractedProfile.copy(email = ""), sideEffect.extractedProfile)
228-
assertEquals(testProfileQuery, sideEffect.profileQuery)
229227
assertNull(result.nextEvent)
230228
}
231229

@@ -689,4 +687,109 @@ class ExecuteBrokerStepActionEventHandlerTest {
689687
assertEquals("John Doe", userData.extractedProfile?.name)
690688
assertEquals("john@example.com", userData.extractedProfile?.email)
691689
}
690+
691+
@Test
692+
fun whenOptOutStepGenerateEmailActionThenRequestsEmailGeneration() = runTest {
693+
val action = BrokerAction.GenerateEmail(id = "action-gen-email")
694+
val optOutStep = OptOutStep(
695+
broker = testBroker,
696+
step = OptOutStepActions(
697+
stepType = "optout",
698+
actions = listOf(action),
699+
optOutType = "form",
700+
),
701+
profileToOptOut = testExtractedProfile,
702+
)
703+
val state = State(
704+
runType = RunType.OPTOUT,
705+
brokerStepsToExecute = listOf(optOutStep),
706+
profileQuery = testProfileQuery,
707+
currentBrokerStepIndex = 0,
708+
currentActionIndex = 0,
709+
stageStatus = PirStageStatus(
710+
currentStage = PirStage.OTHER,
711+
stageStartMs = testStageStartMs,
712+
),
713+
)
714+
val event = ExecuteBrokerStepAction(UserProfile(userProfile = testProfileQuery))
715+
716+
val result = testee.invoke(state, event)
717+
718+
assertEquals(PirStage.EMAIL_GENERATE, result.nextState.stageStatus.currentStage)
719+
assertEquals(testCurrentTimeInMillis, result.nextState.stageStatus.stageStartMs)
720+
val sideEffect = result.sideEffect as GetEmailForProfile
721+
assertEquals("action-gen-email", sideEffect.actionId)
722+
assertEquals(testBrokerName, sideEffect.brokerName)
723+
assertNull(result.nextEvent)
724+
}
725+
726+
@Test
727+
fun whenScanStepGenerateEmailActionThenRequestsEmailGeneration() = runTest {
728+
val action = BrokerAction.GenerateEmail(id = "action-gen-email")
729+
val scanStep = ScanStep(
730+
broker = testBroker,
731+
step = ScanStepActions(
732+
stepType = "scan",
733+
actions = listOf(action),
734+
scanType = "initial",
735+
),
736+
)
737+
val state = State(
738+
runType = RunType.MANUAL,
739+
brokerStepsToExecute = listOf(scanStep),
740+
profileQuery = testProfileQuery,
741+
currentBrokerStepIndex = 0,
742+
currentActionIndex = 0,
743+
stageStatus = PirStageStatus(
744+
currentStage = PirStage.OTHER,
745+
stageStartMs = testStageStartMs,
746+
),
747+
)
748+
val event = ExecuteBrokerStepAction(UserProfile(userProfile = testProfileQuery))
749+
750+
val result = testee.invoke(state, event)
751+
752+
assertEquals(PirStage.EMAIL_GENERATE, result.nextState.stageStatus.currentStage)
753+
assertEquals(testCurrentTimeInMillis, result.nextState.stageStatus.stageStartMs)
754+
val sideEffect = result.sideEffect as GetEmailForProfile
755+
assertEquals("action-gen-email", sideEffect.actionId)
756+
assertEquals(testBrokerName, sideEffect.brokerName)
757+
assertNull(result.nextEvent)
758+
}
759+
760+
@Test
761+
fun whenEmailConfirmationStepGenerateEmailActionThenRequestsEmailGeneration() = runTest {
762+
val action = BrokerAction.GenerateEmail(id = "action-gen-email")
763+
val emailConfirmationStep = EmailConfirmationStep(
764+
broker = testBroker,
765+
step = OptOutStepActions(
766+
stepType = "optout",
767+
actions = listOf(action),
768+
optOutType = "form",
769+
),
770+
emailConfirmationJob = testEmailConfirmationJob,
771+
profileToOptOut = testExtractedProfile,
772+
)
773+
val state = State(
774+
runType = RunType.EMAIL_CONFIRMATION,
775+
brokerStepsToExecute = listOf(emailConfirmationStep),
776+
profileQuery = testProfileQuery,
777+
currentBrokerStepIndex = 0,
778+
currentActionIndex = 0,
779+
stageStatus = PirStageStatus(
780+
currentStage = PirStage.OTHER,
781+
stageStartMs = testStageStartMs,
782+
),
783+
)
784+
val event = ExecuteBrokerStepAction(UserProfile(userProfile = testProfileQuery))
785+
786+
val result = testee.invoke(state, event)
787+
788+
assertEquals(PirStage.EMAIL_GENERATE, result.nextState.stageStatus.currentStage)
789+
assertEquals(testCurrentTimeInMillis, result.nextState.stageStatus.stageStartMs)
790+
val sideEffect = result.sideEffect as GetEmailForProfile
791+
assertEquals("action-gen-email", sideEffect.actionId)
792+
assertEquals(testBrokerName, sideEffect.brokerName)
793+
assertNull(result.nextEvent)
794+
}
692795
}

pir/pir-impl/src/test/kotlin/com/duckduckgo/pir/impl/common/actions/RealPirActionsRunnerStateEngineTest.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,8 +928,6 @@ class RealPirActionsRunnerStateEngineTest {
928928
val testSideEffect = SideEffect.GetEmailForProfile(
929929
actionId = "action-1",
930930
brokerName = testBrokerName,
931-
extractedProfile = mock(),
932-
profileQuery = testProfileQuery,
933931
)
934932

935933
whenever(mockEventHandler.event).thenReturn(Event.Started::class)

0 commit comments

Comments
 (0)