Skip to content

Commit

Permalink
Merge pull request #362 from catenax-ng/main
Browse files Browse the repository at this point in the history
chore: update user manual
  • Loading branch information
ds-mwesener authored Nov 24, 2023
2 parents 38bfc1f + cfa09f8 commit b275fed
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Changed
- Fixed helm repository path for backend & frontend (wrong prefix)
- Updated user manual
- Autocomplete endpoints changed owner String type param to Owner for input validation and sql injection prevention
- Autocomplete endpoints repository uses now criteria api rather than native query
- Fixed several bugs in local filtering of the parts table
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
= Architecture and design patterns

== Module / Package structure

* Main domain name

** application
*** optional: subdomain name (in case of inheritance) → following same structure as main domain
*** mapper: holds the mapper of transforming domain models to response models
*** service: holds the interface for implementation in the domain package
*** rest: holds the controller for providing the api for the domain
** domain
*** optional: subdomain name (in case of inheritance)
*** model: holds the domain model
*** service: Implementation of the interface provided in the application package
*** repository: holds the interface for accessing the infrastructure package
** infrastructure
*** optional: subdomain name (in case of inheritance)
*** model: holds the technical entities
*** repository: holds the data access layer
**** e.g. JPARepository / Impl
** All models (Request / Response) used in the API should be saved in the tx-model project.
To be reusable for cucumber testing.
139 changes: 139 additions & 0 deletions docs/src/docs/arc42/cross-cutting/development-concepts.adoc
Original file line number Diff line number Diff line change
@@ -1 +1,140 @@
= Development concepts

== Build, test, deploy

TraceX is built using Maven and utilizes all the standard concepts of it.
Test execution is part of the build process and a minimum test coverage of 80% is enforced.

The project setup contains a multi-module Maven build.
Commonly used classes (like the TraceX data model) should be extracted into a separate submodule and reused across the project.
However, this is not a "one-size-fits-all" solution.
New submodules should be created with care and require a review by the team.

The Maven build alone only leads up to the JAR artifact of TraceX.
Do create Docker images, the Docker build feature is used.
This copies all resources into a builder image, builds the software and creates a final Docker image at the end that can then be deployed.

Although the Docker image can be deployed in various ways, the standard solution are the provided Helm charts, which describe the required components as well.

== Code generation

There are two methods of code generation in TraceX:

=== Lombok

The Lombok library is heavily used to generate boilerplate code (like Constructors, Getters, Setters, Builders...).
This way, code can be written faster and this boilerplate code is excluded from test coverage, which keeps the test base lean.

=== Swagger / OpenAPI

The API uses OpenAPI annotations to describe the endpoints with all necessary information.
The annotations are then used to automatically generate the OpenAPI specification file, which can be viewed in the Swagger UI that is deployed with the application.

The generated OpenAPI specification file is automatically compared to a fixed, stored version of it to avoid unwanted changes of the API.

== Migration

There currently is no data migration mechanism for TraceX.
In case the model of the persisted data (Jobs) changes, data is dropped and Jobs will need to be recreated.

== Configurability

TraceX utilizes the configuration mechanism provided by Spring Boot.
Configuration properties can be defined in the file `+src/main/resources/application.yml+`

Other profiles should be avoided.
Instead, the configuration can be overwritten using Spring's external configuration mechanism (see https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/boot-features-external-config.html).
The operator must have total control over the configuration of TraceX.

== Java Style Guide

We generally follow the link:https://google.github.io/styleguide/javaguide.html[Google Java Style Guide].

== API Guide

We generally follow the https://swagger.io/specification/

== Unit and Functional Testing

=== General Unit testing

* Code coverage >= 80%
* Writing methods which provide a response to be better testable (avoid void if feasible).
* Naming of unit tests are as follows:

image::https://raw.githubusercontent.com/eclipse-tractusx/traceability-foss/main/docs/src/images/arc42/user-guide/unit_test_naming.png[]

* Use given/when/then pattern for unit test structuring.
E.g:

image::https://raw.githubusercontent.com/eclipse-tractusx/traceability-foss/main/docs/src/images/arc42/user-guide/given_when_then_pattern.png[]

=== Integration Testing

Each public api should have at least two integration tests (positive / negative).
For integration testing, the `+tx-backend/src/main/resources/application-integration.yml+` is used.
Additionally, you need to have a local Docker environment running.
For this, we recommend link:https://rancherdesktop.io/[Rancher Dektop].

== Clean Code

We follow the rules and behaviour of: https://clean-code-developer.com/.

== Secure Coding standards

As there is no other guideline of C-X, we fix any Vulnerabilities, Exposures, Flaw detected by one of our SAST, DAST, Pentesting tools which is higher than "Very Low".

image::https://raw.githubusercontent.com/eclipse-tractusx/traceability-foss/main/docs/src/images/arc42/user-guide/vulnerability_level.png[]

== TRACE-X Technical class responsibilities

=== Controllers

* Have only one dependency to a facade or a service or a validator annotation
* Have no own logic
* Including the swagger documentation annotations
* For each controller exists and Integration Test class
* Uses a static mapper to transform a domain model into the response model
* Returns a ResponseEntity<T>

