diff --git a/.github/changelog.yaml b/.github/changelog.yaml
new file mode 100644
index 0000000..0a5526e
--- /dev/null
+++ b/.github/changelog.yaml
@@ -0,0 +1,13 @@
+sections:
+ - title: Major changes
+ labels:
+ - "release/super-feature"
+ - title: Complete changelog
+ labels:
+ - "bug"
+ - "enhancement"
+ - "dependencies"
+template: |
+ {{ range $section := .Sections }}{{ if $section.Items }}### {{ $section.GetTitle }}{{ range $item := $section.Items }}
+ * [#{{ $item.GetID }}]({{ $item.GetURL }}) - {{ $item.GetTitle }}{{ end }}{{ end }}
+ {{ end }}
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..64412da
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,8 @@
+version: 2
+updates:
+ - package-ecosystem: maven
+ directory: "/"
+ schedule:
+ interval: daily
+ labels:
+ - dependencies
\ No newline at end of file
diff --git a/.github/workflows/build-branch.yml b/.github/workflows/build-branch.yml
new file mode 100644
index 0000000..7270213
--- /dev/null
+++ b/.github/workflows/build-branch.yml
@@ -0,0 +1,13 @@
+name: Build Feature Branch
+
+on:
+ push:
+ branches:
+ - '**'
+ - '!main'
+ - '!fix/[0-9]+.[0-9]+.x'
+
+jobs:
+ branch:
+ uses: onecx/ci-quarkus/.github/workflows/build-branch.yml@v1
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml
new file mode 100644
index 0000000..acff155
--- /dev/null
+++ b/.github/workflows/build-pr.yml
@@ -0,0 +1,9 @@
+name: Build Pull Request
+
+on:
+ pull_request:
+
+jobs:
+ pr:
+ uses: onecx/ci-quarkus/.github/workflows/build-pr.yml@v1
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml
new file mode 100644
index 0000000..b04a59a
--- /dev/null
+++ b/.github/workflows/build-release.yml
@@ -0,0 +1,9 @@
+name: Build Release
+on:
+ push:
+ tags:
+ - '**'
+jobs:
+ release:
+ uses: onecx/ci-quarkus/.github/workflows/build-release.yml@v1
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..efe79be
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,23 @@
+name: Build
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - 'main'
+ - 'fix/[0-9]+.[0-9]+.x'
+ paths-ignore:
+ - '.gitignore'
+ - 'CODEOWNERS'
+ - 'LICENSE'
+ - '*.md'
+ - '*.adoc'
+ - '*.txt'
+ - '.all-contributorsrc'
+
+jobs:
+ build:
+ uses: onecx/ci-quarkus/.github/workflows/build.yml@v1
+ secrets: inherit
+ with:
+ helmEventTargetRepository: onecx/onecx-permission
diff --git a/.github/workflows/create-fix-branch.yml b/.github/workflows/create-fix-branch.yml
new file mode 100644
index 0000000..92af624
--- /dev/null
+++ b/.github/workflows/create-fix-branch.yml
@@ -0,0 +1,7 @@
+name: Create Fix Branch
+on:
+ workflow_dispatch:
+jobs:
+ fix:
+ uses: onecx/ci-common/.github/workflows/create-fix-branch.yml@v1
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml
new file mode 100644
index 0000000..c97eb42
--- /dev/null
+++ b/.github/workflows/create-release.yml
@@ -0,0 +1,7 @@
+name: Create Release Version
+on:
+ workflow_dispatch:
+jobs:
+ release:
+ uses: onecx/ci-common/.github/workflows/create-release.yml@v1
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
new file mode 100644
index 0000000..e43d7dc
--- /dev/null
+++ b/.github/workflows/documentation.yml
@@ -0,0 +1,10 @@
+name: Update documentation
+on:
+ push:
+ branches: [ main ]
+ paths:
+ - 'docs/**'
+jobs:
+ release:
+ uses: onecx/ci-common/.github/workflows/documentation.yml@v1
+ secrets: inherit
diff --git a/.github/workflows/sonar-pr.yml b/.github/workflows/sonar-pr.yml
new file mode 100644
index 0000000..02c3e1e
--- /dev/null
+++ b/.github/workflows/sonar-pr.yml
@@ -0,0 +1,12 @@
+name: Sonar Pull Request
+
+on:
+ workflow_run:
+ workflows: ["Build Pull Request"]
+ types:
+ - completed
+
+jobs:
+ pr:
+ uses: onecx/ci-quarkus/.github/workflows/quarkus-pr-sonar.yml@v1
+ secrets: inherit
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b2b2d1f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,44 @@
+#Maven
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+release.properties
+.flattened-pom.xml
+
+# Eclipse
+.project
+.classpath
+.settings/
+bin/
+
+# IntelliJ
+.idea
+*.ipr
+*.iml
+*.iws
+
+# NetBeans
+nb-configuration.xml
+
+# Visual Studio Code
+.vscode
+.factorypath
+
+# OSX
+.DS_Store
+
+# Vim
+*.swp
+*.swo
+
+# patch
+*.orig
+*.rej
+
+# Local environment
+.env
+
+# Plugin directory
+/.quarkus/cli/plugins/
+
diff --git a/README.md b/README.md
index 37fc34f..542678e 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,9 @@
# onecx-permission-svc
+
OneCx permission service
+
+## Configuration
+
+```properties
+
+```
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..775eacd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,190 @@
+
+
+
+ 4.0.0
+
+
+ io.github.onecx
+ onecx-quarkus3-parent
+ 0.25.0
+
+
+ onecx-permission-svc
+ 999-SNAPSHOT
+
+
+
+ io.github.onecx.quarkus
+ onecx-core
+
+
+ io.github.onecx.quarkus
+ onecx-tenant
+
+
+
+ org.tkit.quarkus.lib
+ tkit-quarkus-rest-context
+
+
+ org.tkit.quarkus.lib
+ tkit-quarkus-jpa-tenant
+
+
+ org.tkit.quarkus.lib
+ tkit-quarkus-jpa
+
+
+ org.tkit.quarkus.lib
+ tkit-quarkus-log-cdi
+
+
+ org.tkit.quarkus.lib
+ tkit-quarkus-log-rs
+
+
+ org.tkit.quarkus.lib
+ tkit-quarkus-log-json
+
+
+ org.tkit.quarkus.lib
+ tkit-quarkus-rest
+
+
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-liquibase
+
+
+ com.github.blagerweij
+ liquibase-sessionlock
+
+
+ io.quarkus
+ quarkus-smallrye-health
+
+
+ io.quarkus
+ quarkus-micrometer-registry-prometheus
+
+
+ io.quarkus
+ quarkus-hibernate-orm
+
+
+ io.quarkus
+ quarkus-resteasy-reactive
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson
+
+
+ io.quarkus
+ quarkus-jdbc-postgresql
+
+
+ io.quarkus
+ quarkus-smallrye-context-propagation
+
+
+ io.quarkus
+ quarkus-smallrye-openapi
+
+
+ io.quarkus
+ quarkus-hibernate-validator
+
+
+ io.quarkus
+ quarkus-opentelemetry
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+ org.mapstruct
+ mapstruct
+
+
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.quarkus
+ quarkus-junit5-mockito
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ org.tkit.quarkus.lib
+ tkit-quarkus-test-db-import
+ test
+
+
+
+
+
+
+ org.openapitools
+ openapi-generator-maven-plugin
+
+ jaxrs-spec
+ DTO
+ false
+ false
+ false
+ false
+ false
+ true
+ quarkus
+
+ /
+ false
+ true
+ true
+ true
+ true
+ true
+ java8
+ true
+ true
+ false
+ true
+
+
+
+
+ internal
+
+ generate
+
+
+ src/main/openapi/onecx-permission-internal-openapi.yaml
+ gen.io.github.onecx.permission.rs.internal
+ gen.io.github.onecx.permission.rs.internal.model
+ DTO
+
+
+
+
+
+
+
+
diff --git a/src/main/docker/Dockerfile.jvm b/src/main/docker/Dockerfile.jvm
new file mode 100644
index 0000000..a554421
--- /dev/null
+++ b/src/main/docker/Dockerfile.jvm
@@ -0,0 +1,13 @@
+FROM registry.access.redhat.com/ubi9/openjdk-17:1.15
+
+ENV LANGUAGE='en_US:en'
+
+COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
+COPY --chown=185 target/quarkus-app/*.jar /deployments/
+COPY --chown=185 target/quarkus-app/app/ /deployments/app/
+COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
+
+EXPOSE 8080
+USER 185
+ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
\ No newline at end of file
diff --git a/src/main/docker/Dockerfile.native b/src/main/docker/Dockerfile.native
new file mode 100644
index 0000000..c97f217
--- /dev/null
+++ b/src/main/docker/Dockerfile.native
@@ -0,0 +1,11 @@
+FROM registry.access.redhat.com/ubi9/ubi-minimal:9.2
+WORKDIR /work/
+RUN chown 1001 /work \
+ && chmod "g+rwX" /work \
+ && chown 1001:root /work
+COPY --chown=1001:root target/*-runner /work/application
+
+EXPOSE 8080
+USER 1001
+
+CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
\ No newline at end of file
diff --git a/src/main/helm/Chart.yaml b/src/main/helm/Chart.yaml
new file mode 100644
index 0000000..2af1aab
--- /dev/null
+++ b/src/main/helm/Chart.yaml
@@ -0,0 +1,17 @@
+apiVersion: v2
+name: onecx-permission-svc
+version: 0.0.0
+appVersion: 0.0.0
+description: Onecx permission service
+keywords:
+ - permission
+sources:
+ - https://github.com/onecx/onecx-permission-svc
+maintainers:
+ - name: Tkit Developer
+ email: tkit_dev@1000kit.org
+dependencies:
+ - name: helm-quarkus-app
+ alias: app
+ version: ^0
+ repository: oci://ghcr.io/onecx/charts
\ No newline at end of file
diff --git a/src/main/helm/values.yaml b/src/main/helm/values.yaml
new file mode 100644
index 0000000..44d921d
--- /dev/null
+++ b/src/main/helm/values.yaml
@@ -0,0 +1,6 @@
+app:
+ name: svc
+ image:
+ repository: "onecx/onecx-permission-svc"
+ db:
+ enabled: true
\ No newline at end of file
diff --git a/src/main/java/io/github/onecx/permission/domain/criteria/PermissionSearchCriteria.java b/src/main/java/io/github/onecx/permission/domain/criteria/PermissionSearchCriteria.java
new file mode 100644
index 0000000..254cd09
--- /dev/null
+++ b/src/main/java/io/github/onecx/permission/domain/criteria/PermissionSearchCriteria.java
@@ -0,0 +1,16 @@
+package io.github.onecx.permission.domain.criteria;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class PermissionSearchCriteria {
+
+ private String appId;
+ private String name;
+ private String object;
+ private String action;
+ private Integer pageNumber;
+ private Integer pageSize;
+}
diff --git a/src/main/java/io/github/onecx/permission/domain/daos/PermissionDAO.java b/src/main/java/io/github/onecx/permission/domain/daos/PermissionDAO.java
new file mode 100644
index 0000000..2d548ec
--- /dev/null
+++ b/src/main/java/io/github/onecx/permission/domain/daos/PermissionDAO.java
@@ -0,0 +1,57 @@
+package io.github.onecx.permission.domain.daos;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.persistence.criteria.Predicate;
+
+import org.tkit.quarkus.jpa.daos.AbstractDAO;
+import org.tkit.quarkus.jpa.daos.Page;
+import org.tkit.quarkus.jpa.daos.PageResult;
+import org.tkit.quarkus.jpa.exceptions.DAOException;
+import org.tkit.quarkus.jpa.utils.QueryCriteriaUtil;
+
+import io.github.onecx.permission.domain.criteria.PermissionSearchCriteria;
+import io.github.onecx.permission.domain.models.Permission;
+import io.github.onecx.permission.domain.models.Permission_;
+
+@ApplicationScoped
+public class PermissionDAO extends AbstractDAO {
+
+ public PageResult findByCriteria(PermissionSearchCriteria criteria) {
+ try {
+ var cb = this.getEntityManager().getCriteriaBuilder();
+ var cq = cb.createQuery(Permission.class);
+ var root = cq.from(Permission.class);
+
+ List predicates = new ArrayList<>();
+
+ if (criteria.getName() != null && !criteria.getName().isBlank()) {
+ predicates.add(cb.like(root.get(Permission_.name), QueryCriteriaUtil.wildcard(criteria.getName())));
+ }
+ if (criteria.getAction() != null && !criteria.getAction().isBlank()) {
+ predicates.add(cb.like(root.get(Permission_.action), QueryCriteriaUtil.wildcard(criteria.getAction())));
+ }
+ if (criteria.getAppId() != null && !criteria.getAppId().isBlank()) {
+ predicates.add(cb.like(root.get(Permission_.appId), QueryCriteriaUtil.wildcard(criteria.getAppId())));
+ }
+ if (criteria.getObject() != null && !criteria.getObject().isBlank()) {
+ predicates.add(cb.like(root.get(Permission_.object), QueryCriteriaUtil.wildcard(criteria.getObject())));
+ }
+
+ if (!predicates.isEmpty()) {
+ cq.where(predicates.toArray(new Predicate[] {}));
+ }
+
+ return createPageQuery(cq, Page.of(criteria.getPageNumber(), criteria.getPageSize())).getPageResult();
+ } catch (Exception ex) {
+ throw new DAOException(ErrorKeys.ERROR_FIND_PERMISSION_BY_CRITERIA, ex);
+ }
+ }
+
+ public enum ErrorKeys {
+
+ ERROR_FIND_PERMISSION_BY_CRITERIA;
+ }
+}
diff --git a/src/main/java/io/github/onecx/permission/domain/models/Permission.java b/src/main/java/io/github/onecx/permission/domain/models/Permission.java
new file mode 100644
index 0000000..2c0debf
--- /dev/null
+++ b/src/main/java/io/github/onecx/permission/domain/models/Permission.java
@@ -0,0 +1,49 @@
+package io.github.onecx.permission.domain.models;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import jakarta.persistence.UniqueConstraint;
+
+import org.tkit.quarkus.jpa.models.TraceableEntity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Entity
+@Table(name = "PERMISSION", uniqueConstraints = {
+ @UniqueConstraint(name = "PERMISSION_KEY", columnNames = { "APP_ID", "OBJECT", "ACTION" })
+})
+@SuppressWarnings("squid:S2160")
+public class Permission extends TraceableEntity {
+
+ @Column(name = "APP_ID")
+ private String appId;
+
+ /**
+ * The permission action.
+ */
+ @Column(name = "ACTION")
+ private String action;
+
+ /**
+ * The permission object.
+ */
+ @Column(name = "OBJECT")
+ private String object;
+
+ /**
+ * The permission name.
+ */
+ @Column(name = "NAME")
+ private String name;
+
+ /**
+ * The permission description.
+ */
+ @Column(name = "DESCRIPTION")
+ private String description;
+
+}
diff --git a/src/main/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestController.java b/src/main/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestController.java
new file mode 100644
index 0000000..fe73a2a
--- /dev/null
+++ b/src/main/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestController.java
@@ -0,0 +1,43 @@
+package io.github.onecx.permission.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.quarkus.log.cdi.LogService;
+
+import gen.io.github.onecx.permission.rs.internal.PermissionInternalApi;
+import gen.io.github.onecx.permission.rs.internal.model.PermissionSearchCriteriaDTO;
+import gen.io.github.onecx.permission.rs.internal.model.ProblemDetailResponseDTO;
+import io.github.onecx.permission.domain.daos.PermissionDAO;
+import io.github.onecx.permission.rs.internal.mappers.ExceptionMapper;
+import io.github.onecx.permission.rs.internal.mappers.PermissionMapper;
+
+@LogService
+@ApplicationScoped
+public class PermissionRestController implements PermissionInternalApi {
+
+ @Inject
+ ExceptionMapper exceptionMapper;
+
+ @Inject
+ PermissionMapper mapper;
+
+ @Inject
+ PermissionDAO dao;
+
+ @Override
+ public Response searchPermissions(PermissionSearchCriteriaDTO permissionSearchCriteriaDTO) {
+ var criteria = mapper.map(permissionSearchCriteriaDTO);
+ var result = dao.findByCriteria(criteria);
+ return Response.ok(mapper.map(result)).build();
+ }
+
+ @ServerExceptionMapper
+ public RestResponse constraint(ConstraintViolationException ex) {
+ return exceptionMapper.constraint(ex);
+ }
+}
diff --git a/src/main/java/io/github/onecx/permission/rs/internal/log/InternalLogParam.java b/src/main/java/io/github/onecx/permission/rs/internal/log/InternalLogParam.java
new file mode 100644
index 0000000..9cddc46
--- /dev/null
+++ b/src/main/java/io/github/onecx/permission/rs/internal/log/InternalLogParam.java
@@ -0,0 +1,23 @@
+package io.github.onecx.permission.rs.internal.log;
+
+import java.util.List;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+import org.tkit.quarkus.log.cdi.LogParam;
+
+import gen.io.github.onecx.permission.rs.internal.model.PermissionSearchCriteriaDTO;
+
+@ApplicationScoped
+public class InternalLogParam implements LogParam {
+
+ @Override
+ public List- getClasses() {
+ return List.of(
+ item(10, PermissionSearchCriteriaDTO.class, x -> {
+ PermissionSearchCriteriaDTO d = (PermissionSearchCriteriaDTO) x;
+ return PermissionSearchCriteriaDTO.class.getSimpleName() + "[" + d.getPageNumber() + "," + d.getPageSize()
+ + "]";
+ }));
+ }
+}
diff --git a/src/main/java/io/github/onecx/permission/rs/internal/mappers/ExceptionMapper.java b/src/main/java/io/github/onecx/permission/rs/internal/mappers/ExceptionMapper.java
new file mode 100644
index 0000000..9e3fe83
--- /dev/null
+++ b/src/main/java/io/github/onecx/permission/rs/internal/mappers/ExceptionMapper.java
@@ -0,0 +1,64 @@
+package io.github.onecx.permission.rs.internal.mappers;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+import jakarta.validation.Path;
+import jakarta.ws.rs.core.Response;
+
+import org.jboss.resteasy.reactive.RestResponse;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper;
+
+import gen.io.github.onecx.permission.rs.internal.model.ProblemDetailInvalidParamDTO;
+import gen.io.github.onecx.permission.rs.internal.model.ProblemDetailParamDTO;
+import gen.io.github.onecx.permission.rs.internal.model.ProblemDetailResponseDTO;
+
+@Mapper(uses = { OffsetDateTimeMapper.class })
+public abstract class ExceptionMapper {
+
+ public RestResponse constraint(ConstraintViolationException ex) {
+ var dto = exception(ErrorKeys.CONSTRAINT_VIOLATIONS.name(), ex.getMessage());
+ dto.setInvalidParams(createErrorValidationResponse(ex.getConstraintViolations()));
+ return RestResponse.status(Response.Status.BAD_REQUEST, dto);
+ }
+
+ @Mapping(target = "removeParamsItem", ignore = true)
+ @Mapping(target = "params", ignore = true)
+ @Mapping(target = "invalidParams", ignore = true)
+ @Mapping(target = "removeInvalidParamsItem", ignore = true)
+ public abstract ProblemDetailResponseDTO exception(String errorCode, String detail);
+
+ public List map(Map params) {
+ if (params == null) {
+ return List.of();
+ }
+ return params.entrySet().stream().map(e -> {
+ var item = new ProblemDetailParamDTO();
+ item.setKey(e.getKey());
+ if (e.getValue() != null) {
+ item.setValue(e.getValue().toString());
+ }
+ return item;
+ }).toList();
+ }
+
+ public abstract List createErrorValidationResponse(
+ Set> constraintViolation);
+
+ @Mapping(target = "name", source = "propertyPath")
+ @Mapping(target = "message", source = "message")
+ public abstract ProblemDetailInvalidParamDTO createError(ConstraintViolation> constraintViolation);
+
+ public String mapPath(Path path) {
+ return path.toString();
+ }
+
+ public enum ErrorKeys {
+ CONSTRAINT_VIOLATIONS;
+ }
+}
diff --git a/src/main/java/io/github/onecx/permission/rs/internal/mappers/PermissionMapper.java b/src/main/java/io/github/onecx/permission/rs/internal/mappers/PermissionMapper.java
new file mode 100644
index 0000000..fc576c5
--- /dev/null
+++ b/src/main/java/io/github/onecx/permission/rs/internal/mappers/PermissionMapper.java
@@ -0,0 +1,24 @@
+package io.github.onecx.permission.rs.internal.mappers;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.tkit.quarkus.jpa.daos.PageResult;
+import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper;
+
+import gen.io.github.onecx.permission.rs.internal.model.PermissionDTO;
+import gen.io.github.onecx.permission.rs.internal.model.PermissionPageResultDTO;
+import gen.io.github.onecx.permission.rs.internal.model.PermissionSearchCriteriaDTO;
+import io.github.onecx.permission.domain.criteria.PermissionSearchCriteria;
+import io.github.onecx.permission.domain.models.Permission;
+
+@Mapper(uses = { OffsetDateTimeMapper.class })
+public interface PermissionMapper {
+
+ PermissionSearchCriteria map(PermissionSearchCriteriaDTO dto);
+
+ @Mapping(target = "removeStreamItem", ignore = true)
+ PermissionPageResultDTO map(PageResult page);
+
+ @Mapping(target = "_object", source = "object")
+ PermissionDTO map(Permission data);
+}
diff --git a/src/main/openapi/onecx-permission-internal-openapi.yaml b/src/main/openapi/onecx-permission-internal-openapi.yaml
new file mode 100644
index 0000000..a3a374c
--- /dev/null
+++ b/src/main/openapi/onecx-permission-internal-openapi.yaml
@@ -0,0 +1,139 @@
+---
+openapi: 3.0.3
+info:
+ title: onecx-permission internal service
+ version: 1.0.0
+servers:
+ - url: "http://onecx-permission-svc:8080"
+tags:
+ - name: permissionInternal
+paths:
+ /internal/permissions/search:
+ post:
+ tags:
+ - permissionInternal
+ description: Search for permissions
+ operationId: searchPermissions
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PermissionSearchCriteria'
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/PermissionPageResult'
+ "400":
+ description: Bad request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ProblemDetailResponse'
+components:
+ schemas:
+ PermissionSearchCriteria:
+ type: object
+ properties:
+ appId:
+ type: string
+ name:
+ type: string
+ object:
+ type: string
+ action:
+ type: string
+ pageNumber:
+ format: int32
+ description: The number of page.
+ default: 0
+ type: integer
+ pageSize:
+ format: int32
+ description: The size of page
+ default: 10
+ type: integer
+ PermissionPageResult:
+ 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/Permission'
+ Permission:
+ type: object
+ properties:
+ modificationCount:
+ format: int32
+ type: integer
+ creationDate:
+ $ref: '#/components/schemas/OffsetDateTime'
+ creationUser:
+ type: string
+ modificationDate:
+ $ref: '#/components/schemas/OffsetDateTime'
+ modificationUser:
+ type: string
+ id:
+ type: string
+ appId:
+ type: string
+ name:
+ type: string
+ object:
+ type: string
+ action:
+ type: string
+ description:
+ type: string
+ OffsetDateTime:
+ format: date-time
+ type: string
+ example: 2022-03-10T12:15:50-04:00
+ ProblemDetailResponse:
+ type: object
+ properties:
+ errorCode:
+ type: string
+ detail:
+ type: string
+ params:
+ type: array
+ items:
+ $ref: '#/components/schemas/ProblemDetailParam'
+ invalidParams:
+ type: array
+ items:
+ $ref: '#/components/schemas/ProblemDetailInvalidParam'
+ ProblemDetailParam:
+ type: object
+ properties:
+ key:
+ type: string
+ value:
+ type: string
+ ProblemDetailInvalidParam:
+ type: object
+ properties:
+ name:
+ type: string
+ message:
+ type: string
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..4cfda36
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,44 @@
+# DEFAULT
+quarkus.datasource.db-kind=postgresql
+quarkus.datasource.jdbc.max-size=30
+quarkus.datasource.jdbc.min-size=10
+
+quarkus.hibernate-orm.database.generation=validate
+quarkus.hibernate-orm.multitenant=DISCRIMINATOR
+quarkus.liquibase.migrate-at-start=true
+quarkus.liquibase.validate-on-migrate=true
+
+# enable or disable multi-tenancy support
+tkit.rs.context.tenant-id.enabled=true
+
+# PROD
+%prod.quarkus.datasource.jdbc.url=${DB_URL:jdbc:postgresql://postgresdb:5432/onecx-permission?sslmode=disable}
+%prod.quarkus.datasource.username=${DB_USER:onecx-permission}
+%prod.quarkus.datasource.password=${DB_PWD:onecx-permission}
+# DEV
+%dev.tkit.rs.context.tenant-id.enabled=true
+%dev.tkit.rs.context.tenant-id.mock.enabled=true
+%dev.tkit.rs.context.tenant-id.mock.default-tenant=test
+%dev.tkit.rs.context.tenant-id.mock.data.org1=tenant100
+
+# TEST
+%test.tkit.rs.context.tenant-id.enabled=true
+%test.tkit.rs.context.tenant-id.mock.enabled=true
+%test.tkit.rs.context.tenant-id.mock.default-tenant=default
+%test.tkit.rs.context.tenant-id.mock.claim-org-id=orgId
+%test.tkit.rs.context.tenant-id.mock.token-header-param=apm-principal-token
+%test.tkit.rs.context.tenant-id.mock.data.org1=tenant-100
+%test.tkit.rs.context.tenant-id.mock.data.org2=tenant-200
+
+# TEST-IT
+quarkus.test.integration-test-profile=test-it
+%test-it.tkit.log.json.enabled=false
+%test-it.tkit.rs.context.tenant-id.mock.enabled=true
+%test-it.tkit.rs.context.tenant-id.mock.default-tenant=default
+%test-it.tkit.rs.context.tenant-id.mock.claim-org-id=orgId
+%test-it.tkit.rs.context.tenant-id.mock.token-header-param=apm-principal-token
+%test-it.tkit.rs.context.tenant-id.mock.data.org1=tenant-100
+%test-it.tkit.rs.context.tenant-id.mock.data.org2=tenant-200
+
+# PIPE CONFIG
+
diff --git a/src/main/resources/db/changeLog.xml b/src/main/resources/db/changeLog.xml
new file mode 100644
index 0000000..44c297b
--- /dev/null
+++ b/src/main/resources/db/changeLog.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/db/v1/2024-01-04-create-tables.xml b/src/main/resources/db/v1/2024-01-04-create-tables.xml
new file mode 100644
index 0000000..fee0490
--- /dev/null
+++ b/src/main/resources/db/v1/2024-01-04-create-tables.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/io/github/onecx/permission/domain/daos/PermissionDAOTest.java b/src/test/java/io/github/onecx/permission/domain/daos/PermissionDAOTest.java
new file mode 100644
index 0000000..c3b665c
--- /dev/null
+++ b/src/test/java/io/github/onecx/permission/domain/daos/PermissionDAOTest.java
@@ -0,0 +1,41 @@
+package io.github.onecx.permission.domain.daos;
+
+import jakarta.inject.Inject;
+import jakarta.persistence.EntityManager;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.mockito.Mockito;
+import org.tkit.quarkus.jpa.exceptions.DAOException;
+
+import io.github.onecx.permission.test.AbstractTest;
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+class PermissionDAOTest extends AbstractTest {
+
+ @Inject
+ PermissionDAO dao;
+
+ @InjectMock
+ EntityManager em;
+
+ @BeforeEach
+ void beforeAll() {
+ Mockito.when(em.getCriteriaBuilder()).thenThrow(new RuntimeException("Test technical error exception"));
+ }
+
+ @Test
+ void methodExceptionTests() {
+ methodExceptionTests(() -> dao.findByCriteria(null),
+ PermissionDAO.ErrorKeys.ERROR_FIND_PERMISSION_BY_CRITERIA);
+ }
+
+ void methodExceptionTests(Executable fn, Enum> key) {
+ var exc = Assertions.assertThrows(DAOException.class, fn);
+ Assertions.assertEquals(key, exc.key);
+ }
+}
diff --git a/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerExceptionTest.java b/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerExceptionTest.java
new file mode 100644
index 0000000..2b304f9
--- /dev/null
+++ b/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerExceptionTest.java
@@ -0,0 +1,53 @@
+package io.github.onecx.permission.rs.internal.controllers;
+
+import static io.restassured.RestAssured.given;
+import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.jboss.resteasy.reactive.RestResponse.Status.INTERNAL_SERVER_ERROR;
+import static org.mockito.ArgumentMatchers.any;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.tkit.quarkus.jpa.exceptions.DAOException;
+
+import gen.io.github.onecx.permission.rs.internal.model.PermissionSearchCriteriaDTO;
+import io.github.onecx.permission.domain.daos.PermissionDAO;
+import io.github.onecx.permission.test.AbstractTest;
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+@TestHTTPEndpoint(PermissionRestController.class)
+class PermissionRestControllerExceptionTest extends AbstractTest {
+
+ @InjectMock
+ PermissionDAO dao;
+
+ @BeforeEach
+ void beforeAll() {
+ Mockito.when(dao.findByCriteria(any()))
+ .thenThrow(new RuntimeException("Test technical error exception"))
+ .thenThrow(new DAOException(PermissionDAO.ErrorKeys.ERROR_FIND_PERMISSION_BY_CRITERIA,
+ new RuntimeException("Test")));
+ }
+
+ @Test
+ void exceptionTest() {
+ var criteria = new PermissionSearchCriteriaDTO();
+ given()
+ .contentType(APPLICATION_JSON)
+ .body(criteria)
+ .post()
+ .then()
+ .statusCode(INTERNAL_SERVER_ERROR.getStatusCode());
+
+ given()
+ .contentType(APPLICATION_JSON)
+ .body(criteria)
+ .post()
+ .then()
+ .statusCode(INTERNAL_SERVER_ERROR.getStatusCode());
+
+ }
+}
diff --git a/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerTest.java b/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerTest.java
new file mode 100644
index 0000000..72db785
--- /dev/null
+++ b/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerTest.java
@@ -0,0 +1,101 @@
+package io.github.onecx.permission.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.jboss.resteasy.reactive.RestResponse.Status.BAD_REQUEST;
+import static org.jboss.resteasy.reactive.RestResponse.Status.OK;
+
+import org.junit.jupiter.api.Test;
+import org.tkit.quarkus.test.WithDBData;
+
+import gen.io.github.onecx.permission.rs.internal.model.PermissionPageResultDTO;
+import gen.io.github.onecx.permission.rs.internal.model.PermissionSearchCriteriaDTO;
+import gen.io.github.onecx.permission.rs.internal.model.ProblemDetailResponseDTO;
+import io.github.onecx.permission.rs.internal.mappers.ExceptionMapper;
+import io.github.onecx.permission.test.AbstractTest;
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+@TestHTTPEndpoint(PermissionRestController.class)
+@WithDBData(value = "data/test-internal.xml", deleteBeforeInsert = true, deleteAfterTest = true, rinseAndRepeat = true)
+class PermissionRestControllerTest extends AbstractTest {
+
+ @Test
+ void searchTest() {
+ var criteria = new PermissionSearchCriteriaDTO();
+
+ var data = given()
+ .contentType(APPLICATION_JSON)
+ .body(criteria)
+ .post()
+ .then()
+ .statusCode(OK.getStatusCode())
+ .contentType(APPLICATION_JSON)
+ .extract()
+ .as(PermissionPageResultDTO.class);
+
+ assertThat(data).isNotNull();
+ assertThat(data.getTotalElements()).isEqualTo(7);
+ assertThat(data.getStream()).isNotNull().hasSize(7);
+
+ criteria.setAppId(" ");
+ criteria.setName(" ");
+ criteria.setObject(" ");
+ criteria.setAction(" ");
+
+ data = given()
+ .contentType(APPLICATION_JSON)
+ .body(criteria)
+ .post()
+ .then()
+ .statusCode(OK.getStatusCode())
+ .contentType(APPLICATION_JSON)
+ .extract()
+ .as(PermissionPageResultDTO.class);
+
+ assertThat(data).isNotNull();
+ assertThat(data.getTotalElements()).isEqualTo(7);
+ assertThat(data.getStream()).isNotNull().hasSize(7);
+ }
+
+ @Test
+ void searchCriteriaTest() {
+ var criteria = new PermissionSearchCriteriaDTO();
+ criteria.setAppId("app1");
+ criteria.setName("n1");
+ criteria.setObject("o1");
+ criteria.setAction("a1");
+
+ var data = given()
+ .contentType(APPLICATION_JSON)
+ .body(criteria)
+ .post()
+ .then()
+ .statusCode(OK.getStatusCode())
+ .contentType(APPLICATION_JSON)
+ .extract()
+ .as(PermissionPageResultDTO.class);
+
+ assertThat(data).isNotNull();
+ assertThat(data.getTotalElements()).isEqualTo(1);
+ assertThat(data.getStream()).isNotNull().hasSize(1);
+ }
+
+ @Test
+ void searchNoBodyTest() {
+ var exception = given()
+ .contentType(APPLICATION_JSON)
+ .post()
+ .then()
+ .statusCode(BAD_REQUEST.getStatusCode())
+ .contentType(APPLICATION_JSON)
+ .extract()
+ .as(ProblemDetailResponseDTO.class);
+
+ assertThat(exception).isNotNull();
+ assertThat(exception.getErrorCode()).isEqualTo(ExceptionMapper.ErrorKeys.CONSTRAINT_VIOLATIONS.name());
+ assertThat(exception.getDetail()).isEqualTo("searchPermissions.permissionSearchCriteriaDTO: must not be null");
+ }
+}
diff --git a/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerTestIT.java b/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerTestIT.java
new file mode 100644
index 0000000..199d388
--- /dev/null
+++ b/src/test/java/io/github/onecx/permission/rs/internal/controllers/PermissionRestControllerTestIT.java
@@ -0,0 +1,8 @@
+package io.github.onecx.permission.rs.internal.controllers;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class PermissionRestControllerTestIT extends PermissionRestControllerTest {
+
+}
diff --git a/src/test/java/io/github/onecx/permission/test/AbstractTest.java b/src/test/java/io/github/onecx/permission/test/AbstractTest.java
new file mode 100644
index 0000000..43defa9
--- /dev/null
+++ b/src/test/java/io/github/onecx/permission/test/AbstractTest.java
@@ -0,0 +1,25 @@
+package io.github.onecx.permission.test;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS;
+import static io.restassured.RestAssured.config;
+import static io.restassured.config.ObjectMapperConfig.objectMapperConfig;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import io.restassured.config.RestAssuredConfig;
+
+@SuppressWarnings("java:S2187")
+public class AbstractTest {
+
+ static {
+ config = RestAssuredConfig.config().objectMapperConfig(
+ objectMapperConfig().jackson2ObjectMapperFactory(
+ (cls, charset) -> {
+ var objectMapper = new ObjectMapper();
+ objectMapper.registerModule(new JavaTimeModule());
+ objectMapper.configure(WRITE_DATES_AS_TIMESTAMPS, false);
+ return objectMapper;
+ }));
+ }
+}
diff --git a/src/test/resources/data/test-internal.xml b/src/test/resources/data/test-internal.xml
new file mode 100644
index 0000000..3fc27d3
--- /dev/null
+++ b/src/test/resources/data/test-internal.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file