Skip to content

Commit e95aeea

Browse files
committed
V14: Integrations (HubSpot/Forms)
- Add new management api for hubsport forms - edit test project
1 parent d371554 commit e95aeea

24 files changed

+982
-64
lines changed

src/Umbraco.Forms.Integrations.Crm.Hubspot.Tests/Umbraco.Forms.Integrations.Crm.Hubspot.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55

66
<IsPackable>false</IsPackable>
77
</PropertyGroup>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Umbraco.Forms.Integrations.Crm.Hubspot.Models.Dtos;
9+
using Umbraco.Forms.Integrations.Crm.Hubspot.Services;
10+
11+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers.Forms
12+
{
13+
public class AuthorizeController : FormsControllerBase
14+
{
15+
public AuthorizeController(IContactService contactService) : base(contactService)
16+
{
17+
}
18+
19+
[HttpPost("authorize")]
20+
[ProducesResponseType(typeof(Task<AuthorizationResult>), StatusCodes.Status200OK)]
21+
public IActionResult Authorize([FromBody] AuthorizationRequest request) => Ok(ContactService.AuthorizeAsync(request.Code));
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Umbraco.Forms.Integrations.Crm.Hubspot.Models.Dtos;
9+
using Umbraco.Forms.Integrations.Crm.Hubspot.Services;
10+
11+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers.Forms
12+
{
13+
public class DeauthorizeController : FormsControllerBase
14+
{
15+
public DeauthorizeController(IContactService contactService) : base(contactService)
16+
{
17+
}
18+
19+
[HttpPost("deauthorize")]
20+
[ProducesResponseType(typeof(AuthorizationResult), StatusCodes.Status200OK)]
21+
public IActionResult Deauthorize() => Ok(ContactService.Deauthorize());
22+
}
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Umbraco.Cms.Web.Common.Routing;
9+
using Umbraco.Forms.Integrations.Crm.Hubspot.Services;
10+
11+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers.Forms
12+
{
13+
[ApiVersion("1.0")]
14+
[BackOfficeRoute($"{Constants.ManagementApi.RootPath}/v{{version:apiVersion}}/forms")]
15+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.FormsGroupName)]
16+
public class FormsControllerBase : HubspotControllerBase
17+
{
18+
protected readonly IContactService ContactService;
19+
public FormsControllerBase(IContactService contactService)
20+
{
21+
ContactService = contactService;
22+
}
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Umbraco.Cms.Web.Common.Attributes;
9+
using Umbraco.Forms.Integrations.Crm.Hubspot.Models.Responses;
10+
using Umbraco.Forms.Integrations.Crm.Hubspot.Services;
11+
12+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers.Forms
13+
{
14+
public class GetAllPropertiesController : FormsControllerBase
15+
{
16+
public GetAllPropertiesController(IContactService contactService) : base(contactService)
17+
{
18+
}
19+
20+
[HttpGet("properties")]
21+
[ProducesResponseType(typeof(IEnumerable<Property>), StatusCodes.Status200OK)]
22+
public IActionResult GetAll() => Ok(ContactService.GetContactPropertiesAsync());
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Umbraco.Forms.Integrations.Crm.Hubspot.Services;
9+
10+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers.Forms
11+
{
12+
public class GetAuthenticationUrlController : FormsControllerBase
13+
{
14+
public GetAuthenticationUrlController(IContactService contactService) : base(contactService)
15+
{
16+
}
17+
18+
[HttpGet("auth-url")]
19+
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
20+
public string GetAuthenticationUrl() => ContactService.GetAuthenticationUrl();
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using Umbraco.Forms.Integrations.Crm.Hubspot.Services;
9+
10+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers.Forms
11+
{
12+
public class IsAuthorizationConfiguredController : FormsControllerBase
13+
{
14+
public IsAuthorizationConfiguredController(IContactService contactService) : base(contactService)
15+
{
16+
}
17+
18+
[HttpGet("auth-configured")]
19+
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
20+
public IActionResult IsAuthorizationConfigured() => Ok(ContactService.IsAuthorizationConfigured().ToString());
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Authorization;
3+
using Microsoft.AspNetCore.Mvc;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Umbraco.Cms.Api.Common.Attributes;
10+
using Umbraco.Cms.Web.Common.Authorization;
11+
using Umbraco.Cms.Web.Common.Routing;
12+
13+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers
14+
{
15+
[ApiController]
16+
[Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)]
17+
[MapToApi(Constants.ManagementApi.ApiName)]
18+
public class HubspotControllerBase : Controller
19+
{
20+
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Options;
5+
using Microsoft.Extensions.Primitives;
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Linq;
9+
using System.Net.Http;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
using Umbraco.Cms.Web.Common.Routing;
13+
using Umbraco.Forms.Integrations.Crm.Hubspot.Configuration;
14+
15+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers.OAuthProxy
16+
{
17+
[ApiVersion("1.0")]
18+
[BackOfficeRoute($"{Constants.ManagementApi.RootPath}/v{{version:apiVersion}}/oauth")]
19+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.OAuthGroupName)]
20+
public class OAuthProxyControllerBase : HubspotControllerBase
21+
{
22+
protected readonly IHttpClientFactory HttpClientFactory;
23+
protected readonly AppSettings AppSettings;
24+
public const string ServiceNameHeaderKey = "service_name";
25+
public const string ServiceAddressReplacePrefixHeaderKey = "service_address_";
26+
27+
public OAuthProxyControllerBase(IHttpClientFactory httpClientFactory, IOptions<AppSettings> appSettings)
28+
{
29+
HttpClientFactory = httpClientFactory;
30+
AppSettings = appSettings.Value;
31+
}
32+
33+
protected async Task ProxyTokenRequest()
34+
{
35+
if (!Request.Headers.TryGetValue(ServiceNameHeaderKey, out var serviceName))
36+
{
37+
serviceName = "Hubspot";
38+
}
39+
40+
if (!ServiceConfiguration.ServiceProviders.ContainsKey(serviceName))
41+
{
42+
throw
43+
new InvalidOperationException($"Provided {ServiceNameHeaderKey} header value of {serviceName} is not supported");
44+
}
45+
46+
var httpClient = GetClient(serviceName);
47+
48+
var response =
49+
await httpClient.PostAsync(ServiceConfiguration.ServiceProviders[serviceName], GetContent(Request.Form, serviceName));
50+
var content = await response.Content.ReadAsStringAsync();
51+
52+
Response.StatusCode = (int)response.StatusCode;
53+
54+
Response.ContentType = response.Content.Headers.ContentType?.ToString();
55+
Response.ContentLength = response.Content.Headers.ContentLength;
56+
57+
await Response.WriteAsync(content);
58+
}
59+
60+
private HttpClient GetClient(string serviceName)
61+
{
62+
var httpClient = HttpClientFactory.CreateClient($"{serviceName}Token");
63+
64+
// Shopify's endpoint (and potentially others in future) for retrieving the access token is directly coupled with the shop's name.
65+
// As a result the address of the client needs to be updated with the request header value for the name of the shop.
66+
var serviceAddressReplaceHeader = Request.Headers.FirstOrDefault(p => p.Key.ToLowerInvariant().Contains(ServiceAddressReplacePrefixHeaderKey));
67+
if (!serviceAddressReplaceHeader.Equals(default(KeyValuePair<string, StringValues>)) && httpClient.BaseAddress != null)
68+
{
69+
var replaceKey = serviceAddressReplaceHeader.Key.ToLowerInvariant().Replace(ServiceAddressReplacePrefixHeaderKey, string.Empty);
70+
71+
var baseAddress = httpClient.BaseAddress.ToString().Replace($"{replaceKey}", serviceAddressReplaceHeader.Value);
72+
httpClient.BaseAddress = new Uri(baseAddress);
73+
}
74+
75+
return httpClient;
76+
}
77+
78+
private HttpContent GetContent(IFormCollection form, string serviceName)
79+
{
80+
var dictionary = form.ToDictionary(x => x.Key, x => x.Value.ToString());
81+
dictionary.Add("client_secret", GetClientSecret(serviceName));
82+
return new FormUrlEncodedContent(dictionary);
83+
}
84+
85+
private string GetClientSecret(string serviceName) => AppSettings[$"{serviceName}ClientSecret"];
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Extensions.Options;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Umbraco.Forms.Integrations.Crm.Hubspot.Configuration;
10+
11+
namespace Umbraco.Forms.Integrations.Crm.Hubspot.Api.Management.Controllers.OAuthProxy
12+
{
13+
public class ProxyTokenRequestController : OAuthProxyControllerBase
14+
{
15+
public ProxyTokenRequestController(IHttpClientFactory httpClientFactory, IOptions<AppSettings> appSettings) : base(httpClientFactory, appSettings)
16+
{
17+
}
18+
19+
[HttpPost("token")]
20+
[ProducesResponseType(StatusCodes.Status200OK)]
21+
public IActionResult Token()
22+
{
23+
return Ok(ProxyTokenRequest());
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)