Skip to content

Commit

Permalink
Addressed comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
Thisara-Welmilla committed Jan 24, 2025
1 parent a37cbc1 commit af2d974
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ public interface ActionInvocationResponseClassProvider {
*
* @return The extended ResponseData class.
*/
Class<? extends ResponseData> getSuccessResponseContextClass();
Class<? extends ResponseData> getSuccessResponseDataClass();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.wso2.carbon.identity.action.execution;

import org.wso2.carbon.identity.action.execution.model.ActionType;
import org.wso2.carbon.identity.action.execution.model.DefaultResponseData;
import org.wso2.carbon.identity.action.execution.model.ResponseData;

/**
Expand All @@ -36,7 +37,7 @@ public static DefaultActionInvocationResponseClassProvider getInstance() {
@Override
public ActionType getSupportedActionType() {

return null;
throw new UnsupportedOperationException("This method should not called for default implementation.");
}

/**
Expand All @@ -45,8 +46,8 @@ public ActionType getSupportedActionType() {
* @return The default ResponseData class.
*/
@Override
public Class<? extends ResponseData> getSuccessResponseContextClass() {
public Class<? extends ResponseData> getSuccessResponseDataClass() {

return ResponseData.DefaultResponseData.class;
return DefaultResponseData.class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
import java.util.Map;

/**
* This class defines the classes for action invocation responses for different action types.
* This class defines the classes for extended implementations of action invocation responses for
* different action types.
* The ActionInvocationResponseClassFactory is the component that is responsible for providing the classes
* defined by the downstream component based on the action type.
*/
Expand Down Expand Up @@ -58,17 +59,18 @@ public static void unregisterActionInvocationResponseClassProvider(
}

/**
* Get the extended ResponseData class for invocation success response based on the action type.
* Get the extended ResponseData class for extended implementations of action invocation responses based on the
* action type.
*
* @param actionType Action type.
* @return The extended ResponseData class.
*/
public static Class<? extends ResponseData> getInvocationSuccessResponseContextClass(ActionType actionType) {
public static Class<? extends ResponseData> getInvocationSuccessResponseDataClass(ActionType actionType) {

ActionInvocationResponseClassProvider classProvider = classProviders.get(actionType);
if (classProvider != null) {
return classProvider.getSuccessResponseContextClass();
return classProvider.getSuccessResponseDataClass();
}
return DefaultActionInvocationResponseClassProvider.getInstance().getSuccessResponseContextClass();
return DefaultActionInvocationResponseClassProvider.getInstance().getSuccessResponseDataClass();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,20 +174,16 @@ protected void unsetRuleEvaluationService(RuleEvaluationService ruleEvaluationSe
)
protected void setInvocationSuccessResponseContextClass(ActionInvocationResponseClassProvider classProvider) {

if (LOG.isDebugEnabled()) {
LOG.debug("Registering ActionInvocationResponseClassProvider: " +
classProvider.getClass().getName() + " in the ActionExecutionServiceComponent.");
}
LOG.debug("Registering ActionInvocationResponseClassProvider: " + classProvider.getClass().getName() +
" in the ActionExecutionServiceComponent.");
ActionInvocationResponseClassFactory.registerActionInvocationResponseClassProvider(
classProvider);
}

protected void unsetInvocationSuccessResponseContextClass(ActionInvocationResponseClassProvider classProvider) {

if (LOG.isDebugEnabled()) {
LOG.debug("Unregistering ActionInvocationResponseClassProvider: " +
classProvider.getClass().getName() + " in the ActionExecutionServiceComponent.");
}
LOG.debug("Unregistering ActionInvocationResponseClassProvider: " + classProvider.getClass().getName() +
" in the ActionExecutionServiceComponent.");
ActionInvocationResponseClassFactory.unregisterActionInvocationResponseClassProvider(classProvider);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public Builder operations(@JsonProperty("operations") List<PerformableOperation>
return this;
}

@JsonDeserialize(using = ResponseData.ResponseDataDeserializer.class)
@JsonDeserialize(using = ResponseDataDeserializer.class)
@JsonProperty("data")
public Builder context(@JsonProperty("data") ResponseData data) {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.wso2.carbon.identity.action.execution.model;

/**
* Default ResponseData implementation, which can be used when there are no extended ResponseData class for
* the action type.
*/
public class DefaultResponseData implements ResponseData {
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,10 @@

package org.wso2.carbon.identity.action.execution.model;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.wso2.carbon.identity.action.execution.impl.ActionInvocationResponseClassFactory;

import java.io.IOException;

/**
* This interface defines the ResponseData for action invocation success response.
* The ResponseData is the class that is responsible for defining structure of the additional data coming from the
* success invocation response received from the action execution.
*/
public interface ResponseData {

/**
* Default ResponseData implementation, which can be used when there are no extended ResponseData class for
* the action type.
*/
class DefaultResponseData implements ResponseData {
}

/**
* Dynamic deserializer for the ResponseData class.
*/
class ResponseDataDeserializer extends JsonDeserializer<ResponseData> {

public static final String ACTION_TYPE_ATTR_NAME = "actionType";

@Override
public ResponseData deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {

ActionType actionType = (ActionType) ctxt.getAttribute(ACTION_TYPE_ATTR_NAME);
JsonNode node = p.getCodec().readTree(p);
ObjectMapper mapper = (ObjectMapper) p.getCodec();
return mapper.treeToValue(node,
ActionInvocationResponseClassFactory.getInvocationSuccessResponseContextClass(actionType));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.wso2.carbon.identity.action.execution.model;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.wso2.carbon.identity.action.execution.impl.ActionInvocationResponseClassFactory;

import java.io.IOException;

/**
* Dynamic deserializer for the ResponseData class.
*/
public class ResponseDataDeserializer extends JsonDeserializer<ResponseData> {

public static final String ACTION_TYPE_ATTR_NAME = "actionType";

@Override
public ResponseData deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {

ActionType actionType = (ActionType) ctxt.getAttribute(ACTION_TYPE_ATTR_NAME);
JsonNode node = p.getCodec().readTree(p);
ObjectMapper mapper = (ObjectMapper) p.getCodec();
return mapper.treeToValue(node,
ActionInvocationResponseClassFactory.getInvocationSuccessResponseDataClass(actionType));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.wso2.carbon.identity.action.execution.model.ActionInvocationSuccessResponse;
import org.wso2.carbon.identity.action.execution.model.ActionType;
import org.wso2.carbon.identity.action.execution.model.ResponseData;
import org.wso2.carbon.identity.action.execution.model.ResponseDataDeserializer;

import java.io.IOException;
import java.net.SocketTimeoutException;
Expand Down Expand Up @@ -257,9 +258,9 @@ private ActionInvocationResponse.APIResponse deserializeSuccessOrFailureResponse
if (actionStatus.equals(ActionExecutionStatus.Status.SUCCESS.name())) {
// Configure dynamic deserializer for the extended ResponseData class based on the action type.
SimpleModule module = new SimpleModule();
module.addDeserializer(ResponseData.class, new ResponseData.ResponseDataDeserializer());
module.addDeserializer(ResponseData.class, new ResponseDataDeserializer());
objectMapper.setConfig(objectMapper.getDeserializationConfig()
.withAttribute(ResponseData.ResponseDataDeserializer.ACTION_TYPE_ATTR_NAME, actionType));
.withAttribute(ResponseDataDeserializer.ACTION_TYPE_ATTR_NAME, actionType));
objectMapper.registerModule(module);
return objectMapper.readValue(jsonResponse, ActionInvocationSuccessResponse.class);
} else if (actionStatus.equals(ActionExecutionStatus.Status.INCOMPLETE.name())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ public void testActionExecuteSuccessWhenRuleConfiguredInActionIsSatisfied() thro
mock(ActionExecutionRequest.class));

ActionInvocationResponse actionInvocationResponse = createSuccessActionInvocationResponse();
when(apiClient.callAPI(any(), any(), any())).thenReturn(actionInvocationResponse);
when(apiClient.callAPI(any(), any(), any(), any())).thenReturn(actionInvocationResponse);

ActionExecutionStatus expectedStatus = new SuccessStatus.Builder().build();
when(actionExecutionResponseProcessor.getSupportedActionType()).thenReturn(actionType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import org.testng.annotations.Test;
import org.wso2.carbon.identity.action.execution.model.ActionType;
import org.wso2.carbon.identity.action.execution.model.DefaultResponseData;
import org.wso2.carbon.identity.action.execution.model.ResponseData;
import org.wso2.carbon.identity.action.execution.util.TestActionInvocationResponseClassProvider;
import org.wso2.carbon.identity.action.execution.util.UserData;
Expand All @@ -34,23 +35,23 @@ public void testRegisterInvocationSuccessResponseContextClass() {
ActionInvocationResponseClassFactory.registerActionInvocationResponseClassProvider(
new TestActionInvocationResponseClassProvider());
Class<? extends ResponseData> registeredResult = ActionInvocationResponseClassFactory
.getInvocationSuccessResponseContextClass(ActionType.AUTHENTICATION);
.getInvocationSuccessResponseDataClass(ActionType.AUTHENTICATION);
assertEquals(registeredResult, UserData.class);
}

@Test(dependsOnMethods = {"testRegisterInvocationSuccessResponseContextClass"})
public void testGetInvocationSuccessResponseContextClassWithDefault() {

Class<? extends ResponseData> extendedClass = ActionInvocationResponseClassFactory
.getInvocationSuccessResponseContextClass(ActionType.PRE_ISSUE_ACCESS_TOKEN);
assertEquals(extendedClass, ResponseData.DefaultResponseData.class);
.getInvocationSuccessResponseDataClass(ActionType.PRE_ISSUE_ACCESS_TOKEN);
assertEquals(extendedClass, DefaultResponseData.class);
}

@Test(dependsOnMethods = {"testRegisterInvocationSuccessResponseContextClass"})
public void testGetInvocationSuccessResponseContextClass() {

Class<? extends ResponseData> extendedClass = ActionInvocationResponseClassFactory
.getInvocationSuccessResponseContextClass(ActionType.AUTHENTICATION);
.getInvocationSuccessResponseDataClass(ActionType.AUTHENTICATION);
assertEquals(extendedClass, UserData.class);
}

Expand All @@ -60,7 +61,7 @@ public void testUnregisterInvocationSuccessResponseContextClass() {
ActionInvocationResponseClassFactory.unregisterActionInvocationResponseClassProvider(
new TestActionInvocationResponseClassProvider());
Class<? extends ResponseData> unregisteredResult = ActionInvocationResponseClassFactory
.getInvocationSuccessResponseContextClass(ActionType.AUTHENTICATION);
assertEquals(unregisteredResult, ResponseData.DefaultResponseData.class);
.getInvocationSuccessResponseDataClass(ActionType.AUTHENTICATION);
assertEquals(unregisteredResult, DefaultResponseData.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.wso2.carbon.identity.action.execution.impl.ActionInvocationResponseClassFactory;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationErrorResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationFailureResponse;
import org.wso2.carbon.identity.action.execution.model.ActionInvocationIncompleteResponse;
Expand Down Expand Up @@ -470,6 +471,32 @@ public void testCallAPIRetryOnTimeoutAndReceiveSuccessResponse() throws Exceptio
verify(httpClient, times(2)).execute(any(HttpPost.class));
}

@Test
public void testCallAPIRetryOnTimeoutAndReceiveSuccessResponseWithExtendedResponseData() throws Exception {

when(httpClient.execute(any(HttpPost.class))).thenThrow(new ConnectTimeoutException("Timeout"))
.thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(statusLine.getStatusCode()).thenReturn(200);

ActionInvocationResponseClassFactory.registerActionInvocationResponseClassProvider(
new TestActionInvocationResponseClassProvider());

String successResponse =
"{\"actionStatus\":\"SUCCESS\", \"data\": {\"id\":\"test-123-id\"}}";
InputStreamEntity entity = new InputStreamEntity(new ByteArrayInputStream(successResponse.getBytes(
StandardCharsets.UTF_8)));
entity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
when(httpResponse.getEntity()).thenReturn(entity);

ActionInvocationResponse response = apiClient.callAPI(ActionType.AUTHENTICATION,
"http://example.com", null, "{}");

assertNotNull(response);
assertTrue(response.isSuccess());
verify(httpClient, times(2)).execute(any(HttpPost.class));
}

@Test
public void testCallAPIRetryOnTimeoutAndReachMaxRetryAttempts() throws Exception {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public ActionType getSupportedActionType() {
}

@Override
public Class<? extends ResponseData> getSuccessResponseContextClass() {
public Class<? extends ResponseData> getSuccessResponseDataClass() {

return UserData.class;
}
Expand Down

0 comments on commit af2d974

Please sign in to comment.