diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/pom.xml b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/pom.xml
new file mode 100644
index 000000000..d01874f66
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/pom.xml
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+ org.wso2.carbon.identity.organization.management
+ identity-organization-management
+ 1.4.58-SNAPSHOT
+ ../../pom.xml
+
+
+ 4.0.0
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service
+ WSO2 - Organization Resource Hierarchy Traverse Service
+ bundle
+
+
+
+ org.wso2.carbon
+ org.wso2.carbon.utils
+
+
+ commons-collections.wso2
+ commons-collections
+
+
+ org.ops4j.pax.logging
+ pax-logging-api
+
+
+ org.wso2.carbon.identity.framework
+ org.wso2.carbon.identity.application.mgt
+
+
+ org.wso2.carbon.identity.organization.management.core
+ org.wso2.carbon.identity.organization.management.service
+
+
+
+
+ org.testng
+ testng
+ test
+
+
+ org.jacoco
+ org.jacoco.agent
+ runtime
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-inline
+ test
+
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+ ${project.artifactId}
+ ${project.artifactId}
+
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.constant,
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.internal,
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.util
+
+
+ org.apache.commons.lang; version="${org.apache.commons.lang.imp.pkg.version.range}",
+ org.apache.commons.logging; version="${org.apache.commons.logging.imp.pkg.version.range}",
+ org.apache.commons.collections; version="${org.apache.commons.collections.imp.pkg.version.range}",
+
+ org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}",
+ org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}",
+
+ org.wso2.carbon.identity.application.mgt;
+ version="${carbon.identity.package.import.version.range}",
+ org.wso2.carbon.identity.application.common;
+ version="${carbon.identity.package.import.version.range}",
+
+ org.wso2.carbon.identity.organization.management.service;
+ version="${org.wso2.identity.organization.mgt.core.imp.pkg.version.range}",
+ org.wso2.carbon.identity.organization.management.service.exception;
+ version="${org.wso2.identity.organization.mgt.core.imp.pkg.version.range}",
+ org.wso2.carbon.identity.organization.management.service.util;
+ version="${org.wso2.identity.organization.mgt.core.imp.pkg.version.range}"
+
+
+ !org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.constant,
+ !org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.internal,
+ !org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.util,
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service,
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception,
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.strategy;
+ version="${project.version}"
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven.surefire.plugin.version}
+
+
+
+ ${argLine}
+ --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED
+ --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
+
+
+ src/test/resources/testng.xml
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+
+
+ org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/constant/*.class
+ org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/*.class
+ org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/internal/*.class
+ org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/AggregationStrategy.class
+ org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverService.class
+
+
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+
+ default-prepare-agent-integration
+
+ prepare-agent-integration
+
+
+
+ default-report
+
+ report
+
+
+
+ default-report-integration
+
+ report-integration
+
+
+
+ default-check
+
+ check
+
+
+
+
+ BUNDLE
+
+
+ COMPLEXITY
+ COVEREDRATIO
+ 0.60
+
+
+
+
+
+
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 8
+
+
+
+
+
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverService.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverService.java
new file mode 100644
index 000000000..873276d05
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service;
+
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.strategy.AggregationStrategy;
+
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Provides a service interface to retrieve resources from an organization's hierarchy.
+ * Supports traversal of both organization and application hierarchies using customizable
+ * resource retrieval and aggregation strategies.
+ *
+ * The service is designed for extensibility, allowing clients to define their own retrieval logic
+ * and aggregation mechanisms via functional interfaces and strategy patterns.
+ */
+public interface OrgResourceResolverService {
+
+ /**
+ * Retrieves resources by traversing the hierarchy of a given organization.
+ *
+ * @param organizationId The unique identifier of the organization.
+ * @param resourceRetriever A function that defines how to fetch a resource for a given organization ID.
+ * The function must return an {@link Optional} containing the resource if found,
+ * or an empty {@link Optional} if not.
+ * @param aggregationStrategy A strategy defining how to aggregate resources retrieved from
+ * different levels of the hierarchy.
+ * @param The type of the resource being retrieved and aggregated.
+ * @return An aggregated resource of type obtained from the organization hierarchy.
+ * @throws OrgResourceHierarchyTraverseException If any errors occur during resource retrieval
+ * or aggregation.
+ */
+ T getResourcesFromOrgHierarchy(String organizationId,
+ Function> resourceRetriever,
+ AggregationStrategy aggregationStrategy)
+ throws OrgResourceHierarchyTraverseException;
+
+ /**
+ * Retrieves resources by traversing the hierarchy of a given organization and application.
+ *
+ * @param organizationId The unique identifier of the organization.
+ * @param applicationId The unique identifier of the application within the organization.
+ * @param resourceRetriever A bi-function that defines how to fetch a resource based on the
+ * organization and application IDs. The function must return an
+ * {@link Optional} containing the resource if found,
+ * or an empty {@link Optional} if not.
+ * @param aggregationStrategy A strategy defining how to aggregate resources retrieved from
+ * different levels of the hierarchy.
+ * @param The type of the resource being retrieved and aggregated.
+ * @return An aggregated resource of type obtained from the organization and application hierarchy.
+ * @throws OrgResourceHierarchyTraverseException If any errors occur during resource retrieval
+ * or aggregation.
+ */
+ T getResourcesFromOrgHierarchy(String organizationId, String applicationId,
+ BiFunction> resourceRetriever,
+ AggregationStrategy aggregationStrategy)
+ throws OrgResourceHierarchyTraverseException;
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverServiceImpl.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverServiceImpl.java
new file mode 100644
index 000000000..c1fb7c19f
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverServiceImpl.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
+import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
+import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementServerException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.constant.OrgResourceHierarchyTraverseConstants;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseServerException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.internal.OrgResourceHierarchyTraverseServiceDataHolder;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.strategy.AggregationStrategy;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.util.OrgResourceHierarchyTraverseUtil;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Implementation of the OrgResourceResolverService interface, responsible for resolving resources within the
+ * given organization/ application hierarchy.
+ */
+public class OrgResourceResolverServiceImpl implements OrgResourceResolverService {
+
+ @Override
+ public T getResourcesFromOrgHierarchy(String organizationId, Function> resourceRetriever,
+ AggregationStrategy aggregationStrategy)
+ throws OrgResourceHierarchyTraverseException {
+
+ List organizationIds = getAncestorOrganizationsIds(organizationId);
+ return aggregationStrategy.aggregate(organizationIds, resourceRetriever);
+ }
+
+ @Override
+ public T getResourcesFromOrgHierarchy(String organizationId, String applicationId,
+ BiFunction> resourceRetriever,
+ AggregationStrategy aggregationStrategy)
+ throws OrgResourceHierarchyTraverseException {
+
+ try {
+ List organizationIds = getAncestorOrganizationsIds(organizationId);
+
+ ApplicationManagementService applicationManagementService = getApplicationManagementService();
+ Map ancestorAppIds = Collections.emptyMap();
+ if (applicationId != null) {
+ ancestorAppIds = applicationManagementService.getAncestorAppIds(applicationId, organizationId);
+ }
+
+ return aggregationStrategy.aggregate(organizationIds, ancestorAppIds, resourceRetriever);
+ } catch (IdentityApplicationManagementException e) {
+ throw OrgResourceHierarchyTraverseUtil.handleServerException(
+ OrgResourceHierarchyTraverseConstants.ErrorMessages
+ .ERROR_CODE_SERVER_ERROR_WHILE_RESOLVING_ANCESTOR_APPLICATIONS,
+ e, organizationId, applicationId);
+ }
+ }
+
+ private List getAncestorOrganizationsIds(String organizationId)
+ throws OrgResourceHierarchyTraverseServerException {
+
+ if (StringUtils.isBlank(organizationId)) {
+ throw OrgResourceHierarchyTraverseUtil.handleServerException(
+ OrgResourceHierarchyTraverseConstants.ErrorMessages.ERROR_CODE_EMPTY_ORGANIZATION_ID);
+ }
+
+ try {
+ OrganizationManager organizationManager = OrgResourceHierarchyTraverseUtil.getOrganizationManager();
+ List organizationIds = organizationManager.getAncestorOrganizationIds(organizationId);
+ if (CollectionUtils.isEmpty(organizationIds)) {
+ throw OrgResourceHierarchyTraverseUtil.handleServerException(OrgResourceHierarchyTraverseConstants
+ .ErrorMessages.ERROR_CODE_INVALID_ANCESTOR_ORGANIZATION_ID_LIST,
+ organizationId);
+ }
+ return organizationIds;
+ } catch (OrganizationManagementServerException e) {
+ throw OrgResourceHierarchyTraverseUtil.handleServerException(
+ OrgResourceHierarchyTraverseConstants.ErrorMessages
+ .ERROR_CODE_SERVER_ERROR_WHILE_RESOLVING_ANCESTOR_ORGANIZATIONS, e, organizationId);
+ }
+ }
+
+ private ApplicationManagementService getApplicationManagementService() {
+
+ return OrgResourceHierarchyTraverseServiceDataHolder.getInstance().getApplicationManagementService();
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/constant/OrgResourceHierarchyTraverseConstants.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/constant/OrgResourceHierarchyTraverseConstants.java
new file mode 100644
index 000000000..3c96c658c
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/constant/OrgResourceHierarchyTraverseConstants.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.constant;
+
+/**
+ * Constants defined for the organization resource hierarchy traverse service.
+ */
+public class OrgResourceHierarchyTraverseConstants {
+
+ private static final String ORGANIZATION_RESOURCE_HIERARCHY_TRAVERSE_ERROR_CODE_PREFIX = "ORHT-";
+
+ /**
+ * Private constructor to prevent instantiation of this constant class.
+ */
+ private OrgResourceHierarchyTraverseConstants() {
+
+ }
+
+ /**
+ * Enum which provides error codes and predefined error messages related to the traversal of
+ * the organization resource hierarchy. It ensures that error handling within the service is consistent
+ * and that detailed error descriptions are available for debugging and troubleshooting.
+ */
+ public enum ErrorMessages {
+
+ // Server errors.
+ ERROR_CODE_EMPTY_ORGANIZATION_ID(
+ "65001",
+ "Empty organization id.",
+ "Organization id cannot be null or empty."),
+ ERROR_CODE_INVALID_ANCESTOR_ORGANIZATION_ID_LIST(
+ "65002",
+ "Invalid ancestor organization id list.",
+ "The ancestor organization id list cannot be empty for the organization with id: %s. " +
+ "At least the organization itself should be included in the list."),
+ ERROR_CODE_SERVER_ERROR_WHILE_RESOLVING_ANCESTOR_ORGANIZATIONS(
+ "65003",
+ "Unable to resolve ancestor organizations.",
+ "Unexpected server error occurred " +
+ "while resolving ancestor organizations for organization with id: %s."),
+ ERROR_CODE_SERVER_ERROR_WHILE_RESOLVING_ANCESTOR_APPLICATIONS(
+ "65004",
+ "Unable to resolve ancestor applications.",
+ "Unexpected server error occurred while resolving ancestor applications for organization " +
+ "with id: %s for application with id: %s.");
+
+ private final String code;
+ private final String message;
+ private final String description;
+
+ /**
+ * Constructor for the ErrorMessages enum.
+ *
+ * This constructor is used to define each error message with a unique error code, a brief message, and
+ * a detailed description.
+ *
+ * @param code The unique error code for the message (prefixed with "ORHT-").
+ * @param message A brief message describing the error.
+ * @param description A detailed description of the error, often including placeholders for dynamic data.
+ */
+ ErrorMessages(String code, String message, String description) {
+
+ this.code = code;
+ this.message = message;
+ this.description = description;
+ }
+
+ /**
+ * Gets the unique error code for the error message.
+ *
+ * The error code is prefixed with "ORHT-" to ensure consistency in the error code system.
+ *
+ * @return The error code prefixed with "ORHT-".
+ */
+ public String getCode() {
+
+ return ORGANIZATION_RESOURCE_HIERARCHY_TRAVERSE_ERROR_CODE_PREFIX + code;
+ }
+
+ /**
+ * Gets the brief message for the error.
+ *
+ * This message provides a short description of the error, usually without context-specific details.
+ * It is used for logging or displaying to the user as part of the error response.
+ *
+ * @return A brief message describing the error.
+ */
+ public String getMessage() {
+
+ return message;
+ }
+
+ /**
+ * Gets the detailed description for the error message.
+ *
+ * This description provides a more detailed explanation of the error and often contains placeholders
+ * for dynamic data (e.g., organization or application IDs). It is typically used for logging or debugging.
+ *
+ * @return A detailed description of the error, including placeholders for dynamic data.
+ */
+ public String getDescription() {
+
+ return description;
+ }
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/NotImplementedException.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/NotImplementedException.java
new file mode 100644
index 000000000..c08cb203d
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/NotImplementedException.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.exception;
+
+/**
+ * A custom runtime exception to indicate that a method or functionality is not yet implemented.
+ */
+public class NotImplementedException extends RuntimeException {
+
+ private static final long serialVersionUID = 5117918786934518376L;
+
+ /**
+ * Constructs a new NotImplementedException with no detail message or cause.
+ *
+ * This constructor is typically used when no additional information is required
+ * to explain why the exception is thrown.
+ */
+ public NotImplementedException() {
+
+ super();
+ }
+
+ /**
+ * Constructs a new NotImplementedException with a detailed message and the root cause.
+ *
+ * @param message A descriptive message explaining why this exception is thrown.
+ * @param cause The underlying cause of this exception.
+ */
+ public NotImplementedException(String message, Throwable cause) {
+
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a new NotImplementedException with a detailed message.
+ *
+ * @param message A descriptive message explaining why this exception is thrown.
+ */
+ public NotImplementedException(String message) {
+
+ super(message);
+ }
+
+ /**
+ * Constructs a new NotImplementedException with the root cause.
+ *
+ * @param cause The underlying cause of this exception.
+ */
+ public NotImplementedException(Throwable cause) {
+
+ super(cause);
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseClientException.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseClientException.java
new file mode 100644
index 000000000..30e459991
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseClientException.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.exception;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Exception class for client-side errors during organization resource hierarchy traversal.
+ *
+ * This class handles exceptions where client-side errors occur, capturing error codes, messages, and descriptions
+ * to provide detailed context for troubleshooting.
+ */
+public class OrgResourceHierarchyTraverseClientException extends OrgResourceHierarchyTraverseException {
+
+ private String[] messages;
+
+ /**
+ * Constructs a new exception with an array of specified error messages.
+ *
+ * @param messages Detailed error messages.
+ */
+ public OrgResourceHierarchyTraverseClientException(String[] messages) {
+
+ super(Arrays.toString(messages));
+ if (messages == null) {
+ return;
+ }
+ List msgList = new ArrayList<>();
+ for (String msg : messages) {
+ if (!msg.trim().isEmpty()) {
+ msgList.add(msg);
+ }
+ }
+ this.messages = msgList.toArray(new String[0]);
+ }
+
+ /**
+ * Constructs a new exception with the specified message.
+ *
+ * @param message Detailed message.
+ */
+ public OrgResourceHierarchyTraverseClientException(String message) {
+
+ super(message);
+ }
+
+ /**
+ * Constructs a new exception with the specified message and cause.
+ *
+ * @param message Detailed message.
+ * @param e Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseClientException(String message, Throwable e) {
+
+ super(message, e);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code and cause.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ */
+ public OrgResourceHierarchyTraverseClientException(String errorCode, String message) {
+
+ super(errorCode, message);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message and cause.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ * @param cause Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseClientException(String errorCode, String message, Throwable cause) {
+
+ super(errorCode, message, cause);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message and description.
+ *
+ * @param errorCode Error code.
+ * @param message Error message.
+ * @param description Error description.
+ */
+ public OrgResourceHierarchyTraverseClientException(String errorCode, String message, String description) {
+
+ super(errorCode, message, description);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message, description and cause.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ * @param description Error description.
+ * @param cause Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseClientException(String errorCode, String message, String description,
+ Throwable cause) {
+
+ super(errorCode, message, description, cause);
+ }
+
+ @SuppressFBWarnings(value = "EI_EXPOSE_REP",
+ justification = "Client exception error messages are internally generated from the server side.")
+ public String[] getMessages() {
+
+ return messages;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseException.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseException.java
new file mode 100644
index 000000000..749498bec
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseException.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.exception;
+
+/**
+ * Custom exception for errors encountered during organization resource hierarchy traversal.
+ *
+ * This exception captures detailed error information, including error codes, messages, descriptions,
+ * and causes, to assist in troubleshooting issues related to resolving ancestor organizations or applications.
+ */
+public class OrgResourceHierarchyTraverseException extends Exception {
+
+ private String errorCode;
+ private String description;
+
+ private static final long serialVersionUID = 5967152066669023668L;
+
+ /**
+ * Constructs a new exception with the specified message.
+ *
+ * @param message Detailed message.
+ */
+ public OrgResourceHierarchyTraverseException(String message) {
+
+ super(message);
+ }
+
+ /**
+ * Constructs a new exception with the specified message and cause.
+ *
+ * @param message Detailed message.
+ * @param e Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseException(String message, Throwable e) {
+
+ super(message, e);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code and cause.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ */
+ public OrgResourceHierarchyTraverseException(String errorCode, String message) {
+
+ super(message);
+ this.errorCode = errorCode;
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message and description.
+ *
+ * @param errorCode Error code.
+ * @param message Error message.
+ * @param description Error description.
+ */
+ public OrgResourceHierarchyTraverseException(String errorCode, String message, String description) {
+
+ super(message);
+ this.errorCode = errorCode;
+ this.description = description;
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message and cause.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ * @param cause Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseException(String errorCode, String message, Throwable cause) {
+
+ super(message, cause);
+ this.errorCode = errorCode;
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message, description and cause.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ * @param description Error description.
+ * @param cause Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseException(String errorCode, String message, String description,
+ Throwable cause) {
+
+ super(message, cause);
+ this.errorCode = errorCode;
+ this.description = description;
+ }
+
+ /**
+ * Returns the error code.
+ *
+ * @return Error code.
+ */
+ public String getErrorCode() {
+
+ return errorCode;
+ }
+
+ /**
+ * Returns the error description.
+ *
+ * @return Error description.
+ */
+ public String getDescription() {
+
+ return description;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseServerException.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseServerException.java
new file mode 100644
index 000000000..2b2f7ecef
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/exception/OrgResourceHierarchyTraverseServerException.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.exception;
+
+/**
+ * Exception class for server-side errors during organization resource hierarchy traversal.
+ *
+ * This class handles exceptions related to server-side failures, including error codes, messages,
+ * descriptions, and causes, to provide detailed information for debugging.
+ */
+public class OrgResourceHierarchyTraverseServerException extends OrgResourceHierarchyTraverseException {
+
+ /**
+ * Constructs a new exception with the specified message.
+ *
+ * @param message Detailed message.
+ */
+ public OrgResourceHierarchyTraverseServerException(String message) {
+
+ super(message);
+ }
+
+ /**
+ * Constructs a new exception with the specified message and cause.
+ *
+ * @param message Detailed message.
+ * @param e Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseServerException(String message, Throwable e) {
+
+ super(message, e);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code and message.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ */
+ public OrgResourceHierarchyTraverseServerException(String errorCode, String message) {
+
+ super(errorCode, message);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message and cause.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ * @param cause Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseServerException(String errorCode, String message, Throwable cause) {
+
+ super(errorCode, message, cause);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message and description.
+ *
+ * @param errorCode Error code.
+ * @param message Error message.
+ * @param description Error description.
+ */
+ public OrgResourceHierarchyTraverseServerException(String errorCode, String message, String description) {
+
+ super(errorCode, message, description);
+ }
+
+ /**
+ * Constructs a new exception with the specified error code, message, description and cause.
+ *
+ * @param errorCode Error code.
+ * @param message Detailed message.
+ * @param description Error description.
+ * @param cause Cause as {@link Throwable}.
+ */
+ public OrgResourceHierarchyTraverseServerException(String errorCode, String message, String description,
+ Throwable cause) {
+
+ super(errorCode, message, description, cause);
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/internal/OrgResourceHierarchyTraverseServiceComponent.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/internal/OrgResourceHierarchyTraverseServiceComponent.java
new file mode 100644
index 000000000..022cc1831
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/internal/OrgResourceHierarchyTraverseServiceComponent.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.internal;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
+import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.OrgResourceResolverService;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.OrgResourceResolverServiceImpl;
+
+/**
+ * OSGi component responsible for managing the activation and deactivation of the organization resource hierarchy
+ * traverse service.
+ *
+ * It manages dynamic references to necessary services required by {@link OrgResourceResolverService} as well.
+ */
+@Component(
+ name = "org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service",
+ immediate = true)
+public class OrgResourceHierarchyTraverseServiceComponent {
+
+ private static final Log LOG = LogFactory.getLog(OrgResourceHierarchyTraverseServiceComponent.class);
+
+ /**
+ * Activates the OSGi component by registering the {@link OrgResourceResolverService} service.
+ * This method is called when the component is activated in the OSGi environment.
+ *
+ * @param context The ComponentContext instance that provides the OSGi environment context.
+ */
+ @Activate
+ protected void activate(ComponentContext context) {
+
+ try {
+ BundleContext bundleContext = context.getBundleContext();
+ bundleContext.registerService(OrgResourceResolverService.class.getName(),
+ new OrgResourceResolverServiceImpl(), null);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("OrgResourceResolverService bundle is activated successfully.");
+ }
+ } catch (Exception e) {
+ LOG.error("Error while activating OrgResourceResolverService bundle.", e);
+ }
+ }
+
+ /**
+ * Deactivates the OSGi component by cleaning up resources and logging the deactivation.
+ * This method is called when the component is deactivated in the OSGi environment.
+ *
+ * @param context The ComponentContext instance that provides the OSGi environment context.
+ */
+ @Deactivate
+ protected void deactivate(ComponentContext context) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("OrgResourceResolverService bundle is deactivated");
+ }
+ }
+
+ /**
+ * Sets the OrganizationManager instance in the OrgResourceHierarchyTraverseServiceDataHolder.
+ *
+ * @param organizationManager The OrganizationManager instance to be assigned.
+ */
+ @Reference(name = "org.wso2.carbon.identity.organization.management.service",
+ service = OrganizationManager.class,
+ cardinality = ReferenceCardinality.MANDATORY,
+ policy = ReferencePolicy.DYNAMIC,
+ unbind = "unsetOrganizationManager")
+ protected void setOrganizationManager(OrganizationManager organizationManager) {
+
+ OrgResourceHierarchyTraverseServiceDataHolder.getInstance().setOrganizationManager(organizationManager);
+ LOG.debug("OrganizationManager set in OrgResourceManagementServiceComponent bundle.");
+ }
+
+ /**
+ * Unsets the OrganizationManager instance in the OrgResourceHierarchyTraverseServiceDataHolder.
+ *
+ * @param organizationManager The OrganizationManager instance to be removed.
+ */
+ protected void unsetOrganizationManager(OrganizationManager organizationManager) {
+
+ OrgResourceHierarchyTraverseServiceDataHolder.getInstance().setOrganizationManager(null);
+ LOG.debug("OrganizationManager unset in OrgResourceManagementServiceComponent bundle.");
+ }
+
+ /**
+ * Sets the ApplicationManagementService instance in the OrgResourceHierarchyTraverseServiceDataHolder.
+ *
+ * @param applicationManagementService The ApplicationManagementService instance to be assigned.
+ */
+ @Reference(
+ name = "org.wso2.carbon.identity.application.mgt",
+ service = ApplicationManagementService.class,
+ cardinality = ReferenceCardinality.MANDATORY,
+ policy = ReferencePolicy.DYNAMIC,
+ unbind = "unsetApplicationManagementService")
+ protected void setApplicationManagementService(ApplicationManagementService applicationManagementService) {
+
+ OrgResourceHierarchyTraverseServiceDataHolder.getInstance()
+ .setApplicationManagementService(applicationManagementService);
+ LOG.debug("ApplicationManagementService set in OrgResourceManagementServiceComponent bundle.");
+ }
+
+ /**
+ * Unsets the ApplicationManagementService instance in the OrgResourceHierarchyTraverseServiceDataHolder.
+ *
+ * @param applicationManagementService The ApplicationManagementService instance to be removed.
+ */
+ protected void unsetApplicationManagementService(ApplicationManagementService applicationManagementService) {
+
+ OrgResourceHierarchyTraverseServiceDataHolder.getInstance().setApplicationManagementService(null);
+ LOG.debug("ApplicationManagementService unset in OrgResourceManagementServiceComponent bundle.");
+ }
+}
+
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/internal/OrgResourceHierarchyTraverseServiceDataHolder.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/internal/OrgResourceHierarchyTraverseServiceDataHolder.java
new file mode 100644
index 000000000..0b4fd6686
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/internal/OrgResourceHierarchyTraverseServiceDataHolder.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.internal;
+
+import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
+import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
+
+/**
+ * Singleton class that serves as a centralized data holder for key service instances used in the organization
+ * resource hierarchy traversal process.
+ */
+public class OrgResourceHierarchyTraverseServiceDataHolder {
+
+ private static final OrgResourceHierarchyTraverseServiceDataHolder INSTANCE =
+ new OrgResourceHierarchyTraverseServiceDataHolder();
+
+ private OrganizationManager organizationManager;
+ private ApplicationManagementService applicationManagementService;
+
+ private OrgResourceHierarchyTraverseServiceDataHolder() {
+
+ }
+
+ /**
+ * Retrieves the Singleton instance of the OrgResourceHierarchyTraverseServiceDataHolder class.
+ *
+ * @return The singleton instance of OrgResourceHierarchyTraverseServiceDataHolder.
+ */
+ public static OrgResourceHierarchyTraverseServiceDataHolder getInstance() {
+
+ return INSTANCE;
+ }
+
+ /**
+ * Retrieves the current instance of the OrganizationManager.
+ *
+ * @return The current OrganizationManager instance that manages organizational data.
+ */
+ public OrganizationManager getOrganizationManager() {
+
+ return organizationManager;
+ }
+
+ /**
+ * Sets the OrganizationManager instance.
+ *
+ * @param organizationManager The OrganizationManager instance to be assigned.
+ */
+ public void setOrganizationManager(OrganizationManager organizationManager) {
+
+ this.organizationManager = organizationManager;
+ }
+
+ /**
+ * Retrieves the current instance of the ApplicationManagementService.
+ *
+ * @return The current ApplicationManagementService instance responsible for managing applications.
+ */
+ public ApplicationManagementService getApplicationManagementService() {
+
+ return applicationManagementService;
+ }
+
+ /**
+ * Sets the ApplicationManagementService instance.
+ *
+ * @param applicationManagementService The ApplicationManagementService instance to be set.
+ */
+ public void setApplicationManagementService(ApplicationManagementService applicationManagementService) {
+
+ this.applicationManagementService = applicationManagementService;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/AggregationStrategy.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/AggregationStrategy.java
new file mode 100644
index 000000000..ef0798a84
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/AggregationStrategy.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.strategy;
+
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.NotImplementedException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseException;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Defines an interface for a strategy used for aggregating resources retrieved from hierarchical structures,
+ * such as organization and application hierarchies. This interface provides default
+ * methods that can be overridden to implement specific aggregation logic.
+ *
+ * Implementations should specify how resources are combined and may use functional
+ * interfaces for flexible retrieval mechanisms.
+ *
+ * @param The type of the resource to be aggregated.
+ */
+public interface AggregationStrategy {
+
+ /**
+ * Aggregates resources resolved from an organization's hierarchical structure.
+ *
+ * This method provides a default implementation that throws a
+ * {@link NotImplementedException}. Subclasses must override this method to define
+ * specific aggregation logic for organization hierarchies.
+ *
+ * @param organizationHierarchy A list representing the organization hierarchy,
+ * where the first element is the root organization
+ * and subsequent elements represent child organizations.
+ * @param resourceRetriever A function that retrieves a resource given an organization ID.
+ * Returns an {@link Optional} containing the resource, or empty
+ * if no resource is found for the given ID.
+ * @return The aggregated resource of type .
+ * @throws OrgResourceHierarchyTraverseException If any error occurs during resource
+ * retrieval or aggregation.
+ */
+ default T aggregate(List organizationHierarchy, Function> resourceRetriever)
+ throws OrgResourceHierarchyTraverseException {
+
+ throw new NotImplementedException("aggregate method is not implemented in " + this.getClass());
+ }
+
+ /**
+ * Aggregates resources resolved from an organization's and application's hierarchical structure.
+ *
+ * This method provides a default implementation that throws a
+ * {@link NotImplementedException}. Subclasses must override this method to define
+ * specific aggregation logic for combined organization and application hierarchies.
+ *
+ * @param organizationHierarchy A list representing the organization hierarchy,
+ * where the first element is the root organization
+ * and subsequent elements represent child organizations.
+ * @param applicationHierarchy A map representing the application hierarchy, where keys
+ * are organization IDs, and values are application-specific
+ * details or IDs for each organization.
+ * @param resourceRetriever A bi-function that retrieves a resource based on both an
+ * organization ID and an application ID. Returns an {@link Optional}
+ * containing the resource, or empty if no resource is found.
+ * @return The aggregated resource of type .
+ * @throws OrgResourceHierarchyTraverseException If any error occurs during resource
+ * retrieval or aggregation.
+ */
+ default T aggregate(List organizationHierarchy, Map applicationHierarchy,
+ BiFunction> resourceRetriever)
+ throws OrgResourceHierarchyTraverseException {
+
+ throw new NotImplementedException("aggregate method is not implemented in " + this.getClass());
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/FirstFoundAggregationStrategy.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/FirstFoundAggregationStrategy.java
new file mode 100644
index 000000000..efede0cb7
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/FirstFoundAggregationStrategy.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.strategy;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.util.OrgResourceHierarchyTraverseUtil;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Aggregation strategy that can be used to traverse the organization hierarchy and retrieve the first resource found.
+ * This strategy is commonly applied when multiple resources might exist at different levels of the hierarchy,
+ * and only the first one encountered at the bottom of the hierarchy needs to be returned.
+ *
+ * @param The type of the resource being retrieved from the organization/ application hierarchy.
+ */
+public class FirstFoundAggregationStrategy implements AggregationStrategy {
+
+ @Override
+ public T aggregate(List organizationHierarchy, Function> resourceRetriever)
+ throws OrgResourceHierarchyTraverseException {
+
+ if (CollectionUtils.isEmpty(organizationHierarchy)) {
+ return null;
+ }
+
+ for (String orgId : organizationHierarchy) {
+ if (OrgResourceHierarchyTraverseUtil.isMinOrgHierarchyDepthReached(orgId)) {
+ break;
+ }
+
+ Optional resource = resourceRetriever.apply(orgId);
+ if (resource.isPresent()) {
+ return resource.get();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public T aggregate(List organizationHierarchy, Map applicationHierarchy,
+ BiFunction> resourceRetriever)
+ throws OrgResourceHierarchyTraverseException {
+
+ if (CollectionUtils.isEmpty(organizationHierarchy)) {
+ return null;
+ }
+
+ for (String orgId : organizationHierarchy) {
+ if (OrgResourceHierarchyTraverseUtil.isMinOrgHierarchyDepthReached(orgId)) {
+ break;
+ }
+
+ String appId = applicationHierarchy.get(orgId);
+ Optional resource = resourceRetriever.apply(orgId, appId);
+ if (resource.isPresent()) {
+ return resource.get();
+ }
+ }
+ return null;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/MergeAllAggregationStrategy.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/MergeAllAggregationStrategy.java
new file mode 100644
index 000000000..2d1ad9372
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/strategy/MergeAllAggregationStrategy.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.strategy;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.util.OrgResourceHierarchyTraverseUtil;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Aggregation strategy to merge all resources in the organization hierarchy using the specified
+ * resource merger function.
+ *
+ * This strategy traverses the hierarchy and applies the provided function
+ * to combine the resources at each level. It ensures that the resources are merged according to the
+ * logic defined by the merger function, which could involve combining attributes, performing calculations, or
+ * resolving conflicts between resources.
+ *
+ * @param The type of the resources being merged in the organization/ application hierarchy.
+ */
+public class MergeAllAggregationStrategy implements AggregationStrategy {
+
+ private final BiFunction resourceMerger;
+
+ /**
+ * Constructor to initialize the aggregation strategy with the resource merger function.
+ *
+ * @param resourceMerger Resource merger function.
+ */
+ public MergeAllAggregationStrategy(BiFunction resourceMerger) {
+
+ this.resourceMerger = resourceMerger;
+ }
+
+ @Override
+ public T aggregate(List organizationHierarchy, Function> resourceRetriever)
+ throws OrgResourceHierarchyTraverseException {
+
+ T aggregatedResource = null;
+ if (CollectionUtils.isEmpty(organizationHierarchy)) {
+ return aggregatedResource;
+ }
+
+ for (String orgId : organizationHierarchy) {
+ if (OrgResourceHierarchyTraverseUtil.isMinOrgHierarchyDepthReached(orgId)) {
+ break;
+ }
+
+ Optional resource = resourceRetriever.apply(orgId);
+ if (resource.isPresent()) {
+ if (aggregatedResource == null) {
+ aggregatedResource = resource.get();
+ } else {
+ aggregatedResource = resourceMerger.apply(aggregatedResource, resource.get());
+ }
+ }
+ }
+ return aggregatedResource;
+ }
+
+ @Override
+ public T aggregate(List organizationHierarchy, Map applicationHierarchy,
+ BiFunction> resourceRetriever)
+ throws OrgResourceHierarchyTraverseException {
+
+ T aggregatedResource = null;
+ if (CollectionUtils.isEmpty(organizationHierarchy)) {
+ return aggregatedResource;
+ }
+
+ for (String orgId : organizationHierarchy) {
+ if (OrgResourceHierarchyTraverseUtil.isMinOrgHierarchyDepthReached(orgId)) {
+ break;
+ }
+
+ String appId = applicationHierarchy.get(orgId);
+ Optional resource = resourceRetriever.apply(orgId, appId);
+ if (resource.isPresent()) {
+ if (aggregatedResource == null) {
+ aggregatedResource = resource.get();
+ } else {
+ aggregatedResource = resourceMerger.apply(aggregatedResource, resource.get());
+ }
+ }
+ }
+ return aggregatedResource;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/util/OrgResourceHierarchyTraverseUtil.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/util/OrgResourceHierarchyTraverseUtil.java
new file mode 100644
index 000000000..7324900a5
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/main/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/util/OrgResourceHierarchyTraverseUtil.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.util;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementServerException;
+import org.wso2.carbon.identity.organization.management.service.util.Utils;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.constant.OrgResourceHierarchyTraverseConstants;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseServerException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.internal.OrgResourceHierarchyTraverseServiceDataHolder;
+
+/**
+ * Utility class for the Organization Resource Hierarchy Traverse Service.
+ *
+ * This class provides helper methods to interact with the organization hierarchy and manage traversal logic,
+ * such as verifying the hierarchy depth and handling server-side exceptions.
+ */
+public class OrgResourceHierarchyTraverseUtil {
+
+ /**
+ * Private constructor to prevent instantiation of the utility class.
+ */
+ private OrgResourceHierarchyTraverseUtil() {
+
+ }
+
+ /**
+ * Retrieve the organization manager instance.
+ *
+ * @return The {@link OrganizationManager} instance used to manage organizations.
+ */
+ public static OrganizationManager getOrganizationManager() {
+
+ return OrgResourceHierarchyTraverseServiceDataHolder.getInstance().getOrganizationManager();
+ }
+
+ /**
+ * Verify if the organization hierarchy depth has reached the minimum required level.
+ *
+ * This method obtains the depth of the specified organization in the hierarchy and compares it
+ * with the configured minimum depth. An exception is thrown if an error occurs during the depth calculation.
+ *
+ * @param orgId The ID of the organization to check.
+ * @return {@code true} if the hierarchy depth is less than the minimum required depth, {@code false} otherwise.
+ * @throws OrgResourceHierarchyTraverseServerException If an error occurs while retrieving the organization's depth.
+ */
+ public static boolean isMinOrgHierarchyDepthReached(String orgId) throws
+ OrgResourceHierarchyTraverseServerException {
+
+ int minHierarchyDepth = Utils.getSubOrgStartLevel() - 1;
+ try {
+ int depthInHierarchy = getOrganizationManager().getOrganizationDepthInHierarchy(orgId);
+ return depthInHierarchy < minHierarchyDepth;
+ } catch (OrganizationManagementServerException e) {
+ throw new OrgResourceHierarchyTraverseServerException(
+ "Error occurred while getting the hierarchy depth of the organization: " + orgId, e);
+ }
+ }
+
+ /**
+ * Create an {@link OrgResourceHierarchyTraverseServerException} to handle server-side errors.
+ *
+ * This method formats the error description using the provided data, if applicable, and constructs
+ * a custom exception for consistent error handling in the service.
+ *
+ * @param error The error enumeration containing predefined error messages and codes.
+ * @param data Optional data to format the error message description.
+ * @return An {@link OrgResourceHierarchyTraverseServerException} with the formatted error details.
+ */
+ public static OrgResourceHierarchyTraverseServerException handleServerException(
+ OrgResourceHierarchyTraverseConstants.ErrorMessages error, String... data) {
+
+ String description = error.getDescription();
+ if (ArrayUtils.isNotEmpty(data)) {
+ description = String.format(description, data);
+ }
+ return new OrgResourceHierarchyTraverseServerException(error.getMessage(), description, error.getCode());
+ }
+
+ /**
+ * Create an {@link OrgResourceHierarchyTraverseServerException} to handle server-side errors.
+ *
+ * This method formats the error description using the provided data, if applicable, and constructs
+ * a custom exception including the underlying cause of the error, for consistent error handling in the service.
+ *
+ * @param error The error enumeration containing predefined error messages and codes.
+ * @param e The underlying cause of the error.
+ * @param data Optional data to format the error message description.
+ * @return An {@link OrgResourceHierarchyTraverseServerException} with the formatted error details.
+ */
+ public static OrgResourceHierarchyTraverseServerException handleServerException(
+ OrgResourceHierarchyTraverseConstants.ErrorMessages error, Throwable e, String... data) {
+
+ String description = error.getDescription();
+ if (ArrayUtils.isNotEmpty(data)) {
+ description = String.format(description, data);
+ }
+ return new OrgResourceHierarchyTraverseServerException(error.getMessage(), description, error.getCode(), e);
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverServiceTest.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverServiceTest.java
new file mode 100644
index 000000000..311ed269c
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/OrgResourceResolverServiceTest.java
@@ -0,0 +1,688 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service;
+
+import org.mockito.Mock;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
+import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
+import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementServerException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.exception.OrgResourceHierarchyTraverseServerException;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.internal.OrgResourceHierarchyTraverseServiceDataHolder;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.mock.resource.impl.MockResourceManagementService;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.mock.resource.impl.model.MockResource;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.strategy.AggregationStrategy;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.strategy.FirstFoundAggregationStrategy;
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.strategy.MergeAllAggregationStrategy;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.openMocks;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertThrows;
+
+/**
+ * Unit tests for the OrgResourceResolverService.
+ */
+public class OrgResourceResolverServiceTest {
+
+ private static final String ROOT_ORG_ID = "10084a8d-113f-4211-a0d5-efe36b082211";
+ private static final String ROOT_APP_ID = "1ee981ab-64e7-435c-ab91-e8d1e0a13b2c";
+ private static final String L1_ORG_ID = "93d996f9-a5ba-4275-a52b-adaad9eba869";
+ private static final String L1_APP_ID = "619d2cb1-174d-4d38-af3b-99c532dddb00";
+ private static final String L2_ORG_ID = "30b701c6-e309-4241-b047-0c299c45d1a0";
+ private static final String L2_APP_ID = "d04d48db-8c5a-437a-9458-4352b84db621";
+ private static final String INVALID_ORG_ID = "invalid-org-id";
+ private static final String INVALID_APP_ID = "invalid-app-id";
+
+ private OrgResourceResolverService orgResourceResolverService;
+ private MockResourceManagementService mockResourceManagementService;
+ private AggregationStrategy firstFoundAggregationStrategy;
+ private AggregationStrategy mergeAllAggregationStrategy;
+
+ @Mock
+ OrganizationManager organizationManager;
+
+ @Mock
+ ApplicationManagementService applicationManagementService;
+
+ /**
+ * Initializes test data and mock services before the test class is run.
+ * This method sets up necessary services and aggregation strategies required for the tests.
+ */
+ @BeforeClass
+ public void init() {
+
+ // Open mock objects for the current test instance.
+ openMocks(this);
+
+ // Set the OrganizationManager and ApplicationManagementService to the data holder for use in tests
+ OrgResourceHierarchyTraverseServiceDataHolder.getInstance().setOrganizationManager(organizationManager);
+ OrgResourceHierarchyTraverseServiceDataHolder.getInstance()
+ .setApplicationManagementService(applicationManagementService);
+
+ // Initialize the aggregation strategies with the appropriate strategy types.
+ firstFoundAggregationStrategy = new FirstFoundAggregationStrategy<>();
+ mergeAllAggregationStrategy = new MergeAllAggregationStrategy<>(this::resourceMerger);
+ }
+
+ /**
+ * Sets up mocks for individual test cases by initializing mock services and resource management.
+ * This method runs before each test method to ensure the environment is ready for testing specific scenarios.
+ */
+ @BeforeMethod
+ public void setUp() throws Exception {
+
+ // Mock responses for the organization manager with different ancestor organization chains.
+ mockAncestorOrganizationRetrieval(Arrays.asList(L2_ORG_ID, L1_ORG_ID, ROOT_ORG_ID));
+ mockAncestorOrganizationRetrieval(Arrays.asList(L1_ORG_ID, ROOT_ORG_ID));
+ mockAncestorOrganizationRetrieval(Collections.singletonList(ROOT_ORG_ID));
+
+ // Mock responses for the application management service with corresponding application ancestor data.
+ mockAncestorApplicationRetrieval(Arrays.asList(L2_ORG_ID, L1_ORG_ID, ROOT_ORG_ID),
+ Arrays.asList(L2_APP_ID, L1_APP_ID, ROOT_APP_ID));
+ mockAncestorApplicationRetrieval(Arrays.asList(L1_ORG_ID, ROOT_ORG_ID),
+ Arrays.asList(L1_APP_ID, ROOT_APP_ID));
+ mockAncestorApplicationRetrieval(Collections.singletonList(ROOT_ORG_ID),
+ Collections.singletonList(ROOT_APP_ID));
+
+ // Initialize the mock resource management service used in tests.
+ mockResourceManagementService = new MockResourceManagementService();
+
+ // Instantiate the OrgResourceResolverService for testing.
+ orgResourceResolverService = new OrgResourceResolverServiceImpl();
+ }
+
+ /**
+ * Resets mock services after each test method to ensure a clean state for subsequent tests.
+ */
+ @AfterMethod
+ public void tearDown() {
+
+ // Reset the mock services to their default state after each test.
+ reset(organizationManager);
+ reset(applicationManagementService);
+ }
+
+ @DataProvider(name = "AggregationStrategyDataProvider")
+ public Object[][] provideAggregationStrategies() {
+
+ return new Object[][]{
+ {firstFoundAggregationStrategy},
+ {mergeAllAggregationStrategy}
+ };
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when no resources are available in the
+ * organization hierarchy.
+ *
+ * This test ensures that the resolver correctly returns null when no resources are found at any organization level.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetOrgLevelResourcesFromOrgHierarchyWhenNoResourceAvailable(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ MockResource resolvedRootResource = invokeOrgLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID);
+ assertNull(resolvedRootResource);
+
+ MockResource resolvedL1Resource = invokeOrgLevelResourceResolver(aggregationStrategy, L1_ORG_ID);
+ assertNull(resolvedL1Resource);
+
+ MockResource resolvedL2Resource = invokeOrgLevelResourceResolver(aggregationStrategy, L2_ORG_ID);
+ assertNull(resolvedL2Resource);
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when an organization-level resource is available at the
+ * root organization level.
+ *
+ * This test ensures that the resolver correctly returns the resource for the root organization and propagates
+ * the resolution to its child organizations (L1 and L2) using the given aggregation strategy.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetOrgLevelResourcesFromOrgHierarchyWhenRootResourceAvailable(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ // Add org-level resource for root organization into the mock resource management service.
+ List createdOrgResource = addOrgResources(Collections.singletonList(ROOT_ORG_ID));
+
+ MockResource resolvedRootResource = invokeOrgLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID);
+ assertResolvedResponse(resolvedRootResource, createdOrgResource.get(0));
+
+ MockResource resolvedL1Resource = invokeOrgLevelResourceResolver(aggregationStrategy, L1_ORG_ID);
+ assertResolvedResponse(resolvedL1Resource, createdOrgResource.get(0));
+
+ MockResource resolvedL2Resource = invokeOrgLevelResourceResolver(aggregationStrategy, L2_ORG_ID);
+ assertResolvedResponse(resolvedL2Resource, createdOrgResource.get(0));
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when an organization-level resource is available at the
+ * L1 organization.
+ *
+ * This test ensures that when a resource is available at the L1 level, it is correctly resolved for
+ * L1, and L2 organizations according to the provided aggregation strategy. At the root level,
+ * its own resource should be returned.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetOrgLevelResourcesFromOrgHierarchyWhenL1OrgResourceAvailable(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ // Add org-level resources for L1 and root organizations into the mock resource management service.
+ List createdOrgResources = addOrgResources(Arrays.asList(ROOT_ORG_ID, L1_ORG_ID));
+
+ MockResource resolvedRootResource = invokeOrgLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID);
+ assertResolvedResponse(resolvedRootResource, createdOrgResources.get(0));
+
+ MockResource resolvedL1Resource = invokeOrgLevelResourceResolver(aggregationStrategy, L1_ORG_ID);
+ assertResolvedResponse(resolvedL1Resource, createdOrgResources.get(1));
+
+ MockResource resolvedL2Resource = invokeOrgLevelResourceResolver(aggregationStrategy, L2_ORG_ID);
+ assertResolvedResponse(resolvedL2Resource, createdOrgResources.get(1));
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when an organization-level resource is available at the
+ * L2 organization.
+ *
+ * This test ensures that when a resource is available at the L2 level, it is correctly resolved for the
+ * L2 organization based on the provided aggregation strategy.
+ * At the root and L1 level, their own resource should be returned.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetOrgLevelResourcesFromOrgHierarchyWhenL2OrgResourceAvailable(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ // Add org-level resources for L2, L1 and root organizations into the mock resource management service.
+ List createdOrgResources = addOrgResources(Arrays.asList(ROOT_ORG_ID, L1_ORG_ID, L2_ORG_ID));
+
+ MockResource resolvedRootResource = invokeOrgLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID);
+ assertResolvedResponse(resolvedRootResource, createdOrgResources.get(0));
+
+ MockResource resolvedL1Resource = invokeOrgLevelResourceResolver(aggregationStrategy, L1_ORG_ID);
+ assertResolvedResponse(resolvedL1Resource, createdOrgResources.get(1));
+
+ MockResource resolvedL2Resource = invokeOrgLevelResourceResolver(aggregationStrategy, L2_ORG_ID);
+ assertResolvedResponse(resolvedL2Resource, createdOrgResources.get(2));
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when no application-level or organization-level resources
+ * are available in the hierarchy.
+ *
+ * This test ensures that when no resources are available at the organization or application levels,
+ * the resolver correctly returns null.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetAppLevelResourcesFromOrgHierarchyWhenNoResourceAvailable(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ MockResource resolvedRootAppResource =
+ invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID);
+ assertNull(resolvedRootAppResource);
+
+ MockResource resolvedL1AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID);
+ assertNull(resolvedL1AppResource);
+
+ MockResource resolvedL2AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID);
+ assertNull(resolvedL2AppResource);
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when resources are available at both root organization
+ * and/ or root application levels in the organization hierarchy.
+ *
+ * This test verifies that when resources are available for the root level, they are correctly resolved at the
+ * organization and application levels across all organization levels (root, L1, and L2).
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources at different levels.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetAppLevelResourcesFromOrgHierarchyWhenRootResourcesAvailable(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ // Add org-level resource for root organization into the mock resource management service.
+ List createdOrgResource = addOrgResources(Collections.singletonList(ROOT_ORG_ID));
+
+ MockResource resolvedRootAppResource =
+ invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID);
+ assertResolvedResponse(resolvedRootAppResource, createdOrgResource.get(0));
+
+ MockResource resolvedL1AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID);
+ assertResolvedResponse(resolvedL1AppResource, createdOrgResource.get(0));
+
+ MockResource resolvedL2AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID);
+ assertResolvedResponse(resolvedL2AppResource, createdOrgResource.get(0));
+
+ // Add app-level resource for root organization into the mock resource management service.
+ List createdAppResource = addAppResources(Collections.singletonList(ROOT_ORG_ID),
+ Collections.singletonList(ROOT_APP_ID));
+
+ resolvedRootAppResource =
+ invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID);
+ assertResolvedResponse(resolvedRootAppResource, createdAppResource.get(0));
+
+ resolvedL1AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID);
+ assertResolvedResponse(resolvedL1AppResource, createdAppResource.get(0));
+
+ resolvedL2AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID);
+ assertResolvedResponse(resolvedL2AppResource, createdAppResource.get(0));
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when organization-level and/ or application-level resources
+ * are available for both root and L1 organizations in the hierarchy.
+ *
+ * This test ensures when resources are available for the L1 level, they are correctly resolved at the
+ * organization and application levels across both organization levels, L1, and L2. At the root level,
+ * its own resource should be returned.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources at different levels.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetAppLevelResourcesFromOrgHierarchyWhenL1ResourcesAvailable(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ // Add org-level resources for L1 and root organizations into the mock resource management service.
+ List createdOrgResources = addOrgResources(Arrays.asList(ROOT_ORG_ID, L1_ORG_ID));
+ // Add app-level resources for root organization into the mock resource management service.
+ List createdAppResources = addAppResources(Collections.singletonList(ROOT_ORG_ID),
+ Collections.singletonList(ROOT_APP_ID));
+
+ MockResource resolvedRootAppResource =
+ invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID);
+ assertResolvedResponse(resolvedRootAppResource, createdAppResources.get(0));
+
+ MockResource resolvedL1AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID);
+ assertResolvedResponse(resolvedL1AppResource, createdOrgResources.get(1));
+
+ MockResource resolvedL2AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID);
+ assertResolvedResponse(resolvedL2AppResource, createdOrgResources.get(1));
+
+ // Add app-level resources for L1 organization into the mock resource management service.
+ createdAppResources.addAll(addAppResources(Collections.singletonList(L1_ORG_ID),
+ Collections.singletonList(L1_APP_ID)));
+
+ resolvedRootAppResource =
+ invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID);
+ assertResolvedResponse(resolvedRootAppResource, createdAppResources.get(0));
+
+ resolvedL1AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID);
+ assertResolvedResponse(resolvedL1AppResource, createdAppResources.get(1));
+
+ resolvedL2AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID);
+ assertResolvedResponse(resolvedL2AppResource, createdAppResources.get(1));
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when organization-level and/ or application-level resources
+ * are available for all organization levels in the hierarchy.
+ *
+ * This test ensures that when a resource is available at the L2 level, it is correctly resolved for the
+ * L2 organization based on the provided aggregation strategy. At the root and L1 level, their own resources
+ * should be returned.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources at different levels.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetAppLevelResourcesFromOrgHierarchyWhenL2ResourcesAvailable(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ // Add org-level resources for L2, L1 and root organizations into the mock resource management service.
+ List createdOrgResources = addOrgResources(Arrays.asList(ROOT_ORG_ID, L1_ORG_ID, L2_ORG_ID));
+ // Add app-level resources for L1 and root organizations into the mock resource management service.
+ List createdAppResources = addAppResources(Arrays.asList(ROOT_ORG_ID, L1_ORG_ID),
+ Arrays.asList(ROOT_APP_ID, L1_APP_ID));
+
+ MockResource resolvedRootAppResource =
+ invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID);
+ assertResolvedResponse(resolvedRootAppResource, createdAppResources.get(0));
+
+ MockResource resolvedL1AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID);
+ assertResolvedResponse(resolvedL1AppResource, createdAppResources.get(1));
+
+ MockResource resolvedL2AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID);
+ assertResolvedResponse(resolvedL2AppResource, createdOrgResources.get(2));
+
+ // Add app-level resources for L2 organization into the mock resource management service.
+ createdAppResources.addAll(addAppResources(Collections.singletonList(L2_ORG_ID),
+ Collections.singletonList(L2_APP_ID)));
+
+ resolvedRootAppResource =
+ invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID);
+ assertResolvedResponse(resolvedRootAppResource, createdAppResources.get(0));
+
+ resolvedL1AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID);
+ assertResolvedResponse(resolvedL1AppResource, createdAppResources.get(1));
+
+ resolvedL2AppResource = invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID);
+ assertResolvedResponse(resolvedL2AppResource, createdAppResources.get(2));
+ }
+
+ @DataProvider(name = "provideAggregationStrategiesForOrgLevelResolverWithInvalidInput")
+ public Object[][] provideAggregationStrategiesForOrgLevelResolverWithInvalidInput() {
+
+ List invalidOrgIds = Arrays.asList(null, "", INVALID_ORG_ID);
+
+ return new Object[][]{
+ {firstFoundAggregationStrategy, invalidOrgIds},
+ {mergeAllAggregationStrategy, invalidOrgIds},
+ };
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when provided with invalid organization IDs.
+ *
+ * This test ensures that when invalid organization IDs are passed to the resolver, it correctly returns null,
+ * indicating that no resources are found for the invalid input.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources at different levels.
+ * @param invalidOrgIds A list of invalid organization IDs to test the resolver's behavior.
+ * @throws Exception If an error occurs during resolution.
+ */
+ @Test(dataProvider = "provideAggregationStrategiesForOrgLevelResolverWithInvalidInput")
+ public void testGetOrgLevelResourcesFromOrgHierarchyForInvalidInput(
+ AggregationStrategy aggregationStrategy, List invalidOrgIds) throws Exception {
+
+ for (String orgId : invalidOrgIds) {
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeOrgLevelResourceResolver(aggregationStrategy, orgId));
+ }
+ }
+
+ @DataProvider(name = "provideAggregationStrategiesForAppLevelResolverWithInvalidInput")
+ public Object[][] provideAggregationStrategiesForAppLevelResolverWithInvalidInput() {
+
+ List invalidOrgIds = Arrays.asList(null, "", INVALID_ORG_ID);
+ List invalidAppIds = Arrays.asList(null, "", INVALID_APP_ID);
+
+ return new Object[][]{
+ {firstFoundAggregationStrategy, invalidOrgIds, invalidAppIds},
+ {mergeAllAggregationStrategy, invalidOrgIds, invalidAppIds},
+ };
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when provided with invalid organization and application IDs.
+ *
+ * This test verifies that the resolver correctly handles invalid input combinations by returning null,
+ * ensuring robustness and appropriate error handling in edge cases. The test covers scenarios where:
+ * - The organization ID is invalid.
+ * - The application ID is invalid.
+ * - Both the organization and application IDs are invalid.
+ *
+ * @param aggregationStrategy The aggregation strategy used to resolve resources across the hierarchy.
+ * @param invalidOrgIds A list of invalid organization IDs to test the resolver's behavior.
+ * @param invalidAppIds A list of invalid application IDs to test the resolver's behavior.
+ * @throws Exception If an error occurs during resolution.
+ */
+ @Test(dataProvider = "provideAggregationStrategiesForAppLevelResolverWithInvalidInput")
+ public void testGetAppLevelResourcesFromOrgHierarchyForInvalidInput(
+ AggregationStrategy aggregationStrategy, List invalidOrgIds,
+ List invalidAppIds) throws Exception {
+
+ for (String invalidOrgId : invalidOrgIds) {
+ for (String invalidAppId : invalidAppIds) {
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeAppLevelResourceResolver(aggregationStrategy, invalidOrgId, invalidAppId));
+ }
+ }
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when server-side errors occur during
+ * organizational hierarchy traversal.
+ *
+ * This test simulates server-side exceptions thrown by the `organizationManager` during the following scenarios:
+ * - Fetching the depth of an organization within the hierarchy.
+ * - Retrieving ancestor organization IDs.
+ *
+ * @param aggregationStrategy The aggregation strategy used for resolving resources.
+ * @throws Exception If an unexpected error occurs.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetOrgLevelResourcesFromOrgHierarchyWhenServerErrorOccurs(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ when(organizationManager.getOrganizationDepthInHierarchy(anyString()))
+ .thenThrow(OrganizationManagementServerException.class);
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeOrgLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID));
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeOrgLevelResourceResolver(aggregationStrategy, L1_ORG_ID));
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeOrgLevelResourceResolver(aggregationStrategy, L2_ORG_ID));
+
+ when(organizationManager.getAncestorOrganizationIds(anyString()))
+ .thenThrow(OrganizationManagementServerException.class);
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeOrgLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID));
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeOrgLevelResourceResolver(aggregationStrategy, L1_ORG_ID));
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeOrgLevelResourceResolver(aggregationStrategy, L2_ORG_ID));
+ }
+
+ /**
+ * Tests the behavior of the OrgResourceResolverService when server-side errors occur during
+ * application hierarchy traversal within an organization.
+ *
+ * This test simulates server-side exceptions thrown by the following:
+ * - The `applicationManagementService` while retrieving ancestor application IDs.
+ * - The `organizationManager` while retrieving ancestor organization IDs.
+ *
+ * @param aggregationStrategy The aggregation strategy used for resolving resources.
+ * @throws Exception If an unexpected error occurs.
+ */
+ @Test(dataProvider = "AggregationStrategyDataProvider")
+ public void testGetAppLevelResourcesFromOrgHierarchyWhenServerErrorOccurs(
+ AggregationStrategy aggregationStrategy) throws Exception {
+
+ when(applicationManagementService.getAncestorAppIds(anyString(), anyString()))
+ .thenThrow(IdentityApplicationManagementException.class);
+
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID));
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID));
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID));
+
+ when(organizationManager.getAncestorOrganizationIds(anyString()))
+ .thenThrow(OrganizationManagementServerException.class);
+
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeAppLevelResourceResolver(aggregationStrategy, ROOT_ORG_ID, ROOT_APP_ID));
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeAppLevelResourceResolver(aggregationStrategy, L1_ORG_ID, L1_APP_ID));
+ assertThrows(OrgResourceHierarchyTraverseServerException.class,
+ () -> invokeAppLevelResourceResolver(aggregationStrategy, L2_ORG_ID, L2_APP_ID));
+ }
+
+ /**
+ * Mock the retrieval of ancestor organization IDs.
+ *
+ * @param orgIds Organization IDs.
+ * @throws OrganizationManagementException If an error occurs while retrieving ancestor organization IDs.
+ */
+ private void mockAncestorOrganizationRetrieval(List orgIds)
+ throws OrganizationManagementException {
+
+ List ancestorOrganizationIds = new ArrayList<>(orgIds);
+ when(organizationManager.getAncestorOrganizationIds(orgIds.get(0))).thenReturn(ancestorOrganizationIds);
+ }
+
+ /**
+ * Mock the retrieval of ancestor application IDs.
+ *
+ * @param orgIds Organization IDs.
+ * @param appIds Application IDs.
+ * @throws IdentityApplicationManagementException If an error occurs while retrieving ancestor application IDs.
+ */
+ private void mockAncestorApplicationRetrieval(List orgIds, List appIds)
+ throws IdentityApplicationManagementException {
+
+ Map ancestorAppIds = new HashMap<>();
+ for (String orgId : orgIds) {
+ ancestorAppIds.put(orgId, appIds.get(orgIds.indexOf(orgId)));
+ }
+
+ when(applicationManagementService.getAncestorAppIds(appIds.get(0), orgIds.get(0))).thenReturn(ancestorAppIds);
+ }
+
+ /**
+ * Add organization resources to the mock resource management service.
+ *
+ * @param orgIds Organization IDs.
+ * @return List of created organization resources.
+ */
+ private List addOrgResources(List orgIds) {
+
+ List createdOrgResources = new ArrayList<>();
+ int resourceId = 1;
+ for (String orgId : orgIds) {
+ MockResource orgResource = new MockResource(resourceId++, orgId + "Org Resource", orgId);
+ mockResourceManagementService.addOrgResource(orgResource);
+ createdOrgResources.add(orgResource);
+ }
+ return createdOrgResources;
+ }
+
+ /**
+ * Add application resources to the mock resource management service.
+ *
+ * @param orgIds Organization IDs.
+ * @param appIds Application IDs.
+ * @return List of created application resources.
+ */
+ private List addAppResources(List orgIds, List appIds) {
+
+ List createdAppResources = new ArrayList<>();
+ int resourceId = 1;
+ for (String orgId : orgIds) {
+ MockResource appResource =
+ new MockResource(resourceId++, orgId + "App Resource", orgId, appIds.get(orgIds.indexOf(orgId)));
+ mockResourceManagementService.addAppResource(appResource);
+ createdAppResources.add(appResource);
+ }
+ return createdAppResources;
+ }
+
+ /**
+ * Resource resolver function used for testing MergeAllAggregationStrategy.
+ *
+ * @param aggregatedResource Aggregated resource.
+ * @param newResource New resource.
+ * @return Merged resource.
+ */
+ private MockResource resourceMerger(MockResource aggregatedResource, MockResource newResource) {
+
+ if (aggregatedResource == null) {
+ return newResource;
+ }
+ return aggregatedResource;
+ }
+
+ /**
+ * Invoke the organization level resource resolver.
+ *
+ * @param aggregationStrategy Aggregation strategy.
+ * @param organizationId Organization ID.
+ * @return Resolved resource.
+ * @throws OrgResourceHierarchyTraverseException If an error occurs while resolving the resource.
+ */
+ private MockResource invokeOrgLevelResourceResolver(AggregationStrategy aggregationStrategy,
+ String organizationId)
+ throws OrgResourceHierarchyTraverseException {
+
+ return orgResourceResolverService.getResourcesFromOrgHierarchy(
+ organizationId,
+ orgId -> {
+ MockResource resource = mockResourceManagementService.getOrgResource(orgId);
+ return Optional.ofNullable(resource);
+ },
+ aggregationStrategy);
+ }
+
+ /**
+ * Invoke the application level resource resolver.
+ *
+ * @param aggregationStrategy Aggregation strategy.
+ * @param organizationId Organization ID.
+ * @param applicationId Application ID.
+ * @return Resolved resource.
+ * @throws OrgResourceHierarchyTraverseException If an error occurs while resolving the resource.
+ */
+ private MockResource invokeAppLevelResourceResolver(AggregationStrategy aggregationStrategy,
+ String organizationId, String applicationId)
+ throws OrgResourceHierarchyTraverseException {
+
+ return orgResourceResolverService.getResourcesFromOrgHierarchy(
+ organizationId,
+ applicationId,
+ (orgId, appId) -> {
+ MockResource resource = mockResourceManagementService.getAppResource(orgId, appId);
+ return Optional.ofNullable(resource);
+ },
+ aggregationStrategy);
+ }
+
+ /**
+ * Assert the resolved resource with the actual resource.
+ *
+ * @param resolvedResource Resolved resource.
+ * @param actualResource Actual resource.
+ */
+ private void assertResolvedResponse(MockResource resolvedResource, MockResource actualResource) {
+
+ assertNotNull(resolvedResource);
+ assertEquals(resolvedResource.getId(), actualResource.getId());
+ assertEquals(resolvedResource.getResourceName(), actualResource.getResourceName());
+ assertEquals(resolvedResource.getOrgId(), actualResource.getOrgId());
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/mock/resource/impl/MockResourceManagementService.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/mock/resource/impl/MockResourceManagementService.java
new file mode 100644
index 000000000..814b010f5
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/mock/resource/impl/MockResourceManagementService.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.mock.resource.impl;
+
+import org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service.mock.resource.impl.model.MockResource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Service for managing mocked resources at both organization and application levels.
+ *
+ * This class maintains separate mappings for organization-level and application-level
+ * resources, providing methods to add and retrieve resources. These mappings simulate a database,
+ * offering a simple in-memory representation for resource storage and retrieval operations.
+ *
+ * This implementation is intended for testing or mocking purposes and simulates a resource management system.
+ */
+public class MockResourceManagementService {
+
+ private final Map orgResources;
+ private final Map appResources;
+
+ /**
+ * Initializes the mock resource management service with empty resource mappings
+ * for both organization-level and application-level resources.
+ */
+ public MockResourceManagementService() {
+
+ this.orgResources = new HashMap<>();
+ this.appResources = new HashMap<>();
+ }
+
+ /**
+ * Adds an organization-level resource to the management service.
+ *
+ * @param orgResource The resource to be added. The resource's organization ID
+ * ({@link MockResource#getOrgId}) is used as the key for storage.
+ */
+ public void addOrgResource(MockResource orgResource) {
+
+ orgResources.put(orgResource.getOrgId(), orgResource);
+ }
+
+ /**
+ * Retrieves an organization-level resource by its organization ID.
+ *
+ * @param orgId The unique identifier of the organization.
+ * @return The resource associated with the given organization ID, or {@code null}
+ * if no resource exists for the provided ID.
+ */
+ public MockResource getOrgResource(String orgId) {
+
+ return orgResources.get(orgId);
+ }
+
+ /**
+ * Adds an application-level resource to the management service.
+ *
+ * @param appResource The resource to be added. The key for storage is derived
+ * by concatenating the resource's organization ID and
+ * application ID ({@link MockResource#getAppId}), separated by a colon.
+ */
+ public void addAppResource(MockResource appResource) {
+
+ appResources.put(appResource.getOrgId() + ":" + appResource.getAppId(), appResource);
+ }
+
+ /**
+ * Retrieves an application-level resource by organization ID and application ID.
+ *
+ * If no application-level resource is found for the given organization and application IDs,
+ * the method falls back to returning the corresponding organization-level resource, if available.
+ *
+ * @param orgId The unique identifier of the organization.
+ * @param appId The unique identifier of the application within the organization.
+ * @return The application-level resource if found; otherwise, the organization-level
+ * resource associated with the organization ID. Returns {@code null} if no matching
+ * resource is available at either level.
+ */
+ public MockResource getAppResource(String orgId, String appId) {
+
+ MockResource appResource = appResources.get(orgId + ":" + appId);
+ if (appResource == null) {
+ return orgResources.get(orgId);
+ }
+ return appResource;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/mock/resource/impl/model/MockResource.java b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/mock/resource/impl/model/MockResource.java
new file mode 100644
index 000000000..9b4cbb86e
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/java/org/wso2/carbon/identity/organization/resource/hierarchy/traverse/service/mock/resource/impl/model/MockResource.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2024, 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.organization.resource.hierarchy.traverse.service.mock.resource.impl.model;
+
+/**
+ * Represents a mocked resource in an organization or application hierarchy.
+ *
+ * This class models resources that are associated either at the organization level
+ * or at the application level within an organization. It includes identifiers and
+ * metadata such as resource ID, resource name, organization ID, and optionally,
+ * an application ID for application-specific resources.
+ */
+public class MockResource {
+
+ private int id;
+ private String resourceName;
+ private String orgId;
+ private String appId;
+
+ /**
+ * Constructs a mocked resource associated with an organization.
+ *
+ * @param id Unique identifier of the resource.
+ * @param resourceName Descriptive name of the resource.
+ * @param orgId Identifier of the organization the resource belongs to.
+ */
+ public MockResource(int id, String resourceName, String orgId) {
+
+ this.id = id;
+ this.resourceName = resourceName;
+ this.orgId = orgId;
+ }
+
+ /**
+ * Constructs a mocked resource associated with a specific application within an organization.
+ *
+ * @param id Unique identifier of the resource.
+ * @param resourceName Descriptive name of the resource.
+ * @param orgId Identifier of the organization the resource belongs to.
+ * @param appId Identifier of the application the resource is associated with.
+ */
+ public MockResource(int id, String resourceName, String orgId, String appId) {
+
+ this(id, resourceName, orgId);
+ this.appId = appId;
+ }
+
+ /**
+ * Retrieves the unique identifier of the resource.
+ *
+ * @return The resource ID.
+ */
+ public int getId() {
+
+ return id;
+ }
+
+ /**
+ * Sets the unique identifier of the resource.
+ *
+ * @param id The resource ID to set.
+ */
+ public void setId(int id) {
+
+ this.id = id;
+ }
+
+ /**
+ * Retrieves the name of the resource.
+ *
+ * @return The resource name.
+ */
+ public String getResourceName() {
+
+ return resourceName;
+ }
+
+ /**
+ * Sets the name of the resource.
+ *
+ * @param resourceName The name to assign to the resource.
+ */
+ public void setResourceName(String resourceName) {
+
+ this.resourceName = resourceName;
+ }
+
+ /**
+ * Retrieves the organization ID associated with the resource.
+ *
+ * @return The organization ID.
+ */
+ public String getOrgId() {
+
+ return orgId;
+ }
+
+ /**
+ * Sets the organization ID associated with the resource.
+ *
+ * @param orgId The organization ID to set.
+ */
+ public void setOrgId(String orgId) {
+
+ this.orgId = orgId;
+ }
+
+ /**
+ * Retrieves the application ID associated with the resource.
+ *
+ * @return The application ID, or {@code null} if the resource is not associated with a specific application.
+ */
+ public String getAppId() {
+
+ return appId;
+ }
+
+ /**
+ * Sets the application ID associated with the resource.
+ *
+ * @param appId The application ID to set.
+ */
+ public void setAppId(String appId) {
+
+ this.appId = appId;
+ }
+}
diff --git a/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/resources/testng.xml b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/resources/testng.xml
new file mode 100644
index 000000000..539837328
--- /dev/null
+++ b/components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service/src/test/resources/testng.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/features/org.wso2.carbon.identity.organization.management.server.feature/pom.xml b/features/org.wso2.carbon.identity.organization.management.server.feature/pom.xml
index ea5333ebc..49690c6e0 100644
--- a/features/org.wso2.carbon.identity.organization.management.server.feature/pom.xml
+++ b/features/org.wso2.carbon.identity.organization.management.server.feature/pom.xml
@@ -83,6 +83,10 @@
org.wso2.carbon.identity.organization.managementorg.wso2.carbon.identity.organization.discovery.service
+
+ org.wso2.carbon.identity.organization.management
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service
+
@@ -120,6 +124,7 @@
org.wso2.carbon.identity.organization.management:org.wso2.carbon.identity.organization.user.invitation.managementorg.wso2.carbon.identity.organization.management:org.wso2.carbon.identity.organization.config.serviceorg.wso2.carbon.identity.organization.management:org.wso2.carbon.identity.organization.discovery.service
+ org.wso2.carbon.identity.organization.management:org.wso2.carbon.identity.organization.resource.hierarchy.traverse.serviceorg.wso2.carbon.core:compatible:${carbon.kernel.feature.version}
diff --git a/pom.xml b/pom.xml
index f8b771c35..dbe7c8e9d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,7 @@
components/org.wso2.carbon.identity.organization.config.servicecomponents/org.wso2.carbon.identity.organization.discovery.servicecomponents/org.wso2.carbon.identity.organization.management.organization.user.sharing
+ components/org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service
@@ -186,6 +187,12 @@
${project.version}provided
+
+ org.wso2.carbon.identity.organization.management
+ org.wso2.carbon.identity.organization.resource.hierarchy.traverse.service
+ ${project.version}
+ provided
+ org.wso2.carbon.identity.framework