-
Notifications
You must be signed in to change notification settings - Fork 42
Ignore irrelevant parameters during start of an operation #1680
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
Ignore irrelevant parameters during start of an operation #1680
Conversation
| private Operation addParameterValues(Operation operation, Set<ParameterMetadata> predefinedParameters) { | ||
| Map<String, Object> parameters = new HashMap<>(operation.getParameters()); | ||
| parameters.putAll(ParameterConversion.toFlowableVariables(predefinedParameters, parameters)); | ||
| filterUnnecessaryParameters(predefinedParameters, parameters); |
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.
Can we perform this filtering in startOperation method before invocation of addServiceParameters and addParameterValues. The idea is to reduce as much as possible to add all user input parameters to the flowable context by mistake.
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 have thought about this to filter as early as possible but after debugging - I have found out that these parameters are added during the creation of the operation and the methods OperationsApiServiceImpl::addParameterValues and OperationsApiServiceImpl::addServiceParameters are actually the earliest places that the parameters of the operation are retrieved - Map<String, Object> parameters = new HashMap<>(operation.getParameters()); - thus I think the most appropriate place out of the two is the OperationsApiServiceImpl::addParameterValues method. The method just before these two, which is supposed to get the predefined parameters does not know anything about the specific parameters the user could provide in the request's body - its only purpose is to get the parameters that correspond to the type of operation - DEPLOY, BLUE_GREEN_DEPLOY, CTS_DEPLOY and etc.
| } | ||
|
|
||
| private Set<String> getNamesOfServiceParameters() { | ||
| return new HashSet<>( |
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.
You can use the feature since java 11 and use Set.of method to create immutable Set without nesting of different collections.
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.
thanks for the suggestion, fixed!
| .anyMatch(message -> message.getType() == MessageType.ERROR); | ||
| } | ||
|
|
||
| private Set<String> getNamesOfServiceParameters() { |
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.
Can you make this method constant located in Constants class of multiapps-controller-web package and reuse this constant also in addServiceParameters method. Making it as constant and reuse it in both methods will avoid errors in future where one variable name is added or removed in one place and forgetting to the same in other method.
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.
fixed - I have used an if else because the switch does not allow method calls - thus Variables.USER.getName() can not be used inside the different cases.
| .startProcess(Mockito.any(), Mockito.anyMap()); | ||
| } | ||
|
|
||
| void testStartOperationWithInvalidParametersForTheProcess() { |
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.
Add test annotation
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.
Fixed!
| .size()); | ||
| } | ||
|
|
||
| void testStartOperationWithValidParametersForTheProcess() { |
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.
Add test annotation
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.
Fixed!
| assertNotEquals(parameters.size(), operation.getParameters() | ||
| .size()); | ||
| assertEquals(2, operation.getParameters() | ||
| .size()); |
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.
When perform unit testing of methods which doesn't return any result, it's not relevant to use assertEquals because there is no returned value. That's why think about how to improve Mockito.verify assertion.
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.
fixed - you are completely right but I was asserting the values returned by the calls of the size() method, but it turned out that the current logic for the testing was not correct at all and I was not asserting what I needed to. Almost done with the new tests.
| operationsApiService.startOperation(mockHttpServletRequest(EXAMPLE_USER), SPACE_GUID, operation); | ||
| Mockito.verify(flowableFacade) | ||
| .startProcess(Mockito.any(), Mockito.anyMap()); | ||
| assertEquals(parameters.size(), operation.getParameters() |
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.
same comment as previous test assertion
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.
Same as the previous comment
| .withParameters(parameters); | ||
| } | ||
|
|
||
| private void filterUnnecessaryParameters(Set<ParameterMetadata> predefinedParameters, Map<String, Object> parameters) { |
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.
try not to use in-parameters. instead of modifying parameters, return a new Map.
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.
fixed, thanks for the suggestion!
| Set<String> namesOfServiceParameters = getNamesOfServiceParameters(); | ||
| Iterator<Map.Entry<String, Object>> it = parameters.entrySet() | ||
| .iterator(); | ||
| while (it.hasNext()) { | ||
| String key = it.next() | ||
| .getKey(); | ||
| if (!(namesOfPredefinedParameters.contains(key) || namesOfServiceParameters.contains(key))) { | ||
| it.remove(); | ||
| } | ||
| } |
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.
you can simplify this method and implicitly use the iterator.
try to use for-each with the entrySet directly and do the filtration in the for-each.
if you use a new map as I mentioned in the other comment this can be simplified
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.
thank you, fixed!
| operationsApiService.startOperation(mockHttpServletRequest(EXAMPLE_USER), SPACE_GUID, operation); | ||
| Mockito.verify(flowableFacade) | ||
| .startProcess(Mockito.any(), Mockito.anyMap()); | ||
| assertNotEquals(parameters.size(), operation.getParameters() |
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.
do you need this assertion as the other verifies the exact size?
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.
right, fixed!
| } | ||
|
|
||
| public static final Set<String> NAMES_OF_SERVICE_PARAMETERS = Set.of( | ||
| org.cloudfoundry.multiapps.controller.persistence.Constants.VARIABLE_NAME_SERVICE_ID, Variables.USER.getName(), |
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.
why is the full package name used?
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.
Because I did not want to have a static import. Should I do it this way? import static org.cloudfoundry.multiapps.controller.persistence.Constants.VARIABLE_NAME_SERVICE_ID;
| String namespace = operation.getNamespace(); | ||
| if (namespace != null) { | ||
| parameters.put(Variables.MTA_NAMESPACE.getName(), namespace); | ||
| for (String key : org.cloudfoundry.multiapps.controller.web.Constants.NAMES_OF_SERVICE_PARAMETERS) { |
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 do not understand why this for loop is needed.
What is the point of iterating and checking for values that have already been added?
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.
The code before that was without any loops, but since Kris has left me a comment about adding this specific set to the Constants class and trying to use it also in the method OperationsApiServiceImpl::addServiceParameters - I was able to come up with only this approach in order to make the method tied to the constant set and use it
| Set<String> allowedParameters = new HashSet<>(); | ||
| Map<String, Object> filteredParameters = new LinkedHashMap<>(); | ||
|
|
||
| for (ParameterMetadata parameterMetadata : predefinedParameters) { |
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.
instead of for loop, can you create allowedParameters from the predefinedParameters
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.
fixed, thank you
|
|
||
| private Map<String, Object> filterUnnecessaryParameters(Set<ParameterMetadata> predefinedParameters, Map<String, Object> parameters) { | ||
| Set<String> allowedParameters = new HashSet<>(); | ||
| Map<String, Object> filteredParameters = new LinkedHashMap<>(); |
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.
why LinkedHashMap?
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.
Because it was easier for while debugging in terms of the ordering and I thought it would not be a problem to have it like that - but I will revert it back to the regular hashMap. Thanks, fixed!
| for (Map.Entry<String, Object> entry : parameters.entrySet()) { | ||
| if (allowedParameters.contains(entry.getKey())) { | ||
| filteredParameters.put(entry.getKey(), entry.getValue()); | ||
| } | ||
| } | ||
| return filteredParameters; |
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.
you can use the java stream API to perform the filtering
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.
fixed
| for (ParameterMetadata parameterMetadata : predefinedParameters) { | ||
| allowedParameters.add(parameterMetadata.getId()); | ||
| } | ||
| allowedParameters.addAll(org.cloudfoundry.multiapps.controller.web.Constants.NAMES_OF_SERVICE_PARAMETERS); |
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.
why full package name needed?
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.
fixed!
| } | ||
|
|
||
| private Operation addParameterValues(Operation operation, Set<ParameterMetadata> predefinedParameters) { | ||
| Map<String, Object> parameters = new HashMap<>(operation.getParameters()); |
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.
can you make the whole filtration 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.
fixed
| if (key.equals(Constants.VARIABLE_NAME_SERVICE_ID)) { | ||
| parameters.put(key, processDefinitionKey); | ||
| } else if (key.equals(Variables.USER.getName())) { | ||
| parameters.put(key, user); | ||
| } else if (key.equals(Variables.USER_GUID.getName())) { | ||
| parameters.put(key, userGuid); | ||
| } else if (key.equals(Variables.SPACE_NAME.getName())) { | ||
| parameters.put(key, space.getName()); | ||
| } else if (key.equals(Variables.SPACE_GUID.getName())) { | ||
| parameters.put(key, spaceGuid); | ||
| } else if (key.equals(Variables.ORGANIZATION_NAME.getName())) { | ||
| parameters.put(key, organization.getName()); | ||
| } else if (key.equals(Variables.ORGANIZATION_GUID.getName())) { | ||
| parameters.put(key, organization.getGuid() | ||
| .toString()); | ||
| } else if (key.equals(Variables.TIMESTAMP.getName())) { | ||
| parameters.put(key, DateTimeFormatter.ofPattern("yyyyMMddHHmmss") | ||
| .format(ZonedDateTime.now())); | ||
| } else if (key.equals(Variables.MTA_NAMESPACE.getName())) { | ||
| String namespace = operation.getNamespace(); | ||
| if (namespace != null) { | ||
| parameters.put(key, namespace); | ||
| } |
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.
Too much ifs, let's return it in previous way.
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.
fixed!
| Set<String> allowedParameters = new HashSet<>(); | ||
| Map<String, Object> filteredParameters = new LinkedHashMap<>(); | ||
|
|
||
| for (ParameterMetadata parameterMetadata : predefinedParameters) { | ||
| allowedParameters.add(parameterMetadata.getId()); | ||
| } | ||
| allowedParameters.addAll(org.cloudfoundry.multiapps.controller.web.Constants.NAMES_OF_SERVICE_PARAMETERS); |
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.
Extract collecting of allowedParameters in a new method.
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.
fixed, thanks!
| private Map<String, Object> filterUnnecessaryParameters(Set<ParameterMetadata> predefinedParameters, Map<String, Object> parameters) { | ||
| Map<String, Object> filteredParameters = new HashMap<>(); | ||
| Set<String> allowedParameters = getAllowedParameters(predefinedParameters); | ||
|
|
||
| parameters.entrySet() | ||
| .stream() | ||
| .filter(entry -> allowedParameters.contains(entry.getKey())) | ||
| .forEach(entry -> filteredParameters.put(entry.getKey(), entry.getValue())); | ||
| return filteredParameters; | ||
| } |
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.
**private Map<String, Object> filterUnnecessaryParameters(
Set predefinedParameters,
Map<String, Object> parameters) {
Set<String> allowedParameters = getAllowedParameters(predefinedParameters);
return parameters.entrySet()
.stream()
.filter(entry -> allowedParameters.contains(entry.getKey()))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a, b) -> b, // keep the latest value if a duplicate key appears
HashMap::new // create a mutable map
));
}**
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.
thank you, fixed!
IvanBorislavovDimitrov
left a comment
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.
do not forget to squash commits
LMCROSSITXSADEPLOY-3120 Fixes regarding service parameters and the unit tests LMCROSSITXSADEPLOY-3120 Some overall fixes regarding formatting LMCROSSITXSADEPLOY-3120 Fix for the stream LMCROSSITXSADEPLOY-3120
a0601b5 to
36a2936
Compare
31eba39
into
cloudfoundry:master
Jira Item: LMCROSSITXSADEPLOY-3120