Skip to content

Commit 6c1f5de

Browse files
authored
Merge pull request #21 from Devlaner/feature/persistence-foundation
feat(persistence): set up JPA,Flyway and integration test with Testcontainers
2 parents 7e489ea + 1e76248 commit 6c1f5de

17 files changed

Lines changed: 714 additions & 13 deletions

File tree

.github/workflows/backend-ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,8 @@ jobs:
3030
- name: Set up Gradle
3131
uses: gradle/actions/setup-gradle@v4
3232

33+
- name: Grant execute permission for gradlew
34+
run: chmod +x gradlew
35+
3336
- name: Spotless, Checkstyle, compile, and test
3437
run: ./gradlew check --no-daemon

backend/apps/api/src/main/java/dev/cleat/api/.gitkeep

Whitespace-only changes.

backend/apps/api/src/main/resources/application.yml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ spring:
33
name: cleat-api
44
autoconfigure:
55
exclude:
6-
# Temporary: database is not wired yet.
7-
# Remove these excludes once datasource is configured in issue #5.
8-
- org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
9-
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
10-
- org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
6+
# Temporary: database is not wired yet.
7+
# Remove these excludes once datasource is configured in issue #5.
8+
- org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
9+
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
10+
- org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
1111

1212
server:
1313
port: 8080
@@ -22,5 +22,3 @@ management:
2222
enabled: true
2323
redis:
2424
enabled: false
25-
db:
26-
enabled: false

backend/build.gradle.kts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import com.diffplug.gradle.spotless.SpotlessExtension
2-
import org.gradle.api.plugins.quality.CheckstyleExtension
32
import org.springframework.boot.gradle.plugin.SpringBootPlugin
43

54
plugins {
@@ -36,7 +35,8 @@ subprojects {
3635
}
3736
}
3837

