initial commit

This commit is contained in:
Lee
2024-04-23 00:43:05 +01:00
commit 749135a91f
18 changed files with 571 additions and 0 deletions

View 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
}
}

View 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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 { }

View 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;
}

View File

@ -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();
}
}

View File

@ -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> { }

View 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();
}
}