Skip to content

Update TestToolv2 Lambda Runtime API for multi function and RequestResponse mode #1912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@page "/"
@page "/"

@using Amazon.Lambda.TestTool.Commands
@using Amazon.Lambda.TestTool.Services
Expand All @@ -9,7 +9,7 @@
@using Microsoft.AspNetCore.Http;

@inject IHttpContextAccessor HttpContextAccessor
@inject IRuntimeApiDataStore RuntimeApiModel
@inject IRuntimeApiDataStoreManager DataStoreManager
@inject IDirectoryManager DirectoryManager

<PageTitle>Lambda Function Tester</PageTitle>
Expand Down Expand Up @@ -62,33 +62,33 @@
</div>
<div class="col-sm-6">
<h3>Active Event:</h3>
@if (RuntimeApiModel.ActiveEvent == null)
@if (DataStore.ActiveEvent == null)
{
<h2>No active event</h2>
}
else
{
<div>
<div style="cursor: pointer" @onclick="() => OnRequeue(RuntimeApiModel.ActiveEvent.AwsRequestId)">
<div style="cursor: pointer" @onclick="() => OnRequeue(DataStore.ActiveEvent.AwsRequestId)">
@((MarkupString)RebootIcon)
</div>
<p><b>Request ID:</b> @RuntimeApiModel.ActiveEvent.AwsRequestId</p>
<p><b>Status:</b> <span style="@GetStatusStyle(RuntimeApiModel.ActiveEvent.EventStatus)">@RuntimeApiModel.ActiveEvent.EventStatus</span></p>
<p><b>Last Updated:</b> @RuntimeApiModel.ActiveEvent.LastUpdated</p>
<p><b>Event JSON:</b><span class="event-value"><span class="fake-link" @onclick="() => ShowEventJson(RuntimeApiModel.ActiveEvent)">@CreateSnippet(RuntimeApiModel.ActiveEvent.EventJson)</span></span></p>
@if (RuntimeApiModel.ActiveEvent.EventStatus == EventContainer.Status.Failure)
<p><b>Request ID:</b> @DataStore.ActiveEvent.AwsRequestId</p>
<p><b>Status:</b> <span style="@GetStatusStyle(DataStore.ActiveEvent.EventStatus)">@DataStore.ActiveEvent.EventStatus</span></p>
<p><b>Last Updated:</b> @DataStore.ActiveEvent.LastUpdated</p>
<p><b>Event JSON:</b><span class="event-value"><span class="fake-link" @onclick="() => ShowEventJson(DataStore.ActiveEvent)">@CreateSnippet(DataStore.ActiveEvent.EventJson)</span></span></p>
@if (DataStore.ActiveEvent.EventStatus == EventContainer.Status.Failure)
{
<p><b>Error Type:</b> @RuntimeApiModel.ActiveEvent.ErrorType</p>
<p><b>Error Type:</b> @DataStore.ActiveEvent.ErrorType</p>
<p>
<b>Error Response:</b>
<pre class="form-control" style="@Constants.ResponseErrorStyleSizeConstraint">@RuntimeApiModel.ActiveEvent.ErrorResponse</pre>
<pre class="form-control" style="@Constants.ResponseErrorStyleSizeConstraint">@DataStore.ActiveEvent.ErrorResponse</pre>
</p>
}
else
{
<p>
<b>Response:</b>
<pre class="form-control" style="@Constants.ResponseSuccessStyleSizeConstraint">@Utils.TryPrettyPrintJson(RuntimeApiModel.ActiveEvent.Response)</pre>
<pre class="form-control" style="@Constants.ResponseSuccessStyleSizeConstraint">@Utils.TryPrettyPrintJson(DataStore.ActiveEvent.Response)</pre>
</p>
}
</div>
Expand All @@ -101,7 +101,7 @@
<div class="col-sm-6">
<h3>Queued Events: <button class="btn btn-secondary btn-sm" @onclick="OnClearQueued">Clear</button></h3>
<div class="col-xs-5 event-list">
@foreach (var evnt in @RuntimeApiModel.QueuedEvents)
@foreach (var evnt in @DataStore.QueuedEvents)
{
<div class="event-list-item">
<div class="row" style="padding: 2px">
Expand All @@ -123,7 +123,7 @@
<div class="col-sm-6">
<h3>Executed Events: <button class="btn btn-secondary btn-sm" @onclick="OnClearExecuted">Clear</button></h3>
<div class="col-xs-5 event-list">
@foreach (var evnt in @RuntimeApiModel.ExecutedEvents.OrderByDescending(x => x.LastUpdated))
@foreach (var evnt in @DataStore.ExecutedEvents.OrderByDescending(x => x.LastUpdated))
{
<div class="event-list-item">
<div class="row" style="padding: 2px">
Expand Down Expand Up @@ -189,6 +189,8 @@

private IDictionary<string, IList<LambdaRequest>> SampleRequests { get; set; } = new Dictionary<string, IList<LambdaRequest>>();

private IRuntimeApiDataStore DataStore => DataStoreManager.GetLambdaRuntimeDataStore(LambdaRuntimeApi.DefaultFunctionName);

string? _selectedSampleRequestName;
string? SelectedSampleRequestName
{
Expand Down Expand Up @@ -218,58 +220,58 @@

protected override void OnInitialized()
{
RuntimeApiModel.StateChange += RuntimeApiModelOnStateChange;
DataStore.StateChange += DataStoreOnStateChange;
SampleRequestManager = new SampleRequestManager(DirectoryManager.GetCurrentDirectory());
SampleRequests = SampleRequestManager.GetSampleRequests();
}

private void RuntimeApiModelOnStateChange(object? sender, EventArgs e)
private void DataStoreOnStateChange(object? sender, EventArgs e)
{
InvokeAsync(this.StateHasChanged);
}

void OnAddEventClick()
{
RuntimeApiModel.QueueEvent(FunctionInput);
DataStore.QueueEvent(FunctionInput, false);
FunctionInput = "";
SelectedSampleRequestName = NoSampleSelectedId;
StateHasChanged();
}

void OnClearQueued()
{
RuntimeApiModel.ClearQueued();
DataStore.ClearQueued();
StateHasChanged();
}

void OnClearExecuted()
{
RuntimeApiModel.ClearExecuted();
DataStore.ClearExecuted();
StateHasChanged();
}

void OnRequeue(string awsRequestId)
{
EventContainer? evnt = null;
if (string.Equals(RuntimeApiModel.ActiveEvent?.AwsRequestId, awsRequestId))
if (string.Equals(DataStore.ActiveEvent?.AwsRequestId, awsRequestId))
{
evnt = RuntimeApiModel.ActiveEvent;
evnt = DataStore.ActiveEvent;
}
else
{
evnt = RuntimeApiModel.ExecutedEvents.FirstOrDefault(x => string.Equals(x.AwsRequestId, awsRequestId));
evnt = DataStore.ExecutedEvents.FirstOrDefault(x => string.Equals(x.AwsRequestId, awsRequestId));
}

if (evnt == null)
return;

RuntimeApiModel.QueueEvent(evnt.EventJson);
DataStore.QueueEvent(evnt.EventJson, false);
StateHasChanged();
}

void OnDeleteEvent(string awsRequestId)
{
RuntimeApiModel.DeleteEvent(awsRequestId);
DataStore.DeleteEvent(awsRequestId);
StateHasChanged();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using Amazon.Lambda.TestTool.Services;

namespace Amazon.Lambda.TestTool.Models;

public class EventContainer
public class EventContainer : IDisposable
{
public enum Status { Queued, Executing, Success, Failure }

Expand All @@ -21,6 +21,9 @@ public enum Status { Queued, Executing, Success, Failure }
public DateTime LastUpdated { get; private set; }

private Status _status = Status.Queued;

private ManualResetEventSlim? _resetEvent;

public Status EventStatus
{
get => _status;
Expand All @@ -33,12 +36,17 @@ public Status EventStatus

private readonly RuntimeApiDataStore _dataStore;

public EventContainer(RuntimeApiDataStore dataStore, int eventCount, string eventJson)
public EventContainer(RuntimeApiDataStore dataStore, int eventCount, string eventJson, bool isRequestResponseMode)
{
LastUpdated = DateTime.Now;
_dataStore = dataStore;
AwsRequestId = eventCount.ToString("D12");
EventJson = eventJson;

if (isRequestResponseMode)
{
_resetEvent = new ManualResetEventSlim(false);
}
}

public string FunctionArn
Expand All @@ -51,6 +59,12 @@ public void ReportSuccessResponse(string response)
LastUpdated = DateTime.Now;
Response = response;
EventStatus = Status.Success;

if (_resetEvent != null)
{
_resetEvent.Set();
}

_dataStore.RaiseStateChanged();
}

Expand All @@ -60,6 +74,50 @@ public void ReportErrorResponse(string errorType, string errorBody)
ErrorType = errorType;
ErrorResponse = errorBody;
EventStatus = Status.Failure;

if (_resetEvent != null)
{
_resetEvent.Set();
}

_dataStore.RaiseStateChanged();
}

public bool WaitForCompletion()
{
if (_resetEvent == null)
{
return false;
}

// The 15 minutes is a fail safe so we at some point we unblock the thread. It is intentionally
// long to give the user time to debug the Lambda function.
return _resetEvent.Wait(TimeSpan.FromMinutes(15));
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}

if (disposing)
{
if (_resetEvent != null)
{
_resetEvent.Dispose();
_resetEvent = null;
}
}

_disposed = true;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using Amazon.Lambda.TestTool.Commands.Settings;
Expand Down Expand Up @@ -37,7 +37,7 @@ public static TestToolProcess Startup(RunCommandSettings settings, CancellationT
{
var builder = WebApplication.CreateBuilder();

builder.Services.AddSingleton<IRuntimeApiDataStore, RuntimeApiDataStore>();
builder.Services.AddSingleton<IRuntimeApiDataStoreManager, RuntimeApiDataStoreManager>();

// Add services to the container.
builder.Services.AddRazorComponents()
Expand Down Expand Up @@ -65,7 +65,7 @@ public static TestToolProcess Startup(RunCommandSettings settings, CancellationT
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();

_ = new LambdaRuntimeApi(app, app.Services.GetService<IRuntimeApiDataStore>()!);
LambdaRuntimeApi.SetupLambdaRuntimeApiEndpoints(app);

var runTask = app.RunAsync(cancellationToken);

Expand Down
Loading
Loading