Skip to content

Commit a36dde6

Browse files
martinbonninBoD
andcommitted
[docs 📚] next version docs (apollographql#5759)
* Add note about directive validation * add doc about Network monitor * tweak title * Bump kotlin_labs to v0.3 * add left panel entry * add experimental websockets doc * wording * wording * Update ApolloCompilerPlugin * use our own note * add a section about arguments and limitations * Remove Espresso IdlingResource from the README as it's deprecated * add beta.6 changelog * formating * typo --------- Co-authored-by: BoD <[email protected]>
1 parent fea1dd8 commit a36dde6

File tree

8 files changed

+336
-12
lines changed

8 files changed

+336
-12
lines changed

CHANGELOG.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,75 @@
11
Change Log
22
==========
33

4+
# Version 4.0.0-beta.6
5+
6+
_2024-04-23_
7+
8+
9+
## SQL cache performance improvements
10+
11+
If you're using a chained memory + SQL cache, #5840 makes sure cache writes are wrapped in a transaction, making them much faster.
12+
13+
## Apollo Compiler Plugins
14+
15+
`Plugin` is renamed to `ApolloCompilerPlugin`. There is a new `documentTransform` API as well as other fixes. More details in the [compiler plugins documentation](https://deploy-preview-5759--apollo-android-docs.netlify.app/advanced/compiler-plugins).
16+
17+
## Experimental WebSockets
18+
19+
A new `.websocket` package is available that makes it easier to retry WebSockets and handle errors. More details and migration guide in the [experimental websockets documentation](https://deploy-preview-5759--apollo-android-docs.netlify.app/advanced/experimental-websockets).
20+
21+
## ApolloIdlingResource is deprecated
22+
23+
We recommend using reactive patterns to test your UI instead. See [this article about ways to do so](https://medium.com/androiddevelopers/alternatives-to-idling-resources-in-compose-tests-8ae71f9fc473).
24+
25+
## Removed androidx.startup dependency
26+
27+
androidx.startup was introduced in beta.5 but is problematic for unit tests and other cases. beta.6 removes that dependency. More details in the [network connectivity documentation](https://deploy-preview-5759--apollo-android-docs.netlify.app/advanced/network-connectivity).
28+
29+
## WasmJS support for apollo-adapter
30+
31+
You can see Wasm in action at https://wasm.confetti-app.dev/
32+
33+
Many thanks to @joreilly, @ychescale9 and @japhib for their contributions to this release 💙!
34+
35+
## 👷‍ All changes
36+
* [normalized-cache]: use a single SQL transaction when using MemoryCache chaining (#5840)
37+
* [compiler] expose apollo-ast as an api dependency (#5838)
38+
* [compiler] Rename `Plugin` to `ApolloCompilerPlugin` and add error message for bad configurations (#5821)
39+
* [IJ Plugin] Fix pulling file from device not working on AS Koala (#5822)
40+
* [compiler] Add `@ApolloEnumConstructor` and make enum as sealed class Unknown constructor opt-in (#5813)
41+
* [runtime] Move ApolloParseException to ApolloNetworkException (#5816)
42+
* [normalized-cache] Let isFromCache be about the ApolloResponse (#5805)
43+
* [compiler] Add DocumentTransform API (#5809)
44+
* [idling-resource] Deprecate ApolloIdlingResource (#5817, #5764)
45+
* [runtime] Share the default OkHttpBuilder (#5811)
46+
* [adapters] Support Kotlin/Wasm for apollo-adapters (#5803)
47+
* [all] Bump Kotlin to 2.0.0-RC1 (#5802)
48+
* [Codegen] Add CompiledArgumentDefinition (#5797, #5837)
49+
* [runtime] Merge experimental WebSocketNetworkTransport in apollo-runtime (#5790)
50+
* [normalized-cache] Cache pagination: add FieldNameGenerator and EmbeddedFieldsProvider (#5772)
51+
* [runtime] Support configuring `ApolloClient` with lazily initialized `Call.Factory`. (#5784)
52+
* [runtime] fix ApolloClient.Builder.okHttpClient() returns null instead of this (#5778)
53+
* [normalized-cache] Fix variable coercion in lists. Absent variables are coerced to null (#5773)
54+
* [IJ Plugin] Fix an NPE (#5770)
55+
* [runtime] Simplify ApolloCall (#5765)
56+
* [runtime] remove `androidx.startup` dependency (#5761, #5720)
57+
* [compiler] Bump kotlin_labs definitions to v0.3 (#5762)
58+
* [Pagination] Support nodes in Connection types (#5754)
59+
* [compiler] Directive validation is now enforced by default (#5758)
60+
* [cache] Make ApolloStore.publish() suspend (#5755)
61+
* [runtime] Change the dispatcher earlier in the chain (#4319)
62+
* [IJ Plugin] Add an advanced setting to include generated code references in GraphQL "Go To Declaration" (#5743)
63+
* [IJ Plugin] Fix presentation of Kotlin elements when navigating to them from GraphQL (#5739)
64+
* [IJ Plugin] Consider all Gradle projects recursively (#5734)
65+
* [runtime] Deprecate ApolloClient.Builder.addInterceptors() (#5733)
66+
* [all] use jdk-release (#5731)
67+
* [http-cache] Ignore IOException when calling ApolloHttpCache.remove (#5729)
68+
* [IJ plugin] Bump platformVersion and pluginSinceBuild from 232 to 233 (#5726)
69+
* [runtime] add ApolloClient.failFastIfOffline (#5725)
70+
* [all] Introduce "filesystem" sourceSet and use okio 3.9.0 (#5719)
71+
* [runtime] Do not use Ktor in Js HttpEngine, use fetch directly instead (#5702)
72+
473
# Version 3.8.3
574

675
_2024-03-20_

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ This library is designed primarily with Android in mind, but you can use it in a
3434
* Auto Persisted Queries
3535
* Query batching
3636
* File uploads
37-
* Espresso IdlingResource
3837
* Fake models for tests
3938
* AppSync and graphql-ws websockets
4039
* GraphQL AST parser

docs/source/advanced/compiler-plugins.mdx

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ Next create your plugin in a `src/main/kotlin/mypackage/MyPlugin` file:
3737
package mypackage
3838

3939
import com.apollographql.apollo3.compiler.OperationOutputGenerator
40-
import com.apollographql.apollo3.compiler.Plugin
40+
import com.apollographql.apollo3.compiler.ApolloCompilerPlugin
4141
import com.apollographql.apollo3.compiler.operationoutput.OperationDescriptor
4242
import com.apollographql.apollo3.compiler.operationoutput.OperationId
4343

44-
class MyPlugin: Plugin {
44+
class MyPlugin: ApolloCompilerPlugin {
4545
override fun operationIds(descriptors: List<OperationDescriptor>): List<OperationId> {
4646
// This assumes the returned ids are in the same order as the descriptors
4747
return registerOperations(descriptors).withIndex().map { OperationId(it.value, descriptors[it.index].name) }
@@ -56,15 +56,15 @@ class MyPlugin: Plugin {
5656
}
5757
```
5858

59-
Make your plugin discoverable by [ServiceLoader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) using a resource in `src/main/resources/META-INF/services/com.apollographql.apollo3.compiler.Plugin`. This file contains the fully qualified name of your plugin:
59+
Make your plugin discoverable by [ServiceLoader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) using a resource in `src/main/resources/META-INF/services/com.apollographql.apollo3.compiler.ApolloCompilerPlugin`. This file contains the fully qualified name of your plugin:
6060

6161
```
6262
mypackage.MyPlugin
6363
```
6464

65-
> [!NOTE]
66-
> The name of the resource file is important. It must be `com.apollographql.apollo3.compiler.Plugin` and be in the `META-INF/services` folder. This is how `ServiceLoader` looks up `Plugins` at runtime.
67-
65+
<Note>
66+
The name of the resource file is important. It must be `com.apollographql.apollo3.compiler.ApolloCompilerPlugin` and be in the `META-INF/services` folder. This is how `ServiceLoader` looks up plugins at runtime.
67+
</Note>
6868
# Adding a plugin to the Apollo compiler classpath
6969

7070
Use the `Service.plugin()` Gradle method to add the plugin to the Apollo compiler classpath:
@@ -88,8 +88,37 @@ apollo {
8888

8989
The plugin code will now be invoked the next time the compiler is invoked.
9090

91+
# Passing arguments to your Apollo compiler plugin
92+
93+
Because the compiler plugin runs in an isolated classpath, you can't use classes or data from your main build logic classpath.
94+
95+
In order to pass build-time arguments to your Apollo compiler plugin, you can use code generation and tools like [gradle-buildconfig-plugin](https://github.com/gmazzo/gradle-buildconfig-plugin):
96+
97+
```kotlin
98+
// my-plugin/build.gradle.kts
99+
plugins {
100+
id("com.github.gmazzo.buildconfig")
101+
}
102+
103+
buildConfig {
104+
useKotlinOutput()
105+
packageName("com.example.myplugin")
106+
107+
buildConfigField("arg1", arg1Value)
108+
buildConfigField("arg2", arg2Value)
109+
// etc...
110+
}
111+
```
112+
113+
# Limitations
114+
115+
Because codegen is run in a separate classloader when using compiler plugins, it's not possible to use `packageNameGenerator`, `operationIdGenerator` or `operationOutputGenerator` at the same time as compiler plugins. If you want to use them, you'll have to:
116+
117+
* use `ApolloCompilerPlugin.layout()` instead of `packageNameGenerator`
118+
* use `ApolloCompilerPlugin.operationIds()` instead of `operationIdGenerator` and `operationOutputGenerator`
119+
91120
# Other references
92121

93-
For other plugin APIs like layout, IR, JavaPoet and KotlinPoet transforms, check out the [Plugin API docs](https://www.apollographql.com/docs/kotlin/kdoc/apollo-compiler/com.apollographql.apollo3.compiler/-plugin/index.html)
122+
For other plugin APIs like layout, IR, JavaPoet and KotlinPoet transforms, check out the [ApolloCompilerPlugin API docs](https://www.apollographql.com/docs/kotlin/kdoc/apollo-compiler/com.apollographql.apollo3.compiler/-apollocompilerplugin/index.html)
94123

95124
For more examples, check out the [integration-tests](https://github.com/apollographql/apollo-kotlin/tree/main/tests/compiler-plugins).
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
---
2+
title: Experimental websockets
3+
---
4+
5+
> ⚠️ **Experimental websockets APIs are [experimental](https://www.apollographql.com/docs/resources/release-stages/#experimental-features) in Apollo Kotlin.** If you have feedback on them, please let us know via [GitHub issues](https://github.com/apollographql/apollo-kotlin/issues/new?assignees=&labels=Type%3A+Bug&template=bug_report.md&title=[Defer%20Support]) or in the [Kotlin Slack community](https://slack.kotl.in/).
6+
7+
8+
Historically, WebSockets have been one of the most complex and error-prone parts of Apollo Kotlin because:
9+
10+
1. The WebSocket transport protocol has no official specification and different implementations have different behaviours.
11+
2. WebSockets are stateful and making them work using the old Kotlin native memory model was challenging.
12+
3. Because WebSockets are long-lived connections, they are more exposed to errors and knowing when (or if) to retry is hard.
13+
4. Not all subscriptions happen on WebSockets. Some use [HTTP multipart](https://www.apollographql.com/docs/router/executing-operations/subscription-multipart-protocol/) for an example.
14+
15+
Starting with 4.0.0, Apollo Kotlin provides a new `com.apollographql.apollo3.network.websocket` package containing new `WebSocketNetworkTransport` and `WebSocketEngine` implementations (instead of `com.apollographql.apollo3.network.ws` for the current implementations).
16+
17+
The `com.apollographql.apollo3.network.websocket` implementation provides the following:
18+
19+
1. Defaults to the [graphql-ws](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) protocol, which has become the de facto standard. Using the other protocols is still possible but having a main, specified, protocol ensures we can write a good and solid test suite.
20+
2. Does not inherit from the old memory model design, making the code considerably simpler. In particular, `WebSocketEngine` is now event based and no attempt at flow control is done. If you buffers grow too much, your subscription fails.
21+
3. Plays nicely with the ApolloClient `retryOnError` API.
22+
4. Handles different Subscription transports more consistently.
23+
24+
## Status
25+
26+
While they are `@ApolloExperimental`, we believe the new `.websocket` APIS to be more robust than the non-experimental `.ws` ones. They are safe to use in non-lib use cases.
27+
28+
The "experimental" tag is to account for required API breaking changes based on community feedback. Ideally no change is needed.
29+
30+
After a feedback phase, the current `.ws` APIs will become deprecated and the `.websocket` one promoted to stable by removing the `@ApolloExperimental` annotations.
31+
32+
## Migration guide
33+
34+
In simple cases where you did not configure the underlying `WsProtocol` or retry logic, the migration should be about replacing `com.apollographql.apollo3.network.ws` with `com.apollographql.apollo3.network.websocket` everywhere:
35+
36+
```kotlin
37+
// Replace
38+
import com.apollographql.apollo3.network.ws.WebSocketNetworkTransport
39+
import com.apollographql.apollo3.network.ws.WebSocketEngine
40+
// etc...
41+
42+
// With
43+
import com.apollographql.apollo3.network.websocket.WebSocketNetworkTransport
44+
import com.apollographql.apollo3.network.websocket.WebSocketEngine
45+
// etc...
46+
```
47+
48+
Because we can't remove the current APIs just yet, the `ApolloClient.Builder` shortcut APIs are still pointing to the `.ws` implementations. To use the newer `.websocket` implementation, pass a `websocket.WebSocketNetworkTransport` directly:
49+
50+
```kotlin
51+
// Replace
52+
val apolloClient = ApolloClient.Builder()
53+
.serverUrl(serverUrl)
54+
.webSocketServerUrl(webSocketServerUrl)
55+
.webSocketEngine(myWebSocketEngine)
56+
.webSocketIdleTimeoutMillis(10_000)
57+
.build()
58+
59+
// With
60+
import com.apollographql.apollo3.network.websocket.*
61+
62+
// [...]
63+
64+
ApolloClient.Builder()
65+
.serverUrl(serverUrl)
66+
.subscriptionNetworkTransport(
67+
WebSocketNetworkTransport.Builder()
68+
.serverUrl(webSocketServerUrl)
69+
// If you didn't set a WsProtocol before, make sure to include this
70+
.wsProtocol(SubscriptionWsProtocol())
71+
// If you were already using GraphQLWsProtocol, this is now the default
72+
//.wsProtocol(GraphQLWsProtocol())
73+
.webSocketEngine(myWebSocketEngine)
74+
.idleTimeoutMillis(10_000)
75+
.build()
76+
)
77+
.build()
78+
```
79+
80+
To account for non-websocket transports, like [multipart subscriptions](https://www.apollographql.com/docs/router/executing-operations/subscription-multipart-protocol/), the retry is now handled on the `ApolloClient` instead of the `NetworkTransport`.
81+
82+
```kotlin
83+
// Replace
84+
val apolloClient = ApolloClient.Builder()
85+
.subscriptionNetworkTransport(
86+
WebSocketNetworkTransport.Builder()
87+
.serverUrl(url)
88+
.reopenWhen { _, _ ->
89+
delay(1000)
90+
true
91+
}
92+
.build()
93+
)
94+
95+
// With
96+
val apolloClient = ApolloClient.Builder()
97+
.subscriptionNetworkTransport(
98+
WebSocketNetworkTransport.Builder()
99+
.serverUrl(url)
100+
.build()
101+
)
102+
// Only retry subscriptions
103+
.retryOnError { it.operation is Subscription }
104+
```
105+
106+
The above uses the default retry algorithm:
107+
108+
* Wait until the network is available if you configured a [NetworkMonitor](network-connectivity).
109+
* Or use exponential backoff else.
110+
111+
To customize the retry logic more, use `addRetryOnErrorInterceptor`:
112+
113+
```kotlin
114+
val apolloClient = ApolloClient.Builder()
115+
.subscriptionNetworkTransport(
116+
WebSocketNetworkTransport.Builder()
117+
.serverUrl(url)
118+
.build()
119+
)
120+
.addRetryOnErrorInterceptor { apolloRequest, exception, attempt ->
121+
// retry logic here
122+
123+
// return true to retry or false to terminate the Flow
124+
true
125+
}
126+
```
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
title: Monitor your network state to reduce latency (experimental)
3+
---
4+
5+
> ⚠️ **Network Monitor APIs are [experimental](https://www.apollographql.com/docs/resources/release-stages/#experimental-features) in Apollo Kotlin.** If you have feedback on them, please let us know via [GitHub issues](https://github.com/apollographql/apollo-kotlin/issues/new?assignees=&labels=Type%3A+Bug&template=bug_report.md&title=[Defer%20Support]) or in the [Kotlin Slack community](https://slack.kotl.in/).
6+
>
7+
Android and Apple targets provide APIs to monitor the network state of your device:
8+
9+
- [ConnectivityManager](https://developer.android.com/training/monitoring-device-state/) on Android targets.
10+
- [NWPathMonitor](https://developer.apple.com/documentation/network/nwpathmonitor) on Apple targets.
11+
12+
You can configure your [ApolloClient](https://www.apollographql.com/docs/kotlin/kdoc/apollo-runtime/com.apollographql.apollo3/-apollo-client/index.html) to use these APIs to improve latency of your requests using the `NetworkMonitor` API:
13+
14+
```kotlin
15+
// androidMain
16+
val networkMonitor = NetworkMonitor(context)
17+
18+
// appleMain
19+
val networkMonitor = NetworkMonitor()
20+
21+
// commonMain
22+
val apolloClient = ApolloClient.Builder()
23+
.serverUrl("https://example.com/graphql")
24+
.networkMonitor(networkMonitor)
25+
.build()
26+
```
27+
28+
### `failFastIfOffline`
29+
30+
When a `NetworkMonitor` is configured, you can use `failFastIfOffline` to avoid trying out request if the device is offline:
31+
32+
```kotlin
33+
// Opt-in `failFastIfOffline` on all queries
34+
val apolloClient = ApolloClient.Builder()
35+
.serverUrl("https://example.com/graphql")
36+
.failFastIfOffline(true)
37+
.build()
38+
39+
val response = apolloClient.query(myQuery).execute()
40+
println(response.exception?.message)
41+
// "The device is offline"
42+
43+
// Opt-out `failFastIfOffline` on a single query
44+
val response = apolloClient.query(myQuery).failFastIfOffline(false).execute()
45+
```
46+
47+
### `retryOnError`
48+
49+
When a `NetworkMonitor` is configured, `retryOnError` uses `NetworkMonitor.waitForNetwork()` instead of the default exponential backoff algorithm in order to reconnect faster when connectivity is back.
50+
51+
### customizing the retry algorithm
52+
53+
You can customize the retry algorithm further by defining you won interceptor. Make sure to:
54+
55+
- add your interceptor last, so that it wraps the network call and doesn't get cache misses or any other errors that may be emitted by upstream interceptors.
56+
- call `retryOnError(false)` when forwarding the request downstream so that the retry is not made twice.
57+
58+
```kotlin
59+
internal class MyRetryInterceptor(private val networkMonitor: NetworkMonitor?): ApolloInterceptor {
60+
override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
61+
// Disable Apollo's built-in retry mechanism
62+
val newRequest = request.newBuilder().retryOnError(false).build()
63+
return chain.proceed(newRequest)
64+
.onEach {
65+
if (it.exception != null && it.exception.shouldRetry()) {
66+
throw RetryException
67+
}
68+
}.retryWhen { cause, _->
69+
if (cause is RetryException) {
70+
// Add your logic here
71+
true
72+
} else {
73+
// Programming error, re-throw it
74+
false
75+
}
76+
}
77+
}
78+
}
79+
```
80+

docs/source/config.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@
7171
},
7272
"Advanced": {
7373
"Uploading files": "/advanced/upload",
74+
"Monitoring your network (experimental)": "/advanced/network-connectivity",
7475
"Handling nullability (experimental)": "/advanced/nullability",
76+
"Experimental WebSocketNetworkTransport (experimental)": "/advanced/experimental-websockets",
7577
"Using aliases": "/advanced/using-aliases",
7678
"Using with Java": "/advanced/java",
7779
"Kotlin native": "/advanced/kotlin-native",

docs/source/index.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ This library is designed primarily with Android in mind, but you can use it in a
2424
* Auto Persisted Queries
2525
* Query batching
2626
* File uploads
27-
* Espresso IdlingResource
2827
* Fake models for tests
2928
* AppSync and graphql-ws websockets
3029
* GraphQL AST parser

0 commit comments

Comments
 (0)