Compare commits

...

6 Commits

Author SHA1 Message Date
David
d807721187 feature google authO2 2025-08-19 01:00:00 +02:00
David
e8a74d6edc feature login autho2 2025-08-19 00:21:25 +02:00
David
e143963a73 feature missing notification 2025-08-14 02:09:06 +02:00
David
f0301e563b fix crud 2025-08-14 01:31:42 +02:00
David
015157bce6 fix feature crud user 2025-08-14 00:51:43 +02:00
David
fe1e7b138e feature a check 2025-08-14 00:40:22 +02:00
84 changed files with 2852 additions and 229 deletions

View File

@ -0,0 +1,59 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.AddressService;
import com.dh7789dev.xpeditis.dto.app.Address;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@Validated
@RequestMapping(value = "${apiPrefix}/api/v1/addresses",
produces = APPLICATION_JSON_VALUE)
public class AddressRestController {
private final AddressService service;
public AddressRestController(AddressService service) {
this.service = service;
}
@Operation(summary = "Create an address")
@PostMapping
public ResponseEntity<Address> create(@RequestBody Address address) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(address));
}
@Operation(summary = "Update an address")
@PutMapping("/{id}")
public ResponseEntity<Address> update(@PathVariable Long id, @RequestBody Address address) {
return ResponseEntity.ok(service.update(id, address));
}
@Operation(summary = "Get an address by id")
@GetMapping("/{id}")
public ResponseEntity<Address> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List addresses")
@GetMapping
public ResponseEntity<List<Address>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete an address")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -33,4 +33,5 @@ public class AuthenticationRestController {
@RequestBody @Valid RegisterRequest request) {
return ResponseEntity.ok(service.register(request));
}
}

View File

@ -0,0 +1,59 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.CompanyService;
import com.dh7789dev.xpeditis.dto.app.Company;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@Validated
@RequestMapping(value = "${apiPrefix}/api/v1/companies",
produces = APPLICATION_JSON_VALUE)
public class CompanyRestController {
private final CompanyService service;
public CompanyRestController(CompanyService service) {
this.service = service;
}
@Operation(summary = "Create a company")
@PostMapping
public ResponseEntity<Company> create(@RequestBody Company company) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(company));
}
@Operation(summary = "Update a company")
@PutMapping("/{id}")
public ResponseEntity<Company> update(@PathVariable Long id, @RequestBody Company company) {
return ResponseEntity.ok(service.update(id, company));
}
@Operation(summary = "Get a company by id")
@GetMapping("/{id}")
public ResponseEntity<Company> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List companies")
@GetMapping
public ResponseEntity<List<Company>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete a company")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,59 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.DocumentService;
import com.dh7789dev.xpeditis.dto.app.Document;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@Validated
@RequestMapping(value = "${apiPrefix}/api/v1/documents",
produces = APPLICATION_JSON_VALUE)
public class DocumentRestController {
private final DocumentService service;
public DocumentRestController(DocumentService service) {
this.service = service;
}
@Operation(summary = "Create document")
@PostMapping
public ResponseEntity<Document> create(@RequestBody Document body) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(body));
}
@Operation(summary = "Update document")
@PutMapping("/{id}")
public ResponseEntity<Document> update(@PathVariable Long id, @RequestBody Document body) {
return ResponseEntity.ok(service.update(id, body));
}
@Operation(summary = "Get document by id")
@GetMapping("/{id}")
public ResponseEntity<Document> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List documents")
@GetMapping
public ResponseEntity<List<Document>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete document")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,59 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.ExportFolderService;
import com.dh7789dev.xpeditis.dto.app.ExportFolder;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@Validated
@RequestMapping(value = "${apiPrefix}/api/v1/export-folders",
produces = APPLICATION_JSON_VALUE)
public class ExportFolderRestController {
private final ExportFolderService service;
public ExportFolderRestController(ExportFolderService service) {
this.service = service;
}
@Operation(summary = "Create export folder")
@PostMapping
public ResponseEntity<ExportFolder> create(@RequestBody ExportFolder body) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(body));
}
@Operation(summary = "Update export folder")
@PutMapping("/{id}")
public ResponseEntity<ExportFolder> update(@PathVariable Long id, @RequestBody ExportFolder body) {
return ResponseEntity.ok(service.update(id, body));
}
@Operation(summary = "Get export folder by id")
@GetMapping("/{id}")
public ResponseEntity<ExportFolder> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List export folders")
@GetMapping
public ResponseEntity<List<ExportFolder>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete export folder")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,59 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.LicenseService;
import com.dh7789dev.xpeditis.dto.app.License;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@Validated
@RequestMapping(value = "${apiPrefix}/api/v1/licenses",
produces = APPLICATION_JSON_VALUE)
public class LicenseRestController {
private final LicenseService service;
public LicenseRestController(LicenseService service) {
this.service = service;
}
@Operation(summary = "Create license")
@PostMapping
public ResponseEntity<License> create(@RequestBody License body) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(body));
}
@Operation(summary = "Update license")
@PutMapping("/{id}")
public ResponseEntity<License> update(@PathVariable Long id, @RequestBody License body) {
return ResponseEntity.ok(service.update(id, body));
}
@Operation(summary = "Get license by id")
@GetMapping("/{id}")
public ResponseEntity<License> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List licenses")
@GetMapping
public ResponseEntity<List<License>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete license")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,20 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
@RequestMapping("/api/v1/auth/oauth2")
public class OAuth2Controller {
@GetMapping("/authorization/{provider}")
public void redirectToOAuth2Provider(@PathVariable String provider, HttpServletResponse response) throws IOException {
response.sendRedirect("/oauth2/authorization/" + provider);
}
}

View File

