Skip to content

Commit

Permalink
Merge pull request #48 from sparkoo/46-latestDownload
Browse files Browse the repository at this point in the history
46 latest download
  • Loading branch information
sparkoo authored Oct 29, 2018
2 parents 750889e + 29bc869 commit 917aa2a
Show file tree
Hide file tree
Showing 14 changed files with 550 additions and 94 deletions.
9 changes: 9 additions & 0 deletions src/main/java/cz/sparko/boxitory/conf/NotFoundException.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,13 @@ private NotFoundException(String message) {
public static NotFoundException boxNotFound(String boxName) {
return new NotFoundException("box [" + boxName + "] does not exist");
}

public static NotFoundException boxVersionNotFound(String boxName, String boxVersion) {
return new NotFoundException("version [" + boxVersion + "] of box [" + boxName + "] does not exist");
}

public static NotFoundException boxVersionProviderNotFound(String boxName, String boxVersion, String boxProvider) {
return new NotFoundException(
"provider [" + boxProvider + "] of version [" + boxVersion + "] of box [" + boxName + "] does not exist");
}
}
14 changes: 0 additions & 14 deletions src/main/java/cz/sparko/boxitory/controller/BoxController.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@
import cz.sparko.boxitory.service.BoxRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
Expand Down Expand Up @@ -52,15 +49,4 @@ public String latestBoxVersion(@PathVariable String boxName) {
.map(BoxVersion::getVersion)
.orElseThrow(() -> NotFoundException.boxNotFound(boxName));
}

