diff --git a/src/main/java/cloud/fogbow/accs/api/http/AccsExceptionToHttpErrorConditionTranslator.java b/src/main/java/cloud/fogbow/accs/api/http/AccsExceptionToHttpErrorConditionTranslator.java new file mode 100644 index 0000000..1356572 --- /dev/null +++ b/src/main/java/cloud/fogbow/accs/api/http/AccsExceptionToHttpErrorConditionTranslator.java @@ -0,0 +1,9 @@ +package cloud.fogbow.accs.api.http; + +import org.springframework.web.bind.annotation.ControllerAdvice; + +import cloud.fogbow.common.http.FogbowExceptionToHttpErrorConditionTranslator; + +@ControllerAdvice +public class AccsExceptionToHttpErrorConditionTranslator extends FogbowExceptionToHttpErrorConditionTranslator { +} diff --git a/src/main/java/cloud/fogbow/accs/api/http/request/ResourceUsage.java b/src/main/java/cloud/fogbow/accs/api/http/request/ResourceUsage.java index af77222..b6e50aa 100644 --- a/src/main/java/cloud/fogbow/accs/api/http/request/ResourceUsage.java +++ b/src/main/java/cloud/fogbow/accs/api/http/request/ResourceUsage.java @@ -53,6 +53,32 @@ public ResponseEntity> getResourceUsageFromOtherUser( throw e; } } + + @RequestMapping(value = "/{userId}/{requester}/{provider}/{initialDate}/{finalDate}", + method = RequestMethod.GET) + public ResponseEntity> getAllResourcesUsageFromOtherUser( + @ApiParam(value = ApiDocumentation.ResourceUsage.USER_ID) + @PathVariable String userId, + @ApiParam(value = ApiDocumentation.ResourceUsage.REQUESTER) + @PathVariable String requester, + @ApiParam(value = ApiDocumentation.ResourceUsage.PROVIDER) + @PathVariable String provider, + @ApiParam(value = ApiDocumentation.ResourceUsage.INITIAL_DATE) + @PathVariable String initialDate, + @ApiParam(value = ApiDocumentation.ResourceUsage.FINAL_DATE) + @PathVariable String finalDate, + @ApiParam(value = cloud.fogbow.common.constants.ApiDocumentation.Token.SYSTEM_USER_TOKEN) + @RequestHeader(value = SystemConstants.SYSTEM_USER_TOKEN_HEADER_KEY) String systemUserToken) + throws Exception { + try { + List records = ApplicationFacade.getInstance().getAllResourcesUserRecords(userId, requester, + provider, initialDate, finalDate, systemUserToken); + return new ResponseEntity<>(records, HttpStatus.OK); + } catch (Exception e) { + LOGGER.info(String.format(Messages.Exception.GENERIC_EXCEPTION, e.getMessage()), e); + throw e; + } + } @ApiOperation(value = ApiDocumentation.ResourceUsage.GET_OPERATION_FROM_SYSTEM_USER) @RequestMapping(value = "/{requester}/{resourceType}/{initialDate}/{finalDate}", method = RequestMethod.GET) diff --git a/src/main/java/cloud/fogbow/accs/api/http/response/AccsApiUtils.java b/src/main/java/cloud/fogbow/accs/api/http/response/AccsApiUtils.java new file mode 100644 index 0000000..b8a0e93 --- /dev/null +++ b/src/main/java/cloud/fogbow/accs/api/http/response/AccsApiUtils.java @@ -0,0 +1,198 @@ +package cloud.fogbow.accs.api.http.response; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.Gson; +import com.google.gson.internal.LinkedTreeMap; + +import cloud.fogbow.accs.constants.Messages; +import cloud.fogbow.accs.core.models.OrderStateHistory; +import cloud.fogbow.accs.core.models.orders.OrderState; +import cloud.fogbow.accs.core.models.specs.ComputeSpec; +import cloud.fogbow.accs.core.models.specs.NetworkSpec; +import cloud.fogbow.accs.core.models.specs.OrderSpec; +import cloud.fogbow.accs.core.models.specs.VolumeSpec; +import cloud.fogbow.common.exceptions.InvalidParameterException; + +public class AccsApiUtils { + private static final String RESOURCE_TYPE_KEY = "resourceType"; + private static final String COMPUTE_RESOURCE = "compute"; + private static final String VOLUME_RESOURCE = "volume"; + private static final String NETWORK_RESOURCE = "network"; + + public AccsApiUtils() { + } + + public List getRecordsFromString(String recordsString) throws InvalidParameterException { + ArrayList recordList = new ArrayList(); + Gson gson = new Gson(); + + ArrayList> rawRecordsList = + gson.fromJson(recordsString, ArrayList.class); + + for (LinkedTreeMap rawRecord : rawRecordsList) { + Record record; + String recordType = (String) rawRecord.get(RESOURCE_TYPE_KEY); + + switch(recordType) { + case COMPUTE_RESOURCE: record = getComputeRecord(rawRecord); break; + case VOLUME_RESOURCE: record = getVolumeRecord(rawRecord); break; + case NETWORK_RESOURCE: record = getNetworkRecord(rawRecord); break; + default: throw new InvalidParameterException( + String.format(Messages.Exception.INVALID_RECORD_TYPE, recordType)); + } + + recordList.add(record); + } + + return recordList; + } + + private ComputeRecord getComputeRecord(LinkedTreeMap rawRecord) throws InvalidParameterException { + Gson gson = new Gson(); + String jsonRepr = gson.toJson((LinkedTreeMap) rawRecord); + ComputeRecord computeRecord = gson.fromJson(jsonRepr, ComputeRecord.class); + computeRecord.validate(); + return computeRecord; + } + + private VolumeRecord getVolumeRecord(LinkedTreeMap rawRecord) throws InvalidParameterException { + Gson gson = new Gson(); + String jsonRepr = gson.toJson((LinkedTreeMap) rawRecord); + VolumeRecord volumeRecord = gson.fromJson(jsonRepr, VolumeRecord.class); + volumeRecord.validate(); + return volumeRecord; + } + + private Record getNetworkRecord(LinkedTreeMap rawRecord) throws InvalidParameterException { + Gson gson = new Gson(); + String jsonRepr = gson.toJson((LinkedTreeMap) rawRecord); + NetworkRecord networkRecord = gson.fromJson(jsonRepr, NetworkRecord.class); + networkRecord.validate(); + return networkRecord; + } + + public class ComputeRecord extends Record { + private ComputeSpec spec; + + public ComputeRecord(Long id, String orderId, String resourceType, ComputeSpec spec, String requester, Timestamp startTime, + Timestamp startDate, Timestamp endTime, Timestamp endDate, long duration, OrderState state, + OrderStateHistory stateHistory) throws InvalidParameterException { + super(id, orderId, resourceType, requester, startTime, startDate, endTime, endDate, + duration, state, stateHistory); + this.spec = spec; + } + + @Override + public ComputeSpec getSpec() { + return spec; + } + + @Override + public void setSpec(OrderSpec orderSpec) { + this.spec = (ComputeSpec) orderSpec; + } + + public void validate() throws InvalidParameterException { + checkRecordPropertyIsNotNull("resourceType", getResourceType()); + checkRecordPropertyIsNotNull("spec", getSpec()); + checkRecordPropertyIsNotNull("startTime", getStartTime()); + checkRecordPropertyIsNotNull("startDate", getStartDate()); + checkRecordPropertyIsNotNull("stateHistory", getStateHistory()); + } + } + + public class VolumeRecord extends Record { + private VolumeSpec spec; + + public VolumeRecord(Long id, String orderId, String resourceType, VolumeSpec spec, String requester, + Timestamp startTime, Timestamp startDate, Timestamp endTime, Timestamp endDate, long duration, + OrderState state, OrderStateHistory stateHistory) { + super(id, orderId, resourceType, requester, startTime, startDate, endTime, endDate, duration, + state, stateHistory); + this.spec = spec; + } + + @Override + public VolumeSpec getSpec() { + return spec; + } + + @Override + public void setSpec(OrderSpec orderSpec) { + this.spec = (VolumeSpec) orderSpec; + } + + public void validate() throws InvalidParameterException { + checkRecordPropertyIsNotNull("resourceType", getResourceType()); + checkRecordPropertyIsNotNull("spec", getSpec()); + checkRecordPropertyIsNotNull("startTime", getStartTime()); + checkRecordPropertyIsNotNull("startDate", getStartDate()); + checkRecordPropertyIsNotNull("stateHistory", getStateHistory()); + } + } + + public class NetworkRecord extends Record { + private NetworkSpec spec; + + public NetworkRecord(Long id, String orderId, String resourceType, NetworkSpec spec, String requester, + Timestamp startTime, Timestamp startDate, Timestamp endTime, Timestamp endDate, long duration, + OrderState state, OrderStateHistory stateHistory) { + super(id, orderId, resourceType, requester, startTime, startDate, endTime, endDate, + duration, state, stateHistory); + this.spec = spec; + } + + @Override + public NetworkSpec getSpec() { + return spec; + } + + @Override + public void setSpec(OrderSpec orderSpec) { + this.spec = (NetworkSpec) orderSpec; + } + + public void validate() throws InvalidParameterException { + checkRecordPropertyIsNotNull("resourceType", getResourceType()); + checkRecordPropertyIsNotNull("spec", getSpec()); + checkRecordPropertyIsNotNull("startTime", getStartTime()); + checkRecordPropertyIsNotNull("startDate", getStartDate()); + checkRecordPropertyIsNotNull("stateHistory", getStateHistory()); + } + } + + private static void checkRecordPropertyIsNotNull(String propertyName, Object o) + throws InvalidParameterException { + if (o == null) { + throw new InvalidParameterException( + String.format(Messages.Exception.INVALID_RECORD_PROPERTY, propertyName)); + } + } + + public Record mountResponseRecord(cloud.fogbow.accs.core.models.Record dbRecord) throws InvalidParameterException { + OrderSpec spec = dbRecord.getSpec(); + + if (spec instanceof ComputeSpec) { + return new ComputeRecord(dbRecord.getId(), dbRecord.getOrderId(), dbRecord.getResourceType(), (ComputeSpec) dbRecord.getSpec(), + dbRecord.getRequestingMember(), dbRecord.getStartTime(), dbRecord.getStartDate(), + dbRecord.getEndTime(), dbRecord.getEndDate(), dbRecord.getDuration(), dbRecord.getState(), + dbRecord.getStateHistory()); + } else if (spec instanceof VolumeSpec) { + return new VolumeRecord(dbRecord.getId(), dbRecord.getOrderId(), dbRecord.getResourceType(), (VolumeSpec) dbRecord.getSpec(), + dbRecord.getRequestingMember(), dbRecord.getStartTime(), dbRecord.getStartDate(), + dbRecord.getEndTime(), dbRecord.getEndDate(), dbRecord.getDuration(), dbRecord.getState(), + dbRecord.getStateHistory()); + } else if (spec instanceof NetworkSpec) { + return new NetworkRecord(dbRecord.getId(), dbRecord.getOrderId(), dbRecord.getResourceType(), (NetworkSpec) dbRecord.getSpec(), + dbRecord.getRequestingMember(), dbRecord.getStartTime(), dbRecord.getStartDate(), + dbRecord.getEndTime(), dbRecord.getEndDate(), dbRecord.getDuration(), dbRecord.getState(), + dbRecord.getStateHistory()); + } else { + // TODO error + return null; + } + } +} diff --git a/src/main/java/cloud/fogbow/accs/api/http/response/Record.java b/src/main/java/cloud/fogbow/accs/api/http/response/Record.java index c9cd620..5719bb0 100644 --- a/src/main/java/cloud/fogbow/accs/api/http/response/Record.java +++ b/src/main/java/cloud/fogbow/accs/api/http/response/Record.java @@ -1,5 +1,6 @@ package cloud.fogbow.accs.api.http.response; +import cloud.fogbow.accs.core.models.OrderStateHistory; import cloud.fogbow.accs.core.models.orders.OrderState; import cloud.fogbow.accs.core.models.specs.OrderSpec; import cloud.fogbow.accs.constants.ApiDocumentation; @@ -10,7 +11,7 @@ import java.util.Objects; @ApiModel -public class Record { +public abstract class Record { @ApiModelProperty(position = 0, example = ApiDocumentation.Record.RECORD_ID) private Long id; @@ -21,8 +22,8 @@ public class Record { @ApiModelProperty(position = 2, example = ApiDocumentation.Record.RESOURCE_TYPE) private String resourceType; - @ApiModelProperty - private OrderSpec spec; + // TODO update documentation + // The spec field was moved to the Record subclasses @ApiModelProperty(position = 4, example = ApiDocumentation.Record.REQUESTER) private String requester; @@ -44,6 +45,9 @@ public class Record { @ApiModelProperty(position = 10, example = ApiDocumentation.Record.STATE) private OrderState state; + + // TODO update documentation + private OrderStateHistory stateHistory; public Long getId() { return id; @@ -69,13 +73,8 @@ public void setResourceType(String resourceType) { this.resourceType = resourceType; } - public OrderSpec getSpec() { - return spec; - } - - public void setSpec(OrderSpec spec) { - this.spec = spec; - } + public abstract OrderSpec getSpec(); + public abstract void setSpec(OrderSpec spec); public void setEndTime(Timestamp endTime) { this.endTime = endTime; @@ -132,6 +131,14 @@ public void setEndDate(Timestamp endDate) { public Timestamp getEndDate() { return endDate; } + + public OrderStateHistory getStateHistory() { + return stateHistory; + } + + public void setStateHistory(OrderStateHistory stateHistory) { + this.stateHistory = stateHistory; + } @Override public boolean equals(Object o) { @@ -142,22 +149,22 @@ public boolean equals(Object o) { id.equals(record.id) && Objects.equals(orderId, record.orderId) && Objects.equals(resourceType, record.resourceType) && - Objects.equals(spec, record.spec) && Objects.equals(requester, record.requester) && Objects.equals(startTime, record.startTime); } @Override public int hashCode() { - return Objects.hash(id, orderId, resourceType, spec, requester, startTime, duration); + return Objects.hash(id, orderId, resourceType, requester, startTime, duration); } - public Record(Long id, String orderId, String resourceType, OrderSpec spec, String requester, Timestamp startTime, - Timestamp startDate, Timestamp endTime, Timestamp endDate, long duration, OrderState state) { + public Record(Long id, String orderId, String resourceType, + String requester, Timestamp startTime, + Timestamp startDate, Timestamp endTime, Timestamp endDate, long duration, OrderState state, + OrderStateHistory orderStateHistory) { this.id = id; this.orderId = orderId; this.resourceType = resourceType; - this.spec = spec; this.requester = requester; this.startTime = startTime; this.startDate = startDate; @@ -165,6 +172,7 @@ public Record(Long id, String orderId, String resourceType, OrderSpec spec, Stri this.endTime = endTime; this.duration = duration; this.state = state; + this.stateHistory = orderStateHistory; } public Record() {} diff --git a/src/main/java/cloud/fogbow/accs/constants/Messages.java b/src/main/java/cloud/fogbow/accs/constants/Messages.java index 74301eb..3b7ecfa 100644 --- a/src/main/java/cloud/fogbow/accs/constants/Messages.java +++ b/src/main/java/cloud/fogbow/accs/constants/Messages.java @@ -13,6 +13,8 @@ public static class Exception { public static final String BILLING_PREDICTIONS = "Accounting predictions are not allowed."; public static final String START_TIME_GREATER_THAN_END_TIME = "Begin time must be smaller than end time."; public static final String GENERIC_EXCEPTION = "Operation returned error: %s."; + public static final String INVALID_RECORD_TYPE = "Invalid record type: %s."; + public static final String INVALID_RECORD_PROPERTY = "Invalid record property: %s."; } public static class Info { diff --git a/src/main/java/cloud/fogbow/accs/constants/SystemConstants.java b/src/main/java/cloud/fogbow/accs/constants/SystemConstants.java index d7f417a..3ee382f 100644 --- a/src/main/java/cloud/fogbow/accs/constants/SystemConstants.java +++ b/src/main/java/cloud/fogbow/accs/constants/SystemConstants.java @@ -7,4 +7,6 @@ public class SystemConstants { public static final String WHITE_LIST_FILE = "white-list.conf"; public static final String SERVICE_BASE_ENDPOINT = "accs/"; public static final String API_VERSION_NUMBER = "1.0.0"; + public static final String SIMPLE_DATE_FORMAT = "yyyy-MM-dd"; + public static final String COMPLETE_DATE_FORMAT = "yyyy-MM-dd_HH:mm:ss"; } diff --git a/src/main/java/cloud/fogbow/accs/core/ApplicationFacade.java b/src/main/java/cloud/fogbow/accs/core/ApplicationFacade.java index 9408e9d..1b8e629 100644 --- a/src/main/java/cloud/fogbow/accs/core/ApplicationFacade.java +++ b/src/main/java/cloud/fogbow/accs/core/ApplicationFacade.java @@ -1,6 +1,7 @@ package cloud.fogbow.accs.core; import cloud.fogbow.accs.api.http.response.Record; +import cloud.fogbow.accs.api.http.response.AccsApiUtils; import cloud.fogbow.accs.constants.Messages; import cloud.fogbow.accs.core.datastore.DatabaseManager; import cloud.fogbow.accs.core.models.AccountingOperation; @@ -12,6 +13,7 @@ import cloud.fogbow.common.exceptions.FogbowException; import cloud.fogbow.common.exceptions.UnauthorizedRequestException; import cloud.fogbow.common.exceptions.InternalServerErrorException; +import cloud.fogbow.common.exceptions.InvalidParameterException; import cloud.fogbow.common.models.SystemUser; import cloud.fogbow.common.plugins.authorization.AuthorizationPlugin; import cloud.fogbow.common.util.CryptoUtil; @@ -73,6 +75,21 @@ public List getUserRecords(String userId, String requestingMember, Strin return records; } + public List getAllResourcesUserRecords(String userId, String requestingMember, String providingMember, + String intervalStart, String intervalEnd, String systemUserToken) throws Exception { + handleAuthIssues(systemUserToken, AccountingOperationType.OTHERS_BILLING); + + List records = new ArrayList<>(); + List dbRecords = dbManager.getUserRecords( + userId, requestingMember, providingMember, intervalStart, intervalEnd); + + for (cloud.fogbow.accs.core.models.Record record : dbRecords) { + records.add(this.mountResponseRecord(record)); + } + + return records; + } + public String getPublicKey() throws InternalServerErrorException { // There is no need to authenticate the user or authorize this operation try { @@ -86,11 +103,8 @@ public void setAuthorizationPlugin(AuthorizationPlugin authorizationPlugin) { this.authorizationPlugin = authorizationPlugin; } - protected Record mountResponseRecord(cloud.fogbow.accs.core.models.Record dbRecord) { - return (new Record(dbRecord.getId(), dbRecord.getOrderId(), dbRecord.getResourceType(), dbRecord.getSpec(), - dbRecord.getRequestingMember(), dbRecord.getStartTime(), dbRecord.getStartDate(), - dbRecord.getEndTime(), dbRecord.getEndDate(), dbRecord.getDuration(), dbRecord.getState())); - + protected Record mountResponseRecord(cloud.fogbow.accs.core.models.Record dbRecord) throws InvalidParameterException { + return new AccsApiUtils().mountResponseRecord(dbRecord); } protected SystemUser handleAuthIssues(String systemUserToken, AccountingOperationType operationType) throws FogbowException{ diff --git a/src/main/java/cloud/fogbow/accs/core/SpecFactory.java b/src/main/java/cloud/fogbow/accs/core/SpecFactory.java index bbca896..5186ef6 100644 --- a/src/main/java/cloud/fogbow/accs/core/SpecFactory.java +++ b/src/main/java/cloud/fogbow/accs/core/SpecFactory.java @@ -27,7 +27,7 @@ public OrderSpec constructSpec(Order order) { ResourceType resourceType = order.getType(); switch (resourceType) { case COMPUTE: - spec = new ComputeSpec(((ComputeOrder) order).getvCPU(), ((ComputeOrder) order).getMemory()); + spec = new ComputeSpec(((ComputeOrder) order).getvCPU(), ((ComputeOrder) order).getRam()); break; case NETWORK: spec = new NetworkSpec(((NetworkOrder) order).getCidr(), ((NetworkOrder) order).getAllocationMode()); diff --git a/src/main/java/cloud/fogbow/accs/core/datastore/DatabaseManager.java b/src/main/java/cloud/fogbow/accs/core/datastore/DatabaseManager.java index 77188d9..8db4a35 100644 --- a/src/main/java/cloud/fogbow/accs/core/datastore/DatabaseManager.java +++ b/src/main/java/cloud/fogbow/accs/core/datastore/DatabaseManager.java @@ -71,6 +71,11 @@ public List getUserRecords(String userId, String requestingMember, Strin return recordService.getUserRecords(userId, requestingMember, providingMember, resourceType, intervalStart, intervalEnd); } + public List getUserRecords(String userId, String requestingMember, String providingMember, + String intervalStart, String intervalEnd) throws ParseException { + return recordService.getAllResourcesUserRecords(userId, requestingMember, providingMember, intervalStart, intervalEnd); + } + public AuditableOrderIdRecorder getIdRecorder() { Optional idRecorderOptional = auditableOrderIdRecorderRepository.findById(SystemConstants.ID_RECORDER_KEY); diff --git a/src/main/java/cloud/fogbow/accs/core/datastore/accountingstorage/RecordRepository.java b/src/main/java/cloud/fogbow/accs/core/datastore/accountingstorage/RecordRepository.java index d63e1a3..2a1b0e1 100644 --- a/src/main/java/cloud/fogbow/accs/core/datastore/accountingstorage/RecordRepository.java +++ b/src/main/java/cloud/fogbow/accs/core/datastore/accountingstorage/RecordRepository.java @@ -24,4 +24,9 @@ public List findByUserAndRequestingMemberAndResourceTypeAndStartDateLess public List findByUserAndRequestingMemberAndResourceTypeAndStartDateLessThanEqualAndStartDateGreaterThanEqualAndStateEquals( AccountingUser user, String requestingMember, String resourceType, Timestamp endTime, Timestamp startTime, OrderState state); + public List findByUserAndRequestingMemberAndStartDateLessThanEqualAndStartDateGreaterThanEqualAndStateEquals( + AccountingUser user, String requestingMember, Timestamp endTime, Timestamp startTime, OrderState fulfilled); + + public List findByUserAndRequestingMemberAndStartDateLessThanEqualAndEndDateGreaterThanEqual( + AccountingUser user, String requestingMember, Timestamp endTime, Timestamp beginTime); } diff --git a/src/main/java/cloud/fogbow/accs/core/datastore/services/RecordService.java b/src/main/java/cloud/fogbow/accs/core/datastore/services/RecordService.java index 539a759..6e3153a 100644 --- a/src/main/java/cloud/fogbow/accs/core/datastore/services/RecordService.java +++ b/src/main/java/cloud/fogbow/accs/core/datastore/services/RecordService.java @@ -1,6 +1,7 @@ package cloud.fogbow.accs.core.datastore.services; import cloud.fogbow.accs.constants.Messages; +import cloud.fogbow.accs.constants.SystemConstants; import cloud.fogbow.accs.core.datastore.accountingstorage.RecordRepository; import cloud.fogbow.accs.core.exceptions.InvalidIntervalException; import cloud.fogbow.accs.core.models.*; @@ -12,17 +13,24 @@ import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; @Service public class RecordService { + // This list contains all the OrderStates that can be described as 'Opened'. + // If a Record is opened, then its state is contained in the list. + private static final List OPENED_RECORDS_STATES = Arrays.asList(OrderState.FULFILLED, + OrderState.PAUSING, OrderState.PAUSED, + OrderState.HIBERNATING, OrderState.HIBERNATED, OrderState.STOPPING, + OrderState.STOPPED, OrderState.SPAWNING); + @Autowired private RecordRepository recordRepository; - private static final String SIMPLE_DATE_FORMAT = "yyyy-MM-dd"; - public RecordService() {} public List getSelfRecords(String requestingMember, String resourceType, String intervalStart, @@ -64,6 +72,56 @@ public List getUserRecords(String userId, String requestingMember, Strin return records; } + + public List getAllResourcesUserRecords(String userId, String requestingMember, String providingMember, + String intervalStart, String intervalEnd) throws ParseException { + Timestamp start = getTimestampFromString(intervalStart); + Timestamp end = getTimestampFromString(intervalEnd); + + checkInterval(start, end); + + AccountingUser user = new AccountingUser(new UserIdentity(providingMember, userId)); + + List closedRecords = this.getClosedRecords(user, requestingMember, start, end); + List openedRecords = this.getOpenedRecords(user, requestingMember, start, end); + + setOpenedRecordsDuration(openedRecords); + + openedRecords.addAll(closedRecords); + List records = openedRecords; + + return records; + } + + protected List getOpenedRecords(AccountingUser user, String requestingMember, Timestamp startTime, Timestamp endTime) { + List openedRecords = new ArrayList(); + + for (OrderState state : OPENED_RECORDS_STATES) { + openedRecords.addAll(this.getRecordsByState(user, requestingMember, + startTime, endTime, state)); + } + + return openedRecords; + } + + protected List getOpenedRecords(AccountingUser user, String requestingMember, String resourceType, + Timestamp startTime, Timestamp endTime) { + List openedRecords = new ArrayList(); + + for (OrderState state : OPENED_RECORDS_STATES) { + openedRecords.addAll(this.getRecordsByState(user, requestingMember, + resourceType, startTime, endTime, state)); + } + + return openedRecords; + } + + protected List getClosedRecords(AccountingUser user, String requestingMember, Timestamp beginTime, + Timestamp endTime) { + return recordRepository.findByUserAndRequestingMemberAndStartDateLessThanEqualAndEndDateGreaterThanEqual( + user, requestingMember, endTime, beginTime + ); + } protected List getClosedRecords(AccountingUser user, String requestingMember, String resourceType, Timestamp beginTime, Timestamp endTime) { return recordRepository.findByUserAndRequestingMemberAndResourceTypeAndStartDateLessThanEqualAndEndDateGreaterThanEqual( @@ -71,14 +129,29 @@ protected List getClosedRecords(AccountingUser user, String requestingMe ); } - protected List getOpenedRecords(AccountingUser user, String requestingMember, String resourceType, Timestamp startTime, Timestamp endTime) { + protected List getRecordsByState(AccountingUser user, String requestingMember, + Timestamp startTime, Timestamp endTime, OrderState state) { + return recordRepository.findByUserAndRequestingMemberAndStartDateLessThanEqualAndStartDateGreaterThanEqualAndStateEquals( + user, requestingMember, endTime, startTime, state + ); + } + + protected List getRecordsByState(AccountingUser user, String requestingMember, String resourceType, + Timestamp startTime, Timestamp endTime, OrderState state) { return recordRepository.findByUserAndRequestingMemberAndResourceTypeAndStartDateLessThanEqualAndStartDateGreaterThanEqualAndStateEquals( - user, requestingMember, resourceType, endTime, startTime, OrderState.FULFILLED + user, requestingMember, resourceType, endTime, startTime, state ); } - + protected Timestamp getTimestampFromString(String date) throws ParseException{ - Date dateRepresentation = new SimpleDateFormat(SIMPLE_DATE_FORMAT).parse(date); + Date dateRepresentation = null; + + try { + dateRepresentation = new SimpleDateFormat(SystemConstants.COMPLETE_DATE_FORMAT).parse(date); + } catch (ParseException e) { + dateRepresentation = new SimpleDateFormat(SystemConstants.SIMPLE_DATE_FORMAT).parse(date); + } + return new Timestamp(dateRepresentation.getTime()); } diff --git a/src/main/java/cloud/fogbow/accs/core/models/OrderStateHistory.java b/src/main/java/cloud/fogbow/accs/core/models/OrderStateHistory.java new file mode 100644 index 0000000..5bf64aa --- /dev/null +++ b/src/main/java/cloud/fogbow/accs/core/models/OrderStateHistory.java @@ -0,0 +1,48 @@ +package cloud.fogbow.accs.core.models; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import cloud.fogbow.accs.core.models.orders.OrderState; + +@Entity +@Table(name = "order_record_state_history") +public class OrderStateHistory { + + private static final String STATE_HISTORY_COLUMN_NAME = "state_history"; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(nullable = false, unique = true) + private Long id; + + @Column(name = STATE_HISTORY_COLUMN_NAME) + @ElementCollection(fetch = FetchType.EAGER) + private Map history; + + public OrderStateHistory() { + this.history = new HashMap(); + } + + public Long getId() { + return id; + } + + public void updateState(OrderState newState, Timestamp changeTime) { + history.put(changeTime, newState); + } + + public Map getHistory() { + return this.history; + } +} diff --git a/src/main/java/cloud/fogbow/accs/core/models/Record.java b/src/main/java/cloud/fogbow/accs/core/models/Record.java index a95d192..8de93a9 100644 --- a/src/main/java/cloud/fogbow/accs/core/models/Record.java +++ b/src/main/java/cloud/fogbow/accs/core/models/Record.java @@ -49,6 +49,9 @@ public class Record { @Column @Enumerated(EnumType.STRING) private OrderState state; + + @OneToOne(cascade={CascadeType.ALL}) + private OrderStateHistory stateHistory; @ManyToOne @JoinColumns({ @@ -125,10 +128,19 @@ public OrderState getState() { return state; } - public void setState(OrderState newState) { + public void updateState(OrderState newState, Timestamp changeTime) { + this.stateHistory.updateState(newState, changeTime); this.state = newState; } - + + public OrderStateHistory getStateHistory() { + return this.stateHistory; + } + + public void setStateHistory(OrderStateHistory newHistory) { + this.stateHistory = newHistory; + } + public void setStartDate(Timestamp startDate) { this.startDate = startDate; } @@ -177,6 +189,7 @@ public Record(String orderId, String resourceType, OrderSpec spec, String reques this.duration = 0; this.state = OrderState.FULFILLED; this.user = user; + this.stateHistory = new OrderStateHistory(); } public Record(String orderId, String resourceType, OrderSpec spec, String requestingMember, AccountingUser user) { @@ -186,6 +199,7 @@ public Record(String orderId, String resourceType, OrderSpec spec, String reques this.requestingMember = requestingMember; this.duration = 0; this.user = user; + this.stateHistory = new OrderStateHistory(); } public Record() {} diff --git a/src/main/java/cloud/fogbow/accs/core/models/orders/ComputeOrder.java b/src/main/java/cloud/fogbow/accs/core/models/orders/ComputeOrder.java index a945830..d76d06a 100644 --- a/src/main/java/cloud/fogbow/accs/core/models/orders/ComputeOrder.java +++ b/src/main/java/cloud/fogbow/accs/core/models/orders/ComputeOrder.java @@ -25,7 +25,7 @@ public class ComputeOrder extends Order { // Memory attribute, must be set in MB. @Column - private int memory; + private int ram; // Disk attribute, must be set in GB. @Column @@ -60,8 +60,8 @@ public int getvCPU() { return vCPU; } - public int getMemory() { - return memory; + public int getRam() { + return ram; } public int getDisk() { diff --git a/src/main/java/cloud/fogbow/accs/core/models/orders/OrderState.java b/src/main/java/cloud/fogbow/accs/core/models/orders/OrderState.java index 498a163..d753b10 100644 --- a/src/main/java/cloud/fogbow/accs/core/models/orders/OrderState.java +++ b/src/main/java/cloud/fogbow/accs/core/models/orders/OrderState.java @@ -17,6 +17,8 @@ public enum OrderState { PAUSING("PAUSING"), HIBERNATING("HIBERNATING"), RESUMING("RESUMING"), + STOPPED("STOPPED"), + STOPPING("STOPPING"), DEACTIVATED("DEACTIVATED"); // an order that has been closed is stored twice in stable storage: // one when the order is deleted (but instanceId != null), @@ -28,4 +30,8 @@ public enum OrderState { OrderState(String repr) { this.repr = repr; } + + public String getRepr() { + return repr; + } } diff --git a/src/main/java/cloud/fogbow/accs/core/models/specs/NetworkAllocationMode.java b/src/main/java/cloud/fogbow/accs/core/models/specs/NetworkAllocationMode.java index c93d464..1922695 100644 --- a/src/main/java/cloud/fogbow/accs/core/models/specs/NetworkAllocationMode.java +++ b/src/main/java/cloud/fogbow/accs/core/models/specs/NetworkAllocationMode.java @@ -1,9 +1,12 @@ package cloud.fogbow.accs.core.models.specs; import com.fasterxml.jackson.annotation.JsonValue; +import com.google.gson.annotations.SerializedName; public enum NetworkAllocationMode { - DYNAMIC("dynamic"), + @SerializedName("dynamic") + DYNAMIC("dynamic"), + @SerializedName("static") STATIC("static"); private String value; diff --git a/src/main/java/cloud/fogbow/accs/core/processors/SyncProcessor.java b/src/main/java/cloud/fogbow/accs/core/processors/SyncProcessor.java index 6a3cd82..d63e0e8 100644 --- a/src/main/java/cloud/fogbow/accs/core/processors/SyncProcessor.java +++ b/src/main/java/cloud/fogbow/accs/core/processors/SyncProcessor.java @@ -1,6 +1,7 @@ package cloud.fogbow.accs.core.processors; import cloud.fogbow.accs.constants.Messages; +import cloud.fogbow.accs.constants.SystemConstants; import cloud.fogbow.accs.core.datastore.DatabaseManager; import cloud.fogbow.accs.core.models.*; import cloud.fogbow.accs.core.models.orders.AuditableOrderStateChange; @@ -24,7 +25,6 @@ public class SyncProcessor implements Runnable { private static final int SLEEP_TIME = 60000; // One minute private static final Logger LOGGER = LoggerFactory.getLogger(SyncProcessor.class); - private final String DATE_FORMAT = "yyyy-MM-dd"; private AuditableOrderIdRecorder idRecorder; @@ -81,21 +81,19 @@ protected void manageRecord(AuditableOrderStateChange auditOrder) { createRecord(auditOrder); } else { if (orderHasFinished(auditOrder.getNewState())) { - rec.setState(auditOrder.getNewState()); rec.setEndTime(auditOrder.getTimestamp()); rec.setEndDate(extractDateFromTimestamp(auditOrder.getTimestamp())); setClosedOrderDuration(auditOrder, rec); } else if (auditOrder.getNewState().equals(OrderState.UNABLE_TO_CHECK_STATUS)) { rec.setDuration(getDuration(auditOrder.getTimestamp(), rec.getStartTime())); - rec.setState(auditOrder.getNewState()); } else if (auditOrder.getNewState().equals(OrderState.FULFILLED)) { - rec.setState(auditOrder.getNewState()); rec.setDuration(0); if (rec.getStartTime() == null) { rec.setStartTime(auditOrder.getTimestamp()); } - } + + rec.updateState(auditOrder.getNewState(), auditOrder.getTimestamp()); dbManager.saveRecord(rec); } } @@ -119,7 +117,7 @@ protected void createRecord(AuditableOrderStateChange auditOrder) { setTimeAttributes(ord, auditOrder, rec); - rec.setState(auditOrder.getNewState()); + rec.updateState(auditOrder.getNewState(), auditOrder.getTimestamp()); dbManager.saveUser(user); dbManager.saveRecord(rec); @@ -166,7 +164,7 @@ protected boolean orderHasFinished(OrderState state) { protected Timestamp extractDateFromTimestamp(Timestamp timestamp) { try { - DateFormat f = new SimpleDateFormat(DATE_FORMAT); + DateFormat f = new SimpleDateFormat(SystemConstants.COMPLETE_DATE_FORMAT); Date d = f.parse(f.format((Date) timestamp)); return new Timestamp(d.getTime()); } catch (ParseException pe) { diff --git a/src/test/java/cloud/fogbow/accs/api/http/response/RecordUtilsTest.java b/src/test/java/cloud/fogbow/accs/api/http/response/RecordUtilsTest.java new file mode 100644 index 0000000..dcefcee --- /dev/null +++ b/src/test/java/cloud/fogbow/accs/api/http/response/RecordUtilsTest.java @@ -0,0 +1,548 @@ +package cloud.fogbow.accs.api.http.response; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import cloud.fogbow.accs.core.models.orders.OrderState; +import cloud.fogbow.accs.core.models.specs.ComputeSpec; +import cloud.fogbow.accs.core.models.specs.NetworkSpec; +import cloud.fogbow.accs.core.models.specs.VolumeSpec; +import cloud.fogbow.common.exceptions.InvalidParameterException; + +// TODO add tests to Timestamp value +// TODO add more tests with missing values +public class RecordUtilsTest { + + private static final long RECORD_ID_1 = 1L; + private static final long RECORD_ID_2 = 2L; + private static final long RECORD_ID_3 = 3L; + private static final long RECORD_ID_4 = 4L; + private static final String ORDER_ID_1 = "orderId1"; + private static final String ORDER_ID_2 = "orderId2"; + private static final String ORDER_ID_3 = "orderId3"; + private static final String ORDER_ID_4 = "orderId4"; + private static final String RESOURCE_TYPE_1 = "compute"; + private static final String RESOURCE_TYPE_2 = "volume"; + private static final String RESOURCE_TYPE_3 = "compute"; + private static final String RESOURCE_TYPE_4 = "network"; + private static final String REQUESTER_1 = "requester1"; + private static final String REQUESTER_2 = "requester2"; + private static final String REQUESTER_3 = "requester3"; + private static final String REQUESTER_4 = "requester4"; + private static final long DURATION_RECORD_1 = 1500000000L; + private static final long DURATION_RECORD_2 = 2000000000L; + private static final long DURATION_RECORD_3 = 2500000000L; + private static final long DURATION_RECORD_4 = 3000000000L; + private static final String START_DATE_1 = "2000-01-01T00:00:00.000+00:00"; + private static final String START_TIME_1 = "2000-01-01T00:00:00.000+00:00"; + private static final String END_DATE_1 = "2000-01-01T03:03:03.300+00:00"; + private static final String END_TIME_1 = "2000-01-01T03:03:03.300+00:00"; + private static final String START_TIME_2 = "2000-01-01T00:00:00.000+00:00"; + private static final String START_DATE_2 = "2000-01-01T00:00:00.000+00:00"; + private static final String END_DATE_2 = "2000-01-01T03:03:03.300+00:00"; + private static final String END_TIME_2 = "2000-01-01T03:03:03.300+00:00"; + private static final String START_TIME_3 = "2000-01-01T00:00:00.000+00:00"; + private static final String START_DATE_3 = "2000-01-01T00:00:00.000+00:00"; + private static final String END_DATE_3 = "2000-01-01T03:03:03.300+00:00"; + private static final String END_TIME_3 = "2000-01-01T03:03:03.300+00:00"; + private static final String START_DATE_4 = "2000-01-01T00:00:00.000+00:00"; + private static final String START_TIME_4 = "2000-01-01T00:00:00.000+00:00"; + private static final String END_DATE_4 = "2000-01-01T03:03:03.300+00:00"; + private static final String END_TIME_4 = "2000-01-01T03:03:03.300+00:00"; + private static final long SPEC_ID_1 = 1L; + private static final long SPEC_ID_2 = 2L; + private static final long SPEC_ID_3 = 3L; + private static final long SPEC_ID_4 = 4L; + private static final int RAM_RECORD_1 = 4; + private static final int VCPU_RECORD_1 = 2; + private static final int SIZE_RECORD_2 = 100; + private static final int VCPU_RECORD_3 = 4; + private static final int RAM_RECORD_3 = 8; + private static final String CIDR_RECORD_4 = "10.0.1.0/24"; + private static final String ALLOCATION_MODE_RECORD_4 = "dynamic"; + private static final String STATE_RECORD_1 = "FULFILLED"; + private static final String STATE_RECORD_2 = "OPEN"; + private static final String STATE_RECORD_3 = "OPEN"; + private static final String STATE_RECORD_4 = "OPEN"; + private static final long STATE_HISTORY_ID_1 = 1L; + private static final long STATE_HISTORY_ID_2 = 2L; + private static final long STATE_HISTORY_ID_3 = 3L; + private static final long STATE_HISTORY_ID_4 = 4L; + private static final String TIMESTAMP_1_STATE_HISTORY_1 = "2000-01-01T00:00:00.000+00:00"; + private static final String TIMESTAMP_1_STATE_HISTORY_2 = "2000-01-03T00:00:00.000+00:00"; + private static final String TIMESTAMP_1_STATE_HISTORY_3 = "2000-01-05T00:00:00.000+00:00"; + private static final String TIMESTAMP_1_STATE_HISTORY_4 = "2000-01-07T00:00:00.000+00:00"; + private static final String STATE_1_STATE_HISTORY_1 = "FULFILLED"; + private static final String STATE_1_STATE_HISTORY_2 = "SPAWNING"; + private static final String STATE_1_STATE_HISTORY_3 = "STOPPED"; + private static final String STATE_1_STATE_HISTORY_4 = "RESUMING"; + private static final String TIMESTAMP_2_STATE_HISTORY_1 = "2000-01-02T00:00:00.000+00:00"; + private static final String TIMESTAMP_2_STATE_HISTORY_2 = "2000-01-04T00:00:00.000+00:00"; + private static final String TIMESTAMP_2_STATE_HISTORY_3 = "2000-01-06T00:00:00.000+00:00"; + private static final String TIMESTAMP_2_STATE_HISTORY_4 = "2000-01-08T00:00:00.000+00:00"; + private static final String STATE_2_STATE_HISTORY_1 = "CLOSED"; + private static final String STATE_2_STATE_HISTORY_2 = "PAUSED"; + private static final String STATE_2_STATE_HISTORY_3 = "RESUMING"; + private static final String STATE_2_STATE_HISTORY_4 = "FULFILLED"; + private AccsApiUtils recordUtils; + + // test case: When calling the getRecordsFromString method, it must parse the given + // String and return a List containing Record instances of the correct types containing + // the data passed as argument. + @Test + public void testGetRecordsFromString() throws InvalidParameterException { + this.recordUtils = new AccsApiUtils(); + + RecordStringBuilder builder = new RecordStringBuilder(); + + builder.addRecordString(new ComputeRecordString(RECORD_ID_1, ORDER_ID_1, RESOURCE_TYPE_1, + SPEC_ID_1, VCPU_RECORD_1, RAM_RECORD_1, STATE_HISTORY_ID_1, TIMESTAMP_1_STATE_HISTORY_1, + STATE_1_STATE_HISTORY_1, TIMESTAMP_2_STATE_HISTORY_1, STATE_2_STATE_HISTORY_1, REQUESTER_1, + START_TIME_1, START_DATE_1, END_DATE_1, END_TIME_1, DURATION_RECORD_1, STATE_RECORD_1)); + builder.addRecordString(new VolumeRecordString(RECORD_ID_2, ORDER_ID_2, RESOURCE_TYPE_2, + SPEC_ID_2, SIZE_RECORD_2, STATE_HISTORY_ID_2, TIMESTAMP_1_STATE_HISTORY_2, + STATE_1_STATE_HISTORY_2, TIMESTAMP_2_STATE_HISTORY_2, STATE_2_STATE_HISTORY_2, + REQUESTER_2, START_TIME_2, START_DATE_2, END_DATE_2, END_TIME_2, DURATION_RECORD_2, STATE_RECORD_2)); + builder.addRecordString(new ComputeRecordString(RECORD_ID_3, ORDER_ID_3, RESOURCE_TYPE_3, + SPEC_ID_3, VCPU_RECORD_3, RAM_RECORD_3, STATE_HISTORY_ID_3, TIMESTAMP_1_STATE_HISTORY_3, + STATE_1_STATE_HISTORY_3, TIMESTAMP_2_STATE_HISTORY_3, STATE_2_STATE_HISTORY_3, REQUESTER_3, + START_TIME_3, START_DATE_3, END_DATE_3, END_TIME_3, DURATION_RECORD_3, STATE_RECORD_3)); + builder.addRecordString(new NetworkRecordString(RECORD_ID_4, ORDER_ID_4, RESOURCE_TYPE_4, + SPEC_ID_4, CIDR_RECORD_4, ALLOCATION_MODE_RECORD_4, STATE_HISTORY_ID_4, TIMESTAMP_1_STATE_HISTORY_4, + STATE_1_STATE_HISTORY_4, TIMESTAMP_2_STATE_HISTORY_4, STATE_2_STATE_HISTORY_4, REQUESTER_4, + START_TIME_4, START_DATE_4, END_DATE_4, END_TIME_4, DURATION_RECORD_4, STATE_RECORD_4)); + + String recordsString = builder.build(); + List records = this.recordUtils.getRecordsFromString(recordsString); + + Record record1 = records.get(0); + + assertEquals(RECORD_ID_1, record1.getId().longValue()); + assertEquals(ORDER_ID_1, record1.getOrderId()); + assertEquals(RESOURCE_TYPE_1, record1.getResourceType()); + assertEquals(SPEC_ID_1, record1.getSpec().getId().longValue()); + assertEquals(VCPU_RECORD_1, ((ComputeSpec) record1.getSpec()).getvCpu()); + assertEquals(RAM_RECORD_1, ((ComputeSpec) record1.getSpec()).getRam()); + assertEquals(STATE_HISTORY_ID_1, record1.getStateHistory().getId().longValue()); + assertEquals(2, record1.getStateHistory().getHistory().size()); + assertTrue(record1.getStateHistory().getHistory().containsValue(OrderState.valueOf(STATE_1_STATE_HISTORY_1))); + assertTrue(record1.getStateHistory().getHistory().containsValue(OrderState.valueOf(STATE_2_STATE_HISTORY_1))); + assertEquals(REQUESTER_1, record1.getRequester()); + assertEquals(DURATION_RECORD_1, record1.getDuration()); + assertEquals(OrderState.valueOf(STATE_RECORD_1), record1.getState()); + + Record record2 = records.get(1); + + assertEquals(RECORD_ID_2, record2.getId().longValue()); + assertEquals(ORDER_ID_2, record2.getOrderId()); + assertEquals(RESOURCE_TYPE_2, record2.getResourceType()); + assertEquals(SPEC_ID_2, record2.getSpec().getId().longValue()); + assertEquals(SIZE_RECORD_2, ((VolumeSpec) record2.getSpec()).getSize()); + assertEquals(2, record2.getStateHistory().getHistory().size()); + assertTrue(record2.getStateHistory().getHistory().containsValue(OrderState.valueOf(STATE_1_STATE_HISTORY_2))); + assertTrue(record2.getStateHistory().getHistory().containsValue(OrderState.valueOf(STATE_2_STATE_HISTORY_2))); + assertEquals(REQUESTER_2, record2.getRequester()); + assertEquals(DURATION_RECORD_2, record2.getDuration()); + assertEquals(OrderState.valueOf(STATE_RECORD_2), record2.getState()); + + Record record3 = records.get(2); + + assertEquals(RECORD_ID_3, record3.getId().longValue()); + assertEquals(ORDER_ID_3, record3.getOrderId()); + assertEquals(RESOURCE_TYPE_3, record3.getResourceType()); + assertEquals(SPEC_ID_3, record3.getSpec().getId().longValue()); + assertEquals(VCPU_RECORD_3, ((ComputeSpec) record3.getSpec()).getvCpu()); + assertEquals(RAM_RECORD_3, ((ComputeSpec) record3.getSpec()).getRam()); + assertEquals(2, record3.getStateHistory().getHistory().size()); + assertTrue(record3.getStateHistory().getHistory().containsValue(OrderState.valueOf(STATE_1_STATE_HISTORY_3))); + assertTrue(record3.getStateHistory().getHistory().containsValue(OrderState.valueOf(STATE_2_STATE_HISTORY_3))); + assertEquals(REQUESTER_3, record3.getRequester()); + assertEquals(DURATION_RECORD_3, record3.getDuration()); + assertEquals(OrderState.valueOf(STATE_RECORD_3), record3.getState()); + + Record record4 = records.get(3); + + assertEquals(RECORD_ID_4, record4.getId().longValue()); + assertEquals(ORDER_ID_4, record4.getOrderId()); + assertEquals(RESOURCE_TYPE_4, record4.getResourceType()); + assertEquals(SPEC_ID_4, record4.getSpec().getId().longValue()); + assertEquals(CIDR_RECORD_4, ((NetworkSpec) record4.getSpec()).getCidr()); + assertEquals(ALLOCATION_MODE_RECORD_4, ((NetworkSpec) record4.getSpec()).getAllocationMode().getValue()); + assertEquals(2, record3.getStateHistory().getHistory().size()); + assertTrue(record3.getStateHistory().getHistory().containsValue(OrderState.valueOf(STATE_1_STATE_HISTORY_3))); + assertTrue(record3.getStateHistory().getHistory().containsValue(OrderState.valueOf(STATE_2_STATE_HISTORY_3))); + assertEquals(REQUESTER_4, record4.getRequester()); + assertEquals(DURATION_RECORD_4, record4.getDuration()); + assertEquals(OrderState.valueOf(STATE_RECORD_4), record4.getState()); + } + + // test case: When calling the getRecordsFromString method and the given String + // contains no record data, it must return an empty list. + @Test + public void testGetRecordsFromStringNoRecords() throws InvalidParameterException { + this.recordUtils = new AccsApiUtils(); + RecordStringBuilder builder = new RecordStringBuilder(); + String recordsString = builder.build(); + + List records = this.recordUtils.getRecordsFromString(recordsString); + + assertTrue(records.isEmpty()); + } + + // test case: When calling the getRecordsFromString method and the given String + // contains records data from types other than "compute", "volume" and "network", + // it must throw an InvalidParameterException. + @Test(expected = InvalidParameterException.class) + public void testGetRecordsFromStringInvalidRecordType() + throws InvalidParameterException { + this.recordUtils = new AccsApiUtils(); + + String recordString = String.format("" + + "{" + + " \"id\": %d," + + " \"orderId\": \"%s\"," + + " \"resourceType\": \"%s\"," + + " \"spec\": {" + + " \"id\": %d," + + " \"info1\": info1," + + " \"info2\": info2" + + " }," + + " \"stateHistory\":{" + + " \"id\": %d," + + " \"history\": {" + + " \"%s\": \"%s\"," + + " \"%s\": \"%s\"" + + " }" + + " }," + + " \"requester\": \"%s\"," + + " \"startTime\": \"%s\"," + + " \"startDate\": \"%s\"," + + " \"endDate\": \"%s\"," + + " \"endTime\": \"%s\"," + + " \"duration\": %d," + + " \"state\": \"%s\"" + + "}", RECORD_ID_1, ORDER_ID_1, "othertype", SPEC_ID_1, STATE_HISTORY_ID_1, + TIMESTAMP_1_STATE_HISTORY_1, STATE_1_STATE_HISTORY_1, TIMESTAMP_2_STATE_HISTORY_1, + STATE_2_STATE_HISTORY_1, REQUESTER_1, START_TIME_1, START_DATE_1, END_DATE_1, + END_TIME_1, DURATION_RECORD_1, STATE_RECORD_1); + + String recordsString = String.format("[%s]", recordString); + + this.recordUtils.getRecordsFromString(recordsString); + } + + // test case: When calling the getRecordsFromString method and the given String + // contains compute records data with missing properties, it must throw an + // InvalidParameterException. + @Test(expected = InvalidParameterException.class) + public void testGetRecordsFromStringInvalidComputeRecord() + throws InvalidParameterException { + this.recordUtils = new AccsApiUtils(); + + String recordString = String.format("" + + "{" + + " \"id\": %d," + + " \"orderId\": \"%s\"," + + " \"resourceType\": \"%s\"," + + " \"spec\": {" + + " \"id\": %d," + + " \"vCpu\": \"%d\"," + + " \"ram\": \"%d\"" + + " }," + + " \"requester\": \"%s\"," + + " \"startDate\": \"%s\"," + + " \"endDate\": \"%s\"," + + " \"endTime\": \"%s\"," + + " \"duration\": %d," + + " \"state\": \"%s\"" + + "}", RECORD_ID_1, ORDER_ID_1, RESOURCE_TYPE_1, SPEC_ID_1, VCPU_RECORD_1, + RAM_RECORD_1, REQUESTER_1, START_DATE_1, END_DATE_1, END_TIME_1, + DURATION_RECORD_1, STATE_RECORD_1); + + String recordsString = String.format("[%s]", recordString); + + this.recordUtils.getRecordsFromString(recordsString); + } + + // test case: When calling the getRecordsFromString method and the given String + // contains volume records data with missing properties, it must throw an + // InvalidParameterException. + @Test(expected = InvalidParameterException.class) + public void testGetRecordsFromStringInvalidVolumeRecord() + throws InvalidParameterException { + this.recordUtils = new AccsApiUtils(); + + String recordString = String.format("" + + "{" + + " \"id\": %d," + + " \"orderId\": \"%s\"," + + " \"resourceType\": \"%s\"," + + " \"spec\": {" + + " \"id\": %d," + + " \"size\": \"%d\"" + + " }," + + " \"requester\": \"%s\"," + + " \"startDate\": \"%s\"," + + " \"endDate\": \"%s\"," + + " \"endTime\": \"%s\"," + + " \"duration\": %d," + + " \"state\": \"%s\"" + + "}", RECORD_ID_2, ORDER_ID_2, RESOURCE_TYPE_2, + SPEC_ID_2, SIZE_RECORD_2, REQUESTER_2, START_DATE_2, END_DATE_2, + END_TIME_2, DURATION_RECORD_2, STATE_RECORD_2); + + String recordsString = String.format("[%s]", recordString); + + this.recordUtils.getRecordsFromString(recordsString); + } + + // test case: When calling the getRecordsFromString method and the given String + // contains network records data with missing properties, it must throw an + // InvalidParameterException. + @Test(expected = InvalidParameterException.class) + public void testGetRecordsFromStringInvalidNetworkRecord() + throws InvalidParameterException { + this.recordUtils = new AccsApiUtils(); + + String recordString = String.format("" + + "{" + + " \"id\": %d," + + " \"orderId\": \"%s\"," + + " \"resourceType\": \"%s\"," + + " \"spec\": {" + + " \"id\": %d," + + " \"cidr\": \"%s\"," + + " \"allocationMode\": \"%s\"" + + " }," + + " \"requester\": \"%s\"," + + " \"startDate\": \"%s\"," + + " \"endDate\": \"%s\"," + + " \"endTime\": \"%s\"," + + " \"duration\": %d," + + " \"state\": \"%s\"" + + "}", RECORD_ID_4, ORDER_ID_4, RESOURCE_TYPE_4, + SPEC_ID_4, CIDR_RECORD_4, ALLOCATION_MODE_RECORD_4, REQUESTER_4, + START_DATE_4, END_DATE_4, END_TIME_4, DURATION_RECORD_4, STATE_RECORD_4); + + String recordsString = String.format("[%s]", recordString); + + this.recordUtils.getRecordsFromString(recordsString); + } + + private abstract class RecordString { + protected long id; + protected String orderId; + protected String resourceType; + protected long specId; + protected long stateHistoryId; + protected String historyTimestamp1; + protected String historyState1; + protected String historyTimestamp2; + protected String historyState2; + protected String requester; + protected String startTime; + protected String startDate; + protected String endDate; + protected String endTime; + protected long duration; + protected String state; + + + public RecordString(long id, String orderId, String resourceType, long specId, + long stateHistoryId, String historyTimestamp1, String historyState1, + String historyTimestamp2, String historyState2, String requester, + String startTime, String startDate, String endDate, String endTime, + long duration, String state) { + this.id = id; + this.orderId = orderId; + this.resourceType = resourceType; + this.specId = specId; + this.stateHistoryId = stateHistoryId; + this.historyTimestamp1 = historyTimestamp1; + this.historyTimestamp2 = historyTimestamp2; + this.historyState1 = historyState1; + this.historyState2 = historyState2; + this.requester = requester; + this.startTime = startTime; + this.startDate = startDate; + this.endDate = endDate; + this.endTime = endTime; + this.duration = duration; + this.state = state; + } + + abstract String getString(); + } + + private class ComputeRecordString extends RecordString { + private int vCPU; + private int ram; + + public ComputeRecordString(long id, String orderId, String resourceType, + long specId, int vCPU, int ram, long stateHistoryId, String historyTimestamp1, + String historyState1, String historyTimestamp2, String historyState2, String requester, + String startTime, String startDate, String endDate, String endTime, long duration, + String state) { + super(id, orderId, resourceType, specId, stateHistoryId, historyTimestamp1, historyState1, + historyTimestamp2, historyState2, requester, startTime, startDate, endDate, + endTime, duration, state); + this.vCPU = vCPU; + this.ram = ram; + } + + public String getString() { + String recordString = String.format("" + + "{" + + " \"id\": %d," + + " \"orderId\": \"%s\"," + + " \"resourceType\": \"%s\"," + + " \"spec\": {" + + " \"id\": %d," + + " \"vCpu\": \"%d\"," + + " \"ram\": \"%d\"" + + " }," + + " \"stateHistory\":{" + + " \"id\": %d," + + " \"history\": {" + + " \"%s\": \"%s\"," + + " \"%s\": \"%s\"" + + " }" + + " }," + + " \"requester\": \"%s\"," + + " \"startTime\": \"%s\"," + + " \"startDate\": \"%s\"," + + " \"endDate\": \"%s\"," + + " \"endTime\": \"%s\"," + + " \"duration\": %d," + + " \"state\": \"%s\"" + + "}", id, orderId, resourceType, specId, vCPU, ram, stateHistoryId, + historyTimestamp1, historyState1, historyTimestamp2, historyState2, requester, + startTime, startDate, endDate, endTime, duration, state); + + return recordString; + } + } + + private class VolumeRecordString extends RecordString { + private int size; + + public VolumeRecordString(long id, String orderId, String resourceType, + long specId, int size, long stateHistoryId, String historyTimestamp1, + String historyState1, String historyTimestamp2, String historyState2, + String requester, String startTime, String startDate, String endDate, + String endTime, long duration, String state) { + super(id, orderId, resourceType, specId, stateHistoryId, historyTimestamp1, + historyState1, historyTimestamp2, historyState2, requester, startTime, + startDate, endDate, endTime, duration, state); + this.size = size; + } + + public String getString() { + String recordString = String.format("" + + "{" + + " \"id\": %d," + + " \"orderId\": \"%s\"," + + " \"resourceType\": \"%s\"," + + " \"spec\": {" + + " \"id\": %d," + + " \"size\": \"%d\"" + + " }," + + " \"stateHistory\":{" + + " \"id\": %d," + + " \"history\": {" + + " \"%s\": \"%s\"," + + " \"%s\": \"%s\"" + + " }" + + " }," + + " \"requester\": \"%s\"," + + " \"startTime\": \"%s\"," + + " \"startDate\": \"%s\"," + + " \"endDate\": \"%s\"," + + " \"endTime\": \"%s\"," + + " \"duration\": %d," + + " \"state\": \"%s\"" + + "}", id, orderId, resourceType, specId, size, stateHistoryId, + historyTimestamp1, historyState1, historyTimestamp2, historyState2, + requester, startTime, startDate, endDate, endTime, duration, state); + + return recordString; + } + } + + private class NetworkRecordString extends RecordString { + private String cidr; + private String allocationMode; + + public NetworkRecordString(long id, String orderId, String resourceType, + long specId, String cidr, String allocationMode, long stateHistoryId, + String historyTimestamp1, String historyState1, String historyTimestamp2, + String historyState2, String requester, String startTime, + String startDate, String endDate, String endTime, long duration, String state) { + super(id, orderId, resourceType, specId, stateHistoryId, historyTimestamp1, historyState1, + historyTimestamp2, historyState2, requester, startTime, startDate, + endDate, endTime, duration, state); + this.cidr = cidr; + this.allocationMode = allocationMode; + } + + public String getString() { + String recordString = String.format("" + + "{" + + " \"id\": %d," + + " \"orderId\": \"%s\"," + + " \"resourceType\": \"%s\"," + + " \"spec\": {" + + " \"id\": %d," + + " \"cidr\": \"%s\"," + + " \"allocationMode\": \"%s\"" + + " }," + + " \"stateHistory\":{" + + " \"id\": %d," + + " \"history\": {" + + " \"%s\": \"%s\"," + + " \"%s\": \"%s\"" + + " }" + + " }," + + " \"requester\": \"%s\"," + + " \"startTime\": \"%s\"," + + " \"startDate\": \"%s\"," + + " \"endDate\": \"%s\"," + + " \"endTime\": \"%s\"," + + " \"duration\": %d," + + " \"state\": \"%s\"" + + "}", id, orderId, resourceType, specId, cidr, allocationMode, + stateHistoryId, historyTimestamp1, historyState1, historyTimestamp2, + historyState2, requester, startTime, startDate, endDate, endTime, + duration, state); + + return recordString; + } + } + + private class RecordStringBuilder { + private List recordStrings; + + public RecordStringBuilder() { + recordStrings = new ArrayList(); + } + + public void addRecordString(RecordString recordString) { + recordStrings.add(recordString); + } + + public String build() { + List strings = new ArrayList(); + + for (RecordString string : recordStrings) { + strings.add(string.getString()); + } + + return String.format("[%s]", String.join(",", strings)); + } + } +} diff --git a/src/test/java/cloud/fogbow/accs/core/ApplicationFacadeTest.java b/src/test/java/cloud/fogbow/accs/core/ApplicationFacadeTest.java index 8a5c54c..82a16d1 100644 --- a/src/test/java/cloud/fogbow/accs/core/ApplicationFacadeTest.java +++ b/src/test/java/cloud/fogbow/accs/core/ApplicationFacadeTest.java @@ -106,6 +106,26 @@ public void testGetSelfRecords() throws FogbowException, ParseException { Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(SystemUser.class)); Mockito.verify(applicationFacade, Mockito.times(getFakeRecordsCollection().size())).mountResponseRecord(Mockito.any(Record.class)); } + + @Test + public void testGetAllResourcesUserRecords() throws Exception { + //setup + DatabaseManager dbManager = testUtils.mockDbManager(); + applicationFacade.setDatabaseManager(dbManager); + Mockito.when(plugin.isAuthorized(Mockito.any(SystemUser.class), Mockito.any(AccountingOperation.class))).thenReturn(true); + Mockito.when(dbManager.getUserRecords(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + .thenReturn(getFakeRecordsCollection()); + + //exercise + applicationFacade.getAllResourcesUserRecords(testUtils.FAKE_USER_ID, testUtils.FAKE_REQUESTING_MEMBER, testUtils.FAKE_PROVIDER_MEMBER, + testUtils.FAKE_INTERVAL, testUtils.FAKE_INTERVAL, testUtils.FAKE_USER_TOKEN); + + //verify + Mockito.verify(applicationFacade, Mockito.times(1)).handleAuthIssues(Mockito.anyString(), Mockito.eq(AccountingOperationType.OTHERS_BILLING)); + Mockito.verify(dbManager, Mockito.times(1)).getUserRecords( + Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + Mockito.verify(applicationFacade, Mockito.times(getFakeRecordsCollection().size())).mountResponseRecord(Mockito.any(Record.class)); + } private List getFakeRecordsCollection() { List records = new ArrayList<>(); diff --git a/src/test/java/cloud/fogbow/accs/core/TestUtils.java b/src/test/java/cloud/fogbow/accs/core/TestUtils.java index f13358a..0afd872 100644 --- a/src/test/java/cloud/fogbow/accs/core/TestUtils.java +++ b/src/test/java/cloud/fogbow/accs/core/TestUtils.java @@ -2,6 +2,7 @@ import cloud.fogbow.accs.core.datastore.DatabaseManager; import cloud.fogbow.accs.core.models.AccountingUser; +import cloud.fogbow.accs.core.models.OrderStateHistory; import cloud.fogbow.accs.core.models.Record; import cloud.fogbow.accs.core.models.UserIdentity; import cloud.fogbow.accs.core.models.orders.AuditableOrderStateChange; @@ -52,8 +53,9 @@ public List createClosedRecords(int size) { for (int i = 0; i < size; i++) { Record rec = new Record(); - rec.setState(OrderState.CLOSED); + rec.setStateHistory(new OrderStateHistory()); rec.setStartTime(new Timestamp(new Date().getTime())); + rec.updateState(OrderState.CLOSED, new Timestamp(new Date().getTime())); records.add(rec); } @@ -63,10 +65,27 @@ public List createClosedRecords(int size) { public List createOpenedRecords(int size) { List records = new ArrayList<>(); + System.out.println(size); for (int i = 0; i < size; i++) { Record rec = new Record(); - rec.setState(OrderState.FULFILLED); + rec.setStateHistory(new OrderStateHistory()); rec.setStartTime(new Timestamp(new Date().getTime())); + rec.updateState(OrderState.FULFILLED, new Timestamp(new Date().getTime())); + records.add(rec); + } + + return records; + } + + public List createRecordsWithState(int size, OrderState state) { + List records = new ArrayList<>(); + + System.out.println(size); + for (int i = 0; i < size; i++) { + Record rec = new Record(); + rec.setStateHistory(new OrderStateHistory()); + rec.setStartTime(new Timestamp(new Date().getTime())); + rec.updateState(state, new Timestamp(new Date().getTime())); records.add(rec); } @@ -80,6 +99,7 @@ public List createSelfUserRecords(int size) { for(int i = 0; i < size; i++) { Record rec = new Record(); + rec.setStateHistory(new OrderStateHistory()); rec.setUser(selfUser); rec.setStartTime(new Timestamp(new Date().getTime())); records.add(rec); @@ -93,6 +113,7 @@ public List createRecordsOwnedByUser(AccountingUser user, int size) { for(int i = 0; i < size; i++) { Record rec = new Record(); + rec.setStateHistory(new OrderStateHistory()); rec.setUser(user); rec.setStartTime(new Timestamp(new Date().getTime())); records.add(rec); @@ -138,6 +159,7 @@ public AuditableOrderStateChange createAuditableOrderStateChange(Order order, Or public Record createRecord(String orderId) { Record record = new Record(); record.setOrderId(orderId); + record.setStateHistory(Mockito.mock(OrderStateHistory.class)); Timestamp startTime = new Timestamp(System.currentTimeMillis()); Timestamp endTime = new Timestamp(System.currentTimeMillis() + TEN_SECONDS); record.setStartTime(startTime); @@ -148,6 +170,7 @@ public Record createRecord(String orderId) { public Record createSimplestRecord(String orderId) { Record record = new Record(); record.setOrderId(orderId); + record.setStateHistory(Mockito.mock(OrderStateHistory.class)); return record; } diff --git a/src/test/java/cloud/fogbow/accs/core/datastore/services/RecordServiceTest.java b/src/test/java/cloud/fogbow/accs/core/datastore/services/RecordServiceTest.java index c2adc85..63ac633 100644 --- a/src/test/java/cloud/fogbow/accs/core/datastore/services/RecordServiceTest.java +++ b/src/test/java/cloud/fogbow/accs/core/datastore/services/RecordServiceTest.java @@ -26,6 +26,7 @@ public class RecordServiceTest extends BaseUnitTests { private final String ANY_VALUE = "any"; private final String TEST_DATE = "2000-01-01"; + private final String COMPLETE_TEST_DATE = "2000-01-01_10:02:59"; private final String DEFAULT_RESOURCE_TYPE = "compute"; private final String FAKE_REQ_MEMBER = "mockedRequestingMember"; private final int DEFAULT_RECORDS_SIZE = 2; @@ -90,21 +91,96 @@ public void testGetTimestampFromString() throws ParseException{ //exercise/verify Assert.assertEquals(twoThousandTimestamp, recordService.getTimestampFromString(TEST_DATE)); } + + //test case: check if the timestamp returned is the expected one. + @Test + public void testGetTimestampFromStringCompleteDateFormat() throws ParseException{ + //setup + Timestamp twoThousandTimestamp = Timestamp.valueOf("2000-01-01 10:02:59"); + + //exercise/verify + Assert.assertEquals(twoThousandTimestamp, recordService.getTimestampFromString(COMPLETE_TEST_DATE)); + } //test case: just exercise the method by checking if it returned what it should return //(exactly what the repository returned, once its up to spring to test the correctness of the repository). @Test - public void testGetOpenedRecords() { + public void testGetOpenedRecordsOfResourceType() { //setup - mockDatabaseOperations(testUtils.RECORDS_BY_STATE, OrderState.FULFILLED, null, null, DEFAULT_RECORDS_SIZE); + AccountingUser accountingUser = testUtils.getAccountingUser(); + Timestamp startTime = new Timestamp(System.currentTimeMillis()); + Timestamp endTime = new Timestamp(System.currentTimeMillis()); + + mockDatabaseResponseOfResourceType(accountingUser, startTime, endTime, OrderState.FULFILLED); + mockDatabaseResponseOfResourceType(accountingUser, startTime, endTime, OrderState.PAUSING); + mockDatabaseResponseOfResourceType(accountingUser, startTime, endTime, OrderState.PAUSED); + mockDatabaseResponseOfResourceType(accountingUser, startTime, endTime, OrderState.HIBERNATING); + mockDatabaseResponseOfResourceType(accountingUser, startTime, endTime, OrderState.HIBERNATED); + mockDatabaseResponseOfResourceType(accountingUser, startTime, endTime, OrderState.STOPPING); + mockDatabaseResponseOfResourceType(accountingUser, startTime, endTime, OrderState.STOPPED); + mockDatabaseResponseOfResourceType(accountingUser, startTime, endTime, OrderState.SPAWNING); + + //exercise + List records = recordService.getOpenedRecords(accountingUser, FAKE_REQ_MEMBER, DEFAULT_RESOURCE_TYPE, + startTime, endTime); + //verify + Assert.assertEquals(16, records.size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.FULFILLED)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.PAUSING)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.PAUSED)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.HIBERNATING)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.HIBERNATED)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.STOPPING)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.STOPPED)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.SPAWNING)).collect(Collectors.toList()).size()); + } + + //test case: just exercise the method by checking if it returned what it should return + //(exactly what the repository returned, once its up to spring to test the correctness of the repository). + @Test + public void testGetOpenedRecords() { + //setup + AccountingUser accountingUser = testUtils.getAccountingUser(); + Timestamp startTime = new Timestamp(System.currentTimeMillis()); + Timestamp endTime = new Timestamp(System.currentTimeMillis()); + + mockDatabaseResponse(accountingUser, startTime, endTime, OrderState.FULFILLED); + mockDatabaseResponse(accountingUser, startTime, endTime, OrderState.PAUSING); + mockDatabaseResponse(accountingUser, startTime, endTime, OrderState.PAUSED); + mockDatabaseResponse(accountingUser, startTime, endTime, OrderState.HIBERNATING); + mockDatabaseResponse(accountingUser, startTime, endTime, OrderState.HIBERNATED); + mockDatabaseResponse(accountingUser, startTime, endTime, OrderState.STOPPING); + mockDatabaseResponse(accountingUser, startTime, endTime, OrderState.STOPPED); + mockDatabaseResponse(accountingUser, startTime, endTime, OrderState.SPAWNING); + //exercise - List records = recordService.getOpenedRecords(testUtils.getAccountingUser(), FAKE_REQ_MEMBER, DEFAULT_RESOURCE_TYPE, - new Timestamp(System.currentTimeMillis()), new Timestamp(System.currentTimeMillis())); + List records = recordService.getOpenedRecords(accountingUser, FAKE_REQ_MEMBER, startTime, endTime); //verify - Assert.assertEquals(2, records.size()); + Assert.assertEquals(16, records.size()); Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.FULFILLED)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.PAUSING)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.PAUSED)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.HIBERNATING)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.HIBERNATED)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.STOPPING)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.STOPPED)).collect(Collectors.toList()).size()); + Assert.assertEquals(2, records.stream().filter(rec -> rec.getState().equals(OrderState.SPAWNING)).collect(Collectors.toList()).size()); + } + + private void mockDatabaseResponse(AccountingUser user, Timestamp startTime, Timestamp endTime, OrderState state) { + Mockito.doReturn(testUtils.createRecordsWithState(DEFAULT_RECORDS_SIZE, state)).when(recordRepository). + findByUserAndRequestingMemberAndStartDateLessThanEqualAndStartDateGreaterThanEqualAndStateEquals( + user, FAKE_REQ_MEMBER, startTime, + endTime, state); + } + + private void mockDatabaseResponseOfResourceType(AccountingUser user, Timestamp startTime, Timestamp endTime, OrderState state) { + Mockito.doReturn(testUtils.createRecordsWithState(DEFAULT_RECORDS_SIZE, state)).when(recordRepository). + findByUserAndRequestingMemberAndResourceTypeAndStartDateLessThanEqualAndStartDateGreaterThanEqualAndStateEquals( + user, FAKE_REQ_MEMBER, DEFAULT_RESOURCE_TYPE, startTime, + endTime, state); } //test case: just exercise the method by checking if it returned what it should return @@ -156,6 +232,22 @@ public void testGetUserRecords() throws Exception { Mockito.verify(recordService, Mockito.times(1)).checkInterval(Mockito.any(Timestamp.class), Mockito.any(Timestamp.class)); Mockito.verify(recordService, Mockito.times(1)).setOpenedRecordsDuration(Mockito.any(List.class)); } + + @Test + public void testGetAllResourcesUserRecords() throws ParseException { + //setup + AccountingUser user = new AccountingUser(new UserIdentity( ANY_VALUE, ANY_VALUE)); + mockDatabaseOperations(testUtils.RECORDS_BY_USER, null, testUtils.OTHER_USER, user, DEFAULT_RECORDS_SIZE); + + //exercise + recordService.getAllResourcesUserRecords(ANY_VALUE, ANY_VALUE, ANY_VALUE, TEST_DATE, TEST_DATE); + + //verify + Mockito.verify(recordService, Mockito.times(1)).getOpenedRecords(Mockito.any(AccountingUser.class), Mockito.anyString(), Mockito.any(Timestamp.class), Mockito.any(Timestamp.class)); + Mockito.verify(recordService, Mockito.times(1)).getClosedRecords(Mockito.any(AccountingUser.class), Mockito.anyString(), Mockito.any(Timestamp.class), Mockito.any(Timestamp.class)); + Mockito.verify(recordService, Mockito.times(1)).checkInterval(Mockito.any(Timestamp.class), Mockito.any(Timestamp.class)); + Mockito.verify(recordService, Mockito.times(1)).setOpenedRecordsDuration(Mockito.any(List.class)); + } private void mockDatabaseOperations(String operation, OrderState state, String userType, AccountingUser user, int size) { switch (operation) { @@ -187,16 +279,23 @@ private void mockRecordsByUser(String userType, AccountingUser user, int size) { private void mockRecordsByState(OrderState state, int size) { switch (state) { + case CLOSED: + Mockito.doReturn(testUtils.createClosedRecords(size)).when(recordRepository).findByUserAndRequestingMemberAndResourceTypeAndStartDateLessThanEqualAndEndDateGreaterThanEqual( + Mockito.any(AccountingUser.class), Mockito.anyString(), Mockito.anyString(), Mockito.any(Timestamp.class), + Mockito.any(Timestamp.class)); + break; case FULFILLED: - Mockito.doReturn(testUtils.createOpenedRecords(size)).when(recordRepository).findByUserAndRequestingMemberAndResourceTypeAndStartDateLessThanEqualAndStartDateGreaterThanEqualAndStateEquals( + case PAUSING: + case PAUSED: + case HIBERNATING: + case HIBERNATED: + case STOPPING: + case STOPPED: + case SPAWNING: + Mockito.doReturn(testUtils.createRecordsWithState(size, state)).when(recordRepository).findByUserAndRequestingMemberAndResourceTypeAndStartDateLessThanEqualAndStartDateGreaterThanEqualAndStateEquals( Mockito.any(AccountingUser.class), Mockito.anyString(), Mockito.anyString(), Mockito.any(Timestamp.class), Mockito.any(Timestamp.class), Mockito.any(OrderState.class)); - break; - case CLOSED: - Mockito.doReturn(testUtils.createClosedRecords(size)).when(recordRepository).findByUserAndRequestingMemberAndResourceTypeAndStartDateLessThanEqualAndEndDateGreaterThanEqual( - Mockito.any(AccountingUser.class), Mockito.anyString(), Mockito.anyString(), Mockito.any(Timestamp.class), - Mockito.any(Timestamp.class)); - break; + break; } } diff --git a/src/test/java/cloud/fogbow/accs/core/models/OrderStateHistoryTest.java b/src/test/java/cloud/fogbow/accs/core/models/OrderStateHistoryTest.java new file mode 100644 index 0000000..4210aa0 --- /dev/null +++ b/src/test/java/cloud/fogbow/accs/core/models/OrderStateHistoryTest.java @@ -0,0 +1,31 @@ +package cloud.fogbow.accs.core.models; + +import static org.junit.Assert.*; + +import java.sql.Timestamp; + +import org.junit.Test; + +import cloud.fogbow.accs.core.models.orders.OrderState; + +public class OrderStateHistoryTest { + private static final Timestamp TIMESTAMP_1 = new Timestamp(1L); + private static final Timestamp TIMESTAMP_2 = new Timestamp(2L); + private OrderStateHistory stateHistory; + + @Test + public void testUpdateAndGetHistory() { + this.stateHistory = new OrderStateHistory(); + + assertTrue(this.stateHistory.getHistory().isEmpty()); + + this.stateHistory.updateState(OrderState.FULFILLED, TIMESTAMP_1); + + assertEquals(OrderState.FULFILLED, this.stateHistory.getHistory().get(TIMESTAMP_1)); + + this.stateHistory.updateState(OrderState.HIBERNATED, TIMESTAMP_2); + + assertEquals(OrderState.FULFILLED, this.stateHistory.getHistory().get(TIMESTAMP_1)); + assertEquals(OrderState.HIBERNATED, this.stateHistory.getHistory().get(TIMESTAMP_2)); + } +} diff --git a/src/test/java/cloud/fogbow/accs/core/processors/SyncProcessorTest.java b/src/test/java/cloud/fogbow/accs/core/processors/SyncProcessorTest.java index 57692d6..58cb446 100644 --- a/src/test/java/cloud/fogbow/accs/core/processors/SyncProcessorTest.java +++ b/src/test/java/cloud/fogbow/accs/core/processors/SyncProcessorTest.java @@ -179,7 +179,8 @@ public void testManageRecordWithClosedStateChange() { //setup Order order = testUtils.createOrder(FAKE_ORDER_ID); AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.CLOSED); - auditableOrderStateChange.setTimestamp(testUtils.getNOW()); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); Record record = testUtils.createRecord(order.getId()); Mockito.when(dbManager.getRecordByOrderId(Mockito.anyString())).thenReturn(record); @@ -190,6 +191,7 @@ public void testManageRecordWithClosedStateChange() { Assert.assertEquals(auditableOrderStateChange.getTimestamp(), record.getEndTime()); Assert.assertEquals(syncProcessor.extractDateFromTimestamp(auditableOrderStateChange.getTimestamp()), record.getEndDate()); Assert.assertEquals(syncProcessor.getDuration(record.getEndTime(), record.getStartTime()), record.getDuration()); + Mockito.verify(record.getStateHistory()).updateState(OrderState.CLOSED, changeTime); } //test case: tests if record's duration and state are set when an UnableToCheckStatus event arrives. @@ -198,7 +200,8 @@ public void testManageRecordWithUnableToCheckStatusStateChange() { //setup Order order = testUtils.createOrder(FAKE_ORDER_ID); AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.UNABLE_TO_CHECK_STATUS); - auditableOrderStateChange.setTimestamp(testUtils.getNOW()); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); Record record = testUtils.createRecord(order.getId()); Mockito.when(dbManager.getRecordByOrderId(Mockito.anyString())).thenReturn(record); @@ -207,6 +210,7 @@ public void testManageRecordWithUnableToCheckStatusStateChange() { //verify Assert.assertEquals(syncProcessor.getDuration(auditableOrderStateChange.getTimestamp(), record.getStartTime()), record.getDuration()); Assert.assertEquals(OrderState.UNABLE_TO_CHECK_STATUS, record.getState()); + Mockito.verify(record.getStateHistory()).updateState(OrderState.UNABLE_TO_CHECK_STATUS, changeTime); } //test case: test if the method works properly when a fulfilled state change event arrives. @@ -216,7 +220,8 @@ public void testManageRecordWithFulfilledStateChange() { //setup Order order = testUtils.createOrder(FAKE_ORDER_ID); AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.FULFILLED); - auditableOrderStateChange.setTimestamp(testUtils.getNOW()); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); Record record = testUtils.createSimplestRecord(order.getId()); Timestamp recordStartTime = new Timestamp(System.currentTimeMillis()+testUtils.TEN_SECONDS); record.setStartTime(recordStartTime); @@ -234,8 +239,141 @@ public void testManageRecordWithFulfilledStateChange() { syncProcessor.manageRecord(auditableOrderStateChange); //verify Assert.assertEquals(auditableOrderStateChange.getTimestamp(), record.getStartTime()); + Mockito.verify(record.getStateHistory(), Mockito.times(2)).updateState(OrderState.FULFILLED, changeTime); + } + + // test case: When invoking the method manageRecord passing a PAUSING state change, + // it must update the record's state history properly. + @Test + public void testManageRecordWithPausingStateChange() { + // setup + Order order = testUtils.createOrder(FAKE_ORDER_ID); + AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.PAUSING); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); + Record record = testUtils.createSimplestRecord(order.getId()); + Timestamp recordStartTime = new Timestamp(System.currentTimeMillis()+testUtils.TEN_SECONDS); + record.setStartTime(recordStartTime); + + Mockito.when(dbManager.getRecordByOrderId(Mockito.anyString())).thenReturn(record); + + // exercise + syncProcessor.manageRecord(auditableOrderStateChange); + + // verify + Mockito.verify(record.getStateHistory()).updateState(OrderState.PAUSING, changeTime); + } + + // test case: When invoking the method manageRecord passing a PAUSED state change, + // it must update the record's state history properly. + @Test + public void testManageRecordWithPausedStateChange() { + // setup + Order order = testUtils.createOrder(FAKE_ORDER_ID); + AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.PAUSED); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); + Record record = testUtils.createSimplestRecord(order.getId()); + Timestamp recordStartTime = new Timestamp(System.currentTimeMillis()+testUtils.TEN_SECONDS); + record.setStartTime(recordStartTime); + + Mockito.when(dbManager.getRecordByOrderId(Mockito.anyString())).thenReturn(record); + + // exercise + syncProcessor.manageRecord(auditableOrderStateChange); + + // verify + Mockito.verify(record.getStateHistory()).updateState(OrderState.PAUSED, changeTime); + } + + // test case: When invoking the method manageRecord passing a HIBERNATING state change, + // it must update the record's state history properly. + @Test + public void testManageRecordWithHibernatingStateChange() { + // setup + Order order = testUtils.createOrder(FAKE_ORDER_ID); + AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.HIBERNATING); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); + Record record = testUtils.createSimplestRecord(order.getId()); + Timestamp recordStartTime = new Timestamp(System.currentTimeMillis()+testUtils.TEN_SECONDS); + record.setStartTime(recordStartTime); + + Mockito.when(dbManager.getRecordByOrderId(Mockito.anyString())).thenReturn(record); + + // exercise + syncProcessor.manageRecord(auditableOrderStateChange); + + // verify + Mockito.verify(record.getStateHistory()).updateState(OrderState.HIBERNATING, changeTime); + } + + // test case: When invoking the method manageRecord passing a HIBERNATED state change, + // it must update the record's state history properly. + @Test + public void testManageRecordWithHibernatedStateChange() { + // setup + Order order = testUtils.createOrder(FAKE_ORDER_ID); + AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.HIBERNATED); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); + Record record = testUtils.createSimplestRecord(order.getId()); + Timestamp recordStartTime = new Timestamp(System.currentTimeMillis()+testUtils.TEN_SECONDS); + record.setStartTime(recordStartTime); + + Mockito.when(dbManager.getRecordByOrderId(Mockito.anyString())).thenReturn(record); + + // exercise + syncProcessor.manageRecord(auditableOrderStateChange); + + // verify + Mockito.verify(record.getStateHistory()).updateState(OrderState.HIBERNATED, changeTime); + } + + // test case: When invoking the method manageRecord passing a STOPPING state change, + // it must update the record's state history properly. + @Test + public void testManageRecordWithStoppingStateChange() { + // setup + Order order = testUtils.createOrder(FAKE_ORDER_ID); + AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.STOPPING); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); + Record record = testUtils.createSimplestRecord(order.getId()); + Timestamp recordStartTime = new Timestamp(System.currentTimeMillis()+testUtils.TEN_SECONDS); + record.setStartTime(recordStartTime); + + Mockito.when(dbManager.getRecordByOrderId(Mockito.anyString())).thenReturn(record); + + // exercise + syncProcessor.manageRecord(auditableOrderStateChange); + + // verify + Mockito.verify(record.getStateHistory()).updateState(OrderState.STOPPING, changeTime); } + + // test case: When invoking the method manageRecord passing a STOPPED state change, + // it must update the record's state history properly. + @Test + public void testManageRecordWithStoppedStateChange() { + // setup + Order order = testUtils.createOrder(FAKE_ORDER_ID); + AuditableOrderStateChange auditableOrderStateChange = testUtils.createAuditableOrderStateChange(order, OrderState.STOPPED); + Timestamp changeTime = testUtils.getNOW(); + auditableOrderStateChange.setTimestamp(changeTime); + Record record = testUtils.createSimplestRecord(order.getId()); + Timestamp recordStartTime = new Timestamp(System.currentTimeMillis()+testUtils.TEN_SECONDS); + record.setStartTime(recordStartTime); + Mockito.when(dbManager.getRecordByOrderId(Mockito.anyString())).thenReturn(record); + + // exercise + syncProcessor.manageRecord(auditableOrderStateChange); + + // verify + Mockito.verify(record.getStateHistory()).updateState(OrderState.STOPPED, changeTime); + } + //test case: check if checkOrdersHistory make the calls properly. @Test public void checkOrdersHistory() {