Compare commits
177 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 64ae2d044d | |||
| 994804b640 | |||
| 9265c0a051 | |||
| 71f9ae9553 | |||
| 19de505ca4 | |||
| 13d51c05d8 | |||
| 0bd80b1878 | |||
| bd565ffd7d | |||
| 60922ea3c7 | |||
| 02ac7e09d3 | |||
| 353d95e377 | |||
| 09409dc698 | |||
| 8f6047bb9f | |||
| 715facf35b | |||
| 5ef85cb484 | |||
| 6874e463fa | |||
| 0844525f8e | |||
| 04fc103016 | |||
| 599bcf96b2 | |||
| 6d104948cc | |||
| 9135a9221f | |||
| c440b08bdf | |||
| a3d781f477 | |||
| dcad69d43c | |||
| 02f46169d9 | |||
| fd95d7e845 | |||
| c5190f7b62 | |||
| b0b930926c | |||
| a90cbb938f | |||
| eb8518afab | |||
| dee92f0e3d | |||
| 773e2ac17e | |||
| ff7e7461e9 | |||
| 50a8082f32 | |||
| 28e76b2374 | |||
| d6c2c863eb | |||
| 4d7c0d6acd | |||
| 24c9c2fe5a | |||
| f96f8270ce | |||
| 3c927f91cf | |||
| d4c4d38aef | |||
| 7042959ee3 | |||
| 15830d7adf | |||
| c7d34ed05f | |||
| 1e56b2885a | |||
| 9bc3370f3b | |||
| 5db10ab1c1 | |||
| d9a413f88d | |||
| c2095f4bcf | |||
| fde28a5880 | |||
| 75c31a7c58 | |||
| a95c745ae2 | |||
| 8083d7fa63 | |||
| ac908e49e6 | |||
| 05857a7d7e | |||
| 28a9a1f670 | |||
| 169ba0d2d1 | |||
| d3c02e164f | |||
| 9b212ccb17 | |||
| f295fcc693 | |||
| 8524ecd6ca | |||
| d1d54c82f9 | |||
| eed5454755 | |||
| 89d70b32d2 | |||
| 5032985787 | |||
| ec5cc7cc0c | |||
| bd5c615017 | |||
| 84203c825c | |||
| d7926a64e2 | |||
| 65a730105d | |||
| 56a9906c58 | |||
| 8f3d1a13de | |||
| 91dc131fba | |||
| d4791a1959 | |||
| 8381a3e4de | |||
| e37ce3c678 | |||
| cc7664519e | |||
| 935b1d01ea | |||
| 947e5d621e | |||
| f5c8910277 | |||
| b9e2e527fd | |||
| 153a7313ef | |||
| 4955a7cc4f | |||
| 78cc13a661 | |||
| e2cf490f9d | |||
| 8c132d12d3 | |||
| df8b7cd007 | |||
| 19c47d3cd2 | |||
| cb48f3017a | |||
| db02993bde | |||
| 3f5aa29a3b | |||
| d6bacc6b4f | |||
| 8254f9387f | |||
| 9aa6593608 | |||
| c76b677f0e | |||
| 829bdeca81 | |||
| 8e18fd9964 | |||
| 134696f73c | |||
| bb18cb53c6 | |||
| 4b3fac59c0 | |||
| 86e46ad205 | |||
| 1f4d4056c7 | |||
| 1760b89e05 | |||
| 3c3f2ef8ad | |||
| 9ef61086df | |||
| a2b4f28c26 | |||
| 2907d40a2e | |||
| 1d648890cc | |||
| 65ad318a5c | |||
| ce02db8f6d | |||
| b487c5f0bc | |||
| e994881ff0 | |||
| e6e3a704bc | |||
| 84aef5d830 | |||
| d35103422b | |||
| 9a6e8c543e | |||
| 81cc70b5e7 | |||
| 6a0e49546c | |||
| c14cfe8a52 | |||
| 6e53f2f1bf | |||
| fa1a03f6e3 | |||
| c537bed4a1 | |||
| 45e46de8f5 | |||
| 58ef4dcdc4 | |||
| f8d5934998 | |||
| 4b241978e0 | |||
| 45a7e60ae4 | |||
| 5a2164b839 | |||
| d2e5586792 | |||
| 96f80e4589 | |||
| 07f2adcc1f | |||
| 0c5a83d3b6 | |||
| 9384011377 | |||
| c0a54291ae | |||
| e408840f6d | |||
| 7dbf3add28 | |||
| 06458cfd10 | |||
| f68217f83a | |||
| 07ab4f83a1 | |||
| b5018dfece | |||
| 7b05343e69 | |||
| 9bd74d036c | |||
| 9ce0e6fcd3 | |||
| 9409638b5c | |||
| dd695a1c2d | |||
| 74f1f6f1c2 | |||
| c0f117a4af | |||
| e0f7c48801 | |||
| e50a9dd95a | |||
| 9dad3e6d37 | |||
| 10d23e456c | |||
| 656aeb17dc | |||
| ce417f8b94 | |||
| 598f074654 | |||
| 3817b1ac5a | |||
| a914282a5d | |||
| 0b0f0b55d0 | |||
| 228f94569b | |||
| 6683ef688e | |||
| e8352dfe3d | |||
| e714c34726 | |||
| 17299a9a94 | |||
| 8bac9db2a9 | |||
| fadd8f468b | |||
| c48d139e6b | |||
| 9a1c690e46 | |||
| 7657691019 | |||
| fb673b100b | |||
| d328d4718f | |||
| 64acb901ff | |||
| 8f4e42c533 | |||
| 5655e46112 | |||
| 37cfacd1e5 | |||
| d05a8a5ce2 | |||
| cf47fb09fe | |||
| 916f4321ea | |||
| 9d7a62c37c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,3 +7,5 @@
|
||||
.vscode
|
||||
nbactions.xml
|
||||
target/
|
||||
__pycache__
|
||||
venv
|
||||
|
||||
3
Backend-libXML/.gitignore
vendored
Normal file
3
Backend-libXML/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea
|
||||
__pycache**
|
||||
venv
|
||||
8
Backend-libXML/Dockerfile
Normal file
8
Backend-libXML/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM tiangolo/meinheld-gunicorn-flask:python3.9
|
||||
|
||||
COPY ./requirements.txt /app/requirements.txt
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
COPY ./main.py /app/
|
||||
COPY ./Parser.py /app/
|
||||
101
Backend-libXML/Parser.py
Normal file
101
Backend-libXML/Parser.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from lxml import etree
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
def formatXML(source: str, prettify: bool) -> str:
|
||||
"""Method used to format XML
|
||||
|
||||
:param source: XML to format
|
||||
:param prettify: sets if XML must be prettified
|
||||
(added indentations etc.) or not
|
||||
:return: formatted XML
|
||||
"""
|
||||
|
||||
# Prolog is removed when XML is parsed
|
||||
# so program has to copy it
|
||||
prolog = ""
|
||||
prolog_start = source.find("<?")
|
||||
|
||||
if prolog_start != -1:
|
||||
prolog_end = source.find("?>") + 2
|
||||
prolog = source[prolog_start:prolog_end]
|
||||
source = source[prolog_end: ]
|
||||
|
||||
byte_input = BytesIO(source.encode("utf-8"))
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
xml = etree.parse(byte_input, parser=parser)
|
||||
|
||||
if prettify:
|
||||
prolog += "\n"
|
||||
|
||||
return prolog + etree.tostring(xml, pretty_print=prettify).decode()
|
||||
|
||||
|
||||
def xpath(source: str, xpath: str) -> str:
|
||||
"""
|
||||
Method used to get nodes from XML string using XPath
|
||||
|
||||
:param source: XML string used for selection
|
||||
:param xpath: XPath query used for selection
|
||||
:return: Nodes selected using XPath
|
||||
"""
|
||||
|
||||
byte_input = BytesIO(source.encode("utf-8"))
|
||||
root = etree.parse(byte_input).getroot()
|
||||
nsmap = root.nsmap
|
||||
|
||||
# LXML doesn't accept empty (None) namespace prefix,
|
||||
# so it need to be deleted if exists
|
||||
if None in nsmap:
|
||||
nsmap.pop(None)
|
||||
|
||||
result = root.xpath(xpath, namespaces=nsmap)
|
||||
|
||||
# root.xpath can return 4 types: float, string, bool and list.
|
||||
# List is the only one that can't be simply converted to str
|
||||
if type(result) is not list:
|
||||
return str(result)
|
||||
else:
|
||||
result_string = ""
|
||||
for e in result:
|
||||
result_string += etree.tostring(e, pretty_print=True).decode() + "\n"
|
||||
return result_string
|
||||
|
||||
|
||||
|
||||
def xsd(source: str, xsd: str) -> bool:
|
||||
"""
|
||||
Method used to validate XML string against XSD schema
|
||||
:param source: XML string used for validation
|
||||
:param xsd: XSD schema to validate XML against
|
||||
:return: Message saying, if the validation was successful or not
|
||||
"""
|
||||
|
||||
schema_input = BytesIO(xsd.encode("utf-8"))
|
||||
xml_schema = etree.XMLSchema(etree.parse(schema_input).getroot())
|
||||
|
||||
document_input = BytesIO(source.encode("utf-8"))
|
||||
xml = etree.parse(document_input).getroot()
|
||||
|
||||
if xml_schema.validate(xml):
|
||||
return "XML is valid."
|
||||
else:
|
||||
return "XML is NOT valid."
|
||||
|
||||
|
||||
def xslt(source: str, xslt: str) -> str:
|
||||
"""
|
||||
Method used to transform XML string using XSLT
|
||||
|
||||
:param source: XML string to transform
|
||||
:param xslt: XSLT string used to transform XML
|
||||
:return: Result of transformation
|
||||
"""
|
||||
xslt_input = BytesIO(xslt.encode("utf-8"))
|
||||
xslt_transform = etree.XSLT(etree.parse(xslt_input))
|
||||
|
||||
document_input = BytesIO(source.encode("utf-8"))
|
||||
xml = etree.parse(document_input).getroot()
|
||||
|
||||
transformed = str(xslt_transform(xml))
|
||||
return formatXML(transformed, True)
|
||||
83
Backend-libXML/main.py
Normal file
83
Backend-libXML/main.py
Normal file
@@ -0,0 +1,83 @@
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from flask import request
|
||||
from lxml import etree
|
||||
import json
|
||||
import time
|
||||
import Parser
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
cors = CORS(app, resource={
|
||||
r"/*":{
|
||||
"origins":"*"
|
||||
}
|
||||
})
|
||||
|
||||
def process_xml(request: request, type: str) -> str:
|
||||
"""Function to process
|
||||
|
||||
:param request: Received request
|
||||
:param type: Type of needed processing: xsd, xslt or xpath
|
||||
:raises ValueError: is raised when type is different than those provided above
|
||||
:return: response JSON converted to string and response code
|
||||
"""
|
||||
start = time.time_ns()
|
||||
code = 200
|
||||
response_json = dict()
|
||||
try:
|
||||
request_data = json.loads(request.get_data(as_text=True))
|
||||
data = request_data['data']
|
||||
process = request_data['process']
|
||||
if (type == "xsd"):
|
||||
response_json['result'] = Parser.xsd(data, process)
|
||||
elif (type == "xslt"):
|
||||
response_json['result'] = Parser.xslt(data, process)
|
||||
elif (type == "xpath"):
|
||||
response_json['result'] = Parser.xpath(data, process)
|
||||
elif (type == "prettify"):
|
||||
response_json['result'] = Parser.formatXML(data, True)
|
||||
elif (type == "minimize"):
|
||||
response_json['result'] = Parser.formatXML(data, False)
|
||||
else:
|
||||
raise ValueError("Valid operation types are: xsd, xslt, xpath")
|
||||
|
||||
response_json['status'] = "OK"
|
||||
except KeyError as e:
|
||||
response_json['result'] = "Missing key: " + str(e)
|
||||
response_json['status'] = "ERR"
|
||||
code = 400
|
||||
except Exception as e:
|
||||
response_json['result'] = str(e)
|
||||
response_json['status'] = "ERR"
|
||||
code = 400
|
||||
finally:
|
||||
exec_time = (time.time_ns() - start) / 10**6
|
||||
response_json['time'] = f"{exec_time:.03f}"
|
||||
response_json['processor'] = "libxml2 over lxml"
|
||||
return json.dumps(response_json), code
|
||||
|
||||
|
||||
@app.route("/xpath", methods=["POST"])
|
||||
def xpath():
|
||||
return process_xml(request, "xpath")
|
||||
|
||||
@app.route("/xsd", methods=["POST"])
|
||||
def xsd():
|
||||
return process_xml(request, "xsd")
|
||||
|
||||
@app.route("/xslt", methods=["POST"])
|
||||
def xslt():
|
||||
return process_xml(request, "xslt")
|
||||
|
||||
@app.route("/prettify", methods=["POST"])
|
||||
def prettify():
|
||||
return process_xml(request, "prettify")
|
||||
|
||||
@app.route("/minimize", methods=["POST"])
|
||||
def minimize():
|
||||
return process_xml(request, "minimize")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
3
Backend-libXML/requirements.txt
Normal file
3
Backend-libXML/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
lxml
|
||||
flask
|
||||
flask_cors
|
||||
19
Backend/mocked-services/Dockerfile
Normal file
19
Backend/mocked-services/Dockerfile
Normal file
@@ -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"]
|
||||
48
Backend/mocked-services/README.md
Normal file
48
Backend/mocked-services/README.md
Normal file
@@ -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.
|
||||
20
Backend/mocked-services/docker-compose.yml
Normal file
20
Backend/mocked-services/docker-compose.yml
Normal file
@@ -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
|
||||
114
Backend/mocked-services/pom.xml
Normal file
114
Backend/mocked-services/pom.xml
Normal file
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.1.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<artifactId>mocked-services</artifactId>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<jedis.version>3.3.0</jedis.version>
|
||||
<logback-redis-appender.version>1.1.6</logback-redis-appender.version>
|
||||
<assertj.version>3.16.1</assertj.version>
|
||||
<mapstruct.version>1.3.1.Final</mapstruct.version>
|
||||
<docker.image.prefix>Release11</docker.image.prefix>
|
||||
<docker.image.name>${project.artifactId}</docker.image.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.cwbase</groupId>
|
||||
<artifactId>logback-redis-appender</artifactId>
|
||||
<version>${logback-redis-appender.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>${jedis.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jettison</groupId>
|
||||
<artifactId>jettison</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<layers>
|
||||
<enabled>true</enabled>
|
||||
<includeLayerTools>true</includeLayerTools>
|
||||
</layers>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg>
|
||||
</compilerArgs>
|
||||
<source>13</source>
|
||||
<target>13</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, Object> redisTemplate() {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setConnectionFactory(jedisConnectionFactory());
|
||||
redisTemplate.setExposeConnection(true);
|
||||
redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.r11.tools.model.EventRequestDto;
|
||||
import com.r11.tools.service.EtrackService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* It's the REST api for {@link com.r11.tools.model.Event}
|
||||
* @author Gabriel Modzelewski
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
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 lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Returns the homepage and provides the api for javascript async requests.
|
||||
* @author Gabriel Modzelewski
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/mock")
|
||||
@AllArgsConstructor
|
||||
public class MockController {
|
||||
private final KlausService klausService;
|
||||
private final Logger log = LogManager.getRootLogger();
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public void errorHandler(Exception ex){
|
||||
log.error(ex.getStackTrace());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates queried message with given set of data
|
||||
* @param body {@link MockedMessageDto} json representation
|
||||
* @return confirmation and 200 OK
|
||||
*/
|
||||
@SneakyThrows
|
||||
@PutMapping
|
||||
public ResponseEntity<String> 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<MockedMessageDto> getListOfMessages(@PathVariable(required = false) String uuidValue){
|
||||
UUID clientUUID;
|
||||
if(uuidValue == null || uuidValue.equals("")) clientUUID = UUID.randomUUID();
|
||||
else clientUUID = UUID.fromString(uuidValue);
|
||||
List<MockedMessageDto> messages = klausService.getAllMockedResponses(clientUUID);
|
||||
if(messages.size() == 0) {
|
||||
klausService.setMockedResponse(buildDefaultMessage(clientUUID));
|
||||
messages = klausService.getAllMockedResponses(clientUUID);
|
||||
}
|
||||
Collections.sort(messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* If provided UUID does not exist in database returns ResponseEntity with new generated UUID(if previous UUID is not provided),
|
||||
* or old UUID(if previous UUID is provided). If provided UUID exists function returns provided UUID.
|
||||
* @param givenUUIDValue the UUID client wants to check exsitance in database
|
||||
* @param previousUUIDValue the previous UUID used by client(optional variable)
|
||||
* @return ResponseEntity with UUID
|
||||
*/
|
||||
@RequestMapping(
|
||||
method = RequestMethod.GET ,
|
||||
path = {"/check/{givenUUIDValue}/{previousUUIDValue}",
|
||||
"/check/{givenUUIDValue}"})
|
||||
public ResponseEntity<String> checkUUID(
|
||||
@PathVariable String givenUUIDValue
|
||||
,@PathVariable(required = false) String previousUUIDValue ){
|
||||
try{
|
||||
UUID.fromString(givenUUIDValue);
|
||||
} catch (IllegalArgumentException ex){
|
||||
log.error("Wrong UUID value!");
|
||||
if (previousUUIDValue == null || previousUUIDValue.equals("")){
|
||||
UUID newUUID = UUID.randomUUID();
|
||||
log.info("New UUID generated.");
|
||||
return ResponseEntity.ok(newUUID.toString());
|
||||
}
|
||||
log.info("Previous UUID value restored.");
|
||||
return ResponseEntity.ok(previousUUIDValue);
|
||||
}
|
||||
return ResponseEntity.ok(givenUUIDValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String> addNewMessage(@PathVariable String uuidValue){
|
||||
UUID clientUUID = UUID.fromString(uuidValue);
|
||||
List<MockedMessageDto> 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<String> 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<String, String> 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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<note>\n" +
|
||||
" <to>Tove</to>\n" +
|
||||
" <from>Jani</from>\n" +
|
||||
" <heading>Reminder</heading>\n" +
|
||||
" <body>Don't forget me this weekend!</body>\n" +
|
||||
"</note>")
|
||||
.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<MockedMessageDto> 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<String> 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).replaceAll("\n",""));
|
||||
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())));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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<List> validationErrorHandler(ConstraintViolationException e){
|
||||
List<String> 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<List> handleBindException(BindException ex){
|
||||
return new ResponseEntity(ex.getAllErrors(), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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<Event>{
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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<String, String> httpHeaders;
|
||||
@HttpCode
|
||||
private Integer httpStatus;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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<MockedMessageDto> {
|
||||
private UUID clientUUID;
|
||||
@NotNull
|
||||
@Positive
|
||||
private Integer mockedResponseId;
|
||||
private String mediaType;
|
||||
private String messageBody;
|
||||
private Map<String, String> httpHeaders;
|
||||
@HttpCode
|
||||
private Integer httpStatus;
|
||||
|
||||
@Override
|
||||
public int compareTo(MockedMessageDto message) {
|
||||
return this.mockedResponseId > message.getMockedResponseId() ? 1 : -1;
|
||||
}
|
||||
}
|
||||
@@ -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<? extends Payload>[] payload() default { };
|
||||
}
|
||||
@@ -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<HttpCode, Integer> {
|
||||
private Set<Integer> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<Event> findEvents(LocalDateTime localDateTimeFrom, LocalDateTime localDateTimeTo,
|
||||
Map<BusinessKey, String> businessKeys);
|
||||
}
|
||||
@@ -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<Event> findEvents(LocalDateTime localDateTimeFrom, LocalDateTime localDateTimeTo,
|
||||
Map<BusinessKey, String> businessKeys) {
|
||||
List<String> eventStrings = findEventsBetweenDates(localDateTimeFrom.toLocalDate(), localDateTimeTo.toLocalDate());
|
||||
if (businessKeys.size() > 0) {
|
||||
eventStrings = businessKeysFilter(eventStrings, businessKeys);
|
||||
}
|
||||
List<Event> 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<String> 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<String> businessKeysFilter(List<String> events, Map<BusinessKey, String> businessKeys) {
|
||||
for (Map.Entry<BusinessKey, String> 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<Event> parseEvents(List<String> eventStrings) {
|
||||
List<Event> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.r11.tools.repository;
|
||||
|
||||
import com.r11.tools.model.MockedMessage;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
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<MockedMessage, String> {
|
||||
/**
|
||||
* Finds all messages by their uuid
|
||||
* @param clientUUID the key-uuid of given set of messages
|
||||
* @return Optional of list of {@link com.r11.tools.model.MockedMessage}
|
||||
*/
|
||||
Optional<List<MockedMessage>> findAllByClientUUID(UUID clientUUID);
|
||||
}
|
||||
@@ -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<Event> getEventsByDateTimeAndBusinessKeys(EventRequestDto eventsDto);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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 lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Spring Service for {@link com.r11.tools.controller.EventController}. Contains logic required for quering
|
||||
* the database for {@link Event} objects
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@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<Event> getEventsByDateTimeAndBusinessKeys(EventRequestDto eventsDto) {
|
||||
Map<BusinessKey, String> 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<Event> events = eventRepository.findEvents(eventsDto.getLocalDateTimeFrom(), eventsDto.getLocalDateTimeTo(),
|
||||
businessKeys);
|
||||
Collections.sort(events);
|
||||
return events;
|
||||
}
|
||||
}
|
||||
@@ -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<String> deleteMockedResponse(UUID clientUUID, int mockedResponseId);
|
||||
List<MockedMessageDto> getAllMockedResponses(UUID clientUUID);
|
||||
MockedMessageDto getMockedResponse(UUID clientUUID, int mockedResponseId);
|
||||
ResponseEntity<String> setMockedResponse(MockedMessageDto mockedMessageDto);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
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 lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class KlausServiceImpl implements KlausService {
|
||||
private final MockedMessageMapper mockedMessageMapper;
|
||||
private final Logger log = LogManager.getRootLogger();
|
||||
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<String> 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<MockedMessageDto> getAllMockedResponses(UUID clientUUID){
|
||||
Optional<List<MockedMessage>> listOptional = mockedResponseRepository.findAllByClientUUID(clientUUID);
|
||||
log.info("Messages for UUID: "+clientUUID+" has been fetched from DB.");
|
||||
return listOptional.map(mockedMessages -> mockedMessages.stream()
|
||||
.map(mockedMessageMapper::mockedMessageToMockedMessageDto)
|
||||
.collect(Collectors.toList())).orElse(List.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<MockedMessage> 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<String> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<ILoggingEvent> {
|
||||
|
||||
JedisPool pool;
|
||||
JSONEventLayout jsonlayout;
|
||||
Layout<ILoggingEvent> 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<String> 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<ILoggingEvent> getLayout() {
|
||||
return layout;
|
||||
}
|
||||
|
||||
public void setLayout(Layout<ILoggingEvent> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<BusinessKey, String> businessKeysMap){
|
||||
for (Map.Entry<BusinessKey, String> entry : businessKeysMap.entrySet()) {
|
||||
MDC.put(entry.getKey().getReasonPhrase(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
redis.host = redis
|
||||
redis.port = 6379
|
||||
43
Backend/mocked-services/src/main/resources/logback.xml
Normal file
43
Backend/mocked-services/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml"/>
|
||||
<property name="HOME_LOG" value="/log/mockServices.log"/>
|
||||
<!--https://github.com/kmtong/logback-redis-appender-->
|
||||
<appender name="LOGSTASH" class="com.r11.tools.utilis.RedisAppender">
|
||||
<host>redis</host>
|
||||
<port>6379</port>
|
||||
<key>logstash</key>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<!--https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html-->
|
||||
<Pattern>
|
||||
{"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
|
||||
</Pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="LOGSTASH" />
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${HOME_LOG}</file>
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>logs/mockServices.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<totalSizeCap>20GB</totalSizeCap>
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
|
||||
<encoder>
|
||||
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="ASYNC" />
|
||||
<appender-ref ref="CONSOLE" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
@import url('https://necolas.github.io/normalize.css/8.0.1/normalize.css');
|
||||
/* @import url('https://fonts.googleapis.com/icon?family=Material+Icons'); */
|
||||
@import url('r11addons.css');
|
||||
@import url('r11tables.css');
|
||||
@import url('r11tool.css');
|
||||
@import url('r11tooltip.css');
|
||||
@import url('r11modal.css');
|
||||
@import url('r11flexbox.css');
|
||||
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/materialicons/v140/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-moz-font-feature-settings: 'liga';
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,4 @@
|
||||
.overflowedTableContent {
|
||||
max-height: 750px;
|
||||
overflow: scroll;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
.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-copy {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
align-content: center;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modification-button.btn-copy img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.modification-button.btn-addtile:hover {
|
||||
color: #58ac43;
|
||||
}
|
||||
|
||||
.tile {
|
||||
width: 100%;
|
||||
padding-top: 40%;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
background: #D5D7E6;
|
||||
margin-bottom: 10px;
|
||||
cursor: default;
|
||||
border-bottom: 1px solid darkgray;
|
||||
}
|
||||
|
||||
.tile:hover {
|
||||
filter: brightness(110%);
|
||||
}
|
||||
|
||||
.tile.active {
|
||||
background: #2A93B0;
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#editable-block {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#uuid-edit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#uuid-edit-field {
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
align-items: center;
|
||||
width: 70%;
|
||||
margin-right: 10px;
|
||||
|
||||
}
|
||||
|
||||
#uuid-edit-field .uuid-inputField-icon{
|
||||
background: none;
|
||||
color: black;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#uuid-edit-field .uuid-inputField-icon:hover{
|
||||
color: #2A93B0;
|
||||
}
|
||||
|
||||
#uuid-input {
|
||||
border: none;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
#uuid-input:focus {
|
||||
outline: none;
|
||||
|
||||
}
|
||||
|
||||
#uuid-validation-strategy input {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
background-color: #CCD1CF;
|
||||
|
||||
}
|
||||
|
||||
.disabled #uuid-input {
|
||||
background-color: #CCD1CF;
|
||||
|
||||
}
|
||||
|
||||
.uuid-inputField-icon-span {
|
||||
font-size: x-large;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
@font-face {
|
||||
font-family: "Nunito";
|
||||
src: url('font/Nunito-VariableFont_wght.ttf') format('truetype');
|
||||
}
|
||||
|
||||
input {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.hyperlink, .hyperlink:visited, .hyperlink:active {
|
||||
color: rgb(47, 125, 146);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hyperlink:hover {
|
||||
filter: brightness(120%);
|
||||
}
|
||||
|
||||
.bordered-field {
|
||||
background-color: #FFFFFF;
|
||||
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: 300;
|
||||
}
|
||||
|
||||
.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: #2A93B0;
|
||||
border: 1px solid #7ed0eb;
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
.action-button.active:hover {
|
||||
filter: brightness(110%);
|
||||
}
|
||||
|
||||
.action-button {
|
||||
background: #CCD1CF;
|
||||
border:1px solid rgba(186, 197, 191, 0.507);
|
||||
border-radius: 5px;
|
||||
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;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 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%;
|
||||
} */
|
||||
@@ -0,0 +1,76 @@
|
||||
.tooltip-window {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
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%);
|
||||
} */
|
||||
324
Backend/mocked-services/src/main/resources/static/html/mock.html
Normal file
324
Backend/mocked-services/src/main/resources/static/html/mock.html
Normal file
@@ -0,0 +1,324 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>R11 MockedServices</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="../css/fontello.css" type="text/css">
|
||||
<link rel="stylesheet" href="../css/main.css" type="text/css">
|
||||
<!-- <link rel="stylesheet" href="css/common.css" type="text/css"> -->
|
||||
<link rel="stylesheet" href="../css/common.css" type="text/css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<!-- <script src="../js/dyn_host.js"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="tool extended">
|
||||
<div class="tool-context">
|
||||
<div>
|
||||
<h1>MockedServices <span class="version-span">v1.0.0</span></h1>
|
||||
</div>
|
||||
<div>
|
||||
<label for="uuid-input" class="block-display">UUID</label>
|
||||
<div id="uuid-edit">
|
||||
<div id="uuid-edit-field" class="bordered-field disabled">
|
||||
<input id="uuid-input" disabled onfocusout="changeUUID(this);" value="UUID" />
|
||||
<button onclick="copyUUIDToClipboard();" class="uuid-inputField-icon modification-button flex-item btn-copy action-button">
|
||||
<span class="material-icons uuid-inputField-icon-span ">content_copy</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="editableBlock">
|
||||
<input type="checkbox" onchange="changeEditionOfUUID(this)" name="editable" id="editable" value="false"/>
|
||||
<label for="editable">Editable</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hiddable" id="uuid-validation-strategy">
|
||||
<label><b>UUID generation strategy:</b></label>
|
||||
|
||||
<input type="radio" checked name="uuid-validation-type" value="new" id="generateNew"/>
|
||||
<label for="generateNew">Generate new UUID</label>
|
||||
|
||||
<input type="radio" name="uuid-validation-type" value="restore" id="restore"/>
|
||||
<label for="restore">Restore previous UUID</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<!-- h2 -->
|
||||
<div id="basicItemData" class="hiddable active"><h2>Your Message</h2></div>
|
||||
<div id="advancedItemData" class="hiddable"><h2>Messaged id: <span id="mockedMessageId">1</span></h2></div>
|
||||
|
||||
<!-- link -->
|
||||
<div>
|
||||
<label for="messageLink" class="block-display">Link</label>
|
||||
<div id="messageLink" class="bordered-field max-width with-padding disabled-background"><a class="hyperlink" href="www.google.com" target="_blank">www.google.com</a></div>
|
||||
<!-- <input id="messageLink" disabled class="bordered-field max-width with-padding" value="http://yourlink.com/r/api/mock/blablabla"> -->
|
||||
</div>
|
||||
<div class="display-space-between max-width">
|
||||
<!-- status and type -->
|
||||
<div class="medium-input block-display small-vertical-margin">
|
||||
<!-- status -->
|
||||
<div class="max-width small-vertical-margin">
|
||||
<label for="httpStatus">Http Status</label>
|
||||
<input id="httpStatus" type="number" class="bordered-field max-width data-field" value="200" list="httpStatusSuggestion"/>
|
||||
<datalist id="httpStatusSuggestion">
|
||||
<option value="200">
|
||||
<option value="300">
|
||||
<option value="400">
|
||||
<option value="403">
|
||||
<option value="404">
|
||||
<option value="500">
|
||||
</datalist>
|
||||
|
||||
</div>
|
||||
<!-- content type -->
|
||||
<div class="max-width small-vertical-margin">
|
||||
<label for="typeSelector">Content Type</label>
|
||||
<input id="typeSelector" class="bordered-field max-width data-field" type="text" value="application/xml" list="contentTypes">
|
||||
<datalist id="contentTypes">
|
||||
<option value="application/xml">
|
||||
<option value="application/json">
|
||||
<option value="text/xml">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div id="btnSave" class="small-margins half-width with-padding action-button" style="background-color: white; border: 0px;">
|
||||
<button id="btn-save" class="small-margins half-width with-padding action-button" style="width: 100%; height: 100%;">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- body -->
|
||||
<div class="small-vertical-margin">
|
||||
<label for="bodyEditor">Body</label>
|
||||
<textarea id="bodyEditor" class="data-field bordered-field max-width with-padding height-300 vertically-resizeable"></textarea>
|
||||
</div>
|
||||
<!-- show/hide -->
|
||||
<button id="optional" class="clickable-text highlight switch"><span class="toggleIndicator"></span> show/hide advanced settings</button>
|
||||
<!-- advanced -->
|
||||
<div id="advanced" class="max-width with-padding hiddable">
|
||||
<!-- tab menu -->
|
||||
<div class="tabmenu medium-vertical-margin">
|
||||
<button id="headersTab" class="tabitem active clickable-text big-font">Headers</button>
|
||||
<button id="historyTab" class="tabitem clickable-text big-font">History</button>
|
||||
</div>
|
||||
<!-- container -->
|
||||
<div class="medium-vertical-margin">
|
||||
<!-- headers -->
|
||||
<div id="headers" class="medium-vertical-margin tabcontent active">
|
||||
<table class="table-map">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="headerMapTable">
|
||||
<tr>
|
||||
<td><input class="key" value="basic value"></td>
|
||||
<td><input value="basic value"></td>
|
||||
<td><button class="modification-button btn-hashmap"><i class="icon-cancel"></i></button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tr>
|
||||
<td><input id="headerKeyInput" placeholder="name"></td>
|
||||
<td><input id="headerValueInput" placeholder="value"></td>
|
||||
<td><button id="btn-newRow" class="modification-button btn-add"><i class="icon-plus"></i></button></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- history -->
|
||||
<div id="history" class="medium-vertical-margin tabcontent">
|
||||
<div class="block-display max-width">
|
||||
<button id="btn-history-filter" class="clickable-text highlight switch"><span class="toggleIndicator"></span> filter</button>
|
||||
<div id ="history-filter" class="display-space-between max-width small-vertical-margin hiddable">
|
||||
<div class="three-fourth-width display-space-evenly">
|
||||
<div class="block-display half-width with-padding">
|
||||
<label for="historyFrom" class="block-label">From</label>
|
||||
<input id="historyFrom" type="date" class="bordered-field max-width with-padding">
|
||||
<input id="historyTimeFrom" type="time" class="small-vertical-margin bordered-field max-width with-padding">
|
||||
</div>
|
||||
<div class="block-display half-width with-padding">
|
||||
<label for="historyTo" class="block-label">To</label>
|
||||
<input id="historyTo" type="date" class="bordered-field max-width with-padding">
|
||||
<input id="historyTimeTo" type="time" class="small-vertical-margin bordered-field max-width with-padding">
|
||||
</div>
|
||||
</div>
|
||||
<button id="btn-searchHistory" class="quater-width action-button active small-margins">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="max-width centered-content large-vertical-margin overflowedTableContent">
|
||||
<table id="historyTable" class="table-default">
|
||||
<thead>
|
||||
<tr class="bottom-border">
|
||||
<th>Timestamp</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- <tr class="even">
|
||||
<td>2021-01-01T10:57:26</td>
|
||||
<td>Client request</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2021-01-01T10:57:26</td>
|
||||
<td>Client request</td>
|
||||
</tr> -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="selectMenuContent" class="tool-extention">
|
||||
<!-- header -->
|
||||
<div>
|
||||
<h2>Message List</h2>
|
||||
</div>
|
||||
<!-- tile list -->
|
||||
<div id="listItems">
|
||||
<!-- <div class="tile">
|
||||
<div class="content">
|
||||
<div class="display-space-between">
|
||||
<div class="centered-vertically">
|
||||
<p>Id: 2</p>
|
||||
<p>Status: 200</p>
|
||||
</div>
|
||||
<div>
|
||||
<button id="test1" class="modification-button btn-tile"><i class="icon-cancel"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div id="new-tile" class="max-width centered-content small-vertical-margin">
|
||||
<button id="btn-newtile" class="modification-button btn-addtile"><i class="icon-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tooltip-window lite">
|
||||
<div>
|
||||
<h2>What's this?</h2>
|
||||
<p>MockedServices is a tool that allows developer to create, in easy and simple way, http server mocked endpoints for integration tests.</p>
|
||||
<h2>Help</h2>
|
||||
<p>When cursor hovers over an item. It's description is displayed below.</p>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="messageLinkTip" class="tip">
|
||||
<h3>Link</h3>
|
||||
<p>Link is an url representing an endpoint at which you can receive your mocked response by simply sending get request.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="btnSaveTip" class="tip">
|
||||
<h3>Save button!</h3>
|
||||
<p>To save message, the message must be changed!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="btn-newTileTip" class="tip">
|
||||
<h3>Add new message</h3>
|
||||
<p>This button adds new message.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="UUIDFieldTip" class="tip">
|
||||
<h3>UUID</h3>
|
||||
<p>UUID is an Unique ID that represents you in API. By UUID your messages is saved in database. You can change it to access your previous configuration of mocked messages</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="UUIDValidationStrategyTip" class="tip">
|
||||
<h3>UUID Checking Strategy</h3>
|
||||
<p>When you provide invalid UUID you can choose what do with it. You can generate new UUID or restore previous.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="UUIDEditionTip" class="tip">
|
||||
<h3>UUID Edition</h3>
|
||||
<p>Unlocks you ability to edit UUID</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="large-vertical-margin">
|
||||
<div id="messagesTip" class="tip">
|
||||
<h3>Message</h3>
|
||||
<p>This is saved messages, with unique id.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="httpStatusTip" class="tip">
|
||||
<h3>Http Status</h3>
|
||||
<p>Value of the field is corresponding to status value that server will return.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="typeSelectorTip" class="tip">
|
||||
<h3>Content Type</h3>
|
||||
<p>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"</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="bodyEditorTip" class="tip">
|
||||
<h3>Body</h3>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="headersTabTip" class="tip">
|
||||
<h3>Headers</h3>
|
||||
<p>Content of this tab allows to set and modify headers that will be included in the response.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="historyTabTip" class="tip">
|
||||
<h3>History</h3>
|
||||
<p>Content of this tab displays the history of requests or responses received/sent to the endpoint</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-vertical-margin">
|
||||
<div id="newHeaderTip" class="tip">
|
||||
<h3>New header</h3>
|
||||
<p>Insert value in the field and press the plus icon to add a new header to the message.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="overlay"></div>
|
||||
<div id="modal-uuidChanged" class="modal">
|
||||
<div class="header">
|
||||
<div>Change UUID info<i class="r-exclamation"></i></div>
|
||||
<button onclick="window.location.reload();">×</button>
|
||||
</div>
|
||||
<div id="changeUUIDSuccess" class="body hiddable uuid-modal-body">Your message UUID has been changed successfully.</div>
|
||||
<div id="newUUID" class="body hiddable uuid-modal-body">You provided wrong UUID! <br> New UUID has been generated!</div>
|
||||
<div id="restoredUUID" class="body hiddable uuid-modal-body">You provided wrong UUID! <br> Old UUID has been restored!</div>
|
||||
<div id="noChgUUID" class="body hiddable uuid-modal-body">You doesn't provide any change to UUID!</div>
|
||||
</div>
|
||||
<div id="modal-confirm" class="modal">
|
||||
<div class="header">
|
||||
<div>Message saved<i class="r-exclamation"></i></div>
|
||||
<button>×</button>
|
||||
</div>
|
||||
<div class="body">Your message has been successfuly saved.<br>You might view it under the link.</div>
|
||||
</div>
|
||||
<div id="modal-query" class="modal">
|
||||
<div class="header">
|
||||
<div>Unsaved data<i class="r-exclamation"></i></div>
|
||||
<button>×</button>
|
||||
</div>
|
||||
<div class="body">You haven't saved your message! Any changes will be lost.<br>Do you want to continue?</div>
|
||||
<div class="function">
|
||||
<button>Yes</button>
|
||||
<button>No</button>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/modal.js"></script>
|
||||
<script type="text/javascript" src="../js/uianimation.js"></script>
|
||||
<script type="text/javascript" src="../js/datatransfer.js"></script>
|
||||
<script type="text/javascript" src="../js/historyloader.js"></script>
|
||||
<script type="text/javascript" src="../js/fiddle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,540 @@
|
||||
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 color_red = "#ff8f8f";
|
||||
const color_grey = "#6b6b6b";
|
||||
|
||||
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();
|
||||
|
||||
|
||||
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 httpStatusInvalid(){
|
||||
value = $('#httpStatus').val();
|
||||
return value == '';
|
||||
}
|
||||
|
||||
function setDataModified(){
|
||||
if(httpStatusInvalid()){
|
||||
$('#btn-save').removeClass('active');
|
||||
$('#btn-save').off();
|
||||
document.getElementById("httpStatus").style.backgroundColor = color_red;
|
||||
return;
|
||||
}
|
||||
dataModified = true;
|
||||
$('#btn-save').addClass('active');
|
||||
$('#btn-save').click(getUpdate);
|
||||
document.getElementById("httpStatus").style.backgroundColor = null;
|
||||
}
|
||||
|
||||
//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<json.length; i++){
|
||||
if(json[i].mockedResponseId == lastId){
|
||||
return lastId;
|
||||
}
|
||||
}
|
||||
if(jsonIndex <= json.length && jsonIndex > 0){
|
||||
jsonIndex -= 1;
|
||||
return json[jsonIndex].mockedResponseId;
|
||||
}
|
||||
return defaultId;
|
||||
}
|
||||
|
||||
function refreshData(){
|
||||
$("#uuid-input").val(clientUUID);
|
||||
fillMessageList();
|
||||
|
||||
let id = idToDisplay();
|
||||
|
||||
loadMessage(id);
|
||||
|
||||
}
|
||||
|
||||
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 <ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function callMethodByName(methodObject){
|
||||
let name = methodObject.name;
|
||||
let id = methodObject.id;
|
||||
switch(name){
|
||||
case addMessageName:
|
||||
addMessage();
|
||||
break;
|
||||
case loadMessageName:
|
||||
loadMessage(id);
|
||||
break;
|
||||
case removeMessageName:
|
||||
removeMessage(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function updateData(){
|
||||
var updatedJson = generateJson();
|
||||
const dataSaved = function () {
|
||||
setDataOrigin();
|
||||
refreshData();
|
||||
savedModalDisplay();
|
||||
}
|
||||
$.ajax({
|
||||
url: host + '/api/mock',
|
||||
type: 'PUT',
|
||||
data: JSON.stringify(updatedJson, null, 2),
|
||||
contentType: "application/json",
|
||||
}).done(dataSaved);
|
||||
}
|
||||
|
||||
function callAddMessage(){
|
||||
if(dataModified){
|
||||
setMethodToCall(addMessageName, null);
|
||||
dataLossModalDisplay();
|
||||
}
|
||||
else {
|
||||
addMessage();
|
||||
}
|
||||
}
|
||||
|
||||
function addMessage(){
|
||||
$.ajax({
|
||||
url: host + '/api/mock/'+clientUUID,
|
||||
type: 'POST',
|
||||
}).done(dataRefresh);
|
||||
}
|
||||
|
||||
function callRemoveMessage(id){
|
||||
if(dataModified){
|
||||
setMethodToCall(removeMessageName, id);
|
||||
dataLossModalDisplay();
|
||||
}
|
||||
else {
|
||||
removeMessage(id);
|
||||
}
|
||||
}
|
||||
|
||||
function removeMessage(id){
|
||||
var jsonObject = findJsonById(id);
|
||||
$.ajax({
|
||||
url: host + '/api/mock/'+clientUUID + '/' + id,
|
||||
type: 'DELETE',
|
||||
}).done(dataRefresh);
|
||||
}
|
||||
|
||||
|
||||
function clearMock(){
|
||||
fillStaticFields('','','','');
|
||||
htable_row = 0;
|
||||
$('#httpStatusValues').html('');
|
||||
}
|
||||
|
||||
function initializeMock(index){
|
||||
clearMock();
|
||||
fillStaticFields(json[index].clientUUID
|
||||
, json[index].mockedResponseId
|
||||
, json[index].mediaType
|
||||
, json[index].messageBody
|
||||
, json[index].httpStatus);
|
||||
fillHeaderTable(json[index].httpHeaders);
|
||||
}
|
||||
|
||||
function fillStaticFields(uuid, id, mediaType, body, httpStatus){
|
||||
let link = createLink(uuid,id);
|
||||
let linkHtml = '<a class="hyperlink" target="_blank" href="'+link+'">'+link+'</a>';
|
||||
$('#messageLink').html(linkHtml);
|
||||
$('#httpStatus').val(httpStatus);
|
||||
$('#uuid-input').val(uuid);
|
||||
$('#typeSelector').val(mediaType);
|
||||
$('#bodyEditor').val(body);
|
||||
$('#mockedMessageId').html(id);
|
||||
|
||||
}
|
||||
|
||||
function changeEditionOfUUID(element){
|
||||
|
||||
var inputFieldId= "#uuid-input"
|
||||
var inputFieldDiv = "#uuid-edit-field"
|
||||
if(element.checked){
|
||||
$(inputFieldId).removeAttr('disabled');
|
||||
$(inputFieldDiv).removeClass('disabled');
|
||||
} else{
|
||||
$(inputFieldId).attr('disabled', true);
|
||||
$(inputFieldDiv).addClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function copyUUIDToClipboard(){
|
||||
navigator.clipboard.writeText( document.getElementById('uuid-input').value );
|
||||
}
|
||||
|
||||
|
||||
async function fetchUUIDCheck(givenUUID , strategy){
|
||||
var newUUID = "UUID" ;
|
||||
url = host + "/api/mock/check/";
|
||||
|
||||
switch(strategy){
|
||||
case "new":{
|
||||
await fetch(url + givenUUID+ "/", { method : "GET" })
|
||||
.then ( response => response.text() )
|
||||
.then ( data => {
|
||||
newUUID = data;
|
||||
|
||||
} )
|
||||
break;
|
||||
}
|
||||
case "restore":{
|
||||
await fetch(url + givenUUID + "/" + clientUUID + "/" , { method: "GET" })
|
||||
.then (response => response.text() )
|
||||
.then (data => {
|
||||
newUUID = data;
|
||||
|
||||
} )
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newUUID ;
|
||||
}
|
||||
|
||||
function checkUUIDChars(uuid) {
|
||||
uuid.replace(/ /g,'')
|
||||
const regex = new RegExp("^[A-z0-9-]+$");
|
||||
|
||||
if(regex.test(uuid) && uuid != ""){
|
||||
return uuid ;
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
function changeUUID(element){
|
||||
|
||||
const uuidStrategy = $('input[name="uuid-validation-type"]:checked').val();
|
||||
const givenUUID = checkUUIDChars(element.value);
|
||||
|
||||
if( givenUUID == clientUUID ){
|
||||
$("#uuid-input").attr("disabled", true);
|
||||
uuidChangeModalDisplay("noChg");
|
||||
return;
|
||||
}
|
||||
|
||||
var newUUID = fetchUUIDCheck(givenUUID , uuidStrategy);
|
||||
var changeMessage = uuidStrategy;
|
||||
newUUID
|
||||
.then( data => {
|
||||
if (givenUUID == data) {
|
||||
changeMessage = "success";
|
||||
}
|
||||
clientUUID = data;
|
||||
$("#editable").attr("checked", false);
|
||||
|
||||
uuidChangeModalDisplay(changeMessage);
|
||||
document.cookie = C_UUID + '=' + data ;
|
||||
} )
|
||||
loadCookies();
|
||||
refreshData();
|
||||
}
|
||||
|
||||
|
||||
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 checkIfHeaderEssential(key){
|
||||
|
||||
if( key == "Connection" || key == "Keep-Alive" || key == "Date" ){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
function buildRowHtml(key, value){
|
||||
|
||||
if(checkIfHeaderEssential(key)){
|
||||
return '' +
|
||||
'<tr>' +
|
||||
'<td><input class="key data-field" value="' + key + '" readonly></td>' +
|
||||
'<td><input class="data-field" value="' + value + '"></td>' +
|
||||
'</tr>';
|
||||
}
|
||||
return '' +
|
||||
'<tr>' +
|
||||
'<td><input class="key data-field" value="' + key + '"></td>' +
|
||||
'<td><input class="data-field" value="' + value + '"></td>' +
|
||||
'<td><button class="modification-button btn-hashmap"><i class="icon-cancel"></i></button></td>' +
|
||||
'</tr>';
|
||||
}
|
||||
|
||||
|
||||
function fillMessageList(){
|
||||
$("#listItems").html('');
|
||||
var innerHTML = '';
|
||||
for(let i=0; i<json.length; i++){
|
||||
innerHTML += generateMessageTileHtml(json[i].mockedResponseId, json[i].httpStatus, json[i].mediaType);
|
||||
}
|
||||
$("#listItems").append(innerHTML);
|
||||
$('.tile').click(function(e) {
|
||||
var element = $(this);
|
||||
var button = element.find('.btn-tile').children().get(0);
|
||||
|
||||
if(!(button == e.target)){
|
||||
|
||||
callLoadMessage(parseInt($(this).attr('tileid')));
|
||||
}
|
||||
});
|
||||
$('.btn-tile').click(function(){
|
||||
//
|
||||
callRemoveMessage($(this).closest('.tile').attr('tileId'));
|
||||
})
|
||||
}
|
||||
|
||||
function findJsonById(id){
|
||||
return json[findJsonIndexById(id)];
|
||||
}
|
||||
|
||||
function findJsonIndexById(id){
|
||||
for(let i=0; i<json.length; i++)
|
||||
if(id == json[i].mockedResponseId) return i;
|
||||
}
|
||||
|
||||
function callLoadMessage(id){
|
||||
if(dataModified) {
|
||||
setMethodToCall(loadMessageName, id);
|
||||
dataLossModalDisplay();
|
||||
}
|
||||
else {
|
||||
loadMessage(id);
|
||||
}
|
||||
}
|
||||
|
||||
function loadMessage(id){
|
||||
if(id == null || id == undefined){
|
||||
|
||||
return;
|
||||
}
|
||||
lastId = id;
|
||||
setCookie();
|
||||
setDataOrigin();
|
||||
for(let i=0; i<json.length; i++){
|
||||
|
||||
if(id == json[i].mockedResponseId){
|
||||
jsonIndex = i;
|
||||
|
||||
initializeMock(jsonIndex);
|
||||
|
||||
selectMessage(id);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function selectMessage(id){
|
||||
const tiles = $('.tile');
|
||||
|
||||
tiles.removeClass("active");
|
||||
|
||||
$('.tile[tileid="'+id+'"]').addClass("active");
|
||||
|
||||
initializeHistory();
|
||||
refreshHeaderTable(innerHTML);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function generateMessageTileHtml(id, httpStatus, mediaType){
|
||||
var innerHTML = '' +
|
||||
'<div tileid="' + id + '" class="tile">' +
|
||||
'<div class="content">' +
|
||||
'<div class="display-space-between">' +
|
||||
'<div class="centered-vertically">' +
|
||||
'<p>Id: ' + id + '</p>' +
|
||||
'<p>Status: ' + httpStatus + '</p>' +
|
||||
'</div>' +
|
||||
'<div>' +
|
||||
'<button class="modification-button btn-tile"><i class="icon-cancel"></i></button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
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();
|
||||
|
||||
json[jsonIndex] = newJson;
|
||||
return newJson;
|
||||
}
|
||||
|
||||
|
||||
function convertTableToJson(){
|
||||
const rows = $('#headerMapTable').children();
|
||||
|
||||
var obj = {};
|
||||
var key;
|
||||
for(let i=0; i<rows.length; i++){
|
||||
key = rows.eq(i).children().eq(0).children().eq(0).val();
|
||||
obj[key] = rows.eq(i).children().eq(1).children().eq(0).val();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
const deleteParent = function(){
|
||||
$(this).closest('div.tile').remove();
|
||||
}
|
||||
|
||||
$('#test1').click(deleteParent);
|
||||
@@ -0,0 +1,58 @@
|
||||
var historyJson = {};
|
||||
const maxIterations = 200;
|
||||
|
||||
function filterHistory(){
|
||||
var dateFrom = new Date($('#historyFrom').val() + 'T' + $('#historyTimeFrom').val());
|
||||
|
||||
var dateTo = new Date($('#historyTo').val() + 'T' + $('#historyTimeTo').val());
|
||||
|
||||
loadHistory(dateFrom, dateTo);
|
||||
}
|
||||
|
||||
const startSearch = function(){
|
||||
filterHistory();
|
||||
}
|
||||
$('#btn-searchHistory').click(startSearch);
|
||||
|
||||
function loadHistory(dateFrom, dateTo){
|
||||
|
||||
var eventRequest = {
|
||||
clientUUID : json[jsonIndex].clientUUID,
|
||||
localDateTimeFrom : dateFrom,
|
||||
localDateTimeTo : dateTo,
|
||||
mockedResponseId : json[jsonIndex].mockedResponseId
|
||||
};
|
||||
$.ajax({
|
||||
url: host + '/api/event',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(eventRequest, null, 2),
|
||||
contentType: "application/json"
|
||||
}).done(function(data){
|
||||
historyJson = data;
|
||||
displayHistory();
|
||||
});
|
||||
}
|
||||
|
||||
function getLast24hHistoryData(){
|
||||
$.getJSON(host + '/api/event/' + clientUUID + '/' + lastId, function(data){
|
||||
historyJson = data;
|
||||
displayHistory();
|
||||
});
|
||||
}
|
||||
|
||||
function historyToHtml(){
|
||||
var innerHTML = '';
|
||||
var iterations = historyJson.length <= maxIterations ? historyJson.length : maxIterations;
|
||||
for(let i=0; i<iterations; i++){
|
||||
let style = i%2==0 ? ' class="even"' : '';
|
||||
innerHTML += '<tr' + style + '>' +
|
||||
'<td>' + historyJson[i].dateTimeStamp + '</td>' +
|
||||
'<td>' + historyJson[i].interfaceName + '</td>' +
|
||||
'</tr>';
|
||||
}
|
||||
return innerHTML;
|
||||
}
|
||||
|
||||
function displayHistory(){
|
||||
$('#historyTable tbody').html(historyToHtml());
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
var modalDisplayed = false;
|
||||
var methodToCall = {
|
||||
name: null,
|
||||
id: null
|
||||
};
|
||||
|
||||
const overlay = $('#overlay');
|
||||
const savedModal = $('#modal-confirm');
|
||||
const dataLossModal = $('#modal-query');
|
||||
const uuidChangeModal = $('#modal-uuidChanged')
|
||||
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() {
|
||||
|
||||
showModal(savedModal);
|
||||
setTimeout(closeModals, 2000);
|
||||
}
|
||||
const dataLossModalDisplay = function(){
|
||||
showModal(dataLossModal);
|
||||
}
|
||||
|
||||
const uuidChangeModalDisplay = function(addidionalMessage){
|
||||
|
||||
switch(addidionalMessage){
|
||||
case "success":{
|
||||
|
||||
$(".uuid-modal-body").removeClass("active");
|
||||
$("#changeUUIDSuccess").addClass("active");
|
||||
break;
|
||||
}
|
||||
case "new":{
|
||||
|
||||
$(".uuid-modal-body").removeClass("active");
|
||||
$("#newUUID").addClass("active");
|
||||
break;
|
||||
}
|
||||
case "restore":{
|
||||
|
||||
$(".uuid-modal-body").removeClass("active");
|
||||
$("#restoredUUID").addClass("active");
|
||||
break;
|
||||
}
|
||||
case "noChg":{
|
||||
|
||||
$(".uuid-modal-body").removeClass("active");
|
||||
$("#noChgUUID").addClass("active");
|
||||
break;
|
||||
}
|
||||
}
|
||||
showModal(uuidChangeModal);
|
||||
}
|
||||
|
||||
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;
|
||||
if ($(uuidChangeModal).hasClass('active')) window.location.reload();
|
||||
overlay.removeClass('active');
|
||||
jmodal.removeClass('active');
|
||||
modalDisplayed = false;
|
||||
}
|
||||
|
||||
btnModalClose.click(closeModals);
|
||||
|
||||
overlay.click(closeModals);
|
||||
|
||||
dataLossModalNo.click(closeModals);
|
||||
dataLossModalYes.click(dropChangesAndClose);
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
var advancedVisibility = false;
|
||||
var selectMenu = $("#selectMenuContent");
|
||||
var advancedTab = $("#advanced");
|
||||
var basicID = $("#basicItemData")
|
||||
var advancedID = $("#advancedItemData");
|
||||
var advancedUUIDOptions = $("#uuid-validation-strategy");
|
||||
var focusedField = false;
|
||||
function changeAdvancedVisibility(){
|
||||
if(advancedVisibility){
|
||||
selectMenu.removeClass('active');
|
||||
advancedTab.removeClass('active');
|
||||
advancedID.removeClass('active');
|
||||
advancedUUIDOptions.removeClass('active');
|
||||
basicID.addClass('active');
|
||||
advancedVisibility = false;
|
||||
}
|
||||
else {
|
||||
selectMenu.addClass('active');
|
||||
advancedTab.addClass('active');
|
||||
advancedID.addClass('active');
|
||||
advancedUUIDOptions.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')});
|
||||
|
||||
$('#btnSave').mouseover(function(){showTip('btnSaveTip');});
|
||||
$('#btnSave').focusin(function(){focusInTip('btnSaveTip')});
|
||||
$('#btnSave').mouseleave(function(){hidTip('btnSaveTip')});
|
||||
$('#btnSave').focusout(function(){focusOutTip('btnSaveTip')});
|
||||
|
||||
$('#new-tile').mouseover(function(){showTip('btn-newTileTip');});
|
||||
$('#new-tile').mouseleave(function(){hidTip('btn-newTileTip')});
|
||||
$('#new-tile').focusout(function(){focusOutTip('btn-newTileTip')});
|
||||
|
||||
$('#listItems').mouseover(function(){showTip('messagesTip');});
|
||||
$('#listItems').mouseleave(function(){hidTip('messagesTip')});
|
||||
|
||||
|
||||
$('#uuid-edit-field').mouseover(function(){ showTip('UUIDFieldTip') });
|
||||
$('#uuid-edit-field').mouseleave(function(){ hidTip('UUIDFieldTip') });
|
||||
|
||||
$('#uuid-validation-strategy').mouseover(function(){ showTip('UUIDValidationStrategyTip') });
|
||||
$('#uuid-validation-strategy').mouseleave(function(){ hidTip('UUIDValidationStrategyTip') });
|
||||
|
||||
$('#editableBlock').mouseover( function(){ showTip('UUIDEditionTip') } );
|
||||
$('#editableBlock').mouseleave(function(){ hidTip('UUIDEditionTip') });
|
||||
25
Backend/pom.xml
Normal file
25
Backend/pom.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.r11.tools</groupId>
|
||||
<artifactId>release11-tools</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<jackson.version>2.14.1</jackson.version>
|
||||
<slf4j.version>2.0.6</slf4j.version>
|
||||
</properties>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>tools-services</module>
|
||||
<module>mocked-services</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -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/
|
||||
|
||||
128
Backend/tools-services/pom.xml
Normal file
128
Backend/tools-services/pom.xml
Normal file
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.r11.tools</groupId>
|
||||
<artifactId>tools-services</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<jackson.version>2.14.1</jackson.version>
|
||||
<slf4j.version>2.0.6</slf4j.version>
|
||||
<log4j.version>2.19.0</log4j.version>
|
||||
<gson.version>2.10.1</gson.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<mainClass>com.r11.tools.SparkInitializer</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spark -->
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.9.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- XSLT -->
|
||||
<dependency>
|
||||
<groupId>net.sf.saxon</groupId>
|
||||
<artifactId>Saxon-HE</artifactId>
|
||||
<version>11.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xalan</groupId>
|
||||
<artifactId>xalan</artifactId>
|
||||
<version>2.7.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.r11.tools;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.r11.tools.controller.JsonController;
|
||||
import com.r11.tools.controller.ProcessorInfoController;
|
||||
import com.r11.tools.controller.XPathController;
|
||||
import com.r11.tools.controller.XsdController;
|
||||
import com.r11.tools.controller.XsltController;
|
||||
import com.r11.tools.controller.internal.RestControllerRegistry;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import spark.Spark;
|
||||
|
||||
public class SparkApplication {
|
||||
|
||||
public static void run() {
|
||||
// TODO: read port from config
|
||||
Spark.port(8081);
|
||||
|
||||
Spark.after((request, response) -> {
|
||||
response.header("Access-Control-Allow-Origin", "*");
|
||||
response.header("access-control-allow-headers", "*");
|
||||
response.header("access-control-expose-headers", "*");
|
||||
response.header("Access-Control-Allow-Methods", "POST");
|
||||
});
|
||||
|
||||
Logger logger = LogManager.getLogger(SparkApplication.class);
|
||||
|
||||
Gson gson = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
RestControllerRegistry registry = new RestControllerRegistry();
|
||||
registry.registerController(new ProcessorInfoController(logger));
|
||||
registry.registerController(new XsdController(gson, logger));
|
||||
registry.registerController(new XPathController(gson, logger));
|
||||
registry.registerController(new XsltController(gson, logger));
|
||||
registry.registerController(new JsonController());
|
||||
|
||||
registry.register();
|
||||
|
||||
logger.info("Server is online at port: " + Spark.port());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.r11.tools;
|
||||
|
||||
public class SparkInitializer {
|
||||
public static void main(String[] args) {
|
||||
SparkApplication.run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.r11.tools.controller.internal.GlobalControllerManifest;
|
||||
import com.r11.tools.controller.internal.HandlerType;
|
||||
import com.r11.tools.controller.internal.RestController;
|
||||
import com.r11.tools.controller.internal.ScopedControllerManifest;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
@GlobalControllerManifest(path = "/json")
|
||||
public class JsonController implements RestController {
|
||||
|
||||
private final Gson prettyGson = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.create();
|
||||
|
||||
@ScopedControllerManifest(method = HandlerType.POST, path = "/formatting")
|
||||
public void formatting(Request request, Response response) {
|
||||
long startProcess = System.currentTimeMillis();
|
||||
JsonObject responseJson = new JsonObject();
|
||||
|
||||
try {
|
||||
JsonObject requestJson = this.gson.fromJson(request.body(), JsonObject.class);
|
||||
|
||||
response.status(200);
|
||||
|
||||
responseJson.addProperty("data", this.prettyGson.toJson(requestJson));
|
||||
responseJson.addProperty("time", System.currentTimeMillis() - startProcess);
|
||||
|
||||
response.body(this.prettyGson.toJson(responseJson));
|
||||
} catch (Exception e) {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
response.status(500);
|
||||
|
||||
responseJson.addProperty("data", cause == null ? e.getMessage() : cause.getMessage());
|
||||
responseJson.addProperty("time", System.currentTimeMillis() - startProcess);
|
||||
|
||||
response.body(this.prettyGson.toJson(responseJson));
|
||||
}
|
||||
}
|
||||
|
||||
@ScopedControllerManifest(method = HandlerType.POST, path = "/minimize")
|
||||
public void minimize(Request request, Response response) {
|
||||
long startProcess = System.currentTimeMillis();
|
||||
JsonObject responseJson = new JsonObject();
|
||||
|
||||
try {
|
||||
JsonObject requestJson = this.prettyGson.fromJson(request.body(), JsonObject.class);
|
||||
|
||||
response.status(200);
|
||||
|
||||
responseJson.addProperty("data", this.gson.toJson(requestJson));
|
||||
responseJson.addProperty("time", System.currentTimeMillis() - startProcess);
|
||||
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
} catch (Exception e) {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
response.status(500);
|
||||
|
||||
responseJson.addProperty("data", cause == null ? e.getMessage() : cause.getMessage());
|
||||
responseJson.addProperty("time", System.currentTimeMillis() - startProcess);
|
||||
|
||||
response.body(this.prettyGson.toJson(responseJson));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.r11.tools.controller.internal.GlobalControllerManifest;
|
||||
import com.r11.tools.controller.internal.HandlerType;
|
||||
import com.r11.tools.controller.internal.RestController;
|
||||
import com.r11.tools.controller.internal.ScopedControllerManifest;
|
||||
import com.r11.tools.xml.Saxon;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
@GlobalControllerManifest
|
||||
public class ProcessorInfoController implements RestController {
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
public ProcessorInfoController(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler that returns processor version
|
||||
*/
|
||||
@ScopedControllerManifest(method = HandlerType.GET, path = "/procinfo")
|
||||
public void processorInfo(Request request, Response response) {
|
||||
try {
|
||||
response.header("processor", "Saxon " + Saxon.getVersion() + " over s9api");
|
||||
response.body(Saxon.getVersion());
|
||||
} catch (Exception ex) {
|
||||
this.logger.error("Error on retrieving engine version. " + ex);
|
||||
response.body(ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.r11.tools.controller.internal.GlobalControllerManifest;
|
||||
import com.r11.tools.controller.internal.HandlerType;
|
||||
import com.r11.tools.controller.internal.RestController;
|
||||
import com.r11.tools.controller.internal.ScopedControllerManifest;
|
||||
import com.r11.tools.xml.Saxon;
|
||||
import com.r11.tools.xml.Xalan;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
@GlobalControllerManifest
|
||||
public class XPathController implements RestController {
|
||||
|
||||
private final Gson gson;
|
||||
private final Logger logger;
|
||||
|
||||
public XPathController(Gson gson, Logger logger) {
|
||||
this.gson = gson;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@ScopedControllerManifest(method = HandlerType.POST, path = "/xpath")
|
||||
public void transform(Request request, Response response) {
|
||||
String body = request.body();
|
||||
|
||||
JsonObject requestJson;
|
||||
try {
|
||||
requestJson = this.gson.fromJson(body, JsonObject.class);
|
||||
} catch (Exception e) {
|
||||
JsonObject responseJson = new JsonObject();
|
||||
responseJson.addProperty("result", e.getMessage());
|
||||
responseJson.addProperty("processor", "N/A");
|
||||
responseJson.addProperty("status", "ERR");
|
||||
responseJson.addProperty("time", "N/A");
|
||||
|
||||
response.status(400);
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
return;
|
||||
}
|
||||
|
||||
String data = requestJson.get("data").getAsString();
|
||||
String query = requestJson.get("process").getAsString();
|
||||
String processor = requestJson.get("processor").getAsString();
|
||||
String version = requestJson.get("version").getAsString();
|
||||
|
||||
String tmp = "";
|
||||
long timeStart;
|
||||
long duration;
|
||||
|
||||
if (processor == null) {
|
||||
response.body("saxon, xalan");
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject responseJson = new JsonObject();
|
||||
switch (processor) {
|
||||
case "saxon":
|
||||
response.header("processor", "Saxon " + Saxon.getVersion() + " " + version + " over s9api");
|
||||
timeStart = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
tmp = Saxon.processXPath(data, query, version).trim();
|
||||
|
||||
response.status(200);
|
||||
|
||||
responseJson.addProperty("result", tmp);
|
||||
responseJson.addProperty("status", "OK");
|
||||
} catch (Exception ex) {
|
||||
this.logger.error("Error on processing XPath using Saxon. " + ex);
|
||||
|
||||
response.status(400);
|
||||
|
||||
responseJson.addProperty("result", ex.getMessage());
|
||||
responseJson.addProperty("status", "ERR");
|
||||
}
|
||||
|
||||
duration = System.currentTimeMillis() - timeStart;
|
||||
this.logger.info("Request (XPath, Saxon) processed in " + duration + " ms.");
|
||||
|
||||
responseJson.addProperty("processor", "Saxon " + Saxon.getVersion() + " " + version + " over s9api");
|
||||
responseJson.addProperty("time", duration);
|
||||
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
return;
|
||||
|
||||
case "xalan":
|
||||
response.header("processor", Xalan.getVersion());
|
||||
timeStart = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
tmp = Xalan.processXPath(data, query).trim();
|
||||
|
||||
response.status(200);
|
||||
|
||||
responseJson.addProperty("result", tmp);
|
||||
responseJson.addProperty("status", "OK");
|
||||
} catch (Exception ex) {
|
||||
this.logger.error("Error on processing XPath using Xalan. " + ex);
|
||||
|
||||
response.status(400);
|
||||
|
||||
responseJson.addProperty("result", ex.getMessage());
|
||||
responseJson.addProperty("status", "ERR");
|
||||
}
|
||||
|
||||
duration = System.currentTimeMillis() - timeStart;
|
||||
this.logger.info("Request (XPath, Xalan) processed in " + duration + " ms.");
|
||||
|
||||
responseJson.addProperty("processor", Xalan.getVersion());
|
||||
responseJson.addProperty("time", duration);
|
||||
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
return;
|
||||
default:
|
||||
response.body("saxon, xalan");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.r11.tools.controller.internal.GlobalControllerManifest;
|
||||
import com.r11.tools.controller.internal.HandlerType;
|
||||
import com.r11.tools.controller.internal.RestController;
|
||||
import com.r11.tools.controller.internal.ScopedControllerManifest;
|
||||
import com.r11.tools.xml.Xalan;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
@GlobalControllerManifest
|
||||
public class XsdController implements RestController {
|
||||
|
||||
private final Gson gson;
|
||||
private final Logger logger;
|
||||
|
||||
public XsdController(Gson gson, Logger logger) {
|
||||
this.gson = gson;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@ScopedControllerManifest(method = HandlerType.POST, path = "/xsd")
|
||||
public Response transform(Request request, Response response) {
|
||||
String body = request.body();
|
||||
|
||||
JsonObject requestJson;
|
||||
try {
|
||||
requestJson = this.gson.fromJson(body, JsonObject.class);
|
||||
} catch (Exception e) {
|
||||
JsonObject responseJson = new JsonObject();
|
||||
responseJson.addProperty("result", e.getMessage());
|
||||
responseJson.addProperty("processor", "N/A");
|
||||
responseJson.addProperty("status", "ERR");
|
||||
responseJson.addProperty("time", "N/A");
|
||||
|
||||
response.status(400);
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
return response;
|
||||
}
|
||||
|
||||
String data = requestJson.get("data").getAsString();
|
||||
String xsd = requestJson.get("process").getAsString();
|
||||
|
||||
response.header("processor", Xalan.getVersion());
|
||||
|
||||
long timeStart = System.currentTimeMillis();
|
||||
String tmp;
|
||||
|
||||
JsonObject responseJson = new JsonObject();
|
||||
try {
|
||||
tmp = Xalan.validate(data, xsd).trim();
|
||||
|
||||
response.status(200);
|
||||
|
||||
responseJson.addProperty("result", tmp);
|
||||
responseJson.addProperty("status", "OK");
|
||||
} catch (Exception ex) {
|
||||
this.logger.error("Error on validation against XSD using Xalan. " + ex);
|
||||
|
||||
response.status(400);
|
||||
|
||||
responseJson.addProperty("result", ex.getMessage());
|
||||
responseJson.addProperty("status", "ERR");
|
||||
}
|
||||
|
||||
long duration = System.currentTimeMillis() - timeStart;
|
||||
this.logger.info("Request (XSD, Xalan) processed in " + duration + " ms.");
|
||||
|
||||
responseJson.addProperty("processor", Xalan.getVersion());
|
||||
responseJson.addProperty("time", duration);
|
||||
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.r11.tools.controller.internal.GlobalControllerManifest;
|
||||
import com.r11.tools.controller.internal.HandlerType;
|
||||
import com.r11.tools.controller.internal.RestController;
|
||||
import com.r11.tools.controller.internal.ScopedControllerManifest;
|
||||
import com.r11.tools.xml.Saxon;
|
||||
import com.r11.tools.xml.Xalan;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
@GlobalControllerManifest
|
||||
public class XsltController implements RestController {
|
||||
|
||||
private final Gson gson;
|
||||
private final Logger logger;
|
||||
|
||||
public XsltController(Gson gson, Logger logger) {
|
||||
this.gson = gson;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@ScopedControllerManifest(method = HandlerType.POST, path = "/xslt")
|
||||
public void transform(Request request, Response response) {
|
||||
String body = request.body();
|
||||
|
||||
JsonObject requestJson;
|
||||
try {
|
||||
requestJson = this.gson.fromJson(body, JsonObject.class);
|
||||
} catch (Exception e) {
|
||||
JsonObject responseJson = new JsonObject();
|
||||
responseJson.addProperty("result", e.getMessage());
|
||||
responseJson.addProperty("processor", "N/A");
|
||||
responseJson.addProperty("status", "ERR");
|
||||
responseJson.addProperty("time", "N/A");
|
||||
|
||||
response.status(400);
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
return;
|
||||
}
|
||||
|
||||
String data = requestJson.get("data").getAsString();
|
||||
String query = requestJson.get("process").getAsString();
|
||||
String processor = requestJson.get("processor").getAsString();
|
||||
String version = requestJson.get("version").getAsString();
|
||||
|
||||
if (processor == null) {
|
||||
response.body("saxon, xalan");
|
||||
return;
|
||||
}
|
||||
|
||||
String tmp;
|
||||
long timeStart;
|
||||
long duration;
|
||||
|
||||
JsonObject responseJson = new JsonObject();
|
||||
switch (processor) {
|
||||
case "saxon":
|
||||
timeStart = System.currentTimeMillis();
|
||||
try {
|
||||
tmp = Saxon.processXSLT(data, query);
|
||||
|
||||
response.status(200);
|
||||
|
||||
responseJson.addProperty("result", tmp);
|
||||
responseJson.addProperty("status", "OK");
|
||||
} catch (Exception ex) {
|
||||
this.logger.error("Error on processing XSLT using Saxon. " + ex);
|
||||
|
||||
response.status(400);
|
||||
|
||||
responseJson.addProperty("result", ex.getMessage());
|
||||
responseJson.addProperty("status", "ERR");
|
||||
}
|
||||
|
||||
duration = System.currentTimeMillis() - timeStart;
|
||||
this.logger.info("Request (XSLT, Saxon) processed in " + duration + " ms.");
|
||||
|
||||
responseJson.addProperty("processor", "Saxon " + Saxon.getVersion() + " " + version);
|
||||
responseJson.addProperty("time", duration);
|
||||
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
return;
|
||||
|
||||
case "xalan":
|
||||
timeStart = System.currentTimeMillis();
|
||||
try {
|
||||
tmp = Xalan.processXSLT(data, query);
|
||||
|
||||
response.status(200);
|
||||
|
||||
responseJson.addProperty("result", tmp);
|
||||
responseJson.addProperty("status", "OK");
|
||||
} catch (Exception ex) {
|
||||
this.logger.error("Error on processing XSLT using Xalan. " + ex);
|
||||
|
||||
response.status(400);
|
||||
|
||||
responseJson.addProperty("result", ex.getMessage());
|
||||
responseJson.addProperty("status", "ERR");
|
||||
}
|
||||
|
||||
duration = System.currentTimeMillis() - timeStart;
|
||||
this.logger.info("Request (XSLT, Xalan) processed in " + duration + " ms.");
|
||||
|
||||
responseJson.addProperty("processor", Xalan.getVersion());
|
||||
responseJson.addProperty("time", duration);
|
||||
|
||||
response.body(this.gson.toJson(responseJson));
|
||||
return;
|
||||
|
||||
default:
|
||||
response.body("saxon, xalan");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.r11.tools.controller.internal;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface GlobalControllerManifest {
|
||||
|
||||
String path() default "";
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.r11.tools.controller.internal;
|
||||
|
||||
public enum HandlerType {
|
||||
|
||||
GET, POST, PUT, DELETE
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.r11.tools.controller.internal;
|
||||
|
||||
public interface RestController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.r11.tools.controller.internal;
|
||||
|
||||
import com.r11.tools.controller.internal.path.PathBuilder;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import spark.Spark;
|
||||
|
||||
public class RestControllerRegistry {
|
||||
|
||||
private final Set<RestController> registeredControllers;
|
||||
|
||||
public RestControllerRegistry() {
|
||||
this.registeredControllers = new HashSet<>();
|
||||
}
|
||||
|
||||
public void registerController(RestController restController) {
|
||||
this.registeredControllers.add(restController);
|
||||
}
|
||||
|
||||
public void register() {
|
||||
this.registeredControllers.forEach(controller -> {
|
||||
if (controller.getClass().isAnnotationPresent(GlobalControllerManifest.class)) {
|
||||
for (Method method : controller.getClass().getMethods()) {
|
||||
this.registerAssignableHandlers(controller.getClass(), controller, method);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void registerAssignableHandlers(Class<? extends RestController> parent, RestController parentValue, Method method) {
|
||||
if (
|
||||
(parent.isAnnotationPresent(GlobalControllerManifest.class)) &&
|
||||
(method.isAnnotationPresent(ScopedControllerManifest.class))
|
||||
) {
|
||||
HandlerType handlerType = method.getAnnotation(ScopedControllerManifest.class).method();
|
||||
|
||||
String path = PathBuilder.resolvePathOf(
|
||||
parent.getAnnotation(GlobalControllerManifest.class).path(),
|
||||
method.getAnnotation(ScopedControllerManifest.class).path()
|
||||
);
|
||||
|
||||
switch (handlerType) {
|
||||
case GET:
|
||||
Spark.get(path, (request, response) -> method.invoke(parentValue, request, response));
|
||||
break;
|
||||
case PUT:
|
||||
Spark.put(path, (request, response) -> method.invoke(parentValue, request, response));
|
||||
break;
|
||||
case POST:
|
||||
Spark.post(path, (request, response) -> method.invoke(parentValue, request, response));
|
||||
break;
|
||||
case DELETE:
|
||||
Spark.delete(path, (request, response) -> method.invoke(parentValue, request, response));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.r11.tools.controller.internal;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface ScopedControllerManifest {
|
||||
|
||||
HandlerType method();
|
||||
|
||||
String path();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.r11.tools.controller.internal.path;
|
||||
|
||||
public final class PathBuilder {
|
||||
|
||||
private static final String PATH_SEPARATOR = "/";
|
||||
|
||||
private PathBuilder() {
|
||||
|
||||
}
|
||||
|
||||
public static String resolvePathOf(String globalPath, String scopedPath) {
|
||||
String resolvedPath =
|
||||
PathBuilder.removeTrailingPathSeparator(globalPath) +
|
||||
PathBuilder.removeTrailingPathSeparator(scopedPath);
|
||||
|
||||
if (resolvedPath.endsWith(PATH_SEPARATOR)) {
|
||||
resolvedPath = resolvedPath.substring(0, resolvedPath.length() - 1);
|
||||
}
|
||||
|
||||
return PATH_SEPARATOR + resolvedPath;
|
||||
}
|
||||
|
||||
private static String removeTrailingPathSeparator(String path) {
|
||||
if (path.endsWith(PATH_SEPARATOR)) {
|
||||
return path.substring(0, path.length() - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package r11.mltx.restxslt.processors;
|
||||
package com.r11.tools.xml;
|
||||
|
||||
import net.sf.saxon.om.NamespaceBinding;
|
||||
import net.sf.saxon.om.NamespaceMap;
|
||||
import net.sf.saxon.s9api.XPathCompiler;
|
||||
import net.sf.saxon.s9api.XdmNode;
|
||||
|
||||
import java.util.Iterator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Handler for saxon namespace scan engine.
|
||||
@@ -13,6 +12,7 @@ import java.util.Iterator;
|
||||
* @author Wojciech Czop
|
||||
*/
|
||||
public class NewNamespaceResolver {
|
||||
private static final Logger LOG = LogManager.getLogger("NewNamespaceResolver");
|
||||
|
||||
private NamespaceMap namespaceMap;
|
||||
|
||||
@@ -21,12 +21,13 @@ public class NewNamespaceResolver {
|
||||
* @param doc dom structure object
|
||||
* @return map of namespaces
|
||||
*/
|
||||
|
||||
// TODO: Closer inspection. Return value is never used according to IntelliJ
|
||||
//
|
||||
public NamespaceMap process(XdmNode doc) {
|
||||
namespaceMap = NamespaceMap.emptyMap();
|
||||
Iterator<XdmNode> it = doc.children().iterator();
|
||||
// TODO: remove
|
||||
while (it.hasNext()) {
|
||||
XdmNode tmp = it.next();
|
||||
// TODO: remove
|
||||
for (XdmNode tmp : doc.children()) {
|
||||
extractNamespace(tmp);
|
||||
}
|
||||
// end
|
||||
@@ -38,12 +39,6 @@ public class NewNamespaceResolver {
|
||||
* @param compiler compiler used to compile xpath statements
|
||||
*/
|
||||
public void exportNamespaces(XPathCompiler compiler){
|
||||
Iterator<NamespaceBinding> it = namespaceMap.iterator();
|
||||
// TODO: remove
|
||||
while(it.hasNext()){
|
||||
System.out.println(it.next());
|
||||
}
|
||||
// end
|
||||
namespaceMap.forEach(namespaceBinding -> compiler.declareNamespace(namespaceBinding.getPrefix(), namespaceBinding.getURI()));
|
||||
}
|
||||
|
||||
@@ -58,17 +53,11 @@ public class NewNamespaceResolver {
|
||||
}
|
||||
if (node.children().iterator().hasNext()) {
|
||||
|
||||
Iterator<XdmNode> it = node.children().iterator();
|
||||
while (it.hasNext()) {
|
||||
XdmNode rNode = it.next();
|
||||
// TODO: remove
|
||||
if(rNode.getUnderlyingNode().getPrefix().isEmpty() && !rNode.getParent().getUnderlyingNode().getPrefix().isEmpty()){
|
||||
|
||||
System.out.println("prefix missing, parent has "+rNode.getParent().getUnderlyingNode().getPrefix() + ", but child has none");
|
||||
|
||||
System.out.println();
|
||||
for (XdmNode rNode : node.children()) {
|
||||
if (rNode.getUnderlyingNode().getPrefix().isEmpty() && !rNode.getParent().getUnderlyingNode().getPrefix().isEmpty()) {
|
||||
LOG.warn("Missing prefix. Parent has " + rNode.getParent().getUnderlyingNode().getPrefix() + ", but child has none");
|
||||
}
|
||||
// end
|
||||
|
||||
extractNamespace(rNode);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package r11.mltx.restxslt.processors;
|
||||
package com.r11.tools.xml;
|
||||
|
||||
import net.sf.saxon.s9api.*;
|
||||
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
@@ -1,6 +1,10 @@
|
||||
package r11.mltx.restxslt.processors;
|
||||
package com.r11.tools.xml;
|
||||
|
||||
import org.apache.xpath.XPathAPI;
|
||||
import org.apache.xpath.objects.XObject;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.traversal.NodeIterator;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
@@ -13,18 +17,14 @@ import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Handler for Xalan engine
|
||||
* @author Wojciech Czop
|
||||
*/
|
||||
public class Xalan {
|
||||
|
||||
/**
|
||||
* Transforms string containing xml document via xslt
|
||||
* @param data xml to be transformed
|
||||
@@ -50,25 +50,60 @@ public class Xalan {
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
private static boolean isTextNode(Node n) {
|
||||
if (n == null)
|
||||
return false;
|
||||
short nodeType = n.getNodeType();
|
||||
return nodeType == Node.CDATA_SECTION_NODE || nodeType == Node.TEXT_NODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process xpath and return either node or wrapped atomic value
|
||||
* @deprecated
|
||||
* Xalan needs assumption of the outcome, which is not implemented. Therefore method is deprecated
|
||||
* @param data xml
|
||||
* @param transform xpath
|
||||
* @return xml processed using given xpath
|
||||
* @throws Exception thrown on node building errors or invalid xpath
|
||||
*/
|
||||
public static String processXPath(String data, String transform) throws Exception{
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
public static String processXPath(String data, String transform) throws Exception {
|
||||
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
// Set up a DOM tree to query.
|
||||
InputSource in = new InputSource(new StringReader(data));
|
||||
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
|
||||
dfactory.setNamespaceAware(true);
|
||||
Document doc = dfactory.newDocumentBuilder().parse(in);
|
||||
|
||||
// Set up an identity transformer to use as serializer.
|
||||
Transformer serializer = TransformerFactory.newInstance().newTransformer();
|
||||
serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
|
||||
// Use the simple XPath API to select a nodeIterator.
|
||||
try {
|
||||
NodeIterator nl = XPathAPI.selectNodeIterator(doc, transform);
|
||||
|
||||
// Serialize the found nodes to result object.
|
||||
StringBuilder result = new StringBuilder();
|
||||
Node n;
|
||||
while ((n = nl.nextNode())!= null) {
|
||||
StringBuilder sb;
|
||||
if (isTextNode(n)) {
|
||||
// DOM may have more than one node corresponding to a
|
||||
// single XPath text node. Coalesce all contiguous text nodes
|
||||
// at this level
|
||||
for (Node nn = n.getNextSibling(); isTextNode(nn); nn = nn.getNextSibling()) {
|
||||
result.append(nn.getNodeValue());
|
||||
}
|
||||
} else {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
serializer.transform(new DOMSource(n), new StreamResult(new OutputStreamWriter(outputStream)));
|
||||
result.append(outputStream);
|
||||
}
|
||||
result.append("\n");
|
||||
}
|
||||
return result.toString();
|
||||
} catch (TransformerException e) {
|
||||
return XPathAPI.eval(doc, transform).toString();
|
||||
}
|
||||
|
||||
xpath.setNamespaceContext(new XalanNamespaceResolver(builder.parse(new InputSource(new StringReader(data))), true));
|
||||
XPathExpression exp = xpath.compile(transform);
|
||||
exp.evaluate(new InputSource(new StringReader(data)), XPathConstants.NODESET);
|
||||
return exp.evaluate(new InputSource(new StringReader(data)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,4 +1,4 @@
|
||||
package r11.mltx.restxslt.processors;
|
||||
package com.r11.tools.xml;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
|
||||
21
Backend/tools-services/src/main/resources/log4j2.xml
Normal file
21
Backend/tools-services/src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%c] %-5level - %msg%n"/>
|
||||
</Console>
|
||||
<File name="File" fileName="/tmp/xml_tools_java_backend.log" append="true">
|
||||
<PatternLayout>
|
||||
<Pattern>%d{HH:mm:ss.SSS} [%c] %-5level - %msg%n</Pattern>
|
||||
</PatternLayout>
|
||||
</File>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="com.r11.tools.SparkApplication" level="info" additivity="true">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Root level="info">
|
||||
<AppenderRef ref="File"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
2
Filebeat/Dockerfile
Normal file
2
Filebeat/Dockerfile
Normal file
@@ -0,0 +1,2 @@
|
||||
FROM docker.elastic.co/beats/filebeat:8.6.2
|
||||
COPY --chown=root:filebeat filebeat.docker.yml /usr/share/filebeat/filebeat.yml
|
||||
21
Filebeat/filebeat.docker.yml
Normal file
21
Filebeat/filebeat.docker.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
filebeat.config:
|
||||
modules:
|
||||
path: ${path.config}/modules.d/*.yml
|
||||
reload.enabled: false
|
||||
|
||||
filebeat.autodiscover:
|
||||
providers:
|
||||
- type: docker
|
||||
hints.enabled: true
|
||||
|
||||
processors:
|
||||
- add_cloud_metadata: ~
|
||||
|
||||
output.elasticsearch:
|
||||
hosts: '${ELASTICSEARCH_HOSTS:elc-0.zipper.release11.com:9200}'
|
||||
index: 'xmltools-${ENV_TYPE:dev}-%{+YYYY.MM}'
|
||||
|
||||
|
||||
setup.template:
|
||||
name: "xmltools"
|
||||
pattern: "xmltools-*"
|
||||
16
Frontend/Dockerfile
Normal file
16
Frontend/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM nginx:stable-alpine
|
||||
|
||||
COPY ./tools/ /usr/share/nginx/html/tools/
|
||||
COPY ./lawful/ /usr/share/nginx/html/lawful/
|
||||
COPY ./assets/ /usr/share/nginx/html/assets/
|
||||
COPY ./index.html /usr/share/nginx/html
|
||||
|
||||
RUN mkdir -p /scripts
|
||||
COPY insert_version.sh /scripts/
|
||||
WORKDIR /scripts
|
||||
|
||||
RUN chmod +x insert_version.sh
|
||||
RUN ./insert_version.sh
|
||||
|
||||
|
||||
EXPOSE 80
|
||||
6
Frontend/assets/css/common/common.css
Normal file
6
Frontend/assets/css/common/common.css
Normal file
@@ -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');
|
||||
BIN
Frontend/assets/css/common/font/fontello.eot
Normal file
BIN
Frontend/assets/css/common/font/fontello.eot
Normal file
Binary file not shown.
14
Frontend/assets/css/common/font/fontello.svg
Normal file
14
Frontend/assets/css/common/font/fontello.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Copyright (C) 2021 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
<missing-glyph horiz-adv-x="1000" />
|
||||
<glyph glyph-name="plus" unicode="" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
||||
|
||||
<glyph glyph-name="cancel" unicode="" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Frontend/assets/css/common/font/fontello.ttf
Normal file
BIN
Frontend/assets/css/common/font/fontello.ttf
Normal file
Binary file not shown.
BIN
Frontend/assets/css/common/font/fontello.woff
Normal file
BIN
Frontend/assets/css/common/font/fontello.woff
Normal file
Binary file not shown.
BIN
Frontend/assets/css/common/font/fontello.woff2
Normal file
BIN
Frontend/assets/css/common/font/fontello.woff2
Normal file
Binary file not shown.
59
Frontend/assets/css/common/fontello.css
vendored
Normal file
59
Frontend/assets/css/common/fontello.css
vendored
Normal file
@@ -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'; } /* '' */
|
||||
145
Frontend/assets/css/frame.css
Normal file
145
Frontend/assets/css/frame.css
Normal file
@@ -0,0 +1,145 @@
|
||||
@font-face {
|
||||
font-family: "Nunito";
|
||||
src: url('../fonts/Nunito-VariableFont_wght.ttf') format('truetype');
|
||||
}
|
||||
|
||||
html {
|
||||
background-image: url("../images/background.jpg");
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Nunito', sans-serif;
|
||||
font-weight: 200;
|
||||
|
||||
color: #2e3133;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
div#header {
|
||||
background-color: #FFFFFF;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#logo {
|
||||
padding: 20px 20px 20px;
|
||||
width: 250px;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
iframe#frame {
|
||||
flex-grow: 1;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
div#content {
|
||||
width: 100%;
|
||||
height: calc(100% - 80px);
|
||||
display: flex;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
div#leftBar {
|
||||
float: left;
|
||||
width: 200px;
|
||||
background-color: transparent;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
div#copyright{
|
||||
color:rgb(192, 192, 192);
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
width: 200px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div#copyright a, a:visited, a:active {
|
||||
color: rgb(192, 192, 192);
|
||||
}
|
||||
|
||||
#toolList {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 10px 0 0 0;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
float: left;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
height: calc(100% - 80px);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.toolListRow a {
|
||||
display: block;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 20px 50px 25px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.toolListRow a:hover {
|
||||
background-color: #2A93B0;
|
||||
color: white;
|
||||
transform: scale(1.25, 1.25);
|
||||
transition-duration: .3s;
|
||||
}
|
||||
|
||||
#leftElements {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#titlebar {
|
||||
/* padding: 10px 0; */
|
||||
color: black;
|
||||
height: fit-content;
|
||||
margin: 0px 20px;
|
||||
font-size: 36px;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
#menu {
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
|
||||
}
|
||||
|
||||
#menu a {
|
||||
display: block;
|
||||
margin: 0px 10px;
|
||||
padding: 0px 10px;
|
||||
font-size: 28px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#menu a.active {
|
||||
border-bottom: 3px solid #2A93B0;
|
||||
|
||||
}
|
||||
|
||||
#menu a:hover {
|
||||
transform: scale(1.25, 1.25);
|
||||
transition-duration: .3s;
|
||||
}
|
||||
69
Frontend/assets/css/json.css
Normal file
69
Frontend/assets/css/json.css
Normal file
@@ -0,0 +1,69 @@
|
||||
.json-block {
|
||||
height: 600px;
|
||||
width: 97%;
|
||||
}
|
||||
|
||||
.json-border {
|
||||
border: 2px solid rgba(93, 99, 96, 0.705);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.json-border:focus {
|
||||
box-shadow: 0 0 5px rgb(81, 203, 238);
|
||||
border: 2px solid rgba(93, 99, 96, 0.705);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/*! Theme: Default Description: Original highlight.js style Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org> Maintainer: @highlightjs/core-team Website: https://highlightjs.org/ License: see project LICENSE Touched: 2021 */
|
||||
pre code.hljs{
|
||||
display:block;
|
||||
overflow-x:auto;
|
||||
padding:1em
|
||||
}
|
||||
code.hljs{
|
||||
padding:3px 5px
|
||||
}
|
||||
.hljs{
|
||||
background:#FFFFFF;
|
||||
color:#444
|
||||
}
|
||||
.hljs-comment{
|
||||
color:#697070
|
||||
}
|
||||
.hljs-punctuation,.hljs-tag{
|
||||
color:#444a
|
||||
}
|
||||
.hljs-tag .hljs-attr,.hljs-tag .hljs-name{
|
||||
color:#444
|
||||
}
|
||||
.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{
|
||||
font-weight:700
|
||||
}
|
||||
.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{
|
||||
color:#800
|
||||
}
|
||||
.hljs-section,.hljs-title{
|
||||
color:#800;
|
||||
font-weight:700
|
||||
}
|
||||
.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{
|
||||
color:#ab5656
|
||||
}
|
||||
.hljs-literal{
|
||||
color:#695
|
||||
}
|
||||
.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{
|
||||
color:#397300
|
||||
}
|
||||
.hljs-meta{
|
||||
color:#1f7199
|
||||
}
|
||||
.hljs-meta .hljs-string{
|
||||
color:#38a
|
||||
}
|
||||
.hljs-emphasis{
|
||||
font-style:italic
|
||||
}
|
||||
.hljs-strong{
|
||||
font-weight:700
|
||||
}
|
||||
42
Frontend/assets/css/lawful.css
Normal file
42
Frontend/assets/css/lawful.css
Normal file
@@ -0,0 +1,42 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@200&display=swap');
|
||||
|
||||
body {
|
||||
font-family: "Nunito", sans-serif;
|
||||
background-color: #FFFFFF;
|
||||
margin: 0px;
|
||||
|
||||
}
|
||||
h1, h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2::before {
|
||||
background: url('/assets/images/sygnet_color.svg') no-repeat;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#header {
|
||||
height: 80px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #FFFFFF;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#logo {
|
||||
width: 250px;
|
||||
margin: 0px 20px;
|
||||
}
|
||||
|
||||
#content {
|
||||
width: 1024px;
|
||||
margin: auto;
|
||||
text-align: justify;
|
||||
background-color: #FFFFFF;
|
||||
padding: 20px 20px;
|
||||
border-radius: 15px;
|
||||
margin-top: 100px;
|
||||
}
|
||||
BIN
Frontend/assets/css/tools/font/fontello.eot
Normal file
BIN
Frontend/assets/css/tools/font/fontello.eot
Normal file
Binary file not shown.
14
Frontend/assets/css/tools/font/fontello.svg
Normal file
14
Frontend/assets/css/tools/font/fontello.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Copyright (C) 2021 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
<missing-glyph horiz-adv-x="1000" />
|
||||
<glyph glyph-name="plus" unicode="" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
|
||||
|
||||
<glyph glyph-name="cancel" unicode="" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Frontend/assets/css/tools/font/fontello.ttf
Normal file
BIN
Frontend/assets/css/tools/font/fontello.ttf
Normal file
Binary file not shown.
BIN
Frontend/assets/css/tools/font/fontello.woff
Normal file
BIN
Frontend/assets/css/tools/font/fontello.woff
Normal file
Binary file not shown.
BIN
Frontend/assets/css/tools/font/fontello.woff2
Normal file
BIN
Frontend/assets/css/tools/font/fontello.woff2
Normal file
Binary file not shown.
59
Frontend/assets/css/tools/fontello.css
vendored
Normal file
59
Frontend/assets/css/tools/fontello.css
vendored
Normal file
@@ -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'; } /* '' */
|
||||
@@ -1,7 +1,14 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;700&display=swap');
|
||||
@import url('https://necolas.github.io/normalize.css/8.0.1/normalize.css');
|
||||
@import url('fontello.css');
|
||||
|
||||
@font-face {
|
||||
font-family: "Nunito";
|
||||
src: url('../../fonts/Nunito-VariableFont_wght.ttf') format('truetype');
|
||||
}
|
||||
|
||||
body {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.hyperlink, .hyperlink:visited, .hyperlink:active {
|
||||
color: rgb(47, 125, 146);
|
||||
@@ -15,12 +22,12 @@
|
||||
.tooltip-window {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
filter: drop-shadow(-2px 0px 2px darkgray);
|
||||
background: #e8f3f7;
|
||||
/* filter: drop-shadow(-2px 0px 2px black); */
|
||||
background: #FFFFFF;
|
||||
padding: 15px 30px;
|
||||
font-family: 'Nunito', sans-serif;
|
||||
width: 40%;
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
height: calc(100% - 25px);
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
@@ -31,9 +38,6 @@
|
||||
content: "▼";
|
||||
} */
|
||||
|
||||
.tooltip-window .tip {
|
||||
}
|
||||
|
||||
.bordered-field {
|
||||
border: 2px solid rgba(93, 99, 96, 0.705);
|
||||
border-radius: 5px;
|
||||
@@ -57,13 +61,15 @@
|
||||
|
||||
.container {
|
||||
font-family: 'Nunito', sans-serif;
|
||||
|
||||
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tool {
|
||||
width: 55%;
|
||||
width: 65%;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
@@ -255,13 +261,15 @@
|
||||
}
|
||||
|
||||
.action-button.active {
|
||||
background: #3bc4f1;
|
||||
background: #2A93B0;
|
||||
border: 1px solid #7ed0eb;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.action-button.active:hover {
|
||||
filter: brightness(110%);
|
||||
transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
@@ -289,6 +297,10 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.half-width {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.max-width.with-padding {
|
||||
width: 94%;
|
||||
}
|
||||
@@ -328,6 +340,10 @@
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.textarea-700 {
|
||||
height: 700px;
|
||||
}
|
||||
|
||||
.centered-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -390,15 +406,18 @@
|
||||
.section-button {
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
margin: 5px 0px;
|
||||
font-size: 18px;
|
||||
background: #b4b4b4c5;
|
||||
background: #D5D7E6;
|
||||
cursor: pointer;
|
||||
border-bottom: darkgray 2px solid !important;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.section-button:hover {
|
||||
/* border-bottom: #3bc4f1 2px solid; */
|
||||
backdrop-filter: brightness(110%);
|
||||
backdrop-filter: brightness(100%);
|
||||
transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.section-button .active {
|
||||
@@ -437,10 +456,6 @@
|
||||
content: "▼";
|
||||
}
|
||||
|
||||
.content.active{
|
||||
|
||||
}
|
||||
|
||||
.hiddable {
|
||||
display: none;
|
||||
}
|
||||
@@ -479,6 +494,24 @@ textarea {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
code{
|
||||
code {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1024px) {
|
||||
.rwd-hideable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rwd-expandable {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user