2022-01-13
This release contains a handful of bug fixes and improvements.
- Kotlin codegen: automatically escape 'companion' fields (#4630)
- Runtime: fix a case where APQ + cache could be misconfigured and throw an exception (#4628)
- Update KTOR to 2.2.2 (#4627)
- Allow having an empty last part in multipart (#4598)
- Add data builders for unknown interface and union types (v3) (#4613)
- Http cache: don't access the disk from the main thread in error case (#4606)
2022-12-20
This release contains a handful of bug fixes and improvements, and also discontinues the legacy JS artifacts.
Many thanks to @StefanChmielewski and @chao2zhang for contributing to the project! 🧡
Historically, Kotlin Multiplatform has had 2 formats of JS artifacts: Legacy and IR, and Apollo Kotlin has been publishing both. However, the Legacy format is about to be deprecated with Kotlin 1.8 and moreover we've seen issues when using the Legacy artifact in the browser. That is why starting with this release, only the IR artifacts will be published. Please reach out if this causes any issue in your project.
- Add
GraphQLWsProtocol.Factory.webSocketPayloadComposer
(#4589) - Escape "Companion" in enum value names (#4558)
- Un-break Gradle configuration cache in multi module cases (#4564)
- Move computing the
alwaysGenerateTypesMatching
to execution time (#4578) - Log deprecation warning instead of printing (#4561)
- Escape spaces when url encoding, for Apple (#4567)
- Fix providing linker flags to the Kotlin compiler with KGP 1.8 (#4573)
- Use
service {}
in all messages/docs (#4572) - Print all duplicate types at once (#4571)
- Fix JavaPoet formatting (#4584)
- Don't publish legacy js artifacts (#4591)
2022-12-05
This patch release brings a few fixes.
Many thanks to @davidshepherd7, @chao2zhang, @agrosner, @MyDogTom, @doucheng, @sam43 and @vincentjames501, for helping improve the library! 🙏
Apollo Kotlin can be configured to work with multiple services and have the package name, schema files location, and other options specified for each of them. When using a single service however it is possible to omit the service
block and set the options directly in the apollo
block - in that case, a default service named service
is automatically defined.
While this saves a few lines, it relies on Gradle afterEvaluate {}
block that makes the execution of the plugin less predictable and more subject to race conditions with other plugins (see here for an example).
What's more, as we move more logic to build time, the name of the service is going to be used more and more in generated code. Since explicit is better than implicit, mandating that service name sounds a good thing to do and a warning is now printed if you do not define your service name.
To remove the warning, embed the options into a service
block:
apollo {
+ service("service") {
packageName.set("com.example")
// ...
+ }
}
- Improve "duplicate type" message by using the full path of the module (#4527)
- Fix using apollo2 and apollo3 Gradle plugins at the same time (#4528)
- Add a warning when using the default service (#4532)
- Fix Java codegen in synthetic fields when using optionals (#4533)
- Make
canBeBatched
andhttpHeaders
orthogonal (#4534) - Fix item wrongly removed from http cache when error in subscriptions (#4537)
- Do not throw on graphql-ws errors and instead return the errors in ApolloResponse (#4540)
- graphql-ws: send pong while waiting for connection_ack (#4555)
2022-11-18
A patch release with a few fixes.
- 👷Data Builders: make DefaultFakeResolver open and stateless (#4468)
- Kotlin 1.7.21 (#4511)
- Introduce HttpFetchPolicyContext (#4509)
- Fix usedCoordinates on interfaces (#4506)
Many thanks to @Holoceo, @juliagarrigos, @davidshepherd7 and @eduardb for the feedbacks 💙
2022-11-18
A patch release to fix an issue where the ApolloCall could end up in a bad state. Many thanks to @WilliamsDHI for diving into this 💙!
- update terminate and responseCallback methods to return Optional.absent() in IDLE/TERMINATED state (#4383)
2022-11-08
This version adds multiple new low level features. These new features expose a lot of API surface, and they will probably stay experimental until 4.0. Feedback is always very welcome.
Compiler hooks allow you to tweak the generated models by exposing the underlying JavaPoet/KotlinPoet structures. You can use it for an example to:
- Add a 'null' default value to model arguments (source)
- Introduce a common interface for all models that implement
__typename
(source) - Add a prefix to generated models (source)
- Any other thing you can think of
To do so, make sure to use the "external" version of the plugin:
plugins {
// Note: using the external plugin here to be able to reference KotlinPoet classes
id("com.apollographql.apollo3.external")
}
And then register your hook to the plugin:
apollo {
service("defaultnullvalues") {
packageName.set("hooks.defaultnullvalues")
compilerKotlinHooks.set(listOf(DefaultNullValuesHooks()))
}
}
By default, Apollo Kotlin models fragments with synthetic nullable fields. If you have a lot of fragments, checking these fields requires using if
statements. For an example, with a query like so:
{
animal {
species
... on WarmBlooded {
temperature
}
... on Pet {
name
}
... on Cat {
mustaches
}
}
}
you can access data like so:
if (animal.onWarmBlooded != null) {
// Cannot smart cast because of https://youtrack.jetbrains.com/issue/KT-8819/
println(animal.onWarmBlooded!!.temperature)
}
if (animal.onPet != null) {
println(animal.onPet!!.name)
}
if (animal.onCat != null) {
println(animal.onCat!!.mustaches)
}
Some of the combinations could be impossible. Maybe all the pets in your schema are warm blooded. Or maybe only cat is a warm blooded. To model this better and work around KT-8819, @chalermpong implemented a new codegen that adds a base sealed interface. Different implementations contain the same synthetic fragment fields as in the default codegen except that their nullability will be updated depending the branch:
when (animal) {
is WarmBloodedPetAnimal -> {
println(animal.onWarmBlooded!!.temperature)
println(animal.onPet!!.name)
}
is PetAnimal -> {
// Some pet that is not warm blooded, e.g. a Turtle maybe?
println(animal.onPet!!.name)
}
is OtherAnimal -> {
println(animal.species)
}
// Note how there is no branch for Cat because it's a WarmBloodedPetAnimal
// Also no branch for WarmBlooded animal because all pets in this (fictional) sample schema are WarmBlooded. This could be different in another schema
}
To try it out, add this to your Gradle scripts:
apollo {
codegenModels.set("experimental_operationBasedWithInterfaces")
}
Many many thanks to @chalermpong for diving into this 💙
By default, Apollo Kotlin only generates the types that are used in your queries. This is important because some schemas are really big and generating all the types would waste a lot of CPU cycles. In multi-modules scenarios, the codegen only knows about types that are used locally in that module. If two sibling modules use the same type and that type is not used upstream, that could lead to errors like this:
duplicate Type '$Foo' generated in modules: feature1, feature2
Use 'alwaysGenerateTypesMatching' in a parent module to generate the type only once
This version introduces new options to detect the used types automatically. It does so by doing a first pass at the GraphQL queries to determine the used type. Upstream modules can use the results of that computation without creating a circular dependency. To set up auto detection of used coordinates, configure your schema module to get the used coordinates from the feature module using the apolloUsedCoordinates
configuration:
// schema/build.gradle.kts
dependencies {
implementation("com.apollographql.apollo3:apollo-runtime")
// Get the used coordinates from your feature module
apolloUsedCoordinates(project(":feature"))
// If you have several, add several dependencies
apolloUsedCoordinates(project(":feature-2"))
}
apollo {
service("my-api") {
packageName.set("com.example.schema")
generateApolloMetadata.set(true)
}
}
And in each of your feature module, configure the apolloSchema
dependency:
// feature/build.gradle.kts
dependencies {
implementation("com.apollographql.apollo3:apollo-runtime")
// Depend on the codegen from the schema
apolloMetadata(project(":schema"))
// But also from the schema so as not to create a circular dependency
apolloSchema(project(":schema"))
}
apollo {
// The service names must match
service("my-api") {
packageName.set("com.example.feature")
}
}
- Add usedCoordinates configuration and use it to automatically compute the used coordinates (#4494)
- Compiler hooks (#4474)
- 🐘 Use
registerJavaGeneratingTask
, fixes lint trying to scan generated sources (#4486) - Rename
generateModelBuilder
togenerateModelBuilders
and add test (#4476) - Data builders: only generate used fields (#4472)
- Only generate used types when generateSchema is true (#4471)
- Suppress deprecation warnings, and opt-in in generated code (#4470)
- Multi-module: fail if inconsistent generateDataBuilders parameters (#4462)
- Add a decapitalizeFields option (#4454)
- Pass protocols to WebSocket constructor in
JSWebSocketEngine
(#4445) - SQLNormalized cache: implement selectAll, fixes calling dump() (#4437)
- Java codegen: Nullability annotations on generics (#4419)
- Java codegen: nullability annotations (#4415)
- OperationBasedWithInterfaces (#4370)
- Connect test sourceSet only when testBuilders are enabled (#4412)
- Java codegen: add support for Optional or nullable fields (#4411)
- Add generatePrimitiveTypes option to Java codegen (#4407)
- Add classesForEnumsMatching codegen option to generate enums as Java enums (#4404)
- Fix data builders + multi module (#4402)
- Relocate the plugin without obfuscating it (#4376)
2022-10-05
A patch version to fix compatibility with Kotlin 1.7.20 and another fix when calling ApolloStore.dump()
with the SQL normalized cache.
- Add support for KGP 1.7.20 (#4439)
- Fix SQLNormalizedCache.dump() (#4437)
2022-10-03
A patch version to fix an issue with data builder and multi modules. Many thanks to @agrosner and @eduardb for catching this.
- Fix data builders in multi-modules scenarios (#4402)
2022-09-08
This version brings initial support for @defer
as well as data builders.
Many thanks to @engdorm, @Goooler, @pt2121 and @StylianosGakis for their contributions!
Starting with this release, Apollo Kotlin is built with Kotlin 1.7.10. This doesn't impact Android and JVM projects (the minimum supported version of Kotlin continues to be 1.5) but if you are on a project using Native or JS, you will need to update the Kotlin version to 1.7.0+.
@defer
support is experimental in the Kotlin Client and currently a Stage 2 GraphQL specification draft to allow incremental delivery of response payloads.
@defer
allows you to specify a fragment as deferrable, meaning it can be omitted in the initial response and delivered as a subsequent payload. This improves latency for all fields that are not in that fragment. You can read more about @defer
in the RFC and contribute/ask question in the @defer
working group.
Apollo Kotlin supports @defer
by default and will deliver the successive payloads as Flow
items. Given the below query:
query GetComputer {
computer {
__typename
id
...ComputerFields @defer
}
}
fragment ComputerFields on Computer {
cpu
year
screen {
resolution
}
}
And the following server payloads:
payload 1:
{
"data": {
"computer": {
"__typename": "Computer",
"id": "Computer1"
}
},
"hasNext": true
}
payload 2:
{
"incremental": [
{
"data": {
"cpu": "386",
"year": 1993,
"screen": {
"resolution": "640x480"
}
},
"path": [
"computer",
]
}
],
"hasNext": true
}
You can listen to payloads by using toFlow()
:
apolloClient.query(query).toFlow().collectIndexed { index, response ->
// This will be called twice
if (index == 0) {
// First time without the fragment
assertNull(response.data?.computer?.computerFields)
} else if (index == 1) {
// Second time with the fragment
assertNotNull(response.data?.computer?.computerFields)
}
}
You can read more about it in the documentation.
As always, feedback is very welcome. Let us know what you think of the feature by either opening an issue on our GitHub repo , joining the community or stopping by our channel in the KotlinLang Slack(get your invite here).
Apollo Kotlin 3.0 introduced test builders. While they are working, they have several limitations. The main one was that being response based, they could generate a lot of code. Also, they required passing custom scalars using their Json encoding, which is cumbersome.
The data builders are a simpler version of the test builders that generate builders based on schema types. This means most of the generated code is shared between all your implementations except for a top level Data {}
function in each of your operation:
// Replace
val data = GetHeroQuery.Data {
hero = humanHero {
name = "Luke"
}
}
// With
val data = GetHeroQuery.Data {
hero = buildHuman {
name = "Luke"
}
}
- fix registering Java scalars. Many thanks @parker for catching this. (#4375)
- Data builders (#4359, #4338, #4331, #4330, #4328, #4323, #4321)
- Add a flag to disable fieldsCanMerge validation on disjoint types (#4342)
- Re-introduce @defer and use new payload format (#4351)
- Multiplatform: add enableCompatibilityMetadataVariant flag (#4329)
- Remove an unnecessary
file.source().buffer()
(#4326) - Always use String for defaultValue in introspection Json (#4315)
- Update Kotlin dependency to 1.7.10 (#4314)
- Remove schema and AST from the IR (#4303)
2022-08-01
With this release, Apollo Kotlin now uses Kotlin Native's new memory model. It also contains a number of other improvements and bug fixes.
Many thanks to @glureau
for carefully adding new watch targets ⌚💙.
Apollo Kotlin is now requiring applications to use the new memory manager, a.k.a. new memory model. Thanks to this change, the restriction that operations had to be executed from the main thread on Apple targets is now removed. You can also use kotlinx.coroutines.test.runTest. Last but not least, benchmarks seem to indicate that performance is better under the new memory manager!
This directive was introduced in v3.3.1 to allow overriding the name of enum values in the generated code. It has now been extended to allow configuring the generated name of Interfaces, Enums, Unions, Scalars and Input objects. This can be used to make the generated code nicer to use, or to avoid name clashes with Kotlin types (e.g. Long
) in Kotlin Native.
From now on, you no longer need to specify explicitly the versions of Apollo dependencies: if omitted, the same version as the Apollo Gradle plugin will be used. This should facilitate upgrades and avoid potential mistakes:
plugins {
plugins {
id("org.jetbrains.kotlin.jvm").version("1.7.10")
id("com.apollographql.apollo3").version("3.5.0")
}
dependencies {
// Replace this
// implementation("com.apollographql.apollo3:apollo-runtime:3.5.0")
// with
implementation("com.apollographql.apollo3:apollo-runtime")
}
}
With the new memory model, Apollo's specific runTest
method from apollo-testing-support
is no longer useful and has been deprecated. If you were using it, you should now be able to use Kotlin's runTest
instead, or simply runBlocking
.
If you have an enum with a type
value, this value used to name clash with the generated type
property. This version now detects this case automatically and escapes type
to type_
. If you had previously used @targetName
to workaround this issue, you can now remove it to avoid it being escaped twice:
# Remove this
extend enum SomeEnum {
type @targetName(name: "type_")
}
- Support watchosArm32 (#4260)
- Support @targetName on Interfaces, Enums, Unions, Scalars and Input objects (#4243)
- 🐘 support lazy APIs for newer AGP versions (#4264)
- Pagination: add connectionFields argument to @typePolicy (#4265)
- 🐘 Get the dependencies version from the plugin automagically (#4279)
- Automatically escape
type
in enum values (#4295) - Fix inferred variables in both nullable and non-nullable locations (#4306)
- Native: assume New Memory Manager (#4287)
- Use internal runTest in all tests (#4292)
2022-07-11
This release contains a few important bug fixes (#4214, #4224, #4247, #4256), makes it possible to compile with Gradle
7.4 and apollo-gradle-plugin
(#4218).
It also introduces -incubating
cache artifacts.
Many thanks to @ArjanSM, @zebehringer, @mm-kk-experiments, @mune0903, @stengvac, @elenigen, @shamsidinb, @StylianosGakis for the awesome contributions 😃!
This version introduces the below artifacts:
apollo-normalized-cache-incubating
apollo-normalized-cache-api-incubating
apollo-normalized-cache-sqlite-incubating
These artifacts introduce new APIs to work with cache expiration and pagination (as well as other cache improvements in the future).
These artifacts have no backward compatibility guarantees and most likely have worse performance than the non-incubating artifacts. Documentation will be added once the API stabilize. In the short term, the best place to look for examples are the integration tests:
Note: The experimental withDates: Boolean
argument was introduced in 3.3.1 in the regular artifacts and is removed
as part of this release. Use the -incubating
artifacts to use it.
- add TrimmableNormalizedCacheFactory (#4239)
- 🚧 remove withDates (#4257)
- 🗄️ Chunk parameters in large responses (#4256)
- Fix for improper handling of JsonNumber in BufferedSinkJsonWriter (#4247)
- Incubating modules for the next gen cache (#4241)
- Pagination: fixes in FieldRecordMerger and MemoryCache (#4237)
- make it possible to reuse a File Upload (#4228)
- Persist Record arguments/metadata with the SQL Json backend (#4211)
- requestedDispatcher -> dispatcher (#4220)
- Fix test errors were emitted outside the Flow (#4224)
- Make it possible to compile with Kotlin 1.5 and apollo-gradle-plugin (#4218)
- 🏖️ Relax MapJsonReader endObject, fixes reading inline + named fragments with compat models (#4214)
- Introduce RecordMerger (#4197)
- Add @typePolicy(embeddedFields: String! = "") (#4196)
2022-06-17
This is a hot fix release that fixes a crash that could happen in the codegen when using responseBased
codegen in a
multimodule setup. It also includes a fix for incorrect generated code when using certain reserved names in enum values.
- Update to KotlinPoet
1.12.0
, fixes generating enum values whose name clashes with other symbols (#4034) - Update to Ktor 2 (#4190)
- Fix NPE in checkCapitalizedFields (#4201)
2022-06-13
This release introduces @typePolicy
on interface/enums, improvements on subscription error handling, and on Test
Builders. It also contains a number of other improvements and bug fixes!
The @typePolicy
directive can now be
declared on interfaces and unions. Thank you @bubba for the contribution!
An issue where websocketReopenWhen
was not called in some cases was fixed. Also, this release
introduces SubscriptionOperationException
. A SubscriptionOperationException
will be thrown instead of the more
generic ApolloNetworkError
if a subscription fails due to a specific operation error.
- A DslMarker was added to improve usage with nested builders (#4089)
- When calling a builder, but not assigning it to a field, an error is now thrown, preventing mistakes (#4122)
- The error message displayed when
__typename
is missing was made clearer (#4146) - Fix: use
rawValue
instead ofname
for enums (#4121)
ApolloClient
now implements okio.Closable
so you can
use use
with it. Thanks @yogurtearl for this
contribution!
If an enum value name is clashing with a reserved name (e.g. type
) you can now use this directive to instruct the
codeGen to use the specified name for the value instead. This directive is experimental for now.
As we add more client directives, the risk of nameclash with existing schema directives increases. If this happens, you
can now import Apollo client directives using @link
:
# extra.graphqls
extend schema @link(url: "https://specs.apollo.dev/kotlin_labs/v0.1")
This adds a @kotlin_labs__
prefix to all Apollo client directives:
{
hero {
name @kotlin_labs__nonnull
}
}
It is no longer necessary to pass a Context
when initializing the SqlNormalizedCacheFactory
on Android. A Context
is automatically provided, via App Startup.
// Before
val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory(context, "apollo.db")
// After
val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory("apollo.db")
This release starts tracking the public API of all modules, including MockServer. Even if the API remains experimental, we'll try to keep the number of breaking changes low in the future.
- 🐘 publish
apollo-gradle-plugin-external
(#4078) - publish the R8 mapping file along the relocated jar (#4085)
- Fix test directories not cleared (#4083)
- Do not use 'header' as a enum value name as it breaks the Kotlin compiler (#4086)
- 🧪 @experimental support (#4091)
- @experimental -> @requiresOptIn (#4175)
- Do not buffer entire body in Http Cache (#4076)
- ⬇️ add SchemaDownloader.download() (#4088)
- add DslMarker for test builders (#4089)
- MockServer: make MockResponse.body a Flow (#4096)
- Issue-3909: add ApolloResponse cache headers (#4102)
- Use rawValue instead of name for enums in test builders (#4121)
- 💧 first drop for a SQLite backend that stores when each field was last updated (#4104)
- Add Operation.Data.toJsonString() convenience function for the jvm (#4124)
- Check for unassigned fields in Test Builders (#4122)
- Add non-breaking spaces after 'return' (#4127)
- 🧶 Use a getter instead of a const val OPERATION_QUERY (#4130)
- Uploads should be read only once even when logging (#4125)
- Keep the 'interfaces' field on the JSON introspection (#4129)
- Allow @typePolicy directive on interfaces and unions (#4131)
- typePolicy on interface: exclude empty keyfields (#4140)
- Sort the type names in the list so the code gen is deterministic. (#4138)
- Use okio.Closable.close instead of dispose on ApolloClient (#4142)
- Parse the interface's interface field in introspection (#4143)
- TestBuilders: improve error message when __typename is missing (#4146)
- Do not bypass websocketReopenWhen {} (#4147)
- SDLWriter: join implemented interfaces with & instead of space (#4151)
- Escape "type" in enums and sealed classes (#4144)
- 🧰 introduce apollo-tooling and apollo-cli (#4153)
- Fix incorrect content-length in MockServer (#4162)
- Allow capitalized field names if flattenModels is true (#4154)
- 🏷️ Allow namespacing and renaming of directives (#4174)
Many thanks to @tajchert, @asimonigh, @hrach, @ArjanSM, @yshrsmz, @ephemient, @bubba, @eboudrant and @yogurtearl for contributing to this release! 🙏
2022-05-04
This is the first release with HMPP support. If you're using multiplatform, updating to Kotlin 1.6.21 is strongly encouraged.
This release also brings WebSocket related improvements and other fixes!
When using Apollo Kotlin on a multiplatform project, this release is compatible with the hierarchical project structure, which makes it easier to share common code among several targets. Using HMPP in your project also fixes some issues when compiling Kotlin metadata. See apollographql#4019 and https://youtrack.jetbrains.com/issue/KT-51970/ for more details.
✋ Note: If you're using multiplatform, we strongly encourage updating to Kotlin 1.6.21. If that is not an option, you might have issues resolving dependencies. More infos in this issue.
This new method can be used in conjunction
with reopenWhen
to force a reconnection to the server. This could be useful for instance when needing to pass new auth tokens in the
headers. If you were using subscriptionManager.reconnect()
in 2.x, closeConnection
is a simple way to achieve the
same behaviour.
With GraphQLWsProtocol
, if you need to pass parameters to the connection payload, previously you would pass them as a
static map to the builder. With this change you can now pass a lambda providing them as needed. This facilitates passing
fresh auth tokens when connecting.
You can now use the --insecure
flag when downloading a schema
with downloadApolloSchema
, to bypass the certificate check, which can be useful if a server is configured with a self-signed certificate for
instance.
- Add WebSocketNetworkTransport.closeConnection (#4049)
- Made connectionPayload as suspend function in GraphQLWsProtocol (#4043)
- ⚡ Ignore unknown websocket messages (#4066)
- Kotlin 1.6.21 & HMPP (#4033)
- Provide a Content-Length when using Upload (#4056)
- ☁️ add HttpRequest.newBuilder(url, method) (#4038)
- Escape enum constants (#4035)
- Fix the Moshi adapter used for OperationOutput. Moshi cannot get the type parameters from the typealias automagically (#4022)
- Add insecure option to download schema (#4021)
- Try to reduce allocations in MapJsonReader (#3935)
- 🔒 Deprecate BearerTokenInterceptor and provide tests and docs instead (#4068)
Many thanks to @CureleaAndrei and @kdk96 for contributing to this release! 🙏
BearerTokenInterceptor
was provided as an example but is too simple for most use cases, and has therefore been deprecated in this release. This page provides more details about authentication.- The previous ways of passing parameters to the connection payload with
GraphQLWsProtocol
has been deprecated (see above).
2022-04-11
A maintenance release to fix the addJvmOverloads
option added in 3.2.0 as well as other fixes. If you're using APQs,
the mutations are now always send using POST
.
See #4006 for details and a way to
override the behaviour if you really need to.
Many thanks to @benedict-lim, @olivierg13, @konomae and @sproctor for their contributions 💙
- Use a constant for JvmOverloads to avoid a crash due to relocation (#4008)
- Always use POST for Mutations in APQs (Auto Persisted Queries) (#4011)
- Add configurable headers to WebSocketNetworkTransport (#3995)
- Handle SqlNormalizedCache merge APIs Exceptions with ApolloExceptionHandler (#4002)
- Add adapter for java.time.OffsetDateTime (#4007)
- ⏰ Add tests for date adapters (#3999)
- Fix wrong LocalDate and LocalDateTime formats in JavaTimeAdapters (#3997)
2022-04-05
This release introduces a few improvements and bug fixes.
When observing the cache with watch
, the behavior was to not emit cache misses at all, which may not desirable in
certain cases. With this new option, you can now choose to emit them: in that case responses will be emitted with a
null data
.
This can be used like so:
apolloClient.query(query)
.fetchPolicy(FetchPolicy.CacheOnly)
.emitCacheMisses(true)
.watch()
.collect { response ->
// response.data will be null in case of cache misses
}
This is also closer to the behavior that was in place in v2. Many thanks to @mateuszkwiecinski for the insights and raising the issue!
⚙️ [breaking] Allow configuration of frame types used in SubscriptionWsProtocol
and default to Text (#3992)
When using subscriptions over WebSockets with SubscriptionWsProtocol
(the default), the frames were sent in the binary
format. It was reported that this was not compatible with certain servers (DGS
, graphql-java-kickstart) that are expecting text
frames. This is now fixed and the default is to send text frames.
⚠️ This may be a breaking change if your server expects binary frames only!
If that is the case, you can use the new frameType
option to configure the frame type to be sent:
client = ApolloClient.Builder()
.webSocketServerUrl("wss://...")
.wsProtocol(GraphQLWsProtocol.Factory(frameType = WsFrameType.Binary))
.build()
Many thanks to @Krillsson and @aviewfromspace1 for the insights and raising the issue!
- Allow configuration of frame types used in SubscriptionWsProtocol and default to Text (#3992)
- add
ApolloRequest.newBuilder(operation: Operation<E>)
(#3988) - Add exception handlers to ApolloCacheInterceptor and SqlNormalizedCache (#3989)
- 📠 Fix some @DeprecatedSince annotations (#3983)
- 👓 add ApolloCall.emitCacheMisses(Boolean) (#3980)
- ⚙️ Fix fragments on the root query type in operationBased codegen (#3973)
Many thanks to @AdamMTGreenberg and @Krillsson for the contributions! 🙏
2022-03-29
💙 Thanks to @undermark5, @demoritas, @rkoron007, @akshay253101, @StylianosGakis, @Goooler, @jeffreydecker, @theBradfo, @anderssandven and @olivierg13 for contributing to this release.
This version adds JS WebSocket support, more options to deal with __typename
amongst other features and bugfixes.
Version 3.2.0 now has WebSocket support for Javascript targets courtesy of @undermark5! This is a huge milestone and means the JS target is now even closer to its JVM and iOS counterparts.
jvm |
Apple | js |
linuxX64 |
|
---|---|---|---|---|
apollo-api (models) |
✅ | ✅ | ✅ | ✅ |
apollo-runtime (network, query batching, apq, ...) |
✅ | ✅ | ✅ | 🚫 |
apollo-normalized-cache |
✅ | ✅ | ✅ | 🚫 |
apollo-adapters |
✅ | ✅ | ✅ | 🚫 |
apollo-normalized-cache-sqlite |
✅ | ✅ | 🚫 | 🚫 |
apollo-http-cache |
✅ | 🚫 | 🚫 | 🚫 |
The implementation is based on the ws
library on Node and
the WebSocket
API on the browser and inspired by Ktor.
This version generates non-nullable fragments when it knows the fragment is always present:
{
cat {
# Because Animal is a supertype of Cat this condition will always be true
... on Animal {
species
}
}
}
In addition, it introduces a addTypename
Gradle option to have better control over when to add the __typename
field:
/**
* When to add __typename. One of "always", "ifFragments", "ifAbstract" or "ifPolymorphic"
*
* - "always": Add '__typename' for every compound field
*
* - "ifFragments": Add '__typename' for every selection set that contains fragments (inline or named)
* This is adding a lot more '__typename' than the other solutions and will be certainly removed in
* a future version. If you require '__typename' explicitly, you can add it to your queries.
* This causes cache misses when introducing fragments where no fragment was present before and will be certainly removed in
* a future version.
*
* - "ifAbstract": Add '__typename' for abstract fields, i.e. fields that are of union or interface type
* Note: It also adds '__typename' on fragment definitions that satisfy the same property because fragments
* could be read from the cache and we don't have a containing field in that case.
*
* - "ifPolymorphic": Add '__typename' for polymorphic fields, i.e. fields that contains a subfragment
* (inline or named) whose type condition isn't a super type of the field type.
* If a field is monomorphic, no '__typename' will be added.
* This adds the bare minimum amount of __typename but the logic is substantially more complex and
* it could cause cache misses when using fragments on monomorphic fields because __typename can be
* required in some cases.
*
* Note: It also adds '__typename' on fragment definitions that satisfy the same property because fragments
* could be read from the cache and we don't have a containing field in that case.
*
* Default value: "ifFragments"
*/
You can read more in the corresponding Typename.md design document.
The Apollo Gradle plugin now creates a new "apollo" publication if maven-publish
is found. This means you can now
publish the Apollo metadata to a maven repository:
# In your producer project
./gradlew publishApolloPublicationTo[SomeRepository]
Assuming your producer project is using com.example:project:version
for maven coordinates, the Apollo metadata will be
published at com.example:project-apollo:version
:
// In your consumer project
dependencies {
implementation("com.example:project:version")
apolloMetadata("com.example:project-apollo:version")
}
Note: There are absolutely no forward/backward compatibility guarantees for Apollo metadata yet. The Apollo version used in the consumer must be the same as the one used in the producer.
For better Java interop, you can now opt-in addJvmOverloads
. addJvmOverloads
will add the @JvmOverloads
to your
Kotlin operations:
@JvmOverloads
class GetHeroQuery(val id: String, val episode: Optional<Episode> = Optional.Absent) {
// ...
}
Meaning you can now create a new query from Java without having to specify episode
: new GetHeroQuery("1002")
- 📖 Add note to tutorial about
graphql-ws
library to tutorial (#3961) - Use ApolloCompositeException for HTTP CachePolicies (#3967)
- 🖋️ bump kotlin poet to 1.11.0 (#3970)
- Add underlying exceptions as suppressed exceptions in ApolloCompositeException (#3957)
- Add macosArm64 and macosX64 targets (#3954)
- JS Websockets: handle remote close (#3952)
- ⚙️ Introduce addTypename Gradle parameter (#3939)
- Optimize CI a bit (#3942)
- Add more field merging diagnostics (#3937)
- ⚙️ Make adapters code work without relying on having a
__typename
IrProperty (#3930) - Add equals and hashCode implementations for models with no properties (#3928)
- 🐘 Unbreak Gradle configuration cache (#3918)
- WebSocket support for JS targets (#3913)
- 🗄️ add apolloClient.httpCache (#3919)
- ⚙️ Detect case insensitive filesystems (like MacOS default one) and rename classes when that happens (#3911)
- Fix exceptions where not caught when reading the body of a batched query (#3910)
- 🗄️ Fix writing fragments programmatically was using the wrong cache key (#3905)
- 📦 Maven publishing for Apollo metadata (#3904)
- Add addJvmOverloads Gradle option for better Java interop (#3907)
- 🐘 Fix using refreshVersions (#3898)
- add support for triple quotes escapes (#3895)
- 👷 Test Builders: Fix enums in test resolver (#3894)
- Validation: Detect missing arguments when there are no arguments at all (#3893)
- Add support for receiving multiple bodies with multipart (#3889)
- ✅ Validation: allow nullable variables in non-null locations if there is a default value (#3879)
- 🗄️ HttpCache: do not cache mutations by default (#3873)
- Chunked Transfer-Encoding support in MockServer (#3870)
- Fix -1 body length in BatchingHttpInterceptor (#3874)
- Fix issue in Java codegen where selectors returned ImmutableMapBuilder instances instead of Map (#3861)
- Make watchers subscribe to the store earlier (#3853)
2022-02-07
Version 3.1.0 introduces new APIs for testing, mapping scalars as well a redesigned cache pipeline.
It also contains bugfixes around the @include
directives, MemoryCache and GraphQL validation amongst other changes.
If you're using packageNamesFromFilePaths()
, the package name of generated fragment classes has changed.
Different generated types have different package names:
- Generated types coming from operations are generated based on the operation path
- Generated types coming from the schema (input objects, custom scalars and enums) are generated based on the schema path
Previously, fragments were using the schema path which is inconsistent because fragments are not defined in the schema but are executable files, like operations.
Version 3.1.0 now uses the same logic for fragments as for operations. To revert to the previous behaviour, you can
use useSchemaPackageNameForFragments
:
apollo {
useSchemaPackageNameForFragments.set(true)
}
This is also done automatically if you're using useVersion2Compat()
. Moving forward, the plan is to
remove useSchemaPackageNameForFragments
in favor of setting a custom PackageNameGenerator
. If you have use cases
that require useSchemaPackageNameForFragments
,
please reach out
.
3.1.0 introduces QueueTestNetworkTransport
to test at the GraphQL layer without needing to run an HTTP server.
To use it, configure your ApolloClient
:
// This uses a QueueTestNetworkTransport that will play the queued responses
val apolloClient = ApolloClient.Builder()
.networkTransport(QueueTestNetworkTransport())
.build()
You can then use the enqueueTestResponse
extension function to specify the GraphQL responses to return:
val testQuery = GetHeroQuery("001")
val testData = GetHeroQuery.Data {
hero = droidHero {
name = "R2D2"
}
}
apolloClient.enqueueTestResponse(testQuery, testData)
val actual = apolloClient.query(testQuery).execute().data!!
assertEquals(testData.hero.name, actual.hero.name)
If you're testing at the HTTP layer, you can now define your own MockServerHandler
to customize how the server is
going to answer to requests:
val customHandler = object : MockServerHandler {
override fun handle(request: MockRequest): MockResponse {
return if (/* Your custom logic here */) {
MockResponse(
body = """{"data": {"random": 42}}""",
headers = mapOf("X-Test" to "true"),
)
} else {
MockResponse(
body = "Internal server error",
statusCode = 500,
)
}
}
}
val mockServer = MockServer(customHandler)
Previously, FetchPolicy
s were limited to policies that emitted at most one response. There was
a executeCacheAndNetwork()
method but it felt asymmetrical. This version introduces FetchPolicy.CacheAndNetwork
that
can emit up to two responses:
apolloClient.query(query)
// Check the cache and also use the network (1 or 2 values can be emitted)
.fetchPolicy(FetchPolicy.CacheAndNetwork)
// Execute the query and collect the responses
.toFlow().collect { response ->
// ...
}
If you need more customized ways to fetch data from the cache or more fine-grained error handling that does not come
with the built-in FetchPolicy
, you can now use fetchPolicyInterceptor
:
// An, interceptor that will only use the network after getting a successful response
val refetchPolicyInterceptor = object : ApolloInterceptor {
var hasSeenValidResponse: Boolean = false
override fun <D : Operation.Data> intercept(
request: ApolloRequest<D>,
chain: ApolloInterceptorChain
): Flow<ApolloResponse<D>> {
return if (!hasSeenValidResponse) {
CacheOnlyInterceptor.intercept(request, chain).onEach {
if (it.data != null) {
// We have valid data, we can now use the network
hasSeenValidResponse = true
}
}
} else {
// If for some reason we have a cache miss, get fresh data from the network
CacheFirstInterceptor.intercept(request, chain)
}
}
}
apolloClient.query(myQuery)
.refetchPolicyInterceptor(cacheOnlyInterceptor)
.watch()
.collect {
//
}
You can now use mapScalar
to specify your scalar mappings:
apollo {
// Replace
customScalarsMapping.set(mapOf(
"Date" to "java.util.Date"
))
// With
mapScalar("Date", "java.util.Date")
}
mapScalar
also works with built-in scalar types so you can map the ID
type to a kotlin Long:
apollo {
// This requires registering an adapter at runtime with `addCustomScalarAdapter()`
mapScalar("ID", "kotlin.Long")
}
As an optimization, you can also provide the adapter at compile time. This will avoid a lookup at runtime everytime such a scalar is read:
apollo {
// No need to call `addCustomScalarAdapter()`, the generated code will use the provided adapter
mapScalar("ID", "kotlin.Long", "com.apollographql.apollo3.api.LongAdapter")
}
For convenience, a helper function is provided for common types:
apollo {
// The generated code will use `kotlin.Long` and the builtin LongAdapter
mapScalarToKotlinLong("ID")
// The generated code will use `kotlin.String` and the builtin StringAdapter
mapScalarToKotlinString("Date")
// The generated code will use `com.apollographql.apollo3.api.Upload` and the builtin UploadAdapter
mapScalarToUpload("Upload")
}
🚧 [Changed] convertApolloSchema
and downloadApolloSchema
now use paths relative to the root of the project (#3773, #3752)
Apollo Kotlin adds two tasks to help to manage schemas: convertApolloSchema
and downloadApolloSchema
. These tasks
are meant to be used from the commandline.
Previously, paths were interpreted using the current working directory with File(path)
. Unfortunately, this is
unreliable because Gradle might change the current working directory in some conditions (
see Gradle#13927
or Gradle#6074 for an example).
With 3.1.0 and onwards, paths, will be interpreted relative to the root project
directory (project.rootProject.file(path)
):
# schema is now interpreted relative to the root project directory and
# not the current working directory anymore. This example assumes there
# is a 'app' module that applies the apollo plugin
./gradlew downloadApolloSchema \
--endpoint="https://your.domain/graphql/endpoint" \
--schema="app/src/main/graphql/com/example/schema.graphqls"
Many thanks to @dhritzkiv, @mune0903, @StylianosGakis, @AchrafAmil and @jamesonwilliams for their awesome contributions! You rock 🎸 🤘 !
- Fix error reporting when there is a "schema.graphqls" but it doesn't contain any type definition (#3844)
- Make guessNumber read the next value only once, fixes parsing custom scalars without a custom adapter (#3839, #3836)
- Clarify need to pass client's customScalarAdapters to store methods (#3838)
- Fix null pointer exception in LruCache while trimming (#3833)
- Add FetchPolicy.CacheAndNetwork (#3828)
- Allow to specify error handling for watch() (#3817)
- Scalar mapping and adapter configuration improvements (#3779)
- Tunnel variables in CustomScalarAdapters (#3813)
- Terminate threads correctly if no subscription has been executed (#3803)
- fix validation of merged fields (#3799)
- Make
reconnectWhen
suspend and pass attempt number (#3772) - Merge HTTP headers when batching (#3776)
- MockServer improvements and TestNetworkTransport (#3757)
- Fix calling ApolloClient.newBuilder() if the original ApolloClient used
.okHttpClient
(#3771) - Make
convertApolloSchema
anddownloadApolloSchema
use path from the root of the project (#3773, #3752) - fix fragment package name in multi-module scenarios (#3775)
- Make the error printer robust to unknown source locations, fixes schemas with duplicate types (#3753)
- Allow to customize the fetchPolicy with interceptors (#3743)
2021-12-15
This is the first stable release for Apollo Android 3 Apollo Kotlin 3 🎉!
There is documentation, a migration guide and a blog post coming soon (we'll update these notes when it's out).
In a nutshell, Apollo Kotlin 3 brings:
- coroutine APIs for easier concurrency
- multiplatform support makes it possible to run the same code on Android, JS, iOS, MacOS and linux
- responseBased codegen is a new optional codegen that models fragments as interfaces
- SQLite batching makes reading from the SQLite cache significantly faster
- Test builders offer a simple APIs to build fake models for your tests
- The @typePolicy and @fieldPolicy directives make it easier to define your cache ids at compile time
- The @nonnull directive catches null values at parsing time, so you don't have to deal with them in your UI code
Feel free to ask questions by either opening an issue on our GitHub repo , joining the community or stopping by our channel in the KotlinLang Slack(get your invite here).
- Fix rewinding the Json stream when lists are involved (#3727)
- Kotlin 1.6.10 (#3723)
- Disable key fields check if unnecessary and optimize its perf (#3720)
- Added an easy way to log cache misses (#3724)
- Add a Data.toJson that uses reflection to lookup the adapter (#3719)
- Do not run the cache on the main thread (#3718)
- Promote JsonWriter extensions to public API (#3715)
- Make customScalarAdapters and subscriptionsNetworkTransport public (#3714)
2021-12-13
Compared to the previous RC, this version adds a few new convenience API and fixes 3 annoying issues.
💙 Many thanks to @ mateuszkwiecinski, @ schoeda and @ fn-jt for all the feedback 💙
- Make
ApolloCall.operation
public (#3698) - Add
SubscriptionWsProtocolAdapter
(#3697) - Add
Operation.composeJsonRequest
(#3697)
- Allow repeated
@fieldPolicy
(#3686) - Fix incorrect merging of nested objects in JSON (#3672)
- Fix duplicate query detection (#3699)
2021-12-10
💙 Many thanks to @michgauz, @joeldenke, @rohandhruva, @schoeda, @CoreFloDev and @sproctor for all the feedback 💙
In order to simplify the API and keep the symmetry with ApolloRequest<D>
and ApolloResponse<D>
, ApolloQueryCall<D, E>
, ApolloSubscriptionCall<D, E>
, ApolloMutationCall<D, E>
are replaced with ApolloCall<D>
.
This change should be mostly transparent but it's technically a breaking change. If you are
passing ApolloQueryCall<D, E>
variables, it is safe to drop the second type parameter and use ApolloCall<D>
instead.
You now have the option to reconnect a WebSocket automatically when an error happens and re-subscribe automatically
after the reconnection has happened. To do so, use the webSocketReconnectWhen
parameter:
val apolloClient = ApolloClient.Builder()
.httpServerUrl("http://localhost:8080/graphql")
.webSocketServerUrl("http://localhost:8080/subscriptions")
.wsProtocol(
SubscriptionWsProtocol.Factory(
connectionPayload = {
mapOf("token" to upToDateToken)
}
)
)
.webSocketReconnectWhen {
// this is called when an error happens on the WebSocket
it is ApolloWebSocketClosedException && it.code == 1001
}
.build()
The HttpBatchingEngine
has been moved to an HttpInterceptor
. You can now configure Http batching with a specific
method:
apolloClient = ApolloClient.Builder()
.serverUrl(mockServer.url())
.httpBatching(batchIntervalMillis = 10)
.build()
- Add 2.x symbols (
Rx2Apollo
,prefetch()
,customAttributes()
,ApolloIdlingResource.create()
) to help the transition (#3679) - Add canBeBatched var to ExecutionOptions (#3677)
- Merge ApolloQueryCall, ApolloSubscriptionCall, ApolloMutationCall (#3676)
- Add
WebSocketNetworkTransport.reconnectWhen {}
(#3674) - Move BatchingHttpEngine to a HttpInterceptor (#3670)
- Add exposeErrorBody (#3661)
- fix the name of the downloadServiceApolloSchemaFromRegistry task (#3669)
- Fix DiskLruHttpCache concurrency (#3667)
2021-12-07
This version is the release candidate for Apollo Android 3 🚀. Please try it and report any issues, we'll fix them urgently.
There is documentation and a migration guide. More details are coming soon. In a nutshell, Apollo Android 3 brings, amongst other things:
- coroutine APIs for easier concurrency
- multiplatform support makes it possible to run the same code on Android, JS, iOS, MacOS and linux
- responseBased codegen is a new optional codegen that models fragments as interfaces
- SQLite batching makes reading from the SQLite cache significantly faster
- Test builders offer a simple APIs to build fake models for your tests
- The @typePolicy and @fieldPolicy directives make it easier to define your cache ids at compile time
- The @nonnull directive catches null values at parsing time, so you don't have to deal with them in your UI code
Compared to beta05
, this version changes the default value of generateOptionalOperationVariables
, is compatible with
Gradle configuration cache and fixes a few other issues.
The default value for the generateOptionalOperationVariables
config is now true
.
What this means:
- By default, operations with nullable variables will be generated with
Optional
parameters - You will need to wrap your parameters at the call site
For instance:
query GetTodos($first: Int, $offset: Int) {
todos(first: $first, offset: $offset) {
...Todo
}
}
// Before
val query = GetTodosQuery(100, null)
// After
val query = GetTodosQuery(Optional.Present(100), Optional.Absent)
- If you prefer, you can set
generateOptionalOperationVariables
tofalse
to generate non-optional parameters globally - This can also be controlled on individual variables with the
@optional
directive - More information about this can be found here
We think this change will make more sense to the majority of users (and is consistent with Apollo Android v2's behavior)
even though it may be more verbose, which is why it is possible to change the behavior via
the generateOptionalOperationVariables
config.
To keep the beta05
behavior, set generateOptionalOperationVariables
to false in your Gradle configuration:
apollo {
generateOptionalOperationVariables.set(false)
}
You can now pass WebSocket related options to the ApolloClient.Builder
directly (previously this would have been done
via NetworkTransport
):
// Before
val apolloClient = ApolloClient.Builder()
// (...)
.subscriptionNetworkTransport(WebSocketNetworkTransport(
serverUrl = "https://example.com/graphql",
idleTimeoutMillis = 1000L,
wsProtocol = SubscriptionWsProtocol.Factory()
))
.build()
// After
val apolloClient = ApolloClient.Builder()
// (...)
.wsProtocol(SubscriptionWsProtocol.Factory())
.webSocketIdleTimeoutMillis(1000L)
.build()
This version upgrades OkHttp to 4.9.3
(from 3.12.11
). This means Apollo Android now requires Android apiLevel
21
+. As OkHttp 3 enters end of life at the end of the year and the vast majority of devices now support apiLevel
21
,
we felt this was a reasonable upgrade.
- Fixed an issue where it was not possible to restart a websocket after a network error (#3646)
- Fixed an issue where Android Java projects could not use the Apollo Gradle plugin (#3652)
- Update a few dependencies (#3653)
- Fix Android Java projects (#3652)
- Expose more configuration options on ApolloClient.Builder (#3647)
- Fix restarting a websocket after a network error (#3646)
- Change optional default value (#3648)
- Fix configuration cache (#3645)