Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Zettersten committed Sep 18, 2024
1 parent 17f32e7 commit 2a095e3
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 53 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/nuget-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: NuGet Publish

on:
release:
types: [published]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.x

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --configuration Release

- name: Pack
run: dotnet pack ./LinkPreview/LinkPreview.csproj --configuration Release --no-build -o ./nupkg

- name: Push to NuGet
run: dotnet nuget push ./nupkg/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{secrets.NUGET_API_KEY}} --skip-duplicate
6 changes: 5 additions & 1 deletion LinkPreview/ILinkPreviewService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace LinkPreview;
using System.Diagnostics.CodeAnalysis;

namespace LinkPreview;

/// <summary>
/// Provides services for interacting with the LinkPreview API.
Expand All @@ -14,6 +16,8 @@ public interface ILinkPreviewService
/// <returns>
/// Link preview information for the specified URL.
/// </returns>
[RequiresUnreferencedCode("")]
[RequiresDynamicCode("")]
Task<LinkPreviewResponse> GetLinkPreviewAsync(
string url,
LinkPreviewOptionalField? optionalFields = null,
Expand Down
36 changes: 35 additions & 1 deletion LinkPreview/LinkPreview.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Authors>Erik Zettersten</Authors>
<Copyright>Copyright © 2024 Erik Zettersten</Copyright>
<Company>LinkPreview</Company>
<Product>LinkPreview SDK</Product>
<PackageId>LinkPreview</PackageId>
<Version>$(GITHUB_REF_NAME.TrimStart('v'))</Version>
<Description></Description>
<PackageProjectUrl>https://github.com/Zettersten/LinkPreview</PackageProjectUrl>
<RepositoryUrl>https://github.com/Zettersten/LinkPreview</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageReleaseNotes></PackageReleaseNotes>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PublishAot>false</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
<NoWarn>CS1591;CS0618;CS1573</NoWarn>
</PropertyGroup>

<ItemGroup>
<None Include="..\icon.png" Pack="true" PackagePath="\" />
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>

<ItemGroup>
Expand All @@ -13,6 +46,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.9.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>

</Project>
34 changes: 17 additions & 17 deletions LinkPreview/LinkPreviewException.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Net;
using System.Net;

namespace LinkPreview;

Expand Down Expand Up @@ -35,10 +35,10 @@ public sealed class LinkPreviewException : Exception
public LinkPreviewException(HttpStatusCode statusCode, LinkPreviewErrorResponse errorResponse)
: base(GetExceptionMessage(statusCode, errorResponse))
{
StatusCode = statusCode;
ErrorCode = errorResponse.Error;
ErrorDescription = errorResponse.Description;
Url = string.IsNullOrEmpty(errorResponse.Url) ? null : errorResponse.Url;
this.StatusCode = statusCode;
this.ErrorCode = errorResponse.Error;
this.ErrorDescription = errorResponse.Description;
this.Url = string.IsNullOrEmpty(errorResponse.Url) ? null : errorResponse.Url;
}

/// <summary>
Expand All @@ -47,10 +47,10 @@ public LinkPreviewException(HttpStatusCode statusCode, LinkPreviewErrorResponse
/// <param name="errorDescription">The error response from the LinkPreview service.</param>
internal LinkPreviewException(string errorDescription)
{
StatusCode = HttpStatusCode.InternalServerError;
ErrorCode = 0;
ErrorDescription = errorDescription;
Url = null;
this.StatusCode = HttpStatusCode.InternalServerError;
this.ErrorCode = 0;
this.ErrorDescription = errorDescription;
this.Url = null;
}

/// <summary>
Expand All @@ -60,10 +60,10 @@ internal LinkPreviewException(string errorDescription)
/// <param name="errorDescription">The error response from the LinkPreview service.</param>
internal LinkPreviewException(HttpStatusCode statusCode, string errorDescription)
{
StatusCode = statusCode;
ErrorCode = 0;
ErrorDescription = errorDescription;
Url = null;
this.StatusCode = statusCode;
this.ErrorCode = 0;
this.ErrorDescription = errorDescription;
this.Url = null;
}

/// <summary>
Expand All @@ -90,9 +90,9 @@ LinkPreviewErrorResponse errorResponse
public override string ToString()
{
return $"{base.ToString()}, "
+ $"StatusCode: {StatusCode}, "
+ $"ErrorCode: {ErrorCode}, "
+ $"ErrorDescription: {ErrorDescription}"
+ (Url != null ? $", URL: {Url}" : "");
+ $"StatusCode: {this.StatusCode}, "
+ $"ErrorCode: {this.ErrorCode}, "
+ $"ErrorDescription: {this.ErrorDescription}"
+ (this.Url != null ? $", URL: {this.Url}" : "");
}
}
7 changes: 6 additions & 1 deletion LinkPreview/LinkPreviewExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Configuration;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

Expand All @@ -15,6 +16,8 @@ public static class LinkPreviewExtensions
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
/// <param name="configuration">The configuration section for LinkPreview options.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
[RequiresUnreferencedCode("")]
[RequiresDynamicCode("")]
public static IServiceCollection AddLinkPreviewService(
this IServiceCollection services,
IConfiguration configuration
Expand All @@ -30,6 +33,8 @@ IConfiguration configuration
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
/// <param name="configurationSection">The configuration section for LinkPreview options.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
[RequiresUnreferencedCode("")]
[RequiresDynamicCode("")]
public static IServiceCollection AddLinkPreviewService(
this IServiceCollection services,
IConfigurationSection configurationSection
Expand Down
14 changes: 7 additions & 7 deletions LinkPreview/LinkPreviewOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;

namespace LinkPreview;

Expand All @@ -7,7 +7,7 @@ namespace LinkPreview;
/// </summary>
public sealed class LinkPreviewOptions
{
private const string DefaultApiBaseUrl = "https://api.linkpreview.net";
private const string defaultApiBaseUrl = "https://api.linkpreview.net";

/// <summary>
/// Gets or sets the base URL for the LinkPreview API.
Expand All @@ -33,8 +33,8 @@ public sealed class LinkPreviewOptions
/// </summary>
public LinkPreviewOptions()
{
ApiBaseUrl = DefaultApiBaseUrl;
ApiKey = string.Empty;
this.ApiBaseUrl = defaultApiBaseUrl;
this.ApiKey = string.Empty;
}

/// <summary>
Expand All @@ -43,12 +43,12 @@ public LinkPreviewOptions()
/// <exception cref="ValidationException">Thrown when the options are invalid.</exception>
public void Validate()
{
if (string.IsNullOrWhiteSpace(ApiKey))
if (string.IsNullOrWhiteSpace(this.ApiKey))
{
throw new ValidationException("API Key must not be empty or whitespace.");
}

if (!Uri.TryCreate(ApiBaseUrl, UriKind.Absolute, out _))
if (!Uri.TryCreate(this.ApiBaseUrl, UriKind.Absolute, out _))
{
throw new ValidationException("API Base URL must be a valid absolute URL.");
}
Expand All @@ -60,6 +60,6 @@ public void Validate()
/// <returns>A string that represents the current object.</returns>
public override string ToString()
{
return $"API Base URL: {ApiBaseUrl}, API Key: {ApiKey[..3]}...{ApiKey[^3..]}";
return $"API Base URL: {this.ApiBaseUrl}, API Key: {this.ApiKey[..3]}...{this.ApiKey[^3..]}";
}
}
22 changes: 11 additions & 11 deletions LinkPreview/LinkPreviewResponse.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;

namespace LinkPreview;
Expand Down Expand Up @@ -99,15 +99,15 @@ public sealed class LinkPreviewResponse
/// <returns>True if the response includes any extended properties; otherwise, false.</returns>
public bool IsExtendedResponse()
{
return ImageSize.HasValue
|| !string.IsNullOrEmpty(ImageType)
|| ImageWidth.HasValue
|| ImageHeight.HasValue
|| !string.IsNullOrEmpty(Icon)
|| !string.IsNullOrEmpty(IconType)
|| IconWidth.HasValue
|| IconHeight.HasValue
|| !string.IsNullOrEmpty(Locale);
return this.ImageSize.HasValue
|| !string.IsNullOrEmpty(this.ImageType)
|| this.ImageWidth.HasValue
|| this.ImageHeight.HasValue
|| !string.IsNullOrEmpty(this.Icon)
|| !string.IsNullOrEmpty(this.IconType)
|| this.IconWidth.HasValue
|| this.IconHeight.HasValue
|| !string.IsNullOrEmpty(this.Locale);
}

/// <summary>
Expand All @@ -116,6 +116,6 @@ public bool IsExtendedResponse()
/// <returns>A string that represents the current object.</returns>
public override string ToString()
{
return $"Title: {Title}, URL: {Url}, Is Extended Response: {IsExtendedResponse()}";
return $"Title: {this.Title}, URL: {this.Url}, Is Extended Response: {this.IsExtendedResponse()}";
}
}
42 changes: 27 additions & 15 deletions LinkPreview/LinkPreviewService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.WebUtilities;
Expand All @@ -12,34 +13,39 @@ namespace LinkPreview;
/// </summary>
public sealed class LinkPreviewService : ILinkPreviewService
{
private readonly HttpClient _httpClient;
private readonly IOptions<LinkPreviewOptions> _options;
private readonly IMemoryCache _cache;
private readonly HttpClient httpClient;
private readonly IOptions<LinkPreviewOptions> options;
private readonly IMemoryCache cache;

public LinkPreviewService(
HttpClient httpClient,
IOptions<LinkPreviewOptions> options,
IMemoryCache cache
)
{
_httpClient = httpClient ?? throw new LinkPreviewException("HTTP client is null.");
_options = options ?? throw new LinkPreviewException("LinkPreview options are null.");
_cache = cache ?? throw new LinkPreviewException("Memory cache is null.");
this.httpClient = httpClient ?? throw new LinkPreviewException("HTTP client is null.");
this.options = options ?? throw new LinkPreviewException("LinkPreview options are null.");
this.cache = cache ?? throw new LinkPreviewException("Memory cache is null.");

try
{
_options.Value.Validate();
this.options.Value.Validate();
}
catch (ValidationException ex)
{
throw new LinkPreviewException($"Invalid LinkPreview options: {ex.Message}");
}

_httpClient.BaseAddress = new Uri(_options.Value.ApiBaseUrl);
_httpClient.DefaultRequestHeaders.Add("X-Linkpreview-Api-Key", _options.Value.ApiKey);
this.httpClient.BaseAddress = new Uri(this.options.Value.ApiBaseUrl);
this.httpClient.DefaultRequestHeaders.Add(
"X-Linkpreview-Api-Key",
this.options.Value.ApiKey
);
}

/// <inheritdoc />
[RequiresUnreferencedCode("")]
[RequiresDynamicCode("")]
public async Task<LinkPreviewResponse> GetLinkPreviewAsync(
string url,
LinkPreviewOptionalField? optionalFields = null,
Expand All @@ -48,22 +54,28 @@ public async Task<LinkPreviewResponse> GetLinkPreviewAsync(
{
var cacheKey = GetCacheKey(url, optionalFields);

if (_cache.TryGetValue(cacheKey, out LinkPreviewResponse? cachedResponse))
if (this.cache.TryGetValue(cacheKey, out LinkPreviewResponse? cachedResponse))
{
return cachedResponse!;
}

var response = await FetchLinkPreviewAsync(url, optionalFields, cancellationToken);
var response = await this.FetchLinkPreviewAsync(url, optionalFields, cancellationToken);

var cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(
TimeSpan.FromMinutes(_options.Value.CacheTTLMinutes)
TimeSpan.FromMinutes(this.options.Value.CacheTTLMinutes)
);

_cache.Set(cacheKey, response, cacheEntryOptions);
this.cache.Set(cacheKey, response, cacheEntryOptions);

return response;
}

[RequiresUnreferencedCode(
"Calls System.Text.Json.JsonSerializer.Deserialize<TValue>(String, JsonSerializerOptions)"
)]
[RequiresDynamicCode(
"Calls System.Text.Json.JsonSerializer.Deserialize<TValue>(String, JsonSerializerOptions)"
)]
private async Task<LinkPreviewResponse> FetchLinkPreviewAsync(
string url,
LinkPreviewOptionalField? optionalFields,
Expand Down Expand Up @@ -92,7 +104,7 @@ CancellationToken cancellationToken
queryParams.Add("fields", fields);
}

var response = await _httpClient.GetAsync(
var response = await this.httpClient.GetAsync(
QueryHelpers.AddQueryString("", queryParams),
cancellationToken
);
Expand Down

0 comments on commit 2a095e3

Please sign in to comment.