Skip to content

Commit 8ab5800

Browse files
committed
OAuth configuration
1 parent 749d3a7 commit 8ab5800

22 files changed

+460
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
function productPickerSettingsController($scope, notificationsService, umbracoCmsIntegrationsCommerceShopifyService, umbracoCmsIntegrationsCommerceShopifyResource) {
2+
3+
var vm = this;
4+
5+
const oauthName = "OAuth";
6+
7+
vm.oauthSetup = {};
8+
vm.status = {};
9+
10+
$scope.$on('formSubmitting', function () {
11+
12+
$scope.model.value.isValid = vm.status.isValid;
13+
$scope.model.value.type = vm.status.type;
14+
15+
});
16+
17+
umbracoCmsIntegrationsCommerceShopifyResource.checkConfiguration()
18+
.then(function (response) {
19+
20+
vm.status = {
21+
isValid: response.isValid === true,
22+
type: response.type,
23+
description: umbracoCmsIntegrationsCommerceShopifyService.configDescription[response.type.value],
24+
useOAuth: response.isValid === true && response.type.value === oauthName
25+
};
26+
27+
console.log("STATUS: ", vm.status);
28+
29+
//if (vm.status.useOAuth) {
30+
// validateOAuthSetup();
31+
//}
32+
33+
if (response.isValid !== true) {
34+
notificationsService.warning("Shopify Configuration",
35+
"Invalid setup. Please review the API/OAuth settings.");
36+
}
37+
});
38+
39+
vm.onConnectClick = function () {
40+
41+
umbracoCmsIntegrationsCommerceShopifyResource.getAuthorizationUrl().then(function (response) {
42+
vm.authWindow = window.open(response,
43+
"Authorize", "width=900,height=700,modal=yes,alwaysRaised=yes");
44+
45+
});
46+
}
47+
48+
vm.onRevokeToken = function () {
49+
//umbracoCmsIntegrationsCommerceShopifyResource.revokeAccessToken().then(function (response) {
50+
// vm.oauthSetup.isConnected = false;
51+
// notificationsService.success("HubSpot Configuration", "OAuth connection revoked.");
52+
//});
53+
}
54+
55+
// authorization listener
56+
window.addEventListener("message", function (event) {
57+
if (event.data.type === "shopify:oauth:success") {
58+
umbracoCmsIntegrationsCommerceShopifyResource.getAccessToken(event.data.code).then(function (response) {
59+
if (response.startsWith("Error:")) {
60+
notificationsService.error("Shopify Configuration", response);
61+
} else {
62+
vm.oauthSetup.isConnected = true;
63+
vm.status.description = umbracoCmsIntegrationsCommerceShopifyService.configDescription.OAuthConnected;
64+
notificationsService.success("Shopify Configuration", "OAuth connected.");
65+
}
66+
});
67+
68+
}
69+
}, false);
70+
71+
72+
//function validateOAuthSetup() {
73+
// umbracoCmsIntegrationsCrmHubspotResource.validateAccessToken().then(function (response) {
74+
75+
// vm.oauthSetup = {
76+
// isConnected: response.isValid,
77+
// isAccessTokenExpired: response.isExpired,
78+
// isAccessTokenValid: response.isValid
79+
// }
80+
81+
// if (vm.oauthSetup.isConnected === true && vm.oauthSetup.isAccessTokenValid === true) {
82+
// vm.status.description = umbracoCmsIntegrationsCrmHubspotService.configDescription.OAuthConnected;
83+
// }
84+
85+
// // refresh access token
86+
// if (vm.oauthSetup.isAccessTokenExpired === true) {
87+
// umbracoCmsIntegrationsCrmHubspotResource.refreshAccessToken().then(function (response) {
88+
// });
89+
// }
90+
91+
// });
92+
//}
93+
}
94+
95+
angular.module("umbraco")
96+
.controller("Umbraco.Cms.Integrations.Commerce.Shopify.ProductPickerSettingsController", productPickerSettingsController);

