public string ReadCloudSchedulerData(CloudEvent cloudEvent) { _logger.LogInformation("Reading cloud scheduler data"); string country = null; switch (cloudEvent.Type) { case EVENT_TYPE_PUBSUB: var messagePublishedData = CloudEventConverters.ConvertCloudEventData <MessagePublishedData>(cloudEvent); var pubSubMessage = messagePublishedData.Message; _logger.LogInformation($"Type: {EVENT_TYPE_PUBSUB} data: {pubSubMessage.Data.ToBase64()}"); country = pubSubMessage.Data.ToStringUtf8(); break; case EVENT_TYPE_SCHEDULER: // Data: {"custom_data":"Q3lwcnVz"} var schedulerJobData = CloudEventConverters.ConvertCloudEventData <SchedulerJobData>(cloudEvent); _logger.LogInformation($"Type: {EVENT_TYPE_SCHEDULER} data: {schedulerJobData.CustomData.ToBase64()}"); country = schedulerJobData.CustomData.ToStringUtf8(); break; } _logger.LogInformation($"Extracted country: {country}"); return(country); }
private static async Task <T> ConvertAndDeserialize <T>(string resourceName) where T : class { var context = GcfEventResources.CreateHttpContext(resourceName); var cloudEvent = await GcfConverters.ConvertGcfEventToCloudEvent(context.Request); return(CloudEventConverters.ConvertCloudEventData <T>(cloudEvent)); }
public void GetDataConverter_Cached() { var converter1 = CloudEventConverters.GetDataConverter <EventData>(); var converter2 = CloudEventConverters.GetDataConverter <EventData>(); Assert.Same(converter1, converter2); }
public void PopulateCloudEvent_Valid() { var evt = new CloudEvent("type", new Uri("//source")); var data = new EventData { Text = "some text" }; CloudEventConverters.PopulateCloudEvent(evt, data); Assert.Equal("some text", evt.Data); }
public void ConvertEventData_Valid() { var evt = new CloudEvent("type", new Uri("//source")) { Data = "some text" }; var data = CloudEventConverters.ConvertCloudEventData <EventData>(evt); Assert.Equal("some text", data.Text); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger <Startup> logger) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } logger.LogInformation("Service is starting..."); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapPost("/", async context => { logger.LogInformation("Handling HTTP POST"); var ceId = context.Request.Headers["ce-id"]; if (string.IsNullOrEmpty(ceId)) { context.Response.StatusCode = StatusCodes.Status400BadRequest; await context.Response.WriteAsync("Bad Request: expected header ce-id"); return; } var cloudEvent = await context.Request.ReadCloudEventAsync(); var storageObjectData = CloudEventConverters.ConvertCloudEventData <StorageObjectData>(cloudEvent); logger.LogInformation( $"Detected change in GCS bucket: {storageObjectData.Bucket}, object name: {storageObjectData.Name}"); // reply with a cloudevent var replyEvent = new CloudEvent( CloudEventsSpecVersion.V1_0, "com.example.kuberun.events.received", new Uri("https://localhost"), Guid.NewGuid().ToString(), DateTime.Now) { DataContentType = new ContentType(MediaTypeNames.Application.Json), Data = JsonConvert.SerializeObject("Event received") }; // TODO: there should be a more concise way to construct the response. context.Response.StatusCode = StatusCodes.Status200OK; context.Response.Headers.Add("Ce-Id", replyEvent.Id); context.Response.Headers.Add("Ce-Specversion", "1.0"); context.Response.Headers.Add("Ce-Type", replyEvent.Type); context.Response.Headers.Add("Ce-Source", replyEvent.Source.ToString()); context.Response.ContentType = MediaTypeNames.Application.Json; await context.Response.WriteAsync(replyEvent.Data.ToString()); }); }); }
public (string, string) ReadCloudStorageData(CloudEvent cloudEvent) { _logger.LogInformation("Reading cloud storage data"); string bucket = null, name = null; switch (cloudEvent.Type) { case EVENT_TYPE_AUDITLOG: //"protoPayload" : {"resourceName":"projects/_/buckets/events-atamel-images-input/objects/atamel.jpg}"; var logEntryData = CloudEventConverters.ConvertCloudEventData <LogEntryData>(cloudEvent); var tokens = logEntryData.ProtoPayload.ResourceName.Split('/'); bucket = tokens[3]; name = tokens[5]; break; case EVENT_TYPE_STORAGE: var storageObjectData = CloudEventConverters.ConvertCloudEventData <StorageObjectData>(cloudEvent); bucket = storageObjectData.Bucket; name = storageObjectData.Name; break; case EVENT_TYPE_PUBSUB: // {"message": { // "data": "eyJidWNrZXQiOiJldmVudHMtYXRhbWVsLWltYWdlcy1pbnB1dCIsIm5hbWUiOiJiZWFjaC5qcGcifQ==", // },"subscription": "projects/events-atamel/subscriptions/cre-europe-west1-trigger-resizer-sub-000"} var messagePublishedData = CloudEventConverters.ConvertCloudEventData <MessagePublishedData>(cloudEvent); var pubSubMessage = messagePublishedData.Message; _logger.LogInformation($"Type: {EVENT_TYPE_PUBSUB} data: {pubSubMessage.Data.ToBase64()}"); var decoded = pubSubMessage.Data.ToStringUtf8(); _logger.LogInformation($"decoded: {decoded}"); var parsed = JValue.Parse(decoded); bucket = (string)parsed["bucket"]; name = (string)parsed["name"]; break; default: // Data: {"bucket":"knative-atamel-images-input","name":"beach.jpg"} _logger.LogInformation($"Type: {cloudEvent.Type} data: {cloudEvent.Data}"); var parsedCustom = JValue.Parse((string)cloudEvent.Data); bucket = (string)parsedCustom["bucket"]; name = (string)parsedCustom["name"]; break; } _logger.LogInformation($"Extracted bucket: {bucket}, name: {name}"); return(bucket, name); }
public async Task NoRetry(PubsubMessage message) { var cloudEvent = new CloudEvent(MessagePublishedData.MessagePublishedCloudEventType, new Uri("//pubsub.googleapis.com")); var data = new MessagePublishedData { Message = message }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); await ExecuteCloudEventRequestAsync(cloudEvent); var logEntry = Assert.Single(GetFunctionLogEntries()); Assert.Equal(LogLevel.Information, logEntry.Level); Assert.Equal("Not retrying...", logEntry.Message); }
public async Task CloudEventInput() { var cloudEvent = new CloudEvent(StorageObjectData.FinalizedCloudEventType, new Uri("//storage.googleapis.com")); var data = new StorageObjectData { Name = "new-file.txt" }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); await ExecuteCloudEventRequestAsync(cloudEvent); var logEntry = Assert.Single(GetFunctionLogEntries()); Assert.Equal("File new-file.txt uploaded", logEntry.Message); Assert.Equal(LogLevel.Information, logEntry.Level); }
public async Task ObjectDeletedEvent() { var cloudEvent = new CloudEvent(StorageObjectData.DeletedCloudEventType, new Uri("//storage.googleapis.com")); var data = new StorageObjectData { Name = "new-file.txt" }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); await ExecuteCloudEventRequestAsync(cloudEvent); var logEntry = Assert.Single(GetFunctionLogEntries()); Assert.Equal($"Unsupported event type: {StorageObjectData.DeletedCloudEventType}", logEntry.Message); Assert.Equal(LogLevel.Warning, logEntry.Level); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger <Startup> logger) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } logger.LogInformation("Service is starting..."); app.UseRouting(); var bucketImages = GetEnvironmentVariable("BUCKET_IMAGES"); _bucketThumbnails = GetEnvironmentVariable("BUCKET_THUMBNAILS"); _projectId = GetEnvironmentVariable("PROJECT_ID"); app.UseEndpoints(endpoints => { endpoints.MapPost("/", async context => { var cloudEvent = await context.Request.ReadCloudEventAsync(); logger.LogInformation("Received CloudEvent\n" + GetEventLog(cloudEvent)); if (EVENT_TYPE_AUDITLOG != cloudEvent.Type) { logger.LogInformation($"Event type '{cloudEvent.Type}' is not {EVENT_TYPE_AUDITLOG}, ignoring."); return; } //"protoPayload" : {"resourceName":"projects/_/buckets/events-atamel-images-input/objects/atamel.jpg}"; var logEntryData = CloudEventConverters.ConvertCloudEventData <LogEntryData>(cloudEvent); var tokens = logEntryData.ProtoPayload.ResourceName.Split('/'); var bucket = tokens[3]; var objectName = tokens[5]; if (bucketImages != bucket) { logger.LogInformation($"Bucket '{bucket}' is not same as {bucketImages}, ignoring."); return; } await DeleteFromThumbnailsAsync(objectName, logger); await DeleteFromFirestore(objectName, logger); }); }); }
public (string, string) ReadCloudStorageData(CloudEvent cloudEvent) { _logger.LogInformation("Reading cloud storage data"); string bucket = null, name = null; switch (cloudEvent.Type) { case EVENT_TYPE_AUDITLOG: var logEntryData = CloudEventConverters.ConvertCloudEventData <LogEntryData>(cloudEvent); var tokens = logEntryData.ProtoPayload.ResourceName.Split('/'); bucket = tokens[3]; name = tokens[5]; break; case EVENT_TYPE_STORAGE: var storageObjectData = CloudEventConverters.ConvertCloudEventData <StorageObjectData>(cloudEvent); bucket = storageObjectData.Bucket; name = storageObjectData.Name; break; case EVENT_TYPE_PUBSUB: var messagePublishedData = CloudEventConverters.ConvertCloudEventData <MessagePublishedData>(cloudEvent); var pubSubMessage = messagePublishedData.Message; _logger.LogInformation($"Type: {EVENT_TYPE_PUBSUB} data: {pubSubMessage.Data.ToBase64()}"); var decoded = pubSubMessage.Data.ToStringUtf8(); _logger.LogInformation($"decoded: {decoded}"); var parsed = JValue.Parse(decoded); bucket = (string)parsed["bucket"]; name = (string)parsed["name"]; break; default: _logger.LogInformation($"Type: {cloudEvent.Type} data: {cloudEvent.Data}"); var parsedCustom = JValue.Parse((string)cloudEvent.Data); bucket = (string)parsedCustom["bucket"]; name = (string)parsedCustom["name"]; break; } _logger.LogInformation($"Extracted bucket: {bucket}, name: {name}"); return(bucket, name); }
public async Task ExecuteCloudEventAsync_CloudEvent() { var cloudEvent = new CloudEvent( StorageObjectData.FinalizedCloudEventType, new Uri("//storage", UriKind.RelativeOrAbsolute)); var data = new StorageObjectData { Name = "test1" }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); using var test = new Test <StorageObjectFunction>(); await test.ExecuteCloudEventRequestAsync(cloudEvent); var log = Assert.Single(test.GetFunctionLogEntries()); Assert.Equal("Name: test1", log.Message); }
/// <summary> /// Convenience method to test CloudEvents by supplying only the most important aspects. /// This method simply constructs a CloudEvent from the parameters and delegates to /// <see cref="ExecuteCloudEventRequestAsync(CloudEvent)"/>. /// </summary> /// <param name="eventType">The CloudEvent type.</param> /// <param name="data">The data to populate the CloudEvent with. The data should be convertible /// via <see cref="CloudEventConverters"/>.</param> /// <param name="source">The source URI for the CloudEvent, or null to use a default of "//test-source".</param> /// <param name="subject">The subject of the CloudEvent, or null if no subject is required.</param> /// <typeparam name="T">The type of the event data. This is used to find the appropriate data converter.</typeparam> public Task ExecuteCloudEventRequestAsync <T>(string eventType, T?data, Uri?source = null, string?subject = null) where T : class { var cloudEvent = new CloudEvent( eventType, source ?? new Uri("//test-source", UriKind.RelativeOrAbsolute), id: Guid.NewGuid().ToString()); if (data is object) { CloudEventConverters.PopulateCloudEvent(cloudEvent, data); } if (subject is object) { cloudEvent.Subject = subject; } return(ExecuteCloudEventRequestAsync(cloudEvent)); }
public async Task RetryTrue() { var cloudEvent = new CloudEvent(MessagePublishedData.MessagePublishedCloudEventType, new Uri("//pubsub.googleapis.com")); var data = new MessagePublishedData { Message = new PubsubMessage { TextData = "{ \"retry\": true }" } }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); // The test server propagates the exception to the caller. The real server would respond // with a status code of 500. await Assert.ThrowsAsync <InvalidOperationException>(() => ExecuteCloudEventRequestAsync(cloudEvent)); Assert.Empty(GetFunctionLogEntries()); }
public async Task MessageWithoutTextData() { var cloudEvent = new CloudEvent(MessagePublishedData.MessagePublishedCloudEventType, new Uri("//pubsub.googleapis.com")); var data = new MessagePublishedData { Message = new PubsubMessage { Attributes = { { "key", "value" } } } }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); await ExecuteCloudEventRequestAsync(cloudEvent); var logEntry = Assert.Single(GetFunctionLogEntries()); Assert.Equal("Hello world", logEntry.Message); Assert.Equal(LogLevel.Information, logEntry.Level); }
public async Task Processing(string textData, int ageInSeconds, string expectedLog) { var cloudEvent = new CloudEvent(MessagePublishedData.MessagePublishedCloudEventType, new Uri("//pubsub.googleapis.com"), "1234", DateTime.UtcNow.AddSeconds(-ageInSeconds)); var data = new MessagePublishedData { Message = new PubsubMessage { TextData = textData } }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); await ExecuteCloudEventRequestAsync(cloudEvent); var logEntry = Assert.Single(GetFunctionLogEntries()); Assert.Equal(LogLevel.Information, logEntry.Level); Assert.Equal(expectedLog, logEntry.Message); }
/// <summary> /// Handles an HTTP request by extracting the Cloud Event from it, deserializing the data, and passing /// both the event and the data to the original Cloud Event Function. /// The request fails if it does not contain a Cloud Event. /// </summary> /// <param name="context">The HTTP context containing the request and response.</param> /// <returns>A task representing the asynchronous operation.</returns> public async Task HandleAsync(HttpContext context) { CloudEvent cloudEvent; TData data; try { cloudEvent = await CloudEventConverter.ConvertRequest(context.Request); data = CloudEventConverters.ConvertCloudEventData <TData>(cloudEvent); } catch (CloudEventConverter.ConversionException e) { _logger.LogError(e.Message); context.Response.StatusCode = 400; return; } await _function.HandleAsync(cloudEvent, data, context.RequestAborted); }
public async Task FileNameIsLogged() { // Prepare the inputs var cloudEvent = new CloudEvent(StorageObjectData.FinalizedCloudEventType, new Uri("//storage.googleapis.com")); var data = new StorageObjectData { Name = "new-file.txt" }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); var logger = new MemoryLogger <HelloGcs.Function>(); // Execute the function var function = new HelloGcs.Function(logger); await function.HandleAsync(cloudEvent, data, CancellationToken.None); // Check the log results var logEntry = Assert.Single(logger.ListLogEntries()); Assert.Equal("File new-file.txt uploaded", logEntry.Message); Assert.Equal(LogLevel.Information, logEntry.Level); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger <Startup> logger) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } logger.LogInformation("Service is starting..."); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapPost("/", async context => { var cloudEvent = await context.Request.ReadCloudEventAsync(); logger.LogInformation("Received CloudEvent\n" + GetEventLog(cloudEvent)); var messagePublishedData = CloudEventConverters.ConvertCloudEventData <MessagePublishedData>(cloudEvent); var pubSubMessage = messagePublishedData.Message; if (pubSubMessage == null) { context.Response.StatusCode = 400; await context.Response.WriteAsync("Bad request: Invalid Pub/Sub message format"); return; } var data = pubSubMessage.Data; logger.LogInformation($"Data: {data.ToBase64()}"); var name = data.ToStringUtf8(); logger.LogInformation($"Extracted name: {name}"); var id = context.Request.Headers["ce-id"]; await context.Response.WriteAsync($"Hello {name}! ID: {id}"); }); }); }
public async Task CloudEventIsLogged() { var client = Server.CreateClient(); var created = DateTimeOffset.UtcNow.AddMinutes(-5); var updated = created.AddMinutes(2); var cloudEvent = new CloudEvent(StorageObjectData.DeletedCloudEventType, new Uri("//storage.googleapis.com"), "1234"); var data = new StorageObjectData { Name = "new-file.txt", Bucket = "my-bucket", Metageneration = 23, TimeCreated = new DateTimeOffset(2020, 7, 9, 13, 0, 5, TimeSpan.Zero).ToTimestamp(), Updated = new DateTimeOffset(2020, 7, 9, 13, 23, 25, TimeSpan.Zero).ToTimestamp() }; CloudEventConverters.PopulateCloudEvent(cloudEvent, data); await ExecuteCloudEventRequestAsync(cloudEvent); var logs = GetFunctionLogEntries(); Assert.All(logs, entry => Assert.Equal(LogLevel.Information, entry.Level)); var actualMessages = logs.Select(entry => entry.Message).ToArray(); var expectedMessages = new[] { "Event: 1234", $"Event Type: {StorageObjectData.DeletedCloudEventType}", "Bucket: my-bucket", "File: new-file.txt", "Metageneration: 23", "Created: 2020-07-09T13:00:05", "Updated: 2020-07-09T13:23:25", }; Assert.Equal(expectedMessages, actualMessages); }
public void GetDataConverter_DoesntImplementInterface() => Assert.Throws <InvalidOperationException>(() => CloudEventConverters.GetDataConverter <NoImplementationData>());
public void GetDataConverter_WrongTargetType() => Assert.Throws <InvalidOperationException>(() => CloudEventConverters.GetDataConverter <WrongTargetTypeData>());
public void GetDataConverter_NoAttribute() => Assert.Throws <InvalidOperationException>(() => CloudEventConverters.GetDataConverter <NoAttributeData>());