private void HappyOrSadPathTruths(SerialSyncResult result, SerialHubSpotContact[] hubSpotContacts, int successCount, int failureCount) { HappyOrSadPathTruths(result, hubSpotContacts.Length, hubSpotContacts.Length, successCount, failureCount); // assert behavior _httpMock.Verify(http => http.Post(It.IsAny <string>(), It.IsAny <SerialHubSpotContact>()), Times.Exactly(hubSpotContacts.Length)); }
private TDto SerialGet <TDto>(SerialHubSpotContact hubSpotContact, SerialSyncResult run) { var response = _http.Get($"contacts/v1/contact/email/{hubSpotContact.Email}/profile?hapikey={_hubSpotApiKey}&property=vid"); run.GetCount++; var dto = default(TDto); switch (response.StatusCode) { case HttpStatusCode.OK: // 200; update only endpoint _logger.LogInformation($"Retrieved: contact {hubSpotContact.Email}.\r\njson: {dto}"); dto = _http.GetResponseContent <TDto>(response); break; case HttpStatusCode.NotFound: // 404; contact with supplied email address does not exist _logger.LogWarning($"Not Found. Contact does not exist\r\njson: {_serializer.Serialize(hubSpotContact)}"); break; default: // could not get contact _logger.LogWarning($"Exception occurred while GETting contact [{hubSpotContact.Email}]"); break; } return(dto); }
private void SetFailureData(SerialSyncResult run, HttpResponseMessage response, IHubSpotContact hubSpotContact, int contactLength, int currentContactIndex) { run.FailureCount++; var failure = new SerialSyncFailure { HttpStatusCode = response.StatusCode, Exception = _http.GetResponseContent <HubSpotException>(response), HubSpotContact = hubSpotContact }; run.Failures.Add(failure); LogContactFailure(failure, hubSpotContact, currentContactIndex, contactLength); }
/// <summary> /// https://developers.hubspot.com/docs/methods/contacts/create_contact /// </summary> public SerialSyncResult SerialCreate(SerialHubSpotContact[] hubSpotContacts) { var run = new SerialSyncResult(_clock.UtcNow) { TotalContacts = hubSpotContacts.Length }; try { for (int currentContactIndex = 0; currentContactIndex < hubSpotContacts.Length; currentContactIndex++) { var contact = hubSpotContacts[currentContactIndex]; var response = _http.Post($"contacts/v1/contact?hapikey={_hubSpotApiKey}", contact); switch (response.StatusCode) { case HttpStatusCode.OK: // 200; create endpoint _logger.LogInformation($"Created: contact {currentContactIndex + 1} of {hubSpotContacts.Length}"); run.SuccessCount++; run.InsertCount++; break; case HttpStatusCode.Conflict: // 409; create endpoint; already exists -- when we attempt to create a contact with an email address that has already been claimed run.EmailAddressesAlreadyExist.Add(contact); run.EmailAddressAlreadyExistsCount++; break; default: // contact was rejected for create SetFailureData(run, response, contact, hubSpotContacts.Length, currentContactIndex); break; } PumpTheBreaksEveryNRequestsToAvoid429Exceptions(currentContactIndex + 1); } return(run); } finally { run.Execution.FinishUtc = _clock.UtcNow; } }
/// <summary> /// https://developers.hubspot.com/docs/methods/contacts/update_contact-by-email /// </summary> public SerialSyncResult SerialUpdate(SerialHubSpotContact[] hubSpotContacts) { var run = new SerialSyncResult(_clock.UtcNow) { TotalContacts = hubSpotContacts.Length }; try { for (int currentContactIndex = 0; currentContactIndex < hubSpotContacts.Length; currentContactIndex++) { var contact = hubSpotContacts[currentContactIndex]; SerialUpdate(currentContactIndex, contact, hubSpotContacts.Length, run); PumpTheBreaksEveryNRequestsToAvoid429Exceptions(currentContactIndex + 1); } return(run); } finally { run.Execution.FinishUtc = _clock.UtcNow; } }
/// <summary> /// Responsible for deleting the contact record of the old email address that is not able to be updated /// to the "new" email address due to the fact that the email address we wish to switch to already exists. /// We're ok deleting the existing account b/c Ministry Platform's dp_Users.User_Name field is our source /// of truth, is not nullable and has a unique constraint; so the contact attempting to update to a given /// email address is the true owner of the account. An edge case, but I've seen this happen and would like to /// minimize the cruft created by this app in HubSpot. /// /// 1) Get contact by old email address /// 2) Delete contact by VID (acquired by old email address) /// 3) Update contact in HubSpot with new email address in both the url and the post body /// </summary> public SerialSyncResult ReconcileConflicts(SerialHubSpotContact[] hubSpotContacts) { const int requestsPerReconciliation = 3, requestsPerSecond = 9; int reconciliationIteration = 1; var run = new SerialSyncResult(_clock.UtcNow) { TotalContacts = hubSpotContacts.Length }; try { for (int currentContactIndex = 0; currentContactIndex < hubSpotContacts.Length; currentContactIndex++) { var contact = hubSpotContacts[currentContactIndex]; var hubSpotContact = SerialGet <HubSpotVidResult>(contact, run); // HubSpot request #1 run = SerialDelete(currentContactIndex, contact, hubSpotContact, hubSpotContacts.Length, run); // HubSpot request #2 contact.Email = contact.Properties.First(p => p.Name == "email").Value; // reset email to the existing one and re-run it run = SerialUpdate(currentContactIndex, contact, hubSpotContacts.Length, run); // HubSpot request #3 PumpTheBreaksEveryNRequestsToAvoid429Exceptions(reconciliationIteration * requestsPerReconciliation, requestsPerSecond); if (reconciliationIteration == requestsPerReconciliation) { reconciliationIteration = 0; // reset for the next sleep 3 reconciliations later } reconciliationIteration++; } return(run); } finally { run.Execution.FinishUtc = _clock.UtcNow; } }
private SerialSyncResult SerialDelete(int currentContactIndex, SerialHubSpotContact hubSpotContact, HubSpotVidResult hubSpotVidResult, int contactCount, SerialSyncResult run) { if (hubSpotVidResult == null) { return(run); } var response = _http.Delete($"contacts/v1/contact/vid/{hubSpotVidResult.ContactVid}?hapikey={_hubSpotApiKey}"); run.DeleteCount++; switch (response.StatusCode) { case HttpStatusCode.OK: // 200; when contact is deleted successfully _logger.LogInformation($"Deleted: contact {currentContactIndex + 1} of {contactCount}"); break; case HttpStatusCode.NotFound: // 404; when the contact vid does not exist _logger.LogWarning($"No contact in HubSpot to delete.\r\njson: {_serializer.Serialize(hubSpotContact)}"); break; default: // contact was rejected for update (application exception) SetFailureData(run, response, hubSpotContact, contactCount, currentContactIndex); break; } return(run); }
private SerialSyncResult SerialUpdate(int currentContactIndex, SerialHubSpotContact hubSpotContact, int contactCount, SerialSyncResult run) { var response = _http.Post($"contacts/v1/contact/email/{hubSpotContact.Email}/profile?hapikey={_hubSpotApiKey}", hubSpotContact); switch (response.StatusCode) { case HttpStatusCode.NoContent: // 204; update only endpoint _logger.LogInformation($"Updated: contact {currentContactIndex + 1} of {contactCount}"); run.SuccessCount++; run.UpdateCount++; break; case HttpStatusCode.NotFound: // 404; update only endpoint; contact does not exist run.EmailAddressesDoNotExist.Add(hubSpotContact); run.EmailAddressDoesNotExistCount++; break; case HttpStatusCode.Conflict: // 409; update endpoint; already exists -- when a contact attempts to update their email address to one already claimed run.EmailAddressesAlreadyExist.Add(hubSpotContact); run.EmailAddressAlreadyExistsCount++; break; default: // contact was rejected for update (application exception) SetFailureData(run, response, hubSpotContact, contactCount, currentContactIndex); break; } return(run); }