public async Task HandleAsync(CloudEvent cloudEvent, CancellationToken cancellationToken) { // This is an example of how CloudEvents are unfortunately inconsistent when it // comes to Data. Is it "pre-serialized" or not? We prevent parsing date/time values, // so that we can get as close to the original JSON as possible. var settings = new JsonSerializerSettings { DateParseHandling = DateParseHandling.None }; cloudEvent.Data = JsonConvert.DeserializeObject <JObject>((string)cloudEvent.Data, settings); // Work around https://github.com/cloudevents/sdk-csharp/issues/59 var attributes = cloudEvent.GetAttributes(); var attributeKeys = attributes.Keys.ToList(); foreach (var key in attributeKeys) { var lowerKey = key.ToLowerInvariant(); var value = attributes[key]; attributes.Remove(key); attributes[lowerKey] = value; } // Write out a structured JSON representation of the CloudEvent var formatter = new JsonEventFormatter(); var bytes = formatter.EncodeStructuredEvent(cloudEvent, out var contentType); await File.WriteAllBytesAsync("function_output.json", bytes); }
public void PartitioningJsonTranscode() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent1 = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(jsonPartitioningKey)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent1, out var contentType); var cloudEvent = jsonFormatter.DecodeStructuredEvent(jsonData, new PartitioningExtension()); Assert.Equal("1", cloudEvent.Extension <PartitioningExtension>().PartitioningKeyValue); }
public void Transcode() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent1 = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(sampleJson)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent1, out var contentType); var cloudEvent = jsonFormatter.DecodeStructuredEvent(jsonData, Sequence.AllAttributes); Assert.Equal("Integer", cloudEvent[Sequence.SequenceTypeAttribute]); Assert.Equal("25", cloudEvent[Sequence.SequenceAttribute]); }
public void Transcode() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent1 = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(sampleJson)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent1, out _); var cloudEvent = jsonFormatter.DecodeStructuredEvent(jsonData); Assert.Equal("abc", cloudEvent["partitionkey"]); Assert.Equal("abc", cloudEvent[Partitioning.PartitionKeyAttribute]); Assert.Equal("abc", cloudEvent.GetPartitionKey()); }
public void Transcode() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent1 = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(sampleJson)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent1, out _); var cloudEvent = jsonFormatter.DecodeStructuredEvent(jsonData, DistributedTracing.AllAttributes); Assert.Equal(SampleParent, cloudEvent[DistributedTracing.TraceParentAttribute]); Assert.Equal(SampleParent, cloudEvent.GetTraceParent()); Assert.Equal(SampleState, cloudEvent[DistributedTracing.TraceStateAttribute]); Assert.Equal(SampleState, cloudEvent.GetTraceState()); }
public static string SaveToQueue([ActivityTrigger] DurableActivityContext inputs, ILogger log) { string notificationMsg; var(orderId, parentId) = inputs.GetInput <(string, string)>(); // dependency tracking var requestActivity = new Activity("command://order.update"); requestActivity.SetParentId(parentId); var requestOperation = _telemetryClient.StartOperation <RequestTelemetry>(requestActivity); var text = new AsciiArt("complete order"); log.LogInformation(text.ToString()); try { ContentType contentType; log.LogInformation($"Sending notification for {orderId} to queue."); var notificationEvent = new CloudEvent( "command://order.update", new Uri("app://ticketing.services.shipping")) { Id = Guid.NewGuid().ToString(), ContentType = new ContentType(MediaTypeNames.Application.Json), Data = JsonConvert.SerializeObject(new ObjectStatus() { Id = orderId, Status = "Delivered" }) }; var jsonFormatter = new JsonEventFormatter(); var messageBody = jsonFormatter.EncodeStructuredEvent(notificationEvent, out contentType); notificationMsg = Encoding.UTF8.GetString(messageBody); } catch (Exception ex) { // dependency tracking _telemetryClient.TrackException(ex); throw; } finally { // dependency tracking _telemetryClient.StopOperation(requestOperation); } return(notificationMsg); }
private static async Task <Response> SendCloudEventsInternalAsync( EventGridPublisherClient client, IEnumerable <CloudEvent> cloudEvents, bool async, CancellationToken cancellationToken = default) { Argument.AssertNotNull(client, nameof(client)); string activityId = null; string traceState = null; Activity currentActivity = Activity.Current; if (currentActivity != null && currentActivity.IsW3CFormat()) { activityId = currentActivity.Id; currentActivity.TryGetTraceState(out traceState); } using var stream = new MemoryStream(); using var writer = new Utf8JsonWriter(stream); writer.WriteStartArray(); foreach (var cloudEvent in cloudEvents) { var attributes = cloudEvent.GetAttributes(); if (activityId != null && !attributes.ContainsKey(TraceParentHeaderName) && !attributes.ContainsKey(TraceStateHeaderName)) { attributes.Add(TraceParentHeaderName, activityId); if (traceState != null) { attributes.Add(TraceStateHeaderName, traceState); } } byte[] bytes = s_eventFormatter.EncodeStructuredEvent(cloudEvent, out var _); using JsonDocument document = JsonDocument.Parse(bytes); document.RootElement.WriteTo(writer); } writer.WriteEndArray(); writer.Flush(); if (async) { return(await client.SendEncodedCloudEventsAsync(stream.GetBuffer().AsMemory(0, (int)stream.Position), cancellationToken).ConfigureAwait(false)); } else { return(client.SendEncodedCloudEvents(stream.GetBuffer().AsMemory(0, (int)stream.Position), cancellationToken)); } }
public void ReserializeTest10() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(jsonv10)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent, out var contentType); var cloudEvent2 = jsonFormatter.DecodeStructuredEvent(jsonData); Assert.Equal(cloudEvent2.SpecVersion, cloudEvent.SpecVersion); Assert.Equal(cloudEvent2.Type, cloudEvent.Type); Assert.Equal(cloudEvent2.Source, cloudEvent.Source); Assert.Equal(cloudEvent2.Id, cloudEvent.Id); AssertTimestampsEqual(cloudEvent2.Time, cloudEvent.Time); Assert.Equal(cloudEvent2.DataContentType, cloudEvent.DataContentType); Assert.Equal(cloudEvent2.Data, cloudEvent.Data); }
public void ReserializeTest() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(json)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent, out var contentType); var cloudEvent2 = jsonFormatter.DecodeStructuredEvent(jsonData); Assert.Equal(cloudEvent2.SpecVersion, cloudEvent.SpecVersion); Assert.Equal(cloudEvent2.Type, cloudEvent.Type); Assert.Equal(cloudEvent2.Source, cloudEvent.Source); Assert.Equal(cloudEvent2.Id, cloudEvent.Id); Assert.Equal(cloudEvent2.Time.Value.ToUniversalTime(), cloudEvent.Time.Value.ToUniversalTime()); Assert.Equal(cloudEvent2.ContentType, cloudEvent.ContentType); Assert.Equal(cloudEvent2.Data, cloudEvent.Data); }
public void SamplingJsonTranscode() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent1 = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(sampleJson)); // Note that the value is just a string here, as we don't know the attribute type. Assert.Equal("1", cloudEvent1["sampledrate"]); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent1, out _); var cloudEvent = jsonFormatter.DecodeStructuredEvent(jsonData, Sampling.AllAttributes); // When parsing with the attributes in place, the value is propagated as an integer. Assert.Equal(1, cloudEvent["sampledrate"]); Assert.Equal(1, cloudEvent.GetSampledRate()); }
public void SamplingJsonTranscode() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent1 = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(jsonSampledRate)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent1, out var contentType); var cloudEvent = jsonFormatter.DecodeStructuredEvent(jsonData, new SamplingExtension()); Assert.Equal(CloudEventsSpecVersion.Default, cloudEvent.SpecVersion); Assert.Equal("com.github.pull.create", cloudEvent.Type); Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source); Assert.Equal("A234-1234-1234", cloudEvent.Id); AssertTimestampsEqual("2018-04-05T17:31:00Z", cloudEvent.Time.Value); Assert.Equal(1, cloudEvent.Extension <SamplingExtension>().SampledRate.Value); }
public void IntegerSequenceJsonTranscode() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent1 = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(jsonSequence)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent1, out var contentType); var cloudEvent = jsonFormatter.DecodeStructuredEvent(jsonData, new IntegerSequenceExtension()); Assert.Equal(CloudEventsSpecVersion.V0_2, cloudEvent.SpecVersion); Assert.Equal("com.github.pull.create", cloudEvent.Type); Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source); Assert.Equal("A234-1234-1234", cloudEvent.Id); Assert.Equal(DateTime.Parse("2018-04-05T17:31:00Z").ToUniversalTime(), cloudEvent.Time.Value.ToUniversalTime()); Assert.Equal(25, cloudEvent.Extension <IntegerSequenceExtension>().Sequence); }
public void DistTraceJsonTranscode() { var jsonFormatter = new JsonEventFormatter(); var cloudEvent1 = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(jsonDistTrace)); var jsonData = jsonFormatter.EncodeStructuredEvent(cloudEvent1, out var contentType); var cloudEvent = jsonFormatter.DecodeStructuredEvent(jsonData, new DistributedTracingExtension()); Assert.Equal(CloudEventsSpecVersion.Default, cloudEvent.SpecVersion); Assert.Equal("com.github.pull.create", cloudEvent.Type); Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source); Assert.Equal("A234-1234-1234", cloudEvent.Id); AssertTimestampsEqual("2018-04-05T17:31:00Z", cloudEvent.Time.Value); Assert.Equal("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", cloudEvent.Extension <DistributedTracingExtension>().TraceParent); Assert.Equal("rojo=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01,congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4=", cloudEvent.Extension <DistributedTracingExtension>().TraceState); }
public void SerializedCloudEvent_AsJObject_IsCloudEvent(CloudEventsSpecVersion specVersion) { // Arrange var cloudEvent = new CloudEvent( specVersion, $"event-type-{Guid.NewGuid()}", new Uri("http://test-host"), id: $"event-id-{Guid.NewGuid()}") { Data = $"event-data-{Guid.NewGuid()}", DataContentType = new ContentType("text/plain") }; var jsonFormatter = new JsonEventFormatter(); byte[] serialized = jsonFormatter.EncodeStructuredEvent(cloudEvent, out ContentType contentType); JObject jObject = JObject.Parse(Encoding.UTF8.GetString(serialized)); // Act bool isCloudEvent = jObject.IsCloudEvent(); // Assert Assert.True(isCloudEvent, "Serialized CloudEvent object should be evaluated as CloudEvent schema"); }
public static void Run( [ServiceBusTrigger("q-payment-in", Connection = "SB-Queue-In-AppSettings")] string paymentItem, [ServiceBus("q-notifications", Connection = "SB-Queue-In-AppSettings")] out string notificationEventString, [ServiceBus("q-shipping-in", Connection = "SB-Queue-In-AppSettings")] out string outboundEventString, ILogger log, ExecutionContext context) { log.LogInformation("start fx_payment function"); outboundEventString = null; notificationEventString = null; byte[] messageBody; var telemetryClient = new TelemetryClient(TelemetryConfiguration.Active); var config = new ConfigurationBuilder() .SetBasePath(context.FunctionAppDirectory) .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); var securityVault = new VaultService(config["Values:VaultName"]); var sbConnectionString = String.Empty; try { sbConnectionString = securityVault.GetSecret("cn-servicebus").Result; } catch (Exception) { sbConnectionString = config["Values:SB-Queue-In-AppSettings"]; } try { ContentType contentType; var jsonFormatter = new JsonEventFormatter(); var inboundEvent = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(paymentItem)); log.LogInformation($" message type : { inboundEvent.Type}"); // TODO: This should be done with a DURABLE function // or implement compensation var text = new AsciiArt("payment"); log.LogInformation(text.ToString()); // 1. perform the payment var paymentCtx = JsonConvert.DeserializeObject <PaymentContext>((string)inboundEvent.Data); Pay(paymentCtx); log.LogInformation("Payment is done for {paymentCtx.Attendee}. OrderId : {paymentCtx.OrderId} - Token {paymentCtx.Token}"); log.LogInformation(new EventId((int)LoggingContext.EventId.Processing), LoggingContext.Template, "cloud event publishing [command://order.pay]", LoggingContext.EntityType.Order.ToString(), LoggingContext.EventId.Processing.ToString(), LoggingContext.Status.Pending.ToString(), "correlationId", LoggingContext.CheckPoint.Publisher.ToString(), "long description"); #region "command://order.update" // new activity introduced for better readibility in E2E dependency tree var requestActivity = new Activity("command://order.update"); var requestOperation = telemetryClient.StartOperation <RequestTelemetry>(requestActivity); try { // 2. notify the change var notificationEvent = new CloudEvent( "command://order.update", new Uri("app://ticketing.services.payment")) { Id = Guid.NewGuid().ToString(), ContentType = new ContentType(MediaTypeNames.Application.Json), Data = JsonConvert.SerializeObject(new ObjectStatus() { Id = paymentCtx.OrderId, Status = "Paid" }) }; // return as string to avoid ContentType deserialization problem messageBody = jsonFormatter.EncodeStructuredEvent(notificationEvent, out contentType); notificationEventString = Encoding.UTF8.GetString(messageBody); } catch (Exception ex) { // dependency tracking telemetryClient.TrackException(ex); throw; } finally { // dependency tracking telemetryClient.StopOperation(requestOperation); } #endregion #region "command://order.deliver" requestActivity = new Activity("command://order.deliver"); requestOperation = telemetryClient.StartOperation <RequestTelemetry>(requestActivity); try { // 3. trigger next step var outboundEvent = new CloudEvent( "command://order.deliver", new Uri("app://ticketing.services.payment")) { Id = Guid.NewGuid().ToString(), ContentType = new ContentType(MediaTypeNames.Application.Json), Data = JsonConvert.SerializeObject(paymentCtx) }; messageBody = jsonFormatter.EncodeStructuredEvent(outboundEvent, out contentType); outboundEventString = Encoding.UTF8.GetString(messageBody); } catch (Exception ex) { // dependency tracking telemetryClient.TrackException(ex); throw; } finally { // dependency tracking telemetryClient.StopOperation(requestOperation); } #endregion } catch (System.Exception ex) { // TODO : compensation var pub = new Publisher("q-errors", sbConnectionString); var result = pub.SendMessagesAsync(paymentItem); log.LogError(ex.Message); } log.LogInformation("end function"); }
public async Task <IActionResult> PlaceOrder([FromBody] OrderCreateDto orderCreate) { // dependency tracking var telemetryClient = _telemetryClientFactory.Create(); _logger.LogInformation("API - Order controller - PlaceOrders"); ConcertUser entityConcertUser; try { entityConcertUser = Mapper.Map <ConcertUser>(orderCreate); _ordersRepository.PlaceOrderAsync(entityConcertUser); await _ordersRepository.SaveChangesAsync(); } catch (Exception ex) { _logger.LogError(ex.Message); if (ex.InnerException.Message.Contains("PK_")) { return(BadRequest(new ProblemDetailsError(StatusCodes.Status400BadRequest, "Duplicate order."))); } throw; } _logger.LogInformation($"Retrieving new order"); var orderDb = await _ordersRepository.GetOrderAsync(entityConcertUser.Token); var orderDto = Mapper.Map <ApiLib.DTOs.OrderDto>(orderDb); // dependency tracking var current = Activity.Current; var requestActivity = new Activity("command://order.pay"); var requestOperation = telemetryClient.StartOperation <RequestTelemetry>(requestActivity); try { // drop message in the queue _logger.LogInformation($"Drop message in the queue"); //TODO KeyVault : local MSI and docker var sbConnectionString = String.Empty; try { sbConnectionString = _vaultService.GetSecret("cn-servicebus").Result; } catch (Exception ex) { // TODO local debug with docker // MSI + docker not working in debug mode? _logger.LogError(ex.Message); } if (String.IsNullOrEmpty(sbConnectionString)) { sbConnectionString = _configuration.GetConnectionString(name: "ServiceBus"); } var pub = new Publisher("q-payment-in", sbConnectionString); var cloudEvent = new CloudEvent("command://order.pay", new Uri("app://ticketing.api")) { Id = Guid.NewGuid().ToString(), ContentType = new ContentType(MediaTypeNames.Application.Json), Data = JsonConvert.SerializeObject(new PaymentContext() { Attendee = orderDto.Attendee, OrderId = orderDto.OrderId.ToString(), Token = orderDto.Token, }) }; _logger.LogInformation(new EventId((int)LoggingContext.EventId.Processing), LoggingContext.Template, "cloud event publishing [command://order.pay]", LoggingContext.EntityType.Order.ToString(), LoggingContext.EventId.Processing.ToString(), LoggingContext.Status.Pending.ToString(), "correlationId", LoggingContext.CheckPoint.Publisher.ToString(), "long description"); _logger.LogInformation("COMMAND - Sending message to the bus."); var jsonFormatter = new JsonEventFormatter(); var messageBody = jsonFormatter.EncodeStructuredEvent(cloudEvent, out var contentType); await pub.SendMessagesAsync(Encoding.UTF8.GetString(messageBody)); } catch (Exception ex) { // dependency tracking telemetryClient.TrackException(ex); throw; } finally { // dependency tracking telemetryClient.StopOperation(requestOperation); } _logger.LogInformation($"Returning order token '{entityConcertUser.Token}'"); return(CreatedAtRoute("Orders_GetOrderDetails", new { token = entityConcertUser.Token }, orderDto)); }