src/Umbraco.Cms.Integrations.Commerce.Shopify/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/js/shopify.resource.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44
const apiEndpoint = "backoffice/UmbracoCmsIntegrationsCommerceShopify/Products";
55

66
return {
7+
checkConfiguration: function () {
8+
return umbRequestHelper.resourcePromise(
9+
$http.get(`${apiEndpoint}/CheckConfiguration`),
10+
"Failed to get resource");
11+
},
12+
getAuthorizationUrl: function () {
13+
return umbRequestHelper.resourcePromise(
14+
$http.get(`${apiEndpoint}/GetAuthorizationUrl`),
15+
"Failed");
16+
},
17+
getAccessToken: function (authorizationCode) {
18+
return umbRequestHelper.resourcePromise(
19+
$http.post(`${apiEndpoint}/GetAccessToken`, { code: authorizationCode }),
20+
"Failed");
21+
},
722
getProductsList: function() {
823
return umbRequestHelper.resourcePromise(
924
$http.get(`${apiEndpoint}/GetList`), "Failed to get resource");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
function shopifyService() {
2+
return {
3+
configDescription: {
4+
API: "An access token is configured and will be used to connect to your Shopify account.",
5+
OAuth:
6+
"No access token is configured. To connect to your Shopify account using OAuth click 'Connect', select your account and agree to the permissions.",
7+
None: "No access token or OAuth configuration could be found. Please review your settings before continuing.",
8+
OAuthConnected:
9+
"OAuth is configured and an access token is available to connect to your HubSpot account. To revoke, click 'Revoke'"
10+
}
11+
}
12+
}
13+
14+
angular.module("umbraco.services")
15+
.service("umbracoCmsIntegrationsCommerceShopifyService", shopifyService);
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"javascript": [
33
"~/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/js/productPicker.controller.js",
4-
"~/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/js/shopify.resource.js"
4+
"~/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/js/productPickerSettings.controller.js",
5+
"~/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/js/shopify.resource.js",
6+
"~/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/js/shopify.service.js"
57
]
68
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1-
<div ng-controller="Umbraco.Cms.Integrations.Commerce.Shopify.ProductPickerController as vm">
2-
<p>TEST</p>
1+
<div ng-controller="Umbraco.Cms.Integrations.Commerce.Shopify.ProductPickerSettingsController as vm">
2+
<div>
3+
<p>{{ vm.status.description }}</p>
4+
</div>
5+
<div ng-if="vm.status.useOAuth">
6+
<umb-button action="vm.onConnectClick()"
7+
type="button"
8+
button-style="primary"
9+
state="init"
10+
label="Connect"
11+
disabled="vm.oauthSetup.isConnected">
12+
</umb-button>
13+
<umb-button action="vm.onRevokeToken()"
14+
type="button"
15+
button-style="danger"
16+
state="init"
17+
label="Revoke"
18+
disabled="!vm.oauthSetup.isConnected">
19+
</umb-button>
20+
</div>
321
</div>

src/Umbraco.Cms.Integrations.Commerce.Shopify/Controllers/ProductsController.cs

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,42 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Net;
34
using System.Net.Http;
45
using System.Threading.Tasks;
5-
6+
using System.Web.Http;
67
using Newtonsoft.Json;
7-
8+
using Umbraco.Cms.Integrations.Commerce.Shopify.Models;
89
using Umbraco.Cms.Integrations.Commerce.Shopify.Models.Dtos;
910
using Umbraco.Cms.Integrations.Shared.Configuration;
1011
using Umbraco.Cms.Integrations.Shared.Controllers;
12+
using Umbraco.Cms.Integrations.Shared.Models;
1113
using Umbraco.Core.Logging;
1214
using Umbraco.Web.Mvc;
1315
using Umbraco.Cms.Integrations.Shared.Models.Dtos;
1416
using Umbraco.Cms.Integrations.Shared.Resolvers;
17+
using Umbraco.Cms.Integrations.Shared.Services;
1518

1619
namespace Umbraco.Cms.Integrations.Commerce.Shopify.Controllers
1720
{
1821
[PluginController("UmbracoCmsIntegrationsCommerceShopify")]
1922
public class ProductsController : BaseAuthorizedApiController
2023
{
21-
public const string ProductsApiEndpoint = "https://{0}.myshopify.com/admin/api/{1}/products.json";
24+
private const string ProductsApiEndpoint = "https://{0}.myshopify.com/admin/api/{1}/products.json";
25+
26+
private const string OAuthClientId = "23c1bc3c70de807d84b79a29b12b49f5";
27+
28+
private const string ShopifyServiceName = "Shopify";
29+
private const string ShopifyServiceAddressReplace = "service_address_shop-replace";
30+
31+
private string ShopifyOAuthProxyUrl = $"{OAuthProxyBaseUrl}oauth/shopify";
32+
private const string ShopifyAuthorizationUrl =
33+
"https://{0}.myshopify.com/admin/oauth/authorize?client_id={1}&redirect_uri={2}&scope=read_products&grant_options[]=value";
34+
35+
private const string AccessTokenDbKey = "Umbraco.Cms.Integrations.Shopify.AccessTokenDbKey";
2236

2337
private readonly JsonSerializerSettings _serializerSettings;
2438

25-
public ProductsController(ILogger logger, IAppSettings appSettings) : base(logger, appSettings)
39+
public ProductsController(ILogger logger, IAppSettings appSettings, ITokenService tokenService) : base(logger, appSettings, tokenService)
2640
{
2741
var resolver = new JsonPropertyRenameContractResolver();
2842
resolver.RenameProperty(typeof(ResponseDto<ProductsListDto>), "Result", "products");
@@ -31,6 +45,74 @@ public ProductsController(ILogger logger, IAppSettings appSettings) : base(logge
3145
_serializerSettings.ContractResolver = resolver;
3246
}
3347

48+
[HttpGet]
49+
public EditorSettings CheckConfiguration()
50+
{
51+
if (string.IsNullOrEmpty(AppSettings[Constants.UmbracoCmsIntegrationsCommerceShopifyShop])
52+
|| string.IsNullOrEmpty(AppSettings[Constants.UmbracoCmsIntegrationsCommerceShopifyApiVersion]))
53+
return new EditorSettings();
54+
55+
return
56+
!string.IsNullOrEmpty(AppSettings[Constants.UmbracoCmsIntegrationsCommerceShopifyAccessToken])
57+
? new EditorSettings { IsValid = true, Type = ConfigurationType.Api }
58+
: !string.IsNullOrEmpty(OAuthClientId)
59+
&& !string.IsNullOrEmpty(OAuthProxyBaseUrl)
60+
&& !string.IsNullOrEmpty(OAuthProxyEndpoint)
61+
? new EditorSettings { IsValid = true, Type = ConfigurationType.OAuth }
62+
: new EditorSettings();
63+
}
64+
65+
[HttpGet]
66+
public string GetAuthorizationUrl()
67+
{
68+
return
69+
string.Format(ShopifyAuthorizationUrl,
70+
AppSettings[Constants.UmbracoCmsIntegrationsCommerceShopifyShop], OAuthClientId, ShopifyOAuthProxyUrl);
71+
}
72+
73+
[HttpPost]
74+
public async Task<string> GetAccessToken([FromBody] OAuthRequestDto authRequestDto)
75+
{
76+
var data = new Dictionary<string, string>
77+
{
78+
{ "client_id", OAuthClientId },
79+
{ "redirect_uri", string.Format(ShopifyOAuthProxyUrl, OAuthProxyBaseUrl) },
80+
{ "code", authRequestDto.Code }
81+
};
82+
83+
var requestMessage = new HttpRequestMessage
84+
{
85+
Method = HttpMethod.Post,
86+
RequestUri = new Uri(string.Format(OAuthProxyEndpoint, OAuthProxyBaseUrl)),
87+
Content = new FormUrlEncodedContent(data)
88+
};
89+
requestMessage.Headers.Add("service_name", ShopifyServiceName);
90+
requestMessage.Headers.Add(ShopifyServiceAddressReplace,
91+
AppSettings[Constants.UmbracoCmsIntegrationsCommerceShopifyShop]);
92+
93+
var response = await ClientFactory().SendAsync(requestMessage);
94+
if (response.IsSuccessStatusCode)
95+
{
96+
var result = await response.Content.ReadAsStringAsync();
97+
98+
var tokenDto = JsonConvert.DeserializeObject<TokenDto>(result);
99+
100+
TokenService.SaveParameters(AccessTokenDbKey, tokenDto.AccessToken);
101+
102+
return result;
103+
}
104+
105+
if (response.StatusCode == HttpStatusCode.BadRequest)
106+
{
107+
var errorResult = await response.Content.ReadAsStringAsync();
108+
var errorDto = JsonConvert.DeserializeObject<ErrorDto>(errorResult);
109+
110+
return "Error: " + errorDto.Message;
111+
}
112+
113+
return "Error: An unexpected error occurred.";
114+
}
115+
34116
public async Task<ResponseDto<ProductsListDto>> GetList()
35117
{
36118
var accessToken = AppSettings[Constants.UmbracoCmsIntegrationsCommerceShopifyAccessToken];
@@ -73,5 +155,24 @@ public async Task<ResponseDto<ProductsListDto>> GetList()
73155

74156
return new ResponseDto<ProductsListDto>();
75157
}
158+
159+
public async Task<ResponseDto<ProductsListDto>> GetListOAuth()
160+
{
161+
TokenService.TryGetParameters(AccessTokenDbKey, out string accessToken);
162+
if (string.IsNullOrEmpty(accessToken))
163+
{
164+
ApiLogger.Info<ProductsController>("Cannot access Shopify - Access Token is missing.");
165+
166+
return new ResponseDto<ProductsListDto>();
167+
}
168+
169+
var requestMessage = new HttpRequestMessage
170+
{
171+
Method = HttpMethod.Get,
172+
RequestUri = new Uri("")
173+
};
174+
175+
return new ResponseDto<ProductsListDto>();
176+
}
76177
}
77178
}
Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
1+
using System.Collections.Generic;
2+
63
using Newtonsoft.Json;
74

85
namespace Umbraco.Cms.Integrations.Commerce.Shopify.Models.Dtos
@@ -11,5 +8,19 @@ public class ProductDto
118
{
129
[JsonProperty("title")]
1310
public string Title { get; set; }
11+
12+
[JsonProperty("body_html")]
13+
public string Body { get; set; }
14+
15+
[JsonProperty("status")]
16+
public string Status { get; set; }
17+
18+
[JsonProperty("variants")]
19+
public IEnumerable<ProductVariantDto> Variants { get; set; }
20+
21+
[JsonProperty("image")]
22+
public ProductImageDto Image { get; set; }
1423
}
24+
25+
1526
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Umbraco.Cms.Integrations.Commerce.Shopify.Models.Dtos
4+
{
5+
public class ProductImageDto
6+
{
7+
[JsonProperty("src")]
8+
public string Src { get; set; }
9+
}
10+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Umbraco.Cms.Integrations.Commerce.Shopify.Models.Dtos
4+
{
5+
public class ProductVariantDto
6+
{
7+
[JsonProperty("price")]
8+
public string Price { get; set; }
9+
10+
[JsonProperty("sku")]
11+
public string Sku { get; set; }
12+
13+
[JsonProperty("position")]
14+
public int Position { get; set; }
15+
16+
[JsonProperty("inventory_quantity")]
17+
public int InventoryQuantity { get; set; }
18+
}
19+
}

0 commit comments

Comments
 (0)