Skip to content

Commit

Permalink
feat: add a GraphQL endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
jgiovaresco committed Jan 28, 2024
1 parent b788988 commit 35ffbfd
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -193,3 +193,5 @@ nbdist/
!.yarn/sdks
!.yarn/versions
.pnp.*

.bruno
4 changes: 4 additions & 0 deletions README.adoc
Expand Up @@ -133,3 +133,7 @@ will respond
}
}
----

=== GraphQL

The application provides a GraphQL endpoint. It exposes the schema through http://localhost:8888/graphql/schema.graphql
2 changes: 2 additions & 0 deletions app-quarkus/build.gradle.kts
Expand Up @@ -36,6 +36,7 @@ dependencies {
implementation(libs.kotlin.faker)
implementation(libs.slf4j.api)
implementation("io.quarkus:quarkus-arc")
implementation("io.quarkus:quarkus-smallrye-graphql")
implementation("io.quarkus:quarkus-grpc")
implementation("io.quarkus:quarkus-kotlin")
implementation("io.quarkus:quarkus-reactive-routes")
Expand All @@ -50,6 +51,7 @@ dependencies {
testImplementation(libs.bundles.strikt)
testImplementation("io.rest-assured:rest-assured")
testImplementation("io.rest-assured:kotlin-extensions")
testImplementation("io.quarkus:quarkus-smallrye-graphql-client")
testImplementation("io.quarkus:quarkus-junit5")
testImplementation("io.smallrye.reactive:smallrye-mutiny-vertx-web-client")

Expand Down
13 changes: 13 additions & 0 deletions app-quarkus/src/main/kotlin/io/apim/samples/core/starwars/Model.kt
@@ -0,0 +1,13 @@
package io.apim.samples.core.starwars

import java.time.LocalDate

class Film(val id: Int, val title: String, val episode: Int, val director: String, val releaseDate: LocalDate)

class Planet(val id: Int, val name: String, val climate: String, val gravity: String, val population: Long, val films: List<Film>)

class Vehicle(val id: Int, val name: String, val model: String, val manufacturer: String, val length: Int, val crew: Int, val passengers: Int, val cargoCapacity: Int, val vehicleClass: String, val films: List<Film>)

class Starship(val id: Int, val name: String, val model: String, val manufacturer: String, val length: Int, val crew: Int, val passengers: Int, val cargoCapacity: Int, val starshipClass: String, val films: List<Film>)

class Person(val id: Int, val name: String, val birthYear: String, val homeWorld: Planet, val films: List<Film>, val vehicles: List<Vehicle>, val starships: List<Starship>)
@@ -0,0 +1,55 @@
package io.apim.samples.core.starwars

import jakarta.enterprise.context.ApplicationScoped
import java.time.LocalDate

@ApplicationScoped
class StarWarsQueryService {
private val films = ArrayList<Film>()
private val planets = ArrayList<Planet>()
private val vehicles = ArrayList<Vehicle>()
private val starships = ArrayList<Starship>()
private val people = ArrayList<Person>()

init {
films.add(Film(1, "A New Hope", 4, "George Lucas", LocalDate.of(1977, 5, 25)))
films.add(Film(2, "The Empire Strikes Back", 5, "Irvin Kershner", LocalDate.of(1980, 5, 21)))
films.add(Film(3, "Return of the Jedi", 6, "Richard Marquand", LocalDate.of(1983, 5, 25)))
films.add(Film(4, "The Phantom Menace", 1, "George Lucas", LocalDate.of(1999, 5, 19)))
films.add(Film(5, "Attack of the Clones", 2, "George Lucas", LocalDate.of(2002, 5, 16)))
films.add(Film(6, "Revenge of the Sith", 3, "George Lucas", LocalDate.of(2005, 5, 19)))
films.add(Film(7, "The Force Awakens", 7, "J. J. Abrams", LocalDate.of(2015, 12, 18)))
films.add(Film(8, "The Last Jedi", 8, "Rian Johnson", LocalDate.of(2017, 12, 15)))
films.add(Film(9, "The Rise of Skywalker", 9, "J. J. Abrams", LocalDate.of(2019, 12, 20)))

planets.add(Planet(1, "Tatooine", "arid", "1 standard", 200000, listOf(films[0], films[2], films[3], films[4], films[5])))
planets.add(Planet(2, "Alderaan", "temperate", "1 standard", 2000000000, listOf(films[0], films[2], films[3])))
planets.add(Planet(3, "Yavin IV", "temperate, tropical", "1 standard", 1000, listOf(films[0])))
planets.add(Planet(4, "Hoth", "frozen", "1.1 standard", 2000000000, listOf(films[1])))
planets.add(Planet(5, "Dagobah", "murky", "N/A", 10000, listOf(films[1], films[5])))
planets.add(Planet(6, "Bespin", "temperate", "1.5 (surface), 1 standard (Cloud City)", 6000000, listOf(films[1])))
planets.add(Planet(7, "Endor", "forests, mountains, lakes", "0.85 standard", 30000000, listOf(films[2])))
planets.add(Planet(8, "Naboo", "temperate", "1 standard", 4500000000, listOf(films[3], films[4], films[5])))

vehicles.add(Vehicle(1, "Snowspeeder", "t-47 airspeeder", "Incom corporation", 4, 2, 0, 10, "airspeeder", listOf(films[1])))
vehicles.add(Vehicle(2, "Imperial Speeder Bike", "74-Z speeder bike", "Aratech Repulsor Company", 3, 1, 1, 4, "speeder", listOf(films[2])))

starships.add(Starship(1, "X-wing", "T-65 X-wing", "Incom Corporation", 12, 1, 0, 110, "starfighter", listOf(films[0],films[1],films[2])))
starships.add(Starship(2, "Imperial shuttle", "Lambda-class T-4a shuttle", "Sienar Fleet Systems", 20, 6, 20, 80000, "armed government transport", listOf(films[1],films[2])))
starships.add(Starship(3, "TIE Advanced x1", "Twin Ion Engine Advanced x1", "Sienar Fleet Systems", 9, 1, 0, 150, "starfighter", listOf(films[1])))

people.add(Person(1, "Luke Skywalker", "19BBY", planets[0], listOf(films[0], films[1], films[2], films[5]), listOf(vehicles[0], vehicles[1]), listOf(starships[0], starships[1])))
people.add(Person(2, "C-3PO", "112BBY", planets[0], listOf(films[0], films[1], films[2], films[3], films[4], films[5]), listOf(), listOf()))
people.add(Person(3, "R2-D2", "33BBY", planets[7], listOf(films[0], films[1], films[2], films[3], films[4], films[5]), listOf(), listOf()))
people.add(Person(4, "Darth Vader", "41.9BBY", planets[0], listOf(films[0], films[1], films[2], films[5]), listOf(), listOf(starships[2])))
}

fun getFilms(): List<Film> = films
fun getFilm(id: Int): Film? = films.find { it.id == id }
fun getPlanets(): List<Planet> = planets
fun getPlanet(id: Int): Planet? = planets.find { it.id == id }
fun getPeople(): List<Person> = people
fun getPerson(id: Int): Person? = people.find { it.id == id }
fun getPersonByFilm(film: Film): List<Person> = people.filter { p -> p.films.any { f -> f.id == film.id } }
fun getPersonByPlanet(planet: Planet): List<Person> = people.filter { p -> p.homeWorld.id == planet.id }
}
@@ -0,0 +1,48 @@
package io.apim.samples.ports.graphql

import io.apim.samples.core.starwars.Film
import io.apim.samples.core.starwars.Person
import io.apim.samples.core.starwars.Planet
import io.apim.samples.core.starwars.StarWarsQueryService
import jakarta.inject.Inject
import org.eclipse.microprofile.graphql.*

@GraphQLApi
class StarWarsResource {
@Inject
lateinit var starWarsQueryService: StarWarsQueryService

@Query("allFilms")
@Description("Get all films")
fun getAllFilms(): List<Film> = starWarsQueryService.getFilms()

@Query
@Description("Get a film by id")
fun getFilm(@Name("filmId") id: Int): Film? = starWarsQueryService.getFilm(id)

@Query("allPlanets")
@Description("Get all planets")
fun getAllPlanets(): List<Planet> = starWarsQueryService.getPlanets()

@Query
@Description("Get a planet by id")
fun getPlanet(@Name("planetId") id: Int): Planet? = starWarsQueryService.getPlanet(id)

@Query("allPeople")
@Description("Get all people")
fun getAllPeople(): List<Person> = starWarsQueryService.getPeople()

@Query
@Description("Get a person by id")
fun getPerson(@Name("personId") id: Int): Person? = starWarsQueryService.getPerson(id)

fun people(@Source film: Film): List<Person> = starWarsQueryService.getPersonByFilm(film)

@JvmName("getPersonByFilms")
fun people(@Source films: List<Film>): List<List<Person>> = films.map { starWarsQueryService.getPersonByFilm(it) }

fun people(@Source planet: Planet): List<Person> = starWarsQueryService.getPersonByPlanet(planet)

@JvmName("getPersonByPlanets")
fun people(@Source planets: List<Planet>): List<List<Person>> = planets.map { starWarsQueryService.getPersonByPlanet(it) }
}
@@ -0,0 +1,73 @@
package io.apim.samples.ports.graphql

import io.quarkus.test.junit.QuarkusTest
import io.smallrye.graphql.client.GraphQLClient
import io.smallrye.graphql.client.core.Document
import io.smallrye.graphql.client.core.Field
import io.smallrye.graphql.client.core.Operation
import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient
import jakarta.inject.Inject
import org.junit.jupiter.api.Test
import strikt.api.expectThat
import strikt.assertions.containsExactlyInAnyOrder
import strikt.assertions.isEqualTo
import strikt.assertions.isNotNull

@QuarkusTest
class StarWarsResourceTest {

@Inject
@GraphQLClient("star-wars")
lateinit var client: DynamicGraphQLClient

@Test
fun `should return all films`() {
val query = Document.document(
Operation.operation("allFilms",
Field.field("allFilms",
Field.field("title")
)
)
)

val result = client.executeSync(query)
val titles = result.data.getJsonArray("allFilms").stream().map { it.asJsonObject().getString("title") }.toList()

expectThat(titles).isNotNull()
.containsExactlyInAnyOrder(
"A New Hope",
"The Empire Strikes Back",
"Return of the Jedi",
"The Phantom Menace",
"Attack of the Clones",
"Revenge of the Sith",
"The Force Awakens",
"The Last Jedi",
"The Rise of Skywalker"
)
}

@Test
fun `should return a film with all people`() {
val query = """
query getFilm{
film(filmId: 1) {
title
episode
people {
name
}
}
}
""".trimIndent()

val result = client.executeSync(query)

expectThat(result.data.getJsonObject("film")).isNotNull().and {
get { getString("title") }.isEqualTo("A New Hope")
get { getInt("episode") }.isEqualTo(4)
get { getJsonArray("people").stream().map { it.asJsonObject().getString("name") }.toList() }.containsExactlyInAnyOrder("Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader")
}

}
}
1 change: 1 addition & 0 deletions app-quarkus/src/test/resources/application.properties
@@ -0,0 +1 @@
quarkus.smallrye-graphql-client.star-wars.url=http://localhost:${quarkus.http.test-port}/graphql

0 comments on commit 35ffbfd

Please sign in to comment.