Skip to content
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

[DRAFT]PP-13333 parity checker checks exemption data matches #5669

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

hjvoid
Copy link
Contributor

@hjvoid hjvoid commented Dec 13, 2024

WHAT YOU DID

When the historical events emitter is emitting events for a change in connector, if the charge has an exemption_3ds_requested or exemption_3ds value then when the historical events emitter emits the events for the charge it should include a GATEWAY_3DS_EXEMPTION_RESULT_OBTAINED with specific event details dependant upon the combination of exemption 3ds data as per below.

Connector exemption3dsRequested Connector exemption3ds Ledger exemption
OPTIMISED HONOURED "exemption": {"requested": true,"outcome": {"result": "honoured"}}
OPTIMISED REJECTED "exemption": {"requested": true,"outcome": {"result": "rejected"}}
OPTIMISED OUT_OF_SCOPE "exemption": {"requested": true,"outcome": {"result": "out of scope"}}
CORPORATE HONOURED "exemption": {"requested": true,"type": "corporate","outcome": {"result": "honoured"}}
CORPORATE REJECTED "exemption": {"requested": true,"type": "corporate","outcome": {"result": "rejected"}}
CORPORATE OUT_OF_SCOPE "exemption": {"requested": true,"type": "corporate","outcome": {"result": "out of scope"}}
null HONOURED "exemption": {"requested": true,"outcome": {"result": "honoured"}}
null REJECTED "exemption": {"requested": true,"outcome": {"result": "rejected"}}
null EXEMPTION_NOT_REQUESTED "exemption": {"requested": false}
null EXEMPTION_OUT_OF_SCOPE "exemption": {"requested": true,"outcome": {"result": "out of scope"}}
null null No exemption object

How to test

TBC

Code review checklist

TBD

Logging

TBC

@hjvoid hjvoid force-pushed the PP-13333_parity_checker_checks_exemption_data branch 2 times, most recently from f1b61f9 to 06efe47 Compare December 16, 2024 09:59
@hjvoid hjvoid force-pushed the PP-13333_parity_checker_checks_exemption_data branch from 06efe47 to 8bd30c9 Compare December 16, 2024 12:51
Copy link
Contributor

@alexbishop1 alexbishop1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Mostly stylistic changes requested. A couple of small logic changes and some more tests also.

Comment on lines 131 to 142
var chargeEvent = chargeEventEntities
.stream()
.filter(event -> event.getStatus() == AUTHORISATION_SUCCESS || event.getStatus() == AUTHORISATION_REJECTED)
.findFirst();

