Skip to content

Commit fe899b7

Browse files
committed
Merge remote-tracking branch 'origin/support/10.x' into develop
# Conflicts: # src/Umbraco.AuthorizedServices/Controllers/AuthorizedServiceController.cs # version.json
2 parents f3c7bd6 + a4dad56 commit fe899b7

File tree

14 files changed

+367
-97
lines changed

14 files changed

+367
-97
lines changed

README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ To make a call to an authorized service, you first need to obtain an instance of
287287
If making a request where all information is provided via the path and querystring, such as GET requests, the following method should be invoked:
288288

289289
```csharp
290-
Task<Attempt<TResponse?>> SendRequestAsync<TResponse>(string serviceAlias, string path, HttpMethod httpMethod);
290+
Task<Attempt<AuthorizedServiceResponse<TResponse>>> SendRequestAsync<TResponse>(string serviceAlias, string path, HttpMethod httpMethod);
291291
```
292292

293293
The parameters for the request are as follows:
@@ -302,7 +302,7 @@ There is also a type parameter:
302302
If you need to provide data in the request, as is usually the case for POST or PUT requests that required the creation or update of a resource, an overload is available:
303303

304304
```csharp
305-
Task<Attempt<TResponse>> SendRequestAsync<TRequest, TResponse>(string serviceAlias, string path, HttpMethod httpMethod, TRequest? requestContent = null)
305+
Task<Attempt<AuthorizedServiceResponse<TResponse>>> SendRequestAsync<TRequest, TResponse>(string serviceAlias, string path, HttpMethod httpMethod, TRequest? requestContent = null)
306306
where TRequest : class;
307307
```
308308

@@ -317,18 +317,24 @@ And additional type parameter:
317317
If you need to work with the raw JSON response, there are equivalent methods for both of these that omit the deserialization step:
318318

319319
```csharp
320-
Task<Attempt<string?>> SendRequestRawAsync(string serviceAlias, string path, HttpMethod httpMethod);
320+
Task<Attempt<AuthorizedServiceResponse<string>>> SendRequestRawAsync(string serviceAlias, string path, HttpMethod httpMethod);
321321

322-
Task<<Attempt<string?>> SendRequestRawAsync<TRequest>(string serviceAlias, string path, HttpMethod httpMethod, TRequest? requestContent = null)
322+
Task<Attempt<AuthorizedServiceResponse<string>>> SendRequestRawAsync<TRequest>(string serviceAlias, string path, HttpMethod httpMethod, TRequest? requestContent = null)
323323
where TRequest : class;
324324
```
325325

326326
Finally, there are convenience extension methods available for each of the common HTTP verbs, allowing you to simplify the requests and omit the `HttpMethod` parameter, e.g.
327327

328328
```csharp
329-
Task<Attempt<TResponse?>> GetRequestAsync<TResponse>(string serviceAlias, string path);
329+
Task<Attempt<AuthorizedServiceResponse<TResponse>>> GetRequestAsync<TResponse>(string serviceAlias, string path);
330330
```
331331

332+
The response is received wrapped in an instance of `AuthorizedServiceResponse` which has three properties:
333+
334+
- `Data` - the response data deserialized into an instance of the provided `TResponse` type.
335+
- `Raw` - the raw JSON response string.
336+
- `Metadata` - various metadata from the service response, provided in headers and parsed into an instance of `ServiceResponseMetadata`.
337+
332338
## Providers
333339

