Releases: apollographql/apollo-kotlin
v3.7.4
This release contains a handful of bug fixes and improvements.
👷 All changes
- 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 (#4613)
- Http cache: don't access the disk from the main thread in error case (#4606)
v3.7.3
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! 🧡
⚙️ Removed JS legacy artifacts (#4591)
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.
👷 All changes
- 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)
v3.7.2
This patch release brings a few fixes.
Many thanks to @davidshepherd7, @chao2zhang, @agrosner, @MyDogTom, @doucheng, @sam43 and @vincentjames501, for helping improve the library! 🙏
🔎 Explicit service declaration
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")
// ...
+ }
}
👷 All changes
- 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)
v3.7.1
A patch release with a few fixes.
👷 All changes
- 👷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 💙
v2.5.14
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 💙!
👷 All changes
- update terminate and responseCallback methods to return Optional.absent() in IDLE/TERMINATED state (#4383)
v3.7.0
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.
✨️ [new & experimental] compiler hooks API (#4474, #4026)
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()))
}
}
✨️ [new & experimental] operationBasedWithInterfaces codegen (#4370)
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 💙
✨️ [new & experimental] usedCoordinates auto detection (#4494)
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")
}
}
👷 All changes
- 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)
v3.6.2
v3.6.1
v3.6.0
This version brings initial support for @defer
as well as data builders.
💙️ External contributors
Many thanks to @engdorm, @Goooler, @pt2121 and @StylianosGakis for their contributions!
✨️ [new] @defer
support
@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).
✨️ [new] Data Builders (#4321)
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"
}
}
✨️ [new] Kotlin 1.7 (#4314)
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, you will need to update the Kotlin version to 1.7.0+.
👷 All changes
- 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)
v2.5.13
A 2.x maintainance release with a couple of bugfixes. Many thanks to @eduardb for diving into #2818 💙
What's Changed
- Fix converting introspection to SDL by @martinbonnin in #4316
- Fix writing fragments overwrites cache entries (#2818) by @eduardb in #4358
Full Changelog: v2.5.12...v2.5.13