Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NAE-1946] Remote file connector to S3 #243

Merged
merged 36 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6db8bb8
[NAE-1946] Disable create case button using menu items
martinkranec Feb 5, 2024
04e7001
[NAE-1946] Disable create case button using menu items
martinkranec Feb 5, 2024
ea66721
[NAE-1946] Disable create case button using menu items
martinkranec Feb 6, 2024
d5bd595
[NAE-1946] Remote file connector to S3
martinkranec Feb 7, 2024
4a975cd
[NAE-1946] Remote file connector to S3
martinkranec Feb 9, 2024
4fe0988
[NAE-1946] Remote file connector to S3
martinkranec Feb 11, 2024
f3fca89
[NAE-1946] Remote file connector to S3
martinkranec Feb 11, 2024
65d363d
[NAE-1946] Remote file connector to S3
martinkranec Feb 12, 2024
484388b
[NAE-1946] Remote file connector to S3
martinkranec Feb 12, 2024
52a37dc
[NAE-1946] Remote file connector to S3
martinkranec Feb 12, 2024
94b5f7d
[NAE-1946] Remote file connector to S3
martinkranec Feb 13, 2024
1f85fa8
[NAE-1946] Remote file connector to S3
martinkranec Feb 14, 2024
623eef5
[NAE-1946] Remote file connector to S3
martinkranec Feb 14, 2024
fb2406e
[NAE-1946] Remote file connector to S3
machacjozef Feb 14, 2024
dafef8c
[NAE-1946] Remote file connector to S3
machacjozef Feb 14, 2024
5425d90
Merge branch 'release/6.4.0' into NAE-1946
martinkranec Mar 19, 2024
dd73761
[NAE-1946] Remote file connector to S3
martinkranec Mar 20, 2024
7c4d243
[NAE-1946] Remote file connector to S3
martinkranec Mar 20, 2024
192e005
[NAE-1946] Remote file connector to S3
martinkranec Mar 22, 2024
783c233
[NAE-1946] Remote file connector to S3
martinkranec Mar 26, 2024
c3e8a2a
[NAE-1946] Remote file connector to S3
martinkranec Mar 26, 2024
fd0f2b3
[NAE-1946] Remote file connector to S3
martinkranec Mar 27, 2024
160fa0f
[NAE-1946] Remote file connector to S3
martinkranec Mar 27, 2024
c51f2fd
[NAE-1946] Remote file connector to S3
martinkranec Mar 27, 2024
5cdf8d8
[NAE-1946] Remote file connector to S3
martinkranec Mar 27, 2024
f18a2b4
[NAE-1946] Remote file connector to S3
martinkranec Mar 27, 2024
aabdb89
Merge remote-tracking branch 'origin/release/6.4.0' into NAE-1946
machacjozef May 17, 2024
1a2ffa9
[NAE-1946] Remote file connector to S3
martinkranec May 24, 2024
b9a2797
[NAE-1946] Remote file connector to S3
martinkranec May 24, 2024
8dcb73c
[NAE-1946] Remote file connector to S3
renczesnetgrif Sep 11, 2024
2aa8675
[NAE-2006] WorkflowService.deleteInstancesOfPetriNet does not remove …
renczesnetgrif Sep 13, 2024
8c5d52e
[NAE-1946] S3 connector
renczesnetgrif Sep 16, 2024
45ef9d4
[NAE-1946] Connector to S3
renczesnetgrif Sep 18, 2024
9f162b5
[NAE-1946] Connector to S3
renczesnetgrif Sep 18, 2024
1c5b4eb
[NAE-1946] Connector to S3
renczesnetgrif Sep 18, 2024
e613d76
[NAE-1946] Connector to S3
renczesnetgrif Sep 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/master-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ jobs:
- 9300:9300
options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10

minio:
image: docker.io/bitnami/minio:2022
ports:
- 9000:9000
- 9001:9001
options: -e="MINIO_ROOT_USER=root" -e="MINIO_ROOT_PASSWORD=password" -e="MINIO_DEFAULT_BUCKETS=default"

steps:
- name: Test Database
env:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ jobs:
- 9300:9300
options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10

minio:
image: docker.io/bitnami/minio:2022
ports:
- 9000:9000
- 9001:9001
options: -e="MINIO_ROOT_USER=root" -e="MINIO_ROOT_PASSWORD=password" -e="MINIO_DEFAULT_BUCKETS=default"

