Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: internal role controller incl. search and tests #15

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
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.tkit.onecx.iam.kc.rs.internal.controllers;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.core.Response;

import org.jboss.resteasy.reactive.RestResponse;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
import org.tkit.onecx.iam.kc.domain.service.KeycloakAdminService;
import org.tkit.onecx.iam.kc.domain.service.KeycloakException;
import org.tkit.onecx.iam.kc.rs.internal.mappers.ExceptionMapper;
import org.tkit.onecx.iam.kc.rs.internal.mappers.RoleMapper;
import org.tkit.quarkus.log.cdi.LogService;
import org.tkit.quarkus.rs.context.token.TokenException;

import gen.org.tkit.onecx.iam.kc.internal.RolesInternalApi;
import gen.org.tkit.onecx.iam.kc.internal.model.ProblemDetailResponseDTO;
import gen.org.tkit.onecx.iam.kc.internal.model.RoleSearchCriteriaDTO;

@LogService
@ApplicationScoped
public class RolesRestController implements RolesInternalApi {

@Inject
KeycloakAdminService adminService;

@Inject
RoleMapper mapper;

@Inject
ExceptionMapper exceptionMapper;

@Override
public Response searchRolesByCriteria(RoleSearchCriteriaDTO roleSearchCriteriaDTO) {
var criteria = mapper.map(roleSearchCriteriaDTO);
var result = adminService.searchRoles(criteria);
return Response.ok(mapper.map(result)).build();
}

@ServerExceptionMapper
public RestResponse<ProblemDetailResponseDTO> constraint(TokenException ex) {
return exceptionMapper.exception(ex.getKey(), ex.getMessage());
}

@ServerExceptionMapper
public RestResponse<ProblemDetailResponseDTO> constraint(KeycloakException ex) {
return exceptionMapper.exception(ex);
}

@ServerExceptionMapper
public RestResponse<ProblemDetailResponseDTO> constraint(ConstraintViolationException ex) {
return exceptionMapper.constraint(ex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import org.tkit.quarkus.log.cdi.LogParam;

import gen.org.tkit.onecx.iam.kc.internal.model.RoleSearchCriteriaDTO;
import gen.org.tkit.onecx.iam.kc.internal.model.UserSearchCriteriaDTO;

@ApplicationScoped
Expand All @@ -17,6 +18,9 @@ public List<Item> getClasses() {
item(10, UserSearchCriteriaDTO.class, x -> {
UserSearchCriteriaDTO d = (UserSearchCriteriaDTO) x;
return UserSearchCriteriaDTO.class.getSimpleName() + "[" + d.getPageNumber() + "," + d.getPageSize() + "]";
}), item(10, RoleSearchCriteriaDTO.class, x -> {
RoleSearchCriteriaDTO d = (RoleSearchCriteriaDTO) x;
return RoleSearchCriteriaDTO.class.getSimpleName() + "[" + d.getPageNumber() + "," + d.getPageSize() + "]";
}));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.tkit.onecx.iam.kc.rs.internal.mappers;

import org.keycloak.representations.idm.RoleRepresentation;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.tkit.onecx.iam.kc.domain.model.PageResult;
import org.tkit.onecx.iam.kc.domain.model.RoleSearchCriteria;

import gen.org.tkit.onecx.iam.kc.internal.model.RolePageResultDTO;
import gen.org.tkit.onecx.iam.kc.internal.model.RoleSearchCriteriaDTO;

@Mapper
public interface RoleMapper {
RoleSearchCriteria map(RoleSearchCriteriaDTO roleSearchCriteriaDTO);

@Mapping(target = "removeStreamItem", ignore = true)
RolePageResultDTO map(PageResult<RoleRepresentation> result);
}
67 changes: 67 additions & 0 deletions src/main/openapi/onecx-iam-kc-svc-internal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,31 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetailResponse'
/internal/roles/search:
post:
tags:
- RolesInternal
summary: Search roles by criteria
operationId: searchRolesByCriteria
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RoleSearchCriteria'
responses:
200:
description: OK
content:
"application/json":
schema:
$ref: "#/components/schemas/RolePageResult"
400:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetailResponse'
components:
schemas:
UserSearchCriteria:
Expand Down Expand Up @@ -103,6 +128,48 @@ components:
type: array
items:
$ref: '#/components/schemas/ProblemDetailInvalidParam'
RoleSearchCriteria:
type: object
properties:
name:
type: string
pageNumber:
format: int32
description: The number of page.
default: 0
type: integer
pageSize:
format: int32
description: The size of page
default: 100
type: integer
RolePageResult:
type: object
properties:
totalElements:
format: int64
description: The total elements in the resource.
type: integer
number:
format: int32
type: integer
size:
format: int32
type: integer
totalPages:
format: int64
type: integer
stream:
type: array
items:
$ref: '#/components/schemas/Role'
Role:
type: object
properties:
name:
type: string
description:
type: string
ProblemDetailParam:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.tkit.onecx.iam.kc.rs.internal.controllers;

import io.quarkus.test.junit.QuarkusIntegrationTest;

@QuarkusIntegrationTest
public class RolesRestControllerIT extends RolesRestControllerTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package org.tkit.onecx.iam.kc.rs.internal.controllers;

import static io.restassured.RestAssured.given;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.assertj.core.api.Assertions.assertThat;
import static org.tkit.onecx.iam.kc.rs.internal.mappers.ExceptionMapper.ErrorKeys.TOKEN_ERROR;
import static org.tkit.quarkus.rs.context.token.TokenParserService.ErrorKeys.ERROR_PARSE_TOKEN;

import jakarta.ws.rs.core.Response;

import org.junit.jupiter.api.Test;
import org.tkit.onecx.iam.kc.rs.internal.mappers.ExceptionMapper;
import org.tkit.onecx.iam.kc.test.AbstractTest;

import gen.org.tkit.onecx.iam.kc.internal.model.ProblemDetailResponseDTO;
import gen.org.tkit.onecx.iam.kc.internal.model.RoleDTO;
import gen.org.tkit.onecx.iam.kc.internal.model.RolePageResultDTO;
import gen.org.tkit.onecx.iam.kc.internal.model.RoleSearchCriteriaDTO;
import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.keycloak.client.KeycloakTestClient;

@QuarkusTest
@TestHTTPEndpoint(RolesRestController.class)
class RolesRestControllerTest extends AbstractTest {

private static final KeycloakTestClient keycloakClient = new KeycloakTestClient();

@Test
void roleSearchNoTokenTest() {

var exception = given()
.contentType(APPLICATION_JSON)
.body(new RoleSearchCriteriaDTO())
.post()
.then()
.statusCode(Response.Status.BAD_REQUEST.getStatusCode())
.extract()
.body().as(ProblemDetailResponseDTO.class);

assertThat(exception).isNotNull();
assertThat(exception.getErrorCode()).isNotNull().isEqualTo(TOKEN_ERROR.name());
assertThat(exception.getDetail()).isNotNull().isEqualTo("Principal token is required");
assertThat(exception.getInvalidParams()).isNull();

exception = given()
.contentType(APPLICATION_JSON)
.header(APM_HEADER_TOKEN, " ")
.body(new RoleSearchCriteriaDTO())
.post()
.then()
.statusCode(Response.Status.BAD_REQUEST.getStatusCode())
.extract()
.body().as(ProblemDetailResponseDTO.class);

assertThat(exception).isNotNull();
assertThat(exception.getErrorCode()).isNotNull().isEqualTo(ERROR_PARSE_TOKEN.name());
assertThat(exception.getDetail()).isNotNull().isEqualTo("Error parse raw token");
assertThat(exception.getInvalidParams()).isNull();

}

@Test
void roleSearchNoBodyTest() {

var exception = given()
.contentType(APPLICATION_JSON)
.header(APM_HEADER_TOKEN, keycloakClient.getAccessToken(USER_BOB))
.post()
.then()
.statusCode(Response.Status.BAD_REQUEST.getStatusCode())
.extract()
.body().as(ProblemDetailResponseDTO.class);

assertThat(exception).isNotNull();
assertThat(exception.getErrorCode()).isNotNull().isEqualTo(ExceptionMapper.ErrorKeys.CONSTRAINT_VIOLATIONS.name());
assertThat(exception.getDetail()).isNotNull()
.isEqualTo("searchRolesByCriteria.roleSearchCriteriaDTO: must not be null");
assertThat(exception.getInvalidParams()).isNotNull().isNotEmpty();
}

@Test
void roleSearchEmptyResultTest() {

var result = given()
.contentType(APPLICATION_JSON)
.header(APM_HEADER_TOKEN, keycloakClient.getAccessToken(USER_BOB))
.body(new RoleSearchCriteriaDTO().name("does-not-exists"))
.post()
.then()
.statusCode(Response.Status.OK.getStatusCode())
.extract()
.body().as(RolePageResultDTO.class);

assertThat(result).isNotNull();
assertThat(result.getStream()).isNotNull().isEmpty();

}

@Test
void roleSearchAllTest() {

var result = given()
.contentType(APPLICATION_JSON)
.header(APM_HEADER_TOKEN, keycloakClient.getAccessToken(USER_BOB))
.body(new RoleSearchCriteriaDTO())
.post()
.then()
.statusCode(Response.Status.OK.getStatusCode())
.extract()
.body().as(RolePageResultDTO.class);

assertThat(result).isNotNull();
assertThat(result.getStream()).isNotNull().isNotEmpty().hasSize(8);
}

@Test
void roleSearchTest() {

var result = given()
.contentType(APPLICATION_JSON)
.header(APM_HEADER_TOKEN, keycloakClient.getAccessToken(USER_BOB))
.body(new RoleSearchCriteriaDTO().name("onecx-admin"))
.post()
.then()
.statusCode(Response.Status.OK.getStatusCode())
.extract()
.body().as(RolePageResultDTO.class);

assertThat(result).isNotNull();
assertThat(result.getStream()).isNotNull().isNotEmpty()
.hasSize(1).contains(new RoleDTO().name("onecx-admin"));

result = given()
.contentType(APPLICATION_JSON)
.header(APM_HEADER_TOKEN, keycloakClient.getAccessToken(USER_BOB))
.body(new RoleSearchCriteriaDTO().name("onecx"))
.post()
.then()
.statusCode(Response.Status.OK.getStatusCode())
.extract()
.body().as(RolePageResultDTO.class);

assertThat(result).isNotNull();
assertThat(result.getStream()).isNotNull().isNotEmpty()
.hasSize(5).contains(
new RoleDTO().name("onecx-admin"),
new RoleDTO().name("onecx-portal-admin"),
new RoleDTO().name("onecx-portal-super-admin"),
new RoleDTO().name("onecx-test"),
new RoleDTO().name("onecx-user"));
}
}
Loading