Skip to content

Commit

Permalink
#4 Implement FirebaseStorageServiceImpl
Browse files Browse the repository at this point in the history
  • Loading branch information
IlyaLisov committed Jan 22, 2024
1 parent 9a588d5 commit f6b7868
Show file tree
Hide file tree
Showing 9 changed files with 831 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/codecov.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
jobs:
codecov:
runs-on: ubuntu-20.04
env:
FIREBASE_SECRET: ${{ secrets.FIREBASE_SECRET }}
FIREBASE_BUCKET: ${{ secrets.FIREBASE_BUCKET }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v3
Expand All @@ -21,6 +24,8 @@ jobs:
restore-keys: |
maven-
- run: |
export FIREBASE_SECRET="${FIREBASE_SECRET}"
export FIREBASE_BUCKET="${FIREBASE_BUCKET}"
mvn clean install
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/maven-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
jobs:
maven:
runs-on: ubuntu-20.04
env:
FIREBASE_SECRET: ${{ secrets.FIREBASE_SECRET }}
FIREBASE_BUCKET: ${{ secrets.FIREBASE_BUCKET }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v3
Expand All @@ -19,4 +22,6 @@ jobs:
restore-keys: |
maven-
- run: |
export FIREBASE_SECRET="${FIREBASE_SECRET}"
export FIREBASE_BUCKET="${FIREBASE_BUCKET}"
mvn clean install
20 changes: 20 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<lombok.version>1.18.30</lombok.version>
<checkstyle.version>3.2.2</checkstyle.version>
<junit.version>5.10.1</junit.version>
<jacoco.version>0.8.11</jacoco.version>
<firebase-admin.version>9.2.0</firebase-admin.version>
<firebase-storage.version>20.3.0</firebase-storage.version>
<google-cloud.version>2.31.0</google-cloud.version>
</properties>
<dependencies>
<dependency>
Expand All @@ -51,6 +55,22 @@
<optional>true</optional>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>${firebase-admin.version}</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-storage</artifactId>
<version>${google-cloud.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
112 changes: 112 additions & 0 deletions src/main/java/io/github/ilyalisov/storage/config/StorageFile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package io.github.ilyalisov.storage.config;

import lombok.Getter;
import lombok.Setter;

import java.io.InputStream;
import java.nio.file.Path;
import java.util.Optional;

/**
* File class.
*/
@Getter
@Setter
public class StorageFile {

/**
* Path to store file in.
*/
private Path path;

/**
* Name of file. Must ends with extension.
*/
private String fileName;

/**
* Content type of file.
*/
private String contentType;

/**
* InputStream with file data.
*/
private InputStream inputStream;

/**
* Creates an object.
*
* @param fileName name of file
* @param contentType content type
* @param inputStream input stream with file data
*/
public StorageFile(
final String fileName,
final String contentType,
final InputStream inputStream
) {
this(
fileName,
null,
contentType,
inputStream
);
}

/**
* Creates an object.
*
* @param path path to file
* @param fileName name of file
* @param contentType content type
* @param inputStream input stream with file data
*/
public StorageFile(
final String fileName,
final Path path,
final String contentType,
final InputStream inputStream
) {
if (!containsExtension(fileName)) {
throw new IllegalArgumentException(
"Filename must contain extension."
);
}
this.path = path;
this.fileName = fileName;
updatePath(fileName);
this.contentType = contentType;
this.inputStream = inputStream;
}

/**
* Checks if file name contains extension.
*
* @param fileName name of file
* @return true - if contains, false - otherwise
*/
private boolean containsExtension(
final String fileName
) {
return Optional.of(fileName)
.filter(f -> f.contains("."))
.map(f -> f.substring(fileName.lastIndexOf(".") + 1))
.isPresent();
}

private void updatePath(
final String fileName
) {
Path newPath;
if (this.path == null) {
newPath = Path.of("", fileName);
} else {
newPath = Path.of(path.toString(), fileName);
}
this.path = newPath.getParent();
this.fileName = newPath.getFileName().toString();

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package io.github.ilyalisov.storage.service;

import com.google.api.gax.paging.Page;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.Bucket;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.cloud.StorageClient;
import io.github.ilyalisov.storage.config.StorageFile;
import lombok.SneakyThrows;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
* Implementation of StorageService based on Firebase.
*/
public class FirebaseStorageServiceImpl implements StorageService {

/**
* Firebase Bucket.
*/
private final Bucket bucket;

/**
* Returns bucket.
*
* @return bucket
*/
public Bucket getBucket() {
return bucket;
}

/**
* Creates an object.
*
* @param credentials input stream with Firebase credentials from JSON file
* @param bucket Firebase bucket name
*/
@SneakyThrows
public FirebaseStorageServiceImpl(
final InputStream credentials,
final String bucket
) {
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(
GoogleCredentials.fromStream(
credentials
)
)
.build();
FirebaseApp.initializeApp(options);
this.bucket = StorageClient.getInstance()
.bucket(bucket);
}

@Override
public Optional<StorageFile> find(
final String fileName
) {
Blob result = bucket.get(fileName);
if (result == null) {
return Optional.empty();
}
StorageFile file = new StorageFile(
fileName,
result.getContentType(),
new ByteArrayInputStream(result.getContent())
);
return Optional.of(file);
}

@Override
public Optional<StorageFile> find(
final String fileName,
final Path path
) {
Blob result = bucket.get(path.toString() + "/" + fileName);
if (result == null) {
return Optional.empty();
}
StorageFile file = new StorageFile(
fileName,
path,
result.getContentType(),
new ByteArrayInputStream(result.getContent())
);
return Optional.of(file);
}

@Override
public List<StorageFile> findAll(
final Path path
) {
Page<Blob> results = bucket.list();
List<StorageFile> files = new ArrayList<>();
results.streamAll().forEach(
(result) -> {
if (result.getName().startsWith(path + "/")) {
StorageFile file = find(result.getName())
.get();
files.add(file);
}
}
);
return files;
}

@Override
public boolean exists(
final String fileName
) {
return bucket.get(fileName) != null;
}

@Override
public boolean exists(
final String fileName,
final Path path
) {
return bucket.get(path.toString() + "/" + fileName) != null;
}

@Override
@SneakyThrows
public Path save(
final StorageFile file
) {
String path = file.getPath() != null ? file.getPath() + "/" : "";
bucket.create(
path + file.getFileName(),
file.getInputStream().readAllBytes(),
file.getContentType()
);
return Path.of(path, file.getFileName());
}

@Override
public void delete(
final String fileName
) {
Blob file = bucket.get(fileName);
if (file != null) {
file.delete();
}
}

@Override
public void delete(
final String fileName,
final Path path
) {
Blob file = bucket.get(path.toString() + "/" + fileName);
if (file != null) {
file.delete();
}
}

@Override
public void delete(
final Path path
) {
Page<Blob> blobs = bucket.list();
String pathToString = path.toString();
blobs.streamAll().forEach(blob -> {
if (blob.getName().startsWith(pathToString)) {
blob.delete();
}
});
}

}
Loading

0 comments on commit f6b7868

Please sign in to comment.