Skip to content

Commit

Permalink
Added endpoint for interaction with traffic package
Browse files Browse the repository at this point in the history
Added unit tests for traffic services
Some refactoring and optimizing
  • Loading branch information
muchnik committed Sep 1, 2019
1 parent 7cb6b6a commit 96881c8
Show file tree
Hide file tree
Showing 35 changed files with 313 additions and 132 deletions.
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@

## Запуск
1. Скомпилировать проект (включить Annotation Processor для Lombok и поставить Lombok Plugin для IDEA)
2. Запустить из IDE или с помощью java -jar app.jar
2. Запустить из IDE через MobileServicesApplication#main или после сборки из target с помощью java -jar mobile-services-1.0.jar

## Использование
1. WebUI для встроенной базы данных: http://localhost:8080/h2-console/
1.1 JDBC Url: jdbc:h2:mem:test
1.2. User: sa
1.3. Password:
JDBC Url: jdbc:h2:mem:test
User: sa
Password:

2. API доступно на порту 8080

| Метод | Тело запроса | Адрес | Описание |
Expand All @@ -42,12 +43,19 @@
| GET | пусто |`localhost:8080/api/v1/sim-card/${номер_телефона}/status` | Статус сим-карты |
| PUT | {"value": <true/false>} |`localhost:8080/api/v1/sim-card/${номер_телефона}/status` | Обновить статус сим-карты |
| GET | пусто |`localhost:8080/api/v1/sim-card/${номер_телефона}/minutes/total` | Количество активных минут |
| GET | пусто |`localhost:8080/api/v1/sim-card/${номер_телефона}/traffic/total` | Количество активных гигабайт |
| GET | пусто |`localhost:8080/api/v1/sim-card/${номер_телефона}/minutes/packages`| Количество активных пакетов минут |
| POST |{"basePackageId": "<id нужного пакета>","addition": <Количество минут>,"daysToLive": "<Время жизни (дней)>"}|`localhost:8080/api/v1/sim-card/${номер_телефона}/minutes` | Добавить пакет минут к сим-карте |
| GET | пусто |`localhost:8080/api/v1/sim-card/${номер_телефона}/traffic/packages`| Количество активных пакетов гигабайт |
| POST |{"basePackageId": "<id базового пакета>","addition": <Количество минут>,"daysToLive": "<Время жизни (дней)>"}|`localhost:8080/api/v1/sim-card/${номер_телефона}/minutes` | Добавить пакет минут к сим-карте |
| POST |{"basePackageId": "<id базового пакета>","addition": <Количество гигабайт>,"daysToLive": "<Время жизни (дней)>"}|`localhost:8080/api/v1/sim-card/${номер_телефона}/traffic` | Добавить пакет гигабайт к сим-карте |
| GET | пусто |`localhost:8080/api/v1/packages-of-minutes/${id_пакета}` | Получить базовый пакет минут (обьект) |
| GET | пусто |`localhost:8080/api/v1/packages-of-minutes/` | Получить все базовые пакеты минут (обьект) |
| GET | пусто |`localhost:8080/api/v1/traffic-packages/${id_пакета}` | Получить базовый пакет траффика (обьект) |
| GET | пусто |`localhost:8080/api/v1/packages-of-minutes/` | Получить все базовые пакеты минут (список) |
| GET | пусто |`localhost:8080/api/v1/traffic-packages/` | Получить все базовые пакеты траффика (список) |
| POST |{"name":"<имя_пакета>","type":"<FREE_ROAMING \ FAVORITE_NUMBER>"} | `localhost:8080/api/v1/packages-of-minutes/` | Сохранить в базу базовый пакет минут |
| PUT |{"value": <количество минут (отр или пол)>} |`localhost:8080/api/v1/packages-of-minutes/details/${details_id}` | Сохранить в базу базовый пакет минут |
| POST |{"name":"<имя_пакета>","type":"<YOUTUBE \ FREE_MESSENGER>"} | `localhost:8080/api/v1/traffic-packages/` | Сохранить в базу базовый пакет траффика |
| PUT |{"value": <количество минут (отр или пол)>} |`localhost:8080/api/v1/packages-of-minutes/details/${details_id}` | Расходовать или добавлять минуты в пакет минут |
| PUT |{"value": <количество гигабайт (отр или пол)>} |`localhost:8080/api/v1/traffic-packages/details/${details_id}` | Расходовать или добавлять гигабайты в пакет траффика |

