Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Submit assessment krub #39

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3aa8fd0
add postgres docker compose
bencomtech Feb 25, 2024
6a8a1ce
test connect to db
bencomtech Feb 25, 2024
f674fe6
change api start to 8888 port
bencomtech Feb 25, 2024
d3bad39
add admin controller, entity and validation
bencomtech Feb 25, 2024
88deb03
add lottery service
bencomtech Feb 25, 2024
57b72c3
add lottery repo
bencomtech Feb 25, 2024
1dc0b58
response with 201
bencomtech Feb 25, 2024
e54e9fa
custom response only ticket
bencomtech Feb 25, 2024
c5c1d60
add basic auth
bencomtech Feb 25, 2024
d96fa89
Merge pull request #1 from bencomtech/feature/EXP01-admin-add-new-lot…
bencomtech Feb 25, 2024
11c396a
add feature list lotteries
bencomtech Feb 25, 2024
5cf2aa0
remove unused import
bencomtech Feb 25, 2024
aedba28
Merge pull request #2 from bencomtech/feature/EXP02-user-list-all-lot…
bencomtech Feb 25, 2024
795fe30
Refactor code
bencomtech Feb 26, 2024
7c4d481
fix path variable
bencomtech Feb 26, 2024
184ef88
Merge pull request #3 from bencomtech/feature/EXP03-user-buy-lottery
bencomtech Feb 26, 2024
75fbaaa
list lottery only current amount > 0
bencomtech Feb 26, 2024
6b9bf7c
Merge pull request #4 from bencomtech/feature/EXP03-user-buy-lottery
bencomtech Feb 26, 2024
f7598c3
add current amount field
bencomtech Feb 26, 2024
ea0e47c
Merge branch 'main' into develop
bencomtech Feb 26, 2024
e39f0e3
list all my lottery
bencomtech Feb 26, 2024
eeba3b4
Merge pull request #5 from bencomtech/feature/EXP04-user-list-all-my-…
bencomtech Feb 26, 2024
cf53c7f
sell back lottery
bencomtech Feb 26, 2024
a6c7f94
Merge pull request #6 from bencomtech/feature/EXP05-user-sell-back-my…
bencomtech Feb 26, 2024
63b5380
refactor code
bencomtech Feb 26, 2024
a12d173
refactor structure
bencomtech Feb 26, 2024
951d7c6
remove unusage import
bencomtech Feb 26, 2024
29b0283
add docker
bencomtech Feb 26, 2024
8b0ec57
add controller test
bencomtech Feb 26, 2024
9f37528
add service test
bencomtech Feb 26, 2024
91fc4ca
add all test and refactor code
bencomtech Feb 26, 2024
91d9e5a
Merge pull request #7 from bencomtech/develop
bencomtech Feb 26, 2024
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
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DATABASE_URL=jdbc:postgresql://db:5432/lottery

POSTGRES_DB=lottery
POSTGRES_USER=lottery_service
POSTGRES_PASSWORD=lottery_p@ssw0d
22 changes: 22 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: '3'

networks:
lottery:

volumes:
db_data:

services:
db:
image: postgres:16
ports:
- "5432:5432"
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- lottery
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'

networks:
lottery:

volumes:
db_data:

services:
api:
build:
context: ./posttest
ports:
- "8888:8888"
depends_on:
- db
environment:
SPRING_DATASOURCE_URL: ${DATABASE_URL}
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER}
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
networks:
- lottery

db:
image: postgres:16
ports:
- "5432:5432"
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- lottery
34 changes: 34 additions & 0 deletions init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
CREATE TABLE users (
user_id INTEGER PRIMARY KEY,
display_name VARCHAR(50) NOT NULL,
created_at TIMESTAMP NOT NULL
);

CREATE TABLE lottery (
id SERIAL PRIMARY KEY,
ticket VARCHAR(6) NOT NULL,
price DECIMAL(6, 2) NOT NULL,
amount INTEGER NOT NULL,
current_amount INTEGER NOT NULL,
created_at TIMESTAMP NOT NULL
);

CREATE TABLE user_ticket (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users (user_id),
lottery_id INTEGER REFERENCES lottery (id),
created_at TIMESTAMP NOT NULL
);

INSERT INTO
users (user_id, display_name, created_at)
VALUES
(1000000001, 'Alice', NOW ()),
(1000000002, 'Bob', NOW ());

INSERT INTO
lottery (ticket, price, amount, current_amount, created_at)
VALUES
('000001', 80, 1, 1, NOW ()),
('000002', 80, 2, 2, NOW ()),
('123456', 80, 3, 3, NOW ());
29 changes: 29 additions & 0 deletions posttest/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
FROM openjdk:17-jdk-slim as build

ENV APP_HOME=/usr/app

