initial commit
This commit is contained in:
30
src/main/java/cc/fascinated/Main.java
Normal file
30
src/main/java/cc/fascinated/Main.java
Normal file
@ -0,0 +1,30 @@
|
||||
package cc.fascinated;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
|
||||
@Log4j2(topic = "Main")
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
@SneakyThrows
|
||||
public static void main(String[] args) {
|
||||
File config = new File("application.yml");
|
||||
if (!config.exists()) { // Saving the default config if it doesn't exist locally
|
||||
Files.copy(Objects.requireNonNull(Main.class.getResourceAsStream("/application.yml")), config.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
log.info("Saved the default configuration to '{}', please re-launch the application", // Log the default config being saved
|
||||
config.getAbsolutePath()
|
||||
);
|
||||
return;
|
||||
}
|
||||
log.info("Found configuration at '{}'", config.getAbsolutePath()); // Log the found config
|
||||
|
||||
SpringApplication.run(Main.class, args); // Start the application
|
||||
}
|
||||
}
|
40
src/main/java/cc/fascinated/controller/PasteController.java
Normal file
40
src/main/java/cc/fascinated/controller/PasteController.java
Normal file
@ -0,0 +1,40 @@
|
||||
package cc.fascinated.controller;
|
||||
|
||||
import cc.fascinated.model.Paste;
|
||||
import cc.fascinated.service.PasteService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/")
|
||||
public class PasteController {
|
||||
|
||||
private final PasteService pasteService;
|
||||
|
||||
@Autowired
|
||||
public PasteController(PasteService pasteService) {
|
||||
this.pasteService = pasteService;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/")
|
||||
public ResponseEntity<?> home() {
|
||||
return ResponseEntity.ok(Map.of(
|
||||
"status", "OK"
|
||||
));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/upload")
|
||||
public ResponseEntity<?> uploadPaste(@RequestBody String content) {
|
||||
String id = pasteService.createPaste(content);
|
||||
return ResponseEntity.ok(Map.of("id", id));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/{id}")
|
||||
public ResponseEntity<Paste> getPaste(@PathVariable String id) {
|
||||
Paste paste = pasteService.getPaste(id);
|
||||
return ResponseEntity.ok(paste);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package cc.fascinated.exception;
|
||||
|
||||
import cc.fascinated.model.response.ErrorResponse;
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
|
||||
@ControllerAdvice
|
||||
public final class ExceptionControllerAdvice {
|
||||
|
||||
/**
|
||||
* Handle a raised exception.
|
||||
*
|
||||
* @param ex the raised exception
|
||||
* @return the error response
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<?> handleException(@NonNull Exception ex) {
|
||||
HttpStatus status = null; // Get the HTTP status
|
||||
if (ex instanceof NoResourceFoundException) { // Not found
|
||||
status = HttpStatus.NOT_FOUND;
|
||||
} else if (ex instanceof UnsupportedOperationException) { // Not implemented
|
||||
status = HttpStatus.NOT_IMPLEMENTED;
|
||||
}
|
||||
if (ex.getClass().isAnnotationPresent(ResponseStatus.class)) { // Get from the @ResponseStatus annotation
|
||||
status = ex.getClass().getAnnotation(ResponseStatus.class).value();
|
||||
}
|
||||
String message = ex.getLocalizedMessage(); // Get the error message
|
||||
if (message == null) { // Fallback
|
||||
message = "An internal error has occurred.";
|
||||
}
|
||||
// Print the stack trace if no response status is present
|
||||
if (status == null) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
if (status == null) { // Fallback to 500
|
||||
status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
return new ResponseEntity<>(new ErrorResponse(status, message), status);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package cc.fascinated.exception.impl;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public class BadRequestException extends RuntimeException {
|
||||
|
||||
public BadRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cc.fascinated.exception.impl;
|
||||
|
||||
import lombok.experimental.StandardException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@StandardException
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public class ResourceNotFoundException extends RuntimeException { }
|
22
src/main/java/cc/fascinated/model/Paste.java
Normal file
22
src/main/java/cc/fascinated/model/Paste.java
Normal file
@ -0,0 +1,22 @@
|
||||
package cc.fascinated.model;
|
||||
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class Paste {
|
||||
/**
|
||||
* The id of the paste.
|
||||
*/
|
||||
@Id @NonNull
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* The content of the paste.
|
||||
*/
|
||||
@NonNull
|
||||
private final String content;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package cc.fascinated.model.response;
|
||||
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Getter @ToString @EqualsAndHashCode
|
||||
public class ErrorResponse {
|
||||
/**
|
||||
* The status code of this error.
|
||||
*/
|
||||
@NonNull
|
||||
private final HttpStatus status;
|
||||
|
||||
/**
|
||||
* The HTTP code of this error.
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* The message of this error.
|
||||
*/
|
||||
@NonNull private final String message;
|
||||
|
||||
/**
|
||||
* The timestamp this error occurred.
|
||||
*/
|
||||
@NonNull private final Date timestamp;
|
||||
|
||||
public ErrorResponse(@NonNull HttpStatus status, @NonNull String message) {
|
||||
this.status = status;
|
||||
code = status.value();
|
||||
this.message = message;
|
||||
timestamp = new Date();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cc.fascinated.repository;
|
||||
|
||||
import cc.fascinated.model.Paste;
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
|
||||
/**
|
||||
* A repository for {@link Paste}s.
|
||||
*/
|
||||
public interface PasteRepository extends MongoRepository<Paste, String> { }
|
57
src/main/java/cc/fascinated/service/PasteService.java
Normal file
57
src/main/java/cc/fascinated/service/PasteService.java
Normal file
@ -0,0 +1,57 @@
|
||||
package cc.fascinated.service;
|
||||
|
||||
import cc.fascinated.exception.impl.ResourceNotFoundException;
|
||||
import cc.fascinated.model.Paste;
|
||||
import cc.fascinated.repository.PasteRepository;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class PasteService {
|
||||
|
||||
/**
|
||||
* The {@link PasteRepository} instance.
|
||||
*/
|
||||
private final PasteRepository pasteRepository;
|
||||
|
||||
/**
|
||||
* The length of the paste id.
|
||||
*/
|
||||
@Value("${paste.id-length}")
|
||||
private int idLength;
|
||||
|
||||
@Autowired
|
||||
public PasteService(PasteRepository pasteRepository) {
|
||||
this.pasteRepository = pasteRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new paste with the specified content.
|
||||
*
|
||||
* @param content The content of the paste.
|
||||
* @return The id of the paste.
|
||||
*/
|
||||
public String createPaste(String content) {
|
||||
return pasteRepository.save(new Paste(RandomStringUtils.randomAlphabetic(idLength), content)).getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of the paste with the specified id.
|
||||
*
|
||||
* @param id The id of the paste.
|
||||
* @return The content of the paste.
|
||||
*/
|
||||
public Paste getPaste(String id) {
|
||||
Optional<Paste> paste = pasteRepository.findById(id);
|
||||
|
||||
// The paste does not exist.
|
||||
if (paste.isEmpty()) {
|
||||
throw new ResourceNotFoundException("Paste with id '%s' not found".formatted(id));
|
||||
}
|
||||
return paste.get();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user