Skip to content

Commit

Permalink
feat: added bulk create and delete assignments + optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
JordenReuter committed Feb 28, 2024
1 parent 8a25ca3 commit 0c5ebe9
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package org.tkit.onecx.permission.domain.daos;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.NoResultException;
import jakarta.persistence.criteria.Predicate;
import jakarta.transaction.Transactional;

import org.tkit.onecx.permission.domain.criteria.AssignmentSearchCriteria;
import org.tkit.onecx.permission.domain.models.Assignment;
import org.tkit.onecx.permission.domain.models.Assignment_;
import org.tkit.onecx.permission.domain.models.Permission_;
import org.tkit.onecx.permission.domain.models.Role_;
import org.tkit.quarkus.jpa.daos.AbstractDAO;
import org.tkit.quarkus.jpa.daos.Page;
import org.tkit.quarkus.jpa.daos.PageResult;
Expand Down Expand Up @@ -59,6 +62,24 @@ public PageResult<Assignment> findByCriteria(AssignmentSearchCriteria criteria)
}
}

public void deleteByRoleAndPermissionId(String roleId, List<String> permissionId) {
var cb = getEntityManager().getCriteriaBuilder();
var dq = this.deleteQuery();
var root = dq.from(Assignment.class);

List<Predicate> predicates = new ArrayList<>();

predicates.add(cb.equal(root.get(Assignment_.ROLE).get(Role_.ID), roleId));

if (permissionId != null) {
predicates.add(root.get(Assignment_.PERMISSION).get(Permission_.ID).in(permissionId));
}

dq.where(cb.and(predicates.toArray(new Predicate[0])));

this.getEntityManager().createQuery(dq).executeUpdate();
}

public enum ErrorKeys {

FIND_ENTITY_BY_ID_FAILED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ public List<Permission> loadByAppId(String appId) {
}
}

public List<Permission> loadByAppIds(List<String> appId) {
try {
var cb = this.getEntityManager().getCriteriaBuilder();
var cq = cb.createQuery(Permission.class);
var root = cq.from(Permission.class);
cq.where(root.get(Permission_.APP_ID).in(appId));
return this.getEntityManager().createQuery(cq).getResultList();
} catch (Exception ex) {
throw new DAOException(ErrorKeys.ERROR_LOAD_BY_APP_ID, ex);
}
}