WORKDIR $APP_HOME

COPY build.gradle settings.gradle gradlew $APP_HOME
COPY gradle $APP_HOME/gradle

RUN chmod +x gradlew
RUN ./gradlew build || return 0

COPY . .

RUN chmod +x gradlew
RUN ./gradlew build

FROM openjdk:17-jdk-slim

ENV JAR_FILE=posttest-0.0.1-SNAPSHOT.jar
ENV APP_HOME=/usr/app

WORKDIR $APP_HOME

COPY --from=build $APP_HOME/build/libs/$JAR_FILE .

EXPOSE 8888

ENTRYPOINT ["java", "-jar", "posttest-0.0.1-SNAPSHOT.jar"]
22 changes: 22 additions & 0 deletions posttest/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'java'
id 'jacoco'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.1.4'
}
Expand All @@ -23,6 +24,8 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.postgresql:postgresql'
compileOnly 'org.projectlombok:lombok'
Expand All @@ -33,3 +36,22 @@ dependencies {
tasks.named('test') {
useJUnitPlatform()
}


test {
finalizedBy jacocoTestReport // report is always generated after tests run
}

jacocoTestReport {
dependsOn test // tests are required to run before generating the report
reports {
xml.required = false
csv.required = false
html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
}
}

jacoco {
toolVersion = "0.8.11"
reportsDirectory = layout.buildDirectory.dir('customJacocoReportDir')
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class PosttestApplication {

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

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.kbtg.bootcamp.posttest;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@EnableWebSecurity
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/admin/**").hasAnyRole("ADMIN")
.requestMatchers("/**").permitAll()
.anyRequest()
.authenticated())
.httpBasic(withDefaults())
.build();
}

@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withUsername("admin")
.password(passwordEncoder().encode("password"))
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(user);
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.kbtg.bootcamp.posttest.controllers;

import com.kbtg.bootcamp.posttest.entities.Lottery;
import com.kbtg.bootcamp.posttest.services.LotteryService;
import com.kbtg.bootcamp.posttest.models.LotteryResponse;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("admin")
public class AdminController {
@Autowired
private final LotteryService lotteryService;

public AdminController(LotteryService lotteryService) {
this.lotteryService = lotteryService;
}

@PostMapping("/lotteries")
public ResponseEntity<LotteryResponse> addLottery(@Valid @RequestBody Lottery lottery) {
var lotteryResponse = lotteryService.addLottery(lottery);

return new ResponseEntity<>(lotteryResponse, HttpStatus.CREATED);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.kbtg.bootcamp.posttest.controllers;

import com.kbtg.bootcamp.posttest.models.LotteriesResponse;
import com.kbtg.bootcamp.posttest.services.LotteryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("lotteries")
public class LotteryController {
@Autowired
private final LotteryService lotteryService;

public LotteryController(LotteryService lotteryService) {
this.lotteryService = lotteryService;
}

@GetMapping("")
public LotteriesResponse listLotteries() {
return lotteryService.listLotteries();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.kbtg.bootcamp.posttest.controllers;

import com.kbtg.bootcamp.posttest.models.BuyLotteryResponse;
import com.kbtg.bootcamp.posttest.models.LotteryResponse;
import com.kbtg.bootcamp.posttest.models.MyLotteryResponse;
import com.kbtg.bootcamp.posttest.services.LotteryService;
import com.kbtg.bootcamp.posttest.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("users")
public class UserLotteryController {
@Autowired
private final UserService userService;

@Autowired
private final LotteryService lotteryService;

public UserLotteryController(UserService userService, LotteryService lotteryService) {
this.userService = userService;
this.lotteryService = lotteryService;
}

@GetMapping("/{userId}/lotteries")
public MyLotteryResponse listAllMyLottery(@PathVariable("userId") Integer userId) {
return userService.listAllMyLottery(userId);
}

@PostMapping("/{userId}/lotteries/{ticketId}")
public BuyLotteryResponse buyLottery(@PathVariable("userId") Integer userId, @PathVariable("ticketId") Integer ticketId) {
return lotteryService.buyLottery(userId, ticketId);
}

@DeleteMapping("/{userId}/lotteries/{ticketId}")
public LotteryResponse sellBackMyLottery(@PathVariable("userId") Integer userId, @PathVariable("ticketId") Integer ticketId) {
return lotteryService.sellBackMyLottery(userId, ticketId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.kbtg.bootcamp.posttest.entities;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.io.Serializable;
import java.util.Date;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
value = {"createdAt"},
allowGetters = true
)
@Data
@Getter
@Setter
@ToString
public abstract class AuditEntity implements Serializable {
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_at", nullable = false, updatable = false)
@CreatedDate
@JsonIgnore
private Date createdAt;
}
Loading