Rewrote most of the project. Changed Spock to JUnit

This commit is contained in:
Artur Kmieckowiak 2020-02-12 20:30:18 +01:00
parent 8210ac6724
commit 1acd2fa781
47 changed files with 1852 additions and 0 deletions

39
build.gradle Normal file
View File

@ -0,0 +1,39 @@
plugins {
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'pl.edu.amu.wmi'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'com.auth0:java-jwt:3.4.0'
implementation 'org.openpnp:opencv:3.2.0-0'
// developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation('org.junit.jupiter:junit-jupiter-api')
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine')
testCompile('org.junit.jupiter:junit-jupiter-params')
}
test {
useJUnitPlatform{
includeEngines 'junit-jupiter'
}
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
gradlew vendored Executable file
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

1
settings.gradle Normal file
View File

@ -0,0 +1 @@
rootProject.name = 'bookapi'

View File

@ -0,0 +1,19 @@
package pl.edu.amu.wmi.bookapi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.*;
import org.springframework.security.crypto.bcrypt.*;
@SpringBootApplication
public class BookapiApplication {
public static void main(String[] args) {
SpringApplication.run(BookapiApplication.class, args);
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,73 @@
package pl.edu.amu.wmi.bookapi.api;
import org.apache.coyote.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import pl.edu.amu.wmi.bookapi.api.dto.BookDto;
import pl.edu.amu.wmi.bookapi.service.BookService;
@RestController
@RequestMapping("/api/books")
public class BookController {
BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping
public ResponseEntity getBooks() {
return ResponseEntity.ok().body(bookService.findAllForUser(
getUserName()
));
}
@GetMapping("/public")
public ResponseEntity getAllBooks() {
return ResponseEntity.ok().body(
bookService.findAll()
);
}
@PatchMapping("/{bookId}")
public ResponseEntity updateBook(@RequestBody BookDto bookDto, @PathVariable String bookId) {
String username = getUserName();
return ResponseEntity.ok(
bookService.updateBook(bookId ,username, bookDto)
);
}
@DeleteMapping("/{bookId}")
public ResponseEntity deleteBook(@PathVariable String bookId) {
bookService.deleteBook(
getUserName(),
bookId
);
return ResponseEntity.ok().build();
}
@PostMapping
public ResponseEntity addBook(@RequestBody BookDto bookDto) {
System.out.println("Save book");
bookService.saveBook(getUserName(), bookDto);
return ResponseEntity.ok().build();
}
@PostMapping("/image")
public ResponseEntity addBookAsImage(
@RequestParam("file") MultipartFile imageFile,
@RequestParam("author") String author,
@RequestParam("title") String title) throws Exception {
bookService.handleImageUpload(imageFile, author, title);
return ResponseEntity.ok().build();
}
private String getUserName() {
return "admin";
}
}

View File

@ -0,0 +1,50 @@
package pl.edu.amu.wmi.bookapi.api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import pl.edu.amu.wmi.bookapi.api.dto.MessageDto;
import pl.edu.amu.wmi.bookapi.service.MessageService;
@RestController
@RequestMapping("/api/messages")
public class MessageController {
private MessageService messageService;
@Autowired
public MessageController(MessageService messageService) {
this.messageService = messageService;
}
@GetMapping
public ResponseEntity listThreads() {
return ResponseEntity.ok(
messageService.getThreads(
getUserId()
)
);
}
@PostMapping
public ResponseEntity createMessage(@RequestBody MessageDto messageDto) {
messageService.createMessage(
messageDto.getContent(),
messageDto.getAuthor(),
messageDto.getRecipient()
);
return ResponseEntity.ok().build();
}
@GetMapping("/{threadId}")
public ResponseEntity getMessagesInThread(@PathVariable String threadId) {
return ResponseEntity.ok(messageService.getMessagesInThread(threadId, getUserId()));
}
private String getUserId() {
return "admin";
}
}

View File

@ -0,0 +1,26 @@
package pl.edu.amu.wmi.bookapi.api;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class SecurityInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}

View File

@ -0,0 +1,33 @@
package pl.edu.amu.wmi.bookapi.api;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.*;
import org.springframework.web.bind.annotation.*;
import pl.edu.amu.wmi.bookapi.exceptions.RegisterException;
import pl.edu.amu.wmi.bookapi.models.*;
import pl.edu.amu.wmi.bookapi.repositories.*;
@RestController
@RequestMapping("/users")
public class UserController {
private UserRepository userRepository;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public UserController(UserRepository userRepository,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@PostMapping("/sign-up")
public void signUp(@RequestBody UserDocument user) throws RegisterException {
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
try {
userRepository.save(user);
} catch (DuplicateKeyException e) {
throw new RegisterException("Login already in use");
}
}
}

View File

@ -0,0 +1,37 @@
package pl.edu.amu.wmi.bookapi.api.dto;
public class BookDto {
private String ean;
private String author;
private String title;
public BookDto(String ean, String author, String title) {
this.ean = ean;
this.author = author;
this.title = title;
}
public String getEan() {
return ean;
}
public void setEan(String ean) {
this.ean = ean;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@ -0,0 +1,37 @@
package pl.edu.amu.wmi.bookapi.api.dto;
public class MessageDto {
private String content;
private String author;
private String recipient;
public MessageDto(String content, String author, String recipient) {
this.content = content;
this.author = author;
this.recipient = recipient;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getRecipient() {
return recipient;
}
public void setRecipient(String recipient) {
this.recipient = recipient;
}
}

View File

@ -0,0 +1,4 @@
package pl.edu.amu.wmi.bookapi.api.dto;
public class PatchBookRequest {
}

View File

@ -0,0 +1,18 @@
package pl.edu.amu.wmi.bookapi.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
@Configuration
public class DateTimeProvider {
@Bean
public Instant time() {
return Instant.now();
}
}

View File

@ -0,0 +1,15 @@
package pl.edu.amu.wmi.bookapi.exceptions;
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 ExceptionHandlerFilter {
@ExceptionHandler(RegisterException.class) public ResponseEntity handleDuplicateUserName() {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body("Cannot use this username");
}
}

View File

@ -0,0 +1,11 @@
package pl.edu.amu.wmi.bookapi.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class RegisterException extends Throwable {
public RegisterException(String message) {
super(message);
}
}

View File

@ -0,0 +1,75 @@
package pl.edu.amu.wmi.bookapi.models;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import pl.edu.amu.wmi.bookapi.api.dto.BookDto;
@Document
public class BookDocument {
@Id
private String id;
private String ownerUsername;
private String ean;
private String author;
private String title;
public BookDocument() {
}
public BookDocument(String ownerUsername, String ean, String author, String title) {
this.ownerUsername = ownerUsername;
this.ean = ean;
this.author = author;
this.title = title;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getOwnerUsername() {
return ownerUsername;
}
public void setOwnerUsername(String ownerUsername) {
this.ownerUsername = ownerUsername;
}
public String getEan() {
return ean;
}
public void setEan(String ean) {
this.ean = ean;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public static BookDocument from(String userName, BookDto bookDto) {
return new BookDocument(
userName,
bookDto.getEan(),
bookDto.getAuthor(),
bookDto.getTitle()
);
}
}

View File

@ -0,0 +1,7 @@
package pl.edu.amu.wmi.bookapi.models;
public enum BookGenre {
DRAMA,
SCIFI,
HORROR
}

View File

@ -0,0 +1,66 @@
package pl.edu.amu.wmi.bookapi.models;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Date;
@Document
public class MessageDocument {
@Id
private String id;
private String author;
private String recipient;
private Date createdAt;
private String content;
private String threadId;
public MessageDocument(String author, String recipient, Date createdAt, String content, String threadId) {
this.author = author;
this.recipient = recipient;
this.createdAt = createdAt;
this.content = content;
this.threadId = threadId;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getRecipient() {
return recipient;
}
public void setRecipient(String recipient) {
this.recipient = recipient;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getThreadId() {
return threadId;
}
public void setThreadId(String threadId) {
this.threadId = threadId;
}
}

View File

@ -0,0 +1,33 @@
package pl.edu.amu.wmi.bookapi.models;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
@Document
public class ThreadDocument {
@Id
private String id;
private List<String> participantsIds;
public ThreadDocument(List<String> participantsIds) {
this.participantsIds = participantsIds;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<String> getParticipantsIds() {
return participantsIds;
}
public void setParticipantsIds(List<String> participantsIds) {
this.participantsIds = participantsIds;
}
}

View File

@ -0,0 +1,55 @@
package pl.edu.amu.wmi.bookapi.models;
import org.springframework.data.annotation.*;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.*;
@Document
public class UserDocument {
@Id
private String id;
@Indexed(unique = true)
private String username;
private String password;
public UserDocument() {
}
public UserDocument(String username, String password) {
this.username = username;
this.password = password;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "UserDocument{" +
"id='" + id + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}

View File

@ -0,0 +1,12 @@
package pl.edu.amu.wmi.bookapi.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import pl.edu.amu.wmi.bookapi.api.dto.BookDto;
import pl.edu.amu.wmi.bookapi.models.BookDocument;
import java.util.List;
public interface BookRepository extends MongoRepository<BookDocument, String>, BookRepositoryCustom {
List<BookDocument> findAllByOwnerUsername(String ownerUsername);
}

View File

@ -0,0 +1,8 @@
package pl.edu.amu.wmi.bookapi.repositories;
import pl.edu.amu.wmi.bookapi.api.dto.BookDto;
import pl.edu.amu.wmi.bookapi.models.BookDocument;
public interface BookRepositoryCustom {
BookDocument updateBook(String bookId, String userId, BookDto bookDto);
}

View File

@ -0,0 +1,31 @@
package pl.edu.amu.wmi.bookapi.repositories;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import pl.edu.amu.wmi.bookapi.api.dto.BookDto;
import pl.edu.amu.wmi.bookapi.models.BookDocument;
public class BookRepositoryCustomImpl implements BookRepositoryCustom {
@Autowired
MongoTemplate mongoTemplate;
@Override
public BookDocument updateBook(String bookId, String userId, BookDto bookDto) {
Update update = new Update();
if(bookDto.getAuthor() != null) update.set("author", bookDto.getAuthor());
if(bookDto.getEan() != null) update.set("ean", bookDto.getEan());
if(bookDto.getTitle() != null) update.set("title", bookDto.getTitle());
return mongoTemplate.findAndModify(
new Query(
Criteria
.where("ownerUsername").is(userId)
.and("id").is(bookId)
), update, FindAndModifyOptions.options().returnNew(true), BookDocument.class
);
}
}

View File

@ -0,0 +1,12 @@
package pl.edu.amu.wmi.bookapi.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import pl.edu.amu.wmi.bookapi.models.MessageDocument;
import java.util.List;
public interface MessageRepository extends MongoRepository<MessageDocument, String> {
@Query("{ $and: [$or: [{ 'recipient' : ?1}, { 'author': ?1}], {'threadId' : ?0} ]}")
List<MessageDocument> findByUserAndThreadId(String threadId, String user);
}

View File

@ -0,0 +1,14 @@
package pl.edu.amu.wmi.bookapi.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import pl.edu.amu.wmi.bookapi.models.ThreadDocument;
import java.util.List;
public interface ThreadRepository extends MongoRepository<ThreadDocument, String>, ThreadRepositoryCustom {
// @Query("'participantsIds': { $in: [?0, ?1]}")
// List<ThreadDocument> findByParticipants(String participantId1, String participantId2);
@Query("{'participantsIds': {$in: [?0]}}")
List<ThreadDocument> findByParticipant(String participant);
}

View File

@ -0,0 +1,9 @@
package pl.edu.amu.wmi.bookapi.repositories;
import pl.edu.amu.wmi.bookapi.models.ThreadDocument;
import java.util.List;
public interface ThreadRepositoryCustom {
List<ThreadDocument> findByParticipants(String participantId1, String participantId2);
}

View File

@ -0,0 +1,31 @@
package pl.edu.amu.wmi.bookapi.repositories;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import pl.edu.amu.wmi.bookapi.models.ThreadDocument;
import java.util.List;
@Component
public class ThreadRepositoryCustomImpl implements ThreadRepositoryCustom {
MongoTemplate mongoTemplate;
@Autowired
public ThreadRepositoryCustomImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
@Override
public List<ThreadDocument> findByParticipants(String participantId1, String participantId2) {
Query query = new Query();
query.addCriteria(
Criteria.where("participantsIds").all(List.of(participantId1, participantId2))
);
return mongoTemplate.find(query, ThreadDocument.class);
}
}

View File

@ -0,0 +1,8 @@
package pl.edu.amu.wmi.bookapi.repositories;
import org.springframework.data.mongodb.repository.*;
import pl.edu.amu.wmi.bookapi.models.*;
public interface UserRepository extends MongoRepository<UserDocument, String> {
UserDocument findByUsername(String username);
}

View File

@ -0,0 +1,62 @@
package pl.edu.amu.wmi.bookapi.security;
import com.auth0.jwt.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import org.springframework.security.core.userdetails.*;
import org.springframework.security.web.authentication.*;
import pl.edu.amu.wmi.bookapi.models.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import static com.auth0.jwt.algorithms.Algorithm.*;
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public static final String SECRET = "SecretKeyToGenJWTs";
public static final long EXPIRATION_TIME = 864_000_000; // 10 days
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
public static final String SIGN_UP_URL = "/users/sign-up";
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
UserDocument creds = new ObjectMapper()
.readValue(req.getInputStream(), UserDocument.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = JWT.create()
.withSubject(((User) auth.getPrincipal()).getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(HMAC512(SECRET.getBytes()));
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}

View File

@ -0,0 +1,55 @@
package pl.edu.amu.wmi.bookapi.security;
import com.auth0.jwt.*;
import com.auth0.jwt.algorithms.*;
import org.springframework.security.authentication.*;
import org.springframework.security.core.context.*;
import org.springframework.security.web.authentication.www.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import static pl.edu.amu.wmi.bookapi.security.JWTAuthenticationFilter.*;
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
System.out.println();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}

View File

@ -0,0 +1,26 @@
package pl.edu.amu.wmi.bookapi.security;
import org.springframework.security.core.userdetails.*;
import org.springframework.stereotype.*;
import pl.edu.amu.wmi.bookapi.models.*;
import pl.edu.amu.wmi.bookapi.repositories.*;
import static java.util.Collections.*;
@Service
public class UserDetailsSecurityServiceImpl implements UserDetailsService {
private UserRepository userRepository;
public UserDetailsSecurityServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDocument applicationUser = userRepository.findByUsername(username);
if (applicationUser == null) {
throw new UsernameNotFoundException(username);
}
return new User(applicationUser.getUsername(), applicationUser.getPassword(), emptyList());
}
}

View File

@ -0,0 +1,55 @@
package pl.edu.amu.wmi.bookapi.security;
import org.springframework.context.annotation.*;
import org.springframework.http.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;
import org.springframework.security.config.http.*;
import org.springframework.security.crypto.bcrypt.*;
import org.springframework.web.cors.*;
import static pl.edu.amu.wmi.bookapi.security.JWTAuthenticationFilter.*;
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsSecurityServiceImpl userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsSecurityServiceImpl userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/books", "/api/messages/", "/api/messages/*", "/api/books/public").permitAll()
.antMatchers(HttpMethod.DELETE, "/api/books/*").permitAll()
.antMatchers(HttpMethod.PATCH, "/api/books/*").permitAll()
.antMatchers(HttpMethod.POST,
SIGN_UP_URL,
"/api/books",
"/api/books/image",
"/api/messages").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}

View File

@ -0,0 +1,50 @@
package pl.edu.amu.wmi.bookapi.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import pl.edu.amu.wmi.bookapi.api.dto.BookDto;
import pl.edu.amu.wmi.bookapi.models.BookDocument;
import pl.edu.amu.wmi.bookapi.repositories.BookRepository;
import java.util.List;
@Service
public class BookService {
BookRepository bookRepository;
ImageProcessingService imageProcessingService;
@Autowired
public BookService(BookRepository bookRepository, ImageProcessingService imageProcessingService) {
this.imageProcessingService = imageProcessingService;
this.bookRepository = bookRepository;
}
public void deleteBook(String userName, String bookId) {
bookRepository.deleteById(bookId);
}
public List<BookDocument> findAllForUser(String userName) {
return bookRepository.findAllByOwnerUsername(userName);
}
public void saveBook(String userName, BookDto bookDto) {
System.out.println("saving");
System.out.println(bookRepository.save(BookDocument.from(userName, bookDto)));
}
public void handleImageUpload(MultipartFile imageFile, String author, String title) throws Exception {
String detectedEan = imageProcessingService.getDecodedEan(imageFile);
if (detectedEan == null) detectedEan = "Test";
saveBook("admin", new BookDto(detectedEan, author, title));
}
public List<BookDocument> findAll() {
return bookRepository.findAll();
}
public BookDocument updateBook(String bookId, String userId, BookDto bookDto) {
return bookRepository.updateBook(bookId, userId, bookDto);
}
}

View File

@ -0,0 +1,59 @@
package pl.edu.amu.wmi.bookapi.service;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;
import static java.lang.System.loadLibrary;
import static org.opencv.imgproc.Imgproc.COLOR_BGR2GRAY;
@Service
public class ImageProcessingService {
public ImageProcessingService() {
}
public String getDecodedEan(MultipartFile imageFile) throws Exception {
String fileId = UUID.randomUUID().toString();
Path path = Path.of(new File(".").getCanonicalPath());
byte[] starting = imageFile.getBytes();
saveImg(starting, path, fileId, 1);
Mat mat = Imgcodecs.imdecode(new MatOfByte(starting), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
Mat gray = new Mat();
Imgproc.cvtColor(mat, gray, COLOR_BGR2GRAY);
byte[] grayed = mat2byteArr(gray);
saveImg(grayed, path, fileId, 2);
return "";
}
private byte[] mat2byteArr(Mat mat) {
byte[] bytes = new byte[0];
mat.get(0, 0, bytes);
return bytes;
}
private void saveImg(byte[] bytes, Path path, String imageName, Integer version) throws IOException {
Files.write(Path.of(path.toString(), "/" + imageName + "__v" + version + ".png"), bytes);
}
}

View File

@ -0,0 +1,76 @@
package pl.edu.amu.wmi.bookapi.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pl.edu.amu.wmi.bookapi.config.DateTimeProvider;
import pl.edu.amu.wmi.bookapi.models.MessageDocument;
import pl.edu.amu.wmi.bookapi.models.ThreadDocument;
import pl.edu.amu.wmi.bookapi.repositories.MessageRepository;
import pl.edu.amu.wmi.bookapi.repositories.ThreadRepository;
import java.util.Date;
import java.util.List;
@Service
public class MessageService {
DateTimeProvider dateTimeProvider;
ThreadRepository threadRepository;
MessageRepository messageRepository;
@Autowired
public MessageService(ThreadRepository threadRepository,
MessageRepository messageRepository,
DateTimeProvider dateTimeProvider) {
this.dateTimeProvider = dateTimeProvider;
this.threadRepository = threadRepository;
this.messageRepository = messageRepository;
}
public List<ThreadDocument> getThreads(String user) {
return threadRepository.findByParticipant(user);
}
public List<MessageDocument> getMessagesInThread(String threadId, String userId) {
return messageRepository.findByUserAndThreadId(threadId, userId);
}
public void createMessage(String content, String author, String recipient) {
String threadId = null;
List<ThreadDocument> foundThreads = threadRepository.findByParticipants(author, recipient);
if(foundThreads.size() == 0) {threadId = createThread(author, recipient);} else {
threadId = foundThreads.get(0).getId();
}
createMessageInThread(
threadId,
author,
recipient,
content,
Date.from(dateTimeProvider.time())
);
}
private String createThread(String author, String recipient) {
ThreadDocument savedThread = threadRepository.save(new ThreadDocument(List.of(author, recipient)));
return savedThread.getId();
}
private void createMessageInThread(
String threadId,
String author,
String recipient,
String content,
Date date) {
messageRepository.save(
new MessageDocument(
author,
recipient,
date,
content,
threadId
)
);
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,21 @@
package pl.edu.amu.wmi.bookapi;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.*;
import pl.edu.amu.wmi.bookapi.models.*;
@SpringBootTest
class BookapiApplicationTests {
@Autowired
MongoTemplate mongoTemplate;
@Test
void contextLoads() {
mongoTemplate.save(new UserDocument("test", "test"));
System.out.println(mongoTemplate.findAll(UserDocument.class));
}
}

View File

@ -0,0 +1,31 @@
package pl.edu.amu.wmi.bookapi.Integration;
import com.mongodb.*;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.*;
import org.springframework.data.mongodb.core.*;
import org.springframework.data.mongodb.core.query.*;
import pl.edu.amu.wmi.bookapi.models.*;
import java.util.*;
public class BaseInt {
public MongoTemplate mongoTemplate;
@Autowired
public BaseInt(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
@BeforeAll
public void cleanUp() {
List.of(
UserDocument.class
).forEach(it -> this.mongoTemplate.remove(it));
System.out.println("Removing classes in cleanUp method");
}
}

View File

@ -0,0 +1,123 @@
package pl.edu.amu.wmi.bookapi.Integration.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.web.servlet.MockMvc;
import pl.edu.amu.wmi.bookapi.fixtures.IntegrationTestUtil;
import pl.edu.amu.wmi.bookapi.fixtures.api.BookControllerRequest;
import pl.edu.amu.wmi.bookapi.models.BookDocument;
import java.util.List;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class })
public class BookControllerInt {
@Autowired
MongoTemplate mongoTemplate;
@Autowired
MockMvc mvc;
@Autowired
IntegrationTestUtil testUtil;
private BookControllerRequest bookRequests;
@BeforeEach
void cleanCollections() {
this.bookRequests = new BookControllerRequest(mvc, new ObjectMapper());
testUtil.cleanCollections();
}
@Test
void should_add_a_book() throws Exception {
this.bookRequests.addBook("admin", "12345", "auth", "title")
.andExpect(status().isOk());
assertEquals(mongoTemplate.findAll(BookDocument.class).get(0).getEan(), "12345");
}
@Test
void should_list_books_for_user() throws Exception {
this.bookRequests.addBook("admin", "12345", "auth", "title")
.andExpect(status().isOk());
this.bookRequests.addBook("admin", "12345", "auth", "title")
.andExpect(status().isOk());
bookRequests.getBooksForUser("admin")
.andExpect(status().isOk())
.andExpect(jsonPath("$.*.ownerUsername", equalTo(List.of("admin", "admin"))));
}
@Test
void should_delete_a_book() throws Exception {
this.bookRequests.addBook("admin", "12345", "auth", "title")
.andExpect(status().isOk());
BookDocument foundBook = mongoTemplate.findAll(BookDocument.class).get(0);
assertEquals(foundBook.getEan(), "12345");
this.bookRequests.deleteBook("admin", foundBook.getId());
assertEquals(0, mongoTemplate.findAll(BookDocument.class).size());
}
@Test
void should_list_all_books() throws Exception {
this.bookRequests.addBook("admin1", "123451", "auth1", "title1")
.andExpect(status().isOk());
this.bookRequests.addBook("admin2", "123452", "auth2", "title2")
.andExpect(status().isOk());
this.bookRequests.addBook("admin3", "123453", "auth3", "title3")
.andExpect(status().isOk());
this.bookRequests.getAllBooks()
.andExpect(status().isOk())
.andExpect(jsonPath("$").isArray());
}
@Test
void should_update_a_book() throws Exception {
this.bookRequests.addBook("admin", "12345", "1", "2")
.andDo(print());
BookDocument foundBook = mongoTemplate.findAll(BookDocument.class).get(0);
String bookId = foundBook.getId();
ObjectMapper objectMapper = new ObjectMapper();
this.bookRequests.updateBook(bookId, "admin",
"{\n" +
" \"ean\": " + objectMapper.writeValueAsString("ean") + ",\n" +
" \"author\": " + objectMapper.writeValueAsString("author") + ",\n" +
" \"title\": " + objectMapper.writeValueAsString("title") + "\n" +
"}").andExpect(status().isOk());
BookDocument foundBookAfterUpdate = mongoTemplate.findAll(BookDocument.class).get(0);
assertEquals("ean", foundBookAfterUpdate.getEan());
assertEquals("author", foundBookAfterUpdate.getAuthor());
assertEquals("title", foundBookAfterUpdate.getTitle());
// And should allow for partial update
this.bookRequests.updateBook(bookId, "admin",
"{\n" +
" \"ean\": " + objectMapper.writeValueAsString("ean-1-modified") + "\n" +
"}").andExpect(status().isOk());
BookDocument foundBookAfterPartialUpdate = mongoTemplate.findAll(BookDocument.class).get(0);
assertEquals("ean-1-modified", foundBookAfterPartialUpdate.getEan());
assertEquals("author", foundBookAfterPartialUpdate.getAuthor());
assertEquals("title", foundBookAfterPartialUpdate.getTitle());
}
}

View File

@ -0,0 +1,97 @@
package pl.edu.amu.wmi.bookapi.Integration.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.web.servlet.MockMvc;
import pl.edu.amu.wmi.bookapi.fixtures.IntegrationTestUtil;
import pl.edu.amu.wmi.bookapi.fixtures.api.BookControllerRequest;
import pl.edu.amu.wmi.bookapi.fixtures.api.MessageControllerRequests;
import pl.edu.amu.wmi.bookapi.models.MessageDocument;
import pl.edu.amu.wmi.bookapi.models.ThreadDocument;
import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class })
public class MessageControllerInt {
@Autowired
MongoTemplate mongoTemplate;
@Autowired
MockMvc mvc;
@Autowired
IntegrationTestUtil testUtil;
private MessageControllerRequests messageControllerRequests;
@BeforeEach
void cleanCollections() {
this.messageControllerRequests = new MessageControllerRequests(mvc, new ObjectMapper());
testUtil.cleanCollections();
}
@Test
void should_start_new_thread_if_does_not_exist() throws Exception {
messageControllerRequests.postMessage(
"content",
"id-1",
"id-2"
).andExpect(status().isOk());;
assertEquals(1, mongoTemplate.findAll(ThreadDocument.class).size());
assertEquals(1, mongoTemplate.findAll(MessageDocument.class).size());
}
@Test
void if_thread_exists_it_should_not_create_new() throws Exception {
messageControllerRequests.postMessage(
"content",
"id-1",
"id-2"
).andExpect(status().isOk());
assertEquals(1, mongoTemplate.findAll(ThreadDocument.class).size());
assertEquals(1, mongoTemplate.findAll(MessageDocument.class).size());
messageControllerRequests.postMessage(
"content",
"id-1",
"id-2"
).andExpect(status().isOk());;
assertEquals(1, mongoTemplate.findAll(ThreadDocument.class).size());
assertEquals(2, mongoTemplate.findAll(MessageDocument.class).size());
messageControllerRequests.postMessage(
"content",
"id-1",
"id-5"
).andExpect(status().isOk());
assertEquals(2, mongoTemplate.findAll(ThreadDocument.class).size());
assertEquals(3, mongoTemplate.findAll(MessageDocument.class).size());
}
@Test
void it_should_allow_to_send_a_message() throws Exception {
messageControllerRequests.postMessage(
"content",
"id-1",
"id-5"
).andExpect(status().isOk());
assertEquals(1, mongoTemplate.findAll(ThreadDocument.class).size());
assertEquals(1, mongoTemplate.findAll(MessageDocument.class).size());
}
}

View File

@ -0,0 +1,57 @@
package pl.edu.amu.wmi.bookapi.Integration.api;
import com.fasterxml.jackson.databind.*;
import org.junit.*;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.context.*;
import org.springframework.data.mongodb.core.*;
import org.springframework.test.web.servlet.*;
import pl.edu.amu.wmi.bookapi.fixtures.*;
import pl.edu.amu.wmi.bookapi.fixtures.api.*;
import pl.edu.amu.wmi.bookapi.models.UserDocument;
import static org.junit.Assert.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class UserControllerInt{
@Autowired
MongoTemplate mongoTemplate;
@Autowired
MockMvc mvc;
@Autowired
IntegrationTestUtil testUtil;
private UserControllerRequests userRequests;
@BeforeEach
void cleanCollections() {
this.userRequests = new UserControllerRequests(mvc, new ObjectMapper());
testUtil.cleanCollections();
}
@Test
void should_register_new_user() throws Exception {
userRequests.registerUser("Abc", "def")
.andExpect(status().isOk());
assertEquals(mongoTemplate.findAll(UserDocument.class).size(), 1);
}
@Test
void user_should_not_be_able_to_create_account_with_already_existing_login() throws Exception {
userRequests.registerUser("a", "def")
.andExpect(status().isOk());
userRequests.registerUser("a", "fed")
.andExpect(status().is4xxClientError());
assertEquals(mongoTemplate.findAll(UserDocument.class).size(), 1);
}
}

View File

@ -0,0 +1,24 @@
package pl.edu.amu.wmi.bookapi.fixtures;
import org.springframework.beans.factory.annotation.*;
import org.springframework.data.mongodb.core.*;
import org.springframework.data.mongodb.core.query.*;
import org.springframework.stereotype.*;
import pl.edu.amu.wmi.bookapi.models.*;
import java.util.*;
@Component
public class IntegrationTestUtil {
@Autowired
MongoTemplate mongoTemplate;
public void cleanCollections() {
List.of(
UserDocument.class,
BookDocument.class,
MessageDocument.class,
ThreadDocument.class
).forEach(it -> mongoTemplate.remove(new Query(), it));
}
}

View File

@ -0,0 +1,54 @@
package pl.edu.amu.wmi.bookapi.fixtures.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import pl.edu.amu.wmi.bookapi.models.BookDocument;
import java.net.URI;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
public class BookControllerRequest {
private final MockMvc mvc;
private final ObjectMapper objectMapper;
public BookControllerRequest(MockMvc mvc, ObjectMapper objectMapper) {
this.mvc = mvc;
this.objectMapper = objectMapper;
}
public ResultActions getBooksForUser(String userName) throws Exception {
return mvc.perform(get("/api/books"));
}
public ResultActions getAllBooks() throws Exception {
return mvc.perform(get("/api/books/public"));
}
public ResultActions updateBook(String bookId, String userId, String jsonBody) throws Exception {
return mvc.perform(patch("/api/books/" + bookId)
.contentType(MediaType.APPLICATION_JSON)
.content(jsonBody));
}
public ResultActions deleteBook(String userName, String bookId) throws Exception {
return mvc.perform(delete("/api/books/" + bookId));
}
public ResultActions addBook(
String userName,
String ean,
String author,
String title
) throws Exception {
return mvc.perform(post(URI.create("/api/books"))
.contentType(MediaType.APPLICATION_JSON)
.content("{\n" +
" \"ean\": " + objectMapper.writeValueAsString(ean) + ",\n" +
" \"author\": " + objectMapper.writeValueAsString(author) + ",\n" +
" \"title\": " + objectMapper.writeValueAsString(title) + "\n" +
"}"));
}
}

View File

@ -0,0 +1,45 @@
package pl.edu.amu.wmi.bookapi.fixtures.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import pl.edu.amu.wmi.bookapi.api.dto.MessageDto;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
public class MessageControllerRequests {
private final MockMvc mvc;
private final ObjectMapper objectMapper;
public MessageControllerRequests(MockMvc mvc, ObjectMapper objectMapper) {
this.mvc = mvc;
this.objectMapper = objectMapper;
}
public ResultActions getThreads(String userId) throws Exception {
return mvc.perform(get("/api/messages"));
}
public ResultActions getMessages(String userId, String threadId) throws Exception {
return mvc.perform(get("/api/messages" + threadId));
}
public ResultActions postMessage(String content, String author, String recipient) throws Exception {
System.out.println("Content");
System.out.println("{\n" +
"\"content\": " + objectMapper.writeValueAsString(content) + ",\n" +
"\"author\": " + objectMapper.writeValueAsString(author) + ",\n" +
"\"recipient\": " + objectMapper.writeValueAsString(recipient) + "\n" +
"}");
return mvc.perform(post("/api/messages")
.contentType(MediaType.APPLICATION_JSON)
.content("{\n" +
"\"content\": " + objectMapper.writeValueAsString(content) + ",\n" +
"\"author\": " + objectMapper.writeValueAsString(author) + ",\n" +
"\"recipient\": " + objectMapper.writeValueAsString(recipient) + "\n" +
"}")
);
}
}

View File

@ -0,0 +1,31 @@
package pl.edu.amu.wmi.bookapi.fixtures.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import java.net.URI;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
public class UserControllerRequests {
private final MockMvc mvc;
private final ObjectMapper objectMapper;
public UserControllerRequests(MockMvc mvc, ObjectMapper objectMapper) {
this.mvc = mvc;
this.objectMapper = objectMapper;
}
public ResultActions registerUser(String userName, String password) throws Exception {
return mvc.perform(post(URI.create("/users/sign-up"))
.contentType(MediaType.APPLICATION_JSON)
.content("{\n" +
" \"username\": " + objectMapper.writeValueAsString(userName) + ",\n" +
" \"password\": " + objectMapper.writeValueAsString(password) + "\n" +
"}"));
}
}