public async Task Interceptors_03() { var svc = Common.Initialize(); var executor = svc.GetService(typeof(IHttpExecutorAsync)) as IHttpExecutorAsync; var result = await executor.ExecuteAsync( new MagicRequest { URL = "modules/interceptors/foo-args", Verb = "get", Query = new Dictionary <string, string>(), Headers = new Dictionary <string, string>(), Cookies = new Dictionary <string, string>(), Host = "localhost", Scheme = "http" }); Assert.Equal(200, result.Result); Assert.Single(result.Headers); var hl = result.Content as string; Assert.NotNull(hl); var lambda = HyperlambdaParser.Parse(hl); // Notice, no [.arguments] node since no arguments were supplied to endpoint file. Assert.Equal(".interceptor-value", lambda.Children.First().Name); Assert.Equal("howdy", lambda.Children.First().Value); Assert.Equal(".endpoint-value", lambda.Children.Skip(1).First().Name); Assert.Equal("world", lambda.Children.Skip(1).First().Value); }
public async Task PostEchoPartialArgumentList() { var svc = Common.Initialize(); var executor = svc.GetService(typeof(IHttpExecutorAsync)) as IHttpExecutorAsync; var input = HyperlambdaParser.Parse(@" input1:foo input2:int:5"); var result = await executor.ExecuteAsync( new MagicRequest { URL = "modules/echo", Verb = "post", Query = new Dictionary <string, string>(), Headers = new Dictionary <string, string>(), Cookies = new Dictionary <string, string>(), Host = "localhost", Scheme = "http", Payload = input }); Assert.Equal(200, result.Result); Assert.Empty(result.Headers); var j = result.Content as JObject; Assert.NotNull(j); Assert.Equal(2, j.Count); Assert.Equal("foo", j["input1"].Value <string>()); Assert.Equal(5, j["input2"].Value <int>()); }
static public Node Evaluate(string hl) { var lambda = HyperlambdaParser.Parse(hl); var signaler = Initialize().GetService(typeof(ISignaler)) as ISignaler; signaler.Signal("eval", lambda); return(lambda); }
static public Node Evaluate(string hl) { var signaler = Initialize(); var lambda = HyperlambdaParser.Parse(hl); signaler.Signal("eval", lambda); return(lambda); }
static async public Task <Node> EvaluateAsync(string hl) { var signaler = Initialize(); var lambda = HyperlambdaParser.Parse(hl); await signaler.SignalAsync("eval", lambda); return(lambda); }
static public async Task <Node> EvaluateAsync(string hl) { var services = Initialize(); var lambda = HyperlambdaParser.Parse(hl); var signaler = services.GetService(typeof(ISignaler)) as ISignaler; await signaler.SignalAsync("eval", lambda); return(lambda); }
public async Task PutEcho() { var svc = Common.Initialize(); var executor = svc.GetService(typeof(IHttpExecutorAsync)) as IHttpExecutorAsync; var input = HyperlambdaParser.Parse(@" input1:foo input2:int:5 input3:bool:true input4 . arr1:bool:true arr2:57 arr3:any-object . arr1:bool:false arr2:int:67 arr3:guid:4c248403-23a7-4808-988c-1be59a4a90af input5 obj1:foo obj2:true"); var result = await executor.ExecuteAsync( new MagicRequest { URL = "modules/echo", Verb = "put", Query = new Dictionary <string, string>(), Headers = new Dictionary <string, string>(), Cookies = new Dictionary <string, string>(), Host = "localhost", Scheme = "http", Payload = input }); Assert.Equal(200, result.Result); Assert.Empty(result.Headers); var j = result.Content as JObject; Assert.NotNull(j); Assert.Equal("foo", j["input1"].Value <string>()); Assert.Equal(5, j["input2"].Value <int>()); Assert.True(j["input3"].Value <bool>()); Assert.NotNull(j["input4"].Value <JArray>()); Assert.Equal(2, j["input4"].Value <JArray>().Count); Assert.True(j["input4"].Value <JArray>()[0]["arr1"].Value <bool>()); Assert.Equal(57, j["input4"].Value <JArray>()[0]["arr2"].Value <int>()); Assert.Equal("any-object", j["input4"].Value <JArray>()[0]["arr3"].Value <string>()); Assert.False(j["input4"].Value <JArray>()[1]["arr1"].Value <bool>()); Assert.Equal(67, j["input4"].Value <JArray>()[1]["arr2"].Value <int>()); Assert.True(j["input4"].Value <JArray>()[1]["arr3"].Value <Guid>().ToString() != Guid.Empty.ToString()); Assert.NotNull(j["input5"].Value <JObject>()); Assert.Equal("foo", j["input5"].Value <JObject>()["obj1"].Value <string>()); Assert.True(j["input5"].Value <JObject>()["obj2"].Value <bool>()); }
static public async Task <Node> EvaluateAsync( string hl, MockSmtpClient smtp, MockPop3Client pop3 = null) { var services = Initialize(smtp, pop3); var lambda = HyperlambdaParser.Parse(hl); var signaler = services.GetService(typeof(ISignaler)) as ISignaler; await signaler.SignalAsync("eval", lambda); return(lambda); }
static public Node Evaluate( string hl, MockSmtpClient smtp = null, MockPop3Client pop3 = null) { var services = Initialize(smtp, pop3); var lambda = HyperlambdaParser.Parse(hl); var signaler = services.GetService(typeof(ISignaler)) as ISignaler; signaler.Signal("eval", lambda); return(lambda); }
/* * Will recursively execute every single Hyperlambda file inside of * the specified folder. */ static void ExecuteHyperlambdaFilesRecursively(IApplicationBuilder app, string folder) { // Creating our services. var fileService = app.ApplicationServices.GetService <IFileService>(); var folderService = app.ApplicationServices.GetService <IFolderService>(); var signaler = app.ApplicationServices.GetService <ISignaler>(); // Startup folder, now executing all Hyperlambda files inside of it. foreach (var idxFile in fileService.ListFiles(folder, ".hl")) { var lambda = HyperlambdaParser.Parse(fileService.Load(idxFile)); signaler.Signal("eval", lambda); } // Recursively checking sub folders. foreach (var idxFolder in folderService.ListFolders(folder)) { ExecuteHyperlambdaFilesRecursively(app, idxFolder); } }
/// <inheritdoc/> public async Task <MagicResponse> ExecuteAsync(MagicRequest request) { // Sanity checking invocation if (string.IsNullOrEmpty(request.URL)) { return new MagicResponse { Result = 404 } } ; // Making sure we never resolve to anything outside of "modules/" and "system/" folder. if (!request.URL.StartsWith("modules/") && !request.URL.StartsWith("system/")) { return new MagicResponse { Result = 401 } } ; // Figuring out file to execute, and doing some basic sanity checking. var path = Utilities.GetEndpointFilePath(_rootResolver, request.URL, request.Verb); if (!await _fileService.ExistsAsync(path)) { return new MagicResponse { Result = 404 } } ; // Creating our lambda object by loading Hyperlambda file. var lambda = HyperlambdaParser.Parse(await _fileService.LoadAsync(path)); // Applying interceptors. lambda = await ApplyInterceptors(lambda, request.URL); // Attaching arguments. _argumentsHandler.Attach(lambda, request.Query, request.Payload); // Invoking method responsible for actually executing lambda object. return(await ExecuteAsync(lambda, request)); } #region [ -- Private helper methods -- ] /* * Applies interceptors to specified Node/Lambda object. */ async Task <Node> ApplyInterceptors(Node result, string url) { // Checking to see if interceptors exists recursively upwards in folder hierarchy. var splits = url.Split(new char [] { '/' }, StringSplitOptions.RemoveEmptyEntries); // Stripping away last entity (filename) of invocation. var folders = splits.Take(splits.Length - 1); // Iterating as long as we have more entities in list of folders. while (true) { // Checking if "current-folder/interceptor.hl" file exists. var current = _rootResolver.AbsolutePath(string.Join("/", folders) + "/interceptor.hl"); if (_fileService.Exists(current)) { result = await ApplyInterceptor(result, current); } // Checking if we're done, and at root folder, at which point we break while loop. if (!folders.Any()) { break; // We're done, no more interceptors! } // Traversing upwards in hierarchy to be able to nest interceptors upwards in hierarchy. folders = folders.Take(folders.Count() - 1); } // Returning result to caller. return(result); } /* * Applies the specified interceptor and returns the transformed Node/Lambda result. */ async Task <Node> ApplyInterceptor(Node lambda, string interceptorFile) { // Getting interceptor lambda. var interceptNode = HyperlambdaParser.Parse(await _fileService.LoadAsync(interceptorFile)); // Moving [.arguments] from endpoint lambda to the top of interceptor lambda if existing. var args = lambda .Children .Where(x => x.Name == ".arguments" || x.Name == ".description" || x.Name == ".type" || x.Name == "auth.ticket.verify" || x.Name.StartsWith("validators.")); // Notice, reversing arguments nodes makes sure we apply arguments in order of appearance. foreach (var idx in args.Reverse().ToList()) { interceptNode.Insert(0, idx); // Notice, will detach the argument from its original position! } // Moving endpoint Lambda to position before any [.interceptor] node found in interceptor lambda. foreach (var idxLambda in new Expression("**/.interceptor").Evaluate(interceptNode).ToList()) { // Iterating through each node in current result and injecting before currently iterated [.lambda] node. foreach (var idx in lambda.Children) { // This logic ensures we keep existing order without any fuzz. // By cloning node we also support having multiple [.interceptor] nodes. idxLambda.InsertBefore(idx.Clone()); } // Removing currently iterated [.interceptor] node in interceptor lambda object. idxLambda.Parent.Remove(idxLambda); } // Returning interceptor Node/Lambda which is now the root of the execution Lambda object. return(interceptNode); } /* * Method responsible for actually executing lambda object after file has been loaded, * interceptors and arguments have been applied, and transforming result of invocation * to a MagicResponse. */ async Task <MagicResponse> ExecuteAsync(Node lambda, MagicRequest request) { // Creating our result wrapper, wrapping whatever the endpoint wants to return to the client. var result = new Node(); var response = new MagicResponse(); try { await _signaler.ScopeAsync("http.request", request, async() => { await _signaler.ScopeAsync("http.response", response, async() => { await _signaler.ScopeAsync("slots.result", result, async() => { await _signaler.SignalAsync("eval", lambda); }); }); }); response.Content = GetReturnValue(response, result); return(response); } catch { if (result.Value is IDisposable disposable) { disposable.Dispose(); } if (response.Content is IDisposable disposable2 && !object.ReferenceEquals(response.Content, result.Value)) { disposable2.Dispose(); } throw; } } /* * Creates a returned payload of some sort and returning to caller. */ object GetReturnValue(MagicResponse httpResponse, Node lambda) { /* * An endpoint can return either a Node/Lambda hierarchy or a simple value. * First we check if endpoint returned a simple value, at which point we convert it to * a string. Notice, we're prioritising simple values, implying if return node has a * simple value, none of its children nodes will be returned. */ if (lambda.Value != null) { // IDisposables (Streams e.g.) are automatically disposed by ASP.NET Core. if (lambda.Value is IDisposable || lambda.Value is byte[]) { return(lambda.Value); } return(lambda.Get <string>()); } else if (lambda.Children.Any()) { // Checking if we should return content as Hyperlambda. if (httpResponse.Headers.TryGetValue("Content-Type", out var val) && val == "application/x-hyperlambda") { return(HyperlambdaGenerator.GetHyperlambda(lambda.Children)); } // Defaulting to returning content as JSON by converting from Lambda to JSON. var convert = new Node(); convert.AddRange(lambda.Children.ToList()); _signaler.Signal(".lambda2json-raw", convert); return(convert.Value); } return(null); // No content } #endregion } }