chargeEvent.ifPresent(event -> {
var threeDsInfoEvent = Gateway3dsExemptionResultObtained.from(charge, event.getUpdated().toInstant());
boolean hasBeenEmittedBefore = emittedEventDao.hasBeenEmittedBefore(threeDsInfoEvent);
if (forceEmission || !hasBeenEmittedBefore) {
eventService.emitAndRecordEvent(threeDsInfoEvent, getDoNotRetryEmitUntilDate());
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can all be done as one:

chargeEventEntities
    .stream()
    .filter(event -> event.getStatus() == AUTHORISATION_SUCCESS || event.getStatus() == AUTHORISATION_REJECTED)
    .findFirst()
    .map(event -> Gateway3dsExemptionResultObtained.from(charge, event.getUpdated().toInstant()))
    .filter(threeDsInfoEvent -> forceEmission || !emittedEventDao.hasBeenEmittedBefore(threeDsInfoEvent))
    .ifPresent(threeDsInfoEvent -> eventService.emitAndRecordEvent(threeDsInfoEvent, getDoNotRetryEmitUntilDate()));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think threeDsInfoEvent has been copied and pasted from elsewhere. gateway3dsExemptionResultObtainedEvent would be better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some logging (inside the ifPresent(…) to say we’re outputting the event (we have similar for other events)?

Comment on lines 148 to 159
var chargeEvent = chargeEventEntities
.stream()
.filter(event -> TERMINAL_AUTHENTICATION_STATES.contains(event.getStatus()))
.findFirst();

chargeEvent.ifPresent(event -> {
var requested3dsExemptionEvent = Requested3dsExemption.from(charge, event.getUpdated().toInstant());
boolean hasBeenEmittedBefore = emittedEventDao.hasBeenEmittedBefore(requested3dsExemptionEvent);
if (forceEmission || !hasBeenEmittedBefore) {
eventService.emitAndRecordEvent(requested3dsExemptionEvent, getDoNotRetryEmitUntilDate());
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly:

chargeEventEntities
    .stream()
    .filter(event -> TERMINAL_AUTHENTICATION_STATES.contains(event.getStatus()))
    .findFirst()
    .map(event -> Requested3dsExemption.from(charge, event.getUpdated().toInstant()))
    .filter(requested3dsExemptionEvent -> forceEmission || !emittedEventDao.hasBeenEmittedBefore(requested3dsExemptionEvent))
    .ifPresent(requested3dsExemptionEvent -> eventService.emitAndRecordEvent(requested3dsExemptionEvent, getDoNotRetryEmitUntilDate()));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add some logging here too.

Comment on lines 377 to 385
if (chargeEntity.getExemption3dsRequested() == null) {
return CONNECTOR_HAS_EXEMPTION_3DS_REQUESTED_NULL;
} else {
if (chargeEntity.getExemption3dsRequested() == Exemption3dsType.OPTIMISED) {
return CONNECTOR_HAS_EXEMPTION_3DS_REQUESTED_OPTIMISED;
} else {
return CONNECTOR_HAS_EXEMPTION_3DS_REQUESTED_CORPORATE;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can all be done in a single elegant switch expression that explicitly enumerates all possible values:

return switch (chargeEntity.getExemption3dsRequested()) {
    case null -> CONNECTOR_HAS_EXEMPTION_3DS_REQUESTED_NULL;
    case OPTIMISED -> CONNECTOR_HAS_EXEMPTION_3DS_REQUESTED_OPTIMISED;
    case CORPORATE -> CONNECTOR_HAS_EXEMPTION_3DS_REQUESTED_CORPORATE;
};

Comment on lines 389 to 398
if (chargeEntity.getExemption3ds() == null) {
return CONNECTOR_HAS_EXEMPTION_NULL;
} else {
return switch (chargeEntity.getExemption3ds()) {
case EXEMPTION_HONOURED -> CONNECTOR_HAS_EXEMPTION_HONOURED;
case EXEMPTION_REJECTED -> CONNECTOR_HAS_EXEMPTION_REJECTED;
case EXEMPTION_NOT_REQUESTED -> CONNECTOR_HAS_EXEMPTION_NOT_REQUESTED;
case EXEMPTION_OUT_OF_SCOPE -> CONNECTOR_HAS_EXEMPTION_OUT_OF_SCOPE;
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we’re living in the future, switch expressions can handle null now:

return switch (chargeEntity.getExemption3ds()) {
    case null -> CONNECTOR_HAS_EXEMPTION_NULL;
    case EXEMPTION_HONOURED -> CONNECTOR_HAS_EXEMPTION_HONOURED;
    case EXEMPTION_REJECTED -> CONNECTOR_HAS_EXEMPTION_REJECTED;
    case EXEMPTION_NOT_REQUESTED -> CONNECTOR_HAS_EXEMPTION_NOT_REQUESTED;
    case EXEMPTION_OUT_OF_SCOPE -> CONNECTOR_HAS_EXEMPTION_OUT_OF_SCOPE;
};

import static uk.gov.pay.connector.tasks.service.LedgerAuthorisationSummaryState.LEDGER_HAS_NO_AUTHORISATION_SUMMARY;
import static uk.gov.pay.connector.tasks.service.LedgerAuthorisationSummaryState.LEDGER_HAS_AUTHORISATION_SUMMARY_WITH_THREE_D_S_REQUIRED_FALSE;
import static uk.gov.pay.connector.tasks.service.LedgerAuthorisationSummaryState.LEDGER_HAS_AUTHORISATION_SUMMARY_WITH_THREE_D_S_REQUIRED_TRUE;
import static uk.gov.pay.connector.tasks.service.LedgerAuthorisationSummaryState.LEDGER_HAS_SOMETHING_COMPLETELY_DIFFERENT;
import static uk.gov.pay.connector.tasks.service.LedgerExemptionState.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don’t like wildcard import statements

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops!

historicalEventEmitterService.emitHistoricEventsById(1L, OptionalLong.empty(), 1L);

ArgumentCaptor<Gateway3dsExemptionResultObtained> argument = ArgumentCaptor.forClass(Gateway3dsExemptionResultObtained.class);
verify(eventService, times(1)).emitAndRecordEvent(argument.capture(), isNotNull());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for times(1): it’s the default

Comment on lines 817 to 820
Arguments.of(EXEMPTION_NOT_REQUESTED, AUTHORISATION_SUCCESS),
Arguments.of(EXEMPTION_HONOURED, AUTHORISATION_SUCCESS),
Arguments.of(EXEMPTION_REJECTED, AUTHORISATION_SUCCESS),
Arguments.of(EXEMPTION_OUT_OF_SCOPE, AUTHORISATION_SUCCESS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably we want AUTHORISATION_REJECTED too?

}

@Test
void shouldCheckEventTypeIsNot3dsWhen3dsDetailsSetToNull(){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what this is supposed to be testing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified with clearer name.

Arguments.of(CORPORATE, "CORPORATE", EXEMPTION_HONOURED, AUTHORISATION_REJECTED),
Arguments.of(CORPORATE, "CORPORATE", EXEMPTION_REJECTED, AUTHORISATION_REJECTED),
Arguments.of(CORPORATE, "CORPORATE", EXEMPTION_OUT_OF_SCOPE, AUTHORISATION_REJECTED)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are other terminal authorisation states as well but it would be silly to test them all

Arguments.of(OPTIMISED, EXEMPTION_HONOURED, "honoured", true),
Arguments.of(OPTIMISED, EXEMPTION_REJECTED, "rejected", true),
Arguments.of(OPTIMISED, EXEMPTION_OUT_OF_SCOPE, "out of scope", true)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want (possibly as part of another test) ones with corporate exemptions too.

And probably at least some where it doesn’t match (focus on the most likely ones, like connector having an exemption result but ledger not).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants