Skip to content

Commit

Permalink
Tweak tutorial wording and paths (#5924)
Browse files Browse the repository at this point in the history
* Tweak tutorial wording and paths

* Backport tweaks from #5975

* Adjust package name/plugin id
  • Loading branch information
BoD committed Jul 29, 2024
1 parent 4e3cb66 commit 0f01aca
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 38 deletions.
34 changes: 28 additions & 6 deletions docs/source/tutorial/01-configure-project.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ In this step, you'll import the starter project and add the Apollo Kotlin SDK to

```
git clone https://github.com/apollographql/apollo-kotlin-tutorial
cd apollo-kotlin-tutorial/compose
cd apollo-kotlin-tutorial
```

The starter project is in the `start` folder.
Expand Down Expand Up @@ -39,13 +39,25 @@ This tutorial uses `4.0.0` because it is the latest version at the time of writi

## Apply the plugin

Apply the Apollo plugin in `app/build.gradle.kts`. There are two `build.gradle.kts` in the project. Make sure to use the one in the `app` directory this time. The Apollo plugin ID is `com.apollographql.apollo`.
Add the Apollo plugin to the version catalog in `gradle/libs.versions.toml`. The Apollo plugin ID is `com.apollographql.apollo`:

```toml title="gradle/libs.versions.toml"
[versions]
# ...
apollo = "4.0.0"

[plugins]
# ...
apollo = { id = "com.apollographql.apollo", version.ref = "apollo" }
```

Then apply the Apollo plugin in `app/build.gradle.kts`. There are two `build.gradle.kts` in the project - make sure to use the one in the `app` directory.

```kotlin title="app/build.gradle.kts"
plugins {
id("com.android.application")
alias(libs.plugins.android.application)
// ...
id("com.apollographql.apollo") version "4.0.0"
alias(libs.plugins.apollo)
}
```

Expand All @@ -67,12 +79,22 @@ apollo {
## Add dependencies

Now add `apollo-runtime` to the list of dependencies. This is the code that executes queries and parses responses.
Now add `apollo-runtime` to the list of dependencies. This is the part of the SDK that executes queries and parses responses.

First add the dependency to the version catalog:

```toml title="gradle/libs.versions.toml"
[libraries]
# ...
apollo-runtime = { module = "com.apollographql.apollo:apollo-runtime", version.ref = "apollo"}
```

Then add the dependency to the `app/build.gradle.kts` file:

```kotlin title="app/build.gradle.kts"
dependencies {
// ...
implementation("com.apollographql.apollo:apollo-runtime:4.0.0")
implementation(libs.apollo.runtime)
}
```

Expand Down
22 changes: 17 additions & 5 deletions docs/source/tutorial/02-add-the-graphql-schema.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,28 @@ In the **Reference** tab, you can now see a list of all of the things available

Apollo Kotlin requires a schema to generate type-safe models and code from your queries. There are multiple ways to get a schema. For example, you can go to the SDL tab and download the raw SDL schema using GraphOS Studio Sandbox.

In this tutorial, we will use the `downloadApolloSchema` Gradle task that is created by our plugin automatically. Since GraphQL supports [introspection](https://graphql.org/learn/introspection/), this will work with any GraphQL endpoint that has introspection enabled.
In this tutorial, we will use the Gradle task that is created by the Apollo plugin automatically. Since GraphQL supports [introspection](https://graphql.org/learn/introspection/), this will work with any GraphQL endpoint that has introspection enabled.

First, add the URL of the GraphQL endpoint and the desired location of the schena to the Apollo Gradle configuration:

```kotlin title="app/build.gradle.kts"
apollo {
service("service") {
packageName.set("com.example.rocketreserver")
introspection { // highlight-line
endpointUrl.set("https://apollo-fullstack-tutorial.herokuapp.com/graphql") // highlight-line
schemaFile.set(file("src/main/graphql/schema.graphqls")) // highlight-line
} // highlight-line
}
}
```

From the root of the project, run the following in Android Studio's Terminal tab:
Then, from the root of the project, run this command in Android Studio's Terminal tab:

```shell title="(shell)"
./gradlew :app:downloadApolloSchema --endpoint='https://apollo-fullstack-tutorial.herokuapp.com/graphql' --schema=app/src/main/graphql/schema.graphqls
./gradlew :app:downloadServiceApolloSchemaFromIntrospection
```

Note that the path given to `--schema` is relative to the root of the project.

This will download a `schema.graphqls` file from your endpoint to `app/src/main/graphql/schema.graphqls`.

Next, [write your first query](03-write-your-first-query) that uses this schema.
28 changes: 15 additions & 13 deletions docs/source/tutorial/03-write-your-first-query.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,41 @@
title: "3. Write your first query"
---

The most common GraphQL operation is the **query**, which requests data from your graph in a structure that conforms to your server's schema. If you return to [the Sandbox](https://studio.apollographql.com/sandbox/explorer?endpoint=https%3A%2F%2Fapollo-fullstack-tutorial.herokuapp.com%2Fgraphql) for your server, you can see available queries in the Schema Reference tab you opened earlier.
The most common GraphQL operation is the **query**, which requests data from your graph in a structure that conforms to your server's schema. If you return to [the Sandbox](https://studio.apollographql.com/sandbox/explorer?endpoint=https%3A%2F%2Fapollo-fullstack-tutorial.herokuapp.com%2Fgraphql) for your server, you can see available query fields in the Schema Reference tab you opened earlier.

Scroll down to the `launches` query to get details about it:
Scroll down to the `launches` field to get details about it:

<img src="images/launches_detail.png" class="screenshot" alt="Detail about launches query"/>

Here, you see both the query term itself, the return type, and information about parameters that can be passed to the query. You can use this information to write a query you'll eventually add to your app.
Here, you see both the field name itself, its return type, and information about parameters that can be passed to it. You can use this information to write a query you'll eventually add to your app.

To start working with this query in the Sandbox Explorer, select the "play" button to the right side of the information:
To start working with this field in the Sandbox Explorer, select the "play" button to the right side of the information:

<img src="images/open_in_explorer_launches.png" class="screenshot" alt="Open in Explorer"/>

This brings you back into Sandbox's Explorer tab with the sidebar on the left showing documentation for the query you've selected:
This brings you back into Sandbox's Explorer tab with the sidebar on the left showing documentation for the field you've selected:

<img src="images/explorer_sandbox_open.png" class="screenshot" alt="Docs open in the left sidebar"/>

Notice the small button next to the `launches` icon. Click this button to add the query to the middle "operations" panel:
Notice the small plus button next to the `launches` field. Click this button to add the field to the middle "operations" panel:

<img src="images/explorer_add_launches_query.png" class="screenshot" alt="Click the button to add this query"/>

When the query is added, it will look like this:
When the field is added, it will look like this:

<img src="images/explorer_initial_added_query.png" class="screenshot" alt="The query once it's been added to the Operations section"/>

Let's break down what you're seeing here:

- The type of the operation, `query`, followed by the name of the operation, currently `Query` (we'll make that more specific in a second), is the outermost set of brackets.
- The actual query being called is the next set of brackets in. Since the `arguments` for this query both have default values, they are not automatically added to the query for you.
- An error in the empty space between the brackets, which is where you'll put the list of information you want back from each launch.
- The type of the operation, `query`, followed by the name of the operation, currently `Query` (we'll make that more specific in a second), is the outermost set of braces.
- The actual field being selected is the next set of braces in. Since the **arguments** for this field both have default values, they are not automatically added for you.
- An error in the empty space between the braces, which is where you'll put the list of information you want back from each launch.

The Apollo Kotlin SDK requires every query to have a name (even though this isn't required by the GraphQL spec). Since you're going to create more than one query, it's also a good idea to give this operation a specific name other than `Query`. Change the name of the operation to `LaunchList`:

<img src="images/explorer_launch_list_rename.png" class="screenshot" alt="Renaming the query"/>

Next, on the left hand side, you can select what fields you want back in the returned object. Start by clicking the button next to the `cursor` field. It will mark that field as selected, then insert it into your operations:
Next, on the left hand side, you can select what fields you want back in the returned object. Start by clicking the plus button next to the `cursor` field. It will mark that field as selected, then insert it into your operations:

<img src="images/explorer_check_cursor.png" class="screenshot" alt="After adding the cursor field."/>

Expand All @@ -48,7 +48,7 @@ However, you can also use auto-complete to help you with this. Add a newline bel

The Sandbox Explorer is a great tool for building and verifying queries so you don't have to repeatedly rebuild your project in Android Studio to try out changes.

As the schema indicates, the `launches` query returns a `LaunchConnection` object. This object includes a list of launches, along with fields related to pagination (`cursor` and `hasMore`). The query you've written so far indicates exactly which fields of this `LaunchConnection` object you want to be returned.
As the schema indicates, the `launches` field returns a `LaunchConnection` object. This object includes a list of launches, along with fields related to pagination (`cursor` and `hasMore`). The query you've written so far indicates exactly which fields of this `LaunchConnection` object you want to be returned.

Run this query by pressing the "Submit Operation" button, which should now have the name of your query, `LaunchList`:

Expand All @@ -64,11 +64,13 @@ Click the button next to the `launches` field at the bottom of the left column.

<img src="images/explorer_launches_drill_in.png" class="screenshot" alt="Status after adding launches field"/>

The fields you add in this set of brackets will be fetched for every launch in the list. Click the buttons next to `id` and `site` properties to add those two fields. When you're done, your operation should look like this:
The fields you add in this set of braces will be fetched for every launch in the list. Click the buttons next to `id` and `site` properties to add those two fields. When you're done, your operation should look like this:

```graphql title="(Sandbox Explorer)"
query LaunchList {
launches {
cursor
hasMore
launches {
id
site
Expand Down
4 changes: 2 additions & 2 deletions docs/source/tutorial/05-connect-queries-to-your-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
title: "5. Connect your queries to your UI"
---

In this chapter, you are going to display a list of Launch Sites in a [LazyColumn](https://developer.android.com/jetpack/compose/lists#lazy).
In this chapter, you are going to display a list of Launches in a [LazyColumn](https://developer.android.com/jetpack/compose/lists#lazy).

## Configure LaunchListAdapter
## Setup the LaunchList composable

In `LaunchList`, declare a list of `LaunchListQuery.Launch`, initialized as empty:

Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorial/06-add-more-info.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Any GraphQL field can take arguments like `missionPatch` above, and arguments ca

<img src="images/sandbox_patch_size_docs.png" alt="The patch size enum in Sandbox's Schema tab" class="screenshot"/>

## Bind the info
## Display the fields

In `LaunchList.kt`, bind the GraphQL data to the mission name, site, and mission patch using Coil's `AsyncImage`:

Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorial/07-paginate-results.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fun LaunchList(onLaunchClick: (launchId: String) -> Unit) {

Pass the `cursor` to the `LaunchListQuery`, and add a special item at the end of the list which updates the `cursor` if `hasNext` is true. This will trigger a new query with the new cursor whenever the user scrolls to the end of the list, and `launchList` will be concatenated with the new results.

> **Note:** this is a basic implementation of paging in Compose. In a real project you may use something more advanced, like the [Jetpack Paging library](https://developer.android.com/jetpack/compose/lists#large-datasets).
> **Note:** this is a basic implementation of pagination in Compose. In a real project you may use something more advanced, like the [Jetpack Paging library](https://developer.android.com/jetpack/compose/lists#large-datasets).

The whole function should look like this:
```kotlin title="app/src/main/java/com/example/rocketreserver/LaunchList.kt"
Expand Down
14 changes: 12 additions & 2 deletions docs/source/tutorial/08-add-a-details-view.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ fun LaunchDetails(launchId: String) {
Now use the state to show the appropriate UI:
```kotlin title="app/src/main/java/com/example/rocketreserver/LaunchDetails.kt"
// Use the state
// Use the state
when (val s = state) {
Loading -> Loading()
is ProtocolError -> ErrorMessage("Oh no... A protocol error happened: ${s.exception.message}")
Expand Down Expand Up @@ -186,7 +186,7 @@ private sealed interface LaunchDetailsState {
}
```

Then, in the `try` block, check for `response.hasErrors()` and wrap the result in the new state:
Then, in the `when` block, check for `response.hasErrors()` and wrap the result in the new state:

```kotlin title="app/src/main/java/com/example/rocketreserver/LaunchDetails.kt"
state = when {
Expand All @@ -202,6 +202,16 @@ state = when {
}
```

You should also update the conditional expression to handle the `ApplicationError` case:
```kotlin title="app/src/main/java/com/example/rocketreserver/LaunchDetails.kt"
when (val s = state) {
Loading -> Loading()
is ApplicationError -> ErrorMessage(text = s.errors.first().message) // highlight-line
is ProtocolError -> ErrorMessage("Oh no... A protocol error happened: ${s.exception.message}")
is Success -> LaunchDetails(s.data)
}
```

`response.errors` contains details about any errors that occurred. Note that this code also null-checks `response.data!!`. In theory, a server should not set `response.data == null` and `response.hasErrors == false` at the same time, but the type system cannot guarantee this.

To trigger an error, replace `LaunchDetailsQuery(launchId)` with `LaunchDetailsQuery("invalidId")`. Disable airplane mode and select a launch. The server will send this response:
Expand Down
10 changes: 5 additions & 5 deletions docs/source/tutorial/09-write-your-first-mutation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ A mutation is used to change data on your server. Here the login mutation will c

## Prototype your mutation in Sandbox Explorer

Open [your Sandbox Explorer](https://studio.apollographql.com/sandbox/explorer?endpoint=https%3A%2F%2Fapollo-fullstack-tutorial.herokuapp.com%2Fgraphql) and click on the plus symbol to add a new tab. Next, click on the Schema icon to get back to looking at your schema, and select "Mutation" to look at your list of mutations:
Open [your Sandbox Explorer](https://studio.apollographql.com/sandbox/explorer?endpoint=https%3A%2F%2Fapollo-fullstack-tutorial.herokuapp.com%2Fgraphql) and click on the plus symbol to add a new tab. Next, click on the Schema icon to get back to looking at your schema, and select "Mutation" to look at your list of mutation fields:

<img alt="The list of available mutations" class="screenshot" src="images/sandbox_schema_mutations.png"/>

Scroll down to take a look at the `login` mutation:
Scroll down to take a look at the `login` field:

<img alt="The definition of login in the schema" class="screenshot" src="images/schema_login_definition.png"/>

Click the play button to the right to open that mutation in the Explorer tab. When it opens, click the plus sign to add the operation:
Click the play button to the right to open that field in the Explorer tab. When it opens, click the plus sign to add field to your operation:

<img alt="The login mutation after initially being added" class="screenshot" src="images/explorer_added_login_mutation.png"/>

Notice the red error indication - this is because the type returned by the mutation is `User`, which is not a **leaf** type: you need to choose which of the user's fields the mutation will return. For our purposes, we only need the `token` field, so add it by clicking the plus sign next to it.
Notice the red error indication - this is because the type returned by the field is `User`, which is not a **leaf** type: you need to choose which of the user's fields the mutation will return. For our purposes, we only need the `token` field, so add it by clicking the plus sign next to it.

You'll also notice that `email` wasn't automatically added as an argument even though it doesn't seem to have a default value: that's because `email` is of type `String` - which remember, in GraphQL, means that it's **not** required (although obviously you won't get too far without it).

Expand All @@ -34,7 +34,7 @@ Click the plus sign next to the `email` argument to have that argument added:

You'll also notice that Sandbox Explorer has added a variable to your "Variables" section to match the login email.

Click the Submit Operation button your mutation. You'll see that since you sent `null` for the email address, you get back `null` for the login:
Click the Submit Operation button to execute your mutation. You'll see that since you sent `null` for the email address, you get back `null` for the login:

<img alt="Results of passing a null email" class="screenshot" src="images/login_mutation_null.png"/>

Expand Down
6 changes: 3 additions & 3 deletions docs/source/tutorial/11-subscriptions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ In this section, you will use subscriptions to get notified whenever someone boo

## Write your subscription

Open your [Sandbox](https://studio.apollographql.com/sandbox/explorer?endpoint=https%3A%2F%2Fapollo-fullstack-tutorial.herokuapp.com%2Fgraphql) back up, click on the Schema tab at the far left. In addition to `queries` and `mutations`, you will see a third type of operations, `subscriptions`. Click on subscriptions to see the `tripsBooked` subscription:
Open your [Sandbox](https://studio.apollographql.com/sandbox/explorer?endpoint=https%3A%2F%2Fapollo-fullstack-tutorial.herokuapp.com%2Fgraphql) back up, click on the Schema tab at the far left. In addition to `queries` and `mutations`, you will see a third type of root fields, `subscriptions`. Click on subscriptions to see the `tripsBooked` field:

<img alt="The definition of tripsBooked in the schema" class="screenshot" src="images/schema_tripsBooked_definition.png"/>

This subscription doesn't take any argument and returns a single scalar named `tripsBooked`. Since you can book multiple trips at once, `tripsBooked` is an `Int`. It will contain the number of trips booked at once or -1 if a trip has been cancelled.
This field doesn't take any argument and returns a single scalar named `tripsBooked`. Since you can book multiple trips at once, `tripsBooked` is an `Int`. It will contain the number of trips booked at once or -1 if a trip has been cancelled.

Click the play button to the left of `tripsBooked` to open the subscription in Explorer. Open a new tab, then check the `tripsBooked` button to have the subscription added:
Click the play button to the left of `tripsBooked` to open the field in Explorer. Open a new tab, then check the plus button next to `tripsBooked` to have the field added:

<img alt="The initial definition of the TripsBooked subscription" class="screenshot" src="images/explorer_tripsbooked_initial.png"/>

Expand Down

0 comments on commit 0f01aca

Please sign in to comment.