-
Notifications
You must be signed in to change notification settings - Fork 7
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
PP-13030: Use CreateServiceRequest pojo instead of JsonNode #2653
Conversation
4a4de0e
to
a3ed16d
Compare
.body(Map.of("service_name", Map.of("fr", "Service name"))) | ||
.post("v1/api/services") | ||
.then() | ||
.statusCode(400) |
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.
Returning a 422 might be more correct here, but getting Jersey to do this from within a custom deserialiser is very hard to do. We return a 400 in other places so I think this is acceptable.
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 agree this is fine for an internal API.
da26a5e
to
bd365a8
Compare
Map<SupportedLanguage, String> supportedLanguageToServiceName = new HashMap<>(); | ||
jsonParser.getCodec().readValue(jsonParser, new TypeReference<Map<String, String>>() {}) | ||
.forEach((key, value) -> | ||
supportedLanguageToServiceName.put(SupportedLanguage.fromIso639AlphaTwoCode(key), value)); | ||
return Collections.unmodifiableMap(supportedLanguageToServiceName); |
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.
return jsonParser.getCodec().readValue(jsonParser, new TypeReference<Map<String, String>>() {})
.entrySet().stream()
.collect(toUnmodifiableMap(entry ->
SupportedLanguage.fromIso639AlphaTwoCode(entry.getKey()), Map.Entry::getValue));
😀
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.
Nice, fixed.
Following the general pattern in most of our Dropwizard codebases, we want to avoid JsonNode as the POST request body where possible so we can advantage of using javax Validation constraints. It is also generally agreed that using hard coded URL strings makes for easier-to-read code. Added a few more test cases in ServiceResourceCreateIT to reflect the pact tests between selfservice and adminusers: * [a valid create service request with empty object](https://github.com/alphagov/pay-selfservice/blob/b2053a1049bc28813e8e7363245ad546ef19b675/test/pact/adminusers-client/service/create-service.pact.test.js#L50) * [a valid create service request with gateway account ids](https://github.com/alphagov/pay-selfservice/blob/b2053a1049bc28813e8e7363245ad546ef19b675/test/pact/adminusers-client/service/create-service.pact.test.js#L85C31-L85C86) * [a valid create service request with service name](https://github.com/alphagov/pay-selfservice/blob/b2053a1049bc28813e8e7363245ad546ef19b675/test/pact/adminusers-client/service/create-service.pact.test.js#L124)
148f7ae
to
9ac402e
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.
Looks good! Just a few small suggestions.
@@ -227,28 +220,21 @@ public Response searchServices(JsonNode payload) { | |||
@ApiResponse(responseCode = "201", description = "Created", | |||
content = @Content(schema = @Schema(implementation = Service.class))), | |||
@ApiResponse(responseCode = "409", description = "Gateway account IDs provided has already been assigned to another service"), | |||
@ApiResponse(responseCode = "400", description = "Service names must be one of 'en' (English) or 'cy' (Welsh)"), |
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 suppose really it’s the service name key that needs to be ‘en’ or ‘cy’.
var nullableCreateServiceRequest = Optional.ofNullable(createServiceRequest); | ||
Service service = serviceServicesFactory.serviceCreator().doCreate( | ||
nullableCreateServiceRequest.map(CreateServiceRequest::gatewayAccountIds).orElse(List.of()), | ||
nullableCreateServiceRequest.map(CreateServiceRequest::serviceName).orElse(Map.of())); |
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.
Presumably the entire createServiceRequest
is not going to be null but the list of gateway account IDs or map of languages to service names might be, so perhaps this would be better:
Service service = serviceServicesFactory.serviceCreator().doCreate(
Optional.ofNullable(createServiceRequest.gatewayAccountIds()).orElse(List.of()),
Optional.ofNullable(createServiceRequest.serviceName()).orElse(Map.of()));
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.
createServiceRequest
is null actually.... otherwise your suggestion would be the better option!
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 this would be easier:
Service service = serviceServicesFactory.serviceCreator().doCreate(
createServiceRequest == null ? List.of() : createServiceRequest.gatewayAccountIds(),
createServiceRequest == null ? Map.of() : createServiceRequest.serviceName());
But am not very fussed here.
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.
⬆️ Actually my suggestion wouldn't work 🙈
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.
Maybe this would work:
Service service = serviceServicesFactory.serviceCreator().doCreate(
Optional.ofNullable(createServiceRequest).map(CreateServiceRequest::gatewayAccountIds).orElse(List.of()),
Optional.ofNullable(createServiceRequest).map(CreateServiceRequest::serviceName).orElse(Map.of()));
It would get rid of the need to have the nullableCreateServiceRequest
variable at the expense of an extra Optional.ofNullable(…)
call (the cost of which is probably absolutely trivial).
This could be slightly rearranged to surface the actual data and types involved a bit more:
List<String> gatewayAccountIds = Optional.ofNullable(createServiceRequest).map(CreateServiceRequest::gatewayAccountIds).orElse(List.of());
Map<SupportedLanguage, String> serviceNames = Optional.ofNullable(createServiceRequest).map(CreateServiceRequest::serviceName).orElse(Map.of());
Service service = serviceServicesFactory.serviceCreator().doCreate(gatewayAccountIds, serviceNames);
private void assertStandardFields(ValidatableResponse validatableResponse) { | ||
validatableResponse | ||
.body("current_psp_test_account_stage", is("NOT_STARTED")) | ||
.body("current_go_live_stage", is("NOT_STARTED")) | ||
.body("default_billing_address_country", is("GB")) | ||
.body("agent_initiated_moto_enabled", is(false)) | ||
.body("takes_payments_over_phone", is(false)) | ||
.body("experimental_features_enabled", is(false)) | ||
.body("internal", is(false)) | ||
.body("archived", is(false)) | ||
.body("redirect_to_service_immediately_on_terminal_state", is(false)) | ||
.body("collect_billing_address", is(true)); | ||
} |
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.
Maybe this method should go after all the tests that rely on it.
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.
How do you mean? Like for other integration tests?
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.
Or do you mean moving this method to the end of the class...
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.
Yeah, at the end of the class or at least after all the tests that rely on it.
.body(Map.of("service_name", Map.of("fr", "Service name"))) | ||
.post("v1/api/services") | ||
.then() | ||
.statusCode(400) |
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 agree this is fine for an internal API.
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.
Looks fine. One take it or leave it suggestion.
var nullableCreateServiceRequest = Optional.ofNullable(createServiceRequest); | ||
Service service = serviceServicesFactory.serviceCreator().doCreate( | ||
nullableCreateServiceRequest.map(CreateServiceRequest::gatewayAccountIds).orElse(List.of()), | ||
nullableCreateServiceRequest.map(CreateServiceRequest::serviceName).orElse(Map.of())); |
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.
Maybe this would work:
Service service = serviceServicesFactory.serviceCreator().doCreate(
Optional.ofNullable(createServiceRequest).map(CreateServiceRequest::gatewayAccountIds).orElse(List.of()),
Optional.ofNullable(createServiceRequest).map(CreateServiceRequest::serviceName).orElse(Map.of()));
It would get rid of the need to have the nullableCreateServiceRequest
variable at the expense of an extra Optional.ofNullable(…)
call (the cost of which is probably absolutely trivial).
This could be slightly rearranged to surface the actual data and types involved a bit more:
List<String> gatewayAccountIds = Optional.ofNullable(createServiceRequest).map(CreateServiceRequest::gatewayAccountIds).orElse(List.of());
Map<SupportedLanguage, String> serviceNames = Optional.ofNullable(createServiceRequest).map(CreateServiceRequest::serviceName).orElse(Map.of());
Service service = serviceServicesFactory.serviceCreator().doCreate(gatewayAccountIds, serviceNames);
I'll take it, but will put it in the next PR. Have another PR incoming for this repo and would like to get this one merged into master. |
Following the general pattern in most of our Dropwizard codebases, we want to avoid JsonNode as the POST request body where possible so we can advantage of using javax Validation constraints.
It is also generally agreed that using hard coded URL strings makes for easier-to-read code.
Added a few more test cases in ServiceResourceCreateIT to reflect the pact tests between selfservice and adminusers: