Skip to content

Commit c1f3efa

Browse files
RonnyB71Ronny Birkeli
and
Ronny Birkeli
authored
Feature/8969 inbound event support in app (#65)
Co-authored-by: Ronny Birkeli <[email protected]>
1 parent 40341c9 commit c1f3efa

File tree

79 files changed

+6708
-479
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+6708
-479
lines changed

.editorconfig

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
2+
[*.{cs,vb}]
3+
#### Naming styles ####
4+
5+
# Naming rules
6+
7+
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
8+
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
9+
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
10+
11+
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
12+
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
13+
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
14+
15+
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
16+
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
17+
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
18+
19+
# Symbol specifications
20+
21+
dotnet_naming_symbols.interface.applicable_kinds = interface
22+
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
23+
dotnet_naming_symbols.interface.required_modifiers =
24+
25+
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
26+
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
27+
dotnet_naming_symbols.types.required_modifiers =
28+
29+
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
30+
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
31+
dotnet_naming_symbols.non_field_members.required_modifiers =
32+
33+
# Naming styles
34+
35+
dotnet_naming_style.begins_with_i.required_prefix = I
36+
dotnet_naming_style.begins_with_i.required_suffix =
37+
dotnet_naming_style.begins_with_i.word_separator =
38+
dotnet_naming_style.begins_with_i.capitalization = pascal_case
39+
40+
dotnet_naming_style.pascal_case.required_prefix =
41+
dotnet_naming_style.pascal_case.required_suffix =
42+
dotnet_naming_style.pascal_case.word_separator =
43+
dotnet_naming_style.pascal_case.capitalization = pascal_case
44+
45+
dotnet_naming_style.pascal_case.required_prefix =
46+
dotnet_naming_style.pascal_case.required_suffix =
47+
dotnet_naming_style.pascal_case.word_separator =
48+
dotnet_naming_style.pascal_case.capitalization = pascal_case
49+
dotnet_style_coalesce_expression = true:suggestion
50+
dotnet_style_null_propagation = true:suggestion
51+
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
52+
dotnet_style_prefer_auto_properties = true:silent
53+
dotnet_style_object_initializer = true:suggestion
54+
dotnet_style_collection_initializer = true:suggestion
55+
dotnet_style_operator_placement_when_wrapping = beginning_of_line
56+
tab_width = 4
57+
indent_size = 4
58+
end_of_line = crlf
59+
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
60+
61+
[*.cs]
62+
csharp_using_directive_placement = outside_namespace:silent
63+
csharp_prefer_simple_using_statement = true:suggestion
64+
csharp_prefer_braces = true:silent
65+
csharp_style_namespace_declarations = block_scoped:silent
66+
csharp_style_prefer_method_group_conversion = true:silent
67+
csharp_style_prefer_top_level_statements = true:silent
68+
csharp_style_expression_bodied_methods = false:silent
69+
csharp_style_expression_bodied_constructors = false:silent
70+
csharp_style_expression_bodied_operators = false:silent
71+
csharp_style_expression_bodied_properties = true:silent
72+
csharp_style_expression_bodied_indexers = true:silent
73+
csharp_style_expression_bodied_accessors = true:silent
74+
csharp_style_expression_bodied_lambdas = true:silent
75+
csharp_style_expression_bodied_local_functions = false:silent
76+
csharp_indent_labels = one_less_than_current
77+
78+
[Program.cs]
79+
dotnet_diagnostic.CA1050.severity = none
80+
dotnet_diagnostic.S1118.severity = none

AppLibDotnet.sln

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ EndProject
1010
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Altinn.App.Api.Tests", "test\Altinn.App.Api.Tests\Altinn.App.Api.Tests.csproj", "{2FD56505-1DB2-4AE1-8911-E076E535EAC6}"
1111
EndProject
1212
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6C8EB054-1747-4BAC-A637-754F304BCAFA}"
13+
ProjectSection(SolutionItems) = preProject
14+
.editorconfig = .editorconfig
15+
EndProjectSection
1316
EndProject
1417
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7AD5FADE-607F-4D5F-8511-6647D0C1AA1C}"
1518
EndProject

doc/Eformidling/integration-flow.drawio.svg

+497
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Altinn.App.Core.Configuration;
4+
using Altinn.App.Core.Features;
5+
using Altinn.App.Core.Internal.Events;
6+
using Altinn.App.Core.Models;
7+
using Microsoft.AspNetCore.Http;
8+
using Microsoft.AspNetCore.Mvc;
9+
using Microsoft.Extensions.Logging;
10+
using Microsoft.Extensions.Options;
11+
12+
namespace Altinn.App.Api.Controllers
13+
{
14+
/// <summary>
15+
/// Controller for handling inbound events from the event system
16+
/// </summary>
17+
[Route("{org}/{app}/api/v1/eventsreceiver")]
18+
public class EventsReceiverController : ControllerBase
19+
{
20+
private readonly IEventHandlerResolver _eventHandlerResolver;
21+
private readonly ILogger _logger;
22+
private readonly IEventSecretCodeProvider _secretCodeProvider;
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="EventsReceiverController"/> class.
26+
/// </summary>
27+
public EventsReceiverController(
28+
IEventHandlerResolver eventHandlerResolver,
29+
ILogger<EventsReceiverController> logger,
30+
IOptions<PlatformSettings> options,
31+
IEventSecretCodeProvider secretCodeProvider)
32+
{
33+
_eventHandlerResolver = eventHandlerResolver;
34+
_logger = logger;
35+
_secretCodeProvider = secretCodeProvider;
36+
}
37+
38+
/// <summary>
39+
/// Create a new inbound event for the app to process.
40+
/// </summary>
41+
[HttpPost]
42+
[ProducesResponseType(StatusCodes.Status200OK)]
43+
[ProducesResponseType(425)]
44+
[ProducesResponseType(500)]
45+
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
46+
public async Task<ActionResult> Post([FromQuery] string code, [FromBody] CloudEvent cloudEvent)
47+
{
48+
if (await _secretCodeProvider.GetSecretCode() != code)
49+
{
50+
return Unauthorized();
51+
}
52+
53+
IEventHandler eventHandler = _eventHandlerResolver.ResolveEventHandler(cloudEvent.Type);
54+
try
55+
{
56+
bool eventSuccessfullyProcessed = await eventHandler.ProcessEvent(cloudEvent);
57+
58+
// A return value of 425 will ensure the event system triggers the retry mecanism.
59+
// Actually any other return value than 200 Ok will do this, but this is the "correct way"
60+
// of saying we would like to be reminded again later.
61+
return eventSuccessfullyProcessed ? Ok() : new StatusCodeResult(425);
62+
}
63+
catch (NotImplementedException)
64+
{
65+
return BadRequest();
66+
}
67+
catch (Exception ex)
68+
{
69+
_logger.LogError("Unable to process event {eventType}. An exception was raised while checking for delivery status of message {messageid}. Exception throw {exceptionMessage}", cloudEvent.Type, cloudEvent.Id, ex.Message);
70+
return new StatusCodeResult(500);
71+
}
72+
}
73+
}
74+
}

src/Altinn.App.Api/Controllers/ProcessController.cs

