diff --git a/README.md b/README.md index 81e616e..2340d87 100644 --- a/README.md +++ b/README.md @@ -14,22 +14,73 @@ O questionário e a especificação da aplicação estão logo abaixo. # Questionário * Você já trabalhou com Spring Boot? + + Sim, atualmente trabalho com um projeto desenvolvido em Spring boot, além de já ter trabalhado em projetos pessoais + * O que você conhece sobre micro-serviços? + + Que é uma arquitetura de projeto em que as diversas funcionalidades são quebradas em serviços independentes, mas que podem se comunicar entre si, diferente das aplicações monolíticas, onde tudo está na mesma api + * Cite algumas vantagens e desvantagens de usar esse modelo arquitetural + + Uma das principais vantagens é a facilidade de se realizar mudanças ou correções. Por estarem dívidas, é fácil encontrar uma determinada funcionalidade e realizar qualquer alteração necessária sem prejudicar as outras funcionalidades ou parar todo a aplicação. + + Já a desvantagem é que esse tipo de arquitetura, aumenta a complexidade das aplicações, que pode ser desnecessária em soluções mais simples + * Qual a sua experiência na construção de APIs? + + Desde que comecei a trabalhar, já participei da criação de várias api (com e sem spring boot) utilizando diversos padrões de projetos + * Alguma vez já teve que construir uma API pública? + + Até o momento não + * Como você controla o acesso à API? + + Depende do projeto e arquitetura utilizadas. Já trabalhei e aplicações que usavam de tokens para a sua validação, mas atualmente, estou estudando a possibilidade de usar o spring security, para as aplicações spring boot + * Como você trata questões da evolução das APIs? + +A evolução de uma API deve ser algo muito bem estudado, para poder calcular os seus impactos nas soluções que elas afetam + * Você acha válido fazer testes automatizados? + + Sim. Muitas aplicações podem ter casos de uso e funcionalidades muito grandes, podendo demandar muito esforço para testar e retestar. A automação do teste facilita esse trabalho. + * 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? + + Nunca tive que trabalhar com esse tipo de situação, mas já conversei com membros de uma equipe sobre um grande projeto da Sabesp e eles comentaram sobre o NodeBalance, que ajudaria nesse tipo de situação. Ainda não conheço a tecnologia, mas pretendo aprender para projetos futuros + * Você conhece ou já trabalhou com containers? + + Conheço. Último projeto que participei e o atual usam o docker para subir em um servidor openshift + * E orquestradores tipo Kubernetes ou Docker Swarm? + + Nunca trabalhei + * Fale um pouco sobre o processo de versionamento de aplicações. Conhece Git? + + Conheço o git e trabalho muito com ele. O versionamento de aplicações é de extrema importância para o trabalho, não só por permitir acessar um histórico da aplicação, que por si só já é importante, mas também por permitir que várias pessoas possam trabalhar de forma eficiente no mesmo projeto. + * Como você usa branches, tags, etc. Tem algo no Github ou Gitlab? + + Geralmente uso branches para separar algum trabalho que venha fazer alguma mudança de algum impacto significativo, que deixado um branche master unicamente para guardar arquivos que irão gerar uma versão do projeto. Já as tags são usadas para guardar versões finalizadas. há alguns pequenos projetos no meu github, que usei para testar alguns conceitos + * Você conhece CI/CD? Já chegou a fazer algum pipeline de CI/CD completo? + + Conheço o conceitos de integração e entrega contínua, porém isso está sendo estudado aqui ainda. + * Você já trabalhou com SCRUM ou Kanban? + + Sim, com os dois + * Conte um pouco sobre como foi fazer parte de um time ágil, quais dificuldades tiveram e como conseguiram superar. + Trabalho com times ágeis desde sempre aqui no trabalho, estamos sempre tentando melhorar a nossa compreensão do scrum. No último projeto que participei, fui scrum master de minha equipe. Uma das maiores dificuldades foi o de retomar o framework depois que fomos forçados a parar por alguma força externa, mas nós policiando, conseguimos voltar ao controle + + + # Teste prático (prazo 3 dias) Considerando a funcionalidade de um blog, construa uma API Rest contendo as operações de básicas de CRUD: inclusão, atualização, exclusão e consulta. O recurso em questão deve ter a seguinte estrutura: diff --git a/post/.gitignore b/post/.gitignore new file mode 100644 index 0000000..82eca33 --- /dev/null +++ b/post/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### 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/ \ No newline at end of file diff --git a/post/.mvn/wrapper/maven-wrapper.jar b/post/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..9cc84ea Binary files /dev/null and b/post/.mvn/wrapper/maven-wrapper.jar differ diff --git a/post/.mvn/wrapper/maven-wrapper.properties b/post/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b573bb5 --- /dev/null +++ b/post/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip diff --git a/post/mvnw b/post/mvnw new file mode 100644 index 0000000..5bf251c --- /dev/null +++ b/post/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/post/mvnw.cmd b/post/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/post/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/post/pom.xml b/post/pom.xml new file mode 100644 index 0000000..0a70aae --- /dev/null +++ b/post/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + br.com.denniscb + post + 0.0.1-SNAPSHOT + jar + + post + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + io.springfox + springfox-swagger2 + 2.7.0 + + + com.h2database + h2 + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/post/src/main/java/br/com/denniscb/post/PostApplication.java b/post/src/main/java/br/com/denniscb/post/PostApplication.java new file mode 100644 index 0000000..7b5d74a --- /dev/null +++ b/post/src/main/java/br/com/denniscb/post/PostApplication.java @@ -0,0 +1,12 @@ +package br.com.denniscb.post; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class PostApplication { + + public static void main(String[] args) { + SpringApplication.run(PostApplication.class, args); + } +} diff --git a/post/src/main/java/br/com/denniscb/post/controller/PostController.java b/post/src/main/java/br/com/denniscb/post/controller/PostController.java new file mode 100644 index 0000000..9d5139a --- /dev/null +++ b/post/src/main/java/br/com/denniscb/post/controller/PostController.java @@ -0,0 +1,53 @@ +package br.com.denniscb.post.controller; + +import java.util.List; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import br.com.denniscb.post.model.PostModel; +import br.com.denniscb.post.service.PostService; + +@RestController +@RequestMapping("/api") +public class PostController { + + @Autowired + private PostService postService; + + @PostMapping("/post") + public ResponseEntity save(@Valid @RequestBody PostModel post) { + return ResponseEntity.ok(postService.save(post)); + } + + @GetMapping("/post") + public ResponseEntity> findAll() { + return ResponseEntity.ok(postService.findAll()); + } + + @GetMapping("/post/titulo/{titulo}") + public ResponseEntity> findByTituloContaining(@PathVariable(value = "titulo") String titulo) { + return ResponseEntity.ok(postService.findByTituloContaining(titulo)); + } + + @GetMapping("/post/id/{id}") + public ResponseEntity findByid(@PathVariable(value = "id") Long id) { + return ResponseEntity.ok(postService.findById(id)); + } + + @DeleteMapping("/post/{id}") + public ResponseEntity delete(@PathVariable(value = "id") Long id){ + postService.delete(id); + return ResponseEntity.ok().build(); + } + +} diff --git a/post/src/main/java/br/com/denniscb/post/exception/NotFoundException.java b/post/src/main/java/br/com/denniscb/post/exception/NotFoundException.java new file mode 100644 index 0000000..71efd78 --- /dev/null +++ b/post/src/main/java/br/com/denniscb/post/exception/NotFoundException.java @@ -0,0 +1,12 @@ +package br.com.denniscb.post.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Post") // 404 +public class NotFoundException extends RuntimeException { + + private static final long serialVersionUID = 7182103246480973666L; + + +} diff --git a/post/src/main/java/br/com/denniscb/post/model/PostModel.java b/post/src/main/java/br/com/denniscb/post/model/PostModel.java new file mode 100644 index 0000000..93a1093 --- /dev/null +++ b/post/src/main/java/br/com/denniscb/post/model/PostModel.java @@ -0,0 +1,88 @@ +package br.com.denniscb.post.model; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.PrePersist; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; +import javax.validation.constraints.NotBlank; + +@Entity +@Table(name = "POST") +public class PostModel { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_sequence") + @SequenceGenerator(name = "post_sequence", sequenceName = "post_seq") + @Column(name = "id_post") + private Long id; + + @NotBlank(message = "O post não pode estar em branco") + @Column(name = "post") + private String post; + + @Column(name = "data_cricao") + private Date dataCricacao; + + @NotBlank(message = "O titulo não pode estar em branco") + @Column(name = "titulo") + private String titulo; + + @NotBlank(message = "A descricao não pode estar em branco") + @Column(name = "descricao") + private String descricao; + + @PrePersist + public void setDataCriacaoPost() { + dataCricacao = new Date(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getPost() { + return post; + } + + public void setPost(String post) { + this.post = post; + } + + public Date getDataCricacao() { + return dataCricacao; + } + + public void setDataCricacao(Date dataCricacao) { + this.dataCricacao = dataCricacao; + } + + public String getTitulo() { + return titulo; + } + + public void setTitulo(String titulo) { + this.titulo = titulo; + } + + public String getDescricao() { + return descricao; + } + + public void setDescricao(String descricao) { + this.descricao = descricao; + } + + + + +} diff --git a/post/src/main/java/br/com/denniscb/post/repository/PostRepository.java b/post/src/main/java/br/com/denniscb/post/repository/PostRepository.java new file mode 100644 index 0000000..3336b5c --- /dev/null +++ b/post/src/main/java/br/com/denniscb/post/repository/PostRepository.java @@ -0,0 +1,16 @@ +package br.com.denniscb.post.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; +import org.springframework.stereotype.Repository; + + +import br.com.denniscb.post.model.PostModel; + +@Repository +public interface PostRepository extends JpaRepository { + + public Optional> findByTituloContaining(String titulo); +} diff --git a/post/src/main/java/br/com/denniscb/post/service/PostService.java b/post/src/main/java/br/com/denniscb/post/service/PostService.java new file mode 100644 index 0000000..a7eb5fd --- /dev/null +++ b/post/src/main/java/br/com/denniscb/post/service/PostService.java @@ -0,0 +1,52 @@ +package br.com.denniscb.post.service; + +import java.util.List; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import br.com.denniscb.post.exception.NotFoundException; +import br.com.denniscb.post.model.PostModel; +import br.com.denniscb.post.repository.PostRepository; + +@Service +public class PostService { + + @Autowired + private PostRepository postRepository; + + public PostModel save(PostModel post) { + return postRepository.save(post); + } + + public List findAll() { + List posts = postRepository.findAll(); + if(CollectionUtils.isEmpty(posts)) { + throw new NotFoundException(); + } + return posts; + } + + public List findByTituloContaining(String titulo) { + Optional> posts = postRepository.findByTituloContaining(titulo); + if (!posts.isPresent()) { + throw new NotFoundException(); + } + return posts.get(); + } + + public PostModel findById(Long id) { + Optional post = postRepository.findById(id); + if (!post.isPresent()) { + throw new NotFoundException(); + } + return post.get(); + } + + public void delete(Long id) { + postRepository.deleteById(id); + } + +} diff --git a/post/src/main/java/br/com/denniscb/post/swagger/SwaggerConfig.java b/post/src/main/java/br/com/denniscb/post/swagger/SwaggerConfig.java new file mode 100644 index 0000000..ecad4a7 --- /dev/null +++ b/post/src/main/java/br/com/denniscb/post/swagger/SwaggerConfig.java @@ -0,0 +1,23 @@ +package br.com.denniscb.post.swagger; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build(); + } +} \ No newline at end of file diff --git a/post/src/main/resources/application.properties b/post/src/main/resources/application.properties new file mode 100644 index 0000000..300e480 --- /dev/null +++ b/post/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.h2.console.enabled=true +spring.h2.console.path=/h2 +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:h2:file:~/post +spring.datasource.username=sa +spring.datasource.password= +spring.datasource.drive-class-name=org.h2.Drive \ No newline at end of file diff --git a/post/src/test/java/br/com/denniscb/post/PostApplicationTests.java b/post/src/test/java/br/com/denniscb/post/PostApplicationTests.java new file mode 100644 index 0000000..5216f57 --- /dev/null +++ b/post/src/test/java/br/com/denniscb/post/PostApplicationTests.java @@ -0,0 +1,25 @@ +package br.com.denniscb.post; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import br.com.denniscb.post.controller.PostController; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class PostApplicationTests { + + @Autowired + private PostController postController; + + @Test + public void contextLoads() throws Exception { + assertThat(postController).isNotNull(); + } + +} diff --git a/post/src/test/java/br/com/denniscb/post/PostControllerTest.java b/post/src/test/java/br/com/denniscb/post/PostControllerTest.java new file mode 100644 index 0000000..7cc7769 --- /dev/null +++ b/post/src/test/java/br/com/denniscb/post/PostControllerTest.java @@ -0,0 +1,72 @@ +package br.com.denniscb.post; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.Mockito.when; +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.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.json.GsonTester; +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 br.com.denniscb.post.controller.PostController; +import br.com.denniscb.post.model.PostModel; +import br.com.denniscb.post.service.PostService; + + +@RunWith(SpringRunner.class) +@WebMvcTest(PostController.class) +public class PostControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private PostService postService; + + @Test + public void findAll() throws Exception { + PostModel fakePost = new PostModel(); + fakePost.setTitulo("Post de teste"); + List fakePosts = Arrays.asList(fakePost); + when(postService.findAll()).thenReturn(fakePosts); + mockMvc.perform(get("/api/post").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))).andExpect(jsonPath("$[0].titulo", is(fakePost.getTitulo()))); + } + + @Test + public void findById() throws Exception { + Long id = 1L; + PostModel fakePost = new PostModel(); + fakePost.setId(1L); + when(postService.findById(id)).thenReturn(fakePost); + mockMvc.perform(get("/api/post/id/"+ id.toString()).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()).andExpect(jsonPath("$.id", is(fakePost.getId().intValue()))); + } + + @Test + public void findByTitulo() throws Exception { + PostModel fakePost = new PostModel(); + fakePost.setTitulo("Post de teste"); + String fragTitulo = "de"; + List fakePosts = Arrays.asList(fakePost); + when(postService.findByTituloContaining(fragTitulo)).thenReturn(fakePosts); + mockMvc.perform(get("/api/post/titulo/"+fragTitulo).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))).andExpect(jsonPath("$[0].titulo", is(fakePost.getTitulo()))); + } + +} + + \ No newline at end of file