## Контакты
[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import ru.muchnik.yota.mobileservices.model.entity.minutes.MinutesPackageCatalog;
import ru.muchnik.yota.mobileservices.model.entity.minutes.MinutesDetails;
import ru.muchnik.yota.mobileservices.model.entity.SimCard;
import ru.muchnik.yota.mobileservices.model.entity.minutes.MinutesDetails;
import ru.muchnik.yota.mobileservices.model.entity.minutes.MinutesPackageCatalog;
import ru.muchnik.yota.mobileservices.model.entity.traffic.TrafficDetails;
import ru.muchnik.yota.mobileservices.model.entity.traffic.TrafficPackageCatalog;
import ru.muchnik.yota.mobileservices.repository.minutes.MinutesPackageCatalogRepository;
import ru.muchnik.yota.mobileservices.repository.SimCardRepository;
import ru.muchnik.yota.mobileservices.repository.minutes.MinutesPackageCatalogRepository;
import ru.muchnik.yota.mobileservices.repository.traffic.TrafficPackageCatalogRepository;

import java.util.Arrays;

/**
* Application entry point
* Application for "YOTA" test task, see readme.md for more information
*
* @author Muchnik Andrey <b>[email protected]</b>
* @since 01.09.2019
*/
@SpringBootApplication
public class MobileServicesApplication {
Expand All @@ -34,12 +37,12 @@ public CommandLineRunner runner(SimCardRepository simCardRepository, MinutesPack
return args -> {
MinutesPackageCatalog freeRoaming = MinutesPackageCatalog.builder()
.name("FreeRoaming")
.type(MinutesPackageCatalog.MinutesPackageType.FREE_ROAMING)
.type(MinutesPackageCatalog.Type.FREE_ROAMING)
.build();

MinutesPackageCatalog favoriteNumber = MinutesPackageCatalog.builder()
.name("FavoriteNumber")
.type(MinutesPackageCatalog.MinutesPackageType.FAVORITE_NUMBER)
.type(MinutesPackageCatalog.Type.FAVORITE_NUMBER)
.build();

MinutesDetails packageDetails = new MinutesDetails(freeRoaming, 300, 30);
Expand All @@ -48,7 +51,7 @@ public CommandLineRunner runner(SimCardRepository simCardRepository, MinutesPack

TrafficPackageCatalog trafficYoutube = TrafficPackageCatalog.builder()
.name("Youtube")
.type(TrafficPackageCatalog.TrafficPackageType.YOUTUBE)
.type(TrafficPackageCatalog.Type.YOUTUBE)
.build();

TrafficDetails trafficDetails = new TrafficDetails(trafficYoutube, 4, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

/**
* Common configuration for aspectj
*
* @apiNote needed to exclude autoconfiguration in test environment
*/
@Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

/**
* Common configuration for web mvc
*
* @apiNote needed to exclude autoconfiguration in test environment
*/
@Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@
import ru.muchnik.yota.mobileservices.service.BaseDetailsService;
import ru.muchnik.yota.mobileservices.service.BasePackageService;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RequiredArgsConstructor
public abstract class BasePackageController<Type extends IPackageCatalog,
Expand All @@ -38,7 +35,7 @@ public ResponseEntity<List<Type>> getPackages() {
public abstract ResponseEntity<Type> savePackage(@RequestBody Type packageToSave);

@PutMapping("/details/{detailsId}")
public ResponseEntity<Void> updateAmount(@PathVariable String detailsId, @RequestBody ValueDTO<Integer> input) {
public ResponseEntity<Void> updatePackageAmount(@PathVariable String detailsId, @RequestBody ValueDTO<Integer> input) {
Integer value = input.getValue();
if (value != 0) {
detailsService.updateAmount(detailsId, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import ru.muchnik.yota.mobileservices.model.dto.CreatePackageDTO;
import ru.muchnik.yota.mobileservices.model.entity.IPackageCatalog;
import ru.muchnik.yota.mobileservices.model.entity.minutes.MinutesDetails;
import ru.muchnik.yota.mobileservices.model.entity.minutes.MinutesPackageCatalog;
import ru.muchnik.yota.mobileservices.service.minutes.MinutesDetailsService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import ru.muchnik.yota.mobileservices.model.dto.ValueDTO;
import ru.muchnik.yota.mobileservices.model.entity.SimCard;
import ru.muchnik.yota.mobileservices.model.entity.minutes.MinutesDetails;
import ru.muchnik.yota.mobileservices.model.entity.traffic.TrafficDetails;
import ru.muchnik.yota.mobileservices.service.SimCardService;
import ru.muchnik.yota.mobileservices.service.minutes.MinutesDetailsService;
import ru.muchnik.yota.mobileservices.service.traffic.TrafficDetailsService;

import java.net.URI;
import java.util.List;
Expand All @@ -20,7 +22,8 @@
@RequestMapping(value = "api/v1/sim-card/", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class SimCardController {
private final SimCardService simCardService;
private final MinutesDetailsService detailsService;
private final MinutesDetailsService minutesDetailsService;
private final TrafficDetailsService trafficDetailsService;

@GetMapping("{number}")
public ResponseEntity<SimCard> getSimCard(@PathVariable String number) {
Expand All @@ -34,28 +37,49 @@ public ResponseEntity<ValueDTO<Boolean>> getSimCardStatus(@PathVariable String n

@PutMapping("{number}/status")
public ResponseEntity<SimCard> updateSimCardStatus(@PathVariable String number,
@RequestBody ValueDTO<Boolean> statusDTO) {
@RequestBody ValueDTO<Boolean> statusDTO) {
return ResponseEntity.ok(simCardService.updateSimCardStatus(number, statusDTO.getValue()));
}

@GetMapping("{number}/minutes/total")
public ResponseEntity<ValueDTO<Integer>> getSimCardActiveMinutesTotal(@PathVariable String number) {
int sumOfMinutesLeft = detailsService.getAllActivePackages(number).stream()
int sumOfMinutesLeft = minutesDetailsService.getAllActivePackages(number).stream()
.mapToInt(MinutesDetails::getMinutesLeft)
.sum();
return ResponseEntity.ok(new ValueDTO<>(sumOfMinutesLeft));
}

@GetMapping("{number}/traffic/total")
public ResponseEntity<ValueDTO<Integer>> getSimCardActiveTrafficTotal(@PathVariable String number) {
int sumOfGigabytesLeft = trafficDetailsService.getAllActivePackages(number).stream()
.mapToInt(TrafficDetails::getGigabytesLeft)
.sum();
return ResponseEntity.ok(new ValueDTO<>(sumOfGigabytesLeft));
}

@GetMapping("{number}/minutes/packages")
public ResponseEntity<List<MinutesDetails>> getSimCardActivePackagesOfMinutes(@PathVariable String number) {
return ResponseEntity.ok(detailsService.getAllActivePackages(number));
public ResponseEntity<List<MinutesDetails>> getSimCardActiveMinutesPackages(@PathVariable String number) {
return ResponseEntity.ok(minutesDetailsService.getAllActivePackages(number));
}

@GetMapping("{number}/traffic/packages")
public ResponseEntity<List<TrafficDetails>> getSimCardActiveTrafficPackages(@PathVariable String number) {
return ResponseEntity.ok(trafficDetailsService.getAllActivePackages(number));
}

@PostMapping("{number}/minutes")
public ResponseEntity<MinutesDetails> addPackageOfMinutesToSimCard(@PathVariable String number,
@RequestBody UpdatePackageRequestDTO requestDTO) {
public ResponseEntity<MinutesDetails> addMinutesPackageToSimCard(@PathVariable String number,
@RequestBody UpdatePackageRequestDTO requestDTO) {
MinutesDetails savedDetails = simCardService.addPackageOfMinutesToSimCard(number, requestDTO.getBasePackageId(), requestDTO.getAddition(), requestDTO.getDaysToLive());
return ResponseEntity.created(URI.create("/api/v1/packages-of-addition/" + savedDetails.getId()))
return ResponseEntity.created(URI.create("/api/v1/packages-of-minutes/" + savedDetails.getId()))
.body(savedDetails);
}

@PostMapping("{number}/traffic")
public ResponseEntity<TrafficDetails> addTrafficPackageToSimCard(@PathVariable String number,
@RequestBody UpdatePackageRequestDTO requestDTO) {
TrafficDetails savedDetails = simCardService.addTrafficPackageToSimCard(number, requestDTO.getBasePackageId(), requestDTO.getAddition(), requestDTO.getDaysToLive());
return ResponseEntity.created(URI.create("/api/v1/traffic-packages/" + savedDetails.getId()))
.body(savedDetails);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import ru.muchnik.yota.mobileservices.model.dto.CreatePackageDTO;
import ru.muchnik.yota.mobileservices.model.entity.traffic.TrafficDetails;
import ru.muchnik.yota.mobileservices.model.entity.traffic.TrafficPackageCatalog;
import ru.muchnik.yota.mobileservices.service.traffic.TrafficDetailsService;
Expand All @@ -16,7 +15,7 @@

@Controller
@RequestMapping(value = "api/v1/traffic-packages/", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class TrafficPackageController extends BasePackageController<TrafficPackageCatalog, TrafficDetails, TrafficPackageService, TrafficDetailsService> {
public class TrafficPackageController extends BasePackageController<TrafficPackageCatalog, TrafficDetails, TrafficPackageService, TrafficDetailsService> {
public TrafficPackageController(final TrafficPackageService packageService, final TrafficDetailsService detailsService) {
super(packageService, detailsService);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public ResponseEntity<ErrorResponse> handleNotFoundException(final Throwable ex)
}

/**
* In case of any validation exceptions, occured on any entity
* In case of any validation exceptions occurred on any entity
*
* @param ex throwed exception
* @return business {@link ResponseEntity}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
import org.springframework.http.HttpStatus;

/**
* In case of exceptions that being throwed in {@link org.springframework.web.bind.annotation.RestController}
* that was catched via {@link org.springframework.web.bind.annotation.ControllerAdvice} for further response to client
* Simple model for response via {@link org.springframework.stereotype.Controller} in case of any errors.
*
* @apiNote In case of exceptions that being throwed in {@link org.springframework.web.bind.annotation.RestController}
* that was catched via {@link org.springframework.web.bind.annotation.ControllerAdvice} for further response to client
* @see ru.muchnik.yota.mobileservices.controller.advice.GlobalControllerExceptionHandler
*/
@Data
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* Data transfer object for update package operation
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import lombok.NoArgsConstructor;

/**
* Simple DTO for single value-container
* Simple data transfer object for single value-container
*
* @param <Type> type of value
*/
@Data
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ru.muchnik.yota.mobileservices.model.entity;

public interface IPackageCatalog<Type> {
public interface IPackageCatalog<Type extends Enum> {
String getId();

String getName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class SimCard {
private boolean isActive;

/**
* All {@link MinutesDetails} packages of addition that is attached to this sim card
* All {@link MinutesDetails} packages of minutes that is attached to this sim card
*/
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "simCard", fetch = FetchType.LAZY)
@NotNull
Expand All @@ -46,7 +46,7 @@ public class SimCard {
private List<MinutesDetails> minutesDetails;

/**
* All {@link MinutesDetails} packages of addition that is attached to this sim card
* All {@link MinutesDetails} packages of traffic that is attached to this sim card
*/
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "simCard", fetch = FetchType.LAZY)
@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class MinutesDetails implements IDetails<MinutesPackageCatalog> {

@PositiveOrZero
private int minutesLeft;

@NotNull
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime activationDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
@AllArgsConstructor
@Builder
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class MinutesPackageCatalog implements IPackageCatalog<MinutesPackageCatalog.MinutesPackageType> {
public class MinutesPackageCatalog implements IPackageCatalog<MinutesPackageCatalog.Type> {
@Id
@EqualsAndHashCode.Include
@Size(max = 36)
Expand All @@ -33,9 +33,9 @@ public class MinutesPackageCatalog implements IPackageCatalog<MinutesPackageCata
* Type, in future may contains relations with other entities that contains package behaviour
*/
@Enumerated
private MinutesPackageType type;
private Type type;

public enum MinutesPackageType {
public enum Type {
FREE_ROAMING, FAVORITE_NUMBER
}
}
Loading

0 comments on commit 96881c8

Please sign in to comment.