334340
The list of providers for which the package has been verified is maintained at the [Umbraco Documentation website](https://docs.umbraco.com/umbraco-dxp/packages/authorized-services#verified-providers).
@@ -456,6 +462,10 @@ Switching the encryption engine to for example `AesSecretEncryptor` can be done
456462
builder.Services.AddUnique<ISecretEncryptor, AesSecretEncryptor>();
457463
```
458464

465+
#### IServiceResponseMetadataParser
466+
467+
Responsible for parsing header values from the response received when calling an authorized service into an instance of `ServiceResponseMetadata`.
468+
459469
#### ITokenFactory
460470

461471
Responsible for instantiating a new strongly typed `Token` instance from the service response. Implemented by `TokenFactory`.

examples/Umbraco.AuthorizedServices.TestSite/Controllers/HubspotContactsController.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.AspNetCore.Mvc;
22
using Umbraco.AuthorizedServices.Extensions;
3+
using Umbraco.AuthorizedServices.Models;
34
using Umbraco.AuthorizedServices.Services;
45
using Umbraco.AuthorizedServices.TestSite.Models.Dtos;
56
using Umbraco.AuthorizedServices.TestSite.Models.ServiceResponses;
@@ -21,15 +22,15 @@ public HubspotContactsController(IAuthorizedServiceCaller authorizedServiceCalle
2122
[HttpGet]
2223
public async Task<IActionResult> Get()
2324
{
24-
Attempt<HubspotContactResponse?> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<HubspotContactResponse>(
25+
Attempt<AuthorizedServiceResponse<HubspotContactResponse>> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<HubspotContactResponse>(
2526
ServiceAlias,
2627
BasePath);
2728
if (!responseAttempt.Success || responseAttempt.Result is null)
2829
{
2930
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve contacts.");
3031
}
3132

32-
HubspotContactResponse response = responseAttempt.Result;
33+
HubspotContactResponse response = responseAttempt.Result.Data!;
3334
return Ok(
3435
response.Results
3536
.Select(MapToDto)
@@ -40,7 +41,7 @@ public async Task<IActionResult> Get()
4041
[Route("{id}")]
4142
public async Task<IActionResult> Get(string id)
4243
{
43-
Attempt<HubspotContactResponse.Result?> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<HubspotContactResponse.Result>(
44+
Attempt<AuthorizedServiceResponse<HubspotContactResponse.Result>> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<HubspotContactResponse.Result>(
4445
ServiceAlias,
4546
$"{BasePath}{id}");
4647

@@ -49,14 +50,14 @@ public async Task<IActionResult> Get(string id)
4950
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve contact.");
5051
}
5152

52-
HubspotContactResponse.Result response = responseAttempt.Result;
53+
HubspotContactResponse.Result response = responseAttempt.Result.Data!;
5354
return Ok(MapToDto(response));
5455
}
5556

5657
[HttpPost]
5758
public async Task<IActionResult> Create([FromBody] ContactDto contact)
5859
{
59-
Attempt<HubspotContactResponse.Result?> responseAttempt = await AuthorizedServiceCaller.PostRequestAsync<HubspotContactResponse.Result, HubspotContactResponse.Result>(
60+
Attempt<AuthorizedServiceResponse<HubspotContactResponse.Result>> responseAttempt = await AuthorizedServiceCaller.PostRequestAsync<HubspotContactResponse.Result, HubspotContactResponse.Result>(
6061
ServiceAlias,
6162
BasePath,
6263
MapToRequest(contact));
@@ -65,14 +66,14 @@ public async Task<IActionResult> Create([FromBody] ContactDto contact)
6566
return HandleFailedRequest(responseAttempt.Exception, "Could not create contact.");
6667
}
6768

68-
HubspotContactResponse.Result response = responseAttempt.Result;
69+
HubspotContactResponse.Result response = responseAttempt.Result.Data!;
6970
return CreatedAtAction(nameof(Get), new { id = response.Id }, MapToDto(response));
7071
}
7172

7273
[HttpPut]
7374
public async Task<IActionResult> Update([FromBody] ContactDto contact)
7475
{
75-
Attempt<HubspotContactResponse.Result?> responseAttempt = await AuthorizedServiceCaller.PatchRequestAsync<HubspotContactResponse.Result, HubspotContactResponse.Result>(
76+
Attempt<AuthorizedServiceResponse<HubspotContactResponse.Result>> responseAttempt = await AuthorizedServiceCaller.PatchRequestAsync<HubspotContactResponse.Result, HubspotContactResponse.Result>(
7677
ServiceAlias,
7778
$"{BasePath}{contact.Id}",
7879
MapToRequest(contact));
@@ -81,7 +82,7 @@ public async Task<IActionResult> Update([FromBody] ContactDto contact)
8182
return HandleFailedRequest(responseAttempt.Exception, "Could not update contact.");
8283
}
8384

84-
HubspotContactResponse.Result response = responseAttempt.Result;
85+
HubspotContactResponse.Result response = responseAttempt.Result.Data!;
8586
return Ok(MapToDto(response));
8687
}
8788

examples/Umbraco.AuthorizedServices.TestSite/Controllers/TestAuthorizedServicesController.cs

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.AspNetCore.Mvc;
22
using Umbraco.AuthorizedServices.Extensions;
3+
using Umbraco.AuthorizedServices.Models;
34
using Umbraco.AuthorizedServices.Services;
45
using Umbraco.AuthorizedServices.TestSite.Models.ServiceResponses;
56
using Umbraco.Cms.Core;
@@ -15,29 +16,29 @@ public TestAuthorizedServicesController(IAuthorizedServiceCaller authorizedServi
1516

1617
public async Task<IActionResult> GetUmbracoContributorsFromGitHub()
1718
{
18-
Attempt<List<GitHubContributorResponse>?> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<List<GitHubContributorResponse>>(
19+
Attempt<AuthorizedServiceResponse<List<GitHubContributorResponse>>> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<List<GitHubContributorResponse>>(
1920
"github",
2021
"/repos/Umbraco/Umbraco-CMS/contributors");
2122
if (!responseAttempt.Success || responseAttempt.Result is null)
2223
{
2324
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve contributors.");
2425
}
2526

26-
List<GitHubContributorResponse> response = responseAttempt.Result;
27+
List<GitHubContributorResponse> response = responseAttempt.Result.Data!;
2728
return Content(string.Join(", ", response.Select(x => x.Login)));
2829
}
2930

3031
public async Task<IActionResult> GetContactsFromHubspot()
3132
{
32-
Attempt<HubspotContactResponse?> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<HubspotContactResponse>(
33+
Attempt<AuthorizedServiceResponse<HubspotContactResponse>> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<HubspotContactResponse>(
3334
"hubspot",
3435
"/crm/v3/objects/contacts?limit=10&archived=false");
3536
if (!responseAttempt.Success || responseAttempt.Result is null)
3637
{
3738
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve contacts.");
3839
}
3940

40-
HubspotContactResponse response = responseAttempt.Result;
41+
HubspotContactResponse response = responseAttempt.Result.Data!;
4142
return Content(
4243
string.Join(
4344
", ",
@@ -48,7 +49,7 @@ public async Task<IActionResult> GetContactsFromHubspot()
4849
public async Task<IActionResult> GetMeetupSelfUserInfo()
4950
{
5051
// This makes a GraphQL query
51-
Attempt<string?> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
52+
Attempt<AuthorizedServiceResponse<string>> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
5253
"meetup",
5354
"/gql",
5455
HttpMethod.Post,
@@ -62,13 +63,13 @@ public async Task<IActionResult> GetMeetupSelfUserInfo()
6263
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve user info.");
6364
}
6465

65-
var response = responseAttempt.Result;
66-
return Content(response);
66+
AuthorizedServiceResponse<string> response = responseAttempt.Result;
67+
return Content(response.Data ?? string.Empty);
6768
}
6869

6970
public async Task<IActionResult> GetFormsFromDynamics()
7071
{
71-
Attempt<DynamicsFormResponse?> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<DynamicsFormResponse>(
72+
Attempt<AuthorizedServiceResponse<DynamicsFormResponse>> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<DynamicsFormResponse>(
7273
"dynamics",
7374
"/msdyncrm_marketingforms");
7475

@@ -77,13 +78,13 @@ public async Task<IActionResult> GetFormsFromDynamics()
7778
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve forms.");
7879
}
7980

80-
DynamicsFormResponse response = responseAttempt.Result;
81+
DynamicsFormResponse response = responseAttempt.Result.Data!;
8182
return Content(string.Join(", ", response.Results.Select(x => x.Name)));
8283
}
8384

8485
public async Task<IActionResult> GetSearchResultsFromGoogle()
8586
{
86-
Attempt<string?> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
87+
Attempt<AuthorizedServiceResponse<string>> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
8788
"google",
8889
"/v1/urlInspection/index:inspect",
8990
HttpMethod.Post,
@@ -99,12 +100,12 @@ public async Task<IActionResult> GetSearchResultsFromGoogle()
99100
}
100101

101102
var response = responseAttempt.Result;
102-
return Content(response);
103+
return Content(response.Data ?? string.Empty);
103104
}
104105

105106
public async Task<IActionResult> GetFoldersFromDropbox()
106107
{
107-
Attempt<string?> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
108+
Attempt<AuthorizedServiceResponse<string>> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
108109
"dropbox",
109110
"/2/files/list_folder",
110111
HttpMethod.Post,
@@ -119,13 +120,13 @@ public async Task<IActionResult> GetFoldersFromDropbox()
119120
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve folders.");
120121
}
121122

122-
var response = responseAttempt.Result;
123-
return Content(response);
123+
var response = responseAttempt.Result.Data;
124+
return Content(response ?? string.Empty);
124125
}
125126

126127
public async Task<IActionResult> GetAssetsFromAssetBank(string assetIds)
127128
{
128-
Attempt<AssetBankSearchResponse?> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<AssetBankSearchResponse>(
129+
Attempt<AuthorizedServiceResponse<AssetBankSearchResponse>> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<AssetBankSearchResponse>(
129130
"assetBank",
130131
"/assetbank-rya-assets-test/rest/asset-search?assetIds=" + assetIds);
131132

@@ -134,13 +135,13 @@ public async Task<IActionResult> GetAssetsFromAssetBank(string assetIds)
134135
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve assets.");
135136
}
136137

137-
AssetBankSearchResponse response = responseAttempt.Result;
138+
AssetBankSearchResponse response = responseAttempt.Result.Data!;
138139
return Content(string.Join(", ", response.Select(x => x.ToString())));
139140
}
140141

141142
public async Task<IActionResult> GetVideoDetailsFromYouTube(string videoId)
142143
{
143-
Attempt<string?> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
144+
Attempt<AuthorizedServiceResponse<string>> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
144145
"youtube",
145146
$"/v3/videos?id={videoId}&part=snippet,contentDetails,statistics,status",
146147
HttpMethod.Get);
@@ -150,13 +151,13 @@ public async Task<IActionResult> GetVideoDetailsFromYouTube(string videoId)
150151
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve video details.");
151152
}
152153

153-
var response = responseAttempt.Result;
154-
return Content(response);
154+
var response = responseAttempt.Result.Data;
155+
return Content(response ?? string.Empty);
155156
}
156157

157158
public async Task<IActionResult> GetInstagramProfile()
158159
{
159-
Attempt<InstagramProfileResponse?> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<InstagramProfileResponse>(
160+
Attempt<AuthorizedServiceResponse<InstagramProfileResponse>> responseAttempt = await AuthorizedServiceCaller.GetRequestAsync<InstagramProfileResponse>(
160161
"instagram",
161162
$"/v3.0/me?fields=username");
162163

@@ -165,12 +166,12 @@ public async Task<IActionResult> GetInstagramProfile()
165166
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve account details.");
166167
}
167168

168-
return Content(responseAttempt.Result.Username);
169+
return Content(responseAttempt.Result.Data!.Username);
169170
}
170171

171172
public async Task<IActionResult> GetTwitterProfileUsingOAuth1()
172173
{
173-
Attempt<string?> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
174+
Attempt<AuthorizedServiceResponse<string>> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
174175
"twitter",
175176
"/1.1/account/settings.json",
176177
HttpMethod.Get);
@@ -180,13 +181,13 @@ public async Task<IActionResult> GetTwitterProfileUsingOAuth1()
180181
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve account details.");
181182
}
182183

183-
var response = responseAttempt.Result;
184-
return Content(response);
184+
var response = responseAttempt.Result.Data;
185+
return Content(response ?? string.Empty);
185186
}
186187

187188
public async Task<IActionResult> GetTwitterProfileUsingOAuth2()
188189
{
189-
Attempt<string?> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
190+
Attempt<AuthorizedServiceResponse<string>> responseAttempt = await AuthorizedServiceCaller.SendRequestRawAsync(
190191
"twitter_oauth2",
191192
"/2/users/me",
192193
HttpMethod.Get);
@@ -196,8 +197,8 @@ public async Task<IActionResult> GetTwitterProfileUsingOAuth2()
196197
return HandleFailedRequest(responseAttempt.Exception, "Could not retrieve account details.");
197198
}
198199

199-
var response = responseAttempt.Result;
200-
return Content(response);
200+
var response = responseAttempt.Result.Data;
201+
return Content(response ?? string.Empty);
201202
}
202203

203204
public async Task<IActionResult> GetApiKey(string serviceAlias)

src/Umbraco.AuthorizedServices/Api/Management/Controllers/Service/SendSampleRequestController.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.Extensions.Options;
66
using Umbraco.AuthorizedServices.Configuration;
77
using Umbraco.AuthorizedServices.Exceptions;
8+
using Umbraco.AuthorizedServices.Models;
89
using Umbraco.AuthorizedServices.Services;
910
using Umbraco.Cms.Core;
1011

@@ -31,7 +32,7 @@ public async Task<IActionResult> SendSampleRequest(string alias)
3132
{
3233
ServiceDetail serviceDetail = ServiceDetailOptions.Get(alias);
3334

34-
Attempt<string?> responseAttempt = await _authorizedServiceCaller.SendRequestRawAsync(alias, serviceDetail.SampleRequest ?? string.Empty, HttpMethod.Get);
35+
Attempt<AuthorizedServiceResponse<string>> responseAttempt = await _authorizedServiceCaller.SendRequestRawAsync(alias, serviceDetail.SampleRequest ?? string.Empty, HttpMethod.Get);
3536
if (responseAttempt.Success && responseAttempt.Result is not null)
3637
{
3738
return Ok(responseAttempt.Result);

src/Umbraco.AuthorizedServices/AuthorizedServicesComposer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ private static void RegisterServices(IUmbracoBuilder builder)
4545
builder.Services.AddUnique<IAuthorizedRequestBuilder, AuthorizedRequestBuilder>();
4646

4747
builder.Services.AddUnique<IAuthorizedServiceCaller, AuthorizedServiceCaller>();
48+
builder.Services.AddUnique<IServiceResponseMetadataParser, ServiceResponseMetadataParser>();
4849
builder.Services.AddUnique<IDateTimeProvider, DateTimeProvider>();
4950
builder.Services.AddUnique<IRefreshTokenParametersBuilder, RefreshTokenParametersBuilder>();
5051

0 commit comments

Comments
 (0)