39-
configure<SpotlessExtension> {
38+
39+
extensions.configure<SpotlessExtension> {
4040
java {
4141
palantirJavaFormat()
4242
removeUnusedImports()
@@ -46,7 +46,7 @@ subprojects {
4646
}
4747
}
4848

49-
configure<CheckstyleExtension> {
49+
extensions.configure<CheckstyleExtension> {
5050
toolVersion = "10.26.1"
5151
configFile = rootProject.file("config/checkstyle/checkstyle.xml")
5252
isIgnoreFailures = false
@@ -55,4 +55,4 @@ subprojects {
5555
tasks.withType<Test> {
5656
useJUnitPlatform()
5757
}
58-
}
58+
}

backend/gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
org.gradle.caching=true
22
org.gradle.parallel=true
33
org.gradle.configuration-cache=true
4+

backend/gradlew

100755100644
File mode changed.
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
plugins {
22
`java-library`
33
}
4-
54
dependencies {
65
api(project(":libs:domain"))
76
implementation(project(":libs:common"))
87
api("org.springframework.boot:spring-boot-starter-data-jpa")
98
implementation("org.flywaydb:flyway-core")
10-
implementation("org.flywaydb:flyway-database-postgresql")
9+
runtimeOnly("org.flywaydb:flyway-database-postgresql")
1110
runtimeOnly("org.postgresql:postgresql")
1211
testImplementation("org.springframework.boot:spring-boot-starter-test")
12+
testImplementation("org.testcontainers:junit-jupiter")
13+
testImplementation("org.testcontainers:postgresql")
14+
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
1315
}
16+
17+
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
package dev.cleat.persistence;
2+
3+
import jakarta.persistence.CascadeType;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.EnumType;
7+
import jakarta.persistence.Enumerated;
8+
import jakarta.persistence.GeneratedValue;
9+
import jakarta.persistence.GenerationType;
10+
import jakarta.persistence.Id;
11+
import jakarta.persistence.OneToMany;
12+
import jakarta.persistence.Table;
13+
import java.math.BigDecimal;
14+
import java.time.OffsetDateTime;
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
import java.util.UUID;
18+
import org.hibernate.annotations.CreationTimestamp;
19+
20+
@Entity
21+
@Table(name = "account")
22+
public class AccountEntity {
23+
24+
@Id
25+
@GeneratedValue(strategy = GenerationType.UUID)
26+
private UUID id;
27+
28+
@Column(name = "login", nullable = false)
29+
private String login;
30+
31+
@Column(name = "name", nullable = false)
32+
private String name;
33+
34+
@Column(name = "type", nullable = false)
35+
@Enumerated(EnumType.STRING)
36+
private AccountType type;
37+
38+
@Column(name = "plan")
39+
@Enumerated(EnumType.STRING)
40+
private Plan plan;
41+
42+
@Column(name = "repo_count")
43+
private Integer repoCount;
44+
45+
@Column(name = "member_count")
46+
private Integer memberCount;
47+
48+
@Column(name = "posture_score")
49+
private Integer postureScore;
50+
51+
@Column(name = "monthly_spend")
52+
private BigDecimal monthlySpend;
53+
54+
@Column(name = "reclaimable")
55+
private BigDecimal reclaimable;
56+
57+
@CreationTimestamp
58+
@Column(name = "created_at", nullable = false, updatable = false)
59+
private OffsetDateTime createdAt;
60+
61+
@OneToMany(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval = true)
62+
private List<RepoEntity> repos = new ArrayList<>();
63+
64+
public AccountEntity(
65+
UUID id,
66+
String login,
67+
String name,
68+
AccountType type,
69+
Plan plan,
70+
Integer repoCount,
71+
Integer memberCount,
72+
Integer postureScore,
73+
BigDecimal monthlySpend,
74+
BigDecimal reclaimable,
75+
OffsetDateTime createdAt,
76+
List<RepoEntity> repos) {
77+
this.id = id;
78+
this.login = login;
79+
this.name = name;
80+
this.type = type;
81+
this.plan = plan;
82+
this.repoCount = repoCount;
83+
this.memberCount = memberCount;
84+
this.postureScore = postureScore;
85+
this.monthlySpend = monthlySpend;
86+
this.reclaimable = reclaimable;
87+
this.createdAt = createdAt;
88+
this.repos = repos;
89+
}
90+
91+
public AccountEntity() {}
92+
93+
public UUID getId() {
94+
return id;
95+
}
96+
97+
public AccountEntity setId(UUID id) {
98+
this.id = id;
99+
return this;
100+
}
101+
102+
public String getLogin() {
103+
return login;
104+
}
105+
106+
public AccountEntity setLogin(String login) {
107+
this.login = login;
108+
return this;
109+
}
110+
111+
public String getName() {
112+
return name;
113+
}
114+
115+
public AccountEntity setName(String name) {
116+
this.name = name;
117+
return this;
118+
}
119+
120+
public AccountType getType() {
121+
return type;
122+
}
123+
124+
public AccountEntity setType(AccountType type) {
125+
this.type = type;
126+
return this;
127+
}
128+
129+
public Plan getPlan() {
130+
return plan;
131+
}
132+
133+
public AccountEntity setPlan(Plan plan) {
134+
this.plan = plan;
135+
return this;
136+
}
137+
138+
public Integer getRepoCount() {
139+
return repoCount;
140+
}
141+
142+
public AccountEntity setRepoCount(Integer repoCount) {
143+
this.repoCount = repoCount;
144+
return this;
145+
}
146+
147+
public Integer getMemberCount() {
148+
return memberCount;
149+
}
150+
151+
public AccountEntity setMemberCount(Integer memberCount) {
152+
this.memberCount = memberCount;
153+
return this;
154+
}
155+
156+
public Integer getPostureScore() {
157+
return postureScore;
158+
}
159+
160+
public AccountEntity setPostureScore(Integer postureScore) {
161+
this.postureScore = postureScore;
162+
return this;
163+
}
164+
165+
public BigDecimal getMonthlySpend() {
166+
return monthlySpend;
167+
}
168+
169+
public AccountEntity setMonthlySpend(BigDecimal monthlySpend) {
170+
this.monthlySpend = monthlySpend;
171+
return this;
172+
}
173+
174+
public BigDecimal getReclaimable() {
175+
return reclaimable;
176+
}
177+
178+
public AccountEntity setReclaimable(BigDecimal reclaimable) {
179+
this.reclaimable = reclaimable;
180+
return this;
181+
}
182+
183+
public OffsetDateTime getCreatedAt() {
184+
return createdAt;
185+
}
186+
187+
public AccountEntity setCreatedAt(OffsetDateTime createdAt) {
188+
this.createdAt = createdAt;
189+
return this;
190+
}
191+
192+
public List<RepoEntity> getRepos() {
193+
return repos;
194+
}
195+
196+
public AccountEntity setRepos(List<RepoEntity> repos) {
197+
this.repos = repos;
198+
return this;
199+
}
200+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dev.cleat.persistence;
2+
3+
import java.util.UUID;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
public interface AccountRepository extends JpaRepository<AccountEntity, UUID> {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dev.cleat.persistence;
2+
3+
public enum AccountType {
4+
USER,
5+
ORG
6+
}

0 commit comments

Comments
 (0)