diff --git a/.github/workflows/server-gradle.yml b/.github/workflows/server-gradle.yml index bb1d544..d04b756 100644 --- a/.github/workflows/server-gradle.yml +++ b/.github/workflows/server-gradle.yml @@ -4,16 +4,22 @@ on: [push] jobs: build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Cache Gradle artifacts (downloaded JARs, the wrapper, and any downloaded JDKs) + uses: actions/cache@v2 with: - java-version: 1.8 + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ~/.gradle/jdks + key: ${{ runner.os }}-gradle-${{ hashFiles('**/.gradle/') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Build with Gradle - run: | - cd server - ./gradlew build + run: ./gradlew build + working-directory: ./server diff --git a/LABTASKS.md b/LABTASKS.md index 7be208e..d5ce709 100644 --- a/LABTASKS.md +++ b/LABTASKS.md @@ -1,4 +1,16 @@ -# Lab Tasks +# Lab Tasks + +* [Notes on notation and structure](#notes-on-notation-and-structure) +* [Exploring the project](#exploring-the-project) +* [Exploring the server](#exploring-the-server) +* [Exploring the client](#exploring-the-client) +* [Use ZenHub to support Agile development](#use-zenhub-to-support-agile-development) + * [Setting up the project ZenHub board](#setting-up-the-project-zenhub-board) + * [Using the board](#using-the-board) +* [The epics/features](#the-epicsfeatures) +* [Questions](#questions) + +## Notes on notation and structure * Questions that you need to answer (as a group!) are indicated with question mark symbols (:question:). * The [Questions](./LABTASKS.md#questions) section is at the end of this document. @@ -32,7 +44,7 @@ Run it according to the instructions in the [README](./README.md), including running the JUnit tests. Answer the following questions. -> Protip: Using Google to gain additional knowledge or support your conjectures +> Pro-tip: Using Google to gain additional knowledge or support your conjectures about how something works is great! It's important that you think about how everything fits together and works, though, so don't use Google as a replacement for building your understanding or you will regret it! diff --git a/README.md b/README.md index e31a90a..7cb90fb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,19 @@ -# CSCI 3601 Lab #2 - Building a web server in Java with Javalin +# CSCI 3601 Lab #2 - Building a web server in Java with Javalin [![Server Build Status](../../workflows/Server%20Java/badge.svg?branch=master)](../../actions?query=workflow%3A"Server+Java") +- [Overview of the project](#overview-of-the-project) +- [Setup](#setup) + - [Cloning the project in GitKraken](#cloning-the-project-in-gitkraken) + - [Open the project in VS Code](#open-the-project-in-vs-code) + - [JSONView browser extension](#jsonview-browser-extension) +- [Running Your project](#running-your-project) +- [Testing Your Project](#testing-your-project) +- [Checking your code coverage](#checking-your-code-coverage) +- [Continuous Integration with GitHub Actions](#continuous-integration-with-github-actions) +- [Go do the lab!](#go-do-the-lab) +- [Resources](#resources) + Here you will explore serving up a simple website that you create, using a server written with the [Javalin framework][javalin-io] server. Javalin is a micro framework for @@ -89,13 +101,12 @@ Don't worry if you don't get the dialog, it is probably because you already have Like in previous labs, click "Install All" to automatically install them. -### Install JSONView browser extension +### JSONView browser extension -You'll also want the JSONView extension for either Firefox or Chrome installed. -This will make JSON in the browser look pretty and actually be readable. +If you use Chrome, you may find it helpful to install the [JSONView][jsonview-chrome] extension. +This will make JSON in the browser look pretty and actually be readable when you visit the different API endpoints. -* Firefox: [JSONView][jsonview-firefox] -* Chrome: [JSONView][jsonview-chrome] +If you use Firefox, this functionality is built-in, so there is no need to install an extension. ## Running Your project @@ -122,14 +133,6 @@ run on. Visit it at [http://localhost:4567][local] in your web browser. The server will continue to run indefinitely until you stop it -To run the JUnit tests: - -```bash -./gradlew test -``` - -You can also run your tests right in VS Code by clicking the "Run Test" link above them. - ## Testing Your Project There's very little meaningful logic in the client component of this @@ -140,22 +143,25 @@ labs. The server-side portion of this project will be tested using JUnit. Server-side tests are located in the `src/test/java` directory. -To run your server-side tests, you have a few options: +To run your server-side tests: while in the `server` directory, run: + +```bash +./gradlew test +``` -You can run `./gradlew test` from the `server` directory in the terminal to run all tests and output info about the run to an HTML file. +This will run all tests and output info about the run to a test report "website". To see the report open the file in your browser: -Additionally, you can go to the Test pane on the left side of VS Code and run all or individual tests from there. -The test pane looks like this: +```text +server/build/reports/tests/test/index.html +``` -![VS Code test pane](https://i.vgy.me/TAfWcG.png) +It will look something like this: -You can also run your tests right in the code editor by clicking the "Run Test" link above them. +![image](https://user-images.githubusercontent.com/1300395/107262491-3cf57780-6a06-11eb-9e5b-68d4491cde47.png) -![VS Code inline testing](https://i.vgy.me/SxWfWh.png) +These test reports are especially helpful when a test fails because you will get the full stack trace there. -This last two options are particularly nice because it gives you better -integrated feedback in VS Code and the ability to just run single -tests or files of tests when you're trying to debug a complex problem. +When a test fails, you will get a notice in the terminal that there were failing tests along with a path to the report. You can copy that path into your browser to see the report. ## Checking your code coverage @@ -166,15 +172,20 @@ so you can see how well your tests cover (i.e., exercise) your code. The command ./gradlew test jacocoTestReport ``` -will run the tests followed by the test coverage report generator. The report is in fact -a small "website" composed of a collection of HTML files. To see the report open the file +will run the tests followed by the test coverage report generator. This report is a "website" like the one from JUnit above. To see the report open the file in your browser: -```bash +```text server/build/jacocoHtml/index.html ``` +It will look something like this: + +![image](https://user-images.githubusercontent.com/1300395/107262605-5c8ca000-6a06-11eb-9844-f7b5d1265eb2.png) + If you generate and look at that report at the start of the lab, you'll see that you start -with 100% coverage of all the `user` files. You'd like to keep it that way, so check your +with 100% coverage of everything but the `Server.java` +file, e.g., we're great on all the `user` files. +You'd like to keep it that way, so check your code coverage after major stories are finished and look for areas that you're not yet testing. :bangbang: The server file itself currently has 0% coverage, although we think we could @@ -202,16 +213,6 @@ most of the actual work of the lab is described. ## Resources -### Running in the command line - -We include a Gradle wrapper which lets you run gradle tasks from the command line. So, you can run tasks like: - -```bash -./gradlew test -``` - -### Handling requests in Javalin - * [Getting started in Javalin](https://javalin.io/documentation#getting-started) * [Javalin tutorials](https://javalin.io/tutorials/) * [Testing Javalin](https://javalin.io/tutorials/testing) @@ -221,12 +222,7 @@ We include a Gradle wrapper which lets you run gradle tasks from the command lin [javalin-io]: https://javalin.io [gradle]: https://gradle.org/ -[intellij-idea]: https://www.jetbrains.com/idea/ -[jasmine]: https://jasmine.github.io/ -[jasmine-introduction]: http://jasmine.github.io/2.0/introduction.html [jsonview-chrome]: https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc?hl=en -[jsonview-firefox]: https://addons.mozilla.org/en-us/firefox/addon/jsonview/ -[karma]: https://karma-runner.github.io/1.0/index.html [labtasks]: LABTASKS.md [local]: http://localhost:4567/ [rest-best-practices]: https://medium.com/@mwaysolutions/10-best-practices-for-better-restful-api-cbe81b06f291 diff --git a/client/javascript/todos.js b/client/javascript/todos.js index cbe4926..529e0ee 100644 --- a/client/javascript/todos.js +++ b/client/javascript/todos.js @@ -1,7 +1,7 @@ // gets todos from the api. // It adds the values of the various inputs to the requested URl to filter and order the returned todos. function getFilteredTodos() { - console.log("Getting all the todos."); + console.log("Getting todos"); var url = "/api/todos?"; if(document.getElementById("owner").value != "") { @@ -24,6 +24,7 @@ function getFilteredTodos() { } get(url, function(returned_json){ + document.getElementById("requestUrl").innerHTML = url; document.getElementById('jsonDump').innerHTML = syntaxHighlight(JSON.stringify(returned_json, null, 2)); }); } diff --git a/client/javascript/users.js b/client/javascript/users.js index 2a99ca2..25aa89a 100644 --- a/client/javascript/users.js +++ b/client/javascript/users.js @@ -1,28 +1,7 @@ -/** - * Function to get all the users! - */ -function getAllUsers() { - console.log("Getting all the users."); - - get("/api/users", function (returned_json) { - document.getElementById('jsonDump').innerHTML = returned_json; - }); -} - -function getAllUsersByAge() { - console.log("Getting all the users."); - - get("/api/users?age=" + document.getElementById("age").value, function (returned_json) { - document.getElementById('jsonDump').innerHTML = returned_json; - }); -} - - - // gets users from the api. // It adds the values of the various inputs to the requested URl to filter and order the returned users. function getFilteredUsers() { - console.log("Getting all the users."); + console.log("Getting users"); var url = "/api/users?"; if(document.getElementById("age").value != "") { @@ -34,6 +13,7 @@ function getFilteredUsers() { get(url, function(returned_json){ + document.getElementById("requestUrl").innerHTML = url; document.getElementById('jsonDump').innerHTML = syntaxHighlight(JSON.stringify(returned_json, null, 2)); }); } diff --git a/client/todos.html b/client/todos.html index c241878..2a4324c 100644 --- a/client/todos.html +++ b/client/todos.html @@ -35,16 +35,14 @@
Limit:
- + -
 Json will go here 
+
Requested URL: (the requested API URL will appear here)
+
JSON will go here
diff --git a/client/users.html b/client/users.html index 49924bb..dd96cf4 100644 --- a/client/users.html +++ b/client/users.html @@ -21,16 +21,13 @@
- -
 Json will go here
+
Requested URL: (the requested API URL will appear here)
+
JSON will go here
diff --git a/server/build.gradle b/server/build.gradle index 56fdaed..cb35c0c 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -2,9 +2,6 @@ // Builds the server side of the project plugins { - // Apply the java plugin to add support for Java - id 'java' - // Apply the application plugin to add support for building a CLI application id 'application' @@ -13,6 +10,13 @@ plugins { id 'jacoco' } +// Build and run the project with Java 11 +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} + // In this section you declare where to find the dependencies of your project repositories { // Use jcenter for resolving your dependencies. @@ -26,31 +30,31 @@ mainClassName = 'umm3601.Server' // External dependencies that our application utilizes dependencies { // Google core libraries for Java, various useful utilities - implementation 'com.google.guava:guava:28.2-jre' + implementation 'com.google.guava:guava:30.1-jre' // Javalin, a simple web framework for Java - compile 'io.javalin:javalin:3.7.0' + implementation 'io.javalin:javalin:3.13.3' // Jackson, a JSON library for Java - implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.1' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.1' // Simple Logging Facade for Java implementation 'org.slf4j:slf4j-simple:1.7.30' implementation 'com.google.code.gson:gson:2.8.6' // JUnit Jupiter API for testing. - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.1' // JUnit Jupiter Engine for testing. - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.1' // Mockito for testing - testImplementation 'org.mockito:mockito-core:3.2.4' + testImplementation 'org.mockito:mockito-core:3.7.7' } application { // Define the main class for the application - mainClassName = 'umm3601.Server' + mainClass = 'umm3601.Server' } test { @@ -58,9 +62,6 @@ test { useJUnitPlatform() } -// We want our source code to be compatible with Java 1.8 -sourceCompatibility = '1.8' - wrapper { distributionType = Wrapper.DistributionType.ALL } diff --git a/server/gradle/wrapper/gradle-wrapper.jar b/server/gradle/wrapper/gradle-wrapper.jar index f3d88b1..e708b1c 100644 Binary files a/server/gradle/wrapper/gradle-wrapper.jar and b/server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/server/gradle/wrapper/gradle-wrapper.properties b/server/gradle/wrapper/gradle-wrapper.properties index 4e1cc9d..80cf08e 100644 --- a/server/gradle/wrapper/gradle-wrapper.properties +++ b/server/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/server/gradlew b/server/gradlew index 2fe81a7..4f906e0 100755 --- a/server/gradlew +++ b/server/gradlew @@ -82,6 +82,7 @@ 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 @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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 diff --git a/server/gradlew.bat b/server/gradlew.bat index 9618d8d..006c7c3 100644 --- a/server/gradlew.bat +++ b/server/gradlew.bat @@ -37,7 +37,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +51,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +61,13 @@ 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% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/server/src/main/java/umm3601/Server.java b/server/src/main/java/umm3601/Server.java index 4535ea0..e2a1a01 100644 --- a/server/src/main/java/umm3601/Server.java +++ b/server/src/main/java/umm3601/Server.java @@ -35,10 +35,10 @@ public static void main(String[] args) { // API endpoints // Get specific user - server.get("api/users/:id", ctx -> userController.getUser(ctx)); + server.get("/api/users/:id", ctx -> userController.getUser(ctx)); // List users, filtered using query parameters - server.get("api/users", ctx -> userController.getUsers(ctx)); + server.get("/api/users", ctx -> userController.getUsers(ctx)); } /*** diff --git a/server/src/test/java/umm3601/user/UserControllerSpec.java b/server/src/test/java/umm3601/user/UserControllerSpec.java index ed7ab67..0ec8453 100644 --- a/server/src/test/java/umm3601/user/UserControllerSpec.java +++ b/server/src/test/java/umm3601/user/UserControllerSpec.java @@ -130,14 +130,14 @@ public void GET_to_request_company_OHMNET_age_25_users() throws IOException { @Test public void GET_to_request_user_with_existent_id() throws IOException { - when(ctx.pathParam("id", String.class)).thenReturn(new Validator("588935f5c668650dc77df581", "")); + when(ctx.pathParam("id", String.class)).thenReturn(new Validator("588935f5c668650dc77df581", "", "id")); userController.getUser(ctx); verify(ctx).status(201); } @Test public void GET_to_request_user_with_nonexistent_id() throws IOException { - when(ctx.pathParam("id", String.class)).thenReturn(new Validator("nonexistent", "")); + when(ctx.pathParam("id", String.class)).thenReturn(new Validator("nonexistent", "", "id")); Assertions.assertThrows(NotFoundResponse.class, () -> { userController.getUser(ctx); });