* Updated the X oEmbed provider to use the x.com domain. * Fixed issues raised in code review.
This commit is contained in:
@@ -12,7 +12,7 @@ public class X : OEmbedProviderBase
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ApiEndpoint => "http://publish.twitter.com/oembed";
|
public override string ApiEndpoint => "https://publish.x.com/oembed";
|
||||||
|
|
||||||
public override string[] UrlSchemeRegex => new[] { @"(https?:\/\/(www\.)?)(twitter|x)\.com\/.*\/status\/.*" };
|
public override string[] UrlSchemeRegex => new[] { @"(https?:\/\/(www\.)?)(twitter|x)\.com\/.*\/status\/.*" };
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,22 @@ using Umbraco.Cms.Core.Services.OperationStatus;
|
|||||||
|
|
||||||
namespace Umbraco.Cms.Core.Services;
|
namespace Umbraco.Cms.Core.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a service for asynchronously retrieving embeddable HTML markup for a specified resource using the oEmbed
|
||||||
|
/// protocol.
|
||||||
|
/// </summary>
|
||||||
public interface IOEmbedService
|
public interface IOEmbedService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously retrieves the embeddable HTML markup for the specified resource.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The returned markup is suitable for embedding in web pages. The width and height parameters
|
||||||
|
/// may be ignored by some providers depending on their capabilities.</remarks>
|
||||||
|
/// <param name="url">The URI of the resource to retrieve markup for. Must be a valid, absolute URI.</param>
|
||||||
|
/// <param name="width">The optional maximum width, in pixels, for the embedded content. If null, the default width is used.</param>
|
||||||
|
/// <param name="height">The optional maximum height, in pixels, for the embedded content. If null, the default height is used.</param>
|
||||||
|
/// <param name="cancellationToken">A token to monitor for cancellation requests. The operation is canceled if the token is triggered.</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation. The result contains an Attempt with the HTML markup if
|
||||||
|
/// successful, or an oEmbed operation status indicating the reason for failure.</returns>
|
||||||
Task<Attempt<string, OEmbedOperationStatus>> GetMarkupAsync(Uri url, int? width, int? height, CancellationToken cancellationToken);
|
Task<Attempt<string, OEmbedOperationStatus>> GetMarkupAsync(Uri url, int? width, int? height, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,30 @@ using Umbraco.Cms.Core.Services.OperationStatus;
|
|||||||
|
|
||||||
namespace Umbraco.Cms.Core.Services;
|
namespace Umbraco.Cms.Core.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements <see cref="IOEmbedService"/> for retrieving embeddable HTML markup using the oEmbed protocol.
|
||||||
|
/// </summary>
|
||||||
public class OEmbedService : IOEmbedService
|
public class OEmbedService : IOEmbedService
|
||||||
{
|
{
|
||||||
private readonly EmbedProvidersCollection _embedProvidersCollection;
|
private readonly EmbedProvidersCollection _embedProvidersCollection;
|
||||||
private readonly ILogger<OEmbedService> _logger;
|
private readonly ILogger<OEmbedService> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="OEmbedService"/> class.
|
||||||
|
/// </summary>
|
||||||
public OEmbedService(EmbedProvidersCollection embedProvidersCollection, ILogger<OEmbedService> logger)
|
public OEmbedService(EmbedProvidersCollection embedProvidersCollection, ILogger<OEmbedService> logger)
|
||||||
{
|
{
|
||||||
_embedProvidersCollection = embedProvidersCollection;
|
_embedProvidersCollection = embedProvidersCollection;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public async Task<Attempt<string, OEmbedOperationStatus>> GetMarkupAsync(Uri url, int? maxWidth, int? maxHeight, CancellationToken cancellationToken)
|
public async Task<Attempt<string, OEmbedOperationStatus>> GetMarkupAsync(Uri url, int? maxWidth, int? maxHeight, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Find the first provider that supports the URL
|
// Find the first provider that supports the URL
|
||||||
IEmbedProvider? matchedProvider = _embedProvidersCollection
|
IEmbedProvider? matchedProvider = _embedProvidersCollection
|
||||||
.FirstOrDefault(provider => provider.UrlSchemeRegex.Any(regex=>new Regex(regex, RegexOptions.IgnoreCase).IsMatch(url.OriginalString)));
|
.FirstOrDefault(provider => provider.UrlSchemeRegex
|
||||||
|
.Any(regex => new Regex(regex, RegexOptions.IgnoreCase).IsMatch(url.OriginalString)));
|
||||||
|
|
||||||
if (matchedProvider is null)
|
if (matchedProvider is null)
|
||||||
{
|
{
|
||||||
@@ -39,8 +47,8 @@ public class OEmbedService : IOEmbedService
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Unexpected exception happened while trying to get oembed markup. Provider: {Provider}",matchedProvider.GetType().Name);
|
_logger.LogError(e, "Unexpected exception happened while trying to get oEmbed markup. Provider: {Provider}", matchedProvider.GetType().Name);
|
||||||
Attempt.FailWithStatus(OEmbedOperationStatus.UnexpectedException, string.Empty, e);
|
return Attempt.FailWithStatus(OEmbedOperationStatus.UnexpectedException, string.Empty, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Attempt.FailWithStatus(OEmbedOperationStatus.ProviderReturnedInvalidResult, string.Empty);
|
return Attempt.FailWithStatus(OEmbedOperationStatus.ProviderReturnedInvalidResult, string.Empty);
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using Umbraco.Cms.Core.DependencyInjection;
|
||||||
|
using Umbraco.Cms.Core.Media.EmbedProviders;
|
||||||
|
using Umbraco.Cms.Core.Services;
|
||||||
|
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||||
|
using Umbraco.Cms.Tests.Common.Testing;
|
||||||
|
using Umbraco.Cms.Tests.Integration.Testing;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
[UmbracoTest(Database = UmbracoTestOptions.Database.None)]
|
||||||
|
internal sealed class OEmbedServiceTests : UmbracoIntegrationTest
|
||||||
|
{
|
||||||
|
private IOEmbedService OEmbedService => GetRequiredService<IOEmbedService>();
|
||||||
|
|
||||||
|
protected override void CustomTestSetup(IUmbracoBuilder builder)
|
||||||
|
{
|
||||||
|
base.CustomTestSetup(builder);
|
||||||
|
|
||||||
|
// Clear all providers and add only the X provider
|
||||||
|
builder.EmbedProviders().Clear().Append<X>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies resolution to https://github.com/umbraco/Umbraco-CMS/issues/21052.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Tests marked as [Explicit] as we don't want a random external service call to X to fail during regular test runs.
|
||||||
|
/// </remarks>
|
||||||
|
[Explicit]
|
||||||
|
[TestCase("https://x.com/THR/status/1995620384344080849?s=20")]
|
||||||
|
[TestCase("https://x.com/SquareEnix/status/1995780120888705216?s=20")]
|
||||||
|
[TestCase("https://x.com/sem_sep/status/1991750339427700739?s=20")]
|
||||||
|
public async Task GetMarkupAsync_WithXUrls_ReturnsSuccessAndMarkup(string url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var uri = new Uri(url);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await OEmbedService.GetMarkupAsync(uri, width: null, height: null, CancellationToken.None);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result.Success, Is.True);
|
||||||
|
Assert.That(result.Status, Is.EqualTo(OEmbedOperationStatus.Success));
|
||||||
|
Assert.That(result.Result, Is.Not.Null.And.Not.Empty);
|
||||||
|
Assert.That(result.Result, Does.Contain("blockquote"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user