You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by lf...@apache.org on 2020/07/20 13:04:42 UTC

[incubator-dlab] 01/02: [DLAB-1714] BE created notification in case project quota is exceeded

This is an automated email from the ASF dual-hosted git repository.

lfrolov pushed a commit to branch DLAB-1944
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git

commit 5b2fe0395fa9e81e8133e3e65f5d918493a35489
Author: Oleh Fuks <ol...@gmail.com>
AuthorDate: Fri Jul 17 18:05:47 2020 +0300

    [DLAB-1714] BE created notification in case project quota is exceeded
---
 .../epam/dlab/backendapi/dao/BaseBillingDAO.java   | 28 +------------
 .../com/epam/dlab/backendapi/dao/BillingDAO.java   |  8 +---
 .../interceptor/BudgetLimitInterceptor.java        |  5 ++-
 .../dlab/backendapi/resources/BillingResource.java |  8 ++++
 .../resources/dto/HealthStatusPageDTO.java         |  4 --
 .../dto/QuotaUsageDTO.java}                        | 22 ++++------
 .../schedulers/CheckProjectQuoteScheduler.java     |  6 +--
 .../dlab/backendapi/service/BillingService.java    | 15 +++++--
 .../service/impl/BillingServiceImpl.java           | 48 +++++++++++++++++++++-
 .../impl/InfrastructureInfoServiceImpl.java        | 11 ++---
 10 files changed, 89 insertions(+), 66 deletions(-)

diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseBillingDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseBillingDAO.java
index 0e21bba..7975eeb 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseBillingDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseBillingDAO.java
@@ -20,10 +20,8 @@
 package com.epam.dlab.backendapi.dao;
 
 import com.epam.dlab.backendapi.domain.BillingReportLine;
-import com.epam.dlab.backendapi.domain.BudgetDTO;
 import com.epam.dlab.backendapi.resources.dto.BillingFilter;
 import com.epam.dlab.dto.billing.BillingResourceType;
-import com.epam.dlab.exceptions.ResourceNotFoundException;
 import com.google.inject.Inject;
 import com.mongodb.client.model.Aggregates;
 import com.mongodb.client.model.Filters;
@@ -101,14 +99,14 @@ public class BaseBillingDAO extends BaseDAO implements BillingDAO {
 	}
 
 	@Override