-6
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,6 @@ public async Task<ActionResult<ProcessState>> StartProcess(
166166
[Authorize(Policy = "InstanceRead")]
167167
[HttpGet("next")]
168168
[ProducesResponseType(StatusCodes.Status200OK)]
169-
[ProducesResponseType(StatusCodes.Status404NotFound)]
170169
[ProducesResponseType(StatusCodes.Status409Conflict)]
171170
public async Task<ActionResult<List<string>>> GetNextElements(
172171
[FromRoute] string org,
@@ -195,11 +194,6 @@ public async Task<ActionResult<List<string>>> GetNextElements(
195194

196195
List<ProcessElement> nextElements = await _flowHydration.NextFollowAndFilterGateways(instance, currentTaskId, false);
197196

198-
if (nextElements.Count == 0)
199-
{
200-
return NotFound("Cannot find any valid process elements that can be reached from current task");
201-
}
202-
203197
return Ok(nextElements.Select(e => e.Id).ToList());
204198
}
205199
catch (PlatformHttpException e)

src/Altinn.App.Core/Altinn.App.Core.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<FrameworkReference Include="Microsoft.AspNetCore.App" />
99
</ItemGroup>
1010
<ItemGroup>
11+
<PackageReference Include="Altinn.ApiClients.Maskinporten" Version="6.0.0" />
1112
<PackageReference Include="Altinn.Common.AccessTokenClient" Version="1.0.6" />
1213
<PackageReference Include="Altinn.Common.EFormidlingClient" Version="1.3.2" />
1314
<PackageReference Include="Altinn.Common.PEP" Version="1.0.39" />
@@ -20,4 +21,7 @@
2021
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
2122
<PackageReference Include="Scrutor" Version="4.2.0" />
2223
</ItemGroup>
24+
<ItemGroup>
25+
<Folder Include="Features\Events\" />
26+
</ItemGroup>
2327
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Altinn.App.Core.EFormidling
2+
{
3+
/// <summary>
4+
/// Shared constants within the Eformidling area.
5+
/// </summary>
6+
public static class EformidlingConstants
7+
{
8+
/// <summary>
9+
/// Name of event type for publishing and subscribing to be remined about instances sent
10+
/// and that needs status checking.
11+
/// </summary>
12+
public const string CheckInstanceStatusEventType = "app.eformidling.reminder.checkinstancestatus";
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using Altinn.App.Core.Infrastructure.Clients.Events;
2+
using Altinn.App.Core.Internal.Events;
3+
using Altinn.App.Core.Models;
4+
using Microsoft.Extensions.Hosting;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace Altinn.App.Core.EFormidling
8+
{
9+
/// <summary>
10+
/// Hosted service to set up prequisites for Eformidling integration.
11+
/// </summary>
12+
public class EformidlingStartup : IHostedService
13+
{
14+
private readonly AppIdentifier _appIdentifier;
15+
private readonly IEventsSubscription _eventsSubscriptionClient;
16+
private readonly ILogger<EformidlingStartup> _logger;
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="EformidlingStartup"/> class.
20+
/// </summary>
21+
public EformidlingStartup(AppIdentifier appId, IEventsSubscription eventsSubscriptionClient, ILogger<EformidlingStartup> logger)
22+
{
23+
_appIdentifier = appId;
24+
_eventsSubscriptionClient = eventsSubscriptionClient;
25+
_logger = logger;
26+
}
27+
28+
///<inheritDoc/>
29+
public async Task StartAsync(CancellationToken cancellationToken)
30+
{
31+
var eventType = EformidlingConstants.CheckInstanceStatusEventType;
32+
try
33+
{
34+
Subscription subscription = await _eventsSubscriptionClient.AddSubscription(_appIdentifier.Org, _appIdentifier.App, eventType);
35+
36+
_logger.LogInformation("Successfully subscribed to event {eventType} for app {appIdentifier}. Subscription {subscriptionId} is being used.", eventType, _appIdentifier, subscription.Id);
37+
}
38+
39+
catch
40+
{
41+
_logger.LogError("Unable to subscribe to event {eventType} for app {appIdentifier}", eventType, _appIdentifier);
42+
throw;
43+
}
44+
}
45+
46+
/// <inheritdoc/>
47+
public Task StopAsync(CancellationToken cancellationToken)
48+
{
49+
return Task.CompletedTask;
50+
}
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Altinn.App.Core.EFormidling.Implementation;
22
using Altinn.App.Core.EFormidling.Interface;
3+
using Altinn.App.Core.Features;
34
using Altinn.Common.EFormidlingClient;
45
using Microsoft.Extensions.Configuration;
56
using Microsoft.Extensions.DependencyInjection;
@@ -12,35 +13,33 @@ namespace Altinn.App.Core.EFormidling.Extensions;
1213
public static class ServiceCollectionExtensions
1314
{
1415
/// <summary>
15-
/// Add Eformidling services and app specific <see cref="Altinn.App.Core.EFormidling.Interface.IEFormidlingMetadata"/> implementation.
16-
/// Default implementation of <see cref="Altinn.App.Core.EFormidling.Interface.IEFormidlingReceivers"/> will be registered <see cref="Altinn.App.Core.EFormidling.Implementation.DefaultEFormidlingReceivers"/>.
16+
/// Add Eformidling services and app specific <see cref="IEFormidlingMetadata"/> implementation.
17+
/// Default implementation of <see cref="IEFormidlingReceivers"/> will be registered <see cref="DefaultEFormidlingReceivers"/>.
1718
/// </summary>
1819
/// <param name="services">The <see cref="IServiceCollection"/> being built.</param>
1920
/// <param name="configuration">A reference to the current <see cref="IConfiguration"/> object.</param>
20-
/// <typeparam name="TM">App specific implementation of <see cref="Altinn.App.Core.EFormidling.Interface.IEFormidlingMetadata"/></typeparam>
21+
/// <typeparam name="TM">App specific implementation of <see cref="IEFormidlingMetadata"/></typeparam>
2122
public static void AddEFormidlingServices<TM>(this IServiceCollection services, IConfiguration configuration) where TM: IEFormidlingMetadata
2223
{
23-
services.AddHttpClient<IEFormidlingClient, Altinn.Common.EFormidlingClient.EFormidlingClient>();
24-
services.AddTransient<IEFormidlingReceivers, DefaultEFormidlingReceivers>();
25-
services.AddTransient<IEFormidlingService, DefaultEFormidlingService>();
26-
services.Configure<Altinn.Common.EFormidlingClient.Configuration.EFormidlingClientSettings>(configuration.GetSection("EFormidlingClientSettings"));
27-
services.AddTransient(typeof(IEFormidlingMetadata), typeof(TM));
24+
AddEFormidlingServices<TM, DefaultEFormidlingReceivers>(services, configuration);
2825
}
2926

3027
/// <summary>
31-
/// Add Eformidling services and app specific <see cref="Altinn.App.Core.EFormidling.Interface.IEFormidlingMetadata" /> and <see cref="Altinn.App.Core.EFormidling.Interface.IEFormidlingReceivers" /> implementation.
32-
/// <see cref="Altinn.App.Core.EFormidling.Implementation.DefaultEFormidlingReceivers" /> will not be registered.
28+
/// Add Eformidling services and app specific <see cref="IEFormidlingMetadata" /> and <see cref="IEFormidlingReceivers" /> implementation.
29+
/// <see cref="DefaultEFormidlingReceivers" /> will not be registered.
3330
/// </summary>
3431
/// <param name="services">The <see cref="IServiceCollection"/> being built.</param>
3532
/// <param name="configuration">A reference to the current <see cref="IConfiguration"/> object.</param>
36-
/// <typeparam name="TM">App specific implementation of <see cref="Altinn.App.Core.EFormidling.Interface.IEFormidlingMetadata"/></typeparam>
37-
/// <typeparam name="TR">App specific implementation of <see cref="Altinn.App.Core.EFormidling.Interface.IEFormidlingReceivers"/></typeparam>
33+
/// <typeparam name="TM">App specific implementation of <see cref="IEFormidlingMetadata"/></typeparam>
34+
/// <typeparam name="TR">App specific implementation of <see cref="IEFormidlingReceivers"/></typeparam>
3835
public static void AddEFormidlingServices<TM, TR>(this IServiceCollection services, IConfiguration configuration) where TM: IEFormidlingMetadata where TR: IEFormidlingReceivers
3936
{
40-
services.AddHttpClient<IEFormidlingClient, Altinn.Common.EFormidlingClient.EFormidlingClient>();
4137
services.AddTransient(typeof(IEFormidlingReceivers), typeof(TR));
38+
services.AddHttpClient<IEFormidlingClient, Common.EFormidlingClient.EFormidlingClient>();
4239
services.AddTransient<IEFormidlingService, DefaultEFormidlingService>();
43-
services.Configure<Altinn.Common.EFormidlingClient.Configuration.EFormidlingClientSettings>(configuration.GetSection("EFormidlingClientSettings"));
40+
services.Configure<Common.EFormidlingClient.Configuration.EFormidlingClientSettings>(configuration.GetSection("EFormidlingClientSettings"));
4441
services.AddTransient(typeof(IEFormidlingMetadata), typeof(TM));
42+
services.AddTransient<IEventHandler, EformidlingStatusCheckEventHandler>();
43+
services.AddHostedService<EformidlingStartup>();
4544
}
4645
}

0 commit comments

Comments
 (0)