Merge branch '97-ersetzen-von-docsify-oder-deaktivieren-um-vulnaribility-loszuwerden' into 'main'

Resolve #97 "Ersetzen von docsify oder Deaktivieren um Vulnaribility loszuwerden"

See merge request kosit/validator!48
This commit is contained in:
Andreas Penski 2022-11-18 07:21:57 +00:00
commit 9d6c4cac9e
100 changed files with 27369 additions and 1072 deletions

View file

@ -9,31 +9,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- [CLI][#93](https://projekte.kosit.org/kosit/validator/-/issues/93) Remove usage information, when validation failed
- [CLI][#95](https://projekte.kosit.org/kosit/validator/-/issues/95) NPE when using empty repository definition (-r "")
- (CLI) [#93](https://projekte.kosit.org/kosit/validator/-/issues/93) Remove usage information, when validation failed
- (CLI) [#95](https://projekte.kosit.org/kosit/validator/-/issues/95) NPE when using empty repository definition (-r "")
- (CORE) [#101](https://github.com/itplr-kosit/validator/issues/101) Role is null in FailedAssert
### Added
- [CLI] Support for multiple configurations and multiple repositories. See [cli documentation](docs/cli.md) for details
- [API ]Possibility to use preconfigured Saxon `Processor` instance for validation
- (CLI) Support for multiple configurations and multiple repositories. See [cli documentation](docs/cli.md) for details
- (API) Possibility to use preconfigured Saxon `Processor` instance for validation
### Changed
- [API] [ResolvingConfigurationStrategy.java#getProcessor()](de/kosit/validationtool/api/ResolvingConfigurationStrategy)
is
removed.
- [INTERNAL] Bump [Saxon HE](https://www.saxonica.com/documentation11/documentation.xml) to 11.4
- [INTERNAL] Bump [jaxb-ri](https://github.com/eclipse-ee4j/jaxb-ri) to 2.3.7
- (DAEMON) UI rewrite based on [Docusaurs](https://docusaurus.io)
- (
API) [ResolvingConfigurationStrategy.java#getProcessor()](de/kosit/validationtool/api/ResolvingConfigurationStrategy)
is removed.
- (CORE) Bump [Saxon HE](https://www.saxonica.com/documentation11/documentation.xml) to 11.4
- (CORE) Bump [jaxb-ri](https://github.com/eclipse-ee4j/jaxb-ri) to 2.3.7
- (CORE) Various other dependency updates. See pom.xml
- [INTERNAL] CLI parsing based on pico-cli, commons-cli is removed
- (CORE) CLI parsing based on pico-cli, commons-cli is removed
## 1.4.2
### Fixed
- [CLI] [#74](https://projekte.kosit.org/kosit/validator/-/issues/74) fix ansi output of the cli version
- (CLI) [#74](https://projekte.kosit.org/kosit/validator/-/issues/74) fix ansi output of the cli version
- [#80](https://github.com/itplr-kosit/validator/issues/80) using classloader to initialize jaxb context (to support
usage in OSGi
environments)
@ -53,7 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- date conversion when
using [ConfigurationBuilder#date(Date)](https://github.com/itplr-kosit/validator/blob/d7beb1040418ae5cbeb9427532fd87482f55756c/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java#L109)
- [CLI] [#51](https://github.com/itplr-kosit/validator/issues/51) Suffix of report xml is missing
- (CLI) [#51](https://github.com/itplr-kosit/validator/issues/51) Suffix of report xml is missing
- [#53](https://github.com/itplr-kosit/validator/issues/53) Fix copyright and licensing information
- [#56](https://github.com/itplr-kosit/validator/issues/56) `namespace` element content needs trimming
- [DAEMON] [#57](https://github.com/itplr-kosit/validator/issues/57) Reading large inputs correctly
@ -61,65 +62,83 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- read saxon XdmNode with InputFactory
- [CLI] custom output without the various log messages
- [CLI] options to set the log level (`-X` = full debug output, `-l <level>` set a specific level)
- [CLI] return code ist <> 0 on rejected results
- [CLI] read (single) test target from stdin
- (CLI) custom output without the various log messages
- (CLI) options to set the log level (`-X` = full debug output, `-l <level>` set a specific level)
- (CLI) return code is not 0 on rejected results
- (CLI) read (single) test target from stdin
- [DAEMON] name inputs via request URI
### Changed
- InputFactory has methods to read any java.xml.transform.Source as Input not only StreamSources
- InputFactory uses a generated UUID as name for SourceInput, if no "real" name can be derived
- saxon dependency update (minor, 9.9.1-7)
- [DAEMON] proper status codes when returning results (see [daemon documentation](./docs/daemon.md#status-codes))
## 1.3.1
### Fixed
- `getFailedAsserts()` and `isSchematronValid()` in [DefaultResult.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/impl/DefaultResult.java)
do not reflect actual schematron validation result
- processing aborts on schematron execution errors (e.g. errors within schematron logic). The validator now generates a report in such cases.
- `getFailedAsserts()` and `isSchematronValid()`
in [DefaultResult.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/impl/DefaultResult.java)
do not reflect actual schematron validation result
- processing aborts on schematron execution errors (e.g. errors within schematron logic). The validator now generates a
report in such cases.
- exception while resolving when using XSLT's `unparsed-text()` function within report generation
### Added
- [CLI] summary report
- (CLI) summary report
### Changed
- engine info contains version number of the validator (configurations can output this in the report for maintainance puposes)
- engine info contains version number of the validator (configurations can output this in the report for maintainance
puposes)
- options to customize serialized report file names (cmdline only) via `--report-prefix` and `--report-postfix`
- remove unused dependency Apache Commons HTTP
## 1.3.0
### Added
- Added a builder style configuration API to configure scenarios
- Added an option to configure xml security e.g. to load from http sources or not from a specific repository
(so loading is configurable less restrictive, default strategy is to only load from a local repository)
(so loading is configurable less restrictive, default strategy is to only load from a local repository)
- Support java.xml.transform.Source as Input
### Changed
- Inputs are NOT read into memory (e.g. Byte-Array) prior processing within the validator. This reduces memory consumption.
- Inputs are NOT read into memory (e.g. Byte-Array) prior processing within the validator. This reduces memory
consumption.
- Overall processing of xml files is based on Saxon s9api. No JAXP or SAX classes are used by
the validator (this further improves performance and memory consumption)
the validator (this further improves performance and memory consumption)
### Deprecations
- CheckConfiguration is deprecated now. Use Configuration.load(...) or Configuration.build(...)
## 1.2.1
### Fixed
- Validator is creating invalid createReportInput xml in case of no scenario match
## 1.2.0
### Added
- Provide access to schematron result through [Result.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Result.java)
- Provide access to schematron result
through [Result.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Result.java)
- *Result#getFailedAsserts()* returns a list of failed asserts found by schematron
- *Result#isSchematronValid()* convinience access to evaluate whether schematron was processed without any *FailedAsserts*
- *Result#isSchematronValid()* convinience access to evaluate whether schematron was processed without any *
FailedAsserts*
### Changed
- *Result#getAcceptRecommendation()* does not _only_ work when _acceptMatch_ is configured in the scenario
- schema correctness is a precondition, if the checked instance is not valid, this evaluates to _REJECTED_
- if _acceptMatch_ is configured, the result is based on the boolean result of the xpath expression evaluated against the generated report
- if _acceptMatch_ is configured, the result is based on the boolean result of the xpath expression evaluated against
the generated report
- if *no* _acceptMatch_ is configured, the result is based on evaluation of schema and schematron correctness
- _UNDEFINED_ is only returned, when processing is stopped somehow
- *Result#isAcceptable()* can now evaluate to true, when no _acceptMatch_ is configured (see above)
@ -129,7 +148,8 @@ the validator (this further improves performance and memory consumption)
### Fixed
- XXE vulnerability when reading xml documents with Saxon [#44](https://github.com/itplr-kosit/validator/issues/44)
- validator unintentionally stopped when schematron processing has errors. See [#41](https://github.com/itplr-kosit/validator/issues/41).
- validator unintentionally stopped when schematron processing has errors.
See [#41](https://github.com/itplr-kosit/validator/issues/41).
## 1.1.2
@ -175,7 +195,8 @@ the validator (this further improves performance and memory consumption)
### Changed
- Removed XRechnung configuration from release artifacts and source (moved to [own repository](https://github.com/itplr-kosit/validator-configuration-xrechnung) )
- Removed XRechnung configuration from release artifacts and source (moved
to [own repository](https://github.com/itplr-kosit/validator-configuration-xrechnung) )
## 1.0.0

18
server/ui/.eslintignore Normal file
View file

@ -0,0 +1,18 @@
**/build/
**/dist/
**/coverage/
**/.nyc_output
**/.husky
**/.vscode
**/.webpack
packages/electron/out
**/node_modules/
**/tmp/
**/package-lock.json
**/pnpm-lock.yaml
**/yarn.lock
**/package.json
**/tsconfig.json
**/*.html
packages/*/types
.docusaurus

55
server/ui/.eslintrc Normal file
View file

@ -0,0 +1,55 @@
{
"plugins": ["prettier", "@typescript-eslint/eslint-plugin", "react"],
"extends": [
"airbnb",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:prettier/recommended"
],
"rules": {
"indent": "off",
"@typescript-eslint/indent": "off",
"@typescript-eslint/explicit-member-accessibility": "off",
"@typescript-eslint/consistent-type-imports": [
"error",
{ "prefer": "type-imports" }
],
"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
"react/jsx-props-no-spreading": "off",
"react/require-default-props": "off",
"react/jsx-filename-extension": "off",
"react/jsx-one-expression-per-line": "off",
"react/function-component-definition": "off",
"react/jsx-no-useless-fragment": "off",
"import/extensions": "off",
"import/no-unresolved": "off",
"no-console": "warn",
"no-shadow": "off",
"no-continue": "off",
"no-restricted-syntax": "off",
"@typescript-eslint/no-shadow": ["error"],
"import/no-relative-packages": "off"
},
"env": {
"es6": true,
"browser": true,
"node": true,
"jest": true
},
"parser": "@typescript-eslint/parser",
"settings": {
"react": {
"version": "17.0"
},
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"]
},
"import/resolver": {
"typescript": {
"alwaysTryTypes": true
}
}
}
}

20
server/ui/.gitignore vendored Normal file
View file

@ -0,0 +1,20 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

20
server/ui/.prettierignore Normal file
View file

@ -0,0 +1,20 @@
**/build/
**/dist/
**/build/
**/coverage/
**/.nyc_output
**/.husky
**/.vscode
**/.webpack
packages/electron/out
**/node_modules/
**/tmp
**/package-lock.json
**/pnpm-lock.yaml
**/yarn.lock
**/package.json
packages/*/types
**/*.yaml
**/*.yml
docs
.docusaurus

6
server/ui/.prettierrc.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
trailingComma: "all",
useTabs: true,
proseWrap: "always",
endOfLine: "auto",
};

27
server/ui/README.md Normal file
View file

@ -0,0 +1,27 @@
# Website
This folder contains the ui, served by the daemon version of the validator. At
the moment, this is generated within this module and copied to the actual source
location of the daemon. There are plans to modularize the whole validator source
in the future so that this will be done by build process.
This ui is built using [Docusaurus 2](https://docusaurus.io/), a modern static
website generator.
### Local Development
```
$ npm start
```
This command starts a local development server and opens up a browser window.
Most changes are reflected live without having to restart the server.
### Build
```
$ npm build
```
This command generates static content into the `build` directory and must be
copied to `src/main/resources/ui`

View file

@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve("@docusaurus/core/lib/babel/preset")],
};

51
server/ui/docs/api.md Normal file
View file

@ -0,0 +1,51 @@
---
sidebar_position: 2
---
# API Usage
The validation service listens to `POST`-requests to any server uri. You need to supply the xml/object to validate in
the post body.
The service expects a single plain input in the post body, e.g. `multipart/form-data` is not supported.
Examples:
* `cURL`
```shell script
curl --location --request POST 'http://localhost:8080' \
--header 'Content-Type: application/xml' \
--data-binary '@/target.xml'
```
* `java` (Apache HttpClient)
```java
HttpClient httpClient=HttpClientBuilder.create().build();
HttpPost postRequest=new HttpPost("http://localhost:8080/");
FileEntity entity=new FileEntity(Paths.get("some.xml").toFile(),ContentType.APPLICATION_XML);
postRequest.setEntity(entity);
HttpResponse response=httpClient.execute(postRequest);
System.out.println(IOUtils.toString(response.getEntity().getContent()));
```
* `javascript`
```javascript
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/xml");
var file = "<file contents here>";
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: file,
redirect: 'follow'
};
fetch("http://localhost:8080", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
```

201
server/ui/docs/changelog.md Normal file
View file

@ -0,0 +1,201 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
# 1.5.0
### Fixed
- (CLI) [#93](https://projekte.kosit.org/kosit/validator/-/issues/93) Remove usage information, when validation failed
- (CLI) [#95](https://projekte.kosit.org/kosit/validator/-/issues/95) NPE when using empty repository definition (-r "")
### Added
- (CLI) Support for multiple configurations and multiple repositories. See [cli documentation](https://github.com/itplr-kosit/validator/blob/master/docs/cli.md) for details
- (API) Possibility to use preconfigured Saxon `Processor` instance for validation
### Changed
- (DAEMON) UI rewrite based on [Docusaurs](https://docusaurus.io)
- (
API) [ResolvingConfigurationStrategy.java#getProcessor()](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy)
is removed.
- (CORE) Bump [Saxon HE](https://www.saxonica.com/documentation11/documentation.xml) to 11.4
- (CORE) Bump [jaxb-ri](https://github.com/eclipse-ee4j/jaxb-ri) to 2.3.7
- (CORE) CLI parsing based on pico-cli, commons-cli is removed
## 1.4.2
### Fixed
- (CLI) [#74](https://projekte.kosit.org/kosit/validator/-/issues/74) fix ansi output of the cli version
- [#80](https://github.com/itplr-kosit/validator/issues/80) using classloader to initialize jaxb context (to support
usage in OSGi
environments)
- [#75] (https://github.com/itplr-kosit/validator/issues/75) Improve logging on invalid documents
## 1.4.1
### Fixed
- Allow more than 3 customLevel elements in scenarios (see xrechnung
configuration [ issue 49](https://github.com/itplr-kosit/validator-configuration-xrechnung/issues/49))
- Remove saxon signature from java8 uber-jar (see [67](https://github.com/itplr-kosit/validator/issues/67))
## 1.4.0
### Fixed
- date conversion when
using [ConfigurationBuilder#date(Date)](https://github.com/itplr-kosit/validator/blob/d7beb1040418ae5cbeb9427532fd87482f55756c/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java#L109)
- (CLI) [#51](https://github.com/itplr-kosit/validator/issues/51) Suffix of report xml is missing
- [#53](https://github.com/itplr-kosit/validator/issues/53) Fix copyright and licensing information
- [#56](https://github.com/itplr-kosit/validator/issues/56) `namespace` element content needs trimming
- [DAEMON] [#57](https://github.com/itplr-kosit/validator/issues/57) Reading large inputs correctly
### Added
- read saxon XdmNode with InputFactory
- (CLI) custom output without the various log messages
- (CLI) options to set the log level (`-X` = full debug output, `-l <level>` set a specific level)
- (CLI) return code is not 0 on rejected results
- (CLI) read (single) test target from stdin
- [DAEMON] name inputs via request URI
### Changed
- InputFactory has methods to read any java.xml.transform.Source as Input not only StreamSources
- InputFactory uses a generated UUID as name for SourceInput, if no "real" name can be derived
- saxon dependency update (minor, 9.9.1-7)
- [DAEMON] proper status codes when returning results (see [daemon documentation](https://github.com/itplr-kosit/validator/blob/master/docs/daemon.md#status-codes))
## 1.3.1
### Fixed
- `getFailedAsserts()` and `isSchematronValid()`
in [DefaultResult.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/impl/DefaultResult.java)
do not reflect actual schematron validation result
- processing aborts on schematron execution errors (e.g. errors within schematron logic). The validator now generates a
report in such cases.
- exception while resolving when using XSLT's `unparsed-text()` function within report generation
### Added
- (CLI) summary report
### Changed
- engine info contains version number of the validator (configurations can output this in the report for maintainance
puposes)
- options to customize serialized report file names (cmdline only) via `--report-prefix` and `--report-postfix`
- remove unused dependency Apache Commons HTTP
## 1.3.0
### Added
- Added a builder style configuration API to configure scenarios
- Added an option to configure xml security e.g. to load from http sources or not from a specific repository
(so loading is configurable less restrictive, default strategy is to only load from a local repository)
- Support java.xml.transform.Source as Input
### Changed
- Inputs are NOT read into memory (e.g. Byte-Array) prior processing within the validator. This reduces memory
consumption.
- Overall processing of xml files is based on Saxon s9api. No JAXP or SAX classes are used by
the validator (this further improves performance and memory consumption)
### Deprecations
- CheckConfiguration is deprecated now. Use Configuration.load(...) or Configuration.build(...)
## 1.2.1
### Fixed
- Validator is creating invalid createReportInput xml in case of no scenario match
## 1.2.0
### Added
- Provide access to schematron result
through [Result.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Result.java)
- *Result#getFailedAsserts()* returns a list of failed asserts found by schematron
- *Result#isSchematronValid()* convinience access to evaluate whether schematron was processed without any *
FailedAsserts*
### Changed
- *Result#getAcceptRecommendation()* does not _only_ work when _acceptMatch_ is configured in the scenario
- schema correctness is a precondition, if the checked instance is not valid, this evaluates to _REJECTED_
- if _acceptMatch_ is configured, the result is based on the boolean result of the xpath expression evaluated against
the generated report
- if *no* _acceptMatch_ is configured, the result is based on evaluation of schema and schematron correctness
- _UNDEFINED_ is only returned, when processing is stopped somehow
- *Result#isAcceptable()* can now evaluate to true, when no _acceptMatch_ is configured (see above)
## 1.1.3
### Fixed
- XXE vulnerability when reading xml documents with Saxon [#44](https://github.com/itplr-kosit/validator/issues/44)
- validator unintentionally stopped when schematron processing has errors.
See [#41](https://github.com/itplr-kosit/validator/issues/41).
## 1.1.2
### Fixed
- NPE in Result.getReportDocument for malformed xml input
## 1.1.1
### Added
- Convenience method for accessing information about well-formedness in Result
- Convenience method for accessing information about schema validation result in Result
### Fixed
- NPE when validating non-XML files
## 1.1.0
### Added
- Enhanced API-Usage e.g. return _Result_ object with processing information
- Support loading scenarios and content from a JAR-File
- Simple Daemon-Mode exposing validation functionality via http
- cli option to serialize the 'report input' xml document to _cwd_ (current working directory)
- Documentation in `docs` folder
### Changed
- Use s9api (e.g. XdmNode) internally for loading and holding xml objects (further memory optimization)
- Builds with java 8 and >= 11
- Packages for java8 and java >= 11 (with jaxb included)
- Translated README.md
## 1.0.2
### Fixed
- Memory issues when validating multiple targets
## 1.0.1
### Changed
- Removed XRechnung configuration from release artifacts and source (moved
to [own repository](https://github.com/itplr-kosit/validator-configuration-xrechnung) )
## 1.0.0
- Initial Release

View file

@ -0,0 +1,20 @@
---
sidebar_position: 1
---
# Configurations
The validator needs a scenario configuration for working properly.
Currently, there are two public third party validation configurations available.
* Validation Configuration for [XRechnung](http://www.xoev.de/de/xrechnung):
* Source code is available on [GitHub](https://github.com/itplr-kosit/validator-configuration-xrechnung)
* [Releases](https://github.com/itplr-kosit/validator-configuration-xrechnung/releases) can also be downloaded
* Validation Configuration for [XGewerbeanzeige](https://xgewerbeanzeige.de/)
* Source code is available on [GitHub](https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige)
* [Releases](https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige/releases) can also be downloaded
For creating custom configurations
see [configuration documentation](https://github.com/itplr-kosit/validator/blob/master/docs/configurations.md)
for details

View file

@ -0,0 +1,184 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/*
* Copyright 2017-2022 Koordinierungsstelle für IT-Standards (KoSIT)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const lightCodeTheme = require("prism-react-renderer/themes/github");
const darkCodeTheme = require("prism-react-renderer/themes/dracula");
const pkg = require("./package.json");
/** @type {import('@docusaurus/types').Config} */
const config = {
title: "KoSIT Validator Daemon",
tagline: "Validating any XML",
url: "https://your-docusaurus-test-site.com",
baseUrl: "/",
onBrokenLinks: "throw",
onBrokenMarkdownLinks: "warn",
favicon: "img/favicon.svg",
customFields: {
// We fake a base endpoint here, so that our proxy works in development mode.
// It does not seem to work when trying to proxy requests to the root, so we
// need to create a base path to proxy against
apiBase: process.env.NODE_ENV === "development" ? "/api" : "/",
},
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
organizationName: "KoSIT", // Usually your GitHub org/user name.
projectName: "Validator", // Usually your repo name.
// Even if you don't use internalization, you can use this field to set useful
// metadata like html lang. For example, if your site is Chinese, you may want
// to replace "en" with "zh-Hans".
i18n: {
defaultLocale: "en",
locales: ["en"],
},
presets: [
[
"classic",
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
sidebarPath: require.resolve("./sidebars.js"),
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl: "https://github.com/itplr-kosit/validator/server/ui",
},
theme: {
customCss: require.resolve("./src/css/custom.css"),
},
}),
],
],
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
navbar: {
style: "primary",
title: "Validator Daemon",
logo: {
alt: "KoSIT Validator Daemon",
src: "img/logo.svg",
},
items: [
{
type: "doc",
docId: "api",
position: "left",
label: "Documentation",
},
{
to: "config",
position: "left",
label: "Validator configuration",
},
{
to: "health",
position: "left",
label: "Health information",
},
],
},
footer: {
style: "dark",
links: [
{
title: "Docs",
items: [
{
label: "Configuration",
to: "/docs/configurations",
},
{
label: "API",
to: "/docs/api",
},
],
},
{
title: "Community",
items: [
{
label: "Github",
href: "https://github.com/itplr-kosit/validator",
},
{
label: "Issues",
href: "https://github.com/itplr-kosit/validator/issues",
},
],
},
{
title: "More",
items: [
{
label: "KoSIT",
href: "https://www.xoev.de",
},
{
label: "XRechnung",
href: "https://www.xoev.de/xrechnung-16828",
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} Koordinierungstelle für IT-Standards (KoSIT)`,
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
},
}),
plugins: [
/** @type {import('@docusaurus/types').PluginModule} */
(
// For the development environment to work, we need to proxy all requests
// that are not meant to fetch static content (js, css, html files), to
// the backend server. The dev server makes tht a little hard for us, as
// it does not allow us to just proxy all requests that don't match any
// static file. That's why we prefix every request with `/api`, and remove
// it again when forwarding the request. In ptoduction mode, the endpoint
// will be just `/` (see `config.customFields.apiBase`)
function proxyPlugin() {
return {
name: "custom-docusaurus-plugin",
configureWebpack() {
return {
devServer: {
proxy: {
"/api": {
target: pkg.apiProxy,
pathRewrite: { "^/api": "" },
},
},
},
};
},
};
}
),
],
};
module.exports = config;

25115
server/ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

68
server/ui/package.json Normal file
View file

@ -0,0 +1,68 @@
{
"name": "validator-frontend",
"version": "0.0.0",
"private": true,
"apiProxy": "http://localhost:8080",
"scripts": {
"docusaurus": "docusaurus",
"start": "cross-env NODE_ENV=development docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc",
"checkFormatting": "prettier . --check",
"format": "prettier . --write",
"lint": "eslint . && npm run checkFormatting",
"lint:fix": "npm run format && eslint . --fix"
},
"dependencies": {
"@docusaurus/core": "2.2.0",
"@docusaurus/preset-classic": "2.2.0",
"@mdx-js/react": "^1.6.22",
"@mui/icons-material": "^5.10.14",
"clsx": "^1.2.1",
"js-file-download": "^0.4.12",
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-dropzone": "^14.2.3"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.2.0",
"@tsconfig/docusaurus": "^1.0.5",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"cross-env": "^7.0.3",
"eslint": "^8.27.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.10",
"eslint-plugin-react-hooks": "^4.6.0",
"prettier": "^2.7.1",
"typescript": "^4.7.4"
},
"browserslist": {
"production": [
"> 0.5%",
"last 2 versions",
"Firefox ESR",
"not dead"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"engines": {
"node": ">=16.14"
}
}

33
server/ui/sidebars.js Normal file
View file

@ -0,0 +1,33 @@
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
// @ts-check
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [{ type: "autogenerated", dirName: "." }],
// But you can create a sidebar manually
/*
tutorialSidebar: [
'intro',
'hello',
{
type: 'category',
label: 'Tutorial',
items: ['tutorial-basics/create-a-document'],
},
],
*/
};
module.exports = sidebars;

View file

@ -0,0 +1,105 @@
:where(.button) {
--button-shadow: var(--ifm-global-shadow-lw);
--button-accent-shadow: var(--ifm-global-shadow-md);
--button-text-color: var(--text-accent-bg-0);
--button-background-color: var(--surface-accent-3);
--button-background-color-hover: var(--surface-accent-4);
--button-background-color-disabled: var(--surface-4);
--button-accent-shadow-opacity: 0;
}
:where([data-theme="dark"] .button) {
--button-shadow: none;
--button-accent-shadow: none;
--button-text-color: var(--text-accent-bg-0);
--button-background-color: var(--surface-accent-4);
--button-background-color-hover: var(--surface-accent-3);
--button-background-color-disabled: var(--surface-5);
--button-accent-shadow-opacity: 0;
}
.button {
position: relative;
background: var(--button-background-color);
font-family: inherit;
font-size: 1rem;
font-size: 0.875rem;
text-transform: uppercase;
color: var(--button-text-color);
font-weight: var(--ifm-font-weight-semibold);
border: none;
padding: 0 1.25em;
height: 2.25em;
line-height: 1;
border-radius: var(--border-radius-small);
box-shadow: var(--button-shadow);
cursor: pointer;
transition: color 150ms ease, background-color 150ms ease;
}
.button::before {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
box-shadow: var(--button-accent-shadow);
opacity: var(--button-accent-shadow-opacity);
transition: opacity 200ms ease;
}
.button:where(:hover, :focus) {
--button-background-color: var(--button-background-color-hover);
}
.button:not([disabled]):where(:hover, :focus) {
--button-accent-shadow-opacity: 1;
}
.button:not([disabled]):where(:active) {
--button-accent-shadow-opacity: 0.5;
}
.button[disabled] {
--button-background-color: var(--button-background-color-disabled);
cursor: auto;
}
.spinnerWrapper {
opacity: 0;
pointer-events: none;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: grid;
place-content: center;
background: #ffffff55;
backdrop-filter: blur(1px);
transition: opacity 150ms ease;
}
.loading .spinnerWrapper {
opacity: 1;
}
.spinner {
--_size: 1.75rem;
--_thickness: 3px;
width: var(--_size);
height: var(--_size);
border: var(--_thickness) solid var(--button-text-color);
border-bottom-color: transparent;
border-radius: 50%;
animation: spin 1100ms infinite cubic-bezier(0.5, 0.1, 0.5, 0.9);
}
@keyframes spin {
0% {
rotate: 0deg;
}
100% {
rotate: 360deg;
}
}

View file

@ -0,0 +1,44 @@
import clsx from "clsx";
import type { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode } from "react";
import React from "react";
import type { ExtendProps } from "../util/types";
import styles from "./Button.module.css";
type HTMLButtonProps = DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>;
type ButtonProps = ExtendProps<
HTMLButtonProps,
{
children: ReactNode;
type?: "button" | "submit" | "reset";
className?: string;
loading?: boolean;
}
>;
function Button({
children,
type = "button",
className,
loading = false,
...props
}: ButtonProps): JSX.Element {
return (
<button
{...props}
className={clsx(styles.button, loading && styles.loading, className)}
// eslint-disable-next-line react/button-has-type
type={type}
aria-busy={loading}
>
<div className={styles.spinnerWrapper} aria-hidden>
<div className={styles.spinner} />
</div>
{children}
</button>
);
}
export default Button;

View file

@ -0,0 +1,3 @@
import Button from "./Button";
export default Button;

View file

@ -0,0 +1,89 @@
.codeblock {
box-shadow: inset var(--ifm-global-shadow-lw);
margin: 0;
}
.wrapper {
position: relative;
}
:where(.buttonWrapper) {
--codeblock-button-text-color: var(--text-main);
--codeblock-button-background-color: var(--surface-2);
--codeblock-button-background-color-hover: var(--surface-accent-1);
--codeblock-button-separator-color: var(--surface-4);
--codeblock-button-border-color: var(--codeblock-button-separator-color);
--codeblock-button-icon-size: 1.5rem;
--codeblock-button-size: 2rem;
--codeblock-button-shadow: var(--ifm-global-shadow-tl);
}
:where([data-theme="dark"] .buttonWrapper) {
--codeblock-button-text-color: var(--text-0);
--codeblock-button-background-color: var(--surface-6);
--codeblock-button-background-color-hover: var(--surface-5);
--codeblock-button-separator-color: var(--codeblock-button-text-color);
--codeblock-button-shadow: var(--ifm-global-shadow-tl);
--codeblock-button-shadow: none;
}
.button {
position: relative;
width: var(--codeblock-button-size);
height: var(--codeblock-button-size);
background: var(--codeblock-button-background-color);
font-family: inherit;
font-size: 1rem;
font-size: 0.875rem;
text-transform: uppercase;
color: var(--codeblock-button-text-color);
font-weight: var(--ifm-font-weight-semibold);
border: none;
padding: 0;
height: 2.25em;
line-height: 1;
border-radius: var(--border-radius-small);
border-radius: 0;
cursor: pointer;
transition: color 200ms ease, background-color 200ms ease;
}
.button:not(:first-child) {
border-left: 1px solid var(--codeblock-button-separator-color);
}
.button:first-child {
border-top-left-radius: var(--border-radius-small);
border-bottom-left-radius: var(--border-radius-small);
}
.button:last-child {
border-top-right-radius: var(--border-radius-small);
border-bottom-right-radius: var(--border-radius-small);
}
.button:hover,
.button:focus {
background: var(--codeblock-button-background-color-hover);
}
.button svg {
width: var(--codeblock-button-icon-size);
height: var(--codeblock-button-icon-size);
}
.buttonWrapper {
position: absolute;
display: flex;
top: 1rem;
right: 1rem;
z-index: 1;
box-shadow: var(--codeblock-button-shadow);
border: 1px solid var(--codeblock-button-border-color);
border-radius: var(--border-radius-small);
opacity: 0.75;
transition: opacity 300ms ease;
}
.buttonWrapper:hover,
.buttonWrapper:focus-within {
opacity: 1;
}

View file

@ -0,0 +1,119 @@
import React, { useEffect, useState } from "react";
import type { PrismTheme, Language } from "prism-react-renderer";
import Highlight, { defaultProps } from "prism-react-renderer";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import clsx from "clsx";
import downloadFile from "js-file-download";
import styles from "./Codeblock.module.css";
type ThemeValue = "light" | "dark";
const getTheme = () =>
(document.documentElement.dataset.theme || "light") as ThemeValue;
function useGlobalTheme() {
const [theme, setTheme] = useState<ThemeValue>(getTheme);
useEffect(() => {
const mo = new MutationObserver(() => {
setTheme(getTheme());
});
mo.observe(document.documentElement, {
subtree: false,
attributeFilter: ["data-theme"],
});
return () => mo.disconnect();
});
return theme;
}
function Codeblock({
children,
language = "markup",
enableCopy = false,
download,
}: {
children: string;
language?: Language;
enableCopy?: boolean;
download?: { fileName: string; mime: string };
}): JSX.Element {
const { siteConfig } = useDocusaurusContext();
const theme = useGlobalTheme();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const codeThemeLight = (siteConfig.themeConfig.prism as any)
.theme as PrismTheme;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const codeThemeDark = (siteConfig.themeConfig.prism as any)
.darkTheme as PrismTheme;
const codeTheme = theme === "light" ? codeThemeLight : codeThemeDark;
const handleCopy = async () => {
try {
navigator.clipboard.writeText(children);
} catch {
// Copying did unfortunately not work, but we'll not crash the app
// beacause of that...
}
};
const handleDownload = () => {
if (!download || !download.fileName || !download.mime) return;
downloadFile(children, download.fileName, download.mime);
};
return (
<div className={styles.wrapper}>
<Highlight
{...defaultProps}
code={children}
language={language}
theme={codeTheme}
>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={clsx(className, styles.codeblock)} style={style}>
{tokens.map((line, i) => (
// eslint-disable-next-line react/jsx-key
<div {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
// eslint-disable-next-line react/jsx-key
<span {...getTokenProps({ token, key })} />
))}
</div>
))}
</pre>
)}
</Highlight>
{(enableCopy || download) && (
<div className={styles.buttonWrapper}>
{enableCopy && (
<button
className={styles.button}
type="button"
aria-label="Copy content"
title="Copy content"
onClick={handleCopy}
>
<svg aria-hidden="true" viewBox="0 0 24 24" fill="currentColor">
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" />
</svg>
</button>
)}
{download && (
<button
className={styles.button}
type="button"
aria-label="Download content as file"
title="Download content as file"
onClick={handleDownload}
>
<svg aria-hidden="true" viewBox="0 0 24 24" fill="currentColor">
<path d="M5 20h14v-2H5v2zM19 9h-4V3H9v6H5l7 7 7-7z" />
</svg>
</button>
)}
</div>
)}
</div>
);
}
export default Codeblock;

View file

@ -0,0 +1,3 @@
import Codeblock from "./Codeblock";
export default Codeblock;

View file

@ -0,0 +1,94 @@
:where(.dropzone) {
--dropzone-color-text: var(--text-faded);
--dropzone-icon-active: var(--text-accent-2);
--dropzone-color-background: var(--surface-2);
--dropzone-color-background-active: var(--surface-accent-0);
--dropzone-color-border: var(--color-border);
--dropzone-color-border-active: var(--color-border-accent);
--dropzone-opacity-hover-preview: 0;
--dropzone-shadow-opacity: 0;
--dropzone-border-size: 0.2rem;
}
:where([data-theme="dark"] .dropzone) {
--dropzone-color-text: var(--text-0);
--dropzone-icon-active: var(--text-accent-0);
--dropzone-color-background: transparent;
--dropzone-color-background-active: var(--surface-accent-5);
}
.dropzone {
position: relative;
cursor: pointer;
width: 100%;
height: 15em;
color: var(--dropzone-color-text);
background: var(--dropzone-color-background);
border: var(--dropzone-border-size) dashed var(--dropzone-color-border);
border-radius: var(--border-radius-medium);
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--ifm-global-shadow-lw);
}
.dropzone::before {
content: "";
position: absolute;
pointer-events: none;
top: 0;
right: 0;
bottom: 0;
left: 0;
box-shadow: var(--ifm-global-shadow-md);
z-index: -1;
opacity: var(--dropzone-shadow-opacity);
transition: opacity 200ms ease;
}
.active {
--dropzone-opacity-hover-preview: 0.8;
--dropzone-shadow-opacity: 1;
}
.hasFiles {
--dropzone-color-background: var(--dropzone-color-background-active);
--dropzone-color-border: var(--dropzone-color-border-active);
--dropzone-shadow-opacity: 0.25;
}
.fileHoverPreview {
--dropzone-color-background: var(--dropzone-color-background-active);
--dropzone-color-border: var(--dropzone-color-border-active);
position: absolute;
top: calc(-1 * var(--dropzone-border-size));
right: calc(-1 * var(--dropzone-border-size));
bottom: calc(-1 * var(--dropzone-border-size));
left: calc(-1 * var(--dropzone-border-size));
display: flex;
justify-content: center;
align-items: center;
color: var(--dropzone-color-text);
background: var(--dropzone-color-background);
border: var(--dropzone-border-size) dashed var(--dropzone-color-border);
border-radius: var(--border-radius-medium);
opacity: var(--dropzone-opacity-hover-preview);
transition: opacity 150ms ease-in-out;
}
.icon {
font-size: 3rem;
width: 1em;
height: 1em;
}
.fileHoverIcon {
font-size: 5rem;
color: var(--dropzone-icon-active);
}
.uploadIcon {
color: var(--dropzone-color-text);
margin-right: 0.5rem;
}

View file

@ -0,0 +1,80 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from "react";
import clsx from "clsx";
import type { DropEvent } from "react-dropzone";
import { useDropzone } from "react-dropzone";
import type { DropzoneProps, RejectionType } from "./types";
import styles from "./Dropzone.module.css";
const Dropzone = ({
accept,
children,
className,
activeClassName,
multiple = false,
name,
onDrop,
hasSelectedFiles,
...props
}: DropzoneProps): JSX.Element => {
const handleDrop = (
accepted: File[],
fileRejections: RejectionType[],
event: DropEvent,
) => {
const rejected = fileRejections.map((rejection) => rejection.file);
onDrop(accepted, rejected, event);
};
const {
getRootProps,
getInputProps,
isDragActive,
isDragAccept,
isDragReject,
} = useDropzone({ accept, multiple, onDrop: handleDrop, ...props });
return (
<div
{...getRootProps()}
className={clsx(
styles.dropzone,
isDragActive && styles.active,
hasSelectedFiles && styles.hasFiles,
className,
isDragActive && activeClassName,
)}
data-testid="dropzone"
data-is-drag-active={isDragActive}
data-is-drag-accepted={isDragAccept}
data-is-drag-rejected={isDragReject}
data-has-files={hasSelectedFiles}
>
<div
className={clsx(
styles.fileHoverPreview,
isDragActive && styles.previewActive,
)}
>
<svg
className={clsx(styles.icon, styles.fileHoverIcon)}
fill="currentColor"
aria-hidden="true"
viewBox="0 0 24 24"
>
<path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z" />
</svg>
</div>
<svg
className={clsx(styles.icon, styles.uploadIcon)}
fill="currentColor"
aria-hidden="true"
viewBox="0 0 24 24"
>
<path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z" />
</svg>
{children}
<input name={name} {...getInputProps()} data-testid="dropzone-input" />
</div>
);
};
export default Dropzone;

View file

@ -0,0 +1,5 @@
import Dropzone from "./Dropzone";
export { default as useDropzone } from "./useDropzone";
export default Dropzone;

View file

@ -0,0 +1,24 @@
import type { ReactNode, RefAttributes } from "react";
import type {
DropEvent,
DropzoneProps as ReactDropzoneProps,
DropzoneRef,
} from "react-dropzone";
import type { ExtendProps } from "../util/types";
export interface RejectionType {
file: File;
}
export type DropzoneProps = ExtendProps<
ReactDropzoneProps & RefAttributes<DropzoneRef>,
{
children?: ReactNode;
className?: string;
activeClassName?: string;
multiple?: boolean;
hasSelectedFiles?: boolean;
name?: string;
onDrop: (accepted: File[], rejections: File[], event: DropEvent) => void;
}
>;

View file

@ -0,0 +1,53 @@
import { useCallback, useMemo, useState } from "react";
interface DropzoneHelpers {
selectedFiles: File[];
rejectedFiles: File[];
hasSelectedFiles: boolean;
getProps: () => {
onDrop: (accepted: File[], rejected: File[]) => void;
multiple: boolean;
accept: string | string[];
hasSelectedFiles: boolean;
};
reset: () => void;
}
function useDropzone({
multiple = false,
accept,
}: {
multiple?: boolean;
accept: string | string[];
}): DropzoneHelpers {
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<File[]>([]);
const hasSelectedFiles = selectedFiles.length > 0;
const getProps = useMemo(() => {
const handleDrop = (accepted: File[], rejected: File[]) => {
setSelectedFiles(accepted);
if (rejected.length === 0) {
setRejectedFiles([]);
} else {
setRejectedFiles(rejected);
}
};
return () => ({
onDrop: handleDrop,
multiple,
accept,
hasSelectedFiles,
});
}, [accept, hasSelectedFiles, multiple]);
const reset = useCallback(() => {
setSelectedFiles([]);
setRejectedFiles([]);
}, []);
return { selectedFiles, rejectedFiles, hasSelectedFiles, getProps, reset };
}
export default useDropzone;

View file

@ -0,0 +1,16 @@
.errorDisplay {
background-color: var(--red-3);
color: var(--text-accent-bg-0);
padding: 0.75em 1.25em;
border-radius: var(--ifm-global-radius);
box-shadow: var(--ifm-global-shadow-lw);
margin: 1em 0;
display: flex;
flex-direction: column;
gap: 0.75em;
}
.title {
display: block;
line-height: 1;
}

View file

@ -0,0 +1,20 @@
import type { ReactNode } from "react";
import React from "react";
import styles from "./ErrorDisplay.module.css";
function ErrorDisplay({
title,
children,
}: {
title: string;
children?: ReactNode;
}): JSX.Element {
return (
<div role="alert" className={styles.errorDisplay}>
<strong className={styles.title}>{title}</strong>
{children}
</div>
);
}
export default ErrorDisplay;

View file

@ -0,0 +1,3 @@
import ErrorDisplay from "./ErrorDisplay";
export default ErrorDisplay;

View file

@ -0,0 +1,3 @@
.headline {
font-size: 3rem;
}

View file

@ -0,0 +1,30 @@
import type { ReactNode } from "react";
import React from "react";
import Layout from "@theme/Layout";
import styles from "./PageLayout.module.css";
function PageLayout({
children,
layoutDescription,
description,
title,
headline,
}: {
children: ReactNode;
layoutDescription: string;
description: string;
headline: string;
title?: string;
}): JSX.Element {
return (
<Layout description={layoutDescription} title={title}>
<main className="container padding-top--md padding-bottom--lg">
<h1 className={styles.headline}>{headline}</h1>
<p>{description}</p>
{children}
</main>
</Layout>
);
}
export default PageLayout;

View file

@ -0,0 +1,3 @@
import PageLayout from "./PageLayout";
export default PageLayout;

View file

@ -0,0 +1,14 @@
.buttonGroup {
display: flex;
justify-content: flex-end;
margin: 1rem 0;
}
.resultDisplay {
margin: 1em 0;
}
.withError {
border: 0.2rem solid var(--text-error);
border-radius: var(--border-radius-small);
}

View file

@ -0,0 +1,115 @@
import type { FormEventHandler } from "react";
import React, { useCallback, useState } from "react";
import clsx from "clsx";
import Dropzone from "../Dropzone";
import Codeblock from "../Codeblock";
import ErrorDisplay from "../ErrorDisplay";
import useRequest, { RequestStatus } from "../util/useRequest";
import Button from "../Button";
import styles from "./Upload.module.css";
const ENDPOINT = "/";
const ACCEPT = {
"text/xml": [".xml", ".XML"],
"application/xml": [".xml", ".XML"],
};
function createFileName(selectedFileName: string | undefined) {
return selectedFileName
? `${selectedFileName.replace(/\.xml$/i, "")}-report.xml`
: "report.xml";
}
function Upload(): JSX.Element {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [rejected, setRejected] = useState<File[]>([]);
const { data, error, request, status } = useRequest();
const handleDrop = useCallback(
(acceptedFiles: File[], rejectedFiles: File[]) => {
if (acceptedFiles.length) {
setSelectedFile(acceptedFiles[0]);
setRejected([]);
} else {
setRejected(rejectedFiles);
}
},
[],
);
const handleSubmit: FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
if (!selectedFile) return;
request(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/xml" },
body: selectedFile,
redirect: "follow",
});
};
const meaningfulErrorResponse = !!error && [406, 422].includes(error.code);
return (
<>
<form onSubmit={handleSubmit}>
{status === RequestStatus.Failure && error && !meaningfulErrorResponse && (
<ErrorDisplay title="An error occurred while validating the file">
<Codeblock enableCopy>{error.message}</Codeblock>
</ErrorDisplay>
)}
{rejected.length > 1 && (
<ErrorDisplay title="Please select a single file only" />
)}
{rejected.length === 1 && (
<ErrorDisplay title="Only XML files are supported">
<Codeblock>{`Invalid file found: ${rejected[0].name}`}</Codeblock>
</ErrorDisplay>
)}
<Dropzone
onDrop={handleDrop}
accept={ACCEPT}
multiple={false}
hasSelectedFiles={!!selectedFile}
>
{selectedFile ? (
selectedFile.name
) : (
<>Drag &amp; drop files here or click to select a file</>
)}
</Dropzone>
<div className={styles.buttonGroup}>
<Button
type="submit"
disabled={!selectedFile}
loading={status === RequestStatus.Loading}
>
Validate
</Button>
</div>
</form>
{((data && status === RequestStatus.Success) ||
meaningfulErrorResponse) && (
<div
className={clsx(
styles.resultDisplay,
meaningfulErrorResponse && styles.withError,
)}
>
<Codeblock
download={{
fileName: createFileName(selectedFile?.name),
mime: "application/xml",
}}
enableCopy
>
{data || error?.message || ""}
</Codeblock>
</div>
)}
</>
);
}
export default Upload;

View file

@ -0,0 +1,3 @@
import Upload from "./Upload";
export default Upload;

View file

@ -0,0 +1,35 @@
import React, { useEffect } from "react";
import Codeblock from "../Codeblock";
import ErrorDisplay from "../ErrorDisplay";
import useRequest, { RequestStatus } from "../util/useRequest";
function XmlView({
endpoint,
fileName,
}: {
endpoint: string;
fileName: string;
}): JSX.Element {
const { request, data, error, status } = useRequest();
useEffect(() => {
request(endpoint, { headers: { "Content-Type": "application/xml" } });
}, [endpoint, request]);
return (
<>
{status === RequestStatus.Failure && error && (
<ErrorDisplay title="An error occurred while fetching">
<Codeblock>{error.message}</Codeblock>
</ErrorDisplay>
)}
{status === RequestStatus.Success && data && (
<Codeblock download={{ fileName, mime: "application/xml" }} enableCopy>
{data}
</Codeblock>
)}
</>
);
}
export default XmlView;

View file

@ -0,0 +1,3 @@
import XmlView from "./XmlView";
export default XmlView;

View file

@ -0,0 +1,6 @@
export type SharedKeys<A, B> = Extract<keyof A, keyof B>;
export type ExtendProps<Base, Extension> = Omit<
Base,
SharedKeys<Base, Extension>
> &
Extension;

View file

@ -0,0 +1,93 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
export enum RequestStatus {
Idle = "idle",
Loading = "loading",
Success = "success",
Failure = "failure",
}
export interface RequestState {
status: RequestStatus;
data: null | string;
error: null | { code: number; message: string };
}
export interface UseRequest extends RequestState {
request: (endpoint: string, init?: RequestInit) => void;
}
const EMPTY_REQUEST: RequestState = {
status: RequestStatus.Idle,
data: null,
error: null,
};
function createEndpoint(endpoint: string, apiBase: string): string {
const segments = apiBase
.split("/")
.concat(endpoint.split("/"))
.filter(Boolean);
return `/${segments.join("/")}`;
}
function useRequest(): UseRequest {
const [requestState, setRequest] = useState(EMPTY_REQUEST);
const { siteConfig } = useDocusaurusContext();
const apiBase = siteConfig.customFields?.apiBase as string;
const isMountedRef = useRef(true);
useEffect(() => {
return () => {
isMountedRef.current = false;
};
}, []);
const request = useCallback(
(endpoint: string, init?: RequestInit) => {
setRequest((prev) => ({ ...prev, status: RequestStatus.Loading }));
fetch(createEndpoint(endpoint, apiBase), init)
.then((response) => {
return response.text().then((text) => ({
data: text,
ok: response.ok,
code: response.status,
}));
})
.then(({ data, ok, code }) => {
if (!isMountedRef.current) return;
if (ok) {
setRequest({
status: RequestStatus.Success,
data,
error: null,
});
} else {
setRequest((prev) => ({
...prev,
status: RequestStatus.Failure,
error: { code, message: data },
}));
}
})
.catch((error) => {
if (!isMountedRef.current) return;
setRequest((prev) => ({
...prev,
status: RequestStatus.Failure,
error: {
code: 0,
message: error?.toString?.() || "An unknown error occurred",
},
}));
});
},
[apiBase],
);
return useMemo(() => ({ ...requestState, request }), [request, requestState]);
}
export default useRequest;

View file

@ -0,0 +1,172 @@
/**
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: var(--blue-3);
--ifm-color-primary-dark: var(--blue-4);
--ifm-color-primary-darker: var(--blue-5);
--ifm-color-primary-darkest: var(--blue-6);
--ifm-color-primary-light: var(--blue-2);
--ifm-color-primary-lighter: var(--blue-1);
--ifm-color-primary-lightest: var(--blue-0);
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
--ifm-global-radius: var(--border-radius-small);
--ifm-font-family-base: var(--font-sans);
--ifm-font-family-monospace: var(--font-mono);
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme="dark"]:root {
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
--ifm-background-color: var(--surface-6);
}
[data-theme] .footer {
--ifm-footer-background-color: var(--surface-accent-4);
--ifm-footer-color: var(--text-accent-bg-main);
--ifm-footer-link-color: var(--text-accent-bg-main);
--ifm-footer-link-hover-color: var(--text-accent-bg-main);
--ifm-footer-title-color: var(--text-accent-bg-main);
}
[data-theme="dark"] .footer {
--ifm-footer-background-color: var(--surface-6);
border-top: 1px solid var(--ifm-table-border-color);
}
[data-theme] .navbar {
--ifm-navbar-background-color: var(--blue-4);
}
/* XÖV Theme */
:where(html) {
--blue-0: hsl(206 100% 95%);
--blue-1: hsl(206 100% 80%);
--blue-2: hsl(206 100% 65%);
--blue-3: hsl(206 100% 47%);
--blue-4: hsl(220 41% 30%);
--blue-5: hsl(220 41% 24%);
--blue-6: hsl(220 41% 18%);
--gray-0: hsl(216 33% 100%);
--gray-1: hsl(216 33% 97%);
--gray-2: hsl(220 21% 95%);
--gray-3: hsl(220 21% 92%);
--gray-4: hsl(212 10% 73%);
--gray-5: hsl(212 10% 45%);
--gray-6: hsl(212 15% 13%);
--red-0: hsl(357 80% 96%);
--red-1: hsl(357 80% 89%);
--red-2: hsl(357 80% 75%);
--red-3: hsl(357 80% 60%);
--red-4: hsl(357 80% 40%);
--red-5: hsl(357 60% 22%);
--orange-0: hsl(46 80% 90%);
--orange-1: hsl(46 80% 80%);
--orange-2: hsl(46 80% 68%);
--orange-3: hsl(46 80% 40%);
--orange-4: hsl(46 80% 25%);
--orange-5: hsl(46 80% 10%);
--green-0: hsl(162 100% 93%);
--green-1: hsl(162 100% 74%);
--green-2: hsl(162 100% 45%);
--green-3: hsl(162 100% 30%);
--green-4: hsl(162 100% 20%);
--green-5: hsl(162 100% 10%);
/* Surface colors */
--surface-0: var(--gray-0);
--surface-1: var(--gray-1);
--surface-2: var(--gray-2);
--surface-3: var(--gray-3);
--surface-4: var(--gray-4);
--surface-5: var(--gray-5);
--surface-6: var(--gray-6);
--surface-accent-0: var(--blue-0);
--surface-accent-1: var(--blue-1);
--surface-accent-2: var(--blue-2);
--surface-accent-3: var(--blue-3);
--surface-accent-4: var(--blue-4);
--surface-accent-5: var(--blue-5);
/* Text colors */
--text-0: var(--gray-4);
--text-1: var(--gray-5);
--text-2: var(--gray-6);
--text-main: var(--text-2);
--text-faded: var(--text-1);
--text-accent-0: var(--blue-3);
--text-accent-1: var(--blue-4);
--text-accent-2: var(--blue-5);
--text-accent: var(--text-accent-2);
--text-accent-bg-0: var(--gray-0);
--text-accent-bg-1: var(--gray-1);
--text-accent-bg-2: var(--gray-4);
--text-accent-bg-3: var(--blue-4);
--text-accent-bg-main: var(--text-accent-bg-0);
--text-negative: var(--red-3);
--text-error: var(--red-3);
--text-warning: var(--orange-3);
--text-info: var(--blue-3);
--text-success: var(--green-3);
/* Misc elements */
--divider: var(--gray-4);
--scrollthumb: var(--gray-4);
--input-background: var(--surface-0);
--input-background-disabled: var(--surface-2);
/* Border Radius */
--border-radius-small: 2px;
--border-radius-medium: 0.2rem;
--border-radius-large: 1rem;
--color-border: var(--gray-4);
--color-border-hover: var(--gray-4);
--color-border-accent: var(--blue-2);
--color-border-accent-hover: var(--blue-2);
/* Fonts */
--font-sans: "Open Sans", system-ui, -apple-system, "Segoe UI", "Roboto",
"Ubuntu", "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol";
--font-serif: ui-serif, serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
--font-mono: "Cascadia Code", "Dank Mono", "Operator Mono", "Inconsolata",
"Fira Mono", ui-monospace, "SF Mono", "Monaco", "Droid Sans Mono",
"Source Code Pro", monospace, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
}
/* Scrollbars: */
/* Firefox */
* {
scrollbar-width: thin;
scrollbar-color: var(--scrollthumb) transparent;
}
/* Chrome, Edge, and Safari */
*::-webkit-scrollbar {
width: min(8px, 0.5rem);
height: min(8px, 0.5rem);
}
*::-webkit-scrollbar-track {
background: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: var(--scrollthumb);
border-radius: 999rem;
}

View file

@ -0,0 +1,18 @@
import React from "react";
import PageLayout from "@site/src/components/PageLayout";
import XmlView from "@site/src/components/XmlView";
function ConfigPage(): JSX.Element {
return (
<PageLayout
title="Validator configuration"
layoutDescription="The currently loaded validator configuration"
headline="Validator configuration"
description="View the currently loaded validator configuration."
>
<XmlView endpoint="/server/config" fileName="config.xml" />
</PageLayout>
);
}
export default ConfigPage;

View file

@ -0,0 +1,3 @@
import ConfigPage from "./ConfigPage";
export default ConfigPage;

View file

@ -0,0 +1,18 @@
import React from "react";
import PageLayout from "@site/src/components/PageLayout";
import XmlView from "@site/src/components/XmlView";
function HealthPage(): JSX.Element {
return (
<PageLayout
title="Health information"
layoutDescription="Health and status information about the system"
headline="Server health information"
description="Information about health and status of the running system."
>
<XmlView endpoint="/server/health" fileName="health.xml" />
</PageLayout>
);
}
export default HealthPage;

View file

@ -0,0 +1,3 @@
import HealthPage from "./HealthPage";
export default HealthPage;

View file

@ -0,0 +1,15 @@
import React from "react";
import Upload from "../components/Upload";
import PageLayout from "../components/PageLayout";
export default function Home(): JSX.Element {
return (
<PageLayout
layoutDescription="KoSIT Validator Daemon"
headline="Try the validator!"
description="Upload an XML file here to validate its contents. Note: this is just a demo implementation, not meant for production usage. If you need a production ready implementation you are welcome to contribute to the open source project."
>
<Upload />
</PageLayout>
);
}

View file

@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

View file

Binary file not shown.

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24">
<path fill="hsl(220, 41%, 30%)" d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm-3.06 16L7.4 14.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L10.94 18zM13 9V3.5L18.5 9H13z"></path>
</svg>

After

Width:  |  Height:  |  Size: 306 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24">
<path fill="#fff" d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm-3.06 16L7.4 14.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L10.94 18zM13 9V3.5L18.5 9H13z"></path>
</svg>

After

Width:  |  Height:  |  Size: 292 B

9
server/ui/tsconfig.json Normal file
View file

@ -0,0 +1,9 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@tsconfig/docusaurus/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"resolveJsonModule": true,
"strict": true
}
}

View file

@ -84,7 +84,7 @@ public class CommandLineApplication {
resultStatus = ReturnValue.HELP_REQUEST;
} else {
resultStatus = ObjectUtils.defaultIfNull(commandLine.getExecutionResult(), ReturnValue.PARSING_ERROR);
if (resultStatus.getCode() < 0) {
if (resultStatus.isError()) {
commandLine.usage(System.out);
}
}

View file

@ -37,7 +37,7 @@ public class ReturnValue {
public static final ReturnValue DAEMON_MODE = new ReturnValue(-100);
public static final ReturnValue PARSING_ERROR = new ReturnValue(-1);;
public static final ReturnValue PARSING_ERROR = new ReturnValue(-1);
private final int code;
@ -45,4 +45,7 @@ public class ReturnValue {
return new ReturnValue(count);
}
public boolean isError() {
return this.code < 0 && this.code != DAEMON_MODE.code;
}
}

View file

@ -30,7 +30,7 @@ import lombok.RequiredArgsConstructor;
public class GuiHandler extends BaseHandler {
private static final URL INDEX_HTML = GuiHandler.class.getClassLoader().getResource("gui/index.html");
private static final URL INDEX_HTML = GuiHandler.class.getClassLoader().getResource("ui/index.html");
public GuiHandler() {
if (INDEX_HTML == null) {
@ -45,7 +45,7 @@ public class GuiHandler extends BaseHandler {
if (path.equals("/")) {
write(exchange, IOUtils.toString(INDEX_HTML, Charset.defaultCharset()).getBytes(), "text/html");
} else {
final URL resource = GuiHandler.class.getClassLoader().getResource("gui" + path);
final URL resource = GuiHandler.class.getClassLoader().getResource("ui" + path);
if (resource != null) {
write(exchange, IOUtils.toString(resource, Charset.defaultCharset()).getBytes(),
Mediatype.resolveBySuffix(resource.getPath()).getMimeType());
@ -59,7 +59,7 @@ public class GuiHandler extends BaseHandler {
@Getter
protected enum Mediatype {
JS("application/javascript"), MD("text/markdown"), CSS("text/css");
JS("application/javascript"), MD("text/markdown"), CSS("text/css"), SVG("image/svg+xml"), HTML("text/html"), PNG("image/png");
private final String mimeType;

View file

@ -1,22 +0,0 @@
# KoSIT Validator - Daemon
[API usage](docs/api)
[configurations](docs/configurations)
# Server information
View [validator configuration](/server/config) or <a href="/server/health" target="_blank">health information</a>
# Try it
<div>
<form>
<div>
<label for="file">Choose a file</label>
<input type="file" id="file" name="myFile">
<input type="submit" id="submit" value="Validate" onclick="return validate();">
</div>
<div id="result"></div>
</form>
</div>

View file

@ -1,42 +0,0 @@
# API Usage
The validation service listens to `POST`-requests to any server uri. You need to supply the xml/object to validate in the post body.
The service expects a single plain input in the post body, e.g. `multipart/form-data` is not supported.
Examples:
* `cURL`
```shell script
curl --location --request POST 'http://localhost:8080' \
--header 'Content-Type: application/xml' \
--data-binary '@/target.xml'
```
* `java` (Apache HttpClient)
```java
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost postRequest = new HttpPost("http://localhost:8080/");
FileEntity entity = new FileEntity(Paths.get("some.xml").toFile(), ContentType.APPLICATION_XML);
postRequest.setEntity(entity);
HttpResponse response = httpClient.execute(postRequest);
System.out.println(IOUtils.toString(response.getEntity().getContent()));
```
* `javascript`
```javascript
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/xml");
var file = "<file contents here>";
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: file,
redirect: 'follow'
};
fetch("http://localhost:8080", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
```

View file

@ -1,15 +0,0 @@
# Configurations
The validator needs a scenario configuration for working properly.
Currently, there are two public third party validation configurations available.
* Validation Configuration for [XRechnung](http://www.xoev.de/de/xrechnung):
* Source code is available on [GitHub](https://github.com/itplr-kosit/validator-configuration-xrechnung)
* [Releases](https://github.com/itplr-kosit/validator-configuration-xrechnung/releases) can also be downloaded
* Validation Configuration for [XGewerbeanzeige](https://xgewerbeanzeige.de/)
* Source code is available on [GitHub](https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige)
* [Releases](https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige/releases) can also be downloaded
For creating custom configurations see [configuration documentation](https://github.com/itplr-kosit/validator/blob/master/docs/configurations.md)
for details

View file

@ -1,81 +0,0 @@
<!--
~ Copyright 2017-2022 Koordinierungsstelle für IT-Standards (KoSIT)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Validator</title>
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width,initial-scale=1" name="viewport">
<link href="lib/vue.css" rel="stylesheet">
<style>
#result {
border: 1pt solid black;
margin-top: 20px
}
#result:empty {
display: none;
}
</style>
</head>
<body>
<script type="text/javascript">
var validate = function () {
const input = document.getElementById('file');
const output = document.getElementById('result');
var headers = new Headers();
headers.append('Content-Type', 'application/xml');
var requestOptions = {
method: 'POST',
headers: headers,
body: input.files[0],
redirect: 'follow',
};
fetch('/', requestOptions)
.then(response => response.text())
.then(result => output.innerText = result)
.catch(error => output.innerText = result);
return false;
};
</script>
<div data-app id="app">Loading validator... </div>
<script>
window.$docsify = {
repo: "itplr-kosit/validator",
loadSidebar: false,
hideSidebar: true,
autoHeader: true,
}
</script>
<script src="lib/docsify.min.js"></script>
</body>
</html>

View file

@ -1,23 +0,0 @@
Sources in this diretory are based on https://github.com/docsifyjs/docsify/
MIT License
Copyright (c) 2016 - present cinwell.li
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because one or more lines are too long

View file

@ -1,844 +0,0 @@
/*
* Copyright 2017-2022 Koordinierungsstelle für IT-Standards (KoSIT)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*/
* {
-webkit-font-smoothing: antialiased;
-webkit-overflow-scrolling: touch;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-text-size-adjust: none;
-webkit-touch-callout: none;
box-sizing: border-box;
}
body:not(.ready) {
overflow: hidden;
}
body:not(.ready) [data-cloak],
body:not(.ready) .app-nav,
body:not(.ready) > nav {
display: none;
}
div#app {
font-size: 30px;
font-weight: lighter;
margin: 40vh auto;
text-align: center;
}
div#app:empty::before {
content: 'Loading...';
}
.emoji {
height: 1.2rem;
vertical-align: middle;
}
.progress {
background-color: var(--theme-color, #42b983);
height: 2px;
left: 0px;
position: fixed;
right: 0px;
top: 0px;
transition: width 0.2s, opacity 0.4s;
width: 0%;
z-index: 999999;
}
.search a:hover {
color: var(--theme-color, #42b983);
}
.search .search-keyword {
color: var(--theme-color, #42b983);
font-style: normal;
font-weight: bold;
}
html,
body {
height: 100%;
}
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
color: #34495e;
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
font-size: 15px;
letter-spacing: 0;
margin: 0;
overflow-x: hidden;
}
img {
max-width: 100%;
}
a[disabled] {
cursor: not-allowed;
opacity: 0.6;
}
kbd {
border: solid 1px #ccc;
border-radius: 3px;
display: inline-block;
font-size: 12px !important;
line-height: 12px;
margin-bottom: 3px;
padding: 3px 5px;
vertical-align: middle;
}
li input[type='checkbox'] {
margin: 0 0.2em 0.25em 0;
vertical-align: middle;
}
.app-nav {
margin: 25px 60px 0 0;
position: absolute;
right: 0;
text-align: right;
z-index: 10;
/* navbar dropdown */
}
.app-nav.no-badge {
margin-right: 25px;
}
.app-nav p {
margin: 0;
}
.app-nav > a {
margin: 0 1rem;
padding: 5px 0;
}
.app-nav ul,
.app-nav li {
display: inline-block;
list-style: none;
margin: 0;
}
.app-nav a {
color: inherit;
font-size: 16px;
text-decoration: none;
transition: color 0.3s;
}
.app-nav a:hover {
color: var(--theme-color, #42b983);
}
.app-nav a.active {
border-bottom: 2px solid var(--theme-color, #42b983);
color: var(--theme-color, #42b983);
}
.app-nav li {
display: inline-block;
margin: 0 1rem;
padding: 5px 0;
position: relative;
cursor: pointer;
}
.app-nav li ul {
background-color: #fff;
border: 1px solid #ddd;
border-bottom-color: #ccc;
border-radius: 4px;
box-sizing: border-box;
display: none;
max-height: calc(100vh - 61px);
overflow-y: auto;
padding: 10px 0;
position: absolute;
right: -15px;
text-align: left;
top: 100%;
white-space: nowrap;
}
.app-nav li ul li {
display: block;
font-size: 14px;
line-height: 1rem;
margin: 0;
margin: 8px 14px;
white-space: nowrap;
}
.app-nav li ul a {
display: block;
font-size: inherit;
margin: 0;
padding: 0;
}
.app-nav li ul a.active {
border-bottom: 0;
}
.app-nav li:hover ul {
display: block;
}
.github-corner {
border-bottom: 0;
position: fixed;
right: 0;
text-decoration: none;
top: 0;
z-index: 1;
}
.github-corner:hover .octo-arm {
-webkit-animation: octocat-wave 560ms ease-in-out;
animation: octocat-wave 560ms ease-in-out;
}
.github-corner svg {
color: #fff;
fill: var(--theme-color, #42b983);
height: 80px;
width: 80px;
}
main {
display: block;
position: relative;
width: 100vw;
height: 100%;
z-index: 0;
}
main.hidden {
display: none;
}
.anchor {
display: inline-block;
text-decoration: none;
transition: all 0.3s;
}
.anchor span {
color: #34495e;
}
.anchor:hover {
text-decoration: underline;
}
.sidebar {
border-right: 1px solid rgba(0,0,0,0.07);
overflow-y: auto;
padding: 40px 0 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
transition: transform 250ms ease-out;
width: 300px;
z-index: 20;
}
.sidebar > h1 {
margin: 0 auto 1rem;
font-size: 1.5rem;
font-weight: 300;
text-align: center;
}
.sidebar > h1 a {
color: inherit;
text-decoration: none;
}
.sidebar > h1 .app-nav {
display: block;
position: static;
}
.sidebar .sidebar-nav {
line-height: 2em;
padding-bottom: 40px;
}
.sidebar li.collapse .app-sub-sidebar {
display: none;
}
.sidebar ul {
margin: 0 0 0 15px;
padding: 0;
}
.sidebar li > p {
font-weight: 700;
margin: 0;
}
.sidebar ul,
.sidebar ul li {
list-style: none;
}
.sidebar ul li a {
border-bottom: none;
display: block;
}
.sidebar ul li ul {
padding-left: 20px;
}
.sidebar::-webkit-scrollbar {
width: 4px;
}
.sidebar::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 4px;
}
.sidebar:hover::-webkit-scrollbar-thumb {
background: rgba(136,136,136,0.4);
}
.sidebar:hover::-webkit-scrollbar-track {
background: rgba(136,136,136,0.1);
}
.sidebar-toggle {
background-color: transparent;
background-color: rgba(255,255,255,0.8);
border: 0;
outline: none;
padding: 10px;
position: absolute;
bottom: 0;
left: 0;
text-align: center;
transition: opacity 0.3s;
width: 284px;
z-index: 30;
cursor: pointer;
}
.sidebar-toggle:hover .sidebar-toggle-button {
opacity: 0.4;
}
.sidebar-toggle span {
background-color: var(--theme-color, #42b983);
display: block;
margin-bottom: 4px;
width: 16px;
height: 2px;
}
body.sticky .sidebar,
body.sticky .sidebar-toggle {
position: fixed;
}
.content {
padding-top: 60px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 300px;
transition: left 250ms ease;
}
.markdown-section {
margin: 0 auto;
max-width: 80%;
padding: 30px 15px 40px 15px;
position: relative;
}
.markdown-section > * {
box-sizing: border-box;
font-size: inherit;
}
.markdown-section > :first-child {
margin-top: 0 !important;
}
.markdown-section hr {
border: none;
border-bottom: 1px solid #eee;
margin: 2em 0;
}
.markdown-section iframe {
border: 1px solid #eee;
/* fix horizontal overflow on iOS Safari */
width: 1px;
min-width: 100%;
}
.markdown-section table {
border-collapse: collapse;
border-spacing: 0;
display: block;
margin-bottom: 1rem;
overflow: auto;
width: 100%;
}
.markdown-section th {
border: 1px solid #ddd;
font-weight: bold;
padding: 6px 13px;
}
.markdown-section td {
border: 1px solid #ddd;
padding: 6px 13px;
}
.markdown-section tr {
border-top: 1px solid #ccc;
}
.markdown-section tr:nth-child(2n) {
background-color: #f8f8f8;
}
.markdown-section p.tip {
background-color: #f8f8f8;
border-bottom-right-radius: 2px;
border-left: 4px solid #f66;
border-top-right-radius: 2px;
margin: 2em 0;
padding: 12px 24px 12px 30px;
position: relative;
}
.markdown-section p.tip:before {
background-color: #f66;
border-radius: 100%;
color: #fff;
content: '!';
font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
font-size: 14px;
font-weight: bold;
left: -12px;
line-height: 20px;
position: absolute;
height: 20px;
width: 20px;
text-align: center;
top: 14px;
}
.markdown-section p.tip code {
background-color: #efefef;
}
.markdown-section p.tip em {
color: #34495e;
}
.markdown-section p.warn {
background: rgba(66,185,131,0.1);
border-radius: 2px;
padding: 1rem;
}
.markdown-section ul.task-list > li {
list-style-type: none;
}
body.close .sidebar {
transform: translateX(-300px);
}
body.close .sidebar-toggle {
width: auto;
}
body.close .content {
left: 0;
}
@media print {
.github-corner,
.sidebar-toggle,
.sidebar,
.app-nav {
display: none;
}
}
@media screen and (max-width: 768px) {
.github-corner,
.sidebar-toggle,
.sidebar {
position: fixed;
}
.app-nav {
margin-top: 16px;
}
.app-nav li ul {
top: 30px;
}
main {
height: auto;
overflow-x: hidden;
}
.sidebar {
left: -300px;
transition: transform 250ms ease-out;
}
.content {
left: 0;
max-width: 100vw;
position: static;
padding-top: 20px;
transition: transform 250ms ease;
}
.app-nav,
.github-corner {
transition: transform 250ms ease-out;
}
.sidebar-toggle {
background-color: transparent;
width: auto;
padding: 30px 30px 10px 10px;
}
body.close .sidebar {
transform: translateX(300px);
}
body.close .sidebar-toggle {
background-color: rgba(255,255,255,0.8);
transition: 1s background-color;
width: 284px;
padding: 10px;
}
body.close .content {
transform: translateX(300px);
}
body.close .app-nav,
body.close .github-corner {
display: none;
}
.github-corner:hover .octo-arm {
-webkit-animation: none;
animation: none;
}
.github-corner .octo-arm {
-webkit-animation: octocat-wave 560ms ease-in-out;
animation: octocat-wave 560ms ease-in-out;
}
}
@-webkit-keyframes octocat-wave {
0%, 100% {
transform: rotate(0);
}
20%, 60% {
transform: rotate(-25deg);
}
40%, 80% {
transform: rotate(10deg);
}
}
@keyframes octocat-wave {
0%, 100% {
transform: rotate(0);
}
20%, 60% {
transform: rotate(-25deg);
}
40%, 80% {
transform: rotate(10deg);
}
}
section.cover {
align-items: center;
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
height: 100vh;
display: none;
}
section.cover.show {
display: flex;
}
section.cover.has-mask .mask {
background-color: #fff;
opacity: 0.8;
position: absolute;
top: 0;
height: 100%;
width: 100%;
}
section.cover .cover-main {
flex: 1;
margin: -20px 16px 0;
text-align: center;
z-index: 1;
}
section.cover a {
color: inherit;
text-decoration: none;
}
section.cover a:hover {
text-decoration: none;
}
section.cover p {
line-height: 1.5rem;
margin: 1em 0;
}
section.cover h1 {
color: inherit;
font-size: 2.5rem;
font-weight: 300;
margin: 0.625rem 0 2.5rem;
position: relative;
text-align: center;
}
section.cover h1 a {
display: block;
}
section.cover h1 small {
bottom: -0.4375rem;
font-size: 1rem;
position: absolute;
}
section.cover blockquote {
font-size: 1.5rem;
text-align: center;
}
section.cover ul {
line-height: 1.8;
list-style-type: none;
margin: 1em auto;
max-width: 500px;
padding: 0;
}
section.cover .cover-main > p:last-child a {
border-color: var(--theme-color, #42b983);
border-radius: 2rem;
border-style: solid;
border-width: 1px;
box-sizing: border-box;
color: var(--theme-color, #42b983);
display: inline-block;
font-size: 1.05rem;
letter-spacing: 0.1rem;
margin: 0.5rem 1rem;
padding: 0.75em 2rem;
text-decoration: none;
transition: all 0.15s ease;
}
section.cover .cover-main > p:last-child a:last-child {
background-color: var(--theme-color, #42b983);
color: #fff;
}
section.cover .cover-main > p:last-child a:last-child:hover {
color: inherit;
opacity: 0.8;
}
section.cover .cover-main > p:last-child a:hover {
color: inherit;
}
section.cover blockquote > p > a {
border-bottom: 2px solid var(--theme-color, #42b983);
transition: color 0.3s;
}
section.cover blockquote > p > a:hover {
color: var(--theme-color, #42b983);
}
body {
background-color: #fff;
}
/* sidebar */
.sidebar {
background-color: #fff;
color: #364149;
}
.sidebar li {
margin: 6px 0 6px 0;
}
.sidebar ul li a {
color: #505d6b;
font-size: 14px;
font-weight: normal;
overflow: hidden;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;
}
.sidebar ul li a:hover {
text-decoration: underline;
}
.sidebar ul li ul {
padding: 0;
}
.sidebar ul li.active > a {
border-right: 2px solid;
color: var(--theme-color, #42b983);
font-weight: 600;
}
.app-sub-sidebar li::before {
content: '-';
padding-right: 4px;
float: left;
}
/* markdown content found on pages */
.markdown-section h1,
.markdown-section h2,
.markdown-section h3,
.markdown-section h4,
.markdown-section strong {
color: #2c3e50;
font-weight: 600;
}
.markdown-section a {
color: var(--theme-color, #42b983);
font-weight: 600;
}
.markdown-section h1 {
font-size: 2rem;
margin: 0 0 1rem;
}
.markdown-section h2 {
font-size: 1.75rem;
margin: 45px 0 0.8rem;
}
.markdown-section h3 {
font-size: 1.5rem;
margin: 40px 0 0.6rem;
}
.markdown-section h4 {
font-size: 1.25rem;
}
.markdown-section h5 {
font-size: 1rem;
}
.markdown-section h6 {
color: #777;
font-size: 1rem;
}
.markdown-section figure,
.markdown-section p {
margin: 1.2em 0;
}
.markdown-section p,
.markdown-section ul,
.markdown-section ol {
line-height: 1.6rem;
word-spacing: 0.05rem;
}
.markdown-section ul,
.markdown-section ol {
padding-left: 1.5rem;
}
.markdown-section blockquote {
border-left: 4px solid var(--theme-color, #42b983);
color: #858585;
margin: 2em 0;
padding-left: 20px;
}
.markdown-section blockquote p {
font-weight: 600;
margin-left: 0;
}
.markdown-section iframe {
margin: 1em 0;
}
.markdown-section em {
color: #7f8c8d;
}
.markdown-section code {
background-color: #f8f8f8;
border-radius: 2px;
color: #e96900;
font-family: 'Roboto Mono', Monaco, courier, monospace;
font-size: 0.8rem;
margin: 0 2px;
padding: 3px 5px;
white-space: pre-wrap;
}
.markdown-section pre {
-moz-osx-font-smoothing: initial;
-webkit-font-smoothing: initial;
background-color: #f8f8f8;
font-family: 'Roboto Mono', Monaco, courier, monospace;
line-height: 1.5rem;
margin: 1.2em 0;
overflow: auto;
padding: 0 1.4rem;
position: relative;
word-wrap: normal;
}
/* code highlight */
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #8e908c;
}
.token.namespace {
opacity: 0.7;
}
.token.boolean,
.token.number {
color: #c76b29;
}
.token.punctuation {
color: #525252;
}
.token.property {
color: #c08b30;
}
.token.tag {
color: #2973b7;
}
.token.string {
color: var(--theme-color, #42b983);
}
.token.selector {
color: #6679cc;
}
.token.attr-name {
color: #2973b7;
}
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #22a2c9;
}
.token.attr-value,
.token.control,
.token.directive,
.token.unit {
color: var(--theme-color, #42b983);
}
.token.keyword,
.token.function {
color: #e96900;
}
.token.statement,
.token.regex,
.token.atrule {
color: #22a2c9;
}
.token.placeholder,
.token.variable {
color: #3d8fd1;
}
.token.deleted {
text-decoration: line-through;
}
.token.inserted {
border-bottom: 1px dotted #202746;
text-decoration: none;
}
.token.italic {
font-style: italic;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.important {
color: #c94922;
}
.token.entity {
cursor: help;
}
.markdown-section pre > code {
-moz-osx-font-smoothing: initial;
-webkit-font-smoothing: initial;
background-color: #f8f8f8;
border-radius: 2px;
color: #525252;
display: block;
font-family: 'Roboto Mono', Monaco, courier, monospace;
font-size: 0.8rem;
line-height: inherit;
margin: 0 2px;
max-width: inherit;
overflow: inherit;
padding: 2.2em 5px;
white-space: inherit;
}
.markdown-section code::after,
.markdown-section code::before {
letter-spacing: 0.05rem;
}
code .token {
-moz-osx-font-smoothing: initial;
-webkit-font-smoothing: initial;
min-height: 1.5rem;
position: relative;
left: auto;
}
pre::after {
color: #ccc;
content: attr(data-lang);
font-size: 0.6rem;
font-weight: 600;
height: 15px;
line-height: 15px;
padding: 5px 10px 0;
position: absolute;
right: 0;
text-align: right;
top: 0;
}

View file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[983],{3769:function(e){e.exports=JSON.parse('{"name":"docusaurus-plugin-content-docs","id":"default"}')}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[237],{8451:function(e,t,a){a.r(t),a.d(t,{default:function(){return N}});var l=a(7294),n=a(6010),r=a(7462),i=a(8523),o={dropzone:"dropzone_p8R5",active:"active_ajsO",hasFiles:"hasFiles_h6nl",fileHoverPreview:"fileHoverPreview_IJ6m",icon:"icon_Zdm0",fileHoverIcon:"fileHoverIcon_TFrZ",uploadIcon:"uploadIcon__9vU"};var c=e=>{let{accept:t,children:a,className:c,activeClassName:s,multiple:d=!1,name:m,onDrop:p,hasSelectedFiles:u,...v}=e;const{getRootProps:h,getInputProps:f,isDragActive:E,isDragAccept:g,isDragReject:Z}=(0,i.uI)({accept:t,multiple:d,onDrop:(e,t,a)=>{const l=t.map((e=>e.file));p(e,l,a)},...v});return l.createElement("div",(0,r.Z)({},h(),{className:(0,n.Z)(o.dropzone,E&&o.active,u&&o.hasFiles,c,E&&s),"data-testid":"dropzone","data-is-drag-active":E,"data-is-drag-accepted":g,"data-is-drag-rejected":Z,"data-has-files":u}),l.createElement("div",{className:(0,n.Z)(o.fileHoverPreview,E&&o.previewActive)},l.createElement("svg",{className:(0,n.Z)(o.icon,o.fileHoverIcon),fill:"currentColor","aria-hidden":"true",viewBox:"0 0 24 24"},l.createElement("path",{d:"M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"}))),l.createElement("svg",{className:(0,n.Z)(o.icon,o.uploadIcon),fill:"currentColor","aria-hidden":"true",viewBox:"0 0 24 24"},l.createElement("path",{d:"M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"})),a,l.createElement("input",(0,r.Z)({name:m},f(),{"data-testid":"dropzone-input"})))},s=a(5833),d=a(7547),m=a(7506),p="button__Owf",u="spinnerWrapper_KcTt",v="loading_FTzi",h="spinner_hC2W";var f=function(e){let{children:t,type:a="button",className:i,loading:o=!1,...c}=e;return l.createElement("button",(0,r.Z)({},c,{className:(0,n.Z)(p,o&&v,i),type:a,"aria-busy":o}),l.createElement("div",{className:u,"aria-hidden":!0},l.createElement("div",{className:h})),t)},E="buttonGroup__nLk",g="resultDisplay_JbbN",Z="withError_Jabi";const _={"text/xml":[".xml",".XML"],"application/xml":[".xml",".XML"]};var b=function(){const[e,t]=(0,l.useState)(null),[a,r]=(0,l.useState)([]),{data:i,error:o,request:p,status:u}=(0,m.Z)(),v=(0,l.useCallback)(((e,a)=>{e.length?(t(e[0]),r([])):r(a)}),[]),h=!!o&&[406,422].includes(o.code);return l.createElement(l.Fragment,null,l.createElement("form",{onSubmit:t=>{t.preventDefault(),e&&p("/",{method:"POST",headers:{"Content-Type":"application/xml"},body:e,redirect:"follow"})}},u===m.e.Failure&&o&&!h&&l.createElement(d.Z,{title:"An error occurred while validating the file"},l.createElement(s.Z,{enableCopy:!0},o.message)),a.length>1&&l.createElement(d.Z,{title:"Please select a single file only"}),1===a.length&&l.createElement(d.Z,{title:"Only XML files are supported"},l.createElement(s.Z,null,`Invalid file found: ${a[0].name}`)),l.createElement(c,{onDrop:v,accept:_,multiple:!1,hasSelectedFiles:!!e},e?e.name:l.createElement(l.Fragment,null,"Drag & drop files here or click to select a file")),l.createElement("div",{className:E},l.createElement(f,{type:"submit",disabled:!e,loading:u===m.e.Loading},"Validate"))),(i&&u===m.e.Success||h)&&l.createElement("div",{className:(0,n.Z)(g,h&&Z)},l.createElement(s.Z,{download:{fileName:(b=null==e?void 0:e.name,b?`${b.replace(/\.xml$/i,"")}-report.xml`:"report.xml"),mime:"application/xml"},enableCopy:!0},i||(null==o?void 0:o.message)||"")));var b},y=a(8222);function N(){return l.createElement(y.Z,{layoutDescription:"KoSIT Validator Daemon",headline:"Try the validator!",description:"Upload an XML file here to validate its contents. Note: this is just a demo implementation, not meant for production usage. If you need a production ready implementation you are welcome to contribute to the open source project."},l.createElement(b,null))}}}]);

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[85],{4247:function(e,t,a){a.r(t),a.d(t,{default:function(){return s}});var n=a(7294),l=a(6010),c=a(1944),r=a(5281),i=a(3285),m=a(9254),o=a(9407),d="mdxPageWrapper_j9I6";function s(e){const{content:t}=e,{metadata:{title:a,description:s,frontMatter:u}}=t,{wrapperClassName:p,hide_table_of_contents:_}=u;return n.createElement(c.FG,{className:(0,l.Z)(p??r.k.wrapper.mdxPages,r.k.page.mdxPage)},n.createElement(c.d,{title:a,description:s}),n.createElement(i.Z,null,n.createElement("main",{className:"container container--fluid margin-vert--lg"},n.createElement("div",{className:(0,l.Z)("row",d)},n.createElement("div",{className:(0,l.Z)("col",!_&&"col--8")},n.createElement("article",null,n.createElement(m.Z,null,n.createElement(t,null)))),!_&&t.toc.length>0&&n.createElement("div",{className:"col col--2"},n.createElement(o.Z,{toc:t.toc,minHeadingLevel:u.toc_min_heading_level,maxHeadingLevel:u.toc_max_heading_level}))))))}}}]);

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[414],{3123:function(e,t,n){n.r(t),n.d(t,{contentTitle:function(){return p},default:function(){return u},frontMatter:function(){return r},metadata:function(){return d},toc:function(){return i}});var a=n(7462),o=(n(7294),n(3905));const r={title:"Markdown page example"},p="Markdown page example",d={type:"mdx",permalink:"/markdown-page",source:"@site/src/pages/markdown-page.md",title:"Markdown page example",description:"You don't need React to write simple standalone pages.",frontMatter:{title:"Markdown page example"}},i=[],l={toc:i};function u(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,a.Z)({},l,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"markdown-page-example"},"Markdown page example"),(0,o.kt)("p",null,"You don't need React to write simple standalone pages."))}u.isMDXComponent=!0}}]);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[910],{809:function(t,e,i){i.r(e),i.d(e,{assets:function(){return u},contentTitle:function(){return r},default:function(){return d},frontMatter:function(){return o},metadata:function(){return l},toc:function(){return s}});var n=i(7462),a=(i(7294),i(3905));const o={sidebar_position:1},r="Configurations",l={unversionedId:"configurations",id:"configurations",title:"Configurations",description:"The validator needs a scenario configuration for working properly.",source:"@site/docs/configurations.md",sourceDirName:".",slug:"/configurations",permalink:"/docs/configurations",draft:!1,editUrl:"https://github.com/itplr-kosit/validator/server/ui/docs/configurations.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",next:{title:"API Usage",permalink:"/docs/api"}},u={},s=[],c={toc:s};function d(t){let{components:e,...i}=t;return(0,a.kt)("wrapper",(0,n.Z)({},c,i,{components:e,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"configurations"},"Configurations"),(0,a.kt)("p",null,"The validator needs a scenario configuration for working properly."),(0,a.kt)("p",null,"Currently, there are two public third party validation configurations available."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Validation Configuration for ",(0,a.kt)("a",{parentName:"li",href:"http://www.xoev.de/de/xrechnung"},"XRechnung"),":",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"Source code is available on ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/itplr-kosit/validator-configuration-xrechnung"},"GitHub")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/itplr-kosit/validator-configuration-xrechnung/releases"},"Releases")," can also be downloaded"))),(0,a.kt)("li",{parentName:"ul"},"Validation Configuration for ",(0,a.kt)("a",{parentName:"li",href:"https://xgewerbeanzeige.de/"},"XGewerbeanzeige"),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"Source code is available on ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige"},"GitHub")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige/releases"},"Releases")," can also be downloaded")))),(0,a.kt)("p",null,"For creating custom configurations\nsee ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/itplr-kosit/validator/blob/master/docs/configurations.md"},"configuration documentation"),"\nfor details"))}d.isMDXComponent=!0}}]);

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[207],{7480:function(e,t,n){n.r(t),n.d(t,{assets:function(){return l},contentTitle:function(){return o},default:function(){return c},frontMatter:function(){return r},metadata:function(){return s},toc:function(){return p}});var a=n(7462),i=(n(7294),n(3905));const r={sidebar_position:2},o="API Usage",s={unversionedId:"api",id:"api",title:"API Usage",description:"The validation service listens to POST-requests to any server uri. You need to supply the xml/object to validate in",source:"@site/docs/api.md",sourceDirName:".",slug:"/api",permalink:"/docs/api",draft:!1,editUrl:"https://github.com/itplr-kosit/validator/server/ui/docs/api.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Configurations",permalink:"/docs/configurations"},next:{title:"Changelog",permalink:"/docs/changelog"}},l={},p=[],u={toc:p};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"api-usage"},"API Usage"),(0,i.kt)("p",null,"The validation service listens to ",(0,i.kt)("inlineCode",{parentName:"p"},"POST"),"-requests to any server uri. You need to supply the xml/object to validate in\nthe post body.\nThe service expects a single plain input in the post body, e.g. ",(0,i.kt)("inlineCode",{parentName:"p"},"multipart/form-data")," is not supported."),(0,i.kt)("p",null,"Examples:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cURL"))),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-shell",metastring:"script",script:!0},"curl --location --request POST 'http://localhost:8080' \\\n--header 'Content-Type: application/xml' \\\n--data-binary '@/target.xml'\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"java")," (Apache HttpClient)")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-java"},'HttpClient httpClient=HttpClientBuilder.create().build();\n HttpPost postRequest=new HttpPost("http://localhost:8080/");\n FileEntity entity=new FileEntity(Paths.get("some.xml").toFile(),ContentType.APPLICATION_XML);\n postRequest.setEntity(entity);\n HttpResponse response=httpClient.execute(postRequest);\n System.out.println(IOUtils.toString(response.getEntity().getContent()));\n')),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"javascript"))),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-javascript"},'var myHeaders = new Headers();\nmyHeaders.append("Content-Type", "application/xml");\n\nvar file = "<file contents here>";\n\nvar requestOptions = {\n method: \'POST\',\n headers: myHeaders,\n body: file,\n redirect: \'follow\'\n};\n\nfetch("http://localhost:8080", requestOptions)\n .then(response => response.text())\n .then(result => console.log(result))\n .catch(error => console.log(\'error\', error));\n')))}c.isMDXComponent=!0}}]);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[118],{6275:function(e,t,n){n.d(t,{Z:function(){return o}});var r=n(7294),a=n(5833),i=n(7547),l=n(7506);var o=function(e){let{endpoint:t,fileName:n}=e;const{request:o,data:c,error:u,status:d}=(0,l.Z)();return(0,r.useEffect)((()=>{o(t,{headers:{"Content-Type":"application/xml"}})}),[t,o]),r.createElement(r.Fragment,null,d===l.e.Failure&&u&&r.createElement(i.Z,{title:"An error occurred while fetching"},r.createElement(a.Z,null,u.message)),d===l.e.Success&&c&&r.createElement(a.Z,{download:{fileName:n,mime:"application/xml"},enableCopy:!0},c))}},3596:function(e,t,n){n.r(t);var r=n(7294),a=n(8222),i=n(6275);t.default=function(){return r.createElement(a.Z,{title:"Validator configuration",layoutDescription:"The currently loaded validator configuration",headline:"Validator configuration",description:"View the currently loaded validator configuration."},r.createElement(i.Z,{endpoint:"/server/config",fileName:"config.xml"}))}}}]);

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[433,981],{6275:function(e,t,n){n.d(t,{Z:function(){return o}});var a=n(7294),r=n(5833),l=n(7547),i=n(7506);var o=function(e){let{endpoint:t,fileName:n}=e;const{request:o,data:u,error:c,status:s}=(0,i.Z)();return(0,a.useEffect)((()=>{o(t,{headers:{"Content-Type":"application/xml"}})}),[t,o]),a.createElement(a.Fragment,null,s===i.e.Failure&&c&&a.createElement(l.Z,{title:"An error occurred while fetching"},a.createElement(r.Z,null,c.message)),s===i.e.Success&&u&&a.createElement(r.Z,{download:{fileName:n,mime:"application/xml"},enableCopy:!0},u))}},2406:function(e,t,n){n.r(t);var a=n(7294),r=n(8222),l=n(6275);t.default=function(){return a.createElement(r.Z,{title:"Health information",layoutDescription:"Health and status information about the system",headline:"Server health information",description:"Information about health and status of the running system."},a.createElement(l.Z,{endpoint:"/server/health",fileName:"health.xml"}))}},6411:function(e,t,n){n.r(t);var a=n(2406);t.default=a.default}}]);

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[53],{1109:function(e){e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Configurations","href":"/docs/configurations","docId":"configurations"},{"type":"link","label":"API Usage","href":"/docs/api","docId":"api"},{"type":"link","label":"Changelog","href":"/docs/changelog","docId":"changelog"}]},"docs":{"api":{"id":"api","title":"API Usage","description":"The validation service listens to POST-requests to any server uri. You need to supply the xml/object to validate in","sidebar":"tutorialSidebar"},"changelog":{"id":"changelog","title":"Changelog","description":"All notable changes to this project will be documented in this file.","sidebar":"tutorialSidebar"},"configurations":{"id":"configurations","title":"Configurations","description":"The validator needs a scenario configuration for working properly.","sidebar":"tutorialSidebar"}}}')}}]);

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[972],{4972:function(e,t,n){n.r(t),n.d(t,{default:function(){return i}});var a=n(7294),o=n(5999),l=n(1944),r=n(3285);function i(){return a.createElement(a.Fragment,null,a.createElement(l.d,{title:(0,o.I)({id:"theme.NotFound.title",message:"Page Not Found"})}),a.createElement(r.Z,null,a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(o.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),a.createElement("p",null,a.createElement(o.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),a.createElement("p",null,a.createElement(o.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken.")))))))}}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[981],{6275:function(e,t,n){n.d(t,{Z:function(){return o}});var a=n(7294),r=n(5833),l=n(7547),i=n(7506);var o=function(e){let{endpoint:t,fileName:n}=e;const{request:o,data:u,error:s,status:c}=(0,i.Z)();return(0,a.useEffect)((()=>{o(t,{headers:{"Content-Type":"application/xml"}})}),[t,o]),a.createElement(a.Fragment,null,c===i.e.Failure&&s&&a.createElement(l.Z,{title:"An error occurred while fetching"},a.createElement(r.Z,null,s.message)),c===i.e.Success&&u&&a.createElement(r.Z,{download:{fileName:n,mime:"application/xml"},enableCopy:!0},u))}},2406:function(e,t,n){n.r(t);var a=n(7294),r=n(8222),l=n(6275);t.default=function(){return a.createElement(r.Z,{title:"Health information",layoutDescription:"Health and status information about the system",headline:"Server health information",description:"Information about health and status of the running system."},a.createElement(l.Z,{endpoint:"/server/health",fileName:"health.xml"}))}}}]);

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[962,118],{6275:function(e,t,n){n.d(t,{Z:function(){return o}});var a=n(7294),r=n(5833),i=n(7547),l=n(7506);var o=function(e){let{endpoint:t,fileName:n}=e;const{request:o,data:c,error:u,status:d}=(0,l.Z)();return(0,a.useEffect)((()=>{o(t,{headers:{"Content-Type":"application/xml"}})}),[t,o]),a.createElement(a.Fragment,null,d===l.e.Failure&&u&&a.createElement(i.Z,{title:"An error occurred while fetching"},a.createElement(r.Z,null,u.message)),d===l.e.Success&&c&&a.createElement(r.Z,{download:{fileName:n,mime:"application/xml"},enableCopy:!0},c))}},3596:function(e,t,n){n.r(t);var a=n(7294),r=n(8222),i=n(6275);t.default=function(){return a.createElement(r.Z,{title:"Validator configuration",layoutDescription:"The currently loaded validator configuration",headline:"Validator configuration",description:"View the currently loaded validator configuration."},a.createElement(i.Z,{endpoint:"/server/config",fileName:"config.xml"}))}},2536:function(e,t,n){n.r(t);var a=n(3596);t.default=a.default}}]);

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[]).push([[870],{5745:function(e){e.exports=JSON.parse('{"name":"docusaurus-plugin-content-pages","id":"default"}')}}]);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,53 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
* @license MIT */
/**
* Prism: Lightweight, robust, elegant syntax highlighting
*
* @license MIT <https://opensource.org/licenses/MIT>
* @author Lea Verou <https://lea.verou.me>
* @namespace
* @public
*/
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

View file

@ -0,0 +1 @@
!function(){"use strict";var e,t,n,r,o,f={},u={};function i(e){var t=u[e];if(void 0!==t)return t.exports;var n=u[e]={id:e,loaded:!1,exports:{}};return f[e].call(n.exports,n,n.exports,i),n.loaded=!0,n.exports}i.m=f,i.c=u,e=[],i.O=function(t,n,r,o){if(!n){var f=1/0;for(d=0;d<e.length;d++){n=e[d][0],r=e[d][1],o=e[d][2];for(var u=!0,c=0;c<n.length;c++)(!1&o||f>=o)&&Object.keys(i.O).every((function(e){return i.O[e](n[c])}))?n.splice(c--,1):(u=!1,o<f&&(f=o));if(u){e.splice(d--,1);var a=r();void 0!==a&&(t=a)}}return t}o=o||0;for(var d=e.length;d>0&&e[d-1][2]>o;d--)e[d]=e[d-1];e[d]=[n,r,o]},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},i.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var o=Object.create(null);i.r(o);var f={};t=t||[null,n({}),n([]),n(n)];for(var u=2&r&&e;"object"==typeof u&&!~t.indexOf(u);u=n(u))Object.getOwnPropertyNames(u).forEach((function(t){f[t]=function(){return e[t]}}));return f.default=function(){return e},i.d(o,f),o},i.d=function(e,t){for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},i.f={},i.e=function(e){return Promise.all(Object.keys(i.f).reduce((function(t,n){return i.f[n](e,t),t}),[]))},i.u=function(e){return"assets/js/"+({53:"935f2afb",80:"9beb87c2",85:"1f391b9e",118:"779c529c",207:"5fbc5cf1",237:"1df93b7f",414:"393be207",433:"798a5b56",514:"1be78505",592:"common",870:"f9512c76",910:"5d7f3e2f",918:"17896441",962:"eae68ef6",981:"e44a8e24",983:"01614a01"}[e]||e)+"."+{53:"66d1c40f",80:"1cc504cf",85:"ff7b907c",118:"e6f903f8",207:"6eb09db1",237:"c492b5ad",414:"36f5a793",433:"5978ee14",514:"6890df1b",523:"3ef240ab",592:"f13082de",601:"da3afc33",870:"5069c08d",910:"89ac2b20",918:"ee21715a",962:"43fba47a",972:"acd6d62f",981:"a5958152",983:"9bc90821"}[e]+".js"},i.miniCssF=function(e){},i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="validator-frontend:",i.l=function(e,t,n,f){if(r[e])r[e].push(t);else{var u,c;if(void 0!==n)for(var a=document.getElementsByTagName("script"),d=0;d<a.length;d++){var l=a[d];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==o+n){u=l;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",u.timeout=120,i.nc&&u.setAttribute("nonce",i.nc),u.setAttribute("data-webpack",o+n),u.src=e),r[e]=[t];var b=function(t,n){u.onerror=u.onload=null,clearTimeout(s);var o=r[e];if(delete r[e],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach((function(e){return e(n)})),t)return t(n)},s=setTimeout(b.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=b.bind(null,u.onerror),u.onload=b.bind(null,u.onload),c&&document.head.appendChild(u)}},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.p="/",i.gca=function(e){return e={17896441:"918","935f2afb":"53","9beb87c2":"80","1f391b9e":"85","779c529c":"118","5fbc5cf1":"207","1df93b7f":"237","393be207":"414","798a5b56":"433","1be78505":"514",common:"592",f9512c76:"870","5d7f3e2f":"910",eae68ef6:"962",e44a8e24:"981","01614a01":"983"}[e]||e,i.p+i.u(e)},function(){var e={303:0,532:0};i.f.j=function(t,n){var r=i.o(e,t)?e[t]:void 0;if(0!==r)if(r)n.push(r[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var o=new Promise((function(n,o){r=e[t]=[n,o]}));n.push(r[2]=o);var f=i.p+i.u(t),u=new Error;i.l(f,(function(n){if(i.o(e,t)&&(0!==(r=e[t])&&(e[t]=void 0),r)){var o=n&&("load"===n.type?"missing":n.type),f=n&&n.target&&n.target.src;u.message="Loading chunk "+t+" failed.\n("+o+": "+f+")",u.name="ChunkLoadError",u.type=o,u.request=f,r[1](u)}}),"chunk-"+t,t)}},i.O.j=function(t){return 0===e[t]};var t=function(t,n){var r,o,f=n[0],u=n[1],c=n[2],a=0;if(f.some((function(t){return 0!==e[t]}))){for(r in u)i.o(u,r)&&(i.m[r]=u[r]);if(c)var d=c(i)}for(t&&t(n);a<f.length;a++)o=f[a],i.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return i.O(d)},n=self.webpackChunkvalidator_frontend=self.webpackChunkvalidator_frontend||[];n.forEach(t.bind(null,0)),n.push=t.bind(null,n.push.bind(n))}()}();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24">
<path fill="hsl(220, 41%, 30%)" d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm-3.06 16L7.4 14.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L10.94 18zM13 9V3.5L18.5 9H13z"></path>
</svg>

After

Width:  |  Height:  |  Size: 306 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24">
<path fill="#fff" d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm-3.06 16L7.4 14.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L10.94 18zM13 9V3.5L18.5 9H13z"></path>
</svg>

After

Width:  |  Height:  |  Size: 292 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://your-docusaurus-test-site.com/config/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://your-docusaurus-test-site.com/config/ConfigPage</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://your-docusaurus-test-site.com/health/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://your-docusaurus-test-site.com/health/HealthPage</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://your-docusaurus-test-site.com/markdown-page</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://your-docusaurus-test-site.com/docs/api</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://your-docusaurus-test-site.com/docs/changelog</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://your-docusaurus-test-site.com/docs/configurations</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://your-docusaurus-test-site.com/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url></urlset>

View file

@ -27,7 +27,7 @@ public class GuiHandlerIT extends BaseIT {
@Test
public void checkGui() {
given().when().get("/").then().statusCode(200).and().contentType(ContentType.HTML);
given().when().get("/README.md").then().statusCode(200).and().contentType("text/markdown");
given().when().get("/index.html").then().statusCode(200).and().contentType("text/html");
given().when().get("/unknown.md").then().statusCode(404).and().contentType(ContentType.TEXT);
}
}