Skip to content

Commit

Permalink
Merge pull request #27 from runeberry/jb-bug
Browse files Browse the repository at this point in the history
Exception handling + crash reporting overhaul
  • Loading branch information
dolphinspired authored May 10, 2021
2 parents 4e3a7e2 + dc8b3c4 commit 73d1c8a
Show file tree
Hide file tree
Showing 86 changed files with 2,591 additions and 223 deletions.
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,4 @@ healthchecksdb
MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

# Signing certificates
*.snk
.ionide/
4 changes: 4 additions & 0 deletions SolutionResources/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ValheimServerGUI.snk
appsettings.secret.json
appsettings.local.json
Secrets.Values.cs
6 changes: 6 additions & 0 deletions SolutionResources/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Solution Resources

### Developer note

You must include the files outlined in the .gitignore in order to properly build and publish this project.
Contact Runeberry Software for more information.
26 changes: 26 additions & 0 deletions SolutionResources/Secrets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Collections.Generic;

namespace ValheimServerGUI.Properties
{
/// <summary>
/// The values in this class are populated by the static constructor in the corresponding
/// partial class, which is kept out of source control.
/// </summary>
public static partial class Secrets
{
/// <summary>
/// The HTTP header that will contain the API key for requests made to the Runeberry API.
/// </summary>
public static string RuneberryApiKeyHeader { get; } = string.Empty;

/// <summary>
/// The API key that will be attached to all VSG client requests to the Runeberry API.
/// </summary>
public static string RuneberryClientApiKey { get; }

/// <summary>
/// The Runeberry API keys that will be accepted by the server.
/// </summary>
public static HashSet<string> RuneberryServerApiKeys { get; } = new();
}
}
3 changes: 2 additions & 1 deletion ValheimServerGUI.Controls/Controls/TextFormField.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions ValheimServerGUI.Controls/Controls/TextFormField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ public int MaxLength
set => this.TextBox.MaxLength = value;
}

public bool Multiline
{
get => this.TextBox.Multiline;
set => this.TextBox.Multiline = value;
}

public TextFormField()
{
InitializeComponent();
Expand Down
4 changes: 3 additions & 1 deletion ValheimServerGUI.Controls/ValheimServerGUI.Controls.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
<TargetFramework>net5.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<RootNamespace>ValheimServerGUI</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>ValheimServerGUI.snk</AssemblyOriginatorKeyFile>
<AssemblyOriginatorKeyFile>..\SolutionResources\ValheimServerGUI.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
Expand Down
14 changes: 14 additions & 0 deletions ValheimServerGUI.Serverless.Tests/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"ValheimServerGUI.Serverless.Tests": {
"commandName": "Project"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"resource": "/{proxy+}",
"path": "/api/values",
"httpMethod": "GET",
"headers": null,
"queryStringParameters": null,
"pathParameters": {
"proxy": "api/values"
},
"stageVariables": null,
"requestContext": {
"accountId": "AAAAAAAAAAAA",
"resourceId": "5agfss",
"stage": "test-invoke-stage",
"requestId": "test-invoke-request",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": "AAAAAAAAAAAA",
"cognitoIdentityId": null,
"caller": "BBBBBBBBBBBB",
"apiKey": "test-invoke-api-key",
"sourceIp": "test-invoke-source-ip",
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": "arn:aws:iam::AAAAAAAAAAAA:root",
"userAgent": "Apache-HttpClient/4.5.x (Java/1.8.0_102)",
"user": "AAAAAAAAAAAA"
},
"resourcePath": "/{proxy+}",
"httpMethod": "GET",
"apiId": "t2yh6sjnmk"
},
"body": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<EnableDefaultContentItems>False</EnableDefaultContentItems>
</PropertyGroup>
<ItemGroup>
<Content Include=".\SampleRequests\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="2.0.0" />
<PackageReference Include="Amazon.Lambda.TestUtilities" Version="2.0.0" />
<PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="2.4.0" />
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ValheimServerGUI.Serverless\ValheimServerGUI.Serverless.csproj" />
<ProjectReference Include="..\ValheimServerGUI.Tools\ValheimServerGUI.Tools.csproj" />
</ItemGroup>
</Project>
41 changes: 41 additions & 0 deletions ValheimServerGUI.Serverless.Tests/ValuesControllerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Xunit;
using Amazon.Lambda.Core;
using Amazon.Lambda.TestUtilities;
using Amazon.Lambda.APIGatewayEvents;

using Newtonsoft.Json;

using ValheimServerGUI.Serverless;


namespace ValheimServerGUI.Serverless.Tests
{
public class ValuesControllerTests
{


[Fact]
public async Task TestGet()
{
var lambdaFunction = new LambdaEntryPoint();

var requestStr = File.ReadAllText("./SampleRequests/ValuesController-Get.json");
var request = JsonConvert.DeserializeObject<APIGatewayProxyRequest>(requestStr);
var context = new TestLambdaContext();
var response = await lambdaFunction.FunctionHandlerAsync(request, context);

Assert.Equal(200, response.StatusCode);
Assert.Equal("[\"value1\",\"value2\"]", response.Body);
Assert.True(response.MultiValueHeaders.ContainsKey("Content-Type"));
Assert.Equal("application/json; charset=utf-8", response.MultiValueHeaders["Content-Type"][0]);
}


}
}
14 changes: 14 additions & 0 deletions ValheimServerGUI.Serverless.Tests/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"Lambda.Logging": {
"IncludeCategory": false,
"IncludeLogLevel": false,
"IncludeNewline": true,
"LogLevel": {
"Default": "Debug",
"Microsoft": "Information"
}
},
"AWS": {
"Region": "DefaultRegion"
}
}
89 changes: 89 additions & 0 deletions ValheimServerGUI.Serverless/Controllers/VsgController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon;
using Amazon.Lambda.Core;
using Amazon.S3;
using Amazon.S3.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using ValheimServerGUI.Tools;