=== Response object

* Should be a public version of the domain object
* It is a result of the transformation which will be done in the facade
* Is not necessary if the domain object can be fully public
* Is not allowed to be implemented in a repository or a DAO

=== Facade

* Should have multiple service classes injected
* Can be implemented in a controller

=== ServiceImpl

* Responsible for retrieving data from storage
* Performs business logic
* Can be a http client
* Returns a jpaEntity → Domain Object
* Should only be implemented in a controller through an interface

=== Repository

* Represents an interface to the underlying repository implementation which uses then the spring repository

=== Domain Object

* Mapped from an entity or external data received
* Will be used as working model until it will be finally transformed to a response object or another domain which will be later on persisted

=== Config Object

* Should have the suffix Config at the end of the class
* Including beans which are automatically created by app startup

=== Constructing objects

* Using builder pattern
** Currently we are using the constructor to create objects in our application.
Main reason is probably to provide immutable objects.
** As the handling with big loaded constructors is not easy and error prune, I would recommend using the builder pattern to have a clear understanding about what we creating at the point of implementation.
* Using lombok for annotation processing
4 changes: 4 additions & 0 deletions docs/src/docs/arc42/cross-cutting/domain-concepts.adoc
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
= Domain concepts

== API Model

For detailed information about the API model, please refer to the link:https://catenax-ng.github.io/tx-traceability-foss/docs/api-specification/api-specification.html[API specification].
32 changes: 32 additions & 0 deletions docs/src/docs/arc42/cross-cutting/operational-concepts.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,35 @@

== Administration

=== Configuration

TraceX can be configured using two mechanisms:

==== application.yml

If you build TraceX yourself, you can modify the application.yml config that is shipped with TraceX.
This file contains all possible config entries for the application.
Once the Docker image has been built, these values can only be overwritten using the Spring external config mechanism (see https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/reference/html/boot-features-external-config.html), e.g. by mounting a config file in the right path or using environment variables.

==== Helm Chart

The most relevant config properties are exposed as environment variables and must be set in the Helm chart so the application can run at all.
Check the TraceX Helm chart in Git for all available variables.

== Scaling

If the number of consumers raises, TraceX can be scaled up by using more resources for the Deployment Pod.
Those resources can be used to utilize more parallel threads to handle Job execution.

== Clustering

TraceX can run in clustered mode, as each running job is only present in one pod at a time.
Note: as soon as a resume feature is implemented, this needs to be addressed here.

== Logging

Logs are being written directly to stdout and are picked up by the cluster management.

== Monitoring

Currently, there is on monitoring supported in TraceX.
41 changes: 41 additions & 0 deletions docs/src/docs/arc42/cross-cutting/under-the-hood.adoc
Original file line number Diff line number Diff line change
@@ -1,2 +1,43 @@
= "Under-the-hood" concepts

== Exception and error handling

There are two types of potential errors in TraceX:

=== Technical errors

Technical errors occur when there is a problem with the application itself, its configuration or directly connected infrastructure, e.g. the Postgres database.
Usually, the application cannot solve these problems by itself and requires some external support (manual work or automated recovery mechanisms, e.g. Kubernetes liveness probes).

These errors are printed mainly to the application log and are relevant for the healthchecks.

=== Functional errors

Functional errors occur when there is a problem with the data that is being processed or external systems are unavailable and data cannot be sent / fetched as required for the process.
While the system might not be able to provide the required function at that moment, it may work with a different dataset or as soon as the external systems recover.

=== Rules for exception handling

==== Throw or log, don't do both

When catching an exception, either log the exception and handle the problem or rethrow it, so it can be handled at a higher level of the code.
By doing both, an exception might be written to the log multiple times, which can be confusing.

==== Write own base exceptions for (internal) interfaces

By defining a common (checked) base exception for an interface, the caller is forced to handle potential errors, but can keep the logic simple.
On the other hand, you still have the possibility to derive various, meaningful exceptions for different error cases, which can then be thrown via the API.

Of course, when using only RuntimeExceptions, this is not necessary - but those can be overlooked quite easily, so be careful there.

==== Central fallback exception handler

There will always be some exception that cannot be handled inside of the code correctly - or it may just have been unforeseen.
A central fallback exception handler is required so all problems are visible in the log and the API always returns meaningful responses.
In some cases, this is as simple as a HTTP 500.

==== Dont expose too much exception details over API

