Example #1
0
        /// <summary>
        /// Update arbitrary contact properties
        /// </summary>
        /// <param name="vid">the unique HubSpot ID of the Contact</param>
        /// <param name="props">A HubSpotContactProperties object that specifies all the properties to be changed</param>
        /// <param name="log"></param>
        /// <param name="isTest"></param>
        /// <returns></returns>
        public async Task <HubSpotContactResult> UpdateContactDetailsAsync(
            string vid,
            HubSpotContactProperties props,
            ILogger log,
            bool isTest)
        {
            // Check that the appropriate Hubspot API key was correctly retrieved in the static constructor
            var activeHapiKey = isTest ? sandbox_hapikey : hapikey;

            if (string.IsNullOrEmpty(activeHapiKey))
            {
                return(new HubSpotContactResult(HttpStatusCode.InternalServerError, "Hubspot API key not found"));
            }

            var url = string.Format($"https://api.hubapi.com/contacts/v1/contact/vid/{vid}/profile?hapikey={activeHapiKey}");

            log.LogInformation($"Updating {vid} details");
            HttpResponseMessage response = await httpClient.PostAsJsonAsync(url, props);

            // Check Status Code.
            if (response.StatusCode == HttpStatusCode.NoContent)
            {
                log.LogInformation("Hubspot status update OK");
                var retval = new HubSpotContactResult(HttpStatusCode.OK);
                return(retval);
            }
            else
            {
                // Some kind of error - return the status code and body to the caller of the function
                string resultText = await response.Content.ReadAsStringAsync();

                log.LogError("Hubspot update failed: {0}: {1}", response.StatusCode, resultText);
                return(new HubSpotContactResult(response.StatusCode, resultText));
            }
        }
        public async Task verify_we_can_update_a_regular_property()
        {
            var contact = await _contactCreationFixture.CreateTestContact();

            var adapter = new HubspotAdapter();
            var props   = new HubSpotContactProperties();

            props.Add("mobilephone", "0499888777");
            props.Add("city", "Update City");
            var response = await adapter.UpdateContactDetailsAsync(_contactCreationFixture.TestContactId, props, _logger, isTest : true);

            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

            response = await adapter.RetrieveHubspotContactById(_contactCreationFixture.TestContactId, false, _logger, isTest : true);

            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

            contact = response.Payload;
            Assert.Equal("Update City", contact.city);
        }
