public bool EvaluateToBoolean(string source, object context) { var result = _jmesPathQuery.Search(source, context); var value = ConditionBoolean(result); return(value); }
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); } } } }
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); }
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); }