Skip to content

Commit

Permalink
Mdas 674 implementierung s3 eai endpunkt archivierung (#29)
Browse files Browse the repository at this point in the history
* ✨ MDAS-674 Add rest resource.

* ♻️ MDAS-674 Add DB connection and entity

* ♻️ MDAS-674 Add bucket or bucket-archive file list

* ♻️ MDAS-674 Add move file to archive

* ♻️ MDAS-674 Rename path to archive

* ♻️ MDAS-674 Put archive HTTP Status 204

* 📝 MDAS-674 Releasenotes complemented

* ♻️ MDAS-674 Pull request issues implemented

* ♻️ MDAS-674 Pull request issues implemented

* ♻️ MDAS-674 Pull request issues implemented

* ♻️ MDAS-674 Pull request issues implemented

* ♻️ MDAS-674 Pull request issues implemented

* ♻️ MDAS-674 Pull request issues implemented
  • Loading branch information
sfi2022 authored Mar 25, 2024
1 parent e5a5b92 commit 2335d21
Show file tree
Hide file tree
Showing 28 changed files with 839 additions and 165 deletions.
1 change: 1 addition & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Sprint 7 (05.03.2024 - 26.03.2024)
### Hinzugefügt
- Archivierung
- Url vorsignierte Adresse erstellen
- Workflows erstellt

Expand Down
41 changes: 41 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,54 @@ Die Enterprise Application Integration Komponente (EAI) bietet eine Webschnittst
Github-Repo: https://github.com/it-at-m/mobidam-s3-eai

# Technisches Setup
# S3
Unser LHM S3 ist eine Implementierung von [StorageGrid](https://docs.netapp.com/us-en/storagegrid-family/).

Für das Projekt existiert ein S3 Tenant dem auf Antrag neue Buckets hinzugefügt werden können.
Jeder Bucket ist fachlich einer Schnittstelle zugeordnet.

Innerhalb des Buckets können Dateien mit einem 'Prefix' geordnet werden. Z. Bsp.
- /Pfad1/Pfad1/Datei1
- /Pfad1/Pfad1/Datei2
- /Pfad1/Pfad2/Datei1
- ...

Bei einem 'Prefix' handelt es sich nicht um eine Pfad, sondern er ist besser als eine 'vorangestellte' Erweiterung des Dateinamens zu verstehen.

Es wird erwartet das Dateien immer mit einem 'Prefix' in einen S3 Bucket importiert werden.

## Openapi
Um neue Openapi Java Source Dateien zu erstellen kann das Maven Profil _generate-openapi_ verwendet werden (mvn clean compile -P generate-openapi).
Das Profil erzeugt die Openapi Java Source Dateien im Maven _target_ Ordner.
Änderungen und neue Features können in die Klassen im Package _de.muenchen.mobidam.rest_ kopiert werden.

Die Openapi Quelle kann mit dem [Swagger Editor](https://editor.swagger.io) bearbeitet werden.

# REST Schnittstelle
Mit dem [Swagger Editor](https://editor.swagger.io) kann die komplette [Openapi REST Beschreibung](https://github.com/it-at-m/mobidam-s3-eai/blob/sprint/src/main/resources/openapi_rest_s3_v1.yaml) angezeigt werden.
Der Workflow für den Import von Dateien in FME sieht folgende Schritt vor:
- Anzeigen von Inhalten eines S3 Buckets.
- Erzeugen eines Download Links für eine Datei.
- Nach dem Herunterladen und dem Import in FME verschieben der Datei in Archiv innerhalb des Buckets. Für alle Buckets ist 'archiv' ein festgelegter Prefix der in allen Buckets gleich ist. Beim Verschieben in das Archiv wird dem Objektnamen bestehend aus Prefix/Objektname das Prefix 'archiv' vorangestellt (s.u).
- Mit dem Verschiebn in das Bucket Archiv wird zusätzlich ein Datenbankeintrag mit einer Verfallsdauer der Datei geschrieben.
- Löschen der Datei aus dem S3 nach dem Ablauf der Verfalldauer.

## Anzeigen nicht verarbeiteter Dateien
Mit der Rest Ressource GET '.../filesInFolder?bucketName=bucket1&path=...[&archived=false]' können die Dateien mit einem bestimmten Prefix selektiert werden.

## Download von Dateien aus dem S3
Mit der REST Ressource '/presignedUrl' läßt sich ein zeitlich begrenzter Download Link für eine Datei aus einem S3 Bucket erstellen.
Z.Bsp. : GET '.../presignedUrl?bucketName=bucket1&objectName=Pfad1/Pfad2/Dateiname'

## Archivieren und Anzeigen von bereits verarbeiteten Dateien
### Archivieren
Mit der Rest Ressource '/archive' lassen sich bereits in FME importierte Dateien in einen vordefinierten 'archiv' Pfad verschieben, damit sie nicht noch einmal verarbeitet werden.
Technisch ist das in S3 eine Erweiterung des Datei Pfads und Namens im S3 Bucket.
Z.Bsp. : PUT '.../archive?bucketName=bucket1&objectName=Pfad1/Pfad2/Datei' wird innerhalb des 'bucket1' verschoben nach 'archive/Pfad1/Pfad2/Datei'.

### Anzeigen
Mit der Rest Ressource GET '.../filesInFolder?bucketName=bucket1&path=...&archived=true' können die archivierten Dateien eines Pfades angezeigt werden.

### Konfiguration

Zur Konfiguration der Credentials der Buckets dient das Property ***mobidam.s3.bucket-credential-config***.
Expand Down
30 changes: 30 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,30 @@
<version>1.7.0</version>
</dependency>

<!-- Database -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jpa-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.5.4</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<!-- Hibernate Model Gen for type safe criterias -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.apache.camel</groupId>
Expand All @@ -178,6 +202,7 @@
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-mock-starter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
Expand Down Expand Up @@ -205,6 +230,11 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

<!-- Sonstiges -->
<dependency>
<groupId>org.projectlombok</groupId>
Expand Down
13 changes: 10 additions & 3 deletions src/main/java/de/muenchen/mobidam/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@ public class Constants {

public static final String MOBIDAM_LOGGER = "de.muenchen.mobidam";
public static final String CAMEL_SERVLET_CONTEXT_PATH = "CamelServletContextPath";
public static final String ARCHIVE_ENTITY = "ArchiveEntity";
public static final String DELIMITER = "/";

/*
* Must be compatible with parameters in resource/openapi_rest_s3_v1.yaml
*/
public static final String CAMEL_SERVLET_CONTEXT_PATH_FILES_IN_FOLDER = "/filesInFolder";
public static final String CAMEL_SERVLET_CONTEXT_PATH_PRESIGNED_URL = "/presignedUrl";
public static final String PATH_ALIAS_PREFIX = "path";
public static final String CAMEL_SERVLET_CONTEXT_PATH_ARCHIVE = "/archive";
public static final String PARAMETER_ARCHIVED = "archived";
public static final String PARAMETER_BUCKET_NAME = "bucketName";
public static final String PARAMETER_OBJECT_NAME = "objectName";
public static final String PARAMETER_PATH = "path";
public static final String ARCHIVE_PATH = "archive/";

public static final String BUCKET_NAME = "bucketName";
public static final String OBJECT_NAME = "objectName";
// S3 url parameter
public static final String S3_PREFIX = "prefix=";

// Headers for bucket credentials
public static final String ACCESS_KEY = "accessKey";
Expand Down
58 changes: 58 additions & 0 deletions src/main/java/de/muenchen/mobidam/domain/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* The MIT License
* Copyright © 2023 Landeshauptstadt München | it@M
*
* 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.
*/
package de.muenchen.mobidam.domain;

import static java.sql.Types.VARCHAR;

import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import java.io.Serializable;
import java.util.UUID;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.JdbcTypeCode;

@MappedSuperclass
@NoArgsConstructor
@Getter
@Setter
@ToString
@EqualsAndHashCode
public abstract class BaseEntity implements Cloneable, Serializable {

private static final long serialVersionUID = 1L;

@Column(name = "id", length = 36)
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
@JdbcTypeCode(VARCHAR)
private UUID id;

}
40 changes: 40 additions & 0 deletions src/main/java/de/muenchen/mobidam/domain/MobidamArchive.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c): it@M - Dienstleister für Informations- und Telekommunikationstechnik
* der Landeshauptstadt München, 2023
*/
package de.muenchen.mobidam.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import java.time.LocalDate;
import lombok.*;

/**
* This class represents a the mobidam archive entity.
* All files archived in S3 have a database entry.
*/
@Entity
@Table(name = "Archive", schema = "mdass3eai")
@Getter
@Setter
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class MobidamArchive extends BaseEntity {

private static final long serialVersionUID = 1L;

@Column(nullable = false)
@NotEmpty
private String bucket;
@Column(nullable = false)
@NotEmpty
private String path;
@Column(nullable = false)
@NotEmpty
private LocalDate creation;
@Column(nullable = false)
@NotEmpty
private LocalDate expiration;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package de.muenchen.mobidam.repository;

import de.muenchen.mobidam.domain.MobidamArchive;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ArchiveRepository extends JpaRepository<MobidamArchive, UUID> {
}
35 changes: 28 additions & 7 deletions src/main/java/de/muenchen/mobidam/rest/S3Api.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ public class S3Api extends RouteBuilder {
public void configure() throws Exception {

/**
* GET /filesInFolder
* GET /filesInFolder : Get S3 bucket object list
**/
rest()
.get("/filesInFolder")
.description("")
.description("Get S3 bucket object list")
.id("filesInFolderGetApi")
.produces("application/json")
.outType(BucketContentInner[].class)
Expand All @@ -36,14 +36,20 @@ public void configure() throws Exception {
.required(false)
.description("S3 path")
.endParam()
.param()
.name("archived")
.type(RestParamType.query)
.required(false)
.description("Return archived files")
.endParam()
.to("{{camel.route.common}}");

/**
* GET /presignedUrl
* GET /presignedUrl : Retrieve download link
**/
rest()
.get("/presignedUrl")
.description("")
.description("Retrieve download link")
.id("viewObjectDownloadLink")
.produces("application/json")
.outType(PresignedUrl.class)
Expand All @@ -59,11 +65,26 @@ public void configure() throws Exception {
.required(true)
.description("Object name")
.endParam()
.to("{{camel.route.common}}");

/**
* PUT /archive : Move &#39;finshed&#39; file to archive.
**/
rest()
.put("/archive")
.description("Move 'finished' file to archive.")
.id("moveFinishedFileToArchiveApi")
.param()
.name("path")
.name("bucketName")
.type(RestParamType.query)
.required(false)
.description("S3 path/prefix")
.required(true)
.description("Bucket name")
.endParam()
.param()
.name("objectName")
.type(RestParamType.query)
.required(true)
.description("Object name")
.endParam()
.to("{{camel.route.common}}");

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/de/muenchen/mobidam/s3/RestResponseWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public void process(Exchange exchange) throws MobidamException {
case Constants.CAMEL_SERVLET_CONTEXT_PATH_PRESIGNED_URL:
presignedUrl(exchange);
break;
case Constants.CAMEL_SERVLET_CONTEXT_PATH_ARCHIVE:
exchange.getMessage().setBody(null);
break;
default:
exchange.getMessage().setBody(ErrorResponseBuilder.build(HttpStatus.NOT_FOUND.value(), "REST ContextPath not found : " + contextPath));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void process(Exchange exchange) throws Exception {
}

private String verifyBucket(Exchange exchange) throws MobidamException {
String bucketName = exchange.getMessage().getHeader(Constants.BUCKET_NAME, String.class);
String bucketName = exchange.getMessage().getHeader(Constants.PARAMETER_BUCKET_NAME, String.class);
if (Strings.isNullOrEmpty(bucketName)) {
exchange.getMessage().setBody(ErrorResponseBuilder.build(HttpStatus.BAD_REQUEST.value(), "Bucket name is missing"));
throw new MobidamException("Bucket name is missing");
Expand Down
Loading

0 comments on commit 2335d21

Please sign in to comment.