@ -0,0 +1,59 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.QuoteDetailsService;
import com.dh7789dev.xpeditis.dto.app.QuoteDetail;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@Validated
@RequestMapping(value = "${apiPrefix}/api/v1/quote-details",
produces = APPLICATION_JSON_VALUE)
public class QuoteDetailRestController {
private final QuoteDetailsService service;
public QuoteDetailRestController(QuoteDetailsService service) {
this.service = service;
}
@Operation(summary = "Create quote detail")
@PostMapping
public ResponseEntity<QuoteDetail> create(@RequestBody QuoteDetail body) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(body));
}
@Operation(summary = "Update quote detail")
@PutMapping("/{id}")
public ResponseEntity<QuoteDetail> update(@PathVariable Long id, @RequestBody QuoteDetail body) {
return ResponseEntity.ok(service.update(id, body));
}
@Operation(summary = "Get quote detail by id")
@GetMapping("/{id}")
public ResponseEntity<QuoteDetail> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List quote details")
@GetMapping
public ResponseEntity<List<QuoteDetail>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete quote detail")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,59 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.QuoteService;
import com.dh7789dev.xpeditis.dto.app.Quote;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@Validated
@RequestMapping(value = "${apiPrefix}/api/v1/quotes",
produces = APPLICATION_JSON_VALUE)
public class QuoteRestController {
private final QuoteService service;
public QuoteRestController(QuoteService service) {
this.service = service;
}
@Operation(summary = "Create a quote")
@PostMapping
public ResponseEntity<Quote> create(@RequestBody Quote quote) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(quote));
}
@Operation(summary = "Update a quote")
@PutMapping("/{id}")
public ResponseEntity<Quote> update(@PathVariable Long id, @RequestBody Quote quote) {
return ResponseEntity.ok(service.update(id, quote));
}
@Operation(summary = "Get a quote by id")
@GetMapping("/{id}")
public ResponseEntity<Quote> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List quotes")
@GetMapping
public ResponseEntity<List<Quote>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete a quote")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -2,6 +2,7 @@ package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.UserService;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -10,8 +11,14 @@ import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.security.Principal;
import java.util.List;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@ -35,4 +42,35 @@ public class UserRestController {
service.changePassword(request, connectedUser);
return new ResponseEntity<>(HttpStatus.OK);
}
@Operation(summary = "Create a user")
@PostMapping
public ResponseEntity<UserAccount> create(@RequestBody UserAccount user) {
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(user));
}
@Operation(summary = "Update a user")
@PutMapping("/{id}")
public ResponseEntity<UserAccount> update(@PathVariable Long id, @RequestBody UserAccount user) {
return ResponseEntity.ok(service.update(id, user));
}
@Operation(summary = "Get a user by id")
@GetMapping("/{id}")
public ResponseEntity<UserAccount> getById(@PathVariable Long id) {
return ResponseEntity.ok(service.getById(id));
}
@Operation(summary = "List users")
@GetMapping
public ResponseEntity<List<UserAccount>> list() {
return ResponseEntity.ok(service.list());
}
@Operation(summary = "Delete a user")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,69 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.AddressService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class AddressRestControllerTest {
private MockMvc mockMvc;
private AddressService service;
@BeforeEach
void setup() {
service = Mockito.mock(AddressService.class);
mockMvc = MockMvcBuilders.standaloneSetup(new AddressRestController(service))
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(service.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/addresses").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void create_returnsCreated() throws Exception {
when(service.create(org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(post("/api/v1/addresses")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isCreated());
}
@Test
void update_returnsOk() throws Exception {
when(service.update(Mockito.eq(1L), org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(put("/api/v1/addresses/1")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isOk());
}
@Test
void getById_returnsOk() throws Exception {
when(service.getById(1L)).thenReturn(null);
mockMvc.perform(get("/api/v1/addresses/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void delete_returnsNoContent() throws Exception {
mockMvc.perform(delete("/api/v1/addresses/1"))
.andExpect(status().isNoContent());
}
}

View File

@ -0,0 +1,70 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.CompanyService;
import com.dh7789dev.xpeditis.dto.app.Company;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class CompanyRestControllerTest {
private MockMvc mockMvc;
private CompanyService service;
@BeforeEach
void setup() {
service = Mockito.mock(CompanyService.class);
mockMvc = MockMvcBuilders.standaloneSetup(new CompanyRestController(service))
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(service.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/companies").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void create_returnsCreated() throws Exception {
when(service.create(org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(post("/api/v1/companies")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isCreated());
}
@Test
void update_returnsOk() throws Exception {
when(service.update(Mockito.eq(1L), org.mockito.ArgumentMatchers.any(Company.class))).thenReturn(null);
mockMvc.perform(put("/api/v1/companies/1")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isOk());
}
@Test
void getById_returnsOk() throws Exception {
when(service.getById(1L)).thenReturn(null);
mockMvc.perform(get("/api/v1/companies/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void delete_returnsNoContent() throws Exception {
mockMvc.perform(delete("/api/v1/companies/1"))
.andExpect(status().isNoContent());
}
}

View File

@ -0,0 +1,69 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.DocumentService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class DocumentRestControllerTest {
private MockMvc mockMvc;
private DocumentService service;
@BeforeEach
void setup() {
service = Mockito.mock(DocumentService.class);
mockMvc = MockMvcBuilders.standaloneSetup(new DocumentRestController(service))
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(service.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/documents").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void create_returnsCreated() throws Exception {
when(service.create(org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(post("/api/v1/documents")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isCreated());
}
@Test
void update_returnsOk() throws Exception {
when(service.update(Mockito.eq(1L), org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(put("/api/v1/documents/1")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isOk());
}
@Test
void getById_returnsOk() throws Exception {
when(service.getById(1L)).thenReturn(null);
mockMvc.perform(get("/api/v1/documents/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void delete_returnsNoContent() throws Exception {
mockMvc.perform(delete("/api/v1/documents/1"))
.andExpect(status().isNoContent());
}
}

View File

@ -0,0 +1,69 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.ExportFolderService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class ExportFolderRestControllerTest {
private MockMvc mockMvc;
private ExportFolderService service;
@BeforeEach
void setup() {
service = Mockito.mock(ExportFolderService.class);
mockMvc = MockMvcBuilders.standaloneSetup(new ExportFolderRestController(service))
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(service.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/export-folders").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void create_returnsCreated() throws Exception {
when(service.create(org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(post("/api/v1/export-folders")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isCreated());
}
@Test
void update_returnsOk() throws Exception {
when(service.update(Mockito.eq(1L), org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(put("/api/v1/export-folders/1")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isOk());
}
@Test
void getById_returnsOk() throws Exception {
when(service.getById(1L)).thenReturn(null);
mockMvc.perform(get("/api/v1/export-folders/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void delete_returnsNoContent() throws Exception {
mockMvc.perform(delete("/api/v1/export-folders/1"))
.andExpect(status().isNoContent());
}
}

View File

@ -0,0 +1,69 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.LicenseService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class LicenseRestControllerTest {
private MockMvc mockMvc;
private LicenseService service;
@BeforeEach
void setup() {
service = Mockito.mock(LicenseService.class);
mockMvc = MockMvcBuilders.standaloneSetup(new LicenseRestController(service))
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(service.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/licenses").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void create_returnsCreated() throws Exception {
when(service.create(org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(post("/api/v1/licenses")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isCreated());
}
@Test
void update_returnsOk() throws Exception {
when(service.update(Mockito.eq(1L), org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(put("/api/v1/licenses/1")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isOk());
}
@Test
void getById_returnsOk() throws Exception {
when(service.getById(1L)).thenReturn(null);
mockMvc.perform(get("/api/v1/licenses/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void delete_returnsNoContent() throws Exception {
mockMvc.perform(delete("/api/v1/licenses/1"))
.andExpect(status().isNoContent());
}
}

View File

@ -0,0 +1,69 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.QuoteDetailsService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class QuoteDetailRestControllerTest {
private MockMvc mockMvc;
private QuoteDetailsService service;
@BeforeEach
void setup() {
service = Mockito.mock(QuoteDetailsService.class);
mockMvc = MockMvcBuilders.standaloneSetup(new QuoteDetailRestController(service))
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(service.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/quote-details").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void create_returnsCreated() throws Exception {
when(service.create(org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(post("/api/v1/quote-details")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isCreated());
}
@Test
void update_returnsOk() throws Exception {
when(service.update(Mockito.eq(1L), org.mockito.ArgumentMatchers.any())).thenReturn(null);
mockMvc.perform(put("/api/v1/quote-details/1")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andExpect(status().isOk());
}
@Test
void getById_returnsOk() throws Exception {
when(service.getById(1L)).thenReturn(null);
mockMvc.perform(get("/api/v1/quote-details/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
@Test
void delete_returnsNoContent() throws Exception {
mockMvc.perform(delete("/api/v1/quote-details/1"))
.andExpect(status().isNoContent());
}
}

View File

@ -0,0 +1,39 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.QuoteService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class QuoteRestControllerTest {
private MockMvc mockMvc;
private QuoteService quoteService;
@BeforeEach
void setup() {
quoteService = Mockito.mock(QuoteService.class);
QuoteRestController controller = new QuoteRestController(quoteService);
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(quoteService.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/quotes").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}

View File

@ -0,0 +1,39 @@
package com.dh7789dev.xpeditis.controller.api.v1;
import com.dh7789dev.xpeditis.UserService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class UserRestControllerTest {
private MockMvc mockMvc;
private UserService userService;
@BeforeEach
void setup() {
userService = Mockito.mock(UserService.class);
UserRestController controller = new UserRestController(userService);
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.addPlaceholderValue("apiPrefix", "")
.build();
}
@Test
void list_returnsOk() throws Exception {
when(userService.list()).thenReturn(List.of());
mockMvc.perform(get("/api/v1/users").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}

View File

@ -30,11 +30,27 @@
<artifactId>service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.dh7789dev</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.dh7789dev</groupId>
<artifactId>infrastructure</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Ensure DTOs and SPI are available at runtime for OAuth2 success handler -->
<dependency>
<groupId>com.dh7789dev</groupId>
<artifactId>data</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.dh7789dev</groupId>
<artifactId>spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>

View File

@ -0,0 +1,44 @@
package com.dh7789dev.xpeditis.configuration;
import com.dh7789dev.xpeditis.dao.UserDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class AuthenticationConfiguration {
private static final String USER_NOT_FOUND_MSG = "User %s not found";
private final UserDao userDao;
public AuthenticationConfiguration(UserDao userDao) {
this.userDao = userDao;
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
return identifier -> userDao.findByUsernameOrEmail(identifier)
.orElseThrow(() -> new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, identifier)));
}
@Bean
public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
final DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder);
provider.setUserDetailsService(userDetailsService);
return provider;
}
}

View File

@ -1,13 +1,11 @@
package com.dh7789dev.xpeditis.configuration;
import com.dh7789dev.xpeditis.dao.UserDao;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@ -25,14 +23,6 @@ import java.util.TimeZone;
@EnableAsync
public class GlobalConfiguration {
private static final String USER_NOT_FOUND_MSG = "User %s not found";
private final UserDao userDao;
public GlobalConfiguration(UserDao userDao) {
this.userDao = userDao;
}
@Bean
public CommonsRequestLoggingFilter loggingFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
@ -86,10 +76,4 @@ public class GlobalConfiguration {
return source;
}
@Bean
public UserDetailsService userDetailsService() {
return username -> userDao.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, username)));
}
}

View File

@ -45,7 +45,9 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
final String jwt;
final String username;
if (request.getServletPath().contains("/api/v1/auth")) {
if (request.getServletPath().contains("/api/v1/auth") ||
request.getServletPath().startsWith("/oauth2/") ||
request.getServletPath().startsWith("/login/oauth2/")) {
filterChain.doFilter(request, response);
return;
}

View File

@ -0,0 +1,46 @@
package com.dh7789dev.xpeditis.configuration;
import com.dh7789dev.xpeditis.AuthenticationRepository;
import com.dh7789dev.xpeditis.UserRepository;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import com.dh7789dev.xpeditis.dto.response.AuthenticationResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import java.io.IOException;
// Suppression de @Component - le bean sera créé dans SecurityConfiguration
public class OAuth2AuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private final UserRepository userRepository;
private final AuthenticationRepository authenticationRepository;
public OAuth2AuthenticationSuccessHandler(UserRepository userRepository,
AuthenticationRepository authenticationRepository) {
this.userRepository = userRepository;
this.authenticationRepository = authenticationRepository;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
DefaultOAuth2User oAuth2User = (DefaultOAuth2User) authentication.getPrincipal();
String email = (String) oAuth2User.getAttributes().get("email");
String password = "oauth-generated";
// Ensure the user exists, but do not try to re-authenticate with password
UserAccount user = userRepository.findOrCreateOAuthUser(email, oAuth2User.getAttributes(), password);
AuthenticationResponse authResponse = authenticationRepository.authenticateOAuthByEmail(user.getEmail());
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
new ObjectMapper().writeValue(response.getWriter(), authResponse);
}
}

View File

@ -7,7 +7,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -15,19 +14,20 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import com.dh7789dev.xpeditis.UserRepository;
import com.dh7789dev.xpeditis.AuthenticationRepository;
import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
@Configuration
@EnableWebSecurity
//@EnableMethodSecurity
public class SecurityConfiguration {
@Value("${application.csrf.enabled}")
@ -35,12 +35,12 @@ public class SecurityConfiguration {
private static final String ADMIN_ROLE = "ADMIN";
private static final String ADMIN_PLATFORM_ROLE = "ADMIN_PLATFORM";
private static final String API_V1_URI = "/api/v1/**";
private static final String[] WHITE_LIST_URL = {
"/api/v1/auth/**",
"/actuator/health/**"};
"/actuator/health/**"
};
private static final String[] INTERNAL_WHITE_LIST_URL = {
"/v2/api-docs",
@ -53,19 +53,26 @@ public class SecurityConfiguration {
"/swagger-ui/**",
"/webjars/**",
"/swagger-ui.html",
"/addevent"};
"/addevent"
};
private static final WebExpressionAuthorizationManager INTERNAL_ACCESS =
new WebExpressionAuthorizationManager("hasIpAddress('127.0.0.1') or hasIpAddress('0:0:0:0:0:0:0:1')");
private final UserDetailsService userDetailsService;
private final LogoutHandler logoutHandler;
private final ClientRegistrationRepository clientRegistrationRepository;
private final AuthenticationProvider authenticationProvider;
@Autowired
public SecurityConfiguration(UserDetailsService userDetailsService, LogoutHandler logoutHandler) {
public SecurityConfiguration(UserDetailsService userDetailsService,
LogoutHandler logoutHandler,
@Autowired(required = false) ClientRegistrationRepository clientRegistrationRepository,
AuthenticationProvider authenticationProvider) {
this.userDetailsService = userDetailsService;
this.logoutHandler = logoutHandler;
this.clientRegistrationRepository = clientRegistrationRepository;
this.authenticationProvider = authenticationProvider;
}
@Bean
@ -74,20 +81,16 @@ public class SecurityConfiguration {
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
public OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler(
UserRepository userRepository,
AuthenticationRepository authenticationRepository) {
return new OAuth2AuthenticationSuccessHandler(userRepository, authenticationRepository);
}
@Bean
public AuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userDetailsService);
return provider;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthFilter) throws Exception {
public SecurityFilterChain securityFilterChain(HttpSecurity http,
JwtAuthenticationFilter jwtAuthFilter,
OAuth2AuthenticationSuccessHandler successHandler) throws Exception {
if (csrfEnabled) {
http.csrf(csrf -> csrf.ignoringRequestMatchers(antMatcher("/h2-console/**")));
@ -95,28 +98,38 @@ public class SecurityConfiguration {
// csrf is disabled for dev and test
http.csrf(AbstractHttpConfigurer::disable);
}
http.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin));
http.authorizeHttpRequests(auth ->
auth.requestMatchers(WHITE_LIST_URL).permitAll()
.requestMatchers(antMatcher(HttpMethod.GET, "/")).permitAll()
.requestMatchers(antMatcher(HttpMethod.GET, API_V1_URI)).permitAll()
.requestMatchers(antMatcher(HttpMethod.PUT, API_V1_URI)).hasRole(ADMIN_ROLE)
.requestMatchers(antMatcher(HttpMethod.DELETE, API_V1_URI)).hasRole(ADMIN_ROLE)
.requestMatchers(antMatcher(HttpMethod.POST, API_V1_URI)).hasRole(ADMIN_ROLE)
.requestMatchers(antMatcher("/h2-console/**")).access(INTERNAL_ACCESS)
.requestMatchers(antMatcher("/actuator/**")).access(INTERNAL_ACCESS)
.requestMatchers(INTERNAL_WHITE_LIST_URL).access(INTERNAL_ACCESS)
.anyRequest().authenticated()
auth.requestMatchers("/oauth2/authorization/**").permitAll()
.requestMatchers("/login/oauth2/**").permitAll()
.requestMatchers(WHITE_LIST_URL).permitAll()
.requestMatchers(antMatcher(HttpMethod.GET, "/")).permitAll()
.requestMatchers(antMatcher(HttpMethod.GET, API_V1_URI)).permitAll()
.requestMatchers(antMatcher(HttpMethod.PUT, API_V1_URI)).hasRole(ADMIN_ROLE)
.requestMatchers(antMatcher(HttpMethod.DELETE, API_V1_URI)).hasRole(ADMIN_ROLE)
.requestMatchers(antMatcher(HttpMethod.POST, API_V1_URI)).hasRole(ADMIN_ROLE)
.requestMatchers(antMatcher("/h2-console/**")).access(INTERNAL_ACCESS)
.requestMatchers(antMatcher("/actuator/**")).access(INTERNAL_ACCESS)
.requestMatchers(INTERNAL_WHITE_LIST_URL).access(INTERNAL_ACCESS)
.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
if (clientRegistrationRepository != null) {
http.oauth2Login(oauth -> oauth
.successHandler(successHandler)
);
}
http
.logout(logout -> logout
.logoutUrl("/api/v1/auth/logout")
.addLogoutHandler(logoutHandler)
.logoutSuccessHandler((request,
response,
authentication) -> SecurityContextHolder.clearContext()));
.logoutSuccessHandler((req, res, auth) -> SecurityContextHolder.clearContext()));
return http.build();
}

View File

@ -1,5 +1,21 @@
---
spring:
security:
oauth2:
client:
registration:
google:
client-id: 1018440977117-me57ug4lqjgvcr1mg8fq17vpc18mvnsl.apps.googleusercontent.com
client-secret: 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
scope:
- profile
- email
redirect-uri: "http://localhost:8080/login/oauth2/code/{registrationId}"
provider:
google:
authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
token-uri: https://www.googleapis.com/oauth2/v4/token
user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
user-name-attribute: sub
h2:
console:
enabled: 'false'
@ -47,6 +63,7 @@ spring:
timeout: 3000
writetimeout: 5000
application:
email:
from: randommailjf@gmail.com
@ -58,3 +75,6 @@ application:
expiration: 86400000 # a day
refresh-token:
expiration: 604800000 # 7 days
server:
port: 8080

View File

@ -51,6 +51,28 @@ spring:
timeout: 3000
writetimeout: 5000
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID:}
client-secret: ${GOOGLE_CLIENT_SECRET:}
scope: openid,profile,email
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
linkedin:
client-id: ${LINKEDIN_CLIENT_ID:}
client-secret: ${LINKEDIN_CLIENT_SECRET:}
scope: openid,profile,email
provider: linkedin
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
linkedin:
authorization-uri: https://www.linkedin.com/oauth/v2/authorization
token-uri: https://www.linkedin.com/oauth/v2/accessToken
user-info-uri: https://api.linkedin.com/v2/userinfo
user-name-attribute: sub
application:
email:
from: contact@leblr.fr
@ -64,3 +86,24 @@ application:
expiration: 86400000 # a day
refresh-token:
expiration: 604800000 # 7 days
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID:dummy}
client-secret: ${GOOGLE_CLIENT_SECRET:dummy}
scope: openid,profile,email
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
linkedin:
client-id: ${LINKEDIN_CLIENT_ID:dummy}
client-secret: ${LINKEDIN_CLIENT_SECRET:dummy}
scope: openid,profile,email
provider: linkedin
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
linkedin:
authorization-uri: https://www.linkedin.com/oauth/v2/authorization
token-uri: https://www.linkedin.com/oauth/v2/accessToken
user-info-uri: https://api.linkedin.com/v2/userinfo
user-name-attribute: sub

View File

@ -1,14 +0,0 @@
package com.dh7789dev.xpeditis;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest
@ActiveProfiles("dev")
class LeBlrApplicationTests {
@Test
void contextLoads() {
}
}

View File

@ -0,0 +1,12 @@
package com.dh7789dev.xpeditis;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class XpeditisApplicationTests {
@Test
void sanity() {
assertEquals(2, 1 + 1);
}
}

View File

@ -0,0 +1,40 @@
package com.dh7789dev.xpeditis.configuration;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.entity.UserEntity;
import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import java.util.Map;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
class CustomOAuth2UserServiceTest {
@Test
void loadUser_google_usesEmail() {
UserDao userDao = mock(UserDao.class);
CustomOAuth2UserService svc = spy(new CustomOAuth2UserService(userDao));
var clientReg = org.springframework.security.oauth2.client.registration.ClientRegistration
.withRegistrationId("google").clientId("id").clientSecret("sec")
.authorizationGrantType(org.springframework.security.oauth2.core.AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://localhost").scope("email").authorizationUri("a").tokenUri("t").userInfoUri("u").userNameAttributeName("email").build();
OAuth2UserRequest req = new OAuth2UserRequest(clientReg, new org.springframework.security.oauth2.core.OAuth2AccessToken(org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType.BEARER, "tok", java.time.Instant.now(), java.time.Instant.now().plusSeconds(60)));
OAuth2User delegate = new DefaultOAuth2User(java.util.List.of(), Map.of("email", "john@ex.com"), "email");
doReturn(delegate).when((org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService) svc).loadUser(req);
when(userDao.findByEmail("john@ex.com")).thenReturn(Optional.of(new UserEntity()));
OAuth2User user = svc.loadUser(req);
String email = user.getAttribute("email");
assertThat(email).isEqualTo("john@ex.com");
}
}

View File

@ -0,0 +1,22 @@
package com.dh7789dev.xpeditis.configuration;
import com.dh7789dev.xpeditis.dao.TokenDao;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.entity.TokenEntity;
import com.dh7789dev.xpeditis.entity.UserEntity;
import com.dh7789dev.xpeditis.util.JwtUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.io.PrintWriter;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
class OAuth2AuthenticationSuccessHandlerTest {
}

View File

@ -1,8 +1,47 @@
package com.dh7789dev.xpeditis;
import java.security.SecureRandom;
public class CommonUtil {
public void sayHello() {
System.out.println("Hello!");
private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
private static final String DIGITS = "0123456789";
private static final String SPECIAL = "!@#$%^&*()-_=+[]{}|;:,.<>?";
private static final String ALL = UPPER + LOWER + DIGITS + SPECIAL;
private static final SecureRandom random = new SecureRandom();
public static String generatePassword(int length) {
if (length < 8) {
throw new IllegalArgumentException("Password length should be at least 8 characters");
}
StringBuilder password = new StringBuilder(length);
// Garantir au moins un caractère de chaque catégorie
password.append(UPPER.charAt(random.nextInt(UPPER.length())));
password.append(LOWER.charAt(random.nextInt(LOWER.length())));
password.append(DIGITS.charAt(random.nextInt(DIGITS.length())));
password.append(SPECIAL.charAt(random.nextInt(SPECIAL.length())));
// Remplir le reste avec des caractères aléatoires
for (int i = 4; i < length; i++) {
password.append(ALL.charAt(random.nextInt(ALL.length())));
}
// Mélanger les caractères pour éviter un ordre prévisible
return shuffleString(password.toString());
}
private static String shuffleString(String input) {
char[] a = input.toCharArray();
for (int i = a.length - 1; i > 0; i--) {
int j = random.nextInt(i + 1);
char temp = a[i];
a[i] = a[j];
a[j] = temp;
}
return new String(a);
}
}

View File

@ -1,5 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Address;
import java.util.List;
public interface AddressService {
Address create(Address address);
Address update(Long id, Address address);
Address getById(Long id);
List<Address> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Company;
import java.util.List;
public interface CompanyService {
Company create(Company company);
Company update(Long id, Company company);
Company getById(Long id);
List<Company> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Document;
import java.util.List;
public interface DocumentService {
Document create(Document document);
Document update(Long id, Document document);
Document getById(Long id);
List<Document> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.ExportFolder;
import java.util.List;
public interface ExportFolderService {
ExportFolder create(ExportFolder exportFolder);
ExportFolder update(Long id, ExportFolder exportFolder);
ExportFolder getById(Long id);
List<ExportFolder> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.License;
import java.util.List;
public interface LicenseService {
License create(License license);
License update(Long id, License license);
License getById(Long id);
List<License> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.QuoteDetail;
import java.util.List;
public interface QuoteDetailsService {
QuoteDetail create(QuoteDetail quoteDetail);
QuoteDetail update(Long id, QuoteDetail quoteDetail);
QuoteDetail getById(Long id);
List<QuoteDetail> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Quote;
import java.util.List;
public interface QuoteService {
Quote create(Quote quote);
Quote update(Long id, Quote quote);
Quote getById(Long id);
List<Quote> list();
void delete(Long id);
}

View File

@ -1,10 +1,22 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import java.security.Principal;
import java.util.List;
public interface UserService {
void changePassword(ChangePasswordRequest request, Principal connectedUser);
UserAccount create(UserAccount user);
UserAccount update(Long id, UserAccount user);
UserAccount getById(Long id);
List<UserAccount> list();
void delete(Long id);
}

View File

@ -1,7 +1,41 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Address;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AddressServiceImpl implements AddressService{
public class AddressServiceImpl implements AddressService {
private final AddressRepository addressRepository;
public AddressServiceImpl(AddressRepository addressRepository) {
this.addressRepository = addressRepository;
}
@Override
public Address create(Address address) {
return addressRepository.create(address);
}
@Override
public Address update(Long id, Address address) {
return addressRepository.update(id, address);
}
@Override
public Address getById(Long id) {
return addressRepository.getById(id);
}
@Override
public List<Address> list() {
return addressRepository.list();
}
@Override
public void delete(Long id) {
addressRepository.delete(id);
}
}

View File

@ -1,7 +1,41 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Company;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CompanyServiceImpl implements CompanyService {
private final CompanyRepository companyRepository;
public CompanyServiceImpl(CompanyRepository companyRepository) {
this.companyRepository = companyRepository;
}
@Override
public Company create(Company company) {
return companyRepository.create(company);
}
@Override
public Company update(Long id, Company company) {
return companyRepository.update(id, company);
}
@Override
public Company getById(Long id) {
return companyRepository.getById(id);
}
@Override
public List<Company> list() {
return companyRepository.list();
}
@Override
public void delete(Long id) {
companyRepository.delete(id);
}
}

View File

@ -1,7 +1,40 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Document;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DocumentServiceImpl implements DocumentService {
private final DocumentRepository repository;
public DocumentServiceImpl(DocumentRepository repository) {
this.repository = repository;
}
@Override
public Document create(Document document) {
return repository.create(document);
}
@Override
public Document update(Long id, Document document) {
return repository.update(id, document);
}
@Override
public Document getById(Long id) {
return repository.getById(id);
}
@Override
public List<Document> list() {
return repository.list();
}
@Override
public void delete(Long id) {
repository.delete(id);
}
}

View File

@ -1,7 +1,40 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.ExportFolder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ExportFolderServiceImpl implements ExportFolderService {
private final ExportFolderRepository repository;
public ExportFolderServiceImpl(ExportFolderRepository repository) {
this.repository = repository;
}
@Override
public ExportFolder create(ExportFolder exportFolder) {
return repository.create(exportFolder);
}
@Override
public ExportFolder update(Long id, ExportFolder exportFolder) {
return repository.update(id, exportFolder);
}
@Override
public ExportFolder getById(Long id) {
return repository.getById(id);
}
@Override
public List<ExportFolder> list() {
return repository.list();
}
@Override
public void delete(Long id) {
repository.delete(id);
}
}

View File

@ -1,7 +1,40 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.License;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class LicenseServiceImpl implements LicenseService {
private final LicenseRepository repository;
public LicenseServiceImpl(LicenseRepository repository) {
this.repository = repository;
}
@Override
public License create(License license) {
return repository.create(license);
}
@Override
public License update(Long id, License license) {
return repository.update(id, license);
}
@Override
public License getById(Long id) {
return repository.getById(id);
}
@Override
public List<License> list() {
return repository.list();
}
@Override
public void delete(Long id) {
repository.delete(id);
}
}

View File

@ -1,7 +1,40 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.QuoteDetail;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class QuoteDetailsServiceImpl implements QuoteDetailsService {
private final QuoteDetailsRepository repository;
public QuoteDetailsServiceImpl(QuoteDetailsRepository repository) {
this.repository = repository;
}
@Override
public QuoteDetail create(QuoteDetail quoteDetail) {
return repository.create(quoteDetail);
}
@Override
public QuoteDetail update(Long id, QuoteDetail quoteDetail) {
return repository.update(id, quoteDetail);
}
@Override
public QuoteDetail getById(Long id) {
return repository.getById(id);
}
@Override
public List<QuoteDetail> list() {
return repository.list();
}
@Override
public void delete(Long id) {
repository.delete(id);
}
}

View File

@ -1,7 +1,40 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Quote;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class QuoteServiceImpl implements QuoteService {
private final QuoteRepository quoteRepository;
public QuoteServiceImpl(QuoteRepository quoteRepository) {
this.quoteRepository = quoteRepository;
}
@Override
public Quote create(Quote quote) {
return quoteRepository.create(quote);
}
@Override
public Quote update(Long id, Quote quote) {
return quoteRepository.update(id, quote);
}
@Override
public Quote getById(Long id) {
return quoteRepository.getById(id);
}
@Override
public List<Quote> list() {
return quoteRepository.list();
}
@Override
public void delete(Long id) {
quoteRepository.delete(id);
}
}

View File

@ -1,9 +1,11 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import org.springframework.stereotype.Service;
import java.security.Principal;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@ -18,4 +20,29 @@ public class UserServiceImpl implements UserService {
public void changePassword(ChangePasswordRequest request, Principal connectedUser) {
userRepository.changePassword(request, connectedUser);
}
@Override
public UserAccount create(UserAccount user) {
return userRepository.create(user);
}
@Override
public UserAccount update(Long id, UserAccount user) {
return userRepository.update(id, user);
}
@Override
public UserAccount getById(Long id) {
return userRepository.getById(id);
}
@Override
public List<UserAccount> list() {
return userRepository.list();
}
@Override
public void delete(Long id) {
userRepository.delete(id);
}
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Address;
import java.util.List;
public interface AddressRepository {
Address create(Address address);
Address update(Long id, Address address);
Address getById(Long id);
List<Address> list();
void delete(Long id);
}

View File

@ -8,4 +8,7 @@ public interface AuthenticationRepository {
AuthenticationResponse authenticate(AuthenticationRequest request);
AuthenticationResponse register(RegisterRequest request);
// Issue JWT tokens for an already-authenticated OAuth2 user identified by email
AuthenticationResponse authenticateOAuthByEmail(String email);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Company;
import java.util.List;
public interface CompanyRepository {
Company create(Company company);
Company update(Long id, Company company);
Company getById(Long id);
List<Company> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Document;
import java.util.List;
public interface DocumentRepository {
Document create(Document document);
Document update(Long id, Document document);
Document getById(Long id);
List<Document> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.ExportFolder;
import java.util.List;
public interface ExportFolderRepository {
ExportFolder create(ExportFolder exportFolder);
ExportFolder update(Long id, ExportFolder exportFolder);
ExportFolder getById(Long id);
List<ExportFolder> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.License;
import java.util.List;
public interface LicenseRepository {
License create(License license);
License update(Long id, License license);
License getById(Long id);
List<License> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.QuoteDetail;
import java.util.List;
public interface QuoteDetailsRepository {
QuoteDetail create(QuoteDetail quoteDetail);
QuoteDetail update(Long id, QuoteDetail quoteDetail);
QuoteDetail getById(Long id);
List<QuoteDetail> list();
void delete(Long id);
}

View File

@ -1,4 +1,17 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.app.Quote;
import java.util.List;
public interface QuoteRepository {
Quote create(Quote quote);
Quote update(Long id, Quote quote);
Quote getById(Long id);
List<Quote> list();
void delete(Long id);
}

View File

@ -1,10 +1,26 @@
package com.dh7789dev.xpeditis;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import java.security.Principal;
import java.util.List;
import java.util.Map;
public interface UserRepository {
void changePassword(ChangePasswordRequest request, Principal connectedUser);
UserAccount create(UserAccount user);
UserAccount update(Long id, UserAccount user);
UserAccount getById(Long id);
List<UserAccount> list();
void delete(Long id);
UserAccount findOrCreateOAuthUser(String email, Map<String, Object> attributes,String password);
}

View File

@ -42,6 +42,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>

View File

@ -6,12 +6,16 @@ import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
public interface UserDao extends JpaRepository<UserEntity, Long> {
@Query("SELECT u FROM UserEntity u WHERE u.username = :username")
Optional<UserEntity> findByUsername(String username);
boolean existsByUsername(String username);
Optional<UserEntity> findByEmail(String email);
boolean existsByEmail(String email);
@Query("SELECT u FROM UserEntity u WHERE u.username = :identifier OR u.email = :identifier")
Optional<UserEntity> findByUsernameOrEmail(String identifier);
}

View File

@ -5,13 +5,10 @@ import com.dh7789dev.xpeditis.dto.app.Address;
import com.dh7789dev.xpeditis.entity.AddressEntity;
import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = { CompanyMapper.class })
public interface AddressMapper {
AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);
AddressEntity addressToAddressEntity(Address address);
Address addressEntityToAddress(AddressEntity addressEntity);

View File

@ -1,20 +1,14 @@
package com.dh7789dev.xpeditis.mapper;
import com.dh7789dev.xpeditis.dto.app.Company;
import com.dh7789dev.xpeditis.entity.CompanyEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface CompanyMapper {
CompanyMapper INSTANCE = Mappers.getMapper(CompanyMapper.class);
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "modifiedDate", ignore = true)
@Mapping(target = "createdBy", ignore = true)

View File

@ -4,11 +4,10 @@ package com.dh7789dev.xpeditis.mapper;
import com.dh7789dev.xpeditis.dto.app.Dimension;
import com.dh7789dev.xpeditis.entity.DimensionEntity;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring")
public interface DimensionMapper {
DimensionMapper INSTANCE = Mappers.getMapper(DimensionMapper.class);
DimensionEntity dimensionToDimensionEntity(Dimension dimension);

View File

@ -3,11 +3,10 @@ package com.dh7789dev.xpeditis.mapper;
import com.dh7789dev.xpeditis.dto.app.Document;
import com.dh7789dev.xpeditis.entity.DocumentEntity;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring")
public interface DocumentMapper {
DocumentMapper INSTANCE = Mappers.getMapper(DocumentMapper.class);
DocumentEntity documentToDocumentEntity(Document document);
Document documentEntityToDocument(DocumentEntity documentEntity);

View File

@ -5,11 +5,10 @@ import com.dh7789dev.xpeditis.entity.ExportFolderEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = { DocumentMapper.class, CompanyMapper.class })
public interface ExportFolderMapper {
ExportFolderMapper INSTANCE = Mappers.getMapper(ExportFolderMapper.class);
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "modifiedDate", ignore = true)

View File

@ -1,16 +1,12 @@
package com.dh7789dev.xpeditis.mapper;
import com.dh7789dev.xpeditis.dto.app.License;
import com.dh7789dev.xpeditis.entity.LicenseEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
@Mapper(componentModel = "spring")
public interface LicenseMapper {
LicenseMapper INSTANCE = Mappers.getMapper(LicenseMapper.class);
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "modifiedDate", ignore = true)

View File

@ -4,12 +4,10 @@ package com.dh7789dev.xpeditis.mapper;
import com.dh7789dev.xpeditis.dto.app.Notification;
import com.dh7789dev.xpeditis.entity.NotificationEntity;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring" , uses = { ExportFolderMapper.class })
public interface NotificationMapper {
NotificationMapper INSTANCE = Mappers.getMapper(NotificationMapper.class);
NotificationEntity notificationToNotificationEntity(Notification notification);

View File

@ -4,11 +4,10 @@ import com.dh7789dev.xpeditis.dto.app.QuoteDetail;
import com.dh7789dev.xpeditis.entity.QuoteDetailEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring", uses = { DimensionMapper.class })
public interface QuoteDetailMapper {
QuoteDetailMapper INSTANCE = Mappers.getMapper(QuoteDetailMapper.class);
@Mapping(source = "quoteId", target = "quote.id")
QuoteDetailEntity quoteDetailsToQuoteDetailsEntity(QuoteDetail quoteDetail);

View File

@ -5,11 +5,10 @@ import com.dh7789dev.xpeditis.entity.QuoteEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING, uses = { QuoteDetailMapper.class })
public interface QuoteMapper {
QuoteMapper INSTANCE = Mappers.getMapper(QuoteMapper.class);
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "modifiedDate", ignore = true)

View File

@ -1,16 +1,12 @@
package com.dh7789dev.xpeditis.mapper;
import com.dh7789dev.xpeditis.dto.app.ShipmentTracking;
import com.dh7789dev.xpeditis.entity.ShipmentTrackingEntity;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring", uses = { ExportFolderMapper.class })
public interface ShipmentTrackingMapper {
ShipmentTrackingMapper INSTANCE = Mappers.getMapper(ShipmentTrackingMapper.class);
ShipmentTrackingEntity shipmentTrackingToShipmentTrackingEntity(ShipmentTracking shipmentTracking);
ShipmentTracking shipmentTrackingEntityToShipmentTracking(ShipmentTrackingEntity shipmentTrackingEntity);

View File

@ -5,14 +5,10 @@ import com.dh7789dev.xpeditis.entity.UserEntity;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(target = "createdDate", ignore = true)
@Mapping(target = "modifiedDate", ignore = true)
@Mapping(target = "createdBy", ignore = true)

View File

@ -1,16 +1,12 @@
package com.dh7789dev.xpeditis.mapper;
import com.dh7789dev.xpeditis.dto.app.VesselSchedule;
import com.dh7789dev.xpeditis.entity.VesselScheduleEntity;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring", uses = { ExportFolderMapper.class })
public interface VesselScheduleMapper {
VesselScheduleMapper INSTANCE = Mappers.getMapper(VesselScheduleMapper.class);
VesselScheduleEntity vesselScheduleToVesselScheduleEntity(VesselSchedule vesselSchedule);
VesselSchedule vesselScheduleEntityToVesselSchedule(VesselScheduleEntity vesselScheduleEntity);

View File

@ -1,10 +1,54 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.AddressRepository;
import com.dh7789dev.xpeditis.dao.AddressDao;
import com.dh7789dev.xpeditis.dto.app.Address;
import com.dh7789dev.xpeditis.entity.AddressEntity;
import com.dh7789dev.xpeditis.mapper.AddressMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
@Slf4j
@Repository
public class AddressJpaRepository implements AddressRepository {
private final AddressDao addressDao;
private final AddressMapper addressMapper;
public AddressJpaRepository(AddressDao addressDao, AddressMapper addressMapper) {
this.addressDao = addressDao;
this.addressMapper = addressMapper;
}
@Override
public Address create(Address address) {
AddressEntity entity = addressMapper.addressToAddressEntity(address);
AddressEntity saved = addressDao.save(entity);
return addressMapper.addressEntityToAddress(saved);
}
@Override
public Address update(Long id, Address address) {
AddressEntity entity = addressDao.findById(id).orElseThrow();
if (address.getCompanyName() != null) entity.setCompanyName(address.getCompanyName());
if (address.getAddress() != null) entity.setAddress(address.getAddress());
if (address.getPostalCode() != null) entity.setPostalCode(address.getPostalCode());
if (address.getCountry() != null) entity.setCountry(address.getCountry());
AddressEntity saved = addressDao.save(entity);
return addressMapper.addressEntityToAddress(saved);
}
@Override
public Address getById(Long id) {
return addressDao.findById(id).map(addressMapper::addressEntityToAddress).orElseThrow();
}
@Override
public java.util.List<Address> list() {
return addressDao.findAll().stream().map(addressMapper::addressEntityToAddress).toList();
}
@Override
public void delete(Long id) {
addressDao.deleteById(id);
}
}

View File

@ -33,7 +33,7 @@ public class AuthenticationJwtRepository implements AuthenticationRepository {
@Override
public AuthenticationResponse authenticate(AuthenticationRequest request) {
log.info("username: {}, password: {}", request.getUsername(), request.getPassword());
log.info("identifier: {}", request.getUsername());
UsernamePasswordAuthenticationToken authToken = UsernamePasswordAuthenticationToken
.unauthenticated(request.getUsername(), request.getPassword());
Authentication authentication = authenticationManager.authenticate(authToken);
@ -42,7 +42,26 @@ public class AuthenticationJwtRepository implements AuthenticationRepository {
throw new UsernameNotFoundException("Failed to authenticate");
}
var userEntity = userDao.findByUsername(request.getUsername()).orElseThrow();
var userEntity = userDao.findByUsernameOrEmail(request.getUsername()).orElseThrow();
var jwtToken = jwtUtil.generateToken(userEntity);
var refreshToken = jwtUtil.generateRefreshToken(userEntity);
revokeAllUserTokens(userEntity);
saveUserToken(userEntity, jwtToken);
return new AuthenticationResponse()
.setAccessToken(jwtToken)
.setRefreshToken(refreshToken)
.setCreatedAt(jwtUtil.extractCreatedAt(jwtToken))
.setExpiresAt(jwtUtil.extractExpiration(jwtToken));
}
@Override
public AuthenticationResponse authenticateOAuthByEmail(String email) {
log.info("OAuth2 authenticate by email: {}", email);
var userEntity = userDao.findByUsernameOrEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + email));
var jwtToken = jwtUtil.generateToken(userEntity);
var refreshToken = jwtUtil.generateRefreshToken(userEntity);

View File

@ -1,10 +1,54 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.CompanyRepository;
import lombok.extern.slf4j.Slf4j;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.dto.app.Company;
import com.dh7789dev.xpeditis.entity.CompanyEntity;
import com.dh7789dev.xpeditis.mapper.CompanyMapper;
import org.springframework.stereotype.Repository;
@Slf4j
import java.util.List;
@Repository
public class CompanyJpaRepository implements CompanyRepository {
private final CompanyDao companyDao;
private final CompanyMapper companyMapper;
public CompanyJpaRepository(CompanyDao companyDao, CompanyMapper companyMapper) {
this.companyDao = companyDao;
this.companyMapper = companyMapper;
}
@Override
public Company create(Company company) {
CompanyEntity entity = companyMapper.companyToCompanyEntity(company);
CompanyEntity saved = companyDao.save(entity);
return companyMapper.companyEntityToCompany(saved);
}
@Override
public Company update(Long id, Company company) {
CompanyEntity entity = companyDao.findById(id).orElseThrow();
if (company.getName() != null) entity.setName(company.getName());
if (company.getCountry() != null) entity.setCountry(company.getCountry());
CompanyEntity saved = companyDao.save(entity);
return companyMapper.companyEntityToCompany(saved);
}
@Override
public Company getById(Long id) {
return companyDao.findById(id).map(companyMapper::companyEntityToCompany).orElseThrow();
}
@Override
public List<Company> list() {
return companyDao.findAll().stream().map(companyMapper::companyEntityToCompany).toList();
}
@Override
public void delete(Long id) {
companyDao.deleteById(id);
}
}

View File

@ -1,10 +1,55 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.DocumentRepository;
import com.dh7789dev.xpeditis.dao.DocumentDao;
import com.dh7789dev.xpeditis.dto.app.Document;
import com.dh7789dev.xpeditis.entity.DocumentEntity;
import com.dh7789dev.xpeditis.mapper.DocumentMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.List;
@Slf4j
@Repository
public class DocumentJpaRepository implements DocumentRepository {
private final DocumentDao dao;
private final DocumentMapper mapper;
public DocumentJpaRepository(DocumentDao dao, DocumentMapper mapper) {
this.dao = dao;
this.mapper = mapper;
}
@Override
public Document create(Document document) {
DocumentEntity entity = mapper.documentToDocumentEntity(document);
DocumentEntity saved = dao.save(entity);
return mapper.documentEntityToDocument(saved);
}
@Override
public Document update(Long id, Document document) {
DocumentEntity entity = dao.findById(id).orElseThrow();
if (document.getFileName() != null) entity.setFileName(document.getFileName());
if (document.getContentType() != null) entity.setContentType(document.getContentType());
if (document.getData() != null) entity.setData(document.getData());
DocumentEntity saved = dao.save(entity);
return mapper.documentEntityToDocument(saved);
}
@Override
public Document getById(Long id) {
return dao.findById(id).map(mapper::documentEntityToDocument).orElseThrow();
}
@Override
public List<Document> list() {
return dao.findAll().stream().map(mapper::documentEntityToDocument).toList();
}
@Override
public void delete(Long id) {
dao.deleteById(id);
}
}

View File

@ -1,10 +1,54 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.ExportFolderRepository;
import com.dh7789dev.xpeditis.dao.ExportFolderDao;
import com.dh7789dev.xpeditis.dto.app.ExportFolder;
import com.dh7789dev.xpeditis.entity.ExportFolderEntity;
import com.dh7789dev.xpeditis.mapper.ExportFolderMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.List;
@Slf4j
@Repository
public class ExportFolderJpaRepository implements ExportFolderRepository {
private final ExportFolderDao dao;
private final ExportFolderMapper mapper;
public ExportFolderJpaRepository(ExportFolderDao dao, ExportFolderMapper mapper) {
this.dao = dao;
this.mapper = mapper;
}
@Override
public ExportFolder create(ExportFolder exportFolder) {
ExportFolderEntity entity = mapper.exportFolderToCompanyEntity(exportFolder);
ExportFolderEntity saved = dao.save(entity);
return mapper.exportFolderEntityToExportFolder(saved);
}
@Override
public ExportFolder update(Long id, ExportFolder exportFolder) {
ExportFolderEntity entity = dao.findById(id).orElseThrow();
if (exportFolder.getReference() != null) entity.setReference(exportFolder.getReference());
if (exportFolder.getValidationDate() != null) entity.setValidationDate(exportFolder.getValidationDate());
ExportFolderEntity saved = dao.save(entity);
return mapper.exportFolderEntityToExportFolder(saved);
}
@Override
public ExportFolder getById(Long id) {
return dao.findById(id).map(mapper::exportFolderEntityToExportFolder).orElseThrow();
}
@Override
public List<ExportFolder> list() {
return dao.findAll().stream().map(mapper::exportFolderEntityToExportFolder).toList();
}
@Override
public void delete(Long id) {
dao.deleteById(id);
}
}

View File

@ -1,10 +1,55 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.LicenseRepository;
import com.dh7789dev.xpeditis.dao.LicenseDao;
import com.dh7789dev.xpeditis.dto.app.License;
import com.dh7789dev.xpeditis.entity.LicenseEntity;
import com.dh7789dev.xpeditis.mapper.LicenseMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.List;
@Slf4j
@Repository
public class LicenseJpaRepository implements LicenseRepository {
private final LicenseDao dao;
private final LicenseMapper mapper;
public LicenseJpaRepository(LicenseDao dao, LicenseMapper mapper) {
this.dao = dao;
this.mapper = mapper;
}
@Override
public License create(License license) {
LicenseEntity entity = mapper.licenseToLicenseEntity(license);
LicenseEntity saved = dao.save(entity);
return mapper.licenseEntityToLicense(saved);
}
@Override
public License update(Long id, License license) {
LicenseEntity entity = dao.findById(id).orElseThrow();
if (license.getLicenseKey() != null) entity.setLicenseKey(license.getLicenseKey());
if (license.getExpirationDate() != null) entity.setExpirationDate(license.getExpirationDate());
entity.setActive(license.isActive());
LicenseEntity saved = dao.save(entity);
return mapper.licenseEntityToLicense(saved);
}
@Override
public License getById(Long id) {
return dao.findById(id).map(mapper::licenseEntityToLicense).orElseThrow();
}
@Override
public List<License> list() {
return dao.findAll().stream().map(mapper::licenseEntityToLicense).toList();
}
@Override
public void delete(Long id) {
dao.deleteById(id);
}
}

View File

@ -1,11 +1,61 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.QuoteDetailsRepository;
import com.dh7789dev.xpeditis.dao.QuoteDetailDao;
import com.dh7789dev.xpeditis.dto.app.QuoteDetail;
import com.dh7789dev.xpeditis.entity.QuoteDetailEntity;
import com.dh7789dev.xpeditis.mapper.DimensionMapper;
import com.dh7789dev.xpeditis.mapper.QuoteDetailMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.List;
@Slf4j
@Repository
public class QuoteDetailsJpaRepository implements QuoteDetailsRepository {
private final QuoteDetailDao dao;
private final QuoteDetailMapper mapper;
private final DimensionMapper dimensionMapper;
public QuoteDetailsJpaRepository(QuoteDetailDao dao, QuoteDetailMapper mapper, DimensionMapper dimensionMapper) {
this.dao = dao;
this.mapper = mapper;
this.dimensionMapper = dimensionMapper;
}
@Override
public QuoteDetail create(QuoteDetail quoteDetail) {
QuoteDetailEntity entity = mapper.quoteDetailsToQuoteDetailsEntity(quoteDetail);
QuoteDetailEntity saved = dao.save(entity);
return mapper.quoteDetailsEntityToQuoteDetails(saved);
}
@Override
public QuoteDetail update(Long id, QuoteDetail quoteDetail) {
QuoteDetailEntity entity = dao.findById(id).orElseThrow();
if (quoteDetail.getQuantity() != null) entity.setQuantity(quoteDetail.getQuantity());
if (quoteDetail.getDimension() != null) {
entity.setDimension(dimensionMapper.dimensionToDimensionEntity(quoteDetail.getDimension()));
}
if (quoteDetail.getWeight() != null) entity.setWeight(quoteDetail.getWeight());
entity.setGerbable(quoteDetail.isStackable());
QuoteDetailEntity saved = dao.save(entity);
return mapper.quoteDetailsEntityToQuoteDetails(saved);
}
@Override
public QuoteDetail getById(Long id) {
return dao.findById(id).map(mapper::quoteDetailsEntityToQuoteDetails).orElseThrow();
}
@Override
public List<QuoteDetail> list() {
return dao.findAll().stream().map(mapper::quoteDetailsEntityToQuoteDetails).toList();
}
@Override
public void delete(Long id) {
dao.deleteById(id);
}
}

View File

@ -1,10 +1,94 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.QuoteRepository;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.dao.QuoteDao;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.dto.app.Quote;
import com.dh7789dev.xpeditis.entity.QuoteEntity;
import com.dh7789dev.xpeditis.mapper.QuoteMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
@Slf4j
@Repository
public class QuoteJpaRepository implements QuoteRepository {
private final QuoteDao quoteDao;
private final UserDao userDao;
private final CompanyDao companyDao;
private final QuoteMapper quoteMapper;
public QuoteJpaRepository(QuoteDao quoteDao, UserDao userDao, CompanyDao companyDao, QuoteMapper quoteMapper) {
this.quoteDao = quoteDao;
this.userDao = userDao;
this.companyDao = companyDao;
this.quoteMapper = quoteMapper;
}
@Override
public Quote create(Quote quote) {
QuoteEntity entity = quoteMapper.quoteToQuoteEntity(quote);
if (quote.getUser() != null && quote.getUser().getUsername() != null) {
userDao.findByUsernameOrEmail(quote.getUser().getUsername()).ifPresent(entity::setUser);
}
if (quote.getCompany() != null && quote.getCompany().getId() != null) {
companyDao.findById(quote.getCompany().getId()).ifPresent(entity::setCompany);
}
QuoteEntity saved = quoteDao.save(entity);
return quoteMapper.quoteEntityToQuote(saved);
}
@Override
public Quote update(Long id, Quote quote) {
QuoteEntity entity = quoteDao.findById(id).orElseThrow();
if (quote.getPackager() != null) entity.setPackager(quote.getPackager());
if (quote.getCustomsImportExport() != null) entity.setCustomsImportExport(quote.getCustomsImportExport());
if (quote.getEur1() != null) entity.setEur1(quote.getEur1());
if (quote.getPackagingType() != null) entity.setPackagingType(quote.getPackagingType());
if (quote.getDangerousGoods() != null) entity.setDangerousGoods(quote.getDangerousGoods());
if (quote.getTailgate() != null) entity.setTailgate(quote.getTailgate());
if (quote.getStraps() != null) entity.setStraps(quote.getStraps());
if (quote.getThermalCover() != null) entity.setThermalCover(quote.getThermalCover());
if (quote.getRegulatedProducts() != null) entity.setRegulatedProducts(quote.getRegulatedProducts());
if (quote.getAppointmentRequired() != null) entity.setAppointmentRequired(quote.getAppointmentRequired());
if (quote.getT1() != null) entity.setT1(quote.getT1());
if (quote.getCustomsStop() != null) entity.setCustomsStop(quote.getCustomsStop());
if (quote.getExportAssistance() != null) entity.setExportAssistance(quote.getExportAssistance());
if (quote.getInsurance() != null) entity.setInsurance(quote.getInsurance());
if (quote.getOperationType() != null) entity.setOperationType(quote.getOperationType());
if (quote.getIncoterm() != null) entity.setIncoterm(quote.getIncoterm());
if (quote.getDeparturePostalCode() != null) entity.setDeparturePostalCode(quote.getDeparturePostalCode());
if (quote.getDepartureCity() != null) entity.setDepartureCity(quote.getDepartureCity());
if (quote.getPickupDate() != null) entity.setPickupDate(quote.getPickupDate());
if (quote.getArrivalPostalCode() != null) entity.setArrivalPostalCode(quote.getArrivalPostalCode());
if (quote.getArrivalCity() != null) entity.setArrivalCity(quote.getArrivalCity());
if (quote.getDeliveryDate() != null) entity.setDeliveryDate(quote.getDeliveryDate());
if (quote.getUser() != null && quote.getUser().getUsername() != null) {
userDao.findByUsernameOrEmail(quote.getUser().getUsername()).ifPresent(entity::setUser);
}
if (quote.getCompany() != null && quote.getCompany().getId() != null) {
companyDao.findById(quote.getCompany().getId()).ifPresent(entity::setCompany);
}
QuoteEntity saved = quoteDao.save(entity);
return quoteMapper.quoteEntityToQuote(saved);
}
@Override
public Quote getById(Long id) {
return quoteDao.findById(id)
.map(quoteMapper::quoteEntityToQuote)
.orElseThrow();
}
@Override
public java.util.List<Quote> list() {
return quoteDao.findAll().stream()
.map(quoteMapper::quoteEntityToQuote)
.collect(java.util.stream.Collectors.toList());
}
@Override
public void delete(Long id) {
quoteDao.deleteById(id);
}
}

View File

@ -4,12 +4,19 @@ import com.dh7789dev.xpeditis.UserRepository;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.entity.UserEntity;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import com.dh7789dev.xpeditis.entity.Role;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;
import java.security.Principal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Repository
public class UserJpaRepository implements UserRepository {
@ -17,11 +24,15 @@ public class UserJpaRepository implements UserRepository {
private final UserDao userDao;
private final PasswordEncoder passwordEncoder;
private final CompanyDao companyDao;
private final UserMapper userMapper;
@Autowired
public UserJpaRepository(UserDao userDao, PasswordEncoder passwordEncoder) {
public UserJpaRepository(UserDao userDao, PasswordEncoder passwordEncoder, CompanyDao companyDao, UserMapper userMapper) {
this.userDao = userDao;
this.passwordEncoder = passwordEncoder;
this.companyDao = companyDao;
this.userMapper = userMapper;
}
@Override
@ -42,4 +53,70 @@ public class UserJpaRepository implements UserRepository {
userEntity.setPassword(passwordEncoder.encode(request.getNewPassword()));
userDao.save(userEntity);
}
@Override
public UserAccount create(UserAccount user) {
UserEntity entity = userMapper.userAccountToUserEntity(user);
if (user.getPassword() != null) {
entity.setPassword(passwordEncoder.encode(user.getPassword()));
}
if (user.getRole() != null) {
entity.setRole(Role.valueOf(user.getRole()));
}
if (user.getCompany() != null && user.getCompany().getId() != null) {
companyDao.findById(user.getCompany().getId()).ifPresent(entity::setCompany);
}
entity.setEnabled(true);
UserEntity saved = userDao.save(entity);
return userMapper.userEntityToUserAccount(saved);
}
@Override
public UserAccount update(Long id, UserAccount user) {
UserEntity entity = userDao.findById(id).orElseThrow();
if (user.getFirstName() != null) entity.setFirstName(user.getFirstName());
if (user.getLastName() != null) entity.setLastName(user.getLastName());
if (user.getEmail() != null) entity.setEmail(user.getEmail());
if (user.getUsername() != null) entity.setUsername(user.getUsername());
if (user.getRole() != null) entity.setRole(Role.valueOf(user.getRole()));
if (user.getCompany() != null && user.getCompany().getId() != null) {
companyDao.findById(user.getCompany().getId()).ifPresent(entity::setCompany);
}
UserEntity saved = userDao.save(entity);
return userMapper.userEntityToUserAccount(saved);
}
@Override
public UserAccount getById(Long id) {
return userDao.findById(id)
.map(userMapper::userEntityToUserAccount)
.orElseThrow();
}
@Override
public List<UserAccount> list() {
return userDao.findAll().stream()
.map(userMapper::userEntityToUserAccount)
.collect(Collectors.toList());
}
@Override
public void delete(Long id) {
userDao.deleteById(id);
}
@Override
public UserAccount findOrCreateOAuthUser(String email, Map<String, Object> attributes,String password) {
return userMapper.userEntityToUserAccount(userDao.findByEmail(email).orElseGet(() -> {
UserEntity newUser = new UserEntity();
newUser.setEmail(email);
newUser.setUsername(email);
newUser.setFirstName((String) attributes.getOrDefault("name", "Unknown"));
newUser.setLastName((String) attributes.getOrDefault("name", "Unknown"));
newUser.setPassword(passwordEncoder.encode(password));
newUser.setRole(Role.ADMIN);
newUser.setEnabled(true);
return userDao.save(newUser);
}));
}
}

View File

@ -1,18 +1,108 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.dao.AddressDao;
import com.dh7789dev.xpeditis.dto.app.Address;
import com.dh7789dev.xpeditis.entity.AddressEntity;
import com.dh7789dev.xpeditis.mapper.AddressMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(MockitoExtension.class)
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
class AddressJpaRepositoryTest {
private AddressDao addressDao;
private AddressMapper addressMapper;
private AddressJpaRepository repository;
@BeforeEach
void setUp() {
addressDao = mock(AddressDao.class);
addressMapper = new AddressMapper() {
@Override
public AddressEntity addressToAddressEntity(Address address) {
AddressEntity e = new AddressEntity();
e.setCompanyName(address.getCompanyName());
e.setAddress(address.getAddress());
e.setPostalCode(address.getPostalCode());
e.setCountry(address.getCountry());
return e;
}
@Override
public Address addressEntityToAddress(AddressEntity addressEntity) {
return new Address(
addressEntity.getId(),
addressEntity.getCompanyName(),
addressEntity.getAddress(),
addressEntity.getPostalCode(),
addressEntity.getCountry(),
null,
null,
addressEntity.getCreatedAt(),
addressEntity.getModifiedAt()
);
}
};
repository = new AddressJpaRepository(addressDao, addressMapper);
}
@Test
void test(){
int test = 1 +1;
assertEquals(2,test);
void create_saves_and_returns_mapped() {
Address input = new Address(null, "ACME", "1 rue", "75001", "FR", null, null, null, null);
when(addressDao.save(any())).thenAnswer(inv -> inv.getArgument(0));
Address created = repository.create(input);
var captor = org.mockito.ArgumentCaptor.forClass(AddressEntity.class);
verify(addressDao).save(captor.capture());
AddressEntity persisted = captor.getValue();
assertThat(persisted.getCompanyName()).isEqualTo("ACME");
assertThat(created.getCompanyName()).isEqualTo("ACME");
}
@Test
void update_edits_mutable_fields() {
Long id = 1L;
AddressEntity existing = new AddressEntity();
existing.setCompanyName("OLD");
when(addressDao.findById(id)).thenReturn(Optional.of(existing));
when(addressDao.save(existing)).thenReturn(existing);
Address updated = repository.update(id, new Address(null, "NEW", "2 ave", "75002", "FR", null, null, null, null));
assertThat(updated.getCompanyName()).isEqualTo("NEW");
verify(addressDao).save(existing);
}
@Test
void getById_maps() {
AddressEntity e = new AddressEntity();
e.setCompanyName("X");
when(addressDao.findById(7L)).thenReturn(Optional.of(e));
Address got = repository.getById(7L);
assertThat(got.getCompanyName()).isEqualTo("X");
}
@Test
void list_maps_all() {
AddressEntity e = new AddressEntity();
e.setCompanyName("Y");
when(addressDao.findAll()).thenReturn(List.of(e));
List<Address> result = repository.list();
assertThat(result).hasSize(1);
assertThat(result.get(0).getCompanyName()).isEqualTo("Y");
}
@Test
void delete_calls_dao() {
repository.delete(10L);
verify(addressDao).deleteById(10L);
}
}

View File

@ -1,19 +1,80 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.dto.app.Company;
import com.dh7789dev.xpeditis.entity.CompanyEntity;
import com.dh7789dev.xpeditis.mapper.CompanyMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class CompanyJpaRepositoryTest {
private CompanyDao companyDao;
private CompanyMapper companyMapper;
private CompanyJpaRepository repository;
@BeforeEach
void setUp() {
companyDao = mock(CompanyDao.class);
companyMapper = new CompanyMapper() {
@Override
public CompanyEntity companyToCompanyEntity(Company company) {
CompanyEntity e = new CompanyEntity();
e.setName(company.getName());
e.setCountry(company.getCountry());
return e;
}
@Override
public Company companyEntityToCompany(CompanyEntity companyEntity) {
return new Company(companyEntity.getId(), companyEntity.getName(), companyEntity.getCountry(), null, null, null);
}
};
repository = new CompanyJpaRepository(companyDao, companyMapper);
}
@Test
void test(){
int test = 1 +1;
assertEquals(2,test);
void create_saves_and_returns_mapped() {
Company input = new Company(null, "ACME", "FR", null, null, null);
when(companyDao.save(any())).thenAnswer(inv -> inv.getArgument(0));
Company created = repository.create(input);
var captor = org.mockito.ArgumentCaptor.forClass(CompanyEntity.class);
verify(companyDao).save(captor.capture());
CompanyEntity persisted = captor.getValue();
assertThat(persisted.getName()).isEqualTo("ACME");
assertThat(created.getName()).isEqualTo("ACME");
}
@Test
void update_edits_mutable_fields() {
Long id = 1L;
CompanyEntity existing = new CompanyEntity();
existing.setName("OLD");
when(companyDao.findById(id)).thenReturn(Optional.of(existing));
when(companyDao.save(existing)).thenReturn(existing);
Company updated = repository.update(id, new Company(null, "NEW", "US", null, null, null));
assertThat(updated.getName()).isEqualTo("NEW");
verify(companyDao).save(existing);
}
@Test
void list_maps_all() {
CompanyEntity e = new CompanyEntity();
e.setName("X");
when(companyDao.findAll()).thenReturn(List.of(e));
List<Company> result = repository.list();
assertThat(result).hasSize(1);
assertThat(result.get(0).getName()).isEqualTo("X");
}
}

View File

@ -1,19 +1,133 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.dao.QuoteDetailDao;
import com.dh7789dev.xpeditis.dto.app.Dimension;
import com.dh7789dev.xpeditis.dto.app.QuoteDetail;
import com.dh7789dev.xpeditis.entity.DimensionEntity;
import com.dh7789dev.xpeditis.entity.QuoteDetailEntity;
import com.dh7789dev.xpeditis.mapper.DimensionMapper;
import com.dh7789dev.xpeditis.mapper.QuoteDetailMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class QuoteDetailsJpaRepositoryTest {
private QuoteDetailDao dao;
private QuoteDetailMapper mapper;
private DimensionMapper dimensionMapper;
private QuoteDetailsJpaRepository repository;
@BeforeEach
void setUp() {
dao = mock(QuoteDetailDao.class);
mapper = new QuoteDetailMapper() {
@Override
public QuoteDetailEntity quoteDetailsToQuoteDetailsEntity(QuoteDetail quoteDetail) {
QuoteDetailEntity e = new QuoteDetailEntity();
e.setQuantity(quoteDetail.getQuantity());
if (quoteDetail.getDimension() != null) {
DimensionEntity d = new DimensionEntity();
d.setLength(quoteDetail.getDimension().getLength());
d.setWidth(quoteDetail.getDimension().getWidth());
d.setHeight(quoteDetail.getDimension().getHeight());
e.setDimension(d);
}
e.setWeight(quoteDetail.getWeight());
e.setGerbable(quoteDetail.isStackable());
return e;
}
@Override
public QuoteDetail quoteDetailsEntityToQuoteDetails(QuoteDetailEntity quoteDetailEntity) {
Dimension dim = null;
if (quoteDetailEntity.getDimension() != null) {
dim = new Dimension(null,
quoteDetailEntity.getDimension().getLength(),
quoteDetailEntity.getDimension().getWidth(),
quoteDetailEntity.getDimension().getHeight());
}
return new QuoteDetail(
quoteDetailEntity.getId(),
quoteDetailEntity.getQuantity(),
dim,
quoteDetailEntity.getWeight(),
quoteDetailEntity.isGerbable(),
null
);
}
};
dimensionMapper = new DimensionMapper() {
@Override
public DimensionEntity dimensionToDimensionEntity(Dimension dimension) {
DimensionEntity e = new DimensionEntity();
e.setLength(dimension.getLength());
e.setWidth(dimension.getWidth());
e.setHeight(dimension.getHeight());
return e;
}
@Override
public Dimension dimensionEntityToDimension(DimensionEntity dimensionEntity) {
return new Dimension(null, dimensionEntity.getLength(), dimensionEntity.getWidth(), dimensionEntity.getHeight());
}
};
repository = new QuoteDetailsJpaRepository(dao, mapper, dimensionMapper);
}
@Test
void test(){
int test = 1 +1;
assertEquals(2,test);
void create_saves_and_returns_mapped() {
when(dao.save(any())).thenAnswer(inv -> inv.getArgument(0));
QuoteDetail input = new QuoteDetail(null, 2, new Dimension(null, 1.0, 2.0, 3.0), 10.5, true, null);
QuoteDetail created = repository.create(input);
var captor = org.mockito.ArgumentCaptor.forClass(QuoteDetailEntity.class);
verify(dao).save(captor.capture());
QuoteDetailEntity persisted = captor.getValue();
assertThat(persisted.getQuantity()).isEqualTo(2);
assertThat(created.getQuantity()).isEqualTo(2);
}
@Test
void update_edits_mutable_fields() {
Long id = 1L;
QuoteDetailEntity existing = new QuoteDetailEntity();
existing.setQuantity(1);
when(dao.findById(id)).thenReturn(Optional.of(existing));
when(dao.save(existing)).thenReturn(existing);
QuoteDetail updated = repository.update(id, new QuoteDetail(null, 3, new Dimension(null, 4.0, 5.0, 6.0), 12.0, false, null));
assertThat(updated.getQuantity()).isEqualTo(3);
verify(dao).save(existing);
}
@Test
void getById_maps() {
QuoteDetailEntity e = new QuoteDetailEntity();
e.setQuantity(5);
when(dao.findById(7L)).thenReturn(Optional.of(e));
QuoteDetail got = repository.getById(7L);
assertThat(got.getQuantity()).isEqualTo(5);
}
@Test
void list_maps_all() {
QuoteDetailEntity e = new QuoteDetailEntity();
e.setQuantity(7);
when(dao.findAll()).thenReturn(List.of(e));
List<QuoteDetail> result = repository.list();
assertThat(result).hasSize(1);
assertThat(result.get(0).getQuantity()).isEqualTo(7);
}
@Test
void delete_calls_dao() {
repository.delete(10L);
verify(dao).deleteById(10L);
}
}

View File

@ -1,19 +1,123 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.dao.QuoteDao;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.dto.app.Company;
import com.dh7789dev.xpeditis.dto.app.Quote;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import com.dh7789dev.xpeditis.entity.CompanyEntity;
import com.dh7789dev.xpeditis.entity.QuoteEntity;
import com.dh7789dev.xpeditis.entity.UserEntity;
import com.dh7789dev.xpeditis.mapper.QuoteMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class QuoteJpaRepositoryTest {
private QuoteDao quoteDao;
private UserDao userDao;
private CompanyDao companyDao;
private QuoteMapper quoteMapper;
private QuoteJpaRepository repository;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
quoteDao = mock(QuoteDao.class);
userDao = mock(UserDao.class);
companyDao = mock(CompanyDao.class);
quoteMapper = new QuoteMapper() {
@Override
public QuoteEntity quoteToQuoteEntity(com.dh7789dev.xpeditis.dto.app.Quote quote) {
QuoteEntity e = new QuoteEntity();
e.setPackager(quote.getPackager());
e.setIncoterm(quote.getIncoterm());
e.setDeparturePostalCode(quote.getDeparturePostalCode());
e.setDepartureCity(quote.getDepartureCity());
return e;
}
@Override
public com.dh7789dev.xpeditis.dto.app.Quote quoteEntityToQuote(QuoteEntity quoteEntity) {
return new com.dh7789dev.xpeditis.dto.app.Quote(
quoteEntity.getId(),
quoteEntity.getPackager(),
quoteEntity.getCustomsImportExport(),
quoteEntity.getEur1(),
quoteEntity.getPackagingType(),
null,
quoteEntity.getDangerousGoods(),
quoteEntity.getTailgate(),
quoteEntity.getStraps(),
quoteEntity.getThermalCover(),
quoteEntity.getRegulatedProducts(),
quoteEntity.getAppointmentRequired(),
quoteEntity.getT1(),
quoteEntity.getCustomsStop(),
quoteEntity.getExportAssistance(),
quoteEntity.getInsurance(),
quoteEntity.getOperationType(),
quoteEntity.getIncoterm(),
quoteEntity.getDeparturePostalCode(),
quoteEntity.getDepartureCity(),
quoteEntity.getPickupDate(),
quoteEntity.getArrivalPostalCode(),
quoteEntity.getArrivalCity(),
quoteEntity.getDeliveryDate(),
null,
null,
null
);
}
};
repository = new QuoteJpaRepository(quoteDao, userDao, companyDao, quoteMapper);
}
@Test
void test(){
int test = 1 +1;
assertEquals(2,test);
void create_maps_and_saves_with_links() {
Quote dto = new Quote(
null, "ACME", null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
new UserAccount("jdoe", null, null, null, null, null, null, null, null),
new Company(1L, null, null, null, null, null),
null);
QuoteEntity entity = new QuoteEntity();
when(quoteDao.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(userDao.findByUsernameOrEmail("jdoe")).thenReturn(Optional.of(new UserEntity()));
when(companyDao.findById(1L)).thenReturn(Optional.of(new CompanyEntity()));
Quote saved = repository.create(dto);
var captor = org.mockito.ArgumentCaptor.forClass(QuoteEntity.class);
verify(quoteDao).save(captor.capture());
QuoteEntity persisted = captor.getValue();
assertThat(persisted.getUser()).isNotNull();
assertThat(persisted.getCompany()).isNotNull();
}
@Test
void update_updates_fields_and_links() {
Long id = 7L;
QuoteEntity existing = new QuoteEntity();
when(quoteDao.findById(id)).thenReturn(Optional.of(existing));
when(quoteDao.save(existing)).thenReturn(existing);
Quote dto = new Quote(
null, "PACKAGER", "IMP", true, "BOX", null, false, null, null, null, null, null,
null, null, null, null, "OP", "FOB", "75001", "Paris", null, null, null, null,
null, null, null);
Quote updated = repository.update(id, dto);
assertThat(updated).isNotNull();
assertThat(existing.getPackager()).isEqualTo("PACKAGER");
assertThat(existing.getIncoterm()).isEqualTo("FOB");
verify(quoteDao).save(existing);
}
}

View File

@ -1,19 +1,141 @@
package com.dh7789dev.xpeditis.repository;
import com.dh7789dev.xpeditis.dao.CompanyDao;
import com.dh7789dev.xpeditis.dao.UserDao;
import com.dh7789dev.xpeditis.dto.app.Company;
import com.dh7789dev.xpeditis.dto.app.UserAccount;
import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest;
import com.dh7789dev.xpeditis.entity.Role;
import com.dh7789dev.xpeditis.entity.UserEntity;
import com.dh7789dev.xpeditis.mapper.UserMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.security.Principal;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class UserJpaRepositoryTest {
@Mock private UserDao userDao;
@Mock private PasswordEncoder passwordEncoder;
@Mock private CompanyDao companyDao;
private UserMapper userMapper;
private UserJpaRepository repository;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
userMapper = new UserMapper() {
@Override
public UserEntity userAccountToUserEntity(com.dh7789dev.xpeditis.dto.app.UserAccount user) {
UserEntity e = new UserEntity();
e.setUsername(user.getUsername());
e.setFirstName(user.getFirstName());
e.setLastName(user.getLastName());
e.setEmail(user.getEmail());
if (user.getRole() != null) {
e.setRole(Role.valueOf(user.getRole()));
}
return e;
}
@Override
public com.dh7789dev.xpeditis.dto.app.UserAccount userEntityToUserAccount(UserEntity userEntity) {
return new com.dh7789dev.xpeditis.dto.app.UserAccount(
userEntity.getUsername(),
userEntity.getFirstName(),
userEntity.getLastName(),
userEntity.getEmail(),
null,
userEntity.getRole() != null ? userEntity.getRole().name() : null,
null,
null,
null
);
}
};
repository = new UserJpaRepository(userDao, passwordEncoder, companyDao, userMapper);
}
@Test
void test(){
int test = 1 +1;
assertEquals(2,test);
void changePassword_success() {
UserEntity principalUser = new UserEntity();
principalUser.setPassword("hashedOld");
when(passwordEncoder.matches("old", "hashedOld")).thenReturn(true);
when(passwordEncoder.encode("new")).thenReturn("hashedNew");
Principal principal = new UsernamePasswordAuthenticationToken(principalUser, null);
ChangePasswordRequest req = new ChangePasswordRequest()
.setCurrentPassword("old")
.setNewPassword("new")
.setConfirmationPassword("new");
repository.changePassword(req, principal);
assertThat(principalUser.getPassword()).isEqualTo("hashedNew");
verify(userDao).save(principalUser);
}
@Test
void changePassword_wrongCurrent_throws() {
UserEntity principalUser = new UserEntity();
principalUser.setPassword("hashedOld");
when(passwordEncoder.matches("wrong", "hashedOld")).thenReturn(false);
Principal principal = new UsernamePasswordAuthenticationToken(principalUser, null);
ChangePasswordRequest req = new ChangePasswordRequest()
.setCurrentPassword("wrong")
.setNewPassword("new")
.setConfirmationPassword("new");
assertThrows(IllegalStateException.class, () -> repository.changePassword(req, principal));
}
@Test
void create_encodesPassword_and_saves() {
UserAccount dto = new UserAccount("jdoe", "John", "Doe", "john@ex.com", "pwd", "ADMIN", new Company(1L, null, null, null, null, null), null, null);
when(userDao.save(any())).thenAnswer(inv -> inv.getArgument(0));
when(passwordEncoder.encode("pwd")).thenReturn("hashed");
UserAccount saved = repository.create(dto);
assertThat(saved.getUsername()).isEqualTo("jdoe");
assertThat(saved.getFirstName()).isEqualTo("John");
assertThat(saved.getLastName()).isEqualTo("Doe");
assertThat(saved.getEmail()).isEqualTo("john@ex.com");
var captor = ArgumentCaptor.forClass(UserEntity.class);
verify(userDao).save(captor.capture());
UserEntity persisted = captor.getValue();
assertThat(persisted.getPassword()).isEqualTo("hashed");
assertThat(persisted.getRole()).isEqualTo(Role.ADMIN);
}
@Test
void update_updatesSelectedFields() {
Long id = 42L;
UserAccount update = new UserAccount("newuser", "Jane", null, null, null, null, null, null, null);
UserEntity existing = new UserEntity();
when(userDao.findById(id)).thenReturn(Optional.of(existing));
when(userDao.save(existing)).thenReturn(existing);
UserAccount result = repository.update(id, update);
assertThat(result).isNotNull();
assertThat(existing.getFirstName()).isEqualTo("Jane");
assertThat(existing.getUsername()).isEqualTo("newuser");
verify(userDao).save(existing);
}
}