diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..964a1cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +/target/ +/.mvn/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ diff --git a/README.md b/README.md index 81e616e..072ad9f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,19 @@ +# Superbid Webservices - Desafio Blog REST API + +* [Quer fazer parte da Superbid Webservices?](#quer-fazer-parte-da-superbid-webservices) +* [Questionário respondido](#questionário) +* [Teste prático (prazo 3 dias)](#teste-prático-prazo-3-dias) +* [Tecnologias utilizadas](#tecnologias-utilizadas) +* [Critérios a serem avaliados](#critérios-a-serem-avaliados) + + +* [Como usar](#como-usar) +* [Quais ferramentas foram usadas](#quais-ferramentas-foram-usadas) +* [Infraestrutura adicional](#infraestrutura-adicional) +* [Como executar, testar, empacotar e entregar o projeto](#como-executar-testar-empacotar-e-entregar-o-projeto) +* [Instruções para ambiente de produção](#instruções-para-ambiente-de-produção) + + # Quer fazer parte da Superbid Webservices? Obrigado por se interessar em fazer parte do nosso time! @@ -14,21 +30,37 @@ O questionário e a especificação da aplicação estão logo abaixo. # Questionário * Você já trabalhou com Spring Boot? +> Sim. Trabalho com Spring Boot desde 2016. * O que você conhece sobre micro-serviços? +> Sobre micro-serviços eu tenho experiência com Spring Boot desde a criação, testes até o deploy utilizando docker. Claro tudo com o adorado Jenkins. :-) * Cite algumas vantagens e desvantagens de usar esse modelo arquitetural +> A principal vantagem é que cada serviço é responsável por um domínio(ou entidade) como diz Eric Evans. A principal desvantagem que vejo é que para testar você têm que criar vários projetos e dependendo do tamanho do projeto dá um trabalho monstro. Porém é legal. :-) * Qual a sua experiência na construção de APIs? +> Possuo experiência na construção de APIs internas apenas, preferencialmente, utilizando a notação /version/entity. * Alguma vez já teve que construir uma API pública? +> Não tive a oportunidade de trabalhar na construção de API públicas, já consumi várias porém construir ainda nenhuma. :-( * Como você controla o acesso à API? +> Utilizando Oauth ou Oauth2, preferencialmente com Bearer Authentication. * Como você trata questões da evolução das APIs? +> A evolução das APIs eu deixo sempre a antiga rodando, e crio sempre uma nova, quando possível, utilizando a notação /version/entity. * Você acha válido fazer testes automatizados? +> Sim, acho muito importante tanto a utilização de testes unitários no backend e BDD nos testes automatizados integrados ou regressivos. * Imagine que você precisa construir uma API que vai ter um grande número de acessos. Como você garante que ela terá um tempo de resposta acessível durante um pico de acesso? +> Neste tipo de problema eu gosto de utilizar filas, geralmente RabbitMQ, Kafka ou até mesmo o Redis já me ajudaram bastante. * Você conhece ou já trabalhou com containers? +> Sim já trabalho com conteiners docker a 3 anos. * E orquestradores tipo Kubernetes ou Docker Swarm? +> K8s eu nunca utilizei, já trabalhei com Docker Swarm por 2 anos. * Fale um pouco sobre o processo de versionamento de aplicações. Conhece Git? +> Gosto muito de utilizar o Git/BitBucket/GitLab facilita muito nossa vida de dev, para os casos que fazemos algumas besteiras no código e é possível retornar a versão anterior/original. * Como você usa branches, tags, etc. Tem algo no Github ou Gitlab? +> Sim, utilizo branches sempre que preciso incluir uma nova característica no código ou então dar manutenção, assim facilita o merge e separa o código bom do código novo(com possíveis bugs). E tagueamente(tag) eu ainda não sei fazer. * Você conhece CI/CD? Já chegou a fazer algum pipeline de CI/CD completo? +> Sim já trabalhei com CI com jenkins e docker efetuando deploy em homologação, CD eu ainda não tive a oportunidade de trabalhar. * Você já trabalhou com SCRUM ou Kanban? +> Sim trabalho com SCRUM(pre-planning,planning,sprint 20 dias,daily, review e retro) e Kanban (JIRA) desde 2016. * Conte um pouco sobre como foi fazer parte de um time ágil, quais dificuldades tiveram e como conseguiram superar. +> Eu gosto muito de fazer parte de um time ágil, porém uma das dificuldades que percebo em alguns times é que alguns membros não entregam o que prometem na daily e nem comunicam os outros membros do time, e o pior é que o membro não comunica os bloqueios durante a daily então dessa forma fica realmente dificil de ajudar. Até hoje não sei se é vergonha de pedir ajuda ou é a educação da pessoa. Talvez um dia eu estude mais sobre isso. :-) # Teste prático (prazo 3 dias) @@ -48,7 +80,7 @@ Considerando a funcionalidade de um blog, construa uma API Rest contendo as oper * Banco de dados em memória * Postman Collection (para testes da api) -## Critérios a serem avaliados: +## Critérios a serem avaliados * Qualidade de Código * Cobertura de Testes @@ -57,3 +89,99 @@ Considerando a funcionalidade de um blog, construa uma API Rest contendo as oper * Qualquer critério que vocês considere pertinente Uma dica: pense no teste prático como sendo um algo real que você faria ou gostaria de fazer no seu trabalho, pois esse será o seu cartão de visitas. + +## Como usar + +* [http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html) + +ou + +| O quê você deseja fazer | Comando curl | Resposta | +|-------------------------|----------------|----------| +| Listar todas os posts do blog | curl -X GET -H 'Content-Type: application/json' "http://localhost:8080/v1/blog/" | lista(array) de posts do blog em formato json | +| Criar um post | curl -XPOST -H'Content-Type: application/json;charset=utf-8' -d'{ "titulo" : "Titulo do post", "descricao" : "descricao qualquer", "dataPublicacao" : "2018-10-28T21:20:01.735Z" }' http://localhost:8080/v1/blog/ | post do blog em formato json com campo id preenchido | +| Atualizar um post | curl -X PUT -H 'Content-Type: application/json' -d '{ "dataPublicacao": "2018-10-29T14:23:09.632Z", "titulo": "Titulo do post atualizado 2", "descricao": "descricao qualquer atualizado 2", "id": 1 }' 'http://localhost:8080/v1/blog/' | post do blog em formato json | +| Consultar um post| curl -X GET -H 'Content-Type: application/json' "http://localhost:8080/v1/blog/" | post do blog formato json | +| Apagar um post | curl -X DELETE -H 'Content-Type: application/json' "http://localhost:8080/v1/blog/" | string "ok" | + +## Quais ferramentas foram usadas + +| Ferramenta | Link para download | +|------------|--------------------| +|Java Development Kit 8 64 bits para Windows 10 javac versão 1.8.0_181 | (http://www.oracle.com/technetwork/pt/java/javase/downloads/jdk8-downloads-2133151.html) | +| Maven 3.5.4 para Windows | (https://maven.apache.org/download.cgi) | +| IntelliJ IDEA Community Edition para Windows 10 | (https://www.jetbrains.com/idea/download/#section=windows) | +| Spring Boot 2.0.5.RELEASE | (https://start.spring.io/) | +| Curl 64 bits para Windows 64bits | (https://curl.haxx.se/download.html#Win64) | +| Lombok 1.18.2 | https://projectlombok.org/download) | + +## Infraestrutura adicional + +### Porta do serviço + +* A porta padrão para rodar dos serviços é: + +``` +8080 = blog-server +``` + +## Como executar, testar, empacotar e entregar o projeto + +1. Baixar e instalar o Java 1.8 ou superior +2. Baixar e instalar o Maven 3.5.4 ou superior +3. Baixar os fontes + +``` +git clone https://github.com/p5f8/backend-developer.git +cd backend-developer +``` + +4. Verificar arquivo src/main/resources/application.properties + + > Confirmar a porta do Serviço (conforme [Porta do serviço](#porta-do-serviço)) + +5. Gerar o pacote Java em formato Jar + +``` +mvn clean install +``` + +> Dentro da pasta target, verificar a existência do arquivo: blog-server-0.0.1.jar + +6. Executar os comandos abaixo: + +``` +cd target +java -jar blog-server-0.0.1.jar +``` + +## Instruções para ambiente de produção + +- [ ] JDK 8 ou superior deve estar instalado na máquina que os microservicos +- [ ] Liberar portas no firewall, conforme [Infraestrutura adicional](#infraestrutura-adicional) +- [ ] Criar uma pasta com nome "**blog-server**" +- [ ] Copiar os arquivos gerado na seção [Como executar, testar, empacotar e entregar o projeto](#como-executar-testar-empacotar-e-entregar-o-projeto) para a pasta criada no passo anterior +- [ ] Executar o comando: + +``` +java -jar blog-server-0.0.1.jar +``` + +- [ ] Para verificar se o serviço está rodando execute: + +``` +curl -X GET "http://localhost:8080/manage/health" +``` + +- [ ] Retornou '{"status":"UP"}' no passo anterior? Tudo Ok e funcionando, pode utilizar todas as urls descritas em na seção [Como usar](#como-usar). +- [ ] Não funcionou? mande um e-mail com a mensagem de erro para p5f8@hotmail.com. Obrigado. + +## Instruções para monitoramento + +- [ ] Executar o comando abaixo para verificar se o serviço está no ar e deve retornar '{"status":"UP"}': +``` +curl -X GET "http://localhost:8080/manage/health" +``` +- [ ] Logs de INFO são gerados no stdout padrão (console) +- [ ] Logs de ERROR são gerados no stderr padrão (console) +- [ ] Não funcionou? mande um e-mail com a mensagem de erro para p5f8@hotmail.com. Obrigado. diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..5bf251c --- /dev/null +++ b/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + 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 + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..22b726d --- /dev/null +++ b/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + com.sbws + blog-server + 0.0.1 + jar + + blog-server + SuperBidWs - Blog Server + + + org.springframework.boot + spring-boot-starter-parent + 2.0.5.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.SR1 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + com.h2database + h2 + + + org.projectlombok + lombok + + + + io.springfox + springfox-swagger2 + 2.4.0 + + + + io.springfox + springfox-swagger-ui + 2.4.0 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/src/main/java/com/sbws/blogserver/BlogServerApplication.java b/src/main/java/com/sbws/blogserver/BlogServerApplication.java new file mode 100644 index 0000000..78a9f0c --- /dev/null +++ b/src/main/java/com/sbws/blogserver/BlogServerApplication.java @@ -0,0 +1,13 @@ +package com.sbws.blogserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BlogServerApplication { + + public static void main(String[] args) { + SpringApplication.run(BlogServerApplication.class, args); + } + +} diff --git a/src/main/java/com/sbws/blogserver/config/CORSConfiguration.java b/src/main/java/com/sbws/blogserver/config/CORSConfiguration.java new file mode 100644 index 0000000..2658f9b --- /dev/null +++ b/src/main/java/com/sbws/blogserver/config/CORSConfiguration.java @@ -0,0 +1,27 @@ +package com.sbws.blogserver.config; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@Configuration +public class CORSConfiguration { + + @Bean + public FilterRegistrationBean corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + source.registerCorsConfiguration("/**", config); + FilterRegistrationBean bean = new FilterRegistrationBean<>(new CorsFilter(source)); + bean.setOrder(0); + return bean; + } + +} diff --git a/src/main/java/com/sbws/blogserver/config/SwaggerConfiguration.java b/src/main/java/com/sbws/blogserver/config/SwaggerConfiguration.java new file mode 100644 index 0000000..daeede4 --- /dev/null +++ b/src/main/java/com/sbws/blogserver/config/SwaggerConfiguration.java @@ -0,0 +1,46 @@ +package com.sbws.blogserver.config; + +import static springfox.documentation.builders.PathSelectors.regex; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; + +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfiguration extends WebMvcConfigurationSupport { + + @Bean + public Docket productApi() { + return new Docket(DocumentationType.SWAGGER_2) + .select().apis(RequestHandlerSelectors.basePackage("com.sbws.blogserver.controller")) + .paths(regex(".*")) + .build() + .apiInfo(metaData()); + } + + private ApiInfo metaData() { + return new ApiInfoBuilder() + .title("Avaliacao SBWS - Blog API") + .description("REST API para SBWS") + .version("1.0.0") + .build(); + } + + @Override + protected void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("swagger-ui.html") + .addResourceLocations("classpath:/META-INF/resources/"); + + registry.addResourceHandler("/webjars/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/"); + } +} \ No newline at end of file diff --git a/src/main/java/com/sbws/blogserver/controller/BlogController.java b/src/main/java/com/sbws/blogserver/controller/BlogController.java new file mode 100644 index 0000000..a02788f --- /dev/null +++ b/src/main/java/com/sbws/blogserver/controller/BlogController.java @@ -0,0 +1,91 @@ +package com.sbws.blogserver.controller; + +import java.util.List; +import java.util.Objects; + +import javax.transaction.Transactional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.WebRequest; + +import com.sbws.blogserver.model.Blog; +import com.sbws.blogserver.service.BlogService; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + +@RestController +@CrossOrigin +@RequestMapping("/v1/blog/") +@Api(value = "Blog SBWS REST API", description = "Operações CRUD para Blog SBWS") +public class BlogController { + + private static final Logger log = LoggerFactory.getLogger(BlogController.class); + + @Autowired + private BlogService service; + + @GetMapping + @ApiOperation(value = "Busca todos posts do blog", response = Blog.class) + public ResponseEntity> allPosts() { + return new ResponseEntity>(service.findAll(), HttpStatus.PARTIAL_CONTENT); + } + + @GetMapping("/{id}") + @ApiOperation(value = "Busca post por ID", response = Blog.class) + public ResponseEntity getById(@PathVariable("id") Long id) { + + Blog blog = service.findById(id); + + HttpStatus httpStatus = null; + + if(Objects.isNull(blog)) { + httpStatus = HttpStatus.NOT_FOUND; + } else { + httpStatus = HttpStatus.OK; + } + + return new ResponseEntity(blog, httpStatus); + } + + @PostMapping + @ApiOperation(value = "Cria um POST no blog (ID é gerado automaticamente)", response = Blog.class) + public ResponseEntity post(@RequestBody Blog blog) { + return new ResponseEntity(service.save(blog), HttpStatus.CREATED); + } + + @PutMapping("/{id}") + @ApiOperation(value = "Cria ou atualiza o Post do Blog (ID é necessário)", response = Blog.class) + public ResponseEntity put(@PathVariable("id") Long id, @RequestBody Blog blog) { + return new ResponseEntity(service.save(blog), HttpStatus.OK); + } + + @DeleteMapping("/{id}") + @ApiOperation(value = "Delete o Post do Blog por ID", response = Blog.class) + @Transactional + public ResponseEntity delete(@PathVariable("id") Long id) { + return new ResponseEntity(service.deleteById(id), HttpStatus.OK); + } + + @ExceptionHandler + public ResponseEntity errorHandle(Exception e, WebRequest request) { + log.error("Ocorreu erro......"); + e.printStackTrace(); + return new ResponseEntity("Ocorreu um erro: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/src/main/java/com/sbws/blogserver/exception/BlogBusinessException.java b/src/main/java/com/sbws/blogserver/exception/BlogBusinessException.java new file mode 100644 index 0000000..5ce94d3 --- /dev/null +++ b/src/main/java/com/sbws/blogserver/exception/BlogBusinessException.java @@ -0,0 +1,11 @@ +package com.sbws.blogserver.exception; + +public class BlogBusinessException extends RuntimeException { + + private static final long serialVersionUID = -7026972515905409151L; + + public BlogBusinessException(String mensagem) { + super(mensagem); + } + +} diff --git a/src/main/java/com/sbws/blogserver/model/Blog.java b/src/main/java/com/sbws/blogserver/model/Blog.java new file mode 100644 index 0000000..6d59115 --- /dev/null +++ b/src/main/java/com/sbws/blogserver/model/Blog.java @@ -0,0 +1,39 @@ +package com.sbws.blogserver.model; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.SequenceGenerator; +import javax.validation.constraints.Size; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@SequenceGenerator(name = "SeqBlog", sequenceName = "SeqBlog", initialValue = 1, allocationSize = 10) +public class Blog implements Serializable { + + private static final long serialVersionUID = -2066484922781496532L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private Long Id; + + private Date dataPublicacao; + + @Size(max=70) + private String titulo; + + private String descricao; + +} diff --git a/src/main/java/com/sbws/blogserver/repository/BlogRepository.java b/src/main/java/com/sbws/blogserver/repository/BlogRepository.java new file mode 100644 index 0000000..3a8b55c --- /dev/null +++ b/src/main/java/com/sbws/blogserver/repository/BlogRepository.java @@ -0,0 +1,12 @@ +package com.sbws.blogserver.repository; + +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.stereotype.Repository; + +import com.sbws.blogserver.model.Blog; + +@Repository +public interface BlogRepository extends PagingAndSortingRepository{ + +} + diff --git a/src/main/java/com/sbws/blogserver/service/BlogService.java b/src/main/java/com/sbws/blogserver/service/BlogService.java new file mode 100644 index 0000000..9b4be62 --- /dev/null +++ b/src/main/java/com/sbws/blogserver/service/BlogService.java @@ -0,0 +1,20 @@ +package com.sbws.blogserver.service; + +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.sbws.blogserver.model.Blog; + +@Service +public interface BlogService { + + public List findAll(); + + public Blog findById(Long id); + + public Blog save(Blog blog); + + public String deleteById(Long id); + +} diff --git a/src/main/java/com/sbws/blogserver/service/BlogServiceImpl.java b/src/main/java/com/sbws/blogserver/service/BlogServiceImpl.java new file mode 100644 index 0000000..75e699e --- /dev/null +++ b/src/main/java/com/sbws/blogserver/service/BlogServiceImpl.java @@ -0,0 +1,38 @@ +package com.sbws.blogserver.service; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.sbws.blogserver.model.Blog; +import com.sbws.blogserver.repository.BlogRepository; + +@Service +public class BlogServiceImpl implements BlogService { + + @Autowired + BlogRepository repository; + + @Override + public List findAll() { + return (List) repository.findAll(); + } + + @Override + public Blog findById(Long id) { + return repository.findById(id).orElse(null); + } + + @Override + public Blog save(Blog blog) { + return repository.save(blog); + } + + @Override + public String deleteById(Long id) { + repository.deleteById(id); + return "ok"; + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..058359b --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,10 @@ +server.port=8080 +management.endpoints.web.base-path=/manage + +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 +spring.datasource.username=sa +spring.datasource.password=sa + +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.show-sql=true diff --git a/src/test/java/com/sbws/blogserver/controller/BlogControllerTest.java b/src/test/java/com/sbws/blogserver/controller/BlogControllerTest.java new file mode 100644 index 0000000..0f9a53e --- /dev/null +++ b/src/test/java/com/sbws/blogserver/controller/BlogControllerTest.java @@ -0,0 +1,163 @@ +package com.sbws.blogserver.controller; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.sbws.blogserver.model.Blog; +import com.sbws.blogserver.service.BlogService; + +@RunWith(SpringRunner.class) +@SpringBootTest +@AutoConfigureMockMvc +public class BlogControllerTest { + + private static final Logger log = LoggerFactory.getLogger(BlogControllerTest.class); + + @Autowired + private MockMvc mockMvc; + + @MockBean + private BlogService service; + + public static final String DESCRICAO_01_POST = "Descricao do post teste"; + public static final String TITULO_01_POST = "Titulo do post teste"; + + public static final String DESCRICAO_02_POST = "Descricao do post teste 2"; + public static final String TITULO_02_POST = "Titulo do post teste 2"; + + public static final String DESCRICAO_03_POST = "Descricao do post teste 3"; + public static final String TITULO_03_POST = "Titulo do post teste 3"; + + private Blog blog01 = null; + private Blog blog01WithId = null; + private Blog blog02 = null; + private Blog blog03 = null; + List listagemTodosBlogPosts = null; + + @Before + public void beforeTests() { + blog01 = criaPostBlog(DESCRICAO_01_POST, TITULO_01_POST); + blog01WithId = criaPostBlog(DESCRICAO_01_POST, TITULO_01_POST); + blog01WithId.setId(300L); + + blog02 = criaPostBlog(DESCRICAO_02_POST, TITULO_02_POST); + blog02.setId(300L); + + blog03 = criaPostBlog(DESCRICAO_03_POST, TITULO_03_POST); + blog03.setId(301L); + + listagemTodosBlogPosts = new ArrayList<>(); + listagemTodosBlogPosts.add(blog01); + listagemTodosBlogPosts.add(blog02); + listagemTodosBlogPosts.add(blog03); + } + + @Test + public void salvarBlogPost() throws Exception { + when(service.save(blog01)).thenReturn(blog01WithId); + + ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + String newBlogPostJSON = ow.writeValueAsString(blog01); + + this.mockMvc.perform(post("/v1/blog/").contentType(MediaType.APPLICATION_JSON).content(newBlogPostJSON)) + .andDo(print()).andExpect(status().isCreated()) + .andExpect(content().string(containsString(DESCRICAO_01_POST))); + } + + @Test + public void atualizarBlogPost() throws Exception { + when(service.save(blog02)).thenReturn(blog02); + + ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + String newBlogPostJSON = ow.writeValueAsString(blog02); + + this.mockMvc.perform(put("/v1/blog/" + blog02.getId()).contentType(MediaType.APPLICATION_JSON).content(newBlogPostJSON)) + .andDo(print()).andExpect(status().isOk()) + .andExpect(content().string(containsString(DESCRICAO_02_POST))); + } + + @Test + public void apagarBlogPost() throws Exception { + + Long idBlogPost = 301L; + + when(service.deleteById(idBlogPost)).thenReturn("ok"); + + this.mockMvc.perform(delete("/v1/blog/" + idBlogPost).contentType(MediaType.APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()) + .andExpect(content().string(containsString("ok"))); + } + + @Test + public void listaTodosOsBlogsPosts() throws Exception { + + when(service.findAll()).thenReturn(listagemTodosBlogPosts); + + this.mockMvc.perform(get("/v1/blog/").contentType(MediaType.APPLICATION_JSON)) + .andDo(print()).andExpect(status().isPartialContent()) + .andExpect(content().string(containsString(DESCRICAO_01_POST))) + .andExpect(content().string(containsString(DESCRICAO_02_POST))) + .andExpect(content().string(containsString(DESCRICAO_03_POST))); + } + + @Test + public void recuperaUmUnicoBlogPostQueExiste() throws Exception { + + when(service.findById(blog01WithId.getId())).thenReturn(blog01WithId); + + this.mockMvc.perform(get("/v1/blog/" + blog01WithId.getId()).contentType(MediaType.APPLICATION_JSON)) + .andDo(print()).andExpect(status().isOk()) + .andExpect(content().string(containsString(DESCRICAO_01_POST))); + } + + @Test + public void recuperaUmUnicoBlogPostQueNaoExiste() throws Exception { + + when(service.findById(blog01WithId.getId())).thenReturn(null); + + this.mockMvc.perform(get("/v1/blog/" + blog01WithId.getId()).contentType(MediaType.APPLICATION_JSON)) + .andDo(print()).andExpect(status().isNotFound()); + } + + @Test + public void enviaIdInvalido() throws Exception { + this.mockMvc.perform(get("/v1/blog/" + "ddd").contentType(MediaType.APPLICATION_JSON)) + .andDo(print()).andExpect(status().isInternalServerError()); + } + + public Blog criaPostBlog(String descricao, String titulo) { + return Blog.builder() + .dataPublicacao(new Date()) + .descricao(descricao) + .titulo(titulo) + .build(); + } + + +} diff --git a/src/test/java/com/sbws/blogserver/repository/BlogRepositoryTest.java b/src/test/java/com/sbws/blogserver/repository/BlogRepositoryTest.java new file mode 100644 index 0000000..65f32f2 --- /dev/null +++ b/src/test/java/com/sbws/blogserver/repository/BlogRepositoryTest.java @@ -0,0 +1,114 @@ +package com.sbws.blogserver.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Date; +import java.util.List; +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; +import org.springframework.test.context.junit4.SpringRunner; + +import com.sbws.blogserver.model.Blog; + +@RunWith(SpringRunner.class) +@DataJpaTest +public class BlogRepositoryTest { + + public static final String DESCRICAO_01_POST = "Descricao do post teste"; + public static final String TITULO_01_POST = "Titulo do post teste"; + + public static final String DESCRICAO_02_POST = "Descricao do post teste 2"; + public static final String TITULO_02_POST = "Titulo do post teste 2"; + + public static final String DESCRICAO_03_POST = "Descricao do post teste 3"; + public static final String TITULO_03_POST = "Titulo do post teste 3"; + + public static final Long ID_DOES_NOT_EXIST = 9998973L; + + Blog blog01 = null; + Blog blog02 = null; + Blog blog03 = null; + + @Autowired + private TestEntityManager entityManager; + + @Autowired + private BlogRepository repository; + + @Before + public void loadDatabase() { + blog01 = criaPostBlog(DESCRICAO_01_POST, TITULO_01_POST); + entityManager.persist(blog01); + + blog02 = criaPostBlog(DESCRICAO_02_POST, TITULO_02_POST); + entityManager.persist(blog02); + + entityManager.flush(); + } + + + @Test + public void whenSaveBlog_thenReturnBlogPostWithId() { + // given + blog03 = criaPostBlog(DESCRICAO_03_POST, TITULO_03_POST); + repository.save(blog03); + + // when + Optional result = repository.findById(blog03.getId()); + + // then + assertThat(result.isPresent()).isEqualTo(true); + assertThat(result.get().getId()).isEqualTo(blog03.getId()); + } + + @Test + public void whenFindAll_thenReturnAllBlogPost() { + // given at @Before + + // when + List result = (List) repository.findAll(); + + // then + assertThat(result).isNotNull(); + assertThat(result.size()).isEqualTo(2); + } + + @Test + public void whenFindByIdThatExist_thenReturnOneBlogPost() { + // given at @Before + + // when + Optional result = repository.findById(blog01.getId()); + + // then + assertThat(result.isPresent()).isEqualTo(true); + assertThat(result.get().getId()).isEqualTo(blog01.getId()); + } + + @Test + public void whenFindByIdThatDoesNotExist_thenReturnNullBlogPost() { + // given + + // when + Optional result = repository.findById(ID_DOES_NOT_EXIST); + + // then + assertThat(result.isPresent()).isEqualTo(false); + } + + public Blog criaPostBlog(String descricao, String titulo) { + return Blog.builder() + .dataPublicacao(new Date()) + .descricao(descricao) + .titulo(titulo) + .build(); + } + +} + diff --git a/src/test/java/com/sbws/blogserver/service/BlogServiceTest.java b/src/test/java/com/sbws/blogserver/service/BlogServiceTest.java new file mode 100644 index 0000000..14a4c64 --- /dev/null +++ b/src/test/java/com/sbws/blogserver/service/BlogServiceTest.java @@ -0,0 +1,124 @@ +package com.sbws.blogserver.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.junit4.SpringRunner; + +import com.sbws.blogserver.model.Blog; +import com.sbws.blogserver.repository.BlogRepository; + +@RunWith(SpringRunner.class) +public class BlogServiceTest { + + @TestConfiguration + static class BlogServiceImplTestContextConfiguration { + + @Bean + public BlogService employeeService() { + return new BlogServiceImpl(); + } + } + + @Autowired + private BlogService service; + + @MockBean + private BlogRepository repository; + + private Blog blog01 = null; + private Blog blog02 = null; + private Blog blog03 = null; + private Blog blogWithoutId = null; + private Blog blogWithId = null; + + public static final String DESCRICAO_01_POST = "Descricao do post teste"; + public static final String TITULO_01_POST = "Titulo do post teste"; + + public static final String DESCRICAO_02_POST = "Descricao do post teste 2"; + public static final String TITULO_02_POST = "Titulo do post teste 2"; + + public static final String DESCRICAO_03_POST = "Descricao do post teste 3"; + public static final String TITULO_03_POST = "Titulo do post teste 3"; + + public static final String DESCRICAO_04_POST = "Descricao do post teste WithoutId"; + public static final String TITULO_04_POST = "Titulo do post teste WithoutId"; + + public static final String DESCRICAO_05_POST = "Descricao do post teste WithId"; + public static final String TITULO_05_POST = "Titulo do post teste WithId"; + + + @Before + public void setUp() { + blog01 = criaPostBlog(DESCRICAO_01_POST, TITULO_01_POST); + blog02 = criaPostBlog(DESCRICAO_02_POST, TITULO_02_POST); + blog03 = criaPostBlog(DESCRICAO_03_POST, TITULO_03_POST); + blogWithoutId = criaPostBlog(DESCRICAO_04_POST, TITULO_04_POST); + blogWithId = criaPostBlog(DESCRICAO_05_POST, TITULO_05_POST); + blogWithId.setId(5L); + + List listBlogPosts = new ArrayList<>(); + listBlogPosts.add(blog01); + listBlogPosts.add(blog02); + listBlogPosts.add(blog03); + + Mockito.when(repository.findById(blog01.getId())).thenReturn(Optional.of(blog01)); + Mockito.when(repository.findAll()).thenReturn(listBlogPosts); + Mockito.when(repository.save(blogWithoutId)).thenReturn(blogWithId); + + BlogRepository myRepositoy = mock(BlogRepository.class); + doNothing().when(myRepositoy).deleteById(blogWithId.getId()); + } + + @Test + public void whenDelete_thenBlogPostShouldBeDeleted() { + String found = service.deleteById(blogWithId.getId()); + + assertThat(found).isNotNull(); + assertThat(found).isEqualTo("ok"); + } + + @Test + public void whenSave_thenBlogPostShouldBeReturnedWithId() { + Blog found = service.save(blogWithoutId); + + assertThat(found.getId()).isNotNull(); + } + + @Test + public void whenFindById_thenBlogPostShouldBeFound() { + Blog found = service.findById(blog01.getId()); + + assertThat(found.getId()).isEqualTo(blog01.getId()); + } + + @Test + public void whenFindAll_thenListBlogPostShouldBeFound() { + List found = service.findAll(); + + assertThat(found.size()).isGreaterThanOrEqualTo(2); + } + + public Blog criaPostBlog(String descricao, String titulo) { + return Blog.builder() + .dataPublicacao(new Date()) + .descricao(descricao) + .titulo(titulo) + .build(); + } + +}