Example #1
0
        public bool EvaluateToBoolean(string source, object context)
        {
            var result = _jmesPathQuery.Search(source, context);
            var value  = ConditionBoolean(result);

            return(value);
        }
Example #2
0
        private async Task ExecuteOperation(ExecutionContext context)
        {
            var operation = context.Operation;

            if (operation.values != null)
            {
                context.AddValuesIn(ProcessValues(operation.values, context.Values));
            }

            var patternOkay = context.PatternMatcher.IsMatch(context.Path);

            var message = ConvertToString(ProcessValues(operation.message, context.Values));

            var conditionOkay = true;

            if (!string.IsNullOrEmpty(operation.condition))
            {
                var conditionResult = _jmesPathQuery.Search(operation.condition, context.Values);
                conditionOkay = ConditionBoolean(conditionResult);
            }

            for (var shouldExecute = patternOkay && conditionOkay; shouldExecute; shouldExecute = await EvaluateRepeat(context))
            {
                if (!string.IsNullOrEmpty(message))
                {
                    _console.WriteLine();
                    _console.WriteLine($"{new string(' ', context.Indent * 2)}- {message.Color(ConsoleColor.Cyan)}");
                }

                var debugPath = Path.Combine(OutputDirectory.Required(), "logs", $"{++_operationCount:000}-{new string('-', context.Indent * 2)}{new string((message ?? operation.write ?? operation.request ?? operation.template ?? string.Empty).Select(ch => char.IsLetterOrDigit(ch) ? ch : '-').ToArray())}.yaml");
                Directory.CreateDirectory(Path.GetDirectoryName(debugPath));
                using (var writer = _secretTracker.FilterTextWriter(File.CreateText(debugPath)))
                {
                    var logentry = new Dictionary <object, object>
                    {
                        {
                            "operation",
                            new Dictionary <object, object>
                            {
                                { "message", message },
                                { "target", operation.target },
                                { "condition", operation.condition },
                                { "repeat", operation.repeat },
                                { "request", operation.request },
                                { "template", operation.template },
                                { "write", operation.write },
                            }
                        },
                        { "valuesIn", context.ValuesIn },
                        { "valuesOut", context.ValuesOut },
                        { "request", null },
                        { "response", null },
                        { "cumulativeValues", context.Values },
                    };
                    try
                    {
                        object result = null;

                        // First special type of operation - executing a request
                        if (!string.IsNullOrWhiteSpace(operation.request))
                        {
                            WorkflowModel.Request request = context.TemplateEngine.Render <WorkflowModel.Request>(
                                operation.request,
                                context.Values);

                            logentry["request"] = request;

                            HttpAuthentication auth = null;
                            if (request.auth != null)
                            {
                                // TODO: remove these defaults
                                auth = new HttpAuthentication
                                {
                                    tenant      = request?.auth?.tenant ?? "common",
                                    resourceId  = request?.auth?.resource ?? "499b84ac-1321-427f-aa17-267ca6975798",
                                    clientId    = request?.auth?.client ?? "e8f3cc86-b3b2-4ebb-867c-9c314925b384",
                                    interactive = IsInteractive
                                };
                            }

                            var client = _clientFactory.Create(auth);

                            var method = new HttpMethod(request.method ?? "GET");
                            if (IsDryRun && method.Method != "GET")
                            {
                                _console.WriteLine($"Skipping {method.Method.ToString().Color(ConsoleColor.DarkYellow)} {request.url}");
                            }
                            else
                            {
                                try
                                {
                                    var jsonRequest = new JsonRequest
                                    {
                                        method  = method,
                                        url     = request.url,
                                        headers = request.headers,
                                        body    = request.body,
                                        secret  = request.secret,
                                    };

                                    var jsonResponse = await client.SendAsync(jsonRequest);

                                    if ((int)jsonResponse.status >= 400)
                                    {
                                        throw new ApplicationException($"Request failed with status code {jsonResponse.status}");
                                    }

                                    result = new WorkflowModel.Response
                                    {
                                        status  = (int)jsonResponse.status,
                                        headers = jsonResponse.headers,
                                        body    = jsonResponse.body,
                                    };

                                    logentry["response"] = result;
                                }
                                catch
                                {
                                    // TODO - retry logic here?
                                    throw;
                                }
                            }
                        }

                        // Second special type of operation - rendering a template
                        if (!string.IsNullOrWhiteSpace(operation.template))
                        {
                            if (!string.IsNullOrEmpty(operation.write))
                            {
                                var targetPath = Path.Combine(OutputDirectory.Required(), operation.write);

                                Directory.CreateDirectory(Path.GetDirectoryName(targetPath));

                                using (var targetWriter = File.CreateText(targetPath))
                                {
                                    if (!string.IsNullOrWhiteSpace(operation.template))
                                    {
                                        context.TemplateEngine.Render(operation.template, context.Values, targetWriter);
                                    }
                                }
                            }
                            else
                            {
                                result = context.TemplateEngine.Render <object>(operation.template, context.Values);
                            }
                        }

                        // Third special type of operation - nested operations
                        if (operation.operations != null)
                        {
                            result = await ExecuteOperations(context, operation.operations);
                        }

                        // If output is specifically stated - use it to query
                        if (operation.output != null)
                        {
                            if (operation.operations != null)
                            {
                                // for nested operations, output expressions can pull in the current operation's cumulative values as well
                                context.AddValuesOut(ProcessValues(operation.output, MergeUtils.Merge(result, context.Values) ?? context.Values));
                            }
                            else if (result != null)
                            {
                                // for request and template operations, the current operation result is a well-known property to avoid collisions
                                var merged = MergeUtils.Merge(new Dictionary <object, object> {
                                    { "result", result }
                                }, context.Values);

                                context.AddValuesOut(ProcessValues(operation.output, merged));
                            }
                            else
                            {
                                // there are no values coming out of this operation - output queries are based only on cumulative values
                                context.AddValuesOut(ProcessValues(operation.output, context.Values));
                            }
                        }

                        if (operation.@throw != null)
                        {
                            var throwMessage = ConvertToString(ProcessValues([email protected], context.Values));
                            throwMessage = string.IsNullOrEmpty(throwMessage) ? message : throwMessage;

                            var throwDetails = ProcessValues([email protected], context.Values);

                            _console.WriteLine(throwMessage.Color(ConsoleColor.DarkRed));
                            if (throwDetails != null)
                            {
                                _console.WriteLine(_serializers.YamlSerializer.Serialize(throwDetails).Color(ConsoleColor.DarkRed));
                            }

                            throw new OperationException(string.IsNullOrEmpty(throwMessage) ? message : throwMessage)
                                  {
                                      Details = throwDetails
                                  };
                        }

                        // otherwise if output is unstated, and there are nested operations with output - those flows back as effective output
                        if (operation.output == null && operation.operations != null && result != null)
                        {
                            context.AddValuesOut(result);
                        }
                    }
                    catch (Exception ex)
                    {
                        logentry["error"] = new Dictionary <object, object>
                        {
                            { "type", ex.GetType().FullName },
                            { "message", ex.Message },
                            { "stack", ex.StackTrace },
                        };

                        throw;
                    }
                    finally
                    {
                        logentry["valuesIn"]         = context?.ValuesIn;
                        logentry["valuesOut"]        = context?.ValuesOut;
                        logentry["cumulativeValues"] = context?.Values;
                        _serializers.YamlSerializer.Serialize(writer, logentry);
                    }
                }
            }
        }
