apollo-kotlin
apollo-kotlin copied to clipboard
Data builders
When working with Jetpack Compose @Preview, faking data is useful in the main source set while currently, the test builders are only generated for the test source set by default. This can be overriden but it's not straightforward. Also because the current test builders are responseBased, they generate a fair amount of code, not to mention that specific code is generated for each and every operation.
Instead, we could generate schemaBased test builders only once and add them to the main source set. For a query like this:
query HeroName {
hero {
name
}
}
generate a fake model like this:
// pseudo code, not sure this can work exactly like this
val data = buildFakeData<HeroName> {
hero = humanHero {
name = "Luke"
// Here it's not really type safe but that's certainly OK for fake models
height = 180
}
}
We could even enable strict mode so that errors are caught at runtime: see https://github.com/apollographql/apollo-kotlin/issues/3344
Todo:
- [x] custom scalars support
- [x] default values
- [ ] use in our own tests
- [ ] add a kotlin-faker resolver
- [ ] java codegen
- [ ] strict mode
- [ ] update doc
- [ ] (maybe) cleanup the IrType2 stuff
Current proposal for generated code:
class HeroQuery {
class Data(val hero: Hero) {
}
class Hero(val name: String)
companion object {
fun Data(block: QueryBuilder.() -> Unit): Data {
// Code goes here
}
}
}
class AliasedProperty(val alias: String, val map: MutableMap<String, Any?>) {
infix fun to(value: Any) {
map[alias] = value
}
}
@Suppress("PropertyName")
abstract class ObjectBuilder {
protected val __map = mutableMapOf<String, Any?>()
var __typename: String by __map
fun alias(alias: String): AliasedProperty {
return AliasedProperty(alias, __map)
}
}
class QueryBuilder: ObjectBuilder() {
var hero: Character? by __map
fun build(): Query {
return Query(__map)
}
}
fun buildQuery(block: QueryBuilder.() -> Unit): Query {
val builder = QueryBuilder()
builder.__typename = "Query"
builder.block()
return builder.build()
}
class Query(map: Map<String, Any?>): Map<String, Any?> by map
class HumanBuilder: ObjectBuilder() {
var name: String? by __map
var height: Double? by __map
fun build(): Human {
return Human(__map)
}
}
fun buildHuman(block: HumanBuilder.() -> Unit): Human {
val builder = HumanBuilder()
builder.__typename = "Human"
builder.block()
return builder.build()
}
class DroidBuilder: ObjectBuilder() {
var name: String? by __map
var primaryFunction: String? by __map
fun build(): Droid {
return Droid(__map)
}
}
fun buildDroid(block: DroidBuilder.() -> Unit): Droid {
val builder = DroidBuilder()
builder.__typename = "Droid"
builder.block()
return builder.build()
}
interface Character
class Human(map: Map<String, Any?>): Character, Map<String, Any?> by map
class Droid(map: Map<String, Any?>): Character, Map<String, Any?> by map
HeroQuery.Data {
hero = buildHuman {
name = "Luke"
height = 1.7
}
alias("hero1") to buildDroid {
name = "c3pO"
primaryFunction = "translation"
}
}
Will be available in 3.6.0