Add household and membership domain with role-based APIs
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Introduced `Household` and `HouseholdMember` entities, services, and repositories, enabling role-based access (`OWNER` and `MEMBER`). Added REST APIs for household creation, member management, and listing. Configured Flyway migrations, updated tests, and included IntelliJ HTTP client script for API testing. Updated `README.md` with feature details, usage instructions, and architecture overview.
This commit is contained in:
55
src/test/http/hemhub-api.http
Normal file
55
src/test/http/hemhub-api.http
Normal file
@ -0,0 +1,55 @@
|
||||
### 1. Get access token for user "maria"
|
||||
POST http://localhost:8081/realms/hemhub/protocol/openid-connect/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
client_id=hemhub-public&
|
||||
grant_type=password&
|
||||
username=maria&
|
||||
password=Passw0rd
|
||||
|
||||
> {%
|
||||
client.global.set("token", response.body.access_token);
|
||||
client.global.set("refresh_token", response.body.refresh_token);
|
||||
%}
|
||||
|
||||
### 2. Decode token (optional, debugging)
|
||||
GET https://jwt.io/?access_token={{token}}
|
||||
|
||||
### 3. Get current user info
|
||||
GET http://localhost:8080/me
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
### 4. Create a household
|
||||
POST http://localhost:8080/api/v1/households
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
{
|
||||
"name": "Familjen Andersson"
|
||||
}
|
||||
|
||||
> {% client.global.set("householdId", JSON.parse(response.body).id); %}
|
||||
|
||||
### 5. List my households
|
||||
GET http://localhost:8080/api/v1/households
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
### 6. List household members
|
||||
GET http://localhost:8080/api/v1/households/{{householdId}}/members
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
### 7. Add a member (requires OWNER role)
|
||||
POST http://localhost:8080/api/v1/households/{{householdId}}/members
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
{
|
||||
"userSub": "ulf-sub",
|
||||
"email": "ulf@example.com",
|
||||
"displayName": "Ulf",
|
||||
"role": "MEMBER"
|
||||
}
|
||||
|
||||
### 8. List members again (should now include Ulf)
|
||||
GET http://localhost:8080/api/v1/households/{{householdId}}/members
|
||||
Authorization: Bearer {{token}}
|
||||
@ -0,0 +1,63 @@
|
||||
package se.urmo.hemhub.integration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@ActiveProfiles("test")
|
||||
class HouseholdControllerIT {
|
||||
|
||||
@Autowired MockMvc mvc;
|
||||
|
||||
@Test
|
||||
void create_household_and_list_for_user() throws Exception {
|
||||
var jwtUser = jwt().jwt(j -> {
|
||||
j.subject("sub-user-1");
|
||||
j.claim("email","u1@example.com");
|
||||
j.claim("preferred_username","u1");
|
||||
j.claim("realm_access", java.util.Map.of("roles", java.util.List.of("OWNER","MEMBER")));
|
||||
});
|
||||
|
||||
// create
|
||||
mvc.perform(post("/api/v1/households")
|
||||
.with(jwtUser)
|
||||
.contentType("application/json")
|
||||
.content("{\"name\":\"Familjen U1\"}"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.id").exists());
|
||||
|
||||
// list
|
||||
mvc.perform(get("/api/v1/households").with(jwtUser))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$[0].name").value("Familjen U1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void add_member_requires_owner() throws Exception {
|
||||
var owner = jwt().jwt(j -> { j.subject("owner-sub"); j.claim("email","o@ex.com"); j.claim("preferred_username","owner"); });
|
||||
var other = jwt().jwt(j -> { j.subject("other-sub"); j.claim("email","x@ex.com"); j.claim("preferred_username","x"); });
|
||||
|
||||
// create household as owner
|
||||
var res = mvc.perform(post("/api/v1/households").with(owner)
|
||||
.contentType("application/json").content("{\"name\":\"H1\"}"))
|
||||
.andExpect(status().isOk()).andReturn();
|
||||
var id = com.jayway.jsonpath.JsonPath.read(res.getResponse().getContentAsString(), "$.id");
|
||||
|
||||
// cannot add as non-owner
|
||||
mvc.perform(post("/api/v1/households/"+id+"/members").with(other)
|
||||
.contentType("application/json")
|
||||
.content("{\"userSub\":\"u2\",\"role\":\"MEMBER\"}"))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package se.urmo.hemhub.web;
|
||||
package se.urmo.hemhub.integration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
@ -1,4 +1,4 @@
|
||||
package se.urmo.hemhub.web;
|
||||
package se.urmo.hemhub.integration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
@ -6,6 +6,12 @@ spring:
|
||||
password:
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
ddl-auto: none # 👈 turn off Hibernate's validate/ddl in tests
|
||||
properties:
|
||||
hibernate.hbm2ddl.auto: none
|
||||
flyway:
|
||||
enabled: false
|
||||
enabled: true
|
||||
locations: classpath:db/migration
|
||||
logging:
|
||||
level:
|
||||
org.flywaydb.core: INFO
|
||||
Reference in New Issue
Block a user