diff --git a/service/dependencies.gradle b/service/dependencies.gradle index 5d7fd4be98..9c7f3a8d79 100644 --- a/service/dependencies.gradle +++ b/service/dependencies.gradle @@ -43,7 +43,8 @@ dependencies { implementation group: 'bio.terra', name: 'terra-cloud-resource-lib', version: "1.2.30-SNAPSHOT" // Terra Landing Zone Service - implementation ('bio.terra:terra-landing-zone-service:0.0.317-SNAPSHOT') + implementation ('bio.terra:terra-landing-zone-service:0.0.319-SNAPSHOT') + implementation ('bio.terra:landing-zone-service-client:0.0.319-SNAPSHOT') // Storage transfer service implementation group: 'com.google.apis', name: 'google-api-services-storagetransfer', version: 'v1-rev20230831-2.0.0' diff --git a/service/src/main/java/bio/terra/workspace/amalgam/AMALGAM_README.md b/service/src/main/java/bio/terra/workspace/amalgam/AMALGAM_README.md index 7725ad3cca..7a122bb2ea 100644 --- a/service/src/main/java/bio/terra/workspace/amalgam/AMALGAM_README.md +++ b/service/src/main/java/bio/terra/workspace/amalgam/AMALGAM_README.md @@ -1,5 +1,7 @@ # Amalgam Package +_NOTE_: This is a deprecated pattern and should not be used going forward. + Amalgam (noun) _a mixture or blend._ The WSM service app is configured to contain other Terra components. There are two reasons why we want to do this: diff --git a/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/AmalgamatedLandingZoneService.java b/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/AmalgamatedLandingZoneService.java new file mode 100644 index 0000000000..12ad1d4cae --- /dev/null +++ b/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/AmalgamatedLandingZoneService.java @@ -0,0 +1,197 @@ +package bio.terra.workspace.amalgam.landingzone.azure; + +import bio.terra.common.exception.ConflictException; +import bio.terra.common.iam.BearerToken; +import bio.terra.landingzone.library.landingzones.deployment.LandingZonePurpose; +import bio.terra.landingzone.service.landingzone.azure.LandingZoneService; +import bio.terra.landingzone.service.landingzone.azure.model.LandingZone; +import bio.terra.landingzone.service.landingzone.azure.model.LandingZoneRequest; +import bio.terra.workspace.common.utils.MapperUtils; +import bio.terra.workspace.generated.model.ApiAzureLandingZone; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneDefinition; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneDefinitionList; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneList; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneParameter; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneResourcesList; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneResourcesPurposeGroup; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneResult; +import bio.terra.workspace.generated.model.ApiCreateLandingZoneResult; +import bio.terra.workspace.generated.model.ApiDeleteAzureLandingZoneJobResult; +import bio.terra.workspace.generated.model.ApiDeleteAzureLandingZoneResult; +import bio.terra.workspace.generated.model.ApiResourceQuota; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * This is the "amalgamated" implementation of our landing zone service layer. It makes calls + * directly to the landing zone library rather than via an HTTP API. NOTE: This is a legacy class + * intended to ease the transition to the newer HTTP LZS service. + */ +@Deprecated +@Component +public class AmalgamatedLandingZoneService implements WorkspaceLandingZoneService { + + private final LandingApiClientTypeAdapter typeAdapter; + private final LandingZoneService landingZoneService; + + @Autowired + public AmalgamatedLandingZoneService(LandingZoneService landingZoneService) { + this.landingZoneService = landingZoneService; + this.typeAdapter = new LandingApiClientTypeAdapter(); + } + + @Override + public ApiCreateLandingZoneResult startLandingZoneCreationJob( + BearerToken bearerToken, + String jobId, + UUID landingZoneId, + String definition, + String version, + List parameters, + UUID billingProfileId, + String asyncResultEndpoint) { + + LandingZoneRequest landingZoneRequest = + LandingZoneRequest.builder() + .landingZoneId(landingZoneId) + .definition(definition) + .version(version) + .parameters(MapperUtils.LandingZoneMapper.landingZoneParametersFrom(parameters)) + .billingProfileId(billingProfileId) + .build(); + var result = + landingZoneService.startLandingZoneCreationJob( + bearerToken, jobId, landingZoneRequest, asyncResultEndpoint); + + return typeAdapter.toApiCreateLandingZoneResult(result); + } + + @Override + public ApiDeleteAzureLandingZoneResult startLandingZoneDeletionJob( + BearerToken bearerToken, String jobId, UUID landingZoneId, String resultEndpoint) { + var result = + landingZoneService.startLandingZoneDeletionJob( + bearerToken, jobId, landingZoneId, resultEndpoint); + return typeAdapter.toApiDeleteAzureLandingZoneResult(result); + } + + @Override + public ApiDeleteAzureLandingZoneJobResult getDeleteLandingZoneResult( + BearerToken bearerToken, UUID landingZoneId, String jobId) { + var response = landingZoneService.getAsyncDeletionJobResult(bearerToken, landingZoneId, jobId); + return typeAdapter.toApiDeleteAzureLandingZoneJobResult(response); + } + + @Override + public ApiAzureLandingZoneResult getAsyncJobResult(BearerToken bearerToken, String jobId) { + var response = landingZoneService.getAsyncJobResult(bearerToken, jobId); + return typeAdapter.toApiAzureLandingZoneResult(response); + } + + @Override + public ApiAzureLandingZone getAzureLandingZone(BearerToken bearerToken, UUID landingZoneId) { + LandingZone landingZoneRecord = landingZoneService.getLandingZone(bearerToken, landingZoneId); + return typeAdapter.toApiAzureLandingZone(landingZoneRecord); + } + + @Override + public ApiAzureLandingZoneList listLandingZonesByBillingProfile( + BearerToken bearerToken, UUID billingProfileId) { + ApiAzureLandingZoneList result = new ApiAzureLandingZoneList(); + List landingZones = + landingZoneService.getLandingZonesByBillingProfile(bearerToken, billingProfileId); + if (landingZones.size() > 0) { + // The enforced logic is 1:1 relation between Billing Profile and a Landing Zone. + // The landing zone service returns one record in the list if landing zone exists + // for a given billing profile. + if (landingZones.size() == 1) { + result.addLandingzonesItem(typeAdapter.toApiAzureLandingZone(landingZones.get(0))); + } else { + throw new ConflictException( + String.format( + "There are more than one landing zone found for the given billing profile: '%s'. Please" + + " check the landing zone deployment is correct.", + billingProfileId)); + } + } + return result; + } + + @Override + public ApiAzureLandingZoneList listLandingZones(BearerToken bearerToken) { + List landingZones = landingZoneService.listLandingZones(bearerToken); + return new ApiAzureLandingZoneList() + .landingzones( + landingZones.stream() + .map(typeAdapter::toApiAzureLandingZone) + .collect(Collectors.toList())); + } + + @Override + public String getLandingZoneRegion(BearerToken bearerToken, UUID landingZoneId) { + return landingZoneService.getLandingZoneRegion(bearerToken, landingZoneId); + } + + @Override + public ApiAzureLandingZoneDefinitionList listLandingZoneDefinitions(BearerToken bearerToken) { + var templates = landingZoneService.listLandingZoneDefinitions(bearerToken); + return new ApiAzureLandingZoneDefinitionList() + .landingzones( + templates.stream() + .map( + t -> + new ApiAzureLandingZoneDefinition() + .definition(t.definition()) + .name(t.name()) + .description(t.description()) + .version(t.version())) + .collect(Collectors.toList())); + } + + @Override + public ApiAzureLandingZoneResourcesList listResourcesWithPurposes( + BearerToken bearerToken, UUID landingZoneId) { + var result = new ApiAzureLandingZoneResourcesList().id(landingZoneId); + landingZoneService + .listResourcesWithPurposes(bearerToken, landingZoneId) + .deployedResources() + .forEach( + (p, dp) -> + result.addResourcesItem( + new ApiAzureLandingZoneResourcesPurposeGroup() + .purpose(p.toString()) + .deployedResources( + dp.stream() + .map(r -> typeAdapter.toApiAzureLandingZoneDeployedResource(r, p)) + .toList()))); + + return result; + } + + @Override + public ApiAzureLandingZoneResourcesList listResourcesMatchingPurpose( + BearerToken bearerToken, UUID landingZoneId, LandingZonePurpose resourcePurpose) { + var result = new ApiAzureLandingZoneResourcesList().id(landingZoneId); + var deployedResources = + landingZoneService + .listResourcesByPurpose(bearerToken, landingZoneId, resourcePurpose) + .stream() + .map(r -> typeAdapter.toApiAzureLandingZoneDeployedResource(r, resourcePurpose)) + .toList(); + result.addResourcesItem( + new ApiAzureLandingZoneResourcesPurposeGroup() + .purpose(resourcePurpose.toString()) + .deployedResources(deployedResources)); + return result; + } + + @Override + public ApiResourceQuota getResourceQuota( + BearerToken bearerToken, UUID landingZoneId, String resourceId) { + var response = landingZoneService.getResourceQuota(bearerToken, landingZoneId, resourceId); + return typeAdapter.toApiResourceQuota(landingZoneId, response); + } +} diff --git a/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/LandingApiClientTypeAdapter.java b/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/LandingApiClientTypeAdapter.java new file mode 100644 index 0000000000..eb0b5ddd88 --- /dev/null +++ b/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/LandingApiClientTypeAdapter.java @@ -0,0 +1,135 @@ +package bio.terra.workspace.amalgam.landingzone.azure; + +import bio.terra.landingzone.job.LandingZoneJobService; +import bio.terra.landingzone.job.model.JobReport; +import bio.terra.landingzone.library.landingzones.deployment.LandingZonePurpose; +import bio.terra.landingzone.library.landingzones.deployment.ResourcePurpose; +import bio.terra.landingzone.library.landingzones.deployment.SubnetResourcePurpose; +import bio.terra.landingzone.library.landingzones.management.quotas.ResourceQuota; +import bio.terra.landingzone.service.landingzone.azure.model.DeletedLandingZone; +import bio.terra.landingzone.service.landingzone.azure.model.DeployedLandingZone; +import bio.terra.landingzone.service.landingzone.azure.model.LandingZone; +import bio.terra.landingzone.service.landingzone.azure.model.LandingZoneResource; +import bio.terra.landingzone.service.landingzone.azure.model.StartLandingZoneCreation; +import bio.terra.landingzone.service.landingzone.azure.model.StartLandingZoneDeletion; +import bio.terra.workspace.common.utils.MapperUtils; +import bio.terra.workspace.generated.model.ApiAzureLandingZone; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneDeployedResource; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneDetails; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneResult; +import bio.terra.workspace.generated.model.ApiCreateLandingZoneResult; +import bio.terra.workspace.generated.model.ApiDeleteAzureLandingZoneJobResult; +import bio.terra.workspace.generated.model.ApiDeleteAzureLandingZoneResult; +import bio.terra.workspace.generated.model.ApiResourceQuota; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * Utilities for transforming from internal landing zone library types to externally facing API + * types. + */ +public class LandingApiClientTypeAdapter { + + public ApiCreateLandingZoneResult toApiCreateLandingZoneResult( + LandingZoneJobService.AsyncJobResult jobResult) { + + return new ApiCreateLandingZoneResult() + .jobReport(MapperUtils.JobReportMapper.from(jobResult.getJobReport())) + .errorReport(MapperUtils.ErrorReportMapper.from(jobResult.getApiErrorReport())) + .landingZoneId(jobResult.getResult().landingZoneId()) + .definition(jobResult.getResult().definition()) + .version(jobResult.getResult().version()); + } + + public ApiAzureLandingZone toApiAzureLandingZone(LandingZone landingZone) { + return new ApiAzureLandingZone() + .billingProfileId(landingZone.billingProfileId()) + .landingZoneId(landingZone.landingZoneId()) + .definition(landingZone.definition()) + .version(landingZone.version()) + .region(landingZone.region()) + .createdDate(landingZone.createdDate()); + } + + public ApiDeleteAzureLandingZoneResult toApiDeleteAzureLandingZoneResult( + LandingZoneJobService.AsyncJobResult jobResult) { + return new ApiDeleteAzureLandingZoneResult() + .jobReport(MapperUtils.JobReportMapper.from(jobResult.getJobReport())) + .errorReport(MapperUtils.ErrorReportMapper.from(jobResult.getApiErrorReport())) + .landingZoneId(jobResult.getResult().landingZoneId()); + } + + public ApiAzureLandingZoneDeployedResource toApiAzureLandingZoneDeployedResource( + LandingZoneResource resource, LandingZonePurpose purpose) { + if (purpose.getClass().equals(ResourcePurpose.class)) { + return new ApiAzureLandingZoneDeployedResource() + .resourceId(resource.resourceId()) + .resourceType(resource.resourceType()) + .tags(resource.tags()) + .region(resource.region()); + } + if (purpose.getClass().equals(SubnetResourcePurpose.class)) { + return new ApiAzureLandingZoneDeployedResource() + .resourceParentId(resource.resourceParentId().orElse(null)) // Only available for subnets + .resourceName(resource.resourceName().orElse(null)) // Only available for subnets + .resourceType(resource.resourceType()) + .resourceId(resource.resourceId()) + .tags(resource.tags()) + .region(resource.region()); + } + throw new LandingZoneUnsupportedPurposeException( + String.format( + "Support for purpose type %s is not implemented.", purpose.getClass().getSimpleName())); + } + + public ApiDeleteAzureLandingZoneJobResult toApiDeleteAzureLandingZoneJobResult( + LandingZoneJobService.AsyncJobResult jobResult) { + var apiJobResult = + new ApiDeleteAzureLandingZoneJobResult() + .jobReport(MapperUtils.JobReportMapper.from(jobResult.getJobReport())) + .errorReport(MapperUtils.ErrorReportMapper.from(jobResult.getApiErrorReport())); + + if (jobResult.getJobReport().getStatus().equals(JobReport.StatusEnum.SUCCEEDED)) { + apiJobResult.landingZoneId(jobResult.getResult().landingZoneId()); + apiJobResult.resources(jobResult.getResult().deleteResources()); + } + return apiJobResult; + } + + public ApiResourceQuota toApiResourceQuota(UUID landingZoneId, ResourceQuota resourceQuota) { + return new ApiResourceQuota() + .landingZoneId(landingZoneId) + .azureResourceId(resourceQuota.resourceId()) + .resourceType(resourceQuota.resourceType()) + .quotaValues(resourceQuota.quota()); + } + + public ApiAzureLandingZoneResult toApiAzureLandingZoneResult( + LandingZoneJobService.AsyncJobResult jobResult) { + ApiAzureLandingZoneDetails azureLandingZone = null; + if (jobResult.getJobReport().getStatus().equals(JobReport.StatusEnum.SUCCEEDED)) { + azureLandingZone = + Optional.ofNullable(jobResult.getResult()) + .map( + lz -> + new ApiAzureLandingZoneDetails() + .id(lz.id()) + .resources( + lz.deployedResources().stream() + .map( + resource -> + new ApiAzureLandingZoneDeployedResource() + .region(resource.region()) + .resourceType(resource.resourceType()) + .resourceId(resource.resourceId())) + .collect(Collectors.toList()))) + .orElse(null); + } + + return new ApiAzureLandingZoneResult() + .jobReport(MapperUtils.JobReportMapper.from(jobResult.getJobReport())) + .errorReport(MapperUtils.ErrorReportMapper.from(jobResult.getApiErrorReport())) + .landingZone(azureLandingZone); + } +} diff --git a/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/LandingZoneApiDispatch.java b/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/LandingZoneApiDispatch.java index f64d13a3fb..68ffdf33d5 100644 --- a/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/LandingZoneApiDispatch.java +++ b/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/LandingZoneApiDispatch.java @@ -1,32 +1,20 @@ package bio.terra.workspace.amalgam.landingzone.azure; -import bio.terra.common.exception.ConflictException; import bio.terra.common.iam.BearerToken; import bio.terra.landingzone.job.LandingZoneJobService; import bio.terra.landingzone.job.model.JobReport; import bio.terra.landingzone.library.landingzones.deployment.LandingZonePurpose; import bio.terra.landingzone.library.landingzones.deployment.ResourcePurpose; -import bio.terra.landingzone.library.landingzones.deployment.SubnetResourcePurpose; -import bio.terra.landingzone.library.landingzones.management.quotas.ResourceQuota; import bio.terra.landingzone.service.landingzone.azure.LandingZoneService; -import bio.terra.landingzone.service.landingzone.azure.model.DeletedLandingZone; import bio.terra.landingzone.service.landingzone.azure.model.DeployedLandingZone; -import bio.terra.landingzone.service.landingzone.azure.model.LandingZone; -import bio.terra.landingzone.service.landingzone.azure.model.LandingZoneDefinition; -import bio.terra.landingzone.service.landingzone.azure.model.LandingZoneRequest; -import bio.terra.landingzone.service.landingzone.azure.model.LandingZoneResource; -import bio.terra.landingzone.service.landingzone.azure.model.StartLandingZoneCreation; -import bio.terra.landingzone.service.landingzone.azure.model.StartLandingZoneDeletion; import bio.terra.workspace.app.configuration.external.FeatureConfiguration; import bio.terra.workspace.common.utils.MapperUtils; import bio.terra.workspace.generated.model.ApiAzureLandingZone; -import bio.terra.workspace.generated.model.ApiAzureLandingZoneDefinition; import bio.terra.workspace.generated.model.ApiAzureLandingZoneDefinitionList; import bio.terra.workspace.generated.model.ApiAzureLandingZoneDeployedResource; import bio.terra.workspace.generated.model.ApiAzureLandingZoneDetails; import bio.terra.workspace.generated.model.ApiAzureLandingZoneList; import bio.terra.workspace.generated.model.ApiAzureLandingZoneResourcesList; -import bio.terra.workspace.generated.model.ApiAzureLandingZoneResourcesPurposeGroup; import bio.terra.workspace.generated.model.ApiAzureLandingZoneResult; import bio.terra.workspace.generated.model.ApiCreateAzureLandingZoneRequestBody; import bio.terra.workspace.generated.model.ApiCreateLandingZoneResult; @@ -37,7 +25,6 @@ import bio.terra.workspace.service.iam.AuthenticatedUserRequest; import bio.terra.workspace.service.iam.SamService; import bio.terra.workspace.service.workspace.model.Workspace; -import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -61,15 +48,15 @@ public class LandingZoneApiDispatch { private static final String AZURE_UAMI_RESOURCE_TYPE = "Microsoft.ManagedIdentity/userAssignedIdentities"; - private final LandingZoneService landingZoneService; private final FeatureConfiguration features; private final SamService samService; + private final AmalgamatedLandingZoneService amalgamated; public LandingZoneApiDispatch( LandingZoneService landingZoneService, FeatureConfiguration features, SamService samService) { - this.landingZoneService = landingZoneService; this.features = features; this.samService = samService; + this.amalgamated = new AmalgamatedLandingZoneService(landingZoneService); } public ApiCreateLandingZoneResult createAzureLandingZone( @@ -85,18 +72,15 @@ public ApiCreateLandingZoneResult createAzureLandingZone( // Prevent deploying more than 1 landing zone per billing profile verifyLandingZoneDoesNotExistForBillingProfile(bearerToken, body); - LandingZoneRequest landingZoneRequest = - LandingZoneRequest.builder() - .landingZoneId(body.getLandingZoneId()) - .definition(body.getDefinition()) - .version(body.getVersion()) - .parameters( - MapperUtils.LandingZoneMapper.landingZoneParametersFrom(body.getParameters())) - .billingProfileId(body.getBillingProfileId()) - .build(); - return toApiCreateLandingZoneResult( - landingZoneService.startLandingZoneCreationJob( - bearerToken, body.getJobControl().getId(), landingZoneRequest, asyncResultEndpoint)); + return amalgamated.startLandingZoneCreationJob( + bearerToken, + body.getJobControl().getId(), + body.getLandingZoneId(), + body.getDefinition(), + body.getVersion(), + body.getParameters(), + body.getBillingProfileId(), + asyncResultEndpoint); } private void verifyLandingZoneDoesNotExistForBillingProfile( @@ -104,8 +88,9 @@ private void verifyLandingZoneDoesNotExistForBillingProfile( // TODO: Catching the exception is a temp solution. // A better approach would be to return an empty list instead of throwing an exception try { - landingZoneService - .getLandingZonesByBillingProfile(bearerToken, body.getBillingProfileId()) + amalgamated + .listLandingZonesByBillingProfile(bearerToken, body.getBillingProfileId()) + .getLandingzones() .stream() .findFirst() .ifPresent( @@ -118,40 +103,16 @@ private void verifyLandingZoneDoesNotExistForBillingProfile( } } - private ApiCreateLandingZoneResult toApiCreateLandingZoneResult( - LandingZoneJobService.AsyncJobResult jobResult) { - - return new ApiCreateLandingZoneResult() - .jobReport(MapperUtils.JobReportMapper.from(jobResult.getJobReport())) - .errorReport(MapperUtils.ErrorReportMapper.from(jobResult.getApiErrorReport())) - .landingZoneId(jobResult.getResult().landingZoneId()) - .definition(jobResult.getResult().definition()) - .version(jobResult.getResult().version()); - } - public ApiAzureLandingZoneResult getCreateAzureLandingZoneResult( BearerToken bearerToken, String jobId) { features.azureEnabledCheck(); - return toApiAzureLandingZoneResult(landingZoneService.getAsyncJobResult(bearerToken, jobId)); + return amalgamated.getAsyncJobResult(bearerToken, jobId); } public ApiAzureLandingZoneDefinitionList listAzureLandingZonesDefinitions( BearerToken bearerToken) { features.azureEnabledCheck(); - List templates = - landingZoneService.listLandingZoneDefinitions(bearerToken); - - return new ApiAzureLandingZoneDefinitionList() - .landingzones( - templates.stream() - .map( - t -> - new ApiAzureLandingZoneDefinition() - .definition(t.definition()) - .name(t.name()) - .description(t.description()) - .version(t.version())) - .collect(Collectors.toList())); + return amalgamated.listLandingZoneDefinitions(bearerToken); } public ApiDeleteAzureLandingZoneResult deleteLandingZone( @@ -160,36 +121,14 @@ public ApiDeleteAzureLandingZoneResult deleteLandingZone( ApiDeleteAzureLandingZoneRequestBody body, String resultEndpoint) { features.azureEnabledCheck(); - return toApiDeleteAzureLandingZoneResult( - landingZoneService.startLandingZoneDeletionJob( - bearerToken, body.getJobControl().getId(), landingZoneId, resultEndpoint)); - } - - private ApiDeleteAzureLandingZoneResult toApiDeleteAzureLandingZoneResult( - LandingZoneJobService.AsyncJobResult jobResult) { - return new ApiDeleteAzureLandingZoneResult() - .jobReport(MapperUtils.JobReportMapper.from(jobResult.getJobReport())) - .errorReport(MapperUtils.ErrorReportMapper.from(jobResult.getApiErrorReport())) - .landingZoneId(jobResult.getResult().landingZoneId()); + return amalgamated.startLandingZoneDeletionJob( + bearerToken, body.getJobControl().getId(), landingZoneId, resultEndpoint); } public ApiAzureLandingZoneResourcesList listAzureLandingZoneResources( BearerToken bearerToken, UUID landingZoneId) { features.azureEnabledCheck(); - var result = new ApiAzureLandingZoneResourcesList().id(landingZoneId); - landingZoneService - .listResourcesWithPurposes(bearerToken, landingZoneId) - .deployedResources() - .forEach( - (p, dp) -> - result.addResourcesItem( - new ApiAzureLandingZoneResourcesPurposeGroup() - .purpose(p.toString()) - .deployedResources( - dp.stream() - .map(r -> toApiAzureLandingZoneDeployedResource(r, p)) - .toList()))); - return result; + return amalgamated.listResourcesWithPurposes(bearerToken, landingZoneId); } public Optional getSharedStorageAccount( @@ -222,41 +161,7 @@ public Optional getSharedDatabaseAdminIdent public ApiAzureLandingZoneResourcesList listAzureLandingZoneResourcesByPurpose( BearerToken bearerToken, UUID landingZoneId, LandingZonePurpose resourcePurpose) { features.azureEnabledCheck(); - var result = new ApiAzureLandingZoneResourcesList().id(landingZoneId); - var deployedResources = - landingZoneService - .listResourcesByPurpose(bearerToken, landingZoneId, resourcePurpose) - .stream() - .map(r -> toApiAzureLandingZoneDeployedResource(r, resourcePurpose)) - .toList(); - result.addResourcesItem( - new ApiAzureLandingZoneResourcesPurposeGroup() - .purpose(resourcePurpose.toString()) - .deployedResources(deployedResources)); - return result; - } - - private ApiAzureLandingZoneDeployedResource toApiAzureLandingZoneDeployedResource( - LandingZoneResource resource, LandingZonePurpose purpose) { - if (purpose.getClass().equals(ResourcePurpose.class)) { - return new ApiAzureLandingZoneDeployedResource() - .resourceId(resource.resourceId()) - .resourceType(resource.resourceType()) - .tags(resource.tags()) - .region(resource.region()); - } - if (purpose.getClass().equals(SubnetResourcePurpose.class)) { - return new ApiAzureLandingZoneDeployedResource() - .resourceParentId(resource.resourceParentId().orElse(null)) // Only available for subnets - .resourceName(resource.resourceName().orElse(null)) // Only available for subnets - .resourceType(resource.resourceType()) - .resourceId(resource.resourceId()) - .tags(resource.tags()) - .region(resource.region()); - } - throw new LandingZoneUnsupportedPurposeException( - String.format( - "Support for purpose type %s is not implemented.", purpose.getClass().getSimpleName())); + return amalgamated.listResourcesMatchingPurpose(bearerToken, landingZoneId, resourcePurpose); } private ApiAzureLandingZoneResult toApiAzureLandingZoneResult( @@ -298,9 +203,12 @@ public UUID getLandingZoneId(BearerToken token, Workspace workspace) { } // getLandingZonesByBillingProfile returns a list. But it always contains only one item - return landingZoneService.getLandingZonesByBillingProfile(token, profileId.get()).stream() + return amalgamated + .listLandingZonesByBillingProfile(token, profileId.get()) + .getLandingzones() + .stream() .findFirst() - .map(LandingZone::landingZoneId) + .map(ApiAzureLandingZone::getLandingZoneId) .orElseThrow( () -> new LandingZoneNotFoundException( @@ -314,87 +222,26 @@ public UUID getLandingZoneId(BearerToken token, Workspace workspace) { public ApiDeleteAzureLandingZoneJobResult getDeleteAzureLandingZoneResult( BearerToken token, UUID landingZoneId, String jobId) { features.azureEnabledCheck(); - return toApiDeleteAzureLandingZoneJobResult( - landingZoneService.getAsyncDeletionJobResult(token, landingZoneId, jobId)); - } - - private ApiDeleteAzureLandingZoneJobResult toApiDeleteAzureLandingZoneJobResult( - LandingZoneJobService.AsyncJobResult jobResult) { - var apiJobResult = - new ApiDeleteAzureLandingZoneJobResult() - .jobReport(MapperUtils.JobReportMapper.from(jobResult.getJobReport())) - .errorReport(MapperUtils.ErrorReportMapper.from(jobResult.getApiErrorReport())); - - if (jobResult.getJobReport().getStatus().equals(JobReport.StatusEnum.SUCCEEDED)) { - apiJobResult.landingZoneId(jobResult.getResult().landingZoneId()); - apiJobResult.resources(jobResult.getResult().deleteResources()); - } - return apiJobResult; + return amalgamated.getDeleteLandingZoneResult(token, landingZoneId, jobId); } public ApiAzureLandingZone getAzureLandingZone(BearerToken bearerToken, UUID landingZoneId) { features.azureEnabledCheck(); - LandingZone landingZoneRecord = landingZoneService.getLandingZone(bearerToken, landingZoneId); - return toApiAzureLandingZone(landingZoneRecord); + return amalgamated.getAzureLandingZone(bearerToken, landingZoneId); } public ApiAzureLandingZoneList listAzureLandingZones( BearerToken bearerToken, UUID billingProfileId) { features.azureEnabledCheck(); if (billingProfileId != null) { - return getAzureLandingZonesByBillingProfile(bearerToken, billingProfileId); - } - List landingZones = landingZoneService.listLandingZones(bearerToken); - return new ApiAzureLandingZoneList() - .landingzones( - landingZones.stream().map(this::toApiAzureLandingZone).collect(Collectors.toList())); - } - - private ApiAzureLandingZoneList getAzureLandingZonesByBillingProfile( - BearerToken bearerToken, UUID billingProfileId) { - ApiAzureLandingZoneList result = new ApiAzureLandingZoneList(); - List landingZones = - landingZoneService.getLandingZonesByBillingProfile(bearerToken, billingProfileId); - if (landingZones.size() > 0) { - // The enforced logic is 1:1 relation between Billing Profile and a Landing Zone. - // The landing zone service returns one record in the list if landing zone exists - // for a given billing profile. - if (landingZones.size() == 1) { - result.addLandingzonesItem(toApiAzureLandingZone(landingZones.get(0))); - } else { - throw new ConflictException( - String.format( - "There are more than one landing zone found for the given billing profile: '%s'. Please" - + " check the landing zone deployment is correct.", - billingProfileId)); - } + return amalgamated.listLandingZonesByBillingProfile(bearerToken, billingProfileId); } - return result; - } - - private ApiAzureLandingZone toApiAzureLandingZone(LandingZone landingZone) { - return new ApiAzureLandingZone() - .billingProfileId(landingZone.billingProfileId()) - .landingZoneId(landingZone.landingZoneId()) - .definition(landingZone.definition()) - .version(landingZone.version()) - .region(landingZone.region()) - .createdDate(landingZone.createdDate()); + return amalgamated.listLandingZones(bearerToken); } public ApiResourceQuota getResourceQuota( BearerToken bearerToken, UUID landingZoneId, String azureResourceId) { - return toApiResourceQuota( - landingZoneId, - landingZoneService.getResourceQuota(bearerToken, landingZoneId, azureResourceId)); - } - - private ApiResourceQuota toApiResourceQuota(UUID landingZoneId, ResourceQuota resourceQuota) { - return new ApiResourceQuota() - .landingZoneId(landingZoneId) - .azureResourceId(resourceQuota.resourceId()) - .resourceType(resourceQuota.resourceType()) - .quotaValues(resourceQuota.quota()); + return amalgamated.getResourceQuota(bearerToken, landingZoneId, azureResourceId); } private Optional getSharedResourceByType( @@ -442,6 +289,6 @@ public String getLandingZoneRegionForWorkspaceUsingWsmToken(Workspace workspace) public String getLandingZoneRegionUsingWsmToken(UUID landingZoneId) { features.azureEnabledCheck(); var token = new BearerToken(samService.getWsmServiceAccountToken()); - return landingZoneService.getLandingZoneRegion(token, landingZoneId); + return amalgamated.getLandingZoneRegion(token, landingZoneId); } } diff --git a/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/WorkspaceLandingZoneService.java b/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/WorkspaceLandingZoneService.java new file mode 100644 index 0000000000..eab6abbf98 --- /dev/null +++ b/service/src/main/java/bio/terra/workspace/amalgam/landingzone/azure/WorkspaceLandingZoneService.java @@ -0,0 +1,62 @@ +package bio.terra.workspace.amalgam.landingzone.azure; + +import bio.terra.common.iam.BearerToken; +import bio.terra.landingzone.library.landingzones.deployment.LandingZonePurpose; +import bio.terra.workspace.generated.model.ApiAzureLandingZone; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneDefinitionList; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneList; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneParameter; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneResourcesList; +import bio.terra.workspace.generated.model.ApiAzureLandingZoneResult; +import bio.terra.workspace.generated.model.ApiCreateLandingZoneResult; +import bio.terra.workspace.generated.model.ApiDeleteAzureLandingZoneJobResult; +import bio.terra.workspace.generated.model.ApiDeleteAzureLandingZoneResult; +import bio.terra.workspace.generated.model.ApiResourceQuota; +import java.util.List; +import java.util.UUID; + +/** + * Represents a means by which we can invoke landing zone related operations. + * + *

The intention is to abstract our "amalgamated" in-memory calls to the LZS library, so we can + * introduce HTTP API client calls to the Landing Zone Service in their place as we move away from + * the amalgamated implementation. + */ +public interface WorkspaceLandingZoneService { + ApiCreateLandingZoneResult startLandingZoneCreationJob( + BearerToken bearerToken, + String jobId, + UUID landingZoneId, + String definition, + String version, + List parameters, + UUID billingProfileId, + String asyncResultEndpoint); + + ApiDeleteAzureLandingZoneResult startLandingZoneDeletionJob( + BearerToken bearerToken, String jobId, UUID landingZoneId, String resultEndpoint); + + ApiDeleteAzureLandingZoneJobResult getDeleteLandingZoneResult( + BearerToken bearerToken, UUID landingZoneId, String jobId); + + ApiAzureLandingZoneResult getAsyncJobResult(BearerToken bearerToken, String jobId); + + ApiAzureLandingZone getAzureLandingZone(BearerToken bearerToken, UUID landingZoneId); + + ApiAzureLandingZoneList listLandingZonesByBillingProfile( + BearerToken bearerToken, UUID billingProfileId); + + ApiAzureLandingZoneList listLandingZones(BearerToken bearerToken); + + String getLandingZoneRegion(BearerToken bearerToken, UUID landingZoneId); + + ApiAzureLandingZoneDefinitionList listLandingZoneDefinitions(BearerToken bearerToken); + + ApiAzureLandingZoneResourcesList listResourcesWithPurposes( + BearerToken bearerToken, UUID landingZoneId); + + ApiAzureLandingZoneResourcesList listResourcesMatchingPurpose( + BearerToken bearerToken, UUID landingZoneId, LandingZonePurpose resourcePurpose); + + ApiResourceQuota getResourceQuota(BearerToken bearerToken, UUID landingZoneId, String resourceId); +}