Skip to content

Commit 93eb6bb

Browse files
authored
Merge pull request #202 from umbraco/v14/feature/42318-V14-Integrations-Zapier
42318 V14 Integration: Zapier
2 parents d43cffd + e2aed6e commit 93eb6bb

File tree

71 files changed

+8519
-772
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+8519
-772
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ src/Testsite/App_Plugins/Our.Umbraco.DashIt
66
src/Testsite/umbraco
77
src/Testsite/Views
88
src/Testsite/wwwroot
9+
src/Umbraco.Cms.Integrations.Automation.Zapier/wwwroot
910
src/Umbraco.Cms.Integrations.Commerce.Shopify/wwwroot
11+
src/Umbraco.Cms.Integrations.Crm.Hubspot/wwwroot
1012

1113

1214
# User-specific files
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Umbraco.Cms.Api.Management.OpenApi;
2+
3+
namespace Umbraco.Cms.Integrations.Automation.Zapier.Api.Configuration
4+
{
5+
public class BackOfficeSecurityRequirementsOperationFilter : BackOfficeSecurityRequirementsOperationFilterBase
6+
{
7+
protected override string ApiName => Constants.ManagementApi.ApiName;
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Options;
5+
using Umbraco.Cms.Integrations.Automation.Zapier.Configuration;
6+
using Umbraco.Cms.Integrations.Automation.Zapier.Helpers;
7+
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
8+
9+
namespace Umbraco.Cms.Integrations.Automation.Zapier.Api.Management.Controllers
10+
{
11+
[ApiVersion("1.0")]
12+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
13+
public class CheckFormExtensionController : ZapierControllerBase
14+
{
15+
public CheckFormExtensionController(IOptions<ZapierSettings> options, IUserValidationService userValidationService)
16+
: base(options, userValidationService)
17+
{
18+
}
19+
20+
[HttpGet("check-form-extension")]
21+
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
22+
public IActionResult CheckFormsExtensionInstalled() => Ok(ReflectionHelper.IsFormsExtensionInstalled);
23+
}
24+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Options;
5+
using Umbraco.Cms.Core.Services;
6+
using Umbraco.Cms.Integrations.Automation.Zapier.Configuration;
7+
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
8+
using Umbraco.Cms.Web.Common;
9+
using Umbraco.Extensions;
10+
11+
namespace Umbraco.Cms.Integrations.Automation.Zapier.Api.Management.Controllers
12+
{
13+
/// <summary>
14+
/// When a Zapier user creates a "New Content Published" triggered, they are authenticated, then select a content type, the API provides an output json with the
15+
/// structure of a content node matching the selected content type.
16+
/// For version 1.0.0 of the Umbraco Zapier App, the GetSampleContent will be used.
17+
/// </summary>
18+
[ApiVersion("1.0")]
19+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
20+
public class GetContentByTypeController : ZapierControllerBase
21+
{
22+
private readonly IContentTypeService _contentTypeService;
23+
24+
private readonly IZapierContentService _zapierContentService;
25+
26+
private readonly UmbracoHelper _umbracoHelper;
27+
28+
public GetContentByTypeController(
29+
IOptions<ZapierSettings> options,
30+
IContentService contentService,
31+
IContentTypeService contentTypeService,
32+
UmbracoHelper umbracoHelper,
33+
IUserValidationService userValidationService,
34+
IZapierContentService zapierContentService)
35+
: base(options, userValidationService)
36+
{
37+
_contentTypeService = contentTypeService;
38+
_zapierContentService = zapierContentService;
39+
_umbracoHelper = umbracoHelper;
40+
}
41+
42+
[HttpGet("content-type/{alias}/content")]
43+
[ProducesResponseType(typeof(List<Dictionary<string, string>>), StatusCodes.Status200OK)]
44+
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
45+
public IActionResult GetContentByContentType(string alias)
46+
{
47+
if (!IsAccessValid())
48+
return Unauthorized();
49+
50+
var contentType = _contentTypeService.Get(alias);
51+
if (contentType == null)
52+
return Ok(new List<Dictionary<string, string>>());
53+
54+
var contentItems = _umbracoHelper.ContentAtRoot().DescendantsOrSelfOfType(alias)
55+
.OrderByDescending(p => p.UpdateDate);
56+
var contentTypeDictionary = new List<Dictionary<string, string>>
57+
{
58+
_zapierContentService.GetContentTypeDictionary(contentType, contentItems.FirstOrDefault())
59+
};
60+
61+
return Ok(contentTypeDictionary);
62+
}
63+
}
64+
}

src/Umbraco.Cms.Integrations.Automation.Zapier/Controllers/ContentController.cs renamed to src/Umbraco.Cms.Integrations.Automation.Zapier/Api/Management/Controllers/GetContentTypesController.cs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,46 @@
1-
using System.Collections.Generic;
2-
using System.Linq;
3-
4-
using Umbraco.Cms.Integrations.Automation.Zapier.Models.Dtos;
5-
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
6-
7-
#if NETCOREAPP
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
84
using Microsoft.Extensions.Options;
9-
105
using Umbraco.Cms.Core.Services;
116
using Umbraco.Cms.Integrations.Automation.Zapier.Configuration;
12-
#else
13-
using Umbraco.Core.Services;
14-
#endif
7+
using Umbraco.Cms.Integrations.Automation.Zapier.Models.Dtos;
8+
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
159

16-
namespace Umbraco.Cms.Integrations.Automation.Zapier.Controllers
10+
namespace Umbraco.Cms.Integrations.Automation.Zapier.Api.Management.Controllers
1711
{
1812
/// <summary>
1913
/// When a Zapier user creates a new "New Content Published" trigger, the API is used to provide the list of content types for handling "Published" event.
2014
/// </summary>
21-
public class ContentController : ZapierAuthorizedApiController
15+
[ApiVersion("1.0")]
16+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
17+
public class GetContentTypesController : ZapierControllerBase
2218
{
2319
private readonly IContentTypeService _contentTypeService;
24-
25-
#if NETCOREAPP
26-
public ContentController(IOptions<ZapierSettings> options, IContentTypeService contentTypeService, IUserValidationService userValidationService)
20+
public GetContentTypesController(IOptions<ZapierSettings> options, IContentTypeService contentTypeService, IUserValidationService userValidationService)
2721
: base(options, userValidationService)
28-
#else
29-
public ContentController(IContentTypeService contentTypeService, IUserValidationService userValidationService)
30-
: base(userValidationService)
31-
#endif
3222
{
3323
_contentTypeService = contentTypeService;
3424
}
3525

36-
public IEnumerable<ContentTypeDto> GetContentTypes()
26+
[HttpGet("content-types")]
27+
[ProducesResponseType(typeof(IEnumerable<ContentTypeDto>), StatusCodes.Status200OK)]
28+
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
29+
public IActionResult GetContentTypes()
3730
{
38-
if (!IsAccessValid()) return null;
31+
if (!IsAccessValid())
32+
return Unauthorized();
3933

4034
var contentTypes = _contentTypeService.GetAll();
41-
42-
return contentTypes
35+
var mapToDto = contentTypes
4336
.Select(q => new ContentTypeDto
4437
{
4538
Id = q.Id,
4639
Alias = q.Alias,
4740
Name = q.Name
4841
});
42+
43+
return Ok(mapToDto);
4944
}
5045

5146
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Options;
5+
using Umbraco.Cms.Integrations.Automation.Zapier.Configuration;
6+
using Umbraco.Cms.Integrations.Automation.Zapier.Models.Dtos;
7+
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
8+
9+
namespace Umbraco.Cms.Integrations.Automation.Zapier.Api.Management.Controllers
10+
{
11+
[ApiVersion("1.0")]
12+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
13+
public class GetSubscriptionHookController : ZapierControllerBase
14+
{
15+
private readonly ZapierSubscriptionHookService _zapierSubscriptionHookService;
16+
17+
public GetSubscriptionHookController(
18+
IOptions<ZapierSettings> options,
19+
IUserValidationService userValidationService,
20+
ZapierSubscriptionHookService zapierSubscriptionHookService)
21+
: base(options, userValidationService) => _zapierSubscriptionHookService = zapierSubscriptionHookService;
22+
23+
[HttpGet("subscription-hooks")]
24+
[ProducesResponseType(typeof(IEnumerable<SubscriptionDto>), StatusCodes.Status200OK)]
25+
public IActionResult GetAll() => Ok(_zapierSubscriptionHookService.GetAll());
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,46 @@
1-
using System.Collections.Generic;
2-
using Umbraco.Cms.Integrations.Automation.Zapier.Models.Dtos;
3-
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
4-
5-
#if NETCOREAPP
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
63
using Microsoft.AspNetCore.Mvc;
74
using Microsoft.Extensions.Options;
8-
95
using Umbraco.Cms.Integrations.Automation.Zapier.Configuration;
10-
#else
11-
using System.Web.Http;
12-
#endif
6+
using Umbraco.Cms.Integrations.Automation.Zapier.Models.Dtos;
7+
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
138

14-
namespace Umbraco.Cms.Integrations.Automation.Zapier.Controllers
9+
namespace Umbraco.Cms.Integrations.Automation.Zapier.Api.Management.Controllers
1510
{
1611
/// <summary>
1712
/// Subscription API handling the ON/OFF trigger events in Zapier.
1813
/// </summary>
19-
public class SubscriptionController : ZapierAuthorizedApiController
14+
[ApiVersion("1.0")]
15+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
16+
public class UpdateSubscriptionHookController : ZapierControllerBase
2017
{
2118
private readonly ZapierSubscriptionHookService _zapierSubscriptionHookService;
2219

23-
#if NETCOREAPP
24-
public SubscriptionController(IOptions<ZapierSettings> options,
20+
public UpdateSubscriptionHookController(IOptions<ZapierSettings> options,
2521
ZapierSubscriptionHookService zapierSubscriptionHookService,
2622
IUserValidationService userValidationService)
2723
: base(options, userValidationService)
28-
#else
29-
public SubscriptionController(
30-
ZapierSubscriptionHookService zapierSubscriptionHookService,
31-
IUserValidationService userValidationService)
32-
: base(userValidationService)
33-
#endif
3424
{
3525
_zapierSubscriptionHookService = zapierSubscriptionHookService;
3626
}
3727

38-
[HttpPost]
39-
public bool UpdatePreferences([FromBody] SubscriptionDto dto)
28+
[HttpPost("subscription")]
29+
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
30+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
31+
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
32+
public IActionResult UpdatePreferences([FromBody] SubscriptionDto dto)
4033
{
41-
if (!IsAccessValid() || dto == null) return false;
34+
if (!IsAccessValid())
35+
return Unauthorized();
36+
37+
if (dto == null) return BadRequest();
4238

4339
var result = dto.SubscribeHook
4440
? _zapierSubscriptionHookService.Add(dto.EntityId, dto.Type, dto.HookUrl)
4541
: _zapierSubscriptionHookService.Delete(dto.EntityId, dto.Type, dto.HookUrl);
4642

47-
return string.IsNullOrEmpty(result);
43+
return Ok(string.IsNullOrEmpty(result));
4844
}
4945
}
5046
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Http;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.Extensions.Options;
5+
using Umbraco.Cms.Integrations.Automation.Zapier.Configuration;
6+
using Umbraco.Cms.Integrations.Automation.Zapier.Models;
7+
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
8+
9+
namespace Umbraco.Cms.Integrations.Automation.Zapier.Api.Management.Controllers
10+
{
11+
/// <summary>
12+
/// When a Zapier user creates triggers using the Umbraco app from the Zapier App Directory, they need to provide valid credentials for a backoffice account.
13+
/// </summary>
14+
[ApiVersion("1.0")]
15+
[ApiExplorerSettings(GroupName = Constants.ManagementApi.GroupName)]
16+
public class ValidateUserController : ZapierControllerBase
17+
{
18+
public ValidateUserController(IOptions<ZapierSettings> options, IUserValidationService userValidationService) : base(options, userValidationService)
19+
{
20+
}
21+
22+
[HttpPost("validate-user")]
23+
[ProducesResponseType(typeof(Task<bool>), StatusCodes.Status200OK)]
24+
public async Task<IActionResult> Validate([FromBody] UserModel userModel)
25+
{
26+
var result = await _userValidationService.Validate(userModel.Username, userModel.Password, userModel.ApiKey);
27+
return Ok(result);
28+
}
29+
}
30+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.Extensions.Options;
3+
using Umbraco.Cms.Api.Common.Attributes;
4+
using Umbraco.Cms.Integrations.Automation.Zapier.Configuration;
5+
using Umbraco.Cms.Integrations.Automation.Zapier.Services;
6+
using Umbraco.Cms.Web.Common.Routing;
7+
8+
namespace Umbraco.Cms.Integrations.Automation.Zapier.Api.Management.Controllers
9+
{
10+
[ApiController]
11+
[BackOfficeRoute($"{Constants.ManagementApi.RootPath}/v{{version:apiVersion}}")]
12+
[MapToApi(Constants.ManagementApi.ApiName)]
13+
public class ZapierControllerBase : Controller
14+
{
15+
private readonly ZapierSettings ZapierSettings;
16+
17+
protected IUserValidationService _userValidationService;
18+
19+
public ZapierControllerBase(IOptions<ZapierSettings> options, IUserValidationService userValidationService)
20+
{
21+
ZapierSettings = options.Value;
22+
23+
_userValidationService = userValidationService;
24+
}
25+
26+
protected bool IsAccessValid()
27+
{
28+
string username = string.Empty;
29+
string password = string.Empty;
30+
string apiKey = string.Empty;
31+
32+
if (Request.Headers.TryGetValue(Constants.ZapierAppConfiguration.UsernameHeaderKey,
33+
out var usernameValues))
34+
username = usernameValues.First();
35+
if (Request.Headers.TryGetValue(Constants.ZapierAppConfiguration.PasswordHeaderKey,
36+
out var passwordValues))
37+
password = passwordValues.First();
38+
if (Request.Headers.TryGetValue(Constants.ZapierAppConfiguration.ApiKeyHeaderKey,
39+
out var apiKeyValues))
40+
apiKey = apiKeyValues.First();
41+
42+
if (string.IsNullOrEmpty(apiKey) && (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))) return false;
43+
44+
var isAuthorized = _userValidationService.Validate(username, password, apiKey).GetAwaiter()
45+
.GetResult();
46+
if (!isAuthorized) return false;
47+
48+
return true;
49+
}
50+
}
51+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

0 commit comments

Comments
 (0)