This is an automated email from the ASF dual-hosted git repository. yasith pushed a commit to branch feat/airavata-service-layer in repository https://gitbox.apache.org/repos/asf/airavata.git
commit eb24efe2f0a5d8d962f1a2a3cdf35e876e937ed0 Author: yasithdev <[email protected]> AuthorDate: Thu Mar 26 10:36:11 2026 -0500 feat: add ApplicationCatalogService Extracts application module, deployment, and interface logic from AiravataServerHandler into a dedicated service layer class, including sharing/permission logic for deployments. Adds 15 unit tests covering all three sub-domains. --- .../appcatalog/ApplicationCatalogService.java | 523 +++++++++++++++++++++ .../appcatalog/ApplicationCatalogServiceTest.java | 231 +++++++++ 2 files changed, 754 insertions(+) diff --git a/airavata-api/src/main/java/org/apache/airavata/service/appcatalog/ApplicationCatalogService.java b/airavata-api/src/main/java/org/apache/airavata/service/appcatalog/ApplicationCatalogService.java new file mode 100644 index 0000000000..8872937927 --- /dev/null +++ b/airavata-api/src/main/java/org/apache/airavata/service/appcatalog/ApplicationCatalogService.java @@ -0,0 +1,523 @@ +package org.apache.airavata.service.appcatalog; + +import org.apache.airavata.common.utils.ServerSettings; +import org.apache.airavata.credential.store.server.CredentialStoreServerHandler; +import org.apache.airavata.model.appcatalog.appdeployment.ApplicationDeploymentDescription; +import org.apache.airavata.model.appcatalog.appdeployment.ApplicationModule; +import org.apache.airavata.model.appcatalog.appinterface.ApplicationInterfaceDescription; +import org.apache.airavata.model.appcatalog.gatewaygroups.GatewayGroups; +import org.apache.airavata.model.appcatalog.groupresourceprofile.GroupComputeResourcePreference; +import org.apache.airavata.model.appcatalog.groupresourceprofile.GroupResourceProfile; +import org.apache.airavata.model.application.io.InputDataObjectType; +import org.apache.airavata.model.application.io.OutputDataObjectType; +import org.apache.airavata.model.commons.airavata_commonsConstants; +import org.apache.airavata.model.group.ResourcePermissionType; +import org.apache.airavata.model.group.ResourceType; +import org.apache.airavata.registry.api.service.handler.RegistryServerHandler; +import org.apache.airavata.service.context.RequestContext; +import org.apache.airavata.service.exception.ServiceAuthorizationException; +import org.apache.airavata.service.exception.ServiceException; +import org.apache.airavata.service.security.GatewayGroupsInitializer; +import org.apache.airavata.sharing.registry.models.Entity; +import org.apache.airavata.sharing.registry.models.EntitySearchField; +import org.apache.airavata.sharing.registry.models.PermissionType; +import org.apache.airavata.sharing.registry.models.SearchCondition; +import org.apache.airavata.sharing.registry.models.SearchCriteria; +import org.apache.airavata.sharing.registry.server.SharingRegistryServerHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class ApplicationCatalogService { + + private static final Logger logger = LoggerFactory.getLogger(ApplicationCatalogService.class); + + private final RegistryServerHandler registryHandler; + private final SharingRegistryServerHandler sharingHandler; + private final CredentialStoreServerHandler credentialHandler; + + public ApplicationCatalogService( + RegistryServerHandler registryHandler, + SharingRegistryServerHandler sharingHandler, + CredentialStoreServerHandler credentialHandler) { + this.registryHandler = registryHandler; + this.sharingHandler = sharingHandler; + this.credentialHandler = credentialHandler; + } + + // ------------------------------------------------------------------------- + // Application Modules + // ------------------------------------------------------------------------- + + public String registerApplicationModule(RequestContext ctx, String gatewayId, ApplicationModule applicationModule) + throws ServiceException { + try { + return registryHandler.registerApplicationModule(gatewayId, applicationModule); + } catch (Exception e) { + throw new ServiceException("Error while adding application module: " + e.getMessage(), e); + } + } + + public ApplicationModule getApplicationModule(RequestContext ctx, String appModuleId) throws ServiceException { + try { + return registryHandler.getApplicationModule(appModuleId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving application module: " + e.getMessage(), e); + } + } + + public boolean updateApplicationModule(RequestContext ctx, String appModuleId, ApplicationModule applicationModule) + throws ServiceException { + try { + return registryHandler.updateApplicationModule(appModuleId, applicationModule); + } catch (Exception e) { + throw new ServiceException("Error while updating application module: " + e.getMessage(), e); + } + } + + public List<ApplicationModule> getAllAppModules(RequestContext ctx, String gatewayId) throws ServiceException { + try { + return registryHandler.getAllAppModules(gatewayId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving all application modules: " + e.getMessage(), e); + } + } + + public List<ApplicationModule> getAccessibleAppModules(RequestContext ctx, String gatewayId) + throws ServiceException { + try { + List<String> accessibleAppDeploymentIds = new ArrayList<>(); + if (isSharingEnabled()) { + List<SearchCriteria> sharingFilters = new ArrayList<>(); + SearchCriteria entityTypeFilter = new SearchCriteria(); + entityTypeFilter.setSearchField(EntitySearchField.ENTITY_TYPE_ID); + entityTypeFilter.setSearchCondition(SearchCondition.EQUAL); + entityTypeFilter.setValue(gatewayId + ":" + ResourceType.APPLICATION_DEPLOYMENT.name()); + sharingFilters.add(entityTypeFilter); + SearchCriteria permissionTypeFilter = new SearchCriteria(); + permissionTypeFilter.setSearchField(EntitySearchField.PERMISSION_TYPE_ID); + permissionTypeFilter.setSearchCondition(SearchCondition.EQUAL); + permissionTypeFilter.setValue(gatewayId + ":" + ResourcePermissionType.READ); + sharingFilters.add(permissionTypeFilter); + sharingHandler + .searchEntities( + ctx.getGatewayId(), + ctx.getUserId() + "@" + gatewayId, + sharingFilters, + 0, + -1) + .forEach(a -> accessibleAppDeploymentIds.add(a.getEntityId())); + } + List<String> accessibleComputeResourceIds = getAccessibleComputeResourceIds(ctx, gatewayId); + return registryHandler.getAccessibleAppModules( + gatewayId, accessibleAppDeploymentIds, accessibleComputeResourceIds); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while retrieving accessible application modules: " + e.getMessage(), e); + } + } + + public boolean deleteApplicationModule(RequestContext ctx, String appModuleId) throws ServiceException { + try { + return registryHandler.deleteApplicationModule(appModuleId); + } catch (Exception e) { + throw new ServiceException("Error while deleting application module: " + e.getMessage(), e); + } + } + + // ------------------------------------------------------------------------- + // Application Deployments + // ------------------------------------------------------------------------- + + public String registerApplicationDeployment( + RequestContext ctx, String gatewayId, ApplicationDeploymentDescription applicationDeployment) + throws ServiceException { + try { + String result = registryHandler.registerApplicationDeployment(gatewayId, applicationDeployment); + Entity entity = new Entity(); + entity.setEntityId(result); + entity.setDomainId(gatewayId); + entity.setEntityTypeId(gatewayId + ":" + ResourceType.APPLICATION_DEPLOYMENT.name()); + entity.setOwnerId(ctx.getUserId() + "@" + gatewayId); + entity.setName(result); + entity.setDescription(applicationDeployment.getAppDeploymentDescription()); + sharingHandler.createEntity(entity); + shareEntityWithAdminGatewayGroups(entity); + return result; + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while adding application deployment: " + e.getMessage(), e); + } + } + + public ApplicationDeploymentDescription getApplicationDeployment(RequestContext ctx, String appDeploymentId) + throws ServiceException { + try { + if (isSharingEnabled()) { + if (!userHasAccess(ctx, appDeploymentId, ResourcePermissionType.READ)) { + throw new ServiceAuthorizationException( + "User does not have access to application deployment " + appDeploymentId); + } + } + return registryHandler.getApplicationDeployment(appDeploymentId); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while retrieving application deployment: " + e.getMessage(), e); + } + } + + public boolean updateApplicationDeployment( + RequestContext ctx, String appDeploymentId, ApplicationDeploymentDescription applicationDeployment) + throws ServiceException { + try { + if (isSharingEnabled()) { + if (!userHasAccess(ctx, appDeploymentId, ResourcePermissionType.WRITE)) { + throw new ServiceAuthorizationException( + "User does not have WRITE access to application deployment " + appDeploymentId); + } + } + return registryHandler.updateApplicationDeployment(appDeploymentId, applicationDeployment); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while updating application deployment: " + e.getMessage(), e); + } + } + + public boolean deleteApplicationDeployment(RequestContext ctx, String appDeploymentId) throws ServiceException { + try { + if (!userHasAccess(ctx, appDeploymentId, ResourcePermissionType.WRITE)) { + throw new ServiceAuthorizationException( + "User does not have WRITE access to application deployment " + appDeploymentId); + } + boolean result = registryHandler.deleteApplicationDeployment(appDeploymentId); + sharingHandler.deleteEntity(ctx.getGatewayId(), appDeploymentId); + return result; + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while deleting application deployment: " + e.getMessage(), e); + } + } + + public List<ApplicationDeploymentDescription> getAllApplicationDeployments(RequestContext ctx, String gatewayId) + throws ServiceException { + return getAccessibleApplicationDeployments(ctx, gatewayId, ResourcePermissionType.READ); + } + + public List<ApplicationDeploymentDescription> getAccessibleApplicationDeployments( + RequestContext ctx, String gatewayId, ResourcePermissionType permissionType) + throws ServiceException { + try { + List<String> accessibleAppDeploymentIds = new ArrayList<>(); + if (isSharingEnabled()) { + List<SearchCriteria> sharingFilters = new ArrayList<>(); + SearchCriteria entityTypeFilter = new SearchCriteria(); + entityTypeFilter.setSearchField(EntitySearchField.ENTITY_TYPE_ID); + entityTypeFilter.setSearchCondition(SearchCondition.EQUAL); + entityTypeFilter.setValue(gatewayId + ":" + ResourceType.APPLICATION_DEPLOYMENT.name()); + sharingFilters.add(entityTypeFilter); + SearchCriteria permissionTypeFilter = new SearchCriteria(); + permissionTypeFilter.setSearchField(EntitySearchField.PERMISSION_TYPE_ID); + permissionTypeFilter.setSearchCondition(SearchCondition.EQUAL); + permissionTypeFilter.setValue(gatewayId + ":" + permissionType.name()); + sharingFilters.add(permissionTypeFilter); + sharingHandler + .searchEntities( + ctx.getGatewayId(), + ctx.getUserId() + "@" + gatewayId, + sharingFilters, + 0, + -1) + .forEach(a -> accessibleAppDeploymentIds.add(a.getEntityId())); + } + List<String> accessibleComputeResourceIds = getAccessibleComputeResourceIds(ctx, gatewayId); + return registryHandler.getAccessibleApplicationDeployments( + gatewayId, accessibleAppDeploymentIds, accessibleComputeResourceIds); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while retrieving application deployments: " + e.getMessage(), e); + } + } + + public List<String> getAppModuleDeployedResources(RequestContext ctx, String appModuleId) throws ServiceException { + try { + return registryHandler.getAppModuleDeployedResources(appModuleId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving application deployments: " + e.getMessage(), e); + } + } + + public List<ApplicationDeploymentDescription> getApplicationDeploymentsForAppModuleAndGroupResourceProfile( + RequestContext ctx, String appModuleId, String groupResourceProfileId) + throws ServiceException { + try { + if (!userHasAccess(ctx, groupResourceProfileId, ResourcePermissionType.READ)) { + throw new ServiceAuthorizationException( + "User is not authorized to access Group Resource Profile " + groupResourceProfileId); + } + GroupResourceProfile groupResourceProfile = + registryHandler.getGroupResourceProfile(groupResourceProfileId); + List<String> accessibleComputeResourceIds = new ArrayList<>(); + for (GroupComputeResourcePreference pref : groupResourceProfile.getComputePreferences()) { + accessibleComputeResourceIds.add(pref.getComputeResourceId()); + } + + List<String> accessibleAppDeploymentIds = new ArrayList<>(); + List<SearchCriteria> sharingFilters = new ArrayList<>(); + SearchCriteria entityTypeFilter = new SearchCriteria(); + entityTypeFilter.setSearchField(EntitySearchField.ENTITY_TYPE_ID); + entityTypeFilter.setSearchCondition(SearchCondition.EQUAL); + entityTypeFilter.setValue(ctx.getGatewayId() + ":" + ResourceType.APPLICATION_DEPLOYMENT.name()); + sharingFilters.add(entityTypeFilter); + SearchCriteria permissionTypeFilter = new SearchCriteria(); + permissionTypeFilter.setSearchField(EntitySearchField.PERMISSION_TYPE_ID); + permissionTypeFilter.setSearchCondition(SearchCondition.EQUAL); + permissionTypeFilter.setValue(ctx.getGatewayId() + ":" + ResourcePermissionType.READ); + sharingFilters.add(permissionTypeFilter); + sharingHandler + .searchEntities( + ctx.getGatewayId(), + ctx.getUserId() + "@" + ctx.getGatewayId(), + sharingFilters, + 0, + -1) + .forEach(a -> accessibleAppDeploymentIds.add(a.getEntityId())); + + return registryHandler.getAccessibleApplicationDeploymentsForAppModule( + ctx.getGatewayId(), appModuleId, accessibleAppDeploymentIds, accessibleComputeResourceIds); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException( + "Error while retrieving application deployments for module and group resource profile: " + + e.getMessage(), + e); + } + } + + // ------------------------------------------------------------------------- + // Application Interfaces + // ------------------------------------------------------------------------- + + public String registerApplicationInterface( + RequestContext ctx, String gatewayId, ApplicationInterfaceDescription applicationInterface) + throws ServiceException { + try { + return registryHandler.registerApplicationInterface(gatewayId, applicationInterface); + } catch (Exception e) { + throw new ServiceException("Error while adding application interface: " + e.getMessage(), e); + } + } + + public String cloneApplicationInterface( + RequestContext ctx, String existingAppInterfaceId, String newApplicationName, String gatewayId) + throws ServiceException { + try { + ApplicationInterfaceDescription existingInterface = + registryHandler.getApplicationInterface(existingAppInterfaceId); + if (existingInterface == null) { + throw new ServiceException( + "Provided application interface does not exist: " + existingAppInterfaceId); + } + existingInterface.setApplicationName(newApplicationName); + existingInterface.setApplicationInterfaceId(airavata_commonsConstants.DEFAULT_ID); + String interfaceId = registryHandler.registerApplicationInterface(gatewayId, existingInterface); + logger.debug("Cloned application interface {} for gateway {}", existingAppInterfaceId, gatewayId); + return interfaceId; + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while cloning application interface: " + e.getMessage(), e); + } + } + + public ApplicationInterfaceDescription getApplicationInterface(RequestContext ctx, String appInterfaceId) + throws ServiceException { + try { + return registryHandler.getApplicationInterface(appInterfaceId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving application interface: " + e.getMessage(), e); + } + } + + public boolean updateApplicationInterface( + RequestContext ctx, String appInterfaceId, ApplicationInterfaceDescription applicationInterface) + throws ServiceException { + try { + return registryHandler.updateApplicationInterface(appInterfaceId, applicationInterface); + } catch (Exception e) { + throw new ServiceException("Error while updating application interface: " + e.getMessage(), e); + } + } + + public boolean deleteApplicationInterface(RequestContext ctx, String appInterfaceId) throws ServiceException { + try { + return registryHandler.deleteApplicationInterface(appInterfaceId); + } catch (Exception e) { + throw new ServiceException("Error while deleting application interface: " + e.getMessage(), e); + } + } + + public Map<String, String> getAllApplicationInterfaceNames(RequestContext ctx, String gatewayId) + throws ServiceException { + try { + return registryHandler.getAllApplicationInterfaceNames(gatewayId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving application interface names: " + e.getMessage(), e); + } + } + + public List<ApplicationInterfaceDescription> getAllApplicationInterfaces(RequestContext ctx, String gatewayId) + throws ServiceException { + try { + return registryHandler.getAllApplicationInterfaces(gatewayId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving application interfaces: " + e.getMessage(), e); + } + } + + public List<InputDataObjectType> getApplicationInputs(RequestContext ctx, String appInterfaceId) + throws ServiceException { + try { + return registryHandler.getApplicationInputs(appInterfaceId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving application inputs: " + e.getMessage(), e); + } + } + + public List<OutputDataObjectType> getApplicationOutputs(RequestContext ctx, String appInterfaceId) + throws ServiceException { + try { + return registryHandler.getApplicationOutputs(appInterfaceId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving application outputs: " + e.getMessage(), e); + } + } + + public Map<String, String> getAvailableAppInterfaceComputeResources(RequestContext ctx, String appInterfaceId) + throws ServiceException { + try { + return registryHandler.getAvailableAppInterfaceComputeResources(appInterfaceId); + } catch (Exception e) { + throw new ServiceException( + "Error while retrieving available compute resources for interface: " + e.getMessage(), e); + } + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + private boolean isSharingEnabled() { + try { + return ServerSettings.isEnableSharing(); + } catch (Exception e) { + return false; + } + } + + private boolean userHasAccess(RequestContext ctx, String entityId, ResourcePermissionType permissionType) { + final String domainId = ctx.getGatewayId(); + final String userId = ctx.getUserId() + "@" + domainId; + try { + final boolean hasOwnerAccess = sharingHandler.userHasAccess( + domainId, userId, entityId, domainId + ":" + ResourcePermissionType.OWNER); + if (permissionType.equals(ResourcePermissionType.WRITE)) { + return hasOwnerAccess + || sharingHandler.userHasAccess( + domainId, userId, entityId, domainId + ":" + ResourcePermissionType.WRITE); + } else if (permissionType.equals(ResourcePermissionType.READ)) { + return hasOwnerAccess + || sharingHandler.userHasAccess( + domainId, userId, entityId, domainId + ":" + ResourcePermissionType.READ); + } else if (permissionType.equals(ResourcePermissionType.MANAGE_SHARING)) { + return hasOwnerAccess + || sharingHandler.userHasAccess( + domainId, userId, entityId, domainId + ":" + ResourcePermissionType.MANAGE_SHARING); + } else if (permissionType.equals(ResourcePermissionType.OWNER)) { + return hasOwnerAccess; + } + return false; + } catch (Exception e) { + throw new RuntimeException("Unable to check if user has access", e); + } + } + + private void shareEntityWithAdminGatewayGroups(Entity entity) throws Exception { + final String domainId = entity.getDomainId(); + GatewayGroups gatewayGroups = retrieveGatewayGroups(domainId); + createManageSharingPermissionTypeIfMissing(domainId); + sharingHandler.shareEntityWithGroups( + domainId, + entity.getEntityId(), + Arrays.asList(gatewayGroups.getAdminsGroupId()), + domainId + ":MANAGE_SHARING", + true); + sharingHandler.shareEntityWithGroups( + domainId, + entity.getEntityId(), + Arrays.asList(gatewayGroups.getAdminsGroupId()), + domainId + ":WRITE", + true); + sharingHandler.shareEntityWithGroups( + domainId, + entity.getEntityId(), + Arrays.asList(gatewayGroups.getAdminsGroupId(), gatewayGroups.getReadOnlyAdminsGroupId()), + domainId + ":READ", + true); + } + + private GatewayGroups retrieveGatewayGroups(String gatewayId) throws Exception { + if (registryHandler.isGatewayGroupsExists(gatewayId)) { + return registryHandler.getGatewayGroups(gatewayId); + } else { + return GatewayGroupsInitializer.initializeGatewayGroups(gatewayId); + } + } + + private void createManageSharingPermissionTypeIfMissing(String domainId) throws Exception { + String permissionTypeId = domainId + ":MANAGE_SHARING"; + if (!sharingHandler.isPermissionExists(domainId, permissionTypeId)) { + PermissionType permissionType = new PermissionType(); + permissionType.setPermissionTypeId(permissionTypeId); + permissionType.setDomainId(domainId); + permissionType.setName("MANAGE_SHARING"); + sharingHandler.createPermissionType(permissionType); + } + } + + private List<String> getAccessibleComputeResourceIds(RequestContext ctx, String gatewayId) throws Exception { + List<String> accessibleComputeResourceIds = new ArrayList<>(); + if (isSharingEnabled()) { + List<SearchCriteria> filters = new ArrayList<>(); + SearchCriteria searchCriteria = new SearchCriteria(); + searchCriteria.setSearchField(EntitySearchField.ENTITY_TYPE_ID); + searchCriteria.setSearchCondition(SearchCondition.EQUAL); + searchCriteria.setValue(gatewayId + ":" + ResourceType.GROUP_RESOURCE_PROFILE.name()); + filters.add(searchCriteria); + List<String> accessibleGroupResProfileIds = new ArrayList<>(); + sharingHandler + .searchEntities(ctx.getGatewayId(), ctx.getUserId() + "@" + gatewayId, filters, 0, -1) + .forEach(p -> accessibleGroupResProfileIds.add(p.getEntityId())); + List<GroupResourceProfile> groupResourceProfiles = + registryHandler.getGroupResourceList(gatewayId, accessibleGroupResProfileIds); + for (GroupResourceProfile groupResourceProfile : groupResourceProfiles) { + List<GroupComputeResourcePreference> groupComputeResourcePreferenceList = + groupResourceProfile.getComputePreferences(); + for (GroupComputeResourcePreference pref : groupComputeResourcePreferenceList) { + accessibleComputeResourceIds.add(pref.getComputeResourceId()); + } + } + } + return accessibleComputeResourceIds; + } +} diff --git a/airavata-api/src/test/java/org/apache/airavata/service/appcatalog/ApplicationCatalogServiceTest.java b/airavata-api/src/test/java/org/apache/airavata/service/appcatalog/ApplicationCatalogServiceTest.java new file mode 100644 index 0000000000..4a9c6257c1 --- /dev/null +++ b/airavata-api/src/test/java/org/apache/airavata/service/appcatalog/ApplicationCatalogServiceTest.java @@ -0,0 +1,231 @@ +package org.apache.airavata.service.appcatalog; + +import org.apache.airavata.credential.store.server.CredentialStoreServerHandler; +import org.apache.airavata.model.appcatalog.appdeployment.ApplicationDeploymentDescription; +import org.apache.airavata.model.appcatalog.appdeployment.ApplicationModule; +import org.apache.airavata.model.appcatalog.appinterface.ApplicationInterfaceDescription; +import org.apache.airavata.model.application.io.InputDataObjectType; +import org.apache.airavata.model.application.io.OutputDataObjectType; +import org.apache.airavata.model.group.ResourcePermissionType; +import org.apache.airavata.registry.api.service.handler.RegistryServerHandler; +import org.apache.airavata.service.context.RequestContext; +import org.apache.airavata.service.exception.ServiceAuthorizationException; +import org.apache.airavata.service.exception.ServiceException; +import org.apache.airavata.sharing.registry.server.SharingRegistryServerHandler; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class ApplicationCatalogServiceTest { + + @Mock RegistryServerHandler registryHandler; + @Mock SharingRegistryServerHandler sharingHandler; + @Mock CredentialStoreServerHandler credentialHandler; + + ApplicationCatalogService service; + RequestContext ctx; + + @BeforeEach + void setUp() { + service = new ApplicationCatalogService(registryHandler, sharingHandler, credentialHandler); + ctx = new RequestContext("testUser", "testGateway", "token123", + Map.of("userName", "testUser", "gatewayId", "testGateway")); + } + + // ------------------------------------------------------------------------- + // Application Modules + // ------------------------------------------------------------------------- + + @Test + void registerApplicationModule_delegatesToRegistry() throws Exception { + ApplicationModule module = new ApplicationModule(); + module.setAppModuleName("Gaussian"); + when(registryHandler.registerApplicationModule("testGateway", module)).thenReturn("mod-1"); + + String result = service.registerApplicationModule(ctx, "testGateway", module); + + assertEquals("mod-1", result); + verify(registryHandler).registerApplicationModule("testGateway", module); + } + + @Test + void getApplicationModule_delegatesToRegistry() throws Exception { + ApplicationModule module = new ApplicationModule(); + module.setAppModuleId("mod-1"); + module.setAppModuleName("Gaussian"); + when(registryHandler.getApplicationModule("mod-1")).thenReturn(module); + + ApplicationModule result = service.getApplicationModule(ctx, "mod-1"); + + assertNotNull(result); + assertEquals("mod-1", result.getAppModuleId()); + verify(registryHandler).getApplicationModule("mod-1"); + } + + @Test + void updateApplicationModule_delegatesToRegistry() throws Exception { + ApplicationModule module = new ApplicationModule(); + module.setAppModuleId("mod-1"); + when(registryHandler.updateApplicationModule("mod-1", module)).thenReturn(true); + + boolean result = service.updateApplicationModule(ctx, "mod-1", module); + + assertTrue(result); + verify(registryHandler).updateApplicationModule("mod-1", module); + } + + @Test + void deleteApplicationModule_delegatesToRegistry() throws Exception { + when(registryHandler.deleteApplicationModule("mod-1")).thenReturn(true); + + boolean result = service.deleteApplicationModule(ctx, "mod-1"); + + assertTrue(result); + verify(registryHandler).deleteApplicationModule("mod-1"); + } + + @Test + void getAllAppModules_delegatesToRegistry() throws Exception { + ApplicationModule m1 = new ApplicationModule(); + m1.setAppModuleId("mod-1"); + ApplicationModule m2 = new ApplicationModule(); + m2.setAppModuleId("mod-2"); + when(registryHandler.getAllAppModules("testGateway")).thenReturn(List.of(m1, m2)); + + List<ApplicationModule> result = service.getAllAppModules(ctx, "testGateway"); + + assertEquals(2, result.size()); + verify(registryHandler).getAllAppModules("testGateway"); + } + + // ------------------------------------------------------------------------- + // Application Deployments (with sharing logic) + // ------------------------------------------------------------------------- + + @Test + void getApplicationDeployment_returnsDeploymentWhenSharingDisabled() throws Exception { + // ServerSettings.isEnableSharing() returns false in test env, so no sharing check is done + ApplicationDeploymentDescription dep = new ApplicationDeploymentDescription(); + dep.setAppDeploymentId("dep-1"); + when(registryHandler.getApplicationDeployment("dep-1")).thenReturn(dep); + + ApplicationDeploymentDescription result = service.getApplicationDeployment(ctx, "dep-1"); + + assertNotNull(result); + assertEquals("dep-1", result.getAppDeploymentId()); + verify(registryHandler).getApplicationDeployment("dep-1"); + } + + @Test + void updateApplicationDeployment_delegatesToRegistry() throws Exception { + ApplicationDeploymentDescription dep = new ApplicationDeploymentDescription(); + dep.setAppDeploymentId("dep-1"); + when(registryHandler.updateApplicationDeployment("dep-1", dep)).thenReturn(true); + + // sharing disabled in test env — no access check + boolean result = service.updateApplicationDeployment(ctx, "dep-1", dep); + + assertTrue(result); + verify(registryHandler).updateApplicationDeployment("dep-1", dep); + } + + @Test + void getAppModuleDeployedResources_delegatesToRegistry() throws Exception { + when(registryHandler.getAppModuleDeployedResources("mod-1")).thenReturn(List.of("dep-1", "dep-2")); + + List<String> result = service.getAppModuleDeployedResources(ctx, "mod-1"); + + assertEquals(2, result.size()); + verify(registryHandler).getAppModuleDeployedResources("mod-1"); + } + + // ------------------------------------------------------------------------- + // Application Interfaces + // ------------------------------------------------------------------------- + + @Test + void registerApplicationInterface_delegatesToRegistry() throws Exception { + ApplicationInterfaceDescription iface = new ApplicationInterfaceDescription(); + iface.setApplicationName("GaussianInterface"); + when(registryHandler.registerApplicationInterface("testGateway", iface)).thenReturn("iface-1"); + + String result = service.registerApplicationInterface(ctx, "testGateway", iface); + + assertEquals("iface-1", result); + verify(registryHandler).registerApplicationInterface("testGateway", iface); + } + + @Test + void getApplicationInterface_delegatesToRegistry() throws Exception { + ApplicationInterfaceDescription iface = new ApplicationInterfaceDescription(); + iface.setApplicationInterfaceId("iface-1"); + when(registryHandler.getApplicationInterface("iface-1")).thenReturn(iface); + + ApplicationInterfaceDescription result = service.getApplicationInterface(ctx, "iface-1"); + + assertNotNull(result); + assertEquals("iface-1", result.getApplicationInterfaceId()); + } + + @Test + void cloneApplicationInterface_throwsWhenSourceMissing() throws Exception { + when(registryHandler.getApplicationInterface("iface-old")).thenReturn(null); + + assertThrows(ServiceException.class, + () -> service.cloneApplicationInterface(ctx, "iface-old", "NewApp", "testGateway")); + } + + @Test + void cloneApplicationInterface_registersNewInterface() throws Exception { + ApplicationInterfaceDescription iface = new ApplicationInterfaceDescription(); + iface.setApplicationInterfaceId("iface-old"); + iface.setApplicationName("OldApp"); + when(registryHandler.getApplicationInterface("iface-old")).thenReturn(iface); + when(registryHandler.registerApplicationInterface(eq("testGateway"), any())).thenReturn("iface-new"); + + String result = service.cloneApplicationInterface(ctx, "iface-old", "NewApp", "testGateway"); + + assertEquals("iface-new", result); + } + + @Test + void deleteApplicationInterface_delegatesToRegistry() throws Exception { + when(registryHandler.deleteApplicationInterface("iface-1")).thenReturn(true); + + boolean result = service.deleteApplicationInterface(ctx, "iface-1"); + + assertTrue(result); + verify(registryHandler).deleteApplicationInterface("iface-1"); + } + + @Test + void getApplicationInputs_delegatesToRegistry() throws Exception { + List<InputDataObjectType> inputs = List.of(new InputDataObjectType()); + when(registryHandler.getApplicationInputs("iface-1")).thenReturn(inputs); + + List<InputDataObjectType> result = service.getApplicationInputs(ctx, "iface-1"); + + assertEquals(1, result.size()); + verify(registryHandler).getApplicationInputs("iface-1"); + } + + @Test + void getApplicationOutputs_delegatesToRegistry() throws Exception { + List<OutputDataObjectType> outputs = List.of(new OutputDataObjectType()); + when(registryHandler.getApplicationOutputs("iface-1")).thenReturn(outputs); + + List<OutputDataObjectType> result = service.getApplicationOutputs(ctx, "iface-1"); + + assertEquals(1, result.size()); + verify(registryHandler).getApplicationOutputs("iface-1"); + } +}
