Merge branch 'master' of github.com:provectus/kafka-ui into ISSUE_754_acl
Conflicts: kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/ReactiveAdminClient.java
This commit is contained in:
commit
a58c2055f5
182 changed files with 6467 additions and 5149 deletions
36
.devcontainer/devcontainer.json
Normal file
36
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "Java",
|
||||
|
||||
"image": "mcr.microsoft.com/devcontainers/java:0-17",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/java:1": {
|
||||
"version": "none",
|
||||
"installMaven": "true",
|
||||
"installGradle": "false"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "java -version",
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions" : [
|
||||
"vscjava.vscode-java-pack",
|
||||
"vscjava.vscode-maven",
|
||||
"vscjava.vscode-java-debug",
|
||||
"EditorConfig.EditorConfig",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"antfu.vite",
|
||||
"ms-kubernetes-tools.vscode-kubernetes-tools",
|
||||
"github.vscode-pull-request-github"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
5
.github/workflows/e2e-automation.yml
vendored
5
.github/workflows/e2e-automation.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
|||
- name: Pull with Docker
|
||||
id: pull_chrome
|
||||
run: |
|
||||
docker pull selenium/standalone-chrome:103.0
|
||||
docker pull selenoid/vnc_chrome:103.0
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
|
@ -52,6 +52,7 @@ jobs:
|
|||
id: compose_app
|
||||
# use the following command until #819 will be fixed
|
||||
run: |
|
||||
docker-compose -f kafka-ui-e2e-checks/docker/selenoid-git.yaml up -d
|
||||
docker-compose -f ./documentation/compose/e2e-tests.yaml up -d
|
||||
- name: Run test suite
|
||||
run: |
|
||||
|
@ -78,7 +79,7 @@ jobs:
|
|||
uses: Sibz/github-status-action@v1.1.6
|
||||
with:
|
||||
authToken: ${{secrets.GITHUB_TOKEN}}
|
||||
context: "Test report"
|
||||
context: "Click Details button to open Allure report"
|
||||
state: "success"
|
||||
sha: ${{ github.sha }}
|
||||
target_url: http://kafkaui-allure-reports.s3-website.eu-central-1.amazonaws.com/${{ github.run_number }}
|
||||
|
|
15
.github/workflows/e2e-checks.yaml
vendored
15
.github/workflows/e2e-checks.yaml
vendored
|
@ -15,20 +15,20 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Configure AWS credentials for Kafka-UI account
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v2
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: eu-central-1
|
||||
- name: Set the values
|
||||
- name: Set up environment
|
||||
id: set_env_values
|
||||
run: |
|
||||
cat "./kafka-ui-e2e-checks/.env.ci" >> "./kafka-ui-e2e-checks/.env"
|
||||
- name: pull docker
|
||||
- name: Pull with Docker
|
||||
id: pull_chrome
|
||||
run: |
|
||||
docker pull selenium/standalone-chrome:103.0
|
||||
docker pull selenoid/vnc_chrome:103.0
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
|
@ -40,12 +40,13 @@ jobs:
|
|||
run: |
|
||||
./mvnw -B -ntp versions:set -DnewVersion=${{ github.event.pull_request.head.sha }}
|
||||
./mvnw -B -V -ntp clean install -Pprod -Dmaven.test.skip=true ${{ github.event.inputs.extraMavenOptions }}
|
||||
- name: compose app
|
||||
- name: Compose with Docker
|
||||
id: compose_app
|
||||
# use the following command until #819 will be fixed
|
||||
run: |
|
||||
docker-compose -f kafka-ui-e2e-checks/docker/selenoid-git.yaml up -d
|
||||
docker-compose -f ./documentation/compose/e2e-tests.yaml up -d
|
||||
- name: e2e run
|
||||
- name: Run test suite
|
||||
run: |
|
||||
./mvnw -B -ntp versions:set -DnewVersion=${{ github.event.pull_request.head.sha }}
|
||||
./mvnw -B -V -ntp -Dsurefire.suiteXmlFiles='src/test/resources/smoke.xml' -f 'kafka-ui-e2e-checks' test -Pprod
|
||||
|
@ -65,7 +66,7 @@ jobs:
|
|||
AWS_S3_BUCKET: 'kafkaui-allure-reports'
|
||||
AWS_REGION: 'eu-central-1'
|
||||
SOURCE_DIR: 'allure-history/allure-results'
|
||||
- name: Post the link to allure report
|
||||
- name: Deploy report to Amazon S3
|
||||
if: always()
|
||||
uses: Sibz/github-status-action@v1.1.6
|
||||
with:
|
||||
|
|
5
.github/workflows/e2e-weekly.yml
vendored
5
.github/workflows/e2e-weekly.yml
vendored
|
@ -23,7 +23,7 @@ jobs:
|
|||
- name: Pull with Docker
|
||||
id: pull_chrome
|
||||
run: |
|
||||
docker pull selenium/standalone-chrome:103.0
|
||||
docker pull selenoid/vnc_chrome:103.0
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
|
@ -39,6 +39,7 @@ jobs:
|
|||
id: compose_app
|
||||
# use the following command until #819 will be fixed
|
||||
run: |
|
||||
docker-compose -f kafka-ui-e2e-checks/docker/selenoid-git.yaml up -d
|
||||
docker-compose -f ./documentation/compose/e2e-tests.yaml up -d
|
||||
- name: Run test suite
|
||||
run: |
|
||||
|
@ -65,7 +66,7 @@ jobs:
|
|||
uses: Sibz/github-status-action@v1.1.6
|
||||
with:
|
||||
authToken: ${{secrets.GITHUB_TOKEN}}
|
||||
context: "Test report"
|
||||
context: "Click Details button to open Allure report"
|
||||
state: "success"
|
||||
sha: ${{ github.sha }}
|
||||
target_url: http://kafkaui-allure-reports.s3-website.eu-central-1.amazonaws.com/${{ github.run_number }}
|
||||
|
|
|
@ -11,14 +11,14 @@ services:
|
|||
test: wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
retries: 10
|
||||
depends_on:
|
||||
kafka0:
|
||||
condition: service_healthy
|
||||
schemaregistry0:
|
||||
condition: service_healthy
|
||||
kafka-connect0:
|
||||
condition: service_healthy
|
||||
kafka0:
|
||||
condition: service_healthy
|
||||
schemaregistry0:
|
||||
condition: service_healthy
|
||||
kafka-connect0:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
KAFKA_CLUSTERS_0_NAME: local
|
||||
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka0:29092
|
||||
|
@ -33,10 +33,10 @@ services:
|
|||
hostname: kafka0
|
||||
container_name: kafka0
|
||||
healthcheck:
|
||||
test: unset JMX_PORT && KAFKA_JMX_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka0 -Dcom.sun.management.jmxremote.rmi.port=9999" && kafka-broker-api-versions --bootstrap-server=localhost:9092
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
test: unset JMX_PORT && KAFKA_JMX_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka0 -Dcom.sun.management.jmxremote.rmi.port=9999" && kafka-broker-api-versions --bootstrap-server=localhost:9092
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
ports:
|
||||
- "9092:9092"
|
||||
- "9997:9997"
|
||||
|
@ -68,12 +68,12 @@ services:
|
|||
- 8085:8085
|
||||
depends_on:
|
||||
kafka0:
|
||||
condition: service_healthy
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "timeout", "1", "curl", "--silent", "--fail", "http://schemaregistry0:8085/subjects"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
test: [ "CMD", "timeout", "1", "curl", "--silent", "--fail", "http://schemaregistry0:8085/subjects" ]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
environment:
|
||||
SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: PLAINTEXT://kafka0:29092
|
||||
SCHEMA_REGISTRY_KAFKASTORE_SECURITY_PROTOCOL: PLAINTEXT
|
||||
|
@ -93,11 +93,11 @@ services:
|
|||
- 8083:8083
|
||||
depends_on:
|
||||
kafka0:
|
||||
condition: service_healthy
|
||||
condition: service_healthy
|
||||
schemaregistry0:
|
||||
condition: service_healthy
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "nc", "127.0.0.1", "8083"]
|
||||
test: [ "CMD", "nc", "127.0.0.1", "8083" ]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
|
@ -118,8 +118,8 @@ services:
|
|||
CONNECT_INTERNAL_VALUE_CONVERTER: org.apache.kafka.connect.json.JsonConverter
|
||||
CONNECT_REST_ADVERTISED_HOST_NAME: kafka-connect0
|
||||
CONNECT_PLUGIN_PATH: "/usr/share/java,/usr/share/confluent-hub-components"
|
||||
# AWS_ACCESS_KEY_ID: ""
|
||||
# AWS_SECRET_ACCESS_KEY: ""
|
||||
# AWS_ACCESS_KEY_ID: ""
|
||||
# AWS_SECRET_ACCESS_KEY: ""
|
||||
|
||||
kafka-init-topics:
|
||||
image: confluentinc/cp-kafka:7.2.1
|
||||
|
@ -127,7 +127,7 @@ services:
|
|||
- ./message.json:/data/message.json
|
||||
depends_on:
|
||||
kafka0:
|
||||
condition: service_healthy
|
||||
condition: service_healthy
|
||||
command: "bash -c 'echo Waiting for Kafka to be ready... && \
|
||||
cub kafka-ready -b kafka0:29092 1 30 && \
|
||||
kafka-topics --create --topic users --partitions 3 --replication-factor 1 --if-not-exists --bootstrap-server kafka0:29092 && \
|
||||
|
@ -142,10 +142,10 @@ services:
|
|||
ports:
|
||||
- 5432:5432
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U dev_user"]
|
||||
test: [ "CMD-SHELL", "pg_isready -U dev_user" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
retries: 5
|
||||
environment:
|
||||
POSTGRES_USER: 'dev_user'
|
||||
POSTGRES_PASSWORD: '12345'
|
||||
|
@ -154,7 +154,7 @@ services:
|
|||
image: ellerbrock/alpine-bash-curl-ssl
|
||||
depends_on:
|
||||
postgres-db:
|
||||
condition: service_healthy
|
||||
condition: service_healthy
|
||||
kafka-connect0:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
|
@ -164,7 +164,7 @@ services:
|
|||
ksqldb:
|
||||
image: confluentinc/ksqldb-server:0.18.0
|
||||
healthcheck:
|
||||
test: ["CMD", "timeout", "1", "curl", "--silent", "--fail", "http://localhost:8088/info"]
|
||||
test: [ "CMD", "timeout", "1", "curl", "--silent", "--fail", "http://localhost:8088/info" ]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
|
@ -174,7 +174,7 @@ services:
|
|||
kafka-connect0:
|
||||
condition: service_healthy
|
||||
schemaregistry0:
|
||||
condition: service_healthy
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- 8088:8088
|
||||
environment:
|
||||
|
|
333
etc/checkstyle/checkstyle-e2e.xml
Normal file
333
etc/checkstyle/checkstyle-e2e.xml
Normal file
|
@ -0,0 +1,333 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
Checkstyle configuration that checks the Google coding conventions from Google Java Style
|
||||
that can be found at https://google.github.io/styleguide/javaguide.html
|
||||
|
||||
Checkstyle is very configurable. Be sure to read the documentation at
|
||||
http://checkstyle.org (or in your downloaded distribution).
|
||||
|
||||
To completely disable a check, just comment it out or delete it from the file.
|
||||
To suppress certain violations please review suppression filters.
|
||||
|
||||
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
|
||||
-->
|
||||
|
||||
<module name = "Checker">
|
||||
<property name="charset" value="UTF-8"/>
|
||||
|
||||
<property name="severity" value="warning"/>
|
||||
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
<!-- Excludes all 'module-info.java' files -->
|
||||
<!-- See https://checkstyle.org/config_filefilters.html -->
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
|
||||
default="checkstyle-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See http://checkstyle.org/config_whitespace.html -->
|
||||
<module name="FileTabCharacter">
|
||||
<property name="eachLine" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="LineLength">
|
||||
<property name="fileExtensions" value="java"/>
|
||||
<property name="max" value="120"/>
|
||||
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
<property name="format"
|
||||
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||
<property name="message"
|
||||
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||
</module>
|
||||
<module name="AvoidEscapedUnicodeCharacters">
|
||||
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||
<property name="allowByTailComment" value="true"/>
|
||||
<property name="allowNonPrintableEscapes" value="true"/>
|
||||
</module>
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
<module name="NoLineWrap">
|
||||
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
|
||||
</module>
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="TEXT"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="NeedBraces">
|
||||
<property name="tokens"
|
||||
value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
|
||||
</module>
|
||||
<module name="LeftCurly">
|
||||
<property name="tokens"
|
||||
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
|
||||
INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
|
||||
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
|
||||
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
|
||||
OBJBLOCK, STATIC_INIT"/>
|
||||
</module>
|
||||
<module name="RightCurly">
|
||||
<property name="id" value="RightCurlySame"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
|
||||
LITERAL_DO"/>
|
||||
</module>
|
||||
<module name="RightCurly">
|
||||
<property name="id" value="RightCurlyAlone"/>
|
||||
<property name="option" value="alone"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
|
||||
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF"/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
|
||||
<property name="id" value="RightCurlyAlone"/>
|
||||
<property name="query" value="//RCURLY[parent::SLIST[count(./*)=1]
|
||||
or preceding-sibling::*[last()][self::LCURLY]]"/>
|
||||
</module>
|
||||
<module name="WhitespaceAfter">
|
||||
<property name="tokens"
|
||||
value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE,
|
||||
LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, DO_WHILE"/>
|
||||
</module>
|
||||
<module name="WhitespaceAround">
|
||||
<property name="allowEmptyConstructors" value="true"/>
|
||||
<property name="allowEmptyLambdas" value="true"/>
|
||||
<property name="allowEmptyMethods" value="true"/>
|
||||
<property name="allowEmptyTypes" value="true"/>
|
||||
<property name="allowEmptyLoops" value="true"/>
|
||||
<property name="tokens"
|
||||
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
|
||||
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
|
||||
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
|
||||
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
|
||||
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
|
||||
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
|
||||
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
|
||||
<message key="ws.notFollowed"
|
||||
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||
<message key="ws.notPreceded"
|
||||
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<!-- <module name="MultipleVariableDeclarations"/>-->
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<module name="FallThrough"/>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="tokens"
|
||||
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapDot"/>
|
||||
<property name="tokens" value="DOT"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapComma"/>
|
||||
<property name="tokens" value="COMMA"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
|
||||
<property name="id" value="SeparatorWrapEllipsis"/>
|
||||
<property name="tokens" value="ELLIPSIS"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
|
||||
<property name="id" value="SeparatorWrapArrayDeclarator"/>
|
||||
<property name="tokens" value="ARRAY_DECLARATOR"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapMethodRef"/>
|
||||
<property name="tokens" value="METHOD_REF"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName">
|
||||
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MemberName">
|
||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LambdaParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="CatchParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LocalVariableName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="InterfaceTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="NoFinalizer"/>
|
||||
<module name="GenericWhitespace">
|
||||
<message key="ws.followed"
|
||||
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||
<message key="ws.preceded"
|
||||
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||
<message key="ws.illegalFollow"
|
||||
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||
<message key="ws.notPreceded"
|
||||
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="Indentation">
|
||||
<property name="basicOffset" value="2"/>
|
||||
<property name="braceAdjustment" value="0"/>
|
||||
<property name="caseIndent" value="2"/>
|
||||
<property name="throwsIndent" value="4"/>
|
||||
<property name="lineWrappingIndentation" value="4"/>
|
||||
<property name="arrayInitIndent" value="2"/>
|
||||
</module>
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
<property name="allowedAbbreviationLength" value="1"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
|
||||
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF"/>
|
||||
</module>
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
<!-- <module name="VariableDeclarationUsageDistance"/>-->
|
||||
<module name="CustomImportOrder">
|
||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||
<property name="separateLineBetweenGroups" value="true"/>
|
||||
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
|
||||
<property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
|
||||
</module>
|
||||
<module name="MethodParamPad">
|
||||
<property name="tokens"
|
||||
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
|
||||
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore">
|
||||
<property name="tokens"
|
||||
value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS,
|
||||
LABELED_STAT, METHOD_REF"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
</module>
|
||||
<module name="ParenPad">
|
||||
<property name="tokens"
|
||||
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
|
||||
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
|
||||
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
|
||||
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA"/>
|
||||
</module>
|
||||
<module name="OperatorWrap">
|
||||
<property name="option" value="NL"/>
|
||||
<property name="tokens"
|
||||
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationMostCases"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationVariables"/>
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</module>
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="InvalidJavadocPosition"/>
|
||||
<module name="JavadocTagContinuationIndentation"/>
|
||||
<module name="SummaryJavadoc">
|
||||
<property name="forbiddenSummaryFragments"
|
||||
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||
</module>
|
||||
<module name="JavadocParagraph"/>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||
<property name="target"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
</module>
|
||||
<module name="JavadocMethod">
|
||||
<property name="accessModifiers" value="public"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowedAnnotations" value="Override, Test"/>
|
||||
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
|
||||
</module>
|
||||
<!-- <module name="MissingJavadocMethod">-->
|
||||
<!-- <property name="scope" value="public"/>-->
|
||||
<!-- <property name="minLineCount" value="2"/>-->
|
||||
<!-- <property name="allowedAnnotations" value="Override, Test"/>-->
|
||||
<!-- <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>-->
|
||||
<!-- </module>-->
|
||||
<module name="MethodName">
|
||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="SingleLineJavadoc">
|
||||
<property name="ignoreInlineTags" value="false"/>
|
||||
</module>
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="exceptionVariableName" value="ignored"/>
|
||||
</module>
|
||||
<module name="CommentsIndentation">
|
||||
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
|
||||
</module>
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
|
||||
<module name="SuppressionXpathFilter">
|
||||
<property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
|
||||
default="checkstyle-xpath-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
|
@ -318,7 +318,7 @@
|
|||
<property name="ignoreInlineTags" value="false"/>
|
||||
</module>
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="exceptionVariableName" value="expected"/>
|
||||
<property name="exceptionVariableName" value="ignored"/>
|
||||
</module>
|
||||
<module name="CommentsIndentation">
|
||||
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
|
||||
|
@ -330,4 +330,4 @@
|
|||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
FROM azul/zulu-openjdk-alpine:17-jre
|
||||
#FROM azul/zulu-openjdk-alpine:17-jre-headless
|
||||
FROM azul/zulu-openjdk-alpine@sha256:a36679ac0d28cb835e2a8c00e1e0d95509c6c51c5081c7782b85edb1f37a771a
|
||||
|
||||
RUN apk add --no-cache gcompat # need to make snappy codec work
|
||||
RUN addgroup -S kafkaui && adduser -S kafkaui -G kafkaui
|
||||
|
|
|
@ -6,7 +6,13 @@ import com.provectus.kafka.ui.config.ClustersProperties;
|
|||
import com.provectus.kafka.ui.connect.ApiClient;
|
||||
import com.provectus.kafka.ui.connect.api.KafkaConnectClientApi;
|
||||
import com.provectus.kafka.ui.connect.model.Connector;
|
||||
import com.provectus.kafka.ui.connect.model.ConnectorPlugin;
|
||||
import com.provectus.kafka.ui.connect.model.ConnectorPluginConfigValidationResponse;
|
||||
import com.provectus.kafka.ui.connect.model.ConnectorStatus;
|
||||
import com.provectus.kafka.ui.connect.model.ConnectorTask;
|
||||
import com.provectus.kafka.ui.connect.model.ConnectorTopics;
|
||||
import com.provectus.kafka.ui.connect.model.NewConnector;
|
||||
import com.provectus.kafka.ui.connect.model.TaskStatus;
|
||||
import com.provectus.kafka.ui.exception.KafkaConnectConflictReponseException;
|
||||
import com.provectus.kafka.ui.exception.ValidationException;
|
||||
import com.provectus.kafka.ui.util.WebClientConfigurator;
|
||||
|
@ -15,11 +21,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
@ -79,6 +81,176 @@ public class RetryingKafkaConnectClient extends KafkaConnectClientApi {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Connector>> createConnectorWithHttpInfo(NewConnector newConnector)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.createConnectorWithHttpInfo(newConnector));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteConnector(String connectorName) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.deleteConnector(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Void>> deleteConnectorWithHttpInfo(String connectorName)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.deleteConnectorWithHttpInfo(connectorName));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Connector> getConnector(String connectorName) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnector(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Connector>> getConnectorWithHttpInfo(String connectorName)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorWithHttpInfo(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Map<String, Object>> getConnectorConfig(String connectorName) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorConfig(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Map<String, Object>>> getConnectorConfigWithHttpInfo(String connectorName)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorConfigWithHttpInfo(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ConnectorPlugin> getConnectorPlugins() throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorPlugins());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<List<ConnectorPlugin>>> getConnectorPluginsWithHttpInfo()
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorPluginsWithHttpInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ConnectorStatus> getConnectorStatus(String connectorName) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorStatus(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<ConnectorStatus>> getConnectorStatusWithHttpInfo(String connectorName)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorStatusWithHttpInfo(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<TaskStatus> getConnectorTaskStatus(String connectorName, Integer taskId)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorTaskStatus(connectorName, taskId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<TaskStatus>> getConnectorTaskStatusWithHttpInfo(String connectorName, Integer taskId)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorTaskStatusWithHttpInfo(connectorName, taskId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ConnectorTask> getConnectorTasks(String connectorName) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorTasks(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<List<ConnectorTask>>> getConnectorTasksWithHttpInfo(String connectorName)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorTasksWithHttpInfo(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Map<String, ConnectorTopics>> getConnectorTopics(String connectorName) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorTopics(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Map<String, ConnectorTopics>>> getConnectorTopicsWithHttpInfo(String connectorName)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorTopicsWithHttpInfo(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> getConnectors(String search) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectors(search));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<List<String>>> getConnectorsWithHttpInfo(String search) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.getConnectorsWithHttpInfo(search));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> pauseConnector(String connectorName) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.pauseConnector(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Void>> pauseConnectorWithHttpInfo(String connectorName) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.pauseConnectorWithHttpInfo(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> restartConnector(String connectorName, Boolean includeTasks, Boolean onlyFailed)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.restartConnector(connectorName, includeTasks, onlyFailed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Void>> restartConnectorWithHttpInfo(String connectorName, Boolean includeTasks,
|
||||
Boolean onlyFailed) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.restartConnectorWithHttpInfo(connectorName, includeTasks, onlyFailed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> restartConnectorTask(String connectorName, Integer taskId) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.restartConnectorTask(connectorName, taskId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Void>> restartConnectorTaskWithHttpInfo(String connectorName, Integer taskId)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.restartConnectorTaskWithHttpInfo(connectorName, taskId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> resumeConnector(String connectorName) throws WebClientResponseException {
|
||||
return super.resumeConnector(connectorName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Void>> resumeConnectorWithHttpInfo(String connectorName)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.resumeConnectorWithHttpInfo(connectorName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<Connector>> setConnectorConfigWithHttpInfo(String connectorName,
|
||||
Map<String, Object> requestBody)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.setConnectorConfigWithHttpInfo(connectorName, requestBody));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ConnectorPluginConfigValidationResponse> validateConnectorPluginConfig(String pluginName,
|
||||
Map<String, Object> requestBody)
|
||||
throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.validateConnectorPluginConfig(pluginName, requestBody));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<ConnectorPluginConfigValidationResponse>> validateConnectorPluginConfigWithHttpInfo(
|
||||
String pluginName, Map<String, Object> requestBody) throws WebClientResponseException {
|
||||
return withRetryOnConflict(super.validateConnectorPluginConfigWithHttpInfo(pluginName, requestBody));
|
||||
}
|
||||
|
||||
private static class RetryingApiClient extends ApiClient {
|
||||
|
||||
public RetryingApiClient(ConnectCluster config,
|
||||
|
@ -108,35 +280,5 @@ public class RetryingKafkaConnectClient extends KafkaConnectClientApi {
|
|||
.configureBufferSize(maxBuffSize)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> invokeAPI(String path, HttpMethod method, Map<String, Object> pathParams,
|
||||
MultiValueMap<String, String> queryParams, Object body,
|
||||
HttpHeaders headerParams,
|
||||
MultiValueMap<String, String> cookieParams,
|
||||
MultiValueMap<String, Object> formParams, List<MediaType> accept,
|
||||
MediaType contentType, String[] authNames,
|
||||
ParameterizedTypeReference<T> returnType)
|
||||
throws RestClientException {
|
||||
return withRetryOnConflict(
|
||||
super.invokeAPI(path, method, pathParams, queryParams, body, headerParams, cookieParams,
|
||||
formParams, accept, contentType, authNames, returnType)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> invokeFluxAPI(String path, HttpMethod method, Map<String, Object> pathParams,
|
||||
MultiValueMap<String, String> queryParams, Object body,
|
||||
HttpHeaders headerParams,
|
||||
MultiValueMap<String, String> cookieParams,
|
||||
MultiValueMap<String, Object> formParams,
|
||||
List<MediaType> accept, MediaType contentType,
|
||||
String[] authNames, ParameterizedTypeReference<T> returnType)
|
||||
throws RestClientException {
|
||||
return withRetryOnConflict(
|
||||
super.invokeFluxAPI(path, method, pathParams, queryParams, body, headerParams,
|
||||
cookieParams, formParams, accept, contentType, authNames, returnType)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.provectus.kafka.ui.config;
|
||||
|
||||
import com.provectus.kafka.ui.model.MetricsConfig;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -8,7 +9,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.PostConstruct;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package com.provectus.kafka.ui.config.auth;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.PostConstruct;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
|
@ -13,12 +13,12 @@ import com.provectus.kafka.ui.model.ClusterConfigValidationDTO;
|
|||
import com.provectus.kafka.ui.model.RestartRequestDTO;
|
||||
import com.provectus.kafka.ui.model.UploadedFileInfoDTO;
|
||||
import com.provectus.kafka.ui.model.rbac.AccessContext;
|
||||
import com.provectus.kafka.ui.service.ApplicationInfoService;
|
||||
import com.provectus.kafka.ui.service.KafkaClusterFactory;
|
||||
import com.provectus.kafka.ui.service.rbac.AccessControlService;
|
||||
import com.provectus.kafka.ui.util.ApplicationRestarter;
|
||||
import com.provectus.kafka.ui.util.DynamicConfigOperations;
|
||||
import com.provectus.kafka.ui.util.DynamicConfigOperations.PropertiesStructure;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -53,18 +53,11 @@ public class ApplicationConfigController implements ApplicationConfigApi {
|
|||
private final DynamicConfigOperations dynamicConfigOperations;
|
||||
private final ApplicationRestarter restarter;
|
||||
private final KafkaClusterFactory kafkaClusterFactory;
|
||||
|
||||
private final ApplicationInfoService applicationInfoService;
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<ApplicationInfoDTO>> getApplicationInfo(ServerWebExchange exchange) {
|
||||
return Mono.just(
|
||||
new ApplicationInfoDTO()
|
||||
.enabledFeatures(
|
||||
dynamicConfigOperations.dynamicConfigEnabled()
|
||||
? List.of(ApplicationInfoDTO.EnabledFeaturesEnum.DYNAMIC_CONFIG)
|
||||
: List.of()
|
||||
)
|
||||
).map(ResponseEntity::ok);
|
||||
return Mono.just(applicationInfoService.getApplicationInfo()).map(ResponseEntity::ok);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -149,10 +149,9 @@ public class KafkaConnectController extends AbstractController implements KafkaC
|
|||
}
|
||||
|
||||
@Override
|
||||
public Mono<ResponseEntity<ConnectorDTO>> setConnectorConfig(String clusterName,
|
||||
String connectName,
|
||||
public Mono<ResponseEntity<ConnectorDTO>> setConnectorConfig(String clusterName, String connectName,
|
||||
String connectorName,
|
||||
@Valid Mono<Object> requestBody,
|
||||
Mono<Map<String, Object>> requestBody,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
Mono<Void> validateAccess = accessControlService.validateAccess(AccessContext.builder()
|
||||
|
@ -164,8 +163,7 @@ public class KafkaConnectController extends AbstractController implements KafkaC
|
|||
return validateAccess.then(
|
||||
kafkaConnectService
|
||||
.setConnectorConfig(getCluster(clusterName), connectName, connectorName, requestBody)
|
||||
.map(ResponseEntity::ok)
|
||||
);
|
||||
.map(ResponseEntity::ok));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -242,7 +240,7 @@ public class KafkaConnectController extends AbstractController implements KafkaC
|
|||
|
||||
@Override
|
||||
public Mono<ResponseEntity<ConnectorPluginConfigValidationResponseDTO>> validateConnectorPluginConfig(
|
||||
String clusterName, String connectName, String pluginName, @Valid Mono<Object> requestBody,
|
||||
String clusterName, String connectName, String pluginName, @Valid Mono<Map<String, Object>> requestBody,
|
||||
ServerWebExchange exchange) {
|
||||
return kafkaConnectService
|
||||
.validateConnectorPluginConfig(
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package com.provectus.kafka.ui.emitter;
|
||||
|
||||
import com.provectus.kafka.ui.model.TopicMessageDTO;
|
||||
import com.provectus.kafka.ui.model.TopicMessageEventDTO;
|
||||
import com.provectus.kafka.ui.model.TopicMessagePhaseDTO;
|
||||
import com.provectus.kafka.ui.serdes.ConsumerRecordDeserializer;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import org.apache.kafka.clients.consumer.Consumer;
|
||||
|
@ -14,13 +11,12 @@ import reactor.core.publisher.FluxSink;
|
|||
|
||||
public abstract class AbstractEmitter {
|
||||
|
||||
private final ConsumerRecordDeserializer recordDeserializer;
|
||||
private final ConsumingStats consumingStats = new ConsumingStats();
|
||||
private final MessagesProcessing messagesProcessing;
|
||||
private final PollingThrottler throttler;
|
||||
protected final PollingSettings pollingSettings;
|
||||
|
||||
protected AbstractEmitter(ConsumerRecordDeserializer recordDeserializer, PollingSettings pollingSettings) {
|
||||
this.recordDeserializer = recordDeserializer;
|
||||
protected AbstractEmitter(MessagesProcessing messagesProcessing, PollingSettings pollingSettings) {
|
||||
this.messagesProcessing = messagesProcessing;
|
||||
this.pollingSettings = pollingSettings;
|
||||
this.throttler = pollingSettings.getPollingThrottler();
|
||||
}
|
||||
|
@ -40,39 +36,27 @@ public abstract class AbstractEmitter {
|
|||
return records;
|
||||
}
|
||||
|
||||
protected boolean sendLimitReached() {
|
||||
return messagesProcessing.limitReached();
|
||||
}
|
||||
|
||||
protected void sendMessage(FluxSink<TopicMessageEventDTO> sink,
|
||||
ConsumerRecord<Bytes, Bytes> msg) {
|
||||
final TopicMessageDTO topicMessage = recordDeserializer.deserialize(msg);
|
||||
sink.next(
|
||||
new TopicMessageEventDTO()
|
||||
.type(TopicMessageEventDTO.TypeEnum.MESSAGE)
|
||||
.message(topicMessage)
|
||||
);
|
||||
ConsumerRecord<Bytes, Bytes> msg) {
|
||||
messagesProcessing.sendMsg(sink, msg);
|
||||
}
|
||||
|
||||
protected void sendPhase(FluxSink<TopicMessageEventDTO> sink, String name) {
|
||||
sink.next(
|
||||
new TopicMessageEventDTO()
|
||||
.type(TopicMessageEventDTO.TypeEnum.PHASE)
|
||||
.phase(new TopicMessagePhaseDTO().name(name))
|
||||
);
|
||||
messagesProcessing.sendPhase(sink, name);
|
||||
}
|
||||
|
||||
protected int sendConsuming(FluxSink<TopicMessageEventDTO> sink,
|
||||
ConsumerRecords<Bytes, Bytes> records,
|
||||
long elapsed) {
|
||||
return consumingStats.sendConsumingEvt(sink, records, elapsed, getFilterApplyErrors(sink));
|
||||
ConsumerRecords<Bytes, Bytes> records,
|
||||
long elapsed) {
|
||||
return messagesProcessing.sentConsumingInfo(sink, records, elapsed);
|
||||
}
|
||||
|
||||
protected void sendFinishStatsAndCompleteSink(FluxSink<TopicMessageEventDTO> sink) {
|
||||
consumingStats.sendFinishEvent(sink, getFilterApplyErrors(sink));
|
||||
messagesProcessing.sendFinishEvent(sink);
|
||||
sink.complete();
|
||||
}
|
||||
|
||||
protected Number getFilterApplyErrors(FluxSink<?> sink) {
|
||||
return sink.contextView()
|
||||
.<MessageFilterStats>getOrEmpty(MessageFilterStats.class)
|
||||
.<Number>map(MessageFilterStats::getFilterApplyErrors)
|
||||
.orElse(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.provectus.kafka.ui.emitter;
|
|||
|
||||
import com.provectus.kafka.ui.model.ConsumerPosition;
|
||||
import com.provectus.kafka.ui.model.TopicMessageEventDTO;
|
||||
import com.provectus.kafka.ui.serdes.ConsumerRecordDeserializer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -31,9 +30,9 @@ public class BackwardRecordEmitter
|
|||
Supplier<KafkaConsumer<Bytes, Bytes>> consumerSupplier,
|
||||
ConsumerPosition consumerPosition,
|
||||
int messagesPerPage,
|
||||
ConsumerRecordDeserializer recordDeserializer,
|
||||
MessagesProcessing messagesProcessing,
|
||||
PollingSettings pollingSettings) {
|
||||
super(recordDeserializer, pollingSettings);
|
||||
super(messagesProcessing, pollingSettings);
|
||||
this.consumerPosition = consumerPosition;
|
||||
this.messagesPerPage = messagesPerPage;
|
||||
this.consumerSupplier = consumerSupplier;
|
||||
|
@ -52,7 +51,7 @@ public class BackwardRecordEmitter
|
|||
int msgsToPollPerPartition = (int) Math.ceil((double) messagesPerPage / readUntilOffsets.size());
|
||||
log.debug("'Until' offsets for polling: {}", readUntilOffsets);
|
||||
|
||||
while (!sink.isCancelled() && !readUntilOffsets.isEmpty()) {
|
||||
while (!sink.isCancelled() && !readUntilOffsets.isEmpty() && !sendLimitReached()) {
|
||||
new TreeMap<>(readUntilOffsets).forEach((tp, readToOffset) -> {
|
||||
if (sink.isCancelled()) {
|
||||
return; //fast return in case of sink cancellation
|
||||
|
@ -61,8 +60,6 @@ public class BackwardRecordEmitter
|
|||
long readFromOffset = Math.max(beginOffset, readToOffset - msgsToPollPerPartition);
|
||||
|
||||
partitionPollIteration(tp, readFromOffset, readToOffset, consumer, sink)
|
||||
.stream()
|
||||
.filter(r -> !sink.isCancelled())
|
||||
.forEach(r -> sendMessage(sink, r));
|
||||
|
||||
if (beginOffset == readFromOffset) {
|
||||
|
@ -106,6 +103,7 @@ public class BackwardRecordEmitter
|
|||
|
||||
EmptyPollsCounter emptyPolls = pollingSettings.createEmptyPollsCounter();
|
||||
while (!sink.isCancelled()
|
||||
&& !sendLimitReached()
|
||||
&& recordsToSend.size() < desiredMsgsToPoll
|
||||
&& !emptyPolls.noDataEmptyPollsReached()) {
|
||||
var polledRecords = poll(sink, consumer, pollingSettings.getPartitionPollTimeout());
|
||||
|
|
|
@ -19,7 +19,7 @@ class ConsumingStats {
|
|||
int sendConsumingEvt(FluxSink<TopicMessageEventDTO> sink,
|
||||
ConsumerRecords<Bytes, Bytes> polledRecords,
|
||||
long elapsed,
|
||||
Number filterApplyErrors) {
|
||||
int filterApplyErrors) {
|
||||
int polledBytes = ConsumerRecordsUtil.calculatePolledSize(polledRecords);
|
||||
bytes += polledBytes;
|
||||
this.records += polledRecords.count();
|
||||
|
@ -32,7 +32,7 @@ class ConsumingStats {
|
|||
return polledBytes;
|
||||
}
|
||||
|
||||
void sendFinishEvent(FluxSink<TopicMessageEventDTO> sink, Number filterApplyErrors) {
|
||||
void sendFinishEvent(FluxSink<TopicMessageEventDTO> sink, int filterApplyErrors) {
|
||||
sink.next(
|
||||
new TopicMessageEventDTO()
|
||||
.type(TopicMessageEventDTO.TypeEnum.DONE)
|
||||
|
@ -41,12 +41,12 @@ class ConsumingStats {
|
|||
}
|
||||
|
||||
private TopicMessageConsumingDTO createConsumingStats(FluxSink<TopicMessageEventDTO> sink,
|
||||
Number filterApplyErrors) {
|
||||
int filterApplyErrors) {
|
||||
return new TopicMessageConsumingDTO()
|
||||
.bytesConsumed(this.bytes)
|
||||
.elapsedMs(this.elapsed)
|
||||
.isCancelled(sink.isCancelled())
|
||||
.filterApplyErrors(filterApplyErrors.intValue())
|
||||
.filterApplyErrors(filterApplyErrors)
|
||||
.messagesConsumed(this.records);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.provectus.kafka.ui.emitter;
|
|||
|
||||
import com.provectus.kafka.ui.model.ConsumerPosition;
|
||||
import com.provectus.kafka.ui.model.TopicMessageEventDTO;
|
||||
import com.provectus.kafka.ui.serdes.ConsumerRecordDeserializer;
|
||||
import java.util.function.Supplier;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||
|
@ -23,9 +22,9 @@ public class ForwardRecordEmitter
|
|||
public ForwardRecordEmitter(
|
||||
Supplier<KafkaConsumer<Bytes, Bytes>> consumerSupplier,
|
||||
ConsumerPosition position,
|
||||
ConsumerRecordDeserializer recordDeserializer,
|
||||
MessagesProcessing messagesProcessing,
|
||||
PollingSettings pollingSettings) {
|
||||
super(recordDeserializer, pollingSettings);
|
||||
super(messagesProcessing, pollingSettings);
|
||||
this.position = position;
|
||||
this.consumerSupplier = consumerSupplier;
|
||||
}
|
||||
|
@ -40,6 +39,7 @@ public class ForwardRecordEmitter
|
|||
|
||||
EmptyPollsCounter emptyPolls = pollingSettings.createEmptyPollsCounter();
|
||||
while (!sink.isCancelled()
|
||||
&& !sendLimitReached()
|
||||
&& !seekOperations.assignedPartitionsFullyPolled()
|
||||
&& !emptyPolls.noDataEmptyPollsReached()) {
|
||||
|
||||
|
@ -50,11 +50,7 @@ public class ForwardRecordEmitter
|
|||
log.debug("{} records polled", records.count());
|
||||
|
||||
for (ConsumerRecord<Bytes, Bytes> msg : records) {
|
||||
if (!sink.isCancelled()) {
|
||||
sendMessage(sink, msg);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
sendMessage(sink, msg);
|
||||
}
|
||||
}
|
||||
sendFinishStatsAndCompleteSink(sink);
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package com.provectus.kafka.ui.emitter;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
|
||||
public class MessageFilterStats {
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private final AtomicLong filterApplyErrors = new AtomicLong();
|
||||
|
||||
public final void incrementApplyErrors() {
|
||||
filterApplyErrors.incrementAndGet();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package com.provectus.kafka.ui.emitter;
|
||||
|
||||
import com.provectus.kafka.ui.model.TopicMessageDTO;
|
||||
import com.provectus.kafka.ui.model.TopicMessageEventDTO;
|
||||
import com.provectus.kafka.ui.model.TopicMessagePhaseDTO;
|
||||
import com.provectus.kafka.ui.serdes.ConsumerRecordDeserializer;
|
||||
import java.util.function.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecords;
|
||||
import org.apache.kafka.common.utils.Bytes;
|
||||
import reactor.core.publisher.FluxSink;
|
||||
|
||||
@Slf4j
|
||||
public class MessagesProcessing {
|
||||
|
||||
private final ConsumingStats consumingStats = new ConsumingStats();
|
||||
private long sentMessages = 0;
|
||||
private int filterApplyErrors = 0;
|
||||
|
||||
private final ConsumerRecordDeserializer deserializer;
|
||||
private final Predicate<TopicMessageDTO> filter;
|
||||
private final @Nullable Integer limit;
|
||||
|
||||
public MessagesProcessing(ConsumerRecordDeserializer deserializer,
|
||||
Predicate<TopicMessageDTO> filter,
|
||||
@Nullable Integer limit) {
|
||||
this.deserializer = deserializer;
|
||||
this.filter = filter;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
boolean limitReached() {
|
||||
return limit != null && sentMessages >= limit;
|
||||
}
|
||||
|
||||
void sendMsg(FluxSink<TopicMessageEventDTO> sink, ConsumerRecord<Bytes, Bytes> rec) {
|
||||
if (!sink.isCancelled() && !limitReached()) {
|
||||
TopicMessageDTO topicMessage = deserializer.deserialize(rec);
|
||||
try {
|
||||
if (filter.test(topicMessage)) {
|
||||
sink.next(
|
||||
new TopicMessageEventDTO()
|
||||
.type(TopicMessageEventDTO.TypeEnum.MESSAGE)
|
||||
.message(topicMessage)
|
||||
);
|
||||
sentMessages++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
filterApplyErrors++;
|
||||
log.trace("Error applying filter for message {}", topicMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sentConsumingInfo(FluxSink<TopicMessageEventDTO> sink,
|
||||
ConsumerRecords<Bytes, Bytes> polledRecords,
|
||||
long elapsed) {
|
||||
if (!sink.isCancelled()) {
|
||||
return consumingStats.sendConsumingEvt(sink, polledRecords, elapsed, filterApplyErrors);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sendFinishEvent(FluxSink<TopicMessageEventDTO> sink) {
|
||||
if (!sink.isCancelled()) {
|
||||
consumingStats.sendFinishEvent(sink, filterApplyErrors);
|
||||
}
|
||||
}
|
||||
|
||||
void sendPhase(FluxSink<TopicMessageEventDTO> sink, String name) {
|
||||
if (!sink.isCancelled()) {
|
||||
sink.next(
|
||||
new TopicMessageEventDTO()
|
||||
.type(TopicMessageEventDTO.TypeEnum.PHASE)
|
||||
.phase(new TopicMessagePhaseDTO().name(name))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,7 +2,6 @@ package com.provectus.kafka.ui.emitter;
|
|||
|
||||
import com.provectus.kafka.ui.model.ConsumerPosition;
|
||||
import com.provectus.kafka.ui.model.TopicMessageEventDTO;
|
||||
import com.provectus.kafka.ui.serdes.ConsumerRecordDeserializer;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Supplier;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -20,9 +19,9 @@ public class TailingEmitter extends AbstractEmitter
|
|||
|
||||
public TailingEmitter(Supplier<KafkaConsumer<Bytes, Bytes>> consumerSupplier,
|
||||
ConsumerPosition consumerPosition,
|
||||
ConsumerRecordDeserializer recordDeserializer,
|
||||
MessagesProcessing messagesProcessing,
|
||||
PollingSettings pollingSettings) {
|
||||
super(recordDeserializer, pollingSettings);
|
||||
super(messagesProcessing, pollingSettings);
|
||||
this.consumerSupplier = consumerSupplier;
|
||||
this.consumerPosition = consumerPosition;
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHan
|
|||
.timestamp(currentTimestamp())
|
||||
.stackTrace(Throwables.getStackTraceAsString(exception));
|
||||
return ServerResponse
|
||||
.status(exception.getStatus())
|
||||
.status(exception.getStatusCode())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(response);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.provectus.kafka.ui.model.rbac;
|
||||
|
||||
import static com.provectus.kafka.ui.model.rbac.Resource.APPLICATIONCONFIG;
|
||||
import static com.provectus.kafka.ui.model.rbac.Resource.CLUSTERCONFIG;
|
||||
import static com.provectus.kafka.ui.model.rbac.Resource.KSQL;
|
||||
|
||||
|
@ -26,6 +27,8 @@ import org.springframework.util.Assert;
|
|||
@EqualsAndHashCode
|
||||
public class Permission {
|
||||
|
||||
private static final List<Resource> RBAC_ACTION_EXEMPT_LIST = List.of(KSQL, CLUSTERCONFIG, APPLICATIONCONFIG);
|
||||
|
||||
Resource resource;
|
||||
List<String> actions;
|
||||
|
||||
|
@ -51,7 +54,7 @@ public class Permission {
|
|||
|
||||
public void validate() {
|
||||
Assert.notNull(resource, "resource cannot be null");
|
||||
if (!List.of(KSQL, CLUSTERCONFIG).contains(this.resource)) {
|
||||
if (!RBAC_ACTION_EXEMPT_LIST.contains(this.resource)) {
|
||||
Assert.notNull(value, "permission value can't be empty for resource " + resource);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package com.provectus.kafka.ui.service;
|
||||
|
||||
import static com.provectus.kafka.ui.model.ApplicationInfoDTO.EnabledFeaturesEnum;
|
||||
|
||||
import com.provectus.kafka.ui.model.ApplicationInfoBuildDTO;
|
||||
import com.provectus.kafka.ui.model.ApplicationInfoDTO;
|
||||
import com.provectus.kafka.ui.model.ApplicationInfoLatestReleaseDTO;
|
||||
import com.provectus.kafka.ui.util.DynamicConfigOperations;
|
||||
import com.provectus.kafka.ui.util.GithubReleaseInfo;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.info.BuildProperties;
|
||||
import org.springframework.boot.info.GitProperties;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ApplicationInfoService {
|
||||
|
||||
private final GithubReleaseInfo githubReleaseInfo = new GithubReleaseInfo();
|
||||
|
||||
private final DynamicConfigOperations dynamicConfigOperations;
|
||||
private final BuildProperties buildProperties;
|
||||
private final GitProperties gitProperties;
|
||||
|
||||
public ApplicationInfoService(DynamicConfigOperations dynamicConfigOperations,
|
||||
@Autowired(required = false) BuildProperties buildProperties,
|
||||
@Autowired(required = false) GitProperties gitProperties) {
|
||||
this.dynamicConfigOperations = dynamicConfigOperations;
|
||||
this.buildProperties = Optional.ofNullable(buildProperties).orElse(new BuildProperties(new Properties()));
|
||||
this.gitProperties = Optional.ofNullable(gitProperties).orElse(new GitProperties(new Properties()));
|
||||
}
|
||||
|
||||
public ApplicationInfoDTO getApplicationInfo() {
|
||||
var releaseInfo = githubReleaseInfo.get();
|
||||
return new ApplicationInfoDTO()
|
||||
.build(getBuildInfo(releaseInfo))
|
||||
.enabledFeatures(getEnabledFeatures())
|
||||
.latestRelease(convert(releaseInfo));
|
||||
}
|
||||
|
||||
private ApplicationInfoLatestReleaseDTO convert(GithubReleaseInfo.GithubReleaseDto releaseInfo) {
|
||||
return new ApplicationInfoLatestReleaseDTO()
|
||||
.htmlUrl(releaseInfo.html_url())
|
||||
.publishedAt(releaseInfo.published_at())
|
||||
.versionTag(releaseInfo.tag_name());
|
||||
}
|
||||
|
||||
private ApplicationInfoBuildDTO getBuildInfo(GithubReleaseInfo.GithubReleaseDto release) {
|
||||
return new ApplicationInfoBuildDTO()
|
||||
.isLatestRelease(release.tag_name() != null && release.tag_name().equals(buildProperties.getVersion()))
|
||||
.commitId(gitProperties.getShortCommitId())
|
||||
.version(buildProperties.getVersion())
|
||||
.buildTime(buildProperties.getTime() != null
|
||||
? DateTimeFormatter.ISO_INSTANT.format(buildProperties.getTime()) : null);
|
||||
}
|
||||
|
||||
private List<EnabledFeaturesEnum> getEnabledFeatures() {
|
||||
var enabledFeatures = new ArrayList<EnabledFeaturesEnum>();
|
||||
if (dynamicConfigOperations.dynamicConfigEnabled()) {
|
||||
enabledFeatures.add(EnabledFeaturesEnum.DYNAMIC_CONFIG);
|
||||
}
|
||||
return enabledFeatures;
|
||||
}
|
||||
|
||||
// updating on startup and every hour
|
||||
@Scheduled(fixedRateString = "${github-release-info-update-rate:3600000}")
|
||||
public void updateGithubReleaseInfo() {
|
||||
githubReleaseInfo.refresh().block();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +1,58 @@
|
|||
package com.provectus.kafka.ui.service;
|
||||
|
||||
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.kafka.common.config.ConfigDef;
|
||||
import org.apache.kafka.common.config.SaslConfigs;
|
||||
import org.apache.kafka.common.config.SslConfigs;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.actuate.endpoint.Sanitizer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
class KafkaConfigSanitizer extends Sanitizer {
|
||||
private static final List<String> DEFAULT_PATTERNS_TO_SANITIZE = Arrays.asList(
|
||||
"basic.auth.user.info", /* For Schema Registry credentials */
|
||||
"password", "secret", "token", "key", ".*credentials.*", /* General credential patterns */
|
||||
"aws.access.*", "aws.secret.*", "aws.session.*" /* AWS-related credential patterns */
|
||||
);
|
||||
class KafkaConfigSanitizer {
|
||||
|
||||
private static final String SANITIZED_VALUE = "******";
|
||||
|
||||
private static final String[] REGEX_PARTS = {"*", "$", "^", "+"};
|
||||
|
||||
private static final List<String> DEFAULT_PATTERNS_TO_SANITIZE = ImmutableList.<String>builder()
|
||||
.addAll(kafkaConfigKeysToSanitize())
|
||||
.add(
|
||||
"basic.auth.user.info", /* For Schema Registry credentials */
|
||||
"password", "secret", "token", "key", ".*credentials.*", /* General credential patterns */
|
||||
"aws.access.*", "aws.secret.*", "aws.session.*" /* AWS-related credential patterns */
|
||||
)
|
||||
.build();
|
||||
|
||||
private final List<Pattern> sanitizeKeysPatterns;
|
||||
|
||||
KafkaConfigSanitizer(
|
||||
@Value("${kafka.config.sanitizer.enabled:true}") boolean enabled,
|
||||
@Value("${kafka.config.sanitizer.patterns:}") List<String> patternsToSanitize
|
||||
) {
|
||||
if (!enabled) {
|
||||
setKeysToSanitize();
|
||||
} else {
|
||||
var keysToSanitize = new HashSet<>(
|
||||
patternsToSanitize.isEmpty() ? DEFAULT_PATTERNS_TO_SANITIZE : patternsToSanitize);
|
||||
keysToSanitize.addAll(kafkaConfigKeysToSanitize());
|
||||
setKeysToSanitize(keysToSanitize.toArray(new String[] {}));
|
||||
}
|
||||
this.sanitizeKeysPatterns = enabled
|
||||
? compile(patternsToSanitize.isEmpty() ? DEFAULT_PATTERNS_TO_SANITIZE : patternsToSanitize)
|
||||
: List.of();
|
||||
}
|
||||
|
||||
private static List<Pattern> compile(Collection<String> patternStrings) {
|
||||
return patternStrings.stream()
|
||||
.map(p -> isRegex(p)
|
||||
? Pattern.compile(p, CASE_INSENSITIVE)
|
||||
: Pattern.compile(".*" + p + "$", CASE_INSENSITIVE))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static boolean isRegex(String str) {
|
||||
return Arrays.stream(REGEX_PARTS).anyMatch(str::contains);
|
||||
}
|
||||
|
||||
private static Set<String> kafkaConfigKeysToSanitize() {
|
||||
|
@ -45,4 +65,17 @@ class KafkaConfigSanitizer extends Sanitizer {
|
|||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Object sanitize(String key, Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
for (Pattern pattern : sanitizeKeysPatterns) {
|
||||
if (pattern.matcher(key).matches()) {
|
||||
return SANITIZED_VALUE;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -225,11 +225,11 @@ public class KafkaConnectService {
|
|||
}
|
||||
|
||||
public Mono<ConnectorDTO> setConnectorConfig(KafkaCluster cluster, String connectName,
|
||||
String connectorName, Mono<Object> requestBody) {
|
||||
String connectorName, Mono<Map<String, Object>> requestBody) {
|
||||
return api(cluster, connectName)
|
||||
.mono(c ->
|
||||
requestBody
|
||||
.flatMap(body -> c.setConnectorConfig(connectorName, (Map<String, Object>) body))
|
||||
.flatMap(body -> c.setConnectorConfig(connectorName, body))
|
||||
.map(kafkaConnectMapper::fromClient));
|
||||
}
|
||||
|
||||
|
@ -298,12 +298,12 @@ public class KafkaConnectService {
|
|||
}
|
||||
|
||||
public Mono<ConnectorPluginConfigValidationResponseDTO> validateConnectorPluginConfig(
|
||||
KafkaCluster cluster, String connectName, String pluginName, Mono<Object> requestBody) {
|
||||
KafkaCluster cluster, String connectName, String pluginName, Mono<Map<String, Object>> requestBody) {
|
||||
return api(cluster, connectName)
|
||||
.mono(client ->
|
||||
requestBody
|
||||
.flatMap(body ->
|
||||
client.validateConnectorPluginConfig(pluginName, (Map<String, Object>) body))
|
||||
client.validateConnectorPluginConfig(pluginName, body))
|
||||
.map(kafkaConnectMapper::fromClient)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,9 +3,8 @@ package com.provectus.kafka.ui.service;
|
|||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import com.provectus.kafka.ui.emitter.BackwardRecordEmitter;
|
||||
import com.provectus.kafka.ui.emitter.ForwardRecordEmitter;
|
||||
import com.provectus.kafka.ui.emitter.MessageFilterStats;
|
||||
import com.provectus.kafka.ui.emitter.MessageFilters;
|
||||
import com.provectus.kafka.ui.emitter.ResultSizeLimiter;
|
||||
import com.provectus.kafka.ui.emitter.MessagesProcessing;
|
||||
import com.provectus.kafka.ui.emitter.TailingEmitter;
|
||||
import com.provectus.kafka.ui.exception.TopicNotFoundException;
|
||||
import com.provectus.kafka.ui.exception.ValidationException;
|
||||
|
@ -14,9 +13,9 @@ import com.provectus.kafka.ui.model.CreateTopicMessageDTO;
|
|||
import com.provectus.kafka.ui.model.KafkaCluster;
|
||||
import com.provectus.kafka.ui.model.MessageFilterTypeDTO;
|
||||
import com.provectus.kafka.ui.model.SeekDirectionDTO;
|
||||
import com.provectus.kafka.ui.model.TopicMessageDTO;
|
||||
import com.provectus.kafka.ui.model.TopicMessageEventDTO;
|
||||
import com.provectus.kafka.ui.serde.api.Serde;
|
||||
import com.provectus.kafka.ui.serdes.ConsumerRecordDeserializer;
|
||||
import com.provectus.kafka.ui.serdes.ProducerRecordCreator;
|
||||
import com.provectus.kafka.ui.util.SslPropertiesUtil;
|
||||
import java.util.List;
|
||||
|
@ -162,13 +161,18 @@ public class MessagesService {
|
|||
@Nullable String valueSerde) {
|
||||
|
||||
java.util.function.Consumer<? super FluxSink<TopicMessageEventDTO>> emitter;
|
||||
ConsumerRecordDeserializer recordDeserializer =
|
||||
deserializationService.deserializerFor(cluster, topic, keySerde, valueSerde);
|
||||
|
||||
var processing = new MessagesProcessing(
|
||||
deserializationService.deserializerFor(cluster, topic, keySerde, valueSerde),
|
||||
getMsgFilter(query, filterQueryType),
|
||||
seekDirection == SeekDirectionDTO.TAILING ? null : limit
|
||||
);
|
||||
|
||||
if (seekDirection.equals(SeekDirectionDTO.FORWARD)) {
|
||||
emitter = new ForwardRecordEmitter(
|
||||
() -> consumerGroupService.createConsumer(cluster),
|
||||
consumerPosition,
|
||||
recordDeserializer,
|
||||
processing,
|
||||
cluster.getPollingSettings()
|
||||
);
|
||||
} else if (seekDirection.equals(SeekDirectionDTO.BACKWARD)) {
|
||||
|
@ -176,33 +180,22 @@ public class MessagesService {
|
|||
() -> consumerGroupService.createConsumer(cluster),
|
||||
consumerPosition,
|
||||
limit,
|
||||
recordDeserializer,
|
||||
processing,
|
||||
cluster.getPollingSettings()
|
||||
);
|
||||
} else {
|
||||
emitter = new TailingEmitter(
|
||||
() -> consumerGroupService.createConsumer(cluster),
|
||||
consumerPosition,
|
||||
recordDeserializer,
|
||||
processing,
|
||||
cluster.getPollingSettings()
|
||||
);
|
||||
}
|
||||
MessageFilterStats filterStats = new MessageFilterStats();
|
||||
return Flux.create(emitter)
|
||||
.contextWrite(ctx -> ctx.put(MessageFilterStats.class, filterStats))
|
||||
.filter(getMsgFilter(query, filterQueryType, filterStats))
|
||||
.map(getDataMasker(cluster, topic))
|
||||
.takeWhile(createTakeWhilePredicate(seekDirection, limit))
|
||||
.map(throttleUiPublish(seekDirection));
|
||||
}
|
||||
|
||||
private Predicate<TopicMessageEventDTO> createTakeWhilePredicate(
|
||||
SeekDirectionDTO seekDirection, int limit) {
|
||||
return seekDirection == SeekDirectionDTO.TAILING
|
||||
? evt -> true // no limit for tailing
|
||||
: new ResultSizeLimiter(limit);
|
||||
}
|
||||
|
||||
private UnaryOperator<TopicMessageEventDTO> getDataMasker(KafkaCluster cluster, String topicName) {
|
||||
var keyMasker = cluster.getMasking().getMaskingFunction(topicName, Serde.Target.KEY);
|
||||
var valMasker = cluster.getMasking().getMaskingFunction(topicName, Serde.Target.VALUE);
|
||||
|
@ -211,32 +204,18 @@ public class MessagesService {
|
|||
return evt;
|
||||
}
|
||||
return evt.message(
|
||||
evt.getMessage()
|
||||
.key(keyMasker.apply(evt.getMessage().getKey()))
|
||||
.content(valMasker.apply(evt.getMessage().getContent())));
|
||||
evt.getMessage()
|
||||
.key(keyMasker.apply(evt.getMessage().getKey()))
|
||||
.content(valMasker.apply(evt.getMessage().getContent())));
|
||||
};
|
||||
}
|
||||
|
||||
private Predicate<TopicMessageEventDTO> getMsgFilter(String query,
|
||||
MessageFilterTypeDTO filterQueryType,
|
||||
MessageFilterStats filterStats) {
|
||||
private Predicate<TopicMessageDTO> getMsgFilter(String query,
|
||||
MessageFilterTypeDTO filterQueryType) {
|
||||
if (StringUtils.isEmpty(query)) {
|
||||
return evt -> true;
|
||||
}
|
||||
var messageFilter = MessageFilters.createMsgFilter(query, filterQueryType);
|
||||
return evt -> {
|
||||
// we only apply filter for message events
|
||||
if (evt.getType() == TopicMessageEventDTO.TypeEnum.MESSAGE) {
|
||||
try {
|
||||
return messageFilter.test(evt.getMessage());
|
||||
} catch (Exception e) {
|
||||
filterStats.incrementApplyErrors();
|
||||
log.trace("Error applying filter '{}' for message {}", query, evt.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return MessageFilters.createMsgFilter(query, filterQueryType);
|
||||
}
|
||||
|
||||
private <T> UnaryOperator<T> throttleUiPublish(SeekDirectionDTO seekDirection) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import static java.util.stream.Collectors.toList;
|
|||
import static java.util.stream.Collectors.toMap;
|
||||
import static org.apache.kafka.clients.admin.ListOffsetsResult.ListOffsetsResultInfo;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
@ -514,6 +515,14 @@ public class ReactiveAdminClient implements Closeable {
|
|||
.flatMap(parts -> listOffsetsUnsafe(parts, offsetSpec));
|
||||
}
|
||||
|
||||
/**
|
||||
* List offset for the specified topics, skipping no-leader partitions.
|
||||
*/
|
||||
public Mono<Map<TopicPartition, Long>> listOffsets(Collection<TopicDescription> topicDescriptions,
|
||||
OffsetSpec offsetSpec) {
|
||||
return listOffsetsUnsafe(filterPartitionsWithLeaderCheck(topicDescriptions, p -> true, false), offsetSpec);
|
||||
}
|
||||
|
||||
private Mono<Collection<TopicPartition>> filterPartitionsWithLeaderCheck(Collection<TopicPartition> partitions,
|
||||
boolean failOnUnknownLeader) {
|
||||
var targetTopics = partitions.stream().map(TopicPartition::topic).collect(Collectors.toSet());
|
||||
|
@ -523,34 +532,44 @@ public class ReactiveAdminClient implements Closeable {
|
|||
descriptions.values(), partitions::contains, failOnUnknownLeader));
|
||||
}
|
||||
|
||||
private Set<TopicPartition> filterPartitionsWithLeaderCheck(Collection<TopicDescription> topicDescriptions,
|
||||
@VisibleForTesting
|
||||
static Set<TopicPartition> filterPartitionsWithLeaderCheck(Collection<TopicDescription> topicDescriptions,
|
||||
Predicate<TopicPartition> partitionPredicate,
|
||||
boolean failOnUnknownLeader) {
|
||||
var goodPartitions = new HashSet<TopicPartition>();
|
||||
for (TopicDescription description : topicDescriptions) {
|
||||
var goodTopicPartitions = new ArrayList<TopicPartition>();
|
||||
for (TopicPartitionInfo partitionInfo : description.partitions()) {
|
||||
TopicPartition topicPartition = new TopicPartition(description.name(), partitionInfo.partition());
|
||||
if (!partitionPredicate.test(topicPartition)) {
|
||||
continue;
|
||||
if (partitionInfo.leader() == null) {
|
||||
if (failOnUnknownLeader) {
|
||||
throw new ValidationException(String.format("Topic partition %s has no leader", topicPartition));
|
||||
} else {
|
||||
// if ANY of topic partitions has no leader - we have to skip all topic partitions
|
||||
goodTopicPartitions.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (partitionInfo.leader() != null) {
|
||||
goodPartitions.add(topicPartition);
|
||||
} else if (failOnUnknownLeader) {
|
||||
throw new ValidationException(String.format("Topic partition %s has no leader", topicPartition));
|
||||
if (partitionPredicate.test(topicPartition)) {
|
||||
goodTopicPartitions.add(topicPartition);
|
||||
}
|
||||
}
|
||||
goodPartitions.addAll(goodTopicPartitions);
|
||||
}
|
||||
return goodPartitions;
|
||||
}
|
||||
|
||||
// 1. NOTE(!): should only apply for partitions with existing leader,
|
||||
// 1. NOTE(!): should only apply for partitions from topics where all partitions have leaders,
|
||||
// otherwise AdminClient will try to fetch topic metadata, fail and retry infinitely (until timeout)
|
||||
// 2. NOTE(!): Skips partitions that were not initialized yet
|
||||
// (UnknownTopicOrPartitionException thrown, ex. after topic creation)
|
||||
// 3. TODO: check if it is a bug that AdminClient never throws LeaderNotAvailableException and just retrying instead
|
||||
@KafkaClientInternalsDependant
|
||||
public Mono<Map<TopicPartition, Long>> listOffsetsUnsafe(Collection<TopicPartition> partitions,
|
||||
OffsetSpec offsetSpec) {
|
||||
@VisibleForTesting
|
||||
Mono<Map<TopicPartition, Long>> listOffsetsUnsafe(Collection<TopicPartition> partitions, OffsetSpec offsetSpec) {
|
||||
if (partitions.isEmpty()) {
|
||||
return Mono.just(Map.of());
|
||||
}
|
||||
|
||||
Function<Collection<TopicPartition>, Mono<Map<TopicPartition, Long>>> call =
|
||||
parts -> {
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.provectus.kafka.ui.service;
|
|||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.provectus.kafka.ui.config.ClustersProperties;
|
||||
import com.provectus.kafka.ui.exception.TopicMetadataException;
|
||||
import com.provectus.kafka.ui.exception.TopicNotFoundException;
|
||||
|
@ -136,22 +137,14 @@ public class TopicsService {
|
|||
}
|
||||
|
||||
private Mono<InternalPartitionsOffsets> getPartitionOffsets(Map<String, TopicDescription>
|
||||
descriptions,
|
||||
descriptionsMap,
|
||||
ReactiveAdminClient ac) {
|
||||
var topicPartitions = descriptions.values().stream()
|
||||
.flatMap(desc ->
|
||||
desc.partitions().stream()
|
||||
// list offsets should only be applied to partitions with existing leader
|
||||
// (see ReactiveAdminClient.listOffsetsUnsafe(..) docs)
|
||||
.filter(tp -> tp.leader() != null)
|
||||
.map(p -> new TopicPartition(desc.name(), p.partition())))
|
||||
.collect(toList());
|
||||
|
||||
return ac.listOffsetsUnsafe(topicPartitions, OffsetSpec.earliest())
|
||||
.zipWith(ac.listOffsetsUnsafe(topicPartitions, OffsetSpec.latest()),
|
||||
var descriptions = descriptionsMap.values();
|
||||
return ac.listOffsets(descriptions, OffsetSpec.earliest())
|
||||
.zipWith(ac.listOffsets(descriptions, OffsetSpec.latest()),
|
||||
(earliest, latest) ->
|
||||
topicPartitions.stream()
|
||||
.filter(tp -> earliest.containsKey(tp) && latest.containsKey(tp))
|
||||
Sets.intersection(earliest.keySet(), latest.keySet())
|
||||
.stream()
|
||||
.map(tp ->
|
||||
Map.entry(tp,
|
||||
new InternalPartitionsOffsets.Offsets(
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.provectus.kafka.ui.service.analyze;
|
|||
|
||||
import com.provectus.kafka.ui.model.TopicAnalysisSizeStatsDTO;
|
||||
import com.provectus.kafka.ui.model.TopicAnalysisStatsDTO;
|
||||
import com.provectus.kafka.ui.model.TopicAnalysisStatsHourlyMsgCountsDTO;
|
||||
import com.provectus.kafka.ui.model.TopicAnalysisStatsHourlyMsgCountsInnerDTO;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
|
@ -78,10 +78,10 @@ class TopicAnalysisStats {
|
|||
}
|
||||
}
|
||||
|
||||
List<TopicAnalysisStatsHourlyMsgCountsDTO> toDto() {
|
||||
List<TopicAnalysisStatsHourlyMsgCountsInnerDTO> toDto() {
|
||||
return hourlyStats.entrySet().stream()
|
||||
.sorted(Comparator.comparingLong(Map.Entry::getKey))
|
||||
.map(e -> new TopicAnalysisStatsHourlyMsgCountsDTO()
|
||||
.map(e -> new TopicAnalysisStatsHourlyMsgCountsInnerDTO()
|
||||
.hourStart(e.getKey())
|
||||
.count(e.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
|
|
|
@ -52,7 +52,10 @@ public class KsqlApiClient {
|
|||
boolean error;
|
||||
|
||||
public Optional<JsonNode> getColumnValue(List<JsonNode> row, String column) {
|
||||
return Optional.ofNullable(row.get(columnNames.indexOf(column)));
|
||||
int colIdx = columnNames.indexOf(column);
|
||||
return colIdx >= 0
|
||||
? Optional.ofNullable(row.get(colIdx))
|
||||
: Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,14 @@ public class KsqlServiceV2 {
|
|||
.name(resp.getColumnValue(row, "name").map(JsonNode::asText).orElse(null))
|
||||
.topic(resp.getColumnValue(row, "topic").map(JsonNode::asText).orElse(null))
|
||||
.keyFormat(resp.getColumnValue(row, "keyFormat").map(JsonNode::asText).orElse(null))
|
||||
.valueFormat(resp.getColumnValue(row, "valueFormat").map(JsonNode::asText).orElse(null)))
|
||||
.valueFormat(
|
||||
// for old versions (<0.13) "format" column is filled,
|
||||
// for new version "keyFormat" & "valueFormat" columns should be filled
|
||||
resp.getColumnValue(row, "valueFormat")
|
||||
.or(() -> resp.getColumnValue(row, "format"))
|
||||
.map(JsonNode::asText)
|
||||
.orElse(null))
|
||||
)
|
||||
.collect(Collectors.toList()));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.provectus.kafka.ui.service.rbac.extractor.GithubAuthorityExtractor;
|
|||
import com.provectus.kafka.ui.service.rbac.extractor.GoogleAuthorityExtractor;
|
||||
import com.provectus.kafka.ui.service.rbac.extractor.LdapAuthorityExtractor;
|
||||
import com.provectus.kafka.ui.service.rbac.extractor.ProviderAuthorityExtractor;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -28,7 +29,6 @@ import java.util.function.Predicate;
|
|||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package com.provectus.kafka.ui.service.rbac.extractor;
|
||||
|
||||
import com.nimbusds.jose.shaded.json.JSONArray;
|
||||
import com.provectus.kafka.ui.model.rbac.Role;
|
||||
import com.provectus.kafka.ui.model.rbac.provider.Provider;
|
||||
import com.provectus.kafka.ui.service.rbac.AccessControlService;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -44,7 +44,7 @@ public class CognitoAuthorityExtractor implements ProviderAuthorityExtractor {
|
|||
.map(Role::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
JSONArray groups = principal.getAttribute(COGNITO_GROUPS_ATTRIBUTE_NAME);
|
||||
List<String> groups = principal.getAttribute(COGNITO_GROUPS_ATTRIBUTE_NAME);
|
||||
if (groups == null) {
|
||||
log.debug("Cognito groups param is not present");
|
||||
return Mono.just(groupsByUsername);
|
||||
|
@ -56,9 +56,8 @@ public class CognitoAuthorityExtractor implements ProviderAuthorityExtractor {
|
|||
.stream()
|
||||
.filter(s -> s.getProvider().equals(Provider.OAUTH_COGNITO))
|
||||
.filter(s -> s.getType().equals("group"))
|
||||
.anyMatch(subject -> Stream.of(groups.toArray())
|
||||
.anyMatch(subject -> Stream.of(groups)
|
||||
.map(Object::toString)
|
||||
.distinct()
|
||||
.anyMatch(cognitoGroup -> cognitoGroup.equals(subject.getValue()))
|
||||
))
|
||||
.map(Role::getName)
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package com.provectus.kafka.ui.util;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.time.Duration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Slf4j
|
||||
public class GithubReleaseInfo {
|
||||
|
||||
private static final String GITHUB_LATEST_RELEASE_RETRIEVAL_URL =
|
||||
"https://api.github.com/repos/provectus/kafka-ui/releases/latest";
|
||||
|
||||
private static final Duration GITHUB_API_MAX_WAIT_TIME = Duration.ofSeconds(2);
|
||||
|
||||
public record GithubReleaseDto(String html_url, String tag_name, String published_at) {
|
||||
|
||||
static GithubReleaseDto empty() {
|
||||
return new GithubReleaseDto(null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private volatile GithubReleaseDto release = GithubReleaseDto.empty();
|
||||
|
||||
private final Mono<Void> refreshMono;
|
||||
|
||||
public GithubReleaseInfo() {
|
||||
this(GITHUB_LATEST_RELEASE_RETRIEVAL_URL);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
GithubReleaseInfo(String url) {
|
||||
this.refreshMono = WebClient.create()
|
||||
.get()
|
||||
.uri(url)
|
||||
.exchangeToMono(resp -> resp.bodyToMono(GithubReleaseDto.class))
|
||||
.timeout(GITHUB_API_MAX_WAIT_TIME)
|
||||
.doOnError(th -> log.trace("Error getting latest github release info", th))
|
||||
.onErrorResume(th -> true, th -> Mono.just(GithubReleaseDto.empty()))
|
||||
.doOnNext(release -> this.release = release)
|
||||
.then();
|
||||
}
|
||||
|
||||
public GithubReleaseDto get() {
|
||||
return release;
|
||||
}
|
||||
|
||||
public Mono<Void> refresh() {
|
||||
return refreshMono;
|
||||
}
|
||||
|
||||
}
|
|
@ -16,7 +16,7 @@ import org.springframework.context.ApplicationContextInitializer;
|
|||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.test.util.TestSocketUtils;
|
||||
import org.testcontainers.containers.KafkaContainer;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
@ -61,7 +61,7 @@ public abstract class AbstractIntegrationTest {
|
|||
System.setProperty("kafka.clusters.0.bootstrapServers", kafka.getBootstrapServers());
|
||||
// List unavailable hosts to verify failover
|
||||
System.setProperty("kafka.clusters.0.schemaRegistry", String.format("http://localhost:%1$s,http://localhost:%1$s,%2$s",
|
||||
SocketUtils.findAvailableTcpPort(), schemaRegistry.getUrl()));
|
||||
TestSocketUtils.findAvailableTcpPort(), schemaRegistry.getUrl()));
|
||||
System.setProperty("kafka.clusters.0.kafkaConnect.0.name", "kafka-connect");
|
||||
System.setProperty("kafka.clusters.0.kafkaConnect.0.userName", "kafka-connect");
|
||||
System.setProperty("kafka.clusters.0.kafkaConnect.0.password", "kafka-connect");
|
||||
|
|
|
@ -5,13 +5,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.actuate.endpoint.Sanitizer;
|
||||
|
||||
class KafkaConfigSanitizerTest {
|
||||
|
||||
@Test
|
||||
void doNothingIfEnabledPropertySetToFalse() {
|
||||
final Sanitizer sanitizer = new KafkaConfigSanitizer(false, Collections.emptyList());
|
||||
final var sanitizer = new KafkaConfigSanitizer(false, Collections.emptyList());
|
||||
assertThat(sanitizer.sanitize("password", "secret")).isEqualTo("secret");
|
||||
assertThat(sanitizer.sanitize("sasl.jaas.config", "secret")).isEqualTo("secret");
|
||||
assertThat(sanitizer.sanitize("database.password", "secret")).isEqualTo("secret");
|
||||
|
@ -19,7 +18,7 @@ class KafkaConfigSanitizerTest {
|
|||
|
||||
@Test
|
||||
void obfuscateCredentials() {
|
||||
final Sanitizer sanitizer = new KafkaConfigSanitizer(true, Collections.emptyList());
|
||||
final var sanitizer = new KafkaConfigSanitizer(true, Collections.emptyList());
|
||||
assertThat(sanitizer.sanitize("sasl.jaas.config", "secret")).isEqualTo("******");
|
||||
assertThat(sanitizer.sanitize("consumer.sasl.jaas.config", "secret")).isEqualTo("******");
|
||||
assertThat(sanitizer.sanitize("producer.sasl.jaas.config", "secret")).isEqualTo("******");
|
||||
|
@ -37,7 +36,7 @@ class KafkaConfigSanitizerTest {
|
|||
|
||||
@Test
|
||||
void notObfuscateNormalConfigs() {
|
||||
final Sanitizer sanitizer = new KafkaConfigSanitizer(true, Collections.emptyList());
|
||||
final var sanitizer = new KafkaConfigSanitizer(true, Collections.emptyList());
|
||||
assertThat(sanitizer.sanitize("security.protocol", "SASL_SSL")).isEqualTo("SASL_SSL");
|
||||
final String[] bootstrapServer = new String[] {"test1:9092", "test2:9092"};
|
||||
assertThat(sanitizer.sanitize("bootstrap.servers", bootstrapServer)).isEqualTo(bootstrapServer);
|
||||
|
@ -45,7 +44,7 @@ class KafkaConfigSanitizerTest {
|
|||
|
||||
@Test
|
||||
void obfuscateCredentialsWithDefinedPatterns() {
|
||||
final Sanitizer sanitizer = new KafkaConfigSanitizer(true, Arrays.asList("kafka.ui", ".*test.*"));
|
||||
final var sanitizer = new KafkaConfigSanitizer(true, Arrays.asList("kafka.ui", ".*test.*"));
|
||||
assertThat(sanitizer.sanitize("consumer.kafka.ui", "secret")).isEqualTo("******");
|
||||
assertThat(sanitizer.sanitize("this.is.test.credentials", "secret")).isEqualTo("******");
|
||||
assertThat(sanitizer.sanitize("this.is.not.credential", "not.credential"))
|
||||
|
|
|
@ -4,8 +4,11 @@ import static com.provectus.kafka.ui.service.ReactiveAdminClient.toMonoWithExcep
|
|||
import static java.util.Objects.requireNonNull;
|
||||
import static org.apache.kafka.clients.admin.ListOffsetsResult.ListOffsetsResultInfo;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||
|
||||
import com.provectus.kafka.ui.AbstractIntegrationTest;
|
||||
import com.provectus.kafka.ui.exception.ValidationException;
|
||||
import com.provectus.kafka.ui.producer.KafkaTestProducer;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
|
@ -22,16 +25,20 @@ import org.apache.kafka.clients.admin.Config;
|
|||
import org.apache.kafka.clients.admin.ConfigEntry;
|
||||
import org.apache.kafka.clients.admin.NewTopic;
|
||||
import org.apache.kafka.clients.admin.OffsetSpec;
|
||||
import org.apache.kafka.clients.admin.TopicDescription;
|
||||
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
||||
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
|
||||
import org.apache.kafka.clients.producer.ProducerRecord;
|
||||
import org.apache.kafka.common.KafkaFuture;
|
||||
import org.apache.kafka.common.Node;
|
||||
import org.apache.kafka.common.TopicPartition;
|
||||
import org.apache.kafka.common.TopicPartitionInfo;
|
||||
import org.apache.kafka.common.config.ConfigResource;
|
||||
import org.apache.kafka.common.errors.UnknownTopicOrPartitionException;
|
||||
import org.apache.kafka.common.internals.KafkaFutureImpl;
|
||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||
import org.assertj.core.api.ThrowableAssert;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -133,6 +140,56 @@ class ReactiveAdminClientTest extends AbstractIntegrationTest {
|
|||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void filterPartitionsWithLeaderCheckSkipsPartitionsFromTopicWhereSomePartitionsHaveNoLeader() {
|
||||
var filteredPartitions = ReactiveAdminClient.filterPartitionsWithLeaderCheck(
|
||||
List.of(
|
||||
// contains partitions with no leader
|
||||
new TopicDescription("noLeaderTopic", false,
|
||||
List.of(
|
||||
new TopicPartitionInfo(0, new Node(1, "n1", 9092), List.of(), List.of()),
|
||||
new TopicPartitionInfo(1, null, List.of(), List.of()))),
|
||||
// should be skipped by predicate
|
||||
new TopicDescription("skippingByPredicate", false,
|
||||
List.of(
|
||||
new TopicPartitionInfo(0, new Node(1, "n1", 9092), List.of(), List.of()))),
|
||||
// good topic
|
||||
new TopicDescription("good", false,
|
||||
List.of(
|
||||
new TopicPartitionInfo(0, new Node(1, "n1", 9092), List.of(), List.of()),
|
||||
new TopicPartitionInfo(1, new Node(2, "n2", 9092), List.of(), List.of()))
|
||||
)),
|
||||
p -> !p.topic().equals("skippingByPredicate"),
|
||||
false
|
||||
);
|
||||
|
||||
assertThat(filteredPartitions)
|
||||
.containsExactlyInAnyOrder(
|
||||
new TopicPartition("good", 0),
|
||||
new TopicPartition("good", 1)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void filterPartitionsWithLeaderCheckThrowExceptionIfThereIsSomePartitionsWithoutLeaderAndFlagSet() {
|
||||
ThrowingCallable call = () -> ReactiveAdminClient.filterPartitionsWithLeaderCheck(
|
||||
List.of(
|
||||
// contains partitions with no leader
|
||||
new TopicDescription("t1", false,
|
||||
List.of(
|
||||
new TopicPartitionInfo(0, new Node(1, "n1", 9092), List.of(), List.of()),
|
||||
new TopicPartitionInfo(1, null, List.of(), List.of()))),
|
||||
new TopicDescription("t2", false,
|
||||
List.of(
|
||||
new TopicPartitionInfo(0, new Node(1, "n1", 9092), List.of(), List.of()))
|
||||
)),
|
||||
p -> true,
|
||||
// setting failOnNoLeader flag
|
||||
true
|
||||
);
|
||||
assertThatThrownBy(call).isInstanceOf(ValidationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListOffsetsUnsafe() {
|
||||
String topic = UUID.randomUUID().toString();
|
||||
|
|
|
@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
import com.provectus.kafka.ui.AbstractIntegrationTest;
|
||||
import com.provectus.kafka.ui.emitter.BackwardRecordEmitter;
|
||||
import com.provectus.kafka.ui.emitter.ForwardRecordEmitter;
|
||||
import com.provectus.kafka.ui.emitter.MessagesProcessing;
|
||||
import com.provectus.kafka.ui.emitter.PollingSettings;
|
||||
import com.provectus.kafka.ui.model.ConsumerPosition;
|
||||
import com.provectus.kafka.ui.model.TopicMessageEventDTO;
|
||||
|
@ -106,12 +107,16 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
);
|
||||
}
|
||||
|
||||
private MessagesProcessing createMessagesProcessing() {
|
||||
return new MessagesProcessing(RECORD_DESERIALIZER, msg -> true, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pollNothingOnEmptyTopic() {
|
||||
var forwardEmitter = new ForwardRecordEmitter(
|
||||
this::createConsumer,
|
||||
new ConsumerPosition(BEGINNING, EMPTY_TOPIC, null),
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -119,7 +124,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
this::createConsumer,
|
||||
new ConsumerPosition(BEGINNING, EMPTY_TOPIC, null),
|
||||
100,
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -141,7 +146,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
var forwardEmitter = new ForwardRecordEmitter(
|
||||
this::createConsumer,
|
||||
new ConsumerPosition(BEGINNING, TOPIC, null),
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -149,7 +154,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
this::createConsumer,
|
||||
new ConsumerPosition(LATEST, TOPIC, null),
|
||||
PARTITIONS * MSGS_PER_PARTITION,
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -170,7 +175,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
var forwardEmitter = new ForwardRecordEmitter(
|
||||
this::createConsumer,
|
||||
new ConsumerPosition(OFFSET, TOPIC, targetOffsets),
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -178,7 +183,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
this::createConsumer,
|
||||
new ConsumerPosition(OFFSET, TOPIC, targetOffsets),
|
||||
PARTITIONS * MSGS_PER_PARTITION,
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -215,7 +220,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
var forwardEmitter = new ForwardRecordEmitter(
|
||||
this::createConsumer,
|
||||
new ConsumerPosition(TIMESTAMP, TOPIC, targetTimestamps),
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -223,7 +228,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
this::createConsumer,
|
||||
new ConsumerPosition(TIMESTAMP, TOPIC, targetTimestamps),
|
||||
PARTITIONS * MSGS_PER_PARTITION,
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -254,7 +259,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
this::createConsumer,
|
||||
new ConsumerPosition(OFFSET, TOPIC, targetOffsets),
|
||||
numMessages,
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
@ -280,7 +285,7 @@ class RecordEmitterTest extends AbstractIntegrationTest {
|
|||
this::createConsumer,
|
||||
new ConsumerPosition(OFFSET, TOPIC, offsets),
|
||||
100,
|
||||
RECORD_DESERIALIZER,
|
||||
createMessagesProcessing(),
|
||||
PollingSettings.createDefault()
|
||||
);
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
class KsqlServiceV2Test extends AbstractIntegrationTest {
|
||||
|
@ -27,8 +26,6 @@ class KsqlServiceV2Test extends AbstractIntegrationTest {
|
|||
private static final Set<String> STREAMS_TO_DELETE = new CopyOnWriteArraySet<>();
|
||||
private static final Set<String> TABLES_TO_DELETE = new CopyOnWriteArraySet<>();
|
||||
|
||||
private static final DataSize maxBuffSize = DataSize.ofMegabytes(20);
|
||||
|
||||
@BeforeAll
|
||||
static void init() {
|
||||
KSQL_DB.start();
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package com.provectus.kafka.ui.util;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
class GithubReleaseInfoTest {
|
||||
|
||||
private final MockWebServer mockWebServer = new MockWebServer();
|
||||
|
||||
@BeforeEach
|
||||
void startMockServer() throws IOException {
|
||||
mockWebServer.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void stopMockServer() throws IOException {
|
||||
mockWebServer.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
mockWebServer.enqueue(new MockResponse()
|
||||
.addHeader("content-type: application/json")
|
||||
.setBody("""
|
||||
{
|
||||
"published_at": "2023-03-09T16:11:31Z",
|
||||
"tag_name": "v0.6.0",
|
||||
"html_url": "https://github.com/provectus/kafka-ui/releases/tag/v0.6.0",
|
||||
"some_unused_prop": "ololo"
|
||||
}
|
||||
"""));
|
||||
var url = mockWebServer.url("repos/provectus/kafka-ui/releases/latest").toString();
|
||||
|
||||
var infoHolder = new GithubReleaseInfo(url);
|
||||
infoHolder.refresh().block();
|
||||
|
||||
var i = infoHolder.get();
|
||||
assertThat(i.html_url())
|
||||
.isEqualTo("https://github.com/provectus/kafka-ui/releases/tag/v0.6.0");
|
||||
assertThat(i.published_at())
|
||||
.isEqualTo("2023-03-09T16:11:31Z");
|
||||
assertThat(i.tag_name())
|
||||
.isEqualTo("v0.6.0");
|
||||
}
|
||||
|
||||
}
|
|
@ -27,20 +27,24 @@
|
|||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>${swagger-annotations.version}</version>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-integration-jakarta</artifactId>
|
||||
<version>2.2.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openapitools</groupId>
|
||||
<artifactId>jackson-databind-nullable</artifactId>
|
||||
<version>${jackson-databind-nullable.version}</version>
|
||||
<version>0.2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<scope>provided</scope>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -71,6 +75,7 @@
|
|||
<library>webclient</library>
|
||||
<useBeanValidation>true</useBeanValidation>
|
||||
<dateLibrary>java8</dateLibrary>
|
||||
<useJakartaEe>true</useJakartaEe>
|
||||
</configOptions>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
@ -80,8 +85,7 @@
|
|||
<goal>generate</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<inputSpec>${project.basedir}/src/main/resources/swagger/kafka-ui-api.yaml
|
||||
</inputSpec>
|
||||
<inputSpec>${project.basedir}/src/main/resources/swagger/kafka-ui-api.yaml</inputSpec>
|
||||
<output>${project.build.directory}/generated-sources/api</output>
|
||||
<generatorName>spring</generatorName>
|
||||
<modelNameSuffix>DTO</modelNameSuffix>
|
||||
|
@ -89,14 +93,12 @@
|
|||
<modelPackage>com.provectus.kafka.ui.model</modelPackage>
|
||||
<apiPackage>com.provectus.kafka.ui.api</apiPackage>
|
||||
<sourceFolder>kafka-ui-contract</sourceFolder>
|
||||
|
||||
<reactive>true</reactive>
|
||||
|
||||
<interfaceOnly>true</interfaceOnly>
|
||||
<skipDefaultInterface>true</skipDefaultInterface>
|
||||
<useBeanValidation>true</useBeanValidation>
|
||||
<useTags>true</useTags>
|
||||
|
||||
<useSpringBoot3>true</useSpringBoot3>
|
||||
<dateLibrary>java8</dateLibrary>
|
||||
</configOptions>
|
||||
<typeMappings>
|
||||
|
@ -116,15 +118,13 @@
|
|||
<generatorName>java</generatorName>
|
||||
<generateApiTests>false</generateApiTests>
|
||||
<generateModelTests>false</generateModelTests>
|
||||
|
||||
<configOptions>
|
||||
<modelPackage>com.provectus.kafka.ui.connect.model</modelPackage>
|
||||
<apiPackage>com.provectus.kafka.ui.connect.api</apiPackage>
|
||||
<sourceFolder>kafka-connect-client</sourceFolder>
|
||||
|
||||
<asyncNative>true</asyncNative>
|
||||
<library>webclient</library>
|
||||
|
||||
<useJakartaEe>true</useJakartaEe>
|
||||
<useBeanValidation>true</useBeanValidation>
|
||||
<dateLibrary>java8</dateLibrary>
|
||||
</configOptions>
|
||||
|
@ -142,15 +142,13 @@
|
|||
<generatorName>java</generatorName>
|
||||
<generateApiTests>false</generateApiTests>
|
||||
<generateModelTests>false</generateModelTests>
|
||||
|
||||
<configOptions>
|
||||
<modelPackage>com.provectus.kafka.ui.sr.model</modelPackage>
|
||||
<apiPackage>com.provectus.kafka.ui.sr.api</apiPackage>
|
||||
<sourceFolder>kafka-sr-client</sourceFolder>
|
||||
|
||||
<asyncNative>true</asyncNative>
|
||||
<library>webclient</library>
|
||||
|
||||
<useJakartaEe>true</useJakartaEe>
|
||||
<useBeanValidation>true</useBeanValidation>
|
||||
<dateLibrary>java8</dateLibrary>
|
||||
</configOptions>
|
||||
|
|
|
@ -2021,6 +2021,26 @@ components:
|
|||
type: string
|
||||
enum:
|
||||
- DYNAMIC_CONFIG
|
||||
build:
|
||||
type: object
|
||||
properties:
|
||||
commitId:
|
||||
type: string
|
||||
version:
|
||||
type: string
|
||||
buildTime:
|
||||
type: string
|
||||
isLatestRelease:
|
||||
type: boolean
|
||||
latestRelease:
|
||||
type: object
|
||||
properties:
|
||||
versionTag:
|
||||
type: string
|
||||
publishedAt:
|
||||
type: string
|
||||
htmlUrl:
|
||||
type: string
|
||||
|
||||
Cluster:
|
||||
type: object
|
||||
|
@ -2493,6 +2513,10 @@ components:
|
|||
- UNKNOWN
|
||||
|
||||
ConsumerGroup:
|
||||
discriminator:
|
||||
propertyName: inherit
|
||||
mapping:
|
||||
details: "#/components/schemas/ConsumerGroupDetails"
|
||||
type: object
|
||||
properties:
|
||||
groupId:
|
||||
|
|
|
@ -27,7 +27,7 @@ This repository is for E2E UI automation.
|
|||
```
|
||||
git clone https://github.com/provectus/kafka-ui.git
|
||||
cd kafka-ui-e2e-checks
|
||||
docker pull selenoid/vnc:chrome_86.0
|
||||
docker pull selenoid/vnc_chrome:103.0
|
||||
```
|
||||
|
||||
### How to run checks
|
||||
|
@ -36,6 +36,7 @@ docker pull selenoid/vnc:chrome_86.0
|
|||
|
||||
```
|
||||
cd kafka-ui
|
||||
docker-compose -f kafka-ui-e2e-checks/docker/selenoid-local.yaml up -d
|
||||
docker-compose -f documentation/compose/e2e-tests.yaml up -d
|
||||
```
|
||||
|
||||
|
@ -51,6 +52,14 @@ docker-compose -f documentation/compose/e2e-tests.yaml up -d
|
|||
-Dbrowser=local
|
||||
```
|
||||
|
||||
Expected Location of Chrome
|
||||
```
|
||||
Linux: /usr/bin/google-chrome1
|
||||
Mac: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
|
||||
Windows XP: %HOMEPATH%\Local Settings\Application Data\Google\Chrome\Application\chrome.exe
|
||||
Windows Vista and newer: C:\Users%USERNAME%\AppData\Local\Google\Chrome\Application\chrome.exe
|
||||
```
|
||||
|
||||
### Qase integration
|
||||
|
||||
Found instruction for Qase.io integration (for internal use only) at `kafka-ui-e2e-checks/QASE.md`
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
---
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
selenoid:
|
||||
network_mode: bridge
|
||||
image: aerokube/selenoid:1.10.7
|
||||
volumes:
|
||||
- "../selenoid/config:/etc/selenoid"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
- "../selenoid/video:/video"
|
||||
- "../selenoid/video:/opt/selenoid/video"
|
||||
- "../selenoid/logs:/opt/selenoid/logs"
|
||||
environment:
|
||||
- OVERRIDE_VIDEO_OUTPUT_DIR=video
|
||||
command: [ "-conf", "/etc/selenoid/browsers.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs" ]
|
||||
- OVERRIDE_VIDEO_OUTPUT_DIR=../selenoid/video
|
||||
command: [ "-conf", "/etc/selenoid/browsersGit.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs" ]
|
||||
ports:
|
||||
- "4444:4444"
|
||||
|
||||
|
@ -22,10 +24,10 @@ services:
|
|||
- selenoid
|
||||
ports:
|
||||
- "8081:8080"
|
||||
command: [ "--selenoid-uri", "http://localhost:4444" ]
|
||||
command: [ "--selenoid-uri", "http://selenoid:4444" ]
|
||||
|
||||
selenoid-chrome:
|
||||
network_mode: bridge
|
||||
image: selenoid/vnc:chrome_96.0
|
||||
image: selenoid/vnc_chrome:103.0
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
33
kafka-ui-e2e-checks/docker/selenoid-local.yaml
Normal file
33
kafka-ui-e2e-checks/docker/selenoid-local.yaml
Normal file
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
selenoid:
|
||||
network_mode: bridge
|
||||
image: aerokube/selenoid:1.10.7
|
||||
volumes:
|
||||
- "../selenoid/config:/etc/selenoid"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
- "../selenoid/video:/opt/selenoid/video"
|
||||
- "../selenoid/logs:/opt/selenoid/logs"
|
||||
environment:
|
||||
- OVERRIDE_VIDEO_OUTPUT_DIR=../selenoid/video
|
||||
command: [ "-conf", "/etc/selenoid/browsersLocal.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs" ]
|
||||
ports:
|
||||
- "4444:4444"
|
||||
|
||||
selenoid-ui:
|
||||
network_mode: bridge
|
||||
image: aerokube/selenoid-ui:latest-release
|
||||
links:
|
||||
- selenoid
|
||||
ports:
|
||||
- "8081:8080"
|
||||
command: [ "--selenoid-uri", "http://selenoid:4444" ]
|
||||
|
||||
selenoid-chrome:
|
||||
network_mode: bridge
|
||||
image: selenoid/vnc_chrome:103.0
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
|
@ -17,15 +17,14 @@
|
|||
<testcontainers.version>1.17.6</testcontainers.version>
|
||||
<httpcomponents.version>5.2.1</httpcomponents.version>
|
||||
<selenium.version>4.8.1</selenium.version>
|
||||
<selenide.version>6.11.2</selenide.version>
|
||||
<selenide.version>6.12.3</selenide.version>
|
||||
<testng.version>7.7.0</testng.version>
|
||||
<allure.version>2.21.0</allure.version>
|
||||
<qase.io.version>3.0.3</qase.io.version>
|
||||
<qase.io.version>3.0.4</qase.io.version>
|
||||
<aspectj.version>1.9.9.1</aspectj.version>
|
||||
<assertj.version>3.24.2</assertj.version>
|
||||
<hamcrest.version>2.2</hamcrest.version>
|
||||
<slf4j.version>1.7.36</slf4j.version>
|
||||
<dotenv.version>2.3.1</dotenv.version>
|
||||
<slf4j.version>2.0.5</slf4j.version>
|
||||
<kafka.version>3.3.1</kafka.version>
|
||||
</properties>
|
||||
|
||||
|
@ -122,6 +121,11 @@
|
|||
<artifactId>selenium</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${org.projectlombok.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.core5</groupId>
|
||||
<artifactId>httpcore5</artifactId>
|
||||
|
@ -132,6 +136,11 @@
|
|||
<artifactId>httpclient5</artifactId>
|
||||
<version>${httpcomponents.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-http-jdk-client</artifactId>
|
||||
<version>${selenium.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-http</artifactId>
|
||||
|
@ -187,16 +196,6 @@
|
|||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${org.projectlombok.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.cdimascio</groupId>
|
||||
<artifactId>dotenv-java</artifactId>
|
||||
<version>${dotenv.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.provectus</groupId>
|
||||
<artifactId>kafka-ui-contract</artifactId>
|
||||
|
@ -265,6 +264,37 @@
|
|||
<artifactId>allure-maven</artifactId>
|
||||
<version>2.10.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.3.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>checkstyle</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<violationSeverity>warning</violationSeverity>
|
||||
<failOnViolation>true</failOnViolation>
|
||||
<failsOnError>true</failsOnError>
|
||||
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||
<configLocation>file:${basedir}/../etc/checkstyle/checkstyle-e2e.xml</configLocation>
|
||||
<headerLocation>file:${basedir}/../etc/checkstyle/apache-header.txt</headerLocation>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
|
15
kafka-ui-e2e-checks/selenoid/config/browsersGit.json
Normal file
15
kafka-ui-e2e-checks/selenoid/config/browsersGit.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"chrome": {
|
||||
"default": "103.0",
|
||||
"versions": {
|
||||
"103.0": {
|
||||
"image": "selenoid/vnc_chrome:103.0",
|
||||
"hosts": [
|
||||
"host.docker.internal:172.17.0.1"
|
||||
],
|
||||
"port": "4444",
|
||||
"path": "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"chrome": {
|
||||
"default": "96.0",
|
||||
"default": "103.0",
|
||||
"versions": {
|
||||
"96.0": {
|
||||
"image": "selenoid/vnc_chrome:96.0",
|
||||
"103.0": {
|
||||
"image": "selenoid/vnc_chrome:103.0",
|
||||
"port": "4444",
|
||||
"path": "/"
|
||||
}
|
|
@ -7,5 +7,5 @@ import lombok.experimental.Accessors;
|
|||
@Accessors(chain = true)
|
||||
public class Connector {
|
||||
|
||||
private String name, config;
|
||||
private String name, config;
|
||||
}
|
||||
|
|
|
@ -1,33 +1,34 @@
|
|||
package com.provectus.kafka.ui.models;
|
||||
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
|
||||
|
||||
import com.provectus.kafka.ui.api.model.SchemaType;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class Schema {
|
||||
|
||||
private String name, valuePath;
|
||||
private SchemaType type;
|
||||
private String name, valuePath;
|
||||
private SchemaType type;
|
||||
|
||||
public static Schema createSchemaAvro() {
|
||||
return new Schema().setName("schema_avro-" + randomAlphabetic(5))
|
||||
.setType(SchemaType.AVRO)
|
||||
.setValuePath(System.getProperty("user.dir") + "/src/main/resources/testData/schemas/schema_avro_value.json");
|
||||
}
|
||||
public static Schema createSchemaAvro() {
|
||||
return new Schema().setName("schema_avro-" + randomAlphabetic(5))
|
||||
.setType(SchemaType.AVRO)
|
||||
.setValuePath(System.getProperty("user.dir") + "/src/main/resources/testData/schemas/schema_avro_value.json");
|
||||
}
|
||||
|
||||
public static Schema createSchemaJson() {
|
||||
return new Schema().setName("schema_json-" + randomAlphabetic(5))
|
||||
.setType(SchemaType.JSON)
|
||||
.setValuePath(System.getProperty("user.dir") + "/src/main/resources/testData/schemas/schema_json_Value.json");
|
||||
}
|
||||
public static Schema createSchemaJson() {
|
||||
return new Schema().setName("schema_json-" + randomAlphabetic(5))
|
||||
.setType(SchemaType.JSON)
|
||||
.setValuePath(System.getProperty("user.dir") + "/src/main/resources/testData/schemas/schema_json_Value.json");
|
||||
}
|
||||
|
||||
public static Schema createSchemaProtobuf() {
|
||||
return new Schema().setName("schema_protobuf-" + randomAlphabetic(5))
|
||||
.setType(SchemaType.PROTOBUF)
|
||||
.setValuePath(System.getProperty("user.dir") + "/src/main/resources/testData/schemas/schema_protobuf_value.txt");
|
||||
}
|
||||
public static Schema createSchemaProtobuf() {
|
||||
return new Schema().setName("schema_protobuf-" + randomAlphabetic(5))
|
||||
.setType(SchemaType.PROTOBUF)
|
||||
.setValuePath(
|
||||
System.getProperty("user.dir") + "/src/main/resources/testData/schemas/schema_protobuf_value.txt");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ import lombok.experimental.Accessors;
|
|||
@Accessors(chain = true)
|
||||
public class Topic {
|
||||
|
||||
private String name, timeToRetainData, maxMessageBytes, messageKey, messageContent, customParameterValue;
|
||||
private int numberOfPartitions;
|
||||
private CustomParameterType customParameterType;
|
||||
private CleanupPolicyValue cleanupPolicyValue;
|
||||
private MaxSizeOnDisk maxSizeOnDisk;
|
||||
private TimeToRetain timeToRetain;
|
||||
private String name, timeToRetainData, maxMessageBytes, messageKey, messageValue, customParameterValue;
|
||||
private int numberOfPartitions;
|
||||
private CustomParameterType customParameterType;
|
||||
private CleanupPolicyValue cleanupPolicyValue;
|
||||
private MaxSizeOnDisk maxSizeOnDisk;
|
||||
private TimeToRetain timeToRetain;
|
||||
}
|
||||
|
|
|
@ -1,135 +1,142 @@
|
|||
package com.provectus.kafka.ui.pages;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$$x;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.ElementsCollection;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.codeborne.selenide.WebDriverRunner;
|
||||
import com.provectus.kafka.ui.pages.panels.enums.MenuItem;
|
||||
import com.provectus.kafka.ui.utilities.WebUtils;
|
||||
import java.time.Duration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openqa.selenium.Keys;
|
||||
import org.openqa.selenium.interactions.Actions;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$$x;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
@Slf4j
|
||||
public abstract class BasePage extends WebUtils {
|
||||
|
||||
protected SelenideElement loadingSpinner = $x("//div[@role='progressbar']");
|
||||
protected SelenideElement submitBtn = $x("//button[@type='submit']");
|
||||
protected SelenideElement tableGrid = $x("//table");
|
||||
protected SelenideElement dotMenuBtn = $x("//button[@aria-label='Dropdown Toggle']");
|
||||
protected SelenideElement alertHeader = $x("//div[@role='alert']//div[@role='heading']");
|
||||
protected SelenideElement alertMessage = $x("//div[@role='alert']//div[@role='contentinfo']");
|
||||
protected SelenideElement confirmationMdl = $x("//div[text()= 'Confirm the action']/..");
|
||||
protected SelenideElement confirmBtn = $x("//button[contains(text(),'Confirm')]");
|
||||
protected SelenideElement cancelBtn = $x("//button[contains(text(),'Cancel')]");
|
||||
protected SelenideElement backBtn = $x("//button[contains(text(),'Back')]");
|
||||
protected SelenideElement nextBtn = $x("//button[contains(text(),'Next')]");
|
||||
protected ElementsCollection ddlOptions = $$x("//li[@value]");
|
||||
protected ElementsCollection gridItems = $$x("//tr[@class]");
|
||||
protected String summaryCellLocator = "//div[contains(text(),'%s')]";
|
||||
protected String tableElementNameLocator = "//tbody//a[contains(text(),'%s')]";
|
||||
protected String columnHeaderLocator = "//table//tr/th//div[text()='%s']";
|
||||
protected String pageTitleFromHeader = "//h1[text()='%s']";
|
||||
protected String pagePathFromHeader = "//a[text()='%s']/../h1";
|
||||
protected SelenideElement loadingSpinner = $x("//div[@role='progressbar']");
|
||||
protected SelenideElement submitBtn = $x("//button[@type='submit']");
|
||||
protected SelenideElement tableGrid = $x("//table");
|
||||
protected SelenideElement searchFld = $x("//input[@type='text'][contains(@id, ':r')]");
|
||||
protected SelenideElement dotMenuBtn = $x("//button[@aria-label='Dropdown Toggle']");
|
||||
protected SelenideElement alertHeader = $x("//div[@role='alert']//div[@role='heading']");
|
||||
protected SelenideElement alertMessage = $x("//div[@role='alert']//div[@role='contentinfo']");
|
||||
protected SelenideElement confirmationMdl = $x("//div[text()= 'Confirm the action']/..");
|
||||
protected SelenideElement confirmBtn = $x("//button[contains(text(),'Confirm')]");
|
||||
protected SelenideElement cancelBtn = $x("//button[contains(text(),'Cancel')]");
|
||||
protected SelenideElement backBtn = $x("//button[contains(text(),'Back')]");
|
||||
protected SelenideElement nextBtn = $x("//button[contains(text(),'Next')]");
|
||||
protected ElementsCollection ddlOptions = $$x("//li[@value]");
|
||||
protected ElementsCollection gridItems = $$x("//tr[@class]");
|
||||
protected String summaryCellLocator = "//div[contains(text(),'%s')]";
|
||||
protected String tableElementNameLocator = "//tbody//a[contains(text(),'%s')]";
|
||||
protected String columnHeaderLocator = "//table//tr/th//div[text()='%s']";
|
||||
protected String pageTitleFromHeader = "//h1[text()='%s']";
|
||||
protected String pagePathFromHeader = "//a[text()='%s']/../h1";
|
||||
|
||||
protected void waitUntilSpinnerDisappear() {
|
||||
log.debug("\nwaitUntilSpinnerDisappear");
|
||||
if (isVisible(loadingSpinner)) {
|
||||
loadingSpinner.shouldBe(Condition.disappear, Duration.ofSeconds(60));
|
||||
}
|
||||
protected void waitUntilSpinnerDisappear(int... timeoutInSeconds) {
|
||||
log.debug("\nwaitUntilSpinnerDisappear");
|
||||
if (isVisible(loadingSpinner, timeoutInSeconds)) {
|
||||
loadingSpinner.shouldBe(Condition.disappear, Duration.ofSeconds(60));
|
||||
}
|
||||
}
|
||||
|
||||
protected void searchItem(String tag) {
|
||||
log.debug("\nsearchItem: {}", tag);
|
||||
sendKeysAfterClear(searchFld, tag);
|
||||
searchFld.pressEnter().shouldHave(Condition.value(tag));
|
||||
waitUntilSpinnerDisappear(1);
|
||||
}
|
||||
|
||||
protected SelenideElement getPageTitleFromHeader(MenuItem menuItem) {
|
||||
return $x(String.format(pageTitleFromHeader, menuItem.getPageTitle()));
|
||||
}
|
||||
|
||||
protected SelenideElement getPagePathFromHeader(MenuItem menuItem) {
|
||||
return $x(String.format(pagePathFromHeader, menuItem.getPageTitle()));
|
||||
}
|
||||
|
||||
protected void clickSubmitBtn() {
|
||||
clickByJavaScript(submitBtn);
|
||||
}
|
||||
|
||||
protected void setJsonInputValue(SelenideElement jsonInput, String jsonConfig) {
|
||||
sendKeysByActions(jsonInput, jsonConfig.replace(" ", ""));
|
||||
new Actions(WebDriverRunner.getWebDriver())
|
||||
.keyDown(Keys.SHIFT)
|
||||
.sendKeys(Keys.PAGE_DOWN)
|
||||
.keyUp(Keys.SHIFT)
|
||||
.sendKeys(Keys.DELETE)
|
||||
.perform();
|
||||
}
|
||||
|
||||
protected SelenideElement getTableElement(String elementName) {
|
||||
log.debug("\ngetTableElement: {}", elementName);
|
||||
return $x(String.format(tableElementNameLocator, elementName));
|
||||
}
|
||||
|
||||
protected ElementsCollection getDdlOptions() {
|
||||
return ddlOptions;
|
||||
}
|
||||
|
||||
protected String getAlertHeader() {
|
||||
log.debug("\ngetAlertHeader");
|
||||
String result = alertHeader.shouldBe(Condition.visible).getText();
|
||||
log.debug("-> {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String getAlertMessage() {
|
||||
log.debug("\ngetAlertMessage");
|
||||
String result = alertMessage.shouldBe(Condition.visible).getText();
|
||||
log.debug("-> {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean isAlertVisible(AlertHeader header) {
|
||||
log.debug("\nisAlertVisible: {}", header.toString());
|
||||
boolean result = getAlertHeader().equals(header.toString());
|
||||
log.debug("-> {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean isAlertVisible(AlertHeader header, String message) {
|
||||
log.debug("\nisAlertVisible: {} {}", header, message);
|
||||
boolean result = isAlertVisible(header) && getAlertMessage().equals(message);
|
||||
log.debug("-> {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void clickConfirmButton() {
|
||||
confirmBtn.shouldBe(Condition.enabled).click();
|
||||
confirmBtn.shouldBe(Condition.disappear);
|
||||
}
|
||||
|
||||
protected void clickCancelButton() {
|
||||
cancelBtn.shouldBe(Condition.enabled).click();
|
||||
cancelBtn.shouldBe(Condition.disappear);
|
||||
}
|
||||
|
||||
protected boolean isConfirmationModalVisible() {
|
||||
return isVisible(confirmationMdl);
|
||||
}
|
||||
|
||||
public enum AlertHeader {
|
||||
SUCCESS("Success"),
|
||||
VALIDATION_ERROR("Validation Error"),
|
||||
BAD_REQUEST("400 Bad Request");
|
||||
|
||||
private final String value;
|
||||
|
||||
AlertHeader(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
protected SelenideElement getPageTitleFromHeader(MenuItem menuItem) {
|
||||
return $x(String.format(pageTitleFromHeader, menuItem.getPageTitle()));
|
||||
}
|
||||
|
||||
protected SelenideElement getPagePathFromHeader(MenuItem menuItem) {
|
||||
return $x(String.format(pagePathFromHeader, menuItem.getPageTitle()));
|
||||
}
|
||||
|
||||
protected void clickSubmitBtn() {
|
||||
clickByJavaScript(submitBtn);
|
||||
}
|
||||
|
||||
protected void setJsonInputValue(SelenideElement jsonInput, String jsonConfig) {
|
||||
sendKeysByActions(jsonInput, jsonConfig.replace(" ", ""));
|
||||
new Actions(WebDriverRunner.getWebDriver())
|
||||
.keyDown(Keys.SHIFT)
|
||||
.sendKeys(Keys.PAGE_DOWN)
|
||||
.keyUp(Keys.SHIFT)
|
||||
.sendKeys(Keys.DELETE)
|
||||
.perform();
|
||||
}
|
||||
|
||||
protected SelenideElement getTableElement(String elementName) {
|
||||
log.debug("\ngetTableElement: {}", elementName);
|
||||
return $x(String.format(tableElementNameLocator, elementName));
|
||||
}
|
||||
|
||||
protected ElementsCollection getDdlOptions() {
|
||||
return ddlOptions;
|
||||
}
|
||||
|
||||
protected String getAlertHeader() {
|
||||
log.debug("\ngetAlertHeader");
|
||||
String result = alertHeader.shouldBe(Condition.visible).getText();
|
||||
log.debug("-> {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String getAlertMessage() {
|
||||
log.debug("\ngetAlertMessage");
|
||||
String result = alertMessage.shouldBe(Condition.visible).getText();
|
||||
log.debug("-> {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean isAlertVisible(AlertHeader header) {
|
||||
log.debug("\nisAlertVisible: {}", header.toString());
|
||||
boolean result = getAlertHeader().equals(header.toString());
|
||||
log.debug("-> {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean isAlertVisible(AlertHeader header, String message) {
|
||||
log.debug("\nisAlertVisible: {} {}", header, message);
|
||||
boolean result = isAlertVisible(header) && getAlertMessage().equals(message);
|
||||
log.debug("-> {}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void clickConfirmButton() {
|
||||
confirmBtn.shouldBe(Condition.enabled).click();
|
||||
confirmBtn.shouldBe(Condition.disappear);
|
||||
}
|
||||
|
||||
protected void clickCancelButton() {
|
||||
cancelBtn.shouldBe(Condition.enabled).click();
|
||||
cancelBtn.shouldBe(Condition.disappear);
|
||||
}
|
||||
|
||||
protected boolean isConfirmationModalVisible() {
|
||||
return isVisible(confirmationMdl);
|
||||
}
|
||||
|
||||
public enum AlertHeader {
|
||||
SUCCESS("Success"),
|
||||
VALIDATION_ERROR("Validation Error"),
|
||||
BAD_REQUEST("400 Bad Request");
|
||||
|
||||
private final String value;
|
||||
|
||||
AlertHeader(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,40 @@
|
|||
package com.provectus.kafka.ui.pages.brokers;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$$x;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$$x;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
public class BrokersConfigTab extends BasePage {
|
||||
|
||||
protected List<SelenideElement> editBtn = $$x("//button[@aria-label='editAction']");
|
||||
protected SelenideElement searchByKeyField = $x("//input[@placeholder='Search by Key']");
|
||||
protected List<SelenideElement> editBtn = $$x("//button[@aria-label='editAction']");
|
||||
protected SelenideElement searchByKeyField = $x("//input[@placeholder='Search by Key']");
|
||||
|
||||
@Step
|
||||
public BrokersConfigTab waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
searchByKeyField.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public BrokersConfigTab waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
searchByKeyField.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isSearchByKeyVisible() {
|
||||
return isVisible(searchByKeyField);
|
||||
}
|
||||
@Step
|
||||
public boolean isSearchByKeyVisible() {
|
||||
return isVisible(searchByKeyField);
|
||||
}
|
||||
|
||||
public List<SelenideElement> getColumnHeaders() {
|
||||
return Stream.of("Key", "Value", "Source")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
public List<SelenideElement> getColumnHeaders() {
|
||||
return Stream.of("Key", "Value", "Source")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<SelenideElement> getEditButtons() {
|
||||
return editBtn;
|
||||
}
|
||||
public List<SelenideElement> getEditButtons() {
|
||||
return editBtn;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,92 +1,91 @@
|
|||
package com.provectus.kafka.ui.pages.brokers;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
public class BrokersDetails extends BasePage {
|
||||
|
||||
protected SelenideElement logDirectoriesTab = $x("//a[text()='Log directories']");
|
||||
protected SelenideElement metricsTab = $x("//a[text()='Metrics']");
|
||||
protected String brokersTabLocator = "//a[text()='%s']";
|
||||
protected SelenideElement logDirectoriesTab = $x("//a[text()='Log directories']");
|
||||
protected SelenideElement metricsTab = $x("//a[text()='Metrics']");
|
||||
protected String brokersTabLocator = "//a[text()='%s']";
|
||||
|
||||
@Step
|
||||
public BrokersDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
Arrays.asList(logDirectoriesTab, metricsTab).forEach(element -> element.shouldBe(Condition.visible));
|
||||
return this;
|
||||
@Step
|
||||
public BrokersDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
Arrays.asList(logDirectoriesTab, metricsTab).forEach(element -> element.shouldBe(Condition.visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public BrokersDetails openDetailsTab(DetailsTab menu) {
|
||||
$(By.linkText(menu.toString())).shouldBe(Condition.enabled).click();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<SelenideElement> getVisibleColumnHeaders() {
|
||||
return Stream.of("Name", "Topics", "Error", "Partitions")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getEnabledColumnHeaders() {
|
||||
return Stream.of("Name", "Error")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getVisibleSummaryCells() {
|
||||
return Stream.of("Segment Size", "Segment Count", "Port", "Host")
|
||||
.map(name -> $x(String.format(summaryCellLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getDetailsTabs() {
|
||||
return Stream.of(DetailsTab.values())
|
||||
.map(name -> $x(String.format(brokersTabLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllEnabledElements() {
|
||||
List<SelenideElement> enabledElements = new ArrayList<>(getEnabledColumnHeaders());
|
||||
enabledElements.addAll(getDetailsTabs());
|
||||
return enabledElements;
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllVisibleElements() {
|
||||
List<SelenideElement> visibleElements = new ArrayList<>(getVisibleSummaryCells());
|
||||
visibleElements.addAll(getVisibleColumnHeaders());
|
||||
visibleElements.addAll(getDetailsTabs());
|
||||
return visibleElements;
|
||||
}
|
||||
|
||||
public enum DetailsTab {
|
||||
LOG_DIRECTORIES("Log directories"),
|
||||
CONFIGS("Configs"),
|
||||
METRICS("Metrics");
|
||||
|
||||
private final String value;
|
||||
|
||||
DetailsTab(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Step
|
||||
public BrokersDetails openDetailsTab(DetailsTab menu) {
|
||||
$(By.linkText(menu.toString())).shouldBe(Condition.enabled).click();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<SelenideElement> getVisibleColumnHeaders() {
|
||||
return Stream.of("Name", "Topics", "Error", "Partitions")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getEnabledColumnHeaders() {
|
||||
return Stream.of("Name", "Error")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getVisibleSummaryCells() {
|
||||
return Stream.of("Segment Size", "Segment Count", "Port", "Host")
|
||||
.map(name -> $x(String.format(summaryCellLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getDetailsTabs() {
|
||||
return Stream.of(DetailsTab.values())
|
||||
.map(name -> $x(String.format(brokersTabLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllEnabledElements() {
|
||||
List<SelenideElement> enabledElements = new ArrayList<>(getEnabledColumnHeaders());
|
||||
enabledElements.addAll(getDetailsTabs());
|
||||
return enabledElements;
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllVisibleElements() {
|
||||
List<SelenideElement> visibleElements = new ArrayList<>(getVisibleSummaryCells());
|
||||
visibleElements.addAll(getVisibleColumnHeaders());
|
||||
visibleElements.addAll(getDetailsTabs());
|
||||
return visibleElements;
|
||||
}
|
||||
|
||||
public enum DetailsTab {
|
||||
LOG_DIRECTORIES("Log directories"),
|
||||
CONFIGS("Configs"),
|
||||
METRICS("Metrics");
|
||||
|
||||
private final String value;
|
||||
|
||||
DetailsTab(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,123 +1,122 @@
|
|||
package com.provectus.kafka.ui.pages.brokers;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.BROKERS;
|
||||
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.BROKERS;
|
||||
|
||||
public class BrokersList extends BasePage {
|
||||
|
||||
@Step
|
||||
public BrokersList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(BROKERS).shouldBe(Condition.visible);
|
||||
return this;
|
||||
@Step
|
||||
public BrokersList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(BROKERS).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public BrokersList openBroker(int brokerId) {
|
||||
getBrokerItem(brokerId).openItem();
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<SelenideElement> getUptimeSummaryCells() {
|
||||
return Stream.of("Broker Count", "Active Controller", "Version")
|
||||
.map(name -> $x(String.format(summaryCellLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getPartitionsSummaryCells() {
|
||||
return Stream.of("Online", "URP", "In Sync Replicas", "Out Of Sync Replicas")
|
||||
.map(name -> $x(String.format(summaryCellLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllVisibleElements() {
|
||||
List<SelenideElement> visibleElements = new ArrayList<>(getUptimeSummaryCells());
|
||||
visibleElements.addAll(getPartitionsSummaryCells());
|
||||
return visibleElements;
|
||||
}
|
||||
|
||||
private List<SelenideElement> getEnabledColumnHeaders() {
|
||||
return Stream.of("Broker ID", "Segment Size", "Segment Count", "Port", "Host")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllEnabledElements() {
|
||||
return getEnabledColumnHeaders();
|
||||
}
|
||||
|
||||
private List<BrokersList.BrokerGridItem> initGridItems() {
|
||||
List<BrokersList.BrokerGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new BrokersList.BrokerGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public BrokerGridItem getBrokerItem(int id) {
|
||||
return initGridItems().stream()
|
||||
.filter(e -> e.getId() == id)
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<BrokerGridItem> getAllBrokers() {
|
||||
return initGridItems();
|
||||
}
|
||||
|
||||
public static class BrokerGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public BrokerGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
private SelenideElement getIdElm() {
|
||||
return element.$x("./td[1]/div/a");
|
||||
}
|
||||
|
||||
@Step
|
||||
public BrokersList openBroker(int brokerId) {
|
||||
getBrokerItem(brokerId).openItem();
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<SelenideElement> getUptimeSummaryCells() {
|
||||
return Stream.of("Broker Count", "Active Controller", "Version")
|
||||
.map(name -> $x(String.format(summaryCellLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getPartitionsSummaryCells() {
|
||||
return Stream.of("Online", "URP", "In Sync Replicas", "Out Of Sync Replicas")
|
||||
.map(name -> $x(String.format(summaryCellLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
public int getId() {
|
||||
return Integer.parseInt(getIdElm().getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllVisibleElements() {
|
||||
List<SelenideElement> visibleElements = new ArrayList<>(getUptimeSummaryCells());
|
||||
visibleElements.addAll(getPartitionsSummaryCells());
|
||||
return visibleElements;
|
||||
}
|
||||
|
||||
private List<SelenideElement> getEnabledColumnHeaders() {
|
||||
return Stream.of("Broker ID", "Segment Size", "Segment Count", "Port", "Host")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
public void openItem() {
|
||||
getIdElm().click();
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllEnabledElements() {
|
||||
return getEnabledColumnHeaders();
|
||||
}
|
||||
|
||||
private List<BrokersList.BrokerGridItem> initGridItems() {
|
||||
List<BrokersList.BrokerGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new BrokersList.BrokerGridItem(item)));
|
||||
return gridItemList;
|
||||
public int getSegmentSize() {
|
||||
return Integer.parseInt(element.$x("./td[2]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public BrokerGridItem getBrokerItem(int id) {
|
||||
return initGridItems().stream()
|
||||
.filter(e -> e.getId() == id)
|
||||
.findFirst().orElseThrow();
|
||||
public int getSegmentCount() {
|
||||
return Integer.parseInt(element.$x("./td[3]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<BrokerGridItem> getAllBrokers() {
|
||||
return initGridItems();
|
||||
public int getPort() {
|
||||
return Integer.parseInt(element.$x("./td[4]").getText().trim());
|
||||
}
|
||||
|
||||
public static class BrokerGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public BrokerGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
private SelenideElement getIdElm() {
|
||||
return element.$x("./td[1]/div/a");
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getId() {
|
||||
return Integer.parseInt(getIdElm().getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public void openItem() {
|
||||
getIdElm().click();
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getSegmentSize() {
|
||||
return Integer.parseInt(element.$x("./td[2]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getSegmentCount() {
|
||||
return Integer.parseInt(element.$x("./td[3]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getPort() {
|
||||
return Integer.parseInt(element.$x("./td[4]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getHost() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
@Step
|
||||
public String getHost() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
package com.provectus.kafka.ui.pages.connectors;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
public class ConnectorCreateForm extends BasePage {
|
||||
|
||||
protected SelenideElement nameField = $x("//input[@name='name']");
|
||||
protected SelenideElement contentTextArea = $x("//textarea[@class='ace_text-input']");
|
||||
protected SelenideElement configField = $x("//div[@id='config']");
|
||||
protected SelenideElement nameField = $x("//input[@name='name']");
|
||||
protected SelenideElement contentTextArea = $x("//textarea[@class='ace_text-input']");
|
||||
protected SelenideElement configField = $x("//div[@id='config']");
|
||||
|
||||
@Step
|
||||
public ConnectorCreateForm waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
nameField.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorCreateForm waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
nameField.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorCreateForm setName(String connectName) {
|
||||
nameField.shouldBe(Condition.enabled).setValue(connectName);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorCreateForm setName(String connectName) {
|
||||
nameField.shouldBe(Condition.enabled).setValue(connectName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorCreateForm setConfig(String configJson) {
|
||||
configField.shouldBe(Condition.enabled).click();
|
||||
setJsonInputValue(contentTextArea, configJson);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorCreateForm setConfig(String configJson) {
|
||||
configField.shouldBe(Condition.enabled).click();
|
||||
setJsonInputValue(contentTextArea, configJson);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorCreateForm setConnectorDetails(String connectName, String configJson) {
|
||||
setName(connectName);
|
||||
setConfig(configJson);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorCreateForm setConnectorDetails(String connectName, String configJson) {
|
||||
setName(connectName);
|
||||
setConfig(configJson);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorCreateForm clickSubmitButton() {
|
||||
clickSubmitBtn();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorCreateForm clickSubmitButton() {
|
||||
clickSubmitBtn();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,84 +1,84 @@
|
|||
package com.provectus.kafka.ui.pages.connectors;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
public class ConnectorDetails extends BasePage {
|
||||
|
||||
protected SelenideElement deleteBtn = $x("//li/div[contains(text(),'Delete')]");
|
||||
protected SelenideElement confirmBtnMdl = $x("//div[@role='dialog']//button[contains(text(),'Confirm')]");
|
||||
protected SelenideElement contentTextArea = $x("//textarea[@class='ace_text-input']");
|
||||
protected SelenideElement taskTab = $x("//a[contains(text(),'Tasks')]");
|
||||
protected SelenideElement configTab = $x("//a[contains(text(),'Config')]");
|
||||
protected SelenideElement configField = $x("//div[@id='config']");
|
||||
protected String connectorHeaderLocator = "//h1[contains(text(),'%s')]";
|
||||
protected SelenideElement deleteBtn = $x("//li/div[contains(text(),'Delete')]");
|
||||
protected SelenideElement confirmBtnMdl = $x("//div[@role='dialog']//button[contains(text(),'Confirm')]");
|
||||
protected SelenideElement contentTextArea = $x("//textarea[@class='ace_text-input']");
|
||||
protected SelenideElement taskTab = $x("//a[contains(text(),'Tasks')]");
|
||||
protected SelenideElement configTab = $x("//a[contains(text(),'Config')]");
|
||||
protected SelenideElement configField = $x("//div[@id='config']");
|
||||
protected String connectorHeaderLocator = "//h1[contains(text(),'%s')]";
|
||||
|
||||
@Step
|
||||
public ConnectorDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
dotMenuBtn.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
dotMenuBtn.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorDetails openConfigTab() {
|
||||
clickByJavaScript(configTab);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorDetails openConfigTab() {
|
||||
clickByJavaScript(configTab);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorDetails setConfig(String configJson) {
|
||||
configField.shouldBe(Condition.enabled).click();
|
||||
clearByKeyboard(contentTextArea);
|
||||
contentTextArea.setValue(configJson);
|
||||
configField.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorDetails setConfig(String configJson) {
|
||||
configField.shouldBe(Condition.enabled).click();
|
||||
clearByKeyboard(contentTextArea);
|
||||
contentTextArea.setValue(configJson);
|
||||
configField.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorDetails clickSubmitButton() {
|
||||
clickSubmitBtn();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorDetails clickSubmitButton() {
|
||||
clickSubmitBtn();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorDetails openDotMenu() {
|
||||
clickByJavaScript(dotMenuBtn);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorDetails openDotMenu() {
|
||||
clickByJavaScript(dotMenuBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorDetails clickDeleteBtn() {
|
||||
clickByJavaScript(deleteBtn);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorDetails clickDeleteBtn() {
|
||||
clickByJavaScript(deleteBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorDetails clickConfirmBtn() {
|
||||
confirmBtnMdl.shouldBe(Condition.enabled).click();
|
||||
confirmBtnMdl.shouldBe(Condition.disappear);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorDetails clickConfirmBtn() {
|
||||
confirmBtnMdl.shouldBe(Condition.enabled).click();
|
||||
confirmBtnMdl.shouldBe(Condition.disappear);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ConnectorDetails deleteConnector() {
|
||||
openDotMenu();
|
||||
clickDeleteBtn();
|
||||
clickConfirmBtn();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConnectorDetails deleteConnector() {
|
||||
openDotMenu();
|
||||
clickDeleteBtn();
|
||||
clickConfirmBtn();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isConnectorHeaderVisible(String connectorName) {
|
||||
return isVisible($x(String.format(connectorHeaderLocator, connectorName)));
|
||||
}
|
||||
@Step
|
||||
public boolean isConnectorHeaderVisible(String connectorName) {
|
||||
return isVisible($x(String.format(connectorHeaderLocator, connectorName)));
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isAlertWithMessageVisible(AlertHeader header, String message) {
|
||||
return isAlertVisible(header, message);
|
||||
}
|
||||
@Step
|
||||
public boolean isAlertWithMessageVisible(AlertHeader header, String message) {
|
||||
return isAlertVisible(header, message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
package com.provectus.kafka.ui.pages.connectors;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.KAFKA_CONNECT;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.KAFKA_CONNECT;
|
||||
|
||||
|
||||
public class KafkaConnectList extends BasePage {
|
||||
|
||||
protected SelenideElement createConnectorBtn = $x("//button[contains(text(),'Create Connector')]");
|
||||
protected SelenideElement createConnectorBtn = $x("//button[contains(text(),'Create Connector')]");
|
||||
|
||||
public KafkaConnectList() {
|
||||
tableElementNameLocator = "//tbody//td[contains(text(),'%s')]";
|
||||
}
|
||||
public KafkaConnectList() {
|
||||
tableElementNameLocator = "//tbody//td[contains(text(),'%s')]";
|
||||
}
|
||||
|
||||
@Step
|
||||
public KafkaConnectList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(KAFKA_CONNECT).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public KafkaConnectList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(KAFKA_CONNECT).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KafkaConnectList clickCreateConnectorBtn() {
|
||||
clickByJavaScript(createConnectorBtn);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public KafkaConnectList clickCreateConnectorBtn() {
|
||||
clickByJavaScript(createConnectorBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KafkaConnectList openConnector(String connectorName) {
|
||||
getTableElement(connectorName).shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public KafkaConnectList openConnector(String connectorName) {
|
||||
getTableElement(connectorName).shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isConnectorVisible(String connectorName) {
|
||||
tableGrid.shouldBe(Condition.visible);
|
||||
return isVisible(getTableElement(connectorName));
|
||||
}
|
||||
@Step
|
||||
public boolean isConnectorVisible(String connectorName) {
|
||||
tableGrid.shouldBe(Condition.visible);
|
||||
return isVisible(getTableElement(connectorName));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
package com.provectus.kafka.ui.pages.consumers;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
public class ConsumersDetails extends BasePage {
|
||||
|
||||
protected String consumerIdHeaderLocator = "//h1[contains(text(),'%s')]";
|
||||
protected String topicElementLocator = "//tbody//td//a[text()='%s']";
|
||||
protected String consumerIdHeaderLocator = "//h1[contains(text(),'%s')]";
|
||||
protected String topicElementLocator = "//tbody//td//a[text()='%s']";
|
||||
|
||||
@Step
|
||||
public ConsumersDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
tableGrid.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConsumersDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
tableGrid.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isRedirectedConsumerTitleVisible(String consumerGroupId) {
|
||||
return isVisible($x(String.format(consumerIdHeaderLocator, consumerGroupId)));
|
||||
}
|
||||
@Step
|
||||
public boolean isRedirectedConsumerTitleVisible(String consumerGroupId) {
|
||||
return isVisible($x(String.format(consumerIdHeaderLocator, consumerGroupId)));
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isTopicInConsumersDetailsVisible(String topicName) {
|
||||
tableGrid.shouldBe(Condition.visible);
|
||||
return isVisible($x(String.format(topicElementLocator, topicName)));
|
||||
}
|
||||
@Step
|
||||
public boolean isTopicInConsumersDetailsVisible(String topicName) {
|
||||
tableGrid.shouldBe(Condition.visible);
|
||||
return isVisible($x(String.format(topicElementLocator, topicName)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package com.provectus.kafka.ui.pages.consumers;
|
||||
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.CONSUMERS;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.CONSUMERS;
|
||||
|
||||
public class ConsumersList extends BasePage {
|
||||
|
||||
@Step
|
||||
public ConsumersList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(CONSUMERS).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ConsumersList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(CONSUMERS).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
package com.provectus.kafka.ui.pages.ksqlDb;
|
||||
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import com.provectus.kafka.ui.pages.ksqlDb.enums.KsqlMenuTabs;
|
||||
import io.qameta.allure.Step;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.KSQL_DB;
|
||||
|
||||
public class KsqlDbList extends BasePage {
|
||||
|
||||
protected SelenideElement executeKsqlBtn = $x("//button[text()='Execute KSQL Request']");
|
||||
protected SelenideElement tablesTab = $x("//nav[@role='navigation']/a[text()='Tables']");
|
||||
protected SelenideElement streamsTab = $x("//nav[@role='navigation']/a[text()='Streams']");
|
||||
|
||||
@Step
|
||||
public KsqlDbList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(KSQL_DB).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlDbList clickExecuteKsqlRequestBtn() {
|
||||
clickByJavaScript(executeKsqlBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlDbList openDetailsTab(KsqlMenuTabs menu) {
|
||||
$(By.linkText(menu.toString())).shouldBe(Condition.visible).click();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<KsqlDbList.KsqlTablesGridItem> initTablesItems() {
|
||||
List<KsqlDbList.KsqlTablesGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new KsqlDbList.KsqlTablesGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlDbList.KsqlTablesGridItem getTableByName(String tableName) {
|
||||
return initTablesItems().stream()
|
||||
.filter(e -> e.getTableName().equals(tableName))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
private List<KsqlDbList.KsqlStreamsGridItem> initStreamsItems() {
|
||||
List<KsqlDbList.KsqlStreamsGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new KsqlDbList.KsqlStreamsGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlDbList.KsqlStreamsGridItem getStreamByName(String streamName) {
|
||||
return initStreamsItems().stream()
|
||||
.filter(e -> e.getStreamName().equals(streamName))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
public static class KsqlTablesGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public KsqlTablesGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTableName() {
|
||||
return element.$x("./td[1]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTopicName() {
|
||||
return element.$x("./td[2]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getKeyFormat() {
|
||||
return element.$x("./td[3]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValueFormat() {
|
||||
return element.$x("./td[4]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getIsWindowed() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
}
|
||||
|
||||
public static class KsqlStreamsGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public KsqlStreamsGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getStreamName() {
|
||||
return element.$x("./td[1]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTopicName() {
|
||||
return element.$x("./td[2]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getKeyFormat() {
|
||||
return element.$x("./td[3]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValueFormat() {
|
||||
return element.$x("./td[4]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getIsWindowed() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
package com.provectus.kafka.ui.pages.ksqlDb;
|
||||
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.ElementsCollection;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.codeborne.selenide.Condition.visible;
|
||||
import static com.codeborne.selenide.Selenide.$$x;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
public class KsqlQueryForm extends BasePage {
|
||||
protected SelenideElement clearBtn = $x("//div/button[text()='Clear']");
|
||||
protected SelenideElement executeBtn = $x("//div/button[text()='Execute']");
|
||||
protected SelenideElement stopQueryBtn = $x("//div/button[text()='Stop query']");
|
||||
protected SelenideElement clearResultsBtn = $x("//div/button[text()='Clear results']");
|
||||
protected SelenideElement addStreamPropertyBtn = $x("//button[text()='Add Stream Property']");
|
||||
protected SelenideElement queryAreaValue = $x("//div[@class='ace_content']");
|
||||
protected SelenideElement queryArea = $x("//div[@id='ksql']/textarea[@class='ace_text-input']");
|
||||
protected ElementsCollection ksqlGridItems = $$x("//tbody//tr");
|
||||
protected ElementsCollection keyField = $$x("//input[@aria-label='key']");
|
||||
protected ElementsCollection valueField = $$x("//input[@aria-label='value']");
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
executeBtn.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickClearBtn() {
|
||||
clickByJavaScript(clearBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickExecuteBtn() {
|
||||
clickByActions(executeBtn);
|
||||
if (queryAreaValue.getText().contains("EMIT CHANGES;")) {
|
||||
loadingSpinner.shouldBe(Condition.visible);
|
||||
} else {
|
||||
waitUntilSpinnerDisappear();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickStopQueryBtn() {
|
||||
clickByActions(stopQueryBtn);
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickClearResultsBtn() {
|
||||
clickByActions(clearResultsBtn);
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickAddStreamProperty() {
|
||||
clickByJavaScript(addStreamPropertyBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm setQuery(String query) {
|
||||
queryAreaValue.shouldBe(Condition.visible).click();
|
||||
queryArea.setValue(query);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm.KsqlResponseGridItem getTableByName(String name) {
|
||||
return initItems().stream()
|
||||
.filter(e -> e.getName().equalsIgnoreCase(name))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean areResultsVisible() {
|
||||
boolean visible = false;
|
||||
try {
|
||||
visible = initItems().size() > 0;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
private List<KsqlQueryForm.KsqlResponseGridItem> initItems() {
|
||||
List<KsqlQueryForm.KsqlResponseGridItem> gridItemList = new ArrayList<>();
|
||||
ksqlGridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new KsqlQueryForm.KsqlResponseGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
public static class KsqlResponseGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
private KsqlResponseGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getType() {
|
||||
return element.$x("./td[1]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getName() {
|
||||
return element.$x("./td[2]").scrollTo().getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isVisible() {
|
||||
boolean isVisible = false;
|
||||
try {
|
||||
element.$x("./td[2]").shouldBe(visible, Duration.ofMillis(500));
|
||||
isVisible = true;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTopic() {
|
||||
return element.$x("./td[3]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getKeyFormat() {
|
||||
return element.$x("./td[4]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValueFormat() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getIsWindowed() {
|
||||
return element.$x("./td[6]").getText().trim();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package com.provectus.kafka.ui.pages.ksqlDb.enums;
|
||||
|
||||
public enum KsqlMenuTabs {
|
||||
|
||||
TABLES("Table"),
|
||||
STREAMS("Streams");
|
||||
|
||||
private final String value;
|
||||
|
||||
KsqlMenuTabs(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package com.provectus.kafka.ui.pages.ksqlDb.enums;
|
||||
|
||||
public enum KsqlQueryConfig {
|
||||
|
||||
SHOW_TABLES("show tables;"),
|
||||
SHOW_STREAMS("show streams;"),
|
||||
SELECT_ALL_FROM("SELECT * FROM %s\n" +
|
||||
"EMIT CHANGES;");
|
||||
|
||||
private final String query;
|
||||
|
||||
KsqlQueryConfig(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package com.provectus.kafka.ui.pages.ksqldb;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.KSQL_DB;
|
||||
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import com.provectus.kafka.ui.pages.ksqldb.enums.KsqlMenuTabs;
|
||||
import io.qameta.allure.Step;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
public class KsqlDbList extends BasePage {
|
||||
|
||||
protected SelenideElement executeKsqlBtn = $x("//button[text()='Execute KSQL Request']");
|
||||
protected SelenideElement tablesTab = $x("//nav[@role='navigation']/a[text()='Tables']");
|
||||
protected SelenideElement streamsTab = $x("//nav[@role='navigation']/a[text()='Streams']");
|
||||
|
||||
@Step
|
||||
public KsqlDbList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(KSQL_DB).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlDbList clickExecuteKsqlRequestBtn() {
|
||||
clickByJavaScript(executeKsqlBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlDbList openDetailsTab(KsqlMenuTabs menu) {
|
||||
$(By.linkText(menu.toString())).shouldBe(Condition.visible).click();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<KsqlDbList.KsqlTablesGridItem> initTablesItems() {
|
||||
List<KsqlDbList.KsqlTablesGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new KsqlDbList.KsqlTablesGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlDbList.KsqlTablesGridItem getTableByName(String tableName) {
|
||||
return initTablesItems().stream()
|
||||
.filter(e -> e.getTableName().equals(tableName))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
private List<KsqlDbList.KsqlStreamsGridItem> initStreamsItems() {
|
||||
List<KsqlDbList.KsqlStreamsGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new KsqlDbList.KsqlStreamsGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlDbList.KsqlStreamsGridItem getStreamByName(String streamName) {
|
||||
return initStreamsItems().stream()
|
||||
.filter(e -> e.getStreamName().equals(streamName))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
public static class KsqlTablesGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public KsqlTablesGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTableName() {
|
||||
return element.$x("./td[1]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTopicName() {
|
||||
return element.$x("./td[2]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getKeyFormat() {
|
||||
return element.$x("./td[3]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValueFormat() {
|
||||
return element.$x("./td[4]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getIsWindowed() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
}
|
||||
|
||||
public static class KsqlStreamsGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public KsqlStreamsGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getStreamName() {
|
||||
return element.$x("./td[1]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTopicName() {
|
||||
return element.$x("./td[2]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getKeyFormat() {
|
||||
return element.$x("./td[3]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValueFormat() {
|
||||
return element.$x("./td[4]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getIsWindowed() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package com.provectus.kafka.ui.pages.ksqldb;
|
||||
|
||||
import static com.codeborne.selenide.Condition.visible;
|
||||
import static com.codeborne.selenide.Selenide.$$x;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.ElementsCollection;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class KsqlQueryForm extends BasePage {
|
||||
protected SelenideElement clearBtn = $x("//div/button[text()='Clear']");
|
||||
protected SelenideElement executeBtn = $x("//div/button[text()='Execute']");
|
||||
protected SelenideElement stopQueryBtn = $x("//div/button[text()='Stop query']");
|
||||
protected SelenideElement clearResultsBtn = $x("//div/button[text()='Clear results']");
|
||||
protected SelenideElement addStreamPropertyBtn = $x("//button[text()='Add Stream Property']");
|
||||
protected SelenideElement queryAreaValue = $x("//div[@class='ace_content']");
|
||||
protected SelenideElement queryArea = $x("//div[@id='ksql']/textarea[@class='ace_text-input']");
|
||||
protected ElementsCollection ksqlGridItems = $$x("//tbody//tr");
|
||||
protected ElementsCollection keyField = $$x("//input[@aria-label='key']");
|
||||
protected ElementsCollection valueField = $$x("//input[@aria-label='value']");
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
executeBtn.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickClearBtn() {
|
||||
clickByJavaScript(clearBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickExecuteBtn() {
|
||||
clickByActions(executeBtn);
|
||||
if (queryAreaValue.getText().contains("EMIT CHANGES;")) {
|
||||
loadingSpinner.shouldBe(Condition.visible);
|
||||
} else {
|
||||
waitUntilSpinnerDisappear();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickStopQueryBtn() {
|
||||
clickByActions(stopQueryBtn);
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickClearResultsBtn() {
|
||||
clickByActions(clearResultsBtn);
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm clickAddStreamProperty() {
|
||||
clickByJavaScript(addStreamPropertyBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm setQuery(String query) {
|
||||
queryAreaValue.shouldBe(Condition.visible).click();
|
||||
queryArea.setValue(query);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public KsqlQueryForm.KsqlResponseGridItem getTableByName(String name) {
|
||||
return initItems().stream()
|
||||
.filter(e -> e.getName().equalsIgnoreCase(name))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean areResultsVisible() {
|
||||
boolean visible = false;
|
||||
try {
|
||||
visible = initItems().size() > 0;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
private List<KsqlQueryForm.KsqlResponseGridItem> initItems() {
|
||||
List<KsqlQueryForm.KsqlResponseGridItem> gridItemList = new ArrayList<>();
|
||||
ksqlGridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new KsqlQueryForm.KsqlResponseGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
public static class KsqlResponseGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
private KsqlResponseGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getType() {
|
||||
return element.$x("./td[1]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getName() {
|
||||
return element.$x("./td[2]").scrollTo().getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isVisible() {
|
||||
boolean isVisible = false;
|
||||
try {
|
||||
element.$x("./td[2]").shouldBe(visible, Duration.ofMillis(500));
|
||||
isVisible = true;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTopic() {
|
||||
return element.$x("./td[3]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getKeyFormat() {
|
||||
return element.$x("./td[4]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValueFormat() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getIsWindowed() {
|
||||
return element.$x("./td[6]").getText().trim();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.provectus.kafka.ui.pages.ksqldb.enums;
|
||||
|
||||
public enum KsqlMenuTabs {
|
||||
|
||||
TABLES("Table"),
|
||||
STREAMS("Streams");
|
||||
|
||||
private final String value;
|
||||
|
||||
KsqlMenuTabs(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.provectus.kafka.ui.pages.ksqldb.enums;
|
||||
|
||||
public enum KsqlQueryConfig {
|
||||
|
||||
SHOW_TABLES("show tables;"),
|
||||
SHOW_STREAMS("show streams;"),
|
||||
SELECT_ALL_FROM("SELECT * FROM %s\n" + "EMIT CHANGES;");
|
||||
|
||||
private final String query;
|
||||
|
||||
KsqlQueryConfig(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.provectus.kafka.ui.pages.ksqlDb.models;
|
||||
package com.provectus.kafka.ui.pages.ksqldb.models;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
@ -7,5 +7,5 @@ import lombok.experimental.Accessors;
|
|||
@Accessors(chain = true)
|
||||
public class Stream {
|
||||
|
||||
private String name, topicName, valueFormat, partitions;
|
||||
private String name, topicName, valueFormat, partitions;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.provectus.kafka.ui.pages.ksqlDb.models;
|
||||
package com.provectus.kafka.ui.pages.ksqldb.models;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
@ -7,5 +7,5 @@ import lombok.experimental.Accessors;
|
|||
@Accessors(chain = true)
|
||||
public class Table {
|
||||
|
||||
private String name, streamName;
|
||||
private String name, streamName;
|
||||
}
|
|
@ -1,64 +1,63 @@
|
|||
package com.provectus.kafka.ui.pages.panels;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.settings.BaseSource.CLUSTER_NAME;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.panels.enums.MenuItem;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import com.provectus.kafka.ui.pages.panels.enums.MenuItem;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.settings.BaseSource.CLUSTER_NAME;
|
||||
|
||||
public class NaviSideBar extends BasePage {
|
||||
|
||||
protected SelenideElement dashboardMenuItem = $x("//a[@title='Dashboard']");
|
||||
protected String sideMenuOptionElementLocator = ".//ul/li[contains(.,'%s')]";
|
||||
protected String clusterElementLocator = "//aside/ul/li[contains(.,'%s')]";
|
||||
protected SelenideElement dashboardMenuItem = $x("//a[@title='Dashboard']");
|
||||
protected String sideMenuOptionElementLocator = ".//ul/li[contains(.,'%s')]";
|
||||
protected String clusterElementLocator = "//aside/ul/li[contains(.,'%s')]";
|
||||
|
||||
private SelenideElement expandCluster(String clusterName) {
|
||||
SelenideElement clusterElement = $x(String.format(clusterElementLocator, clusterName)).shouldBe(Condition.visible);
|
||||
if (clusterElement.parent().$$x(".//ul").size() == 0) {
|
||||
clickByActions(clusterElement);
|
||||
}
|
||||
return clusterElement;
|
||||
private SelenideElement expandCluster(String clusterName) {
|
||||
SelenideElement clusterElement = $x(String.format(clusterElementLocator, clusterName)).shouldBe(Condition.visible);
|
||||
if (clusterElement.parent().$$x(".//ul").size() == 0) {
|
||||
clickByActions(clusterElement);
|
||||
}
|
||||
return clusterElement;
|
||||
}
|
||||
|
||||
@Step
|
||||
public NaviSideBar waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
dashboardMenuItem.shouldBe(Condition.visible, Duration.ofSeconds(30));
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public NaviSideBar waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
dashboardMenuItem.shouldBe(Condition.visible, Duration.ofSeconds(30));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getPagePath(MenuItem menuItem) {
|
||||
return getPagePathFromHeader(menuItem)
|
||||
.shouldBe(Condition.visible)
|
||||
.getText().trim();
|
||||
}
|
||||
@Step
|
||||
public String getPagePath(MenuItem menuItem) {
|
||||
return getPagePathFromHeader(menuItem)
|
||||
.shouldBe(Condition.visible)
|
||||
.getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public NaviSideBar openSideMenu(String clusterName, MenuItem menuItem) {
|
||||
clickByActions(expandCluster(clusterName).parent()
|
||||
.$x(String.format(sideMenuOptionElementLocator, menuItem.getNaviTitle())));
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public NaviSideBar openSideMenu(String clusterName, MenuItem menuItem) {
|
||||
clickByActions(expandCluster(clusterName).parent()
|
||||
.$x(String.format(sideMenuOptionElementLocator, menuItem.getNaviTitle())));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public NaviSideBar openSideMenu(MenuItem menuItem) {
|
||||
openSideMenu(CLUSTER_NAME, menuItem);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public NaviSideBar openSideMenu(MenuItem menuItem) {
|
||||
openSideMenu(CLUSTER_NAME, menuItem);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<SelenideElement> getAllMenuButtons() {
|
||||
expandCluster(CLUSTER_NAME);
|
||||
return Stream.of(MenuItem.values())
|
||||
.map(menuItem -> $x(String.format(sideMenuOptionElementLocator, menuItem.getNaviTitle())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
public List<SelenideElement> getAllMenuButtons() {
|
||||
expandCluster(CLUSTER_NAME);
|
||||
return Stream.of(MenuItem.values())
|
||||
.map(menuItem -> $x(String.format(sideMenuOptionElementLocator, menuItem.getNaviTitle())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
package com.provectus.kafka.ui.pages.panels;
|
||||
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class TopPanel extends BasePage {
|
||||
|
||||
protected SelenideElement kafkaLogo = $x("//a[contains(text(),'UI for Apache Kafka')]");
|
||||
protected SelenideElement kafkaVersion = $x("//a[@title='Current commit']");
|
||||
protected SelenideElement logOutBtn = $x("//button[contains(text(),'Log out')]");
|
||||
protected SelenideElement gitBtn = $x("//a[@href='https://github.com/provectus/kafka-ui']");
|
||||
protected SelenideElement discordBtn = $x("//a[contains(@href,'https://discord.com/invite')]");
|
||||
protected SelenideElement kafkaLogo = $x("//a[contains(text(),'UI for Apache Kafka')]");
|
||||
protected SelenideElement kafkaVersion = $x("//a[@title='Current commit']");
|
||||
protected SelenideElement logOutBtn = $x("//button[contains(text(),'Log out')]");
|
||||
protected SelenideElement gitBtn = $x("//a[@href='https://github.com/provectus/kafka-ui']");
|
||||
protected SelenideElement discordBtn = $x("//a[contains(@href,'https://discord.com/invite')]");
|
||||
|
||||
public List<SelenideElement> getAllVisibleElements() {
|
||||
return Arrays.asList(kafkaLogo, kafkaVersion, gitBtn, discordBtn);
|
||||
}
|
||||
public List<SelenideElement> getAllVisibleElements() {
|
||||
return Arrays.asList(kafkaLogo, kafkaVersion, gitBtn, discordBtn);
|
||||
}
|
||||
|
||||
public List<SelenideElement> getAllEnabledElements() {
|
||||
return Arrays.asList(gitBtn, discordBtn, kafkaLogo);
|
||||
}
|
||||
public List<SelenideElement> getAllEnabledElements() {
|
||||
return Arrays.asList(gitBtn, discordBtn, kafkaLogo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
package com.provectus.kafka.ui.pages.panels.enums;
|
||||
|
||||
public enum MenuItem {
|
||||
|
||||
DASHBOARD("Dashboard", "Dashboard"),
|
||||
BROKERS("Brokers", "Brokers"),
|
||||
TOPICS("Topics", "Topics"),
|
||||
CONSUMERS("Consumers", "Consumers"),
|
||||
SCHEMA_REGISTRY("Schema Registry", "Schema Registry"),
|
||||
KAFKA_CONNECT("Kafka Connect", "Connectors"),
|
||||
KSQL_DB("KSQL DB", "KSQL DB");
|
||||
|
||||
private final String naviTitle;
|
||||
private final String pageTitle;
|
||||
|
||||
MenuItem(String naviTitle, String pageTitle) {
|
||||
this.naviTitle = naviTitle;
|
||||
this.pageTitle = pageTitle;
|
||||
}
|
||||
|
||||
public String getNaviTitle() {
|
||||
return naviTitle;
|
||||
}
|
||||
|
||||
public String getPageTitle() {
|
||||
return pageTitle;
|
||||
}
|
||||
|
||||
DASHBOARD("Dashboard", "Dashboard"),
|
||||
BROKERS("Brokers", "Brokers"),
|
||||
TOPICS("Topics", "Topics"),
|
||||
CONSUMERS("Consumers", "Consumers"),
|
||||
SCHEMA_REGISTRY("Schema Registry", "Schema Registry"),
|
||||
KAFKA_CONNECT("Kafka Connect", "Connectors"),
|
||||
KSQL_DB("KSQL DB", "KSQL DB");
|
||||
|
||||
private final String naviTitle;
|
||||
private final String pageTitle;
|
||||
|
||||
MenuItem(String naviTitle, String pageTitle) {
|
||||
this.naviTitle = naviTitle;
|
||||
this.pageTitle = pageTitle;
|
||||
}
|
||||
|
||||
public String getNaviTitle() {
|
||||
return naviTitle;
|
||||
}
|
||||
|
||||
public String getPageTitle() {
|
||||
return pageTitle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
package com.provectus.kafka.ui.pages.schemas;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$;
|
||||
import static com.codeborne.selenide.Selenide.$$x;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static org.openqa.selenium.By.id;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.codeborne.selenide.WebDriverRunner;
|
||||
|
@ -7,133 +12,130 @@ import com.provectus.kafka.ui.api.model.CompatibilityLevel;
|
|||
import com.provectus.kafka.ui.api.model.SchemaType;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
import org.openqa.selenium.Keys;
|
||||
import org.openqa.selenium.interactions.Actions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.*;
|
||||
import static org.openqa.selenium.By.id;
|
||||
import org.openqa.selenium.Keys;
|
||||
import org.openqa.selenium.interactions.Actions;
|
||||
|
||||
public class SchemaCreateForm extends BasePage {
|
||||
|
||||
protected SelenideElement schemaNameField = $x("//input[@name='subject']");
|
||||
protected SelenideElement pageTitle = $x("//h1['Edit']");
|
||||
protected SelenideElement schemaTextArea = $x("//textarea[@name='schema']");
|
||||
protected SelenideElement newSchemaInput = $("#newSchema [wrap]");
|
||||
protected SelenideElement schemaTypeDdl = $x("//ul[@name='schemaType']");
|
||||
protected SelenideElement compatibilityLevelList = $x("//ul[@name='compatibilityLevel']");
|
||||
protected SelenideElement newSchemaTextArea = $x("//div[@id='newSchema']");
|
||||
protected SelenideElement latestSchemaTextArea = $x("//div[@id='latestSchema']");
|
||||
protected SelenideElement leftVersionDdl = $(id("left-select"));
|
||||
protected SelenideElement rightVersionDdl = $(id("right-select"));
|
||||
protected List<SelenideElement> visibleMarkers = $$x("//div[@class='ace_scroller']//div[contains(@class,'codeMarker')]");
|
||||
protected List<SelenideElement> elementsCompareVersionDdl = $$x("//ul[@role='listbox']/ul/li");
|
||||
protected String ddlElementLocator = "//li[@value='%s']";
|
||||
protected SelenideElement schemaNameField = $x("//input[@name='subject']");
|
||||
protected SelenideElement pageTitle = $x("//h1['Edit']");
|
||||
protected SelenideElement schemaTextArea = $x("//textarea[@name='schema']");
|
||||
protected SelenideElement newSchemaInput = $("#newSchema [wrap]");
|
||||
protected SelenideElement schemaTypeDdl = $x("//ul[@name='schemaType']");
|
||||
protected SelenideElement compatibilityLevelList = $x("//ul[@name='compatibilityLevel']");
|
||||
protected SelenideElement newSchemaTextArea = $x("//div[@id='newSchema']");
|
||||
protected SelenideElement latestSchemaTextArea = $x("//div[@id='latestSchema']");
|
||||
protected SelenideElement leftVersionDdl = $(id("left-select"));
|
||||
protected SelenideElement rightVersionDdl = $(id("right-select"));
|
||||
protected List<SelenideElement> visibleMarkers =
|
||||
$$x("//div[@class='ace_scroller']//div[contains(@class,'codeMarker')]");
|
||||
protected List<SelenideElement> elementsCompareVersionDdl = $$x("//ul[@role='listbox']/ul/li");
|
||||
protected String ddlElementLocator = "//li[@value='%s']";
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
pageTitle.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
pageTitle.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm setSubjectName(String name) {
|
||||
schemaNameField.setValue(name);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm setSubjectName(String name) {
|
||||
schemaNameField.setValue(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm setSchemaField(String text) {
|
||||
schemaTextArea.setValue(text);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm setSchemaField(String text) {
|
||||
schemaTextArea.setValue(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm selectSchemaTypeFromDropdown(SchemaType schemaType) {
|
||||
schemaTypeDdl.shouldBe(Condition.enabled).click();
|
||||
$x(String.format(ddlElementLocator, schemaType.getValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm selectSchemaTypeFromDropdown(SchemaType schemaType) {
|
||||
schemaTypeDdl.shouldBe(Condition.enabled).click();
|
||||
$x(String.format(ddlElementLocator, schemaType.getValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm clickSubmitButton() {
|
||||
clickSubmitBtn();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm clickSubmitButton() {
|
||||
clickSubmitBtn();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm selectCompatibilityLevelFromDropdown(CompatibilityLevel.CompatibilityEnum level) {
|
||||
compatibilityLevelList.shouldBe(Condition.enabled).click();
|
||||
$x(String.format(ddlElementLocator, level.getValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm selectCompatibilityLevelFromDropdown(CompatibilityLevel.CompatibilityEnum level) {
|
||||
compatibilityLevelList.shouldBe(Condition.enabled).click();
|
||||
$x(String.format(ddlElementLocator, level.getValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm openLeftVersionDdl() {
|
||||
leftVersionDdl.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm openLeftVersionDdl() {
|
||||
leftVersionDdl.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm openRightVersionDdl() {
|
||||
rightVersionDdl.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm openRightVersionDdl() {
|
||||
rightVersionDdl.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getVersionsNumberFromList() {
|
||||
return elementsCompareVersionDdl.size();
|
||||
}
|
||||
@Step
|
||||
public int getVersionsNumberFromList() {
|
||||
return elementsCompareVersionDdl.size();
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm selectVersionFromDropDown(int versionNumberDd) {
|
||||
$x(String.format(ddlElementLocator, versionNumberDd)).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm selectVersionFromDropDown(int versionNumberDd) {
|
||||
$x(String.format(ddlElementLocator, versionNumberDd)).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getMarkedLinesNumber() {
|
||||
return visibleMarkers.size();
|
||||
}
|
||||
@Step
|
||||
public int getMarkedLinesNumber() {
|
||||
return visibleMarkers.size();
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaCreateForm setNewSchemaValue(String configJson) {
|
||||
newSchemaTextArea.shouldBe(Condition.visible).click();
|
||||
newSchemaInput.shouldBe(Condition.enabled);
|
||||
new Actions(WebDriverRunner.getWebDriver())
|
||||
.sendKeys(Keys.PAGE_UP)
|
||||
.keyDown(Keys.SHIFT)
|
||||
.sendKeys(Keys.PAGE_DOWN)
|
||||
.keyUp(Keys.SHIFT)
|
||||
.sendKeys(Keys.DELETE)
|
||||
.perform();
|
||||
setJsonInputValue(newSchemaInput, configJson);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaCreateForm setNewSchemaValue(String configJson) {
|
||||
newSchemaTextArea.shouldBe(Condition.visible).click();
|
||||
newSchemaInput.shouldBe(Condition.enabled);
|
||||
new Actions(WebDriverRunner.getWebDriver())
|
||||
.sendKeys(Keys.PAGE_UP)
|
||||
.keyDown(Keys.SHIFT)
|
||||
.sendKeys(Keys.PAGE_DOWN)
|
||||
.keyUp(Keys.SHIFT)
|
||||
.sendKeys(Keys.DELETE)
|
||||
.perform();
|
||||
setJsonInputValue(newSchemaInput, configJson);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllDetailsPageElements() {
|
||||
return Stream.of(compatibilityLevelList, newSchemaTextArea, latestSchemaTextArea, submitBtn, schemaTypeDdl)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@Step
|
||||
public List<SelenideElement> getAllDetailsPageElements() {
|
||||
return Stream.of(compatibilityLevelList, newSchemaTextArea, latestSchemaTextArea, submitBtn, schemaTypeDdl)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isSubmitBtnEnabled() {
|
||||
return isEnabled(submitBtn);
|
||||
}
|
||||
@Step
|
||||
public boolean isSubmitBtnEnabled() {
|
||||
return isEnabled(submitBtn);
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isSchemaDropDownEnabled() {
|
||||
boolean enabled = true;
|
||||
try {
|
||||
String attribute = schemaTypeDdl.getAttribute("disabled");
|
||||
enabled = false;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return enabled;
|
||||
@Step
|
||||
public boolean isSchemaDropDownEnabled() {
|
||||
boolean enabled = true;
|
||||
try {
|
||||
String attribute = schemaTypeDdl.getAttribute("disabled");
|
||||
enabled = false;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,69 +1,69 @@
|
|||
package com.provectus.kafka.ui.pages.schemas;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
public class SchemaDetails extends BasePage {
|
||||
|
||||
protected SelenideElement actualVersionTextArea = $x("//div[@id='schema']");
|
||||
protected SelenideElement compatibilityField = $x("//h4[contains(text(),'Compatibility')]/../p");
|
||||
protected SelenideElement editSchemaBtn = $x("//button[contains(text(),'Edit Schema')]");
|
||||
protected SelenideElement removeBtn = $x("//*[contains(text(),'Remove')]");
|
||||
protected SelenideElement confirmBtn = $x("//div[@role='dialog']//button[contains(text(),'Confirm')]");
|
||||
protected SelenideElement schemaTypeField = $x("//h4[contains(text(),'Type')]/../p");
|
||||
protected SelenideElement latestVersionField = $x("//h4[contains(text(),'Latest version')]/../p");
|
||||
protected SelenideElement compareVersionBtn = $x("//button[text()='Compare Versions']");
|
||||
protected String schemaHeaderLocator = "//h1[contains(text(),'%s')]";
|
||||
protected SelenideElement actualVersionTextArea = $x("//div[@id='schema']");
|
||||
protected SelenideElement compatibilityField = $x("//h4[contains(text(),'Compatibility')]/../p");
|
||||
protected SelenideElement editSchemaBtn = $x("//button[contains(text(),'Edit Schema')]");
|
||||
protected SelenideElement removeBtn = $x("//*[contains(text(),'Remove')]");
|
||||
protected SelenideElement confirmBtn = $x("//div[@role='dialog']//button[contains(text(),'Confirm')]");
|
||||
protected SelenideElement schemaTypeField = $x("//h4[contains(text(),'Type')]/../p");
|
||||
protected SelenideElement latestVersionField = $x("//h4[contains(text(),'Latest version')]/../p");
|
||||
protected SelenideElement compareVersionBtn = $x("//button[text()='Compare Versions']");
|
||||
protected String schemaHeaderLocator = "//h1[contains(text(),'%s')]";
|
||||
|
||||
@Step
|
||||
public SchemaDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
actualVersionTextArea.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
actualVersionTextArea.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getCompatibility() {
|
||||
return compatibilityField.getText();
|
||||
}
|
||||
@Step
|
||||
public String getCompatibility() {
|
||||
return compatibilityField.getText();
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isSchemaHeaderVisible(String schemaName) {
|
||||
return isVisible($x(String.format(schemaHeaderLocator, schemaName)));
|
||||
}
|
||||
@Step
|
||||
public boolean isSchemaHeaderVisible(String schemaName) {
|
||||
return isVisible($x(String.format(schemaHeaderLocator, schemaName)));
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getLatestVersion() {
|
||||
return Integer.parseInt(latestVersionField.getText());
|
||||
}
|
||||
@Step
|
||||
public int getLatestVersion() {
|
||||
return Integer.parseInt(latestVersionField.getText());
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getSchemaType() {
|
||||
return schemaTypeField.getText();
|
||||
}
|
||||
@Step
|
||||
public String getSchemaType() {
|
||||
return schemaTypeField.getText();
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaDetails openEditSchema() {
|
||||
editSchemaBtn.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaDetails openEditSchema() {
|
||||
editSchemaBtn.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaDetails openCompareVersionMenu() {
|
||||
compareVersionBtn.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaDetails openCompareVersionMenu() {
|
||||
compareVersionBtn.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaDetails removeSchema() {
|
||||
clickByJavaScript(dotMenuBtn);
|
||||
removeBtn.shouldBe(Condition.enabled).click();
|
||||
confirmBtn.shouldBe(Condition.visible).click();
|
||||
confirmBtn.shouldBe(Condition.disappear);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaDetails removeSchema() {
|
||||
clickByJavaScript(dotMenuBtn);
|
||||
removeBtn.shouldBe(Condition.enabled).click();
|
||||
confirmBtn.shouldBe(Condition.visible).click();
|
||||
confirmBtn.shouldBe(Condition.disappear);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
package com.provectus.kafka.ui.pages.schemas;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.SCHEMA_REGISTRY;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.SCHEMA_REGISTRY;
|
||||
|
||||
public class SchemaRegistryList extends BasePage {
|
||||
|
||||
protected SelenideElement createSchemaBtn = $x("//button[contains(text(),'Create Schema')]");
|
||||
protected SelenideElement createSchemaBtn = $x("//button[contains(text(),'Create Schema')]");
|
||||
|
||||
@Step
|
||||
public SchemaRegistryList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(SCHEMA_REGISTRY).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaRegistryList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(SCHEMA_REGISTRY).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaRegistryList clickCreateSchema() {
|
||||
clickByJavaScript(createSchemaBtn);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaRegistryList clickCreateSchema() {
|
||||
clickByJavaScript(createSchemaBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public SchemaRegistryList openSchema(String schemaName) {
|
||||
getTableElement(schemaName)
|
||||
.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public SchemaRegistryList openSchema(String schemaName) {
|
||||
getTableElement(schemaName)
|
||||
.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isSchemaVisible(String schemaName) {
|
||||
tableGrid.shouldBe(Condition.visible);
|
||||
return isVisible(getTableElement(schemaName));
|
||||
}
|
||||
@Step
|
||||
public boolean isSchemaVisible(String schemaName) {
|
||||
tableGrid.shouldBe(Condition.visible);
|
||||
return isVisible(getTableElement(schemaName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,57 +1,56 @@
|
|||
package com.provectus.kafka.ui.pages.topics;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.codeborne.selenide.Selenide.refresh;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.codeborne.selenide.Selenide.refresh;
|
||||
|
||||
public class ProduceMessagePanel extends BasePage {
|
||||
|
||||
protected SelenideElement keyTextArea = $x("//div[@id='key']/textarea");
|
||||
protected SelenideElement contentTextArea = $x("//div[@id='content']/textarea");
|
||||
protected SelenideElement headersTextArea = $x("//div[@id='headers']/textarea");
|
||||
protected SelenideElement submitBtn = headersTextArea.$x("../../../..//button[@type='submit']");
|
||||
protected SelenideElement partitionDdl = $x("//ul[@name='partition']");
|
||||
protected SelenideElement keySerdeDdl = $x("//ul[@name='keySerde']");
|
||||
protected SelenideElement contentSerdeDdl = $x("//ul[@name='valueSerde']");
|
||||
protected SelenideElement keyTextArea = $x("//div[@id='key']/textarea");
|
||||
protected SelenideElement valueTextArea = $x("//div[@id='content']/textarea");
|
||||
protected SelenideElement headersTextArea = $x("//div[@id='headers']/textarea");
|
||||
protected SelenideElement submitBtn = headersTextArea.$x("../../../..//button[@type='submit']");
|
||||
protected SelenideElement partitionDdl = $x("//ul[@name='partition']");
|
||||
protected SelenideElement keySerdeDdl = $x("//ul[@name='keySerde']");
|
||||
protected SelenideElement contentSerdeDdl = $x("//ul[@name='valueSerde']");
|
||||
|
||||
@Step
|
||||
public ProduceMessagePanel waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
Arrays.asList(partitionDdl, keySerdeDdl, contentSerdeDdl).forEach(element -> element.shouldBe(Condition.visible));
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ProduceMessagePanel waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
Arrays.asList(partitionDdl, keySerdeDdl, contentSerdeDdl).forEach(element -> element.shouldBe(Condition.visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ProduceMessagePanel setKeyField(String value) {
|
||||
clearByKeyboard(keyTextArea);
|
||||
keyTextArea.setValue(value);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ProduceMessagePanel setKeyField(String value) {
|
||||
clearByKeyboard(keyTextArea);
|
||||
keyTextArea.setValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ProduceMessagePanel setContentFiled(String value) {
|
||||
clearByKeyboard(contentTextArea);
|
||||
contentTextArea.setValue(value);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ProduceMessagePanel setValueFiled(String value) {
|
||||
clearByKeyboard(valueTextArea);
|
||||
valueTextArea.setValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ProduceMessagePanel setHeaderFiled(String value) {
|
||||
headersTextArea.setValue(value);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ProduceMessagePanel setHeadersFld(String value) {
|
||||
headersTextArea.setValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ProduceMessagePanel submitProduceMessage() {
|
||||
clickByActions(submitBtn);
|
||||
submitBtn.shouldBe(Condition.disappear);
|
||||
refresh();
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ProduceMessagePanel submitProduceMessage() {
|
||||
clickByActions(submitBtn);
|
||||
submitBtn.shouldBe(Condition.disappear);
|
||||
refresh();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
package com.provectus.kafka.ui.pages.topics;
|
||||
|
||||
import com.codeborne.selenide.*;
|
||||
import static com.codeborne.selenide.Selenide.$;
|
||||
import static com.codeborne.selenide.Selenide.$$;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static org.openqa.selenium.By.id;
|
||||
|
||||
import com.codeborne.selenide.ClickOptions;
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.ElementsCollection;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import com.provectus.kafka.ui.pages.topics.enums.CleanupPolicyValue;
|
||||
import com.provectus.kafka.ui.pages.topics.enums.CustomParameterType;
|
||||
|
@ -8,269 +17,264 @@ import com.provectus.kafka.ui.pages.topics.enums.MaxSizeOnDisk;
|
|||
import com.provectus.kafka.ui.pages.topics.enums.TimeToRetain;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.*;
|
||||
|
||||
public class TopicCreateEditForm extends BasePage {
|
||||
|
||||
protected SelenideElement timeToRetainField = $x("//input[@id='timeToRetain']");
|
||||
protected SelenideElement partitionsField = $x("//input[@name='partitions']");
|
||||
protected SelenideElement nameField = $x("//input[@name='name']");
|
||||
protected SelenideElement maxMessageBytesField = $x("//input[@name='maxMessageBytes']");
|
||||
protected SelenideElement minInSyncReplicasField = $x("//input[@name='minInSyncReplicas']");
|
||||
protected SelenideElement cleanUpPolicyDdl = $x("//ul[@id='topicFormCleanupPolicy']");
|
||||
protected SelenideElement maxSizeOnDiscDdl = $x("//ul[@id='topicFormRetentionBytes']");
|
||||
protected SelenideElement customParameterDdl = $x("//ul[contains(@name,'customParams')]");
|
||||
protected SelenideElement deleteCustomParameterBtn = $x("//span[contains(@title,'Delete customParam')]");
|
||||
protected SelenideElement addCustomParameterTypeBtn = $x("//button[contains(text(),'Add Custom Parameter')]");
|
||||
protected SelenideElement customParameterValueField = $x("//input[@placeholder='Value']");
|
||||
protected SelenideElement validationCustomParameterValueMsg = $x("//p[contains(text(),'Value is required')]");
|
||||
protected String ddlElementLocator = "//li[@value='%s']";
|
||||
protected String btnTimeToRetainLocator = "//button[@class][text()='%s']";
|
||||
protected SelenideElement timeToRetainField = $x("//input[@id='timeToRetain']");
|
||||
protected SelenideElement partitionsField = $x("//input[@name='partitions']");
|
||||
protected SelenideElement nameField = $(id("topicFormName"));
|
||||
protected SelenideElement maxMessageBytesField = $x("//input[@name='maxMessageBytes']");
|
||||
protected SelenideElement minInSyncReplicasField = $x("//input[@name='minInSyncReplicas']");
|
||||
protected SelenideElement cleanUpPolicyDdl = $x("//ul[@id='topicFormCleanupPolicy']");
|
||||
protected SelenideElement maxSizeOnDiscDdl = $x("//ul[@id='topicFormRetentionBytes']");
|
||||
protected SelenideElement customParameterDdl = $x("//ul[contains(@name,'customParams')]");
|
||||
protected SelenideElement deleteCustomParameterBtn = $x("//span[contains(@title,'Delete customParam')]");
|
||||
protected SelenideElement addCustomParameterTypeBtn = $x("//button[contains(text(),'Add Custom Parameter')]");
|
||||
protected SelenideElement customParameterValueField = $x("//input[@placeholder='Value']");
|
||||
protected SelenideElement validationCustomParameterValueMsg = $x("//p[contains(text(),'Value is required')]");
|
||||
protected String ddlElementLocator = "//li[@value='%s']";
|
||||
protected String btnTimeToRetainLocator = "//button[@class][text()='%s']";
|
||||
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
nameField.shouldBe(Condition.visible);
|
||||
return this;
|
||||
@Step
|
||||
public TopicCreateEditForm waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
nameField.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isCreateTopicButtonEnabled() {
|
||||
return isEnabled(submitBtn);
|
||||
}
|
||||
|
||||
public boolean isDeleteCustomParameterButtonEnabled() {
|
||||
return isEnabled(deleteCustomParameterBtn);
|
||||
}
|
||||
|
||||
public boolean isNameFieldEnabled() {
|
||||
return isEnabled(nameField);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setTopicName(String topicName) {
|
||||
sendKeysAfterClear(nameField, topicName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setMinInsyncReplicas(Integer minInsyncReplicas) {
|
||||
minInSyncReplicasField.setValue(minInsyncReplicas.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setTimeToRetainDataInMs(Long ms) {
|
||||
timeToRetainField.setValue(ms.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setTimeToRetainDataInMs(String ms) {
|
||||
timeToRetainField.setValue(ms);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setMaxSizeOnDiskInGB(MaxSizeOnDisk maxSizeOnDisk) {
|
||||
maxSizeOnDiscDdl.shouldBe(Condition.visible).click();
|
||||
$x(String.format(ddlElementLocator, maxSizeOnDisk.getOptionValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm clickAddCustomParameterTypeButton() {
|
||||
addCustomParameterTypeBtn.click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm openCustomParameterTypeDdl() {
|
||||
customParameterDdl.shouldBe(Condition.visible).click();
|
||||
ddlOptions.shouldHave(CollectionCondition.sizeGreaterThan(0));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ElementsCollection getAllDdlOptions() {
|
||||
return getDdlOptions();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setCustomParameterType(CustomParameterType customParameterType) {
|
||||
openCustomParameterTypeDdl();
|
||||
$x(String.format(ddlElementLocator, customParameterType.getOptionValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm clearCustomParameterValue() {
|
||||
clearByKeyboard(customParameterValueField);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setNumberOfPartitions(int partitions) {
|
||||
partitionsField.shouldBe(Condition.enabled).clear();
|
||||
partitionsField.sendKeys(String.valueOf(partitions));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setTimeToRetainDataByButtons(TimeToRetain timeToRetain) {
|
||||
$x(String.format(btnTimeToRetainLocator, timeToRetain.getButton())).shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm selectCleanupPolicy(CleanupPolicyValue cleanupPolicyOptionValue) {
|
||||
cleanUpPolicyDdl.shouldBe(Condition.visible).click();
|
||||
$x(String.format(ddlElementLocator, cleanupPolicyOptionValue.getOptionValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm selectRetentionBytes(String visibleValue) {
|
||||
return selectFromDropDownByVisibleText("retentionBytes", visibleValue);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm selectRetentionBytes(Long optionValue) {
|
||||
return selectFromDropDownByOptionValue("retentionBytes", optionValue.toString());
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm clickSaveTopicBtn() {
|
||||
clickSubmitBtn();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm addCustomParameter(String customParameterName,
|
||||
String customParameterValue) {
|
||||
ElementsCollection customParametersElements =
|
||||
$$("ul[role=listbox][name^=customParams][name$=name]");
|
||||
KafkaUiSelectElement kafkaUiSelectElement = null;
|
||||
if (customParametersElements.size() == 1) {
|
||||
if ("Select".equals(customParametersElements.first().getText())) {
|
||||
kafkaUiSelectElement = new KafkaUiSelectElement(customParametersElements.first());
|
||||
}
|
||||
} else {
|
||||
$$("button")
|
||||
.find(Condition.exactText("Add Custom Parameter"))
|
||||
.click();
|
||||
customParametersElements = $$("ul[role=listbox][name^=customParams][name$=name]");
|
||||
kafkaUiSelectElement = new KafkaUiSelectElement(customParametersElements.last());
|
||||
}
|
||||
if (kafkaUiSelectElement != null) {
|
||||
kafkaUiSelectElement.selectByVisibleText(customParameterName);
|
||||
}
|
||||
$(String.format("input[name=\"customParams.%d.value\"]", customParametersElements.size() - 1))
|
||||
.setValue(customParameterValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm updateCustomParameter(String customParameterName,
|
||||
String customParameterValue) {
|
||||
SelenideElement selenideElement = $$("ul[role=listbox][name^=customParams][name$=name]")
|
||||
.find(Condition.exactText(customParameterName));
|
||||
String name = selenideElement.getAttribute("name");
|
||||
if (name != null) {
|
||||
name = name.substring(0, name.lastIndexOf("."));
|
||||
}
|
||||
$(String.format("input[name^=%s]", name)).setValue(customParameterValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getCleanupPolicy() {
|
||||
return new KafkaUiSelectElement("cleanupPolicy").getCurrentValue();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTimeToRetain() {
|
||||
return timeToRetainField.getValue();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getMaxSizeOnDisk() {
|
||||
return new KafkaUiSelectElement("retentionBytes").getCurrentValue();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getMaxMessageBytes() {
|
||||
return maxMessageBytesField.getValue();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setMaxMessageBytes(Long bytes) {
|
||||
maxMessageBytesField.setValue(bytes.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setMaxMessageBytes(String bytes) {
|
||||
return setMaxMessageBytes(Long.parseLong(bytes));
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isValidationMessageCustomParameterValueVisible() {
|
||||
return isVisible(validationCustomParameterValueMsg);
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getCustomParameterValue() {
|
||||
return customParameterValueField.getValue();
|
||||
}
|
||||
|
||||
private TopicCreateEditForm selectFromDropDownByOptionValue(String dropDownElementName,
|
||||
String optionValue) {
|
||||
KafkaUiSelectElement select = new KafkaUiSelectElement(dropDownElementName);
|
||||
select.selectByOptionValue(optionValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
private TopicCreateEditForm selectFromDropDownByVisibleText(String dropDownElementName,
|
||||
String visibleText) {
|
||||
KafkaUiSelectElement select = new KafkaUiSelectElement(dropDownElementName);
|
||||
select.selectByVisibleText(visibleText);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static class KafkaUiSelectElement {
|
||||
|
||||
private final SelenideElement selectElement;
|
||||
|
||||
public KafkaUiSelectElement(String selectElementName) {
|
||||
this.selectElement = $("ul[role=listbox][name=" + selectElementName + "]");
|
||||
}
|
||||
|
||||
public boolean isCreateTopicButtonEnabled() {
|
||||
return isEnabled(submitBtn);
|
||||
public KafkaUiSelectElement(SelenideElement selectElement) {
|
||||
this.selectElement = selectElement;
|
||||
}
|
||||
|
||||
public boolean isDeleteCustomParameterButtonEnabled() {
|
||||
return isEnabled(deleteCustomParameterBtn);
|
||||
public void selectByOptionValue(String optionValue) {
|
||||
selectElement.click();
|
||||
selectElement
|
||||
.$$x(".//ul/li[@role='option']")
|
||||
.find(Condition.attribute("value", optionValue))
|
||||
.click(ClickOptions.usingJavaScript());
|
||||
}
|
||||
|
||||
public boolean isNameFieldEnabled() {
|
||||
return isEnabled(nameField);
|
||||
public void selectByVisibleText(String visibleText) {
|
||||
selectElement.click();
|
||||
selectElement
|
||||
.$$("ul>li[role=option]")
|
||||
.find(Condition.exactText(visibleText))
|
||||
.click();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setTopicName(String topicName) {
|
||||
nameField.shouldBe(Condition.enabled).clear();
|
||||
if (topicName != null) {
|
||||
nameField.sendKeys(topicName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setMinInsyncReplicas(Integer minInsyncReplicas) {
|
||||
minInSyncReplicasField.setValue(minInsyncReplicas.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setTimeToRetainDataInMs(Long ms) {
|
||||
timeToRetainField.setValue(ms.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setTimeToRetainDataInMs(String ms) {
|
||||
timeToRetainField.setValue(ms);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setMaxSizeOnDiskInGB(MaxSizeOnDisk MaxSizeOnDisk) {
|
||||
maxSizeOnDiscDdl.shouldBe(Condition.visible).click();
|
||||
$x(String.format(ddlElementLocator, MaxSizeOnDisk.getOptionValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm clickAddCustomParameterTypeButton() {
|
||||
addCustomParameterTypeBtn.click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm openCustomParameterTypeDdl() {
|
||||
customParameterDdl.shouldBe(Condition.visible).click();
|
||||
ddlOptions.shouldHave(CollectionCondition.sizeGreaterThan(0));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ElementsCollection getAllDdlOptions() {
|
||||
return getDdlOptions();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setCustomParameterType(CustomParameterType customParameterType) {
|
||||
openCustomParameterTypeDdl();
|
||||
$x(String.format(ddlElementLocator, customParameterType.getOptionValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm clearCustomParameterValue() {
|
||||
clearByKeyboard(customParameterValueField);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setNumberOfPartitions(int partitions) {
|
||||
partitionsField.shouldBe(Condition.enabled).clear();
|
||||
partitionsField.sendKeys(String.valueOf(partitions));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setTimeToRetainDataByButtons(TimeToRetain timeToRetain) {
|
||||
$x(String.format(btnTimeToRetainLocator, timeToRetain.getButton())).shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm selectCleanupPolicy(CleanupPolicyValue cleanupPolicyOptionValue) {
|
||||
cleanUpPolicyDdl.shouldBe(Condition.visible).click();
|
||||
$x(String.format(ddlElementLocator, cleanupPolicyOptionValue.getOptionValue())).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm selectRetentionBytes(String visibleValue) {
|
||||
return selectFromDropDownByVisibleText("retentionBytes", visibleValue);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm selectRetentionBytes(Long optionValue) {
|
||||
return selectFromDropDownByOptionValue("retentionBytes", optionValue.toString());
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm clickCreateTopicBtn() {
|
||||
clickSubmitBtn();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm addCustomParameter(String customParameterName,
|
||||
String customParameterValue) {
|
||||
ElementsCollection customParametersElements =
|
||||
$$("ul[role=listbox][name^=customParams][name$=name]");
|
||||
KafkaUISelectElement kafkaUISelectElement = null;
|
||||
if (customParametersElements.size() == 1) {
|
||||
if ("Select".equals(customParametersElements.first().getText())) {
|
||||
kafkaUISelectElement = new KafkaUISelectElement(customParametersElements.first());
|
||||
}
|
||||
} else {
|
||||
$$("button")
|
||||
.find(Condition.exactText("Add Custom Parameter"))
|
||||
.click();
|
||||
customParametersElements = $$("ul[role=listbox][name^=customParams][name$=name]");
|
||||
kafkaUISelectElement = new KafkaUISelectElement(customParametersElements.last());
|
||||
}
|
||||
if (kafkaUISelectElement != null) {
|
||||
kafkaUISelectElement.selectByVisibleText(customParameterName);
|
||||
}
|
||||
$(String.format("input[name=\"customParams.%d.value\"]", customParametersElements.size() - 1))
|
||||
.setValue(customParameterValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm updateCustomParameter(String customParameterName,
|
||||
String customParameterValue) {
|
||||
SelenideElement selenideElement = $$("ul[role=listbox][name^=customParams][name$=name]")
|
||||
.find(Condition.exactText(customParameterName));
|
||||
String name = selenideElement.getAttribute("name");
|
||||
if (name != null) {
|
||||
name = name.substring(0, name.lastIndexOf("."));
|
||||
}
|
||||
$(String.format("input[name^=%s]", name)).setValue(customParameterValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getCleanupPolicy() {
|
||||
return new KafkaUISelectElement("cleanupPolicy").getCurrentValue();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getTimeToRetain() {
|
||||
return timeToRetainField.getValue();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getMaxSizeOnDisk() {
|
||||
return new KafkaUISelectElement("retentionBytes").getCurrentValue();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getMaxMessageBytes() {
|
||||
return maxMessageBytesField.getValue();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setMaxMessageBytes(Long bytes) {
|
||||
maxMessageBytesField.setValue(bytes.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicCreateEditForm setMaxMessageBytes(String bytes) {
|
||||
return setMaxMessageBytes(Long.parseLong(bytes));
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isValidationMessageCustomParameterValueVisible() {
|
||||
return isVisible(validationCustomParameterValueMsg);
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getCustomParameterValue() {
|
||||
return customParameterValueField.getValue();
|
||||
}
|
||||
|
||||
private TopicCreateEditForm selectFromDropDownByOptionValue(String dropDownElementName,
|
||||
String optionValue) {
|
||||
KafkaUISelectElement select = new KafkaUISelectElement(dropDownElementName);
|
||||
select.selectByOptionValue(optionValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
private TopicCreateEditForm selectFromDropDownByVisibleText(String dropDownElementName,
|
||||
String visibleText) {
|
||||
KafkaUISelectElement select = new KafkaUISelectElement(dropDownElementName);
|
||||
select.selectByVisibleText(visibleText);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static class KafkaUISelectElement {
|
||||
|
||||
private final SelenideElement selectElement;
|
||||
|
||||
public KafkaUISelectElement(String selectElementName) {
|
||||
this.selectElement = $("ul[role=listbox][name=" + selectElementName + "]");
|
||||
}
|
||||
|
||||
public KafkaUISelectElement(SelenideElement selectElement) {
|
||||
this.selectElement = selectElement;
|
||||
}
|
||||
|
||||
public void selectByOptionValue(String optionValue) {
|
||||
selectElement.click();
|
||||
selectElement
|
||||
.$$x(".//ul/li[@role='option']")
|
||||
.find(Condition.attribute("value", optionValue))
|
||||
.click(ClickOptions.usingJavaScript());
|
||||
}
|
||||
|
||||
public void selectByVisibleText(String visibleText) {
|
||||
selectElement.click();
|
||||
selectElement
|
||||
.$$("ul>li[role=option]")
|
||||
.find(Condition.exactText(visibleText))
|
||||
.click();
|
||||
}
|
||||
|
||||
public String getCurrentValue() {
|
||||
return selectElement.$("li").getText();
|
||||
}
|
||||
public String getCurrentValue() {
|
||||
return selectElement.$("li").getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,463 +1,475 @@
|
|||
package com.provectus.kafka.ui.pages.topics;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$$x;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.codeborne.selenide.Selenide.sleep;
|
||||
import static com.provectus.kafka.ui.pages.topics.TopicDetails.TopicMenu.OVERVIEW;
|
||||
import static org.testcontainers.shaded.org.apache.commons.lang3.RandomUtils.nextInt;
|
||||
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.ElementsCollection;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.YearMonth;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.util.*;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.*;
|
||||
import static org.testcontainers.shaded.org.apache.commons.lang3.RandomUtils.nextInt;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class TopicDetails extends BasePage {
|
||||
|
||||
protected SelenideElement clearMessagesBtn = $x(("//div[contains(text(), 'Clear messages')]"));
|
||||
protected SelenideElement recreateTopicBtn = $x("//div[text()='Recreate Topic']");
|
||||
protected SelenideElement messageAmountCell = $x("//tbody/tr/td[5]");
|
||||
protected SelenideElement overviewTab = $x("//a[contains(text(),'Overview')]");
|
||||
protected SelenideElement messagesTab = $x("//a[contains(text(),'Messages')]");
|
||||
protected SelenideElement seekTypeDdl = $x("//ul[@id='selectSeekType']/li");
|
||||
protected SelenideElement seekTypeField = $x("//label[text()='Seek Type']//..//div/input");
|
||||
protected SelenideElement addFiltersBtn = $x("//button[text()='Add Filters']");
|
||||
protected SelenideElement savedFiltersLink = $x("//div[text()='Saved Filters']");
|
||||
protected SelenideElement addFilterCodeModalTitle = $x("//label[text()='Filter code']");
|
||||
protected SelenideElement addFilterCodeInput = $x("//div[@id='ace-editor']//textarea");
|
||||
protected SelenideElement saveThisFilterCheckBoxAddFilterMdl = $x("//input[@name='saveFilter']");
|
||||
protected SelenideElement displayNameInputAddFilterMdl = $x("//input[@placeholder='Enter Name']");
|
||||
protected SelenideElement cancelBtnAddFilterMdl = $x("//button[text()='Cancel']");
|
||||
protected SelenideElement addFilterBtnAddFilterMdl = $x("//button[text()='Add filter']");
|
||||
protected SelenideElement addFiltersBtnMessages = $x("//button[text()='Add Filters']");
|
||||
protected SelenideElement selectFilterBtnAddFilterMdl = $x("//button[text()='Select filter']");
|
||||
protected SelenideElement editSettingsMenu = $x("//li[@role][contains(text(),'Edit settings')]");
|
||||
protected SelenideElement removeTopicBtn = $x("//ul[@role='menu']//div[contains(text(),'Remove Topic')]");
|
||||
protected SelenideElement produceMessageBtn = $x("//div//button[text()='Produce Message']");
|
||||
protected SelenideElement contentMessageTab = $x("//html//div[@id='root']/div/main//table//p");
|
||||
protected SelenideElement cleanUpPolicyField = $x("//div[contains(text(),'Clean Up Policy')]/../span/*");
|
||||
protected SelenideElement partitionsField = $x("//div[contains(text(),'Partitions')]/../span");
|
||||
protected SelenideElement backToCreateFiltersLink = $x("//div[text()='Back To create filters']");
|
||||
protected ElementsCollection messageGridItems = $$x("//tbody//tr");
|
||||
protected SelenideElement actualCalendarDate = $x("//div[@class='react-datepicker__current-month']");
|
||||
protected SelenideElement previousMonthButton = $x("//button[@aria-label='Previous Month']");
|
||||
protected SelenideElement nextMonthButton = $x("//button[@aria-label='Next Month']");
|
||||
protected SelenideElement calendarTimeFld = $x("//input[@placeholder='Time']");
|
||||
protected String dayCellLtr = "//div[@role='option'][contains(text(),'%d')]";
|
||||
protected String seekFilterDdlLocator = "//ul[@id='selectSeekType']/ul/li[text()='%s']";
|
||||
protected String savedFilterNameLocator = "//div[@role='savedFilter']/div[contains(text(),'%s')]";
|
||||
protected String consumerIdLocator = "//a[@title='%s']";
|
||||
protected String topicHeaderLocator = "//h1[contains(text(),'%s')]";
|
||||
protected String activeFilterNameLocator = "//div[@data-testid='activeSmartFilter'][contains(text(),'%s')]";
|
||||
protected String settingsGridValueLocator = "//tbody/tr/td/span[text()='%s']//ancestor::tr/td[2]/span";
|
||||
protected SelenideElement clearMessagesBtn = $x(("//div[contains(text(), 'Clear messages')]"));
|
||||
protected SelenideElement recreateTopicBtn = $x("//div[text()='Recreate Topic']");
|
||||
protected SelenideElement messageAmountCell = $x("//tbody/tr/td[5]");
|
||||
protected SelenideElement overviewTab = $x("//a[contains(text(),'Overview')]");
|
||||
protected SelenideElement messagesTab = $x("//a[contains(text(),'Messages')]");
|
||||
protected SelenideElement seekTypeDdl = $x("//ul[@id='selectSeekType']//li");
|
||||
protected SelenideElement seekTypeField = $x("//label[text()='Seek Type']//..//div/input");
|
||||
protected SelenideElement addFiltersBtn = $x("//button[text()='Add Filters']");
|
||||
protected SelenideElement savedFiltersLink = $x("//div[text()='Saved Filters']");
|
||||
protected SelenideElement addFilterCodeModalTitle = $x("//label[text()='Filter code']");
|
||||
protected SelenideElement addFilterCodeInput = $x("//div[@id='ace-editor']//textarea");
|
||||
protected SelenideElement saveThisFilterCheckBoxAddFilterMdl = $x("//input[@name='saveFilter']");
|
||||
protected SelenideElement displayNameInputAddFilterMdl = $x("//input[@placeholder='Enter Name']");
|
||||
protected SelenideElement cancelBtnAddFilterMdl = $x("//button[text()='Cancel']");
|
||||
protected SelenideElement addFilterBtnAddFilterMdl = $x("//button[text()='Add filter']");
|
||||
protected SelenideElement addFiltersBtnMessages = $x("//button[text()='Add Filters']");
|
||||
protected SelenideElement selectFilterBtnAddFilterMdl = $x("//button[text()='Select filter']");
|
||||
protected SelenideElement editSettingsMenu = $x("//li[@role][contains(text(),'Edit settings')]");
|
||||
protected SelenideElement removeTopicBtn = $x("//ul[@role='menu']//div[contains(text(),'Remove Topic')]");
|
||||
protected SelenideElement produceMessageBtn = $x("//div//button[text()='Produce Message']");
|
||||
protected SelenideElement contentMessageTab = $x("//html//div[@id='root']/div/main//table//p");
|
||||
protected SelenideElement cleanUpPolicyField = $x("//div[contains(text(),'Clean Up Policy')]/../span/*");
|
||||
protected SelenideElement partitionsField = $x("//div[contains(text(),'Partitions')]/../span");
|
||||
protected SelenideElement backToCreateFiltersLink = $x("//div[text()='Back To create filters']");
|
||||
protected ElementsCollection messageGridItems = $$x("//tbody//tr");
|
||||
protected SelenideElement actualCalendarDate = $x("//div[@class='react-datepicker__current-month']");
|
||||
protected SelenideElement previousMonthButton = $x("//button[@aria-label='Previous Month']");
|
||||
protected SelenideElement nextMonthButton = $x("//button[@aria-label='Next Month']");
|
||||
protected SelenideElement calendarTimeFld = $x("//input[@placeholder='Time']");
|
||||
protected String detailsTabLtr = "//nav//a[contains(text(),'%s')]";
|
||||
protected String dayCellLtr = "//div[@role='option'][contains(text(),'%d')]";
|
||||
protected String seekFilterDdlLocator = "//ul[@id='selectSeekType']/ul/li[text()='%s']";
|
||||
protected String savedFilterNameLocator = "//div[@role='savedFilter']/div[contains(text(),'%s')]";
|
||||
protected String consumerIdLocator = "//a[@title='%s']";
|
||||
protected String topicHeaderLocator = "//h1[contains(text(),'%s')]";
|
||||
protected String activeFilterNameLocator = "//div[@data-testid='activeSmartFilter'][contains(text(),'%s')]";
|
||||
protected String settingsGridValueLocator = "//tbody/tr/td/span[text()='%s']//ancestor::tr/td[2]/span";
|
||||
|
||||
@Step
|
||||
public TopicDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
overviewTab.shouldBe(Condition.visible);
|
||||
return this;
|
||||
@Step
|
||||
public TopicDetails waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
$x(String.format(detailsTabLtr, OVERVIEW)).shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openDetailsTab(TopicMenu menu) {
|
||||
$x(String.format(detailsTabLtr, menu.toString())).shouldBe(Condition.enabled).click();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getSettingsGridValueByKey(String key) {
|
||||
return $x(String.format(settingsGridValueLocator, key)).scrollTo().shouldBe(Condition.visible).getText();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openDotMenu() {
|
||||
clickByJavaScript(dotMenuBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isAlertWithMessageVisible(AlertHeader header, String message) {
|
||||
return isAlertVisible(header, message);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickEditSettingsMenu() {
|
||||
editSettingsMenu.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isConfirmationMdlVisible() {
|
||||
return isConfirmationModalVisible();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickClearMessagesMenu() {
|
||||
clearMessagesBtn.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isClearMessagesMenuEnabled() {
|
||||
return !Objects.requireNonNull(clearMessagesBtn.shouldBe(Condition.visible)
|
||||
.$x("./..").getAttribute("class"))
|
||||
.contains("disabled");
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickRecreateTopicMenu() {
|
||||
recreateTopicBtn.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getCleanUpPolicy() {
|
||||
return cleanUpPolicyField.getText();
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getPartitions() {
|
||||
return Integer.parseInt(partitionsField.getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isTopicHeaderVisible(String topicName) {
|
||||
return isVisible($x(String.format(topicHeaderLocator, topicName)));
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickDeleteTopicMenu() {
|
||||
removeTopicBtn.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickConfirmBtnMdl() {
|
||||
clickConfirmButton();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickProduceMessageBtn() {
|
||||
clickByJavaScript(produceMessageBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails selectSeekTypeDdlMessagesTab(String seekTypeName) {
|
||||
seekTypeDdl.shouldBe(Condition.enabled).click();
|
||||
$x(String.format(seekFilterDdlLocator, seekTypeName)).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails setSeekTypeValueFldMessagesTab(String seekTypeValue) {
|
||||
seekTypeField.shouldBe(Condition.enabled).sendKeys(seekTypeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickSubmitFiltersBtnMessagesTab() {
|
||||
clickByJavaScript(submitBtn);
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickMessagesAddFiltersBtn() {
|
||||
addFiltersBtn.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickNextButton() {
|
||||
nextBtn.shouldBe(Condition.enabled).click();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openSavedFiltersListMdl() {
|
||||
savedFiltersLink.shouldBe(Condition.enabled).click();
|
||||
backToCreateFiltersLink.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isFilterVisibleAtSavedFiltersMdl(String filterName) {
|
||||
return isVisible($x(String.format(savedFilterNameLocator, filterName)));
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails selectFilterAtSavedFiltersMdl(String filterName) {
|
||||
$x(String.format(savedFilterNameLocator, filterName)).shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickSelectFilterBtnAtSavedFiltersMdl() {
|
||||
selectFilterBtnAddFilterMdl.shouldBe(Condition.enabled).click();
|
||||
addFilterCodeModalTitle.shouldBe(Condition.disappear);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails waitUntilAddFiltersMdlVisible() {
|
||||
addFilterCodeModalTitle.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails setFilterCodeFieldAddFilterMdl(String filterCode) {
|
||||
addFilterCodeInput.shouldBe(Condition.enabled).sendKeys(filterCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails selectSaveThisFilterCheckboxMdl(boolean select) {
|
||||
selectElement(saveThisFilterCheckBoxAddFilterMdl, select);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isSaveThisFilterCheckBoxSelected() {
|
||||
return isSelected(saveThisFilterCheckBoxAddFilterMdl);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails setDisplayNameFldAddFilterMdl(String displayName) {
|
||||
displayNameInputAddFilterMdl.shouldBe(Condition.enabled).sendKeys(displayName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickAddFilterBtnAndCloseMdl(boolean closeModal) {
|
||||
addFilterBtnAddFilterMdl.shouldBe(Condition.enabled).click();
|
||||
if (closeModal) {
|
||||
addFilterCodeModalTitle.shouldBe(Condition.hidden);
|
||||
} else {
|
||||
addFilterCodeModalTitle.shouldBe(Condition.visible);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isAddFilterBtnAddFilterMdlEnabled() {
|
||||
return isEnabled(addFilterBtnAddFilterMdl);
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isBackButtonEnabled() {
|
||||
return isEnabled(backBtn);
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isNextButtonEnabled() {
|
||||
return isEnabled(nextBtn);
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isActiveFilterVisible(String activeFilterName) {
|
||||
return isVisible($x(String.format(activeFilterNameLocator, activeFilterName)));
|
||||
}
|
||||
|
||||
public List<SelenideElement> getAllAddFilterModalVisibleElements() {
|
||||
return Arrays.asList(savedFiltersLink, displayNameInputAddFilterMdl, addFilterBtnAddFilterMdl,
|
||||
cancelBtnAddFilterMdl);
|
||||
}
|
||||
|
||||
public List<SelenideElement> getAllAddFilterModalEnabledElements() {
|
||||
return Arrays.asList(displayNameInputAddFilterMdl, cancelBtnAddFilterMdl);
|
||||
}
|
||||
|
||||
public List<SelenideElement> getAllAddFilterModalDisabledElements() {
|
||||
return Collections.singletonList(addFilterBtnAddFilterMdl);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openConsumerGroup(String consumerId) {
|
||||
$x(String.format(consumerIdLocator, consumerId)).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void selectYear(int expectedYear) {
|
||||
while (getActualCalendarDate().getYear() > expectedYear) {
|
||||
clickByJavaScript(previousMonthButton);
|
||||
sleep(1000);
|
||||
if (LocalTime.now().plusMinutes(3).isBefore(LocalTime.now())) {
|
||||
throw new IllegalArgumentException("Unable to select year");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void selectMonth(int expectedMonth) {
|
||||
while (getActualCalendarDate().getMonthValue() > expectedMonth) {
|
||||
clickByJavaScript(previousMonthButton);
|
||||
sleep(1000);
|
||||
if (LocalTime.now().plusMinutes(3).isBefore(LocalTime.now())) {
|
||||
throw new IllegalArgumentException("Unable to select month");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void selectDay(int expectedDay) {
|
||||
Objects.requireNonNull($$x(String.format(dayCellLtr, expectedDay)).stream()
|
||||
.filter(day -> !Objects.requireNonNull(day.getAttribute("class")).contains("outside-month"))
|
||||
.findFirst().orElseThrow()).shouldBe(Condition.enabled).click();
|
||||
}
|
||||
|
||||
private void setTime(LocalDateTime dateTime) {
|
||||
calendarTimeFld.shouldBe(Condition.enabled)
|
||||
.sendKeys(String.valueOf(dateTime.getHour()), String.valueOf(dateTime.getMinute()));
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails selectDateAndTimeByCalendar(LocalDateTime dateTime) {
|
||||
setTime(dateTime);
|
||||
selectYear(dateTime.getYear());
|
||||
selectMonth(dateTime.getMonthValue());
|
||||
selectDay(dateTime.getDayOfMonth());
|
||||
return this;
|
||||
}
|
||||
|
||||
private LocalDate getActualCalendarDate() {
|
||||
String monthAndYearStr = actualCalendarDate.getText().trim();
|
||||
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.append(DateTimeFormatter.ofPattern("MMMM yyyy"))
|
||||
.toFormatter(Locale.ENGLISH);
|
||||
YearMonth yearMonth = formatter.parse(monthAndYearStr, YearMonth::from);
|
||||
return yearMonth.atDay(1);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openCalendarSeekType() {
|
||||
seekTypeField.shouldBe(Condition.enabled).click();
|
||||
actualCalendarDate.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getMessageCountAmount() {
|
||||
return Integer.parseInt(messageAmountCell.getText().trim());
|
||||
}
|
||||
|
||||
private List<TopicDetails.MessageGridItem> initItems() {
|
||||
List<TopicDetails.MessageGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new TopicDetails.MessageGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails.MessageGridItem getMessageByOffset(int offset) {
|
||||
return initItems().stream()
|
||||
.filter(e -> e.getOffset() == offset)
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails.MessageGridItem getMessageByKey(String key) {
|
||||
return initItems().stream()
|
||||
.filter(e -> e.getKey().equals(key))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<MessageGridItem> getAllMessages() {
|
||||
return initItems();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails.MessageGridItem getRandomMessage() {
|
||||
return getMessageByOffset(nextInt(0, initItems().size() - 1));
|
||||
}
|
||||
|
||||
public enum TopicMenu {
|
||||
OVERVIEW("Overview"),
|
||||
MESSAGES("Messages"),
|
||||
CONSUMERS("Consumers"),
|
||||
SETTINGS("Settings");
|
||||
|
||||
private final String value;
|
||||
|
||||
TopicMenu(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MessageGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
private MessageGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openDetailsTab(TopicMenu menu) {
|
||||
$(By.linkText(menu.toString())).shouldBe(Condition.visible).click();
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
public MessageGridItem clickExpand() {
|
||||
clickByJavaScript(element.$x("./td[1]/span"));
|
||||
return this;
|
||||
}
|
||||
|
||||
private SelenideElement getOffsetElm() {
|
||||
return element.$x("./td[2]");
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getSettingsGridValueByKey(String key) {
|
||||
return $x(String.format(settingsGridValueLocator, key)).scrollTo().shouldBe(Condition.visible).getText();
|
||||
public int getOffset() {
|
||||
return Integer.parseInt(getOffsetElm().getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openDotMenu() {
|
||||
clickByJavaScript(dotMenuBtn);
|
||||
return this;
|
||||
public int getPartition() {
|
||||
return Integer.parseInt(element.$x("./td[3]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isAlertWithMessageVisible(AlertHeader header, String message) {
|
||||
return isAlertVisible(header, message);
|
||||
public LocalDateTime getTimestamp() {
|
||||
String timestampValue = element.$x("./td[4]/div").getText().trim();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/yyyy, HH:mm:ss");
|
||||
return LocalDateTime.parse(timestampValue, formatter);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickEditSettingsMenu() {
|
||||
editSettingsMenu.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
public String getKey() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isConfirmationMdlVisible() {
|
||||
return isConfirmationModalVisible();
|
||||
public String getValue() {
|
||||
return element.$x("./td[6]").getAttribute("title");
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickClearMessagesMenu() {
|
||||
clearMessagesBtn.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickRecreateTopicMenu() {
|
||||
recreateTopicBtn.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getCleanUpPolicy() {
|
||||
return cleanUpPolicyField.getText();
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getPartitions() {
|
||||
return Integer.parseInt(partitionsField.getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isTopicHeaderVisible(String topicName) {
|
||||
return isVisible($x(String.format(topicHeaderLocator, topicName)));
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickDeleteTopicMenu() {
|
||||
removeTopicBtn.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickConfirmBtnMdl() {
|
||||
clickConfirmButton();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickProduceMessageBtn() {
|
||||
clickByJavaScript(produceMessageBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails selectSeekTypeDdlMessagesTab(String seekTypeName) {
|
||||
seekTypeDdl.shouldBe(Condition.enabled).click();
|
||||
$x(String.format(seekFilterDdlLocator, seekTypeName)).shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails setSeekTypeValueFldMessagesTab(String seekTypeValue) {
|
||||
seekTypeField.shouldBe(Condition.enabled).sendKeys(seekTypeValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickSubmitFiltersBtnMessagesTab() {
|
||||
clickByJavaScript(submitBtn);
|
||||
waitUntilSpinnerDisappear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickMessagesAddFiltersBtn() {
|
||||
addFiltersBtn.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickNextButton() {
|
||||
nextBtn.shouldBe(Condition.enabled).click();
|
||||
waitUntilSpinnerDisappear();
|
||||
public MessageGridItem openDotMenu() {
|
||||
getOffsetElm().hover();
|
||||
element.$x("./td[7]/div/button[@aria-label='Dropdown Toggle']")
|
||||
.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openSavedFiltersListMdl() {
|
||||
savedFiltersLink.shouldBe(Condition.enabled).click();
|
||||
backToCreateFiltersLink.shouldBe(Condition.visible);
|
||||
return this;
|
||||
public MessageGridItem clickCopyToClipBoard() {
|
||||
clickByJavaScript(element.$x("./td[7]//li[text() = 'Copy to clipboard']")
|
||||
.shouldBe(Condition.visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isFilterVisibleAtSavedFiltersMdl(String filterName) {
|
||||
return isVisible($x(String.format(savedFilterNameLocator, filterName)));
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails selectFilterAtSavedFiltersMdl(String filterName) {
|
||||
$x(String.format(savedFilterNameLocator, filterName)).shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickSelectFilterBtnAtSavedFiltersMdl() {
|
||||
selectFilterBtnAddFilterMdl.shouldBe(Condition.enabled).click();
|
||||
addFilterCodeModalTitle.shouldBe(Condition.disappear);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails waitUntilAddFiltersMdlVisible() {
|
||||
addFilterCodeModalTitle.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails setFilterCodeFieldAddFilterMdl(String filterCode) {
|
||||
addFilterCodeInput.shouldBe(Condition.enabled).sendKeys(filterCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails selectSaveThisFilterCheckboxMdl(boolean select) {
|
||||
selectElement(saveThisFilterCheckBoxAddFilterMdl, select);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isSaveThisFilterCheckBoxSelected() {
|
||||
return isSelected(saveThisFilterCheckBoxAddFilterMdl);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails setDisplayNameFldAddFilterMdl(String displayName) {
|
||||
displayNameInputAddFilterMdl.shouldBe(Condition.enabled).sendKeys(displayName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails clickAddFilterBtnAndCloseMdl(boolean closeModal) {
|
||||
addFilterBtnAddFilterMdl.shouldBe(Condition.enabled).click();
|
||||
if (closeModal) {
|
||||
addFilterCodeModalTitle.shouldBe(Condition.hidden);
|
||||
} else {
|
||||
addFilterCodeModalTitle.shouldBe(Condition.visible);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isAddFilterBtnAddFilterMdlEnabled() {
|
||||
return isEnabled(addFilterBtnAddFilterMdl);
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isBackButtonEnabled() {
|
||||
return isEnabled(backBtn);
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isNextButtonEnabled() {
|
||||
return isEnabled(nextBtn);
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isActiveFilterVisible(String activeFilterName) {
|
||||
return isVisible($x(String.format(activeFilterNameLocator, activeFilterName)));
|
||||
}
|
||||
|
||||
public List<SelenideElement> getAllAddFilterModalVisibleElements() {
|
||||
return Arrays.asList(savedFiltersLink, displayNameInputAddFilterMdl, addFilterBtnAddFilterMdl, cancelBtnAddFilterMdl);
|
||||
}
|
||||
|
||||
public List<SelenideElement> getAllAddFilterModalEnabledElements() {
|
||||
return Arrays.asList(displayNameInputAddFilterMdl, cancelBtnAddFilterMdl);
|
||||
}
|
||||
|
||||
public List<SelenideElement> getAllAddFilterModalDisabledElements() {
|
||||
return Collections.singletonList(addFilterBtnAddFilterMdl);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openConsumerGroup(String consumerId) {
|
||||
$x(String.format(consumerIdLocator, consumerId)).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isKeyMessageVisible(String keyMessage) {
|
||||
return keyMessage.equals($("td[title]").getText());
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isContentMessageVisible(String contentMessage) {
|
||||
return contentMessage.matches(contentMessageTab.getText().trim());
|
||||
}
|
||||
|
||||
private void selectYear(int expectedYear) {
|
||||
while (getActualCalendarDate().getYear() > expectedYear) {
|
||||
clickByJavaScript(previousMonthButton);
|
||||
sleep(1000);
|
||||
if (LocalTime.now().plusMinutes(3).isBefore(LocalTime.now())) {
|
||||
throw new IllegalArgumentException("Unable to select year");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void selectMonth(int expectedMonth) {
|
||||
while (getActualCalendarDate().getMonthValue() > expectedMonth) {
|
||||
clickByJavaScript(previousMonthButton);
|
||||
sleep(1000);
|
||||
if (LocalTime.now().plusMinutes(3).isBefore(LocalTime.now())) {
|
||||
throw new IllegalArgumentException("Unable to select month");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void selectDay(int expectedDay) {
|
||||
Objects.requireNonNull($$x(String.format(dayCellLtr, expectedDay)).stream()
|
||||
.filter(day -> !Objects.requireNonNull(day.getAttribute("class")).contains("outside-month"))
|
||||
.findFirst().orElseThrow()).shouldBe(Condition.enabled).click();
|
||||
}
|
||||
|
||||
private void setTime(LocalDateTime dateTime) {
|
||||
calendarTimeFld.shouldBe(Condition.enabled)
|
||||
.sendKeys(String.valueOf(dateTime.getHour()), String.valueOf(dateTime.getMinute()));
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails selectDateAndTimeByCalendar(LocalDateTime dateTime) {
|
||||
setTime(dateTime);
|
||||
selectYear(dateTime.getYear());
|
||||
selectMonth(dateTime.getMonthValue());
|
||||
selectDay(dateTime.getDayOfMonth());
|
||||
return this;
|
||||
}
|
||||
|
||||
private LocalDate getActualCalendarDate() {
|
||||
String monthAndYearStr = actualCalendarDate.getText().trim();
|
||||
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
|
||||
.parseCaseInsensitive()
|
||||
.append(DateTimeFormatter.ofPattern("MMMM yyyy"))
|
||||
.toFormatter(Locale.ENGLISH);
|
||||
YearMonth yearMonth = formatter.parse(monthAndYearStr, YearMonth::from);
|
||||
return yearMonth.atDay(1);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails openCalendarSeekType() {
|
||||
seekTypeField.shouldBe(Condition.enabled).click();
|
||||
actualCalendarDate.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getMessageCountAmount() {
|
||||
return Integer.parseInt(messageAmountCell.getText().trim());
|
||||
}
|
||||
|
||||
private List<TopicDetails.MessageGridItem> initItems() {
|
||||
List<TopicDetails.MessageGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new TopicDetails.MessageGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails.MessageGridItem getMessageByOffset(int offset) {
|
||||
return initItems().stream()
|
||||
.filter(e -> e.getOffset() == offset)
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<MessageGridItem> getAllMessages() {
|
||||
return initItems();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicDetails.MessageGridItem getRandomMessage() {
|
||||
return getMessageByOffset(nextInt(0, initItems().size() - 1));
|
||||
}
|
||||
|
||||
public enum TopicMenu {
|
||||
OVERVIEW("Overview"),
|
||||
MESSAGES("Messages"),
|
||||
CONSUMERS("Consumers"),
|
||||
SETTINGS("Settings");
|
||||
|
||||
private final String value;
|
||||
|
||||
TopicMenu(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MessageGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
private MessageGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public MessageGridItem clickExpand() {
|
||||
clickByJavaScript(element.$x("./td[1]/span"));
|
||||
return this;
|
||||
}
|
||||
|
||||
private SelenideElement getOffsetElm() {
|
||||
return element.$x("./td[2]");
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getOffset() {
|
||||
return Integer.parseInt(getOffsetElm().getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getPartition() {
|
||||
return Integer.parseInt(element.$x("./td[3]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public LocalDateTime getTimestamp() {
|
||||
String timestampValue = element.$x("./td[4]/div").getText().trim();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/yyyy, HH:mm:ss");
|
||||
return LocalDateTime.parse(timestampValue, formatter);
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getKey() {
|
||||
return element.$x("./td[5]").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValue() {
|
||||
return element.$x("./td[6]/span/p").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public MessageGridItem openDotMenu() {
|
||||
getOffsetElm().hover();
|
||||
element.$x("./td[7]/div/button[@aria-label='Dropdown Toggle']")
|
||||
.shouldBe(Condition.visible).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public MessageGridItem clickCopyToClipBoard() {
|
||||
clickByJavaScript(element.$x("./td[7]//li[text() = 'Copy to clipboard']")
|
||||
.shouldBe(Condition.visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public MessageGridItem clickSaveAsFile() {
|
||||
clickByJavaScript(element.$x("./td[7]//li[text() = 'Save as a file']")
|
||||
.shouldBe(Condition.visible));
|
||||
return this;
|
||||
}
|
||||
public MessageGridItem clickSaveAsFile() {
|
||||
clickByJavaScript(element.$x("./td[7]//li[text() = 'Save as a file']")
|
||||
.shouldBe(Condition.visible));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +1,65 @@
|
|||
package com.provectus.kafka.ui.pages.topics;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
|
||||
public class TopicSettingsTab extends BasePage {
|
||||
|
||||
protected SelenideElement defaultValueColumnHeaderLocator = $x("//div[text() = 'Default Value']");
|
||||
protected SelenideElement defaultValueColumnHeaderLocator = $x("//div[text() = 'Default Value']");
|
||||
|
||||
@Step
|
||||
public TopicSettingsTab waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
defaultValueColumnHeaderLocator.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public TopicSettingsTab waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
defaultValueColumnHeaderLocator.shouldBe(Condition.visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<SettingsGridItem> initGridItems() {
|
||||
List<SettingsGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new SettingsGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
private List<SettingsGridItem> initGridItems() {
|
||||
List<SettingsGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new SettingsGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
private TopicSettingsTab.SettingsGridItem getItemByKey(String key) {
|
||||
return initGridItems().stream()
|
||||
.filter(e -> e.getKey().equals(key))
|
||||
.findFirst().orElseThrow();
|
||||
private TopicSettingsTab.SettingsGridItem getItemByKey(String key) {
|
||||
return initGridItems().stream()
|
||||
.filter(e -> e.getKey().equals(key))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValueByKey(String key) {
|
||||
return getItemByKey(key).getValue();
|
||||
}
|
||||
|
||||
public static class SettingsGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public SettingsGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValueByKey(String key) {
|
||||
return getItemByKey(key).getValue();
|
||||
public String getKey() {
|
||||
return element.$x("./td[1]/span").getText().trim();
|
||||
}
|
||||
|
||||
public static class SettingsGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public SettingsGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getKey() {
|
||||
return element.$x("./td[1]/span").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getValue() {
|
||||
return element.$x("./td[2]/span").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getDefaultValue() {
|
||||
return element.$x("./td[3]/span").getText().trim();
|
||||
}
|
||||
@Step
|
||||
public String getValue() {
|
||||
return element.$x("./td[2]/span").getText().trim();
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getDefaultValue() {
|
||||
return element.$x("./td[3]/span").getText().trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,265 +1,283 @@
|
|||
package com.provectus.kafka.ui.pages.topics;
|
||||
|
||||
import static com.codeborne.selenide.Condition.visible;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.TOPICS;
|
||||
|
||||
import com.codeborne.selenide.CollectionCondition;
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.provectus.kafka.ui.pages.BasePage;
|
||||
import io.qameta.allure.Step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.codeborne.selenide.Condition.visible;
|
||||
import static com.codeborne.selenide.Selenide.$x;
|
||||
import static com.provectus.kafka.ui.pages.panels.enums.MenuItem.TOPICS;
|
||||
|
||||
public class TopicsList extends BasePage {
|
||||
|
||||
protected SelenideElement addTopicBtn = $x("//button[normalize-space(text()) ='Add a Topic']");
|
||||
protected SelenideElement searchField = $x("//input[@placeholder='Search by Topic Name']");
|
||||
protected SelenideElement showInternalRadioBtn = $x("//input[@name='ShowInternalTopics']");
|
||||
protected SelenideElement deleteSelectedTopicsBtn = $x("//button[text()='Delete selected topics']");
|
||||
protected SelenideElement copySelectedTopicBtn = $x("//button[text()='Copy selected topic']");
|
||||
protected SelenideElement purgeMessagesOfSelectedTopicsBtn = $x("//button[text()='Purge messages of selected topics']");
|
||||
protected SelenideElement clearMessagesBtn = $x("//ul[contains(@class ,'open')]//div[text()='Clear Messages']");
|
||||
protected SelenideElement recreateTopicBtn = $x("//ul[contains(@class ,'open')]//div[text()='Recreate Topic']");
|
||||
protected SelenideElement removeTopicBtn = $x("//ul[contains(@class ,'open')]//div[text()='Remove Topic']");
|
||||
protected SelenideElement addTopicBtn = $x("//button[normalize-space(text()) ='Add a Topic']");
|
||||
protected SelenideElement searchField = $x("//input[@placeholder='Search by Topic Name']");
|
||||
protected SelenideElement showInternalRadioBtn = $x("//input[@name='ShowInternalTopics']");
|
||||
protected SelenideElement deleteSelectedTopicsBtn = $x("//button[text()='Delete selected topics']");
|
||||
protected SelenideElement copySelectedTopicBtn = $x("//button[text()='Copy selected topic']");
|
||||
protected SelenideElement purgeMessagesOfSelectedTopicsBtn =
|
||||
$x("//button[text()='Purge messages of selected topics']");
|
||||
protected SelenideElement clearMessagesBtn = $x("//ul[contains(@class ,'open')]//div[text()='Clear Messages']");
|
||||
protected SelenideElement recreateTopicBtn = $x("//ul[contains(@class ,'open')]//div[text()='Recreate Topic']");
|
||||
protected SelenideElement removeTopicBtn = $x("//ul[contains(@class ,'open')]//div[text()='Remove Topic']");
|
||||
|
||||
@Step
|
||||
public TopicsList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(TOPICS).shouldBe(visible);
|
||||
return this;
|
||||
@Step
|
||||
public TopicsList waitUntilScreenReady() {
|
||||
waitUntilSpinnerDisappear();
|
||||
getPageTitleFromHeader(TOPICS).shouldBe(visible);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickAddTopicBtn() {
|
||||
clickByJavaScript(addTopicBtn);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isTopicVisible(String topicName) {
|
||||
tableGrid.shouldBe(visible);
|
||||
return isVisible(getTableElement(topicName));
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isShowInternalRadioBtnSelected() {
|
||||
return isSelected(showInternalRadioBtn);
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList setShowInternalRadioButton(boolean select) {
|
||||
if (select) {
|
||||
if (!showInternalRadioBtn.isSelected()) {
|
||||
clickByJavaScript(showInternalRadioBtn);
|
||||
waitUntilSpinnerDisappear(1);
|
||||
}
|
||||
} else {
|
||||
if (showInternalRadioBtn.isSelected()) {
|
||||
clickByJavaScript(showInternalRadioBtn);
|
||||
waitUntilSpinnerDisappear(1);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList openTopic(String topicName) {
|
||||
getTopicItem(topicName).openItem();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList openDotMenuByTopicName(String topicName) {
|
||||
getTopicItem(topicName).openDotMenu();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isCopySelectedTopicBtnEnabled() {
|
||||
return isEnabled(copySelectedTopicBtn);
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getActionButtons() {
|
||||
return Stream.of(deleteSelectedTopicsBtn, copySelectedTopicBtn, purgeMessagesOfSelectedTopicsBtn)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickCopySelectedTopicBtn() {
|
||||
copySelectedTopicBtn.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickPurgeMessagesOfSelectedTopicsBtn() {
|
||||
purgeMessagesOfSelectedTopicsBtn.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickClearMessagesBtn() {
|
||||
clickByJavaScript(clearMessagesBtn.shouldBe(visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickRecreateTopicBtn() {
|
||||
clickByJavaScript(recreateTopicBtn.shouldBe(visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickRemoveTopicBtn() {
|
||||
clickByJavaScript(removeTopicBtn.shouldBe(visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickConfirmBtnMdl() {
|
||||
clickConfirmButton();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickCancelBtnMdl() {
|
||||
clickCancelButton();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isConfirmationMdlVisible() {
|
||||
return isConfirmationModalVisible();
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isAlertWithMessageVisible(AlertHeader header, String message) {
|
||||
return isAlertVisible(header, message);
|
||||
}
|
||||
|
||||
private List<SelenideElement> getVisibleColumnHeaders() {
|
||||
return Stream.of("Replication Factor", "Number of messages", "Topic Name", "Partitions", "Out of sync replicas",
|
||||
"Size")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getEnabledColumnHeaders() {
|
||||
return Stream.of("Topic Name", "Partitions", "Out of sync replicas", "Size")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllVisibleElements() {
|
||||
List<SelenideElement> visibleElements = new ArrayList<>(getVisibleColumnHeaders());
|
||||
visibleElements.addAll(Arrays.asList(searchField, addTopicBtn, tableGrid));
|
||||
visibleElements.addAll(getActionButtons());
|
||||
return visibleElements;
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllEnabledElements() {
|
||||
List<SelenideElement> enabledElements = new ArrayList<>(getEnabledColumnHeaders());
|
||||
enabledElements.addAll(Arrays.asList(searchField, showInternalRadioBtn, addTopicBtn));
|
||||
return enabledElements;
|
||||
}
|
||||
|
||||
private List<TopicGridItem> initGridItems() {
|
||||
List<TopicGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new TopicGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicGridItem getTopicItem(String name) {
|
||||
TopicGridItem topicGridItem = initGridItems().stream()
|
||||
.filter(e -> e.getName().equals(name))
|
||||
.findFirst().orElse(null);
|
||||
if (topicGridItem == null) {
|
||||
searchItem(name);
|
||||
topicGridItem = initGridItems().stream()
|
||||
.filter(e -> e.getName().equals(name))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
return topicGridItem;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicGridItem getAnyNonInternalTopic() {
|
||||
return getNonInternalTopics().stream()
|
||||
.findAny().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<TopicGridItem> getNonInternalTopics() {
|
||||
return initGridItems().stream()
|
||||
.filter(e -> !e.isInternal())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<TopicGridItem> getInternalTopics() {
|
||||
return initGridItems().stream()
|
||||
.filter(TopicGridItem::isInternal)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static class TopicGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public TopicGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickAddTopicBtn() {
|
||||
clickByJavaScript(addTopicBtn);
|
||||
return this;
|
||||
public TopicsList selectItem(boolean select) {
|
||||
selectElement(element.$x("./td[1]/input"), select);
|
||||
return new TopicsList();
|
||||
}
|
||||
|
||||
private SelenideElement getNameElm() {
|
||||
return element.$x("./td[2]");
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isTopicVisible(String topicName) {
|
||||
tableGrid.shouldBe(visible);
|
||||
return isVisible(getTableElement(topicName));
|
||||
public boolean isInternal() {
|
||||
boolean internal = false;
|
||||
try {
|
||||
internal = getNameElm().$x("./a/span").isDisplayed();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return internal;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isShowInternalRadioBtnSelected() {
|
||||
return isSelected(showInternalRadioBtn);
|
||||
public String getName() {
|
||||
return getNameElm().$x("./a").getAttribute("title");
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList setShowInternalRadioButton(boolean select) {
|
||||
selectElement(showInternalRadioBtn, select);
|
||||
return this;
|
||||
public void openItem() {
|
||||
getNameElm().click();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList openTopic(String topicName) {
|
||||
getTopicItem(topicName).openItem();
|
||||
return this;
|
||||
public int getPartition() {
|
||||
return Integer.parseInt(element.$x("./td[3]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList openDotMenuByTopicName(String topicName) {
|
||||
getTopicItem(topicName).openDotMenu();
|
||||
return this;
|
||||
public int getOutOfSyncReplicas() {
|
||||
return Integer.parseInt(element.$x("./td[4]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isCopySelectedTopicBtnEnabled() {
|
||||
return isEnabled(copySelectedTopicBtn);
|
||||
public int getReplicationFactor() {
|
||||
return Integer.parseInt(element.$x("./td[5]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getActionButtons() {
|
||||
return Stream.of(deleteSelectedTopicsBtn, copySelectedTopicBtn, purgeMessagesOfSelectedTopicsBtn)
|
||||
.collect(Collectors.toList());
|
||||
public int getNumberOfMessages() {
|
||||
return Integer.parseInt(element.$x("./td[6]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickCopySelectedTopicBtn() {
|
||||
copySelectedTopicBtn.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
public int getSize() {
|
||||
return Integer.parseInt(element.$x("./td[7]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickPurgeMessagesOfSelectedTopicsBtn() {
|
||||
purgeMessagesOfSelectedTopicsBtn.shouldBe(Condition.enabled).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickClearMessagesBtn() {
|
||||
clickByJavaScript(clearMessagesBtn.shouldBe(visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickRecreateTopicBtn() {
|
||||
clickByJavaScript(recreateTopicBtn.shouldBe(visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickRemoveTopicBtn() {
|
||||
clickByJavaScript(removeTopicBtn.shouldBe(visible));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickConfirmBtnMdl() {
|
||||
clickConfirmButton();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList clickCancelBtnMdl() {
|
||||
clickCancelButton();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isConfirmationMdlVisible() {
|
||||
return isConfirmationModalVisible();
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isAlertWithMessageVisible(AlertHeader header, String message) {
|
||||
return isAlertVisible(header, message);
|
||||
}
|
||||
|
||||
private List<SelenideElement> getVisibleColumnHeaders() {
|
||||
return Stream.of("Replication Factor", "Number of messages", "Topic Name", "Partitions", "Out of sync replicas", "Size")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SelenideElement> getEnabledColumnHeaders() {
|
||||
return Stream.of("Topic Name", "Partitions", "Out of sync replicas", "Size")
|
||||
.map(name -> $x(String.format(columnHeaderLocator, name)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllVisibleElements() {
|
||||
List<SelenideElement> visibleElements = new ArrayList<>(getVisibleColumnHeaders());
|
||||
visibleElements.addAll(Arrays.asList(searchField, addTopicBtn, tableGrid));
|
||||
visibleElements.addAll(getActionButtons());
|
||||
return visibleElements;
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<SelenideElement> getAllEnabledElements() {
|
||||
List<SelenideElement> enabledElements = new ArrayList<>(getEnabledColumnHeaders());
|
||||
enabledElements.addAll(Arrays.asList(searchField, showInternalRadioBtn, addTopicBtn));
|
||||
return enabledElements;
|
||||
}
|
||||
|
||||
private List<TopicGridItem> initGridItems() {
|
||||
List<TopicGridItem> gridItemList = new ArrayList<>();
|
||||
gridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
|
||||
.forEach(item -> gridItemList.add(new TopicGridItem(item)));
|
||||
return gridItemList;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicGridItem getTopicItem(String name) {
|
||||
return initGridItems().stream()
|
||||
.filter(e -> e.getName().equals(name))
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicGridItem getAnyNonInternalTopic() {
|
||||
return getNonInternalTopics().stream()
|
||||
.findAny().orElseThrow();
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<TopicGridItem> getNonInternalTopics() {
|
||||
return initGridItems().stream()
|
||||
.filter(e -> !e.isInternal())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Step
|
||||
public List<TopicGridItem> getInternalTopics() {
|
||||
return initGridItems().stream()
|
||||
.filter(TopicGridItem::isInternal)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static class TopicGridItem extends BasePage {
|
||||
|
||||
private final SelenideElement element;
|
||||
|
||||
public TopicGridItem(SelenideElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Step
|
||||
public TopicsList selectItem(boolean select) {
|
||||
selectElement(element.$x("./td[1]/input"), select);
|
||||
return new TopicsList();
|
||||
}
|
||||
|
||||
private SelenideElement getNameElm() {
|
||||
return element.$x("./td[2]");
|
||||
}
|
||||
|
||||
@Step
|
||||
public boolean isInternal() {
|
||||
boolean internal = false;
|
||||
try {
|
||||
internal = getNameElm().$x("./a/span").isDisplayed();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return internal;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getName() {
|
||||
return getNameElm().$x("./a").getAttribute("title");
|
||||
}
|
||||
|
||||
@Step
|
||||
public void openItem() {
|
||||
getNameElm().click();
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getPartition() {
|
||||
return Integer.parseInt(element.$x("./td[3]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getOutOfSyncReplicas() {
|
||||
return Integer.parseInt(element.$x("./td[4]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getReplicationFactor() {
|
||||
return Integer.parseInt(element.$x("./td[5]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getNumberOfMessages() {
|
||||
return Integer.parseInt(element.$x("./td[6]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public int getSize() {
|
||||
return Integer.parseInt(element.$x("./td[7]").getText().trim());
|
||||
}
|
||||
|
||||
@Step
|
||||
public void openDotMenu() {
|
||||
element.$x("./td[8]//button").click();
|
||||
}
|
||||
public void openDotMenu() {
|
||||
element.$x("./td[8]//button").click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,24 +2,24 @@ package com.provectus.kafka.ui.pages.topics.enums;
|
|||
|
||||
public enum CleanupPolicyValue {
|
||||
|
||||
DELETE("delete", "Delete"),
|
||||
COMPACT("compact", "Compact"),
|
||||
COMPACT_DELETE("compact,delete", "Compact,Delete");
|
||||
DELETE("delete", "Delete"),
|
||||
COMPACT("compact", "Compact"),
|
||||
COMPACT_DELETE("compact,delete", "Compact,Delete");
|
||||
|
||||
private final String optionValue;
|
||||
private final String visibleText;
|
||||
private final String optionValue;
|
||||
private final String visibleText;
|
||||
|
||||
CleanupPolicyValue(String optionValue, String visibleText) {
|
||||
this.optionValue = optionValue;
|
||||
this.visibleText = visibleText;
|
||||
}
|
||||
CleanupPolicyValue(String optionValue, String visibleText) {
|
||||
this.optionValue = optionValue;
|
||||
this.visibleText = visibleText;
|
||||
}
|
||||
|
||||
public String getOptionValue() {
|
||||
return optionValue;
|
||||
}
|
||||
public String getOptionValue() {
|
||||
return optionValue;
|
||||
}
|
||||
|
||||
public String getVisibleText() {
|
||||
return visibleText;
|
||||
}
|
||||
public String getVisibleText() {
|
||||
return visibleText;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,36 +2,36 @@ package com.provectus.kafka.ui.pages.topics.enums;
|
|||
|
||||
public enum CustomParameterType {
|
||||
|
||||
COMPRESSION_TYPE("compression.type"),
|
||||
DELETE_RETENTION_MS("delete.retention.ms"),
|
||||
FILE_DELETE_DELAY_MS("file.delete.delay.ms"),
|
||||
FLUSH_MESSAGES("flush.messages"),
|
||||
FLUSH_MS("flush.ms"),
|
||||
FOLLOWER_REPLICATION_THROTTLED_REPLICAS("follower.replication.throttled.replicas"),
|
||||
INDEX_INTERVAL_BYTES("index.interval.bytes"),
|
||||
LEADER_REPLICATION_THROTTLED_REPLICAS("leader.replication.throttled.replicas"),
|
||||
MAX_COMPACTION_LAG_MS("max.compaction.lag.ms"),
|
||||
MESSAGE_DOWNCONVERSION_ENABLE("message.downconversion.enable"),
|
||||
MESSAGE_FORMAT_VERSION("message.format.version"),
|
||||
MESSAGE_TIMESTAMP_DIFFERENCE_MAX_MS("message.timestamp.difference.max.ms"),
|
||||
MESSAGE_TIMESTAMP_TYPE("message.timestamp.type"),
|
||||
MIN_CLEANABLE_DIRTY_RATIO("min.cleanable.dirty.ratio"),
|
||||
MIN_COMPACTION_LAG_MS("min.compaction.lag.ms"),
|
||||
PREALLOCATE("preallocate"),
|
||||
RETENTION_BYTES("retention.bytes"),
|
||||
SEGMENT_BYTES("segment.bytes"),
|
||||
SEGMENT_INDEX_BYTES("segment.index.bytes"),
|
||||
SEGMENT_JITTER_MS("segment.jitter.ms"),
|
||||
SEGMENT_MS("segment.ms"),
|
||||
UNCLEAN_LEADER_ELECTION_ENABLE("unclean.leader.election.enable");
|
||||
COMPRESSION_TYPE("compression.type"),
|
||||
DELETE_RETENTION_MS("delete.retention.ms"),
|
||||
FILE_DELETE_DELAY_MS("file.delete.delay.ms"),
|
||||
FLUSH_MESSAGES("flush.messages"),
|
||||
FLUSH_MS("flush.ms"),
|
||||
FOLLOWER_REPLICATION_THROTTLED_REPLICAS("follower.replication.throttled.replicas"),
|
||||
INDEX_INTERVAL_BYTES("index.interval.bytes"),
|
||||
LEADER_REPLICATION_THROTTLED_REPLICAS("leader.replication.throttled.replicas"),
|
||||
MAX_COMPACTION_LAG_MS("max.compaction.lag.ms"),
|
||||
MESSAGE_DOWNCONVERSION_ENABLE("message.downconversion.enable"),
|
||||
MESSAGE_FORMAT_VERSION("message.format.version"),
|
||||
MESSAGE_TIMESTAMP_DIFFERENCE_MAX_MS("message.timestamp.difference.max.ms"),
|
||||
MESSAGE_TIMESTAMP_TYPE("message.timestamp.type"),
|
||||
MIN_CLEANABLE_DIRTY_RATIO("min.cleanable.dirty.ratio"),
|
||||
MIN_COMPACTION_LAG_MS("min.compaction.lag.ms"),
|
||||
PREALLOCATE("preallocate"),
|
||||
RETENTION_BYTES("retention.bytes"),
|
||||
SEGMENT_BYTES("segment.bytes"),
|
||||
SEGMENT_INDEX_BYTES("segment.index.bytes"),
|
||||
SEGMENT_JITTER_MS("segment.jitter.ms"),
|
||||
SEGMENT_MS("segment.ms"),
|
||||
UNCLEAN_LEADER_ELECTION_ENABLE("unclean.leader.election.enable");
|
||||
|
||||
private final String optionValue;
|
||||
private final String optionValue;
|
||||
|
||||
CustomParameterType(String optionValue) {
|
||||
this.optionValue = optionValue;
|
||||
}
|
||||
CustomParameterType(String optionValue) {
|
||||
this.optionValue = optionValue;
|
||||
}
|
||||
|
||||
public String getOptionValue() {
|
||||
return optionValue;
|
||||
}
|
||||
public String getOptionValue() {
|
||||
return optionValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,26 +2,26 @@ package com.provectus.kafka.ui.pages.topics.enums;
|
|||
|
||||
public enum MaxSizeOnDisk {
|
||||
|
||||
NOT_SET("-1", "Not Set"),
|
||||
SIZE_1_GB("1073741824", "1 GB"),
|
||||
SIZE_10_GB("10737418240", "10 GB"),
|
||||
SIZE_20_GB("21474836480", "20 GB"),
|
||||
SIZE_50_GB("53687091200", "50 GB");
|
||||
NOT_SET("-1", "Not Set"),
|
||||
SIZE_1_GB("1073741824", "1 GB"),
|
||||
SIZE_10_GB("10737418240", "10 GB"),
|
||||
SIZE_20_GB("21474836480", "20 GB"),
|
||||
SIZE_50_GB("53687091200", "50 GB");
|
||||
|
||||
private final String optionValue;
|
||||
private final String visibleText;
|
||||
private final String optionValue;
|
||||
private final String visibleText;
|
||||
|
||||
MaxSizeOnDisk(String optionValue, String visibleText) {
|
||||
this.optionValue = optionValue;
|
||||
this.visibleText = visibleText;
|
||||
}
|
||||
MaxSizeOnDisk(String optionValue, String visibleText) {
|
||||
this.optionValue = optionValue;
|
||||
this.visibleText = visibleText;
|
||||
}
|
||||
|
||||
public String getOptionValue() {
|
||||
return optionValue;
|
||||
}
|
||||
public String getOptionValue() {
|
||||
return optionValue;
|
||||
}
|
||||
|
||||
public String getVisibleText() {
|
||||
return visibleText;
|
||||
}
|
||||
public String getVisibleText() {
|
||||
return visibleText;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,25 +2,25 @@ package com.provectus.kafka.ui.pages.topics.enums;
|
|||
|
||||
public enum TimeToRetain {
|
||||
|
||||
BTN_12_HOURS("12 hours", "43200000"),
|
||||
BTN_1_DAY("1 day", "86400000"),
|
||||
BTN_2_DAYS("2 days", "172800000"),
|
||||
BTN_7_DAYS("7 days", "604800000"),
|
||||
BTN_4_WEEKS("4 weeks", "2419200000");
|
||||
BTN_12_HOURS("12 hours", "43200000"),
|
||||
BTN_1_DAY("1 day", "86400000"),
|
||||
BTN_2_DAYS("2 days", "172800000"),
|
||||
BTN_7_DAYS("7 days", "604800000"),
|
||||
BTN_4_WEEKS("4 weeks", "2419200000");
|
||||
|
||||
private final String button;
|
||||
private final String value;
|
||||
private final String button;
|
||||
private final String value;
|
||||
|
||||
TimeToRetain(String button, String value) {
|
||||
this.button = button;
|
||||
this.value = value;
|
||||
}
|
||||
TimeToRetain(String button, String value) {
|
||||
this.button = button;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getButton() {
|
||||
return button;
|
||||
}
|
||||
public String getButton() {
|
||||
return button;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,272 +1,282 @@
|
|||
package com.provectus.kafka.ui.services;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.sleep;
|
||||
import static com.provectus.kafka.ui.utilities.FileUtils.fileToString;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.provectus.kafka.ui.api.ApiClient;
|
||||
import com.provectus.kafka.ui.api.api.*;
|
||||
import com.provectus.kafka.ui.api.model.*;
|
||||
import com.provectus.kafka.ui.api.api.KafkaConnectApi;
|
||||
import com.provectus.kafka.ui.api.api.KsqlApi;
|
||||
import com.provectus.kafka.ui.api.api.MessagesApi;
|
||||
import com.provectus.kafka.ui.api.api.SchemasApi;
|
||||
import com.provectus.kafka.ui.api.api.TopicsApi;
|
||||
import com.provectus.kafka.ui.api.model.CreateTopicMessage;
|
||||
import com.provectus.kafka.ui.api.model.KsqlCommandV2;
|
||||
import com.provectus.kafka.ui.api.model.KsqlCommandV2Response;
|
||||
import com.provectus.kafka.ui.api.model.KsqlResponse;
|
||||
import com.provectus.kafka.ui.api.model.NewConnector;
|
||||
import com.provectus.kafka.ui.api.model.NewSchemaSubject;
|
||||
import com.provectus.kafka.ui.api.model.TopicCreation;
|
||||
import com.provectus.kafka.ui.models.Connector;
|
||||
import com.provectus.kafka.ui.models.Schema;
|
||||
import com.provectus.kafka.ui.models.Topic;
|
||||
import com.provectus.kafka.ui.pages.ksqlDb.models.Stream;
|
||||
import com.provectus.kafka.ui.pages.ksqlDb.models.Table;
|
||||
import com.provectus.kafka.ui.pages.ksqldb.models.Stream;
|
||||
import com.provectus.kafka.ui.pages.ksqldb.models.Table;
|
||||
import com.provectus.kafka.ui.settings.BaseSource;
|
||||
import io.qameta.allure.Step;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.sleep;
|
||||
import static com.provectus.kafka.ui.utilities.FileUtils.fileToString;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
|
||||
|
||||
@Slf4j
|
||||
public class ApiService extends BaseSource {
|
||||
|
||||
@SneakyThrows
|
||||
private TopicsApi topicApi() {
|
||||
return new TopicsApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
|
||||
}
|
||||
@SneakyThrows
|
||||
private TopicsApi topicApi() {
|
||||
return new TopicsApi(new ApiClient().setBasePath(BASE_API_URL));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private SchemasApi schemaApi() {
|
||||
return new SchemasApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
|
||||
}
|
||||
@SneakyThrows
|
||||
private SchemasApi schemaApi() {
|
||||
return new SchemasApi(new ApiClient().setBasePath(BASE_API_URL));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private KafkaConnectApi connectorApi() {
|
||||
return new KafkaConnectApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
|
||||
}
|
||||
@SneakyThrows
|
||||
private KafkaConnectApi connectorApi() {
|
||||
return new KafkaConnectApi(new ApiClient().setBasePath(BASE_API_URL));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private MessagesApi messageApi() {
|
||||
return new MessagesApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
|
||||
}
|
||||
@SneakyThrows
|
||||
private MessagesApi messageApi() {
|
||||
return new MessagesApi(new ApiClient().setBasePath(BASE_API_URL));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private KsqlApi ksqlApi() {
|
||||
return new KsqlApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
|
||||
}
|
||||
@SneakyThrows
|
||||
private KsqlApi ksqlApi() {
|
||||
return new KsqlApi(new ApiClient().setBasePath(BASE_API_URL));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void createTopic(String clusterName, String topicName) {
|
||||
TopicCreation topic = new TopicCreation();
|
||||
topic.setName(topicName);
|
||||
topic.setPartitions(1);
|
||||
topic.setReplicationFactor(1);
|
||||
try {
|
||||
topicApi().createTopic(clusterName, topic).block();
|
||||
sleep(2000);
|
||||
} catch (WebClientResponseException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
@SneakyThrows
|
||||
private void createTopic(String clusterName, String topicName) {
|
||||
TopicCreation topic = new TopicCreation();
|
||||
topic.setName(topicName);
|
||||
topic.setPartitions(1);
|
||||
topic.setReplicationFactor(1);
|
||||
try {
|
||||
topicApi().createTopic(clusterName, topic).block();
|
||||
sleep(2000);
|
||||
} catch (WebClientResponseException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService createTopic(Topic topic) {
|
||||
createTopic(CLUSTER_NAME, topic.getName());
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService createTopic(Topic topic) {
|
||||
createTopic(CLUSTER_NAME, topic.getName());
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void deleteTopic(String clusterName, String topicName) {
|
||||
try {
|
||||
topicApi().deleteTopic(clusterName, topicName).block();
|
||||
} catch (WebClientResponseException ignore) {
|
||||
}
|
||||
@SneakyThrows
|
||||
private void deleteTopic(String clusterName, String topicName) {
|
||||
try {
|
||||
topicApi().deleteTopic(clusterName, topicName).block();
|
||||
} catch (WebClientResponseException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService deleteTopic(String topicName) {
|
||||
deleteTopic(CLUSTER_NAME, topicName);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService deleteTopic(String topicName) {
|
||||
deleteTopic(CLUSTER_NAME, topicName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void createSchema(String clusterName, Schema schema) {
|
||||
NewSchemaSubject schemaSubject = new NewSchemaSubject();
|
||||
schemaSubject.setSubject(schema.getName());
|
||||
schemaSubject.setSchema(fileToString(schema.getValuePath()));
|
||||
schemaSubject.setSchemaType(schema.getType());
|
||||
try {
|
||||
schemaApi().createNewSchema(clusterName, schemaSubject).block();
|
||||
} catch (WebClientResponseException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
@SneakyThrows
|
||||
private void createSchema(String clusterName, Schema schema) {
|
||||
NewSchemaSubject schemaSubject = new NewSchemaSubject();
|
||||
schemaSubject.setSubject(schema.getName());
|
||||
schemaSubject.setSchema(fileToString(schema.getValuePath()));
|
||||
schemaSubject.setSchemaType(schema.getType());
|
||||
try {
|
||||
schemaApi().createNewSchema(clusterName, schemaSubject).block();
|
||||
} catch (WebClientResponseException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService createSchema(Schema schema) {
|
||||
createSchema(CLUSTER_NAME, schema);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService createSchema(Schema schema) {
|
||||
createSchema(CLUSTER_NAME, schema);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void deleteSchema(String clusterName, String schemaName) {
|
||||
try {
|
||||
schemaApi().deleteSchema(clusterName, schemaName).block();
|
||||
} catch (WebClientResponseException ignore) {
|
||||
}
|
||||
@SneakyThrows
|
||||
private void deleteSchema(String clusterName, String schemaName) {
|
||||
try {
|
||||
schemaApi().deleteSchema(clusterName, schemaName).block();
|
||||
} catch (WebClientResponseException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService deleteSchema(String schemaName) {
|
||||
deleteSchema(CLUSTER_NAME, schemaName);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService deleteSchema(String schemaName) {
|
||||
deleteSchema(CLUSTER_NAME, schemaName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void deleteConnector(String clusterName, String connectName, String connectorName) {
|
||||
try {
|
||||
connectorApi().deleteConnector(clusterName, connectName, connectorName).block();
|
||||
} catch (WebClientResponseException ignore) {
|
||||
}
|
||||
@SneakyThrows
|
||||
private void deleteConnector(String clusterName, String connectName, String connectorName) {
|
||||
try {
|
||||
connectorApi().deleteConnector(clusterName, connectName, connectorName).block();
|
||||
} catch (WebClientResponseException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService deleteConnector(String connectName, String connectorName) {
|
||||
deleteConnector(CLUSTER_NAME, connectName, connectorName);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService deleteConnector(String connectName, String connectorName) {
|
||||
deleteConnector(CLUSTER_NAME, connectName, connectorName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService deleteConnector(String connectorName) {
|
||||
deleteConnector(CLUSTER_NAME, CONNECT_NAME, connectorName);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService deleteConnector(String connectorName) {
|
||||
deleteConnector(CLUSTER_NAME, CONNECT_NAME, connectorName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void createConnector(String clusterName, String connectName, Connector connector) {
|
||||
NewConnector connectorProperties = new NewConnector();
|
||||
connectorProperties.setName(connector.getName());
|
||||
Map<String, Object> configMap = new ObjectMapper().readValue(connector.getConfig(), HashMap.class);
|
||||
connectorProperties.setConfig(configMap);
|
||||
try {
|
||||
connectorApi().deleteConnector(clusterName, connectName, connector.getName()).block();
|
||||
} catch (WebClientResponseException ignored) {
|
||||
}
|
||||
connectorApi().createConnector(clusterName, connectName, connectorProperties).block();
|
||||
@SneakyThrows
|
||||
private void createConnector(String clusterName, String connectName, Connector connector) {
|
||||
NewConnector connectorProperties = new NewConnector();
|
||||
connectorProperties.setName(connector.getName());
|
||||
Map<String, Object> configMap = new ObjectMapper().readValue(connector.getConfig(), HashMap.class);
|
||||
connectorProperties.setConfig(configMap);
|
||||
try {
|
||||
connectorApi().deleteConnector(clusterName, connectName, connector.getName()).block();
|
||||
} catch (WebClientResponseException ignored) {
|
||||
}
|
||||
connectorApi().createConnector(clusterName, connectName, connectorProperties).block();
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService createConnector(String connectName, Connector connector) {
|
||||
createConnector(CLUSTER_NAME, connectName, connector);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService createConnector(String connectName, Connector connector) {
|
||||
createConnector(CLUSTER_NAME, connectName, connector);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService createConnector(Connector connector) {
|
||||
createConnector(CLUSTER_NAME, CONNECT_NAME, connector);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService createConnector(Connector connector) {
|
||||
createConnector(CLUSTER_NAME, CONNECT_NAME, connector);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public String getFirstConnectName(String clusterName) {
|
||||
return Objects.requireNonNull(connectorApi().getConnects(clusterName).blockFirst()).getName();
|
||||
}
|
||||
@Step
|
||||
public String getFirstConnectName(String clusterName) {
|
||||
return Objects.requireNonNull(connectorApi().getConnects(clusterName).blockFirst()).getName();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private void sendMessage(String clusterName, Topic topic) {
|
||||
CreateTopicMessage createMessage = new CreateTopicMessage();
|
||||
createMessage.setPartition(0);
|
||||
createMessage.setKeySerde("String");
|
||||
createMessage.setValueSerde("String");
|
||||
createMessage.setKey(topic.getMessageKey());
|
||||
createMessage.setContent(topic.getMessageContent());
|
||||
try {
|
||||
messageApi().sendTopicMessages(clusterName, topic.getName(), createMessage).block();
|
||||
} catch (WebClientResponseException ex) {
|
||||
ex.getRawStatusCode();
|
||||
}
|
||||
@SneakyThrows
|
||||
private void sendMessage(String clusterName, Topic topic) {
|
||||
CreateTopicMessage createMessage = new CreateTopicMessage();
|
||||
createMessage.setPartition(0);
|
||||
createMessage.setKeySerde("String");
|
||||
createMessage.setValueSerde("String");
|
||||
createMessage.setKey(topic.getMessageKey());
|
||||
createMessage.setContent(topic.getMessageValue());
|
||||
try {
|
||||
messageApi().sendTopicMessages(clusterName, topic.getName(), createMessage).block();
|
||||
} catch (WebClientResponseException ex) {
|
||||
ex.getRawStatusCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService sendMessage(Topic topic) {
|
||||
sendMessage(CLUSTER_NAME, topic);
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService sendMessage(Topic topic) {
|
||||
sendMessage(CLUSTER_NAME, topic);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService createStream(Stream stream) {
|
||||
KsqlCommandV2Response pipeIdStream = ksqlApi()
|
||||
.executeKsql(CLUSTER_NAME, new KsqlCommandV2()
|
||||
.ksql(String.format("CREATE STREAM %s (profileId VARCHAR, latitude DOUBLE, longitude DOUBLE) ",
|
||||
stream.getName())
|
||||
+ String.format("WITH (kafka_topic='%s', value_format='json', partitions=1);",
|
||||
stream.getTopicName())))
|
||||
.block();
|
||||
assert pipeIdStream != null;
|
||||
List<KsqlResponse> responseListStream = ksqlApi()
|
||||
.openKsqlResponsePipe(CLUSTER_NAME, pipeIdStream.getPipeId())
|
||||
.collectList()
|
||||
.block();
|
||||
assert Objects.requireNonNull(responseListStream).size() != 0;
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService createStream(Stream stream) {
|
||||
KsqlCommandV2Response pipeIdStream = ksqlApi()
|
||||
.executeKsql(CLUSTER_NAME, new KsqlCommandV2()
|
||||
.ksql(String.format("CREATE STREAM %s (profileId VARCHAR, latitude DOUBLE, longitude DOUBLE) ",
|
||||
stream.getName())
|
||||
+ String.format("WITH (kafka_topic='%s', value_format='json', partitions=1);",
|
||||
stream.getTopicName())))
|
||||
.block();
|
||||
assert pipeIdStream != null;
|
||||
List<KsqlResponse> responseListStream = ksqlApi()
|
||||
.openKsqlResponsePipe(CLUSTER_NAME, pipeIdStream.getPipeId())
|
||||
.collectList()
|
||||
.block();
|
||||
assert Objects.requireNonNull(responseListStream).size() != 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService createTables(Table firstTable, Table secondTable) {
|
||||
KsqlCommandV2Response pipeIdTable1 = ksqlApi()
|
||||
.executeKsql(CLUSTER_NAME, new KsqlCommandV2()
|
||||
.ksql(String.format("CREATE TABLE %s AS ", firstTable.getName())
|
||||
+ " SELECT profileId, "
|
||||
+ " LATEST_BY_OFFSET(latitude) AS la, "
|
||||
+ " LATEST_BY_OFFSET(longitude) AS lo "
|
||||
+ String.format(" FROM %s ", firstTable.getStreamName())
|
||||
+ " GROUP BY profileId "
|
||||
+ " EMIT CHANGES;"))
|
||||
.block();
|
||||
assert pipeIdTable1 != null;
|
||||
List<KsqlResponse> responseListTable = ksqlApi()
|
||||
.openKsqlResponsePipe(CLUSTER_NAME, pipeIdTable1.getPipeId())
|
||||
.collectList()
|
||||
.block();
|
||||
assert Objects.requireNonNull(responseListTable).size() != 0;
|
||||
KsqlCommandV2Response pipeIdTable2 = ksqlApi()
|
||||
.executeKsql(CLUSTER_NAME, new KsqlCommandV2()
|
||||
.ksql(String.format("CREATE TABLE %s AS ", secondTable.getName())
|
||||
+ " SELECT ROUND(GEO_DISTANCE(la, lo, 37.4133, -122.1162), -1) AS distanceInMiles, "
|
||||
+ " COLLECT_LIST(profileId) AS riders, "
|
||||
+ " COUNT(*) AS count "
|
||||
+ String.format(" FROM %s ", firstTable.getName())
|
||||
+ " GROUP BY ROUND(GEO_DISTANCE(la, lo, 37.4133, -122.1162), -1);"))
|
||||
.block();
|
||||
assert pipeIdTable2 != null;
|
||||
List<KsqlResponse> responseListTable2 = ksqlApi()
|
||||
.openKsqlResponsePipe(CLUSTER_NAME, pipeIdTable2.getPipeId())
|
||||
.collectList()
|
||||
.block();
|
||||
assert Objects.requireNonNull(responseListTable2).size() != 0;
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService createTables(Table firstTable, Table secondTable) {
|
||||
KsqlCommandV2Response pipeIdTable1 = ksqlApi()
|
||||
.executeKsql(CLUSTER_NAME, new KsqlCommandV2()
|
||||
.ksql(String.format("CREATE TABLE %s AS ", firstTable.getName())
|
||||
+ " SELECT profileId, "
|
||||
+ " LATEST_BY_OFFSET(latitude) AS la, "
|
||||
+ " LATEST_BY_OFFSET(longitude) AS lo "
|
||||
+ String.format(" FROM %s ", firstTable.getStreamName())
|
||||
+ " GROUP BY profileId "
|
||||
+ " EMIT CHANGES;"))
|
||||
.block();
|
||||
assert pipeIdTable1 != null;
|
||||
List<KsqlResponse> responseListTable = ksqlApi()
|
||||
.openKsqlResponsePipe(CLUSTER_NAME, pipeIdTable1.getPipeId())
|
||||
.collectList()
|
||||
.block();
|
||||
assert Objects.requireNonNull(responseListTable).size() != 0;
|
||||
KsqlCommandV2Response pipeIdTable2 = ksqlApi()
|
||||
.executeKsql(CLUSTER_NAME, new KsqlCommandV2()
|
||||
.ksql(String.format("CREATE TABLE %s AS ", secondTable.getName())
|
||||
+ " SELECT ROUND(GEO_DISTANCE(la, lo, 37.4133, -122.1162), -1) AS distanceInMiles, "
|
||||
+ " COLLECT_LIST(profileId) AS riders, "
|
||||
+ " COUNT(*) AS count "
|
||||
+ String.format(" FROM %s ", firstTable.getName())
|
||||
+ " GROUP BY ROUND(GEO_DISTANCE(la, lo, 37.4133, -122.1162), -1);"))
|
||||
.block();
|
||||
assert pipeIdTable2 != null;
|
||||
List<KsqlResponse> responseListTable2 = ksqlApi()
|
||||
.openKsqlResponsePipe(CLUSTER_NAME, pipeIdTable2.getPipeId())
|
||||
.collectList()
|
||||
.block();
|
||||
assert Objects.requireNonNull(responseListTable2).size() != 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Step
|
||||
public ApiService insertInto(Stream stream) {
|
||||
String streamName = stream.getName();
|
||||
KsqlCommandV2Response pipeIdInsert = ksqlApi()
|
||||
.executeKsql(CLUSTER_NAME, new KsqlCommandV2()
|
||||
.ksql("INSERT INTO " + streamName + " (profileId, latitude, longitude) VALUES ('c2309eec', 37.7877, -122.4205);"
|
||||
+ "INSERT INTO " + streamName +
|
||||
" (profileId, latitude, longitude) VALUES ('18f4ea86', 37.3903, -122.0643); "
|
||||
+ "INSERT INTO " + streamName +
|
||||
" (profileId, latitude, longitude) VALUES ('4ab5cbad', 37.3952, -122.0813); "
|
||||
+ "INSERT INTO " + streamName +
|
||||
" (profileId, latitude, longitude) VALUES ('8b6eae59', 37.3944, -122.0813); "
|
||||
+ "INSERT INTO " + streamName +
|
||||
" (profileId, latitude, longitude) VALUES ('4a7c7b41', 37.4049, -122.0822); "
|
||||
+ "INSERT INTO " + streamName +
|
||||
" (profileId, latitude, longitude) VALUES ('4ddad000', 37.7857, -122.4011);"))
|
||||
.block();
|
||||
assert pipeIdInsert != null;
|
||||
List<KsqlResponse> responseListInsert = ksqlApi()
|
||||
.openKsqlResponsePipe(CLUSTER_NAME, pipeIdInsert.getPipeId())
|
||||
.collectList()
|
||||
.block();
|
||||
assert Objects.requireNonNull(responseListInsert).size() != 0;
|
||||
return this;
|
||||
}
|
||||
@Step
|
||||
public ApiService insertInto(Stream stream) {
|
||||
String streamName = stream.getName();
|
||||
KsqlCommandV2Response pipeIdInsert = ksqlApi()
|
||||
.executeKsql(CLUSTER_NAME, new KsqlCommandV2()
|
||||
.ksql("INSERT INTO " + streamName
|
||||
+ " (profileId, latitude, longitude) VALUES ('c2309eec', 37.7877, -122.4205);"
|
||||
+ "INSERT INTO " + streamName
|
||||
+ " (profileId, latitude, longitude) VALUES ('18f4ea86', 37.3903, -122.0643); "
|
||||
+ "INSERT INTO " + streamName
|
||||
+ " (profileId, latitude, longitude) VALUES ('4ab5cbad', 37.3952, -122.0813); "
|
||||
+ "INSERT INTO " + streamName
|
||||
+ " (profileId, latitude, longitude) VALUES ('8b6eae59', 37.3944, -122.0813); "
|
||||
+ "INSERT INTO " + streamName
|
||||
+ " (profileId, latitude, longitude) VALUES ('4a7c7b41', 37.4049, -122.0822); "
|
||||
+ "INSERT INTO " + streamName
|
||||
+ " (profileId, latitude, longitude) VALUES ('4ddad000', 37.7857, -122.4011);"))
|
||||
.block();
|
||||
assert pipeIdInsert != null;
|
||||
List<KsqlResponse> responseListInsert = ksqlApi()
|
||||
.openKsqlResponsePipe(CLUSTER_NAME, pipeIdInsert.getPipeId())
|
||||
.collectList()
|
||||
.block();
|
||||
assert Objects.requireNonNull(responseListInsert).size() != 0;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,29 @@
|
|||
package com.provectus.kafka.ui.settings;
|
||||
|
||||
import static com.provectus.kafka.ui.variables.Browser.LOCAL;
|
||||
|
||||
import com.provectus.kafka.ui.settings.configs.Config;
|
||||
import org.aeonbits.owner.ConfigFactory;
|
||||
|
||||
public abstract class BaseSource {
|
||||
|
||||
public static final String BASE_CONTAINER_URL = "http://host.testcontainers.internal:8080";
|
||||
public static final String BASE_LOCAL_URL = "http://localhost:8080";
|
||||
public static final String CLUSTER_NAME = "local";
|
||||
public static final String CONNECT_NAME = "first";
|
||||
private static Config config;
|
||||
public static final String BROWSER = config().browser();
|
||||
public static final String SUITE_NAME = config().suite();
|
||||
public static final String CLUSTER_NAME = "local";
|
||||
public static final String CONNECT_NAME = "first";
|
||||
private static final String LOCAL_HOST = "localhost";
|
||||
public static final String REMOTE_URL = String.format("http://%s:4444/wd/hub", LOCAL_HOST);
|
||||
public static final String BASE_API_URL = String.format("http://%s:8080", LOCAL_HOST);
|
||||
private static Config config;
|
||||
public static final String BROWSER = config().browser();
|
||||
public static final String BASE_HOST = BROWSER.equals(LOCAL)
|
||||
? LOCAL_HOST
|
||||
: "host.docker.internal";
|
||||
public static final String BASE_UI_URL = String.format("http://%s:8080", BASE_HOST);
|
||||
public static final String SUITE_NAME = config().suite();
|
||||
|
||||
private static Config config() {
|
||||
if (config == null) {
|
||||
config = ConfigFactory.create(Config.class, System.getProperties());
|
||||
}
|
||||
return config;
|
||||
private static Config config() {
|
||||
if (config == null) {
|
||||
config = ConfigFactory.create(Config.class, System.getProperties());
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package com.provectus.kafka.ui.settings.configs;
|
||||
|
||||
import org.aeonbits.owner.Config;
|
||||
|
||||
import static com.provectus.kafka.ui.variables.Browser.CONTAINER;
|
||||
import static com.provectus.kafka.ui.variables.Suite.CUSTOM;
|
||||
|
||||
import org.aeonbits.owner.Config;
|
||||
|
||||
public interface Profiles extends Config {
|
||||
|
||||
@Key("browser")
|
||||
@DefaultValue(CONTAINER)
|
||||
String browser();
|
||||
@Key("browser")
|
||||
@DefaultValue(CONTAINER)
|
||||
String browser();
|
||||
|
||||
@Key("suite")
|
||||
@DefaultValue(CUSTOM)
|
||||
String suite();
|
||||
@Key("suite")
|
||||
@DefaultValue(CUSTOM)
|
||||
String suite();
|
||||
}
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
package com.provectus.kafka.ui.settings.drivers;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.clearBrowserCookies;
|
||||
import static com.codeborne.selenide.Selenide.clearBrowserLocalStorage;
|
||||
import static com.codeborne.selenide.Selenide.open;
|
||||
import static com.codeborne.selenide.Selenide.refresh;
|
||||
|
||||
import com.codeborne.selenide.Configuration;
|
||||
import com.codeborne.selenide.WebDriverRunner;
|
||||
import com.codeborne.selenide.logevents.SelenideLogger;
|
||||
import io.qameta.allure.Step;
|
||||
import io.qameta.allure.selenide.AllureSelenide;
|
||||
import org.openqa.selenium.chrome.ChromeOptions;
|
||||
|
||||
public abstract class LocalWebDriver {
|
||||
|
||||
private static org.openqa.selenium.WebDriver getWebDriver() {
|
||||
try {
|
||||
return WebDriverRunner.getWebDriver();
|
||||
} catch (IllegalStateException ex) {
|
||||
Configuration.headless = false;
|
||||
Configuration.browser = "chrome";
|
||||
Configuration.browserSize = "1920x1080";
|
||||
/**screenshots and savePageSource config is needed for local debug
|
||||
* optionally can be set as 'false' to not duplicate Allure report
|
||||
*/
|
||||
Configuration.screenshots = true;
|
||||
Configuration.savePageSource = true;
|
||||
Configuration.pageLoadTimeout = 120000;
|
||||
Configuration.browserCapabilities = new ChromeOptions()
|
||||
.addArguments("--remote-allow-origins=*")
|
||||
.addArguments("--lang=en_US");
|
||||
open();
|
||||
return WebDriverRunner.getWebDriver();
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void openUrl(String url) {
|
||||
if (!getWebDriver().getCurrentUrl().equals(url)) {
|
||||
getWebDriver().get(url);
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void browserInit() {
|
||||
getWebDriver();
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void browserClear() {
|
||||
clearBrowserLocalStorage();
|
||||
clearBrowserCookies();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void browserQuit() {
|
||||
getWebDriver().quit();
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void loggerSetup() {
|
||||
SelenideLogger.addListener("AllureSelenide", new AllureSelenide()
|
||||
.screenshots(true)
|
||||
.savePageSource(false));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package com.provectus.kafka.ui.settings.drivers;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.clearBrowserCookies;
|
||||
import static com.codeborne.selenide.Selenide.clearBrowserLocalStorage;
|
||||
import static com.codeborne.selenide.Selenide.refresh;
|
||||
import static com.provectus.kafka.ui.settings.BaseSource.BROWSER;
|
||||
import static com.provectus.kafka.ui.settings.BaseSource.REMOTE_URL;
|
||||
import static com.provectus.kafka.ui.variables.Browser.CONTAINER;
|
||||
import static com.provectus.kafka.ui.variables.Browser.LOCAL;
|
||||
|
||||
import com.codeborne.selenide.Configuration;
|
||||
import com.codeborne.selenide.Selenide;
|
||||
import com.codeborne.selenide.WebDriverRunner;
|
||||
import com.codeborne.selenide.logevents.SelenideLogger;
|
||||
import io.qameta.allure.Step;
|
||||
import io.qameta.allure.selenide.AllureSelenide;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openqa.selenium.chrome.ChromeOptions;
|
||||
import org.openqa.selenium.remote.DesiredCapabilities;
|
||||
|
||||
@Slf4j
|
||||
public abstract class WebDriver {
|
||||
|
||||
@Step
|
||||
public static void browserSetup() {
|
||||
Configuration.headless = false;
|
||||
Configuration.browser = "chrome";
|
||||
Configuration.browserSize = "1920x1080";
|
||||
Configuration.screenshots = true;
|
||||
Configuration.savePageSource = false;
|
||||
Configuration.pageLoadTimeout = 120000;
|
||||
ChromeOptions options = new ChromeOptions()
|
||||
.addArguments("--no-sandbox")
|
||||
.addArguments("--verbose")
|
||||
.addArguments("--remote-allow-origins=*")
|
||||
.addArguments("--disable-dev-shm-usage")
|
||||
.addArguments("--disable-gpu")
|
||||
.addArguments("--lang=en_US");
|
||||
switch (BROWSER) {
|
||||
case (LOCAL) -> Configuration.browserCapabilities = options;
|
||||
case (CONTAINER) -> {
|
||||
Configuration.remote = REMOTE_URL;
|
||||
Configuration.remoteConnectionTimeout = 180000;
|
||||
DesiredCapabilities capabilities = new DesiredCapabilities();
|
||||
capabilities.setCapability("enableVNC", true);
|
||||
capabilities.setCapability("enableVideo", false);
|
||||
Configuration.browserCapabilities = capabilities.merge(options);
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: " + BROWSER);
|
||||
}
|
||||
}
|
||||
|
||||
private static org.openqa.selenium.WebDriver getWebDriver() {
|
||||
try {
|
||||
return WebDriverRunner.getWebDriver();
|
||||
} catch (IllegalStateException ex) {
|
||||
browserSetup();
|
||||
Selenide.open();
|
||||
return WebDriverRunner.getWebDriver();
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void openUrl(String url) {
|
||||
org.openqa.selenium.WebDriver driver = getWebDriver();
|
||||
if (!driver.getCurrentUrl().equals(url)) {
|
||||
driver.get(url);
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void browserInit() {
|
||||
getWebDriver();
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void browserClear() {
|
||||
clearBrowserLocalStorage();
|
||||
clearBrowserCookies();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void browserQuit() {
|
||||
org.openqa.selenium.WebDriver driver = null;
|
||||
try {
|
||||
driver = WebDriverRunner.getWebDriver();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
if (driver != null) {
|
||||
driver.quit();
|
||||
}
|
||||
}
|
||||
|
||||
@Step
|
||||
public static void loggerSetup() {
|
||||
SelenideLogger.addListener("AllureSelenide", new AllureSelenide()
|
||||
.screenshots(true)
|
||||
.savePageSource(false));
|
||||
}
|
||||
}
|
|
@ -1,35 +1,39 @@
|
|||
package com.provectus.kafka.ui.settings.listeners;
|
||||
|
||||
import static java.nio.file.Files.newInputStream;
|
||||
|
||||
import com.codeborne.selenide.Screenshots;
|
||||
import io.qameta.allure.Allure;
|
||||
import io.qameta.allure.testng.AllureTestNg;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.testng.ITestListener;
|
||||
import org.testng.ITestResult;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.nio.file.Files.newInputStream;
|
||||
|
||||
@Slf4j
|
||||
public class AllureListener extends AllureTestNg implements ITestListener {
|
||||
|
||||
private void takeScreenshot() {
|
||||
File screenshot = Screenshots.takeScreenShotAsFile();
|
||||
try {
|
||||
Allure.addAttachment(Objects.requireNonNull(screenshot).getName(), newInputStream(screenshot.toPath()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
private void takeScreenshot() {
|
||||
File screenshot = Screenshots.takeScreenShotAsFile();
|
||||
try {
|
||||
if (screenshot != null) {
|
||||
Allure.addAttachment(screenshot.getName(), newInputStream(screenshot.toPath()));
|
||||
} else {
|
||||
log.warn("Unable to take screenshot");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailure(ITestResult result) {
|
||||
takeScreenshot();
|
||||
}
|
||||
@Override
|
||||
public void onTestFailure(ITestResult result) {
|
||||
takeScreenshot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSkipped(ITestResult result) {
|
||||
takeScreenshot();
|
||||
}
|
||||
@Override
|
||||
public void onTestSkipped(ITestResult result) {
|
||||
takeScreenshot();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,31 +7,31 @@ import org.testng.TestListenerAdapter;
|
|||
@Slf4j
|
||||
public class LoggerListener extends TestListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void onTestStart(final ITestResult testResult) {
|
||||
log.info(String.format("\n------------------------------------------------------------------------ " +
|
||||
"\nTEST STARTED: %s.%s \n------------------------------------------------------------------------ \n",
|
||||
testResult.getInstanceName(), testResult.getName()));
|
||||
}
|
||||
@Override
|
||||
public void onTestStart(final ITestResult testResult) {
|
||||
log.info(String.format("\n------------------------------------------------------------------------ "
|
||||
+ "\nTEST STARTED: %s.%s \n------------------------------------------------------------------------ \n",
|
||||
testResult.getInstanceName(), testResult.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSuccess(final ITestResult testResult) {
|
||||
log.info(String.format("\n------------------------------------------------------------------------ " +
|
||||
"\nTEST PASSED: %s.%s \n------------------------------------------------------------------------ \n",
|
||||
testResult.getInstanceName(), testResult.getName()));
|
||||
}
|
||||
@Override
|
||||
public void onTestSuccess(final ITestResult testResult) {
|
||||
log.info(String.format("\n------------------------------------------------------------------------ "
|
||||
+ "\nTEST PASSED: %s.%s \n------------------------------------------------------------------------ \n",
|
||||
testResult.getInstanceName(), testResult.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailure(final ITestResult testResult) {
|
||||
log.info(String.format("\n------------------------------------------------------------------------ " +
|
||||
"\nTEST FAILED: %s.%s \n------------------------------------------------------------------------ \n",
|
||||
testResult.getInstanceName(), testResult.getName()));
|
||||
}
|
||||
@Override
|
||||
public void onTestFailure(final ITestResult testResult) {
|
||||
log.info(String.format("\n------------------------------------------------------------------------ "
|
||||
+ "\nTEST FAILED: %s.%s \n------------------------------------------------------------------------ \n",
|
||||
testResult.getInstanceName(), testResult.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSkipped(final ITestResult testResult) {
|
||||
log.info(String.format("\n------------------------------------------------------------------------ " +
|
||||
"\nTEST SKIPPED: %s.%s \n------------------------------------------------------------------------ \n",
|
||||
testResult.getInstanceName(), testResult.getName()));
|
||||
}
|
||||
@Override
|
||||
public void onTestSkipped(final ITestResult testResult) {
|
||||
log.info(String.format("\n------------------------------------------------------------------------ "
|
||||
+ "\nTEST SKIPPED: %s.%s \n------------------------------------------------------------------------ \n",
|
||||
testResult.getInstanceName(), testResult.getName()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
package com.provectus.kafka.ui.settings.listeners;
|
||||
|
||||
import com.provectus.kafka.ui.utilities.qaseUtils.annotations.Automation;
|
||||
import com.provectus.kafka.ui.utilities.qaseUtils.annotations.Status;
|
||||
import com.provectus.kafka.ui.utilities.qaseUtils.annotations.Suite;
|
||||
import static io.qase.api.utils.IntegrationUtils.getCaseTitle;
|
||||
|
||||
import com.provectus.kafka.ui.utilities.qase.annotations.Automation;
|
||||
import com.provectus.kafka.ui.utilities.qase.annotations.Status;
|
||||
import com.provectus.kafka.ui.utilities.qase.annotations.Suite;
|
||||
import io.qase.api.QaseClient;
|
||||
import io.qase.api.StepStorage;
|
||||
import io.qase.api.annotation.QaseId;
|
||||
import io.qase.client.ApiClient;
|
||||
import io.qase.client.api.CasesApi;
|
||||
import io.qase.client.model.*;
|
||||
import io.qase.client.model.GetCasesFiltersParameter;
|
||||
import io.qase.client.model.ResultCreateStepsInner;
|
||||
import io.qase.client.model.TestCase;
|
||||
import io.qase.client.model.TestCaseCreate;
|
||||
import io.qase.client.model.TestCaseCreateStepsInner;
|
||||
import io.qase.client.model.TestCaseListResponse;
|
||||
import io.qase.client.model.TestCaseListResponseAllOfResult;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.testng.Assert;
|
||||
|
@ -16,108 +30,107 @@ import org.testng.ITestListener;
|
|||
import org.testng.ITestResult;
|
||||
import org.testng.TestListenerAdapter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
import static io.qase.api.utils.IntegrationUtils.getCaseTitle;
|
||||
|
||||
@Slf4j
|
||||
public class QaseCreateListener extends TestListenerAdapter implements ITestListener {
|
||||
|
||||
private static final CasesApi QASE_API = getQaseApi();
|
||||
private static final CasesApi QASE_API = getQaseApi();
|
||||
|
||||
private static CasesApi getQaseApi() {
|
||||
ApiClient apiClient = QaseClient.getApiClient();
|
||||
apiClient.setApiKey(System.getProperty("QASEIO_API_TOKEN"));
|
||||
return new CasesApi(apiClient);
|
||||
private static CasesApi getQaseApi() {
|
||||
ApiClient apiClient = QaseClient.getApiClient();
|
||||
apiClient.setApiKey(System.getProperty("QASEIO_API_TOKEN"));
|
||||
return new CasesApi(apiClient);
|
||||
}
|
||||
|
||||
private static int getStatus(Method method) {
|
||||
if (method.isAnnotationPresent(Status.class)) {
|
||||
return method.getDeclaredAnnotation(Status.class).status().getValue();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int getStatus(Method method) {
|
||||
if (method.isAnnotationPresent(Status.class))
|
||||
return method.getDeclaredAnnotation(Status.class).status().getValue();
|
||||
return 1;
|
||||
private static int getAutomation(Method method) {
|
||||
if (method.isAnnotationPresent(Automation.class)) {
|
||||
return method.getDeclaredAnnotation(Automation.class).state().getValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int getAutomation(Method method) {
|
||||
if (method.isAnnotationPresent(Automation.class))
|
||||
return method.getDeclaredAnnotation(Automation.class).state().getValue();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static HashMap<Long, String> getCaseTitlesAndIdsFromQase() {
|
||||
HashMap<Long, String> cases = new HashMap<>();
|
||||
boolean getCases = true;
|
||||
int offSet = 0;
|
||||
while (getCases) {
|
||||
getCases = false;
|
||||
TestCaseListResponse response = QASE_API.getCases(System.getProperty("QASE_PROJECT_CODE"),
|
||||
new GetCasesFiltersParameter().status(GetCasesFiltersParameter.SERIALIZED_NAME_STATUS), 100, offSet);
|
||||
TestCaseListResponseAllOfResult result = response.getResult();
|
||||
Assert.assertNotNull(result);
|
||||
List<TestCase> entities = result.getEntities();
|
||||
Assert.assertNotNull(entities);
|
||||
if (entities.size() > 0) {
|
||||
for (TestCase testCase : entities) {
|
||||
cases.put(testCase.getId(), testCase.getTitle());
|
||||
}
|
||||
offSet = offSet + 100;
|
||||
getCases = true;
|
||||
}
|
||||
@SneakyThrows
|
||||
private static HashMap<Long, String> getCaseTitlesAndIdsFromQase() {
|
||||
HashMap<Long, String> cases = new HashMap<>();
|
||||
boolean getCases = true;
|
||||
int offSet = 0;
|
||||
while (getCases) {
|
||||
getCases = false;
|
||||
TestCaseListResponse response = QASE_API.getCases(System.getProperty("QASE_PROJECT_CODE"),
|
||||
new GetCasesFiltersParameter().status(GetCasesFiltersParameter.SERIALIZED_NAME_STATUS), 100, offSet);
|
||||
TestCaseListResponseAllOfResult result = response.getResult();
|
||||
Assert.assertNotNull(result);
|
||||
List<TestCase> entities = result.getEntities();
|
||||
Assert.assertNotNull(entities);
|
||||
if (entities.size() > 0) {
|
||||
for (TestCase testCase : entities) {
|
||||
cases.put(testCase.getId(), testCase.getTitle());
|
||||
}
|
||||
return cases;
|
||||
offSet = offSet + 100;
|
||||
getCases = true;
|
||||
}
|
||||
}
|
||||
return cases;
|
||||
}
|
||||
|
||||
private static boolean isCaseWithTitleExistInQase(Method method) {
|
||||
HashMap<Long, String> cases = getCaseTitlesAndIdsFromQase();
|
||||
String title = getCaseTitle(method);
|
||||
if (cases.containsValue(title)) {
|
||||
for (Map.Entry<Long, String> map : cases.entrySet()) {
|
||||
if (map.getValue().matches(title)) {
|
||||
long id = map.getKey();
|
||||
log.warn(String.format("Test case with @QaseTitle='%s' already exists with @QaseId=%d. " +
|
||||
"Please verify @QaseTitle annotation", title, id));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private static boolean isCaseWithTitleExistInQase(Method method) {
|
||||
HashMap<Long, String> cases = getCaseTitlesAndIdsFromQase();
|
||||
String title = getCaseTitle(method);
|
||||
if (cases.containsValue(title)) {
|
||||
for (Map.Entry<Long, String> map : cases.entrySet()) {
|
||||
if (map.getValue().matches(title)) {
|
||||
long id = map.getKey();
|
||||
log.warn(String.format("Test case with @QaseTitle='%s' already exists with @QaseId=%d. "
|
||||
+ "Please verify @QaseTitle annotation", title, id));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onTestSuccess(final ITestResult testResult) {
|
||||
Method method = testResult.getMethod()
|
||||
.getConstructorOrMethod()
|
||||
.getMethod();
|
||||
String title = getCaseTitle(method);
|
||||
if (!method.isAnnotationPresent(QaseId.class)) {
|
||||
if (title != null) {
|
||||
if (!isCaseWithTitleExistInQase(method)) {
|
||||
LinkedList<ResultCreateStepsInner> resultSteps = StepStorage.stopSteps();
|
||||
LinkedList<TestCaseCreateStepsInner> createSteps = new LinkedList<>();
|
||||
resultSteps.forEach(step -> {
|
||||
TestCaseCreateStepsInner caseStep = new TestCaseCreateStepsInner();
|
||||
caseStep.setAction(step.getAction());
|
||||
caseStep.setExpectedResult(step.getExpectedResult());
|
||||
createSteps.add(caseStep);
|
||||
});
|
||||
TestCaseCreate newCase = new TestCaseCreate();
|
||||
newCase.setTitle(title);
|
||||
newCase.setStatus(getStatus(method));
|
||||
newCase.setAutomation(getAutomation(method));
|
||||
newCase.setSteps(createSteps);
|
||||
if (method.isAnnotationPresent(Suite.class)) {
|
||||
long suiteId = method.getDeclaredAnnotation(Suite.class).id();
|
||||
newCase.suiteId(suiteId);
|
||||
}
|
||||
Long id = Objects.requireNonNull(QASE_API.createCase(System.getProperty("QASE_PROJECT_CODE"),
|
||||
newCase).getResult()).getId();
|
||||
log.info(String.format("New test case '%s' was created with @QaseId=%d", title, id));
|
||||
}
|
||||
} else
|
||||
log.warn("To create new test case in Qase.io please add @QaseTitle annotation");
|
||||
} else
|
||||
log.warn("To create new test case in Qase.io please remove @QaseId annotation");
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void onTestSuccess(final ITestResult testResult) {
|
||||
Method method = testResult.getMethod()
|
||||
.getConstructorOrMethod()
|
||||
.getMethod();
|
||||
String title = getCaseTitle(method);
|
||||
if (!method.isAnnotationPresent(QaseId.class)) {
|
||||
if (title != null) {
|
||||
if (!isCaseWithTitleExistInQase(method)) {
|
||||
LinkedList<ResultCreateStepsInner> resultSteps = StepStorage.stopSteps();
|
||||
LinkedList<TestCaseCreateStepsInner> createSteps = new LinkedList<>();
|
||||
resultSteps.forEach(step -> {
|
||||
TestCaseCreateStepsInner caseStep = new TestCaseCreateStepsInner();
|
||||
caseStep.setAction(step.getAction());
|
||||
caseStep.setExpectedResult(step.getExpectedResult());
|
||||
createSteps.add(caseStep);
|
||||
});
|
||||
TestCaseCreate newCase = new TestCaseCreate();
|
||||
newCase.setTitle(title);
|
||||
newCase.setStatus(getStatus(method));
|
||||
newCase.setAutomation(getAutomation(method));
|
||||
newCase.setSteps(createSteps);
|
||||
if (method.isAnnotationPresent(Suite.class)) {
|
||||
long suiteId = method.getDeclaredAnnotation(Suite.class).id();
|
||||
newCase.suiteId(suiteId);
|
||||
}
|
||||
Long id = Objects.requireNonNull(QASE_API.createCase(System.getProperty("QASE_PROJECT_CODE"),
|
||||
newCase).getResult()).getId();
|
||||
log.info(String.format("New test case '%s' was created with @QaseId=%d", title, id));
|
||||
}
|
||||
} else {
|
||||
log.warn("To create new test case in Qase.io please add @QaseTitle annotation");
|
||||
}
|
||||
} else {
|
||||
log.warn("To create new test case in Qase.io please remove @QaseId annotation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
package com.provectus.kafka.ui.settings.listeners;
|
||||
|
||||
import static io.qase.api.utils.IntegrationUtils.getCaseId;
|
||||
import static io.qase.api.utils.IntegrationUtils.getCaseTitle;
|
||||
import static io.qase.api.utils.IntegrationUtils.getStacktrace;
|
||||
import static io.qase.client.model.ResultCreate.StatusEnum.FAILED;
|
||||
import static io.qase.client.model.ResultCreate.StatusEnum.PASSED;
|
||||
import static io.qase.client.model.ResultCreate.StatusEnum.SKIPPED;
|
||||
|
||||
import io.qase.api.StepStorage;
|
||||
import io.qase.api.config.QaseConfig;
|
||||
import io.qase.api.services.QaseTestCaseListener;
|
||||
|
@ -7,6 +14,9 @@ import io.qase.client.model.ResultCreate;
|
|||
import io.qase.client.model.ResultCreateCase;
|
||||
import io.qase.client.model.ResultCreateStepsInner;
|
||||
import io.qase.testng.guice.module.TestNgModule;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Optional;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -15,88 +25,81 @@ import org.testng.ITestListener;
|
|||
import org.testng.ITestResult;
|
||||
import org.testng.TestListenerAdapter;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.qase.api.utils.IntegrationUtils.*;
|
||||
import static io.qase.client.model.ResultCreate.StatusEnum.*;
|
||||
|
||||
@Slf4j
|
||||
public class QaseResultListener extends TestListenerAdapter implements ITestListener {
|
||||
|
||||
private static final String REPORTER_NAME = "TestNG";
|
||||
private static final String REPORTER_NAME = "TestNG";
|
||||
|
||||
static {
|
||||
System.setProperty(QaseConfig.QASE_CLIENT_REPORTER_NAME_KEY, REPORTER_NAME);
|
||||
}
|
||||
|
||||
@Getter(lazy = true, value = AccessLevel.PRIVATE)
|
||||
private final QaseTestCaseListener qaseTestCaseListener = createQaseListener();
|
||||
|
||||
private static QaseTestCaseListener createQaseListener() {
|
||||
return TestNgModule.getInjector().getInstance(QaseTestCaseListener.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestStart(ITestResult tr) {
|
||||
getQaseTestCaseListener().onTestCaseStarted();
|
||||
super.onTestStart(tr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSuccess(ITestResult tr) {
|
||||
getQaseTestCaseListener()
|
||||
.onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, PASSED));
|
||||
super.onTestSuccess(tr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSkipped(ITestResult tr) {
|
||||
getQaseTestCaseListener()
|
||||
.onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, SKIPPED));
|
||||
super.onTestSuccess(tr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailure(ITestResult tr) {
|
||||
getQaseTestCaseListener()
|
||||
.onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, FAILED));
|
||||
super.onTestFailure(tr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(ITestContext testContext) {
|
||||
getQaseTestCaseListener().onTestCasesSetFinished();
|
||||
super.onFinish(testContext);
|
||||
}
|
||||
|
||||
private void setupResultItem(ResultCreate resultCreate, ITestResult result, ResultCreate.StatusEnum status) {
|
||||
Optional<Throwable> resultThrowable = Optional.ofNullable(result.getThrowable());
|
||||
String comment = resultThrowable
|
||||
.flatMap(throwable -> Optional.of(throwable.toString())).orElse(null);
|
||||
Boolean isDefect = resultThrowable
|
||||
.flatMap(throwable -> Optional.of(throwable instanceof AssertionError))
|
||||
.orElse(false);
|
||||
String stacktrace = resultThrowable
|
||||
.flatMap(throwable -> Optional.of(getStacktrace(throwable)))
|
||||
.orElse(null);
|
||||
Method method = result.getMethod()
|
||||
.getConstructorOrMethod()
|
||||
.getMethod();
|
||||
Long caseId = getCaseId(method);
|
||||
String caseTitle = null;
|
||||
if (caseId == null) {
|
||||
caseTitle = getCaseTitle(method);
|
||||
}
|
||||
LinkedList<ResultCreateStepsInner> steps = StepStorage.stopSteps();
|
||||
resultCreate
|
||||
._case(caseTitle == null ? null : new ResultCreateCase().title(caseTitle))
|
||||
.caseId(caseId)
|
||||
.status(status)
|
||||
.comment(comment)
|
||||
.stacktrace(stacktrace)
|
||||
.steps(steps.isEmpty() ? null : steps)
|
||||
.defect(isDefect);
|
||||
static {
|
||||
System.setProperty(QaseConfig.QASE_CLIENT_REPORTER_NAME_KEY, REPORTER_NAME);
|
||||
}
|
||||
|
||||
@Getter(lazy = true, value = AccessLevel.PRIVATE)
|
||||
private final QaseTestCaseListener qaseTestCaseListener = createQaseListener();
|
||||
|
||||
private static QaseTestCaseListener createQaseListener() {
|
||||
return TestNgModule.getInjector().getInstance(QaseTestCaseListener.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestStart(ITestResult tr) {
|
||||
getQaseTestCaseListener().onTestCaseStarted();
|
||||
super.onTestStart(tr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSuccess(ITestResult tr) {
|
||||
getQaseTestCaseListener()
|
||||
.onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, PASSED));
|
||||
super.onTestSuccess(tr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestSkipped(ITestResult tr) {
|
||||
getQaseTestCaseListener()
|
||||
.onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, SKIPPED));
|
||||
super.onTestSuccess(tr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestFailure(ITestResult tr) {
|
||||
getQaseTestCaseListener()
|
||||
.onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, tr, FAILED));
|
||||
super.onTestFailure(tr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(ITestContext testContext) {
|
||||
getQaseTestCaseListener().onTestCasesSetFinished();
|
||||
super.onFinish(testContext);
|
||||
}
|
||||
|
||||
private void setupResultItem(ResultCreate resultCreate, ITestResult result, ResultCreate.StatusEnum status) {
|
||||
Optional<Throwable> resultThrowable = Optional.ofNullable(result.getThrowable());
|
||||
String comment = resultThrowable
|
||||
.flatMap(throwable -> Optional.of(throwable.toString())).orElse(null);
|
||||
Boolean isDefect = resultThrowable
|
||||
.flatMap(throwable -> Optional.of(throwable instanceof AssertionError))
|
||||
.orElse(false);
|
||||
String stacktrace = resultThrowable
|
||||
.flatMap(throwable -> Optional.of(getStacktrace(throwable)))
|
||||
.orElse(null);
|
||||
Method method = result.getMethod()
|
||||
.getConstructorOrMethod()
|
||||
.getMethod();
|
||||
Long caseId = getCaseId(method);
|
||||
String caseTitle = null;
|
||||
if (caseId == null) {
|
||||
caseTitle = getCaseTitle(method);
|
||||
}
|
||||
LinkedList<ResultCreateStepsInner> steps = StepStorage.stopSteps();
|
||||
resultCreate
|
||||
._case(caseTitle == null ? null : new ResultCreateCase().title(caseTitle))
|
||||
.caseId(caseId)
|
||||
.status(status)
|
||||
.comment(comment)
|
||||
.stacktrace(stacktrace)
|
||||
.steps(steps.isEmpty() ? null : steps)
|
||||
.defect(isDefect);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,26 @@
|
|||
package com.provectus.kafka.ui.utilities;
|
||||
|
||||
import org.testcontainers.shaded.org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.apache.kafka.common.utils.Utils.readFileAsString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.testcontainers.shaded.org.apache.commons.io.IOUtils;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
public static String getResourceAsString(String resourceFileName) {
|
||||
try {
|
||||
return IOUtils.resourceToString("/" + resourceFileName, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public static String getResourceAsString(String resourceFileName) {
|
||||
try {
|
||||
return IOUtils.resourceToString("/" + resourceFileName, StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String fileToString(String path) {
|
||||
try {
|
||||
return readFileAsString(path);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public static String fileToString(String path) {
|
||||
try {
|
||||
return readFileAsString(path);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
package com.provectus.kafka.ui.utilities;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static com.codeborne.selenide.Selenide.sleep;
|
||||
|
||||
import java.time.LocalTime;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.sleep;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class TimeUtils {
|
||||
|
||||
public static void waitUntilNewMinuteStarted() {
|
||||
int secondsLeft = 60 - LocalTime.now().getSecond();
|
||||
log.debug("\nwaitUntilNewMinuteStarted: {}s", secondsLeft);
|
||||
sleep(secondsLeft * 1000);
|
||||
}
|
||||
public static void waitUntilNewMinuteStarted() {
|
||||
int secondsLeft = 60 - LocalTime.now().getSecond();
|
||||
log.debug("\nwaitUntilNewMinuteStarted: {}s", secondsLeft);
|
||||
sleep(secondsLeft * 1000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,90 +1,110 @@
|
|||
package com.provectus.kafka.ui.utilities;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.executeJavaScript;
|
||||
|
||||
import com.codeborne.selenide.Condition;
|
||||
import com.codeborne.selenide.SelenideElement;
|
||||
import com.codeborne.selenide.WebDriverRunner;
|
||||
import java.time.Duration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openqa.selenium.Keys;
|
||||
import org.openqa.selenium.interactions.Actions;
|
||||
|
||||
import static com.codeborne.selenide.Selenide.executeJavaScript;
|
||||
|
||||
@Slf4j
|
||||
public class WebUtils {
|
||||
|
||||
public static void clickByActions(SelenideElement element) {
|
||||
log.debug("\nclickByActions: {}", element.getSearchCriteria());
|
||||
element.shouldBe(Condition.enabled);
|
||||
new Actions(WebDriverRunner.getWebDriver())
|
||||
.moveToElement(element)
|
||||
.click(element)
|
||||
.perform();
|
||||
}
|
||||
public static int getTimeout(int... timeoutInSeconds) {
|
||||
return (timeoutInSeconds != null && timeoutInSeconds.length > 0) ? timeoutInSeconds[0] : 4;
|
||||
}
|
||||
|
||||
public static void sendKeysByActions(SelenideElement element, String keys) {
|
||||
log.debug("\nsendKeysByActions: {} \nsend keys '{}'", element.getSearchCriteria(), keys);
|
||||
element.shouldBe(Condition.enabled);
|
||||
new Actions(WebDriverRunner.getWebDriver())
|
||||
.moveToElement(element)
|
||||
.sendKeys(element, keys)
|
||||
.perform();
|
||||
public static void sendKeysAfterClear(SelenideElement element, String keys) {
|
||||
log.debug("\nsendKeysAfterClear: {} \nsend keys '{}'", element.getSearchCriteria(), keys);
|
||||
element.shouldBe(Condition.enabled).clear();
|
||||
if (keys != null) {
|
||||
element.sendKeys(keys);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clickByJavaScript(SelenideElement element) {
|
||||
log.debug("\nclickByJavaScript: {}", element.getSearchCriteria());
|
||||
element.shouldBe(Condition.enabled);
|
||||
String script = "arguments[0].click();";
|
||||
executeJavaScript(script, element);
|
||||
}
|
||||
public static void clickByActions(SelenideElement element) {
|
||||
log.debug("\nclickByActions: {}", element.getSearchCriteria());
|
||||
element.shouldBe(Condition.enabled);
|
||||
new Actions(WebDriverRunner.getWebDriver())
|
||||
.moveToElement(element)
|
||||
.click(element)
|
||||
.perform();
|
||||
}
|
||||
|
||||
public static void clearByKeyboard(SelenideElement field) {
|
||||
log.debug("\nclearByKeyboard: {}", field.getSearchCriteria());
|
||||
field.shouldBe(Condition.enabled).sendKeys(Keys.END);
|
||||
field.sendKeys(Keys.chord(Keys.CONTROL + "a"), Keys.DELETE);
|
||||
}
|
||||
public static void sendKeysByActions(SelenideElement element, String keys) {
|
||||
log.debug("\nsendKeysByActions: {} \nsend keys '{}'", element.getSearchCriteria(), keys);
|
||||
element.shouldBe(Condition.enabled);
|
||||
new Actions(WebDriverRunner.getWebDriver())
|
||||
.moveToElement(element)
|
||||
.sendKeys(element, keys)
|
||||
.perform();
|
||||
}
|
||||
|
||||
public static boolean isVisible(SelenideElement element) {
|
||||
log.debug("\nisVisible: {}", element.getSearchCriteria());
|
||||
boolean isVisible = false;
|
||||
try {
|
||||
element.shouldBe(Condition.visible);
|
||||
isVisible = true;
|
||||
} catch (Throwable e) {
|
||||
log.debug("{} is not visible", element.getSearchCriteria());
|
||||
}
|
||||
return isVisible;
|
||||
}
|
||||
public static void clickByJavaScript(SelenideElement element) {
|
||||
log.debug("\nclickByJavaScript: {}", element.getSearchCriteria());
|
||||
element.shouldBe(Condition.enabled);
|
||||
String script = "arguments[0].click();";
|
||||
executeJavaScript(script, element);
|
||||
}
|
||||
|
||||
public static boolean isEnabled(SelenideElement element) {
|
||||
log.debug("\nisEnabled: {}", element.getSearchCriteria());
|
||||
boolean isEnabled = false;
|
||||
try {
|
||||
element.shouldBe(Condition.enabled);
|
||||
isEnabled = true;
|
||||
} catch (Throwable e) {
|
||||
log.debug("{} is not enabled", element.getSearchCriteria());
|
||||
}
|
||||
return isEnabled;
|
||||
}
|
||||
public static void clearByKeyboard(SelenideElement field) {
|
||||
log.debug("\nclearByKeyboard: {}", field.getSearchCriteria());
|
||||
field.shouldBe(Condition.enabled).sendKeys(Keys.END);
|
||||
field.sendKeys(Keys.chord(Keys.CONTROL + "a"), Keys.DELETE);
|
||||
}
|
||||
|
||||
public static boolean isSelected(SelenideElement element) {
|
||||
log.debug("\nisSelected: {}", element.getSearchCriteria());
|
||||
boolean isSelected = false;
|
||||
try {
|
||||
element.shouldBe(Condition.selected);
|
||||
isSelected = true;
|
||||
} catch (Throwable e) {
|
||||
log.debug("{} is not selected", element.getSearchCriteria());
|
||||
}
|
||||
return isSelected;
|
||||
public static boolean isVisible(SelenideElement element, int... timeoutInSeconds) {
|
||||
log.debug("\nisVisible: {}", element.getSearchCriteria());
|
||||
boolean isVisible = false;
|
||||
try {
|
||||
element.shouldBe(Condition.visible,
|
||||
Duration.ofSeconds(getTimeout(timeoutInSeconds)));
|
||||
isVisible = true;
|
||||
} catch (Throwable e) {
|
||||
log.debug("{} is not visible", element.getSearchCriteria());
|
||||
}
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
public static boolean selectElement(SelenideElement element, boolean select) {
|
||||
if (select) {
|
||||
if (!element.isSelected()) clickByJavaScript(element);
|
||||
} else {
|
||||
if (element.isSelected()) clickByJavaScript(element);
|
||||
}
|
||||
return true;
|
||||
public static boolean isEnabled(SelenideElement element, int... timeoutInSeconds) {
|
||||
log.debug("\nisEnabled: {}", element.getSearchCriteria());
|
||||
boolean isEnabled = false;
|
||||
try {
|
||||
element.shouldBe(Condition.enabled,
|
||||
Duration.ofSeconds(getTimeout(timeoutInSeconds)));
|
||||
isEnabled = true;
|
||||
} catch (Throwable e) {
|
||||
log.debug("{} is not enabled", element.getSearchCriteria());
|
||||
}
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
public static boolean isSelected(SelenideElement element, int... timeoutInSeconds) {
|
||||
log.debug("\nisSelected: {}", element.getSearchCriteria());
|
||||
boolean isSelected = false;
|
||||
try {
|
||||
element.shouldBe(Condition.selected,
|
||||
Duration.ofSeconds(getTimeout(timeoutInSeconds)));
|
||||
isSelected = true;
|
||||
} catch (Throwable e) {
|
||||
log.debug("{} is not selected", element.getSearchCriteria());
|
||||
}
|
||||
return isSelected;
|
||||
}
|
||||
|
||||
public static boolean selectElement(SelenideElement element, boolean select) {
|
||||
if (select) {
|
||||
if (!element.isSelected()) {
|
||||
clickByJavaScript(element);
|
||||
}
|
||||
} else {
|
||||
if (element.isSelected()) {
|
||||
clickByJavaScript(element);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package com.provectus.kafka.ui.utilities.qase;
|
||||
|
||||
import static com.provectus.kafka.ui.settings.BaseSource.SUITE_NAME;
|
||||
import static com.provectus.kafka.ui.variables.Suite.MANUAL;
|
||||
import static org.apache.commons.lang3.BooleanUtils.FALSE;
|
||||
import static org.apache.commons.lang3.BooleanUtils.TRUE;
|
||||
import static org.apache.commons.lang3.StringUtils.isEmpty;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class QaseSetup {
|
||||
|
||||
public static void qaseIntegrationSetup() {
|
||||
String qaseApiToken = System.getProperty("QASEIO_API_TOKEN");
|
||||
if (isEmpty(qaseApiToken)) {
|
||||
log.warn("Integration with Qase is disabled due to run config or token wasn't defined.");
|
||||
System.setProperty("QASE_ENABLE", FALSE);
|
||||
} else {
|
||||
log.warn("Integration with Qase is enabled. Find this run at https://app.qase.io/run/KAFKAUI.");
|
||||
String automation = SUITE_NAME.equalsIgnoreCase(MANUAL) ? "" : "Automation ";
|
||||
System.setProperty("QASE_ENABLE", TRUE);
|
||||
System.setProperty("QASE_PROJECT_CODE", "KAFKAUI");
|
||||
System.setProperty("QASE_API_TOKEN", qaseApiToken);
|
||||
System.setProperty("QASE_USE_BULK", TRUE);
|
||||
System.setProperty("QASE_RUN_NAME", DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
|
||||
.format(OffsetDateTime.now(ZoneOffset.UTC)) + ": " + automation + SUITE_NAME.toUpperCase() + " suite");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package com.provectus.kafka.ui.utilities.qaseUtils.annotations;
|
||||
|
||||
import com.provectus.kafka.ui.utilities.qaseUtils.enums.State;
|
||||
package com.provectus.kafka.ui.utilities.qase.annotations;
|
||||
|
||||
import com.provectus.kafka.ui.utilities.qase.enums.State;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
@ -11,5 +10,5 @@ import java.lang.annotation.Target;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Automation {
|
||||
|
||||
State state();
|
||||
State state();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.provectus.kafka.ui.utilities.qaseUtils.annotations;
|
||||
package com.provectus.kafka.ui.utilities.qase.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
|
@ -9,5 +9,5 @@ import java.lang.annotation.Target;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Status {
|
||||
|
||||
com.provectus.kafka.ui.utilities.qaseUtils.enums.Status status();
|
||||
com.provectus.kafka.ui.utilities.qase.enums.Status status();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue