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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 35 additions & 37 deletions src/main/java/app/ApplicationConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@
import app.dto.company.CompanyResponseDTO;
import app.dto.company.CreateCompanyRequestDTO;
import app.dto.company.UpdateCompanyRequestDTO;
import app.dto.login.LoginResponseDTO;
import app.dto.randomuser.RandomUserViewDTO;
import app.dto.user.CreateUserRequestDTO;
import app.dto.user.UpdateUserRequestDTO;
import app.dto.user.UserResponseDTO;
import app.entities.Company;
import app.entities.User;
import app.exceptions.ApiErrorResponse;
import app.exceptions.ConflictException;
import app.exceptions.UnauthorizedException;
import app.services.AuthService;
import app.services.JwtService;
import app.services.PasswordService;
import app.services.RandomUserService;
import app.services.*;

import io.javalin.Javalin;
import jakarta.persistence.EntityManagerFactory;
Expand All @@ -34,10 +33,21 @@ public static Javalin startApp(int port, EntityManagerFactory emf)
PasswordService passwordService = new PasswordService();
JwtService jwtService = new JwtService();
AuthService authService = new AuthService(userDAO, passwordService, jwtService);
UserServiceImpl userService = new UserServiceImpl(userDAO, companyDAO, passwordService);
RandomUserService randomUserService = new RandomUserService();