steps:
- name: Test Database
env:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ jobs:
- 9300:9300
options: -e="discovery.type=single-node" -e="xpack.security.enabled=false" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10

minio:
image: docker.io/bitnami/minio:2022
ports:
- 9000:9000
- 9001:9001
options: -e="MINIO_ROOT_USER=root" -e="MINIO_ROOT_PASSWORD=password" -e="MINIO_DEFAULT_BUCKETS=default"

steps:
- name: Test Database
env:
Expand Down
20 changes: 20 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ services:
image: redis:6
ports:
- "6379:6379"
minio:
image: docker.io/bitnami/minio:2022
ports:
- '9000:9000'
- '9001:9001'
networks:
- minionetwork
volumes:
- 'minio_data:/data'
environment:
- MINIO_ROOT_USER=root
- MINIO_ROOT_PASSWORD=password
- MINIO_DEFAULT_BUCKETS=default
networks:
minionetwork:
driver: bridge

volumes:
minio_data:
driver: local

# kibana:
# image: docker.elastic.co/kibana/kibana:7.17.4
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,11 @@
<artifactId>jackson-module-jsonSchema</artifactId>
<version>2.13.2</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.8</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.netgrif.application.engine.petrinet.domain.dataset


import com.netgrif.application.engine.files.StorageType
import org.springframework.data.mongodb.core.mapping.Document

