-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PRSD-689: Creates UpdateJourney base class #239
base: main
Are you sure you want to change the base?
Conversation
f082376
to
960d45c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My immediate thought is that we're passing around the journeyDataKey
far too much - it's ended up everywhere. As it stands it's going to make the whole journey logic so much harder to understand. Especially because we're often passing both the data and the key around together - which shouldn't be necessary. The purpose of the key is to be able to get the data, after all!
I think for this all to work, we should basically only retrieve the journeyData from the session once per request and then just pass it around.
I've not looked through this whole review thoroughly, but I think that will need to be addressed before we can really look at everything else. If the above ends up requiring anything more than a minimal refactor you should check in with @Travis-Softwire before starting on it, and it could probably get its own PR.
Edit: See other comment!
I've had another thought overnight about how we could make this all work without having to do a big refactor. Since the |
960d45c
to
90bd1dc
Compare
Implemented this and it looks like it's working - thanks! The PR's ready for another review |
90bd1dc
to
1a16a66
Compare
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/Journey.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/LandlordRegistrationJourney.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/JourneyWithTaskList.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/services/JourneyDataService.kt
Outdated
Show resolved
Hide resolved
…tInitialised into JourneyWithTaskList
1a16a66
to
9d5e593
Compare
9d5e593
to
89b40db
Compare
89b40db
to
afa9575
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of good stuff here. I've left a couple of thoughts on stuff I would change though.
I've only reviewed the main code, not the tests yet!
src/main/kotlin/uk/gov/communities/prsdb/webapp/services/JourneyDataService.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/JourneyWithTaskList.kt
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/JourneyWithTaskList.kt
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/Journey.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/Journey.kt
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/Journey.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/LaUserRegistrationJourney.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/UpdateJourney.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/uk/gov/communities/prsdb/webapp/forms/journeys/UpdateJourney.kt
Outdated
Show resolved
Hide resolved
abstract val updateStepId: StepId | ||
|
||
protected val originalDataKey = "ORIGINAL_$journeyType.name" | ||
|
||
override fun getUnreachableStepRedirect(journeyData: JourneyData) = | ||
if (journeyData[originalDataKey] == null) { | ||
updateStepId.urlPathSegment | ||
} else { | ||
last().step.id.urlPathSegment | ||
} | ||
|
||
override fun iterator(): Iterator<StepDetails<T>> { | ||
val journeyData = journeyDataService.getJourneyDataFromSession() | ||
|
||
val originalData = JourneyDataHelper.getPageData(journeyData, originalDataKey) | ||
|
||
// For any fields where the data is updated, replace the original value with the new value | ||
val updatedData = | ||
journeyData.keys | ||
.union(originalData?.keys ?: setOf()) | ||
.map { key -> | ||
key to if (journeyData.containsKey(key)) journeyData[key] else originalData?.get(key) | ||
}.associate { it } | ||
|
||
return ReachableStepDetailsIterator(updatedData, steps, initialStepId, validator) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a problem here with what this abstract class is responsible for and what it delegates to its children. This says:
- If
originalDataKey
has not been set, redirect to the step with theUpdateStepId
- Whenever we traverse the journey, combine the
originalDataKey
data with the main journey data.
This works at the moment because the only subclass does get the originalDataKey
set exactly when you redirect to the update step. I think that, conceptually, this abstract class needs to be aware of the data being set and so it can complete the loop of:
- If
originalDataKey
has not been set, redirect to where... - We set data for the
originalDataKey
so that... - Whenever we traverse the journey, we can combine the
originalDataKey
data with the main journey data.
What I think that looks like is:
- moving
initialiseJourneyDataIfNotInitialised
to the abstractUpdateJourney
- making what is currently
createOriginalLandlordJourneyData
an override of an abstract method onUpdateJourney
- (potentially) creating a function here
populateModelAndGetTemplateForUpdateStep
, which callsinitialise
and thenpopulateModel...
with theupdateStepId
8b26e38
to
6814679
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of comments on the tests!
fun getJourneyDataFromSession(): JourneyData = | ||
try { | ||
objectToStringKeyedMap(session.getAttribute(journeyDataKey)) ?: mapOf() | ||
} catch (exception: UninitializedPropertyAccessException) { | ||
throw PrsdbWebException("journeyDataKey has not been set") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed - prefer:
if(!this::journeyDataKey.isInitialized) {
throw PrsdbWebException("journeyDataKey has not been set")
}
objectToStringKeyedMap(session.getAttribute(journeyDataKey)) ?: mapOf()
@@ -176,6 +176,7 @@ class JourneyDataServiceTests { | |||
val journeyType = JourneyType.LANDLORD_REGISTRATION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to write some new tests covering the new, basic functionality of this service, something like:
- Once a key is set is is used
- If a different key is used, it throws
- If you try to get/set/clear without first setting the key, it throws
argThat { households -> households == 0 }, | ||
argThat { tenants -> tenants == 0 }, | ||
any(), | ||
) | ||
} | ||
|
||
@Test | ||
fun `when a custom property type is chosen and then replaced, the custom type is not saved to the database`() { | ||
// Arrange | ||
val journeyData = | ||
JourneyDataBuilder | ||
.propertyDefault(addressDataService, localAuthorityService) | ||
.withPropertyType(PropertyType.OTHER, "Bungalow") | ||
.withPropertyType(PropertyType.FLAT) | ||
|
||
whenever(mockJourneyDataService.getJourneyDataFromSession()).thenReturn(journeyData.build()) | ||
|
||
// Act | ||
completeStep(RegisterPropertyStepId.Declaration) | ||
|
||
// Assert | ||
verify(mockPropertyRegistrationService).registerPropertyAndReturnPropertyRegistrationNumber( | ||
any(), | ||
argThat { type -> type == PropertyType.FLAT }, | ||
any(), | ||
any(), | ||
any(), | ||
any(), | ||
any(), | ||
any(), | ||
) | ||
} | ||
|
||
@Test | ||
fun `when a licence is added and then the licence type is changed, only the correct licence number is saved`() { | ||
// Arrange | ||
val journeyData = | ||
JourneyDataBuilder | ||
.propertyDefault(addressDataService, localAuthorityService) | ||
.withLicensingType(LicensingType.SELECTIVE_LICENCE, LicensingType.SELECTIVE_LICENCE.toString()) | ||
.withLicensingType(LicensingType.HMO_MANDATORY_LICENCE, LicensingType.HMO_MANDATORY_LICENCE.toString()) | ||
|
||
whenever(mockJourneyDataService.getJourneyDataFromSession()).thenReturn(journeyData.build()) | ||
|
||
// Act | ||
completeStep(RegisterPropertyStepId.Declaration) | ||
|
||
// Assert | ||
verify(mockPropertyRegistrationService).registerPropertyAndReturnPropertyRegistrationNumber( | ||
any(), | ||
any(), | ||
argThat { type -> type == LicensingType.HMO_MANDATORY_LICENCE }, | ||
argThat { licenceNumber -> licenceNumber == LicensingType.HMO_MANDATORY_LICENCE.toString() }, | ||
any(), | ||
any(), | ||
any(), | ||
any(), | ||
) | ||
} | ||
|
||
@Test | ||
fun `when a licence is added and then licensing is removed, no licence number is saved`() { | ||
// Arrange | ||
val journeyData = | ||
JourneyDataBuilder | ||
.propertyDefault(addressDataService, localAuthorityService) | ||
.withLicensingType(LicensingType.SELECTIVE_LICENCE, LicensingType.SELECTIVE_LICENCE.toString()) | ||
.withLicensingType(LicensingType.NO_LICENSING) | ||
|
||
whenever(mockJourneyDataService.getJourneyDataFromSession()).thenReturn(journeyData.build()) | ||
|
||
// Act | ||
completeStep(RegisterPropertyStepId.Declaration) | ||
|
||
// Assert | ||
verify(mockPropertyRegistrationService).registerPropertyAndReturnPropertyRegistrationNumber( | ||
any(), | ||
any(), | ||
argThat { type -> type == LicensingType.NO_LICENSING }, | ||
argThat { licenceNumber -> licenceNumber.isNullOrBlank() }, | ||
any(), | ||
any(), | ||
any(), | ||
any(), | ||
) | ||
} | ||
|
||
private fun completeStep( | ||
stepId: RegisterPropertyStepId, | ||
pageData: PageData = mapOf(), | ||
) { | ||
testJourney.updateJourneyDataAndGetViewNameOrRedirect( | ||
stepId = stepId, | ||
pageData = pageData, | ||
subPageNumber = null, | ||
principal = mock(), | ||
model = mock(), | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't delete all the tests I wrote for PRSD-804!
@Nested | ||
inner class JourneyDataManipulationTests { | ||
@Test | ||
fun `when there is no journey data in the session or the database, journey data is not loaded`() { | ||
val principalName = "principalName" | ||
val testJourney = | ||
PropertyRegistrationJourney( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appreciate you've moved the logic for this into Journey
so you've moved the tests too, which makes sense. Can you update these tests to use the TestJourney from above instead of the PropertyRegistrationJourney?
Journey
responsible for determining its path segment (rather thanJourneyType
)journeyDataKey
to indexJourneyData
UpdateJourney
base classPropertyRegistrationJourney.initialiseJourneyDataIfNotInitialised
intoJourneyWithTaskList