Javalin app = Javalin.create(config ->
{
config.bundledPlugins.enableCors(cors ->
{
cors.addRule(it ->
{
it.allowHost(
"https://membersystem.obli.dk",
"http://localhost:5173"
);
});
});
config.router.apiBuilder(() ->
{
// TODO: Split routes into separate controller classes later.
Expand All @@ -63,11 +73,18 @@ public static Javalin startApp(int port, EntityManagerFactory emf)
ctx.json(new ApiErrorResponse(400, e.getMessage()));
});

app.exception(UnauthorizedException.class, (e, ctx) -> {
app.exception(UnauthorizedException.class, (e, ctx) ->
{
ctx.status(401);
ctx.json(new app.exceptions.ApiErrorResponse(401, e.getMessage()));
});

app.exception(ConflictException.class, (e, ctx) ->
{
ctx.status(409);
ctx.json(new ApiErrorResponse(409, e.getMessage()));
});

app.exception(Exception.class, (e, ctx) ->
{
e.printStackTrace();
Expand All @@ -82,13 +99,14 @@ public static Javalin startApp(int port, EntityManagerFactory emf)
// --------------------
// TODO: Add authentication endpoints like /login.

app.post("/login", ctx -> {
app.post("/login", ctx ->
{
var request = ctx.bodyAsClass(app.dto.login.LoginRequestDTO.class);

String token = authService.login(request.email(), request.password());

ctx.status(200);
ctx.json(new app.dto.login.LoginResponseDTO(token));
ctx.json(new LoginResponseDTO(token));
});

// --------------------
Expand Down Expand Up @@ -126,6 +144,11 @@ public static Javalin startApp(int port, EntityManagerFactory emf)

CreateCompanyRequestDTO request = ctx.bodyAsClass(CreateCompanyRequestDTO.class);

if (companyDAO.findByName(request.name()).isPresent())
{
throw new ConflictException("Company already exists with name: " + request.name());
}

Company company = Company.builder()
.name(request.name())
.build();
Expand Down Expand Up @@ -222,34 +245,7 @@ public static Javalin startApp(int port, EntityManagerFactory emf)
requireAuth(ctx, jwtService);

CreateUserRequestDTO request = ctx.bodyAsClass(CreateUserRequestDTO.class);

Company company = companyDAO.getById(request.companyId());

String hashedPassword = passwordService.hashPassword(request.password());

User user = User.builder()
.company(company)
.email(request.email())
.firstname(request.firstname())
.lastname(request.lastname())
.dob(request.dob())
.role(request.role())
.passwordHash(hashedPassword)
.build();

User created = userDAO.create(user);
User createdWithCompany = userDAO.getByIdWithCompany(created.getId());

UserResponseDTO response = new UserResponseDTO(
createdWithCompany.getId(),
createdWithCompany.getEmail(),
createdWithCompany.getFirstname(),
createdWithCompany.getLastname(),
createdWithCompany.getDob(),
createdWithCompany.getRole(),
createdWithCompany.getCompany().getId(),
createdWithCompany.getCompany().getName()
);
UserResponseDTO response = userService.create(request);

ctx.status(201);
ctx.json(response);
Expand Down Expand Up @@ -343,10 +339,12 @@ public static Javalin startApp(int port, EntityManagerFactory emf)
// Helper methods
// --------------------

private static void requireAuth(io.javalin.http.Context ctx, JwtService jwtService) {
private static void requireAuth(io.javalin.http.Context ctx, JwtService jwtService)
{
String authHeader = ctx.header("Authorization");

if (authHeader == null || !authHeader.startsWith("Bearer ")) {
if (authHeader == null || !authHeader.startsWith("Bearer "))
{
throw new UnauthorizedException("Missing or invalid Authorization header");
}

Expand Down
23 changes: 16 additions & 7 deletions src/main/java/app/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import app.entities.Role;
import app.entities.User;
import app.services.PasswordService;
import app.utils.Utils;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityNotFoundException;

import java.time.LocalDate;

Expand All @@ -31,25 +33,32 @@ private static void seedBootstrapAdmin(EntityManagerFactory emf)
PasswordService passwordService = new PasswordService();

String adminEmail = "admin@obli.dk";
String companyName = "Membersystem Bootstrap Company";

if (userDAO.findByEmail(adminEmail).isPresent())
{
System.out.println("Bootstrap admin already exists.");
return;
}

Company company = companyDAO.create(
Company.builder()
.name("Membersystem Bootstrap Company")
.build()
);
Company company = companyDAO.findByName(companyName)
.orElseGet(() -> companyDAO.create(
Company.builder()
.name(companyName)
.build()
));

// String password = "Test1234!";
String password = System.getenv("BOOTSTRAP_ADMIN_PASSWORD");
if (password == null || password.isBlank())
{
password = Utils.getPropertyValue("PASSWORD", "config.properties");
}

if (password == null || password.isBlank())
{
throw new IllegalStateException("BOOTSTRAP_ADMIN_PASSWORD is not set");
}

String hashedPassword = passwordService.hashPassword(password);

User admin = User.builder()
Expand All @@ -74,6 +83,6 @@ private static int getPort()
{
return Integer.parseInt(portEnv);
}
return 7000;
return 7070;
}
}
60 changes: 45 additions & 15 deletions src/main/java/app/daos/CompanyDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@
import jakarta.persistence.EntityNotFoundException;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

public class CompanyDAO implements IDAO<Company> {
public class CompanyDAO implements IDAO<Company>
{

private final EntityManagerFactory emf;

public CompanyDAO(EntityManagerFactory emf) {
public CompanyDAO(EntityManagerFactory emf)
{
this.emf = emf;
}

@Override
public Company create(Company company) {
try (EntityManager em = emf.createEntityManager()) {
public Company create(Company company)
{
try (EntityManager em = emf.createEntityManager())
{
em.getTransaction().begin();
em.persist(company);
em.getTransaction().commit();
Expand All @@ -28,8 +33,10 @@ public Company create(Company company) {
}

@Override
public Set<Company> getAll() {
try (EntityManager em = emf.createEntityManager()) {
public Set<Company> getAll()
{
try (EntityManager em = emf.createEntityManager())
{
return new HashSet<>(
em.createQuery("SELECT c FROM Company c", Company.class)
.getResultList()
Expand All @@ -38,21 +45,41 @@ public Set<Company> getAll() {
}

@Override
public Company getById(Long id) {
try (EntityManager em = emf.createEntityManager()) {
public Company getById(Long id)
{
try (EntityManager em = emf.createEntityManager())
{
Company company = em.find(Company.class, id);
if (company == null) {
if (company == null)
{
throw new EntityNotFoundException("Company not found with id: " + id);
}
return company;
}
}

public Optional<Company> findByName(String name)
{
try (EntityManager em = emf.createEntityManager())
{
return em.createQuery(
"SELECT c FROM Company c WHERE c.name = :name",
Company.class
)
.setParameter("name", name)
.getResultStream()
.findFirst();
}
}

@Override
public Company update(Company company) {
try (EntityManager em = emf.createEntityManager()) {
public Company update(Company company)
{
try (EntityManager em = emf.createEntityManager())
{
Company found = em.find(Company.class, company.getId());
if (found == null) {
if (found == null)
{
throw new EntityNotFoundException("Company not found with id: " + company.getId());
}

Expand All @@ -64,10 +91,13 @@ public Company update(Company company) {
}

@Override
public Long delete(Company company) {
try (EntityManager em = emf.createEntityManager()) {
public Long delete(Company company)
{
try (EntityManager em = emf.createEntityManager())
{
Company found = em.find(Company.class, company.getId());
if (found == null) {
if (found == null)
{
throw new EntityNotFoundException("Company not found with id: " + company.getId());
}

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/app/exceptions/ConflictException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package app.exceptions;

public class ConflictException extends RuntimeException {
public ConflictException(String message) {
super(message);
}
}
20 changes: 20 additions & 0 deletions src/main/java/app/interfaces/IUserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package app.interfaces;

import app.dto.user.CreateUserRequestDTO;
import app.dto.user.UpdateUserRequestDTO;
import app.dto.user.UserResponseDTO;

import java.util.List;

public interface IUserService
{
UserResponseDTO create(CreateUserRequestDTO request);

UserResponseDTO getById(Long id);

List<UserResponseDTO> getAll();

UserResponseDTO update(Long id, UpdateUserRequestDTO request);

void delete(Long id);
}
Loading
Loading