Created
March 3, 2017 06:04
-
-
Save mentiflectax/6ddcf5b29c6cbed10528501ff4599986 to your computer and use it in GitHub Desktop.
CapsuleCrmSubsystem.kt
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
package com.mycompany.myproduct.capsulecrm | |
import com.mycompany.myproduct.telegram.cmd.bp1add.Bp1CompanyData | |
import com.mycompany.myproduct.telegram.cmd.bp1add.ContactDataType | |
import com.mycompany.myproduct.validation.FailableOperationResult | |
import com.mycompany.myproduct.validation.ValidationResult | |
import com.beust.klaxon.JsonObject | |
import com.beust.klaxon.array | |
import com.beust.klaxon.long | |
import com.beust.klaxon.string | |
import com.fasterxml.jackson.databind.ObjectMapper | |
import org.apache.commons.io.IOUtils | |
import org.apache.commons.lang3.StringUtils | |
import org.apache.http.client.methods.CloseableHttpResponse | |
import org.apache.http.client.methods.HttpGet | |
import org.apache.http.client.methods.HttpPost | |
import org.apache.http.client.methods.HttpUriRequest | |
import org.apache.http.entity.ContentType | |
import org.apache.http.entity.StringEntity | |
import org.apache.http.impl.client.CloseableHttpClient | |
import org.apache.http.impl.client.HttpClients | |
import org.json.JSONArray | |
import org.json.JSONObject | |
import org.slf4j.Logger | |
import org.slf4j.LoggerFactory | |
import java.net.URL | |
/** | |
* Created by pisarenko on 31.01.2017. | |
*/ | |
open class CapsuleCrmSubsystem( | |
val logger: Logger = LoggerFactory.getLogger("com.mycompany.myproduct"), | |
val protocol:Logger = LoggerFactory.getLogger("protocol") | |
) : ICapsuleCrmSubsystem { | |
companion object { | |
val WwwPrefix = "www." | |
val ApiToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" | |
val AgentFieldId = 31331 | |
val AddNoteUrl = "https://api.capsulecrm.com/api/v2/entries" | |
val CreateCompanyUrl = "https://api.capsulecrm.com/api/v2/parties" | |
} | |
var httpClient:CloseableHttpClient? = null | |
override fun init() { | |
httpClient = createDefaultHttpClient() | |
} | |
override fun close() { | |
httpClient?.close() | |
} | |
override fun findPartiesByUrlFragment(urlFragment: String): PartiesSearchResult { | |
val client = httpClient | |
if (client == null) { | |
return PartiesSearchResult(false, "Internal error", emptyList()) | |
} | |
var res:CloseableHttpResponse? = null | |
var req: HttpUriRequest? | |
try { | |
req = composeFindPartiesByUrlFragmentRequest(urlFragment) | |
res = client.execute(req) | |
if (res == null) { | |
logger.error("findPartiesByUrlFragment(urlFragment='$urlFragment'): Null resspone") | |
return PartiesSearchResult(false, "Null response", emptyList()) | |
} | |
if (res.statusLine.statusCode != 200) { | |
logger.error("findPartiesByUrlFragment(urlFragment='$urlFragment'): Wrong status code ${res.statusLine.statusCode}") | |
return PartiesSearchResult(false, "Wrong status code", emptyList()) | |
} | |
if (res.entity == null) { | |
logger.error("findPartiesByUrlFragment(urlFragment='$urlFragment'): Null entity") | |
return PartiesSearchResult(false, "Null entity", emptyList()) | |
} | |
val content = res.entity.content | |
if (content == null) { | |
logger.error("findPartiesByUrlFragment(urlFragment='$urlFragment'): Null content") | |
return PartiesSearchResult(false, "Null content", emptyList()) | |
} | |
val parser = createJsonParser() | |
val json:JsonObject = parser.parse(content) | |
val partiesJson:List<JsonObject> = json["parties"] as List<JsonObject> | |
if (partiesJson == null) { | |
logger.error("findPartiesByUrlFragment(urlFragment='$urlFragment'): Null parties object") | |
return PartiesSearchResult(false, "Null parties object", emptyList()) | |
} | |
val parties = composePartiesList(partiesJson, urlFragment) | |
return PartiesSearchResult(true, "", parties) | |
} | |
catch (throwable:Throwable) { | |
logger.error("findPartiesByUrlFragment(urlFragment='$urlFragment')", throwable) | |
return PartiesSearchResult(false, composeErrorMsg (throwable.message), emptyList()) | |
} | |
finally { | |
close(res) | |
} | |
return PartiesSearchResult(false, "", emptyList()) | |
} | |
open fun close(httpClient: CloseableHttpClient?) { | |
httpClient?.close() | |
} | |
open fun close(res: CloseableHttpResponse?) { | |
res?.close() | |
} | |
open fun composePartiesList(partiesJson: List<JsonObject>, urlFragment: String) = | |
partiesJson | |
.map { toPartyObject(it, urlFragment) } | |
.toList() | |
open fun createJsonParser():TestableJsonParser = KlaxonParserWrapper() | |
open fun toPartyObject(json: JsonObject, urlFragment: String): Party { | |
val id = defaultValue(json.long("id"), -1L) | |
val websites: List<String> = composeWebSites(json, urlFragment) | |
return Party(id, websites) | |
} | |
open fun composeWebSites(json: JsonObject, urlFragment: String): List<String> { | |
val websitesArr = json.array<JsonObject>("websites") | |
val websites: List<String> | |
if (websitesArr != null) { | |
val searchItem = urlFragment.toLowerCase() | |
websites = websitesArr.filter { it != null } | |
.map { it.string("url") } | |
.filter { it != null } | |
.map { it as String } | |
.map(String::toLowerCase) | |
.filter { x -> (x != null) && x.contains(searchItem) } | |
.toList() | |
} else { | |
websites = emptyList() | |
} | |
return websites | |
} | |
open fun defaultValue(maybeNull: Long?, defaultValue: Long): Long { | |
if (maybeNull == null) { | |
return defaultValue | |
} | |
return maybeNull | |
} | |
open fun createDefaultHttpClient() = HttpClients.createDefault() | |
open fun composeFindPartiesByUrlFragmentRequest(urlFragment: String): HttpUriRequest { | |
val req = HttpGet("https://api.capsulecrm.com/api/v2/parties/search?q=${urlFragment}") | |
req.setHeader("Authorization", "Bearer ${ApiToken}") | |
req.setHeader("Accept", "application/json"); | |
return req | |
} | |
open fun composeErrorMsg(message: String?): String { | |
if (StringUtils.isBlank(message) || (message == null)) { | |
return "Unknown error" | |
} | |
return message | |
} | |
override fun createCompany(compData: Bp1CompanyData): ValidationResult { | |
val client = httpClient | |
if (client == null) { | |
return ValidationResult(false, "Internal error") | |
} | |
val companyExistsRes:FailableOperationResult<Boolean> = companyExists(compData) | |
if (!companyExistsRes.success || (companyExistsRes.result == null)) { | |
return ValidationResult(false, "Database error") | |
} | |
if (companyExistsRes.result) { | |
return ValidationResult(false, "Company already exists in the database") | |
} | |
return createCompanyProper(compData, client) | |
} | |
open fun createCompanyProper(compData: Bp1CompanyData, client: CloseableHttpClient): ValidationResult { | |
val createCompanyRes: FailableOperationResult<Long> = createCompanyInCapsule(client, compData) | |
val compId = createCompanyRes.result | |
when { | |
(createCompanyRes.success && (compId != null) && StringUtils.isNotBlank(compData.note)) -> | |
return addNote(compData.note, compId) | |
(createCompanyRes.success && (compId != null)) -> | |
return ValidationResult(true, "") | |
else -> return ValidationResult(false, "Couldn't create company") | |
} | |
} | |
open fun addNote(note: String, compId: Long): ValidationResult { | |
val client = httpClient | |
if (client == null) { | |
return ValidationResult(false, "Internal error") | |
} | |
var res: CloseableHttpResponse? = null | |
var req: HttpUriRequest? | |
try { | |
req = composeAddNoteRequest(note, compId) | |
res = client.execute(req) | |
if (res.statusLine.statusCode != 201) { | |
logger.error("addNote(note='$note', compId=$compId): Wrong status code ${res.statusLine.statusCode}") | |
return ValidationResult(false, "Wrong status code (CRM interaction)") | |
} | |
return ValidationResult(true, "") | |
} | |
catch (throwable: Throwable) { | |
logger.error("addNote(note='$note', compId=$compId)", throwable) | |
return ValidationResult(false, "Database error") | |
} finally { | |
close(res) | |
} | |
return ValidationResult(false, "Internal logic error") | |
} | |
open fun composeAddNoteRequest(text: String, compId: Long): HttpUriRequest { | |
val req = createHttpPost(AddNoteUrl) | |
req.setHeader("Authorization", "Bearer $ApiToken") | |
req.setHeader("Accept", "application/json"); | |
req.setHeader("Content-Type", "application/json"); | |
val jsonTxt = composeAddNoteJson(compId, text) | |
protocol.info("addNote(note='$text', compId=$compId): $jsonTxt") | |
logger.info("addNote(note='$text', compId=$compId): $jsonTxt") | |
req.entity = createStringEntity(jsonTxt) | |
return req | |
} | |
open fun createHttpPost(url: String) = HttpPost(url) | |
open fun composeAddNoteJson(compId: Long, text: String): String { | |
val entryJson = JsonObject( | |
mapOf( | |
"party" to JsonObject( | |
mapOf( | |
"id" to compId | |
) | |
), | |
"type" to "note", | |
"content" to text | |
) | |
) | |
val partyJson = JsonObject( | |
mapOf( | |
"entry" to entryJson | |
) | |
) | |
val jsonTxt = partyJson.toJsonString() | |
return jsonTxt | |
} | |
open fun createCompanyInCapsule( | |
client: CloseableHttpClient, | |
compData: Bp1CompanyData | |
): FailableOperationResult<Long> { | |
var res: CloseableHttpResponse? = null | |
var req: HttpUriRequest? | |
try { | |
req = createCreateCompanyRequest(compData) | |
res = client.execute(req) | |
if (res == null) { | |
logger.error("createCompanyProper(compData='${compData.toString()}'): Null resspone") | |
return FailableOperationResult<Long>(false, "Null response", null) | |
} | |
if (res.statusLine.statusCode != 201) { | |
logger.error("createCompanyProper(compData='${compData.toString()}'): Wrong status code ${res.statusLine.statusCode}") | |
return FailableOperationResult<Long>(false, "Wrong status code", null) | |
} | |
val companyId:Long = extractCompanyId(res) | |
return FailableOperationResult<Long>(true, "", companyId) | |
} catch (throwable: Throwable) { | |
logger.error("createCompanyProper(compData='${compData.toString()}')", throwable) | |
return FailableOperationResult<Long>(false, "Database error", null) | |
} finally { | |
close(res) | |
} | |
return FailableOperationResult<Long>(false, "Logic error", null) | |
} | |
open fun extractCompanyId(res: CloseableHttpResponse): Long { | |
val json = ObjectMapper().readTree(res.entity.content) | |
val party = json["party"] | |
val id = party["id"] | |
return id.asLong() | |
} | |
open fun composeListFieldsRequest(): HttpUriRequest { | |
val req = HttpGet("https://api.capsulecrm.com/api/v2/parties/fields/definitions") | |
req.setHeader("Authorization", "Bearer $ApiToken") | |
return req | |
} | |
open fun composeTagsRequest(): HttpUriRequest { | |
val req = HttpGet("https://api.capsulecrm.com/api/v2/parties/tags") | |
req.setHeader("Authorization", "Bearer $ApiToken") | |
return req | |
} | |
open fun createCreateCompanyRequest(data: Bp1CompanyData): HttpUriRequest { | |
val req = createHttpPost(CreateCompanyUrl) | |
req.setHeader("Authorization", "Bearer $ApiToken") | |
req.setHeader("Accept", "application/json"); | |
req.setHeader("Content-Type", "application/json"); | |
val jsonTxt = createCreateCompanyJson(data) | |
protocol.info("createCreateCompanyRequest(${data.toString()}): $jsonTxt") | |
logger.info("createCreateCompanyRequest(${data.toString()}): $jsonTxt") | |
req.entity = createStringEntity(jsonTxt) | |
return req | |
} | |
open fun createCreateCompanyJson(data: Bp1CompanyData): String { | |
val orjJson = JSONObject() | |
orjJson.put("type", "organisation") | |
orjJson.put("title", data.url) | |
orjJson.put("name", data.url) | |
if (data.ctype == ContactDataType.EMAIL) { | |
orjJson.put("emailAddresses", createEmailAddressJson(data.email)) | |
} | |
orjJson.put("websites", composeWebSitesJson(data)) | |
val agentJson = JSONObject() | |
agentJson.put("value", data.agent) | |
agentJson.put("definition", composeFieldDefinition(AgentFieldId)) | |
val fieldsList = JSONArray(listOf(agentJson)) | |
orjJson.put("fields", fieldsList) | |
val tags = JSONArray(listOf(JSONObject(mapOf( | |
"name" to "SEO" | |
)))) | |
orjJson.put("tags", tags) | |
val partyJson = JsonObject( | |
mapOf( | |
"party" to orjJson | |
) | |
) | |
val jsonTxt = partyJson.toJsonString() | |
return jsonTxt | |
} | |
open fun createStringEntity(jsonTxt: String) = StringEntity(jsonTxt, ContentType.APPLICATION_JSON) | |
open fun composeFieldDefinition(id: Int): JSONObject = | |
JSONObject( | |
mapOf( | |
"id" to id | |
) | |
) | |
open fun composeWebSitesJson(data: Bp1CompanyData): JSONArray { | |
val urls:List<String> | |
when (data.ctype) { | |
ContactDataType.EMAIL -> urls = listOf(data.url) | |
ContactDataType.CONTACT_FORM -> urls = listOf<String>(data.url, data.contactFormUrl) | |
ContactDataType.UNKNOWN -> urls = emptyList() | |
} | |
val res = JSONArray() | |
urls.map { urlToJson(it) }.forEach { res.put(it) } | |
return res | |
} | |
open fun urlToJson(url: String): JSONObject = JSONObject(mapOf( | |
"service" to "URL", | |
"address" to url, | |
"type" to "Work" | |
)) | |
open fun createEmailAddressJson(email: String): JSONArray { | |
val node = JSONObject() | |
node.put("type", "Work") | |
node.put("address", email) | |
val res = JSONArray() | |
res.put(node) | |
return res | |
} | |
open fun companyExists(compData: Bp1CompanyData): FailableOperationResult<Boolean> { | |
val searchItem = extractHomepageUrl(compData.url) | |
val searchRes = this.findPartiesByUrlFragment(searchItem) | |
if (!searchRes.success || (searchRes.parties == null)) { | |
return FailableOperationResult(false, "Database error", null) | |
} | |
val res = !searchRes.parties.isEmpty() | |
return FailableOperationResult(true, "", res) | |
} | |
open fun extractHomepageUrl(url: String): String { | |
var rightUrl = url | |
if (!rightUrl.startsWith("http")) { | |
rightUrl = "http://${url}" | |
} | |
try { | |
val urlObj = URL(rightUrl) | |
var host = urlObj.host | |
if (host.startsWith(WwwPrefix)) { | |
host = host.substring(WwwPrefix.length) | |
} | |
return host.toLowerCase() | |
} catch (throwable:Throwable) { | |
return url | |
} | |
} | |
/** | |
* Don't fucking delete this method. | |
*/ | |
fun listCustomFields() { | |
var httpClient:CloseableHttpClient? = null | |
var res:CloseableHttpResponse? = null | |
var req: HttpUriRequest? | |
try { | |
req = composeListFieldsRequest() | |
httpClient = createDefaultHttpClient() | |
res = httpClient.execute(req) | |
if (res == null) { | |
logger.error("listCustomFields") | |
return | |
} | |
if (res.statusLine.statusCode != 200) { | |
logger.error("listCustomFields") | |
return | |
} | |
val result = IOUtils.toString(res.entity.content) | |
logger.info("result: $result") | |
return | |
} | |
catch (throwable:Throwable) { | |
logger.error("listCustomFields')", throwable) | |
} | |
finally { | |
close(res) | |
} | |
} | |
/** | |
* Don't fucking delete this method. | |
*/ | |
fun listTags() { | |
var httpClient:CloseableHttpClient? = null | |
var res:CloseableHttpResponse? = null | |
var req: HttpUriRequest? | |
try { | |
req = composeTagsRequest() | |
httpClient = createDefaultHttpClient() | |
res = httpClient.execute(req) | |
if (res == null) { | |
logger.error("listCustomFields") | |
return | |
} | |
if (res.statusLine.statusCode != 200) { | |
logger.error("listCustomFields") | |
return | |
} | |
val result = IOUtils.toString(res.entity.content) | |
logger.info("result: $result") | |
return | |
} | |
catch (throwable:Throwable) { | |
logger.error("listCustomFields')", throwable) | |
} | |
finally { | |
close(res) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment