public static IActionResult RunUploadToQnaMaker( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, [Queue("upload-to-qna"), StorageAccount("AzureWebJobsStorage")] ICollector <QnAQueueMessageBatch> msg, ILogger log, ExecutionContext executionContext) { log.LogInformation("UploadToQnAMaker Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } var msgBatch = new QnAQueueMessageBatch(); WebApiSkillResponse response = WebApiSkillHelpers.ProcessRequestRecords(skillName, requestRecords, (inRecord, outRecord) => { string id = (inRecord.Data.TryGetValue("id", out object idObject) ? idObject : null) as string; string blobName = (inRecord.Data.TryGetValue("blobName", out object blobNameObject) ? blobNameObject : null) as string; string blobUrl = (inRecord.Data.TryGetValue("blobUrl", out object blobUrlObject) ? blobUrlObject : null) as string; string sasToken = (inRecord.Data.TryGetValue("sasToken", out object sasTokenObject) ? sasTokenObject : null) as string; long blobSizeInKBs = (inRecord.Data.TryGetValue("blobSize", out object blobSizeObject) ? (long)blobSizeObject : 0) / 1024; if (string.IsNullOrWhiteSpace(blobUrl)) { outRecord.Errors.Add(new WebApiErrorWarningContract() { Message = $"Parameter '{nameof(blobUrl)}' is required to be present and a valid uri." }); return(outRecord); } string fileUri = WebApiSkillHelpers.CombineSasTokenWithUri(blobUrl, sasToken); if (!IsValidFile(blobName, blobSizeInKBs)) { log.LogError("upload-to-qna-queue-trigger: unable to extract qnas from this file " + blobName + " of size " + blobSizeInKBs); outRecord.Data["status"] = "Failed"; } else { var queueMessage = new QnAQueueMessage { Id = id, FileName = blobName, FileUri = fileUri }; msgBatch.Values.Add(queueMessage); if (msgBatch.Values.Count >= 10) { msg.Add(msgBatch); msgBatch.Values.Clear(); } outRecord.Data["status"] = "InQueue"; } return(outRecord); });
public static IActionResult RunDecryptBlobFile( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("DecryptBlobFile Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } // Set up access to keyvault to retrieve the key to decrypt the document with // Requires that this Azure Function has access via managed identity to the Keyvault where the key is stored. var azureServiceTokenProvider1 = new AzureServiceTokenProvider(); var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider1.KeyVaultTokenCallback)); KeyVaultKeyResolver cloudResolver = new KeyVaultKeyResolver(kv); BlobEncryptionPolicy policy = new BlobEncryptionPolicy(null, cloudResolver); BlobRequestOptions options = new BlobRequestOptions() { EncryptionPolicy = policy }; WebApiSkillResponse response = WebApiSkillHelpers.ProcessRequestRecords(skillName, requestRecords, (inRecord, outRecord) => { string blobUrl = (inRecord.Data.TryGetValue("blobUrl", out object blobUrlObject) ? blobUrlObject : null) as string; string sasToken = (inRecord.Data.TryGetValue("sasToken", out object sasTokenObject) ? sasTokenObject : null) as string; if (string.IsNullOrWhiteSpace(blobUrl)) { outRecord.Errors.Add(new WebApiErrorWarningContract() { Message = $"Parameter '{nameof(blobUrl)}' is required to be present and a valid uri." }); return(outRecord); } CloudBlockBlob blob = new CloudBlockBlob(new Uri(WebApiSkillHelpers.CombineSasTokenWithUri(blobUrl, sasToken))); byte[] decryptedFileData; using (var np = new MemoryStream()) { blob.DownloadToStream(np, null, options, null); decryptedFileData = np.ToArray(); } FileReference decryptedFileReference = new FileReference() { data = Convert.ToBase64String(decryptedFileData) }; JObject jObject = JObject.FromObject(decryptedFileReference); jObject["$type"] = "file"; outRecord.Data["decrypted_file_data"] = jObject; return(outRecord); });
public void CanCombineNormalUriWithNormalSasToken() { // This is typical case when user supplies blob metadata_storage_path and metadata_storage_sas_token var uri = @"http://www.myblob.com"; var sasToken = @"?sp=rl&st=2020-01-17T23:22:10Z&se=2020-01-31T23:22:00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3D"; var result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "http://www.myblob.com:80/?sp=rl&st=2020-01-17T23%3a22%3a10Z&se=2020-01-31T23%3a22%3a00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3d" , result); }
public static async Task <IActionResult> RunAnalyzeForm( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Analyze Form Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } string formsRecognizerEndpointUrl = Environment.GetEnvironmentVariable(formsRecognizerApiEndpointSetting, EnvironmentVariableTarget.Process).TrimEnd('/'); string formsRecognizerApiKey = Environment.GetEnvironmentVariable(formsRecognizerApiKeySetting, EnvironmentVariableTarget.Process); // string modelId = Environment.GetEnvironmentVariable(modelIdSetting, EnvironmentVariableTarget.Process); int retryDelay = int.TryParse(Environment.GetEnvironmentVariable(retryDelaySetting, EnvironmentVariableTarget.Process), out int parsedRetryDelay) ? parsedRetryDelay : 1000; int maxAttempts = int.TryParse(Environment.GetEnvironmentVariable(maxAttemptsSetting, EnvironmentVariableTarget.Process), out int parsedMaxAttempts) ? parsedMaxAttempts : 100; Dictionary <string, string> fieldMappings = JsonConvert.DeserializeObject <Dictionary <string, string> >( File.ReadAllText($"{executionContext.FunctionAppDirectory}\\field-mappings.json")); WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords, async (inRecord, outRecord) => { var formUrl = inRecord.Data["formUrl"] as string; var formSasToken = inRecord.Data["formSasToken"] as string; string formUri = WebApiSkillHelpers.CombineSasTokenWithUri(formUrl, formSasToken); // Create the job string jobId = await GetJobId(formsRecognizerEndpointUrl, formUri, formsRecognizerApiKey); // Get the results for (int attempt = 0; attempt < maxAttempts; attempt++) { (string status, JToken result) = await GetJobStatus(jobId, formsRecognizerApiKey); if (status == "failed") { var errors = result.SelectToken("analyzeResult.errors") as JArray; outRecord.Errors.AddRange(errors.Children().Select(error => new WebApiErrorWarningContract { Message = error.SelectToken("message").ToObject <string>() })); return(outRecord); } if (status == "succeeded") { //List<Page> pages = result.SelectToken("analyzeResult.pageResults").ToObject<List<Page>>(); List <Page> pages = result.SelectToken("analyzeResult.readResults").ToObject <List <Page> >(); foreach (KeyValuePair <string, string> kvp in fieldMappings) { string value = GetField(pages, kvp.Key); if (!string.IsNullOrWhiteSpace(value)) { outRecord.Data[kvp.Value] = value; } } return(outRecord); } await Task.Delay(retryDelay); } outRecord.Errors.Add(new WebApiErrorWarningContract { Message = $"The forms recognizer did not finish the job after {maxAttempts} attempts." }); return(outRecord); }); return(new OkObjectResult(response)); }
public static async Task <IActionResult> RunSplitImageSkill( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext executionContext) { log.LogInformation("Split Image Custom Skill: C# HTTP trigger function processed a request."); string skillName = executionContext.FunctionName; IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req); if (requestRecords == null) { return(new BadRequestObjectResult($"{skillName} - Invalid request record array.")); } WebApiSkillResponse response = WebApiSkillHelpers.ProcessRequestRecords(skillName, requestRecords, (inRecord, outRecord) => { var imageUrl = (inRecord.Data.TryGetValue("imageLocation", out object imageUrlObject) ? imageUrlObject : null) as string; var sasToken = (inRecord.Data.TryGetValue("imageLocation", out object sasTokenObject) ? sasTokenObject : null) as string; if (string.IsNullOrWhiteSpace(imageUrl)) { outRecord.Errors.Add(new WebApiErrorWarningContract() { Message = $"Parameter '{nameof(imageUrl)}' is required to be present and a valid uri." }); return(outRecord); } JArray splitImages = new JArray(); using (WebClient client = new WebClient()) { byte[] fileData = executionContext.FunctionName == "unitTestFunction" ? fileData = File.ReadAllBytes(imageUrl) // this is a unit test, find the file locally : fileData = client.DownloadData(new Uri(WebApiSkillHelpers.CombineSasTokenWithUri(imageUrl, sasToken))); // download the file from remote server using (var stream = new MemoryStream(fileData)) { var originalImage = Image.Load(stream); // chunk the document up into pieces // overlap the chunks to reduce the chances of cutting words in half // (and not being able to OCR that data) for (int x = 0; x < originalImage.Width; x += (MaxImageDimension - ImageOverlapInPixels)) { for (int y = 0; y < originalImage.Height; y += (MaxImageDimension - ImageOverlapInPixels)) { int startX = x; int endX = x + MaxImageDimension >= originalImage.Width ? originalImage.Width : x + MaxImageDimension; int startY = y; int endY = y + MaxImageDimension >= originalImage.Height ? originalImage.Height : y + MaxImageDimension; var newImageData = CropImage(originalImage, startX, endX, startY, endY); var imageData = new JObject(); imageData["$type"] = "file"; imageData["data"] = System.Convert.ToBase64String(newImageData); imageData["width"] = endX - startX; imageData["height"] = endY - startY; splitImages.Add(imageData); } } } } outRecord.Data["splitImages"] = splitImages; return(outRecord); });
public void CanCombineLessWellFormedUriAndSas() { var uri = @"www.myblob.com"; var sasToken = @"?sp=rl&st=2020-01-17T23:22:10Z&se=2020-01-31T23:22:00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3D"; var result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "http://www.myblob.com:80/?sp=rl&st=2020-01-17T23%3a22%3a10Z&se=2020-01-31T23%3a22%3a00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3d" , result); sasToken = @"sp=rl&st=2020-01-17T23:22:10Z&se=2020-01-31T23:22:00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3D"; result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "http://www.myblob.com:80/?sp=rl&st=2020-01-17T23%3a22%3a10Z&se=2020-01-31T23%3a22%3a00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3d" , result); sasToken = @"sp=rl&st=2020-01-17T23:22:10Z"; result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "http://www.myblob.com:80/?sp=rl&st=2020-01-17T23%3a22%3a10Z" , result); uri = @"https://www.myblob.com"; sasToken = @"?sp=rl&st=2020-01-17T23:22:10Z&se=2020-01-31T23:22:00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3D"; result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "https://www.myblob.com:443/?sp=rl&st=2020-01-17T23%3a22%3a10Z&se=2020-01-31T23%3a22%3a00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3d" , result); uri = @"ftp://www.myblob.net"; result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "ftp://www.myblob.net:21/?sp=rl&st=2020-01-17T23%3a22%3a10Z&se=2020-01-31T23%3a22%3a00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3d" , result); uri = @"http://www.myblob.net:7777"; result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "http://www.myblob.net:7777/?sp=rl&st=2020-01-17T23%3a22%3a10Z&se=2020-01-31T23%3a22%3a00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3d" , result); uri = @"http://www.myblob.net:7777?otherUrlParam=value"; result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "http://www.myblob.net:7777/?otherUrlParam=value&sp=rl&st=2020-01-17T23%3a22%3a10Z&se=2020-01-31T23%3a22%3a00Z&sv=2019-02-02&sr=b&sig=wcXZOnZGjJssuJYHgjSeSXIrEs6FVckj6lgTtB1VpVc%3d" , result); uri = @"http://www.myblob.net"; sasToken = null; // no sas needed result = WebApiSkillHelpers.CombineSasTokenWithUri(uri, sasToken); Assert.AreEqual( "http://www.myblob.net:80/" , result); }