Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
!.jar
!target/cib-interns-test-task.jar

/target/
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM openjdk:11-jre-slim
VOLUME /tmp
EXPOSE 8080
ADD /target/cib-interns-test-task.jar cib-interns-test-task.jar
ENTRYPOINT ["java", "-jar", "cib-interns-test-task.jar"]
52 changes: 52 additions & 0 deletions INSTRUCTIONS.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## Решение тестового задания.
### Варианты запуска:
#### 1. Docker
- Загрузить и распаковать репозиторий
- Запустить терминал в папке проекта
- Запустить команду сборки, передавая имя образа в опции -t
`docker build -t cib-interns-test-task .`
- Проверить, что образ отображается при выполнении команды
`docker images --all`
- Выполнить команду
`docker-compose up`
- Приложение доступно по: http://localhost:8080

#### 2. Heroku:
- Ссылка на приложение:
https://cib-interns-test-task-by-simbi.herokuapp.com/

### Список URL HTTP-методов
- POST /api/socks/income
Регистрирует приход носков на склад.

Параметры запроса передаются в теле запроса в виде JSON-объекта со следующими атрибутами:

color — цвет носков, строка (например, black, red, yellow);
cottonPart — процентное содержание хлопка в составе носков, целое число от 0 до 100 (например, 30, 18, 42);
quantity — количество пар носков, целое число больше 0.
Результаты:

HTTP 200 — удалось добавить приход;
HTTP 400 — параметры запроса отсутствуют или имеют некорректный формат;
HTTP 500 — произошла ошибка, не зависящая от вызывающей стороны (например, база данных недоступна).

- POST /api/socks/outcome
Регистрирует отпуск носков со склада. Здесь параметры и результаты аналогичные, но общее количество носков указанного цвета и состава не увеличивается, а уменьшается.

- GET /api/socks
Возвращает общее количество носков на складе, соответствующих переданным в параметрах критериям запроса.

Параметры запроса передаются в URL:

color — цвет носков, строка;
operation — оператор сравнения значения количества хлопка в составе носков, одно значение из: moreThan, lessThan, equal;
cottonPart — значение процента хлопка в составе носков из сравнения.
Результаты:

HTTP 200 — запрос выполнен, результат в теле ответа в виде строкового представления целого числа;
HTTP 400 — параметры запроса отсутствуют или имеют некорректный формат;
HTTP 500 — произошла ошибка, не зависящая от вызывающей стороны (например, база данных недоступна).
Примеры запросов:

/api/socks?color=red&operation=moreThan&cottonPart=90 — должен вернуть общее количество красных носков с долей хлопка более 90%;
/api/socks?color=black&operation=lessThan?cottonPart=10 — должен вернуть общее количество черных носков с долей хлопка менее 10%.
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: java $JAVA_OPTS -Dserver.port=$PORT -jar target/*.jar
36 changes: 36 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
version: "3"
services:
API:
image: cib-interns-test-task
ports:
- 8080:8080
depends_on:
postgres:
condition: service_healthy
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/storage
- SPRING_DATASOURCE_USERNAME=postgres
- SPRING_DATASOURCE_PASSWORD=password
#- SPRING_JPA_HIBERNATE_DDL_AUTO=update
postgres:
image: library/postgres:alpine
build: ./src/main/resources/db/migration
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_USER=postgres
- POSTGRES_DB=storage
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
flyway:
#boxfuse/flyway:5-alpine
image: flyway/flyway:6-alpine
command: -url=jdbc:postgresql://postgres:5432/storage -schemas=public -user=postgres -password=postgres migrate
volumes:
- ./src/main/resources/db/migration:/flyway/sql
depends_on:
- postgres
5 changes: 5 additions & 0 deletions heroku.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
jpa.showSql=false
hibernate.format_sql=false
hibernate.use_sql_comments=false
database.init=false
jdbc.initLocation=db.migration/V1__init_db
140 changes: 140 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ru.simbial</groupId>
<artifactId>cib-interns-test-task</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cib-interns-test-task</name>
<description>my task solution</description>

<properties>
<java.version>11</java.version>
<postgres.version>42.3.0</postgres.version>
<flyway.version>8.0.2</flyway.version>
<gson.version>2.8.5</gson.version>
</properties>

<dependencies>

<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>${flyway.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgres.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>

<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>${flyway.version}</version>
<configuration>
<url>jdbc:postgresql://localhost:5432/storage</url>
<user></user>
<password></password>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals><goal>copy</goal></goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.heroku</groupId>
<artifactId>webapp-runner</artifactId>
<version>9.0.41.0</version>
<destFileName>webapp-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.simbial.cibinternstesttask;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CibInternsTestTaskApplication {

public static void main(String[] args) {
SpringApplication.run(CibInternsTestTaskApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ru.simbial.cibinternstesttask.app;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public enum CottonPartComparisonOperation {
MORE_THAN("moreThan"),
LESS_THAN("lessThan"),
EQUAL("equal");
/*DEFAULT("");*/

