Skip to content

Commit

Permalink
added "bee.queryString" variable
Browse files Browse the repository at this point in the history
  • Loading branch information
erik.araojo committed Feb 16, 2023
1 parent 131f7fb commit a9a99a8
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 40 deletions.
12 changes: 9 additions & 3 deletions src/BeeRock.Core/Entities/Middlewares/ReverseProxyMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,15 @@ public static void ConfigureReverseProxy(this IApplicationBuilder app, IProxyRou
context.Response.StatusCode = (int)responseMessage.StatusCode;
CopyResponseHeaders(context, responseMessage);
var content = await responseMessage.Content.ReadAsByteArrayAsync();
if (responseMessage.StatusCode != HttpStatusCode.NoContent) await context.Response.Body.WriteAsync(content);
var doNotWriteToBody = responseMessage.StatusCode == HttpStatusCode.NoContent ||
targetRequestMessage.Method == HttpMethod.Head ||
(targetRequestMessage.Method == HttpMethod.Delete && responseMessage.StatusCode == HttpStatusCode.Accepted);
if (!doNotWriteToBody) {
var content = await responseMessage.Content.ReadAsByteArrayAsync();
await context.Response.Body.WriteAsync(content);
}
var metric = new RoutingMetric {
RouteIndex = routeIndex,
Expand Down Expand Up @@ -97,7 +103,7 @@ private static void CopyRequestHeaders(HttpContext context, HttpRequestMessage r
requestMessage.Content = streamContent;
}


var isGet = HttpMethods.IsGet(requestMethod);
foreach (var header in context.Request.Headers)
if (contentheaders.Contains(header.Key))
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
Expand Down
11 changes: 7 additions & 4 deletions src/BeeRock.Core/Entities/ProxyRouteSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,18 @@ public Uri BuildUri(Uri source) {

SelectedRouteConfig = routeConfig;

if (routeConfig == null) {
throw new Exception($"Unable to match a route to the request {source.AbsoluteUri}");
}
if (routeConfig == null) throw new Exception($"Unable to match a route to the request {source.AbsoluteUri}");

var sb = new StringBuilder(routeConfig.To.PathTemplate);
foreach (var (key, value) in routeParameters) sb.Replace(key, value);

var path = sb.ToString().TrimStart('/');
return new Uri($"{routeConfig.To.Scheme}://{routeConfig.To.Host}/{path}");
var uriPath = $"{routeConfig.To.Scheme}://{routeConfig.To.Host}/{path}";
if (!string.IsNullOrWhiteSpace(source.Query)) {
return new Uri(uriPath + source.Query);
}

return new Uri(uriPath);
}

public ProxyRoute SelectedRouteConfig { get; private set; }
Expand Down
12 changes: 12 additions & 0 deletions src/BeeRock.Core/Entities/RequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace BeeRock.Core.Entities;
public static class RequestHandler {
public const string HeaderKey = "headers";
public const string ContextKey = "httpContext";
public const string QueryStringKey = "queryString";
public static IRestRequestTestArgsProvider TestArgsProvider { get; set; }

/// <summary>
Expand Down Expand Up @@ -56,6 +57,8 @@ private static T HandleInternal<T>(string methodName, Dictionary<string, object>

//wrap the http request/response headers for easy access to the .py scripts
CreateHttpHeadersVariable(variables);
CreateQueryStringVariable(variables);

var methodArgs = TestArgsProvider.Find(methodName);

try {
Expand Down Expand Up @@ -177,4 +180,13 @@ private static void CreateHttpHeadersVariable(Dictionary<string, object> variabl
variables[HeaderKey] = wrapper;
// return header;
}

/// <summary>
/// Wrap the headers in a function that can be accessed easily in the script
/// </summary>
private static void CreateQueryStringVariable(Dictionary<string, object> variables) {
var ctx = (HttpContext)variables[ContextKey];
var wrapper = new ScriptingQueryString(ctx.Request.Query);
variables[QueryStringKey] = wrapper;
}
}
1 change: 1 addition & 0 deletions src/BeeRock.Core/Entities/RestControllerReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ static string FormatTypeName(Type type) {
.ToList();

p.Add(ScriptingVarUtils.GetHeadersParamInfo());
p.Add(ScriptingVarUtils.GetQueryStringParamInfo());
p.Add(ScriptingVarUtils.GetFileRespParamInfo());
p.Add(ScriptingVarUtils.GetRunParamInfo());
p.Add(ScriptingVarUtils.GetRmqParamInfo());
Expand Down
20 changes: 12 additions & 8 deletions src/BeeRock.Core/Entities/Scripting/ScriptingHttpHeader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Http;

// ReSharper disable UnusedMember.Global. The methods here will be called in the python script

namespace BeeRock.Core.Entities.Scripting;
Expand All @@ -19,13 +20,14 @@ private static string Get(IHeaderDictionary headers, string key) {
var all = string.Join(", ", headers.Keys);
return $"Header {key} not found. Current headers are : {all}";
}

private static void Add(IHeaderDictionary headers, string key, string value) {
if (headers.ContainsKey(key))
headers[key] = value;
else {
else
headers.TryAdd(key, value);
}
}

private static void Remove(IHeaderDictionary headers, string key) {
if (headers.ContainsKey(key))
headers.Remove(key);
Expand All @@ -35,29 +37,31 @@ public class Req {
public IHeaderDictionary Headers { get; init; }

public string Get(string header) {
return ScriptingHttpHeader.Get(this.Headers, header);
return ScriptingHttpHeader.Get(Headers, header);
}

public void Add(string key, string value) {
ScriptingHttpHeader.Add(this.Headers, key, value);
ScriptingHttpHeader.Add(Headers, key, value);
}

public void Remove(string key) {
ScriptingHttpHeader.Remove(this.Headers, key);
ScriptingHttpHeader.Remove(Headers, key);
}
}

public class Resp {
public IHeaderDictionary Headers { get; init; }

public string Get(string header) {
return ScriptingHttpHeader.Get(this.Headers, header);
return ScriptingHttpHeader.Get(Headers, header);
}

public void Add(string key, string value) {
ScriptingHttpHeader.Add(this.Headers, key, value);
ScriptingHttpHeader.Add(Headers, key, value);
}

public void Remove(string key) {
ScriptingHttpHeader.Remove(this.Headers, key);
ScriptingHttpHeader.Remove(Headers, key);
}
}
}
17 changes: 17 additions & 0 deletions src/BeeRock.Core/Entities/Scripting/ScriptingQueryString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Http;

namespace BeeRock.Core.Entities.Scripting;

public class ScriptingQueryString {
private readonly IQueryCollection _query;

public ScriptingQueryString(IQueryCollection query) {
_query = query;
}

public string Get(string key) {
if (_query.TryGetValue(key, out var vals)) return vals.ToString();

return "";
}
}
30 changes: 15 additions & 15 deletions src/BeeRock.Core/Entities/Scripting/ScriptingVarContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Collections.Immutable;
using System.Net;
using Microsoft.AspNetCore.Http;

Expand All @@ -10,13 +9,15 @@ public class ScriptingVarContext {
public ScriptingVarContext(HttpContext ctx) {
_ctx = ctx;
if (ctx != null) {
Request = new Req() { HttpRequest = ctx.Request };
Response = new Resp() { HttpResponse = ctx.Response };
Request = new Req { HttpRequest = ctx.Request };
Response = new Resp { HttpResponse = ctx.Response };
Items = ctx.Items;
Headers = new ScriptingHttpHeader(ctx.Request.Headers, ctx.Response.Headers);
QueryString = new ScriptingQueryString(ctx.Request.Query);
}
}

public ScriptingQueryString QueryString { get; init; }
public ScriptingHttpHeader Headers { get; init; }

public IDictionary<object, object> Items { get; }
Expand All @@ -28,45 +29,44 @@ public ScriptingVarContext(HttpContext ctx) {
// public bool IsPassThrough => Items.ContainsKey(nameof(PassThroughResponse));

public void Capture(object result) {
if (this.Response.IsPassThrough) {
if (Response.IsPassThrough) {
if (result is not string)
throw new Exception("Only string (text) is supported for PassThrough responses");

Items[nameof(PassThroughResponse)] = new PassThroughResponse {
Content = result == null ? "" : (this.Response.ContentType.TrimEnd().EndsWith("/json") ? result.ToString().Trim() : result.ToString()),
ContentType = this.Response.ContentType,
StatusCode = this.Response.StatusCode
Content = result == null ? "" : Response.ContentType.TrimEnd().EndsWith("/json") ? result.ToString().Trim() : result.ToString(),
ContentType = Response.ContentType,
StatusCode = Response.StatusCode
};
}
}


public class Resp {
static int[] intCodes = Enum.GetValues(typeof(HttpStatusCode)).Cast<int>().ToArray();
private static readonly int[] intCodes = Enum.GetValues(typeof(HttpStatusCode)).Cast<int>().ToArray();
public HttpResponse HttpResponse { get; init; }

public string ContentType { get; private set; } = "application/json";
public bool IsPassThrough { get; private set;} = false;
public int StatusCode { get; private set;} = 200;
public bool IsPassThrough { get; private set; }
public int StatusCode { get; private set; } = 200;

public void SetContentType(string contentType) {
this.ContentType = contentType;
ContentType = contentType;
}

public void SetStatusCode(int statusCode) {
if (!intCodes.Contains(statusCode))
throw new Exception($"Unable to assign an invalid status code value of {statusCode}");

this.StatusCode = statusCode;
StatusCode = statusCode;
}

public void SetAsPassThrough(bool isPassThrough = true) {
this.IsPassThrough = isPassThrough;
IsPassThrough = isPassThrough;
}
}

public class Req {
public HttpRequest HttpRequest { get; init; }


}
}
18 changes: 18 additions & 0 deletions src/BeeRock.Core/Entities/Scripting/ScriptingVarUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ public static ParamInfo GetFileRespParamInfo() {
return p;
}

public static ParamInfo GetQueryStringParamInfo() {
var p = new ParamInfo {
Name = $"{RequestHandler.QueryStringKey}",
Type = typeof(ScriptingQueryString),
TypeName = "QueryString Parameters",
DisplayValue = @"
----------------------------------------------------
Use ""queryString"" to get query parameters in the URL
----------------------------------------------------
Sample usage:
1. Get a query parameter from URL. Ex : http://localhost/v1?key=value
Ex. <<queryString.Get(""key"")>>
"
};

return p;
}

public static ParamInfo GetLogParamInfo() {
var t = default(ScriptingVarBee);
var p = new ParamInfo {
Expand Down
3 changes: 2 additions & 1 deletion src/BeeRock/UI/ViewModels/ProxyRouteItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public ProxyRouteItem(ProxyRoute proxyRoute, IDocProxyRouteRepo proxyRouteRepo,
t => t.FromPathTemplate,
t => t.ToHost,
t => t.ToScheme,
t => t.ToPathTemplate)
t => t.ToPathTemplate,
t => t.IsEnabled)
.Throttle(TimeSpan.FromSeconds(1))
.Subscribe(t => Save())
.Void(d => disposable.Add(d));
Expand Down
2 changes: 1 addition & 1 deletion src/BeeRock/UI/ViewModels/TabItemReverseProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public int PortNumber {
public string Name { get; set; }
public ICommand CloseCommand { get; }
public string TabType { get; } = "ReverseProxyTab";
public string HeaderText { get; } = "Gateway";
public string HeaderText { get; } = "Reverse Proxy";
public bool IsServiceHost { get; } = false;

public async Task Init() {
Expand Down
17 changes: 11 additions & 6 deletions src/BeeRock/UI/Views/ReverseProxyControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@
</StackPanel>
</Grid>

<!-- Routing Configuration -->

<!-- Statistics Section -->
<Grid Grid.Row="2" RowDefinitions="Auto, Auto" Width="1200" HorizontalAlignment="Center">
<StackPanel Grid.Row="0" Orientation="Vertical" Margin="0 10 0 0">
<TextBlock Text="Statistics" Classes="h2" />
Expand Down Expand Up @@ -142,7 +141,9 @@
</Border>
</Grid>
</Grid>
<Border Width="1200" BorderThickness="0" IsVisible="True" Grid.Row="3" Height="15" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
<Border Width="1200" BorderThickness="0" IsVisible="True" Grid.Row="3" Height="25" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />

<!-- Routing Configuration Section-->
<Grid Grid.Row="4" RowDefinitions="Auto, Auto, *" Width="1200" HorizontalAlignment="Center">
<Grid.Resources>
<Flyout x:Key="Flyout" ShowMode="TransientWithDismissOnPointerMoveAway" Opened="Flyout_OnOpened">
Expand Down Expand Up @@ -174,22 +175,26 @@
</Viewbox>
</Button>
<Border Width="1" Background="Gray" Margin="5" Height="16" />
<Button Background="Transparent" Command="{Binding MoveUpCommand}">
<Button Background="Transparent" Command="{Binding MoveUpCommand}"
IsEnabled="{Binding SelectedProxyRoute, Converter={x:Static ObjectConverters.IsNotNull}}">
<Viewbox Width="18" Height="18">
<Canvas Width="24" Height="24">
<Path Fill="WhiteSmoke" Data="M13,20H11V8L5.5,13.5L4.08,12.08L12,4.16L19.92,12.08L18.5,13.5L13,8V20Z" />
</Canvas>
</Viewbox>
</Button>
<Button Margin="5 0 0 0" Background="Transparent" Command="{Binding MoveDownCommand}">
<Button Margin="5 0 0 0" Background="Transparent"
IsEnabled="{Binding SelectedProxyRoute, Converter={x:Static ObjectConverters.IsNotNull}}"
Command="{Binding MoveDownCommand}">
<Viewbox Width="18" Height="18">
<Canvas Width="24 " Height="24">
<Path Fill="WhiteSmoke" Data="M11,4H13V16L18.5,10.5L19.92,11.92L12,19.84L4.08,11.92L5.5,10.5L11,16V4Z" />
</Canvas>
</Viewbox>
</Button>
<Border Width="1" Background="Gray" Margin="5" Height="16" />
<Button Padding="0" Background="Transparent" HorizontalAlignment="Center" Margin="5"
<Button Background="Transparent" HorizontalAlignment="Center" Margin="5"
IsEnabled="{Binding SelectedProxyRoute, Converter={x:Static ObjectConverters.IsNotNull}}"
Flyout="{StaticResource Flyout}">
<Viewbox Width="18" Height="18">
<Canvas Width="24" Height="24">
Expand Down
4 changes: 2 additions & 2 deletions test/BeeRock.Tests/UseCases/AddServiceUseCaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ await d.AddService(addParams)
Assert.AreEqual("POST", o.Methods[0].HttpMethod);
Assert.AreEqual("AddPet", o.Methods[0].MethodName);
Assert.AreEqual(typeof(void), o.Methods[0].ReturnType);
Assert.AreEqual(8, o.Methods[0].Parameters.Count);
Assert.AreEqual(9, o.Methods[0].Parameters.Count);
Assert.AreEqual("bee.Log", o.Methods[0].Parameters.Last().Name);
Assert.AreEqual("FakePet", o.Methods[0].Parameters[0].TypeName);
Assert.AreEqual("v2/pet", o.Methods[0].RouteTemplate);
Assert.AreEqual("GET", o.Methods[1].HttpMethod);
Assert.AreEqual("FindPetsByStatus", o.Methods[1].MethodName);
Assert.AreEqual(typeof(List<FakePet>), o.Methods[1].ReturnType);
Assert.AreEqual(8, o.Methods[1].Parameters.Count);
Assert.AreEqual(9, o.Methods[1].Parameters.Count);
Assert.AreEqual("List<FakeStatus>", o.Methods[1].Parameters[0].TypeName);
},
exc => { Assert.Fail("Should not reach this part because we already predefined TestController"); });
Expand Down

0 comments on commit a9a99a8

Please sign in to comment.