Interface to the Force.com REST API
I haven't found a way to properly test/mock SalesforceRequest when exercising it's consumers.

What it did do was make me realize


Naming the class Request and then providing multiple "request"-like methods on it was a mistake. Each SalesforceRequest subtype was a full blown API provider.
Following from 1, I took the generics approach to make the SalesforceApiGateway type-safe (which was the initial driver for the request classes).


I ended up with the following interface:

interface SalesforceObject {
    val Id: String
    val CreatedById: String
    val LastModifiedDate: Instant
}

interface SalesforceApiGateway {
    val API_VERSION: String
    val MAX_RECENT_DAYS: Duration

    fun &lt;T: SalesforceObject&gt; getLastUpdated(type: KClass&lt;T&gt;, backTill: Duration = Duration.ofDays(1)): LastUpdated

    fun &lt;T: SalesforceObject&gt; getLastUpdated(type: KClass&lt;T&gt;, backTill: Instant): LastUpdated
            = getLastUpdated(type, Duration.between(backTill, Instant.now(Clock.systemUTC())))

    fun &lt;T: SalesforceObject&gt; getDetails(type: KClass&lt;T&gt;, uuid: String, fields: List&lt;String&gt; = type.members.map { it.name }): Details&lt;T&gt;

    data class LastUpdatedResponse(val ids: List&lt;String&gt; = emptyList(),
                                   val latestDateCovered: String = ISO_DATES.format(ZonedDateTime.now(Clock.systemUTC())))
    data class LastUpdated(val error: Throwable? = null, val response: LastUpdatedResponse? = null)

    data class DetailsResponse&lt;T&gt;(val wrapped: T)
    data class Details&lt;T&gt;(val error: Throwable? = null, val response: DetailsResponse&lt;T&gt;? = null)
    companion object {
        val ISO_DATES = DateTimeFormatter.ISO_OFFSET_DATE_TIME
    }
}


The upside is that I can now use Kotlin's awesome defaulted parameters to allow my gateway consumers to not care about fields they want to select, but advanced, future uses can still override that choice. It also allows me to express that I want gateway implementations to be in charge of properly de-serializing responses to the correct requested type. Plus, I still get to keep my interface, so testing is trivial by implementing a dummy.

There's several things that were suggested in the process:


Use the Result class to model success/failure instead of custom "result" classes. I wasn't able to wrap my head around their model of creating a result from an existing basic type (like my List&lt;String&gt;). Maybe next time.
Using Retrofit (thanks orangy for mentioning that!) to generate the code for the gateway. However, Jake Wharton confirmed Retrofit won't allow me to template my method, resulting n methods per SalesforceObject. I tried it and just copy and pasting my methods made my skin crawl.