Example #3
0
        public async Task <JsonResponse> SendAsync <TResponse>(JsonRequest jsonRequest)
        {
            var request = new HttpRequestMessage(jsonRequest.method, jsonRequest.url);

            if (jsonRequest.body != null)
            {
                if (jsonRequest.body is string)
                {
                    request.Content = new StringContent((string)jsonRequest.body);
                }
                else if (jsonRequest.body is byte[])
                {
                    request.Content = new ByteArrayContent((byte[])jsonRequest.body);
                }
                else
                {
                    var memoryStream = new MemoryStream();
                    using (var writer = new StreamWriter(memoryStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), bufferSize: 1024, leaveOpen: true))
                    {
                        _serializer.Serialize(writer, jsonRequest.body);
                    }

                    memoryStream.Position = 0;
                    request.Content       = new StreamContent(memoryStream)
                    {
                        Headers =
                        {
                            ContentType = new MediaTypeHeaderValue("application/json")
                        }
                    };
                }
            }

            if (jsonRequest.headers != null)
            {
                foreach (var kv in jsonRequest.headers)
                {
                    var name = kv.Key.ToString();
                    IEnumerable <string> values;
                    if (kv.Value is IList <object> )
                    {
                        values = ((IList <object>)kv.Value).Select(value => value.ToString());
                    }
                    else
                    {
                        values = new[] { kv.Value.ToString() };
                    }

                    foreach (var value in values)
                    {
                        if (!request.Headers.TryAddWithoutValidation(name, values))
                        {
                            request?.Content.Headers.TryAddWithoutValidation(name, values);
                        }
                    }
                }
            }

            var response = await _httpClient.SendAsync(request);

            var jsonResponse = new JsonResponse
            {
                status  = (int)response.StatusCode,
                headers = response.Headers
                          .Concat(response?.Content?.Headers ?? Enumerable.Empty <KeyValuePair <string, IEnumerable <string> > >())
                          .ToDictionary(kv => (object)kv.Key, kv => (object)kv.Value.ToList <object>()),
            };

            // var responseBody = await response.Content.ReadAsStringAsync();
            // using (var reader = new StringReader(responseBody))
            using (var reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
            {
                jsonResponse.body = _deserializer.Deserialize <TResponse>(reader);
            }

            // Information is added to secret tracker as soon as possible
            if (!string.IsNullOrEmpty(jsonRequest.secret) && jsonResponse.body != null)
            {
                var searchResult = _jmesPath.Search(jsonRequest.secret, new { result = jsonResponse });

                var secrets = searchResult as IEnumerable <object>;
                if (secrets != null)
                {
                    foreach (var secret in secrets)
                    {
                        if (secret != null)
                        {
                            _secretTracker.AddSecret(secret.ToString());
                        }
                    }
                }
                else
                {
                    if (searchResult != null)
                    {
                        _secretTracker.AddSecret(searchResult.ToString());
                    }
                }
            }

            return(jsonResponse);
        }