@ExceptionHandler
public ResponseEntity<String> handleException(Exception e) {
final HttpStatus status;
if (e instanceof NotFoundException) {
status = HttpStatus.NOT_FOUND;
} else {
status = HttpStatus.INTERNAL_SERVER_ERROR;
}
return new ResponseEntity<>(e.getMessage(), status);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package cz.sparko.boxitory.controller;

import cz.sparko.boxitory.conf.NotFoundException;
import cz.sparko.boxitory.model.BoxStream;
import cz.sparko.boxitory.service.BoxRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
public class DownloadController {
private static final Logger LOG = LoggerFactory.getLogger(DownloadController.class);

private final BoxRepository boxRepository;

public DownloadController(BoxRepository boxRepository) {
this.boxRepository = boxRepository;
}

@RequestMapping(value = "/download/{boxName}/{boxProvider}/{boxVersion}", method = RequestMethod.GET)
public void downloadBox(
HttpServletResponse response,
@PathVariable String boxName,
@PathVariable String boxProvider,
@PathVariable String boxVersion) throws IOException {
LOG.info("Downloading box [{}], provider [{}], version [{}]", boxName, boxProvider, boxVersion);

BoxStream boxFile = boxRepository.getBoxStream(boxName, boxProvider, boxVersion)
.orElseThrow(() -> NotFoundException.boxVersionProviderNotFound(boxName, boxVersion, boxProvider));

response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + boxFile.getFilename() + "\"");
FileCopyUtils.copy(boxFile.getStream(), response.getOutputStream());
}

@RequestMapping(value = "/download/{boxName}/{boxProvider}/latest", method = RequestMethod.GET)
public void downloadLatestBox(
HttpServletResponse response,
@PathVariable String boxName,
@PathVariable String boxProvider) throws IOException {
LOG.info("Downloading latest version of box [{}], provider [{}]", boxName, boxProvider);
downloadBox(response, boxName, boxProvider, boxRepository.latestVersionOfBox(boxName, boxProvider));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cz.sparko.boxitory.controller;

import cz.sparko.boxitory.conf.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalControllerExceptionHandler {
@ExceptionHandler
public ResponseEntity<String> handleException(Exception e) {
final HttpStatus status;
if (e instanceof NotFoundException) {
status = HttpStatus.NOT_FOUND;
} else {
status = HttpStatus.INTERNAL_SERVER_ERROR;
}
return new ResponseEntity<>(e.getMessage(), status);
}
}
8 changes: 8 additions & 0 deletions src/main/java/cz/sparko/boxitory/domain/Box.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

/**
* Full description of Vagrant's box as needed in http API.
Expand Down Expand Up @@ -53,4 +54,11 @@ public String getDescription() {
public List<BoxVersion> getVersions() {
return versions;
}

public List<String> getProviders() {
return getVersions().stream()
.flatMap(v -> v.getProviders().stream().map(BoxProvider::getName))
.distinct()
.collect(Collectors.toList());
}
}
18 changes: 13 additions & 5 deletions src/main/java/cz/sparko/boxitory/domain/BoxProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
@JsonInclude(JsonInclude.Include.NON_NULL)
public class BoxProvider {
private final String url;
private final String localUrl;
private final String name;
@JsonProperty("checksum_type")
private final String checksumType;
private final String checksum;

public BoxProvider(String url, String name, String checksumType, String checksum) {
public BoxProvider(String url, String localUrl, String name, String checksumType, String checksum) {
this.url = url;
this.localUrl = localUrl;
this.name = name;
this.checksumType = checksumType;
this.checksum = checksum;
Expand All @@ -25,32 +27,38 @@ public BoxProvider(String url, String name, String checksumType, String checksum
public String toString() {
return "BoxProvider{" +
"url='" + url + '\'' +
", localUrl='" + localUrl + '\'' +
", name='" + name + '\'' +
", checksumType='" + checksumType + '\'' +
", checksum='" + checksum + '\'' +
", checksum='" + checksum + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BoxProvider that = (BoxProvider) o;
return Objects.equals(url, that.url) &&
Objects.equals(localUrl, that.localUrl) &&
Objects.equals(name, that.name) &&
Objects.equals(checksumType, that.checksumType) &&
Objects.equals(checksum, that.checksum);
}

@Override
public int hashCode() {
return Objects.hash(url, name, checksumType, checksum);
return Objects.hash(url, localUrl, name, checksumType, checksum);
}

public String getUrl() {
return url;
}

public String getLocalUrl() {
return localUrl;
}

public String getName() {
return name;
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/cz/sparko/boxitory/model/BoxStream.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cz.sparko.boxitory.model;

import java.io.IOException;
import java.io.InputStream;

public interface BoxStream {
String getFilename();

InputStream getStream() throws IOException;
}
25 changes: 25 additions & 0 deletions src/main/java/cz/sparko/boxitory/model/FileBoxStream.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cz.sparko.boxitory.model;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FileBoxStream implements BoxStream {
private final File file;

public FileBoxStream(File file) {
this.file = file;
}

@Override
public String getFilename() {
return file.getName();
}

@Override
public InputStream getStream() throws IOException {
return new BufferedInputStream(new FileInputStream(file));
}
}
34 changes: 32 additions & 2 deletions src/main/java/cz/sparko/boxitory/service/BoxRepository.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cz.sparko.boxitory.service;

import cz.sparko.boxitory.domain.Box;
import cz.sparko.boxitory.model.BoxStream;

import java.util.List;
import java.util.Optional;
Expand All @@ -16,9 +17,38 @@ public interface BoxRepository {

/**
* Returns {@link List} of names of available {@link Box}es. Call {@link BoxRepository#getBox(String)} with any of
* returned name should get valid result.
* returned name should get full {@link Box} instance.
*
* @return names of available {@link Box}es
*/
List<String> getBoxes();
List<String> getBoxNames();

/**
* Finds all available and valid boxes and provides full {@link Box} instances of them.
*
* @return list of available {@link Box}es
*/
List<Box> getBoxes();

/**
* Provides byte stream of box found by given parameters.
*
* @param boxName name of box to find
* @param boxProvider provider of box to find
* @param boxVersion version of box to find
* @return {@link BoxStream} found by given parameters, {@link Optional#empty} when box file not found
* @throws cz.sparko.boxitory.conf.NotFoundException when box does not exist or don't have given provider or dont
* have given version
*/
Optional<BoxStream> getBoxStream(String boxName, String boxProvider, String boxVersion);

/**
* Gets latest version of box with given name and provider
*
* @param boxName name of box to find
* @param boxProvider provider of box to find
* @return latest version of box with provider
* @throws {@link cz.sparko.boxitory.conf.NotFoundException} when box does not exist of does not have given provider
*/
String latestVersionOfBox(String boxName, String boxProvider);
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
package cz.sparko.boxitory.service.filesystem;

import cz.sparko.boxitory.conf.AppProperties;
import cz.sparko.boxitory.conf.NotFoundException;
import cz.sparko.boxitory.domain.Box;
import cz.sparko.boxitory.domain.BoxProvider;
import cz.sparko.boxitory.domain.BoxVersion;
import cz.sparko.boxitory.model.BoxStream;
import cz.sparko.boxitory.model.CalculatedChecksumCounter;
import cz.sparko.boxitory.model.FileBoxStream;
import cz.sparko.boxitory.service.BoxRepository;
import cz.sparko.boxitory.service.DescriptionProvider;
import cz.sparko.boxitory.service.HashService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -43,7 +53,7 @@ public FilesystemBoxRepository(AppProperties appProperties,
}

@Override
public List<String> getBoxes() {
public List<String> getBoxNames() {
return listPotentialBoxDirs()
.filter(this::containsValidBoxFile)
.map(File::getName)
Expand All @@ -67,6 +77,55 @@ public Optional<Box> getBox(String boxName) {
}
}

@Override
public List<Box> getBoxes() {
return getBoxNames().stream()
.map(this::getBox)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}

@Override
public Optional<BoxStream> getBoxStream(String boxName, String boxProvider, String boxVersion) {
Box box = getBox(boxName).orElseThrow(() -> NotFoundException.boxNotFound(boxName));
BoxVersion version = getBoxVersion(box, boxVersion);
BoxProvider provider = getBoxVersionProvider(version, boxProvider);

File boxFile = new File(provider.getLocalUrl());
if (boxFile.exists() && boxFile.isFile()) {
return Optional.of(new FileBoxStream(new File(provider.getLocalUrl())));
} else {
return Optional.empty();
}
}

@Override
public String latestVersionOfBox(String boxName, String boxProvider) {
return getBox(boxName)
.orElseThrow(() -> NotFoundException.boxNotFound(boxName))
.getVersions().stream().sorted(BoxVersion.VERSION_COMPARATOR.reversed())
.filter(v -> v.getProviders().stream().anyMatch(p -> p.getName().equals(boxProvider)))
.findFirst()
.orElseThrow(() -> NotFoundException.boxNotFound(boxName))
.getVersion();
}

private BoxVersion getBoxVersion(Box box, String boxVersion) {
return box.getVersions().stream()
.filter(v -> v.getVersion().equals(boxVersion))
.findFirst()
.orElseThrow(() -> NotFoundException.boxVersionNotFound(box.getName(), boxVersion));
}

private BoxProvider getBoxVersionProvider(BoxVersion boxVersion, String boxProvider) {
return boxVersion.getProviders().stream()
.filter(p -> p.getName().equals(boxProvider))
.findFirst()
.orElseThrow(() -> NotFoundException.boxVersionProviderNotFound(
boxVersion.getDescription(), boxVersion.getVersion(), boxProvider));
}

private Optional<File> getBoxDir(String boxName) {
return listPotentialBoxDirs()
.filter(File::isDirectory)
Expand Down Expand Up @@ -157,6 +216,7 @@ private BoxProvider createBoxProviderFromFile(File file, CalculatedChecksumCount
}
return new BoxProvider(
hostPrefix + file.getAbsolutePath(),
file.getAbsolutePath(),
provider,
hashService.getHashType(),
hashService.getChecksum(
Expand Down
Loading

0 comments on commit 917aa2a

Please sign in to comment.