Example #3
0
        /// <summary>
        /// Create a contact in Hubspot.
        /// </summary>
        /// <param name="email"></param>
        /// <param name="firstname"></param>
        /// <param name="lastname"></param>
        /// <returns>Returns the created contact. If a contact with this email already exists, returns the conflicting contact from HubSpot</returns>
        /// <see cref="https://developers.hubspot.com/docs/methods/contacts/create_contact"/>
        public async Task <HubSpotContactResult> CreateHubspotContactAsync(
            string email,
            string firstname,
            string lastname,
            string preferredName,
            string primaryPhone,
            string streetAddress1,
            string streetAddress2,
            string city,
            string state,
            string postcode,
            string leadStatus,
            bool installationRecordExists,
            ILogger log,
            bool isTest)
        {
            // Check that the appropriate Hubspot API key was correctly retrieved in the static constructor
            var activeHapiKey = isTest ? sandbox_hapikey : hapikey;

            // Check that the Hubspot API key was correctly retrieved in the static constructor
            if (string.IsNullOrEmpty(activeHapiKey))
            {
                return(new HubSpotContactResult(HttpStatusCode.InternalServerError, "Hubspot API key not found"));
            }

            if (!(new EmailAddressAttribute().IsValid(email)))
            {
                return(new HubSpotContactResult(HttpStatusCode.InternalServerError, "New Contact email address not supplied"));
            }

            // To send a Contact to Hubspot via the API we need an object that will serialise into a bunch of {property, value} pairs
            HubSpotContactProperties newContactProperties = AssembleContactProperties(
                email,
                firstname,
                lastname,
                preferredName,
                primaryPhone,
                streetAddress1,
                streetAddress2,
                city,
                state,
                postcode,
                leadStatus,
                installationRecordExists);

            if (newContactProperties == null)
            {
                return(new HubSpotContactResult(HttpStatusCode.InternalServerError, "unhandled error assembling new contact command"));
            }

            var url = string.Format($"https://api.hubapi.com/contacts/v1/contact/?hapikey={activeHapiKey}");

            //var dbg = JsonConvert.SerializeObject(newContactProperties);

            // Need to POST to Hubspot to create a contact
            log.LogInformation($"Posting to HubSpot to create {firstname} {lastname}");
            HttpResponseMessage response = await httpClient.PostAsJsonAsync(url, newContactProperties);

            HttpContent content = response.Content;

            // Check Status Code.
            if (response.StatusCode == HttpStatusCode.OK)
            {
                log.LogInformation("Hubspot creation OK");
                // All good. Read the string out of the body
                string resultText = await content.ReadAsStringAsync();

                var newContactResponse = ConvertHubspotJsonToCanonicalContact(resultText, fetchPreviousValues: false, log: log);

                log.LogInformation("New Contact identifier is {0}", newContactResponse.contactId);

                var retval = new HubSpotContactResult(newContactResponse);
                return(retval);
            }
            else if (response.StatusCode == HttpStatusCode.Conflict)
            {
                // The contact already exists. That's OK - we need to direct this contact to a human for review.
                log.LogWarning($"Contact already exists: {email}");

                // Let's read the whole contact back, then.
                var actualContact = await RetrieveHubspotContactByEmailAddr(email, false, log, isTest);

                var retval = new HubSpotContactResult(actualContact.Payload);
                retval.StatusCode = response.StatusCode;
                return(retval);
            }
            else
            {
                // Some other error - return the status code and body to the caller of the function
                string resultText = await content.ReadAsStringAsync();

                log.LogError("Hubspot creation failed: {0}: {1}", response.StatusCode, resultText);
                return(new HubSpotContactResult(response.StatusCode, resultText));
            }
        }
Example #4
0
        /// <summary>
        /// Convert a bunch of properties to the name-value structure required by the HubSpot 'create' API
        /// </summary>
        /// <param name="email"></param>
        /// <param name="firstname"></param>
        /// <param name="lastname"></param>
        /// <param name="preferredName"></param>
        /// <param name="primaryPhone"></param>
        /// <param name="streetAddress"></param>
        /// <param name="city"></param>
        /// <param name="state"></param>
        /// <param name="postcode"></param>
        /// <param name="leadStatus"></param>
        /// <returns></returns>
        internal static HubSpotContactProperties AssembleContactProperties(
            string email,
            string firstname,
            string lastname,
            string preferredName,
            string primaryPhone,
            string streetAddress1,
            string streetAddress2,
            string city,
            string state,
            string postcode,
            string leadStatus,
            bool installationRecordExists)
        {
            var newContactProperties = new HubSpotContactProperties();

            // Need to sanitise the properties received here.
            if (new EmailAddressAttribute().IsValid(email))
            {
                newContactProperties.Add("email", email);
            }
            else
            {
                // This should not occur, as it will be filtered out by the caller.
                return(null);
            }

            if (UserInputIsValid(firstname))
            {
                newContactProperties.Add("firstname", firstname);
            }

            if (UserInputIsValid(lastname))
            {
                newContactProperties.Add("lastname", lastname);
            }

            if (UserInputIsValid(preferredName))
            {
                newContactProperties.Add("preferred_name", preferredName);
            }

            if (UserInputIsValid(primaryPhone))
            {
                newContactProperties.Add("phone", primaryPhone);
            }

            if (UserInputIsValid(streetAddress1, false) || UserInputIsValid(streetAddress2, false))
            {
                var comma = string.Empty;
                if (!string.IsNullOrEmpty(streetAddress1) && !string.IsNullOrEmpty(streetAddress2))
                {
                    comma = ", ";
                }
                var address = streetAddress1 + comma + streetAddress2;
                newContactProperties.Add("address", address); // Hubspot expects street, unit, apartment, etc to be concatenated.
            }

            if (UserInputIsValid(city))
            {
                newContactProperties.Add("city", city);
            }

            if (UserInputIsValid(state))
            {
                newContactProperties.Add("state", state);
            }

            if (UserInputIsValid(postcode))
            {
                newContactProperties.Add("zip", postcode);
            }

            newContactProperties.Add("hs_lead_status", ResolveLeadStatus(leadStatus));
            newContactProperties.Add("lifecyclestage", "lead"); // They are not just a subscriber
            newContactProperties.Add("installationrecordexists", installationRecordExists?"true":"false");

            return(newContactProperties);
        }
