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

46 latest download #48

Merged
merged 7 commits into from
Oct 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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