diff --git a/Homepage/Frontend/css/r11addons.css b/Homepage/Frontend/css/r11addons.css new file mode 100644 index 0000000..afb4425 --- /dev/null +++ b/Homepage/Frontend/css/r11addons.css @@ -0,0 +1,59 @@ +.modification-button.btn-tile:hover { + color: #ca1111; +} + +.modification-button.btn-tile { + width: 10%; + margin: 20% 0 0 0; + font-size: 14px; + color: #00000020 +} + +.modification-button.btn-addtile { + font-size: 38px; + color: #00000030; +} + +.modification-button.btn-addtile:hover { + color: #58ac43; +} + +.tile { + width: 100%; + padding-top: 40%; + border: 1px solid gray; + border-radius: 3px; + position: relative; + background: #f0f0f095; + margin-bottom: 10px; + cursor: default; +} + +.tile:hover { + filter: brightness(110%); +} + +.tile.active { + background: #00000070; + color: white; + filter: none; +} + +.tile.active .btn-tile { + opacity: 0; +} + +.tile .content { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + padding: 0 2% 0 7%; + display: flex; +} + +.content p { + margin: 0; + padding: 0; +} diff --git a/Homepage/Frontend/css/r11modal.css b/Homepage/Frontend/css/r11modal.css new file mode 100644 index 0000000..7848a43 --- /dev/null +++ b/Homepage/Frontend/css/r11modal.css @@ -0,0 +1,104 @@ +#overlay { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + opacity: 0; + background: rgba(0, 0 , 0, 0.5); + pointer-events: none; +} + +#overlay.active { + pointer-events: all; + opacity: 1; +} + +.modal { + display: none; + width: 390px; + min-height: 71px; + max-height: 700px; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: white; + padding: 5px; + border: 1px solid #f0f0f0; +} + +.modal.active { + display: block; +} + +.modal div.header { + width: 384px; + height: 24px; + background: #2e3133; + color: white; + font-size: 24px; + font-weight: 700; + padding: 3px; + margin-bottom: 5px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal div.header button { + font-size: 100%; + font-family: inherit; + border: 0; + padding: 0; + background: 0; + color: inherit; + cursor: pointer; +} + +.modal div.header button:hover { + color: white; + font-weight: 700; +} + +.modal div.body { + width: 370px; + padding: 10px; + background: #f0f0f0; + color: #2e3133; + min-height: 16px; + text-align: justify; + font-size: 16px; +} + +.modal div.function { + width: 385px; + min-height: 30px; + padding-top: 5px; + display: flex; + justify-content: space-evenly; + background: inherit; +} + +.modal div.function button { + min-height: 22px; + min-width: 34px; + max-width: 74px; + padding: 3px 20px; + outline: none; + border: 1px solid #f0f0f0; + background: rgba(205,205,205,1); + font-size: 16px; + text-align: center; + cursor: pointer; +} + +.modal div.function button:hover { + filter: brightness(110%); +} + +.r-exclamation:before { + content: '!'; + color: #3bc4f1; + font-style: normal; +} diff --git a/Homepage/Frontend/css/r11tables.css b/Homepage/Frontend/css/r11tables.css new file mode 100644 index 0000000..0a680ab --- /dev/null +++ b/Homepage/Frontend/css/r11tables.css @@ -0,0 +1,71 @@ +.table-map { + width: 60%; +} + +.table-map input{ + font-size: 16px; + padding: 7px; + border: 1px solid rgba(145, 146, 146, 0.849); + border-radius: 5px; +} + +.table-map input.key { + background: #f0f0f0; +} + +.modification-button.btn-add { + font-size: 16px; + color: #00000030; + margin: auto 0 auto 0; +} + +.modification-button.btn-add:hover { + color:#58ac43; +} + +.modification-button.btn-hashmap { + font-size: 16px; + color: #00000030; + margin: auto 0 auto 0; +} + +.modification-button.btn-hashmap:hover { + color: #ca1111; +} + +.table-default { + width: 80%; + border-collapse: collapse; + border-spacing: 0; +} + +.table-default tr { + background: #f0f0f02d; +} + +.table-default tr.bottom-border { + border-bottom: 1px solid black; +} + +.table-default th { + background: #ffffff; +} + +.table-default tr.even { + background: #f0f0f0; +} + +.table-doc td, .table-doc th{ + border-spacing: 0px; + padding: 0px 10px; +} + +.table-doc td { + background-color: rgba(155, 165, 160, 0.342); +} + +.table-doc th { + background-color: #3bc4f1; + text-align: left; + color: white; +} \ No newline at end of file diff --git a/Homepage/Frontend/css/r11tool.css b/Homepage/Frontend/css/r11tool.css new file mode 100644 index 0000000..2243166 --- /dev/null +++ b/Homepage/Frontend/css/r11tool.css @@ -0,0 +1,300 @@ +@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;700&display=swap'); +.hyperlink, .hyperlink:visited, .hyperlink:active { + color: rgb(47, 125, 146); + cursor: pointer; +} + +.hyperlink:hover { + filter: brightness(120%); +} + +.bordered-field { + border: 2px solid rgba(93, 99, 96, 0.705); + border-radius: 5px; + padding: 8px; + +} + +.bordered-field:focus { + outline: none; + box-shadow: 0 0 5px rgba(81, 203, 238); + border: 2px solid #00000070; +} + +.bordered-field:disabled { + background: #eeeeeed2; +} + +.vertically-resizeable { + resize: vertical; +} + +body { + font-family: 'Nunito', sans-serif; +} + +.container { + display: flex; + justify-content: left; + width: 100%; +} + +.tool { + width: 55%; + display: flex; + justify-content: space-evenly; +} + +.tool.extended { + width: 65%; +} + +.tool .tool-context { + width: 90%; +} + +.tool.extended .tool-context { + width: 75%; +} + +.tool.extended .tool-extention { + width: 20%; + padding-top: 2%; + display: block; +} + +.tool .tool-extention { + display: none; +} + +.tool-extention { + opacity: 0; + pointer-events: none; +} + +.tool-extention.active { + opacity: 100%; + pointer-events: all; +} + +.clickable-text { + padding: 0; + outline: none; + background: none; + border: none; + font-weight: 300; + cursor: pointer; +} + +.clickable-text.highlight:hover { + color: #3bc4f1; +} + +.clickable-text.switch { + font-size: 18px; + font-weight: 700; +} + +.clickable-text.switch span.toggleIndicator:before { + content: '>'; +} + +.clickable-text.switch span.toggleIndicator.active:before { + content: 'v'; +} + +.modification-button { + padding: 0; + outline: none; + background: none; + border: none; + font-weight: 300; +} + +.text-aligned-to-right { + text-align: right; +} + +.centered-vertically { + margin-top: auto; + margin-bottom: auto; +} + +.display-space-between { + width: 100%; + display: flex; + justify-content: space-between; +} + +.display-space-evenly { + display: flex; + justify-content: space-evenly; +} + +.float-left { + display: flex; + justify-content: left; + width: 100%; +} + +.version-span { + font-size: 13px; + font-weight: 400; + color: rgba(85,85,85,0.555); +} + +.block-display { + display: block; +} + +.block-label { + display: block; + margin: 0 0 0 5px; +} + +.tabmenu { + display: flex; + flex-direction: row; + text-align: center; + border-bottom: 1px solid rgba(185, 185, 185, 0.5); +} + +.tabitem { + flex-grow: 1; + cursor: pointer; + padding: 5px 0; +} + +.tabitem:hover { + font-weight: 700; +} + +.tabitem.active { + background: rgba(33, 34, 34, 0.705); + color: white; + font-weight: 700; + cursor:default; + flex-grow: 1; +} + +.big-font { + font-size: 20px; +} + +.action-button.active { + background: #3bc4f1; + border: 1px solid #7ed0eb; + cursor: pointer; +} + +.action-button.active:hover { + filter: brightness(110%); +} + +.action-button { + background: rgba(155, 165, 160, 0.507); + border:1px solid rgba(186, 197, 191, 0.507); + color: white; + padding: 10px 20px; + font-weight: 700; + margin: 3px 0; +} + +.quater-width { + width: 25%; +} + +.half-width { + width: 50%; +} + +.tree-fourth-width { + width: 75%; +} + +.half-width.with-padding { + width: 45%; +} + +.max-width { + width: 100%; +} + +.max-width.with-padding { + width: 94%; +} + +.max-height { + height: 100%; +} + +.height-300 { + height: 300px; +} + +.max-height.with-padding { + height: 90%; +} + +.small-margins { + margin: 3%; +} + +.small-vertical-margin { + margin-top: 10px; + margin-bottom: 10px; +} + +.medium-vertical-margin { + margin-top: 30px; + margin-bottom: 30px; +} + +.large-vertical-margin { + margin-top: 50px; + margin-bottom: 50px; +} + +.textarea-300 { + height: 300px; +} + +.centered-content { + display: flex; + justify-content: center; +} + + +.tabcontent { + display: none; +} + +.tabcontent.active { + display: flex; + justify-content: center; +} + +.hiddable { + display: none; +} + +.hiddable.active { + display: inherit; +} + +/* In case of collision with classes that use 'active' */ +.hidden { + display: none; +} + +/* TODO: Add proper class */ +/* textarea { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} */ + +/* TODO: Add proper class */ +/* code{ + line-height: 150%; +} */ \ No newline at end of file diff --git a/Homepage/Frontend/css/r11tooltip.css b/Homepage/Frontend/css/r11tooltip.css new file mode 100644 index 0000000..34fcbfb --- /dev/null +++ b/Homepage/Frontend/css/r11tooltip.css @@ -0,0 +1,77 @@ +.tooltip-window { + position: fixed; + right: 0; + filter: drop-shadow(-2px 0px 2px darkgray); + background: #e8f3f7; + padding: 15px 30px; + font-family: 'Nunito', sans-serif; + width: 40%; + height: 100%; + overflow: scroll; +} + +.tooltip-window.lite { + width: 30%; +} + +.tip { + display: none; +} + +.tip.active { + display: block; +} + +/* TODO: Remove !important. It's bad practice and it can cause errors in future */ +.section-button { + width: 100%; + padding: 15px 0; + font-size: 18px; + background: #b4b4b4c5; + cursor: pointer; + border-bottom: darkgray 2px solid !important; +} + +.section-button:hover { + backdrop-filter: brightness(110%); +} + +.section-button .active { + background: #00000030; +} + +.List .collapsibleContent { + border-left: #bdc5c9 2px solid; + overflow: hidden; + background: #ffffff50; +} + +/* TODO: .section class is to generic. It should be renamed */ +.section{ + padding: 10px 0px 20px 0px ; +} + +/* TODO: content subclass already in use. Creating content class overrides the subclass. +Make .content a subclass of .content */ +/* .content { + padding: 0px 15px 0px 15px ; + text-align: justify; + overflow: hidden; + transition: max-height .2s ease-out; + max-height: 0px; + border-left: #c0c2c3 2px solid; + +} */ + +.collapsibleMini::before{ + content: "►"; +} + +.collapsibleMini.active::before{ + content: "▼"; +} + +/* TODO: Add proper class */ +/* button:hover{ + filter: brightness(110%); +} */ \ No newline at end of file diff --git a/Homepage/common.css b/Homepage/common.css index b5c1801..492ef0f 100644 --- a/Homepage/common.css +++ b/Homepage/common.css @@ -1,101 +1,6 @@ -/* TODO Ogarnac czcionke!! */ -@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@200&display=swap'); - -body { - font-family: 'Nunito', sans-serif; - color: #2e3133; - font-weight: normal; - margin: 0px; -} - -textarea:focus { - box-shadow: 0 0 5px rgba(81, 203, 238); - outline: none; -} - -.tooltip{ - border: 2px solid rgba(155, 165, 160, 0.507); - border-radius: 15px; - padding: 20px; -} - -a, a:visited, a:active { - color: rgb(47, 125, 146); -} - -a:hover{ - filter: brightness(120%); -} - -.field { - border: 2px solid rgba(56, 59, 58, 0.507); - border-radius: 5px; -} - -button { - font-size: 20px; - text-align: center; - cursor: pointer; - border: none; - text-decoration: none; -} - -input { - border-radius: 5px; - border: 1px solid rgba(155, 165, 160, 0.507); -} - -button:hover{ - filter: brightness(110%); - /* TODO Insert animation here! */ -} - -.tooltip h1{ - margin: 0px; - font-size: 24px; - font-weight: bold; -} - -.tooltip button{ - background-color: rgba(155, 165, 160, 0.507); - color: rgb(44, 44, 44); - border-bottom: 2px solid rgba(99, 99, 99, 0.507); - padding: 14px; -} - -.btn-action { - background-color: #3bc4f1; - color: white; - padding: 15px 32px; - display: inline-block; -} - -.versionInfo { - color: rgba(85, 85, 85, 0.555); - font-size: 13px; -} - -.resizeVertical { - resize: vertical; -} - -.resizeNone { - resize: none; -} - -table{ - border: none; -} - -.collapsibleData { - padding: 0 18px; - /* max-height: 0; */ - overflow: hidden; - /* transition: height 0.2s ease-out; */ - /* transition: max-height 0.2s ease-out; */ - background-color: #f1f1f1; -} - -.textField-key{ - background-color: #e6e6e6; -} \ No newline at end of file +@import url('https://necolas.github.io/normalize.css/8.0.1/normalize.css'); +@import url('Frontend/css/r11addons.css'); +@import url('Frontend/css/r11tables.css'); +@import url('Frontend/css/r11tool.css'); +@import url('Frontend/css/r11tooltip.css'); +@import url('Frontend/css/r11modal.css'); diff --git a/Homepage/index.html b/Homepage/index.html index 7c39399..0b20021 100644 --- a/Homepage/index.html +++ b/Homepage/index.html @@ -16,14 +16,14 @@
- +
diff --git a/REST_XSLT/.gitignore b/REST_XSLT/.gitignore deleted file mode 100644 index da016ff..0000000 --- a/REST_XSLT/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -target/ -nbactions.xml -.idea/ -.settings/ -.classpath -.settings -.project -*.iml -.vscode diff --git a/REST_XSLT/pom.xml b/REST_XSLT/pom.xml deleted file mode 100644 index 5d3fdca..0000000 --- a/REST_XSLT/pom.xml +++ /dev/null @@ -1,142 +0,0 @@ - - - 4.0.0 - - r11.mltx - REST_XSLT - 1.0-SNAPSHOT - - - 11 - 11 - UTF-8 - 2.14.1 - 2.0.6 - - - - - - - - - - - - - - - - - - - - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - - maven-assembly-plugin - 3.3.0 - - - - - true - lib/ - r11.mltx.restxslt.Main - - - - jar-with-dependencies - - - - - - make-assembly - package - - single - - - - - - - - - - - - - com.sparkjava - spark-core - 2.9.3 - - - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - - - net.sf.saxon - Saxon-HE - 11.4 - - - xalan - xalan - 2.7.2 - - - - - org.slf4j - slf4j-simple - ${slf4j.version} - test - - - org.slf4j - jcl-over-slf4j - ${slf4j.version} - runtime - - - org.slf4j - slf4j-api - ${slf4j.version} - runtime - - - org.slf4j - slf4j-log4j12 - ${slf4j.version} - runtime - - - - org.apache.logging.log4j - log4j-core - 2.19.0 - - - - - \ No newline at end of file diff --git a/backend/mocked-services/Dockerfile b/backend/mocked-services/Dockerfile new file mode 100644 index 0000000..994b2b0 --- /dev/null +++ b/backend/mocked-services/Dockerfile @@ -0,0 +1,19 @@ +FROM maven:3.6.3-jdk-14 as builder +WORKDIR application +COPY ./ ./ +RUN mvn clean install + +FROM openjdk:14 as layerBuilder +WORKDIR application +ARG JAR_FILE=application/target/*.jar +COPY --from=builder ${JAR_FILE} application.jar +RUN java -Djarmode=layertools -jar application.jar extract + +FROM openjdk:14 +WORKDIR application +COPY --from=layerBuilder application/dependencies/ ./ +COPY --from=layerBuilder application/spring-boot-loader/ ./ +COPY --from=layerBuilder application/snapshot-dependencies/ ./ +COPY --from=layerBuilder application/application/ ./ + +ENTRYPOINT ["java", "-Djava.security.cgd=file:/dev/./urandom", "-Dspring.profiles.active=DEV", "org.springframework.boot.loader.JarLauncher"] diff --git a/backend/mocked-services/README.md b/backend/mocked-services/README.md new file mode 100644 index 0000000..9abd3e8 --- /dev/null +++ b/backend/mocked-services/README.md @@ -0,0 +1,48 @@ +# R11-MockedServices +Mockup service for middleware testing. + +Available scripts: + +### Running the application on a local machine +For Intellij: +Plugins: +settings -> plugins -> install lombok plugin +settings -> annotation processors -> enable annotation processing + +A connection to a Redis database is expected to run the application successfully. You need to download and run Redis DB on +your local machine with default values localhost 6379. + +You can also run the application via Docker. +#### docker-compose up --build -d + +However, you need either to run it with Docker-compose ensuring that all containers are within a network or start application locally. +In order to change environment two properties must be changed. + +1) data-access.properties - property redis.host +2) logback.xml - configuration/appender/host element value + +If application is to be run locally, both of above should be set to "localhost" +If application is to be run in docker environment, both should be set to name of a redis container (by default "redis") + +Docker automaticly translates container name to IP address, considering all containers are within same network. +Try to avoid using any symbols in names of containers, because it may cause that URL exception to be thrown. Instead use letters only. + +### Operations on Redis DB + +Use Redis CLI or attach to Redis docker image in order to manually operate on DB. + +#### docker exec -it mockedservices_redis-server redis-cli +Attach to redis server image and open a redis client. + +Useful redis-cli commands: +###### KEYS * +Show all keys in the db. +###### TYPE key +Show key type. +###### LRANGE key start stop +Display elements from the list. +###### LLEN key +Display list length. +###### SMEMBERS key +Display elements from hashSet. +Logs can be found in lists with names logstash_yyyy-mm-dd. \ No newline at end of file diff --git a/backend/mocked-services/docker-compose.yml b/backend/mocked-services/docker-compose.yml new file mode 100644 index 0000000..9452511 --- /dev/null +++ b/backend/mocked-services/docker-compose.yml @@ -0,0 +1,20 @@ +version: '3' +services: + redis: + image: 'redis' + restart: "no" + klaus: + build: + context: . + dockerfile: Dockerfile + container_name: klaus + restart: "no" + ports: + - "8097:8097" + depends_on: + - redis + environment: + SPRING_PROFILES_ACTIVE: DEV +networks: + default: + name: shared_network_mocked_services diff --git a/backend/mocked-services/pom.xml b/backend/mocked-services/pom.xml new file mode 100644 index 0000000..a3dd5ac --- /dev/null +++ b/backend/mocked-services/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.3.1.RELEASE + + + + mocked-services + + + 11 + 3.3.0 + 1.1.6 + 3.16.1 + 1.3.1.Final + Release11 + ${project.artifactId} + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.springframework.boot + spring-boot-starter-data-redis + + + com.cwbase + logback-redis-appender + ${logback-redis-appender.version} + + + redis.clients + jedis + ${jedis.version} + + + org.codehaus.jettison + jettison + 1.4.1 + + + io.springfox + springfox-boot-starter + 3.0.0 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + true + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + org.projectlombok + lombok + ${lombok.version} + + + + -Amapstruct.defaultComponentModel=spring + + 13 + 13 + + + + + \ No newline at end of file diff --git a/backend/mocked-services/src/main/java/com/r11/tools/KlausApplication.java b/backend/mocked-services/src/main/java/com/r11/tools/KlausApplication.java new file mode 100644 index 0000000..47cb9e9 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/KlausApplication.java @@ -0,0 +1,18 @@ +package com.r11.tools; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * It's generic Spring context starter. Move along... + * + * @author Rafał Żukowicz + */ +@SpringBootApplication +public class KlausApplication { + + public static void main(String[] args) { + SpringApplication.run(KlausApplication.class, args); + } + +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/config/RedisConfig.java b/backend/mocked-services/src/main/java/com/r11/tools/config/RedisConfig.java new file mode 100644 index 0000000..c3438d2 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/config/RedisConfig.java @@ -0,0 +1,68 @@ +package com.r11.tools.config; + +import java.util.Objects; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import redis.clients.jedis.JedisPool; + +/** + * Class containing configuration for Redis db client + * @author Rafał Żukowicz + */ +@Configuration +@EnableRedisRepositories +@PropertySource("classpath:data-access.properties") +public class RedisConfig { + + @Autowired + private Environment environment; + + /** + * Bean of JedisPool - the Redis client. It stores requests in "the pool" and then fires them at Redis. + * It's considered super lightweight and fast client variant + * @return lightweight client of the Redis - the JedisPool + */ + @Bean + JedisPool jedisPool(){ + final JedisPool pool = new JedisPool(environment.getProperty("redis.host"), + Integer.parseInt(environment.getProperty("redis.port"))); + return pool; + } + + /** + * Bean of a factory for connenction object. + * It's initialized with Redis db url property and is fed to other methods. + * @return the factory for RedisTemplates + */ + @Bean + JedisConnectionFactory jedisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfiguration = + new RedisStandaloneConfiguration(Objects.requireNonNull(environment.getProperty("redis.host")), + Integer.parseInt(Objects.requireNonNull(environment.getProperty("redis.port")))); + return new JedisConnectionFactory(redisStandaloneConfiguration); + } + + /** + * RedisTemplate is the tool to store and retrieve given type (object) of hash from the database. + * It's like you could store your Java object by just naming it inside database. You might thing about it + * as of DAO. + * @return RedisTemplate the redis dao. + */ + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(jedisConnectionFactory()); + redisTemplate.setExposeConnection(true); + redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer()); + return redisTemplate; + } + +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/config/SpringFoxConfig.java b/backend/mocked-services/src/main/java/com/r11/tools/config/SpringFoxConfig.java new file mode 100644 index 0000000..4d3da67 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/config/SpringFoxConfig.java @@ -0,0 +1,22 @@ +package com.r11.tools.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SpringFoxConfig { + @Bean + public Docket api(){ + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.ant("/api/**")) + .build(); + } +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/controller/EventController.java b/backend/mocked-services/src/main/java/com/r11/tools/controller/EventController.java new file mode 100644 index 0000000..0f20b7c --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/controller/EventController.java @@ -0,0 +1,58 @@ +package com.r11.tools.controller; + +import com.r11.tools.model.EventRequestDto; +import com.r11.tools.service.EtrackService; +import java.time.LocalDateTime; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +/** + * It's the REST api for {@link com.r11.tools.model.Event} + * @author Gabriel Modzelewski + */ +@Slf4j +@RestController +@RequestMapping(path = "/api/event") +@AllArgsConstructor +public class EventController { + private final EtrackService service; + + /** + * Returns the list of Events in given time bracket. + * The list of objects is received via {@link EventRequestDto}, which contains time brackets, + * as well as the key - uuid. + * @param event EventRequestDto object that contains data needed to query the database + * @return list of {@link com.r11.tools.model.Event} + */ + @PostMapping + public ResponseEntity filterHistory(@RequestBody EventRequestDto event){ + return new ResponseEntity(service.getEventsByDateTimeAndBusinessKeys(event), HttpStatus.OK); + } + + /** + * Returns the list of Events of last 24h from given date. + * @param uuid unique id of message list + * @param messageId unique id of message in message list + * @return list of {@link com.r11.tools.model.Event} + */ + @GetMapping(path = "/{uuid}/{messageId}") + public ResponseEntity getLastDay(@PathVariable UUID uuid, + @PathVariable Integer messageId){ + LocalDateTime requestTime = LocalDateTime.now(); + LocalDateTime dayBeforeRequest = requestTime.minusDays(1L); + EventRequestDto eventRequestDto = EventRequestDto.builder() + .clientUUID(uuid) + .mockedResponseId(messageId) + .localDateTimeFrom(dayBeforeRequest) + .localDateTimeTo(requestTime) + .build(); + return new ResponseEntity(service.getEventsByDateTimeAndBusinessKeys(eventRequestDto), HttpStatus.OK); + } + + +} + diff --git a/backend/mocked-services/src/main/java/com/r11/tools/controller/MainController.java b/backend/mocked-services/src/main/java/com/r11/tools/controller/MainController.java new file mode 100644 index 0000000..e4c3b66 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/controller/MainController.java @@ -0,0 +1,27 @@ +package com.r11.tools.controller; + +import lombok.SneakyThrows; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +/** + * Class responsible for returning homepage html + * @author Gabriel Modzelewski + */ +@Controller +@RequestMapping("/") +public class MainController { + /** + * Default path to get the homepage + * @return the view of homepage + */ + @SneakyThrows + @GetMapping + public ModelAndView showHome(){ + ModelAndView mov = new ModelAndView(); + mov.setViewName("html/mock"); + return mov; + } +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/controller/MockController.java b/backend/mocked-services/src/main/java/com/r11/tools/controller/MockController.java new file mode 100644 index 0000000..bd63b8b --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/controller/MockController.java @@ -0,0 +1,173 @@ +package com.r11.tools.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.r11.tools.model.MockedMessageDto; +import com.r11.tools.service.KlausService; +import com.r11.tools.utilis.BusinessKey; +import com.r11.tools.utilis.TrackingClient; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.*; +import org.springframework.web.bind.annotation.*; + +/** + * Returns the homepage and provides the api for javascript async requests. + * @author Gabriel Modzelewski + */ +@Slf4j +@RestController +@RequestMapping(path = "/api/mock") +@AllArgsConstructor +public class MockController { + private final KlausService klausService; + + + + /** + * Updates queried message with given set of data + * @param body {@link MockedMessageDto} json representation + * @return confirmation and 200 OK + */ + @SneakyThrows + @PutMapping + public ResponseEntity updateMessage(@RequestBody String body){ + ObjectMapper mapper = new ObjectMapper(); + MockedMessageDto message = mapper.readValue(body, MockedMessageDto.class); + return klausService.setMockedResponse(message); + } + + /** + * Returns the full list of messages. It's used by javascript on the client side to initialize homepage + * with data from the database. + * @param uuidValue the key-uuid of given set of messages + * @return responds with 200 OK and list of {@link MockedMessageDto} + */ + @GetMapping({"/", "/{uuidValue}"}) + public List getListOfMessages(@PathVariable(required = false) String uuidValue){ + UUID clientUUID; + if(uuidValue == null || uuidValue.equals("")) clientUUID = UUID.randomUUID(); + else clientUUID = UUID.fromString(uuidValue); + List messages = klausService.getAllMockedResponses(clientUUID); + if(messages.size() == 0) { + klausService.setMockedResponse(buildDefaultMessage(clientUUID)); + messages = klausService.getAllMockedResponses(clientUUID); + } + Collections.sort(messages); + return messages; + } + + /** + * Accepts empty post request and creates new message in given set. The new message has default set of data, + * which is constructed in {@link #buildDefaultMessage(UUID, int)} method. + * @param uuidValue the key-uuid of given set of messages + * @return confirmation response with 200 OK + */ + @PostMapping("/{uuidValue}") + public ResponseEntity addNewMessage(@PathVariable String uuidValue){ + UUID clientUUID = UUID.fromString(uuidValue); + List messages = klausService.getAllMockedResponses(clientUUID); + MockedMessageDto nextMessage = buildDefaultMessage(clientUUID, findNextId(messages)); + return klausService.setMockedResponse(nextMessage); + } + + /** + * Deletes message of given id via client request + * @param uuidValue the key-uuid of given set of messages + * @param idValue unique id of given message + * @return after deletion the confirmation is send with status 200 OK + */ + @DeleteMapping("/{uuidValue}/{idValue}") + public ResponseEntity removeMessage(@PathVariable String uuidValue, + @PathVariable String idValue){ + UUID clientUUID = UUID.fromString(uuidValue); + int id = Integer.parseInt(idValue); + return klausService.deleteMockedResponse(clientUUID, id); + } + + + /** + * Recalls {@link #buildDefaultMessage(UUID)} for message construction and sets id of message + * @param uuid the key-uuid of given set of messages + * @param id unique id of given message + * @return message with default dataset and set id + */ + private static MockedMessageDto buildDefaultMessage(UUID uuid, int id){ + MockedMessageDto message = buildDefaultMessage(uuid); + message.setMockedResponseId(id); + return message; + } + + /** + * Constructs message with default set of data + * @param uuid the key-uuid of given set of messages + * @return message with default dataset + */ + private static MockedMessageDto buildDefaultMessage(UUID uuid){ + Map headers = new HashMap<>(); + headers.put("Keep-Alive", "timeout=60"); + headers.put("Connection", "keep-alive"); + headers.put("Date", LocalDateTime.now().toString()); + return MockedMessageDto.builder() + .clientUUID(uuid) + .mockedResponseId(1) + .mediaType(MediaType.APPLICATION_XML_VALUE) + .messageBody("\n" + + "\n" + + " Tove\n" + + " Jani\n" + + " Reminder\n" + + " Don't forget me this weekend!\n" + + "") + .httpHeaders(headers) + .httpStatus(200) + .build(); + } + + /** + * Finds the highest id in the list and returns it incremented by 1 + * @param messages list of messages + * @return highest id incremented by 1 + */ + public static int findNextId(List messages) { + int highestId = 0; + for (MockedMessageDto m : messages) + highestId = highestId > m.getMockedResponseId() ? highestId : m.getMockedResponseId(); + return ++highestId; + } + + /** + * It's one of the most important features - the bread and butter of the Mocked Service. It's link that allows + * to receive mocked response from the server and use it to mock! + * @param requestEntity Logs the data of request + * @param clientUUID the key-uuid of given set of messages + * @param mockedResponseId unique id of given message + * @return + */ + @GetMapping(value = "/r/{clientUUID}/{mockedResponseId}") + public ResponseEntity getMockedResponse(RequestEntity requestEntity, + @PathVariable UUID clientUUID, + @PathVariable int mockedResponseId) { + TrackingClient.setBusinessKeys(Map.of(BusinessKey.INTERFACE_NAME, "getMockedResponse - request", + BusinessKey.CLIENT_UUID, String.valueOf(clientUUID), + BusinessKey.MESSAGE_ID, String.valueOf(mockedResponseId))); + log.info(requestEntity.toString().replaceAll("\"", "\\\\\"").substring(1)); + TrackingClient.setBusinessKeys(Map.of(BusinessKey.INTERFACE_NAME, "getMockedResponse - response", + BusinessKey.CLIENT_UUID, String.valueOf(clientUUID), + BusinessKey.MESSAGE_ID, String.valueOf(mockedResponseId))); + MockedMessageDto mockedMessageDto = klausService.getMockedResponse(clientUUID, mockedResponseId); + HttpHeaders httpHeaders = new HttpHeaders(); + if (mockedMessageDto.getHttpHeaders() != null) mockedMessageDto.getHttpHeaders().forEach(httpHeaders::set); + return new ResponseEntity<>(mockedMessageDto.getMessageBody(), httpHeaders, + Objects.requireNonNull(HttpStatus.valueOf(mockedMessageDto.getHttpStatus()))); + } + + +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/controller/MvcExceptionHandler.java b/backend/mocked-services/src/main/java/com/r11/tools/controller/MvcExceptionHandler.java new file mode 100644 index 0000000..2c9f582 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/controller/MvcExceptionHandler.java @@ -0,0 +1,47 @@ +package com.r11.tools.controller; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.ConstraintViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + + +/** + * Custom exception handler for {@link ConstraintViolationException} + * @author Rafał Żukowicz + */ +@ControllerAdvice +public class MvcExceptionHandler { + + /** + * Provides handling for {@link ConstraintViolationException} + * @param e exception argument + * @return response with error list and status 400 bad request + */ + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity validationErrorHandler(ConstraintViolationException e){ + List errors = new ArrayList<>(e.getConstraintViolations().size()); + + e.getConstraintViolations().forEach(constraintViolation -> { + errors.add(constraintViolation.getPropertyPath() + " : " + constraintViolation.getMessage()); + }); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + /** + * Provides handling for {@link BindException} + * @param ex exception argument + * @return response with error list and status 400 bad request + */ + @ExceptionHandler(BindException.class) + public ResponseEntity handleBindException(BindException ex){ + return new ResponseEntity(ex.getAllErrors(), HttpStatus.BAD_REQUEST); + } + +} + + diff --git a/backend/mocked-services/src/main/java/com/r11/tools/mappers/MockedMessageMapper.java b/backend/mocked-services/src/main/java/com/r11/tools/mappers/MockedMessageMapper.java new file mode 100644 index 0000000..882ab09 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/mappers/MockedMessageMapper.java @@ -0,0 +1,17 @@ +package com.r11.tools.mappers; + +import com.r11.tools.model.MockedMessage; +import com.r11.tools.model.MockedMessageDto; +import org.mapstruct.*; + +/** + * Creates key value for redis entry + * @author Rafał Źukowicz + */ +@Mapper +public interface MockedMessageMapper { + @Mapping( target = "compositePrimaryKey", expression = "java(mockedMessageDto.getClientUUID() + \"_\"" + + " + mockedMessageDto.getMockedResponseId())") + MockedMessage mockedMessageDtoToMockedMessage(MockedMessageDto mockedMessageDto); + MockedMessageDto mockedMessageToMockedMessageDto(MockedMessage mockedMessage); +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/model/Event.java b/backend/mocked-services/src/main/java/com/r11/tools/model/Event.java new file mode 100644 index 0000000..4bd0b4e --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/model/Event.java @@ -0,0 +1,36 @@ +package com.r11.tools.model; + +import java.time.LocalDateTime; +import lombok.*; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.lang.Nullable; + +/** + * Pojo class for Event entity + * @author Rafał Żukowicz + */ +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class Event implements Comparable{ + + @DateTimeFormat(pattern = "yyyy-MM-ddTHH:mm:ss") + private LocalDateTime dateTimeStamp; + @Nullable + private String interfaceName; + @Nullable + private String clientUUID; + @Nullable + private Integer messageId; + private String thread; + private String level; + @Nullable + private String message; + + @Override + public int compareTo(Event o) { + return this.getDateTimeStamp().compareTo(o.getDateTimeStamp()) * -1; + } +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/model/EventRequestDto.java b/backend/mocked-services/src/main/java/com/r11/tools/model/EventRequestDto.java new file mode 100644 index 0000000..ecfc03f --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/model/EventRequestDto.java @@ -0,0 +1,28 @@ +package com.r11.tools.model; + +import java.time.LocalDateTime; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * Pojo for history query request. Contains information necessary to obtain {@link Event} list + * @author Rafał Żukowicz + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EventRequestDto { + + private UUID clientUUID; + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime localDateTimeFrom; + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime localDateTimeTo; + private Integer mockedResponseId; + +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/model/MockedMessage.java b/backend/mocked-services/src/main/java/com/r11/tools/model/MockedMessage.java new file mode 100644 index 0000000..29989d4 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/model/MockedMessage.java @@ -0,0 +1,40 @@ +package com.r11.tools.model; + +import com.r11.tools.model.constraints.HttpCode; +import java.io.Serializable; +import java.util.Map; +import java.util.UUID; +import javax.validation.constraints.Positive; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; + +/** + * MockedMessage redis entity pojo + * @author Rafał Żukowicz + */ +@Data +@ToString +@RedisHash("mockedMessage") +@NoArgsConstructor +@AllArgsConstructor +public class MockedMessage implements Serializable { + @Id + private String compositePrimaryKey; + @Indexed + private UUID clientUUID; + @Positive + private Integer mockedResponseId; + private String mediaType; + private String messageBody; + private Map httpHeaders; + @HttpCode + private Integer httpStatus; + +} + + diff --git a/backend/mocked-services/src/main/java/com/r11/tools/model/MockedMessageDto.java b/backend/mocked-services/src/main/java/com/r11/tools/model/MockedMessageDto.java new file mode 100644 index 0000000..caae824 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/model/MockedMessageDto.java @@ -0,0 +1,36 @@ +package com.r11.tools.model; + +import com.r11.tools.model.constraints.HttpCode; +import java.io.Serializable; +import java.util.Map; +import java.util.UUID; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Positive; +import lombok.*; + +/** + * Alternative version of {@link MockedMessage} used in http body + * @author Rafał Żukowicz + * @author Gabriel Modzelewski + */ +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class MockedMessageDto implements Serializable, Comparable { + private UUID clientUUID; + @NotNull + @Positive + private Integer mockedResponseId; + private String mediaType; + private String messageBody; + private Map httpHeaders; + @HttpCode + private Integer httpStatus; + + @Override + public int compareTo(MockedMessageDto message) { + return this.mockedResponseId > message.getMockedResponseId() ? 1 : -1; + } +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/model/constraints/HttpCode.java b/backend/mocked-services/src/main/java/com/r11/tools/model/constraints/HttpCode.java new file mode 100644 index 0000000..6d8a992 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/model/constraints/HttpCode.java @@ -0,0 +1,23 @@ +package com.r11.tools.model.constraints; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.validation.Constraint; +import javax.validation.Payload; + +/** + * Annotation interface that is used to annotate Integer fields that contain http status values. + * It provides validation and throws an error when trying to send response with incorrect status. + * @author Rafał Żukowicz + */ +@Target({ ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = HttpCodeValidation.class ) +public @interface HttpCode { + String message() default "must be a valid http code"; + + Class[] groups() default { }; + Class[] payload() default { }; +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/model/constraints/HttpCodeValidation.java b/backend/mocked-services/src/main/java/com/r11/tools/model/constraints/HttpCodeValidation.java new file mode 100644 index 0000000..0780b1f --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/model/constraints/HttpCodeValidation.java @@ -0,0 +1,39 @@ +package com.r11.tools.model.constraints; + +import com.r11.tools.model.MockedMessage; +import com.r11.tools.model.MockedMessageDto; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import org.springframework.http.HttpStatus; + +/** + * It's validator class. It checks if status value of {@link MockedMessageDto} is within bonds of http status values map. + * @author Rafał Żukowicz + */ +public class HttpCodeValidation implements ConstraintValidator { + private Set allowedValues; + + /** + * Initializes {@link #allowedValues} with possible http status values. + * @param targetEnum HttpCode context + */ + @Override + public void initialize(HttpCode targetEnum) { + allowedValues = Stream.of(HttpStatus.values()) + .map(HttpStatus::value) + .collect(Collectors.toSet()); + } + + /** + * @param integer value of {@link MockedMessageDto#getHttpStatus()} or {@link MockedMessage#getHttpStatus()} + * @param context context for validation + * @return true if valid + */ + @Override + public boolean isValid(Integer integer, ConstraintValidatorContext context) { + return allowedValues.contains(integer); + } +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/repository/EventRepository.java b/backend/mocked-services/src/main/java/com/r11/tools/repository/EventRepository.java new file mode 100644 index 0000000..c634732 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/repository/EventRepository.java @@ -0,0 +1,20 @@ +package com.r11.tools.repository; + +import com.r11.tools.model.Event; +import com.r11.tools.utilis.BusinessKey; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +/** + * Event entity dao interface + * @author Rafał Żukowicz + */ +@Repository +@Transactional +public interface EventRepository { + List findEvents(LocalDateTime localDateTimeFrom, LocalDateTime localDateTimeTo, + Map businessKeys); +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/repository/EventRepositoryImpl.java b/backend/mocked-services/src/main/java/com/r11/tools/repository/EventRepositoryImpl.java new file mode 100644 index 0000000..3de0e2a --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/repository/EventRepositoryImpl.java @@ -0,0 +1,99 @@ +package com.r11.tools.repository; + +import static com.r11.tools.utilis.RedisAppender.LOG_PREFIX; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.r11.tools.model.Event; +import com.r11.tools.utilis.BusinessKey; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Repository; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +/** + * Builds Event list based on logs created via {@link com.r11.tools.utilis.TrackingClient} and {@link com.r11.tools.utilis.RedisAppender} + * @author Rafał Żukowicz + */ +@Repository +@AllArgsConstructor +public class EventRepositoryImpl implements EventRepository { + private final JedisPool jedisPool; + private final ObjectMapper objectMapper; + + /** + * Creates list of {@link Event} based on {@link com.r11.tools.model.EventRequestDto} data via searching logs + * @param localDateTimeFrom date from which logs are retrieved + * @param localDateTimeTo date to which logs are retrieved + * @param businessKeys set keys for redis values + * @return + */ + @Override + public List findEvents(LocalDateTime localDateTimeFrom, LocalDateTime localDateTimeTo, + Map businessKeys) { + List eventStrings = findEventsBetweenDates(localDateTimeFrom.toLocalDate(), localDateTimeTo.toLocalDate()); + if (businessKeys.size() > 0) { + eventStrings = businessKeysFilter(eventStrings, businessKeys); + } + List events = parseEvents(eventStrings); + if (localDateTimeFrom.toLocalTime() != LocalTime.MIN) { + events = events.stream().filter(event -> event.getDateTimeStamp().compareTo(localDateTimeFrom) >= 0) + .collect(Collectors.toList()); + } + return events.stream().filter(event -> event.getDateTimeStamp().compareTo(localDateTimeTo) <= 0) + .collect(Collectors.toList()); + } + + /** + * Returns logs between given dates + * @param localDateFrom date from which logs are retrieved + * @param localDateTo date to which logs are retrieved + * @return + */ + private List findEventsBetweenDates(LocalDate localDateFrom, LocalDate localDateTo) { + try (Jedis jedis = jedisPool.getResource()) { + return localDateFrom.datesUntil(localDateTo.plusDays(1)).map(day -> LOG_PREFIX + day.toString()) + .map(key -> jedis.lrange(key, 0, -1)).flatMap(Collection::stream).collect(Collectors.toList()); + } + } + + /** + * Filters keys so only the ones queried are retirned + * @param events list of logs + * @param businessKeys set keys for redis values + * @return filtered list of logs + */ + private List businessKeysFilter(List events, Map businessKeys) { + for (Map.Entry entry : businessKeys.entrySet()) { + String stringPattern = entry.getKey().getReasonPhrase()+ "\"" + ":" + "\"" + entry.getValue() + "\""; + events = events.stream().filter(s -> s.contains(stringPattern)).collect(Collectors.toList()); + } + return events; + } + + /** + * Parses list of logs into list of {@link Event} + * @param eventStrings list of logs + * @return list of {@link Event} + */ + private List parseEvents(List eventStrings) { + List events = new ArrayList<>(); + for (String eventString : eventStrings) { + eventString = eventString.replaceAll("\\R", ""); + try { + events.add(objectMapper.readValue(eventString, Event.class)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return events; + } +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/repository/MockedResponseRepository.java b/backend/mocked-services/src/main/java/com/r11/tools/repository/MockedResponseRepository.java new file mode 100644 index 0000000..21d06a5 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/repository/MockedResponseRepository.java @@ -0,0 +1,23 @@ +package com.r11.tools.repository; + +import com.r11.tools.model.MockedMessage; +import java.util.List; +import java.util.UUID; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +/** + * Spring repository that allows to retrieve message list by key-uuid from redis database + * @author Rafał Żukowicz + */ +@Repository +@Transactional +public interface MockedResponseRepository extends CrudRepository { + /** + * Finds all messages by their uuid + * @param clientUUID the key-uuid of given set of messages + * @return list of {@link com.r11.tools.model.MockedMessage} + */ + List findAllByClientUUID(UUID clientUUID); +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/service/EtrackService.java b/backend/mocked-services/src/main/java/com/r11/tools/service/EtrackService.java new file mode 100644 index 0000000..98c6658 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/service/EtrackService.java @@ -0,0 +1,20 @@ +package com.r11.tools.service; + +import com.r11.tools.model.Event; +import com.r11.tools.model.EventRequestDto; +import java.util.List; +import org.springframework.stereotype.Service; + +/** + * Spring service interface for {@link com.r11.tools.controller.EventController} + * @author Rafał Żukowicz + */ +@Service +public interface EtrackService { + /** + * Searches for {@link Event} objects between date brackets + * @param eventsDto object containing required data for request + * @return list of {@link Event} + */ + List getEventsByDateTimeAndBusinessKeys(EventRequestDto eventsDto); +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/service/EtrackServiceImpl.java b/backend/mocked-services/src/main/java/com/r11/tools/service/EtrackServiceImpl.java new file mode 100644 index 0000000..d630580 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/service/EtrackServiceImpl.java @@ -0,0 +1,45 @@ +package com.r11.tools.service; + +import com.r11.tools.model.Event; +import com.r11.tools.model.EventRequestDto; +import com.r11.tools.repository.EventRepository; +import com.r11.tools.utilis.BusinessKey; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * Spring Service for {@link com.r11.tools.controller.EventController}. Contains logic required for quering + * the database for {@link Event} objects + * @author Rafał Żukowicz + */ +@Slf4j +@Service +@AllArgsConstructor +public class EtrackServiceImpl implements EtrackService { + + private final EventRepository eventRepository; + + /** + * Adds {@link BusinessKey} to {@link EventRequestDto} + * in order to create query via{@link com.r11.tools.repository.EventRepositoryImpl} + * @param eventsDto object containing required data for request + * @return list of {@link Event} + */ + @Override + public List getEventsByDateTimeAndBusinessKeys(EventRequestDto eventsDto) { + Map businessKeys = new HashMap<>(); + businessKeys.put(BusinessKey.CLIENT_UUID, eventsDto.getClientUUID().toString()); + if (eventsDto.getMockedResponseId() != null){ + businessKeys.put(BusinessKey.MESSAGE_ID, String.valueOf(eventsDto.getMockedResponseId())); + } + List events = eventRepository.findEvents(eventsDto.getLocalDateTimeFrom(), eventsDto.getLocalDateTimeTo(), + businessKeys); + Collections.sort(events); + return events; + } +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/service/KlausService.java b/backend/mocked-services/src/main/java/com/r11/tools/service/KlausService.java new file mode 100644 index 0000000..917f878 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/service/KlausService.java @@ -0,0 +1,19 @@ +package com.r11.tools.service; + +import com.r11.tools.model.MockedMessageDto; +import java.util.List; +import java.util.UUID; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +/** + * Service interface for {@link com.r11.tools.controller.KlausController} and {@link com.r11.tools.controller.MockController} + * @author Rafał Żukowicz + */ +@Service +public interface KlausService { + ResponseEntity deleteMockedResponse(UUID clientUUID, int mockedResponseId); + List getAllMockedResponses(UUID clientUUID); + MockedMessageDto getMockedResponse(UUID clientUUID, int mockedResponseId); + ResponseEntity setMockedResponse(MockedMessageDto mockedMessageDto); +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/service/KlausServiceImpl.java b/backend/mocked-services/src/main/java/com/r11/tools/service/KlausServiceImpl.java new file mode 100644 index 0000000..f3cdfaf --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/service/KlausServiceImpl.java @@ -0,0 +1,99 @@ +package com.r11.tools.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.r11.tools.mappers.MockedMessageMapper; +import com.r11.tools.model.MockedMessage; +import com.r11.tools.model.MockedMessageDto; +import com.r11.tools.repository.MockedResponseRepository; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +/** + * Service for {@link com.r11.tools.controller.MockController} and {@link com.r11.tools.controller.MockController} + * Allows for performing CRUD operations on {@link MockedMessageDto} + * @author Rafał Żukowicz + * @author Gabriel Modzelewski + */ +@Slf4j +@Service +@AllArgsConstructor +public class KlausServiceImpl implements KlausService { + private final MockedMessageMapper mockedMessageMapper; + private final MockedResponseRepository mockedResponseRepository; + private final ObjectMapper objectMapper; + + /** + * Removes message of given id in given key-uuid set + * @param clientUUID the key-uuid of given set of messages + * @param mockedResponseId unique id of given message + * @return confirmation and status 200 OK + */ + @Override + public ResponseEntity deleteMockedResponse(UUID clientUUID, int mockedResponseId) { + String key = clientUUID.toString() + "_" + mockedResponseId; + mockedResponseRepository.deleteById(key); + log.info("Message " + mockedResponseId + " has been removed."); + return new ResponseEntity<>("MockedResponse has been removed successfully", + new HttpHeaders(), HttpStatus.ACCEPTED); + } + + /** + * Returns all messages of given key-uuid + * @param clientUUID the key-uuid of given set of messages + * @return List of {@link MockedMessageDto} + */ + @Override + public List getAllMockedResponses(UUID clientUUID) { + return mockedResponseRepository.findAllByClientUUID(clientUUID).stream() + .map(mockedMessageMapper::mockedMessageToMockedMessageDto) + .collect(Collectors.toList()); + } + + /** + * Returns {@link MockedMessageDto} of given id and key-uuid. If message doesn't then empty message is returned + * @param clientUUID the key-uuid of given set of messages + * @param mockedResponseId unique id of given message + * @return {@link MockedMessageDto} object + */ + @SneakyThrows + @Override + public MockedMessageDto getMockedResponse(UUID clientUUID, int mockedResponseId){ + String key = clientUUID.toString() + "_" + mockedResponseId; + Optional optionalMockedMessage = mockedResponseRepository.findById(key); + MockedMessageDto mockedMessageDto = MockedMessageDto.builder() + .clientUUID(clientUUID) + .mockedResponseId(mockedResponseId) + .build(); + if (optionalMockedMessage.isPresent()) { + mockedMessageDto = mockedMessageMapper.mockedMessageToMockedMessageDto(optionalMockedMessage.get()); + log.info(mockedMessageDto.toString().replaceAll("\"", "\\\\\"")); + return mockedMessageDto; + } + log.info(mockedMessageDto.toString().replaceAll("\"", "\\\\\"")); + return mockedMessageDto; + } + + /** + * Allows to modify mocked message. If message of given id and key-uuid doesn't exist a new entry is created + * @param mockedMessageDto message to be saved + * @return Confirmation and status 200 OK + */ + @SneakyThrows + @Override + public ResponseEntity setMockedResponse(MockedMessageDto mockedMessageDto) { + mockedResponseRepository.save(mockedMessageMapper.mockedMessageDtoToMockedMessage(mockedMessageDto)); + log.info(mockedMessageDto.toString().replaceAll("\"", "\\\\\"")); + return new ResponseEntity<>("MockedResponse has been setup successfully!", new HttpHeaders(), + HttpStatus.ACCEPTED); + } + +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/utilis/BusinessKey.java b/backend/mocked-services/src/main/java/com/r11/tools/utilis/BusinessKey.java new file mode 100644 index 0000000..fc90cdd --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/utilis/BusinessKey.java @@ -0,0 +1,24 @@ +package com.r11.tools.utilis; + +import lombok.AllArgsConstructor; + +/** + * Enum of keys for redis database. + * @author Rafał Żukowicz + */ +@AllArgsConstructor +public enum BusinessKey { + INTERFACE_NAME("interfaceName"), + CLIENT_UUID("clientUUID"), + MESSAGE_ID("messageId"); + + private final String phrase; + + /** + * Returns string value of given enum variant + * @return string value of enum + */ + public String getReasonPhrase() { + return this.phrase; + } +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/utilis/RedisAppender.java b/backend/mocked-services/src/main/java/com/r11/tools/utilis/RedisAppender.java new file mode 100644 index 0000000..0626848 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/utilis/RedisAppender.java @@ -0,0 +1,235 @@ +package com.r11.tools.utilis; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Layout; +import ch.qos.logback.core.UnsynchronizedAppenderBase; +import com.cwbase.logback.AdditionalField; +import com.cwbase.logback.JSONEventLayout; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Iterator; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.Protocol; + +/** + * Class is used to insert logs directly to Redis. {@link com.release11.klaus.repository.EventRepositoryImpl} is using those logs. + * @author Rafał Żukowicz + * @author Gabriel Modzelewski + */ +public class RedisAppender extends UnsynchronizedAppenderBase { + + JedisPool pool; + JSONEventLayout jsonlayout; + Layout layout; + String host = "localhost"; + int port = Protocol.DEFAULT_PORT; + String key = null; + int timeout = Protocol.DEFAULT_TIMEOUT; + String password = null; + int database = Protocol.DEFAULT_DATABASE; + + public static final String LOG_PREFIX = "logstash_"; + + + public RedisAppender() { + jsonlayout = new JSONEventLayout(); + } + + /** + * Appends JedisPool by another log + * @param event object containing log info + */ + @Override + protected void append(ILoggingEvent event) { + Jedis client = pool.getResource(); + try { + String json = layout == null ? jsonlayout.doLayout(event) : layout.doLayout(event); + key = LOG_PREFIX + LocalDate.now(); + client.rpush(key, json); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (client != null) { + client.close(); + } + } + } + + @Deprecated + public String getSource() { + return jsonlayout.getSource(); + } + + @Deprecated + public void setSource(String source) { + jsonlayout.setSource(source); + } + + @Deprecated + public String getSourceHost() { + return jsonlayout.getSourceHost(); + } + + @Deprecated + public void setSourceHost(String sourceHost) { + jsonlayout.setSourceHost(sourceHost); + } + + @Deprecated + public String getSourcePath() { + return jsonlayout.getSourcePath(); + } + + @Deprecated + public void setSourcePath(String sourcePath) { + jsonlayout.setSourcePath(sourcePath); + } + + @Deprecated + public String getTags() { + if (jsonlayout.getTags() != null) { + Iterator i = jsonlayout.getTags().iterator(); + StringBuilder sb = new StringBuilder(); + while (i.hasNext()) { + sb.append(i.next()); + if (i.hasNext()) { + sb.append(','); + } + } + return sb.toString(); + } + return null; + } + + @Deprecated + public void setTags(String tags) { + if (tags != null) { + String[] atags = tags.split(","); + jsonlayout.setTags(Arrays.asList(atags)); + } + } + + @Deprecated + public String getType() { + return jsonlayout.getType(); + } + + @Deprecated + public void setType(String type) { + jsonlayout.setType(type); + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getDatabase() { + return database; + } + + public void setDatabase(int database) { + this.database = database; + } + + @Deprecated + public void setMdc(boolean flag) { + jsonlayout.setProperties(flag); + } + + @Deprecated + public boolean getMdc() { + return jsonlayout.getProperties(); + } + + @Deprecated + public void setLocation(boolean flag) { + jsonlayout.setLocationInfo(flag); + } + + @Deprecated + public boolean getLocation() { + return jsonlayout.getLocationInfo(); + } + + @Deprecated + public void setCallerStackIndex(int index) { + jsonlayout.setCallerStackIdx(index); + } + + @Deprecated + public int getCallerStackIndex() { + return jsonlayout.getCallerStackIdx(); + } + + @Deprecated + public void addAdditionalField(AdditionalField p) { + jsonlayout.addAdditionalField(p); + } + + public Layout getLayout() { + return layout; + } + + public void setLayout(Layout layout) { + this.layout = layout; + } + + /** + * Starts new instance of JedisPool + */ + @Override + public void start() { + super.start(); + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setTestOnBorrow(true); + pool = new JedisPool(config, host, port, timeout, password, database); + } + + /** + * Stops and destroys JedisPool object + */ + @Override + public void stop() { + super.stop(); + pool.destroy(); + } + +} diff --git a/backend/mocked-services/src/main/java/com/r11/tools/utilis/TrackingClient.java b/backend/mocked-services/src/main/java/com/r11/tools/utilis/TrackingClient.java new file mode 100644 index 0000000..3f49400 --- /dev/null +++ b/backend/mocked-services/src/main/java/com/r11/tools/utilis/TrackingClient.java @@ -0,0 +1,24 @@ +package com.r11.tools.utilis; + +import java.util.Map; +import org.slf4j.MDC; + +/** + * This static class has one purpose and one purpose only. It logs data about incomming requests. + * The data from logs is received via {@link com.release11.klaus.repository.EventRepositoryImpl} + * @author Rafał Żukowski + */ +public final class TrackingClient { + + /** + * Logs data inside the given map + * @param businessKeysMap map containing all the information about incomming request + */ + public static void setBusinessKeys(Map businessKeysMap){ + for (Map.Entry entry : businessKeysMap.entrySet()) { + MDC.put(entry.getKey().getReasonPhrase(), entry.getValue()); + } + } + + +} diff --git a/backend/mocked-services/src/main/resources/application.properties b/backend/mocked-services/src/main/resources/application.properties new file mode 100644 index 0000000..bd52dd8 --- /dev/null +++ b/backend/mocked-services/src/main/resources/application.properties @@ -0,0 +1,7 @@ +#environment: +server.port = 8097 +spring.application.name = klaus +spring.mvc.view.suffix=.html +logging.level.root=INFO +logging.level.org.springframework.web=INFO +logging.level.com.release11=INFO diff --git a/backend/mocked-services/src/main/resources/data-access.properties b/backend/mocked-services/src/main/resources/data-access.properties new file mode 100644 index 0000000..27196b6 --- /dev/null +++ b/backend/mocked-services/src/main/resources/data-access.properties @@ -0,0 +1,2 @@ +redis.host = redis +redis.port = 6379 \ No newline at end of file diff --git a/backend/mocked-services/src/main/resources/logback.xml b/backend/mocked-services/src/main/resources/logback.xml new file mode 100644 index 0000000..37a10bf --- /dev/null +++ b/backend/mocked-services/src/main/resources/logback.xml @@ -0,0 +1,26 @@ + + + + + + redis + 6379 + logstash + + + + {"dateTimeStamp" : "%d{yyyy-MM-dd}T%d{HH:mm:ss}", "eventId":"%X{eventId}", "interfaceName":"%X{interfaceName}", "clientUUID":"%X{clientUUID}", "messageId":"%X{messageId}", "thread":"%t","level":"%-5level", "message":"%msg"}%n + + + + + + + + + + + + + + diff --git a/backend/mocked-services/src/main/resources/static/css/common.css b/backend/mocked-services/src/main/resources/static/css/common.css new file mode 100644 index 0000000..0c7e754 --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/common.css @@ -0,0 +1,6 @@ +@import url('https://necolas.github.io/normalize.css/8.0.1/normalize.css'); +@import url('r11addons.css'); +@import url('r11tables.css'); +@import url('r11tool.css'); +@import url('r11tooltip.css'); +@import url('r11modal.css'); diff --git a/backend/mocked-services/src/main/resources/static/css/font/fontello.eot b/backend/mocked-services/src/main/resources/static/css/font/fontello.eot new file mode 100644 index 0000000..8eb8762 Binary files /dev/null and b/backend/mocked-services/src/main/resources/static/css/font/fontello.eot differ diff --git a/backend/mocked-services/src/main/resources/static/css/font/fontello.svg b/backend/mocked-services/src/main/resources/static/css/font/fontello.svg new file mode 100644 index 0000000..66886e8 --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/font/fontello.svg @@ -0,0 +1,14 @@ + + + +Copyright (C) 2021 by original authors @ fontello.com + + + + + + + + + + \ No newline at end of file diff --git a/backend/mocked-services/src/main/resources/static/css/font/fontello.ttf b/backend/mocked-services/src/main/resources/static/css/font/fontello.ttf new file mode 100644 index 0000000..a6a2ea2 Binary files /dev/null and b/backend/mocked-services/src/main/resources/static/css/font/fontello.ttf differ diff --git a/backend/mocked-services/src/main/resources/static/css/font/fontello.woff b/backend/mocked-services/src/main/resources/static/css/font/fontello.woff new file mode 100644 index 0000000..8e3c9e6 Binary files /dev/null and b/backend/mocked-services/src/main/resources/static/css/font/fontello.woff differ diff --git a/backend/mocked-services/src/main/resources/static/css/font/fontello.woff2 b/backend/mocked-services/src/main/resources/static/css/font/fontello.woff2 new file mode 100644 index 0000000..b1c349b Binary files /dev/null and b/backend/mocked-services/src/main/resources/static/css/font/fontello.woff2 differ diff --git a/backend/mocked-services/src/main/resources/static/css/fontello.css b/backend/mocked-services/src/main/resources/static/css/fontello.css new file mode 100644 index 0000000..28bc34a --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/fontello.css @@ -0,0 +1,59 @@ +@font-face { + font-family: 'fontello'; + src: url('font/fontello.eot?49304387'); + src: url('font/fontello.eot?49304387#iefix') format('embedded-opentype'), + url('font/fontello.woff2?49304387') format('woff2'), + url('font/fontello.woff?49304387') format('woff'), + url('font/fontello.ttf?49304387') format('truetype'), + url('font/fontello.svg?49304387#fontello') format('svg'); + font-weight: normal; + font-style: normal; + } + /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ + /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ + /* + @media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'fontello'; + src: url('../font/fontello.svg?49304387#fontello') format('svg'); + } + } + */ + + [class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "fontello"; + font-style: normal; + font-weight: normal; + speak: never; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ + } + + .icon-plus:before { content: '\e801'; } /* '' */ + .icon-cancel:before { content: '\e802'; } /* '' */ \ No newline at end of file diff --git a/backend/mocked-services/src/main/resources/static/css/main.css b/backend/mocked-services/src/main/resources/static/css/main.css new file mode 100644 index 0000000..2f8c9ff --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/main.css @@ -0,0 +1,4 @@ +.overflowedTableContent { + max-height: 750px; + overflow: scroll; +} \ No newline at end of file diff --git a/backend/mocked-services/src/main/resources/static/css/r11addons.css b/backend/mocked-services/src/main/resources/static/css/r11addons.css new file mode 100644 index 0000000..afb4425 --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/r11addons.css @@ -0,0 +1,59 @@ +.modification-button.btn-tile:hover { + color: #ca1111; +} + +.modification-button.btn-tile { + width: 10%; + margin: 20% 0 0 0; + font-size: 14px; + color: #00000020 +} + +.modification-button.btn-addtile { + font-size: 38px; + color: #00000030; +} + +.modification-button.btn-addtile:hover { + color: #58ac43; +} + +.tile { + width: 100%; + padding-top: 40%; + border: 1px solid gray; + border-radius: 3px; + position: relative; + background: #f0f0f095; + margin-bottom: 10px; + cursor: default; +} + +.tile:hover { + filter: brightness(110%); +} + +.tile.active { + background: #00000070; + color: white; + filter: none; +} + +.tile.active .btn-tile { + opacity: 0; +} + +.tile .content { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + padding: 0 2% 0 7%; + display: flex; +} + +.content p { + margin: 0; + padding: 0; +} diff --git a/backend/mocked-services/src/main/resources/static/css/r11modal.css b/backend/mocked-services/src/main/resources/static/css/r11modal.css new file mode 100644 index 0000000..7848a43 --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/r11modal.css @@ -0,0 +1,104 @@ +#overlay { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + opacity: 0; + background: rgba(0, 0 , 0, 0.5); + pointer-events: none; +} + +#overlay.active { + pointer-events: all; + opacity: 1; +} + +.modal { + display: none; + width: 390px; + min-height: 71px; + max-height: 700px; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: white; + padding: 5px; + border: 1px solid #f0f0f0; +} + +.modal.active { + display: block; +} + +.modal div.header { + width: 384px; + height: 24px; + background: #2e3133; + color: white; + font-size: 24px; + font-weight: 700; + padding: 3px; + margin-bottom: 5px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal div.header button { + font-size: 100%; + font-family: inherit; + border: 0; + padding: 0; + background: 0; + color: inherit; + cursor: pointer; +} + +.modal div.header button:hover { + color: white; + font-weight: 700; +} + +.modal div.body { + width: 370px; + padding: 10px; + background: #f0f0f0; + color: #2e3133; + min-height: 16px; + text-align: justify; + font-size: 16px; +} + +.modal div.function { + width: 385px; + min-height: 30px; + padding-top: 5px; + display: flex; + justify-content: space-evenly; + background: inherit; +} + +.modal div.function button { + min-height: 22px; + min-width: 34px; + max-width: 74px; + padding: 3px 20px; + outline: none; + border: 1px solid #f0f0f0; + background: rgba(205,205,205,1); + font-size: 16px; + text-align: center; + cursor: pointer; +} + +.modal div.function button:hover { + filter: brightness(110%); +} + +.r-exclamation:before { + content: '!'; + color: #3bc4f1; + font-style: normal; +} diff --git a/backend/mocked-services/src/main/resources/static/css/r11tables.css b/backend/mocked-services/src/main/resources/static/css/r11tables.css new file mode 100644 index 0000000..0a680ab --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/r11tables.css @@ -0,0 +1,71 @@ +.table-map { + width: 60%; +} + +.table-map input{ + font-size: 16px; + padding: 7px; + border: 1px solid rgba(145, 146, 146, 0.849); + border-radius: 5px; +} + +.table-map input.key { + background: #f0f0f0; +} + +.modification-button.btn-add { + font-size: 16px; + color: #00000030; + margin: auto 0 auto 0; +} + +.modification-button.btn-add:hover { + color:#58ac43; +} + +.modification-button.btn-hashmap { + font-size: 16px; + color: #00000030; + margin: auto 0 auto 0; +} + +.modification-button.btn-hashmap:hover { + color: #ca1111; +} + +.table-default { + width: 80%; + border-collapse: collapse; + border-spacing: 0; +} + +.table-default tr { + background: #f0f0f02d; +} + +.table-default tr.bottom-border { + border-bottom: 1px solid black; +} + +.table-default th { + background: #ffffff; +} + +.table-default tr.even { + background: #f0f0f0; +} + +.table-doc td, .table-doc th{ + border-spacing: 0px; + padding: 0px 10px; +} + +.table-doc td { + background-color: rgba(155, 165, 160, 0.342); +} + +.table-doc th { + background-color: #3bc4f1; + text-align: left; + color: white; +} \ No newline at end of file diff --git a/backend/mocked-services/src/main/resources/static/css/r11tool.css b/backend/mocked-services/src/main/resources/static/css/r11tool.css new file mode 100644 index 0000000..2243166 --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/r11tool.css @@ -0,0 +1,300 @@ +@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;700&display=swap'); +.hyperlink, .hyperlink:visited, .hyperlink:active { + color: rgb(47, 125, 146); + cursor: pointer; +} + +.hyperlink:hover { + filter: brightness(120%); +} + +.bordered-field { + border: 2px solid rgba(93, 99, 96, 0.705); + border-radius: 5px; + padding: 8px; + +} + +.bordered-field:focus { + outline: none; + box-shadow: 0 0 5px rgba(81, 203, 238); + border: 2px solid #00000070; +} + +.bordered-field:disabled { + background: #eeeeeed2; +} + +.vertically-resizeable { + resize: vertical; +} + +body { + font-family: 'Nunito', sans-serif; +} + +.container { + display: flex; + justify-content: left; + width: 100%; +} + +.tool { + width: 55%; + display: flex; + justify-content: space-evenly; +} + +.tool.extended { + width: 65%; +} + +.tool .tool-context { + width: 90%; +} + +.tool.extended .tool-context { + width: 75%; +} + +.tool.extended .tool-extention { + width: 20%; + padding-top: 2%; + display: block; +} + +.tool .tool-extention { + display: none; +} + +.tool-extention { + opacity: 0; + pointer-events: none; +} + +.tool-extention.active { + opacity: 100%; + pointer-events: all; +} + +.clickable-text { + padding: 0; + outline: none; + background: none; + border: none; + font-weight: 300; + cursor: pointer; +} + +.clickable-text.highlight:hover { + color: #3bc4f1; +} + +.clickable-text.switch { + font-size: 18px; + font-weight: 700; +} + +.clickable-text.switch span.toggleIndicator:before { + content: '>'; +} + +.clickable-text.switch span.toggleIndicator.active:before { + content: 'v'; +} + +.modification-button { + padding: 0; + outline: none; + background: none; + border: none; + font-weight: 300; +} + +.text-aligned-to-right { + text-align: right; +} + +.centered-vertically { + margin-top: auto; + margin-bottom: auto; +} + +.display-space-between { + width: 100%; + display: flex; + justify-content: space-between; +} + +.display-space-evenly { + display: flex; + justify-content: space-evenly; +} + +.float-left { + display: flex; + justify-content: left; + width: 100%; +} + +.version-span { + font-size: 13px; + font-weight: 400; + color: rgba(85,85,85,0.555); +} + +.block-display { + display: block; +} + +.block-label { + display: block; + margin: 0 0 0 5px; +} + +.tabmenu { + display: flex; + flex-direction: row; + text-align: center; + border-bottom: 1px solid rgba(185, 185, 185, 0.5); +} + +.tabitem { + flex-grow: 1; + cursor: pointer; + padding: 5px 0; +} + +.tabitem:hover { + font-weight: 700; +} + +.tabitem.active { + background: rgba(33, 34, 34, 0.705); + color: white; + font-weight: 700; + cursor:default; + flex-grow: 1; +} + +.big-font { + font-size: 20px; +} + +.action-button.active { + background: #3bc4f1; + border: 1px solid #7ed0eb; + cursor: pointer; +} + +.action-button.active:hover { + filter: brightness(110%); +} + +.action-button { + background: rgba(155, 165, 160, 0.507); + border:1px solid rgba(186, 197, 191, 0.507); + color: white; + padding: 10px 20px; + font-weight: 700; + margin: 3px 0; +} + +.quater-width { + width: 25%; +} + +.half-width { + width: 50%; +} + +.tree-fourth-width { + width: 75%; +} + +.half-width.with-padding { + width: 45%; +} + +.max-width { + width: 100%; +} + +.max-width.with-padding { + width: 94%; +} + +.max-height { + height: 100%; +} + +.height-300 { + height: 300px; +} + +.max-height.with-padding { + height: 90%; +} + +.small-margins { + margin: 3%; +} + +.small-vertical-margin { + margin-top: 10px; + margin-bottom: 10px; +} + +.medium-vertical-margin { + margin-top: 30px; + margin-bottom: 30px; +} + +.large-vertical-margin { + margin-top: 50px; + margin-bottom: 50px; +} + +.textarea-300 { + height: 300px; +} + +.centered-content { + display: flex; + justify-content: center; +} + + +.tabcontent { + display: none; +} + +.tabcontent.active { + display: flex; + justify-content: center; +} + +.hiddable { + display: none; +} + +.hiddable.active { + display: inherit; +} + +/* In case of collision with classes that use 'active' */ +.hidden { + display: none; +} + +/* TODO: Add proper class */ +/* textarea { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} */ + +/* TODO: Add proper class */ +/* code{ + line-height: 150%; +} */ \ No newline at end of file diff --git a/backend/mocked-services/src/main/resources/static/css/r11tooltip.css b/backend/mocked-services/src/main/resources/static/css/r11tooltip.css new file mode 100644 index 0000000..34fcbfb --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/css/r11tooltip.css @@ -0,0 +1,77 @@ +.tooltip-window { + position: fixed; + right: 0; + filter: drop-shadow(-2px 0px 2px darkgray); + background: #e8f3f7; + padding: 15px 30px; + font-family: 'Nunito', sans-serif; + width: 40%; + height: 100%; + overflow: scroll; +} + +.tooltip-window.lite { + width: 30%; +} + +.tip { + display: none; +} + +.tip.active { + display: block; +} + +/* TODO: Remove !important. It's bad practice and it can cause errors in future */ +.section-button { + width: 100%; + padding: 15px 0; + font-size: 18px; + background: #b4b4b4c5; + cursor: pointer; + border-bottom: darkgray 2px solid !important; +} + +.section-button:hover { + backdrop-filter: brightness(110%); +} + +.section-button .active { + background: #00000030; +} + +.List .collapsibleContent { + border-left: #bdc5c9 2px solid; + overflow: hidden; + background: #ffffff50; +} + +/* TODO: .section class is to generic. It should be renamed */ +.section{ + padding: 10px 0px 20px 0px ; +} + +/* TODO: content subclass already in use. Creating content class overrides the subclass. +Make .content a subclass of .content */ +/* .content { + padding: 0px 15px 0px 15px ; + text-align: justify; + overflow: hidden; + transition: max-height .2s ease-out; + max-height: 0px; + border-left: #c0c2c3 2px solid; + +} */ + +.collapsibleMini::before{ + content: "►"; +} + +.collapsibleMini.active::before{ + content: "▼"; +} + +/* TODO: Add proper class */ +/* button:hover{ + filter: brightness(110%); +} */ \ No newline at end of file diff --git a/backend/mocked-services/src/main/resources/static/html/mock.html b/backend/mocked-services/src/main/resources/static/html/mock.html new file mode 100644 index 0000000..76ae45d --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/html/mock.html @@ -0,0 +1,249 @@ + + + + R11 MockedServices + + + + + + + +
+
+
+
+

MockedServices v1.0.0

+
+
+ +

Your Message

+

Messaged id: 1

+ +
+ +
+ +
+ + + +
+
+ +
+ +
+ + + + +
+ +
+ + + + +
+
+ +
+ +
+ + +
+ + + +
+ +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + +
NameValue
+
+ +
+
+ +
+
+
+ + + +
+
+ + + +
+
+ +
+ +
+ + + + + + + + + + +
TimestampType
+
+
+
+
+
+
+
+
+ +
+

Message List

+
+ +
+ +
+
+ +
+
+
+
+
+

What's this?

+

MockedServices is a tool that allows developer to create, in easy and simple way, http server mocked endpoints for integration tests.

+

Help

+

When cursor hovers over an item. It's description is displayed below.

+
+
+

Link

+

Link is an url representing an endpoint at which you can receive your mocked response by simply sending get request.

+
+
+
+
+

Http Status

+

Value of the field is corresponding to status value that server will return.

+
+
+
+
+

Content Type

+

Value of the field describes content of body payload contained in the response. For example if content is in xml format the value should be "application/xml" or "text/xml"

+
+
+
+
+

Body

+

Value of the field describes content of response body. It's basicly the message we want server to return. If it's simple response like 200 OK or 404 not found then field might be left empty.

+
+
+
+
+

Headers

+

Content of this tab allows to set and modify headers that will be included in the response.

+
+
+
+
+

History

+

Content of this tab displays the history of requests or responses received/sent to the endpoint

+
+
+
+
+

New header

+

Insert value in the field and press the plus icon to add a new header to the message.

+
+
+
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/backend/mocked-services/src/main/resources/static/js/datatransfer.js b/backend/mocked-services/src/main/resources/static/js/datatransfer.js new file mode 100644 index 0000000..ce82f5d --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/js/datatransfer.js @@ -0,0 +1,421 @@ +var clientUUID = ''; +var advancedDisplayed = false; +var json = {}; +var jsonIndex = 0; +var lastId = 1; +var htable_row = 0; +var host = getDomain(); +var dataModified = false; +const addMessageName = 'addMessage'; +const loadMessageName = 'changeMessage'; +const removeMessageName = 'removeMessage'; + +const C_UUID = 'mock-uuid'; +const C_ID = 'last-displayed-id'; +const C_ADV = 'advanced-mode'; +const setModified = function(){ + setDataModified(); +} +const setOrigin = function(){ + setDataOrigin(); +} + +const getUpdate = function(){ + updateData(); +} +const dataRefresh = function(){ + getData(); +} +$('#btn-newtile').click(function(){callAddMessage()}); +// $('#btn-addRow').click(function(){addRow()}); +// $('#btn-save').click(getUpdate); + +function getData(){ + $.getJSON(host + '/api/mock/'+clientUUID, function(data) { + json = data; + checkUuid(); + console.log(JSON.stringify(json)); + console.log("Json received"); + refreshData(); + }); +} + + + +function checkUuid(){ + if(clientUUID == null || clientUUID == undefined || clientUUID == ''){ + clientUUID = json[0].clientUUID; + setCookie(); + } +} + +function getDomain(){ + var url = window.location.href; + var arr = url.split("/"); + var result = arr[0] + "//" + arr[2]; + return result; +} + +function setDataModified(){ + if(dataModified) return; + dataModified = true; + $('#btn-save').addClass('active'); + $('#btn-save').click(getUpdate); +} + +//Adding change listener to fields +$('.data-field').change(setModified); + +function setDataOrigin(){ + dataModified = false; + $('#btn-save').removeClass('active'); + $('#btn-save').off(); +} + +const idToDisplay = function(){ + let defaultId = json[0].mockedResponseId; + if(lastId == undefined || lastId == null) return defaultId; + for(let i=0; i 0){ + jsonIndex -= 1; + return json[jsonIndex].mockedResponseId; + } + return defaultId; +} + +function refreshData(){ + fillMessageList(); + console.log("List initiated"); + let id = idToDisplay(); + console.log('Loading message of id: ' + id); + loadMessage(id); + console.log("Message loaded"); +} + +function setCookie(){ + document.cookie = C_UUID + '=' +clientUUID; + document.cookie = C_ID + '=' + lastId; + document.cookie = C_ADV + '=' + advancedVisibility; +} + +function loadCookies(){ + clientUUID = getCookie(C_UUID); + lastId = getCookie(C_ID); + advancedDisplayed = getCookie(C_ADV) == 'true'; +} + +function getCookie(cname) { + var name = cname + '='; + var decodedCookie = decodeURIComponent(document.cookie); + var ca = decodedCookie.split(';'); + for(var i = 0; i '+link+''; + $('#messageLink').html(linkHtml); + $('#httpStatus').val(httpStatus); + $('#typeSelector').val(mediaType); + $('#bodyEditor').val(body); + $('#mockedMessageId').html(id); + +} + +function createLink(uuid, id){ + var link = host + '/api/mock/r/'+uuid+'/'+id; + return link; +} + +function fillHeaderTable(headers){ + var innerHTML = buildHeaderMapHtml(headers); + refreshHeaderTable(innerHTML); +} + +function refreshHeaderTable(html){ + $('#headerMapTable').html(html); + $('.table-map').change(function(){setDataModified()}); + $('.btn-hashmap').click(function(){ + $(this).closest('tr').remove(); + setDataModified(); + }) +} + +function buildHeaderMapHtml(headers){ + var innerHTML = ''; + for(var key in headers){ + innerHTML += buildRowHtml(key, headers[key]); + } + return innerHTML; +} + +function addRow(key, value){ + var headerMap = $('#headerMapTable'); + var headersMapHtml = headerMap.html(); + headersMapHtml += buildRowHtml(key, value); + refreshHeaderTable(headersMapHtml); +} + +const newRowInput = function(){ + const hName = $('#headerKeyInput'); + const hValue = $('#headerValueInput'); + if(checkIfInputValid(hName.val()) && checkIfInputValid(hValue.val())){ + addRow(hName.val(), hValue.val()); + hName.val(null); + hValue.val(null); + setDataModified(); + } +} + +$('#btn-newRow').click(newRowInput); + +function checkIfInputValid(input){ + return !(input == '' || input == null || input == undefined); +} + +function buildRowHtml(key, value){ + return '' + + '' + + '' + + '' + + '' + + ''; +} + + +function fillMessageList(){ + $("#listItems").html(''); + var innerHTML = ''; + for(let i=0; i' + + '
' + + '
' + + '
' + + '

Id: ' + id + '

' + + '

Status: ' + httpStatus + '

' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + ''; + return innerHTML; +} + + + +const onbuild = function(){ + loadCookies(); + getData(); + if(advancedDisplayed) { + changeAdvancedVisibility(); + } +} + +$(document).ready(onbuild); + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + + + +function generateJson(){ + var newJson = + { + clientUUID: json[jsonIndex].clientUUID, + mockedResponseId: json[jsonIndex].mockedResponseId, + mediaType: $('#typeSelector').val(), + messageBody: $('#bodyEditor').val(), + httpStatus: $('#httpStatus').val(), + httpHeaders: {}, + }; + newJson['httpHeaders'] = convertTableToJson(); + console.log(JSON.stringify(newJson, null, 2)); + json[jsonIndex] = newJson; + return newJson; +} + + +function convertTableToJson(){ + const rows = $('#headerMapTable').children(); + console.log("Rows: "+rows.length); + var obj = {}; + var key; + for(let i=0; i' + + '' + historyJson[i].dateTimeStamp + '' + + '' + historyJson[i].interfaceName + '' + + ''; + } + return innerHTML; +} + +function displayHistory(){ + $('#historyTable tbody').html(historyToHtml()); +} diff --git a/backend/mocked-services/src/main/resources/static/js/modal.js b/backend/mocked-services/src/main/resources/static/js/modal.js new file mode 100644 index 0000000..3ea450c --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/js/modal.js @@ -0,0 +1,54 @@ +var modalDisplayed = false; +var methodToCall = { + name: null, + id: null +}; + +const overlay = $('#overlay'); +const savedModal = $('#modal-confirm'); +const dataLossModal = $('#modal-query'); +const dataLossModalYes = dataLossModal.children().eq(2).children().eq(0); +const dataLossModalNo = dataLossModal.children().eq(2).children().eq(1); +const allModals = $('.modal'); +const btnModalClose = $('.modal button'); +const closeModals = function() { + hideModal(allModals); +} +const savedModalDisplay = function() { + console.log('SavedModal displayed'); + showModal(savedModal); + setTimeout(closeModals, 2000); +} +const dataLossModalDisplay = function(){ + showModal(dataLossModal); +} + +function setMethodToCall(name, id){ + methodToCall.name = name; + methodToCall.id = id; +} + +const dropChangesAndClose = function(){ + callMethodByName(methodToCall) + hideModal(dataLossModal); +} + +function showModal(jmodal){ + if(modalDisplayed) return; + overlay.addClass('active'); + jmodal.addClass('active'); + modalDisplayed = true; +} + +function hideModal(jmodal){ + if(!modalDisplayed) return; + overlay.removeClass('active'); + jmodal.removeClass('active'); + modalDisplayed = false; +} + +btnModalClose.click(closeModals); +overlay.click(closeModals); +dataLossModalNo.click(closeModals); +dataLossModalYes.click(dropChangesAndClose); + diff --git a/backend/mocked-services/src/main/resources/static/js/uianimation.js b/backend/mocked-services/src/main/resources/static/js/uianimation.js new file mode 100644 index 0000000..2d7add0 --- /dev/null +++ b/backend/mocked-services/src/main/resources/static/js/uianimation.js @@ -0,0 +1,116 @@ +var advancedVisibility = false; +var selectMenu = $("#selectMenuContent"); +var advancedTab = $("#advanced"); +var basicID = $("#basicItemData") +var advancedID = $("#advancedItemData"); +var focusedField = false; +function changeAdvancedVisibility(){ + if(advancedVisibility){ + selectMenu.removeClass('active'); + advancedTab.removeClass('active'); + advancedID.removeClass('active'); + basicID.addClass('active'); + advancedVisibility = false; + } + else { + selectMenu.addClass('active'); + advancedTab.addClass('active'); + advancedID.addClass('active'); + basicID.removeClass('active'); + advancedVisibility = true; + } + setCookie(); +} + +const historyFilter = $('#history-filter'); +const historyFilterSwitch = function(){ + historyFilter.toggleClass('active'); +} + +$("#optional").click(changeAdvancedVisibility); +$('#historyTab').click(showHistory); +$('#btn-history-filter').click(historyFilterSwitch); + + + +const tabitem = $('.tabitem'); +function showHistory(){ + $('#headersTab').click(showHeaders); + tabitem.removeClass('active'); + $('.tabcontent').removeClass('active'); + $('#history').addClass('active'); + $('#historyTab').addClass('active'); + $('#historyTab').off('click'); + initializeHistory(); +} + +function initializeHistory(){ + historyFilter.removeClass('active'); + getLast24hHistoryData(); +} + + + +function showHeaders(){ + $('#historyTab').click(showHistory); + tabitem.removeClass('active'); + $('.tabcontent').removeClass('active'); + $('#headers').addClass('active'); + $('#headersTab').addClass('active'); + $('#headersTab').off('click'); +} + +function focusInTip(element){ + showTip(element); + focusedField = true; +} + +function focusOutTip(element){ + focusedField = false; + hidTip(element); +} + +function hidTip(element){ + if(focusedField) return; + $('#'+element).removeClass('active'); +} + +function showTip(element){ + if(focusedField) return; + $('.tip').removeClass('active'); + $('#'+element).addClass('active'); +} + +$('#messageLink').mouseover(function(){showTip('messageLinkTip')}); +$('#messageLink').mouseleave(function(){hidTip('messageLinkTip')}); + +$('#httpStatus').mouseover(function(){showTip('httpStatusTip')}); +$('#httpStatus').focusin(function(){focusInTip('httpStatusTip')}); +$('#httpStatus').mouseleave(function(){hidTip('httpStatusTip')}); +$('#httpStatus').focusout(function(){focusOutTip('httpStatusTip')}); + +$('#typeSelector').mouseover(function(){showTip('typeSelectorTip')}); +$('#typeSelector').focusin(function(){focusInTip('typeSelectorTip')}); +$('#typeSelector').mouseleave(function(){hidTip('typeSelectorTip')}); +$('#typeSelector').focusout(function(){focusOutTip('typeSelectorTip')}); + +$('#bodyEditor').mouseover(function(){showTip('bodyEditorTip')}); +$('#bodyEditor').focusin(function(){focusInTip('bodyEditorTip')}); +$('#bodyEditor').mouseleave(function(){hidTip('bodyEditorTip')}); +$('#bodyEditor').focusout(function(){focusOutTip('bodyEditorTip')}); + +$('#headersTab').mouseover(function(){showTip('headersTabTip')}); +$('#headersTab').mouseleave(function(){hidTip('headersTabTip')}); + +$('#historyTab').mouseover(function(){showTip('historyTabTip')}); +$('#historyTab').mouseleave(function(){hidTip('historyTabTip')}); + +$('#headerKeyInput').mouseover(function(){showTip('newHeaderTip')}); +$('#headerKeyInput').focusin(function(){focusInTip('newHeaderTip')}); +$('#headerKeyInput').mouseleave(function(){hidTip('newHeaderTip')}); +$('#headerKeyInput').focusout(function(){focusOutTip('newHeaderTip')}); + +$('#headerValueInput').mouseover(function(){showTip('newHeaderTip')}); +$('#headerValueInput').focusin(function(){focusInTip('newHeaderTip')}); +$('#headerValueInput').mouseleave(function(){hidTip('newHeaderTip')}); +$('#headerValueInput').focusout(function(){focusOutTip('newHeaderTip')}); diff --git a/backend/pom.xml b/backend/pom.xml new file mode 100644 index 0000000..e6ca669 --- /dev/null +++ b/backend/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + com.r11.tools + release11-tools + 1.0-SNAPSHOT + + + 11 + 11 + UTF-8 + 2.14.1 + 2.0.6 + + + pom + + + xslt-rest + mocked-services + + \ No newline at end of file diff --git a/REST_XSLT/Dockerfile b/backend/xslt-rest/Dockerfile similarity index 90% rename from REST_XSLT/Dockerfile rename to backend/xslt-rest/Dockerfile index 282927f..4b1b3f7 100644 --- a/REST_XSLT/Dockerfile +++ b/backend/xslt-rest/Dockerfile @@ -1,10 +1,10 @@ # Using maven image, based on java 8 FROM maven:3.6.3-jdk-8 as target -WORKDIR /build -COPY pom.xml . -COPY src/ /build/src/ +WORKDIR build +COPY ./ ./ RUN mvn -ntp package + # Go to working directory in docker image # WORKDIR /usr/app/ diff --git a/backend/xslt-rest/pom.xml b/backend/xslt-rest/pom.xml new file mode 100644 index 0000000..db97ca3 --- /dev/null +++ b/backend/xslt-rest/pom.xml @@ -0,0 +1,140 @@ + + + 4.0.0 + + com.r11.tools + xslt-rest + 1.0-SNAPSHOT + + + 11 + 11 + UTF-8 + 2.14.1 + 2.0.6 + + + + + + + + + + + + + + + + + + + + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + maven-assembly-plugin + 3.3.0 + + + + + true + lib/ + com.r11.tools.xslt.Main + + + + jar-with-dependencies + + + + + + make-assembly + package + + single + + + + + + + + + + + + com.sparkjava + spark-core + 2.9.3 + + + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + + net.sf.saxon + Saxon-HE + 11.4 + + + xalan + xalan + 2.7.2 + + + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + runtime + + + org.slf4j + slf4j-api + ${slf4j.version} + runtime + + + org.slf4j + slf4j-log4j12 + ${slf4j.version} + runtime + + + + org.apache.logging.log4j + log4j-core + 2.19.0 + + + + \ No newline at end of file diff --git a/REST_XSLT/src/main/java/r11/mltx/restxslt/Main.java b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/Main.java similarity index 89% rename from REST_XSLT/src/main/java/r11/mltx/restxslt/Main.java rename to backend/xslt-rest/src/main/java/com/r11/tools/xslt/Main.java index 42e540d..0ed6142 100644 --- a/REST_XSLT/src/main/java/r11/mltx/restxslt/Main.java +++ b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/Main.java @@ -1,4 +1,4 @@ -package r11.mltx.restxslt; +package com.r11.tools.xslt; /** * Application initializer diff --git a/REST_XSLT/src/main/java/r11/mltx/restxslt/SparkInitializer.java b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/SparkInitializer.java similarity index 98% rename from REST_XSLT/src/main/java/r11/mltx/restxslt/SparkInitializer.java rename to backend/xslt-rest/src/main/java/com/r11/tools/xslt/SparkInitializer.java index 2cd00c3..8464c6f 100644 --- a/REST_XSLT/src/main/java/r11/mltx/restxslt/SparkInitializer.java +++ b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/SparkInitializer.java @@ -1,9 +1,9 @@ -package r11.mltx.restxslt; +package com.r11.tools.xslt; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; -import r11.mltx.restxslt.processors.Saxon; -import r11.mltx.restxslt.processors.Xalan; +import com.r11.tools.xslt.processors.Saxon; +import com.r11.tools.xslt.processors.Xalan; import spark.*; import java.util.HashMap; diff --git a/REST_XSLT/src/main/java/r11/mltx/restxslt/processors/NewNamespaceResolver.java b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/NewNamespaceResolver.java similarity index 98% rename from REST_XSLT/src/main/java/r11/mltx/restxslt/processors/NewNamespaceResolver.java rename to backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/NewNamespaceResolver.java index c6fb17c..f89da0b 100644 --- a/REST_XSLT/src/main/java/r11/mltx/restxslt/processors/NewNamespaceResolver.java +++ b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/NewNamespaceResolver.java @@ -1,4 +1,4 @@ -package r11.mltx.restxslt.processors; +package com.r11.tools.xslt.processors; import net.sf.saxon.om.NamespaceBinding; import net.sf.saxon.om.NamespaceMap; diff --git a/REST_XSLT/src/main/java/r11/mltx/restxslt/processors/Saxon.java b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/Saxon.java similarity index 98% rename from REST_XSLT/src/main/java/r11/mltx/restxslt/processors/Saxon.java rename to backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/Saxon.java index 0aabc97..34ee6a4 100644 --- a/REST_XSLT/src/main/java/r11/mltx/restxslt/processors/Saxon.java +++ b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/Saxon.java @@ -1,4 +1,4 @@ -package r11.mltx.restxslt.processors; +package com.r11.tools.xslt.processors; import net.sf.saxon.s9api.*; import javax.xml.transform.stream.StreamSource; diff --git a/REST_XSLT/src/main/java/r11/mltx/restxslt/processors/Xalan.java b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/Xalan.java similarity index 99% rename from REST_XSLT/src/main/java/r11/mltx/restxslt/processors/Xalan.java rename to backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/Xalan.java index 5f68156..b85a089 100644 --- a/REST_XSLT/src/main/java/r11/mltx/restxslt/processors/Xalan.java +++ b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/Xalan.java @@ -1,4 +1,4 @@ -package r11.mltx.restxslt.processors; +package com.r11.tools.xslt.processors; import org.w3c.dom.Document; import org.xml.sax.InputSource; diff --git a/REST_XSLT/src/main/java/r11/mltx/restxslt/processors/XalanNamespaceResolver.java b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/XalanNamespaceResolver.java similarity index 99% rename from REST_XSLT/src/main/java/r11/mltx/restxslt/processors/XalanNamespaceResolver.java rename to backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/XalanNamespaceResolver.java index 5fb932c..ca6d194 100644 --- a/REST_XSLT/src/main/java/r11/mltx/restxslt/processors/XalanNamespaceResolver.java +++ b/backend/xslt-rest/src/main/java/com/r11/tools/xslt/processors/XalanNamespaceResolver.java @@ -1,4 +1,4 @@ -package r11.mltx.restxslt.processors; +package com.r11.tools.xslt.processors; import org.w3c.dom.*; diff --git a/REST_XSLT/src/main/resources/log4j.properties b/backend/xslt-rest/src/main/resources/log4j.properties similarity index 100% rename from REST_XSLT/src/main/resources/log4j.properties rename to backend/xslt-rest/src/main/resources/log4j.properties diff --git a/docker-compose.yml b/docker-compose.yml index 54fc72d..a0163c7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,10 @@ version: "3" services: + redis: + image: 'redis' + restart: "no" + xmltools-frontend: build: ./Homepage container_name: xmltools-frontend @@ -9,8 +13,25 @@ services: - 8086:80 xmltools-backend: - build: ./REST_XSLT + build: ./backend/xslt-rest container_name: xmltools-backend image: xmltools-backend ports: - - 8081:8081 \ No newline at end of file + - 8081:8081 + + xmltools-mocked-services: + build: + context: ./backend/mocked-services + dockerfile: Dockerfile + container_name: xmltools-mocked-services + restart: "no" + ports: + - "8097:8097" + depends_on: + - redis + environment: + SPRING_PROFILES_ACTIVE: DEV + +networks: + default: + name: shared_network