Commit 9866be59 authored by Lukas Wiest's avatar Lukas Wiest 🚂
Browse files

Merge branch '1-write-docs' into 'master'

write project docs (mkdocs)

Closes #1

See merge request !1
parents 32ea786f c4430fda
Pipeline #1602 passed with stages
in 1 minute and 19 seconds
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
......@@ -12,6 +12,9 @@ env/
# Apple
.DS_Store
# VSCode
.vscode/
# NetBeans
**/nbproject/private/
**/nbproject/Makefile-*.mk
......
{
"tagPrefix": ""
}
Information on how to contribute to this project, is written in the docs.
See here: [docs/unified-ticketing/contributing.md](docs/unified-ticketing/contributing.md)
......@@ -2,7 +2,7 @@ GNU General Public License
==========================
_Version 3, 29 June 2007_
_Copyright © 2007 Free Software Foundation, Inc. &lt;<http://fsf.org/>&gt;_
_Copyright © 2007 [Free Software Foundation, Inc.](https://fsf.org)_
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.
......
......@@ -4,3 +4,8 @@ This is the repository of the unified-ticketing library.
Documentation for this is done with GitLab Pages using MkDocs and is available here:
https://transfer.hft-stuttgart.de/pages/unified-ticketing/
If you don't want to open the Webpage or it is not available for whatever reason,
the documentation root is in [docs/index.md][docs-root]
[docs-root]: docs/index.md
# Logging
The library uses the built-in logger from `java.util.logging`.
It configures to not use the parent logger and places a new ConsoleHandler
on static initialization. This outputs the log in formatted lines like:
```text
[2020-11-27T14:40:26+0100] [WARNING] [de.hftstuttgart.unifiedticketing.core.Logging.test] calling from app
```
This will _not_ change visual presentation based on any localization.
## change log-level
Default is to use _INFO_-level.
You can change the used level:
```java
Logging.setLevel(Level);
```
The parameter passed is of type `java.util.logging.Level`.
## test logging output
To test if the logging as such works,
you can pass a test string:
```java
Logging.test("test message");
```
This message will be logged by the `Logging`-class own Logger,
once per level.
## set custom formatter
If you need another format,
you can replace the default formatter of this lib by calling:
```java
Logging.setFormatter(Formatter);
```
This formatter has to be of type `java.util.logging.Formatter`
# Ticketsystem
A `TicketSystem` object represents a connection definition to a space in an external ticket system.
Part of this is a base URL, and some system specific identifier for e.g. a project on that system.
If the access needs some kind of authentication,
this has to be configured in this object on creation.
## create new instance
The constructor of `TicketSystem` is not publicly callable.
Acquiring an instance has to be done either by passing an URI or using the builder mechanism.
### by URI
This library has a global definition, for how an URI has to start.
The end of it is defined by each system specific implementation.
!!! info "global URI definition"
`unifiedticketing:<system identifier>:<additional information>`
_For system specific details look into the systems part of this docs_
To instantiate a `TicketSystem` from an URI, call the static method `fromUri(String)`:
```java
TicketSystem ts = TicketSystem.fromUri(<uri>);
```
!!! warning
If the given String does not match the criteria for a valid URI,
or the system identifier is unknown, it throws an `AssertionException`
### by builder
The library has a builder mechanism defined to instantiate a new `TicketSystem` object, using fluent-api.
To start it, call the static method `fromBuilder()`.
The returning object gives you all supported systems as choice,
which then brings you to the system specific builder mechanism.
At the end, you'll have to finish the building process with a call to `build()`.
!!! warning
If not all mandatory information where given before calling `build()`,
it will throw an `AssertionException`
## configuration options
You can set configurations of two kinds:
- simple boolean switches to enable/disable a behaviour/feature
- string values
To set a configuration, call the instance method `config`. It provides two signatures:
- `config(TicketSystem.ConfigurationOption, boolean)`
- `config(TicketSystem.ConfigurationOption, String)`
??? abstract "currently available configuration options"
| ConfigurationOption | type | default | description |
| :------------------- | :------ | :------ | :------------------------------------------------------------------------------------------------ |
| RETURN_NULL_ON_ERROR | boolean | `false` | If true, don't throw exception on error, but return null or the calling object, if in fluent-api. |
!!! warning
option `RETURN_NULL_ON_ERROR` is not fully implemented and tested yet.
Expect it still throws Exceptions sometimes, but returns null/self also sometimes.
## list tickets
Tickets are requested by using the filter mechanism.
To get _all_ Tickets just don't define any filters before calling `get()`.
They are returned as `List<Ticket>`.
??? warning "default pagination"
many systems have an implicit default pagination enabled,
which could block you from simply getting _all_ tickets.
you can ask the `TicketSystem` object, to tell you if it uses a default pagination:
```java
ts.hasDefaultPagination();
```
!!! info "assignees"
Tickets have a field of type `Assignee`.
These have final String fields,
where the system implementation puts values if supported.
!!! example "requesting tickets"
```java
ts.find()
.setPage(1)
.setPageSize(10)
.get();
```
You can add various filter options, before finally calling `get()`.
Each supported systems implementation is free to define special filter options.
These are only callable, if your variable has the dedicated child-type.
The globally available ones are listed below.
They are marked `single` or `multi` types.
A `single` typed filter will override previous values,
whereas `multi` typed ones will be added to the previous values.
| filter option method definition | type | description |
| :------------------------------- | :------- | :--------------------------------------------------------------------------------------------------------------- |
| `isClosed()` | `single` | returns only tickets closed, or _considered_ closed by the system specific implementation |
| `isOpen()` | `single` | returns only tickets open, or _considered_ open by the system specific implementation |
| `setPage(int)` | `single` | page number of pagination to request |
| `setPageSize(int)` | `single` | page size of request to paginate with |
| `withAssigneeId(String)` | `multi` | tickets with given assigneeId as assignee. <br/> On multiple calls ticket must have _ALL_ given ids as assignees |
| `withAssigneeName(String)` | `multi` | same as assigneeId above |
| `withDescriptionContain(String)` | `single` | substring the description has to contain |
| `withDescriptionMatch(String)` | `single` | fully qualified regular expression, the whole description has to match |
| `withId(String)` | `multi` | id a ticket must have. <br/> multiple calls keeps tickets matching _any_ of the set ids |
| `withLabel(String)` | `multi` | label a ticket must have. <br/> Multiple calls a ticket must have _all_ given labels |
| `withTitleContain(String)` | `single` | substring the title has to contain |
| `withTitleMatch(String)` | `single` | fully qualified regular expression, the whole title has to match |
## get single ticket
To request a single ticket the method `getTicketById` is provided,
available with two signatures:
- `getTicketById(String)`
- `getTicketById(int)`
The integer variant is just for convenience, if you have the id as number.
It calls the String one with your number transformed.
!!! example
```java
Ticket t = ts.getTicketById("5");
```
## create new ticket
The creation of a new Ticket is started by calling `createTicket()`.
This starts another fluid-api chain,
in which you can set everything your new ticket should contain.
The chain finishes calling `create()` at the end.
!!! warning
if a mandatory information for ticket creation is not given,
prior calling `create()`,
an `AssertionException` is thrown.
!!! example
```java
Ticket t = ts.createTicket()
.title("some title")
.create();
```
Global available setters for ticket creation are:
| method | additional information |
| :--------------------------------------------- | :---------------------------------------------------------------------- |
| `assignees(String[])` | array of assignee identifiers, check system specific part, what to pass |
| `description(String)` | |
| `labels(Set<String>)` <br/> `labels(String[])` | |
| `title(String)` | |
# Ticket
A ticket instance represents one ticket of the connected space.
## get/create ticket
The class is not publicly constructable,
as it always belongs to a `TicketSystem` object.
Getting an instance for a existing ticket,
or creating a new one,
is done from a `TicketSystem` instance.
[->For details see here <-](./ticket-system.md)
## reading data
There are various getters available.
Below the globally defined ones are listed,
each system specific implementation is free to provide additional ones.
!!! warning
systems are not guaranteed to support all data,
a global getter is defined for.
Therefore it can happen, you get an null value back
or exception thrown in this case.
| method | return |
| :----------------- | :-------------------- |
| `getAssignees()` | `Set<TicketAssignee>` |
| `getDescription()` | `String` |
| `getId()` | `String` |
| `getLabels()` | `Set<String>` |
| `getParent()` | `TicketSystem` |
| `getTitle()` | `String` |
| `isOpen()` | `boolean` |
## changing data
You can change data of supported fields through dedicated setters.
Every changed field is flagged as changed.
To persist the changes to the connected space,
you need to call `save()`.
This forms an update request for all flagged fields
and returns a new instance of `Ticket`,
formed off the answer from the connected system.
Below the globally defined setters are listed,
each system implementation is free to offer more.
| method | description |
| :-------------------------------------------------- | :-------------------------------------------------------------- |
| `addAssignee(String)`<br/>`removeAssignee(String)` | adds/removes assignee, check system specific part, what to pass |
| `addLabel(String)` | add label |
| `clearAssignees()` | remove all assignees |
| `open()`<br/>`close()` | opens/closes the ticket |
| `setDescription(String)` | |
| `setLabels(Set<String>)`<br/>`setLabels(String[])` | replace all labels with the passed ones |
| `setTitle(String)` | |
# Exceptions
The library defines its own set of exceptions.
Throughout the whole library, these are used.
They are located in the package `de.hftstuttgart.unifiedticketing.exceptions`.
Base for all others is `UnifiedticketingException`,
which extends `RuntimeException`.
Every other custom exception extends this exception.
This provides you with the ability,
to catch a specific or all library related problems.
Both without catching exceptions of your own code,
that happen to be in the same try block.
## Possible Exceptions
- `AssertionException`
- `DeserializationException`
- `HttpRequestException`
- `HttpResponseException`
- `SerializationException`
- `UnsupportedFunctionException`
# Java-API
This is the API-Documentation,
holding detailed information for each publicly available action you can do with each class.
Below the structure of the library is explained and some generic examples are shown.
## library structure
The library is structured into three packages:
1. `de.hftstuttgart.unifiedticketing.core`
1. `de.hftstuttgart.unifiedticketing.exceptions`
1. `de.hftstuttgart.unifiedticketing.systems`
The core package contains the classes defining the generic API,
to be implemented by each supported system.
The systems package contains a sub-package per supported system,
which then holds the implementations of the core classes.
The exceptions package has some own exception types,
used throughout the lib.
For each package, this documentation provides a chapter in the navigation on the left.
## examples
!!! info
These examples are not specific to a certain system.
Therefore they can contain placeholders for system specific parts.
To get these parts, you'll have to go to the dedicated API-docs part.
??? example "create TicketSystem instance"
__from builder:__
```java
TicketSystem ts = TicketSystem.fromBuilder()
.<methods specifying connection details>
.build();
```
__from uri:__
```java
TicketSystem ts = TicketSystem.fromUri(<uri>);
```
??? example "searching tickets"
__without filters:__
```java
ts.find().get();
```
__only open tickets and explicit pagination:__
```java
ts.find()
.isOpen()
.setPage(1)
.setPageSize(10)
.get();
```
??? warning "default pagination"
many systems have an implicit default pagination enabled,
which could block you from simply getting _all_ tickets.
!!! tip
you can ask the `TicketSystem` object, to tell you if it uses a default pagination:
```java
ts.hasDefaultPagination();
```
# GitLab-Ticketsystem
This documents GitLab specific things for the integration to this library.
For the global parts, see in the [core sections Ticketsystem site][core-ts].
## create new instance
A `GitlabTicketSystem` object is always referencing a specific GitLab project.
Mandatory information to instantiate it, are the GitLab instances base url and the projects id.
For public accessible actions, it can be used anonymously.
Other operations have to be authenticated using an api-key.
For all calls the GitLab api V4 is used (at the time of writing this, the only available one).
If not configured otherwise, connections will use https.
### by URI
```java
// recommended option
GitlabTicketSystem ts = (GitlabTicketSystem) TicketSystem.fromUri("<uri>");
// second possible declaration
TicketSystem<
GitlabTicket,
GitlabTicketSystem,
GitlabTicketBuilder,
GitlabFilter
> gl = TicketSystem.fromUri("<uri>");
```
The first declaration option uses the child-class from the GitLab implementation directly.
The second option uses the parent-class instead,
defining the different implementations with Generics.
Use the first option, if you're sure you want to use GitLab no matter what.
This way you can see GitLab specific methods,
whereas the second option keeps you limited on the global defined ones.
If you may switch ticket systems in the future,
the second option forces you to not use system specific stuff, making a transition easier.
The URI to pass has to match this format:
```
unifiedticketing:gitlab:[http://|https://]<base url>:<project id>[:api key]
```
- protocol for http/https is optional. If not given, https is used
- base url can contain also non-standard port hosted gitlab instances
- api key if authentication needed, otherwise anonymous access is used
??? example "URI examples"
- non-https anonymous, project 234, public gitlab
`unifiedticketing:gitlab:http://wwww.gitlab.com:234`
- implicit https authenticated, project 234, public gitlab
`unifiedticketing:gitlab:gitlab.com:234:1234896102765abcddbda324bb3a3`
- explicit https authenticated, project 42, fictional gitlab, non-standard port
`unifiedticketing:gitlab:https://gitlab.example.org:8080:42:1234896102765abcddbda324bb3a3`
### by builder
For instantiation from the builder,
mostly the same like building by URI applies.
!!! warning
Passing the base url ensure the protocol (http or https) is removed
??? example "builder examples"
- non-https anonymous, project 234, public gitlab
```java
GitlabTicketSystem ts = TicketSystem.fromBuilder()
.gitlab()
.withBaseUrl("www.gitlab.com")
.withHttp()
.withProjectId(234) // String and int variant available
.build();
```
- implicit https authenticated, project 234, public gitlab
```java
GitlabTicketSystem ts = TicketSystem.fromBuilder()
.gitlab()
.withBaseUrl("gitlab.com")
.withProjectId("234")
.withApiKey("1234896102765abcddbda324bb3a3")
.build();
```
- explicit https authenticated, project 42, fictional gitlab, non-standard port
```java
GitlabTicketSystem ts = TicketSystem.fromBuilder()
.gitlab()
.withBaseUrl("gitlab.example.org:8080")
.withHttps()
.withProjectId(42)
.withApiKey("1234896102765abcddbda324bb3a3")
.build();
```
## list tickets
!!! warning "default pagination"
GitLab has a default pagination on _20_ elements per page.
Highest possible value is _100_ elements. [[Link]][gl-api-docs-pagination]
!!! warning "mutual exclusive filters"
if you filter for assignees, you can only use either assignee-ids or their usernames. [[Link]][gl-api-docs-issues-list]
!!! info "assignees"
GitLab implementation supports the fields `id`, `username` and `full name`.
If ticket has no assignee, assignee field stays `null`.
From the filters shown on the global `TicketSystem` docs,
the ones for regex-matching are not natively supported by the GitLab API.
If such a filter is used,
this get's done locally after receiving the response from GitLab,
before handing it to the user.
## get single ticket
!!! info
This uses the list mechanism internally,
with the given issue-id as filter and just unwraps it from the received list.
## create new ticket
For a new GitLab ticket a title is mandatory.
Everything else is optional.
!!! example "minimal example"
```java
GitlabTicket = ts.createTicket()
.title("my new ticket")
.create();
```
Setting an assignee, has to be by user-id. Setting by user name is not supported.
[core-ts]: ../../core/ticket-system.md
[gl-api-docs-issues-list]: https://docs.gitlab.com/ee/api/issues.html#list-issues
[gl-api-docs-pagination]: https://docs.gitlab.com/ee/api/#pagination
# Gitlab-Ticket
!!! todo
write usage api docs
# Machine Setup
## General
### Git
The whole project is a Git repository,
to do anything you'll need to have Git setup on your machine.
!!! tip
We highly recommend you use Git directly typing commands to your terminal,
instead of relying on any GUI tools, IDE integration's whatsoever.
They make life easy, sure.
But do you know, what exact calls they do under the hood?
#### Installation
=== "*nix"
Should be part of all major systems default software distribution path.
=== "Windows"
!!! warning "disclaimer"
This is for people having not much experience.
If you e.g. use Cygwin, you could just install Git from there and ignore this^^
- Download Git: [Git-SCM][gitscm-win]
- Installation recommendations:
- opt-out from `Enable Git Credential Manager`
_We had bad experiences with it, storing wrong passwords an no one knew where to remove them as none of the pros used this thing^^_
#### Configuration
After installing Git, you'll have to set a few information,
in order to be able to work with it.
Mandatory is your username and email.
Globally set your most used username and email address you'll be using.
```bash
git config --global user.name "<username>"
git config --global user.email "<email address>"
```
!!! attention
If your global email is different from the one for this projects GitLab,
make sure to either add your main email address as committer email in your profile,
or change the git config for this project individually after cloning it.
#### Clone project
As of writing, the project can be accessed with HTTPS and SSH.
If this is not working,
check the repository from the link in the upper right corner of the page.
(Maybe the path changed and someone forgot to update this part of the docs)
=== "SSH"
```bash
git clone git@transfer.hft-stuttgart.de:9Lukas5/unified-ticketing.git
```
=== "HTTPS"
```bash
git clone https://transfer.hft-stuttgart.de/gitlab/9Lukas5/unified-ticketing.git
```
## Java Development
The Java library is development with OpenJDK 8 and Maven 3.6
### Requirements
- Git setup
- OpenJDK 1.8.0+
- Maven 3.6.0+
### Installation
#### OpenJDK
=== "*nix"
Check your specific systems package names.
__Ubuntu 20.04__:
```bash
sudo apt install openjdk-11-jdk
```
=== "Windows"
- download OpenJDK: [jdk.java.net][java-net]
- extract archive to permanent place
- create/update system environment variable `JAVA_HOME`,
to the path you extracted OpenJDK to.
- update `PATH` variable including the `bin` folder of your newly placed OpenJDK
- check with a terminal that `java --version` works
#### Maven
=== "*nix"
Same as before, check your specific systems package names.
__Ubuntu:__
```bash
sudo apt install maven
```
=== "Windows"
- download Maven binary: [maven.apache.org][maven-dl]
- extract archive to permanent place
- create/update system environment variable `MAVEN_HOME`,
to the path you extracted Maven to.
- update `PATH` including `bin` folder of maven home
- check that `mvn --version` works
### Import project to IDE
Following is described, how to import the maven project to your IDE.
I really hope, you're familiar enough to know how to use your IDE,
so this is a just-in-case service.
Also, this does not claim to be up-to-date/correct/cover all available IDE's.
You are invited to contribute to this list below
if you're IDE of choice is not listed yet or has some faults.
=== "IntelliJ IDEA"
- Select `Open or Import`
- Choose the folder you cloned the project into
## MkDocs (Documentation)
This project is documented using MkDocs Material.
It means we write Markdown files, organize them in a configuration file
and MkDocs generates a static Website out of it we host and you are reading right now.
### Requirements
- Python 3.5+
- pip
- Python virtual environment _(optional)_
### Installation
=== "*nix"
check system specific names and commands, below are some examples collected over time:
__Ubuntu:__
```bash
sudo apt install python3 python3-pip python3-venv
```
=== "Windows"
- download & install Python: [python.org][python-win-dl] (use executable installer version)
- open a terminal inside the project's root directory
- _optional:_ create & activate new virtual environment:
=== "*unix"
```bash
python3 -m venv env
. ./env/bin/activate
```
=== "Windows"
=== "CMD"
```cmd
python -m venv env
env\scripts\activate.bat
```
=== "Power Shell"
!!! attention "execution policy"
You may have to first set the correct execution policy,
before able to run the activation script.
```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```
```powershell
python -m venv env
env\scripts\Activate.ps1
```
- install all required packages:
```
pip install -Ur requirements.txt
```
### Usage
To work on the documentation, the mkdocs development server has to run.
On start it tells you on which port it runs (default is `8000`).
Open the Browser on the shown URL and edit the Markdown files.
On saving changes your Browser should automatically reload the page.
- _optional:_ enter python virtual environment
=== "*unix"
```bash
. ./env/bin/activate
```
=== "Windows"
=== "CMD"
```cmd
env\scripts\activate.bat
```
=== "Power Shell"
!!! attention "execution policy"
You may have to first set the correct execution policy,
before able to run the activation script.
```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
```
```powershell
env\scripts\Activate.ps1
```
- start MkDocs development server:
```bash
mkdocs serve
```
- work
- kill dev server by pressing `ctrl+c`
- _optional:_ exit python virtual environment:
```bash
deactivate
```
### Editor/IDE
You don't need any special IDE, as a bare minimum a text editor would work (even vim is possible).
__But don't you dare to use this crappy Microsoft built in editor!__
Practically it's very supportive of course,
to use a IDE that has good markdown support in terms of code-highlighting,
table formatting, etc.
Below are collected tips for different IDE's.
=== "VSCode"
recommended extensions:
_these are the extension id's searchable from vscode_
- `editorconfig.editorconfig`
- `waderyan.gitblame`
- `yzhang.markdown-all-in-one`
- `jebbs.plantuml`
[gitscm-win]: https://git-scm.com/download/win
[java-net]: https://jdk.java.net/
[maven-dl]: https://maven.apache.org/download.cgi
[python-win-dl]: https://www.python.org/downloads/windows/
# Styleguide
If not explicitly stated otherwise,
the following takes effect for all files:
- character encoding __utf-8__
- line endings with __lf__
- indentation with __4 spaces__
- final new line
In general,
it's a good idea to have a look into the yet existing code,
to check how certain things were done up to now.
## Java
The most important thing to say here is: opening braces are written into the next line.
_(more to come if s.o. does s.th. differently I didn't think of)_
## Markdown
most important bullet points:
- leave always an empty line above and beneath of lists.
It may render fine in normal markdown viewers,
but not in the MkDocs Pages docs (like this)
??? example inline end
__bad:__
```markdown
some text
- bullet point
- another item
more normal text
```
__good:__
```markdown
some text
- bullet point
- another item
more normal text
```
- if you insert a link,
__always__ write the link to the bottom of the file with an alias,
ordered alphabetically.
??? example
__bad:__
```markdown
Look [here](https://www.coolsite.not/with/ultra/long/url) to read more about this.
```
__good:__
```markdown
Look [here][coolsite] to read more about this.
[coolsite]: https://www.coolsite.not/with/ultra/long/url
```
- split long sentences on punctuation and connection words like `and` & `or`.
It will have the same appearance in viewers,
but be much easier to maintain on code side.
??? example
__bad__:
```markdown
Some sentence that is very long, therefore it has punctuation and has absolutely no sense at all.
```
> Some sentence that is very long, therefore it has punctuation and has absolutely no sense at all.
__good:__
```markdown
Some sentence that is very long,
therefore it has punctuation
and has absolutely no sense at all.
```
> Some sentence that is very long,
> therefore it has punctuation
> and has absolutely no sense at all.
- tables have to be formatted in code
??? example
__bad:__
```markdown
||column 1|
|:-|:-|
|__row 1__|cell 1|
```
__good:__
```markdown
| | column 1 |
| :-------- | :------- |
| __row 1__ | cell 1 |
```
# Library UML-Diagram
This is the full UML-diagram for the Unified-Ticketing library.
!!! tip
You most likely can't read anything embedded to the site,
please open the graphics-URL in an extra tab and zoom in.
```plantuml
abstract Filter {
__ generics definition __
T extends Ticket
F extends Filter
==
- {static} logger: Logger
# {static} addToSet(\n\tfilterName: FilterNames,\n\tmap: Map<String, Object>,\n\tnewValue: String\n)
__ instance definition __
# setFilters: Map<String, Object>
+ constructor()
+ withAssigneeId(id: String): F
+ withAssigneeName(name: String): F
+ withDescriptionContain(substring: String): F
+ withDescriptionMatch(regex: String): F
+ withId(id: string): F
+ isOpen(): F
+ isClosed(): F
+ withLabel(label: String): F
+ setPage(page: int): F
+ setPageSize(size: int): F
+ withTitleContain(substring: String): F
+ withTitleMatch(regex: String): F
+ {abstract} get(): List<T>
}
enum FilterNames {
+ ASSIGNEEID
+ ASSIGNEENAME
+ DESCRIPTION_CONTAIN
+ DESCRIPTION_MATCH
+ IDS
+ LABELS
+ OPEN
+ PAGE
+ PAGINATION
+ TITLE_CONTAINS
+ TITLE_MATCH
}
class Logging {
- {static} handler: Handler
- {static} mainLogger: Logger
- {static} logger: Logger
+ {static} getLogger(name: String): Logger
+ {static} setFormatter(formatter: Formatter): void
+ {static} setLevel(level: Level): void
+ {static} test(message: String): void
}
class RegisteredSystems <<(S, orange)>> {
- {static} logger: Logger
- {static} instance: RegisteredSystems
# {static} getInstance(): RegisteredSystems
__ instance definition __
+ constructor()
+ gitlab(): GitlabTicketSystemBuilder
}
abstract Ticket {
__ generics definition __
T \t extends Ticket
TS \t extends TicketSystem
==
- {static} logger: Logger
__ instance definition __
# assignees: Set<TicketAssignee>
# description: String
# id: String
# labels: Set<String>
# open: boolean
# title: String
# updatedFields: Set<String>
# parent: TS
# constructor(parent: TS)
+ getAssignees(): Set<? extends TicketAssignee>
+ {abstract} addAssignee(identifier: String): T
+ clearAssignees(): T
+ {asbtract} removeAssignee(identifier: String): T
+ getDescription(): String
+ setDescription(description: String): T
+ getId(): String
+ addLabel(label: String): T
+ getLabels(): Set<String>
+ setLabels(labels: Set<String>): T
+ setLabels(labels: String[]): T
+ isOpen(): boolean
+ open(): T
+ close(): T
+ getParent(): TS
+ getTitle(): String
+ setTitle(title: String): T
+ {abstract} save(): T
+ equals(o: Object): boolean
+ hashCode(): int
+ toString(): String
}
enum FieldNames {
+ ASSIGNEES
+ DESCRIPTION
+ ID
+ LABELS
+ OPEN
+ TITLE
}
abstract TicketAssignee {
+ email: String
+ id: String
+ fullName: String
+ username: String
# constructor(\n\temail: String,\n\tid: String,\n\tusername: String,\n\tfullName: String\n)
+ equals(o: Object): boolean
+ hashCode(): int
+ toString(): String
}
abstract TicketBuilder {
__ generics definition __
B \t extends TicketBuilder
T \t extends Ticket
TS \t extends TicketSystem
==
- {static} logger: Logger
__ instance definition __
# assignees: Set<String>
# description: String
# labels: Set<String>
# title: String
# constructor(parent: TS)
+ assignees(identifiers: String...): B
+ description(description: String): B
+ labels(labels: Set<String>): B
+ labels(labels: String[]): B
+ title(title: String): B
+ {abstract} create(): T
}
abstract TicketSystem {
__ generics definition __
B \t extends TicketBuilder
F \t extends Filter
T \t extends Ticket
TS \t extends TicketSystem
==
- {static} logger: Logger
+ {static} fromBuilder(): RegisteredSystems
+ {static} fromUri(uri: String): TicketSystem
__ instance definition __
+ apiKey: String
+ baseUrl: String
+ password: String
+ username: String
# configuration: Map<String, String>
+ constructor()
+ config(option: ConfigurationOptions, value: String): TS
+ config(option: ConfigurationOptions, value: boolean): TS
+ getConfigTrue(option: ConfigurationOptions): boolean
+ getConfigValue(option: ConfigurationOptions): Object
+ {abstract} createTicket(): TB
+ {abstract} find(): F
+ {abstract} getTicketById(id: String): T
+ {abstract} getTicketById(id: int): T
+ {abstract} hasAssigneeSupport(): boolean
+ {abstract} hasDefaultPagination(): boolean
+ {abstract} hasLabelSupport(): boolean
+ {abstract} hasPaginationSupport(): boolean
+ {abstract} hasReturnNullOnErrorSupport(): boolean
}
enum ConfigurationOptions {
+ RETURN_NULL_ON_ERROR
}
abstract TicketSystemBuilder {
__ generics definition __
B \t extends TicketBuilder
TS \t extends TicketSystem
==
- {static} logger: Logger
__ instance definition __
+ baseUrl: String
+ withBaseUrl(url: String): B
+ {abstract} build(): TS
}
' package connections
Filter *-- FilterNames: "inner class"
TicketSystem --> RegisteredSystems: "fromBuilder()"
TicketSystem *-- ConfigurationOptions: "inner class"
Ticket *-- FieldNames: "inner class"
Ticket --> Ticket: "save()"
Ticket o-- TicketSystem: "parent"
Ticket o-- TicketAssignee
TicketSystem --> TicketBuilder: "createTicket()"
TicketBuilder --> Ticket: "create()"
class AssertionException {
+ constructor()
+ constructor(msg: String)
+ constructor(msg: String, cause: Throwable)
+ constructor(suppressed: Throwable)
}
class DeserializationException {
+ constructor()
+ constructor(msg: String)
+ constructor(msg: String, cause: Throwable)
+ constructor(suppressed: Throwable)
}
class HttpRequestException {
+ constructor()
+ constructor(msg: String)
+ constructor(msg: String, cause: Throwable)
+ constructor(suppressed: Throwable)
}
class HttpResponseException {
+ constructor()
+ constructor(msg: String)
+ constructor(msg: String, cause: Throwable)
+ constructor(suppressed: Throwable)
}
class SerializationException {
+ constructor()
+ constructor(msg: String)
+ constructor(msg: String, cause: Throwable)
+ constructor(suppressed: Throwable)
}
class UnifiedticketingException {
+ constructor()
+ constructor(msg: String)
+ constructor(msg: String, cause: Throwable)
+ constructor(suppressed: Throwable)
}
class UnsupportedFunctionException {
+ constructor()
+ constructor(msg: String)
+ constructor(msg: String, cause: Throwable)
+ constructor(suppressed: Throwable)
}
AssertionException -[hidden]down- DeserializationException
DeserializationException -[hidden]down- HttpRequestException
HttpRequestException -[hidden]down- HttpResponseException
HttpResponseException -[hidden]down- SerializationException
SerializationException -[hidden]down- UnifiedticketingException
UnifiedticketingException -[hidden]down- UnsupportedFunctionException
class GitlabFilter {
- {static} logger: Logger
__ instance definition __
# parent: GitlabTicketSystem
# getHttpClient(): OkHttpClient
+ get(): List<GitlabTicket>
}
class GitlabTicket {
- {static} logger: Logger
# {static} fromTicketResponse(\n\tparent: GitlabTicketSystem,\n\tresponse: GitlabTicketResponse\n): GitlabTicket
__ instance definition __
# constructor(parent: GitLabTicketSystem)
+ addAssignee(userId: String): GitlabTicket
+ addAssignee(userId: int): GitlabTicket
+ removeAssignee(userId: String): GitlabTicket
+ removeAssignee(userId: int): GitlabTicket
+ save(): GitlabTicket
+ deepEquals(o: Object): boolean
}
class GitlabTicketAssignee {
# constructor(id: int, fullName: String, username: String)
}
class GitlabTicketBuilder {
- {static} logger: Logger
__ instant definition __
# constructor(parent: GitlabTicketSystem)
+ assignees(identifiers: String...): GitlabTicketBuilder
+ assignees(identifiers: int...): GitlabTicketBuilder
# getHttpClient(): OkHttpClient
+ create(): GitlabTicket
}
class GitlabTicketResponse {
+ assignees: List<Assignee>
+ description: String
+ iid: int
+ labels: Set<String>
+ state: String
+ title: String
# constructor()
}
class Assignee {
+ id: int
+ name: String
+ username: String
}
class GitlabTicketSystem {
- {static} logger: Logger
+ {static} fromUri(uri: String): GitlabTicketSystem
__ instance definition __
# constructor()
+ createTicket(): GitlabTicketBuilder
+ find(): GitlabFilter
+ getTicketById(id: String): GitlabTicket
+ hasAssigneeSupport(): boolean
+ hasDefaultPagination(): boolean
+ hasLabelSupport(): boolean
+ hasPaginationSupport(): boolean
+ hasReturnNullOnErrorSupport(): boolean
}
class GitlabTicketSystemBuilder {
- {static} logger: Logger
__ instance definition __
# apiKey: String
# apiVersion: String
# projectId: int
# https: boolean
+ constructor()
+ withApiKey(apiKey: String): GitlabTicketSystemBuilder
+ withHttp(): GitlabTicketSystemBuilder
+ withHttps(): GitlabTicketSystemBuilder
+ withProjectId(projectId: int): GitlabTicketSystemBuilder
+ withProjectId(projectId: String): GitlabTicketSystemBuilder
+ build(): GitlabTicketSystem
}
' package connections
GitlabFilter o-- GitlabTicketSystem: "parent"
GitlabTicketResponse o- Assignee: "inner class"
GitlabTicketSystemBuilder --> GitlabTicketSystem: "build()"
GitlabTicketSystem --> GitlabTicketBuilder: "createTicket()"
GitlabTicketBuilder --> GitlabTicket: "create()"
GitlabTicketSystem *-- GitlabTicketResponse
' core package connections
GitlabFilter --|> Filter
GitlabTicket --|> Ticket
GitlabTicketAssignee --|> TicketAssignee
GitlabTicketBuilder --|> TicketBuilder
GitlabTicketSystem --|> TicketSystem
GitlabTicketSystemBuilder --|> TicketSystemBuilder
RegisteredSystems --> GitlabTicketSystemBuilder: "gitlab()"
```
# Workflow
This project is maintained in a GitLab repository,
hosted on the [M4Lab's][m4lab] [Transfer-Portal][tr-portal] [GitLab instance][tr-gitlab].
Source code for the maven Java library and the markdown docs are managed together in a [single repository][lib-repo-home].
This page tells you for the different parts of this project,
how to do it.
The following applies project wide:
- language is english: code, comments, documentation, tickets, discussion, ...
## Git/GitLab
!!! danger "disclaimer"
There are a few rules any developer HAS TO follow,
in order to get any chance his merge-requests will be accepted.
This project is managed using Git, hosted on the universities GitLab instance.
### Issues/Merge Requests handling
Project management happens in the GitLab repositories issue board.
First of all, make sure for everything you do a ticket exists.
Extending this, also check regularly your tickets are:
- assigned to you
- are in the correct column
There are three columns as of writing this documentation: `To Do`, `Doing` and `review`.
Planning something, a new ticket get's created and placed into the `To Do` column.
If you start to work on this ticket
- move it to `Doing`
- create a new feature branch from the master
- named like: `<ticket-id>-<some-descriptive-name>`
- push the new branch
- create a new merge request
- title starting with `wip: `
- description containing `closes #<ticket-id>`
- assign it to you (even if you're not able to merge)
- start working
After implementing your change
- move your ticket to `review` column
- change your merge requests labels: remove `Doing` and add `review`
- change assignee to someone to review your work
### Branch Names
Branches should always start with the number of the related ticket, followed by a dash.
This way the branch get's automatically shown in the ticket.
### Committing
!!! tip "golden rules"
- __many small__ > ~~few big~~ commits
- unrelated changes in separate commit
We encourage everyone to make heavy use of `git status` and `git diff`.
Please always check before committing, what exactly you have changed.
What we won't accept:
- whole file content removed and added back
This most likely happens if you make great use of auto format,
messing everything up.
Another one error source is something changed line endings.
- empty lines containing spaces, or trailing spaces on line end (without technical reason)
No big deal to fix, but time waste if we have to block your MR because of this.
- multiple changes in one commit, that have nothing to do with each other
Great you have fixed a typo in a comment at the other end of the file.
What do you think happens, if five people fix the same typo in their commits?
We would have four merge conflicts, needing manual work to fix this mess.
So create a new ticket for what you found,
instead of just doing it as it doesn't hurt.
It hurts, trust me.
- commit messages not following conventional commits specification
!!! tip "view diff of not yet tracked files"
If you want to check a new file for flaws, sure `git diff` doesn't show anything.
But you can add the file and before committing check with `git diff --cached`.
!!! tip "add only parts of changes in a file"
As we want to have more small commits, than one single big one,
it can be useful to split your changes into multiple commits,
even inside the same file.
You can use `git add -p [path|file]`.
This allows you to exactly decide which lines of your changes should be staged
, before committing.
#### Commit Messages
We use the [conventional commits][conv-comm] specification in this repository,
to auto generate a changelog with [conventional-changelog][conv-change].
### Protected Branches/Tags
The repository has the master branch and release tags (format `^[0-9]+\.[0-9]+\.[0-9]+$`)
protected to project maintainers.
### Branching-Model
We use a branch per feature based branching model.
Base and target to merge is always latest `master`.
## Java Development
First step before changing the source code,
would be to check if there's something going on yet,
or this topic possibly was discussed yet and abandoned for some reason.
For this, check the [defined GitLab workflow](#gitgitlab).
This library is a maven project.
In order to contribute to it,
make sure your machine is setup according to the [setup guide][setup-guide].
### Change an existing functionality
- check with the [source structure below](#source-code-structure),
where to find the part you want to change.
- make your changes
- make sure all unittests still pass
- commit your changes following the Git/GitLab workflow defined.
### Add integration for new system
- familiarize with the [code structure](#source-code-structure)
- create a new package under `de.hftstuttgart.unifiedticketing.systems`,
identifying the new system
- prepare to implement all abstract classes from the `core` package
- class names have to be the same as the `core` ones,
prefixed with the identifier you named your package after
- create unittests for the planned integration,
either by putting a `fail();` into each testmethod,
or really writing tests first for TDD
- commit & push this skeleton-like state
- create WIP merge-request
- implement
### Source code structure
After cloning the repository,
you'll have to open/import an existing maven project in your IDE of choice.
There WILL NOT be any configuration files for a specific IDE committed into the code.
In regards to the maven project parts,
only the `pom.xml` and the contents under `/src` are committed.
The main package for everything of this library is `de.hftstuttgart.unifiedticketing`.
Beneath it is split into three sub-packages:
| subpackage | content |
| :----------- | :------------------------------------------------------------------------- |
| `core` | root classes defining lib core, generic public interface |
| `exceptions` | library own exceptions |
| `systems` | subpackage per system integration, implementation of abstract core-classes |
__core:__
The majority of the classes are abstract ones.
They define the generic interface available to the user of this lib.
Logic identical for all system integrations are found in these classes.
Only few methods are really declared abstract,
in most cases it's the part calling the external system.
__exceptions:__
The library defines its own exceptions, based on `RuntimeException`.
If you throw any exception,
__only__ use exceptions from this package,
never anything else.
If you miss an exception you'd like to throw,
add a new one that extends `UnifiedTicketingException`.
__systems:__
Each system that's integrated into this lib,
will have a sub-package under `de.hftstuttgart.unifiedticketing.systems`.
In this very own package every abstract class from the `core`-package has to be implemented.
Apart from that,
every system can define as many helper classes
or additional sub-packages.
### Unittests
The logical code is structured under `/src/main/java`.
For unittests the same package structure is created under `/src/test/java`.
Tests are written using the `jupiter-engine` from JUnit5,
combined with `Mockito3`.
- test-classes are named identical to the class under test,
suffixed with `Test`
- test-classes have to be in the same package than the class under test
What has to be tested:
- constructors
- getters/setters
- calculations/transformations/...
- fluent-api to return the same instance
- serializing request data before request
- failing web-request
- successful web-request & deserializing of received data
!!! warning "testing external resources"
All tests _have to_ run offline.
To test external resources,
e.g. a web-request,
use Mockito to block the code under test to really access the external resource
and transparently return your mock answer expected for the assertions following.
??? tip "test abstract classes"
To test non-abstract code of abstract classes,
you can instantiate a mock of it through Mockito,
set to call the real methods:
```java
instance = mock(ClassUnderTest.class,
withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS));
```
## Documentation
Documentation for the project is done entirely in Markdown
and rendered through MkDocs to a Website.
The repository global `README.md` just points to the documentation root,
which is `/docs/index.md`.
The same applies for the `CONTRIBUTING.md`.
`CHANGELOG.md` and `LICENSE.md` are kept with content in project root,
they are linked with a relative soft-link into the documentation folder.
Files related to the documentation:
| path | description |
| :----------------- | :---------------------------------------------- |
| `mkdocs.yml` | configuration file for mkdocs |
| `requirements.txt` | needed pypi packages to pass to pip for install |
| `/docs/**/*` | documentation files |
| `/docs/index.md` | Pages root |
With the aim to give the documentation an appealing appearance,
additional to the default markdown specification,
a few extensions are enabled in the mkdocs config.
Please have a look at them and make use of,
where it seems adequate.
Reference for them can be found in the [MkDocs-Material site][mat-mkdocs-ext-ref].
To make any kind of diagram, sketch or similar,
use [PlantUML][puml].
You have to surround the PlantUML code with code fences,
marked as `plantuml` as language identifier.
Then they will be rendered into an svg on pages creation.
!!! bug
I tried to put this into an collapsed example admonition,
but the PlantUML diagram get's always placed outside the admonition.
Same applies for a tabbed environment.
Example:
__code:__
``````
```plantuml
queue "write\nmarkdown" as write
queue "commit/push/\nmerge changes" as update
queue "pipeline\ntriggered" as cicd
queue "MkDocs\nMaterial" as MkDocsMat
queue "transpiled HTML\nonline" as deployed
write -right-> update
update -right-> cicd
cicd -right-> MkDocsMat
MkDocsMat -right-> deployed
```
``````
__result:__
```plantuml
queue "write\nmarkdown" as write
queue "commit/push/\nmerge changes" as update
queue "pipeline\ntriggered" as cicd
queue "MkDocs\nMaterial" as MkDocsMat
queue "transpiled HTML\nonline" as deployed
write -right-> update
update -right-> cicd
cicd -right-> MkDocsMat
MkDocsMat -right-> deployed
```
## Schedule a release
For releases this project uses [standard-version][std-ver].
This generates an automatic changelog based on the commits
and tags it.
The version of the library has three numbers
and has to follow the [semantic-version][semver] scheme.
Tags marking a release have the format `^[0-9]+\.[0-9]+\.[0-9]+$`.
Pushing these tags is restricted in the GitLab Repo settings
for maintainers only.
Pushing a release tag triggers the CI/CD release pipeline for the maven project.
## CI/CD
This project uses GitLab-CI pipelines for
- publishing the maven library to the repository own package registry
- deploying this pages documentation
These pipelines are defined in several files:
| file | content |
| :---------------------- | :------------------------------------------------------------ |
| `.gitlab-ci.yml` | root file, stages definition, include of other pipeline parts |
| `.gitlab-ci-maven.yml` | jobs regarding java library |
| `.gitlab-ci-mkdocs.yml` | jobs regarding pages documentation |
pipeline graph overview:
```plantuml
rectangle "compile" as stageCompile {
' maven
agent "maven:compile" as mvnCompile
' mkdocs
agent "mkdocs:compile" as mkdocsCompile
}
rectangle "test" as stageTest {
' maven
agent "maven:test" as mvnTest
' mkdocs
agent "none" as mkdocsTest
}
rectangle "deploy" as stageDeploy {
' maven
agent "maven:publish:master" as mvnPublMaster
agent "maven:publish:release" as mvnPublRelease
label "get's deployed with\n'devel' as version" as labelMvnPublMaster
label "get's deployed\nwith release version" as labelMvnPublRelease
' mkdocs
agent "mkdocs:bundle" as mkdocsBundle
agent "mkdocs:deploy" as mkdocsDeploy
label "creates tar archive as\ndownloadable job artifact" as labelMkdocsBundle
label "only triggered\non master branch" as labelMkdocsDeploy
}
' maven pipeline connections
mvnCompile -right-> mvnTest
mvnTest -right-> mvnPublMaster
mvnTest -right-> mvnPublRelease
mvnPublMaster -right- labelMvnPublMaster
mvnPublRelease -right- labelMvnPublRelease
' mkdocs pipeline connections
mkdocsCompile -right-> mkdocsTest
mkdocsTest -right-> mkdocsBundle
mkdocsTest -right-> mkdocsDeploy
mkdocsBundle -right- labelMkdocsBundle
mkdocsDeploy -right- labelMkdocsDeploy
' compile stage keep in place
mvnCompile --[hidden]down- mkdocsCompile
' test stage keep in place
mvnTest --[hidden]down-mkdocsTest
' deploy stage keep in place
mvnPublMaster -[hidden]down- mvnPublRelease
mvnPublRelease -[hidden]down- mkdocsBundle
mkdocsBundle -[hidden]down- mkdocsDeploy
```
These jobs are defined with the `needs` keyword,
to make them only depend on exactly the needed previous jobs.
This makes it possible to run docs and library pipelines in parallel,
without waiting that all jobs of a stage must have finished before entering the next stage.
With the `only` keyword the trigger of these pipelines is optimized,
to only run if necessary.
In general the final deployment jobs have configurations to run only on the secured branches/tags.
Additionally all jobs are configured to only run,
if changes on related files of this pipeline were made.
[conv-comm]: https://www.conventionalcommits.org/en/v1.0.0/
[conv-change]: https://github.com/conventional-changelog/conventional-changelog
[lib-repo-home]: https://transfer.hft-stuttgart.de/gitlab/9Lukas5/unified-ticketing/
[m4lab]: https://www.hft-stuttgart.de/forschung/innovative-hochschule-m4-lab
[mat-mkdocs-ext-ref]: https://squidfunk.github.io/mkdocs-material/reference/abbreviations/
[puml]: https://plantuml.com/
[setup-guide]: /404.html
[semver]: https://semver.org/
[std-ver]: https://github.com/conventional-changelog/standard-version
[tr-gitlab]: https://transfer.hft-stuttgart.de/gitlab/explore/projects
[tr-portal]: https://transfer.hft-stuttgart.de/
[![maven][badge-latest-tag]][project-packages]
[![pipeline state][badge-master-pipe]][project-pipelines]
[![latest dev pipeline][badge-latest-pipe]][project-pipelines]
[![License GPLv3][badge-gplv3]][gplv3]
[![Conventional Commits][badge-conventional-commits]][convcomm]
# Unified Ticketing
!!! Todo
This is the documentation for a Java-library called `unified-ticketing`.
This lib provides generic interfaces to interact with tickets
from different ticket systems out of your Java code.
This library is a maven artifact,
published under the [GNU GPL-v3][gplv3] license.
The source code is publicly available from the [transfer portal][transferportal] of the [University of Applied Sciences Stuttgart][hfthome].
## What to find where
The documentation is split in three main parts, navigable on the top-bar.
Each main-part gives you a navigation of it's content on the left.
On the right, the currents page table of contents is shown.
The icon in the upper left brings you from anywhere back to this start-page.
In the upper right you can go to the source code repository of this library and documentation.
Leftwards a full-text search is provided.
The part you are reading right now (__Home__), is the main website.
Following is a list, what each site contains:
- __Changelog:__ auto-generated changelog from the commits
- __Contributing:__
- Prerequisites to be able to be a contributor to this project
- __License:__ GNU GPLv3 content
- __Usage:__
- needed entries to get this library into your own maven-project
- some code-snippets showing the libs capabilites
The __API__ part holds a detailed description, for each publicly accessible class and their methods.
There is an own index file, explaining the package structure of the library
and how it is documented.
The __Developers Guide__ is the part, you have to lookup how to setup your machine and conventions and workflows used in this project,
if you decide to contribute to it.
## Maintainer
This library and it's development belongs to the University of Applied Sciences Stuttgart.
It was originally developed as bachelors-thesis by Lukas Wiest
[:material-email-send:{: style="color: black"}][9l5-hft-mail]
[:fontawesome-brands-twitter:{: style="color: #1DA1F2"}][9l5Twitter]
[:fontawesome-brands-github:{: style="color: black"}][9l5GH]
[:fontawesome-brands-gitlab:{: style="color: orange"}][9l5GL].
The guardian professor was Prof. Dr. Gero Lueckemeyer
[:material-email-send:{: style="color: black"}][lueck-hft-mail]
[:fontawesome-solid-university:{: style="color: black"}][lueck-hft-profile]
[:fontawesome-brands-github:{: style="color: black"}][lueck-GH]
[:fontawesome-brands-xing:{: style="color: teal"}][lueck-Xing].
He is the current maintainer for this project and the appropriate contact for questions, access request, etc.
[9l5-hft-mail]: mailto:62wilu1bif@hft-stuttgart.de
[9l5GH]: https://www.github.com/9Lukas5
[9l5GL]: https://www.gitlab.com/9Lukas5
[9l5Twitter]: https://twitter.com/9Lukas5
[badge-conventional-commits]: https://img.shields.io/badge/Conventional%20Commits-1.0.0-orange.svg
[badge-gplv3]: https://img.shields.io/badge/License-GPLv3-blue.svg
[badge-latest-pipe]: https://img.shields.io/badge/dynamic/json?color=lightgrey&label=latest%20dev%20pipeline&query=%24%5B0%5D.status&url=https%3A%2F%2Ftransfer.hft-stuttgart.de%2Fgitlab%2Fapi%2Fv4%2F%2Fprojects%2F154%2Fpipelines%3Fper_page%3D1
[badge-latest-tag]: https://img.shields.io/badge/dynamic/json?color=green&label=maven&query=%24%5B0%5D.name&url=https%3A%2F%2Ftransfer.hft-stuttgart.de%2Fgitlab%2Fapi%2Fv4%2Fprojects%2F154%2Frepository%2Ftags%3Fper_page%3D1
[badge-master-pipe]: https://transfer.hft-stuttgart.de/gitlab/9Lukas5/unified-ticketing/badges/master/pipeline.svg
[convcomm]: https://conventionalcommits.org
[gplv3]: unified-ticketing/license-softlink.md
[hfthome]: https://www.hft-stuttgart.de
[lueck-GH]: https://github.com/lueckemeyer
[lueck-hft-mail]: mailto:gero.lueckemeyer@hft-stuttgart.de
[lueck-hft-profile]: https://www.hft-stuttgart.de/p/gero-lueckemeyer
[lueck-Xing]: https://www.xing.com/profile/Gero_Lueckemeyer
[project-packages]: https://transfer.hft-stuttgart.de/gitlab/9Lukas5/unified-ticketing/-/packages
[project-pipelines]: https://transfer.hft-stuttgart.de/gitlab/9Lukas5/unified-ticketing/pipelines
[transferportal]: https://transfer.hft-stuttgart.de
../../CHANGELOG.md
\ No newline at end of file
# Contributing
To contribute to this project,
you primarily have to be a member of the University of Applied Sciences Stuttgart.
This project is hosted on our Transferportal's GitLab instance,
to which only HFT members have access to my knowledge.
Apart from that, for contributing to the library code itself,
you should be able to program Java and be familiar with maven.
For the documentation you need to have Python installed and know how to write Markdown.
For all contributions you need to use Git.
For details see the [Developer Guide][dev-guide].
There you can find information about getting ready,
what rules to follow
and more developers than user based documentation about the library.
[dev-guide]: ../developers-guide/machine-setup.md
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment