Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
39dca18
Functional upload to a directory in the desktop by multipart file POST.
manoger Apr 8, 2019
9359b64
refatoração de metodos e novas configurações em application properties
manoger Apr 12, 2019
e63440e
Reformulated the Profile Images's API
manoger Apr 14, 2019
9f3baec
Added Delete Method.
manoger Apr 14, 2019
362c801
Added new Exceptions and alteration in the root images directory.
manoger Apr 14, 2019
f55ec29
Removed verifications of user existence.
manoger Apr 19, 2019
7631c72
Added:Image reSizer util
manoger Apr 20, 2019
c5abfa8
Alteration: Controller reduced to POST,GET,DELETE
manoger Apr 20, 2019
dd832d1
Added: Class intended to do general cool things like tree directories.
manoger Apr 20, 2019
6e2113f
Alteration: Only multiPartFile upload test is working properly for now.
manoger Apr 20, 2019
2371ec6
Alteration: Only multiPartFile upload test is working properly for now.
manoger Apr 20, 2019
49053f3
Alteration: ImageStorer can resize images now!
manoger Apr 20, 2019
5ee2aa5
New exception for 415(NotSupportedMediaType) http response.
manoger Apr 22, 2019
9fadf1a
Added upload size limit.
manoger Apr 22, 2019
7e9621a
new Exception for 413(PayloadTooLarge) http response
manoger Apr 22, 2019
5847d29
Added validation of Image extension
manoger Apr 22, 2019
6620d33
Updated tests
manoger Apr 23, 2019
19b1ba7
Removed user existence validation
manoger Apr 23, 2019
ccaecf7
correction in typo
manoger Apr 23, 2019
69d8cbc
Deleted class typo
manoger Apr 23, 2019
6f0a2ce
Added image type validation
manoger Apr 26, 2019
fb272e3
removed verifyIfIsPayloadTooLarge and verifyIfHasImageExtension methods
manoger Apr 26, 2019
70c1a15
Scope changes.
manoger Apr 26, 2019
33cb12a
New tests and mocks.
manoger Apr 26, 2019
a6ce8ed
Renamed the entire module
manoger Apr 27, 2019
d03fa89
Added polymorphism to StoreImageProfile()
manoger Apr 27, 2019
dfcdfb5
UtilsController can generate random profile images.
manoger Apr 27, 2019
1afe85f
SetUp for tests with valid image
manoger Apr 27, 2019
6f8f5f4
Added an special user to try the API
manoger Apr 27, 2019
e2039dd
Removed the entire module with "Album" as name
manoger Apr 27, 2019
c98756b
EOL at EOF
manoger Apr 27, 2019
ccb9592
Refactored @RequestMapping
manoger Apr 27, 2019
d287db2
EOL at EOF
manoger Apr 27, 2019
b6374fb
Switched explicit variable types to var
manoger Apr 27, 2019
e282e72
Regex: added "start of line"
manoger Apr 27, 2019
5da3af0
Regex: added "start of line"
manoger Apr 27, 2019
8185cf1
Added spacing between methods
manoger Apr 27, 2019
6a4d617
EOL at EOF
manoger Apr 27, 2019
f5d9d13
Comments removed
manoger Apr 27, 2019
54bc130
Alteration on the url for tests
manoger Apr 27, 2019
52bbb2c
Deleted the "hey" test
manoger Apr 27, 2019
f5a4353
Import static for asserts
manoger Apr 27, 2019
e2b96ee
Deleted test user, method "hey" and "searchSomeImage"
manoger Apr 27, 2019
882455c
added "mugshot"
manoger Apr 27, 2019
74e5bef
Solved bug of pull request
manoger Apr 28, 2019
3930583
Changed path to database file
manoger Apr 28, 2019
81cb2eb
changed @autowired position
manoger Apr 28, 2019
11d2a1a
EOL at EOF
manoger Apr 28, 2019
348a9b4
Increased test coverage
manoger Apr 28, 2019
42e01a1
Added spaces
manoger Apr 28, 2019
f7618cf
Added spaces
manoger Apr 28, 2019
f149ed0
Added spaces
manoger Apr 28, 2019
871fc9d
Added spaces
manoger Apr 28, 2019
d511aff
preparação do ambiente para o novo repositório
fcidade May 2, 2019
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
53 changes: 53 additions & 0 deletions mugshot/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
buildscript {
ext {
springBootVersion = '2.1.3.RELEASE'
springCloudVersion = 'Greenwich.SR1'
}
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" as Object
classpath "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" as Object
}
}

