public async Task <Message> Run( [BlobTrigger("car-images/cameras/{camera}/{name}", Connection = "SECCTRL_CAR_IMAGES")] CloudBlockBlob imageBlob, string camera, string name, ILogger log, [DurableClient] IDurableOrchestrationClient orchestrationClient) { log.LogInformation($"Start processing of new image {name} from camera {camera}"); // Store timestamp when image was received in blob storage var timestamp = imageBlob.Properties.LastModified; // Read image file from blob storage and convert content to Base64 log.LogInformation("Downloading image data"); var file = new byte[imageBlob.Properties.Length]; await imageBlob.DownloadToByteArrayAsync(file, 0); // Try to recognize the license plate log.LogInformation("Starting license plate recognition"); var recognitionResult = await licensePlateRecognizer.RecognizeAsync(file, configuration); // Move image to archive log.LogInformation("Moving image to archive folder"); var archiveImageId = Guid.NewGuid().ToString(); var archiveBlob = imageBlob.Container.GetBlockBlobReference($"archive/{archiveImageId}.jpg"); await archiveBlob.StartCopyAsync(imageBlob); await imageBlob.DeleteAsync(); log.LogInformation("Building plate read result"); if (recognitionResult != null) { // We have recognized a license plate var read = new PlateRead { ReadTimestamp = timestamp.Value.Ticks, CameraID = camera, LicensePlate = recognitionResult.Plate, Confidence = recognitionResult.Confidence, Nationality = recognitionResult.Region, NationalityConfidence = recognitionResult.RegionConfidence, ImageID = archiveImageId }; var readQuality = "low"; if (recognitionResult.Confidence >= 93.3d && recognitionResult.RegionConfidence >= 25d) { readQuality = "high"; } if (readQuality == "low") { var instanceId = await orchestrationClient.StartNewAsync( "OrchestrateRequestApproval", new PlateReadApproval { Read = read }); log.LogInformation($"Durable Function Ochestration started: {instanceId}"); } return(CreateMessage(read, readQuality)); } else { // No license plate found var read = new EmptyPlateRead { ReadTimestamp = timestamp.Value.Ticks, CameraID = camera, ImageID = archiveImageId }; return(CreateMessage(read, "empty")); } }
public static async Task BlobTriggerStart([BlobTrigger("original-store/{name}")] CloudBlockBlob myBlob, string name, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { string instanceId = await starter.StartNewAsync(nameof(FileProcessingOrchestrator), input : name); log.LogInformation($"Started orchestration with ID = '{instanceId}', Blob '{name}'."); }
public static async Task Run([EventHubTrigger(Constants.EventHubAddPlayers, Connection = "EVENTHUB_CONNECTION_STRING")] EventData[] events, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { var exceptions = new List <Exception>(); // Connection to the Redis database - using Event Hubs to batch requests and reduce number of connections IDatabase cache = Helper.Connection.GetDatabase(); foreach (EventData eventData in events) { try { string messageBody = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count); log.LogInformation($"BatchAddUsers Event Hub trigger function processed a message: {messageBody}"); // Processing logic. Player tempPlayer = JsonConvert.DeserializeObject <Player>(messageBody); string playerGUID = tempPlayer.GUID; string playerMatchmaking = tempPlayer.MatchmakingSettings; if (playerMatchmaking == null) { return; } // Start unique player GUID orchestrator for match request handling var instanceId = await starter.StartNewAsync("MatchRequest", playerGUID, Constants.MatchmakingTimeoutInSeconds); // TODO: pass match request information // Flush existing key string playerKey = string.Format("{0}:{1}", Constants.PlayerHash, playerGUID); cache.KeyDelete(playerKey); log.LogInformation($"Player key flushed: {playerKey}"); // Create player Hash cache.HashSet(playerKey, tempPlayer.ToHashEntryArray(), CommandFlags.FireAndForget); log.LogInformation($"Hash created including the player information: {playerKey}"); // Add timestamp to the PlayersTimeStartedMatchmaking Sorted Set cache.SortedSetAdd(Constants.PlayerTimestampsSortedSet, playerGUID, new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(), CommandFlags.FireAndForget); log.LogInformation($"Player matchmaking started timestamp added: {Constants.PlayerTimestampsSortedSet}"); // Find a suitable game session for the user and create it if doesn't exist // SDIFF MatchMakingSessions and SessionsReady to find a suitable game session that is not full // RedisValue[] SetCombine(SetOperation operation, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None); string keySessionsPerMatchmaking = string.Format("{0}:{1}", Constants.SessionsPerMatchmakingSet, playerMatchmaking); string sessionKey = null; string sessionGUID = null; int capacity = -1; var results = cache.SetCombine(SetOperation.Difference, keySessionsPerMatchmaking, Constants.SessionsReadySet); if (results != null && results.Length > 0) { RedisValue entry = results.First(); // Get a session from the set, optimization could be to leverage the timestamp picking the oldest one sessionGUID = entry.ToString(); sessionKey = string.Format("{0}:{1}", Constants.SessionHash, sessionGUID); capacity = Int32.Parse(cache.HashGet(sessionKey, Constants.CapacityField)); log.LogInformation($"Session found for the player: {sessionGUID}; capacity: {capacity.ToString()}"); } else // Create session { Guid randomGuid = Guid.NewGuid(); sessionGUID = randomGuid.ToString(); sessionKey = string.Format("{0}:{1}", Constants.SessionHash, sessionGUID); cache.KeyDelete(sessionKey); var tempSession = new Session { //Initialization GUID = sessionGUID, Capacity = Constants.CapacityMax, MatchmakingSettings = playerMatchmaking }; var outputJson = JsonConvert.SerializeObject(tempSession); log.LogInformation($"Session not found for the player, a new one was created: {sessionKey}"); // Add new game session to the Sessions Hash cache.HashSet(sessionKey, tempSession.ToHashEntryArray(), CommandFlags.FireAndForget); log.LogInformation($"New session added to the Sessions Hash: {sessionKey}"); // Add the session to the matchmaking bucket cache.SetAdd(keySessionsPerMatchmaking, sessionGUID, CommandFlags.FireAndForget); log.LogInformation($"New session added to the matchmaking bucket: {keySessionsPerMatchmaking}"); capacity = Constants.CapacityMax; } // Add player to the game session sessionKey = string.Format("{0}:{1}", Constants.SessionPlayersSet, sessionGUID); if (!cache.SetContains(sessionKey, playerGUID)) // Is the player in this game session already { cache.SetAdd(sessionKey, playerGUID, CommandFlags.FireAndForget); } log.LogInformation($"Player added to the session: {sessionKey}"); // Update the capacity for the session capacity--; sessionKey = string.Format("{0}:{1}", Constants.SessionHash, sessionGUID); cache.HashSet(sessionKey, Constants.CapacityField, capacity.ToString()); if (capacity == 0) { // Add the session to the SessionsReady Set cache.SetAdd(Constants.SessionsReadySet, sessionGUID, CommandFlags.FireAndForget); log.LogInformation($"Session added to the sessions ready set: {Constants.SessionsReadySet}"); } } catch (Exception e) { // We need to keep processing the rest of the batch - capture this exception and continue. // Also, consider capturing details of the message that failed processing so it can be processed again later. exceptions.Add(e); } } // Once processing of the batch is complete, if any messages in the batch failed processing throw an exception so that there is a record of the failure. if (exceptions.Count > 1) { throw new AggregateException(exceptions); } if (exceptions.Count == 1) { throw exceptions.Single(); } }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "lmi/webhook")] HttpRequest?request, [DurableClient] IDurableOrchestrationClient starter) { try { logger.LogInformation("Received webhook request"); using var streamReader = new StreamReader(request?.Body !); var requestBody = await streamReader.ReadToEndAsync().ConfigureAwait(false); if (string.IsNullOrEmpty(requestBody)) { logger.LogError($"{nameof(request)} body is null"); return(new BadRequestResult()); } string? instanceId = null; SocRequestModel?socRequest = null; var webhookRequestModel = lmiWebhookReceiverService.ExtractEvent(requestBody); switch (webhookRequestModel.WebhookCommand) { case WebhookCommand.SubscriptionValidation: return(new OkObjectResult(webhookRequestModel.SubscriptionValidationResponse)); case WebhookCommand.TransformAllSocToJobGroup: socRequest = new SocRequestModel { Uri = webhookRequestModel.Url, }; instanceId = await starter.StartNewAsync(nameof(LmiOrchestrationTrigger.RefreshOrchestrator), socRequest).ConfigureAwait(false); break; case WebhookCommand.TransformSocToJobGroup: socRequest = new SocRequestModel { Uri = webhookRequestModel.Url, SocId = webhookRequestModel.ContentId, }; instanceId = await starter.StartNewAsync(nameof(LmiOrchestrationTrigger.RefreshJobGroupOrchestrator), socRequest).ConfigureAwait(false); break; case WebhookCommand.PurgeAllJobGroups: socRequest = new SocRequestModel { Uri = webhookRequestModel.Url, }; instanceId = await starter.StartNewAsync(nameof(LmiOrchestrationTrigger.PurgeOrchestrator), socRequest).ConfigureAwait(false); break; case WebhookCommand.PurgeJobGroup: socRequest = new SocRequestModel { Uri = webhookRequestModel.Url, SocId = webhookRequestModel.ContentId, }; instanceId = await starter.StartNewAsync(nameof(LmiOrchestrationTrigger.PurgeJobGroupOrchestrator), socRequest).ConfigureAwait(false); break; default: return(new BadRequestResult()); } logger.LogInformation($"Started orchestration with ID = '{instanceId}' for SOC {socRequest?.Uri}"); return(starter.CreateCheckStatusResponse(request, instanceId)); } catch (Exception ex) { logger.LogError(ex.ToString()); return(new StatusCodeResult((int)HttpStatusCode.InternalServerError)); } }
/// <summary> /// Starts a new execution of the specified orchestrator function. /// </summary> /// <param name="client">The client object.</param> /// <param name="orchestratorFunctionName">The name of the orchestrator function to start.</param> /// <returns>A task that completes when the orchestration is started. The task contains the instance id of the started /// orchestratation instance.</returns> /// <exception cref="ArgumentException"> /// The specified function does not exist, is disabled, or is not an orchestrator function. /// </exception> public static Task <string> StartNewAsync( this IDurableOrchestrationClient client, string orchestratorFunctionName) { return(client.StartNewAsync <object>(orchestratorFunctionName, null)); }
public static async Task Run([TimerTrigger("0 0 1 * * *")] TimerInfo timerInfo, [DurableClient] IDurableOrchestrationClient orchestrationClient) { await orchestrationClient.StartNewAsync(nameof(DemoOrchestration), null, Guid.NewGuid()); }
public async Task <HttpResponseMessage> ProcessWebHook([HttpTrigger(AuthorizationLevel.Function, "POST")] HttpRequestMessage request, [DurableClient] IDurableOrchestrationClient orchestrationClient, ExecutionContext executionContext) { DateTime startTime = DateTime.UtcNow; string requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false); request.Headers.TryGetValues("X-GitHub-Delivery", out IEnumerable <string> deliveryGuidValues); if (deliveryGuidValues == null || !deliveryGuidValues.Any()) { string errorMessage = $"WebHookProcessor requires 'X-GitHub-Event' header."; this.telemetryClient.TrackTrace(errorMessage, SeverityLevel.Error); throw new FatalException(errorMessage); } string sessionId = deliveryGuidValues.First(); request.Headers.TryGetValues("X-GitHub-Event", out IEnumerable <string> eventTypeValues); if (eventTypeValues == null || !eventTypeValues.Any()) { string errorMessage = $"WebHookProcessor endpoint requires 'X-GitHub-Event' header."; this.telemetryClient.TrackTrace(errorMessage, SeverityLevel.Error); throw new FatalException(errorMessage); } string eventType = eventTypeValues.First(); // The request does not have to go through the LogicApp. This might happen e.g., for local testing. Therefore, assume that Logic-App-related headers are optional: // X-LogicApp-Timestamp // X-Ms-Workflow-Run-Id request.Headers.TryGetValues("X-LogicApp-Timestamp", out IEnumerable <string> logicAppStartDateValues); string logicAppStartDate = string.Empty; if (logicAppStartDateValues != null && logicAppStartDateValues.Any()) { logicAppStartDate = logicAppStartDateValues.First(); } request.Headers.TryGetValues("X-Ms-Workflow-Run-Id", out IEnumerable <string> logicAppRunIdValues); string logicAppRunId = string.Empty; if (logicAppRunIdValues != null && logicAppRunIdValues.Any()) { logicAppRunId = logicAppRunIdValues.First(); } OrchestrationContext context = new OrchestrationContext() { RequestBody = requestBody, CollectorType = CollectorType.Main.ToString(), SessionId = sessionId, EventType = eventType, FunctionStartDate = startTime, LogicAppStartDate = logicAppStartDate, LogicAppRunId = logicAppRunId, InvocationId = executionContext.InvocationId.ToString(), }; ITelemetryClient telemetryClient = new GitHubApplicationInsightsTelemetryClient(this.telemetryClient, context); telemetryClient.TrackEvent("SessionStart", GetMainCollectorSessionStartEventProperties(context, identifier: eventType, logicAppRunId)); string instanceId = await orchestrationClient.StartNewAsync("ProcessWebHookOrchestration", context).ConfigureAwait(false); return(orchestrationClient.CreateCheckStatusResponse(request, instanceId, returnInternalServerErrorOnFailure: true)); }
public static async void Run([BlobTrigger("csvimports/{name}", Connection = "StorageConnection")] Stream myBlob, string name, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { string instanceId = await starter.StartNewAsync("CSVImport_Orchestrator", name); log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes"); }