Created
December 11, 2022 15:45
-
-
Save Vergil333/58996d4e7900a4eb58521488e98c708f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.fasterxml.jackson.annotation.JsonProperty | |
import com.fasterxml.jackson.core.type.TypeReference | |
import com.fasterxml.jackson.databind.DeserializationFeature | |
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper | |
import com.fasterxml.jackson.module.kotlin.registerKotlinModule | |
import java.net.HttpURLConnection | |
import java.net.IDN | |
import java.net.URI | |
import java.net.URL | |
import kotlin.reflect.KVisibility | |
import kotlin.reflect.full.findAnnotations | |
import kotlin.reflect.full.memberProperties | |
import kotlin.reflect.full.primaryConstructor | |
import kotlin.reflect.jvm.javaField | |
/** | |
* @param accessToken has to have [web, chatter_api, api] permissions for querying | |
* @param instanceUrl acquired in the same response as accessToken | |
* | |
* @credits to mjg123 https://github.com/mjg123/java-http-clients/blob/master/src/main/java/com/twilio/JavaHttpURLConnectionDemo.java | |
* @credits to madmax1028, broot https://discuss.kotlinlang.org/t/how-to-specify-generic-output-type-to-be-subclass-of-generic-type/24637/12 | |
*/ | |
class SalesforceClient( | |
@PublishedApi internal inline val accessToken: String, | |
@PublishedApi internal inline val instanceUrl: String, | |
) { | |
@PublishedApi | |
internal val apiVersion = "v53.0" | |
@PublishedApi | |
internal val mapper = jacksonObjectMapper().registerKotlinModule() | |
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) | |
inline fun <reified T: SObjectInterface> getAll(): SfResponse<T> = | |
getResponse<T>("SELECT ${getFields<T>().joinToString()} FROM ${T::class.simpleName}") | |
inline fun <reified T: SObjectInterface> count(): Int = | |
getResponse<T>("SELECT COUNT() FROM+${T::class.simpleName}") | |
.totalSize | |
@PublishedApi | |
internal inline fun <reified T: SObjectInterface> getResponse(query: String): SfResponse<T> { | |
val url = URL("$instanceUrl/services/data/$apiVersion/query?q=$query").toUri().toURL() | |
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection | |
connection.setRequestProperty("accept", "application/json") | |
connection.setRequestProperty("Authorization", "Bearer $accessToken") | |
return mapper.readValue(connection.inputStream, object : TypeReference<SfResponse<T>>() {}) | |
} | |
@OptIn(ExperimentalStdlibApi::class) | |
inline fun <reified T: SObjectInterface> getFields(): List<String> { | |
val propertiesWeWant = T::class.java.kotlin.memberProperties | |
.filter { prop -> | |
prop.visibility == KVisibility.PUBLIC | |
&& listOf("java.util", "java.lang").contains(prop.javaField?.type?.packageName) | |
&& prop.name != "attributes" | |
} | |
val bodyProps = propertiesWeWant.mapNotNull { it.javaField?.getAnnotation(JsonProperty::class.java)?.value } | |
val constructorProps = T::class.java.kotlin.primaryConstructor?.parameters | |
?.filter { it.name in (propertiesWeWant.map { it.name }) } | |
?.map { it.findAnnotations(JsonProperty::class).first().value } | |
?: emptyList() | |
return bodyProps + constructorProps | |
} | |
/** | |
* URI is used for URL encoding. | |
*/ | |
@PublishedApi | |
internal fun URL.toUri() = URI( | |
this.protocol, | |
this.userInfo, | |
IDN.toASCII(this.host), | |
this.port, | |
this.path, | |
this.query, this.ref, | |
) | |
} | |
class SfResponse<T: SObjectInterface>( | |
val totalSize: Int, | |
val done: Boolean, | |
val records: List<T>, | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface SObjectInterface | |
data class SObjectAttributes<T: SObjectType>( | |
val type: T, | |
val url: String? = null, | |
) | |
abstract class AbstractSObject<T: SObjectType>( | |
type: T, | |
) : SObjectInterface { | |
val attributes: SObjectAttributes<T> = SObjectAttributes(type = type) | |
@JsonProperty("Id") val id: String? = null | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum class SObjectType(val jsonName: String) { | |
@JsonProperty("TestObject__c") TEST_OBJECT__C("TestObject__c"), | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @property createdById Lookup(User) | |
* @property createdDate Date/Time | |
* @property email__c Email | |
* @property id Lookup() | |
* @property isDeleted Checkbox | |
* @property lastModifiedById Lookup(User) | |
* @property lastModifiedDate Date/Time | |
* @property name Text(80) | |
* @property ownerId Lookup(User,Group) | |
* @property systemModstamp Date/Time | |
* @property userRecordAccessId Lookup(User Record Access) | |
*/ | |
class TestObject__c ( | |
@JsonProperty("CreatedById") val createdById: String, | |
@JsonProperty("CreatedDate") val createdDate: String, | |
@JsonProperty("email__c") val email__c: String? = null, | |
@JsonProperty("IsDeleted") val isDeleted: Boolean, | |
@JsonProperty("LastModifiedById") val lastModifiedById: String, | |
@JsonProperty("LastModifiedDate") val lastModifiedDate: String, | |
@JsonProperty("Name") val name: String? = null, | |
@JsonProperty("OwnerId") val ownerId: String, | |
@JsonProperty("SystemModstamp") val systemModstamp: String, | |
@JsonProperty("UserRecordAccessId") val userRecordAccessId: String? = null, | |
) : AbstractSObject<SObjectType>(type = SObjectType.TEST_OBJECT__C) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment