Modal do primeiro acesso
This commit is contained in:
parent
e99e1cd586
commit
22760ba661
22 changed files with 417 additions and 4 deletions
|
|
@ -6,6 +6,7 @@ public class UserOrganization {
|
||||||
private User user;
|
private User user;
|
||||||
private Organization organization;
|
private Organization organization;
|
||||||
private UserOrganizationRole role;
|
private UserOrganizationRole role;
|
||||||
|
private UserOrganizationStatus status;
|
||||||
|
|
||||||
public User getUser() {
|
public User getUser() {
|
||||||
return user;
|
return user;
|
||||||
|
|
@ -30,4 +31,12 @@ public class UserOrganization {
|
||||||
public void setRole(UserOrganizationRole role) {
|
public void setRole(UserOrganizationRole role) {
|
||||||
this.role = role;
|
this.role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserOrganizationStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(UserOrganizationStatus status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package net.beetechgroup.beetask.entities.organization;
|
||||||
|
|
||||||
|
public enum UserOrganizationStatus {
|
||||||
|
PENDING,
|
||||||
|
ACTIVE,
|
||||||
|
REJECTED
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,11 @@ package net.beetechgroup.beetask.frameworks.config;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.enterprise.inject.Produces;
|
import jakarta.enterprise.inject.Produces;
|
||||||
import net.beetechgroup.beetask.usecase.organization.create.CreateOrganizationUseCase;
|
import net.beetechgroup.beetask.usecase.organization.create.CreateOrganizationUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.search.SearchOrganizationsUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.join.RequestJoinOrganizationUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.requests.ListPendingRequestsUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.requests.HandleJoinRequestUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationAdminUseCase;
|
||||||
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
|
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
|
||||||
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||||
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||||
|
|
@ -10,10 +15,40 @@ import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class OrganizationUseCaseConfig {
|
public class OrganizationUseCaseConfig {
|
||||||
|
|
||||||
|
@Produces
|
||||||
|
public AuthorizeOrganizationAdminUseCase authorizeOrganizationAdminUseCase(UserRepository userRepository,
|
||||||
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
|
return new AuthorizeOrganizationAdminUseCase(userRepository, userOrganizationRepository);
|
||||||
|
}
|
||||||
|
|
||||||
@Produces
|
@Produces
|
||||||
public CreateOrganizationUseCase createOrganizationUseCase(OrganizationRepository organizationRepository,
|
public CreateOrganizationUseCase createOrganizationUseCase(OrganizationRepository organizationRepository,
|
||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
UserOrganizationRepository userOrganizationRepository) {
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
return new CreateOrganizationUseCase(organizationRepository, userRepository, userOrganizationRepository);
|
return new CreateOrganizationUseCase(organizationRepository, userRepository, userOrganizationRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Produces
|
||||||
|
public SearchOrganizationsUseCase searchOrganizationsUseCase(OrganizationRepository organizationRepository) {
|
||||||
|
return new SearchOrganizationsUseCase(organizationRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Produces
|
||||||
|
public RequestJoinOrganizationUseCase requestJoinOrganizationUseCase(OrganizationRepository organizationRepository,
|
||||||
|
UserRepository userRepository,
|
||||||
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
|
return new RequestJoinOrganizationUseCase(organizationRepository, userRepository, userOrganizationRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Produces
|
||||||
|
public ListPendingRequestsUseCase listPendingRequestsUseCase(AuthorizeOrganizationAdminUseCase authorizer,
|
||||||
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
|
return new ListPendingRequestsUseCase(authorizer, userOrganizationRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Produces
|
||||||
|
public HandleJoinRequestUseCase handleJoinRequestUseCase(AuthorizeOrganizationAdminUseCase authorizer,
|
||||||
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
|
return new HandleJoinRequestUseCase(authorizer, userOrganizationRepository);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,9 @@ import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import net.beetechgroup.beetask.entities.organization.UserOrganizationRole;
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationRole;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
|
||||||
|
import jakarta.persistence.EnumType;
|
||||||
|
import jakarta.persistence.Enumerated;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "user_organizations")
|
@Table(name = "user_organizations")
|
||||||
|
|
@ -21,7 +24,10 @@ public class UserOrganizationEntity {
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "organization_id")
|
@JoinColumn(name = "organization_id")
|
||||||
private OrganizationEntity organization;
|
private OrganizationEntity organization;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
private UserOrganizationRole role;
|
private UserOrganizationRole role;
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private UserOrganizationStatus status;
|
||||||
|
|
||||||
public UserEntity getUser() {
|
public UserEntity getUser() {
|
||||||
return user;
|
return user;
|
||||||
|
|
@ -46,4 +52,12 @@ public class UserOrganizationEntity {
|
||||||
public void setRole(UserOrganizationRole role) {
|
public void setRole(UserOrganizationRole role) {
|
||||||
this.role = role;
|
this.role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserOrganizationStatus getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(UserOrganizationStatus status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ public class UserOrganizationEntityMapper {
|
||||||
domain.setUser(UserEntityMapper.toDomain(entity.getUser()));
|
domain.setUser(UserEntityMapper.toDomain(entity.getUser()));
|
||||||
domain.setOrganization(OrganizationEntityMapper.toDomain(entity.getOrganization()));
|
domain.setOrganization(OrganizationEntityMapper.toDomain(entity.getOrganization()));
|
||||||
domain.setRole(entity.getRole());
|
domain.setRole(entity.getRole());
|
||||||
|
domain.setStatus(entity.getStatus());
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,6 +21,7 @@ public class UserOrganizationEntityMapper {
|
||||||
entity.setUser(UserEntityMapper.toEntity(domain.getUser()));
|
entity.setUser(UserEntityMapper.toEntity(domain.getUser()));
|
||||||
entity.setOrganization(OrganizationEntityMapper.toEntity(domain.getOrganization()));
|
entity.setOrganization(OrganizationEntityMapper.toEntity(domain.getOrganization()));
|
||||||
entity.setRole(domain.getRole());
|
entity.setRole(domain.getRole());
|
||||||
|
entity.setStatus(domain.getStatus());
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ import net.beetechgroup.beetask.frameworks.persistence.entities.OrganizationEnti
|
||||||
import net.beetechgroup.beetask.frameworks.persistence.mapper.OrganizationEntityMapper;
|
import net.beetechgroup.beetask.frameworks.persistence.mapper.OrganizationEntityMapper;
|
||||||
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
|
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class OrganizationRepositoryImpl implements OrganizationRepository, PanacheRepository<OrganizationEntity> {
|
public class OrganizationRepositoryImpl implements OrganizationRepository, PanacheRepository<OrganizationEntity> {
|
||||||
|
|
||||||
|
|
@ -23,4 +26,16 @@ public class OrganizationRepositoryImpl implements OrganizationRepository, Panac
|
||||||
return OrganizationEntityMapper.toDomain(entity);
|
return OrganizationEntityMapper.toDomain(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Organization> search(String query) {
|
||||||
|
return find("name like ?1", "%" + query + "%").stream()
|
||||||
|
.map(OrganizationEntityMapper::toDomain)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Organization> findOrganizationById(Long id) {
|
||||||
|
return findByIdOptional(id)
|
||||||
|
.map(OrganizationEntityMapper::toDomain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,11 +4,13 @@ import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
|
||||||
import net.beetechgroup.beetask.frameworks.persistence.entities.UserOrganizationEntity;
|
import net.beetechgroup.beetask.frameworks.persistence.entities.UserOrganizationEntity;
|
||||||
import net.beetechgroup.beetask.frameworks.persistence.mapper.UserOrganizationEntityMapper;
|
import net.beetechgroup.beetask.frameworks.persistence.mapper.UserOrganizationEntityMapper;
|
||||||
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class UserOrganizationRepositoryImpl implements UserOrganizationRepository, PanacheRepository<UserOrganizationEntity> {
|
public class UserOrganizationRepositoryImpl implements UserOrganizationRepository, PanacheRepository<UserOrganizationEntity> {
|
||||||
|
|
@ -20,10 +22,43 @@ public class UserOrganizationRepositoryImpl implements UserOrganizationRepositor
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserOrganization> findByUserIdAndStatus(Long userId, UserOrganizationStatus status) {
|
||||||
|
return find("user.id = ?1 and status = ?2", userId, status).stream()
|
||||||
|
.map(UserOrganizationEntityMapper::toDomain)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserOrganization> findByOrganizationIdAndStatus(Long organizationId, UserOrganizationStatus status) {
|
||||||
|
return find("organization.id = ?1 and status = ?2", organizationId, status).stream()
|
||||||
|
.map(UserOrganizationEntityMapper::toDomain)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<UserOrganization> findByUserAndOrganization(Long userId, Long organizationId) {
|
||||||
|
return find("user.id = ?1 and organization.id = ?2", userId, organizationId)
|
||||||
|
.firstResultOptional()
|
||||||
|
.map(UserOrganizationEntityMapper::toDomain);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void save(UserOrganization userOrganization) {
|
public void save(UserOrganization userOrganization) {
|
||||||
UserOrganizationEntity entity = UserOrganizationEntityMapper.toEntity(userOrganization);
|
UserOrganizationEntity entity = UserOrganizationEntityMapper.toEntity(userOrganization);
|
||||||
|
if (entity.getUser().getId() != null && entity.getOrganization().getId() != null) {
|
||||||
|
// Find existing by user and org to update instead of creating new if it exists
|
||||||
|
Optional<UserOrganizationEntity> existing = find("user.id = ?1 and organization.id = ?2",
|
||||||
|
entity.getUser().getId(), entity.getOrganization().getId()).firstResultOptional();
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
UserOrganizationEntity existingEntity = existing.get();
|
||||||
|
existingEntity.setRole(entity.getRole());
|
||||||
|
existingEntity.setStatus(entity.getStatus());
|
||||||
|
getEntityManager().merge(existingEntity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
persist(entity);
|
persist(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import net.beetechgroup.beetask.entities.User;
|
||||||
import net.beetechgroup.beetask.frameworks.persistence.entities.UserEntity;
|
import net.beetechgroup.beetask.frameworks.persistence.entities.UserEntity;
|
||||||
import net.beetechgroup.beetask.frameworks.persistence.mapper.UserEntityMapper;
|
import net.beetechgroup.beetask.frameworks.persistence.mapper.UserEntityMapper;
|
||||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
|
||||||
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||||
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||||
|
|
||||||
|
|
@ -46,6 +47,6 @@ public class UserRepositoryImpl implements UserRepository, PanacheRepository<Use
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<UserOrganization> findUserOrganizations(Long userId) {
|
public List<UserOrganization> findUserOrganizations(Long userId) {
|
||||||
return userOrganizationRepository.findByUserId(userId);
|
return userOrganizationRepository.findByUserIdAndStatus(userId, UserOrganizationStatus.ACTIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,23 @@ import io.quarkus.security.Authenticated;
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.POST;
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.PATCH;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
import net.beetechgroup.beetask.usecase.organization.create.CreateOrganizationInput;
|
import net.beetechgroup.beetask.usecase.organization.create.CreateOrganizationInput;
|
||||||
import net.beetechgroup.beetask.usecase.organization.create.CreateOrganizationOutput;
|
import net.beetechgroup.beetask.usecase.organization.create.CreateOrganizationOutput;
|
||||||
import net.beetechgroup.beetask.usecase.organization.create.CreateOrganizationUseCase;
|
import net.beetechgroup.beetask.usecase.organization.create.CreateOrganizationUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.search.SearchOrganizationsUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.search.SearchOrganizationOutput;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.join.RequestJoinOrganizationUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.requests.ListPendingRequestsUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.requests.JoinRequestOutput;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.requests.HandleJoinRequestUseCase;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Path("/organizations")
|
@Path("/organizations")
|
||||||
public class OrganizationController {
|
public class OrganizationController {
|
||||||
|
|
@ -16,6 +28,18 @@ public class OrganizationController {
|
||||||
@Inject
|
@Inject
|
||||||
CreateOrganizationUseCase createOrganizationUseCase;
|
CreateOrganizationUseCase createOrganizationUseCase;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SearchOrganizationsUseCase searchOrganizationsUseCase;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RequestJoinOrganizationUseCase requestJoinOrganizationUseCase;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ListPendingRequestsUseCase listPendingRequestsUseCase;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
HandleJoinRequestUseCase handleJoinRequestUseCase;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SecurityIdentity securityIdentity;
|
SecurityIdentity securityIdentity;
|
||||||
|
|
||||||
|
|
@ -28,4 +52,48 @@ public class OrganizationController {
|
||||||
CreateOrganizationOutput output = createOrganizationUseCase.execute(input);
|
CreateOrganizationOutput output = createOrganizationUseCase.execute(input);
|
||||||
return OrganizationControllerMapper.toCreateOrganizationResponse(output);
|
return OrganizationControllerMapper.toCreateOrganizationResponse(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/search")
|
||||||
|
@Authenticated
|
||||||
|
@Operation(summary = "Search organizations", description = "Search for organizations by name")
|
||||||
|
public List<SearchOrganizationOutput> search(@QueryParam("q") String query) {
|
||||||
|
return searchOrganizationsUseCase.execute(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{id}/join")
|
||||||
|
@Authenticated
|
||||||
|
@Operation(summary = "Request to join", description = "Request to join an organization")
|
||||||
|
public void requestJoin(@PathParam("id") Long organizationId) {
|
||||||
|
String userEmail = securityIdentity.getPrincipal().getName();
|
||||||
|
requestJoinOrganizationUseCase.execute(organizationId, userEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/requests")
|
||||||
|
@Authenticated
|
||||||
|
@Operation(summary = "List pending requests", description = "List all pending join requests for an organization (Owner only)")
|
||||||
|
public List<JoinRequestOutput> listRequests(@PathParam("id") Long organizationId) {
|
||||||
|
String userEmail = securityIdentity.getPrincipal().getName();
|
||||||
|
return listPendingRequestsUseCase.execute(userEmail, organizationId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PATCH
|
||||||
|
@Path("/{id}/requests/{userId}/approve")
|
||||||
|
@Authenticated
|
||||||
|
@Operation(summary = "Approve request", description = "Approve a join request (Owner only)")
|
||||||
|
public void approveRequest(@PathParam("id") Long organizationId, @PathParam("userId") Long userId) {
|
||||||
|
String userEmail = securityIdentity.getPrincipal().getName();
|
||||||
|
handleJoinRequestUseCase.execute(userEmail, organizationId, new HandleJoinRequestUseCase.Input(userId, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PATCH
|
||||||
|
@Path("/{id}/requests/{userId}/reject")
|
||||||
|
@Authenticated
|
||||||
|
@Operation(summary = "Reject request", description = "Reject a join request (Owner only)")
|
||||||
|
public void rejectRequest(@PathParam("id") Long organizationId, @PathParam("userId") Long userId) {
|
||||||
|
String userEmail = securityIdentity.getPrincipal().getName();
|
||||||
|
handleJoinRequestUseCase.execute(userEmail, organizationId, new HandleJoinRequestUseCase.Input(userId, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.organization.auth;
|
||||||
|
|
||||||
|
public abstract class AuthenticatedOrganizationUseCase<I, O> {
|
||||||
|
protected final AuthorizeOrganizationAdminUseCase authorizer;
|
||||||
|
|
||||||
|
protected AuthenticatedOrganizationUseCase(AuthorizeOrganizationAdminUseCase authorizer) {
|
||||||
|
this.authorizer = authorizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public O execute(String userEmail, Long organizationId, I input) {
|
||||||
|
authorizer.execute(userEmail, organizationId);
|
||||||
|
return doExecute(organizationId, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract O doExecute(Long organizationId, I input);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.organization.auth;
|
||||||
|
|
||||||
|
import net.beetechgroup.beetask.entities.User;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationRole;
|
||||||
|
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||||
|
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class AuthorizeOrganizationAdminUseCase {
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final UserOrganizationRepository userOrganizationRepository;
|
||||||
|
|
||||||
|
public AuthorizeOrganizationAdminUseCase(UserRepository userRepository,
|
||||||
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.userOrganizationRepository = userOrganizationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(String userEmail, Long organizationId) {
|
||||||
|
Optional<User> userOptional = userRepository.findByEmail(userEmail);
|
||||||
|
if (userOptional.isEmpty()) {
|
||||||
|
throw new RuntimeException("Unauthorized: User not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userOptional.get();
|
||||||
|
Optional<UserOrganization> association = userOrganizationRepository.findByUserAndOrganization(user.getId(), organizationId);
|
||||||
|
|
||||||
|
if (association.isEmpty()) {
|
||||||
|
throw new RuntimeException("Forbidden: Not a member of this organization");
|
||||||
|
}
|
||||||
|
|
||||||
|
UserOrganization uo = association.get();
|
||||||
|
if (uo.getRole() != UserOrganizationRole.OWNER && uo.getRole() != UserOrganizationRole.ADMIN) {
|
||||||
|
throw new RuntimeException("Forbidden: Only owners or admins can perform this action");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import net.beetechgroup.beetask.entities.User;
|
||||||
import net.beetechgroup.beetask.entities.organization.Organization;
|
import net.beetechgroup.beetask.entities.organization.Organization;
|
||||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||||
import net.beetechgroup.beetask.entities.organization.UserOrganizationRole;
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationRole;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
|
||||||
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
|
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
|
||||||
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||||
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||||
|
|
@ -38,6 +39,7 @@ public class CreateOrganizationUseCase {
|
||||||
userOrganization.setUser(user);
|
userOrganization.setUser(user);
|
||||||
userOrganization.setOrganization(organization);
|
userOrganization.setOrganization(organization);
|
||||||
userOrganization.setRole(UserOrganizationRole.OWNER);
|
userOrganization.setRole(UserOrganizationRole.OWNER);
|
||||||
|
userOrganization.setStatus(UserOrganizationStatus.ACTIVE);
|
||||||
userOrganizationRepository.save(userOrganization);
|
userOrganizationRepository.save(userOrganization);
|
||||||
|
|
||||||
return CreateOrganizationMapper.toCreateOrganizationOutput(organization);
|
return CreateOrganizationMapper.toCreateOrganizationOutput(organization);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.organization.join;
|
||||||
|
|
||||||
|
import net.beetechgroup.beetask.entities.User;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.Organization;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationRole;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
|
||||||
|
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
|
||||||
|
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||||
|
import net.beetechgroup.beetask.usecase.repository.UserRepository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class RequestJoinOrganizationUseCase {
|
||||||
|
private final OrganizationRepository organizationRepository;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final UserOrganizationRepository userOrganizationRepository;
|
||||||
|
|
||||||
|
public RequestJoinOrganizationUseCase(OrganizationRepository organizationRepository,
|
||||||
|
UserRepository userRepository,
|
||||||
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
|
this.organizationRepository = organizationRepository;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.userOrganizationRepository = userOrganizationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(Long organizationId, String userEmail) {
|
||||||
|
Optional<User> userOptional = userRepository.findByEmail(userEmail);
|
||||||
|
Optional<Organization> orgOptional = organizationRepository.findOrganizationById(organizationId);
|
||||||
|
|
||||||
|
if (userOptional.isEmpty() || orgOptional.isEmpty()) {
|
||||||
|
throw new RuntimeException("User or Organization not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userOptional.get();
|
||||||
|
Organization organization = orgOptional.get();
|
||||||
|
|
||||||
|
Optional<UserOrganization> existing = userOrganizationRepository.findByUserAndOrganization(user.getId(), organization.getId());
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
if (existing.get().getStatus() == UserOrganizationStatus.ACTIVE) {
|
||||||
|
throw new RuntimeException("User is already a member of this organization");
|
||||||
|
} else if (existing.get().getStatus() == UserOrganizationStatus.PENDING) {
|
||||||
|
throw new RuntimeException("A join request is already pending for this organization");
|
||||||
|
}
|
||||||
|
// If REJECTED, we allow a new request (or we could just update the existing one to PENDING)
|
||||||
|
}
|
||||||
|
|
||||||
|
UserOrganization request = new UserOrganization();
|
||||||
|
request.setUser(user);
|
||||||
|
request.setOrganization(organization);
|
||||||
|
request.setRole(UserOrganizationRole.MEMBER);
|
||||||
|
request.setStatus(UserOrganizationStatus.PENDING);
|
||||||
|
|
||||||
|
userOrganizationRepository.save(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.organization.requests;
|
||||||
|
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.auth.AuthenticatedOrganizationUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationAdminUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class HandleJoinRequestUseCase extends AuthenticatedOrganizationUseCase<HandleJoinRequestUseCase.Input, Void> {
|
||||||
|
private final UserOrganizationRepository userOrganizationRepository;
|
||||||
|
|
||||||
|
public HandleJoinRequestUseCase(AuthorizeOrganizationAdminUseCase authorizer,
|
||||||
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
|
super(authorizer);
|
||||||
|
this.userOrganizationRepository = userOrganizationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doExecute(Long organizationId, Input input) {
|
||||||
|
Optional<UserOrganization> associationOptional = userOrganizationRepository.findByUserAndOrganization(input.userId(), organizationId);
|
||||||
|
|
||||||
|
if (associationOptional.isEmpty()) {
|
||||||
|
throw new RuntimeException("Request not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
UserOrganization association = associationOptional.get();
|
||||||
|
if (association.getStatus() != UserOrganizationStatus.PENDING) {
|
||||||
|
throw new RuntimeException("Request is not pending");
|
||||||
|
}
|
||||||
|
|
||||||
|
association.setStatus(input.approved() ? UserOrganizationStatus.ACTIVE : UserOrganizationStatus.REJECTED);
|
||||||
|
userOrganizationRepository.save(association);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Input(Long userId, boolean approved) {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.organization.requests;
|
||||||
|
|
||||||
|
public record JoinRequestOutput(Long userId, String name, String email, String photo) {}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.organization.requests;
|
||||||
|
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.auth.AuthenticatedOrganizationUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.organization.auth.AuthorizeOrganizationAdminUseCase;
|
||||||
|
import net.beetechgroup.beetask.usecase.repository.UserOrganizationRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ListPendingRequestsUseCase extends AuthenticatedOrganizationUseCase<Void, List<JoinRequestOutput>> {
|
||||||
|
private final UserOrganizationRepository userOrganizationRepository;
|
||||||
|
|
||||||
|
public ListPendingRequestsUseCase(AuthorizeOrganizationAdminUseCase authorizer,
|
||||||
|
UserOrganizationRepository userOrganizationRepository) {
|
||||||
|
super(authorizer);
|
||||||
|
this.userOrganizationRepository = userOrganizationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<JoinRequestOutput> doExecute(Long organizationId, Void input) {
|
||||||
|
List<UserOrganization> pending = userOrganizationRepository.findByOrganizationIdAndStatus(organizationId, UserOrganizationStatus.PENDING);
|
||||||
|
return pending.stream()
|
||||||
|
.map(uo -> new JoinRequestOutput(
|
||||||
|
uo.getUser().getId(),
|
||||||
|
uo.getUser().getName(),
|
||||||
|
uo.getUser().getEmail(),
|
||||||
|
uo.getUser().getPhoto()
|
||||||
|
))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.organization.search;
|
||||||
|
|
||||||
|
public record SearchOrganizationOutput(Long id, String name) {}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package net.beetechgroup.beetask.usecase.organization.search;
|
||||||
|
|
||||||
|
import net.beetechgroup.beetask.entities.organization.Organization;
|
||||||
|
import net.beetechgroup.beetask.usecase.repository.OrganizationRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SearchOrganizationsUseCase {
|
||||||
|
private final OrganizationRepository organizationRepository;
|
||||||
|
|
||||||
|
public SearchOrganizationsUseCase(OrganizationRepository organizationRepository) {
|
||||||
|
this.organizationRepository = organizationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SearchOrganizationOutput> execute(String query) {
|
||||||
|
List<Organization> results = organizationRepository.search(query);
|
||||||
|
return results.stream()
|
||||||
|
.map(org -> new SearchOrganizationOutput(org.getId(), org.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,11 @@ package net.beetechgroup.beetask.usecase.repository;
|
||||||
|
|
||||||
|
|
||||||
import net.beetechgroup.beetask.entities.organization.Organization;
|
import net.beetechgroup.beetask.entities.organization.Organization;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface OrganizationRepository {
|
public interface OrganizationRepository {
|
||||||
Organization saveOrganization(Organization organization);
|
Organization saveOrganization(Organization organization);
|
||||||
|
List<Organization> search(String query);
|
||||||
|
Optional<Organization> findOrganizationById(Long id);
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
package net.beetechgroup.beetask.usecase.repository;
|
package net.beetechgroup.beetask.usecase.repository;
|
||||||
|
|
||||||
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
import net.beetechgroup.beetask.entities.organization.UserOrganization;
|
||||||
|
import net.beetechgroup.beetask.entities.organization.UserOrganizationStatus;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface UserOrganizationRepository {
|
public interface UserOrganizationRepository {
|
||||||
List<UserOrganization> findByUserId(Long userId);
|
List<UserOrganization> findByUserId(Long userId);
|
||||||
|
List<UserOrganization> findByUserIdAndStatus(Long userId, UserOrganizationStatus status);
|
||||||
|
List<UserOrganization> findByOrganizationIdAndStatus(Long organizationId, UserOrganizationStatus status);
|
||||||
|
Optional<UserOrganization> findByUserAndOrganization(Long userId, Long organizationId);
|
||||||
void save(UserOrganization userOrganization);
|
void save(UserOrganization userOrganization);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,16 @@ public class GetUserProfileUseCase {
|
||||||
List<UserOrganization> userOrganizations = userRepository.findUserOrganizations(user.getId());
|
List<UserOrganization> userOrganizations = userRepository.findUserOrganizations(user.getId());
|
||||||
|
|
||||||
List<UserProfileOutput.OrganizationProfileOutput> orgs = userOrganizations.stream()
|
List<UserProfileOutput.OrganizationProfileOutput> orgs = userOrganizations.stream()
|
||||||
.collect(Collectors.groupingBy(uo -> uo.getOrganization().getName()))
|
.collect(Collectors.groupingBy(uo -> uo.getOrganization().getId()))
|
||||||
.entrySet().stream()
|
.entrySet().stream()
|
||||||
.map(entry -> new UserProfileOutput.OrganizationProfileOutput(
|
.map(entry -> {
|
||||||
|
UserOrganization firstUo = entry.getValue().get(0);
|
||||||
|
return new UserProfileOutput.OrganizationProfileOutput(
|
||||||
entry.getKey(),
|
entry.getKey(),
|
||||||
|
firstUo.getOrganization().getName(),
|
||||||
entry.getValue().stream().map(uo -> uo.getRole().name()).collect(Collectors.toList())
|
entry.getValue().stream().map(uo -> uo.getRole().name()).collect(Collectors.toList())
|
||||||
))
|
);
|
||||||
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
return new UserProfileOutput(
|
return new UserProfileOutput(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ public record UserProfileOutput(
|
||||||
List<OrganizationProfileOutput> organizations
|
List<OrganizationProfileOutput> organizations
|
||||||
) {
|
) {
|
||||||
public record OrganizationProfileOutput(
|
public record OrganizationProfileOutput(
|
||||||
|
Long id,
|
||||||
String name,
|
String name,
|
||||||
List<String> roles
|
List<String> roles
|
||||||
) {}
|
) {}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue