Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions InvvardDev.Ifttt.Service.Api.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34330.188
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InvvardDev.Ifttt.Service.Api.Core", "src\InvvardDev.Ifttt.Service.Api.Core\InvvardDev.Ifttt.Service.Api.Core.csproj", "{2A300429-5FD8-40E3-BECD-6CDBE16F2CD6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InvvardDev.Ifttt.Service.Api.Trigger", "src\InvvardDev.Ifttt.Service.Api.Trigger\InvvardDev.Ifttt.Service.Api.Trigger.csproj", "{7A94E577-6B7C-4AC5-A602-01FC41ED4D84}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{8217D2BC-C98F-444C-B8D7-47CB074F6A66}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sample", "Sample", "{D3D189E6-DB32-4E25-ACEE-A253B03C7281}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvvardDev.Ifttt.Trigger.UpdatedNuget", "sample\InvvardDev.Ifttt.Trigger.UpdatedNuget\InvvardDev.Ifttt.Trigger.UpdatedNuget.csproj", "{15680BDC-86C0-4810-87A4-02200D01CFC4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvvardDev.Ifttt.Service.Api.Trigger.Tests", "tests\InvvardDev.Ifttt.Service.Api.Trigger.Tests\InvvardDev.Ifttt.Service.Api.Trigger.Tests.csproj", "{F94D7981-BB31-467B-AA87-8924964CA186}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2A300429-5FD8-40E3-BECD-6CDBE16F2CD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A300429-5FD8-40E3-BECD-6CDBE16F2CD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A300429-5FD8-40E3-BECD-6CDBE16F2CD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A300429-5FD8-40E3-BECD-6CDBE16F2CD6}.Release|Any CPU.Build.0 = Release|Any CPU
{7A94E577-6B7C-4AC5-A602-01FC41ED4D84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A94E577-6B7C-4AC5-A602-01FC41ED4D84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A94E577-6B7C-4AC5-A602-01FC41ED4D84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A94E577-6B7C-4AC5-A602-01FC41ED4D84}.Release|Any CPU.Build.0 = Release|Any CPU
{15680BDC-86C0-4810-87A4-02200D01CFC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15680BDC-86C0-4810-87A4-02200D01CFC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{15680BDC-86C0-4810-87A4-02200D01CFC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15680BDC-86C0-4810-87A4-02200D01CFC4}.Release|Any CPU.Build.0 = Release|Any CPU
{F94D7981-BB31-467B-AA87-8924964CA186}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F94D7981-BB31-467B-AA87-8924964CA186}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F94D7981-BB31-467B-AA87-8924964CA186}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F94D7981-BB31-467B-AA87-8924964CA186}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {61C7BA3B-15B1-46EE-9471-EEA384386D41}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{15680BDC-86C0-4810-87A4-02200D01CFC4} = {D3D189E6-DB32-4E25-ACEE-A253B03C7281}
{F94D7981-BB31-467B-AA87-8924964CA186} = {8217D2BC-C98F-444C-B8D7-47CB074F6A66}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
</ItemGroup>

<ItemGroup>
<Content Update="appsettings.Development.json">
<DependentUpon>appsettings.json</DependentUpon>
</Content>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\InvvardDev.Ifttt.Service.Api.Trigger\InvvardDev.Ifttt.Service.Api.Trigger.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@InvvardDev.Ifttt.Trigger.UpdatedNuget_HostAddress = http://localhost:5268

###
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using InvvardDev.Ifttt.Service.Api.Trigger.Attributes;
using InvvardDev.Ifttt.Service.Api.Trigger.Models;
using InvvardDev.Ifttt.Trigger.UpdatedNuget.Triggers;

namespace InvvardDev.Ifttt.Trigger.UpdatedNuget.Models;

[TriggerFields(NugetPackageUpdatedTrigger.TriggerSlug)]
public class WatchedNugetTriggerFields : TriggerFieldsBase
{
[TriggerField("nuget_package_name")]
public string NugetPackageName { get; init; }

[TriggerField("updated_version")]
public string UpdatedVersion { get; init; }

[TriggerField("updated_date")]
public DateTime UpdatedDate { get; init; }
}
33 changes: 33 additions & 0 deletions sample/InvvardDev.Ifttt.Trigger.UpdatedNuget/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using InvvardDev.Ifttt.Service.Api.Trigger;

var builder = WebApplication.CreateBuilder(args);

ConfigureServices(builder.Services);

var webApp = builder.Build();

Configure(webApp);

webApp.Run();

void ConfigureServices(IServiceCollection services)
{
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
services.AddControllers();

services.AddTriggers();
}

void Configure(WebApplication app)
{
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapControllers();

app.ConfigureTriggers();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:15593",
"sslPort": 44322
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5268",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7125;http://localhost:5268",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using InvvardDev.Ifttt.Service.Api.Trigger;
using InvvardDev.Ifttt.Service.Api.Trigger.Attributes;
using InvvardDev.Ifttt.Service.Api.Trigger.Contracts;
using InvvardDev.Ifttt.Service.Api.Trigger.Models;
using InvvardDev.Ifttt.Trigger.UpdatedNuget.Models;

namespace InvvardDev.Ifttt.Trigger.UpdatedNuget.Triggers;

[Trigger(TriggerSlug)]
public class NugetPackageUpdatedTrigger : ITrigger
{
internal const string TriggerSlug = "nuget_package_updated";

public Task ExecuteAsync(TriggerRequest triggerRequest, CancellationToken cancellationToken = default)
{
var triggerFields = triggerRequest.TriggerFields.To<WatchedNugetTriggerFields>();
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"IftttOptions": {
"ServiceKey": "<YOUR_IFTTT_SERVICE_KEY>",
"BypassServiceKey": true
}
}
12 changes: 12 additions & 0 deletions sample/InvvardDev.Ifttt.Trigger.UpdatedNuget/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"IftttOptions": {
"ServiceKey": "<YOUR_IFTTT_SERVICE_KEY>"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using InvvardDev.Ifttt.Service.Api.Core.Configuration;
using Microsoft.Extensions.Options;

namespace InvvardDev.Ifttt.Service.Api.Core.Authentication;

public class ServiceKeyMiddleware(RequestDelegate next, IOptions<IftttOptions> options)
{
private readonly string serviceKey = options.Value.ServiceKey;

public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Headers.TryGetValue(IftttConstants.ServiceKeyHeader, out var receivedServiceKey) && receivedServiceKey == serviceKey)
{
await next(context);
}
else
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace InvvardDev.Ifttt.Service.Api.Core.Configuration;

public static class IftttConstants
{
public static string ServiceKeyHeader => "IFTTT-Service-Key";

private const string BaseApiPath = "ifttt/v1";
public const string BaseTriggersApiPath = $"{BaseApiPath}/triggers";
public const string StatusApiPath = $"{BaseApiPath}/status";
public const string TestingApiPath = $"{BaseApiPath}/test/setup";

public static string TriggerHttpClientName => "ifttt-trigger";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;

namespace InvvardDev.Ifttt.Service.Api.Core.Configuration;

public class IftttOptions
{
public const string DefaultSectionName = nameof(IftttOptions);

[Required]
public required string ServiceKey { get; init; } = string.Empty;

public bool BypassServiceKey { get; init; } = false;

public string RealTimeBaseAddress { get; set; } = "https://realtime.ifttt.com/v1/notifications/";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using InvvardDev.Ifttt.Service.Api.Core.Configuration;
using Microsoft.AspNetCore.Mvc;

namespace InvvardDev.Ifttt.Service.Api.Core.Controllers;

[ApiController]
[Route(IftttConstants.StatusApiPath)]
public class StatusController : ControllerBase
{
[HttpGet]
public IActionResult GetStatus()
{
return Ok();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using InvvardDev.Ifttt.Service.Api.Core.Configuration;
using Microsoft.AspNetCore.Mvc;

namespace InvvardDev.Ifttt.Service.Api.Core.Controllers;

[ApiController]
[Route(IftttConstants.TestingApiPath)]
[Consumes("application/json")]
[Produces("application/json")]
public class TestSetupController : ControllerBase
{
[HttpPost]
public Task<IActionResult> SetupTest()
{
return Task.FromResult<IActionResult>(Ok());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using InvvardDev.Ifttt.Service.Api.Core.Authentication;
using InvvardDev.Ifttt.Service.Api.Core.Configuration;
using Microsoft.Extensions.Options;

namespace InvvardDev.Ifttt.Service.Api.Core;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddIftttApiClient(this IServiceCollection services)
{
services.AddOptions<IftttOptions>()
.BindConfiguration(IftttOptions.DefaultSectionName)
.ValidateDataAnnotations();

return services;
}

public static IApplicationBuilder ConfigureIftttApiClient(this WebApplication app)
{
var options = app.Services.GetService<IOptions<IftttOptions>>();
if (!app.Environment.IsDevelopment() || options?.Value.BypassServiceKey is false)
{
app.UseMiddleware<ServiceKeyMiddleware>();
}

return app;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0"/>
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions src/InvvardDev.Ifttt.Service.Api.Core/Models/Source.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace InvvardDev.Ifttt.Service.Api.Core.Models;

public record Source(string? Id, string? Url);
14 changes: 14 additions & 0 deletions src/InvvardDev.Ifttt.Service.Api.Core/Models/TopLevelBaseModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
using System.Text.Json;

namespace InvvardDev.Ifttt.Service.Api.Core.Models;

public class TopLevelBaseModel
{
protected static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
};
}
14 changes: 14 additions & 0 deletions src/InvvardDev.Ifttt.Service.Api.Core/Models/TopLevelErrorModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Text.Json;

namespace InvvardDev.Ifttt.Service.Api.Core.Models;

public class TopLevelErrorModel(IList<ErrorMessage> Errors) : TopLevelBaseModel
{
public static string Serialize(IList<ErrorMessage> errors)
=> Serialize(errors, JsonSerializerOptions);

public static string Serialize(IList<ErrorMessage> errors, JsonSerializerOptions options)
=> JsonSerializer.Serialize(new TopLevelErrorModel(errors), options);
}

public record ErrorMessage(string Message);
Loading