diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1981ac5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +target \ No newline at end of file diff --git a/README.md b/README.md index b1cd613..62d6a23 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # onecx-parameter-svc + OneCx Parameter Management Service diff --git a/openapi/openapi-internal.yaml b/openapi/openapi-internal.yaml new file mode 100644 index 0000000..a73ec8f --- /dev/null +++ b/openapi/openapi-internal.yaml @@ -0,0 +1,590 @@ +--- +openapi: 3.0.3 +info: + title: onecx-parameters + description: OneCx parameters + version: "2.0" +servers: +- url: http://onecx-parameters-svc:8080/ +tags: +- name: internal +paths: + /histories: + get: + tags: + - internal + description: Find all parameters history + operationId: getAllApplicationParametersHistory + parameters: + - name: applicationId + in: query + schema: + description: The application parameter id. + type: string + - name: key + in: query + schema: + description: The application parameter key. + type: string + - name: pageNumber + in: query + schema: + format: int32 + description: The number of page. + default: "0" + type: integer + - name: pageSize + in: query + schema: + format: int32 + description: The size of page + default: "100" + type: integer + - name: type + in: query + schema: + description: The application parameter type. + type: array + items: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationParameterHistoryPageResultDTO' + "400": + description: Bad request + "500": + description: Internal Server Error + /histories/counts: + get: + tags: + - internal + description: Get creation dates and counts by criteria + operationId: getCountsByCriteria + parameters: + - name: applicationId + in: query + schema: + description: The application parameter id. + type: string + - name: key + in: query + schema: + description: The application parameter key. + type: string + - name: pageNumber + in: query + schema: + format: int32 + description: The number of page. + default: "0" + type: integer + - name: pageSize + in: query + schema: + format: int32 + description: The size of page + default: "100" + type: integer + - name: type + in: query + schema: + description: The application parameter type. + type: array + items: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ParameterHistoryCountDTO' + "400": + description: Bad request + "500": + description: Internal Server Error + /histories/latest: + get: + tags: + - internal + description: Find all parameters history latest + operationId: getAllApplicationParametersHistoryLatest + parameters: + - name: applicationId + in: query + schema: + description: The application parameter id. + type: string + - name: key + in: query + schema: + description: The application parameter key. + type: string + - name: pageNumber + in: query + schema: + format: int32 + description: The number of page. + default: "0" + type: integer + - name: pageSize + in: query + schema: + format: int32 + description: The size of page + default: "100" + type: integer + - name: type + in: query + schema: + description: The application parameter type. + type: array + items: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationParameterHistoryPageResultDTO' + "400": + description: Bad request + "500": + description: Internal Server Error + /histories/{id}: + get: + tags: + - internal + description: Find parameters history by Id + operationId: getApplicationParametersHistoryById + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationParameterHistoryDTO' + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error + /parameters: + get: + tags: + - internal + description: Find all parameters + operationId: getAllApplicationParameters + parameters: + - name: applicationId + in: query + schema: + description: The application parameter id. + type: string + - name: key + in: query + schema: + description: The application parameter key. + type: string + - name: name + in: query + schema: + description: The application parameter name. + type: string + - name: pageNumber + in: query + schema: + format: int32 + description: The number of page. + default: "0" + type: integer + - name: pageSize + in: query + schema: + format: int32 + description: The size of page + default: "100" + type: integer + - name: type + in: query + schema: + description: The application parameter type. + type: object + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationParameterPageResultDTO' + "400": + description: Bad request + "500": + description: Internal Server Error + post: + tags: + - internal + description: Create parameter + operationId: createParameterValue + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationParameterCreateDTO' + responses: + "204": + description: No Content + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/RestExceptionDTO' + "404": + description: Not Found + "500": + description: Internal Server Error + /parameters/applications: + get: + tags: + - internal + description: Find all parameters + operationId: getAllApplications + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationsPageResultDTO' + "400": + description: Bad request + "500": + description: Internal Server Error + /parameters/keys: + get: + tags: + - internal + description: Find all parameters + operationId: getAllKeys + parameters: + - name: applicationId + in: query + schema: + description: The application parameter id. + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/KeysPageResultDTO' + "400": + description: Bad request + "500": + description: Internal Server Error + /parameters/{id}: + get: + tags: + - internal + description: Find parameter by id + operationId: getParameterById + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationParameterDTO' + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error + put: + tags: + - internal + description: Update parameter + operationId: updateParameterValue + parameters: + - name: id + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationParameterUpdateDTO' + responses: + "204": + description: No Content + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/RestExceptionDTO' + "404": + description: Not Found + "500": + description: Internal Server Error + delete: + tags: + - internal + description: Delete parameter + operationId: deleteParameter + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "204": + description: No Content + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/RestExceptionDTO' + "500": + description: Internal Server Error +components: + schemas: + ApplicationParameterCreateDTO: + type: object + properties: + key: + type: string + applicationId: + type: string + value: + type: string + type: + type: string + description: + type: string + unit: + type: string + rangeFrom: + format: int32 + type: integer + rangeTo: + format: int32 + type: integer + ApplicationParameterDTO: + type: object + properties: + id: + type: string + modificationCount: + format: int32 + type: integer + creationDate: + format: date-time + type: string + example: 2022-03-10T12:15:50-04:00 + creationUser: + type: string + modificationDate: + format: date-time + type: string + example: 2022-03-10T12:15:50-04:00 + modificationUser: + type: string + name: + type: string + description: + type: string + applicationId: + type: string + key: + type: string + setValue: + type: string + importValue: + type: string + type: + type: string + unit: + type: string + rangeFrom: + format: int32 + type: integer + rangeTo: + format: int32 + type: integer + ApplicationParameterHistoryDTO: + type: object + properties: + id: + type: string + modificationCount: + format: int32 + type: integer + creationDate: + format: date-time + type: string + example: 2022-03-10T12:15:50-04:00 + creationUser: + type: string + modificationDate: + format: date-time + type: string + example: 2022-03-10T12:15:50-04:00 + modificationUser: + type: string + applicationId: + type: string + key: + type: string + usedValue: + type: string + defaultValue: + type: string + type: + type: string + instanceId: + type: string + ApplicationParameterHistoryPageResultDTO: + 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/ApplicationParameterHistoryDTO' + ApplicationParameterPageResultDTO: + 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/ApplicationParameterDTO' + ApplicationParameterUpdateDTO: + type: object + properties: + value: {} + description: + type: string + unit: + type: string + rangeFrom: + format: int32 + type: integer + rangeTo: + format: int32 + type: integer + ApplicationsPageResultDTO: + 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: + type: string + KeysPageResultDTO: + 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: + type: string + ParameterHistoryCountDTO: + type: object + properties: + creationDate: + format: date-time + type: string + example: 2022-03-10T12:15:50 + count: + format: int64 + type: integer + RestExceptionDTO: + type: object + properties: + errorCode: + type: string + message: + type: string + parameters: + type: array + items: {} + namedParameters: + type: object + additionalProperties: {} diff --git a/openapi/openapi-v2.yaml b/openapi/openapi-v2.yaml new file mode 100644 index 0000000..2501391 --- /dev/null +++ b/openapi/openapi-v2.yaml @@ -0,0 +1,245 @@ +--- +openapi: 3.0.3 +info: + title: onecx-parameters + description: OneCx parameters + version: "2.0" +servers: +- url: http://onecx-parameters-svc:8080/ +tags: +- name: external +paths: + /v2/booleanParameters: + get: + tags: + - external + description: Get application parameter as Boolean by application id and parameter + key + operationId: getBoolean + parameters: + - name: applicationId + in: query + schema: + type: string + - name: parameterKey + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + type: boolean + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error + /v2/integerParameters: + get: + tags: + - external + description: Get application parameter as Integer by application id and parameter + key + operationId: getInteger + parameters: + - name: applicationId + in: query + schema: + type: string + - name: parameterKey + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + format: int32 + type: integer + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error + /v2/longParameters: + get: + tags: + - external + description: Get application parameter as Long by application id and parameter + key + operationId: getLong + parameters: + - name: applicationId + in: query + schema: + type: string + - name: parameterKey + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + format: int64 + type: integer + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error + /v2/parameters: + get: + tags: + - external + description: Get application parameter by application id and parameter key + operationId: getParameter + parameters: + - name: applicationId + in: query + schema: + type: string + - name: parameterKey + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationParameterDTOV2' + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error + post: + tags: + - external + description: Get all application parameters by application id and/or parameter + key + operationId: getAllParameters + parameters: + - name: applicationId + in: query + schema: + type: string + requestBody: + content: + application/json: + schema: + type: array + items: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ApplicationParameterDTOV2' + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error + /v2/stringParameters: + get: + tags: + - external + description: Get application parameter as String by application id and parameter + key + operationId: getString + parameters: + - name: applicationId + in: query + schema: + type: string + - name: parameterKey + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + type: string + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error +components: + schemas: + ApplicationParameterDTOV2: + type: object + properties: + persisted: + type: boolean + id: + type: string + version: + format: int32 + type: integer + key: + type: string + name: + type: string + value: + type: string + description: + type: string + type: + $ref: '#/components/schemas/ApplicationParameterTypeDTOV2' + applicationId: + type: string + roleType: + $ref: '#/components/schemas/ApplicationParameterRoleTypeDTOV2' + unit: + type: string + valueRangeFrom: + format: int32 + type: integer + valueRangeTo: + format: int32 + type: integer + ApplicationParameterRoleTypeDTOV2: + enum: + - BUSINESS + - SYSTEM + type: string + ApplicationParameterTypeDTOV2: + enum: + - NUMBER + - STRING + - BOOLEAN + - TEXT_PASSWORD + - DATE + type: string diff --git a/openapi/openapi-v3.yaml b/openapi/openapi-v3.yaml new file mode 100644 index 0000000..0109162 --- /dev/null +++ b/openapi/openapi-v3.yaml @@ -0,0 +1,96 @@ +--- +openapi: 3.0.3 +info: + title: onecx-parameters + description: OneCx parameters + version: "3.0" +servers: +- url: http://onecx-parameters-svc:8080/ +tags: +- name: external +paths: + /v3/{appId}/history: + post: + tags: + - external + description: Update or create parameters for specific application + operationId: createOrUpdateParameters + parameters: + - name: appId + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ParametersBucketDTO' + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ParametersBucketDTO' + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error + /v3/{appId}/parameters: + get: + tags: + - external + description: Get parameters by application id + operationId: getApplicationParameters + parameters: + - name: appId + in: path + required: true + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: {} + "400": + description: Bad request + "404": + description: Not Found + "500": + description: Internal Server Error +components: + schemas: + ParameterInfoDTO: + type: object + properties: + count: + format: int64 + type: integer + type: + type: string + defaultValue: + type: string + currentValue: + type: string + ParametersBucketDTO: + type: object + properties: + parameters: + type: object + additionalProperties: + $ref: '#/components/schemas/ParameterInfoDTO' + instanceId: + type: string + start: + format: date-time + type: string + example: 2022-03-10T12:15:50-04:00 + end: + format: date-time + type: string + example: 2022-03-10T12:15:50-04:00 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e48c5be --- /dev/null +++ b/pom.xml @@ -0,0 +1,174 @@ + + + 4.0.0 + + io.github.onecx + onecx-quarkus3-parent + 0.9.0 + + + onecx-parameter-svc + 999-SNAPSHOT + + + + + + org.tkit.quarkus.lib + tkit-quarkus-data-import + + + 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-scheduler + + + io.quarkus + quarkus-liquibase + + + io.quarkus + quarkus-smallrye-health + + + io.quarkus + quarkus-micrometer-registry-prometheus + + + io.quarkus + quarkus-hibernate-orm + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-smallrye-openapi + + + + + 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 + + + + + + openapi + + + + io.smallrye + smallrye-open-api-maven-plugin + + + default + + generate-schema + + + openapi-internal + http://onecx-parameters-svc:8080/ + org.tkit.parameters.rs.internal + onecx-parameters + OneCx parameters + 2.0 + + + + v2 + + generate-schema + + + openapi-v2 + http://onecx-parameters-svc:8080/ + org.tkit.parameters.rs.external.v2 + onecx-parameters + OneCx parameters + 2.0 + + + + v3 + + generate-schema + + + openapi-v3 + http://onecx-parameters-svc:8080/ + org.tkit.parameters.rs.external.v3 + onecx-parameters + OneCx parameters + 3.0 + + + + + + + + + \ No newline at end of file diff --git a/src/main/docker/Dockerfile.jvm b/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..818f9b1 --- /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..2cdc686 --- /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..61ae084 --- /dev/null +++ b/src/main/helm/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: onecx-parameter-svc +version: 0.0.0 +appVersion: 0.0.0 +description: Onecx parameter management +keywords: + - iam + - parameters +sources: + - https://github.com/onecx/onecx-parameter-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 diff --git a/src/main/helm/values.yaml b/src/main/helm/values.yaml new file mode 100644 index 0000000..32a6e55 --- /dev/null +++ b/src/main/helm/values.yaml @@ -0,0 +1,6 @@ +app: + image: + repository: "onecx/onecx-parameter-svc" + tag: 999-SNAPSHOT + db: + enabled: true \ No newline at end of file diff --git a/src/main/java/io/github/onecx/parameters/domain/criteria/ApplicationParameterHistorySearchCriteria.java b/src/main/java/io/github/onecx/parameters/domain/criteria/ApplicationParameterHistorySearchCriteria.java new file mode 100644 index 0000000..bd726b0 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/criteria/ApplicationParameterHistorySearchCriteria.java @@ -0,0 +1,30 @@ +package io.github.onecx.parameters.domain.criteria; + +import java.util.List; + +import lombok.Getter; +import lombok.Setter; + +/** + * The application parameter search criteria. + */ +@Getter +@Setter +public class ApplicationParameterHistorySearchCriteria { + + /** + * The application ID. + */ + private String applicationId; + + /** + * The application parameter key. + */ + private String key; + + private List type; + + private Integer pageNumber; + + private Integer pageSize; +} diff --git a/src/main/java/io/github/onecx/parameters/domain/criteria/ApplicationParameterSearchCriteria.java b/src/main/java/io/github/onecx/parameters/domain/criteria/ApplicationParameterSearchCriteria.java new file mode 100644 index 0000000..cfbc1e8 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/criteria/ApplicationParameterSearchCriteria.java @@ -0,0 +1,32 @@ +package io.github.onecx.parameters.domain.criteria; + +import java.util.List; + +import lombok.Getter; +import lombok.Setter; + +/** + * The application parameter search criteria. + */ +@Getter +@Setter +public class ApplicationParameterSearchCriteria { + + /** + * The application ID. + */ + private String applicationId; + + /** + * The application parameter key. + */ + private String key; + + private String name; + + private List type; + + private Integer pageNumber; + + private Integer pageSize; +} diff --git a/src/main/java/io/github/onecx/parameters/domain/criteria/KeysSearchCriteria.java b/src/main/java/io/github/onecx/parameters/domain/criteria/KeysSearchCriteria.java new file mode 100644 index 0000000..dfa8d69 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/criteria/KeysSearchCriteria.java @@ -0,0 +1,12 @@ +package io.github.onecx.parameters.domain.criteria; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class KeysSearchCriteria { + + private String applicationId; + +} diff --git a/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAO.java b/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAO.java new file mode 100644 index 0000000..4644a38 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAO.java @@ -0,0 +1,174 @@ +package io.github.onecx.parameters.domain.daos; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.persistence.criteria.*; +import jakarta.transaction.Transactional; + +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 io.github.onecx.parameters.domain.criteria.ApplicationParameterSearchCriteria; +import io.github.onecx.parameters.domain.criteria.KeysSearchCriteria; +import io.github.onecx.parameters.domain.models.ApplicationParameter; +import io.github.onecx.parameters.domain.models.ApplicationParameter_; + +@ApplicationScoped +@Transactional(value = Transactional.TxType.NOT_SUPPORTED, rollbackOn = DAOException.class) +public class ApplicationParameterDAO extends AbstractDAO { + + // keys in format + public Map findApplicationParametersByKeys(Set keys, String separator) { + try { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ApplicationParameter.class); + Root root = cq.from(ApplicationParameter.class); + + // 'app__key' in ( ... ) + cq.where(cb.concat( + cb.concat(root.get(ApplicationParameter_.APPLICATION_ID), separator), root.get(ApplicationParameter_.KEY)) + .in(keys)); + + List params = getEntityManager().createQuery(cq).getResultList(); + return params.stream().collect(Collectors.toMap(x -> x.getApplicationId() + separator + x.getKey(), x -> x)); + } catch (Exception e) { + throw new DAOException(ErrorKeys.FIND_PARAMETER_BY_APPLICATION_ID_AND_PARAMETER_KEY_FAILED, e); + } + } + + public List findByApplicationIdAndParameterAndTypeKeys(String applicationId, + String parameterKey, + String parameterType) { + try { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ApplicationParameter.class); + Root root = cq.from(ApplicationParameter.class); + cq.where(cb.and( + cb.equal(root.get(ApplicationParameter_.APPLICATION_ID), applicationId), + cb.equal(root.get(ApplicationParameter_.KEY), parameterKey), + cb.equal(root.get(ApplicationParameter_.TYPE), parameterType))); + return getEntityManager().createQuery(cq).getResultList(); + } catch (Exception e) { + throw new DAOException(ErrorKeys.FIND_PARAMETERS_BY_APPLICATION_AND_PARAMETER_KEY_TYPE_FAILED, e); + } + } + + public List findByApplicationIdAndParameterKeys(String applicationId, List parametersKeys) { + + try { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ApplicationParameter.class); + Root root = cq.from(ApplicationParameter.class); + cq.where(cb.and( + cb.equal(root.get(ApplicationParameter_.APPLICATION_ID), applicationId), + root.get(ApplicationParameter_.KEY).in(parametersKeys))); + return getEntityManager().createQuery(cq).getResultList(); + } catch (Exception e) { + throw new DAOException(ErrorKeys.FIND_PARAMETERS_BY_APPLICATION_AND_PARAMETER_KEYS_FAILED, e); + } + } + + public Map findAllByApplicationId(String applicationId) { + try { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ApplicationParameter.class); + Root root = cq.from(ApplicationParameter.class); + cq.where(cb.and( + cb.equal(root.get(ApplicationParameter_.APPLICATION_ID), applicationId), + cb.or( + cb.isNotNull(root.get(ApplicationParameter_.SET_VALUE)), + cb.isNotNull(root.get(ApplicationParameter_.IMPORT_VALUE))))); + + return getEntityManager() + .createQuery(cq) + .getResultStream() + .collect(Collectors.toMap(ApplicationParameter::getKey, + p -> p.getSetValue() != null ? p.getSetValue() : p.getImportValue())); + + } catch (Exception e) { + throw new DAOException(ErrorKeys.FIND_ALL_PARAMETERS_BY_APPLICATION_ID_FAILED, e); + } + } + + public PageResult searchByCriteria(ApplicationParameterSearchCriteria criteria) { + try { + CriteriaQuery cq = criteriaQuery(); + Root root = cq.from(ApplicationParameter.class); + List predicates = new ArrayList<>(); + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + if (criteria.getApplicationId() != null && !criteria.getApplicationId().isEmpty()) { + predicates.add(cb.like(cb.lower(root.get(ApplicationParameter_.APPLICATION_ID)), + stringPattern(criteria.getApplicationId()))); + } + if (criteria.getKey() != null && !criteria.getKey().isEmpty()) { + predicates.add(cb.like(cb.lower(root.get(ApplicationParameter_.KEY)), stringPattern(criteria.getKey()))); + } + if (criteria.getName() != null && !criteria.getName().isEmpty()) { + predicates.add(cb.like(cb.lower(root.get(ApplicationParameter_.NAME)), stringPattern(criteria.getName()))); + } + if (!criteria.getType().isEmpty()) { + predicates.add(root.get(ApplicationParameter_.TYPE).in(criteria.getType())); + } + if (!predicates.isEmpty()) { + cq.where(cb.and(predicates.toArray(new Predicate[0]))); + } + return createPageQuery(cq, Page.of(criteria.getPageNumber(), criteria.getPageSize())).getPageResult(); + } catch (Exception exception) { + throw new DAOException(ErrorKeys.FIND_ALL_PARAMETERS_FAILED, exception); + } + } + + public PageResult searchAllKeys(KeysSearchCriteria criteria) { + try { + var cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(String.class); + Root root = cq.from(ApplicationParameter.class); + cq.select(root.get(ApplicationParameter_.KEY)).distinct(true); + + if (criteria.getApplicationId() != null && !criteria.getApplicationId().isEmpty()) { + cq.where(cb.equal(root.get(ApplicationParameter_.APPLICATION_ID), criteria.getApplicationId())); + } + + var results = getEntityManager().createQuery(cq).getResultList(); + return new PageResult<>(results.size(), results.stream(), Page.of(0, 1)); + } catch (Exception exception) { + throw new DAOException(ErrorKeys.FIND_ALL_KEYS_FAILED, exception); + } + } + + public PageResult searchAllApplications() { + try { + var cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(String.class); + Root root = cq.from(ApplicationParameter.class); + cq.select(root.get(ApplicationParameter_.APPLICATION_ID)).distinct(true); + var results = getEntityManager().createQuery(cq).getResultList(); + return new PageResult<>(results.size(), results.stream(), Page.of(0, 1)); + } catch (Exception exception) { + throw new DAOException(ErrorKeys.FIND_ALL_APPLICATIONS_FAILED, exception); + } + } + + private static String stringPattern(String value) { + return (value.toLowerCase() + "%"); + } + + public enum ErrorKeys { + + FIND_ALL_PARAMETERS_BY_APPLICATION_ID_FAILED, + FIND_PARAMETER_BY_APPLICATION_ID_AND_PARAMETER_KEY_FAILED, + FIND_PARAMETERS_BY_APPLICATION_AND_PARAMETER_KEYS_FAILED, + FIND_PARAMETERS_BY_APPLICATION_AND_PARAMETER_KEY_TYPE_FAILED, + FIND_ALL_APPLICATIONS_FAILED, + + FIND_ALL_KEYS_FAILED, + FIND_ALL_PARAMETERS_FAILED; + } +} diff --git a/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDataDAO.java b/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDataDAO.java new file mode 100644 index 0000000..f72d717 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDataDAO.java @@ -0,0 +1,68 @@ +package io.github.onecx.parameters.domain.daos; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.persistence.NoResultException; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import jakarta.transaction.Transactional; + +import org.tkit.quarkus.jpa.daos.AbstractDAO; +import org.tkit.quarkus.jpa.exceptions.DAOException; + +import io.github.onecx.parameters.domain.models.ApplicationParameterData; +import io.github.onecx.parameters.domain.models.ApplicationParameterData_; + +@ApplicationScoped +@Transactional(value = Transactional.TxType.NOT_SUPPORTED, rollbackOn = DAOException.class) +public class ApplicationParameterDataDAO extends AbstractDAO { + + public ApplicationParameterData findByParameterId(String parameterId) throws DAOException { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ApplicationParameterData.class); + Root root = cq.from(ApplicationParameterData.class); + + cq.select(root); + cq.where(cb.equal(root.get(ApplicationParameterData_.APPLICATION_PARAMETER_GUID), parameterId)); + + try { + return this.em.createQuery(cq).getSingleResult(); + } catch (NoResultException e) { + return null; + } + } + + public List findByParameterIds(List parameterIds) throws DAOException { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ApplicationParameterData.class); + Root root = cq.from(ApplicationParameterData.class); + + cq.select(root); + cq.where(root.get(ApplicationParameterData_.APPLICATION_PARAMETER_GUID).in(parameterIds)); + + return this.em.createQuery(cq).getResultList(); + } + + @Transactional(value = Transactional.TxType.SUPPORTS, rollbackOn = DAOException.class) + public void deleteByParameterId(String parameterId) throws DAOException { + try { + CriteriaDelete cq = deleteQuery(); + Root root = cq.from(ApplicationParameterData.class); + cq.where( + getEntityManager().getCriteriaBuilder() + .equal(root.get(ApplicationParameterData_.APPLICATION_PARAMETER_GUID), parameterId)); + getEntityManager().createQuery(cq).executeUpdate(); + getEntityManager().flush(); + } catch (Exception e) { + throw handleConstraint(e, Errors.FAILED_TO_DELETE_BY_GUID_QUERY); + } + } + + enum Errors { + FAILED_TO_DELETE_BY_GUID_QUERY; + + } +} diff --git a/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterHistoryDAO.java b/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterHistoryDAO.java new file mode 100644 index 0000000..b975887 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/daos/ApplicationParameterHistoryDAO.java @@ -0,0 +1,138 @@ +package io.github.onecx.parameters.domain.daos; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.persistence.criteria.*; +import jakarta.transaction.Transactional; + +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.models.AbstractTraceableEntity_; +import org.tkit.quarkus.jpa.models.TraceableEntity_; + +import io.github.onecx.parameters.domain.criteria.ApplicationParameterHistorySearchCriteria; +import io.github.onecx.parameters.domain.models.ApplicationParameterHistory; +import io.github.onecx.parameters.domain.models.ApplicationParameterHistory_; +import io.github.onecx.parameters.domain.models.ApplicationParameter_; +import io.github.onecx.parameters.domain.models.ParameterHistoryCountTuple; + +@ApplicationScoped +@Transactional(value = Transactional.TxType.NOT_SUPPORTED, rollbackOn = DAOException.class) +public class ApplicationParameterHistoryDAO extends AbstractDAO { + + @Transactional(value = Transactional.TxType.REQUIRED, rollbackOn = DAOException.class) + public void deleteApplicationHistoryOlderThan(LocalDateTime date) { + try { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaDelete cd = deleteQuery(); + Root root = cd.from(ApplicationParameterHistory.class); + cd.where(cb.lessThanOrEqualTo(root.get(AbstractTraceableEntity_.CREATION_DATE), date)); + getEntityManager().createQuery(cd).executeUpdate(); + } catch (Exception e) { + throw new DAOException(ErrorKeys.DELETE_PARAMETER_HISTORY_OLDER_THAN_FAILED, e); + } + } + + public PageResult searchByCriteria(ApplicationParameterHistorySearchCriteria criteria) { + try { + CriteriaQuery cq = criteriaQuery(); + Root root = cq.from(ApplicationParameterHistory.class); + List predicates = new ArrayList<>(); + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + if (criteria.getApplicationId() != null && !criteria.getApplicationId().isEmpty()) { + predicates.add(cb.like(cb.lower(root.get(ApplicationParameter_.APPLICATION_ID)), + stringPattern(criteria.getApplicationId()))); + } + if (criteria.getKey() != null && !criteria.getKey().isEmpty()) { + predicates.add(cb.like(cb.lower(root.get(ApplicationParameterHistory_.KEY)), stringPattern(criteria.getKey()))); + } + if (!criteria.getType().isEmpty()) { + predicates.add(cb.lower(root.get(ApplicationParameterHistory_.TYPE)).in(toLowerCase(criteria.getType()))); + } + if (!predicates.isEmpty()) { + cq.where(cb.and(predicates.toArray(new Predicate[0]))); + } + cq.orderBy(cb.asc(root.get(AbstractTraceableEntity_.CREATION_DATE))); + return createPageQuery(cq, Page.of(criteria.getPageNumber(), criteria.getPageSize())).getPageResult(); + } catch (Exception exception) { + throw new DAOException(ErrorKeys.FIND_ALL_PARAMETERS_HISTORY_FAILED, exception); + } + } + + public PageResult searchOnlyLatestByCriteria( + ApplicationParameterHistorySearchCriteria criteria) { + try { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ApplicationParameterHistory.class); + Root root = cq.from(ApplicationParameterHistory.class); + + Subquery maxDateSubquery = cq.subquery(Number.class); + Root maxDateSubqueryRoot = maxDateSubquery.from(ApplicationParameterHistory.class); + maxDateSubquery.select(cb.max(maxDateSubqueryRoot.get(AbstractTraceableEntity_.CREATION_DATE))) + .groupBy(maxDateSubqueryRoot.get(ApplicationParameterHistory_.INSTANCE_ID)); + + List predicates = new ArrayList<>(); + predicates.add(root.get(AbstractTraceableEntity_.CREATION_DATE).in(maxDateSubquery)); + + if (criteria.getApplicationId() != null && !criteria.getApplicationId().isEmpty()) { + predicates.add(cb.equal(cb.lower(root.get(ApplicationParameter_.APPLICATION_ID)), + criteria.getApplicationId().toLowerCase())); + } + if (criteria.getKey() != null && !criteria.getKey().isEmpty()) { + predicates.add(cb.equal(cb.lower(root.get(ApplicationParameterHistory_.KEY)), criteria.getKey().toLowerCase())); + } + + cq.select(root) + .where(cb.and(predicates.toArray(new Predicate[0]))) + .groupBy(root.get(ApplicationParameterHistory_.INSTANCE_ID), root.get(TraceableEntity_.ID)); + + return createPageQuery(cq, Page.of(criteria.getPageNumber(), criteria.getPageSize())).getPageResult(); + } catch (Exception exception) { + throw new DAOException(ErrorKeys.FIND_ALL_PARAMETERS_HISTORY_FAILED, exception); + } + } + + public List searchCountsByCriteria(ApplicationParameterHistorySearchCriteria criteria) { + try { + var cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(ParameterHistoryCountTuple.class); + Root root = cq.from(ApplicationParameterHistory.class); + cq.select( + cb.construct(ParameterHistoryCountTuple.class, root.get(AbstractTraceableEntity_.CREATION_DATE), + root.get(ApplicationParameterHistory_.COUNT))); + List predicates = new ArrayList<>(); + + if (criteria.getApplicationId() != null && !criteria.getApplicationId().isEmpty()) { + predicates.add(cb.like(cb.lower(root.get(ApplicationParameter_.APPLICATION_ID)), + stringPattern(criteria.getApplicationId()))); + } + if (criteria.getKey() != null && !criteria.getKey().isEmpty()) { + predicates.add(cb.like(cb.lower(root.get(ApplicationParameterHistory_.KEY)), stringPattern(criteria.getKey()))); + } + if (!predicates.isEmpty()) { + cq.where(cb.and(predicates.toArray(new Predicate[0]))); + } + return em.createQuery(cq).getResultList(); + } catch (Exception exception) { + throw new DAOException(ErrorKeys.FIND_ALL_PARAMETERS_HISTORY_FAILED, exception); + } + } + + private static String stringPattern(String value) { + return (value.toLowerCase() + "%"); + } + + private static List toLowerCase(List value) { + return value.stream().map(String::toLowerCase).toList(); + } + + public enum ErrorKeys { + DELETE_PARAMETER_HISTORY_OLDER_THAN_FAILED, + FIND_ALL_PARAMETERS_HISTORY_FAILED; + } +} diff --git a/src/main/java/io/github/onecx/parameters/domain/daos/JobDAO.java b/src/main/java/io/github/onecx/parameters/domain/daos/JobDAO.java new file mode 100644 index 0000000..2d75e27 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/daos/JobDAO.java @@ -0,0 +1,46 @@ +package io.github.onecx.parameters.domain.daos; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.persistence.LockModeType; +import jakarta.persistence.NoResultException; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import jakarta.transaction.Transactional; + +import org.tkit.quarkus.jpa.daos.AbstractDAO; +import org.tkit.quarkus.jpa.exceptions.DAOException; +import org.tkit.quarkus.jpa.models.TraceableEntity_; + +import io.github.onecx.parameters.domain.di.models.Job; + +@ApplicationScoped +public class JobDAO extends AbstractDAO { + + @Transactional(value = Transactional.TxType.REQUIRED, rollbackOn = DAOException.class) + public Job getJob(String id) throws DAOException { + try { + CriteriaBuilder cb = getEntityManager().getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(Job.class); + Root root = cq.from(Job.class); + cq.where(cb.equal(root.get(TraceableEntity_.ID), id)); + + return getEntityManager() + .createQuery(cq) + .setMaxResults(1) + .setLockMode(LockModeType.PESSIMISTIC_WRITE) + .setHint("javax.persistence.lock.timeout", -2) + .getSingleResult(); + + } catch (NoResultException ex) { + return null; + } catch (Exception ex) { + throw new DAOException(Error.GET_JOB_FAILED, ex); + } + } + + public enum Error { + + GET_JOB_FAILED; + } +} diff --git a/src/main/java/io/github/onecx/parameters/domain/di/ApplicationParameterDataImportService.java b/src/main/java/io/github/onecx/parameters/domain/di/ApplicationParameterDataImportService.java new file mode 100644 index 0000000..7c1e0b2 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/di/ApplicationParameterDataImportService.java @@ -0,0 +1,180 @@ +package io.github.onecx.parameters.domain.di; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.tkit.quarkus.dataimport.DataImport; +import org.tkit.quarkus.dataimport.DataImportConfig; +import org.tkit.quarkus.dataimport.DataImportService; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.github.onecx.parameters.domain.daos.ApplicationParameterDAO; +import io.github.onecx.parameters.domain.di.models.ApplicationParameterDataImport; +import io.github.onecx.parameters.domain.models.ApplicationParameter; + +/** + * Import format + * { + * "appId" : { + * "key1": { + * "description": "description_1", + * "name": "name_1", + * "value": "value_1" + * }, + * "key2": { + * "description": "description_2", + * "name": "name_2", + * "value": "value_2" + * } + * } + * } + * operation: + * - CLEAN_INSERT - delete all data and import new set + * - UPDATE - update existing data from file or create new parameters + */ +@DataImport("parameters") +public class ApplicationParameterDataImportService implements DataImportService { + + private static final Logger log = LoggerFactory.getLogger(ApplicationParameterDataImportService.class); + + public static final String KEY_SEPARATOR = "__"; + + @Inject + ApplicationParameterDAO dao; + + @Inject + ObjectMapper mapper; + + @Override + @Transactional(Transactional.TxType.REQUIRES_NEW) + public void importData(DataImportConfig config) { + try { + String operation = config.getMetadata().getOrDefault("operation", "NONE"); + + Consumer>> action = switch (operation) { + case "CLEAN_INSERT" -> this::cleanInsert; + case "UPDATE" -> this::update; + default -> null; + }; + + if (action == null) { + log.warn("Not supported operation '{}' for the import configuration key '{}'", operation, config.getKey()); + return; + } + + if (config.getData() == null || config.getData().length == 0) { + log.warn("Import configuration key {} does not contains any data to import", config.getKey()); + return; + } + + Map> data = mapper.readValue(config.getData(), + new TypeReference>>() { + }); + if (data.isEmpty()) { + log.warn("Import configuration key {} does not contains any JSON data to import", config.getKey()); + return; + } + action.accept(data); + } catch (Exception ex) { + throw new ErrorImportException(ex); + } + } + + public static class ErrorImportException extends RuntimeException { + public ErrorImportException(Exception ex) { + super(ex.getMessage(), ex); + } + } + + private void cleanInsert(Map> data) { + + // convert import model to list of parameters + List params = new ArrayList<>(); + data.forEach((app, keys) -> { + if (keys != null && !keys.isEmpty()) { + keys.forEach((key, value) -> params.add(create(app, key, value))); + } + }); + + // delete all parameters + dao.deleteQueryAll(); + + // create new parameters + dao.create(params); + } + + private void update(Map> data) { + Map values = new HashMap<>(); + data.forEach((app, keys) -> { + if (keys != null && !keys.isEmpty()) { + keys.forEach((key, value) -> values.put(id(app, key), value)); + } + }); + + // update existing parameter + Map params = dao.findApplicationParametersByKeys(values.keySet(), KEY_SEPARATOR); + if (!params.isEmpty()) { + List updated = new ArrayList<>(); + params.forEach((k, v) -> { + updated.add(update(v, values.get(k))); + values.remove(k); + + }); + dao.update(updated); + } + + // no new application parameter to create + if (values.isEmpty()) { + return; + } + + // create new parameter + List created = new ArrayList<>(); + values.forEach((k, value) -> { + String[] tmp = k.split(KEY_SEPARATOR); + created.add(create(tmp[0], tmp[1], value)); + }); + dao.create(created); + } + + private ApplicationParameter update(ApplicationParameter param, ApplicationParameterDataImport value) { + if (isValue(value.getName())) { + param.setName(value.getName()); + } + if (isValue(value.getDescription())) { + param.setDescription(value.getDescription()); + } + if (isValue(value.getValue())) { + param.setImportValue(value.getValue()); + } + return param; + } + + private ApplicationParameter create(String app, String key, ApplicationParameterDataImport value) { + ApplicationParameter param = new ApplicationParameter(); + param.setApplicationId(app); + param.setKey(key); + param.setName(value.getName()); + param.setDescription(value.getDescription()); + param.setImportValue(value.getValue()); + return param; + } + + private boolean isValue(String value) { + return value != null && !value.isBlank(); + } + + private String id(String app, String key) { + return app + KEY_SEPARATOR + key; + } +} diff --git a/src/main/java/io/github/onecx/parameters/domain/di/models/ApplicationParameterDataImport.java b/src/main/java/io/github/onecx/parameters/domain/di/models/ApplicationParameterDataImport.java new file mode 100644 index 0000000..4303788 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/di/models/ApplicationParameterDataImport.java @@ -0,0 +1,15 @@ +package io.github.onecx.parameters.domain.di.models; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ApplicationParameterDataImport { + + private String description; + + private String name; + + private String value; +} diff --git a/src/main/java/io/github/onecx/parameters/domain/di/models/Job.java b/src/main/java/io/github/onecx/parameters/domain/di/models/Job.java new file mode 100644 index 0000000..f0714f9 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/di/models/Job.java @@ -0,0 +1,17 @@ +package io.github.onecx.parameters.domain.di.models; + +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +import org.tkit.quarkus.jpa.models.TraceableEntity; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "APM_JOB") +public class Job extends TraceableEntity { + +} diff --git a/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameter.java b/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameter.java new file mode 100644 index 0000000..7c4804a --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameter.java @@ -0,0 +1,72 @@ +package io.github.onecx.parameters.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 = "APM_APP_PARAM", uniqueConstraints = { + @UniqueConstraint(columnNames = { "APPLICATION_ID", "PARAM_KEY" }) }) +@SuppressWarnings("java:S2160") +public class ApplicationParameter extends TraceableEntity { + + /** + * The UID for this class. + */ + private static final long serialVersionUID = 9009055375282015896L; + + /** + * The application parameter key. + */ + @Column(name = "PARAM_KEY") + private String key; + + /** + * The application parameter name. + */ + @Column(name = "PARAM_NAME") + private String name; + + /** + * The name of the parameter as it appears in functional specifications + * (FSS). + */ + @Column(name = "PARAM_DESCRIPTION") + private String description; + + /** + * The application. + */ + @Column(name = "APPLICATION_ID") + private String applicationId; + + /** + * The application parameter set value. + */ + @Column(name = "SET_VALUE") + private String setValue; + + /** + * The application parameter set value. + */ + @Column(name = "IMPORT_VALUE") + private String importValue; + + /** + * The application parameter type. + * + * @deprecated use only in v2 API + */ + @SuppressWarnings("java:S1133") + @Deprecated(forRemoval = true, since = "1.0.0") + @Column(name = "VALUE_TYPE") + private String type; +} diff --git a/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameterData.java b/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameterData.java new file mode 100644 index 0000000..67a2ed5 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameterData.java @@ -0,0 +1,34 @@ +package io.github.onecx.parameters.domain.models; + +import java.util.UUID; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "APM_APP_PARAM_DATA") +public class ApplicationParameterData { + + @Id + @Column(name = "GUID") + private String id = UUID.randomUUID().toString(); + + @Column(name = "UNIT") + private String unit; + + @Column(name = "RANGE_FROM") + private Integer rangeFrom; + + @Column(name = "RANGE_TO") + private Integer rangeTo; + + @Column(name = "PARAMETER_GUID", nullable = false, unique = true) + private String applicationParameterGuid; +} diff --git a/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameterHistory.java b/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameterHistory.java new file mode 100644 index 0000000..0396efd --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/models/ApplicationParameterHistory.java @@ -0,0 +1,78 @@ +package io.github.onecx.parameters.domain.models; + +import java.time.LocalDateTime; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +import org.tkit.quarkus.jpa.models.TraceableEntity; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "APM_APP_PARAM_HISTORY") +@SuppressWarnings("java:S2160") +public class ApplicationParameterHistory extends TraceableEntity { + /** + * The UID for this class. + */ + private static final long serialVersionUID = 9009055375282015896L; + + /** + * The application parameter key. + */ + @Column(name = "PARAM_KEY") + private String key; + + /** + * The application. + */ + @Column(name = "APPLICATION_ID") + private String applicationId; + + /** + * The application parameter type. + */ + @Column(name = "VALUE_TYPE") + private String type; + + /** + * The application parameter used value. + */ + @Column(name = "USED_VALUE") + private String usedValue; + + /** + * The application parameter used value. + */ + @Column(name = "DEFAULT_VALUE") + private String defaultValue; + + /** + * Count of hit + */ + @Column(name = "COUNT") + private Long count; + + /** + * Interval start time + */ + @Column(name = "INTERVAL_START") + private LocalDateTime start; + + /** + * Interval end time + */ + @Column(name = "INTERVAL_END") + private LocalDateTime end; + + /** + * The instance ID. + */ + @Column(name = "INSTANCE_ID") + private String instanceId; +} diff --git a/src/main/java/io/github/onecx/parameters/domain/models/ParameterHistoryCountTuple.java b/src/main/java/io/github/onecx/parameters/domain/models/ParameterHistoryCountTuple.java new file mode 100644 index 0000000..ab9efc9 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/models/ParameterHistoryCountTuple.java @@ -0,0 +1,22 @@ +package io.github.onecx.parameters.domain.models; + +import java.time.LocalDateTime; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@RegisterForReflection +public class ParameterHistoryCountTuple { + + private LocalDateTime creationDate; + + private Long count; + + public ParameterHistoryCountTuple(LocalDateTime creationDate, Long count) { + this.creationDate = creationDate; + this.count = count; + } +} diff --git a/src/main/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryService.java b/src/main/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryService.java new file mode 100644 index 0000000..11a6d75 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryService.java @@ -0,0 +1,51 @@ +package io.github.onecx.parameters.domain.timer; + +import java.time.Duration; +import java.time.LocalDateTime; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.transaction.Transactional; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.tkit.quarkus.jpa.exceptions.DAOException; + +import io.github.onecx.parameters.domain.daos.ApplicationParameterHistoryDAO; +import io.github.onecx.parameters.domain.daos.JobDAO; +import io.github.onecx.parameters.domain.di.models.Job; +import io.quarkus.scheduler.Scheduled; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Singleton +public class MaintenanceHistoryService { + + @ConfigProperty(name = "onecx.parameters.history.scheduler.duration", defaultValue = "P7D") + Duration duration; + + @Inject + ApplicationParameterHistoryDAO dao; + + @Inject + JobDAO jobDAO; + + static final String JOB_ID = "maintenance.history"; + + // find older items and delete it + @Scheduled(identity = "maintenance.history", cron = "${onecx.parameters.history.scheduler.expr}") + @Transactional(value = Transactional.TxType.REQUIRES_NEW, rollbackOn = DAOException.class) + void maintenanceHistoryData() { + LocalDateTime dt = LocalDateTime.now().minus(duration); + Job job = jobDAO.getJob(JOB_ID); + if (job != null) { + log.info("Scheduler for job id: '{}' started.", JOB_ID); + try { + dao.deleteApplicationHistoryOlderThan(dt); + log.info("Scheduler for job id: '{}' finished.", JOB_ID); + } catch (Exception ex) { + log.error("Scheduler for job id: '" + JOB_ID + "' failed.", ex); + throw ex; + } + } + } +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2.java b/src/main/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2.java new file mode 100644 index 0000000..9eb63fc --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2.java @@ -0,0 +1,168 @@ +package io.github.onecx.parameters.rs.external.v2; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import io.github.onecx.parameters.domain.daos.ApplicationParameterDAO; +import io.github.onecx.parameters.domain.daos.ApplicationParameterDataDAO; +import io.github.onecx.parameters.domain.models.ApplicationParameter; +import io.github.onecx.parameters.domain.models.ApplicationParameterData; +import io.github.onecx.parameters.rs.external.v2.dtos.AbstractEntityDTOV2; +import io.github.onecx.parameters.rs.external.v2.dtos.ApplicationParameterDTOV2; +import io.github.onecx.parameters.rs.external.v2.dtos.enums.ApplicationParameterTypeDTOV2; +import io.github.onecx.parameters.rs.external.v2.mappers.ApplicationParameterDataMapperV2; +import io.github.onecx.parameters.rs.external.v2.mappers.ApplicationParameterMapperV2; + +@ApplicationScoped +@Path("/v2") +@Tag(name = "external") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Transactional(value = Transactional.TxType.NOT_SUPPORTED) +public class ParameterRestControllerV2 { + + @Inject + ApplicationParameterDAO applicationParameterService; + + @Inject + ApplicationParameterDataDAO applicationParameterDataDAO; + + @Inject + ApplicationParameterMapperV2 extApplicationParameterMapperV2; + + @Inject + ApplicationParameterDataMapperV2 applicationParameterDataMapperV2; + + @GET + @Path("/parameters") + @Operation(operationId = "getParameter", description = "Get application parameter by application id and parameter key") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApplicationParameterDTOV2.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getParameter(@QueryParam("applicationId") String applicationId, + @QueryParam("parameterKey") String parameterKey) { + if (applicationId == null || applicationId.isEmpty() || parameterKey == null || parameterKey.isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST).entity("ApplicationId and parameterKey are mandatory.").build(); + } + + var applicationParameterList = applicationParameterService + .findByApplicationIdAndParameterKeys(applicationId, List.of(parameterKey)); + return getAllParameters(applicationParameterList); + } + + @POST + @Path("/parameters") + @Operation(operationId = "getAllParameters", description = "Get all application parameters by application id and/or parameter key") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApplicationParameterDTOV2.class, type = SchemaType.ARRAY))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getAllParameters(@QueryParam("applicationId") String applicationId, List parametersKeys) { + if (applicationId == null || applicationId.isEmpty() || parametersKeys == null || parametersKeys.isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST).entity("ApplicationId and parametersKeys are mandatory.") + .build(); + } + + var applicationParameterList = applicationParameterService + .findByApplicationIdAndParameterKeys(applicationId, parametersKeys); + return getAllParameters(applicationParameterList); + } + + Response getAllParameters(List applicationParameterList) { + if (!applicationParameterList.isEmpty()) { + List results = extApplicationParameterMapperV2.finds(applicationParameterList); + + // map ApplicationParameterData to ApplicationParameter due to backward compatibility + + var parametersData = applicationParameterDataDAO + .findByParameterIds(results.stream().map(AbstractEntityDTOV2::getId).toList()) + .stream() + .collect(Collectors.toMap(ApplicationParameterData::getApplicationParameterGuid, d -> d)); + + results.forEach(p -> applicationParameterDataMapperV2.map(parametersData.get(p.getId()), p)); + + return Response.ok(results).build(); + } + return Response.status(Response.Status.NOT_FOUND).build(); + } + + @GET + @Path("/stringParameters") + @Operation(operationId = "getString", description = "Get application parameter as String by application id and parameter key") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = String.class, type = SchemaType.ARRAY))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getString(@QueryParam("applicationId") String applicationId, + @QueryParam("parameterKey") String parameterKey) { + return getValue(applicationId, parameterKey, ApplicationParameterTypeDTOV2.STRING, s -> s); + } + + @GET + @Path("/longParameters") + @Operation(operationId = "getLong", description = "Get application parameter as Long by application id and parameter key") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Long.class, type = SchemaType.ARRAY))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getLong(@QueryParam("applicationId") String applicationId, + @QueryParam("parameterKey") String parameterKey) { + return getValue(applicationId, parameterKey, ApplicationParameterTypeDTOV2.NUMBER, Long::valueOf); + } + + @GET + @Path("/integerParameters") + @Operation(operationId = "getInteger", description = "Get application parameter as Integer by application id and parameter key") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Integer.class, type = SchemaType.ARRAY))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getInteger(@QueryParam("applicationId") String applicationId, + @QueryParam("parameterKey") String parameterKey) { + return getValue(applicationId, parameterKey, ApplicationParameterTypeDTOV2.NUMBER, Integer::valueOf); + } + + @GET + @Path("/booleanParameters") + @Operation(operationId = "getBoolean", description = "Get application parameter as Boolean by application id and parameter key") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Boolean.class, type = SchemaType.ARRAY))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getBoolean(@QueryParam("applicationId") String applicationId, + @QueryParam("parameterKey") String parameterKey) { + return getValue(applicationId, parameterKey, ApplicationParameterTypeDTOV2.BOOLEAN, Boolean::valueOf); + } + + Response getValue(String applicationId, String parameterKey, ApplicationParameterTypeDTOV2 paramType, + Function convert) { + if (applicationId == null || applicationId.isEmpty() || parameterKey == null || parameterKey.isEmpty()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Query parameters applicationId and parameterKey are mandatory.").build(); + } + String type = paramType.name(); + List applicationParameterList = applicationParameterService + .findByApplicationIdAndParameterAndTypeKeys(applicationId, parameterKey, type); + if (!applicationParameterList.isEmpty()) { + List results = applicationParameterList.stream().map(p -> convert.apply(p.getSetValue())).toList(); + return Response.ok(results).build(); + } + return Response.status(Response.Status.NOT_FOUND).build(); + } +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/AbstractEntityDTOV2.java b/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/AbstractEntityDTOV2.java new file mode 100644 index 0000000..067ce78 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/AbstractEntityDTOV2.java @@ -0,0 +1,28 @@ +package io.github.onecx.parameters.rs.external.v2.dtos; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class AbstractEntityDTOV2 { + + /** + * The persisted flag. + */ + private boolean persisted; + + /** + * The ID. + */ + @EqualsAndHashCode.Include + private String id; + + /** + * The optimistic lock version + */ + private Integer version; + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/ApplicationParameterDTOV2.java b/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/ApplicationParameterDTOV2.java new file mode 100644 index 0000000..62da5ff --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/ApplicationParameterDTOV2.java @@ -0,0 +1,25 @@ +package io.github.onecx.parameters.rs.external.v2.dtos; + +import io.github.onecx.parameters.rs.external.v2.dtos.enums.ApplicationParameterRoleTypeDTOV2; +import io.github.onecx.parameters.rs.external.v2.dtos.enums.ApplicationParameterTypeDTOV2; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@RegisterForReflection + +public class ApplicationParameterDTOV2 extends AbstractEntityDTOV2 { + + private String key; + private String name; + private String value; + private String description; + private ApplicationParameterTypeDTOV2 type; + private String applicationId; + private ApplicationParameterRoleTypeDTOV2 roleType; + private String unit; + private Integer valueRangeFrom; + private Integer valueRangeTo; +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/enums/ApplicationParameterRoleTypeDTOV2.java b/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/enums/ApplicationParameterRoleTypeDTOV2.java new file mode 100644 index 0000000..e3f577b --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/enums/ApplicationParameterRoleTypeDTOV2.java @@ -0,0 +1,14 @@ +package io.github.onecx.parameters.rs.external.v2.dtos.enums; + +public enum ApplicationParameterRoleTypeDTOV2 { + + /** + * The business. + */ + BUSINESS, + /** + * The system. + */ + SYSTEM; + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/enums/ApplicationParameterTypeDTOV2.java b/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/enums/ApplicationParameterTypeDTOV2.java new file mode 100644 index 0000000..ea38d39 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v2/dtos/enums/ApplicationParameterTypeDTOV2.java @@ -0,0 +1,26 @@ +package io.github.onecx.parameters.rs.external.v2.dtos.enums; + +public enum ApplicationParameterTypeDTOV2 { + + /** + * The number. + */ + NUMBER, + /** + * The string. + */ + STRING, + /** + * The boolean. + */ + BOOLEAN, + /** + * The password. + */ + TEXT_PASSWORD, + /** + * The date. + */ + DATE + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v2/mappers/ApplicationParameterDataMapperV2.java b/src/main/java/io/github/onecx/parameters/rs/external/v2/mappers/ApplicationParameterDataMapperV2.java new file mode 100644 index 0000000..6e48564 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v2/mappers/ApplicationParameterDataMapperV2.java @@ -0,0 +1,19 @@ +package io.github.onecx.parameters.rs.external.v2.mappers; + +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; + +import io.github.onecx.parameters.domain.models.ApplicationParameterData; +import io.github.onecx.parameters.rs.external.v2.dtos.ApplicationParameterDTOV2; + +@Mapper(injectionStrategy = InjectionStrategy.CONSTRUCTOR) +public interface ApplicationParameterDataMapperV2 { + + @Mapping(target = "id", ignore = true) + @Mapping(target = "roleType", constant = "SYSTEM") + @Mapping(target = "valueRangeFrom", source = "rangeFrom") + @Mapping(target = "valueRangeTo", source = "rangeTo") + ApplicationParameterDTOV2 map(ApplicationParameterData entity, @MappingTarget ApplicationParameterDTOV2 dto); +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v2/mappers/ApplicationParameterMapperV2.java b/src/main/java/io/github/onecx/parameters/rs/external/v2/mappers/ApplicationParameterMapperV2.java new file mode 100644 index 0000000..dc607d3 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v2/mappers/ApplicationParameterMapperV2.java @@ -0,0 +1,29 @@ +package io.github.onecx.parameters.rs.external.v2.mappers; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +import io.github.onecx.parameters.domain.models.ApplicationParameter; +import io.github.onecx.parameters.rs.external.v2.dtos.ApplicationParameterDTOV2; + +@Mapper +@MapperConfig(unmappedTargetPolicy = ReportingPolicy.ERROR) +public abstract class ApplicationParameterMapperV2 { + + /** + * The mapping method for find of list entities. + * + * @param data the list of entities. + * @return the corresponding list of DTO objects. + */ + public abstract List finds(List data); + + @Mapping(target = "value", source = "setValue") + @Mapping(target = "version", source = "modificationCount") + public abstract ApplicationParameterDTOV2 map(ApplicationParameter data); + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3.java b/src/main/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3.java new file mode 100644 index 0000000..8a0a8ae --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3.java @@ -0,0 +1,75 @@ +package io.github.onecx.parameters.rs.external.v3; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import io.github.onecx.parameters.domain.daos.ApplicationParameterDAO; +import io.github.onecx.parameters.domain.daos.ApplicationParameterHistoryDAO; +import io.github.onecx.parameters.domain.models.ApplicationParameterHistory; +import io.github.onecx.parameters.rs.external.v3.mappers.ApplicationParameterHistoryMapper; +import io.github.onecx.parameters.rs.external.v3.models.ParametersBucketDTO; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@ApplicationScoped +@Path("/v3") +@Tag(name = "external") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Transactional(value = Transactional.TxType.NOT_SUPPORTED) +public class ParameterRestControllerV3 { + + @Inject + ApplicationParameterDAO applicationParameterDAO; + + @Inject + ApplicationParameterHistoryDAO historyDAO; + + @Inject + ApplicationParameterHistoryMapper mapper; + + @GET + @Path("/{appId}/parameters") + @Operation(operationId = "getApplicationParameters", description = "Get parameters by application id") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Object.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getApplicationParameters(@PathParam(value = "appId") String appId) { + Map applicationParameters = applicationParameterDAO.findAllByApplicationId(appId); + return Response.ok(applicationParameters).build(); + } + + @POST + @Path("/{appId}/history") + @Operation(operationId = "createOrUpdateParameters", description = "Update or create parameters for specific application") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ParametersBucketDTO.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response bucketRequest(@PathParam(value = "appId") String appId, ParametersBucketDTO dto) { + if (dto == null || dto.getParameters().isEmpty()) { + return Response.status(Response.Status.OK).build(); + } + List items = new ArrayList<>(); + dto.getParameters().forEach((key, value) -> items + .add(mapper.mapItem(value, key, dto.getStart(), dto.getEnd(), dto.getInstanceId(), appId, + value.getCurrentValue()))); + historyDAO.create(items); + return Response.status(Response.Status.OK).build(); + } +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v3/mappers/ApplicationParameterHistoryMapper.java b/src/main/java/io/github/onecx/parameters/rs/external/v3/mappers/ApplicationParameterHistoryMapper.java new file mode 100644 index 0000000..c901521 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v3/mappers/ApplicationParameterHistoryMapper.java @@ -0,0 +1,19 @@ +package io.github.onecx.parameters.rs.external.v3.mappers; + +import java.time.OffsetDateTime; + +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; +import org.mapstruct.ReportingPolicy; +import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper; + +import io.github.onecx.parameters.domain.models.ApplicationParameterHistory; +import io.github.onecx.parameters.rs.external.v3.models.ParameterInfoDTO; + +@Mapper(uses = OffsetDateTimeMapper.class) +@MapperConfig(unmappedTargetPolicy = ReportingPolicy.ERROR) +public interface ApplicationParameterHistoryMapper { + + ApplicationParameterHistory mapItem(ParameterInfoDTO dto, String key, OffsetDateTime start, + OffsetDateTime end, String instanceId, String applicationId, String usedValue); +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v3/models/ParameterInfoDTO.java b/src/main/java/io/github/onecx/parameters/rs/external/v3/models/ParameterInfoDTO.java new file mode 100644 index 0000000..1827f2a --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v3/models/ParameterInfoDTO.java @@ -0,0 +1,20 @@ +package io.github.onecx.parameters.rs.external.v3.models; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@RegisterForReflection +public class ParameterInfoDTO { + + private Long count; + + private String type; + + private String defaultValue; + + private String currentValue; + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/external/v3/models/ParametersBucketDTO.java b/src/main/java/io/github/onecx/parameters/rs/external/v3/models/ParametersBucketDTO.java new file mode 100644 index 0000000..7454ee4 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/external/v3/models/ParametersBucketDTO.java @@ -0,0 +1,24 @@ +package io.github.onecx.parameters.rs.external.v3.models; + +import java.time.OffsetDateTime; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@RegisterForReflection +public class ParametersBucketDTO { + + private Map parameters = new ConcurrentHashMap<>(); + + private String instanceId; + + private OffsetDateTime start; + + private OffsetDateTime end; + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestController.java b/src/main/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestController.java new file mode 100644 index 0000000..0b97e55 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestController.java @@ -0,0 +1,91 @@ +package io.github.onecx.parameters.rs.internal; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import io.github.onecx.parameters.domain.criteria.ApplicationParameterHistorySearchCriteria; +import io.github.onecx.parameters.domain.daos.ApplicationParameterHistoryDAO; +import io.github.onecx.parameters.domain.models.ApplicationParameterHistory; +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterHistoryDTO; +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterHistoryPageResultDTO; +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterHistorySearchCriteriaDTO; +import io.github.onecx.parameters.rs.internal.dtos.ParameterHistoryCountDTO; +import io.github.onecx.parameters.rs.internal.mappers.ApplicationParameterInternalMapper; + +@ApplicationScoped +@Tag(name = "internal") +@Path("/histories") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Transactional(value = Transactional.TxType.NOT_SUPPORTED) +public class ApplicationParameterHistoryRestController { + + @Inject + ApplicationParameterInternalMapper applicationParameterInternalMapper; + + @Inject + ApplicationParameterHistoryDAO historyDAO; + + @GET + @Path("latest") + @Operation(operationId = "getAllApplicationParametersHistoryLatest", description = "Find all parameters history latest") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApplicationParameterHistoryPageResultDTO.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getAllApplicationParametersHistoryLatest(@BeanParam ApplicationParameterHistorySearchCriteriaDTO dto) { + var criteria = applicationParameterInternalMapper.map(dto); + var parametersHistories = historyDAO.searchOnlyLatestByCriteria(criteria); + return Response.ok(applicationParameterInternalMapper.mapHistory(parametersHistories)).build(); + } + + @GET + @Operation(operationId = "getAllApplicationParametersHistory", description = "Find all parameters history") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApplicationParameterHistoryPageResultDTO.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getAllApplicationParametersHistory(@BeanParam ApplicationParameterHistorySearchCriteriaDTO dto) { + var criteria = applicationParameterInternalMapper.map(dto); + var parametersHistories = historyDAO.searchByCriteria(criteria); + return Response.ok(applicationParameterInternalMapper.mapHistory(parametersHistories)).build(); + } + + @GET + @Path("/{id}") + @Operation(operationId = "getApplicationParametersHistoryById", description = "Find parameters history by Id") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApplicationParameterHistoryDTO.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getApplicationParametersHistoryById(@PathParam("id") String id) { + ApplicationParameterHistory parameter = historyDAO.findById(id); + if (parameter == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + return Response.ok(applicationParameterInternalMapper.mapHistory(parameter)).build(); + } + + @GET + @Path("/counts") + @Operation(operationId = "getCountsByCriteria", description = "Get creation dates and counts by criteria") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ParameterHistoryCountDTO.class, type = SchemaType.ARRAY))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getCountsByCriteria(@BeanParam ApplicationParameterHistorySearchCriteriaDTO dto) { + ApplicationParameterHistorySearchCriteria criteria = applicationParameterInternalMapper.map(dto); + var counts = historyDAO.searchCountsByCriteria(criteria); + var results = applicationParameterInternalMapper.mapCountList(counts); + return Response.ok(results).build(); + } + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestController.java b/src/main/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestController.java new file mode 100644 index 0000000..dda2d00 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestController.java @@ -0,0 +1,209 @@ +package io.github.onecx.parameters.rs.internal; + +import java.util.List; +import java.util.stream.Collectors; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; + +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import io.github.onecx.parameters.domain.daos.ApplicationParameterDAO; +import io.github.onecx.parameters.domain.daos.ApplicationParameterDataDAO; +import io.github.onecx.parameters.domain.models.ApplicationParameter; +import io.github.onecx.parameters.domain.models.ApplicationParameterData; +import io.github.onecx.parameters.rs.internal.dtos.*; +import io.github.onecx.parameters.rs.internal.mappers.ApplicationParameterDataMapper; +import io.github.onecx.parameters.rs.internal.mappers.ApplicationParameterInternalMapper; +import lombok.extern.slf4j.Slf4j; + +@ApplicationScoped +@Slf4j +@Tag(name = "internal") +@Path("/parameters") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Transactional(value = Transactional.TxType.NOT_SUPPORTED) +public class ApplicationParameterRestController { + + @Inject + ApplicationParameterDAO applicationParameterDAO; + + @Inject + ApplicationParameterDataDAO applicationParameterDataDAO; + + @Inject + ApplicationParameterInternalMapper applicationParameterInternalMapper; + + @Inject + ApplicationParameterDataMapper applicationParameterDataMapper; + + @GET() + @Path("applications") + @Operation(operationId = "getAllApplications", description = "Find all parameters") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApplicationsPageResultDTO.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getAllApplications() { + var apps = applicationParameterDAO.searchAllApplications(); + return Response.ok(applicationParameterInternalMapper.apps(apps)).build(); + } + + @GET() + @Path("keys") + @Operation(operationId = "getAllKeys", description = "Find all parameters") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = KeysPageResultDTO.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getAllKeys(@BeanParam KeysSearchCriteriaDTO dto) { + var criteria = applicationParameterInternalMapper.map(dto); + var keys = applicationParameterDAO.searchAllKeys(criteria); + return Response.ok(applicationParameterInternalMapper.apps(keys)).build(); + } + + @GET + @Operation(operationId = "getAllApplicationParameters", description = "Find all parameters") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApplicationParameterPageResultDTO.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getAllApplicationParameters(@BeanParam ApplicationParameterSearchCriteriaDTO dto) { + var criteria = applicationParameterInternalMapper.map(dto); + var parameters = applicationParameterDAO.searchByCriteria(criteria); + ApplicationParameterPageResultDTO results = applicationParameterInternalMapper.map(parameters); + + // map ApplicationParameterData to ApplicationParameter due to backward compatibility + if (!results.getStream().isEmpty()) { + List data = results.getStream(); + var parametersData = applicationParameterDataDAO.findByParameterIds(data.stream().map(TraceableDTO::getId).toList()) + .stream() + .collect(Collectors.toMap(ApplicationParameterData::getApplicationParameterGuid, d -> d)); + + data.forEach(p -> applicationParameterDataMapper.map(parametersData.get(p.getId()), p)); + + results.setStream(data); + } + + return Response.ok(results).build(); + } + + @GET + @Path("/{id}") + @Operation(operationId = "getParameterById", description = "Find parameter by id") + @APIResponse(responseCode = "200", description = "OK", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ApplicationParameterDTO.class))) + @APIResponse(responseCode = "400", description = "Bad request") + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response getParameterById(@PathParam("id") String id) { + ApplicationParameter param = applicationParameterDAO.findById(id); + if (param == null) { + return Response.status(Response.Status.NOT_FOUND) + .entity(applicationParameterInternalMapper.createRestException(Response.Status.NOT_FOUND.name(), + "Parameter with id" + id + " not found.")) + .build(); + } + ApplicationParameterDTO parameterDTO = applicationParameterInternalMapper.map(param); + + // map ApplicationParameterData to ApplicationParameter due to backward compatibility + ApplicationParameterData parameterData = applicationParameterDataDAO.findByParameterId(id); + applicationParameterDataMapper.map(parameterData, parameterDTO); + + return Response.ok(parameterDTO).build(); + } + + @PUT + @Path("/{id}") + @Transactional + @Operation(operationId = "updateParameterValue", description = "Update parameter") + @APIResponse(responseCode = "204", description = "No Content") + @APIResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = RestExceptionDTO.class))) + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response updateParameterValue(@PathParam("id") String id, + ApplicationParameterUpdateDTO applicationParameterUpdateDTO) { + ApplicationParameter applicationParameter = applicationParameterDAO.findById(id); + if (applicationParameter == null) { + return Response.status(Response.Status.NOT_FOUND) + .entity(applicationParameterInternalMapper.createRestException(Response.Status.NOT_FOUND.name(), + "Parameter with id" + id + " not found.")) + .build(); + } + applicationParameterInternalMapper.update(applicationParameterUpdateDTO, applicationParameter); + applicationParameter = applicationParameterDAO.update(applicationParameter); + + // ApplicationParameterData update/creation due to backward compatibility + if ((applicationParameterUpdateDTO.getUnit() != null && !applicationParameterUpdateDTO.getUnit().isBlank()) + || applicationParameterUpdateDTO.getRangeFrom() != null + || applicationParameterUpdateDTO.getRangeTo() != null) { + ApplicationParameterData applicationParameterData = applicationParameterDataDAO + .findByParameterId(applicationParameter.getId()); + if (applicationParameterData != null) { + applicationParameterData = applicationParameterDataMapper.update(applicationParameterUpdateDTO, + applicationParameterData); + applicationParameterDataDAO.update(applicationParameterData); + } else { + applicationParameterData = applicationParameterDataMapper.create(applicationParameterUpdateDTO, + applicationParameter.getId()); + applicationParameterDataDAO.create(applicationParameterData); + } + } + + return Response.status(Response.Status.NO_CONTENT.getStatusCode()).build(); + } + + @POST + @Transactional + @Operation(operationId = "createParameterValue", description = "Create parameter") + @APIResponse(responseCode = "204", description = "No Content") + @APIResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = RestExceptionDTO.class))) + @APIResponse(responseCode = "404", description = "Not Found") + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response createParameterValue(ApplicationParameterCreateDTO request, @Context UriInfo uriInfo) { + ApplicationParameter param = new ApplicationParameter(); + param.setApplicationId(request.getApplicationId()); + param.setKey(request.getKey()); + param.setType(request.getType()); + param.setDescription(request.getDescription()); + param.setSetValue(request.getValue()); + param = applicationParameterDAO.create(param); + + // ApplicationParameterData creation due to backward compatibility + if ((request.getUnit() != null && !request.getUnit().isBlank()) + || request.getRangeFrom() != null + || request.getRangeTo() != null) { + ApplicationParameterData applicationParameterData = applicationParameterDataMapper.create(request, param.getId()); + applicationParameterDataDAO.create(applicationParameterData); + } + + return Response + .created(uriInfo.getAbsolutePathBuilder().path(param.getId()).build()) + .build(); + } + + @DELETE + @Path("/{id}") + @Transactional + @Operation(operationId = "deleteParameter", description = "Delete parameter") + @APIResponse(responseCode = "204", description = "No Content") + @APIResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = RestExceptionDTO.class))) + @APIResponse(responseCode = "500", description = "Internal Server Error") + public Response deleteParameter(@PathParam("id") String id) { + ApplicationParameter parameter = applicationParameterDAO.findById(id); + if (parameter != null) { + applicationParameterDAO.delete(parameter); + // ApplicationParameterData deletion due to backward compatibility + applicationParameterDataDAO.deleteByParameterId(parameter.getId()); + } + return Response.status(Response.Status.NO_CONTENT.getStatusCode()).build(); + } + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterCreateDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterCreateDTO.java new file mode 100644 index 0000000..255693e --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterCreateDTO.java @@ -0,0 +1,29 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +@RegisterForReflection +public class ApplicationParameterCreateDTO { + + private String key; + + private String applicationId; + + private String value; + + private String type; + + private String description; + + private String unit; + + private Integer rangeFrom; + + private Integer rangeTo; +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterDTO.java new file mode 100644 index 0000000..be5066f --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterDTO.java @@ -0,0 +1,34 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +@RegisterForReflection +@SuppressWarnings("java:S2160") +public class ApplicationParameterDTO extends TraceableDTO { + + private String name; + + private String description; + + private String applicationId; + + private String key; + + private String setValue; + + private String importValue; + + private String type; + + private String unit; + + private Integer rangeFrom; + + private Integer rangeTo; +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistoryDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistoryDTO.java new file mode 100644 index 0000000..ef37f0a --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistoryDTO.java @@ -0,0 +1,26 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +@RegisterForReflection +@SuppressWarnings("java:S2160") +public class ApplicationParameterHistoryDTO extends TraceableDTO { + + private String applicationId; + + private String key; + + private String usedValue; + + private String defaultValue; + + private String type; + + private String instanceId; +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistoryPageResultDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistoryPageResultDTO.java new file mode 100644 index 0000000..762e7f5 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistoryPageResultDTO.java @@ -0,0 +1,7 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class ApplicationParameterHistoryPageResultDTO extends PageResultDTO { +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistorySearchCriteriaDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistorySearchCriteriaDTO.java new file mode 100644 index 0000000..31c127c --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterHistorySearchCriteriaDTO.java @@ -0,0 +1,41 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import java.util.List; + +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.QueryParam; + +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class ApplicationParameterHistorySearchCriteriaDTO { + + @QueryParam("applicationId") + @Schema(description = "The application parameter id.") + private String applicationId; + + @QueryParam("key") + @Schema(description = "The application parameter key.") + private String key; + + @QueryParam("type") + @Schema(description = "The application parameter type.", implementation = String.class, type = SchemaType.ARRAY) + private List type; + + @DefaultValue("0") + @QueryParam("pageNumber") + @Schema(description = "The number of page.") + private Integer pageNumber = 0; + + @DefaultValue("100") + @QueryParam("pageSize") + @Schema(description = "The size of page") + private Integer pageSize = 100; +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterPageResultDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterPageResultDTO.java new file mode 100644 index 0000000..e665b36 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterPageResultDTO.java @@ -0,0 +1,8 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class ApplicationParameterPageResultDTO extends PageResultDTO { + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterSearchCriteriaDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterSearchCriteriaDTO.java new file mode 100644 index 0000000..a471681 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterSearchCriteriaDTO.java @@ -0,0 +1,44 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import java.util.List; + +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.QueryParam; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class ApplicationParameterSearchCriteriaDTO { + + @QueryParam("applicationId") + @Schema(description = "The application parameter id.") + private String applicationId; + + @QueryParam("key") + @Schema(description = "The application parameter key.") + private String key; + + @QueryParam("name") + @Schema(description = "The application parameter name.") + private String name; + + @QueryParam("type") + @Schema(description = "The application parameter type.") + private List type; + + @DefaultValue("0") + @QueryParam("pageNumber") + @Schema(description = "The number of page.") + private Integer pageNumber = 0; + + @DefaultValue("100") + @QueryParam("pageSize") + @Schema(description = "The size of page") + private Integer pageSize = 100; +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterUpdateDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterUpdateDTO.java new file mode 100644 index 0000000..6f67f21 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationParameterUpdateDTO.java @@ -0,0 +1,23 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +@RegisterForReflection +public class ApplicationParameterUpdateDTO { + + private Object value; + + private String description; + + private String unit; + + private Integer rangeFrom; + + private Integer rangeTo; +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationsPageResultDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationsPageResultDTO.java new file mode 100644 index 0000000..153abca --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ApplicationsPageResultDTO.java @@ -0,0 +1,7 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class ApplicationsPageResultDTO extends PageResultDTO { +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/KeysPageResultDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/KeysPageResultDTO.java new file mode 100644 index 0000000..b3bb145 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/KeysPageResultDTO.java @@ -0,0 +1,7 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class KeysPageResultDTO extends PageResultDTO { +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/KeysSearchCriteriaDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/KeysSearchCriteriaDTO.java new file mode 100644 index 0000000..cba0b35 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/KeysSearchCriteriaDTO.java @@ -0,0 +1,20 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import jakarta.ws.rs.QueryParam; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class KeysSearchCriteriaDTO { + + @QueryParam("applicationId") + @Schema(description = "The application parameter id.") + private String applicationId; + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/PageResultDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/PageResultDTO.java new file mode 100644 index 0000000..7d0a242 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/PageResultDTO.java @@ -0,0 +1,42 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import java.util.List; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@RegisterForReflection +public class PageResultDTO { + + /** + * The total elements in the database. + */ + @Schema(description = "The total elements in the resource.") + private long totalElements; + + /** + * The page index. + */ + private int number; + + /** + * The page size. + */ + private int size; + + /** + * The number of pages. + */ + private long totalPages; + + /** + * The data stream. + */ + private List stream; + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ParameterHistoryCountDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ParameterHistoryCountDTO.java new file mode 100644 index 0000000..86147e7 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/ParameterHistoryCountDTO.java @@ -0,0 +1,20 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import java.time.LocalDateTime; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +@RegisterForReflection +public class ParameterHistoryCountDTO { + + private LocalDateTime creationDate; + + private Long count; + +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/RestExceptionDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/RestExceptionDTO.java new file mode 100644 index 0000000..bcb6b03 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/RestExceptionDTO.java @@ -0,0 +1,35 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@RegisterForReflection +public class RestExceptionDTO { + + /** + * The error code. + */ + private String errorCode; + + /** + * The message. + */ + private String message; + + /** + * The error parameters. + */ + private List parameters; + + /** + * The named parameters. + */ + private Map namedParameters = new HashMap<>(); +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/dtos/TraceableDTO.java b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/TraceableDTO.java new file mode 100644 index 0000000..1ccd729 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/dtos/TraceableDTO.java @@ -0,0 +1,95 @@ +package io.github.onecx.parameters.rs.internal.dtos; + +import java.time.OffsetDateTime; +import java.util.Objects; +import java.util.UUID; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@RegisterForReflection +public class TraceableDTO { + + /** + * The ID. + */ + private String id = UUID.randomUUID().toString(); + + /** + * Optimistic lock version + */ + private Integer modificationCount; + + /** + * The creation date. + */ + private OffsetDateTime creationDate; + /** + * The creation user. + */ + private String creationUser; + /** + * The modification date. + */ + private OffsetDateTime modificationDate; + /** + * The modification user. + */ + private String modificationUser; + + /** + * Overwrite the {@code toString} method for the logger. + * + * @return the className:ID + */ + @Override + public String toString() { + return this.getClass().getSimpleName() + ":" + getId(); + } + + /** + * {@inheritDoc } + * + * @see Object#equals(Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + TraceableDTO other = (TraceableDTO) obj; + Object guid = getId(); + Object otherGuid = other.getId(); + + if (guid == null) { + if (otherGuid != null) { + return false; + } else { + return super.equals(obj); + } + } else + return guid.equals(otherGuid); + } + + /** + * {@inheritDoc } + * + * @see Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Objects.hashCode(getId()); + return result; + } +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/mappers/ApplicationParameterDataMapper.java b/src/main/java/io/github/onecx/parameters/rs/internal/mappers/ApplicationParameterDataMapper.java new file mode 100644 index 0000000..ed616a4 --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/mappers/ApplicationParameterDataMapper.java @@ -0,0 +1,50 @@ +package io.github.onecx.parameters.rs.internal.mappers; + +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.InjectionStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; + +import io.github.onecx.parameters.domain.models.ApplicationParameterData; +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterCreateDTO; +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterDTO; +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterUpdateDTO; + +@Mapper(injectionStrategy = InjectionStrategy.CONSTRUCTOR) +public interface ApplicationParameterDataMapper { + + default ApplicationParameterData update(ApplicationParameterUpdateDTO dto, ApplicationParameterData entity) { + if (StringUtils.isNotBlank(dto.getUnit())) { + entity.setUnit(dto.getUnit()); + } + if (dto.getRangeFrom() != null) { + entity.setRangeFrom(dto.getRangeFrom()); + } + if (dto.getRangeTo() != null) { + entity.setRangeTo(dto.getRangeTo()); + } + return entity; + } + + default ApplicationParameterData create(ApplicationParameterUpdateDTO dto, String applicationParameterId) { + ApplicationParameterData entity = new ApplicationParameterData(); + entity.setApplicationParameterGuid(applicationParameterId); + entity.setUnit(dto.getUnit()); + entity.setRangeFrom(dto.getRangeFrom()); + entity.setRangeTo(dto.getRangeTo()); + return entity; + } + + default ApplicationParameterData create(ApplicationParameterCreateDTO dto, String applicationParameterId) { + ApplicationParameterData entity = new ApplicationParameterData(); + entity.setApplicationParameterGuid(applicationParameterId); + entity.setUnit(dto.getUnit()); + entity.setRangeFrom(dto.getRangeFrom()); + entity.setRangeTo(dto.getRangeTo()); + return entity; + } + + @Mapping(target = "id", ignore = true) + ApplicationParameterDTO map(ApplicationParameterData entity, @MappingTarget ApplicationParameterDTO dto); +} diff --git a/src/main/java/io/github/onecx/parameters/rs/internal/mappers/ApplicationParameterInternalMapper.java b/src/main/java/io/github/onecx/parameters/rs/internal/mappers/ApplicationParameterInternalMapper.java new file mode 100644 index 0000000..b7b055c --- /dev/null +++ b/src/main/java/io/github/onecx/parameters/rs/internal/mappers/ApplicationParameterInternalMapper.java @@ -0,0 +1,61 @@ +package io.github.onecx.parameters.rs.internal.mappers; + +import java.util.List; + +import org.mapstruct.*; +import org.tkit.quarkus.jpa.daos.PageResult; +import org.tkit.quarkus.rs.mappers.OffsetDateTimeMapper; + +import io.github.onecx.parameters.domain.criteria.ApplicationParameterHistorySearchCriteria; +import io.github.onecx.parameters.domain.criteria.ApplicationParameterSearchCriteria; +import io.github.onecx.parameters.domain.criteria.KeysSearchCriteria; +import io.github.onecx.parameters.domain.models.ApplicationParameter; +import io.github.onecx.parameters.domain.models.ApplicationParameterHistory; +import io.github.onecx.parameters.domain.models.ParameterHistoryCountTuple; +import io.github.onecx.parameters.rs.internal.dtos.*; + +@Mapper(injectionStrategy = InjectionStrategy.CONSTRUCTOR, uses = { OffsetDateTimeMapper.class }) +public interface ApplicationParameterInternalMapper { + + KeysSearchCriteria map(KeysSearchCriteriaDTO dto); + + KeysPageResultDTO keys(PageResult page); + + ApplicationsPageResultDTO apps(PageResult page); + + ApplicationParameterHistorySearchCriteria map(ApplicationParameterHistorySearchCriteriaDTO dto); + + ApplicationParameterSearchCriteria map(ApplicationParameterSearchCriteriaDTO dto); + + ApplicationParameterHistoryPageResultDTO mapHistory(PageResult page); + + ApplicationParameterHistoryDTO mapHistory(ApplicationParameterHistory applicationParameterHistory); + + List mapCountList(List count); + + ApplicationParameterPageResultDTO map(PageResult page); + + ApplicationParameterDTO map(ApplicationParameter applicationParameter); + + @Named("setValue") + default String objectToSetValue(Object value) { + if (value != null) { + return value.toString(); + } + return null; + } + + default void update(ApplicationParameterUpdateDTO dto, ApplicationParameter applicationParameter) { + if (dto == null) { + return; + } + if (dto.getDescription() != null && !dto.getDescription().isEmpty()) { + applicationParameter.setDescription(dto.getDescription()); + } + if (dto.getValue() != null) { + applicationParameter.setSetValue(objectToSetValue(dto.getValue())); + } + } + + RestExceptionDTO createRestException(String errorCode, String message); +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..2af77e0 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,20 @@ +quarkus.banner.enabled=false +quarkus.hibernate-orm.database.generation=validate +quarkus.liquibase.migrate-at-start=true +quarkus.liquibase.validate-on-migrate=true + +onecx.parameters.history.scheduler.duration=P7D +onecx.parameters.history.scheduler.expr=0 15 2 * * ? + +# TEST +%test.quarkus.scheduler.enabled=false + +# set JSON console formatting extension. +%test.tkit.log.json.enabled=false + +# disable metrics for tests +%test.quarkus.datasource.metrics.enabled=false +%test.quarkus.hibernate-orm.metrics.enabled=false +# disable tracing for the tests +%test.quarkus.jaeger.enabled=false + diff --git a/src/main/resources/db/changeLog.xml b/src/main/resources/db/changeLog.xml new file mode 100644 index 0000000..046a8b8 --- /dev/null +++ b/src/main/resources/db/changeLog.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/main/resources/db/createApplicationParameterDataTable.xml b/src/main/resources/db/createApplicationParameterDataTable.xml new file mode 100644 index 0000000..94454b8 --- /dev/null +++ b/src/main/resources/db/createApplicationParameterDataTable.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/createApplicationParametersTable.xml b/src/main/resources/db/createApplicationParametersTable.xml new file mode 100644 index 0000000..e6160fe --- /dev/null +++ b/src/main/resources/db/createApplicationParametersTable.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/createTables.xml b/src/main/resources/db/createTables.xml new file mode 100644 index 0000000..14b8a0d --- /dev/null +++ b/src/main/resources/db/createTables.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/data-import-tables.xml b/src/main/resources/db/data-import-tables.xml new file mode 100644 index 0000000..32af98f --- /dev/null +++ b/src/main/resources/db/data-import-tables.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/job-table.xml b/src/main/resources/db/job-table.xml new file mode 100644 index 0000000..06de375 --- /dev/null +++ b/src/main/resources/db/job-table.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/refactoring-model.xml b/src/main/resources/db/refactoring-model.xml new file mode 100644 index 0000000..c247a49 --- /dev/null +++ b/src/main/resources/db/refactoring-model.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAOExceptionTest.java b/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAOExceptionTest.java new file mode 100644 index 0000000..e229449 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAOExceptionTest.java @@ -0,0 +1,75 @@ +package io.github.onecx.parameters.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.mockito.Mockito; +import org.tkit.quarkus.jpa.exceptions.DAOException; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class ApplicationParameterDAOExceptionTest { + + @Inject + ApplicationParameterDAO dao; + + @InjectMock + EntityManager em; + + @BeforeEach + void beforeAll() { + Mockito.when(em.getCriteriaBuilder()).thenThrow(new RuntimeException("Test technical error exception")); + } + + @Test + void findApplicationParametersByKeysTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.findApplicationParametersByKeys(null, null)); + Assertions.assertEquals(ApplicationParameterDAO.ErrorKeys.FIND_PARAMETER_BY_APPLICATION_ID_AND_PARAMETER_KEY_FAILED, + exc.key); + } + + @Test + void findByApplicationIdAndParameterAndTypeKeysTest() { + var exc = Assertions.assertThrows(DAOException.class, + () -> dao.findByApplicationIdAndParameterAndTypeKeys(null, null, null)); + Assertions.assertEquals(ApplicationParameterDAO.ErrorKeys.FIND_PARAMETERS_BY_APPLICATION_AND_PARAMETER_KEY_TYPE_FAILED, + exc.key); + } + + @Test + void findByApplicationIdAndParameterKeysTest() { + var exc = Assertions.assertThrows(DAOException.class, + () -> dao.findByApplicationIdAndParameterKeys(null, null)); + Assertions.assertEquals(ApplicationParameterDAO.ErrorKeys.FIND_PARAMETERS_BY_APPLICATION_AND_PARAMETER_KEYS_FAILED, + exc.key); + } + + @Test + void findAllByApplicationIdTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.findAllByApplicationId(null)); + Assertions.assertEquals(ApplicationParameterDAO.ErrorKeys.FIND_ALL_PARAMETERS_BY_APPLICATION_ID_FAILED, exc.key); + } + + @Test + void searchByCriteriaTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.searchByCriteria(null)); + Assertions.assertEquals(ApplicationParameterDAO.ErrorKeys.FIND_ALL_PARAMETERS_FAILED, exc.key); + } + + @Test + void searchAllApplicationsTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.searchAllApplications()); + Assertions.assertEquals(ApplicationParameterDAO.ErrorKeys.FIND_ALL_APPLICATIONS_FAILED, exc.key); + } + + @Test + void searchAllKeysTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.searchAllKeys(null)); + Assertions.assertEquals(ApplicationParameterDAO.ErrorKeys.FIND_ALL_KEYS_FAILED, exc.key); + } +} diff --git a/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAOTest.java b/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAOTest.java new file mode 100644 index 0000000..e4671a9 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDAOTest.java @@ -0,0 +1,41 @@ +package io.github.onecx.parameters.domain.daos; + +import java.util.Map; +import java.util.Set; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.tkit.quarkus.test.WithDBData; + +import io.github.onecx.parameters.domain.di.ApplicationParameterDataImportService; +import io.github.onecx.parameters.domain.models.ApplicationParameter; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class ApplicationParameterDAOTest { + + @Inject + ApplicationParameterDAO dao; + + @Test + @WithDBData(value = { "data/parameters-dao-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void findApplicationParametersByKeysTest() { + Set data = Set.of("app1__param", "app1__integer_param"); + Map result = dao.findApplicationParametersByKeys(data, + ApplicationParameterDataImportService.KEY_SEPARATOR); + Assertions.assertNotNull(result); + Assertions.assertEquals(2, result.size()); + } + + @Test + @WithDBData(value = { "data/parameters-dao-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void findApplicationParametersByKeysNoResultTest() { + Set data = Set.of("appx__paramx"); + Map result = dao.findApplicationParametersByKeys(data, + ApplicationParameterDataImportService.KEY_SEPARATOR); + Assertions.assertNotNull(result); + Assertions.assertEquals(0, result.size()); + } +} diff --git a/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDataDAOExceptionTest.java b/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDataDAOExceptionTest.java new file mode 100644 index 0000000..37e9de4 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterDataDAOExceptionTest.java @@ -0,0 +1,35 @@ +package io.github.onecx.parameters.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.mockito.Mockito; +import org.tkit.quarkus.jpa.exceptions.DAOException; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class ApplicationParameterDataDAOExceptionTest { + + @Inject + ApplicationParameterDataDAO dao; + + @InjectMock + EntityManager em; + + @BeforeEach + void beforeAll() { + Mockito.when(em.getCriteriaBuilder()).thenThrow(new RuntimeException("Test technical error exception")); + } + + @Test + void findApplicationParametersByKeysTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.deleteByParameterId("id")); + Assertions.assertEquals(ApplicationParameterDataDAO.Errors.FAILED_TO_DELETE_BY_GUID_QUERY, + exc.key); + } +} diff --git a/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterHistoryDAOExceptionTest.java b/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterHistoryDAOExceptionTest.java new file mode 100644 index 0000000..ef6aa26 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/domain/daos/ApplicationParameterHistoryDAOExceptionTest.java @@ -0,0 +1,49 @@ +package io.github.onecx.parameters.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.mockito.Mockito; +import org.tkit.quarkus.jpa.exceptions.DAOException; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class ApplicationParameterHistoryDAOExceptionTest { + + @Inject + ApplicationParameterHistoryDAO dao; + + @InjectMock + EntityManager em; + + @BeforeEach + void beforeAll() { + Mockito.when(em.getCriteriaBuilder()).thenThrow(new RuntimeException("Test technical error exception")); + } + + @Test + void searchByCriteriaTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.searchByCriteria(null)); + Assertions.assertEquals(ApplicationParameterHistoryDAO.ErrorKeys.FIND_ALL_PARAMETERS_HISTORY_FAILED, + exc.key); + } + + @Test + void searchOnlyLatestByCriteriaTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.searchOnlyLatestByCriteria(null)); + Assertions.assertEquals(ApplicationParameterHistoryDAO.ErrorKeys.FIND_ALL_PARAMETERS_HISTORY_FAILED, + exc.key); + } + + @Test + void searchCountsByCriteriaTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.searchCountsByCriteria(null)); + Assertions.assertEquals(ApplicationParameterHistoryDAO.ErrorKeys.FIND_ALL_PARAMETERS_HISTORY_FAILED, + exc.key); + } +} diff --git a/src/test/java/io/github/onecx/parameters/domain/daos/JobDAOExceptionTest.java b/src/test/java/io/github/onecx/parameters/domain/daos/JobDAOExceptionTest.java new file mode 100644 index 0000000..c75487d --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/domain/daos/JobDAOExceptionTest.java @@ -0,0 +1,34 @@ +package io.github.onecx.parameters.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.mockito.Mockito; +import org.tkit.quarkus.jpa.exceptions.DAOException; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class JobDAOExceptionTest { + @Inject + JobDAO dao; + + @InjectMock + EntityManager em; + + @BeforeEach + void beforeAll() { + Mockito.when(em.getCriteriaBuilder()).thenThrow(new RuntimeException("Test technical error exception")); + } + + @Test + void findApplicationParametersByKeysTest() { + var exc = Assertions.assertThrows(DAOException.class, () -> dao.getJob("test")); + Assertions.assertEquals(JobDAO.Error.GET_JOB_FAILED, exc.key); + } + +} diff --git a/src/test/java/io/github/onecx/parameters/domain/di/ApplicationParameterDataImportServiceTest.java b/src/test/java/io/github/onecx/parameters/domain/di/ApplicationParameterDataImportServiceTest.java new file mode 100644 index 0000000..69639c5 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/domain/di/ApplicationParameterDataImportServiceTest.java @@ -0,0 +1,236 @@ +package io.github.onecx.parameters.domain.di; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.tkit.quarkus.dataimport.DataImportConfig; +import org.tkit.quarkus.test.WithDBData; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.github.onecx.parameters.domain.daos.ApplicationParameterDAO; +import io.github.onecx.parameters.domain.di.models.ApplicationParameterDataImport; +import io.github.onecx.parameters.domain.models.ApplicationParameter; +import io.github.onecx.parameters.test.AbstractTest; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) +class ApplicationParameterDataImportServiceTest extends AbstractTest { + + @Inject + ApplicationParameterDataImportService service; + + @Inject + ApplicationParameterDAO dao; + + @Inject + ObjectMapper mapper; + + @Test + void importNoneTest() { + Map metadata = new HashMap<>(); + metadata.put("operation", "CUSTOM_NOT_SUPPORTED"); + DataImportConfig config = new DataImportConfig() { + @Override + public Map getMetadata() { + return metadata; + } + }; + service.importData(config); + + List params = dao.findAll().collect(Collectors.toList()); + Assertions.assertNotNull(params); + Assertions.assertEquals(9, params.size()); + + config.getMetadata().put("operation", "NONE"); + + params = dao.findAll().collect(Collectors.toList()); + Assertions.assertNotNull(params); + Assertions.assertEquals(9, params.size()); + } + + @Test + void importCleanInsertTest() { + + ApplicationParameterDataImport param = new ApplicationParameterDataImport(); + param.setDescription("desc"); + param.setName("name-of-name"); + + var data = new HashMap>(); + data.put("app1", Map.of("key1", param)); + data.put("empty", Map.of()); + data.put("null", null); + + service.importData(new DataImportConfig() { + @Override + public Map getMetadata() { + return Map.of("operation", "CLEAN_INSERT"); + } + + @Override + public byte[] getData() { + try { + return mapper.writeValueAsBytes(data); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + + List params = dao.findAll().collect(Collectors.toList()); + Assertions.assertNotNull(params); + Assertions.assertEquals(1, params.size()); + + } + + @Test + void importUpdateTest() { + + ApplicationParameterDataImport param = new ApplicationParameterDataImport(); + param.setDescription("desc"); + param.setName("name-of-name"); + param.setValue("123"); + + ApplicationParameterDataImport emptyValueParam = new ApplicationParameterDataImport(); + emptyValueParam.setDescription(""); + emptyValueParam.setName(""); + emptyValueParam.setValue(""); + + var data = new HashMap>(); + data.put("app1", + Map.of( + "param", param, + "integer_param", new ApplicationParameterDataImport(), + "boolean_param", emptyValueParam)); + data.put("app_new", Map.of("key_new", param)); + data.put("empty", Map.of()); + data.put("null", null); + + service.importData(new DataImportConfig() { + @Override + public Map getMetadata() { + return Map.of("operation", "UPDATE"); + } + + @Override + public byte[] getData() { + try { + return mapper + .writeValueAsBytes(data); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + + List params = dao.findAll().collect(Collectors.toList()); + Assertions.assertNotNull(params); + Assertions.assertEquals(10, params.size()); + List p1 = dao.findByApplicationIdAndParameterKeys("app1", List.of("param")); + Assertions.assertNotNull(p1); + Assertions.assertEquals(1, p1.size()); + Assertions.assertEquals(param.getName(), p1.get(0).getName()); + Assertions.assertEquals(param.getValue(), p1.get(0).getImportValue()); + + List p2 = dao.findByApplicationIdAndParameterKeys("app_new", List.of("key_new")); + Assertions.assertNotNull(p2); + Assertions.assertEquals(1, p2.size()); + Assertions.assertEquals(param.getName(), p2.get(0).getName()); + Assertions.assertEquals(param.getValue(), p2.get(0).getImportValue()); + } + + @Test + void importUpdateNoDataToUpdateTest() { + + var data = new HashMap>(); + data.put("empty", Map.of()); + data.put("null", null); + + service.importData(new DataImportConfig() { + @Override + public Map getMetadata() { + return Map.of("operation", "UPDATE"); + } + + @Override + public byte[] getData() { + try { + return mapper + .writeValueAsBytes(data); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + + List params = dao.findAll().collect(Collectors.toList()); + Assertions.assertNotNull(params); + Assertions.assertEquals(9, params.size()); + List p1 = dao.findByApplicationIdAndParameterKeys("app1", List.of("param")); + Assertions.assertNotNull(p1); + Assertions.assertEquals(1, p1.size()); + + } + + @Test + void importEmptyDataTest() { + Assertions.assertDoesNotThrow(() -> { + service.importData(new DataImportConfig() { + @Override + public Map getMetadata() { + return Map.of("operation", "UPDATE"); + } + }); + + service.importData(new DataImportConfig() { + @Override + public Map getMetadata() { + return Map.of("operation", "UPDATE"); + } + + @Override + public byte[] getData() { + return new byte[] {}; + } + }); + + service.importData(new DataImportConfig() { + @Override + public Map getMetadata() { + return Map.of("operation", "UPDATE"); + } + + @Override + public byte[] getData() { + try { + return mapper.writeValueAsBytes(Map.of()); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + + }); + + var config = new DataImportConfig() { + @Override + public Map getMetadata() { + return Map.of("operation", "UPDATE"); + } + + @Override + public byte[] getData() { + return new byte[] { 0 }; + } + }; + Assertions.assertThrows(RuntimeException.class, () -> service.importData(config)); + + } +} diff --git a/src/test/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryServiceExceptionTest.java b/src/test/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryServiceExceptionTest.java new file mode 100644 index 0000000..0e9961d --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryServiceExceptionTest.java @@ -0,0 +1,48 @@ +package io.github.onecx.parameters.domain.timer; + +import static io.github.onecx.parameters.domain.timer.MaintenanceHistoryService.JOB_ID; + +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.mockito.Mockito; +import org.tkit.quarkus.jpa.exceptions.DAOException; + +import io.github.onecx.parameters.domain.daos.ApplicationParameterHistoryDAO; +import io.github.onecx.parameters.domain.daos.JobDAO; +import io.github.onecx.parameters.domain.di.models.Job; +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class MaintenanceHistoryServiceExceptionTest { + + @Inject + MaintenanceHistoryService service; + + @InjectMock + EntityManager em; + + @InjectMock + JobDAO dao; + + @BeforeEach + void beforeAll() { + Mockito.when(dao.getJob(JOB_ID)).thenReturn(new Job()); + Mockito.when(em.getCriteriaBuilder()) + .thenThrow(new RuntimeException("Test technical error exception")); + } + + @Test + void testDaoException() { + var exc = Assertions.assertThrows(Exception.class, () -> { + service.maintenanceHistoryData(); + }); + var de = Assertions.assertInstanceOf(DAOException.class, exc); + Assertions.assertEquals(ApplicationParameterHistoryDAO.ErrorKeys.DELETE_PARAMETER_HISTORY_OLDER_THAN_FAILED, de.key); + } + +} diff --git a/src/test/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryServiceTest.java b/src/test/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryServiceTest.java new file mode 100644 index 0000000..aa8d7f4 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/domain/timer/MaintenanceHistoryServiceTest.java @@ -0,0 +1,55 @@ +package io.github.onecx.parameters.domain.timer; + +import java.util.List; +import java.util.stream.Collectors; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.tkit.quarkus.test.WithDBData; + +import io.github.onecx.parameters.domain.daos.ApplicationParameterHistoryDAO; +import io.github.onecx.parameters.domain.daos.JobDAO; +import io.github.onecx.parameters.domain.models.ApplicationParameterHistory; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@WithDBData(value = { "data/history-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) +class MaintenanceHistoryServiceTest { + + @Inject + MaintenanceHistoryService service; + + @Inject + ApplicationParameterHistoryDAO dao; + + @Inject + JobDAO jobDAO; + + @Test + @Order(1) + void maintenanceHistoryDataTest() { + service.maintenanceHistoryData(); + List result = dao.findAll().collect(Collectors.toList()); + Assertions.assertNotNull(result); + Assertions.assertEquals(2, result.size()); + } + + @Test + @Order(2) + void maintenanceHistoryNoDataTest() { + var result = dao.findAll().collect(Collectors.toList()); + Assertions.assertNotNull(result); + Assertions.assertEquals(10, result.size()); + + jobDAO.deleteQueryById(MaintenanceHistoryService.JOB_ID); + service.maintenanceHistoryData(); + + result = dao.findAll().collect(Collectors.toList()); + Assertions.assertNotNull(result); + Assertions.assertEquals(10, result.size()); + } + +} diff --git a/src/test/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2Test.java b/src/test/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2Test.java new file mode 100644 index 0000000..0601d8a --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2Test.java @@ -0,0 +1,234 @@ +package io.github.onecx.parameters.rs.external.v2; + +import static io.restassured.RestAssured.given; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.tkit.quarkus.test.WithDBData; + +import io.github.onecx.parameters.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@TestHTTPEndpoint(ParameterRestControllerV2.class) +class ParameterRestControllerV2Test extends AbstractTest { + + static Stream badQueryParameters() { + return Stream.of( + Arguments.of(Map.of()), + Arguments.of(Map.of("applicationId", "")), + Arguments.of(Map.of("applicationId", "", "parameterKey", "")), + Arguments.of(Map.of("applicationId", "1")), + Arguments.of(Map.of("applicationId", "1", "parameterKey", "")), + Arguments.of(Map.of("parameterKey", "")), + Arguments.of(Map.of("parameterKey", "1")), + Arguments.of(Map.of("parameterKey", "1", "applicationId", ""))); + } + + @ParameterizedTest + @MethodSource("badQueryParameters") + void getParameterBadRequest(Map parameters) { + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParams(parameters) + .get("parameters") + .then() + .statusCode(Response.Status.BAD_REQUEST.getStatusCode()); + } + + static Stream badQueryAllParameters() { + return Stream.of( + Arguments.of(Map.of(), null), + Arguments.of(Map.of(), List.of()), + Arguments.of(Map.of("applicationId", ""), null), + Arguments.of(Map.of("applicationId", ""), List.of()), + Arguments.of(Map.of("applicationId", ""), List.of("x")), + Arguments.of(Map.of("applicationId", "1"), null), + Arguments.of(Map.of("applicationId", "1"), List.of())); + } + + @ParameterizedTest + @MethodSource("badQueryAllParameters") + void getAllParameterBadRequest(Map parameters, List body) { + var tmp = given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParams(parameters); + + if (body != null) { + tmp.body(body); + } + + tmp.post("parameters") + .then() + .statusCode(Response.Status.BAD_REQUEST.getStatusCode()); + } + + @Test + @WithDBData(value = { + "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true, deleteAfterTest = true) + void testParametersNotFound() { + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "applicationId") + .queryParam("parameterKey", "parameterKey") + .get("parameters") + .then() + .statusCode(Response.Status.NOT_FOUND.getStatusCode()); + } + + @Test + @WithDBData(value = { + "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true, deleteAfterTest = true) + void getParameterData() { + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "app1") + .queryParam("parameterKey", "param") + .get("parameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()); + } + + @Test + @WithDBData(value = { + "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true, deleteAfterTest = true) + void getAllParametersData() { + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "app1") + .body(List.of("param")) + .post("parameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()); + } + + static Stream badQueryStringParameters() { + return Stream.of( + Arguments.of(Map.of()), + Arguments.of(Map.of("applicationId", "")), + Arguments.of(Map.of("applicationId", "", "parameterKey", "")), + Arguments.of(Map.of("applicationId", "1")), + Arguments.of(Map.of("applicationId", "1", "parameterKey", "")), + Arguments.of(Map.of("parameterKey", "")), + Arguments.of(Map.of("parameterKey", "1")), + Arguments.of(Map.of("parameterKey", "1", "applicationId", ""))); + } + + @ParameterizedTest + @MethodSource("badQueryStringParameters") + void getParameterStringDataBadRequest(Map queryParams) { + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParams(queryParams) + .get("stringParameters") + .then() + .statusCode(Response.Status.BAD_REQUEST.getStatusCode()); + } + + @Test + void getParameterStringDataNotFound() { + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "app3") + .queryParam("parameterKey", "string1") + .get("stringParameters") + .then() + .statusCode(Response.Status.NOT_FOUND.getStatusCode()); + } + + @Test + @WithDBData(value = { + "data/parameters-testdata-v2.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true, deleteAfterTest = true) + void getParameterStringData() { + var tmp = given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "app3") + .queryParam("parameterKey", "string") + .get("stringParameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .extract().jsonPath().getList(".").get(0); + Assertions.assertEquals("string-value", tmp); + } + + @Test + @WithDBData(value = { + "data/parameters-testdata-v2.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true, deleteAfterTest = true) + void getParameterLongData() { + var tmp = given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "app3") + .queryParam("parameterKey", "long") + .get("longParameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .extract().jsonPath().getList(".").get(0); + Assertions.assertEquals(2, tmp); + } + + @Test + @WithDBData(value = { + "data/parameters-testdata-v2.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true, deleteAfterTest = true) + void getParameterIntegerData() { + var tmp = given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "app3") + .queryParam("parameterKey", "integer") + .get("integerParameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .extract().jsonPath().getList(".").get(0); + Assertions.assertEquals(2, tmp); + } + + @Test + @WithDBData(value = { + "data/parameters-testdata-v2.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true, deleteAfterTest = true) + void getParameterBooleanData() { + var tmp = given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "app3") + .queryParam("parameterKey", "boolean") + .get("booleanParameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .extract().jsonPath().getList(".").get(0); + Assertions.assertEquals(true, tmp); + } + + @Test + @WithDBData(value = { + "data/parameters-testdata-v2.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true, deleteAfterTest = true) + void getParameterWrongTypeData() { + given() + .when() + .contentType(MediaType.APPLICATION_JSON) + .queryParam("applicationId", "app3") + .queryParam("parameterKey", "type") + .get("integerParameters") + .then() + .statusCode(Response.Status.NOT_FOUND.getStatusCode()); + } +} diff --git a/src/test/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2TestIT.java b/src/test/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2TestIT.java new file mode 100644 index 0000000..56d24d1 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/rs/external/v2/ParameterRestControllerV2TestIT.java @@ -0,0 +1,8 @@ +package io.github.onecx.parameters.rs.external.v2; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class ParameterRestControllerV2TestIT extends ParameterRestControllerV2Test { + +} diff --git a/src/test/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3Test.java b/src/test/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3Test.java new file mode 100644 index 0000000..bf317dc --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3Test.java @@ -0,0 +1,158 @@ +package io.github.onecx.parameters.rs.external.v3; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.util.Map; + +import jakarta.ws.rs.core.Response; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.tkit.quarkus.test.WithDBData; + +import io.github.onecx.parameters.rs.external.v3.models.ParameterInfoDTO; +import io.github.onecx.parameters.rs.external.v3.models.ParametersBucketDTO; +import io.github.onecx.parameters.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.path.json.JsonPath; + +@QuarkusTest +@TestHTTPEndpoint(ParameterRestControllerV3.class) +class ParameterRestControllerV3Test extends AbstractTest { + + @Test + void shouldNotFindParametersWithGivenApplicationId() { + Map applicationParameters = given() + .when() + .pathParam("appId", "not-exist") + .get("{appId}/parameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().jsonPath().getMap(".", String.class, String.class); + Assertions.assertTrue(applicationParameters.isEmpty()); + } + + @Test + @WithDBData(value = { "data/parameters-importdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldReturnImportValueParameter() { + Map applicationParameters = given() + .when() + .contentType(APPLICATION_JSON) + .pathParam("appId", "import-app") + .get("{appId}/parameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().jsonPath().getMap(".", String.class, String.class); + Assertions.assertEquals(1, applicationParameters.size()); + Assertions.assertEquals("import-value", applicationParameters.get("importParam")); + } + + @Test + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldReturnParameter() { + Map applicationParameters = given() + .when() + .pathParam("appId", "access-mgmt") + .get("{appId}/parameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().jsonPath().getMap(".", String.class, String.class); + Assertions.assertEquals(1, applicationParameters.size()); + Assertions.assertEquals("KOGITO", applicationParameters.get("ENGINE")); + } + + @Test + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldNotReturnParameterWithNullSetValue() { + JsonPath applicationParameters = given() + .when() + .pathParam("appId", "access-mgmt") + .get("{appId}/parameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body() + .jsonPath(); + Assertions.assertNull(applicationParameters.get("COUNTER")); + } + + @Test + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldCreateNewParameter() { + ParametersBucketDTO parametersBucketDTO = new ParametersBucketDTO(); + ParameterInfoDTO parameterInfoDTO1 = new ParameterInfoDTO(); + parameterInfoDTO1.setCount(1L); + parameterInfoDTO1.setCurrentValue("DefaultValue"); + parameterInfoDTO1.setDefaultValue("DefaultValue"); + parameterInfoDTO1.setType("STRING"); + parametersBucketDTO.getParameters().put("testKey", parameterInfoDTO1); + given() + .contentType(APPLICATION_JSON) + .body(parametersBucketDTO) + .pathParam("appId", "new-application") + .post("{appId}/history") + .then() + .statusCode(Response.Status.OK.getStatusCode()); + Map applicationParameters = given() + .when() + .pathParam("appId", "new-application") + .get("{appId}/parameters") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().jsonPath().getMap(".", String.class, String.class); + Assertions.assertEquals(0, applicationParameters.size()); + } + + @Test + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldUpdateParameters() { + ParametersBucketDTO parametersBucketDTO = new ParametersBucketDTO(); + ParameterInfoDTO parameterInfoDTO1 = new ParameterInfoDTO(); + parameterInfoDTO1.setCount(2L); + parameterInfoDTO1.setCurrentValue("10"); + parameterInfoDTO1.setDefaultValue("10"); + parameterInfoDTO1.setType("INTEGER"); + parametersBucketDTO.getParameters().put("COUNTER", parameterInfoDTO1); + given() + .contentType(APPLICATION_JSON) + .body(parametersBucketDTO) + .pathParam("appId", "access-mgmt") + .post("{appId}/history") + .then() + .statusCode(Response.Status.OK.getStatusCode()); + } + + @Test + void bucketRequestEmptyDTO() { + given() + .contentType(APPLICATION_JSON) + .pathParam("appId", "test") + .post("{appId}/history") + .then() + .statusCode(Response.Status.OK.getStatusCode()); + } + + @Test + void bucketRequestNoParametersDTO() { + ParametersBucketDTO parametersBucketDTO = new ParametersBucketDTO(); + given() + .contentType(APPLICATION_JSON) + .body(parametersBucketDTO) + .pathParam("appId", "test") + .post("{appId}/history") + .then() + .statusCode(Response.Status.OK.getStatusCode()); + } + +} diff --git a/src/test/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3TestIT.java b/src/test/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3TestIT.java new file mode 100644 index 0000000..ef0467d --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/rs/external/v3/ParameterRestControllerV3TestIT.java @@ -0,0 +1,7 @@ +package io.github.onecx.parameters.rs.external.v3; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class ParameterRestControllerV3TestIT extends ParameterRestControllerV3Test { +} diff --git a/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestControllerIT.java b/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestControllerIT.java new file mode 100644 index 0000000..2eda005 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestControllerIT.java @@ -0,0 +1,8 @@ +package io.github.onecx.parameters.rs.internal; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class ApplicationParameterHistoryRestControllerIT extends ApplicationParameterHistoryRestControllerTest { + +} diff --git a/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestControllerTest.java b/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestControllerTest.java new file mode 100644 index 0000000..f154a98 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterHistoryRestControllerTest.java @@ -0,0 +1,155 @@ +package io.github.onecx.parameters.rs.internal; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.ws.rs.core.Response; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.tkit.quarkus.test.WithDBData; + +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterHistoryDTO; +import io.github.onecx.parameters.rs.internal.dtos.PageResultDTO; +import io.github.onecx.parameters.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@TestHTTPEndpoint(ApplicationParameterHistoryRestController.class) +@WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) +class ApplicationParameterHistoryRestControllerTest extends AbstractTest { + + @Test + void shouldFindAllParametersHistoryWithoutCriteria() { + var pageResultDTO = given() + .get() + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract().body().as(PageResultDTO.class); + + Assertions.assertEquals(6, pageResultDTO.getStream().size()); + Assertions.assertEquals(Long.valueOf(6), pageResultDTO.getTotalElements()); + + } + + static Stream findByCriteriaTestData() { + return Stream.of( + Arguments.of(Map.of(), 6), + Arguments.of(Map.of("applicationId", "", "key", "", "type", ""), 0), + Arguments.of(Map.of("applicationId", "app0", "key", "key0", "type", "type0"), 0), + Arguments.of(Map.of("applicationId", "access-mgmt"), 2), + Arguments.of(Map.of("applicationId", "app0"), 0), + Arguments.of(Map.of("applicationId", "app1"), 1), + Arguments.of(Map.of("applicationId", "app2"), 3)); + } + + @ParameterizedTest + @MethodSource("findByCriteriaTestData") + void shouldFindParametersHistoryByCriteria(Map queryParams, Integer expectedArraySize) { + var pageResultDTO = given() + .when() + .queryParams(queryParams) + .get() + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(PageResultDTO.class); + Assertions.assertEquals(expectedArraySize, pageResultDTO.getStream().size()); + } + + static Stream findByCriteriaTestDataQueryLatest() { + return Stream.of( + Arguments.of(Map.of(), 0), + Arguments.of(Map.of("applicationId", "access-mgmt"), 0), + Arguments.of(Map.of("applicationId", "", "key", ""), 0), + Arguments.of(Map.of("applicationId", "", "key", "key1"), 0), + Arguments.of(Map.of("applicationId", ""), 0), + Arguments.of(Map.of("applicationId", "app0"), 0), + Arguments.of(Map.of("applicationId", "app1"), 0), + Arguments.of(Map.of("applicationId", "app2"), 0)); + } + + @ParameterizedTest + @MethodSource("findByCriteriaTestDataQueryLatest") + void shouldFindParametersHistoryByCriteriaQueryLatest(Map queryParams, Integer expectedArraySize) { + var pageResultDTO = given() + .when() + .queryParams(queryParams) + .get("latest") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(PageResultDTO.class); + Assertions.assertEquals(expectedArraySize, pageResultDTO.getStream().size()); + } + + @Test + void getApplicationParametersHistoryByIdNoFoundTest() { + given() + .when() + .pathParam("id", "not-id") + .get("{id}") + .then() + .statusCode(Response.Status.NOT_FOUND.getStatusCode()); + } + + static Stream getApplicationParametersHistoryByIds() { + return Stream.of( + Arguments.of("1", "access-mgmt"), + Arguments.of("2", "access-mgmt"), + Arguments.of("h1", "app1"), + Arguments.of("h2", "app2")); + } + + @ParameterizedTest + @MethodSource("getApplicationParametersHistoryByIds") + void getApplicationParametersHistoryById(String id, String applicationId) { + var result = given() + .when() + .pathParam("id", id) + .get("{id}") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(ApplicationParameterHistoryDTO.class); + Assertions.assertNotNull(result); + Assertions.assertEquals(id, result.getId()); + Assertions.assertEquals(applicationId, result.getApplicationId()); + } + + static Stream findCountByCriteriaTestData() { + return Stream.of( + Arguments.of(Map.of(), 6), + Arguments.of(Map.of("applicationId", "", "key", ""), 6), + Arguments.of(Map.of("applicationId", "", "key", "key1"), 1), + Arguments.of(Map.of("applicationId", "access-mgmt"), 2), + Arguments.of(Map.of("applicationId", "app0"), 0), + Arguments.of(Map.of("applicationId", "app1"), 1), + Arguments.of(Map.of("applicationId", "app2"), 3)); + } + + @ParameterizedTest + @MethodSource("findCountByCriteriaTestData") + void getCountsByCriteriaTest(Map queryParams, Integer expectedArraySize) { + var tmp = given() + .when() + .queryParams(queryParams) + .get("counts") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .extract().jsonPath(); + + Assertions.assertEquals(expectedArraySize, tmp.getList(".").size()); + } +} diff --git a/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestControllerTest.java b/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestControllerTest.java new file mode 100644 index 0000000..6d139e9 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestControllerTest.java @@ -0,0 +1,422 @@ +package io.github.onecx.parameters.rs.internal; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.jboss.resteasy.util.HttpHeaderNames.CONTENT_TYPE; + +import java.util.Map; +import java.util.stream.Stream; + +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.tkit.quarkus.test.WithDBData; + +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterCreateDTO; +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterDTO; +import io.github.onecx.parameters.rs.internal.dtos.ApplicationParameterUpdateDTO; +import io.github.onecx.parameters.rs.internal.dtos.PageResultDTO; +import io.github.onecx.parameters.test.AbstractTest; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@TestHTTPEndpoint(ApplicationParameterRestController.class) +class ApplicationParameterRestControllerTest extends AbstractTest { + + static final String PATH_PARAM_ID = "id"; + static final String PATH_PARAM_ID_PATH = "{" + PATH_PARAM_ID + "}"; + + @Test + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldFindAllParametersWithoutCriteria() { + PageResultDTO pageResultDTO = given() + .get() + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .extract().body().as(PageResultDTO.class); + + Assertions.assertEquals(9, pageResultDTO.getStream().size()); + Assertions.assertEquals(Long.valueOf(9), pageResultDTO.getTotalElements()); + + } + + @Test + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void searchAllApplicationsTest() { + PageResultDTO pageResultDTO = given() + .when() + .get("applications") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(PageResultDTO.class); + Assertions.assertEquals(4, pageResultDTO.getStream().size()); + } + + static Stream findAllKeys() { + return Stream.of( + Arguments.of(Map.of(), 9), + Arguments.of(Map.of("applicationId", ""), 9), + Arguments.of(Map.of("applicationId", "app1"), 5)); + } + + @ParameterizedTest + @MethodSource("findAllKeys") + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void searchAllKeysTest(Map queryParams, int expectedArraySize) { + var pageResultDTO = given() + .when() + .queryParams(queryParams) + .get("keys") + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(PageResultDTO.class); + Assertions.assertEquals(expectedArraySize, pageResultDTO.getStream().size()); + } + + static Stream findByCriteriaTestData() { + return Stream.of( + Arguments.of(Map.of("applicationId", ""), 9), + Arguments.of(Map.of("applicationId", "access-mgmt"), 2), + Arguments.of(Map.of("applicationId", "incorrect_app"), 0), + Arguments.of(Map.of("applicationId", "incorrect_app", "key", "", "type", "", "name", ""), 0), + Arguments.of(Map.of("type", "custom,custom2", "name", "custom"), 0), + Arguments.of(Map.of("key", "ENGINE"), 1), + Arguments.of(Map.of("key", "incorrect_key"), 0)); + } + + @ParameterizedTest + @MethodSource("findByCriteriaTestData") + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldFindParametersByCriteria(Map queryParams, Integer expectedArraySize) { + PageResultDTO pageResultDTO = given() + .when() + .queryParams(queryParams) + .get() + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(PageResultDTO.class); + Assertions.assertEquals(expectedArraySize, pageResultDTO.getStream().size()); + } + + @Test + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldFindParameterById() { + ApplicationParameterDTO applicationParameterDTO = given() + .when() + .pathParam(PATH_PARAM_ID, "111") + .get(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(ApplicationParameterDTO.class); + Assertions.assertNotNull(applicationParameterDTO); + Assertions.assertEquals("access-mgmt", applicationParameterDTO.getApplicationId()); + Assertions.assertEquals("ENGINE", applicationParameterDTO.getKey()); + Assertions.assertEquals("KOGITO", applicationParameterDTO.getSetValue()); + Assertions.assertEquals("Engine", applicationParameterDTO.getName()); + Assertions.assertNull(applicationParameterDTO.getDescription()); + } + + @Test + void shouldNotFindParameterById() { + given() + .when() + .pathParam(PATH_PARAM_ID, "150") + .get(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NOT_FOUND.getStatusCode()); + } + + @Test + void shouldNotFindUpdateParameterById() { + ApplicationParameterUpdateDTO applicationParameterUpdateDTO = new ApplicationParameterUpdateDTO(); + applicationParameterUpdateDTO.setValue("JBPM"); + applicationParameterUpdateDTO.setDescription("Test description"); + + given() + .when() + .contentType(APPLICATION_JSON) + .body(applicationParameterUpdateDTO) + .pathParam(PATH_PARAM_ID, "150") + .put(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NOT_FOUND.getStatusCode()); + } + + static Stream updateParameterTestInput() { + return Stream.of( + Arguments.of("app1", "Test description", "GUID1", "JBPM", "DAYS", 0, 100, "DAYS"), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", null, null, null, null), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", "", null, null, null), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", " ", null, null, null), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", "DAYS", null, null, "DAYS"), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", "DAYS", 0, null, "DAYS"), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", "DAYS", null, 100, "DAYS"), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", "DAYS", 0, 100, "DAYS"), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", null, 0, null, null), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", null, null, 100, null), + Arguments.of("access-mgmt", "Test description", "111", "JBPM", null, 0, 100, null)); + } + + @ParameterizedTest + @MethodSource("updateParameterTestInput") + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldUpdateParameterTest(String appId, String desc, String id, String value, String unit, Integer from, Integer to, + String checkUnit) { + var request = new ApplicationParameterUpdateDTO(); + request.setValue(value); + request.setDescription(desc); + request.setUnit(unit); + request.setRangeTo(to); + request.setRangeFrom(from); + + given() + .body(request) + .contentType(APPLICATION_JSON) + .when() + .pathParam(PATH_PARAM_ID, id) + .put(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NO_CONTENT.getStatusCode()); + + var dto = given() + .when() + .pathParam(PATH_PARAM_ID, id) + .get(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .header(CONTENT_TYPE, APPLICATION_JSON) + .extract() + .body().as(ApplicationParameterDTO.class); + Assertions.assertNotNull(dto); + Assertions.assertEquals(appId, dto.getApplicationId()); + Assertions.assertEquals(value, dto.getSetValue()); + Assertions.assertEquals(desc, dto.getDescription()); + Assertions.assertEquals(checkUnit, dto.getUnit()); + Assertions.assertEquals(from, dto.getRangeFrom()); + Assertions.assertEquals(to, dto.getRangeTo()); + } + + static Stream incorrectValueForStringParameter() { + return Stream.of( + Arguments.of("111", 1000, "Test description"), + Arguments.of("111", true, "Test description")); + } + + @ParameterizedTest + @MethodSource("incorrectValueForStringParameter") + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldNotUpdateStringParameterWithIncorrectValue(String id, Object wrongValue, String description) { + ApplicationParameterUpdateDTO applicationParameterUpdateDTO = new ApplicationParameterUpdateDTO(); + applicationParameterUpdateDTO.setValue(wrongValue); + applicationParameterUpdateDTO.setDescription(description); + given() + .body(applicationParameterUpdateDTO) + .contentType(APPLICATION_JSON) + .when() + .pathParam(PATH_PARAM_ID, id) + .put(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NO_CONTENT.getStatusCode()); + ApplicationParameterDTO applicationParameterDTO = given() + .when() + .pathParam(PATH_PARAM_ID, id) + .get(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(ApplicationParameterDTO.class); + Assertions.assertNotNull(applicationParameterDTO); + Assertions.assertEquals(String.valueOf(wrongValue), applicationParameterDTO.getSetValue()); + Assertions.assertEquals(description, applicationParameterDTO.getDescription()); + } + + static Stream incorrectValueForIntegerParameter() { + return Stream.of( + Arguments.of("112", "incorrectValue", "Test description"), + Arguments.of("112", false, "Test description")); + } + + @ParameterizedTest + @MethodSource("incorrectValueForIntegerParameter") + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldNotUpdateIntegerParameterWithIncorrectValue(String id, Object wrongValue, String description) { + ApplicationParameterUpdateDTO applicationParameterUpdateDTO = new ApplicationParameterUpdateDTO(); + applicationParameterUpdateDTO.setValue(wrongValue); + applicationParameterUpdateDTO.setDescription(description); + + given() + .body(applicationParameterUpdateDTO) + .contentType(APPLICATION_JSON) + .when() + .pathParam(PATH_PARAM_ID, id) + .put(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NO_CONTENT.getStatusCode()); + ApplicationParameterDTO applicationParameterDTO = given() + .when() + .pathParam(PATH_PARAM_ID, id) + .get(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(ApplicationParameterDTO.class); + Assertions.assertNotNull(applicationParameterDTO); + Assertions.assertEquals(String.valueOf(wrongValue), applicationParameterDTO.getSetValue()); + Assertions.assertEquals(description, applicationParameterDTO.getDescription()); + } + + static Stream incorrectValueForBooleanParameter() { + return Stream.of( + Arguments.of("113", "incorrectValue", "Test description"), + Arguments.of("113", 1000, "Test description")); + } + + @ParameterizedTest + @MethodSource("incorrectValueForBooleanParameter") + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void shouldNotUpdateBooleanParameterWithIncorrectValue(String id, Object wrongValue, String description) { + ApplicationParameterUpdateDTO applicationParameterUpdateDTO = new ApplicationParameterUpdateDTO(); + applicationParameterUpdateDTO.setValue(wrongValue); + applicationParameterUpdateDTO.setDescription(description); + + given() + .body(applicationParameterUpdateDTO) + .contentType(APPLICATION_JSON) + .when() + .pathParam(PATH_PARAM_ID, id) + .put(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NO_CONTENT.getStatusCode()); + ApplicationParameterDTO applicationParameterDTO = given() + .when() + .pathParam(PATH_PARAM_ID, id) + .get(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .contentType(APPLICATION_JSON) + .extract() + .body().as(ApplicationParameterDTO.class); + Assertions.assertNotNull(applicationParameterDTO); + Assertions.assertEquals(String.valueOf(wrongValue), applicationParameterDTO.getSetValue()); + Assertions.assertEquals(description, applicationParameterDTO.getDescription()); + } + + static Stream createParameterTestInput() { + return Stream.of( + Arguments.of("app_10", "description", "key_10", "value_10", null, null, null, null), + Arguments.of("app_10", "description", "key_11", "value_10", "", null, null, null), + Arguments.of("app_10", "description", "key_12", "value_10", " ", null, null, null), + Arguments.of("app_10", "description", "key_13", "value_10", "DAYS", null, null, "DAYS"), + Arguments.of("app_10", "description", "key_14", "value_10", "DAYS", 0, null, "DAYS"), + Arguments.of("app_10", "description", "key_15", "value_10", "DAYS", null, 100, "DAYS"), + Arguments.of("app_10", "description", "key_16", "value_10", "DAYS", 0, 100, "DAYS"), + Arguments.of("app_10", "description", "key_17", "value_10", null, 0, null, null), + Arguments.of("app_10", "description", "key_18", "value_10", null, null, 100, null), + Arguments.of("app_10", "description", "key_19", "value_10", null, 0, 100, null)); + } + + @ParameterizedTest + @MethodSource("createParameterTestInput") + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void createParameterTest(String appId, String desc, String key, String value, String unit, Integer from, Integer to, + String checkUnit) { + ApplicationParameterCreateDTO dto = new ApplicationParameterCreateDTO(); + dto.setApplicationId(appId); + dto.setDescription(desc); + dto.setKey(key); + dto.setValue(value); + dto.setUnit(unit); + dto.setRangeFrom(from); + dto.setRangeTo(to); + + String uri = given() + .body(dto) + .contentType(APPLICATION_JSON) + .post() + .then() + .statusCode(Response.Status.CREATED.getStatusCode()) + .extract().header(HttpHeaders.LOCATION); + + ApplicationParameterDTO dto2 = given() + .contentType(APPLICATION_JSON) + .get(uri) + .then() + .statusCode(Response.Status.OK.getStatusCode()) + .extract() + .body().as(ApplicationParameterDTO.class); + + Assertions.assertNotNull(dto2); + Assertions.assertEquals(dto.getApplicationId(), dto2.getApplicationId()); + Assertions.assertEquals(dto.getDescription(), dto2.getDescription()); + Assertions.assertEquals(dto.getKey(), dto2.getKey()); + Assertions.assertEquals(dto.getValue(), dto2.getSetValue()); + Assertions.assertEquals(checkUnit, dto2.getUnit()); + Assertions.assertEquals(dto.getRangeFrom(), dto2.getRangeFrom()); + Assertions.assertEquals(dto.getRangeTo(), dto2.getRangeTo()); + + } + + static Stream deleteParameterTestInput() { + return Stream.of( + Arguments.of("GUID1"), + Arguments.of("GUID2"), + Arguments.of("GUID3"), + Arguments.of("GUID4"), + Arguments.of("GUID5")); + } + + @ParameterizedTest + @MethodSource("deleteParameterTestInput") + @WithDBData(value = { "data/parameters-testdata.xml" }, deleteBeforeInsert = true, rinseAndRepeat = true) + void deleteParameterTest(String id) { + + given() + .contentType(APPLICATION_JSON) + .pathParam(PATH_PARAM_ID, id) + .get(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.OK.getStatusCode()); + + given() + .contentType(APPLICATION_JSON) + .pathParam(PATH_PARAM_ID, id) + .delete(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NO_CONTENT.getStatusCode()); + + given() + .contentType(APPLICATION_JSON) + .pathParam(PATH_PARAM_ID, id) + .get(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NOT_FOUND.getStatusCode()); + + } + + @Test + void deleteNoneExistsParameterTest() { + given() + .contentType(APPLICATION_JSON) + .pathParam(PATH_PARAM_ID, "NONE_EXISTS") + .delete(PATH_PARAM_ID_PATH) + .then() + .statusCode(Response.Status.NO_CONTENT.getStatusCode()); + + } +} diff --git a/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestControllerTestIT.java b/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestControllerTestIT.java new file mode 100644 index 0000000..00f8099 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/rs/internal/ApplicationParameterRestControllerTestIT.java @@ -0,0 +1,7 @@ +package io.github.onecx.parameters.rs.internal; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class ApplicationParameterRestControllerTestIT extends ApplicationParameterRestControllerTest { +} diff --git a/src/test/java/io/github/onecx/parameters/test/AbstractTest.java b/src/test/java/io/github/onecx/parameters/test/AbstractTest.java new file mode 100644 index 0000000..1cdbd57 --- /dev/null +++ b/src/test/java/io/github/onecx/parameters/test/AbstractTest.java @@ -0,0 +1,24 @@ +package io.github.onecx.parameters.test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import io.restassured.RestAssured; +import io.restassured.config.ObjectMapperConfig; +import io.restassured.config.RestAssuredConfig; + +public abstract class AbstractTest { + + static { + RestAssured.config = RestAssuredConfig.config().objectMapperConfig( + ObjectMapperConfig.objectMapperConfig().jackson2ObjectMapperFactory( + (cls, charset) -> { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + return objectMapper; + })); + } + +} diff --git a/src/test/resources/data/history-testdata.xml b/src/test/resources/data/history-testdata.xml new file mode 100644 index 0000000..3694b03 --- /dev/null +++ b/src/test/resources/data/history-testdata.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/data/parameters-dao-testdata.xml b/src/test/resources/data/parameters-dao-testdata.xml new file mode 100644 index 0000000..8e66754 --- /dev/null +++ b/src/test/resources/data/parameters-dao-testdata.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/test/resources/data/parameters-importdata.xml b/src/test/resources/data/parameters-importdata.xml new file mode 100644 index 0000000..b2a0a06 --- /dev/null +++ b/src/test/resources/data/parameters-importdata.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/test/resources/data/parameters-testdata-v2.xml b/src/test/resources/data/parameters-testdata-v2.xml new file mode 100644 index 0000000..76aa78e --- /dev/null +++ b/src/test/resources/data/parameters-testdata-v2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/data/parameters-testdata.xml b/src/test/resources/data/parameters-testdata.xml new file mode 100644 index 0000000..47073d4 --- /dev/null +++ b/src/test/resources/data/parameters-testdata.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file