Example #4
0
        public async Task <JsonResponse> SendAsync <TResponse>(JsonRequest jsonRequest)
        {
            var request = new HttpRequestMessage(jsonRequest.method, jsonRequest.url);

            if (jsonRequest.body != null)
            {
                if (jsonRequest.body is string textBody)
                {
                    request.Content = new StringContent(textBody);
                }
                else if (jsonRequest.body is byte[] binaryBody)
                {
                    request.Content = new ByteArrayContent(binaryBody);
                }
                else
                {
                    var memoryStream = new MemoryStream();
                    using (var writer = new StreamWriter(memoryStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), bufferSize: 1024, leaveOpen: true))
                    {
                        _serializers.JsonSerializer.Serialize(writer, jsonRequest.body);
                    }

                    memoryStream.Position = 0;
                    request.Content       = new StreamContent(memoryStream)
                    {
                        Headers =
                        {
                            ContentType = new MediaTypeHeaderValue("application/json")
                        }
                    };
                }
            }

            if (jsonRequest.headers != null)
            {
                foreach (var kv in jsonRequest.headers)
                {
                    var name = kv.Key.ToString();
                    IEnumerable <string> values;
                    if (kv.Value is IList <object> )
                    {
                        values = ((IList <object>)kv.Value).Select(value => value.ToString());
                    }
                    else
                    {
                        values = new[] { kv.Value.ToString() };
                    }

                    foreach (var value in values)
                    {
                        if (!request.Headers.TryAddWithoutValidation(name, values))
                        {
                            request?.Content.Headers.TryAddWithoutValidation(name, values);
                        }
                    }
                }
            }

            var response = await _httpClient.SendAsync(request);

            var jsonResponse = new JsonResponse
            {
                status  = (int)response.StatusCode,
                headers = response.Headers
                          .Concat(response?.Content?.Headers ?? Enumerable.Empty <KeyValuePair <string, IEnumerable <string> > >())
                          .ToDictionary(kv => (object)kv.Key, kv => (object)kv.Value.ToList <object>()),
            };

            // var responseBody = await response.Content.ReadAsStringAsync();
            // using (var reader = new StringReader(responseBody))
            using (var reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
            {
                try
                {
                    jsonResponse.body = _serializers.YamlDeserializer.Deserialize <TResponse>(reader);
                }
                catch (YamlDotNet.Core.SyntaxErrorException)
                {
                    var failed = true;
                    try
                    {
                        // There is one known case where Newtonsoft supports a value yamldotnet does not.
                        // Specifically when it looks like /Date(1366340594875)/
                        var responseBody = await response.Content.ReadAsStringAsync();

                        var temporary = Newtonsoft.Json.JsonConvert.DeserializeObject(responseBody);
                        var json      = Newtonsoft.Json.JsonConvert.SerializeObject(temporary);
                        jsonResponse.body = _serializers.YamlDeserializer.Deserialize <TResponse>(json);
                        failed            = false;
                    }
                    catch
                    {
                        var responseBody = await response.Content.ReadAsStringAsync();

                        jsonResponse.body = responseBody;
                        failed            = false;
                    }

                    if (failed)
                    {
                        throw;
                    }
                }
            }

            // Information is added to secret tracker as soon as possible
            if (!string.IsNullOrEmpty(jsonRequest.secret) && jsonResponse.body != null)
            {
                var searchResult = _jmesPath.Search(jsonRequest.secret, new { result = jsonResponse });

                var secrets = searchResult as IEnumerable <object>;
                if (secrets != null)
                {
                    foreach (var secret in secrets)
                    {
                        if (secret != null)
                        {
                            _secretTracker.AddSecret(secret.ToString());
                        }
                    }
                }
                else
                {
                    if (searchResult != null)
                    {
                        _secretTracker.AddSecret(searchResult.ToString());
                    }
                }
            }

            return(jsonResponse);
        }