public async Task ExecuteAsync(WorkerJobHelper <AIJob> jobHelper)
        {
            S3Locator inputFile;

            if (!jobHelper.JobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile))
            {
                throw new Exception($"Invalid or missing input file");
            }

            string mediaFileUrl;

            if (!string.IsNullOrWhiteSpace(inputFile.HttpEndpoint))
            {
                mediaFileUrl = inputFile.HttpEndpoint;
            }
            else
            {
                var bucketLocation = await inputFile.GetBucketLocationAsync();

                var s3SubDomain = !string.IsNullOrWhiteSpace(bucketLocation) ? $"s3-{bucketLocation}" : "s3";
                mediaFileUrl = $"https://{s3SubDomain}.amazonaws.com/{inputFile.AwsS3Bucket}/{inputFile.AwsS3Key}";
            }

            var authTokenUrl  = jobHelper.Request.ApiUrl() + "/auth/" + jobHelper.Request.Location() + "/Accounts/" + jobHelper.Request.AccountID() + "/AccessToken?allowEdit=true";
            var customHeaders = new Dictionary <string, string> {
                ["Ocp-Apim-Subscription-Key"] = jobHelper.Request.SubscriptionKey()
            };

            Logger.Debug($"Generate Azure Video Indexer Token: Doing a GET on {authTokenUrl}");
            var mcmaHttp = new McmaHttpClient();
            var response = await mcmaHttp.GetAsync(authTokenUrl, headers : customHeaders).WithErrorHandling();

            var apiToken = await response.Content.ReadAsJsonAsync();

            Logger.Debug($"Azure API Token: {apiToken}");

            // call the Azure API to process the video
            // in this scenario the video is located in a public link
            // so no need to upload the file to Azure

            /* Sample URL Structure
             *  https://api.videoindexer.ai/{location}/Accounts/{accountId}/Videos?
             *      accessToken={accessToken}&
             *      name={name}?description={string}&
             *      partition={string}&
             *      externalId={string}&
             *      callbackUrl={string}&
             *      metadata={string}&
             *      language={string}&
             *      videoUrl={string}&
             *      fileName={string}&
             *      indexingPreset={string}&
             *      streamingPreset=Default&
             *      linguisticModelId={string}&
             *      privacy={string}&
             *      externalUrl={string}" */

            var secureHost    = new Uri(jobHelper.JobAssignmentId, UriKind.Absolute).Host;
            var nonSecureHost = new Uri(jobHelper.Request.GetRequiredContextVariable("PublicUrlNonSecure"), UriKind.Absolute).Host;

            var callbackUrl = Uri.EscapeDataString(jobHelper.JobAssignmentId.Replace(secureHost, nonSecureHost) + "/notifications");

            var postVideoUrl = jobHelper.Request.ApiUrl() + "/" + jobHelper.Request.Location() + "/Accounts/" + jobHelper.Request.AccountID() + "/Videos?accessToken=" + apiToken + "&name=" + inputFile.AwsS3Key + "&callbackUrl=" + callbackUrl + "&videoUrl=" + mediaFileUrl + "&fileName=" + inputFile.AwsS3Key;

            Logger.Debug($"Call Azure Video Indexer API: Doing a POST on {postVideoUrl}");
            var postVideoResponse = await mcmaHttp.PostAsync(postVideoUrl, null, customHeaders).WithErrorHandling();

            var azureAssetInfo = await postVideoResponse.Content.ReadAsJsonAsync();

            Logger.Debug("azureAssetInfo: ", azureAssetInfo);

            try
            {
                jobHelper.JobOutput["jobInfo"] = azureAssetInfo;

                await jobHelper.UpdateJobAssignmentOutputAsync();
            }
            catch (Exception error)
            {
                Logger.Error("Error updating the job", error);
            }
        }
        internal static async Task ProcessJobAssignmentAsync(AzureAiServiceWorkerRequest @event)
        {
            var resourceManager = @event.GetAwsV4ResourceManager();
            var table           = new DynamoDbTable(@event.StageVariables["TableName"]);
            var jobAssignmentId = @event.JobAssignmentId;
            var azure           = new AzureConfig(@event);

            try
            {
                // 1. Setting job assignment status to RUNNING
                await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "RUNNING", null);

                // 2. Retrieving WorkflowJob
                var job = await RetrieveJobAsync(resourceManager, table, jobAssignmentId);

                // 3. Retrieve JobProfile
                var jobProfile = await RetrieveJobProfileAsync(resourceManager, job);

                // 4. Retrieve job inputParameters
                var jobInput = job.JobInput;

                // 5. Check if we support jobProfile and if we have required parameters in jobInput
                ValidateJobProfile(jobProfile, jobInput);

                S3Locator inputFile;
                if (!jobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile))
                {
                    throw new Exception("Invalid or missing input file.");
                }

                string mediaFileUrl;
                if (!string.IsNullOrWhiteSpace(inputFile.HttpEndpoint))
                {
                    mediaFileUrl = inputFile.HttpEndpoint;
                }
                else
                {
                    var bucketLocation = await inputFile.GetBucketLocationAsync();

                    var s3SubDomain = !string.IsNullOrWhiteSpace(bucketLocation) ? $"s3-{bucketLocation}" : "s3";
                    mediaFileUrl = $"https://{s3SubDomain}.amazonaws.com/{inputFile.AwsS3Bucket}/{inputFile.AwsS3Key}";
                }

                switch (jobProfile.Name)
                {
                case JOB_PROFILE_TRANSCRIBE_AUDIO:
                case JOB_PROFILE_TRANSLATE_TEXT:
                    throw new NotImplementedException($"{jobProfile.Name} profile has not yet been implemented for Azure.");

                case JOB_PROFILE_EXTRACT_ALL_AI_METADATA:
                    var authTokenUrl  = azure.ApiUrl + "/auth/" + azure.Location + "/Accounts/" + azure.AccountID + "/AccessToken?allowEdit=true";
                    var customHeaders = new Dictionary <string, string> {
                        ["Ocp-Apim-Subscription-Key"] = azure.SubscriptionKey
                    };

                    Logger.Debug($"Generate Azure Video Indexer Token: Doing a GET on {authTokenUrl}");
                    var mcmaHttp = new McmaHttpClient();
                    var response = await mcmaHttp.GetAsync(authTokenUrl, headers : customHeaders).WithErrorHandling();

                    var apiToken = await response.Content.ReadAsJsonAsync();

                    Logger.Debug($"Azure API Token: {apiToken}");

                    // call the Azure API to process the video
                    // in this scenario the video is located in a public link
                    // so no need to upload the file to Azure

                    /* Sample URL Structure
                     *  https://api.videoindexer.ai/{location}/Accounts/{accountId}/Videos?
                     *      accessToken={accessToken}&
                     *      name={name}?description={string}&
                     *      partition={string}&
                     *      externalId={string}&
                     *      callbackUrl={string}&
                     *      metadata={string}&
                     *      language={string}&
                     *      videoUrl={string}&
                     *      fileName={string}&
                     *      indexingPreset={string}&
                     *      streamingPreset=Default&
                     *      linguisticModelId={string}&
                     *      privacy={string}&
                     *      externalUrl={string}" */

                    var secureHost    = new Uri(jobAssignmentId, UriKind.Absolute).Host;
                    var nonSecureHost = new Uri(@event.StageVariables["PublicUrlNonSecure"], UriKind.Absolute).Host;

                    var callbackUrl = Uri.EscapeDataString(jobAssignmentId.Replace(secureHost, nonSecureHost) + "/notifications");

                    var postVideoUrl = azure.ApiUrl + "/" + azure.Location + "/Accounts/" + azure.AccountID + "/Videos?accessToken=" + apiToken + "&name=" + inputFile.AwsS3Key + "&callbackUrl=" + callbackUrl + "&videoUrl=" + mediaFileUrl + "&fileName=" + inputFile.AwsS3Key;

                    Logger.Debug($"Call Azure Video Indexer API: Doing a POST on {postVideoUrl}");
                    var postVideoResponse = await mcmaHttp.PostAsync(postVideoUrl, null, customHeaders).WithErrorHandling();

                    var azureAssetInfo = await postVideoResponse.Content.ReadAsJsonAsync();

                    Logger.Debug("azureAssetInfo: ", azureAssetInfo);

                    try
                    {
                        var jobOutput = new JobParameterBag();
                        jobOutput["jobInfo"] = azureAssetInfo;

                        await UpdateJobAssignmentWithOutputAsync(table, jobAssignmentId, jobOutput);
                    }
                    catch (Exception error)
                    {
                        Logger.Error("Error updating the job", error);
                    }

                    break;
                }
            }
            catch (Exception ex)
            {
                Logger.Exception(ex);

                try
                {
                    await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "FAILED", ex.ToString());
                }
                catch (Exception innerEx)
                {
                    Logger.Exception(innerEx);
                }
            }
        }