plugins {
id "io.spring.dependency-management" version "1.0.5.RELEASE"
id 'java'
id 'org.springframework.boot' version '2.1.3.RELEASE'
}

apply plugin: 'io.spring.dependency-management'

group = 'hive'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}

dependencies {
implementation project(':common')
implementation project(':entity')

implementation 'javax.xml.bind:jaxb-api:2.3.1'
implementation 'javax.activation:activation:1.1.1'
implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.2'

implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions mugshot/src/main/java/hive/mugshot/MugshotApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package hive.mugshot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
@EntityScan( basePackages = {"hive.entity"} )
public class MugshotApplication {
public static void main(String[] args) {
SpringApplication.run(MugshotApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package hive.mugshot.controller;

import hive.mugshot.exception.NotAcceptedFileFormatException;
import hive.mugshot.storage.ImageStorer;
import hive.common.security.HiveHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.core.io.Resource;

import static hive.mugshot.storage.ImageUtils.validateIfHasAnImageAsExtension;

@RestController
@RequestMapping("/")
public class MugshotController {

@Value("${hive.mugshot.profile-image-name}")
private String imageName;
private final ImageStorer imageStorer;

@Autowired
public MugshotController(ImageStorer imageStorer) {
this.imageStorer = imageStorer;
}

@ResponseStatus(code = HttpStatus.OK, reason = "Profile image successfully stored")
@PostMapping
public void sendImageProfile(
@RequestParam("image") MultipartFile insertedImage,
@RequestHeader(name = HiveHeaders.AUTHENTICATED_USER_ID) final String userId
){
if(!validateIfHasAnImageAsExtension(insertedImage.getOriginalFilename())) {
throw new NotAcceptedFileFormatException();
}
imageStorer.storeImageProfile(userId,insertedImage,imageName);
}

@GetMapping(produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<Resource> searchProfileImage(
@RequestHeader(name = HiveHeaders.AUTHENTICATED_USER_ID) final String userId
){
Resource file = imageStorer.loadImage(userId,imageName);
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").body(file);
}

@ResponseStatus(code = HttpStatus.NO_CONTENT, reason = "Profile image successfully deleted")
@DeleteMapping
public void deleteProfileImage(
@RequestHeader(name = HiveHeaders.AUTHENTICATED_USER_ID) final String userId
){
imageStorer.deleteImage(userId,imageName);
}

}
31 changes: 31 additions & 0 deletions mugshot/src/main/java/hive/mugshot/controller/UtilsController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package hive.mugshot.controller;

import hive.mugshot.storage.ImageStorer;
import hive.mugshot.storage.ImageUtils;
import hive.common.security.HiveHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/utils")
public class UtilsController {

private final ImageStorer imageStorer;
@Value("${hive.mugshot.profile-image-name}")
private String imageName;

@Autowired
public UtilsController(ImageStorer imageStorer){
this.imageStorer=imageStorer;
}

@ResponseStatus(code = HttpStatus.OK, reason = "Random image generated and successfully stored")
Copy link
Contributor

@SamuelLevi SamuelLevi Apr 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need

Suggested change
@ResponseStatus(code = HttpStatus.OK, reason = "Random image generated and successfully stored")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nop, success isn't Exception

@PostMapping("/generateRandomImage")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@PostMapping("/generateRandomImage")
@PostMapping

public void generateRandomImage(@RequestHeader(name = HiveHeaders.AUTHENTICATED_USER_ID) final String userId) {
var generatedImage=ImageUtils.generateRandomImage();
imageStorer.storeImageProfile(userId,generatedImage,imageName);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package hive.mugshot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.PAYLOAD_TOO_LARGE,reason = "Invalid size of file")
public class FileSizeException extends RuntimeException{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package hive.mugshot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "The Image already exists")
public class ImageAlreadyExistException extends RuntimeException{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package hive.mugshot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Image not found for this user")
public class ImageNotFound extends RuntimeException{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package hive.mugshot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Failed to store the image due to some I/O problem or permission")
public class ImageProfileException extends RuntimeException{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package hive.mugshot.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "Invalid path")
public class InvalidPathException extends RuntimeException {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package hive.mugshot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.UNSUPPORTED_MEDIA_TYPE, reason = "Media type unsupported")
public class NotAcceptedFileFormatException extends RuntimeException{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package hive.mugshot.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "The user does not exist")
public class UserNotFoundException extends RuntimeException {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package hive.mugshot.repository;

import hive.entity.user.User;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Integer> {
User findByUsername(String username);
}
83 changes: 83 additions & 0 deletions mugshot/src/main/java/hive/mugshot/storage/ImageStorer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package hive.mugshot.storage;

import hive.mugshot.exception.*;
import hive.mugshot.exception.InvalidPathException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.*;

@Service
public class ImageStorer {
@Value("${hive.mugshot.image-directory-path}")
private String rootDir;
@Value("${hive.mugshot.profile-image-dimension}")
private int imageSizeInPixels;

public void storeImageProfile(String userDirectoryName, MultipartFile insertedImage, String imageStoredName){
createDirectoryIfNotExist(userDirectoryName);
try {
var buff = ImageUtils.resizeImageToSquare(ImageIO.read(insertedImage.getInputStream()),imageSizeInPixels);
ImageIO.write(buff, "jpg", createFullPathToTheFile(userDirectoryName, imageStoredName).toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public void storeImageProfile(String userDirectoryName, BufferedImage insertedImage, String imageStoredName){
createDirectoryIfNotExist(userDirectoryName);
try {
var buff = ImageUtils.resizeImageToSquare(insertedImage,imageSizeInPixels);
ImageIO.write(buff, "jpg", createFullPathToTheFile(userDirectoryName, imageStoredName).toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public Resource loadImage(String userDirectoryName,String imageName) {
try {
var file = createFullPathToTheFile(userDirectoryName,imageName);
var resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
}else{
throw new ImageNotFound();
}
} catch (MalformedURLException e) {
e.printStackTrace();
throw new InvalidPathException();
}
}

public void deleteImage(String userDirectoryName, String imageName) {
var parentDir = createFullPathToTheFile(userDirectoryName,imageName);
try {
Files.deleteIfExists(parentDir);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Unable to delete the directory.\n"+e);
}
}

private void createDirectoryIfNotExist(String userDirectoryPath){
Path parentDir = Paths.get(rootDir,userDirectoryPath);
if (!Files.exists(parentDir)) {
try {
Files.createDirectories(parentDir);
} catch (IOException e) {
throw new RuntimeException("Unable to create the directory.\n"+e);
}
}
}

private Path createFullPathToTheFile(String userDirectoryName, String filename) {
return Paths.get(rootDir).resolve(userDirectoryName).resolve(filename);
}

}
65 changes: 65 additions & 0 deletions mugshot/src/main/java/hive/mugshot/storage/ImageUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package hive.mugshot.storage;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
import java.util.regex.Pattern;

public final class ImageUtils {
private static final String IMAGE_PATTERN = "(^.+\\.(gif|png|bmp|jpeg|jpg)$)";

private ImageUtils(){
}

public static boolean validateIfHasAnImageAsExtension(final String image){
var pattern = Pattern.compile(IMAGE_PATTERN);
var matcher = pattern.matcher(image);
return matcher.matches();
}

public static BufferedImage resizeImageToSquare(BufferedImage inputtedImage,int imageSizeInPixels) {
// multi-pass bilinear div 2
var bufferedImageWithNewSize = new BufferedImage(imageSizeInPixels, imageSizeInPixels, BufferedImage.TYPE_INT_RGB);
var reSizer = bufferedImageWithNewSize.createGraphics();
var resizingMode=
(inputtedImage.getHeight()<imageSizeInPixels)
? RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
: RenderingHints.VALUE_INTERPOLATION_BILINEAR;
reSizer.setRenderingHint(RenderingHints.KEY_INTERPOLATION, resizingMode);
reSizer.drawImage(inputtedImage, 0, 0, imageSizeInPixels, imageSizeInPixels, null);
reSizer.dispose();
return bufferedImageWithNewSize;
}

public static BufferedImage generateRandomImage(){
var yellow=0xF6BD60;
var orange=0xE9724C;
var gray = 0xE8E9EB;
var blue = 0x5C9EAD;
var black = 0x313638;
var colorsCombinations=new int[][]{
{yellow,black},
{orange,black},
{blue,black},
{blue,gray},
{orange,yellow}
};
var combinationIndex=new Random().nextInt(colorsCombinations.length);
var img=new BufferedImage(9,9,BufferedImage.TYPE_INT_RGB);
var maxH=img.getHeight();
var maxV=img.getWidth();
for(int vertical=0;vertical<maxV;vertical++){
for(int horizontal=0;horizontal<maxH;horizontal++){
var pixel=0;
if(Math.random()<0.7){
pixel=colorsCombinations[combinationIndex][0];
}else{
pixel=colorsCombinations[combinationIndex][1];
}
img.setRGB(horizontal,vertical,pixel);
}
}
return img;
}

}
Loading