public List<Permission> findPermissionForUser(String appId, List<String> roles) {
try {
var cb = this.getEntityManager().getCriteriaBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.tkit.onecx.permission.rs.internal.controllers;

import java.util.List;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
Expand All @@ -15,11 +18,12 @@
import org.tkit.onecx.permission.rs.internal.mappers.AssignmentMapper;
import org.tkit.onecx.permission.rs.internal.mappers.ExceptionMapper;
import org.tkit.quarkus.jpa.exceptions.ConstraintException;
import org.tkit.quarkus.jpa.models.TraceableEntity;
import org.tkit.quarkus.log.cdi.LogService;

import gen.org.tkit.onecx.permission.rs.internal.AssignmentInternalApi;
import gen.org.tkit.onecx.permission.rs.internal.model.AssignmentSearchCriteriaDTO;
import gen.org.tkit.onecx.permission.rs.internal.model.CreateAssignmentRequestDTO;
import gen.org.tkit.onecx.permission.rs.internal.model.CreateRevokeAssignmentRequestDTO;
import gen.org.tkit.onecx.permission.rs.internal.model.ProblemDetailResponseDTO;

@LogService
Expand Down Expand Up @@ -53,6 +57,39 @@ public Response getAssignment(String id) {
return Response.ok(mapper.map(data)).build();
}

@Override
@Transactional
public Response revokeAssignments(CreateRevokeAssignmentRequestDTO createRevokeAssignmentRequestDTO) {
var role = roleDAO.findById(createRevokeAssignmentRequestDTO.getRoleId());
if (role == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}

// case 1 ONLY ROLE ID
if (createRevokeAssignmentRequestDTO.getPermissionId() == null && createRevokeAssignmentRequestDTO.getAppId() == null) {
dao.deleteByRoleAndPermissionId(role.getId(), null);
}

// case 2 ROLE ID + APP ID
if (createRevokeAssignmentRequestDTO.getAppId() != null) {
var permissions = permissionDAO.loadByAppIds(createRevokeAssignmentRequestDTO.getAppId());
if (permissions.isEmpty()) {
return Response.status(Response.Status.NOT_FOUND).build();
}
dao.deleteByRoleAndPermissionId(role.getId(), permissions.stream().map(TraceableEntity::getId).toList());
}

// case 3 ROLE ID + permissionID
if (createRevokeAssignmentRequestDTO.getPermissionId() != null) {
var permission = permissionDAO.findById(createRevokeAssignmentRequestDTO.getPermissionId());
if (permission == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
dao.deleteByRoleAndPermissionId(role.getId(), List.of(permission.getId()));
}
return Response.status(Response.Status.NO_CONTENT).build();
}

@Override
public Response searchAssignments(AssignmentSearchCriteriaDTO assignmentSearchCriteriaDTO) {
var criteria = mapper.map(assignmentSearchCriteriaDTO);
Expand All @@ -61,22 +98,42 @@ public Response searchAssignments(AssignmentSearchCriteriaDTO assignmentSearchCr
}

@Override
public Response createAssignment(CreateAssignmentRequestDTO createAssignmentRequestDTO) {
@Transactional
public Response createAssignment(CreateRevokeAssignmentRequestDTO createAssignmentRequestDTO) {
var role = roleDAO.findById(createAssignmentRequestDTO.getRoleId());
if (role == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
var permission = permissionDAO.findById(createAssignmentRequestDTO.getPermissionId());
if (permission == null) {
return Response.status(Response.Status.NOT_FOUND).build();

// single assignment
if (createAssignmentRequestDTO.getPermissionId() != null) {
var permission = permissionDAO.findById(createAssignmentRequestDTO.getPermissionId());
if (permission == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}

var data = mapper.create(role, permission);
data = dao.create(data);
return Response
.created(uriInfo.getAbsolutePathBuilder().path(data.getId()).build())
.entity(mapper.mapResponseList(null, data))
.build();
}

// batch operation for all permissions by appId
if (createAssignmentRequestDTO.getAppId() != null) {
var permissions = permissionDAO.loadByAppIds(createAssignmentRequestDTO.getAppId());
if (permissions.isEmpty()) {
return Response.status(Response.Status.NOT_FOUND).build();
}
var data = mapper.createList(role, permissions);

dao.deleteByRoleAndPermissionId(role.getId(), null);
var result = dao.create(data).toList();
return Response.status(Response.Status.CREATED).entity(mapper.mapResponseList(result, null)).build();
}

var data = mapper.create(role, permission);
data = dao.create(data);
return Response
.created(uriInfo.getAbsolutePathBuilder().path(data.getId()).build())
.entity(mapper.map(data))
.build();
return Response.status(Response.Status.NOT_FOUND).build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@ public List<Item> getClasses() {
return RoleSearchCriteriaDTO.class.getSimpleName() + "[" + d.getPageNumber() + "," + d.getPageSize()
+ "]";
}),
item(10, CreateAssignmentRequestDTO.class, x -> {
CreateAssignmentRequestDTO d = (CreateAssignmentRequestDTO) x;
return CreateAssignmentRequestDTO.class.getSimpleName() + ":r=" + d.getRoleId() + ",p="
+ d.getPermissionId();
item(10, CreateRevokeAssignmentRequestDTO.class, x -> {
CreateRevokeAssignmentRequestDTO d = (CreateRevokeAssignmentRequestDTO) x;
return CreateRevokeAssignmentRequestDTO.class.getSimpleName() + ":r=" + d.getRoleId();
}),
item(10, CreateRolesRequestDTO.class,
x -> x.getClass().getSimpleName() + ": size: " + ((CreateRolesRequestDTO) x).getRoles().size()),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.tkit.onecx.permission.rs.internal.mappers;

import java.util.ArrayList;
import java.util.List;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.tkit.onecx.permission.domain.criteria.AssignmentSearchCriteria;
Expand All @@ -12,6 +15,7 @@
import gen.org.tkit.onecx.permission.rs.internal.model.AssignmentDTO;
import gen.org.tkit.onecx.permission.rs.internal.model.AssignmentPageResultDTO;
import gen.org.tkit.onecx.permission.rs.internal.model.AssignmentSearchCriteriaDTO;
import gen.org.tkit.onecx.permission.rs.internal.model.CreateAssignmentResponseDTO;

@Mapper(uses = { OffsetDateTimeMapper.class })
public interface AssignmentMapper {
Expand All @@ -36,4 +40,22 @@ public interface AssignmentMapper {

@Mapping(target = "appId", source = "permission.appId")
AssignmentDTO map(Assignment data);

default List<Assignment> createList(Role role, List<Permission> permissions) {
List<Assignment> assignments = new ArrayList<>();
permissions.forEach(permission -> assignments.add(create(role, permission)));
return assignments;
}

default CreateAssignmentResponseDTO mapResponseList(List<Assignment> assignmentList, Assignment singleAssignment) {
CreateAssignmentResponseDTO responseDTO = new CreateAssignmentResponseDTO();
if (assignmentList != null) {
responseDTO.setAssignments(mapList(assignmentList));
} else {
responseDTO.setAssignments(List.of(map(singleAssignment)));
}
return responseDTO;
}

List<AssignmentDTO> mapList(List<Assignment> data);
}
47 changes: 37 additions & 10 deletions src/main/openapi/onecx-permission-internal-openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateAssignmentRequest'
$ref: '#/components/schemas/CreateRevokeAssignmentRequest'
responses:
201:
description: New assignment created
Expand All @@ -62,7 +62,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Assignment'
$ref: '#/components/schemas/CreateAssignmentResponse'
404:
description: Data not found
400:
Expand All @@ -71,6 +71,27 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetailResponse'
/internal/assignments/revoke:
post:
tags:
- assignmentInternal
description: delete assignments by criteria
operationId: revokeAssignments
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateRevokeAssignmentRequest'
responses:
204:
description: New assignment created
400:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetailResponse'
/internal/assignments/{id}:
get:
tags:
Expand Down Expand Up @@ -121,12 +142,6 @@ paths:
responses:
201:
description: New role created
headers:
Location:
required: true
schema:
type: string
format: url
content:
application/json:
schema:
Expand Down Expand Up @@ -318,16 +333,26 @@ components:
type: array
items:
$ref: '#/components/schemas/Assignment'
CreateAssignmentRequest:
CreateRevokeAssignmentRequest:
type: object
required:
- roleId
- permissionId
properties:
roleId:
type: string
permissionId:
type: string
appId:
type: array
items:
type: string
CreateAssignmentResponse:
type: object
properties:
assignments:
type: array
items:
$ref: '#/components/schemas/Assignment'
Assignment:
type: object
properties:
Expand All @@ -348,6 +373,8 @@ components:
type: string
appId:
type: string
id:
type: string
UpdateRoleRequest:
type: object
required:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ void methodExceptionTests() {
PermissionDAO.ErrorKeys.ERROR_FIND_PERMISSION_FOR_USER);
methodExceptionTests(() -> dao.loadByAppId(null),
PermissionDAO.ErrorKeys.ERROR_LOAD_BY_APP_ID);
methodExceptionTests(() -> dao.loadByAppIds(null),
PermissionDAO.ErrorKeys.ERROR_LOAD_BY_APP_ID);
methodExceptionTests(() -> dao.findByCriteria(null),
PermissionDAO.ErrorKeys.ERROR_FIND_PERMISSION_BY_CRITERIA);
}
Expand Down
Loading

0 comments on commit 0c5ebe9

Please sign in to comment.