Skip to content

Commit

Permalink
feat: Task CRUD (#15)
Browse files Browse the repository at this point in the history
* feat: Task CRUD

* feat: add support for querying specific properties in findCompany

- Updated the findCompany method to accept an optional list of properties.
- Constructed the URL to append these properties as query parameters.
- This allows users to specify which fields to retrieve for a company
  • Loading branch information
BlahBlahBleeBlahBloop authored Jun 3, 2024
1 parent 20fd19e commit 35d7ec8
Show file tree
Hide file tree
Showing 22 changed files with 294 additions and 80 deletions.
105 changes: 57 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# 🤖 HubSpot Kotlin SDK

General implementation of [HubSpot](https://developers.hubspot.com/docs/api/crm/companies) CRM API in tiny Kotlin SDK.

🎈 Currently in progress, send issues or pull requests 🙌🏼

🚀 Install from official Maven repository with `org.boomevents:hubspot-sdk:$VERSION`<br>

## Supported features
| Feature | List | Read | Create | Change | Delete |
|------------------|--------|--------|----------|----------|----------|
| Company ||||||
| Custom objects ||||||
| Deal ||||||
| Contact ||||||
| Associations ||||||

| Feature | List | Read | Create | Change | Delete |
|----------------|--------|--------|----------|----------|----------|
| Company ||||||
| Custom objects ||||||
| Deal ||||||
| Contact ||||||
| Associations ||||||
| Task ||||||

## Supported types

Expand All @@ -28,38 +31,40 @@ General implementation of [HubSpot](https://developers.hubspot.com/docs/api/crm/
## Usage Examples

Basic SDK client configuration

```kotlin
// All basic types are supported
class MyCustomCompanyProperties(
val name: String,
val age: Int,
val email: String,
val newsletter: Boolean,

// You can customize final property name send to HubSpot API
@JsonProperty("billing_bank_iban")
val iban: String? = null
val name: String,
val age: Int,
val email: String,
val newsletter: Boolean,

// You can customize final property name send to HubSpot API
@JsonProperty("billing_bank_iban")
val iban: String? = null
)

val client = Client(
apiBasePath = "https://api.hubapi.com",
apiBasePath = "https://api.hubapi.com",

// Found in HubSpot company management -> Integrations -> API Keys -> Active API Key
apiKey = "xxx"
// Found in HubSpot company management -> Integrations -> API Keys -> Active API Key
apiKey = "xxx"
)
```

## #️⃣ Company entity

### Create brand-new company

```kotlin
val companyRequest = CompanyRequest(
properties = MyCustomCompanyProperties(
name = "John Doe",
age = 34,
email = "[email protected]",
newsletter = true
)
properties = MyCustomCompanyProperties(
name = "John Doe",
age = 34,
email = "[email protected]",
newsletter = true
)
)

val companyResponse = companiesClient.createCompany(companyRequest)
Expand All @@ -69,14 +74,15 @@ println(companyResponse.properties["name"]) // John Doe
```

### Change existing company

```kotlin
val companyRequest = CompanyRequest(
properties = MyCustomCompanyProperties(
name = "John Doe",
age = 34,
email = "[email protected]",
newsletter = true
)
properties = MyCustomCompanyProperties(
name = "John Doe",
age = 34,
email = "[email protected]",
newsletter = true
)
)

val companyResponse = companiesClient.changeCompany(123456789, companyRequest)
Expand All @@ -88,22 +94,23 @@ println(companyResponse.properties["name"]) // John Doe
## #️⃣ Custom objects

### Create brand-new custom object record

```kotlin
val request = CustomObjectRequest(
properties = MySuperEventProperties(
name = "Party #2022",
address = "New York",

// Date must be formatted as "YYYY-MM-DD"
dateFrom = LocalDate
.now()
.plusDays(10)
.format(DateTimeFormatter.ISO_LOCAL_DATE),
dateUntil = LocalDate
.now()
.plusDays(15)
.format(DateTimeFormatter.ISO_LOCAL_DATE),
)
properties = MySuperEventProperties(
name = "Party #2022",
address = "New York",

// Date must be formatted as "YYYY-MM-DD"
dateFrom = LocalDate
.now()
.plusDays(10)
.format(DateTimeFormatter.ISO_LOCAL_DATE),
dateUntil = LocalDate
.now()
.plusDays(15)
.format(DateTimeFormatter.ISO_LOCAL_DATE),
)
)

// HubSpot client and name of custom object table
Expand All @@ -114,16 +121,18 @@ val response = myEventsClient.createCustomObjectRecord(request)
println(response.id) // HubSpot ID
println(response.properties["name"]) // Party #2022
```

### Associate a contact to an existing company with default label

```kotlin

val associationClient = AssociationClient(hubspotClient)

val associationRequest = AssociationRequest(
fromObjectType = CONTACT, // contact, company, deal, etc
fromObjectId = 1, // HubSpot ID of the contact
toObjectType = COMPANY, // contact, company, deal, etc
toObjectId = 2 // HubSpot ID of the company
fromObjectType = CONTACT, // contact, company, deal, etc
fromObjectId = 1, // HubSpot ID of the contact
toObjectType = COMPANY, // contact, company, deal, etc
toObjectId = 2 // HubSpot ID of the company
)

val associationResponse = associationClient.createDefaultAssociation(associationRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ object ClientRequestCatalog {

const val CONTACTS = "/crm/v3/objects/contacts"
const val CONTACTS_DETAIL = "/crm/v3/objects/contacts/{contactId}"
}

const val TASKS = "/crm/v3/objects/tasks"
const val TASKS_DETAIL = "/crm/v3/objects/tasks/{taskId}"
}

object V4 {
const val OBJECT_DEFAULT_ASSOCIATION = "/crm/v4/objects/{fromObjectType}/{fromObjectId}/associations/default/{toObjectType}/{toObjectId}"
const val OBJECT_DEFAULT_ASSOCIATION =
"/crm/v4/objects/{fromObjectType}/{fromObjectId}/associations/default/{toObjectType}/{toObjectId}"
const val OBJECT_WITH_LABEL_ASSOCIATION = "/crm/v4/objects/{fromObjectType}/{fromObjectId}/associations/{toObjectType}/{toObjectId}"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.boomevents.hubspot.domain.associations

enum class AssociationTypeCategory {
enum class AssociationCategory {
HUBSPOT_DEFINED,
USER_DEFINED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.boomevents.hubspot.domain.associations

data class AssociationObject(
val to: AssociationObjectTo,
val types: List<AssociationObjectType>
) {

data class AssociationObjectTo(
val id: Long
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.boomevents.hubspot.domain.associations

enum class AssociationObjectType(val value: String) {
CONTACT("contact"),
COMPANY("company"),
DEAL("deal"),
}
data class AssociationObjectType(
val associationCategory: AssociationCategory,
val associationTypeId: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.boomevents.hubspot.domain.associations

enum class AssociationObjectTypeId(val value: String) {
CONTACT("contact"),
COMPANY("company"),
DEAL("deal"),
TASK("task")
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package org.boomevents.hubspot.domain.associations
import java.math.BigInteger

data class AssociationRequest(
val fromObjectType: AssociationObjectType,
val fromObjectType: AssociationObjectTypeId,
val fromObjectId: BigInteger,
val toObjectType: AssociationObjectType,
val toObjectId: BigInteger) {
}
val toObjectType: AssociationObjectTypeId,
val toObjectId: BigInteger
)
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.boomevents.hubspot.domain.associations

class AssociationResult (
class AssociationResult(
val status: String? = null,
val results: Array<Any>? = null,
val startedAt: String? = null,
val completedAt: String? = null,
val completedAt: String? = null
)

Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package org.boomevents.hubspot.domain.associations

enum class AssociationType(val value: Int) {
// Contact
CONTACT_TO_COMPANY_PRIMARY(1),
COMPANY_TO_CONTACT_PRIMARY(2),
CONTACT_TO_COMPANY(279),

// Company
COMPANY_TO_CONTACT_PRIMARY(2),
COMPANY_TO_CONTACT(280),

// Task
TASK_TO_COMPANY(192),
TASK_TO_CONTACT(204),
TASK_TO_DEAL(216)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.boomevents.hubspot.domain.company

import org.boomevents.hubspot.model.http.RequestMethod
import org.boomevents.hubspot.model.http.Requester
import org.boomevents.hubspot.Client
import org.boomevents.hubspot.ClientRequestCatalog
import org.boomevents.hubspot.domain.company.exceptions.CompanyNotFoundException
import org.boomevents.hubspot.model.http.RequestMethod
import org.boomevents.hubspot.model.http.Requester
import org.boomevents.hubspot.model.http.exceptions.HttpRequestException
import org.boomevents.hubspot.model.mapper.Mapper
import java.math.BigInteger
Expand All @@ -22,15 +22,33 @@ class CompanyClient(private val hubSpotClient: Client) {
}


/**
* Retrieves the details of a specific company by its ID from the HubSpot API.
*
* @param companyId The unique identifier of the company to be retrieved.
* @param properties An optional list of property names to be included in the response.
* If provided, these properties will be appended as query parameters
* to the request URL to specify which fields to retrieve.
* @return A Company object containing the details of the requested company.
* @throws CompanyNotFoundException if the company with the specified ID is not found.
* @throws HttpRequestException if the request to the HubSpot API fails for any other reason.
*/

@Throws(
CompanyNotFoundException::class,
HttpRequestException::class
)
fun findCompany(companyId: BigInteger): Company {
val requestUrl = ClientRequestCatalog.V3.COMPANIES_DETAIL.replace(
fun findCompany(companyId: BigInteger, properties: List<String>? = null): Company {
val baseUrl = ClientRequestCatalog.V3.COMPANIES_DETAIL.replace(
"{companyId}", companyId.toString()
)

val requestUrl = if (properties != null && properties.isNotEmpty()) {
"$baseUrl?properties=${properties.joinToString(",")}"
} else {
baseUrl
}

val response = Requester.requestJson(hubSpotClient, RequestMethod.GET, requestUrl)

if (response.isSuccess) {
Expand All @@ -55,8 +73,8 @@ class CompanyClient(private val hubSpotClient: Client) {
return Mapper.mapToObject(response.body)
} else {
when (response.status) {
404 -> throw CompanyNotFoundException(companyId)
else -> throw HttpRequestException(response.status, response.statusText)
404 -> throw CompanyNotFoundException(companyId)
else -> throw HttpRequestException(response.status, response.statusText)
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions hubspot/src/main/kotlin/org/boomevents/hubspot/domain/task/Task.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.boomevents.hubspot.domain.task

import org.boomevents.hubspot.domain.DataEntity
import java.math.BigInteger
import java.time.LocalDateTime

class Task(
override val id: BigInteger,
override val properties: Map<String, Any>,
override val createdAt: LocalDateTime,
override val updatedAt: LocalDateTime,
override val archived: Boolean
) : DataEntity
Loading

0 comments on commit 35d7ec8

Please sign in to comment.