private final String label;

CottonPartComparisonOperation(String label) {
this.label = label;
}

public String getLabel() {
return label;
}

public static List<String> getLabels() {
return Arrays.stream(CottonPartComparisonOperation
.values())
.map(CottonPartComparisonOperation::getLabel)
.collect(Collectors.toList());
}

public static CottonPartComparisonOperation getByLabel(String label) {
for(CottonPartComparisonOperation v : values())
if(v.getLabel().equalsIgnoreCase(label)) return v;
throw new IllegalArgumentException();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ru.simbial.cibinternstesttask.app;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
public class HelloController {

@GetMapping(produces = "application/json")
public String getHelloMsg() {
return
"Available endpoints: " +
"\n GET | /api/socks |required params: color, operation(moreThan/lessThan/equal), cottonPart" +
"\n POST| /api/socks/income |required request body attrs: color, cottonPart, quantity" +
"\n POST| /api/socks/outcome|required request body attrs: color, cottonPart, quantity";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ru.simbial.cibinternstesttask.app;

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import ru.simbial.cibinternstesttask.app.model.SocksRequestData;

@RestController
@RequestMapping("/socks")
public class SocksController {
private final SocksService service;

public SocksController(SocksService service) {
this.service = service;
}

@GetMapping()
public ResponseEntity<?> getSocksCountByFilter(
@RequestParam String color,
@RequestParam String operation,
@RequestParam Integer cottonPart) {

return service.countSocksByFilter(color, operation, cottonPart);
}

@PostMapping(value = "/income", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> addSocks(@RequestBody /*@Valid*/ SocksRequestData data) {
return service.registerSocksIncome(data);
}

@PostMapping(value = "/outcome", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> withdrawSocks(@RequestBody /*@Valid */SocksRequestData data) {
return service.registerSocksOutcome(data);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ru.simbial.cibinternstesttask.app;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.simbial.cibinternstesttask.app.model.SocksDBModel;


@Repository
public interface SocksRepository extends JpaRepository<SocksDBModel, SocksDBModel.SocksId> {

@Query("select coalesce(sum(s.quantity), 0) from SocksDBModel s " +
" where s.id.color=?1 and s.id.cottonPart>?2")
Long getQuantityByColorAndCottonPartMoreThan(String color, Integer cottonPart);


@Query("select coalesce(sum(s.quantity), 0) from SocksDBModel s " +
"where s.id.color=?1" +
" and s.id.cottonPart<?2 ")
Long getQuantityByColorAndCottonPartLessThan(String color, Integer cottonPart);

@Query("select coalesce(sum(s.quantity), 0) from SocksDBModel s " +
"where s.id.color=?1" +
" and s.id.cottonPart=?2 ")
Long getQuantityByColorAndCottonPartEqual(String color, Integer cottonPart);

}
Loading