You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by of...@apache.org on 2020/04/08 11:44:03 UTC

[incubator-dlab] 02/02: billing

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

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

commit 754252d33ae4e33a95323ad8f892ee19ad89d891
Author: Oleh Fuks <ol...@gmail.com>
AuthorDate: Wed Apr 8 14:43:28 2020 +0300

    billing
---
 .../billing/gcp/controller/BillingController.java  |   8 -
 .../com/epam/dlab/billing/gcp/dao/BillingDAO.java  |   5 -
 .../billing/gcp/dao/impl/BigQueryBillingDAO.java   |  82 ----------
 .../gcp/service/impl/BillingServiceImpl.java       |   9 --
 .../com/epam/dlab/dto/billing/BillingData.java     |   1 +
 services/self-service/self-service.yml             |   3 +
 .../epam/dlab/backendapi/dao/BaseBillingDAO.java   | 109 ++++++++++++-
 .../java/com/epam/dlab/backendapi/dao/BaseDAO.java |  64 +++++++-
 .../com/epam/dlab/backendapi/dao/BillingDAO.java   |  13 ++
 .../dlab/backendapi/domain/BillingReportLine.java  |   4 +
 .../resources/dto/ProjectInfrastructureInfo.java   |   4 +-
 .../schedulers/billing/BillingScheduler.java       |  52 ++++++
 .../dlab/backendapi/service/BillingService.java    |   6 +-
 .../service/impl/BillingServiceImpl.java           | 177 +++++++++------------
 .../impl/InfrastructureInfoServiceImpl.java        |  32 ++--
 .../epam/dlab/backendapi/util/BillingUtils.java    |   8 +-
 16 files changed, 333 insertions(+), 244 deletions(-)

diff --git a/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/controller/BillingController.java b/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/controller/BillingController.java
index c1b178a..ea45d89 100644
--- a/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/controller/BillingController.java
+++ b/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/controller/BillingController.java
@@ -41,12 +41,4 @@ public class BillingController {
     public ResponseEntity<List<BillingData>> getBilling() {
         return new ResponseEntity<>(billingService.getBillingData(), HttpStatus.OK);
     }
-
-//    @GetMapping("/report")
-//    public ResponseEntity<List<BillingData>> getBilling(@RequestParam("date-start") String dateStart,
-//                                                        @RequestParam("date-end") String dateEnd,
-//                                                        @RequestParam("dlab-id") String dlabId,
-//                                                        @RequestParam("product") List<String> products) {
-//        return new ResponseEntity<>(billingDAO.getBillingReport(dateStart, dateEnd, dlabId, products), HttpStatus.OK);
-//    }
 }
diff --git a/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/dao/BillingDAO.java b/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/dao/BillingDAO.java
index 0a04502..7c791df 100644
--- a/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/dao/BillingDAO.java
+++ b/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/dao/BillingDAO.java
@@ -24,10 +24,5 @@ import com.epam.dlab.dto.billing.BillingData;
 import java.util.List;
 
 public interface BillingDAO {
-
     List<BillingData> getBillingData() throws InterruptedException;
-
-    List<BillingData> getBillingReport(String dateStart, String dateEnd, String dlabId, List<String> products);
-
-    List<BillingData> getBillingReport(List<String> dlabIds);
 }
diff --git a/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/dao/impl/BigQueryBillingDAO.java b/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/dao/impl/BigQueryBillingDAO.java
index ef28679..6949d95 100644
--- a/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/dao/impl/BigQueryBillingDAO.java
+++ b/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/dao/impl/BigQueryBillingDAO.java
@@ -22,10 +22,8 @@ package com.epam.dlab.billing.gcp.dao.impl;
 import com.epam.dlab.billing.gcp.conf.DlabConfiguration;
 import com.epam.dlab.billing.gcp.dao.BillingDAO;
 import com.epam.dlab.billing.gcp.model.BillingHistory;
-import com.epam.dlab.billing.gcp.model.GcpBillingData;
 import com.epam.dlab.billing.gcp.repository.BillingHistoryRepository;
 import com.epam.dlab.dto.billing.BillingData;
-import com.epam.dlab.exceptions.DlabException;
 import com.google.cloud.bigquery.BigQuery;
 import com.google.cloud.bigquery.FieldValueList;
 import com.google.cloud.bigquery.QueryJobConfiguration;
@@ -33,32 +31,20 @@ import com.google.cloud.bigquery.QueryParameterValue;
 import com.google.cloud.bigquery.Table;
 import com.google.cloud.bigquery.TableInfo;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.mongodb.core.MongoTemplate;
-import org.springframework.data.mongodb.core.aggregation.Aggregation;
-import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
-import org.springframework.data.mongodb.core.aggregation.GroupOperation;
-import org.springframework.data.mongodb.core.aggregation.MatchOperation;
-import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.stereotype.Component;
 