Example #5
0
        /// <summary>
        /// Updates a HubSpot contact fields with the given contract status
        /// </summary>
        /// <param name="email"></param>
        /// <param name="status"></param>
        /// <param name="log"></param>
        /// <param name="isTest"></param>
        /// <returns></returns>
        /// <remarks>Make 'status' an enum that reflects the Sharepoint 'ContractStatus' field</remarks>
        public async Task <HubSpotContactResult> UpdateContractStatusAsync(
            string email,
            string status,
            ILogger log,
            bool isTest)
        {
            // Check that the appropriate Hubspot API key was correctly retrieved in the static constructor
            var activeHapiKey = isTest ? sandbox_hapikey : hapikey;

            if (string.IsNullOrEmpty(activeHapiKey))
            {
                return(new HubSpotContactResult(HttpStatusCode.InternalServerError, "Hubspot API key not found"));
            }

            // To update a Contact Property we need a HubSpotContactProperties object that specifies all the properties
            var props = new HubSpotContactProperties();

            var internalLeadStatusValue = string.Empty;

            // For now, we update a few fields:
            // 'Lead Status' - used by Brian for reporting. Not sustainable, will fall over when the a contact has two installations
            //                 I would like to retire these two states from Lead Status and re-do the reports.
            //
            // 'TotalSentContracts'
            // 'TotalSignedContracts' - these will support aggregates. But aggregates are expense to obtain from Sharepoint. When we
            //                          move the Installations table to SQL Server, we will revisit this logic and compute aggregates.
            //
            // So there is a temporary hack here right now. We're not computing proper totals.

            switch (status.ToLower())
            {
            case "sent":
                props.Add("totalsentcontracts", "1");
                internalLeadStatusValue = "CONTRACT_SENT";
                break;

            case "signed":
                props.Add("totalsignedcontracts", "1");
                internalLeadStatusValue = "CONTRACT_SIGNED";
                break;

            case "rejected":
                props.Add("totalsignedcontracts", "0");
                internalLeadStatusValue = "NOT_INTERESTED";
                break;

            default:
                throw new CrmUpdateHandlerException("Unrecognised contract status: '" + status + "'");
            }
            props.Add("hs_lead_status", internalLeadStatusValue);

            // We need to resolve the email into a contact id (a 'vid', in HubSpot language)
            var hubSpotContactResult = await this.RetrieveHubspotContactByEmailAddr(email, false, log, isTest);

            if (hubSpotContactResult.StatusCode != HttpStatusCode.OK)
            {
                log.LogInformation($"Error {hubSpotContactResult.StatusCode} retrieving HubSpot details for '{email}': {hubSpotContactResult.ErrorMessage}");
                return(hubSpotContactResult);
            }

            // Great, we managed to retrieve the contact via the email address.
            var vid = hubSpotContactResult.Payload.contactId;

            // Now just POST it - see https://developers.hubspot.com/docs/methods/contacts/update_contact
            // Returns a 204 No Content on success
            var url = string.Format($"https://api.hubapi.com/contacts/v1/contact/vid/{vid}/profile?hapikey={activeHapiKey}");

            log.LogInformation($"Updating {vid} contract status to {status}");
            HttpResponseMessage response = await httpClient.PostAsJsonAsync(url, props);

            // Check Status Code.
            if (response.StatusCode == HttpStatusCode.NoContent)
            {
                log.LogInformation("Hubspot status update OK");
                var retval = new HubSpotContactResult(HttpStatusCode.OK);
                return(retval);
            }
            else
            {
                // Some kind of error - return the status code and body to the caller of the function
                string resultText = await response.Content.ReadAsStringAsync();

                log.LogError("Hubspot update failed: {0}: {1}", response.StatusCode, resultText);
                return(new HubSpotContactResult(response.StatusCode, string.Format($"Error {response.StatusCode}: '{resultText}'")));
            }
        }