It's good to inform the user, why their request did not work, but only if they can do something about it (HTTP 4xx).
So in case of application problems, you should not expose details of the problem to the caller.
This way, we avoid opening potential attack vectors.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
[tableHeader]='"page.asBuiltParts" | i18n'
(filterActivated)="filterActivated(true, $event )"
[tableType]="PartTableType.AS_BUILT_CUSTOMER"
[mainAspectType]="MainAspectType.AS_BUILT"
></app-parts-table>
</ng-template>
</div>
Expand Down Expand Up @@ -66,6 +67,7 @@
[multiSelectActive]="true"
(filterActivated)="filterActivated(false, $event )"
[tableType]="PartTableType.AS_PLANNED_CUSTOMER"
[mainAspectType]="MainAspectType.AS_PLANNED"
></app-parts-table>
</ng-template>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
[multiSortList]="tableSupplierAsBuiltSortList"
(filterActivated)="filterActivated(true, $event )"
[tableType]="PartTableType.AS_BUILT_SUPPLIER"
[mainAspectType]="MainAspectType.AS_BUILT"
></app-parts-table>
</ng-template>
</div>
Expand Down Expand Up @@ -74,6 +75,7 @@
(configChanged)="onAsPlannedTableConfigChange($event)"
(clickSelectAction)="isInvestigationOpen$.next(true)"
[multiSortList]="tableSupplierAsPlannedSortList"
[mainAspectType]="MainAspectType.AS_PLANNED"
[multiSelectActive]="true"
(filterActivated)="filterActivated(false, $event )"
[tableType]="PartTableType.AS_PLANNED_SUPPLIER"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import {DatePipe} from '@angular/common';
import {SemanticDataModel} from '@page/parts/model/parts.model';
import {
MultiSelectAutocompleteComponent
} from '@shared/components/multi-select-autocomplete/multi-select-autocomplete.component';
import {
FormatPartSemanticDataModelToCamelCasePipe
} from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe';
import {SharedModule} from '@shared/shared.module';
import {renderComponent} from '@tests/test-render.utils';
import {PartTableType} from "@shared/components/table/table.model";
import {Owner} from "@page/parts/model/owner.enum";
import {MatDatepickerInputEvent} from "@angular/material/datepicker";
import { DatePipe } from '@angular/common';
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
import { Owner } from '@page/parts/model/owner.enum';
import { SemanticDataModel } from '@page/parts/model/parts.model';
import { MultiSelectAutocompleteComponent } from '@shared/components/multi-select-autocomplete/multi-select-autocomplete.component';
import { PartTableType } from '@shared/components/table/table.model';
import { FormatPartSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe';
import { SharedModule } from '@shared/shared.module';
import { renderComponent } from '@tests/test-render.utils';

describe('MultiSelectAutocompleteComponent', () => {
const renderMultiSelectAutoCompleteComponent = (multiple = true) => {
Expand Down Expand Up @@ -414,4 +410,21 @@ describe('MultiSelectAutocompleteComponent', () => {
expect(eventMock.stopPropagation).not.toHaveBeenCalled();
});

it('should not stop event propagation for space key', async() => {
// Arrange
const {fixture} = await renderMultiSelectAutoCompleteComponent();
const {componentInstance} = fixture;
const eventMock = {
key: ' ',
ctrlKey: false,
stopPropagation: jasmine.createSpy('stopPropagation')
};

// Act
componentInstance.filterKeyCommands(eventMock);

// Assert
expect(eventMock.stopPropagation).toHaveBeenCalled();
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,18 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import {DatePipe, registerLocaleData} from '@angular/common';
import { DatePipe, registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import localeDeExtra from '@angular/common/locales/extra/de';
import {Component, EventEmitter, Inject, Input, LOCALE_ID, OnChanges, Output, ViewChild} from '@angular/core';
import {FormControl} from '@angular/forms';
import {DateAdapter, MAT_DATE_LOCALE} from '@angular/material/core';
import {MatDatepickerInputEvent} from '@angular/material/datepicker';
import {Owner} from '@page/parts/model/owner.enum';
import {PartTableType} from '@shared/components/table/table.model';
import {
FormatPartSemanticDataModelToCamelCasePipe
} from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe';
import {PartsService} from '@shared/service/parts.service';
import {firstValueFrom} from "rxjs";
import { Component, EventEmitter, Inject, Input, LOCALE_ID, OnChanges, Output, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
import { Owner } from '@page/parts/model/owner.enum';
import { PartTableType } from '@shared/components/table/table.model';
import { FormatPartSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe';
import { PartsService } from '@shared/service/parts.service';
import { firstValueFrom } from 'rxjs';

@Component({
selector: 'app-multiselect',
Expand Down Expand Up @@ -256,7 +254,6 @@ export class MultiSelectAutocompleteComponent implements OnChanges {

// Returns plain strings array of filtered values
getFilteredOptionsValues(): string[] {
console.log("getFiltered")
const filteredValues = [];
this.filteredOptions.forEach(option => {
if(option.length) {
Expand Down Expand Up @@ -322,7 +319,7 @@ export class MultiSelectAutocompleteComponent implements OnChanges {
}

filterKeyCommands(event: any) {
if (event.key === 'Enter' || (event.ctrlKey && event.key === 'a')) {
if (event.key === 'Enter' || (event.ctrlKey && event.key === 'a' || event.key === ' ')) {
event.stopPropagation();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
@RequiredArgsConstructor
public class DashboardServiceImpl implements DashboardService {

//TODO: rework this to use Service classes instead of using the Repository directly
private final AssetAsBuiltRepository assetAsBuiltRepository;
private final AssetAsPlannedRepository assetAsPlannedRepository;
private final InvestigationRepository investigationsRepository;
Expand Down

0 comments on commit b275fed

Please sign in to comment.