-import java.math.BigDecimal;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
-import static org.springframework.data.mongodb.core.aggregation.Aggregation.group;
-import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
-
 @Component
 @Slf4j
 public class BigQueryBillingDAO implements BillingDAO {
@@ -107,62 +93,6 @@ public class BigQueryBillingDAO implements BillingDAO {
 				.collect(Collectors.toList());
 	}
 
-	@Override
-	public List<BillingData> getBillingReport(String dateStart, String dateEnd, String dlabId, List<String> products) {
-		try {
-			List<AggregationOperation> aggregationOperations = new ArrayList<>();
-			aggregationOperations.add(Aggregation.match(Criteria.where("dlabId").regex(dlabId, "i")));
-			if (!products.isEmpty()) {
-				aggregationOperations.add(Aggregation.match(Criteria.where("product").in(products)));
-			}
-			getMatchCriteria(dateStart, Criteria.where("usage_date").gte(dateStart))
-					.ifPresent(aggregationOperations::add);
-			getMatchCriteria(dateEnd, Criteria.where("usage_date").lte(dateEnd))
-					.ifPresent(aggregationOperations::add);
-			aggregationOperations.add(getGroupOperation());
-
-			Aggregation aggregation = newAggregation(aggregationOperations);
-
-			return mongoTemplate.aggregate(aggregation, "billing", GcpBillingData.class).getMappedResults()
-					.stream()
-					.map(this::toBillingData)
-					.collect(Collectors.toList());
-		} catch (Exception e) {
-			log.error("Cannot retrieve billing information ", e);
-			throw new DlabException("Cannot retrieve billing information", e);
-		}
-	}
-
-	@Override
-	public List<BillingData> getBillingReport(List<String> dlabIds) {
-		try {
-			GroupOperation groupOperation = getGroupOperation();
-			MatchOperation matchOperation = Aggregation.match(Criteria.where("dlabId").in(dlabIds));
-			Aggregation aggregation = newAggregation(matchOperation, groupOperation);
-
-			return mongoTemplate.aggregate(aggregation, "billing", GcpBillingData.class).getMappedResults()
-					.stream()
-					.map(this::toBillingData)
-					.collect(Collectors.toList());
-		} catch (Exception e) {
-			log.error("Cannot retrieve billing information ", e);
-			throw new DlabException("Cannot retrieve billing information", e);
-		}
-	}
-
-	private GroupOperation getGroupOperation() {
-		return group("product", "currency", "dlabId")
-				.min("from").as("from")
-				.max("to").as("to")
-				.sum("cost").as("cost");
-	}
-
-	private Optional<MatchOperation> getMatchCriteria(String dateStart, Criteria criteria) {
-		return Optional.ofNullable(dateStart)
-				.filter(StringUtils::isNotEmpty)
-				.map(date -> Aggregation.match(criteria));
-	}
-
 	private Stream<? extends BillingData> bigQueryResultSetStream(Table table) {
 		try {
 			final String tableName = table.getTableId().getTable();
@@ -199,16 +129,4 @@ public class BigQueryBillingDAO implements BillingDAO {
 		return LocalDate.from(Instant.ofEpochMilli(fieldValues.get(timestampFieldName).getTimestampValue() / 1000)
 				.atZone(ZoneId.systemDefault()));
 	}
-
-	private BillingData toBillingData(GcpBillingData billingData) {
-		return BillingData.builder()
-				.usageDateFrom(billingData.getUsageDateFrom())
-				.usageDateTo(billingData.getUsageDateTo())
-				.product(billingData.getProduct())
-				.usageType(billingData.getUsageType())
-				.cost(BigDecimal.valueOf(billingData.getCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue())
-				.currency(billingData.getCurrency())
-				.tag(billingData.getTag())
-				.build();
-	}
 }
diff --git a/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/service/impl/BillingServiceImpl.java b/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/service/impl/BillingServiceImpl.java
index 12e526b..5661dfb 100644
--- a/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/service/impl/BillingServiceImpl.java
+++ b/services/billing-gcp/src/main/java/com/epam/dlab/billing/gcp/service/impl/BillingServiceImpl.java
@@ -32,7 +32,6 @@ import java.util.List;
 @Service
 @Slf4j
 public class BillingServiceImpl implements BillingService {
-	private static final String USAGE_DATE_FORMAT = "yyyy-MM";
 
 	private final BillingDAO billingDAO;
 
@@ -45,14 +44,6 @@ public class BillingServiceImpl implements BillingService {
 	public List<BillingData> getBillingData() {
 		try {
 			return billingDAO.getBillingData();
-//					.stream()
-//					.collect(Collectors.groupingBy(bd -> bd.getUsageDate().substring(0, USAGE_DATE_FORMAT.length())));
-
-//			billingData.forEach((usageDate, billingDataList) -> {
-//				log.info("Updating billing information for month {}", usageDate);
-//				billingRepository.deleteByUsageDateRegex("^" + usageDate);
-//				billingRepository.insert(billingDataList);
-//			});
 		} catch (Exception e) {
 			log.error("Can not update billing due to: {}", e.getMessage(), e);
 			return Collections.emptyList();
diff --git a/services/dlab-model/src/main/java/com/epam/dlab/dto/billing/BillingData.java b/services/dlab-model/src/main/java/com/epam/dlab/dto/billing/BillingData.java
index 8409fc0..c95a02e 100644
--- a/services/dlab-model/src/main/java/com/epam/dlab/dto/billing/BillingData.java
+++ b/services/dlab-model/src/main/java/com/epam/dlab/dto/billing/BillingData.java
@@ -31,6 +31,7 @@ import java.time.LocalDate;
 @JsonIgnoreProperties(ignoreUnknown = true)
 public class BillingData {
     private final String tag;
+    private String application;
     @JsonProperty("from")
     private LocalDate usageDateFrom;
     @JsonProperty("to")
diff --git a/services/self-service/self-service.yml b/services/self-service/self-service.yml
index 53bd131..4e71316 100644
--- a/services/self-service/self-service.yml
+++ b/services/self-service/self-service.yml
@@ -150,6 +150,9 @@ schedulers:
   checkEndpointStatusScheduler:
     enabled: true
     cron: "0 */15 * ? * *"
+  billingScheduler:
+    enabled: true
+    cron: "*/50 * * ? * * *"
 
 
 guacamole:
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 d22e400..0612a1a 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
@@ -19,30 +19,56 @@
 
 package com.epam.dlab.backendapi.dao;
 
+import com.epam.dlab.backendapi.domain.BillingReportLine;
+import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+import com.epam.dlab.dto.billing.BillingResourceType;
 import com.google.inject.Inject;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.bson.Document;
 import org.bson.conversions.Bson;
 
 import java.math.BigDecimal;
+import java.time.ZoneId;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import static com.epam.dlab.backendapi.dao.MongoCollections.BILLING;
+import static com.mongodb.client.model.Accumulators.max;
+import static com.mongodb.client.model.Accumulators.min;
 import static com.mongodb.client.model.Accumulators.sum;
 import static com.mongodb.client.model.Aggregates.group;
 import static com.mongodb.client.model.Aggregates.match;
+import static com.mongodb.client.model.Filters.and;
 import static com.mongodb.client.model.Filters.eq;
+import static com.mongodb.client.model.Filters.gte;
+import static com.mongodb.client.model.Filters.in;
+import static com.mongodb.client.model.Filters.lte;
+import static com.mongodb.client.model.Filters.regex;
 import static java.util.Collections.singletonList;
 
 @Slf4j
 public class BaseBillingDAO extends BaseDAO implements BillingDAO {
-
-	private static final String PROJECT = "project";
 	private static final int ONE_HUNDRED = 100;
-	private static final String TOTAL_FIELD_NAME = "total";
 	private static final String COST_FIELD = "$cost";
+	private static final String TOTAL_FIELD_NAME = "total";
+	private static final String PROJECT = "project";
+	private static final String APPLICATION = "application";
+	private static final String USAGE_DATE = "usageDate";
+	private static final String USER = "user";
+	private static final String RESOURCE_TYPE = "resource_type";
+	private static final String DLAB_ID = "dlabId";
+	private static final String FROM = "from";
+	private static final String TO = "to";
+	private static final String PRODUCT = "product";
+	private static final String CURRENCY = "currency";
+	private static final String COST = "cost";
 
 	@Inject
 	protected SettingsDAO settings;
@@ -102,10 +128,37 @@ public class BaseBillingDAO extends BaseDAO implements BillingDAO {
 	}
 
 	@Override
+	public List<BillingReportLine> findBillingData(List<String> dlabIds) {
+		return find(BILLING, in(DLAB_ID, dlabIds), 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);
+		if (!matchCriteria.isEmpty()) {
+			pipeline.addAll(matchCriteria);
+		}
+		pipeline.add(groupCriteria());
+		return StreamSupport.stream(getCollection(BILLING).aggregate(pipeline).spliterator(), false)
+				.map(this::toBillingReport)
+				.collect(Collectors.toList());
+	}
+
+	@Override
+	public void deleteByUsageDate(String application, String usageDate) {
+		deleteMany(BILLING, and(eq(APPLICATION, application), regex(USAGE_DATE, "^" + usageDate)));
+	}
+
+	@Override
+	public void save(List<BillingReportLine> billingData) {
+		insertMany(BILLING, new ArrayList<>(billingData));
+	}
+
 	private Integer toPercentage(Supplier<Optional<Integer>> allowedBudget, Double totalCost) {
 		return allowedBudget.get()
 				.map(userBudget -> (totalCost * ONE_HUNDRED) / userBudget)
@@ -118,4 +171,54 @@ public class BaseBillingDAO extends BaseDAO implements BillingDAO {
 				.map(d -> d.getDouble(TOTAL_FIELD_NAME))
 				.orElse(BigDecimal.ZERO.doubleValue());
 	}
+
+	private Bson groupCriteria() {
+		return group(getGroupingFields(USER, DLAB_ID, RESOURCE_TYPE, PROJECT, PRODUCT, CURRENCY),
+				sum(COST, "$" + COST),
+				min(FROM, "$" + FROM),
+				max(TO, "$" + TO));
+	}
+
+	private List<Bson> matchCriteria(BillingFilter filter) {
+		List<Bson> searchCriteria = new ArrayList<>();
+
+		if (CollectionUtils.isNotEmpty(filter.getUsers())) {
+			searchCriteria.add(in(USER, filter.getUsers()));
+		}
+		if (CollectionUtils.isNotEmpty(filter.getResourceTypes())) {
+			searchCriteria.add(in(RESOURCE_TYPE, filter.getResourceTypes()));
+		}
+		if (StringUtils.isNotEmpty(filter.getDlabId())) {
+			searchCriteria.add(regex(DLAB_ID, filter.getDlabId(), "i"));
+		}
+		if (StringUtils.isNotEmpty(filter.getDateStart())) {
+			searchCriteria.add(gte(FROM, filter.getDateStart()));
+		}
+		if (StringUtils.isNotEmpty(filter.getDateEnd())) {
+			searchCriteria.add(lte(TO, filter.getDateEnd()));
+		}
+		if (CollectionUtils.isNotEmpty(filter.getProjects())) {
+			searchCriteria.add(in(PROJECT, filter.getProjects()));
+		}
+		if (CollectionUtils.isNotEmpty(filter.getProducts())) {
+			searchCriteria.add(in(PRODUCT, filter.getProducts()));
+		}
+
+		return searchCriteria;
+	}
+
+	private BillingReportLine toBillingReport(Document d) {
+		Document id = (Document) d.get("_id");
+		return BillingReportLine.builder()
+				.dlabId(id.getString(DLAB_ID))
+				.project(id.getString(PROJECT))
+				.user(id.getString(USER))
+				.product(id.getString(PRODUCT))
+				.resourceType(BillingResourceType.valueOf(id.getString(RESOURCE_TYPE)))
+				.usageDateFrom(d.getDate(FROM).toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
+				.usageDateTo(d.getDate(TO).toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
+				.cost(d.getDouble(COST))
+				.currency(id.getString(CURRENCY))
+				.build();
+	}
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseDAO.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseDAO.java
index 034011a..c2ff69b 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseDAO.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/dao/BaseDAO.java
@@ -31,7 +31,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.inject.Inject;
 import com.mongodb.BasicDBObject;
 import com.mongodb.MongoException;
-import com.mongodb.client.*;
+import com.mongodb.client.AggregateIterable;
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoCursor;
+import com.mongodb.client.MongoIterable;
 import com.mongodb.client.model.UpdateOptions;
 import com.mongodb.client.result.DeleteResult;
 import com.mongodb.client.result.UpdateResult;
@@ -41,13 +45,21 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
-import static com.mongodb.client.model.Filters.*;
+import static com.mongodb.client.model.Filters.and;
+import static com.mongodb.client.model.Filters.exists;
+import static com.mongodb.client.model.Filters.ne;
 
 /**
  * Implements the base API for Mongo database.
@@ -158,6 +170,29 @@ public class BaseDAO {
 	}
 
 	/**
+	 * Serializes objects and inserts into the collection.
+	 *
+	 * @param collection collection name.
+	 * @param object     for inserting to collection.
+	 */
+	protected void insertMany(String collection, List<Object> object) {
+		try {
+			mongoService.getCollection(collection)
+					.insertMany(convertToBson(object)
+							.stream()
+							.peek(o -> {
+								o.append(ID, generateUUID());
+								o.append(TIMESTAMP, new Date());
+							})
+							.collect(Collectors.toList())
+					);
+		} catch (MongoException e) {
+			LOGGER.warn("Insert to Mongo DB fails: {}", e.getLocalizedMessage(), e);
+			throw new DlabException("Insert to Mongo DB fails: " + e.getLocalizedMessage(), e);
+		}
+	}
+
+	/**
 	 * Updates single document in the collection by condition.
 	 *
 	 * @param collection collection name.
@@ -230,6 +265,22 @@ public class BaseDAO {
 	}
 
 	/**
+	 * Removes many documents in the collection by condition.
+	 *
+	 * @param collection collection name.
+	 * @param condition  condition for search documents in collection.
+	 */
+	protected DeleteResult deleteMany(String collection, Bson condition) {
+		try {
+			return mongoService.getCollection(collection)
+					.deleteMany(condition);
+		} catch (MongoException e) {
+			LOGGER.warn("Removing document from Mongo DB fails: {}", e.getLocalizedMessage(), e);
+			throw new DlabException("Removing document from Mongo DB fails: " + e.getLocalizedMessage(), e);
+		}
+	}
+
+	/**
 	 * Finds and returns all documents from the collection.
 	 *
 	 * @param collection collection name.
@@ -362,6 +413,13 @@ public class BaseDAO {
 		}
 	}
 
+	List<Document> convertToBson(List<Object> objects) {
+		return objects
+				.stream()
+				.map(this::convertToBson)
+				.collect(Collectors.toList());
+	}
+
 	/**
 	 * Finds and returns one object as given class from the collection by condition.
 	 *
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 d50c62f..3a00d5b 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
@@ -18,6 +18,11 @@
  */
 package com.epam.dlab.backendapi.dao;
 
+import com.epam.dlab.backendapi.domain.BillingReportLine;
+import com.epam.dlab.backendapi.resources.dto.BillingFilter;
+
+import java.util.List;
+
 public interface BillingDAO {
 	Double getTotalCost();
 
@@ -36,4 +41,12 @@ public interface BillingDAO {
 	boolean isUserQuoteReached(String user);
 
 	boolean isProjectQuoteReached(String project);
+
+	List<BillingReportLine> findBillingData(List<String> dlabIds);
+
+	List<BillingReportLine> aggregateBillingData(BillingFilter filter);
+
+	void deleteByUsageDate(String application, String usageDate);
+
+	void save(List<BillingReportLine> billingData);
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLine.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLine.java
index ed97a44..123a5c3 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLine.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/domain/BillingReportLine.java
@@ -21,6 +21,7 @@ package com.epam.dlab.backendapi.domain;
 
 import com.epam.dlab.dto.UserInstanceStatus;
 import com.epam.dlab.dto.billing.BillingResourceType;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Builder;
 import lombok.Data;
@@ -29,8 +30,10 @@ import java.time.LocalDate;
 
 @Data
 @Builder
+@JsonIgnoreProperties(ignoreUnknown = true)
 public class BillingReportLine {
     private String dlabId;
+    private String application;
     @JsonProperty("resource_name")
     private String resourceName;
     private String project;
@@ -39,6 +42,7 @@ public class BillingReportLine {
     private LocalDate usageDateFrom;
     @JsonProperty("to")
     private LocalDate usageDateTo;
+    private String usageDate;
     private String product;
     private String usageType;
     private Double cost;
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/ProjectInfrastructureInfo.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/ProjectInfrastructureInfo.java
index e1f09e9..d215ac2 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/ProjectInfrastructureInfo.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/resources/dto/ProjectInfrastructureInfo.java
@@ -19,8 +19,8 @@
 
 package com.epam.dlab.backendapi.resources.dto;
 
+import com.epam.dlab.backendapi.domain.BillingReportLine;
 import com.epam.dlab.backendapi.domain.EndpointDTO;
-import com.epam.dlab.dto.billing.BillingData;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.AllArgsConstructor;
 import lombok.ToString;
@@ -41,7 +41,7 @@ public class ProjectInfrastructureInfo {
 	@JsonProperty
 	private Iterable<Document> exploratory;
 	@JsonProperty
-	private List<BillingData> exploratoryBilling;
+	private List<BillingReportLine> exploratoryBilling;
 	@JsonProperty
 	private List<EndpointDTO> endpoints;
 }
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/billing/BillingScheduler.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/billing/BillingScheduler.java
new file mode 100644
index 0000000..45563a2
--- /dev/null
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/schedulers/billing/BillingScheduler.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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 com.epam.dlab.backendapi.schedulers.billing;
+
+import com.epam.dlab.backendapi.schedulers.internal.Scheduled;
+import com.epam.dlab.backendapi.service.BillingService;
+import com.epam.dlab.backendapi.service.SecurityService;
+import com.google.inject.Inject;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+
+@Scheduled("billingScheduler")
+@Slf4j
+public class BillingScheduler implements Job {
+
+    private final BillingService billingService;
+    private final SecurityService securityService;
+
+    @Inject
+    public BillingScheduler(BillingService billingService, SecurityService securityService) {
+        this.billingService = billingService;
+        this.securityService = securityService;
+    }
+
+    @Override
+    public void execute(JobExecutionContext jobExecutionContext) {
+        log.info("Trying to update billing");
+        try {
+            billingService.updateRemoteBillingData(securityService.getServiceAccountInfo("admin"));
+        } catch (Exception e) {
+            log.error("Something went wrong {}", e.getMessage());
+        }
+    }
+}
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 40a5c14..7d30f74 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
@@ -23,8 +23,6 @@ import com.epam.dlab.auth.UserInfo;
 import com.epam.dlab.backendapi.domain.BillingReport;
 import com.epam.dlab.backendapi.domain.BillingReportLine;
 import com.epam.dlab.backendapi.resources.dto.BillingFilter;
-import com.epam.dlab.dto.UserInstanceDTO;
-import com.epam.dlab.dto.billing.BillingData;
 
 import java.util.List;
 
@@ -33,7 +31,7 @@ public interface BillingService {
 
     String downloadReport(UserInfo userInfo, BillingFilter filter);
 
-    List<BillingReportLine> getBillingReportLines(UserInfo userInfo, BillingFilter filter);
+    List<BillingReportLine> getExploratoryBillingData(String exploratoryId, List<String> resources);
 
-    List<BillingData> getExploratoryRemoteBillingData(UserInfo user, String endpoint, List<UserInstanceDTO> userInstanceDTOS);
+    void updateRemoteBillingData(UserInfo userInfo);
 }
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 7d6ab11..fdbb789 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
@@ -21,6 +21,7 @@ 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.ImageExploratoryDao;
 import com.epam.dlab.backendapi.domain.BillingReport;
 import com.epam.dlab.backendapi.domain.BillingReportLine;
@@ -35,8 +36,8 @@ import com.epam.dlab.backendapi.service.EndpointService;
 import com.epam.dlab.backendapi.service.ExploratoryService;
 import com.epam.dlab.backendapi.service.ProjectService;
 import com.epam.dlab.backendapi.util.BillingUtils;
+import com.epam.dlab.cloud.CloudProvider;
 import com.epam.dlab.constants.ServiceConsts;
-import com.epam.dlab.dto.UserInstanceDTO;
 import com.epam.dlab.dto.billing.BillingData;
 import com.epam.dlab.exceptions.DlabException;
 import com.epam.dlab.rest.client.RESTService;
@@ -52,7 +53,6 @@ import java.math.BigDecimal;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.time.LocalDate;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -61,19 +61,12 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
-
 @Slf4j
 public class BillingServiceImpl implements BillingService {
     private static final String BILLING_PATH = "/api/billing";
-    private static final String BILLING_REPORT_PATH = "/api/billing/report";
+    private static final String USAGE_DATE_FORMAT = "yyyy-MM";
 
     private final ProjectService projectService;
     private final EndpointService endpointService;
@@ -81,24 +74,28 @@ public class BillingServiceImpl implements BillingService {
     private final SelfServiceApplicationConfiguration configuration;
     private final RESTService provisioningService;
     private final ImageExploratoryDao imageExploratoryDao;
+    private final BillingDAO billingDAO;
     private final String sbn;
 
     @Inject
     public BillingServiceImpl(ProjectService projectService, EndpointService endpointService,
                               ExploratoryService exploratoryService, SelfServiceApplicationConfiguration configuration,
-                              @Named(ServiceConsts.PROVISIONING_SERVICE_NAME) RESTService provisioningService, ImageExploratoryDao imageExploratoryDao) {
+                              @Named(ServiceConsts.PROVISIONING_SERVICE_NAME) RESTService provisioningService, ImageExploratoryDao imageExploratoryDao,
+                              BillingDAO billingDAO) {
         this.projectService = projectService;
         this.endpointService = endpointService;
         this.exploratoryService = exploratoryService;
         this.configuration = configuration;
         this.provisioningService = provisioningService;
         this.imageExploratoryDao = imageExploratoryDao;
+        this.billingDAO = billingDAO;
         sbn = configuration.getServiceBaseName();
     }
 
     @Override
     public BillingReport getBillingReport(UserInfo user, BillingFilter filter) {
-        List<BillingReportLine> billingReportLines = getBillingReportLines(user, filter);
+        setUserFilter(user, filter);
+        List<BillingReportLine> billingReportLines = billingDAO.aggregateBillingData(filter);
         LocalDate min = billingReportLines.stream().min(Comparator.comparing(BillingReportLine::getUsageDateFrom)).map(BillingReportLine::getUsageDateFrom).orElse(null);
         LocalDate max = billingReportLines.stream().max(Comparator.comparing(BillingReportLine::getUsageDateTo)).map(BillingReportLine::getUsageDateTo).orElse(null);
         double sum = billingReportLines.stream().mapToDouble(BillingReportLine::getCost).sum();
@@ -129,31 +126,57 @@ public class BillingServiceImpl implements BillingService {
         }
     }
 
-    @Override
-    public List<BillingReportLine> getBillingReportLines(UserInfo user, BillingFilter filter) {
-        setUserFilter(user, filter);
-        Set<ProjectDTO> projects;
-        if (isFullReport(user)) {
-            projects = new HashSet<>(projectService.getProjects());
-        } else {
-            projects = new HashSet<>(projectService.getProjects(user));
-            projects.addAll(projectService.getUserProjects(user, false));
+    public List<BillingReportLine> getExploratoryBillingData(String exploratoryId, List<String> resources) {
+        List<String> dlabIds = null;
+        try {
+            dlabIds = Stream.concat(
+                    BillingUtils.getExploratoryIds(exploratoryId).stream(),
+                    resources
+                            .stream()
+                            .map(BillingUtils::getComputationalIds)
+                            .flatMap(Collection::stream)
+            )
+                    .collect(Collectors.toList());
+
+            return billingDAO.findBillingData(dlabIds);
+        } catch (Exception e) {
+            log.error("Cannot retrieve billing information for {} {}", dlabIds, e.getMessage());
+            return Collections.emptyList();
         }
+    }
 
-        final Map<String, BillingReportLine> billableResources = getBillableResources(projects);
+    public void updateRemoteBillingData(UserInfo userInfo) {
+        try {
+            List<EndpointDTO> endpoints = endpointService.getEndpoints();
+            if (CollectionUtils.isEmpty(endpoints)) {
+                log.error("Cannot update billing info. There are no endpoints");
+                throw new DlabException("Cannot update billing info. There are no endpoints");
+            }
 
-        List<BillingReportLine> billingReport = getRemoteBillingData(user, filter)
-                .stream()
-                .filter(bd -> billableResources.containsKey(bd.getTag()))
-                .map(bd -> toBillingData(bd, billableResources.get(bd.getTag())))
-                .filter(getBillingReportFilter(filter))
-                .collect(Collectors.toList());
-        log.debug("Billing report: {}", billingReport);
+            Map<EndpointDTO, List<BillingData>> billingDataMap = endpoints
+                    .stream()
+                    .collect(Collectors.toMap(e -> e, e -> provisioningService.get(getBillingUrl(e.getUrl(), BILLING_PATH), userInfo.getAccessToken(),
+                            new GenericType<List<BillingData>>() {
+                            })));
 
-        return billingReport;
+            billingDataMap.forEach((endpointDTO, billingData) -> {
+                log.info("Updating billing information for endpoint {}", endpointDTO.getName());
+                updateBillingData(userInfo, endpointDTO, billingData);
+            });
+        } catch (DlabException e) {
+            log.error("Cannot retrieve billing information for {}", e.getMessage());
+        }
     }
 
-    private Map<String, BillingReportLine> getBillableResources(Set<ProjectDTO> projects) {
+    private Map<String, BillingReportLine> getBillableResources(UserInfo userInfo) {
+        Set<ProjectDTO> projects;
+        if (isFullReport(userInfo)) {
+            projects = new HashSet<>(projectService.getProjects());
+        } else {
+            projects = new HashSet<>(projectService.getProjects(userInfo));
+            projects.addAll(projectService.getUserProjects(userInfo, false));
+        }
+
         final Stream<BillingReportLine> ssnBillingDataStream = BillingUtils.ssnBillingDataStream(sbn);
         final Stream<BillingReportLine> billableEdges = projects
                 .stream()
@@ -189,59 +212,27 @@ public class BillingServiceImpl implements BillingService {
                         endpoint.getStatus().toString()));
     }
 
-    public List<BillingData> getExploratoryRemoteBillingData(UserInfo user, String endpoint, List<UserInstanceDTO> userInstanceDTOS) {
-        List<String> dlabIds = null;
-        try {
-            dlabIds = userInstanceDTOS
-                    .stream()
-                    .map(instance -> Stream.concat(BillingUtils.getExploratoryIds(instance.getExploratoryId()).stream(), instance.getResources()
-                            .stream()
-                            .map(cr -> BillingUtils.getComputationalIds(cr.getComputationalId()))
-                            .flatMap(Collection::stream)
-                    ))
-                    .flatMap(a -> a)
-                    .collect(Collectors.toList());
-
-            EndpointDTO endpointDTO = endpointService.get(endpoint);
-            return provisioningService.get(getBillingUrl(endpointDTO.getUrl(), BILLING_PATH), user.getAccessToken(),
-                    new GenericType<List<BillingData>>() {
-                    }, Collections.singletonMap("dlabIds", String.join(",", dlabIds)));
-        } catch (Exception e) {
-            log.error("Cannot retrieve billing information for {} {}", dlabIds, e.getMessage());
-            return Collections.emptyList();
-        }
-    }
-
-    private List<BillingData> getRemoteBillingData(UserInfo userInfo, BillingFilter filter) {
-        List<EndpointDTO> endpoints = endpointService.getEndpoints();
-        ExecutorService executor = Executors.newFixedThreadPool(endpoints.size());
-        List<Callable<List<BillingData>>> callableTasks = new ArrayList<>();
-        endpoints.forEach(e -> callableTasks.add(getTask(userInfo, getBillingUrl(e.getUrl(), BILLING_REPORT_PATH), filter)));
+    private void updateBillingData(UserInfo userInfo, EndpointDTO endpointDTO, List<BillingData> billingData) {
+        final String endpointName = endpointDTO.getName();
+        final Map<String, BillingReportLine> billableResources = getBillableResources(userInfo);
 
-        List<BillingData> billingData;
-        try {
-            log.debug("Trying to retrieve billing info for {}", endpoints);
-            billingData = executor.invokeAll(callableTasks)
+        if (endpointDTO.getCloudProvider() == CloudProvider.GCP) {
+            final Map<String, List<BillingReportLine>> gcpBillingData = billingData
                     .stream()
-                    .map(this::getBillingReportDTOS)
-                    .flatMap(Collection::stream)
-                    .collect(Collectors.toList());
-        } catch (Exception e) {
-            executor.shutdown();
-            log.error("Cannot retrieve billing information {}", e.getMessage(), e);
-            throw new DlabException("Cannot retrieve billing information", e);
+                    .filter(bd -> billableResources.containsKey(bd.getTag()))
+                    .peek(bd -> bd.setApplication(endpointName))
+                    .map(bd -> toBillingData(bd, billableResources.get(bd.getTag())))
+                    .collect(Collectors.groupingBy(bd -> bd.getUsageDate().substring(0, USAGE_DATE_FORMAT.length())));
+
+            updateGcpBillingData(endpointName, gcpBillingData);
         }
-        executor.shutdown();
-        return billingData;
     }
 
-    private List<BillingData> getBillingReportDTOS(Future<List<BillingData>> s) {
-        try {
-            return s.get();
-        } catch (InterruptedException | ExecutionException e) {
-            log.error("Cannot retrieve billing information {}", e.getMessage());
-            return Collections.emptyList();
-        }
+    private void updateGcpBillingData(String endpointName, Map<String, List<BillingReportLine>> billingData) {
+        billingData.forEach((usageDate, billingReportLines) -> {
+            billingDAO.deleteByUsageDate(endpointName, usageDate);
+            billingDAO.save(billingReportLines);
+        });
     }
 
     private String getBillingUrl(String endpointUrl, String path) {
@@ -260,27 +251,6 @@ public class BillingServiceImpl implements BillingService {
                 .toString();
     }
 
-    private Callable<List<BillingData>> getTask(UserInfo userInfo, String url, BillingFilter filter) {
-        return () -> provisioningService.get(url, userInfo.getAccessToken(),
-                new GenericType<List<BillingData>>() {
-                },
-                Stream.of(new String[][]{
-                        {"date-start", filter.getDateStart()},
-                        {"date-end", filter.getDateEnd()},
-                        {"dlab-id", filter.getDlabId()},
-                        {"product", String.join(",", filter.getProducts())}
-                }).collect(Collectors.toMap(data -> data[0], data -> data[1])));
-    }
-
-    private Predicate<BillingReportLine> getBillingReportFilter(BillingFilter filter) {
-        return br ->
-                (CollectionUtils.isEmpty(filter.getUsers()) || filter.getUsers().contains(br.getUser())) &&
-                        (CollectionUtils.isEmpty(filter.getProjects()) || filter.getProjects().contains(br.getProject())) &&
-                        (CollectionUtils.isEmpty(filter.getResourceTypes()) || filter.getResourceTypes().contains(String.valueOf(br.getResourceType()))) &&
-                        (CollectionUtils.isEmpty(filter.getStatuses()) || filter.getStatuses().contains(br.getStatus())) &&
-                        (CollectionUtils.isEmpty(filter.getShapes()) || filter.getShapes().contains(br.getShape()));
-    }
-
     private boolean isFullReport(UserInfo userInfo) {
         return UserRoles.checkAccess(userInfo, RoleType.PAGE, "/api/infrastructure_provision/billing",
                 userInfo.getRoles());
@@ -294,18 +264,19 @@ public class BillingServiceImpl implements BillingService {
 
     private BillingReportLine toBillingData(BillingData billingData, BillingReportLine billingReportLine) {
         return BillingReportLine.builder()
-                .cost(billingData.getCost())
+                .application(billingData.getApplication())
+                .cost(BigDecimal.valueOf(billingData.getCost()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue())
                 .currency(billingData.getCurrency())
                 .product(billingData.getProduct())
                 .project(billingReportLine.getProject())
-                .usageDateTo(billingData.getUsageDateTo())
                 .usageDateFrom(billingData.getUsageDateFrom())
+                .usageDateTo(billingData.getUsageDateTo())
+                .usageDate(billingData.getUsageDate())
                 .usageType(billingData.getUsageType())
                 .user(billingReportLine.getUser())
                 .dlabId(billingData.getTag())
                 .resourceType(billingReportLine.getResourceType())
                 .resourceName(billingReportLine.getResourceName())
-                .status(billingReportLine.getStatus())
                 .shape(billingReportLine.getShape())
                 .build();
     }
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 28b94e9..88f4b9a 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
@@ -22,8 +22,8 @@ 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.EnvDAO;
 import com.epam.dlab.backendapi.dao.ExploratoryDAO;
+import com.epam.dlab.backendapi.domain.BillingReportLine;
 import com.epam.dlab.backendapi.domain.EndpointDTO;
 import com.epam.dlab.backendapi.domain.ProjectEndpointDTO;
 import com.epam.dlab.backendapi.resources.dto.HealthStatusEnum;
@@ -38,7 +38,6 @@ import com.epam.dlab.dto.InfrastructureMetaInfoDTO;
 import com.epam.dlab.dto.aws.edge.EdgeInfoAws;
 import com.epam.dlab.dto.azure.edge.EdgeInfoAzure;
 import com.epam.dlab.dto.base.edge.EdgeInfo;
-import com.epam.dlab.dto.billing.BillingData;
 import com.epam.dlab.dto.gcp.edge.EdgeInfoGcp;
 import com.epam.dlab.exceptions.DlabException;
 import com.google.inject.Inject;
@@ -46,7 +45,6 @@ import com.jcabi.manifests.Manifests;
 import lombok.extern.slf4j.Slf4j;
 import org.bson.Document;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -62,7 +60,6 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 	private static final String RELEASE_NOTES_FORMAT = "https://github.com/apache/incubator-dlab/blob/%s" +
 			"/RELEASE_NOTES.md";
 	private final ExploratoryDAO expDAO;
-	private final EnvDAO envDAO;
 	private final SelfServiceApplicationConfiguration configuration;
 	private final BillingDAO billingDAO;
 	private final ProjectService projectService;
@@ -70,11 +67,10 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 	private final BillingService billingService;
 
 	@Inject
-	public InfrastructureInfoServiceImpl(ExploratoryDAO expDAO, EnvDAO envDAO, SelfServiceApplicationConfiguration configuration,
+	public InfrastructureInfoServiceImpl(ExploratoryDAO expDAO, SelfServiceApplicationConfiguration configuration,
 										 BillingDAO billingDAO, ProjectService projectService, EndpointService endpointService,
 										 BillingService billingService) {
 		this.expDAO = expDAO;
-		this.envDAO = envDAO;
 		this.configuration = configuration;
 		this.billingDAO = billingDAO;
 		this.projectService = projectService;
@@ -88,8 +84,7 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 		try {
 			Iterable<Document> documents = expDAO.findExploratory(user.getName());
 			List<EndpointDTO> allEndpoints = endpointService.getEndpoints();
-			return StreamSupport.stream(documents.spliterator(),
-					false)
+			return StreamSupport.stream(documents.spliterator(), false)
 					.collect(Collectors.groupingBy(d -> d.getString("project")))
 					.entrySet()
 					.stream()
@@ -100,18 +95,15 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 										.anyMatch(endpoint1 -> endpoint1.getName().equals(endpoint.getName())))
 								.collect(Collectors.toList());
 
-						List<BillingData> collect = e.getValue()
+						List<BillingReportLine> billingData = e.getValue()
 								.stream()
-								.map(exp -> {
-									List<BillingData> exploratoryRemoteBillingData = new ArrayList<>();
-									try {
-										exploratoryRemoteBillingData = billingService.getExploratoryRemoteBillingData(user, (String) exp.get("endpoint"),
-												expDAO.findExploratories(e.getKey(), (String) exp.get("endpoint"), user.getName()));
-									} catch (Exception ex) {
-										log.error("Cannot retrieve billing information", ex);
-									}
-									return exploratoryRemoteBillingData;
-								})
+								.map(exp ->
+										billingService.getExploratoryBillingData(exp.getString("exploratory_id"),
+												Optional.ofNullable(exp.get("computational_resources")).map(cr -> (List<Document>) cr).get()
+														.stream()
+														.map(cr -> cr.getString("computational_id"))
+														.collect(Collectors.toList()))
+								)
 								.flatMap(Collection::stream)
 								.collect(Collectors.toList());
 
@@ -120,7 +112,7 @@ public class InfrastructureInfoServiceImpl implements InfrastructureInfoService
 										.stream()
 										.collect(Collectors.toMap(ProjectEndpointDTO::getName, this::getSharedInfo));
 						return new ProjectInfrastructureInfo(e.getKey(),
-								billingDAO.getBillingProjectQuoteUsed(e.getKey()), projectEdges, e.getValue(), collect, endpointResult);
+								billingDAO.getBillingProjectQuoteUsed(e.getKey()), projectEdges, e.getValue(), billingData, endpointResult);
 					})
 					.collect(Collectors.toList());
 		} catch (Exception e) {
diff --git a/services/self-service/src/main/java/com/epam/dlab/backendapi/util/BillingUtils.java b/services/self-service/src/main/java/com/epam/dlab/backendapi/util/BillingUtils.java
index c8454b2..16b828c 100644
--- a/services/self-service/src/main/java/com/epam/dlab/backendapi/util/BillingUtils.java
+++ b/services/self-service/src/main/java/com/epam/dlab/backendapi/util/BillingUtils.java
@@ -22,7 +22,6 @@ package com.epam.dlab.backendapi.util;
 import com.epam.dlab.backendapi.domain.BillingReportLine;
 import com.epam.dlab.backendapi.resources.dto.ImageInfoRecord;
 import com.epam.dlab.dto.UserInstanceDTO;
-import com.epam.dlab.dto.UserInstanceStatus;
 import com.epam.dlab.dto.base.DataEngineType;
 import com.epam.dlab.dto.computational.UserComputationalResource;
 import jersey.repackaged.com.google.common.collect.Lists;
@@ -78,7 +77,7 @@ public class BillingUtils {
         final String endpointBucketId = String.format(PROJECT_ENDPOINT_BUCKET_FORMAT, sbn, project.toLowerCase(), endpoint);
 
         return Stream.concat(Stream.of(
-                BillingReportLine.builder().resourceName("EDGE node").user(SHARED_RESOURCE).project(project).dlabId(userEdgeId).resourceType(EDGE).status(UserInstanceStatus.of(status)).build(),
+                BillingReportLine.builder().resourceName("EDGE node").user(SHARED_RESOURCE).project(project).dlabId(userEdgeId).resourceType(EDGE).build(),
                 BillingReportLine.builder().resourceName("EDGE volume").user(SHARED_RESOURCE).project(project).dlabId(edgeVolumeId).resourceType(VOLUME).build(),
                 BillingReportLine.builder().resourceName("Project endpoint shared bucket").user(SHARED_RESOURCE).project(project).dlabId(endpointBucketId).resourceType(BUCKET).build()
                 ),
@@ -108,8 +107,7 @@ public class BillingUtils {
                 .stream()
                 .filter(cr -> cr.getComputationalId() != null)
                 .flatMap(cr -> Stream.concat(Stream.of(
-                        withUserProject(userInstance).dlabId(cr.getComputationalId()).resourceName(cr.getComputationalName()).resourceType(COMPUTATIONAL)
-                                .status(UserInstanceStatus.of(cr.getStatus())).shape(getComputationalShape(cr)).build(),
+                        withUserProject(userInstance).dlabId(cr.getComputationalId()).resourceName(cr.getComputationalName()).resourceType(COMPUTATIONAL).shape(getComputationalShape(cr)).build(),
                         withUserProject(userInstance).resourceName(cr.getComputationalName() + ":" + VOLUME_PRIMARY).dlabId(String.format(VOLUME_PRIMARY_COMPUTATIONAL_FORMAT, cr.getComputationalId(), "m"))
                                 .resourceType(VOLUME).build(),
                         withUserProject(userInstance).resourceName(cr.getComputationalName() + ":" + VOLUME_SECONDARY).dlabId(String.format(VOLUME_SECONDARY_COMPUTATIONAL_FORMAT, cr.getComputationalId(), "m"))
@@ -121,7 +119,7 @@ public class BillingUtils {
         final String primaryVolumeId = String.format(VOLUME_PRIMARY_FORMAT, exploratoryId);
         final String secondaryVolumeId = String.format(VOLUME_SECONDARY_FORMAT, exploratoryId);
         final Stream<BillingReportLine> exploratoryStream = Stream.of(
-                withUserProject(userInstance).resourceName(userInstance.getExploratoryName()).dlabId(exploratoryId).resourceType(EXPLORATORY).status(UserInstanceStatus.of(userInstance.getStatus())).shape(userInstance.getShape()).build(),
+                withUserProject(userInstance).resourceName(userInstance.getExploratoryName()).dlabId(exploratoryId).resourceType(EXPLORATORY).shape(userInstance.getShape()).build(),
                 withUserProject(userInstance).resourceName(VOLUME_PRIMARY).dlabId(primaryVolumeId).resourceType(VOLUME).build(),
                 withUserProject(userInstance).resourceName(VOLUME_SECONDARY).dlabId(secondaryVolumeId).resourceType(VOLUME).build());
 


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