-	public Double getProjectCost(String project) {
+	public Double getOverallProjectCost(String project) {
 		final List<Bson> pipeline = Arrays.asList(match(eq(PROJECT, project)),
 				group(null, sum(TOTAL_FIELD_NAME, COST_FIELD)));
 		return aggregateBillingData(pipeline);
 	}
 
 	@Override
-	public Double getProjectCost(String project, LocalDate date) {
+	public Double getMonthlyProjectCost(String project, LocalDate date) {
 		final List<Bson> pipeline = Arrays.asList(match(
 				and(
 						eq(PROJECT, project),
@@ -143,28 +141,10 @@ public class BaseBillingDAO extends BaseDAO implements BillingDAO {
 	}
 
 	@Override
-	public boolean isProjectQuoteReached(String project) {
-		final boolean monthlyBudget = Optional.ofNullable(projectDAO.get(project)
-				.orElseThrow(projectNotFound())
-				.getBudget())
-				.map(BudgetDTO::isMonthlyBudget)
-				.orElse(Boolean.FALSE);
-		final Double projectCost = monthlyBudget ? getProjectCost(project, LocalDate.now()) : getProjectCost(project);
-		return projectDAO.getAllowedBudget(project)
-				.filter(allowedBudget -> projectCost.intValue() != 0 && allowedBudget <= projectCost)
-				.isPresent();
-	}
-
-	@Override
 	public List<BillingReportLine> findBillingData(String project, String endpoint, List<String> resourceNames) {
 		return find(BILLING, and(eq(PROJECT, project), eq(ENDPOINT, endpoint), in(RESOURCE_NAME, resourceNames)), BillingReportLine.class);
 	}
 
-	@Override
-	public int getBillingProjectQuoteUsed(String project) {
-		return toPercentage(() -> projectDAO.getAllowedBudget(project), getProjectCost(project));
-	}
-
 	public List<BillingReportLine> aggregateBillingData(BillingFilter filter) {
 		List<Bson> pipeline = new ArrayList<>();
 		List<Bson> matchCriteria = matchCriteria(filter);
@@ -262,8 +242,4 @@ public class BaseBillingDAO extends BaseDAO implements BillingDAO {
 				.currency(id.getString(CURRENCY))
 				.build();
 	}
-
-	private Supplier<ResourceNotFoundException> projectNotFound() {
-		return () -> new ResourceNotFoundException("Project with passed name not found");
-	}
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BillingDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BillingDAO.java
index da1c9a7..088c319 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BillingDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BillingDAO.java
@@ -29,22 +29,18 @@ public interface BillingDAO {
 
 	Double getUserCost(String user);
 
-	Double getProjectCost(String project);
+	Double getOverallProjectCost(String project);
 
-	Double getProjectCost(String project, LocalDate date);
+	Double getMonthlyProjectCost(String project, LocalDate date);
 
 	int getBillingQuoteUsed();
 
 	int getBillingUserQuoteUsed(String user);
 
-	int getBillingProjectQuoteUsed(String project);
-
 	boolean isBillingQuoteReached();
 
 	boolean isUserQuoteReached(String user);
 
-	boolean isProjectQuoteReached(String project);
-
 	List<BillingReportLine> findBillingData(String project, String endpoint, List<String> resourceNames);
 
 	List<BillingReportLine> aggregateBillingData(BillingFilter filter);
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/BudgetLimitInterceptor.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/BudgetLimitInterceptor.java
index 43fdaf6..ce32e10 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/BudgetLimitInterceptor.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/interceptor/BudgetLimitInterceptor.java
@@ -22,6 +22,7 @@ package com.epam.dlab.backendapi.interceptor;
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.annotation.Project;
 import com.epam.dlab.backendapi.dao.BillingDAO;
+import com.epam.dlab.backendapi.service.BillingService;
 import com.epam.dlab.exceptions.ResourceQuoteReachedException;
 import com.google.inject.Inject;
 import lombok.extern.slf4j.Slf4j;
@@ -38,6 +39,8 @@ import java.util.stream.IntStream;
 public class BudgetLimitInterceptor implements MethodInterceptor {
 	@Inject
 	private BillingDAO billingDAO;
+	@Inject
+	private BillingService billingService;
 
 	@Override
 	public Object invoke(MethodInvocation mi) throws Throwable {
@@ -66,7 +69,7 @@ public class BudgetLimitInterceptor implements MethodInterceptor {
 				.filter(i -> Objects.nonNull(parameters[i].getAnnotation(Project.class)))
 				.mapToObj(i -> (String) mi.getArguments()[i])
 				.findAny()
-				.map(billingDAO::isProjectQuoteReached)
+				.map(billingService::isProjectQuoteReached)
 				.orElse(Boolean.FALSE);
 	}
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
index 1916a38..a03edb3 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/BillingResource.java
@@ -28,6 +28,7 @@ import io.dropwizard.auth.Auth;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
@@ -46,6 +47,13 @@ public class BillingResource {
         this.billingService = billingService;
     }
 
+    @GET
+    @Path("/quota")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getQuota(@Auth UserInfo userInfo) {
+        return Response.ok(billingService.getQuotas(userInfo)).build();
+    }
+
     @POST
     @Path("/report")
     @Produces(MediaType.APPLICATION_JSON)
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
index 642dd06..73fd421 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/HealthStatusPageDTO.java
@@ -44,10 +44,6 @@ public class HealthStatusPageDTO {
 	@JsonProperty
 	private boolean projectAdmin;
 	@JsonProperty
-	private int billingQuoteUsed;
-	@JsonProperty
-	private int billingUserQuoteUsed;
-	@JsonProperty
 	private boolean projectAssigned;
 	@JsonProperty
 	private BucketBrowser bucketBrowser;
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/QuotaUsageDTO.java
similarity index 58%
copy from services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
copy to services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/QuotaUsageDTO.java
index b76b141..e611091 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/QuotaUsageDTO.java
@@ -17,20 +17,16 @@
  * under the License.
  */
 
-package com.epam.dlab.backendapi.service;
+package com.epam.dlab.backendapi.resources.dto;
 
-import com.epam.dlab.auth.UserInfo;
-import com.epam.dlab.backendapi.domain.BillingReport;
-import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+import lombok.Builder;
+import lombok.Data;
 
-import java.util.List;
+import java.util.Map;
 
-public interface BillingService {
-    BillingReport getBillingReport(UserInfo userInfo, BillingFilter filter);
-
-    String downloadReport(UserInfo userInfo, BillingFilter filter);
-
-    BillingReport getExploratoryBillingData(String project, String endpoint, String exploratoryName, List<String> compNames);
-
-    void updateRemoteBillingData(UserInfo userInfo);
+@Data
+@Builder
+public class QuotaUsageDTO {
+	private int totalQuotaUsed;
+	private Map<String, Integer> projectQuotas;
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/CheckProjectQuoteScheduler.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/CheckProjectQuoteScheduler.java
index 030c10d..2dc499f 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/CheckProjectQuoteScheduler.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/CheckProjectQuoteScheduler.java
@@ -19,9 +19,9 @@
 
 package com.epam.dlab.backendapi.schedulers;
 
-import com.epam.dlab.backendapi.dao.BillingDAO;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.schedulers.internal.Scheduled;
+import com.epam.dlab.backendapi.service.BillingService;
 import com.epam.dlab.backendapi.service.EnvironmentService;
 import com.epam.dlab.backendapi.service.ProjectService;
 import com.google.inject.Inject;
@@ -34,7 +34,7 @@ import org.quartz.JobExecutionContext;
 public class CheckProjectQuoteScheduler implements Job {
 
 	@Inject
-	private BillingDAO billingDAO;
+	private BillingService billingService;
 	@Inject
 	private EnvironmentService environmentService;
 	@Inject
@@ -45,7 +45,7 @@ public class CheckProjectQuoteScheduler implements Job {
 		projectService.getProjects()
 				.stream()
 				.map(ProjectDTO::getName)
-				.filter(billingDAO::isProjectQuoteReached)
+				.filter(billingService::isProjectQuoteReached)
 				.peek(p -> log.debug("Stopping {} project env because of reaching user billing quote", p))
 				.forEach(environmentService::stopProjectEnvironment);
 	}
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
index b76b141..7291cc7 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/BillingService.java
@@ -22,15 +22,22 @@ package com.epam.dlab.backendapi.service;
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.domain.BillingReport;
 import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+import com.epam.dlab.backendapi.resources.dto.QuotaUsageDTO;
 
 import java.util.List;
 
 public interface BillingService {
-    BillingReport getBillingReport(UserInfo userInfo, BillingFilter filter);
+	BillingReport getBillingReport(UserInfo userInfo, BillingFilter filter);
 
-    String downloadReport(UserInfo userInfo, BillingFilter filter);
+	String downloadReport(UserInfo userInfo, BillingFilter filter);
 
-    BillingReport getExploratoryBillingData(String project, String endpoint, String exploratoryName, List<String> compNames);
+	BillingReport getExploratoryBillingData(String project, String endpoint, String exploratoryName, List<String> compNames);
 
-    void updateRemoteBillingData(UserInfo userInfo);
+	void updateRemoteBillingData(UserInfo userInfo);
+
+	QuotaUsageDTO getQuotas(UserInfo userInfo);
+
+	boolean isProjectQuoteReached(String project);
+
+	int getBillingProjectQuoteUsed(String project);
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImpl.java
index c0b9aa2..2bcf644 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/BillingServiceImpl.java
@@ -23,12 +23,15 @@ import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
 import com.epam.dlab.backendapi.dao.BillingDAO;
 import com.epam.dlab.backendapi.dao.ImageExploratoryDao;
+import com.epam.dlab.backendapi.dao.ProjectDAO;
 import com.epam.dlab.backendapi.domain.BillingReport;
 import com.epam.dlab.backendapi.domain.BillingReportLine;
+import com.epam.dlab.backendapi.domain.BudgetDTO;
 import com.epam.dlab.backendapi.domain.EndpointDTO;
 import com.epam.dlab.backendapi.domain.ProjectDTO;
 import com.epam.dlab.backendapi.domain.ProjectEndpointDTO;
 import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+import com.epam.dlab.backendapi.resources.dto.QuotaUsageDTO;
 import com.epam.dlab.backendapi.roles.RoleType;
 import com.epam.dlab.backendapi.roles.UserRoles;
 import com.epam.dlab.backendapi.service.BillingService;
@@ -63,7 +66,9 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -73,6 +78,7 @@ public class BillingServiceImpl implements BillingService {
     private static final String USAGE_DATE_FORMAT = "yyyy-MM";
 
     private final ProjectService projectService;
+    private final ProjectDAO projectDAO;
     private final EndpointService endpointService;
     private final ExploratoryService exploratoryService;
     private final SelfServiceApplicationConfiguration configuration;
@@ -82,11 +88,12 @@ public class BillingServiceImpl implements BillingService {
     private final String sbn;
 
     @Inject
-    public BillingServiceImpl(ProjectService projectService, EndpointService endpointService,
+    public BillingServiceImpl(ProjectService projectService, ProjectDAO projectDAO, EndpointService endpointService,
                               ExploratoryService exploratoryService, SelfServiceApplicationConfiguration configuration,
                               @Named(ServiceConsts.BILLING_SERVICE_NAME) RESTService provisioningService, ImageExploratoryDao imageExploratoryDao,
                               BillingDAO billingDAO) {
         this.projectService = projectService;
+        this.projectDAO = projectDAO;
         this.endpointService = endpointService;
         this.exploratoryService = exploratoryService;
         this.configuration = configuration;
@@ -176,6 +183,38 @@ public class BillingServiceImpl implements BillingService {
         });
     }
 
+    @Override
+    public QuotaUsageDTO getQuotas(UserInfo userInfo) {
+        int totalQuota = billingDAO.getBillingQuoteUsed();
+        Map<String, Integer> projectQuotas = projectService.getProjects(userInfo)
+                .stream()
+                .collect(Collectors.toMap(ProjectDTO::getName, p -> getBillingProjectQuoteUsed(p.getName())));
+        return QuotaUsageDTO.builder()
+                .totalQuotaUsed(totalQuota)
+                .projectQuotas(projectQuotas)
+                .build();
+    }
+
+    @Override
+    public boolean isProjectQuoteReached(String project) {
+        final Double projectCost = getProjectCost(project);
+        return projectDAO.getAllowedBudget(project)
+                .filter(allowedBudget -> projectCost.intValue() != 0 && allowedBudget <= projectCost)
+                .isPresent();
+    }
+
+    @Override
+    public int getBillingProjectQuoteUsed(String project) {
+        return toPercentage(() -> projectDAO.getAllowedBudget(project), getProjectCost(project));
+    }
+
+    private Double getProjectCost(String project) {
+        final boolean monthlyBudget = Optional.ofNullable(projectService.get(project).getBudget())
+                .map(BudgetDTO::isMonthlyBudget)
+                .orElse(Boolean.FALSE);
+        return monthlyBudget ? billingDAO.getMonthlyProjectCost(project, LocalDate.now()) : billingDAO.getOverallProjectCost(project);
+    }
+
     private Map<String, BillingReportLine> getBillableResources() {
         Set<ProjectDTO> projects = new HashSet<>(projectService.getProjects());
         final Stream<BillingReportLine> ssnBillingDataStream = BillingUtils.ssnBillingDataStream(sbn);
@@ -341,4 +380,11 @@ public class BillingServiceImpl implements BillingService {
                 .exploratoryName(billingReportLine.getExploratoryName())
                 .build();
     }
+
+    private Integer toPercentage(Supplier<Optional<Integer>> allowedBudget, Double totalCost) {
+        return allowedBudget.get()
+                .map(userBudget -> (totalCost * 100) / userBudget)
+                .map(Double::intValue)
+                .orElse(BigDecimal.ZERO.intValue());
+    }
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
index 475f2db..98c2f6d 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/service/impl/InfrastructureInfoServiceImpl.java
@@ -21,7 +21,6 @@ package com.epam.dlab.backendapi.service.impl;
 
 import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.conf.SelfServiceApplicationConfiguration;
-import com.epam.dlab.backendapi.dao.BillingDAO;
 import com.epam.dlab.backendapi.dao.ExploratoryDAO;
 import com.epam.dlab.backendapi.domain.BillingReport;
 import com.epam.dlab.backendapi.domain.EndpointDTO;
@@ -65,18 +64,16 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 
 	private final ExploratoryDAO expDAO;
 	private final SelfServiceApplicationConfiguration configuration;
-	private final BillingDAO billingDAO;
 	private final ProjectService projectService;
 	private final EndpointService endpointService;
 	private final BillingService billingService;
 
 	@Inject
 	public InfrastructureInfoServiceImpl(ExploratoryDAO expDAO, SelfServiceApplicationConfiguration configuration,
-										 BillingDAO billingDAO, ProjectService projectService, EndpointService endpointService,
-										 BillingService billingService) {
+	                                     ProjectService projectService, EndpointService endpointService,
+	                                     BillingService billingService) {
 		this.expDAO = expDAO;
 		this.configuration = configuration;
-		this.billingDAO = billingDAO;
 		this.projectService = projectService;
 		this.endpointService = endpointService;
 		this.billingService = billingService;
@@ -91,7 +88,7 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 					.stream()
 					.map(p -> {
 						List<UserInstanceDTO> exploratories = expDAO.findExploratories(user.getName(), p.getName());
-						return new ProjectInfrastructureInfo(p.getName(), billingDAO.getBillingProjectQuoteUsed(p.getName()),
+						return new ProjectInfrastructureInfo(p.getName(), billingService.getBillingProjectQuoteUsed(p.getName()),
 								getSharedInfo(p.getName()), exploratories, getExploratoryBillingData(exploratories),
 								getEndpoints(allEndpoints, p));
 					})
@@ -115,8 +112,6 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 					.projectAdmin(UserRoles.isProjectAdmin(userInfo))
 					.admin(UserRoles.isAdmin(userInfo))
 					.projectAssigned(projectService.isAnyProjectAssigned(userInfo))
-					.billingQuoteUsed(billingDAO.getBillingQuoteUsed())
-					.billingUserQuoteUsed(billingDAO.getBillingUserQuoteUsed(user))
 					.bucketBrowser(HealthStatusPageDTO.BucketBrowser.builder()
 							.view(checkAccess(userInfo, PERMISSION_VIEW))
 							.upload(checkAccess(userInfo, PERMISSION_UPLOAD))


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org