Skip to content

feat: adds lesson_26 homework and lesson_27 pre-work #593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/check_lesson_26_java_pr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Check Lesson 26 Java Pull Request

on:
pull_request:
branches: [ "main" ]
paths:
- "lesson_26/api/java/**"

jobs:
build:

runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write

steps:
- uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Build Lesson 26 with Java
working-directory: ./lesson_26/api/java
run: ./gradlew check
28 changes: 28 additions & 0 deletions .github/workflows/check_lesson_26_ts_pr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Check Lesson 26 TS Pull Request

on:
pull_request:
branches: [ "main" ]
paths:
- "lesson_26/api/javascript/api_app/**"

jobs:
build:

runs-on: ubuntu-latest
permissions:
contents: read

steps:
- uses: actions/checkout@v4

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'

- name: Build Lesson 26 with Node.js
working-directory: ./lesson_26/api/javascript/api_app
run: |
npm ci
npm run check
10 changes: 10 additions & 0 deletions .github/workflows/check_push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,13 @@ jobs:
working-directory: ./lesson_17/bank
run: ./gradlew check

- name: Build Lesson 26 with Java
working-directory: ./lesson_26/api/java
run: ./gradlew assemble

- name: Build Lesson 26 with Node.js
working-directory: ./lesson_26/api/javascript/api_app
run: |
npm ci
npm run build

47 changes: 46 additions & 1 deletion lesson_26/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,49 @@ Please review the following resources before lecture:

## Homework

- TODO(anthonydmays): Add details here.
- [ ] Complete the [Creating a Library API](#creating-a-library-api) assignment.
- [ ] Do pre-work for [lesson 27](/lesson_27/).

### Creating a Library API

We are continuing to build atop the foundation of our library app. For this assignment, you will help implement the API that will be used by a yet-to-come front-end app.

* You will implement the [MediaItemsController][controller-file] to enable the following API:
* `GET /items` - Retrieves a list of media items
* `POST /items` - Creates a new media item
* `GET /items/:id` - Retrieves a single media item with the given ID.
* `DELETE /items/:id` - Deletes a single media item with the given ID.
* Study the tests in [MediaItemsControllerTest][controller-test-file] to understand what you should accept and return in the API.
* You should not need to make any code changes outside of the `com.codedifferently.lesson26.web` package.

#### Running the API

You can run the server using the usual `./gradlew run` command from the `api/java` directory. If you want to test that the server is running correctly, you can use `curl` like so:

```bash
curl http://localhost:3001/items | json_pp
```

The project also includes an OpenAPI user interface (Swagger) for navigating the API. Just visit http://localhost:3001/swagger-ui.html to access it.

Alternatively, you can also test the API using the tool [Postman][postman-link]. I recommend installing this tool to make it easier to test things.

#### Debugging the API

Remember that you can debug the API by visiting the main function in [Lesson26.java][main-file] and clicking `Debug main`. You'll be able to set breakpoints in your code to see what's happening and fix issues.

![Debugging the API](./debug.png)


#### TypeScript API

This project also includes a fully functioning TypeScript version of the Java project. You can visit `api/javascript/api_app` to execute it using `npm start` and view the OpenAPI documentation at http://localhost:3000/api (note that it runs on port 3000).

## Additional resources

* [gRPC vs REST: Comparing API Styles in Practice (Article)](https://dev.to/anthonydmays/grpc-vs-rest-comparing-api-styles-in-practice-4bl): This article explains why the stuff most people call REST isn't actually.

[controller-file]: ./api/java/api_app/src/main/java/com/codedifferently/lesson26/web/MediaItemsController.java
[controller-test-file]: ./api/java/api_app/src/test/java/com/codedifferently/lesson26/web/MediaItemsControllerTest.java
[postman-link]: https://www.postman.com/downloads/
[main-file]: ./api/java/api_app/src/main/java/com/codedifferently/lesson26/Lesson26.java
9 changes: 9 additions & 0 deletions lesson_26/api/java/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf

# These are Windows script files and should use crlf
*.bat text eol=crlf

5 changes: 5 additions & 0 deletions lesson_26/api/java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build
79 changes: 79 additions & 0 deletions lesson_26/api/java/api_app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
application
eclipse
id("com.diffplug.spotless") version "6.25.0"
id("org.springframework.boot") version "3.4.0"
id("com.adarshr.test-logger") version "4.0.0"
id("io.freefair.lombok") version "8.6"
}

apply(plugin = "io.spring.dependency-management")

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}

dependencies {
// Use JUnit Jupiter for testing.
testImplementation("com.codedifferently.instructional:instructional-lib")
testImplementation("org.junit.jupiter:junit-jupiter:5.11.3")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.assertj:assertj-core:3.26.3")
testImplementation("at.favre.lib:bcrypt:0.10.2")
testImplementation("org.springframework.boot:spring-boot-starter-test")

// This dependency is used by the application.
implementation("com.codedifferently.instructional:instructional-lib")
implementation("com.google.guava:guava:33.3.1-jre")
implementation("com.google.code.gson:gson:2.11.0")
implementation("commons-cli:commons-cli:1.6.0")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0")
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.springframework.boot:spring-boot-devtools")
implementation("com.opencsv:opencsv:5.9")
implementation("org.apache.commons:commons-csv:1.10.0")
implementation("org.xerial:sqlite-jdbc:3.36.0")
implementation("org.hibernate.orm:hibernate-community-dialects:6.2.7.Final")
}

application {
// Define the main class for the application.
mainClass.set("com.codedifferently.lesson26.Lesson26")
}

tasks.named<JavaExec>("run") {
standardInput = System.`in`
}

tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}


configure<com.diffplug.gradle.spotless.SpotlessExtension> {

format("misc", {
// define the files to apply `misc` to
target("*.gradle", ".gitattributes", ".gitignore")

// define the steps to apply to those files
trimTrailingWhitespace()
indentWithTabs() // or spaces. Takes an integer argument if you don't like 4
endWithNewline()
})

java {
// don't need to set target, it is inferred from java

// apply a specific flavor of google-java-format
googleJavaFormat()
// fix formatting of type annotations
formatAnnotations()
}
}
2 changes: 2 additions & 0 deletions lesson_26/api/java/api_app/lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This file is generated by the 'io.freefair.lombok' Gradle plugin
config.stopBubbling = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.codedifferently.lesson26;

import com.codedifferently.lesson26.cli.LibraryApp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;

@Configuration
@SpringBootApplication(scanBasePackages = "com.codedifferently")
public class Lesson26 implements CommandLineRunner {
@Autowired private LibraryApp libraryApp;

public static void main(String[] args) {
var application = new SpringApplication(Lesson26.class);
application.run(args);
}

@Override
public void run(String... args) throws Exception {
// Don't run as an app if we're running as a JUnit test.
if (isJUnitTest()) {
return;
}

libraryApp.run(args);
}

private static boolean isJUnitTest() {
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
if (element.getClassName().startsWith("org.junit.")) {
return true;
}
}
return false;
}
}
Loading
Loading