namespace ValheimServerGUI.Serverless.Controllers
{
[ApiController]
public class VsgController : ControllerBase
{
private readonly ILogger Logger;
private readonly IConfiguration Configuration;

private ILambdaContext LambdaContext => this.HttpContext.Items["LambdaContext"] as ILambdaContext;

public VsgController(ILogger<VsgController> logger, IConfiguration configuration)
{
Logger = logger;
Configuration = configuration;
}

[HttpPost("crash-report")]
public async Task<IActionResult> CreateCrashReport([FromBody] CrashReport request)
{
Logger.LogInformation("Receiving crash report (standard logger)");

Exception exception;
int statusCode;

try
{
var s3BucketName = Configuration.GetValue<string>("S3BucketName");
var s3BucketRegion = Configuration.GetValue<string>("S3BucketRegion");

var client = new AmazonS3Client(RegionEndpoint.GetBySystemName(s3BucketRegion));

// Ensure that each crash report has an ID
request.CrashReportId ??= Guid.NewGuid().ToString();
request.Source ??= "CrashReport";
request.Timestamp ??= DateTimeOffset.UtcNow;
var filename = $"{request.Source}-{request.Timestamp.Value.ToFileTime()}-{request.CrashReportId}.json";

var s3Request = new PutObjectRequest
{
BucketName = s3BucketName,
Key = $"crash-reports/{filename}",
ContentType = "application/json",
ContentBody = JsonConvert.SerializeObject(request),
};
var s3Response = await client.PutObjectAsync(s3Request);

Logger.LogInformation($"Crash report created: {request.CrashReportId}");

return Accepted(request);
}
catch (AmazonS3Exception e)
{
exception = e;
statusCode = (int)e.StatusCode;
}
catch (Exception e)
{
exception = e;
statusCode = 500;
}

Logger.LogException(exception, $"{exception.GetType().Name} occurred during S3 upload");
Logger.LogError(exception.Message);
Logger.LogError(exception.StackTrace);

return StatusCode(statusCode, new { message = exception.Message });
}

[HttpGet("player-steam-info")]
public async Task<IActionResult> GetPlayerSteamInfo([FromQuery] string steamId)
{
return Ok("Player steam info");
}
}
}
13 changes: 13 additions & 0 deletions ValheimServerGUI.Serverless/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM public.ecr.aws/lambda/dotnet:5.0

WORKDIR /var/task

# This COPY command copies the .NET Lambda project's build artifacts from the host machine into the image.
# The source of the COPY should match where the .NET Lambda project publishes its build artifacts. If the Lambda function is being built
# with the AWS .NET Lambda Tooling, the `--docker-host-build-output-dir` switch controls where the .NET Lambda project
# will be built. The .NET Lambda project templates default to having `--docker-host-build-output-dir`
# set in the aws-lambda-tools-defaults.json file to "bin/Release/net5.0/linux-x64/publish".
#
# Alternatively Docker multi-stage build could be used to build the .NET Lambda project inside the image.
# For more information on this approach checkout the project's README.md file.
COPY "bin/Release/net5.0/linux-x64/publish" .
57 changes: 57 additions & 0 deletions ValheimServerGUI.Serverless/LambdaEntryPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace ValheimServerGUI.Serverless
{
/// <summary>
/// This class extends from APIGatewayProxyFunction which contains the method FunctionHandlerAsync which is the
/// actual Lambda function entry point. The Lambda handler field should be set to
///
/// ValheimServerGUI.Serverless::ValheimServerGUI.Serverless.LambdaEntryPoint::FunctionHandlerAsync
/// </summary>
public class LambdaEntryPoint :

// The base class must be set to match the AWS service invoking the Lambda function. If not Amazon.Lambda.AspNetCoreServer
// will fail to convert the incoming request correctly into a valid ASP.NET Core request.
//
// API Gateway REST API -> Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction
// API Gateway HTTP API payload version 1.0 -> Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction
// API Gateway HTTP API payload version 2.0 -> Amazon.Lambda.AspNetCoreServer.APIGatewayHttpApiV2ProxyFunction
// Application Load Balancer -> Amazon.Lambda.AspNetCoreServer.ApplicationLoadBalancerFunction
//
// Note: When using the AWS::Serverless::Function resource with an event type of "HttpApi" then payload version 2.0
// will be the default and you must make Amazon.Lambda.AspNetCoreServer.APIGatewayHttpApiV2ProxyFunction the base class.

Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction
{
/// <summary>
/// The builder has configuration, logging and Amazon API Gateway already configured. The startup class
/// needs to be configured in this method using the UseStartup<>() method.
/// </summary>
/// <param name="builder"></param>
protected override void Init(IWebHostBuilder builder)
{
builder
.ConfigureAppConfiguration(config =>
{
config.AddJsonFile("appsettings.secret.json");
})
.UseStartup<Startup>();
}

/// <summary>
/// Use this override to customize the services registered with the IHostBuilder.
///
/// It is recommended not to call ConfigureWebHostDefaults to configure the IWebHostBuilder inside this method.
/// Instead customize the IWebHostBuilder in the Init(IWebHostBuilder) overload.
/// </summary>
/// <param name="builder"></param>
protected override void Init(IHostBuilder builder)
{
}
}
}
Loading

0 comments on commit 73d1c8a

Please sign in to comment.