@Document
class FileField extends Field<FileFieldValue> {

private Boolean remote
private StorageType storageType

FileField() {
super()
Expand Down Expand Up @@ -42,39 +42,19 @@ class FileField extends Field<FileFieldValue> {
this.setDefaultValue(FileFieldValue.fromString(defaultValue))
}

/**
* Get complete file path to the file
* Path is generated as follow:
* - if file is remote, path is field value / remote URI
* - if file is local
* - saved file name consists of Case id, field import id and original file name separated by dash
* @param caseId
* @return path to the saved file
*/
String getFilePath(String caseId) {
if (this.remote)
return this.getValue().getPath()
return this.getValue().getPath(caseId, getStringId())
}

String getFilePreviewPath(String caseId) {
return this.getValue().getPreviewPath(caseId, getStringId())
StorageType getStorageType() {
return this.storageType
}

boolean isRemote() {
return this.remote
}

void setRemote(boolean remote) {
this.remote = remote
void setStorageType(StorageType storageType) {
this.storageType = storageType
}

@Override
Field clone() {
FileField clone = new FileField()
super.clone(clone)
clone.remote = this.remote

clone.storageType = this.storageType
return clone
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package com.netgrif.application.engine.petrinet.domain.dataset

import com.netgrif.application.engine.configuration.ApplicationContextProvider
import com.netgrif.application.engine.workflow.domain.FileStorageConfiguration

class FileFieldValue implements Serializable {

private static final long serialVersionUID = 1299918326436821185L;
private static final long serialVersionUID = 1299918326436821185L

private String name

private String path

private String previewPath

FileFieldValue() {
}

Expand All @@ -19,6 +18,12 @@ class FileFieldValue implements Serializable {
this.path = path
}

FileFieldValue(String name, String path, String previewPath) {
this.name = name
this.path = path
this.previewPath = previewPath
}

static FileFieldValue fromString(String value) {
if (!value.contains(":"))
return new FileFieldValue(value, null)
Expand All @@ -39,21 +44,18 @@ class FileFieldValue implements Serializable {
return path
}

String getPath(String caseId, String fieldId) {
FileStorageConfiguration fileStorageConfiguration = ApplicationContextProvider.getBean("fileStorageConfiguration") as FileStorageConfiguration
return "${fileStorageConfiguration.getStoragePath()}/${caseId}-${fieldId}-${name}"
void setPath(String path) {
this.path = path
}

String getPreviewPath(String caseId, String fieldId) {
FileStorageConfiguration fileStorageConfiguration = ApplicationContextProvider.getBean("fileStorageConfiguration") as FileStorageConfiguration
return "${fileStorageConfiguration.getStoragePath()}/file_preview/${caseId}-${fieldId}-${name}"
String getPreviewPath() {
return previewPath
}

void setPath(String path) {
this.path = path
void setPreviewPath(String previewPath) {
this.previewPath = previewPath
}


@Override
String toString() {
return path
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.netgrif.application.engine.petrinet.domain.dataset

import com.netgrif.application.engine.files.StorageType

class FileListField extends Field<FileListFieldValue> {
private Boolean remote
private StorageType storageType
martinkranec marked this conversation as resolved.
Show resolved Hide resolved

FileListField() {
super()
Expand Down Expand Up @@ -48,37 +50,20 @@ class FileListField extends Field<FileListFieldValue> {
this.getValue().getNamesPaths().add(new FileFieldValue(fileName, path))
}

/**
* Get complete file path to the file
* Path is generated as follow:
* - if file is remote, path is field value / remote URI
* - if file is local
* - saved file path consists of Case id, slash field import id, slash original file name
* @param caseId
* @param name
* @return path to the saved file
*/
String getFilePath(String caseId, String name) {
if (this.remote) {
FileFieldValue first = this.getValue().getNamesPaths().find({ namePath -> namePath.name == name })
return first != null ? first.path : null
}
return FileListFieldValue.getPath(caseId, getStringId(), name)
StorageType getStorageType() {
return storageType
}

boolean isRemote() {
return this.remote
void setStorageType(StorageType storageType) {
this.storageType = storageType
}

void setRemote(boolean remote) {
this.remote = remote
}

@Override
Field clone() {
FileListField clone = new FileListField()
super.clone(clone)
clone.remote = this.remote
clone.storageType = this.storageType

return clone
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.netgrif.application.engine.petrinet.domain.dataset

import com.netgrif.application.engine.configuration.ApplicationContextProvider
import com.netgrif.application.engine.workflow.domain.FileStorageConfiguration

class FileListFieldValue implements Serializable {

private static final long serialVersionUID = 5299918326436821185L;
Expand Down Expand Up @@ -39,11 +36,6 @@ class FileListFieldValue implements Serializable {
return newVal
}

static String getPath(String caseId, String fieldId, String name) {
FileStorageConfiguration fileStorageConfiguration = ApplicationContextProvider.getBean("fileStorageConfiguration")
return "${fileStorageConfiguration.getStoragePath()}/${caseId}/${fieldId}/${name}"
}

@Override
String toString() {
return namesPaths.toString()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.netgrif.application.engine.files;

import com.netgrif.application.engine.files.interfaces.IStorageService;
import com.netgrif.application.engine.files.throwable.StorageNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
public class StorageResolverService {

private List<IStorageService> storageServices;
martinkranec marked this conversation as resolved.
Show resolved Hide resolved

@Autowired
private void setStorageServices(List<IStorageService> storageServices) {
this.storageServices = storageServices;
}

public IStorageService resolve(StorageType type) {
if (storageServices == null) {
log.error("Storage services with interface IStorageService not found.");
throw new StorageNotFoundException("Remote Storage not available.");
}
Optional<IStorageService> storageService = storageServices.stream().filter(service -> service.getType().equals(type)).collect(Collectors.toList()).stream().findFirst();
martinkranec marked this conversation as resolved.
Show resolved Hide resolved
if (storageService.isPresent()) {
return storageService.get();
}
throw new StorageNotFoundException("Storage Service with type: " + type + " not available.");

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.netgrif.application.engine.files;

import lombok.Getter;

import java.util.Arrays;
import java.util.Optional;

@Getter
public enum StorageType {
martinkranec marked this conversation as resolved.
Show resolved Hide resolved

MINIO("MINIO"),
LOCAL("LOCAL");

private final String type;

StorageType(String type) {
this.type = type;
}

public static StorageType fromString(String value) {
Optional<StorageType> storageType = Arrays.stream(StorageType.values()).filter(it -> it.getType().equals(value)).findFirst();
if (storageType.isPresent()) {
return storageType.get();
}
throw new NullPointerException("Storage type with value: "+ value + " not found.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.netgrif.application.engine.files.interfaces;

import com.netgrif.application.engine.files.StorageType;
import com.netgrif.application.engine.files.throwable.BadRequestException;
import com.netgrif.application.engine.files.throwable.ServiceErrorException;
import com.netgrif.application.engine.files.throwable.StorageException;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

public interface IStorageService {
StorageType getType();

InputStream get(String path) throws BadRequestException, ServiceErrorException;

boolean save(String path, MultipartFile file) throws StorageException;

boolean save(String path, InputStream stream) throws StorageException;

void delete(String path) throws StorageException;

String getPreviewPath(String caseId, String fieldId, String name);

String getPath(String caseId, String fieldId, String name);
}
Loading
Loading