Skip to content

Instantly share code, notes, and snippets.

@Vergil333
Created December 13, 2022 04:30
Show Gist options
  • Save Vergil333/9d75408aae78af32f609060a91db9747 to your computer and use it in GitHub Desktop.
Save Vergil333/9d75408aae78af32f609060a91db9747 to your computer and use it in GitHub Desktop.
/**
* @property accountNumber Text(40)
* @property accountSource Picklist
* @property active__c Picklist
* @property annualRevenue Currency(18, 0)
* @property billingAddress Address
* @property cleanStatus Picklist
* @property createdById Lookup(User)
* @property createdDate Date/Time
* @property customerPriority__c Picklist
* @property dandbCompanyId Lookup(D&B Company)
* @property description Long Text Area(32000)
* @property dunsNumber Text(9)
* @property fax Fax
* @property id Lookup()
* @property industry Picklist
* @property isDeleted Checkbox
* @property jigsaw Text(20)
* @property jigsawCompanyId External Lookup
* @property lastActivityDate Date
* @property lastModifiedById Lookup(User)
* @property lastModifiedDate Date/Time
* @property lastReferencedDate Date/Time
* @property lastViewedDate Date/Time
* @property masterRecordId Lookup(Account)
* @property naicsCode Text(8)
* @property naicsDesc Text(120)
* @property name Name
* @property numberOfEmployees Number(8, 0)
* @property numberofLocations__c Number(3, 0)
* @property operatingHoursId Lookup(Operating Hours)
* @property ownerId Lookup(User)
* @property ownership Picklist
* @property parentId Hierarchy
* @property phone Phone
* @property photoUrl URL(255)
* @property rating Picklist
* @property sLAExpirationDate__c Date
* @property sLASerialNumber__c Text(10)
* @property sLA__c Picklist
* @property shippingAddress Address
* @property sic Text(20)
* @property sicDesc Text(80)
* @property site Text(80)
* @property systemModstamp Date/Time
* @property tickerSymbol Content(20)
* @property tradestyle Text(255)
* @property type Picklist
* @property upsellOpportunity__c Picklist
* @property userRecordAccessId Lookup(User Record Access)
* @property website URL(255)
* @property yearStarted Text(4)
*/
class Account (
@JsonProperty("AccountNumber") val accountNumber: String? = null,
@JsonProperty("AccountSource") val accountSource: String? = null,
@JsonProperty("Active__c") val active__c: String? = null,
@JsonProperty("AnnualRevenue") val annualRevenue: Double? = null,
@JsonProperty("BillingAddress") val billingAddress: Map<String, Any?>? = null,
@JsonProperty("CleanStatus") val cleanStatus: String? = null,
@JsonProperty("CreatedById") val createdById: String,
@JsonProperty("CreatedDate") val createdDate: String,
@JsonProperty("CustomerPriority__c") val customerPriority__c: String? = null,
@JsonProperty("DandbCompanyId") val dandbCompanyId: String? = null,
@JsonProperty("Description") val description: String? = null,
@JsonProperty("DunsNumber") val dunsNumber: String? = null,
@JsonProperty("Fax") val fax: String? = null,
@JsonProperty("Industry") val industry: String? = null,
@JsonProperty("IsDeleted") val isDeleted: Boolean,
@JsonProperty("Jigsaw") val jigsaw: String? = null,
@JsonProperty("JigsawCompanyId") val jigsawCompanyId: String? = null,
@JsonProperty("LastActivityDate") val lastActivityDate: String? = null,
@JsonProperty("LastModifiedById") val lastModifiedById: String,
@JsonProperty("LastModifiedDate") val lastModifiedDate: String,
@JsonProperty("LastReferencedDate") val lastReferencedDate: String? = null,
@JsonProperty("LastViewedDate") val lastViewedDate: String? = null,
@JsonProperty("MasterRecordId") val masterRecordId: String? = null,
@JsonProperty("NaicsCode") val naicsCode: String? = null,
@JsonProperty("NaicsDesc") val naicsDesc: String? = null,
@JsonProperty("Name") val name: String,
@JsonProperty("NumberOfEmployees") val numberOfEmployees: Double? = null,
@JsonProperty("NumberofLocations__c") val numberofLocations__c: Double? = null,
@JsonProperty("OperatingHoursId") val operatingHoursId: String? = null,
@JsonProperty("OwnerId") val ownerId: String,
@JsonProperty("Ownership") val ownership: String? = null,
@JsonProperty("ParentId") val parentId: Map<String, Any?>? = null,
@JsonProperty("Phone") val phone: String? = null,
@JsonProperty("PhotoUrl") val photoUrl: String? = null,
@JsonProperty("Rating") val rating: String? = null,
@JsonProperty("SLAExpirationDate__c") val sLAExpirationDate__c: String? = null,
@JsonProperty("SLASerialNumber__c") val sLASerialNumber__c: String? = null,
@JsonProperty("SLA__c") val sLA__c: String? = null,
@JsonProperty("ShippingAddress") val shippingAddress: Map<String, Any?>? = null,
@JsonProperty("Sic") val sic: String? = null,
@JsonProperty("SicDesc") val sicDesc: String? = null,
@JsonProperty("Site") val site: String? = null,
@JsonProperty("SystemModstamp") val systemModstamp: String,
@JsonProperty("TickerSymbol") val tickerSymbol: String? = null,
@JsonProperty("Tradestyle") val tradestyle: String? = null,
@JsonProperty("Type") val type: String? = null,
@JsonProperty("UpsellOpportunity__c") val upsellOpportunity__c: String? = null,
@JsonProperty("UserRecordAccessId") val userRecordAccessId: String? = null,
@JsonProperty("Website") val website: String? = null,
@JsonProperty("YearStarted") val yearStarted: String? = null,
) : AbstractSObject<SObjectType>(type = SObjectType.ACCOUNT)
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>,
)
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
}
enum class SObjectType(val jsonName: String) {
@JsonProperty("Account") ACCOUNT("Account"),
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment