From f1c11281dc67e5c61a5d0c66cfdb6d6aca9c1eba Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Fri, 21 Jul 2017 09:26:37 -0600 Subject: [PATCH 01/19] Initial commit --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..4fa332f5c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build +.gradle +.idea +*.iml +ci/variables.yml From 6a9f623082b775dfae9815af737c7c23d4a18248 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 09:34:22 -0600 Subject: [PATCH 02/19] Simple Spring Boot app --- build.gradle | 12 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54706 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++++++++++++++++ gradlew.bat | 84 +++++++++ settings.gradle | 1 + .../pal/tracker/PalTrackerApplication.java | 12 ++ .../pal/tracker/WelcomeController.java | 13 ++ 8 files changed, 300 insertions(+) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java create mode 100644 src/main/java/io/pivotal/pal/tracker/WelcomeController.java diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..bf82fb8f5 --- /dev/null +++ b/build.gradle @@ -0,0 +1,12 @@ +plugins { + id "java" + id "org.springframework.boot" version "1.5.4.RELEASE" +} + +repositories { + mavenCentral() +} + +dependencies { + compile("org.springframework.boot:spring-boot-starter-web") +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..1a98bfa24cb2ab2e5b1b78189abfdde5d4828663 GIT binary patch literal 54706 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..26cc55834 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Jul 20 09:14:39 MDT 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 000000000..cccdd3d51 --- /dev/null +++ b/gradlew @@ -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" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..e95643d6a --- /dev/null +++ b/gradlew.bat @@ -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 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..ef961960e --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "pal-tracker" \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java new file mode 100644 index 000000000..e33c3526e --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java @@ -0,0 +1,12 @@ +package io.pivotal.pal.tracker; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class PalTrackerApplication { + + public static void main(String[] args) { + SpringApplication.run(PalTrackerApplication.class, args); + } +} diff --git a/src/main/java/io/pivotal/pal/tracker/WelcomeController.java b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java new file mode 100644 index 000000000..4670637ec --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java @@ -0,0 +1,13 @@ +package io.pivotal.pal.tracker; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class WelcomeController { + + @GetMapping("/") + public String sayHello() { + return "hello"; + } +} From e246778801b23120d90f4c5ac73eb0ec26b79ce4 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 13:56:50 -0600 Subject: [PATCH 03/19] Add tests for deployment lab --- .../pal/tracker/EnvControllerTest.java | 28 +++++++++++++++++++ .../pal/tracker/WelcomeControllerTest.java | 16 +++++++++++ .../pal/trackerapi/WelcomeApiTest.java | 26 +++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java create mode 100644 src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java create mode 100644 src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java diff --git a/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java b/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java new file mode 100644 index 000000000..fda0f0f34 --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java @@ -0,0 +1,28 @@ +package test.pivotal.pal.tracker; + +import org.junit.Test; + +import java.util.Map; +import io.pivotal.pal.tracker.EnvController; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EnvControllerTest { + @Test + public void getEnv() throws Exception { + EnvController controller = new EnvController( + "8675", + "12G", + "34", + "123.sesame.street" + ); + + Map env = controller.getEnv(); + + assertThat(env.get("PORT")).isEqualTo("8675"); + assertThat(env.get("MEMORY_LIMIT")).isEqualTo("12G"); + assertThat(env.get("CF_INSTANCE_INDEX")).isEqualTo("34"); + assertThat(env.get("CF_INSTANCE_ADDR")).isEqualTo("123.sesame.street"); + } + +} diff --git a/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java b/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java new file mode 100644 index 000000000..bfa8271a0 --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java @@ -0,0 +1,16 @@ +package test.pivotal.pal.tracker; + +import io.pivotal.pal.tracker.WelcomeController; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class WelcomeControllerTest { + + @Test + public void itSaysHello() throws Exception { + WelcomeController controller = new WelcomeController("A welcome message"); + + assertThat(controller.sayHello()).isEqualTo("A welcome message"); + } +} diff --git a/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java new file mode 100644 index 000000000..cc7091ed4 --- /dev/null +++ b/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java @@ -0,0 +1,26 @@ +package test.pivotal.pal.trackerapi; + +import io.pivotal.pal.tracker.PalTrackerApplication; +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.boot.test.web.client.TestRestTemplate; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +public class WelcomeApiTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void exampleTest() { + String body = this.restTemplate.getForObject("/", String.class); + assertThat(body).isEqualTo("Hello from test"); + } +} From b78b30a28d9b198359ba1e46e1aaf935a3fc14b3 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 10:02:55 -0600 Subject: [PATCH 04/19] Add manifest file for deployment to PCF --- build.gradle | 10 +++++ manifest.yml | 7 ++++ .../io/pivotal/pal/tracker/EnvController.java | 41 +++++++++++++++++++ .../pal/tracker/WelcomeController.java | 11 ++++- 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 manifest.yml create mode 100644 src/main/java/io/pivotal/pal/tracker/EnvController.java diff --git a/build.gradle b/build.gradle index bf82fb8f5..b24dc23eb 100644 --- a/build.gradle +++ b/build.gradle @@ -9,4 +9,14 @@ repositories { dependencies { compile("org.springframework.boot:spring-boot-starter-web") + + testCompile("org.springframework.boot:spring-boot-starter-test") } + +bootRun.environment([ + "WELCOME_MESSAGE": "hello", +]) + +test.environment([ + "WELCOME_MESSAGE": "Hello from test", +]) diff --git a/manifest.yml b/manifest.yml new file mode 100644 index 000000000..7f06f91ee --- /dev/null +++ b/manifest.yml @@ -0,0 +1,7 @@ +--- +applications: +- name: pal-tracker + path: build/libs/pal-tracker.jar + random-route: true + env: + WELCOME_MESSAGE: Hello from Cloud Foundry \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/EnvController.java b/src/main/java/io/pivotal/pal/tracker/EnvController.java new file mode 100644 index 000000000..985368b3d --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/EnvController.java @@ -0,0 +1,41 @@ +package io.pivotal.pal.tracker; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController +public class EnvController { + + private final String port; + private final String memoryLimit; + private final String cfInstanceIndex; + private final String cfInstanceAddress; + + public EnvController( + @Value("${PORT:NOT SET}") String port, + @Value("${MEMORY_LIMIT:NOT SET}") String memoryLimit, + @Value("${CF_INSTANCE_INDEX:NOT SET}") String cfInstanceIndex, + @Value("${CF_INSTANCE_ADDR:NOT SET}") String cfInstanceAddress + ) { + this.port = port; + this.memoryLimit = memoryLimit; + this.cfInstanceIndex = cfInstanceIndex; + this.cfInstanceAddress = cfInstanceAddress; + } + + @GetMapping("/env") + public Map getEnv() { + Map env = new HashMap<>(); + + env.put("PORT", port); + env.put("MEMORY_LIMIT", memoryLimit); + env.put("CF_INSTANCE_INDEX", cfInstanceIndex); + env.put("CF_INSTANCE_ADDR", cfInstanceAddress); + + return env; + } +} \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/WelcomeController.java b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java index 4670637ec..8f36bc260 100644 --- a/src/main/java/io/pivotal/pal/tracker/WelcomeController.java +++ b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java @@ -1,13 +1,22 @@ package io.pivotal.pal.tracker; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class WelcomeController { + private String welcomeMessage; + + public WelcomeController( + @Value("${welcome_message}") String welcomeMessage + ) { + this.welcomeMessage = welcomeMessage; + } + @GetMapping("/") public String sayHello() { - return "hello"; + return welcomeMessage; } } From 950ce726daa87fdcbc8c9753e7f66a7aae66fa5d Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 10:29:35 -0600 Subject: [PATCH 05/19] Add deployment pipeline --- ci/build.yml | 22 ++++++++++++++++++++++ ci/pipeline.yml | 31 +++++++++++++++++++++++++++++++ ci/variables.example.yml | 9 +++++++++ manifest.yml | 2 -- 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 ci/build.yml create mode 100644 ci/pipeline.yml create mode 100644 ci/variables.example.yml diff --git a/ci/build.yml b/ci/build.yml new file mode 100644 index 000000000..e735e47d6 --- /dev/null +++ b/ci/build.yml @@ -0,0 +1,22 @@ +platform: linux + +image_resource: + type: docker-image + source: + repository: openjdk + tag: '8-jdk' + +inputs: + - name: pal-tracker + +outputs: + - name: build-output + +run: + path: bash + args: + - -exc + - | + cd pal-tracker + ./gradlew build + cp build/libs/pal-tracker.jar ../build-output \ No newline at end of file diff --git a/ci/pipeline.yml b/ci/pipeline.yml new file mode 100644 index 000000000..01d389043 --- /dev/null +++ b/ci/pipeline.yml @@ -0,0 +1,31 @@ +--- +resources: +- name: pal-tracker + type: git + source: + uri: {{github-repository}} + branch: master + private_key: {{github-private-key}} + +- name: deploy + type: cf + source: + api: {{cf-api-url}} + username: {{cf-username}} + password: {{cf-password}} + organization: {{cf-org}} + space: sandbox + +jobs: +- name: build-and-deploy + plan: + - get: pal-tracker + trigger: true + - task: build and test + file: pal-tracker/ci/build.yml + - put: deploy + params: + manifest: pal-tracker/manifest.yml + path: build-output/pal-tracker.jar + environment_variables: + WELCOME_MESSAGE: "Hello from Concourse" \ No newline at end of file diff --git a/ci/variables.example.yml b/ci/variables.example.yml new file mode 100644 index 000000000..649a717a6 --- /dev/null +++ b/ci/variables.example.yml @@ -0,0 +1,9 @@ +cf-api-url: CF_API_URL +cf-username: CF_USERNAME +cf-password: CF_PASSWORD +cf-org: CF_ORG +github-repository: git@github.com:GITHUB_USERNAME/pal-tracker.git +github-private-key: | + -----BEGIN RSA PRIVATE KEY----- + REPLACE WITH YOUR PRIVATE KEY HERE + -----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/manifest.yml b/manifest.yml index 7f06f91ee..2cefccc45 100644 --- a/manifest.yml +++ b/manifest.yml @@ -3,5 +3,3 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar random-route: true - env: - WELCOME_MESSAGE: Hello from Cloud Foundry \ No newline at end of file From 27cbea301053fc4c81b2997397362d69720dc811 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 11:05:07 -0600 Subject: [PATCH 06/19] Deploy to multiple environments --- ci/build.yml | 5 +- ci/pipeline.yml | 66 ++++++++++++++++++++++--- ci/variables.example.yml | 3 ++ manifest.yml => manifest-production.yml | 2 +- manifest-review.yml | 5 ++ 5 files changed, 71 insertions(+), 10 deletions(-) rename manifest.yml => manifest-production.yml (76%) create mode 100644 manifest-review.yml diff --git a/ci/build.yml b/ci/build.yml index e735e47d6..481594d97 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -8,6 +8,7 @@ image_resource: inputs: - name: pal-tracker + - name: version outputs: - name: build-output @@ -18,5 +19,5 @@ run: - -exc - | cd pal-tracker - ./gradlew build - cp build/libs/pal-tracker.jar ../build-output \ No newline at end of file + ./gradlew -P version=$(cat ../version/number) build + cp build/libs/pal-tracker-*.jar ../build-output diff --git a/ci/pipeline.yml b/ci/pipeline.yml index 01d389043..c34c3b2a9 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -7,25 +7,77 @@ resources: branch: master private_key: {{github-private-key}} -- name: deploy +- name: pal-tracker-artifacts + type: s3 + source: + bucket: {{aws-bucket}} + regexp: releases/pal-tracker-(.*).jar + access_key_id: {{aws-access-key-id}} + secret_access_key: {{aws-secret-access-key}} + +- name: version + type: semver + source: + bucket: {{aws-bucket}} + key: pal-tracker/version + access_key_id: {{aws-access-key-id}} + secret_access_key: {{aws-secret-access-key}} + +- name: review-deployment + type: cf + source: + api: {{cf-api-url}} + username: {{cf-username}} + password: {{cf-password}} + organization: {{cf-org}} + space: review + +- name: production-deployment type: cf source: api: {{cf-api-url}} username: {{cf-username}} password: {{cf-password}} organization: {{cf-org}} - space: sandbox + space: production jobs: -- name: build-and-deploy +- name: build plan: - get: pal-tracker trigger: true + - get: version + params: {bump: patch} - task: build and test file: pal-tracker/ci/build.yml - - put: deploy + - put: pal-tracker-artifacts + params: + file: build-output/pal-tracker-*.jar + - put: version + params: + file: version/number + +- name: deploy-review + plan: + - get: pal-tracker + - get: pal-tracker-artifacts + trigger: true + passed: [build] + - put: review-deployment + params: + manifest: pal-tracker/manifest-review.yml + path: pal-tracker-artifacts/pal-tracker-*.jar + environment_variables: + WELCOME_MESSAGE: "Hello from the review environment" + +- name: deploy-production + plan: + - get: pal-tracker + - get: pal-tracker-artifacts + passed: [deploy-review] + - put: production-deployment params: - manifest: pal-tracker/manifest.yml - path: build-output/pal-tracker.jar + manifest: pal-tracker/manifest-production.yml + path: pal-tracker-artifacts/pal-tracker-*.jar environment_variables: - WELCOME_MESSAGE: "Hello from Concourse" \ No newline at end of file + WELCOME_MESSAGE: "Hello from the production environment" \ No newline at end of file diff --git a/ci/variables.example.yml b/ci/variables.example.yml index 649a717a6..07bddcf7a 100644 --- a/ci/variables.example.yml +++ b/ci/variables.example.yml @@ -2,6 +2,9 @@ cf-api-url: CF_API_URL cf-username: CF_USERNAME cf-password: CF_PASSWORD cf-org: CF_ORG +aws-access-key-id: AWS_ACCESS_KEY +aws-secret-access-key: AWS_SECRET_KEY +aws-bucket: AWS_S3_BUCKET github-repository: git@github.com:GITHUB_USERNAME/pal-tracker.git github-private-key: | -----BEGIN RSA PRIVATE KEY----- diff --git a/manifest.yml b/manifest-production.yml similarity index 76% rename from manifest.yml rename to manifest-production.yml index 2cefccc45..193682320 100644 --- a/manifest.yml +++ b/manifest-production.yml @@ -2,4 +2,4 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - random-route: true + host: ps-pal-tracker diff --git a/manifest-review.yml b/manifest-review.yml new file mode 100644 index 000000000..65369e833 --- /dev/null +++ b/manifest-review.yml @@ -0,0 +1,5 @@ +--- +applications: +- name: pal-tracker + path: build/libs/pal-tracker.jar + host: ps-pal-tracker-review From 5d4a235eda1933835ad956a43fcf83dbdd85d151 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 15:04:20 -0600 Subject: [PATCH 07/19] Add tests for MVC lab --- .../InMemoryTimeEntryRepositoryTest.java | 71 +++++++++++ .../pal/tracker/TimeEntryControllerTest.java | 107 ++++++++++++++++ .../pal/trackerapi/TimeEntryApiTest.java | 119 ++++++++++++++++++ 3 files changed, 297 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java create mode 100644 src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java create mode 100644 src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java diff --git a/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java b/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java new file mode 100644 index 000000000..95e810cad --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java @@ -0,0 +1,71 @@ +package test.pivotal.pal.tracker; + +import io.pivotal.pal.tracker.InMemoryTimeEntryRepository; +import io.pivotal.pal.tracker.TimeEntry; + +import org.junit.Test; + +import java.util.List; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +public class InMemoryTimeEntryRepositoryTest { + @Test + public void create() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + TimeEntry createdTimeEntry = repo.create(new TimeEntry(123, 456, "today", 8)); + + TimeEntry expected = new TimeEntry(1L, 123, 456, "today", 8); + assertThat(createdTimeEntry).isEqualTo(expected); + + TimeEntry readEntry = repo.find(createdTimeEntry.getId()); + assertThat(readEntry).isEqualTo(expected); + } + + @Test + public void find() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + repo.create(new TimeEntry(123, 456, "today", 8)); + + TimeEntry expected = new TimeEntry(1L, 123, 456, "today", 8); + TimeEntry readEntry = repo.find(1L); + assertThat(readEntry).isEqualTo(expected); + } + + @Test + public void list() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + repo.create(new TimeEntry(123, 456, "today", 8)); + repo.create(new TimeEntry(789, 654, "yesterday", 4)); + + List expected = asList( + new TimeEntry(1L, 123, 456, "today", 8), + new TimeEntry(2L, 789, 654, "yesterday", 4) + ); + assertThat(repo.list()).isEqualTo(expected); + } + + @Test + public void update() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + TimeEntry created = repo.create(new TimeEntry(123, 456, "today", 8)); + + TimeEntry updatedEntry = repo.update( + created.getId(), + new TimeEntry(321, 654, "tomorrow", 5)); + + TimeEntry expected = new TimeEntry(created.getId(), 321, 654, "tomorrow", 5); + assertThat(updatedEntry).isEqualTo(expected); + assertThat(repo.find(created.getId())).isEqualTo(expected); + } + + @Test + public void delete() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + TimeEntry created = repo.create(new TimeEntry(123, 456, "today", 8)); + + repo.delete(created.getId()); + assertThat(repo.list()).isEmpty(); + } +} diff --git a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java new file mode 100644 index 000000000..a6f1100b5 --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java @@ -0,0 +1,107 @@ +package test.pivotal.pal.tracker; + +import io.pivotal.pal.tracker.TimeEntryRepository; +import io.pivotal.pal.tracker.TimeEntryController; +import io.pivotal.pal.tracker.TimeEntry; +import org.junit.Before; +import org.junit.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.util.List; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class TimeEntryControllerTest { + private TimeEntryRepository timeEntryRepository; + private TimeEntryController controller; + + @Before + public void setUp() throws Exception { + timeEntryRepository = mock(TimeEntryRepository.class); + controller = new TimeEntryController(timeEntryRepository); + } + + @Test + public void testCreate() throws Exception { + TimeEntry expected = new TimeEntry(1L, 123, 456, "today", 8); + doReturn(expected) + .when(timeEntryRepository) + .create(any(TimeEntry.class)); + + ResponseEntity response = controller.create(new TimeEntry(123, 456, "today", 8)); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testRead() throws Exception { + TimeEntry expected = new TimeEntry(1L, 123, 456, "today", 8); + doReturn(expected) + .when(timeEntryRepository) + .find(1L); + + ResponseEntity response = controller.read(1L); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testRead_NotFound() throws Exception { + doReturn(null) + .when(timeEntryRepository) + .find(1L); + + ResponseEntity response = controller.read(1L); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + @Test + public void testList() throws Exception { + List expected = asList( + new TimeEntry(1, 123, 456, "today", 8), + new TimeEntry(2, 789, 321, "yesterday", 4) + ); + doReturn(expected).when(timeEntryRepository).list(); + + ResponseEntity> response = controller.list(); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testUpdate() throws Exception { + TimeEntry expected = new TimeEntry(1, 987, 654, "yesterday", 4); + doReturn(expected) + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); + + ResponseEntity response = controller.update(1L, expected); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testUpdate_NotFound() throws Exception { + doReturn(null) + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); + + ResponseEntity response = controller.update(1L, new TimeEntry()); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + @Test + public void testDelete() throws Exception { + ResponseEntity response = controller.delete(1L); + verify(timeEntryRepository).delete(1L); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + } +} diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java new file mode 100644 index 000000000..36dd6c687 --- /dev/null +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -0,0 +1,119 @@ +package test.pivotal.pal.trackerapi; + +import com.jayway.jsonpath.DocumentContext; +import io.pivotal.pal.tracker.PalTrackerApplication; +import io.pivotal.pal.tracker.TimeEntry; +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.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Collection; + +import static com.jayway.jsonpath.JsonPath.parse; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +public class TimeEntryApiTest { + + @Autowired + private TestRestTemplate restTemplate; + + private TimeEntry timeEntry = new TimeEntry(123, 456, "today", 8); + + @Test + public void testCreate() throws Exception { + ResponseEntity createResponse = restTemplate.postForEntity("/time-entries", timeEntry, String.class); + + + assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED); + + DocumentContext createJson = parse(createResponse.getBody()); + assertThat(createJson.read("$.id", Long.class)).isGreaterThan(0); + assertThat(createJson.read("$.projectId", Long.class)).isEqualTo(123L); + assertThat(createJson.read("$.userId", Long.class)).isEqualTo(456L); + assertThat(createJson.read("$.date", String.class)).isEqualTo("today"); + assertThat(createJson.read("$.hours", Long.class)).isEqualTo(8); + } + + @Test + public void testList() throws Exception { + Long id = createTimeEntry(); + + + ResponseEntity listResponse = restTemplate.getForEntity("/time-entries", String.class); + + + assertThat(listResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + + DocumentContext listJson = parse(listResponse.getBody()); + + Collection timeEntries = listJson.read("$[*]", Collection.class); + assertThat(timeEntries.size()).isEqualTo(1); + + Long readId = listJson.read("$[0].id", Long.class); + assertThat(readId).isEqualTo(id); + } + + @Test + public void testRead() throws Exception { + Long id = createTimeEntry(); + + + ResponseEntity readResponse = this.restTemplate.getForEntity("/time-entries/" + id, String.class); + + + assertThat(readResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + DocumentContext readJson = parse(readResponse.getBody()); + assertThat(readJson.read("$.id", Long.class)).isEqualTo(id); + assertThat(readJson.read("$.projectId", Long.class)).isEqualTo(123L); + assertThat(readJson.read("$.userId", Long.class)).isEqualTo(456L); + assertThat(readJson.read("$.date", String.class)).isEqualTo("today"); + assertThat(readJson.read("$.hours", Long.class)).isEqualTo(8); + } + + @Test + public void testUpdate() throws Exception { + Long id = createTimeEntry(); + TimeEntry updatedTimeEntry = new TimeEntry(2, 3, "tomorrow", 9); + + + ResponseEntity updateResponse = restTemplate.exchange("/time-entries/" + id, HttpMethod.PUT, new HttpEntity<>(updatedTimeEntry, null), String.class); + + + assertThat(updateResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + + DocumentContext updateJson = parse(updateResponse.getBody()); + assertThat(updateJson.read("$.id", Long.class)).isEqualTo(id); + assertThat(updateJson.read("$.projectId", Long.class)).isEqualTo(2L); + assertThat(updateJson.read("$.userId", Long.class)).isEqualTo(3L); + assertThat(updateJson.read("$.date", String.class)).isEqualTo("tomorrow"); + assertThat(updateJson.read("$.hours", Long.class)).isEqualTo(9); + } + + @Test + public void testDelete() throws Exception { + Long id = createTimeEntry(); + + + ResponseEntity deleteResponse = restTemplate.exchange("/time-entries/" + id, HttpMethod.DELETE, null, String.class); + + + assertThat(deleteResponse.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + + ResponseEntity deletedReadResponse = this.restTemplate.getForEntity("/time-entries/" + id, String.class); + assertThat(deletedReadResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + private Long createTimeEntry() { + return restTemplate.postForObject("/time-entries", timeEntry, TimeEntry.class).getId(); + } +} From cfc4dbe277bc128ccab501864082e8d843fded15 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 15:04:46 -0600 Subject: [PATCH 08/19] Add TimeEntry MVC in memory --- .../tracker/InMemoryTimeEntryRepository.java | 38 ++++++++ .../pal/tracker/PalTrackerApplication.java | 6 ++ .../io/pivotal/pal/tracker/TimeEntry.java | 86 +++++++++++++++++++ .../pal/tracker/TimeEntryController.java | 57 ++++++++++++ .../pal/tracker/TimeEntryRepository.java | 11 +++ 5 files changed, 198 insertions(+) create mode 100644 src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntry.java create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntryController.java create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java diff --git a/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java new file mode 100644 index 000000000..3f9e0373d --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java @@ -0,0 +1,38 @@ +package io.pivotal.pal.tracker; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class InMemoryTimeEntryRepository implements TimeEntryRepository { + private HashMap timeEntries = new HashMap<>(); + + @Override + public TimeEntry create(TimeEntry timeEntry) { + timeEntry.setId(timeEntries.size() + 1); + timeEntries.put(timeEntry.getId(), timeEntry); + return timeEntry; + } + + @Override + public TimeEntry find(Long id) { + return timeEntries.get(id); + } + + @Override + public List list() { + return new ArrayList<>(timeEntries.values()); + } + + @Override + public TimeEntry update(Long id, TimeEntry timeEntry) { + timeEntries.replace(id, timeEntry); + timeEntry.setId(id); + return timeEntry; + } + + @Override + public void delete(Long id) { + timeEntries.remove(id); + } +} diff --git a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java index e33c3526e..dbcc4c352 100644 --- a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java +++ b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java @@ -2,6 +2,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; @SpringBootApplication public class PalTrackerApplication { @@ -9,4 +10,9 @@ public class PalTrackerApplication { public static void main(String[] args) { SpringApplication.run(PalTrackerApplication.class, args); } + + @Bean + TimeEntryRepository timeEntryRepository() { + return new InMemoryTimeEntryRepository(); + } } diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntry.java b/src/main/java/io/pivotal/pal/tracker/TimeEntry.java new file mode 100644 index 000000000..471d3052f --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntry.java @@ -0,0 +1,86 @@ +package io.pivotal.pal.tracker; + +public class TimeEntry { + private long id; + private long projectId; + private long userId; + private String date; + private int hours; + + public TimeEntry() { + } + + public TimeEntry(long projectId, long userId, String date, int hours) { + this.projectId = projectId; + this.userId = userId; + this.date = date; + this.hours = hours; + } + + public TimeEntry(long id, long projectId, long userId, String date, int hours) { + this.id = id; + this.projectId = projectId; + this.userId = userId; + this.date = date; + this.hours = hours; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getProjectId() { + return projectId; + } + + public long getUserId() { + return userId; + } + + public String getDate() { + return date; + } + + public int getHours() { + return hours; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TimeEntry timeEntry = (TimeEntry) o; + + if (id != timeEntry.id) return false; + if (projectId != timeEntry.projectId) return false; + if (userId != timeEntry.userId) return false; + if (hours != timeEntry.hours) return false; + return date != null ? date.equals(timeEntry.date) : timeEntry.date == null; + } + + @Override + public int hashCode() { + int result = (int) (id ^ (id >>> 32)); + result = 31 * result + (int) (projectId ^ (projectId >>> 32)); + result = 31 * result + (int) (userId ^ (userId >>> 32)); + result = 31 * result + (date != null ? date.hashCode() : 0); + result = 31 * result + hours; + return result; + } + + @Override + public String toString() { + return "TimeEntry{" + + "id=" + id + + ", projectId=" + projectId + + ", userId=" + userId + + ", date='" + date + '\'' + + ", hours=" + hours + + '}'; + } +} diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java new file mode 100644 index 000000000..a9b854f6c --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java @@ -0,0 +1,57 @@ +package io.pivotal.pal.tracker; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/time-entries") +public class TimeEntryController { + + private TimeEntryRepository timeEntriesRepo; + + public TimeEntryController(TimeEntryRepository timeEntriesRepo) { + this.timeEntriesRepo = timeEntriesRepo; + } + + @PostMapping + public ResponseEntity create(@RequestBody TimeEntry timeEntry) { + TimeEntry createdTimeEntry = timeEntriesRepo.create(timeEntry); + + return new ResponseEntity<>(createdTimeEntry, HttpStatus.CREATED); + } + + @GetMapping("{id}") + public ResponseEntity read(@PathVariable Long id) { + TimeEntry timeEntry = timeEntriesRepo.find(id); + if (timeEntry != null) { + return new ResponseEntity<>(timeEntry, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @GetMapping + public ResponseEntity> list() { + return new ResponseEntity<>(timeEntriesRepo.list(), HttpStatus.OK); + } + + @PutMapping("{id}") + public ResponseEntity update(@PathVariable Long id, @RequestBody TimeEntry timeEntry) { + TimeEntry updatedTimeEntry = timeEntriesRepo.update(id, timeEntry); + if (updatedTimeEntry != null) { + return new ResponseEntity<>(updatedTimeEntry, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @DeleteMapping("{id}") + public ResponseEntity delete(@PathVariable Long id) { + timeEntriesRepo.delete(id); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java new file mode 100644 index 000000000..f106aa201 --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java @@ -0,0 +1,11 @@ +package io.pivotal.pal.tracker; + +import java.util.List; + +public interface TimeEntryRepository { + TimeEntry create(TimeEntry timeEntry); + TimeEntry find(Long id); + List list(); + TimeEntry update(Long id, TimeEntry timeEntry); + void delete(Long id); +} From b661b6490a39efe4c1739f129a821de2a363adff Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 16:00:48 -0600 Subject: [PATCH 09/19] Add migrations --- databases/tracker/create_databases.sql | 9 +++++++++ databases/tracker/migrations/V1__initial_schema.sql | 11 +++++++++++ 2 files changed, 20 insertions(+) create mode 100644 databases/tracker/create_databases.sql create mode 100644 databases/tracker/migrations/V1__initial_schema.sql diff --git a/databases/tracker/create_databases.sql b/databases/tracker/create_databases.sql new file mode 100644 index 000000000..e99807019 --- /dev/null +++ b/databases/tracker/create_databases.sql @@ -0,0 +1,9 @@ +DROP DATABASE IF EXISTS tracker_dev; +DROP DATABASE IF EXISTS tracker_test; + +CREATE USER 'tracker'@'localhost' + IDENTIFIED BY ''; +GRANT ALL PRIVILEGES ON *.* TO 'tracker' @'localhost'; + +CREATE DATABASE tracker_dev; +CREATE DATABASE tracker_test; diff --git a/databases/tracker/migrations/V1__initial_schema.sql b/databases/tracker/migrations/V1__initial_schema.sql new file mode 100644 index 000000000..7ac472e52 --- /dev/null +++ b/databases/tracker/migrations/V1__initial_schema.sql @@ -0,0 +1,11 @@ +CREATE TABLE time_entries ( + id BIGINT(20) NOT NULL AUTO_INCREMENT, + project_id BIGINT(20), + user_id BIGINT(20), + date VARCHAR(255), + hours INT, + + PRIMARY KEY (id) +) + ENGINE = innodb + DEFAULT CHARSET = utf8; From 25dbc077a248e9706fdcbc1f4ae8a04211a85cee Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 11:45:04 -0600 Subject: [PATCH 10/19] Add tests for JDBC lab --- .../tracker/JdbcTimeEntryRepositoryTest.java | 154 ++++++++++++++++++ .../pal/trackerapi/TimeEntryApiTest.java | 12 ++ 2 files changed, 166 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java diff --git a/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java new file mode 100644 index 000000000..b4aeb0bed --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java @@ -0,0 +1,154 @@ +package test.pivotal.pal.tracker; + + +import com.mysql.cj.jdbc.MysqlDataSource; +import io.pivotal.pal.tracker.JdbcTimeEntryRepository; +import io.pivotal.pal.tracker.TimeEntry; +import io.pivotal.pal.tracker.TimeEntryRepository; +import org.junit.Before; +import org.junit.Test; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JdbcTimeEntryRepositoryTest { + private TimeEntryRepository subject; + private JdbcTemplate jdbcTemplate; + + @Before + public void setUp() throws Exception { + MysqlDataSource dataSource = new MysqlDataSource(); + dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); + + subject = new JdbcTimeEntryRepository(dataSource); + + jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.execute("DELETE FROM time_entries"); + } + + @Test + public void createInsertsATimeEntryRecord() throws Exception { + TimeEntry newTimeEntry = new TimeEntry(123, 321, "today", 8); + TimeEntry entry = subject.create(newTimeEntry); + + Map foundEntry = jdbcTemplate.queryForMap("Select * from time_entries where id = ?", entry.getId()); + + assertThat(foundEntry.get("id")).isEqualTo(entry.getId()); + assertThat(foundEntry.get("project_id")).isEqualTo(123L); + assertThat(foundEntry.get("user_id")).isEqualTo(321L); + assertThat(foundEntry.get("date")).isEqualTo("today"); + assertThat(foundEntry.get("hours")).isEqualTo(8); + } + + @Test + public void createReturnsTheCreatedTimeEntry() throws Exception { + TimeEntry newTimeEntry = new TimeEntry(123, 321, "today", 8); + TimeEntry entry = subject.create(newTimeEntry); + + assertThat(entry.getId()).isNotNull(); + assertThat(entry.getProjectId()).isEqualTo(123); + assertThat(entry.getUserId()).isEqualTo(321); + assertThat(entry.getDate()).isEqualTo("today"); + assertThat(entry.getHours()).isEqualTo(8); + } + + @Test + public void findFindsATimeEntry() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (999, 123, 321, 'today', 8)" + ); + + TimeEntry timeEntry = subject.find(999L); + + assertThat(timeEntry.getId()).isEqualTo(999L); + assertThat(timeEntry.getProjectId()).isEqualTo(123L); + assertThat(timeEntry.getUserId()).isEqualTo(321L); + assertThat(timeEntry.getDate()).isEqualTo("today"); + assertThat(timeEntry.getHours()).isEqualTo(8); + } + + @Test + public void findReturnsNullWhenNotFound() throws Exception { + TimeEntry timeEntry = subject.find(999L); + + assertThat(timeEntry).isNull(); + } + + @Test + public void listFindsAllTimeEntries() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (999, 123, 321, 'today', 8), (888, 456, 678, 'yesterday', 9)" + ); + + List timeEntries = subject.list(); + assertThat(timeEntries.size()).isEqualTo(2); + + TimeEntry timeEntry = timeEntries.get(0); + assertThat(timeEntry.getId()).isEqualTo(888L); + assertThat(timeEntry.getProjectId()).isEqualTo(456L); + assertThat(timeEntry.getUserId()).isEqualTo(678L); + assertThat(timeEntry.getDate()).isEqualTo("yesterday"); + assertThat(timeEntry.getHours()).isEqualTo(9); + + timeEntry = timeEntries.get(1); + assertThat(timeEntry.getId()).isEqualTo(999L); + assertThat(timeEntry.getProjectId()).isEqualTo(123L); + assertThat(timeEntry.getUserId()).isEqualTo(321L); + assertThat(timeEntry.getDate()).isEqualTo("today"); + assertThat(timeEntry.getHours()).isEqualTo(8); + } + + @Test + public void updateReturnsTheUpdatedRecord() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (1000, 123, 321, 'today', 8)"); + + TimeEntry timeEntryUpdates = new TimeEntry(456, 987, "tomorrow", 10); + + TimeEntry updatedTimeEntry = subject.update(1000L, timeEntryUpdates); + + assertThat(updatedTimeEntry.getId()).isEqualTo(1000L); + assertThat(updatedTimeEntry.getProjectId()).isEqualTo(456L); + assertThat(updatedTimeEntry.getUserId()).isEqualTo(987L); + assertThat(updatedTimeEntry.getDate()).isEqualTo("tomorrow"); + assertThat(updatedTimeEntry.getHours()).isEqualTo(10); + } + + @Test + public void updateUpdatesTheRecord() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (1000, 123, 321, 'today', 8)"); + + TimeEntry updatedTimeEntry = new TimeEntry(456, 322, "tomorrow", 10); + + TimeEntry timeEntry = subject.update(1000L, updatedTimeEntry); + + Map foundEntry = jdbcTemplate.queryForMap("Select * from time_entries where id = ?", timeEntry.getId()); + + assertThat(foundEntry.get("id")).isEqualTo(timeEntry.getId()); + assertThat(foundEntry.get("project_id")).isEqualTo(456L); + assertThat(foundEntry.get("user_id")).isEqualTo(322L); + assertThat(foundEntry.get("date")).isEqualTo("tomorrow"); + assertThat(foundEntry.get("hours")).isEqualTo(10); + } + + @Test + public void deleteRemovesTheRecord() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (999, 123, 321, 'today', 8)" + ); + + subject.delete(999L); + + Map foundEntry = jdbcTemplate.queryForMap("Select count(*) count from time_entries where id = ?", 999); + assertThat(foundEntry.get("count")).isEqualTo(0L); + } +} diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java index 36dd6c687..a07c3fad8 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -1,8 +1,10 @@ package test.pivotal.pal.trackerapi; import com.jayway.jsonpath.DocumentContext; +import com.mysql.cj.jdbc.MysqlDataSource; import io.pivotal.pal.tracker.PalTrackerApplication; import io.pivotal.pal.tracker.TimeEntry; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -12,6 +14,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.util.Collection; @@ -29,6 +32,15 @@ public class TimeEntryApiTest { private TimeEntry timeEntry = new TimeEntry(123, 456, "today", 8); + @Before + public void setUp() throws Exception { + MysqlDataSource dataSource = new MysqlDataSource(); + dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); + + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.execute("TRUNCATE time_entries"); + } + @Test public void testCreate() throws Exception { ResponseEntity createResponse = restTemplate.postForEntity("/time-entries", timeEntry, String.class); From f5889b79418a3c5db9d810a57423e129a7255c95 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 11:45:04 -0600 Subject: [PATCH 11/19] pipeline10 --- build.gradle | 25 +++++- ci/build.yml | 23 ++++- manifest-production.yml | 4 +- manifest-review.yml | 4 +- .../pal/tracker/JdbcTimeEntryRepository.java | 87 +++++++++++++++++++ .../pal/tracker/PalTrackerApplication.java | 6 +- .../pal/trackerapi/TimeEntryApiTest.java | 2 +- 7 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java diff --git a/build.gradle b/build.gradle index b24dc23eb..5d3e3ed57 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,9 @@ +import org.flywaydb.gradle.task.FlywayMigrateTask + plugins { id "java" id "org.springframework.boot" version "1.5.4.RELEASE" + id "org.flywaydb.flyway" version "4.2.0" } repositories { @@ -9,14 +12,32 @@ repositories { dependencies { compile("org.springframework.boot:spring-boot-starter-web") + compile("org.springframework.boot:spring-boot-starter-jdbc") + + compile("mysql:mysql-connector-java:6.0.6") testCompile("org.springframework.boot:spring-boot-starter-test") } +def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" bootRun.environment([ - "WELCOME_MESSAGE": "hello", + "WELCOME_MESSAGE": "hello", + "SPRING_DATASOURCE_URL": developmentDbUrl, ]) +def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" test.environment([ - "WELCOME_MESSAGE": "Hello from test", + "WELCOME_MESSAGE": "Hello from test", + "SPRING_DATASOURCE_URL": testDbUrl, ]) + +flyway { + url = developmentDbUrl + user = "tracker" + password = "" + locations = ["filesystem:databases/tracker/migrations"] +} + +task testMigrate(type: FlywayMigrateTask) { + url = testDbUrl +} \ No newline at end of file diff --git a/ci/build.yml b/ci/build.yml index 481594d97..b01d4da2d 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -18,6 +18,25 @@ run: args: - -exc - | + + function stop_mysql { + service mysql stop + } + trap stop_mysql EXIT + + export DEBIAN_FRONTEND="noninteractive" + + apt-get update + apt-get install -y software-properties-common + apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db + apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 + add-apt-repository 'deb http://ftp.osuosl.org/pub/mariadb/repo/10.2/ubuntu trusty main' + + apt-get -y install mariadb-server + service mysql start + cd pal-tracker - ./gradlew -P version=$(cat ../version/number) build - cp build/libs/pal-tracker-*.jar ../build-output + + mysql -uroot < databases/tracker/create_databases.sql + ./gradlew -P version=$(cat ../version/number) testMigrate build + cp build/libs/pal-tracker-*.jar ../build-output \ No newline at end of file diff --git a/manifest-production.yml b/manifest-production.yml index 193682320..2e4a54d68 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -2,4 +2,6 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - host: ps-pal-tracker + host: ps-pal-tracker-seokwon + services: + - tracker-databse diff --git a/manifest-review.yml b/manifest-review.yml index 65369e833..f9e7c3247 100644 --- a/manifest-review.yml +++ b/manifest-review.yml @@ -2,4 +2,6 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - host: ps-pal-tracker-review + host: ps-pal-tracker-review-seokwon + service: + - tracker-database diff --git a/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java new file mode 100644 index 000000000..ce494d6ec --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java @@ -0,0 +1,87 @@ +package io.pivotal.pal.tracker; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; + +import javax.sql.DataSource; +import java.sql.PreparedStatement; +import java.util.List; + +import static java.sql.Statement.RETURN_GENERATED_KEYS; + +public class JdbcTimeEntryRepository implements TimeEntryRepository { + + private final JdbcTemplate jdbcTemplate; + + public JdbcTimeEntryRepository(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public TimeEntry create(TimeEntry timeEntry) { + KeyHolder generatedKeyHolder = new GeneratedKeyHolder(); + + jdbcTemplate.update(connection -> { + PreparedStatement statement = connection.prepareStatement( + "INSERT INTO time_entries (project_id, user_id, date, hours) " + + "VALUES (?, ?, ?, ?)", + RETURN_GENERATED_KEYS + ); + + statement.setLong(1, timeEntry.getProjectId()); + statement.setLong(2, timeEntry.getUserId()); + statement.setString(3, timeEntry.getDate()); + statement.setInt(4, timeEntry.getHours()); + + return statement; + }, generatedKeyHolder); + + return find(generatedKeyHolder.getKey().longValue()); + } + + @Override + public TimeEntry find(Long id) { + return jdbcTemplate.query( + "SELECT id, project_id, user_id, date, hours FROM time_entries WHERE id = ?", + new Object[]{id}, + extractor); + } + + @Override + public List list() { + return jdbcTemplate.query("SELECT id, project_id, user_id, date, hours FROM time_entries", mapper); + } + + @Override + public TimeEntry update(Long id, TimeEntry timeEntry) { + jdbcTemplate.update("UPDATE time_entries " + + "SET project_id = ?, user_id = ?, date = ?, hours = ? " + + "WHERE id = ?", + timeEntry.getProjectId(), + timeEntry.getUserId(), + timeEntry.getDate(), + timeEntry.getHours(), + id); + + return find(id); + } + + @Override + public void delete(Long id) { + jdbcTemplate.update("DELETE FROM time_entries WHERE id = ?", id); + } + + private final RowMapper mapper = (rs, rowNum) -> new TimeEntry( + rs.getLong("id"), + rs.getLong("project_id"), + rs.getLong("user_id"), + rs.getString("date"), + rs.getInt("hours") + ); + + private final ResultSetExtractor extractor = + (rs) -> rs.next() ? mapper.mapRow(rs, 1) : null; +} diff --git a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java index dbcc4c352..eda0a516f 100644 --- a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java +++ b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java @@ -4,6 +4,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import javax.sql.DataSource; + @SpringBootApplication public class PalTrackerApplication { @@ -12,7 +14,7 @@ public static void main(String[] args) { } @Bean - TimeEntryRepository timeEntryRepository() { - return new InMemoryTimeEntryRepository(); + TimeEntryRepository timeEntryRepository(DataSource dataSource) { + return new JdbcTimeEntryRepository(dataSource); } } diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java index a07c3fad8..6463b3806 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -128,4 +128,4 @@ public void testDelete() throws Exception { private Long createTimeEntry() { return restTemplate.postForObject("/time-entries", timeEntry, TimeEntry.class).getId(); } -} +} \ No newline at end of file From 2f593231feb76279cb8fbf39fed9df237f460011 Mon Sep 17 00:00:00 2001 From: Seokwon Park Date: Thu, 7 Sep 2017 14:54:49 +0900 Subject: [PATCH 12/19] pipeline11 --- manifest-production.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest-production.yml b/manifest-production.yml index 2e4a54d68..e6c9fb2ec 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -4,4 +4,4 @@ applications: path: build/libs/pal-tracker.jar host: ps-pal-tracker-seokwon services: - - tracker-databse + - tracker-database From a673b2ea8f4ebadc3d0c22d58e49f84378fdc09f Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 12:28:32 -0600 Subject: [PATCH 13/19] Add tests for Actuator lab --- .../pivotal/pal/trackerapi/HealthApiTest.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java diff --git a/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java new file mode 100644 index 000000000..b3eef23cc --- /dev/null +++ b/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java @@ -0,0 +1,38 @@ +package test.pivotal.pal.trackerapi; + +import com.jayway.jsonpath.DocumentContext; +import io.pivotal.pal.tracker.PalTrackerApplication; +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.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static com.jayway.jsonpath.JsonPath.parse; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +public class HealthApiTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void healthTest() { + ResponseEntity response = this.restTemplate.getForEntity("/health", String.class); + + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + + DocumentContext healthJson = parse(response.getBody()); + + assertThat(healthJson.read("$.status", String.class)).isEqualTo("UP"); + assertThat(healthJson.read("$.db.status", String.class)).isEqualTo("UP"); + assertThat(healthJson.read("$.diskSpace.status", String.class)).isEqualTo("UP"); + } +} From 6c761186477999747c1fc03d531de5eb4cd8c07b Mon Sep 17 00:00:00 2001 From: Seokwon Date: Mon, 11 Sep 2017 10:02:18 +0900 Subject: [PATCH 14/19] 01pipeline0911 --- build.gradle | 8 ++++++-- manifest-review.yml | 2 +- .../io/pivotal/pal/tracker/EnvController.class | Bin 0 -> 1631 bytes .../tracker/InMemoryTimeEntryRepository.class | Bin 0 -> 1945 bytes .../pal/tracker/JdbcTimeEntryRepository.class | Bin 0 -> 5777 bytes .../pal/tracker/PalTrackerApplication.class | Bin 0 -> 1090 bytes .../io/pivotal/pal/tracker/TimeEntry.class | Bin 0 -> 2419 bytes .../pal/tracker/TimeEntryController.class | Bin 0 -> 3445 bytes .../pal/tracker/TimeEntryRepository.class | Bin 0 -> 560 bytes .../pivotal/pal/tracker/WelcomeController.class | Bin 0 -> 837 bytes .../pivotal/pal/tracker/EnvControllerTest.class | Bin 0 -> 1469 bytes .../InMemoryTimeEntryRepositoryTest.class | Bin 0 -> 3052 bytes .../tracker/JdbcTimeEntryRepositoryTest.class | Bin 0 -> 6067 bytes .../pal/tracker/TimeEntryControllerTest.class | Bin 0 -> 5111 bytes .../pal/tracker/WelcomeControllerTest.class | Bin 0 -> 1029 bytes .../pal/trackerapi/TimeEntryApiTest.class | Bin 0 -> 6931 bytes .../pivotal/pal/trackerapi/WelcomeApiTest.class | Bin 0 -> 1704 bytes 17 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 out/production/classes/io/pivotal/pal/tracker/EnvController.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/JdbcTimeEntryRepository.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/PalTrackerApplication.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntry.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryRepository.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/WelcomeController.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/EnvControllerTest.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/TimeEntryControllerTest.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/WelcomeControllerTest.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class diff --git a/build.gradle b/build.gradle index 5d3e3ed57..efe803324 100644 --- a/build.gradle +++ b/build.gradle @@ -17,18 +17,22 @@ dependencies { compile("mysql:mysql-connector-java:6.0.6") testCompile("org.springframework.boot:spring-boot-starter-test") + compile("org.springframework.boot:spring-boot-starter-actuator") + } def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" bootRun.environment([ - "WELCOME_MESSAGE": "hello", + "WELCOME_MESSAGE" : "hello", "SPRING_DATASOURCE_URL": developmentDbUrl, + "MANAGEMENT_SECURITY_ENABLED": false, ]) def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" test.environment([ - "WELCOME_MESSAGE": "Hello from test", + "WELCOME_MESSAGE" : "Hello from test", "SPRING_DATASOURCE_URL": testDbUrl, + "MANAGEMENT_SECURITY_ENABLED": false, ]) flyway { diff --git a/manifest-review.yml b/manifest-review.yml index f9e7c3247..50eabefb2 100644 --- a/manifest-review.yml +++ b/manifest-review.yml @@ -3,5 +3,5 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar host: ps-pal-tracker-review-seokwon - service: + services: - tracker-database diff --git a/out/production/classes/io/pivotal/pal/tracker/EnvController.class b/out/production/classes/io/pivotal/pal/tracker/EnvController.class new file mode 100644 index 0000000000000000000000000000000000000000..7eab781d1c865e4064fc1033768c5b213022d36a GIT binary patch literal 1631 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 GKK~Cc3a-%r literal 0 HcmV?d00001 diff --git a/out/production/classes/io/pivotal/pal/tracker/TimeEntryRepository.class b/out/production/classes/io/pivotal/pal/tracker/TimeEntryRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..7f2d9dfdfd81f3a85f7ae54dbc34aea50b2ce830 GIT binary patch literal 560 zcma)3y-ve05dN;D2`zu6U3i3q0WTFx5nDS&LZYadL>_0<@703Hf)4H6wP zq=R+R_j7mt?)&HS3&0eU2-gwDgfMeTdPP{oskWlhTkE9}6+iD}_O6_G(I0AFdbgV; z-4a6Zrpuf#jnlQNozu~A`F+dc9~je!;+i-aGzCWH`-gbyW8hRxa~T} z!9f@uvVMA9tIUT4LXU3^^aH{P0)T5DVN)ZVGCyFphaux|irwDM0G*87XcgP0-QF^2^1jFgryCwU_68L&3#-a_o(%m5w7eDRiM0G}t!c+-Z%vj-OA_wwPqbILjFnTw z^jcVvOE2v+tqJdy(ZsktGIq>eQOZ6l3ueZi@I(sj_>oAxv8P=81@Ks86G_~SY3b10 z?|+=P<9%ND;}gS*6Q?g^mKla#f3Wy`hRy$_T|m)HCOlF4@T{2U((j3)s2k2WL+ijy zY$|tE*~g8Ao;)o7iQ&dNnSvH6Dwp_DcU6)kZldWgAE^iL-3`lY0=&$Y`KT%P;D2NDB7s25#cgL zgsWI-V9AddQFmx{9j=gB!`1mMoFYaQjp0`WpUDXb2$EAKdlhk17tD!!ly6gJomTf9 Rn5!W@?2=J1%Y=8=D|}o2sdBpBbul=WOlQy7 z6rSzuEi@v_M5yK7SiCH3*YOoBGW2Xz9AO{wE!(%L25HBkbgS14$-L|B83z@rrBO>$ z+jJ|Q#aC=uzV3!Xj7j|obfA-A;5?~QbLM7dr>Owr6OR}olT!@s;}er(KKIQ-KIRLL zb0Ha*61WU%Y3%9xMp4221Rfww8mkox<*ndh0vTi#HFLh2ZrT(+%U_wk*)%AXlR0h+v2`S zyty!MZSYdnv1#PkrDmFz$n2ZmCO@ih$KnAmjivGXXa5+&&}{p;ql#H7x(xGS6=&J~ zm9q9S`q54w9QliYnuboYU34;NWwbU8pMt%sUU!p?S3wOIX`cuja0xwhVlV7s6O9z}muCQ-CGI zSX^RKT@+H?btq(8kt8KugrrF466t$J3OVVMs`~#j`Y!UiMhp@3l9$Xa aK>7&jCnQG54cw%itib@S47bQeF!%$aGJFF7 literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.class b/out/test/classes/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.class new file mode 100644 index 0000000000000000000000000000000000000000..b29b333b1498268f830af0d93e84df54181cd510 GIT binary patch literal 3052 zcmbVO>sHiO7~O{nWQ;H zLtmkPMpkY6yQ?qI-+hs;?vrFPQ*l5$YmuCM=R4=y``h~@{Q1u>zXQnOrG|63qvEcH z4tc-V$*}x=9}mPIX-sKQFfB9g%h6{VW-zPabL8dYoQ8Qkkh2ftHK`BcLC242JZkLN zp_QidB%)0 zXKlJ(ZOPuWrkud5Em#%Tw*wjCnsEXsc$OKkg5q&XAeXn@_uY!ybVu71lR^S#+wGL( za9!GZS+NR%RTS7qAjJ@*fHw8AU{wO!b$k`y2=pyfoq!ycZQmyUNyj18aGIs_u2(WP zs_df?lTy}ghdU}|+Ye;IhPh=L)xa(rc}7^a_rL9|RT?eLy^%YyH;G%ai`7ao?4la? zK|qf}4Mtr>t8B?WYm08xD_GOEl=a*m0$q~Gb)3h5z~%QBMxejVqC^9M&cH32HCg0a z9V7Toj!YG6ItnPNuym}Wq~j@U6&pI9p{(ONHYr$*7P34pR^jMyQPFV&&*eH#g)i5! z`{}@?13mYJjtNwC+{TuU7n0D8|7%-H8Y)^gos#ijZG#39IMNIy<`FoSot=#&4U-oG z&vr`VGxDrWXZO~2aPJhFlQr5EIW4_u`o84_8%DwPEW@nW#$*_f?xr82*`=o@N!-r1 zfQ^`q4~G8UxyPC>Pk9h!MLW`ogKsbRmShTO`%}-WW_gL{{^t8vPHJ|RtYC)cjqk8o zfrCvU@~%@7P`Au-)q1c_jk2?YEd=E>A?I34hp~5Rd*p%eR8M-ISz|A|OkW;wf%DlX zEp?JchR9mq13lRoPiyc>jHelEBY~`yG~w+^H&X;YY2}$<+EuUSa`tJtb$!^Uz1{gv zojkH*V|hMQx?{5v)NlqL@jEJiU0`c5!0QD*8N3VLJBMCD{LDcIvT+h{0D~OsQ38fA z%qPLu%K%d0;$NXbejgWJqhs|IQu*Q6P*yYO%s}ng*PVU^t&aV3GN`pi_4$!)9JpA= z!Ij~WIu324yN<(SN}tjQH<6rz$_m{`<1kL)2u6{?79LWH>pMT{_wSzN>= zB0a_b9}IjFHG?;Z{0-qjIU8^cGi@Na&*(BrEjAZS>6z*KvFsACmHk zka8-tQ$%@ylrNBSmavETVYvj4@<@yIM-UUr&tl5!1V4(KA?3)3QDmHvcJfUm)c=m{ z>F!E>u3^E5(n;BY(~))i=VeSfG=n6rk10~rKIIMjCb6c{1K3A$S4cZY+Sf?)IDL_9@a(=FoV667NZ^4=JEJE=Y;t`1E83maU*i2wiq literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.class b/out/test/classes/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.class new file mode 100644 index 0000000000000000000000000000000000000000..98958f406f1811e27b5c1f570e940da9279c6840 GIT binary patch literal 6067 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 literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/tracker/TimeEntryControllerTest.class b/out/test/classes/test/pivotal/pal/tracker/TimeEntryControllerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..3dfcce7ec5975da0ba2c1b8ffcb90f5065cc18bc GIT binary patch literal 5111 zcmbVP`Fj)B6+I&_Ba30P4GqKz1Pa&&EE-G#H6b-#AdYMoTiCcMsAG9-kC8QEG%~np zn{?kx_q|Q`y|hV#m(V2co~HZH{ra!;>$x*yX^bRW6n{uF@4b8PIrpA>XY~4iUwQ>V zJN^|x2NEjY5rK~JI=mT46%!HEz>u3s6;m}lDF2@JFO=89n2sO}Q*357;VM!Q%p$!B zAy~4J5<q*vLlUAXkSuT#w)e=4I`jvi5WY?~>bh<2_>bj95G?=iVzH?^}%-@-M1( ztOoB4JqWnla}rM^8mF{O{O$@~UA+ZuXkHs#)5tahf&tb&JQj*)fN2V4jm=UE1>$r{eZ48ix@NE#W(w9;7>Ur?}XD3_L` zjF?%Ikvh|9X2IQdLD*B#nH&*E&l;1KZ3ue_-enuQLzrRXM#e}w#)N`>8zzy)VcW=N z7@DRnF^kT#8QV-xP1^dbaow`7MyDMo6CLsou~4QnPqS_ASr3TMCoC|A44sj8uFcha zW;I{^m4-OIG4?Zrv3)gWV!(2Gtz4SHT^unp>j=sjeNK<&95WS_6Dc=`sW!5HOG`t0!3R){~ zDHUJT@Fjd%!)ZKLfn_16#QKVcGk9FV-i-pX2OMicpBJOAYWNzyuHqXSzKQ2ld`rW( z1;BUkUD^JgTzy}~4>bG`KT`2y4L`w8Rs2lD&v8q|FEsoT&ue%AzmlC7RoqswpkWb9 z8kX^rhCBGRhL>?m!z;L@pl+TSHf%;$@v4Ru{6@oX@jLnWy@D-kizqJtpyH1b9;J+tCtO@jO#*(dG?1tT2!}WHRn*?(6FdxEgk3 zP;@*Yd*KOd$Z&FYnzJwnzz^q5mQ~1ECpSJ$)9A2V7&g%~1u5OVPj1(z4Z1#9(sdz6 zd2)?Gcm2APoHlHZ=_)|S%~OUm z%m+s<>pjs9HLo+@&vVFohe>CT#Gr5t+nI?b`HYF`88h1HF33}gv&Y+wPwN!vxnNbg ztl5li%M+5REA3}ID-Uz0aMJV4!28RRE8tV0b`AA(#(TP{_3}fxU_ie|?+FNJgPHBQ zmeW&lOTnoiyESqy&XCV6&qrkm&RP@HTqBcTLZ^@4yK@ghK19p9)PjdshnhPWVpe*J znVi#8Ib(2=Bc-`72vD|}x9Uk#9;od>l#}t&dd~P(<+ySY z)K2*CJhgL%ZBDYE8j2#yQz$YpnCKcDi1!S{@eqGcb?|poh<|*Eaj*DR!tXjL@mror-D6$+taTT6pqtm4cK|);6trnrKx6-r1?*VD z&KK~81>85vyIo7zeRu))j~1ZsvUop}9zz%V<$NMJ}MVM7dT# zEgvE4qeW!9S)=oAgIr*9BoITI6%n$Y_AH<5ai*gmqoj5L5Bk~@+L97)?^1qmY@M3D zEuVm8UzZ7=dRrroc^r%Qv5F5KJj|e_L{CcFVS+~f)mZu__Si}huDk0I3+h?9cFH@pd zDA5EbhmMmN=O|4EBzn#Ve=;x8a**Gihy1MHLv1qIyw-mtCi8Czk8=8XopdsUeDj=I p+dTE83{0_ir?Cenj$$T&sLe;DaZ0%rr!{*{12uME!hA7 literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/tracker/WelcomeControllerTest.class b/out/test/classes/test/pivotal/pal/tracker/WelcomeControllerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..a49636ed7b17145d6c5abfaff7a477b310365353 GIT binary patch literal 1029 zcmb7D%Wl&^6g`uqb?TV5q%D2$Xm}?nWLU64K}b;)#KHoJM5(%*m{Bspu~U0o=x4EM z5lDOhABDJMM-5T~DwaIgb06oPduIIe*S8-4o`c(1!JdViMclHH!)>$Nv9XVejk~yK z;l70j49hQ7p!6$-e6`kRD73>5l3~qNfqa`x1~Tf2flr>Y8+yX;i%6OM%vsPQ6*D|@ zwTv~Ns*_L)pHJ!4k?=msh#yMd3n#K22099TpWrC7$xu?dD^BA#()U9K+q;M})Lj)` z4U2H>&Sy_fwF-mS!b67L_er4DMD|szh-WJZh*BD6Sa-u{$j1rI%V#v~ZkR-#e67rg zdke`n#^OXcuu)>DUX>Zc^;YFmHhX0v<5&!(!Fc50F{&174(d3t@WjDWG#DEHCm_vY zSaDwjL;h|smY!zVn7b}SVc4p=bAMMyDi}7+K^J3jI#YkMTDzRXuy527aV#S}=3W>{ zE~bjNQisu-#LuF=k)ZaSMU7I$FJla1b3b%@B%-c7PGsQ843|!nFK+wS5{4BOca9U` z_vj|Q_>)DZOtbcH7PHRuQxxf!F;5OO8%ne~wBuwM*`@k9*q1bXjn?JVS;Q)RJ6QtO zuueNVd@9bT#XYh)^6h*_?x>tUN5MTf!_rqQm-A<^4(E8QlxdJ`Lfj@KgL@;Zo#C2D Y85@MZNwEUiEy~E4Y?Ec!p*4ry-!S$OL;wH) literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class b/out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class new file mode 100644 index 0000000000000000000000000000000000000000..07430bccd718504d51f828b8dabcb6e4d9ef2b84 GIT binary patch literal 6931 zcmb_h33wc38Gip9oy}%Q%Qj6(Xz7)fG&C8IaE7*|$+jeH4s$frigB`;CX;SEnv zd*1^9n#8Iws&JEn*Ol^7{<+$}5s!rM`Y?*{hB9>FjSAirMi@7RaB~=M#x3&UEehTm z#!|d3gtyDzcSy>2hVU*0w<>sd81KP*!+0OwAI1mp!7_XZ9}eR-+%DHX62=|)Xb2xu z@Nos7D94?+OOhQ9^D19@GHTaf-Zwsilk#_oxiBv*&41wmDWshp3%Rq%o|;6Q+@Lw0=JK7BDvpRQG0*jyYlMHq+*C!e~h*Ek}1u zE14F!Fj%soC)3(KJ?>ccl&0qxw3duxO`5hbLa}91W;E&8Qv&N_rlqCKgA|<5QvB!G zdVIfOYyDXoGAy~oOq$L%f#SN&y9G*Gtr7aRG-f7^uFS-+VfX7aPaqPr;(B7YZkzJi zUo3IPOxoKRa~%#)rKiliww9DBfuPdTwBZb-1i~Y_qxV@EJ5CK7VsUFio0v);NNDkK zZG2=nu64?%wj2>zRk@xI!j?d&T;8~48(K#JHMzslLvbVJ!mQvsu2+oVc!m&YJB)qg zkVMj(R@-o+ToHF~7`+r@Lo+uAY8-Q%l$IqT{-!e}C3(ypX)7tP(e-a!pPJOCv~ez_ zbZ1O!v*MWv=18lRbc{n1oT}MKPo=VGZ#_}Xm&C^PgSwW`lcQRnBlEL)8wD?O`8{X- zK!l{LF*EH56qzHEB+nVM$Vb2v+|GI?5R#9cL*?0AGYuv|mB*TK%!JlzB@#wl!b|~6 z2;fLNg}cgn`QVn4K2ji5l6XR0@CQ;%3}U@3}wXTgvl>y@dkqHX~t3?urrj zCQrKDTcF}XsKTg{@g7(Le_mv$J-#RO&A8 zZRu+3?i}jr?(J7G0QRS~i?uYu>{uiEl%)T@iazuUtXs6PFrs}uz3pAQhT2;CTl%^O zdRwDI1HCZ?KTz>Q{7A)*@q|EqP{$8$Hd6mjWTNyAbhWnhN7u??86tu6M-l-kBIHhQ{#9j;degiu8h zo{>iFt-n&B(lzS1?xBieQ9>D_R1r(4M3hQFd(Xbo^_patW!u)IB2-nJA}VX)&#N8OIjn2M#USSC(YMMPAnVmY2s zMWwu~5LK#JDNa*GwWtx;;#Tts9*6?V^Pc1mC`Ftu&>CdbNfwg8X>}bP1r5zV658c? zUzlxEX~&pg=tkK+lLrMh)y-upY>d7fa>(P{PB7bpIH;xCTR)`9v%TOrmli0-AtRoV z{raMXon2s_Vil#Vw6oK)y+8@111u@<#w(-df)%%UHSaB0@o7%xygxI@gMiV1_7dbi z4t$xkH*lTFBWGS_nSmZLo8lTtCcC-qjPtzUj3bAv7I(oLAH&{m|Cr8WeODn6t=2?J zx8(rW?5Ue8XE-gVM2;g_N>4HsFI*v>eGF%7ZP0l+f=1@bhL-QEI5!Xyd4`g^; zSxlKi?`8#TUe9v=C=9_4YlMSx<*f7G7bCDLs4dDlqlbeV&ly4~2f|r4_%*Tl1ZTYs zK`M?L3E3j$cqA}b=&Y}(AYZc?93(Q=%o+E5ugQ7ZTqxO{`xZ$LNs|N8&H8!q&XY3h z&Gat%;utoQlOyq#x{i}k6fHC}hxEefGp5^p#(|8Hj2k(FwUcDwfWurV=q`GIAqBW* zBQXc*1!_DyrFspFvIU}DouVRYv-K%QFmUMH!`!R*~Rr# z%1AQL8Unm4OdAh*|FET{v*>e0n0NkodY-=C((#q&hjg)4(`h(k7VRwV8Q^fYNSpD9 zY30^JuY4;5JCr+vbY>Ecxm(WboqDQSo>L*?P@G9S%5dCKD9SfFP*iSy@U2*w$PfMh zJCT;OMPt$aDBniAx`$f3yZWPDgsrTryMJhB_dpjrb@uS|P!V{3kbn7jX2Hv^ERgVd zBA<b3e>rAD;JJKS z!rxwZmG1@o4e^<;6}%4e=`xh@4ydU=0#SbyMSChyT#3?%vMh82p?gqvm&K*Rsfp;`>oGjg_&prg7Q~st==P8mABPY1J{Tn89jVc}8Rn zsn*_$btO24H8WUWHjNE~TOze{Hi+CbHgaoIz*ecZb*8H{D~>wqBFZ#2lc_#{iI!{} zlo%C524E9|1vH|P9|qUsTr{8wJ8>SpKOfid1K=&V5O?9C0?ZEkm{sCd7}!UI8t^iV zVvLxXlqEyx?TzzpE{gj}Baq0V$hV*pCKQllx-`T*D6=p&c5cbSs7V<4_Q6@ zxd$DH<8n+O<|44ov(!hRl>Sa2NpDtg*CG~0RI|AzNP%Mu!+7lvBXdmIfcs(WIEvOiTf`Bx zWn&QSYM7O@eDaf4rf5L*yHVgyQ2B3_<;uOF`rq#F1q7l+HUP2(b%33-J3u3|6Rgux zcDj|U=r&^5i*a_t{W!!ry_rbg$?ScM8U7S=TSlYF!zvezC;e!w#N*`YXI{7Q{}`qi zmtxAf5m!-WNiO@ny>af%*B!3T#syEX;$-%ZEtLI-ahb@WpKlrMvSrK@xGZ^NK3E-+ zJl`2>+*)_&=_Ir7D)zx^==-(obk`MRFv#rL6Q8v!_FcP)Lpbe@OQP>9C&<;PGojo1u2?<7Hy+tTIf4BX%v>7A#m zeItwUBA4n0_eeW~^e-muOX7GbYo`b=-wP4PEAUDKufc2aYSMm= A^8f$< literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class b/out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class new file mode 100644 index 0000000000000000000000000000000000000000..f2402b774f4ccc70b4482b3200fb43b572c7f5f8 GIT binary patch literal 1704 zcmah}ZBraY5Ps%fSU64+4^1Q*f-x$P$oPUW@nX!0q|`b?3a+5ae6Z%00fy}DTxMq> z`B_$3(XvX*&;BUOXZHvkVZD5~>Fb{Er@No-*?<21<1YZqXw^`~`~+%P7{_9O>j7@m z@DY}3kob5UpVaUvJ`1ocPmU+qzwu^qq-!cbw|!L+c*PuC-ozI?6-c zm0@Ib>}7}g`cQCFU}imyz3J&qlbCL#TXAf?^2WxAz^!6rt>Q%PsL)&YOseAm*-E`V zG)`|5cI=LcjlU~Uo?qA$sI1s+mU^yjV*McPb+zj#W)o<%ZK$G6aD{A^Q_EH8RaA_#YrE#pCBzIfJ?=il5prVwaHw!7t z`v`69^;0kNyvuSU9~cn`M1bPJlPSK49)x|v=^nKmgpo=TO}>`e#>xV=Iw75HKP>>3YQ^5HZe z4Os?(&4sfjfh$>E*~&P-FGK6J%xGxkhQYh94Q@MoiVtbEAooJP1ZOe2N%lbf%ARr? z`bnzeP!Di9a9N!8e?tT&OtShUGcS)GMDaNBErRDiLHAyKbtBb!u{E}ZccxKL^(=^WvfDU*AZ&C_epuLm_|AY$v0kez0 zqSPoiD&;>>eU7oeQE6ZQ9YJIKIVQGFP^VC)_eG+x_3_aF literal 0 HcmV?d00001 From 364db690e3174b835c2544e4472813452ef19626 Mon Sep 17 00:00:00 2001 From: Seokwon Date: Mon, 11 Sep 2017 11:03:04 +0900 Subject: [PATCH 15/19] 02pipeline0911 --- .../pal/tracker/TimeEntryController.java | 29 ++++++++++++---- .../pal/tracker/TimeEntryHealthIndicator.java | 29 ++++++++++++++++ .../pal/tracker/TimeEntryControllerTest.java | 33 +++++++++++-------- 3 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java index a9b854f6c..33f7ba97e 100644 --- a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java @@ -1,24 +1,36 @@ package io.pivotal.pal.tracker; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; + import org.springframework.boot.actuate.metrics.CounterService; + import org.springframework.boot.actuate.metrics.GaugeService; + import org.springframework.http.HttpStatus; + import org.springframework.http.ResponseEntity; + import org.springframework.web.bind.annotation.*; -import java.util.List; + import java.util.List; @RestController @RequestMapping("/time-entries") public class TimeEntryController { + private final CounterService counter; + private final GaugeService gauge; private TimeEntryRepository timeEntriesRepo; - public TimeEntryController(TimeEntryRepository timeEntriesRepo) { + public TimeEntryController( + TimeEntryRepository timeEntriesRepo, + CounterService counter, + GaugeService gauge + ) { this.timeEntriesRepo = timeEntriesRepo; + this.counter = counter; + this.gauge = gauge; } @PostMapping public ResponseEntity create(@RequestBody TimeEntry timeEntry) { TimeEntry createdTimeEntry = timeEntriesRepo.create(timeEntry); + counter.increment("TimeEntry.created"); + gauge.submit("timeEntries.count", timeEntriesRepo.list().size()); return new ResponseEntity<>(createdTimeEntry, HttpStatus.CREATED); } @@ -27,6 +39,7 @@ public ResponseEntity create(@RequestBody TimeEntry timeEntry) { public ResponseEntity read(@PathVariable Long id) { TimeEntry timeEntry = timeEntriesRepo.find(id); if (timeEntry != null) { + counter.increment("TimeEntry.read"); return new ResponseEntity<>(timeEntry, HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -35,6 +48,7 @@ public ResponseEntity read(@PathVariable Long id) { @GetMapping public ResponseEntity> list() { + counter.increment("TimeEntry.listed"); return new ResponseEntity<>(timeEntriesRepo.list(), HttpStatus.OK); } @@ -42,6 +56,7 @@ public ResponseEntity> list() { public ResponseEntity update(@PathVariable Long id, @RequestBody TimeEntry timeEntry) { TimeEntry updatedTimeEntry = timeEntriesRepo.update(id, timeEntry); if (updatedTimeEntry != null) { + counter.increment("TimeEntry.updated"); return new ResponseEntity<>(updatedTimeEntry, HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -51,7 +66,9 @@ public ResponseEntity update(@PathVariable Long id, @RequestBody Time @DeleteMapping("{id}") public ResponseEntity delete(@PathVariable Long id) { timeEntriesRepo.delete(id); + counter.increment("TimeEntry.deleted"); + gauge.submit("timeEntries.count", timeEntriesRepo.list().size()); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } -} +} \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java new file mode 100644 index 000000000..49b1f175d --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java @@ -0,0 +1,29 @@ +package io.pivotal.pal.tracker; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component +public class TimeEntryHealthIndicator implements HealthIndicator { + + private static final int MAX_TIME_ENTRIES = 5; + private final TimeEntryRepository timeEntryRepo; + + public TimeEntryHealthIndicator(TimeEntryRepository timeEntryRepo) { + this.timeEntryRepo = timeEntryRepo; + } + + @Override + public Health health() { + Health.Builder builder = new Health.Builder(); + + if(timeEntryRepo.list().size() < MAX_TIME_ENTRIES) { + builder.up(); + } else { + builder.down(); + } + + return builder.build(); + } +} \ No newline at end of file diff --git a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java index a6f1100b5..d1752eef1 100644 --- a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java +++ b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java @@ -5,6 +5,8 @@ import io.pivotal.pal.tracker.TimeEntry; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,19 +23,24 @@ public class TimeEntryControllerTest { private TimeEntryRepository timeEntryRepository; private TimeEntryController controller; + private CounterService counterService; + private GaugeService gaugeService; + @Before public void setUp() throws Exception { + counterService = mock(CounterService.class); + gaugeService = mock(GaugeService.class); timeEntryRepository = mock(TimeEntryRepository.class); - controller = new TimeEntryController(timeEntryRepository); + controller = new TimeEntryController(timeEntryRepository, counterService, gaugeService); } @Test public void testCreate() throws Exception { TimeEntry expected = new TimeEntry(1L, 123, 456, "today", 8); doReturn(expected) - .when(timeEntryRepository) - .create(any(TimeEntry.class)); + .when(timeEntryRepository) + .create(any(TimeEntry.class)); ResponseEntity response = controller.create(new TimeEntry(123, 456, "today", 8)); @@ -45,8 +52,8 @@ public void testCreate() throws Exception { public void testRead() throws Exception { TimeEntry expected = new TimeEntry(1L, 123, 456, "today", 8); doReturn(expected) - .when(timeEntryRepository) - .find(1L); + .when(timeEntryRepository) + .find(1L); ResponseEntity response = controller.read(1L); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); @@ -56,8 +63,8 @@ public void testRead() throws Exception { @Test public void testRead_NotFound() throws Exception { doReturn(null) - .when(timeEntryRepository) - .find(1L); + .when(timeEntryRepository) + .find(1L); ResponseEntity response = controller.read(1L); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); @@ -66,8 +73,8 @@ public void testRead_NotFound() throws Exception { @Test public void testList() throws Exception { List expected = asList( - new TimeEntry(1, 123, 456, "today", 8), - new TimeEntry(2, 789, 321, "yesterday", 4) + new TimeEntry(1, 123, 456, "today", 8), + new TimeEntry(2, 789, 321, "yesterday", 4) ); doReturn(expected).when(timeEntryRepository).list(); @@ -80,8 +87,8 @@ public void testList() throws Exception { public void testUpdate() throws Exception { TimeEntry expected = new TimeEntry(1, 987, 654, "yesterday", 4); doReturn(expected) - .when(timeEntryRepository) - .update(eq(1L), any(TimeEntry.class)); + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); ResponseEntity response = controller.update(1L, expected); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); @@ -91,8 +98,8 @@ public void testUpdate() throws Exception { @Test public void testUpdate_NotFound() throws Exception { doReturn(null) - .when(timeEntryRepository) - .update(eq(1L), any(TimeEntry.class)); + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); ResponseEntity response = controller.update(1L, new TimeEntry()); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); From e508237757b65ad6523132e1a4af28f43a7e99f6 Mon Sep 17 00:00:00 2001 From: Seokwon Date: Mon, 11 Sep 2017 11:14:36 +0900 Subject: [PATCH 16/19] 03pipeline0911 --- .../io/pivotal/pal/tracker/TimeEntryController.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java index 33f7ba97e..427ed4aaf 100644 --- a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java @@ -1,12 +1,12 @@ package io.pivotal.pal.tracker; - import org.springframework.boot.actuate.metrics.CounterService; - import org.springframework.boot.actuate.metrics.GaugeService; - import org.springframework.http.HttpStatus; - import org.springframework.http.ResponseEntity; - import org.springframework.web.bind.annotation.*; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.boot.actuate.metrics.GaugeService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; - import java.util.List; +import java.util.List; @RestController @RequestMapping("/time-entries") From 84ec8fd2ad6ce6606b7c5f0139e1ef8a083d811a Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 12:50:47 -0600 Subject: [PATCH 17/19] Add tests for Security lab --- .../pal/trackerapi/SecurityApiTest.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java diff --git a/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java new file mode 100644 index 000000000..72099994b --- /dev/null +++ b/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java @@ -0,0 +1,52 @@ +package test.pivotal.pal.trackerapi; + +import io.pivotal.pal.tracker.PalTrackerApplication; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +public class SecurityApiTest { + + @LocalServerPort + private String port; + private TestRestTemplate authorizedRestTemplate; + + @Autowired + private TestRestTemplate unAuthorizedRestTemplate; + + @Before + public void setUp() throws Exception { + RestTemplateBuilder builder = new RestTemplateBuilder() + .rootUri("http://localhost:" + port) + .basicAuthorization("user", "password"); + + authorizedRestTemplate = new TestRestTemplate(builder); + } + + @Test + public void unauthorizedTest() { + ResponseEntity response = this.unAuthorizedRestTemplate.getForEntity("/", String.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); + } + + @Test + public void authorizedTest() { + ResponseEntity response = this.authorizedRestTemplate.getForEntity("/", String.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } +} From 196f61c8a6de5c415f171e352ff990ea0a2b1e18 Mon Sep 17 00:00:00 2001 From: Seokwon Date: Mon, 11 Sep 2017 13:17:38 +0900 Subject: [PATCH 18/19] 04pipeline0911 --- build.gradle | 1 + manifest-production.yml | 2 + manifest-review.yml | 2 + .../pal/tracker/SecurityConfiguration.class | Bin 0 -> 4862 bytes .../pal/tracker/TimeEntryController.class | Bin 3445 -> 4289 bytes .../tracker/TimeEntryHealthIndicator.class | Bin 0 -> 1394 bytes .../pal/tracker/TimeEntryControllerTest.class | Bin 5111 -> 5569 bytes .../pal/trackerapi/HealthApiTest.class | Bin 0 -> 3804 bytes .../pal/trackerapi/SecurityApiTest.class | Bin 0 -> 3404 bytes .../pal/trackerapi/TimeEntryApiTest.class | Bin 6931 -> 7683 bytes .../pal/trackerapi/WelcomeApiTest.class | Bin 1704 -> 2726 bytes .../pal/tracker/SecurityConfiguration.java | 32 ++++ .../pivotal/pal/trackerapi/HealthApiTest.java | 56 ++++++- .../pal/trackerapi/SecurityApiTest.java | 58 ++++++- .../pal/trackerapi/TimeEntryApiTest.java | 148 +++++++++++++++++- .../pal/trackerapi/WelcomeApiTest.java | 46 +++++- 16 files changed, 335 insertions(+), 10 deletions(-) create mode 100644 out/production/classes/io/pivotal/pal/tracker/SecurityConfiguration.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryHealthIndicator.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/HealthApiTest.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/SecurityApiTest.class create mode 100644 src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java diff --git a/build.gradle b/build.gradle index efe803324..a374dab97 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,7 @@ dependencies { testCompile("org.springframework.boot:spring-boot-starter-test") compile("org.springframework.boot:spring-boot-starter-actuator") + compile("org.springframework.boot:spring-boot-starter-security") } diff --git a/manifest-production.yml b/manifest-production.yml index e6c9fb2ec..348d53181 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -5,3 +5,5 @@ applications: host: ps-pal-tracker-seokwon services: - tracker-database + env: + SECURITY_FORCE_HTTPS: true \ No newline at end of file diff --git a/manifest-review.yml b/manifest-review.yml index 50eabefb2..66c9fc251 100644 --- a/manifest-review.yml +++ b/manifest-review.yml @@ -5,3 +5,5 @@ applications: host: ps-pal-tracker-review-seokwon services: - tracker-database + env: + SECURITY_FORCE_HTTPS: true \ No newline at end of file diff --git a/out/production/classes/io/pivotal/pal/tracker/SecurityConfiguration.class b/out/production/classes/io/pivotal/pal/tracker/SecurityConfiguration.class new file mode 100644 index 0000000000000000000000000000000000000000..868f62a203bab5f5531f90b04a938500fd6ca0a7 GIT binary patch literal 4862 zcmd5=TXz#x6#h<|cET`iX$s|zpcUFu884_{5okl8#e_oA3-@}GoF+rlnJ_bHDWE7S zUhw*+Yw3^hCAwToSD$?FH@RHylgu<}sYnt)yH;oBoSd`wx4-?JJv;g1uU~!xa1h_9 z*oDJEyrE(cM}l}$#d;i75yCMQ$1$WJtRje$a^{qRkswA@oW@%UVnK|lXvbMOKBpiq zmn2k-V?sVns+hug6&G+Zh)XIi<4O=$RZQb;dEp9@3Q`QMM-0<&k1{m(_DwPb!d6-^ zbVLnPoGHvDg+0!bIhqMYtrX8qa@&w&Z!+L!4Ts@C)UfotF>kp%r|0S4wRvh**m^>w z3bx@chAnf($P{eu8kRZ8urB3Ikl|Qw)Uq?WleY~sGh_2Pv0&M=y5nut%S&}`nq*$? z*B3-mPZo?^TG)<0;<|a?&0r~lEX^>SetcFL)EUc83F(TW4MnfnIVUC!#~`^3EhmroJG3T-q~NBb&+wFM*^AZLrqHYf z!xm{`Sxs04O2kP+Dsr2j_7&7aK_wX@gx8Mj{!6zC%#eIfgo_M5!l~(YD zhO4-*;Y)nQFi=ZHWVljC2)-`LYOT^~h038c&%45A=&Iy)xhk#9_@d*AIeHyrge%N> zhP}NHmbtzMr)UQWkLT!IZ>rugmb@lX?qJ_Vs(bcIX)^5eL@MPe95=?@R94t=kue{wqr>Rt2sJ?b` z?|cdm73FwW8c|ua77j||-3FPeIH(&1!+LJ|>L&8` z^ zV{Do9-9aDqTuxA5aYDG<$T?pBzJl6YHB$RH-BX*hV7OU{kebRWpq{80zJDAAA#=dlP}9$7 zjt%^dZ3Ce|5iQ@LbAJ)7MJP)M7O`%MMrz6JDp^ puFA8A=pLc}enJzZNKeqpc0_T8?#F37i%yCv3Wvti_#B^s{S7dlTu1-_ literal 0 HcmV?d00001 diff --git a/out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class b/out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class index eba45958f5a782b19307f4ca8e9093bb15de2462..007dd034b8795f7c5856ff5e8be270df355c50f3 100644 GIT binary patch literal 4289 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 kb4s6_-5JrN9)muld~p&>6iCYIqM zF2ym4H!XvTCuHHxBL9{+-io)y@g&|}i+A9ial8xfmbv%H+w9}uw*io}Ox z--qSrsX3dmU{G=UoblEgT#n--_^60|OvFB};uE6slW}|spPmzVB^bn>5^SH5-Jg}+ zpAGWxv0|AH)hQEYxm#nURjtf0=d#?AJO>rD2USvzOBwlk^Vo-WJQv(|*|8fiVt z|E^=C&YO;&EZ*I$AfB?R!A_@5rySX~a5rUNm$LI2*L3<#XTnOE3J!GH&WN7NI#y<6 z*fGY(H&Slia7}&ObR8>|)7$2WQkgblsodur{XIL*7VU!bp9zTXVFq?Cd1fx%N_`NY%EX2`Vn*3 zc1$XYi}5zcq`wr@n3u9<$~A`+Y+5q36s~d1T$ZLO+E~(uqpq9P`+`kOAIqJj*lpfk zA4tHbMqu=r1}(3e@2kn|oWHuuHPNy(_CtiRa?a*JkL`9aM`+x`(PiaaK{;kj7<%5d z(z?u0V$V%eWf~eNANYKeW$70xUKCGehrB4(2`~R8!pz%p)J&Ta=y<=KcT#4DCCRxi ztk(Hywo|}pcn}XM*!LQXO~qF=d<|dMa1`wd_Li+CT4)+Na7;l{`OQYd8_-&YM1ipI z{Dy`jXj8CZDMPG3*B&w^Md+IvzJ+hA_>P9};u#g+)9`)a`vd$?mVYFpXH`6>;m5e5 z;;M!zTvIWvVFuS#+|ck7Jg?yeyeKQPDt@ZsXBvKvUugIxex>2pcuB)=a9zX8xUQgf zlF>CCT37K~4Zp+hHT(g8l%GGbP0mjsQTb;Te-R^p)$lj`UBf@{Px<+mhJTCZ|KJ%J zy`tg2qJEbo&#g2g7K&#xZDdCD-oY{U4;~kVsjxwDR-!*{r_S^E(7XL7o2JYt*wWw^ zm#3#KZRB#zjgc8&GgJNPh9#@Ja6~nith{m#iSh-bnc6I;z?z26&dvz*ex5>^k>-bF zt!BvXGu^zCVc(Au4(Rz^bk0RNH$ON?QN3RVrbG7YqAt97oxZ#{YEt#qlBx?a37uIV zRo88}sZrD6kyK%1c)C}of@7g$X_qrrOBD^#ucl`fa7=euHcGXR(r3)JGPf#NK4QB4 zJS6iu|McG0u*g~VAP+QuGwG}s8%$KwamVx&&r{vVT6(KDAty5XtiPHZHCPnKqETtH z$Fqhbw*k7Yw4TA7T&LW^PG2((Z(fjG0iOc3t*^Z`*?yF@RWh4B#}x#qcgHOOVK1_B z?HBS!I%)Gh7G*ator^sbFbnyp48ak5h&fkJ=a z8KEa9jC9`Y9cBw_=!^oCW%{jh*pkb`o+wIhsjRA~WSLRAxrFqBaW1-3#d?o39zDIu zfsWpjJ&d{EmA%_btK%4x%*JXX=U;8MG@LE=a=FP{H=)E_P@-$w7b}w@c)J5~ z1q};iy#Ne@R=U6nTNE6OI<35{D~L04G4le$$Dnr7J^A1Qbt9;C@##45DqMSnUp5B4B-kxN8PZAsXMSGZEvXzL*rHP}M+=9F(?`+suA;IjQRR)RuA;grp?YKWDr%Z;p!Q7D6%MX&(-l--L;Nb16>P48eAjNEy^UCn zO}HDI(ahfhhL!-s8@aENNKT-SBf;A5AxnnHUPPQ20rLj5Gx2&Xug8kS%G+wL@f&XY zH0lJ$4C-T;g__W&ar-Q^8LW!oG*_01P*moQDXi|kX9{bkvGzIKIfZqn`S$v0+_ilQ z>rWRbY(foVd&uA|h-A`=$7>@nq^8gMoG{?|F z#~+L^N`{PD14aizMrWx_QpulrbPR+f`;||1|#$b8}|SV29#o

(62-ht1Y3w5FdyB{($8N7s zPk2Q#z&K@)j6})~$nItgPhx=7PT;N}_k^~%#9w>x zUvx*q6kRB~sgT@dufc-fvBe3wFctB{H5?$J5-A$<>da5kA*QIw6dh*g7-gkdOwzGP ziXIEV?+;USA;?1^yc&k bUx*;u86eWwDWr21ipHN=tY}Q?jbZrz>L}NH literal 5111 zcmbVP`Fj)B6+I&_Ba30P4GqKz1Pa&&EE-G#H6b-#AdYMoTiCcMsAG9-kC8QEG%~np zn{?kx_q|Q`y|hV#m(V2co~HZH{ra!;>$x*yX^bRW6n{uF@4b8PIrpA>XY~4iUwQ>V zJN^|x2NEjY5rK~JI=mT46%!HEz>u3s6;m}lDF2@JFO=89n2sO}Q*357;VM!Q%p$!B zAy~4J5<q*vLlUAXkSuT#w)e=4I`jvi5WY?~>bh<2_>bj95G?=iVzH?^}%-@-M1( ztOoB4JqWnla}rM^8mF{O{O$@~UA+ZuXkHs#)5tahf&tb&JQj*)fN2V4jm=UE1>$r{eZ48ix@NE#W(w9;7>Ur?}XD3_L` zjF?%Ikvh|9X2IQdLD*B#nH&*E&l;1KZ3ue_-enuQLzrRXM#e}w#)N`>8zzy)VcW=N z7@DRnF^kT#8QV-xP1^dbaow`7MyDMo6CLsou~4QnPqS_ASr3TMCoC|A44sj8uFcha zW;I{^m4-OIG4?Zrv3)gWV!(2Gtz4SHT^unp>j=sjeNK<&95WS_6Dc=`sW!5HOG`t0!3R){~ zDHUJT@Fjd%!)ZKLfn_16#QKVcGk9FV-i-pX2OMicpBJOAYWNzyuHqXSzKQ2ld`rW( z1;BUkUD^JgTzy}~4>bG`KT`2y4L`w8Rs2lD&v8q|FEsoT&ue%AzmlC7RoqswpkWb9 z8kX^rhCBGRhL>?m!z;L@pl+TSHf%;$@v4Ru{6@oX@jLnWy@D-kizqJtpyH1b9;J+tCtO@jO#*(dG?1tT2!}WHRn*?(6FdxEgk3 zP;@*Yd*KOd$Z&FYnzJwnzz^q5mQ~1ECpSJ$)9A2V7&g%~1u5OVPj1(z4Z1#9(sdz6 zd2)?Gcm2APoHlHZ=_)|S%~OUm z%m+s<>pjs9HLo+@&vVFohe>CT#Gr5t+nI?b`HYF`88h1HF33}gv&Y+wPwN!vxnNbg ztl5li%M+5REA3}ID-Uz0aMJV4!28RRE8tV0b`AA(#(TP{_3}fxU_ie|?+FNJgPHBQ zmeW&lOTnoiyESqy&XCV6&qrkm&RP@HTqBcTLZ^@4yK@ghK19p9)PjdshnhPWVpe*J znVi#8Ib(2=Bc-`72vD|}x9Uk#9;od>l#}t&dd~P(<+ySY z)K2*CJhgL%ZBDYE8j2#yQz$YpnCKcDi1!S{@eqGcb?|poh<|*Eaj*DR!tXjL@mror-D6$+taTT6pqtm4cK|);6trnrKx6-r1?*VD z&KK~81>85vyIo7zeRu))j~1ZsvUop}9zz%V<$NMJ}MVM7dT# zEgvE4qeW!9S)=oAgIr*9BoITI6%n$Y_AH<5ai*gmqoj5L5Bk~@+L97)?^1qmY@M3D zEuVm8UzZ7=dRrroc^r%Qv5F5KJj|e_L{CcFVS+~f)mZu__Si}huDk0I3+h?9cFH@pd zDA5EbhmMmN=O|4EBzn#Ve=;x8a**Gihy1MHLv1qIyw-mtCi8Czk8=8XopdsUeDj=I p+dTE83{0_ir?Cenj$$T&sLe;DaZ0%rr!{*{12uME!hA7 diff --git a/out/test/classes/test/pivotal/pal/trackerapi/HealthApiTest.class b/out/test/classes/test/pivotal/pal/trackerapi/HealthApiTest.class new file mode 100644 index 0000000000000000000000000000000000000000..8d56c3e50a65dfa5ecb4d276428bd18d976e5824 GIT binary patch literal 3804 zcmb7HcURk16#qR8$T&(u97>urA%mpMppG;HNE&d+G^3bNO4C%9U$DTI6iH^Jd+)v1 zhiFf0(&n_MfA>@Lll1gH$u>5~gd7f*-hFrc?tD-G{P)LS08Zei2%0b=VN}9c1T}a{ z!gvIA7zraGo)Zl?ib?S{CAO!-csh(3@%oH}*$8&xS;28VjOQfWknp@9sSzZhMUVn- zz)j3WkcKX3^ARi{6Gm2ou?;3H!E-ANI|2uK7;YFj35yK1mT9{T(fGW&s45xNm{St2 zts8TF4133OhO1}!l_Bnf8K`G)-60OvB~N zuEMiPo=WkQ5;rw9li>Cux5vmDX_js7xDz~UWmK1Ms>~%uWr-&hEu(Y8RVHXXUM5V+ z>icy=ch50|+B>GmqnMc@ojc>Y!AEk0!JbeFz3@@sm{M(BtP8|iH?5O9U2)Hlr7x0; z8O5T1*H*O!ZmX8AT;^)VO%GVQuuc~19PUn93{oI!LJY>(BazJ= zR3AgeqHddp2)u+9qD@!Os9&xKgt4NLp+E=kh4W~oR3l=w zRkf`*uVaSZ`<0C49M{b1x4A8B-)7jeeg+kKgkhH`=w3z1hz?Ghj@v8a1-vNZ7#^1K z5?+?^3SOn&&pDzS!xm+O22x7KYj~Zyvnn(%t!18xe3o~qK=y`=4s=qtD}E1B>8=M_ zK^jfYbIq0UCf<_pwv2c1u7vkwypIngd?@21d@SJ;8K2@a8K2_|3176=LsR=kM(Pd8ruLq+b%xjlR z0Y*09?+c#xUgO`>Y=8TGN4tnwgj$~C?nTq~4-*D;bG+WD>YFX|!0(DG9t59PZ9y>( zU}e}&;1XWv_=kTR<+Q5sWtt^EGo|^UU=y?3cIWBlVsk|_!hlEcZ#ofwcOtFQ$$P2X z?U~g|w{8I+b@u2raa3=CmsB1P_N592h6ddkx|O4I z$)pik?pQ^xsybD2MWW~rnkhQCDtoD*hGBouFca61F}icmxlK_NcbN(dg!MuZ_o&r$Ygn{ZgzN##mal6Z}?=8yYW# zRM|Y08$=jR1l7pBt^_ztpA=2>eNsbzbe4jljaK#aZ1<>i@X<3wYq}lK?{Qiw^knp` z>s$r<*<5Uc40r^O(u(0R5)OI7FQJx90cY-_W+wC}LaV5~i@KRr)JLT} z!kxdOfgX88^4J!YqucY?5%0WN-w+)Sy zgBHTqLDU#|cb&XiqQHtkAM&vl{A{Gpzcc71|2i;=KJ=4MXNgOs!{0kcZ^aBhNjo8& rr{5Y3c+?hp8zhdHh6@;?#l=}L4VQ44BaUHQBi;z@U%@i2p5OaF#@vGN literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/trackerapi/SecurityApiTest.class b/out/test/classes/test/pivotal/pal/trackerapi/SecurityApiTest.class new file mode 100644 index 0000000000000000000000000000000000000000..2c3ed40de077de262792c35e7914ce489abfbb25 GIT binary patch literal 3404 zcmbtX3s)0I6#gbWHpCS%Dn4qpzG?tp7sayNA>j1ZbA~+pq?HMJ3F&??)|>|n#n)^{`oh6Yxp^ZL%6Tufrf`EB=D(* zM=5mReiAt~KJLPCOsKa>^?53Z&ytu{uQM9*DeS|nl9*GE`6NEqu%O|IT3<}z3w)Wv zS9scmuVJQ8fTiAw8aP9{=L*S?&aIfMrcpMXWn)GP+gToFI656T(ys71+qVm4o^>2o zn$mV1pJ6!XiepXGNFN~aqr4T6vZzUjF#wcHM1t+1Nz^O+?!C*)V?t2t& zBG(sdz9gk*kj+qNoRUSZlcwHgN344_XWV*QB_li2@wS2(?vHR+9o4-Zb85y7gvIaK zDkKNDdG?aZa)x6KdPLwjTo{z<^R_IJk&acf9MInDv0%e6)-pUqa?O|ttAI3+B2o~) zF)WRy-O;vDieQzCW|d9f=aep&a<*&4B#SX+mh(~GWIeBJTVXkp&2Gwpac9*Qu2Z4n z&`_ea`KC1n{h>lQRx>glOYluN&o*l%+k2+76L%*cKb@MK&g(db4;gN>Y!18UD#Osb z?i>qz=~k$d2xZ%5IJ~`+RqKFZze?93!zimdExEoN(y@YPI^M?#9c5H>IB==L17B5K z(xYflTPy1D@SG~JCA2WC4a`(L8!J&EBY3B9TE`ijW?%*-^LCs~OOu5aZb=c@w$laEx2@VC6jmq0(-yWBFo-`R&d2#UMwDSMft#UwN8m>X?|F)8%gqm{!Xse{^GPM9 zI;xN>bSn|uP^FL!7ouBCD*Bo)nRMFSZ=^luRyr=EhZj&NC*j5L$sGjD;9 zygoaToy|X(oPPLZ{4T?}w`6rOo>dDp3|+QA{yd;_gF4$_BgLkuwWPO2Dlz$p;aVKC zHys(&_>R-{7HFgUD)m}WXlT|+;|DY*RAUTB>WvNG&eB>t&FMs;=XshLG%^}H`ZvLT z3AbeEsT6?>^wCp~1kjHGnlW6U&28c4SEMXKYlE+mSZMnTZJTI+jgEy)bf&cpB>R6u z7Y!RoZD3DYPw(A8cdq|6_ALx-qUU$)uN&;8dpHRDDf-J0<_kEBn}kyV=#7k20bIad zT*M^;V$e6m=JFQzk7$MA3IR-n04r61CkaV9eIR|1Kpxtl+tk1Y4*!88zvAcyj?F6= zZDbbcrV(Vchq#^~5Own>tLA&?2f$VOdBN~8Nh;q&E7$3*7J?h}g%SKX82e*kycY-K zGO4~oFs}X&7;P9NFbNEW;7-unFhLu!a0|C-^2s8Yg*(V{jN=~el5C2;k75~PcaHuC DoEqeP literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class b/out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class index 07430bccd718504d51f828b8dabcb6e4d9ef2b84..ef19ac0a9753f0c272f04e8077f5dba170e75ef7 100644 GIT binary patch literal 7683 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 delta 2945 zcma);d3+S*8OML`9?b0QkicX&Au&J_Lexz*3nfHIg%AQN5~3n8L_`{r)g(ezHycn9 zrxfjBD~jWN--;(mbQ6pq2o;ZB*m~O*Yb)A=9`@3<>hGP|4Mr0`-9O%U-g(~ldEfW@ z{GMla^YK=t`Qj%}?E+9C@-<{*i-N1X9LnE@)Ps0l#nl>I*y=+)u2EoV(6B|twHmI& z^>T27f*Unt;wBY0%l;NQ^HvqNDY#w19UAV$T^jDjJsR%CeLmce2Q)m0Z5qtG@sNgx z@ra5?6+EWkaX+@BN6zcj(1#~fJSqF6ihdOX^8F4CPhp#i!Sq2@jk`T9H`ys&?^5xM zg57>h#~wNVSq*!!PsIS#<5dN(S(l4btU_15m}PBp2a8rmn>)KA=QOt@ zV)2V2&7GaGM027&))}ekPQ=!?$D^&*0a0OXaBa6Pa4#0G_w97gbc;8w564{SZonB< zrxNxz;4K}eW0^pqrML^N{YqZO+d6)UU+MTY4hn>nTWSXyL9{p2X&l+Q>`<6l`|E*tK+x$or3pt9K!nwKG5-d{6WDVb^Hm3 z75rJpU+`BQf5V3g{;uO6_@}kYH`~O&bo?71$=M$(_>YeN;(r4E;_~z3v5sg5ST%HhsK zgspcngVy}4JZr07BgPB+qCeFKx_I;qKF$vFTJ5BkYxne`i!7^C$f{ssVhxl!T@r6k(b0#jU46gMsNTV zdCW=1WcN-J1v^ma!EQ_*K#?zrDUD@Dv9t^`1mI3$DorILn7oeZXnWVRJ8p7}p(JrE z7llSx#G_1Y?12fMU1BPMk8L>uEILJ)fiNm?0%r35mAHhp<$BCQ4`!#Z+4oIR3*dG{ zaSoY;aXHRK8>zL^myDug>)_LuIiANk!o-?1NM@dgasfI+{Rr1NG}9!@mz1SRMkGm& zQY7oc#x!I4=Xg!8^5lAU;A;W{nBhsHqA_gD98RFho9iWRGB7I%VK{?1jORQASuLkv zA&H!b8k|HupNtkP##+?b4CXpVO)>ED#%s~Zdj@HZk%o&a=iz+PaC3ba7a-2-wZM-q z&Xe?Epqov$Zx{Qrf_79S;M9uXgx zR}$5z;tP*1zOXTx_`+W$K5{p0@r76dK*U~t7)p5C3MP9sEz1eBi-@=I%Qy3vfF{k^^6#aIWlc& z8>tRdr)(p{qDB@1F8fj?FpB~>!6^mlEGVZ&!F=_y=H)Q67ZBr8bg(X-hYOjm*An)2 zYWOAU{16o`15)A88Vbm}set6*0M{&|xEJt$%tZ{1n|`jq#q{ZsIbhml!ZCKxI7*qo zhID9fkO7l9(Dqe1(2JAAPymjUjzzL`42zvPLf#w+*vWGKk@}El=X`>9T~7&K%!+sk zZ@-Z>@6yl7vTcY$ONv4Wb2(GigeLyA+-Osfvdo6dq$6pRx^#@AQ*g;H|=Sg><`=|F8CHF kZ*IY5Od%J(y#k^I-@$jI_yMlO_c>Ri{d;%=SIo=(3_2JzPXGV_ diff --git a/out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class b/out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class index f2402b774f4ccc70b4482b3200fb43b572c7f5f8..799ed13a260fe0276411b93813f3371415aaebb0 100644 GIT binary patch literal 2726 zcmbVOYjfK~6g?X|k?X22C$zNCG@%8Y2NjRfHV@i3sY_d&Cd5uc8lcGXCf-z*)YUo- zo#96@0}ew6hR^&chP#pz+l?KjjK^B-YVSSg-h1v!fB*CQp8#&aOJfLk4BRy^mqs6Q z2IkY~$DI@w^my+CE+Vh*?(4^2rtlzzhx+!Bfv?gygM!|%n8ITNO9qzp(${IMpqN4_ zja94}SZ7EEzEljE!j8RbTdwVGTSX;>w>`sfai#7lQR8bO6lIs^JkM9Q622EQ%oKdN zZH0l}x+U!zf8on#R@wKJ<@lcBd&=UqGOtv4#VYuY?H0M*<#L(O$g?SVs7kyRxVGZQ zI#aaKdcn(<;|lI6t3>maHe+%&Fe^Nv?l2^>V`~)Af?pw@X9~jOkL$!imTY3LW7Is> zY$^1-xtLT{LE%gmBA`I*Qi!e<&|gX0dB&w32y30Yj$h-sKxlY^><_tG4H%4aUAPr4 z8K!#Yqm6w#^ZD46yruJd4iBQl8u%t6O1a&sQ&iSG-|{7=O(*%DP5I)Q0R>t1D?c)v zjZovAO={_)UzZNQFLcO*oy@Q6A~RfSvyyesK*h*leB@(`0Tqt{sM)s&Ty_e5*?>tYzCR#bwC_fh(Lyjf8oc za-8>eh4j4|sl>n&T3hc}Bhfw73>OYS@?8V`FP_&rz3RfODNB zJ$vJLEig>KspUdFRDMmo6#gN8O9X`W|3-Nr`WNyRNmo^ zGQqKBVB16$g1Y$ucU^yEOZv4D4QF7-#51@I;}Lonu^eq&>W3EcVb2+;F)SP(+=1dd zHT#+t80NB@y+zfQp!Vdn3OJ)ruK_y+j}LB!q3qF(BveCE zudPbT=3-ahJyXzwc?&yJwnO3M>ZBpwk-1Jb?!Ml_DdzUy2JJ~VK^be{PQ{;t3GvjJB!2Uj&*s6e50p& zqW7yT=LST58W&<={j#lZ&pfN#nI#=oX9?dKo8IAqp`(Qsu+pf}8 z8vz+yqpKNj!29@sW(*&aaUwGQ2}yzire2|MBk>0k`$)b*|HeKBGDZWb@n3L)h6d6N zoXnV+Qw^LhjK9K}jfs7n{T1h0famGk3&eVqcxQ?CBpq;(UJHG=i9U#vxZaUli!2iOn67>JBw{j5cb^iE&m8*n{G28?H^Cg-!flROe1RF- Oo2KVe*ueDM#eV^db0EtA delta 504 zcmXw!OHUJF6o#K~E}eEdTslal7V3Z(uviBW6+{K%MiUZEG%?ZDSO!v3W=t6oRxVw+ zAm4v5Zd@3Y7!!?d{SmtIZx~N2ZocekBt9GY~@m{q3tQ|j$cQS?bG+p0U_-k9yZWOkoWHWq_by~^h(qY4>Ds@%gHTUuZ z40;st^e?L}=UnqxVA0{a#}YRb19#$fI}J8E>2{C_*5RhdEtd6+-N-qtC~j+IRrF7z zs^8fqwb;u(TEXF#^GZ#2KMM+@yF#>2vYcqYs39ub response = this.restTemplate.getForEntity("/health", String.class); @@ -36,3 +48,43 @@ public void healthTest() { assertThat(healthJson.read("$.diskSpace.status", String.class)).isEqualTo("UP"); } } + + +//package test.pivotal.pal.trackerapi; +// +//import com.jayway.jsonpath.DocumentContext; +//import io.pivotal.pal.tracker.PalTrackerApplication; +//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.boot.test.web.client.TestRestTemplate; +//import org.springframework.http.HttpStatus; +//import org.springframework.http.ResponseEntity; +//import org.springframework.test.context.junit4.SpringRunner; +// +//import static com.jayway.jsonpath.JsonPath.parse; +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +// +//@RunWith(SpringRunner.class) +//@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +//public class HealthApiTest { +// +// @Autowired +// private TestRestTemplate restTemplate; +// +// @Test +// public void healthTest() { +// ResponseEntity response = this.restTemplate.getForEntity("/health", String.class); +// +// +// assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); +// +// DocumentContext healthJson = parse(response.getBody()); +// +// assertThat(healthJson.read("$.status", String.class)).isEqualTo("UP"); +// assertThat(healthJson.read("$.db.status", String.class)).isEqualTo("UP"); +// assertThat(healthJson.read("$.diskSpace.status", String.class)).isEqualTo("UP"); +// } +//} diff --git a/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java index 72099994b..75c6f155a 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java @@ -30,8 +30,8 @@ public class SecurityApiTest { @Before public void setUp() throws Exception { RestTemplateBuilder builder = new RestTemplateBuilder() - .rootUri("http://localhost:" + port) - .basicAuthorization("user", "password"); + .rootUri("http://localhost:" + port) + .basicAuthorization("user", "password"); authorizedRestTemplate = new TestRestTemplate(builder); } @@ -50,3 +50,57 @@ public void authorizedTest() { assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); } } + + +//package test.pivotal.pal.trackerapi; +// +//import io.pivotal.pal.tracker.PalTrackerApplication; +//import org.junit.Before; +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.context.embedded.LocalServerPort; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.boot.test.web.client.TestRestTemplate; +//import org.springframework.boot.web.client.RestTemplateBuilder; +//import org.springframework.http.HttpStatus; +//import org.springframework.http.ResponseEntity; +//import org.springframework.test.context.junit4.SpringRunner; +// +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +// +//@RunWith(SpringRunner.class) +//@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +//public class SecurityApiTest { +// +// @LocalServerPort +// private String port; +// private TestRestTemplate authorizedRestTemplate; +// +// @Autowired +// private TestRestTemplate unAuthorizedRestTemplate; +// +// @Before +// public void setUp() throws Exception { +// RestTemplateBuilder builder = new RestTemplateBuilder() +// .rootUri("http://localhost:" + port) +// .basicAuthorization("user", "password"); +// +// authorizedRestTemplate = new TestRestTemplate(builder); +// } +// +// @Test +// public void unauthorizedTest() { +// ResponseEntity response = this.unAuthorizedRestTemplate.getForEntity("/", String.class); +// +// assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); +// } +// +// @Test +// public void authorizedTest() { +// ResponseEntity response = this.authorizedRestTemplate.getForEntity("/", String.class); +// +// assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); +// } +//} diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java index 6463b3806..4e1141b53 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -7,9 +7,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -27,16 +28,23 @@ @SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) public class TimeEntryApiTest { - @Autowired private TestRestTemplate restTemplate; - private TimeEntry timeEntry = new TimeEntry(123, 456, "today", 8); + @LocalServerPort + private String port; + @Before public void setUp() throws Exception { MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); + RestTemplateBuilder builder = new RestTemplateBuilder() + .rootUri("http://localhost:" + port) + .basicAuthorization("user", "password"); + + restTemplate = new TestRestTemplate(builder); + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.execute("TRUNCATE time_entries"); } @@ -128,4 +136,136 @@ public void testDelete() throws Exception { private Long createTimeEntry() { return restTemplate.postForObject("/time-entries", timeEntry, TimeEntry.class).getId(); } -} \ No newline at end of file +} + +//package test.pivotal.pal.trackerapi; +// +//import com.jayway.jsonpath.DocumentContext; +//import com.mysql.cj.jdbc.MysqlDataSource; +//import io.pivotal.pal.tracker.PalTrackerApplication; +//import io.pivotal.pal.tracker.TimeEntry; +//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.context.SpringBootTest; +//import org.springframework.boot.test.web.client.TestRestTemplate; +//import org.springframework.http.HttpEntity; +//import org.springframework.http.HttpMethod; +//import org.springframework.http.HttpStatus; +//import org.springframework.http.ResponseEntity; +//import org.springframework.jdbc.core.JdbcTemplate; +//import org.springframework.test.context.junit4.SpringRunner; +// +//import java.util.Collection; +// +//import static com.jayway.jsonpath.JsonPath.parse; +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +// +//@RunWith(SpringRunner.class) +//@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +//public class TimeEntryApiTest { +// +// @Autowired +// private TestRestTemplate restTemplate; +// +// private TimeEntry timeEntry = new TimeEntry(123, 456, "today", 8); +// +// @Before +// public void setUp() throws Exception { +// MysqlDataSource dataSource = new MysqlDataSource(); +// dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); +// +// JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); +// jdbcTemplate.execute("TRUNCATE time_entries"); +// } +// +// @Test +// public void testCreate() throws Exception { +// ResponseEntity createResponse = restTemplate.postForEntity("/time-entries", timeEntry, String.class); +// +// +// assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED); +// +// DocumentContext createJson = parse(createResponse.getBody()); +// assertThat(createJson.read("$.id", Long.class)).isGreaterThan(0); +// assertThat(createJson.read("$.projectId", Long.class)).isEqualTo(123L); +// assertThat(createJson.read("$.userId", Long.class)).isEqualTo(456L); +// assertThat(createJson.read("$.date", String.class)).isEqualTo("today"); +// assertThat(createJson.read("$.hours", Long.class)).isEqualTo(8); +// } +// +// @Test +// public void testList() throws Exception { +// Long id = createTimeEntry(); +// +// +// ResponseEntity listResponse = restTemplate.getForEntity("/time-entries", String.class); +// +// +// assertThat(listResponse.getStatusCode()).isEqualTo(HttpStatus.OK); +// +// DocumentContext listJson = parse(listResponse.getBody()); +// +// Collection timeEntries = listJson.read("$[*]", Collection.class); +// assertThat(timeEntries.size()).isEqualTo(1); +// +// Long readId = listJson.read("$[0].id", Long.class); +// assertThat(readId).isEqualTo(id); +// } +// +// @Test +// public void testRead() throws Exception { +// Long id = createTimeEntry(); +// +// +// ResponseEntity readResponse = this.restTemplate.getForEntity("/time-entries/" + id, String.class); +// +// +// assertThat(readResponse.getStatusCode()).isEqualTo(HttpStatus.OK); +// DocumentContext readJson = parse(readResponse.getBody()); +// assertThat(readJson.read("$.id", Long.class)).isEqualTo(id); +// assertThat(readJson.read("$.projectId", Long.class)).isEqualTo(123L); +// assertThat(readJson.read("$.userId", Long.class)).isEqualTo(456L); +// assertThat(readJson.read("$.date", String.class)).isEqualTo("today"); +// assertThat(readJson.read("$.hours", Long.class)).isEqualTo(8); +// } +// +// @Test +// public void testUpdate() throws Exception { +// Long id = createTimeEntry(); +// TimeEntry updatedTimeEntry = new TimeEntry(2, 3, "tomorrow", 9); +// +// +// ResponseEntity updateResponse = restTemplate.exchange("/time-entries/" + id, HttpMethod.PUT, new HttpEntity<>(updatedTimeEntry, null), String.class); +// +// +// assertThat(updateResponse.getStatusCode()).isEqualTo(HttpStatus.OK); +// +// DocumentContext updateJson = parse(updateResponse.getBody()); +// assertThat(updateJson.read("$.id", Long.class)).isEqualTo(id); +// assertThat(updateJson.read("$.projectId", Long.class)).isEqualTo(2L); +// assertThat(updateJson.read("$.userId", Long.class)).isEqualTo(3L); +// assertThat(updateJson.read("$.date", String.class)).isEqualTo("tomorrow"); +// assertThat(updateJson.read("$.hours", Long.class)).isEqualTo(9); +// } +// +// @Test +// public void testDelete() throws Exception { +// Long id = createTimeEntry(); +// +// +// ResponseEntity deleteResponse = restTemplate.exchange("/time-entries/" + id, HttpMethod.DELETE, null, String.class); +// +// +// assertThat(deleteResponse.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); +// +// ResponseEntity deletedReadResponse = this.restTemplate.getForEntity("/time-entries/" + id, String.class); +// assertThat(deletedReadResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); +// } +// +// private Long createTimeEntry() { +// return restTemplate.postForObject("/time-entries", timeEntry, TimeEntry.class).getId(); +// } +//} \ No newline at end of file diff --git a/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java index cc7091ed4..504aa1b0a 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java @@ -1,11 +1,13 @@ package test.pivotal.pal.trackerapi; import io.pivotal.pal.tracker.PalTrackerApplication; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -15,12 +17,52 @@ @SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) public class WelcomeApiTest { - @Autowired + @LocalServerPort + private String port; private TestRestTemplate restTemplate; + @Before + public void setUp() throws Exception { + RestTemplateBuilder builder = new RestTemplateBuilder() + .rootUri("http://localhost:" + port) + .basicAuthorization("user", "password"); + + restTemplate = new TestRestTemplate(builder); + } + @Test public void exampleTest() { String body = this.restTemplate.getForObject("/", String.class); assertThat(body).isEqualTo("Hello from test"); } } + + +//package test.pivotal.pal.trackerapi; +// +//import io.pivotal.pal.tracker.PalTrackerApplication; +//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.boot.test.web.client.TestRestTemplate; +//import org.springframework.test.context.junit4.SpringRunner; +// +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +// +//@RunWith(SpringRunner.class) +//@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +//public class WelcomeApiTest { +// +// @Autowired +// private TestRestTemplate restTemplate; +// +// @Test +// public void exampleTest() { +// String body = this.restTemplate.getForObject("/", String.class); +// assertThat(body).isEqualTo("Hello from test"); +// } +//} + + From bbe75459a64adaac99c5fbc3803a810a4a1ec63f Mon Sep 17 00:00:00 2001 From: Seokwon Date: Mon, 11 Sep 2017 13:23:43 +0900 Subject: [PATCH 19/19] 05pipeline0911 --- manifest-production.yml | 4 ++-- manifest-review.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest-production.yml b/manifest-production.yml index 348d53181..a216aad07 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -5,5 +5,5 @@ applications: host: ps-pal-tracker-seokwon services: - tracker-database - env: - SECURITY_FORCE_HTTPS: true \ No newline at end of file + env: + SECURITY_FORCE_HTTPS: true \ No newline at end of file diff --git a/manifest-review.yml b/manifest-review.yml index 66c9fc251..e073a647d 100644 --- a/manifest-review.yml +++ b/manifest-review.yml @@ -5,5 +5,5 @@ applications: host: ps-pal-tracker-review-seokwon services: - tracker-database - env: + env: SECURITY_FORCE_HTTPS: true \ No newline at end of file