public async Task <WebApiSkillResponse> ProcessInvoicesRecordsAsync(IEnumerable <WebApiRequestRecord> requestRecords)
        {
            WebApiSkillResponse response = new WebApiSkillResponse();

            foreach (WebApiRequestRecord inRecord in requestRecords)
            {
                WebApiResponseRecord outRecord = new WebApiResponseRecord()
                {
                    RecordId = inRecord.RecordId
                };

                try
                {
                    outRecord = await ProcessInvoiceRecord(inRecord, outRecord);
                }

                catch (Exception e)
                {
                    var erorMessage = $"{ServiceConstants.FormAnalyzerServiceName} - Error processing the request record: {e.ToString() }";
                    outRecord.Errors.Add(new WebApiErrorWarningContract()
                    {
                        Message = erorMessage
                    });

                    _log.LogError(erorMessage);
                }
                response.Values.Add(outRecord);
            }

            return(response);
        }
        public async Task ImageUriIsRequired()
        {
            WebApiSkillResponse skillOutput = await Helpers.QueryFunction(Helpers.BuildPayload(new { }), PowerSkills.Vision.SplitImage.SplitImage.RunSplitImageSkill);

            Assert.AreEqual(1, skillOutput.Values[0].Errors.Count);
            Assert.AreEqual("Parameter 'imageUrl' is required to be present and a valid uri.", skillOutput.Values[0].Errors[0].Message);
        }
예제 #3
0
        public static IActionResult RunCryptonymLinker(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Link Cryptonyms 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."));
            }

            CryptonymLinker     cryptonymLinker = new CryptonymLinker(executionContext.FunctionAppDirectory);
            WebApiSkillResponse response        = WebApiSkillHelpers.ProcessRequestRecords(skillName, requestRecords,
                                                                                           (inRecord, outRecord) => {
                string word = inRecord.Data["word"] as string;
                if (word.All(Char.IsUpper) && cryptonymLinker.Cryptonyms.TryGetValue(word, out string description))
                {
                    outRecord.Data["cryptonym"] = new { value = word, description };
                }
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #4
0
        public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req, TraceWriter log, ExecutionContext executionContext)
        {
            log.Info("C# HTTP trigger function processed a request.");
            string skillName = executionContext.FunctionName;

            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(req.CreateErrorResponse(HttpStatusCode.BadRequest, $"{skillName} - Invalid request record array."));
            }
            dynamic obj = requestRecords.First().Data.First().Value;

            string val = await MakeRequest(obj);

            ContentModerator     mod    = JsonConvert.DeserializeObject <ContentModerator>(val);
            WebApiResponseRecord output = new WebApiResponseRecord();

            output.RecordId    = requestRecords.First().RecordId;
            output.Data["PII"] = mod.PII;
            WebApiSkillResponse resp = new WebApiSkillResponse();

            resp.Values = new List <WebApiResponseRecord>();
            resp.Values.Add(output);
            return(req.CreateResponse(HttpStatusCode.OK, resp));
        }
        public async Task MissingTextBadRequest()
        {
            // tests against incorrect input (missing text)
            WebApiSkillResponse outputContent = await CustomEntitySearchHelpers.QueryEntitySearchFunction(TestData.MissingTextBadRequestInput);

            Assert.IsTrue(outputContent.Values[0].Errors[0].Message.Contains(TestData.MissingTextExpectedResponse));
        }
예제 #6
0
        public static IActionResult RunCryptonymLinkerForLists([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
                                                               ILogger log,
                                                               ExecutionContext executionContext)
        {
            log.LogInformation("Link Cryptonyms List 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."));
            }

            CryptonymLinker     cryptonymLinker = new CryptonymLinker(executionContext.FunctionAppDirectory);
            WebApiSkillResponse response        = WebApiSkillHelpers.ProcessRequestRecords(skillName, requestRecords,
                                                                                           (inRecord, outRecord) => {
                var words   = JsonConvert.DeserializeObject <JArray>(JsonConvert.SerializeObject(inRecord.Data["words"]));
                var cryptos = words.Select(jword =>
                {
                    var word = jword.Value <string>();
                    if (word.All(Char.IsUpper) && cryptonymLinker.Cryptonyms.TryGetValue(word, out string description))
                    {
                        return(new { value = word, description });
                    }
                    return(null);
                });

                outRecord.Data["cryptonyms"] = cryptos.ToArray();
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> RunEntitySearch(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Entity Search 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 bingApiKey = Environment.GetEnvironmentVariable(bingApiKeySetting, EnvironmentVariableTarget.Process);

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var entityName = inRecord.Data["name"] as string;
                string uri     = bingApiEndpoint
                                 + "?q=" + Uri.EscapeDataString(entityName)
                                 + "&mkt=en-us&safeSearch=Moderate";

                IEnumerable <BingEntity> entities =
                    await WebApiSkillHelpers.FetchAsync <BingEntity>(uri, "Ocp-Apim-Subscription-Key", bingApiKey, "entities.value");

                ExtractTopEntityMetadata(entities, outRecord.Data);
                outRecord.Data["entities"] = entities;
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #8
0
        public static async Task <HttpResponseMessage> WebApiSkill([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req, TraceWriter log)
        {
            try
            {
                // Get request body
                var jsonRequest = await req.Content.ReadAsStringAsync();

                var docs = JsonConvert.DeserializeObject <WebApiSkillRequest>(jsonRequest);

                WebApiSkillResponse response = new WebApiSkillResponse();

                HttpClient httpClient = new HttpClient();

                foreach (var inRecord in docs.values)
                {
                    var outRecord = new WebApiResponseRecord()
                    {
                        recordId = inRecord.recordId
                    };

                    string name = inRecord.data["name"] as string;
                    log.Info($"Creating Search Document:{name}");
                    string blobUrl = ((string)inRecord.data["url"]) + inRecord.data["querystring"] as string;

                    try
                    {
                        log.Info($"Downloading Document:{blobUrl}");
                        var aa = await httpClient.GetAsync(blobUrl);

                        aa.EnsureSuccessStatusCode();
                        using (var stream = await aa.Content.ReadAsStreamAsync())
                        {
                            log.Info($"Processing Document...");
                            var annotations = await ProcessDocument(stream);

                            log.Info($"Creating Search Document...");
                            var searchDocument = CreateSearchDocument(name, annotations);
                            log.Info($"Document complete");

                            outRecord.data["metadata"] = searchDocument.Metadata;
                            outRecord.data["text"]     = searchDocument.Text;
                            outRecord.data["entities"] = searchDocument.LinkedEntities;
                        }
                    }
                    catch (Exception e)
                    {
                        log.Error(e.ToString());
                        outRecord.errors.Add("Error processing the Document: " + e.ToString());
                    }
                    response.values.Add(outRecord);
                }

                return(req.CreateResponse(HttpStatusCode.OK, response));
            }
            catch (Exception ex)
            {
                log.Error(ex.ToString());
                return(req.CreateResponse(HttpStatusCode.BadRequest, "Error: " + ex.ToString()));
            }
        }
예제 #9
0
        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 static async Task <object> QuerySkill(
            Func <HttpRequest, ILogger, Microsoft.Azure.WebJobs.ExecutionContext, Task <IActionResult> > skillFunction,
            object payload,
            string outputPath)
        {
            WebApiSkillResponse skillOutput = await QueryFunction(BuildPayload(payload), skillFunction);

            return(skillOutput.Values[0].Data.TryGetValue(outputPath, out object output) ? output : null);
        }
        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 formsRecognizerApiKey = Environment.GetEnvironmentVariable(formsRecognizerApiKeySetting, EnvironmentVariableTarget.Process);
            string modelId = Environment.GetEnvironmentVariable(modelIdSetting, EnvironmentVariableTarget.Process);

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var formUrl      = inRecord.Data["formUrl"] as string;
                var formSasToken = inRecord.Data["formSasToken"] as string;

                // Fetch the document
                byte[] documentBytes;
                using (var webClient = new WebClient())
                {
                    documentBytes = await webClient.DownloadDataTaskAsync(new Uri(formUrl + formSasToken));
                }

                string uri = formsRecognizerApiEndpoint + "/models/" + Uri.EscapeDataString(modelId) + "/analyze";

                List <Page> pages =
                    (await WebApiSkillHelpers.FetchAsync <Page>(
                         uri,
                         apiKeyHeader: "Ocp-Apim-Subscription-Key",
                         apiKey: formsRecognizerApiKey,
                         collectionPath: "pages",
                         method: HttpMethod.Post,
                         postBody: documentBytes,
                         contentType))
                    .ToList();

                foreach (KeyValuePair <string, string> kvp in fieldMappings)
                {
                    var value = GetField(pages, kvp.Key);
                    if (!string.IsNullOrWhiteSpace(value))
                    {
                        outRecord.Data[kvp.Value] = value;
                    }
                }
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #13
0
        public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req, TraceWriter log, ExecutionContext executionContext)
        {
            log.Info("C# HTTP trigger function processed a request.");
            string skillName = executionContext.FunctionName;

            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(req.CreateErrorResponse(HttpStatusCode.BadRequest, $"{skillName} - Invalid request record array."));
            }
            dynamic obj = requestRecords.First().Data.First().Value;

            if (obj.Length > 1024)
            {
                obj = obj.Substring(0, 1024);
            }

            string val = await MakeRequest(obj);

            ContentModerator mod = JsonConvert.DeserializeObject <ContentModerator>(val);

            bool requiresModeration = false;

            //Jon Dobrzeniecki helped with the code below, since may 2019 the CM API isn't returning the PII section if there is no data for it
            if (mod.PII != null)
            {
                if (mod.PII.Email.Length > 0)
                {
                    requiresModeration = true;
                }
                if (mod.PII.Address.Length > 0)
                {
                    requiresModeration = true;
                }
                if (mod.PII.IPA.Length > 0)
                {
                    requiresModeration = true;
                }
                if (mod.PII.Phone.Length > 0)
                {
                    requiresModeration = true;
                }
            }
            WebApiResponseRecord output = new WebApiResponseRecord();

            output.RecordId     = requestRecords.First().RecordId;
            output.Data["text"] = requiresModeration;

            WebApiSkillResponse resp = new WebApiSkillResponse();

            resp.Values = new List <WebApiResponseRecord>();
            resp.Values.Add(output);
            return(req.CreateResponse(HttpStatusCode.OK, resp));
        }
        public static IActionResult RunProjection(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log, ExecutionContext executionContext)
        {
            log.LogInformation("Projection: C# HTTP trigger function processed a request.");
            string skillName = executionContext.FunctionName;

            log.LogInformation($"REQUEST: {new StreamReader(req.Body).ReadToEnd()}");
            req.Body.Position = 0;
            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) =>
            {
                Conversation[] turns       = JsonConvert.DeserializeObject <Conversation[]>(inRecord.Data["conversation"].ToString());
                List <Conversation> output = turns.ToList <Conversation>();
                if (output == null || output.All(x => x == null))
                {
                    outRecord.Data["result"]  = null;
                    outRecord.Data["summary"] = null;
                    return(outRecord);
                }
                var sortedList = output.OrderBy(foo => foo.offset).ToList();
                ConversationSummary summary = new ConversationSummary();
                summary.Turns = turns.Length;
                if (sortedList.Where(c => c.speaker == "1").FirstOrDefault() == null)
                {
                    summary.AverageSentiment = 0;
                    summary.LowestSentiment  = 0;
                    summary.HighestSentiment = 0;
                    summary.MaxChange        = new Tuple <int, float>(0, 0.0f);
                }
                else
                {
                    summary.AverageSentiment = sortedList.Where(c => c.speaker == "1").Select(a => a.sentiment).Average();
                    summary.LowestSentiment  = sortedList.Where(c => c.speaker == "1").Select(a => a.sentiment).Min();
                    summary.HighestSentiment = sortedList.Where(c => c.speaker == "1").Select(a => a.sentiment).Max();
                    //summary.MaxChange = ConversationSummary.MaxDiff(sortedList.Where(c => c.speaker == "1").Select(a => a.sentiment).ToList());
                    summary.Moment = ConversationSummary.GetKeyMoment(sortedList);
                }
                outRecord.Data["result"]  = sortedList;
                outRecord.Data["summary"] = summary;
                return(outRecord);
            });

            log.LogInformation(JsonConvert.SerializeObject(response));
            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> RunCustomVision(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Custom Vision 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 predictionUrl = Environment.GetEnvironmentVariable(customVisionPredictionUrlSetting, EnvironmentVariableTarget.Process);
            var predictionKey = Environment.GetEnvironmentVariable(customVisionApiKeySetting, EnvironmentVariableTarget.Process);
            var maxPages      = Convert.ToInt32(Environment.GetEnvironmentVariable(maxPagesSetting, EnvironmentVariableTarget.Process));

            if (maxPages == 0)
            {
                maxPages = 1;
            }
            var threshold = Convert.ToDouble(Environment.GetEnvironmentVariable(minProbabilityThresholdSetting, EnvironmentVariableTarget.Process));

            if (threshold == 0.0)
            {
                threshold = 0.5;
            }

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var pages       = inRecord.Data["pages"] as JArray;
                var tags        = new List <string>();
                var predictions = new List <object>();
                foreach (var page in pages.Take(maxPages))
                {
                    var pageBinaryData  = Convert.FromBase64String(page.ToString());
                    var pagePredictions = await GetPredictionsForImageAsync(pageBinaryData, predictionUrl, predictionKey);

                    var analyzeResult = JsonConvert.DeserializeObject <AnalyzeResult>(pagePredictions);
                    var pageTags      = analyzeResult.Predictions.Where(p => p.Probability >= threshold).Select(p => p.TagName).Distinct();
                    tags.AddRange(pageTags);
                }

                outRecord.Data["tags"] = tags.Distinct().ToArray();
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #16
0
        public static async Task <IActionResult> RunStoreImage(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Image Store Custom Skill: C# HTTP trigger function processed a request.");

            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null || requestRecords.Count() != 1)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array: Skill requires exactly 1 image per request."));
            }

            string blobStorageConnectionString = Environment.GetEnvironmentVariable(blobStorageConnectionStringSetting, EnvironmentVariableTarget.Process);
            string blobContainerName           = String.IsNullOrEmpty(req.Headers[blobContainerHeaderName])
                ? Environment.GetEnvironmentVariable(blobStorageContainerNameSetting, EnvironmentVariableTarget.Process)
                : (string)req.Headers[blobContainerHeaderName];

            if (String.IsNullOrEmpty(blobStorageConnectionString) || String.IsNullOrEmpty(blobContainerName))
            {
                return(new BadRequestObjectResult($"{skillName} - Information for the blob storage account is missing"));
            }
            var imageStore = new BlobImageStore(blobStorageConnectionString, blobContainerName);

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var imageData = inRecord.Data["imageData"] as string;
                var imageName = inRecord.Data["imageName"] as string;
                if (String.IsNullOrEmpty(imageName))
                {
                    imageName = Guid.NewGuid().ToString();
                }
                var mimeType = inRecord.Data["mimeType"] as string;
                if (String.IsNullOrEmpty(mimeType))
                {
                    mimeType = "image/jpeg";
                }
                string imageUri = await imageStore.UploadToBlobAsync(imageData, imageName, mimeType);
                outRecord.Data["imageStoreUri"] = imageUri;
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #17
0
        public async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation($"{ServiceConstants.FormAnalyzerServiceName} Custom Skill: C# HTTP trigger function processed a request.");

            IList <WebApiRequestRecord> requestRecords = await _documentProcessingService.DeserializeRequestAsync(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{ServiceConstants.FormAnalyzerServiceName} - Invalid request record array."));
            }

            WebApiSkillResponse response = await _documentProcessingService.ProcessInvoicesRecordsAsync(requestRecords);

            return(new OkObjectResult(response));
        }
예제 #18
0
        public static IActionResult RunKewit(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log, ExecutionContext executionContext)
        {
            log.LogInformation("Projection: C# HTTP trigger function processed a request.");
            string skillName = executionContext.FunctionName;

            log.LogInformation($"REQUEST: {new StreamReader(req.Body).ReadToEnd()}");
            req.Body.Position = 0;
            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) =>
            {
                string content = inRecord.Data["text"].ToString();
                string regex   = inRecord.Data["regex"].ToString();
                if (regex == null)
                {
                    regex = "^[A-Z]{2,5}$";
                }
                Regex rx = new Regex(regex);
                MatchCollection matches = rx.Matches(content);
                foreach (Match match in matches)
                {
                    GroupCollection groups = match.Groups;
                    Console.WriteLine("'{0}' repeated at positions {1} and {2}",
                                      groups["word"].Value,
                                      groups[0].Index,
                                      groups[1].Index);
                }
                var list = matches.Cast <Match>().Select(match => new { match.Value, match.Index }).ToList();
                outRecord.Data["matches"] = list;

                return(outRecord);
            });

            //log.LogInformation(JsonConvert.SerializeObject(response));
            return(new OkObjectResult(response));
        }
예제 #19
0
        public static async Task <IActionResult> RunDistinct(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Distinct 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."));
            }

            Thesaurus thesaurus = null;

            try
            {
                thesaurus = new Thesaurus(executionContext.FunctionAppDirectory);
            }
            catch (Exception e)
            {
                throw new Exception("Failed to read and parse thesaurus.json.", e);
            }
            WebApiSkillResponse response = WebApiSkillHelpers.ProcessRequestRecords(skillName, requestRecords,
                                                                                    (inRecord, outRecord) =>
            {
                JArray wordsParameter = inRecord.Data.TryGetValue("words", out object wordsParameterObject) ?
                                        wordsParameterObject as JArray : null;
                if (wordsParameter is null)
                {
                    throw new ArgumentException("Input data is missing a `words` array of words to de-duplicate.", "words");
                }
                var words = wordsParameter.Values <string>();
                outRecord.Data["distinct"] = thesaurus.Dedupe(words);
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #20
0
        public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req, TraceWriter log, ExecutionContext executionContext)
        {
            try
            {
                log.Info("C# HTTP trigger function processed a request.");

                string skillName = executionContext.FunctionName;

                IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);
                if (requestRecords == null)
                {
                    return(req.CreateErrorResponse(HttpStatusCode.BadRequest, $"{skillName} - Invalid request record array."));
                }
                log.Info($"Content Moderator : {requestRecords.ToString()}");
                dynamic obj = requestRecords.First().Data.First().Value;
                log.Info($"Content Moderator : {obj.ToString()}");

                string val = await MakeRequest(obj);

                ContentModerator     mod    = JsonConvert.DeserializeObject <ContentModerator>(val);
                WebApiResponseRecord output = new WebApiResponseRecord();
                output.RecordId = requestRecords.First().RecordId;
                if (mod.PII.Email.Length > 0)
                {
                    output.Data["PII"] = mod.PII.Email.FirstOrDefault().Detected;
                }
                else
                {
                    output.Data["PII"] = null;
                }
                WebApiSkillResponse resp = new WebApiSkillResponse();
                resp.Values = new List <WebApiResponseRecord>();
                resp.Values.Add(output);
                return(req.CreateResponse(HttpStatusCode.OK, resp));
            }
            catch (System.Exception ex)
            {
                log.Info(ex.StackTrace);
            }
            return(null);
        }
        public static async Task <IActionResult> RunGeoPointFromName(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Geo Point From Name 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 azureMapsKey = Environment.GetEnvironmentVariable(azureMapsKeySetting, EnvironmentVariableTarget.Process);

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var address = inRecord.Data["address"] as string;
                string uri  = azureMapsUri
                              + "?api-version=1.0&query=" + Uri.EscapeDataString(address)
                              + "&subscription-key=" + Uri.EscapeDataString(azureMapsKey);

                IEnumerable <Geography> geographies =
                    await WebApiSkillHelpers.FetchAsync <Geography>(uri, "results");

                if (geographies.FirstOrDefault() is Geography mainGeoPoint)
                {
                    outRecord.Data["mainGeoPoint"] = new {
                        Type        = "Point",
                        Coordinates = new double[] { mainGeoPoint.Position.Lon, mainGeoPoint.Position.Lat }
                    };
                }
                outRecord.Data["results"] = geographies;
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> RunCustomEntityLookup(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Custom Entity Search function: C# HTTP trigger function processed a request.");

            if (_impl == null || executionContext.FunctionName == "unitTestFunction")
            {
                _impl = new CustomEntityLookupImplementation(CustomEntitiesDefinition.ParseCustomEntityDefinition(EntityDefinitionLocation));
            }

            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{executionContext.FunctionName} - Invalid request record array."));
            }

            WebApiSkillResponse response = WebApiSkillHelpers.ProcessRequestRecords(executionContext.FunctionName, requestRecords,
                                                                                    (inRecord, outRecord) =>
            {
                if (!inRecord.Data.ContainsKey("text") || inRecord.Data["text"] == null)
                {
                    outRecord.Errors.Add(new WebApiErrorWarningContract {
                        Message = "Cannot process record without the given key 'text' with a string value"
                    });
                    return(outRecord);
                }

                string text = inRecord.Data["text"] as string;

                var foundEntities = _impl.GetCustomLookupResult(text, System.Threading.CancellationToken.None);

                outRecord.Data["entities"] = foundEntities;
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> RunJapaneseOCR(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("JapaneseOCR remove spaces 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 inputText             = inRecord.Data["text"] as string;
                outRecord.Data["ocrtext"] = "";

                if (string.IsNullOrEmpty(inputText))
                {
                    return(outRecord);
                }

                // 正規表現で全角文字+半白スペースの場合、半角スペースのみを除去
                var newContent = Regex.Replace(inputText, @"([^\x01-\x7E])\x20", "$1");
                // 全角文字+ハイフン+半角スペースの場合、ハイフンを全角長音に変換し半角スペースを除去
                newContent = Regex.Replace(newContent, @"([^\x01-\x7E])-\x20?", "$1ー");

                log.LogInformation($"newContent:  {newContent}");

                outRecord.Data["ocrtext"] = newContent;

                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #24
0
        public static IActionResult RunTokenizer(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Tokenizer 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 text = new TextData {
                    Text = inRecord.Data["text"] as string
                };
                StopWordsRemovingEstimator.Language language =
                    MapToMlNetLanguage(inRecord.Data.TryGetValue("languageCode", out object languageCode) ? languageCode as string : "en");

                var mlContext           = new MLContext();
                IDataView emptyDataView = mlContext.Data.LoadFromEnumerable(new List <TextData>());
                EstimatorChain <StopWordsRemovingTransformer> textPipeline = mlContext.Transforms.Text
                                                                             .NormalizeText("Text", caseMode: TextNormalizingEstimator.CaseMode.Lower, keepDiacritics: true, keepPunctuations: false, keepNumbers: false)
                                                                             .Append(mlContext.Transforms.Text.TokenizeIntoWords("Words", "Text", separators: new[] { ' ' }))
                                                                             .Append(mlContext.Transforms.Text.RemoveDefaultStopWords("Words", language: language));
                TransformerChain <StopWordsRemovingTransformer> textTransformer   = textPipeline.Fit(emptyDataView);
                PredictionEngine <TextData, TransformedTextData> predictionEngine = mlContext.Model.CreatePredictionEngine <TextData, TransformedTextData>(textTransformer);

                outRecord.Data["words"] = predictionEngine.Predict(text).Words ?? Array.Empty <string>();
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #25
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)

        {
            log.LogInformation("ABBYY OCR 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."));
            }

            // Create the response record
            WebApiSkillResponse response = new WebApiSkillResponse();

            using (var ocrClient = GetOcrClientWithRetryPolicy())
            {
                List <Task> TaskList = new List <Task>();
                int         idx      = 0;
                foreach (var record in requestRecords)
                {
                    TaskList.Add(ProcessRecord(record, ocrClient, idx));
                    idx += 1;
                }
                Task.WaitAll(TaskList.ToArray());

                // Apply all the records from the concurrent bag
                foreach (var record in bag)
                {
                    response.Values.Add(record);
                }
            }

            return(new OkObjectResult(response));
        }
예제 #26
0
        public static async Task <IActionResult> RunHelloWorld(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Hello World 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 name = inRecord.Data["name"] as string;
                outRecord.Data["greeting"] = $"Hello, {name}";
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
예제 #27
0
        public static async Task <HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req, TraceWriter log, ILogger logger, ExecutionContext executionContext)
        {
            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(req.CreateErrorResponse(HttpStatusCode.BadRequest, $"{skillName} - Invalid request record array."));
            }
            WebApiSkillResponse resp = new WebApiSkillResponse();

            resp.Values = new List <WebApiResponseRecord>();
            foreach (WebApiRequestRecord reqRec in requestRecords)
            {
                double lat                  = Convert.ToDouble(reqRec.Data["latitude"]);
                double lng                  = Convert.ToDouble(reqRec.Data["longitude"]);
                double review               = Convert.ToDouble(reqRec.Data["reviews_rating"]);
                string language             = (string)reqRec.Data["language"];
                WebApiResponseRecord output = new WebApiResponseRecord();
                try
                {
                    if (review > 5)
                    {
                        review = review / 2;
                    }

                    Address addr = await reverseGeocode(lat, lng, logger);

                    if (addr != null)
                    {
                        output.Data["state"]   = addr.adminDistrict;
                        output.Data["country"] = addr.countryRegion;
                    }
                    else
                    {
                        output.Data["state"]   = "UNKNOWN";
                        output.Data["country"] = "UNKNOWN";
                    }

                    output.RecordId = reqRec.RecordId;

                    output.Data["reviews_rating"] = review;


                    resp.Values.Add(output);
                }
                catch (System.Exception ex)
                {
                    log.Info($"EXCEPTION !!!! {ex.Message}");
                    log.Info(ex.StackTrace);
                    output.RecordId = reqRec.RecordId;
                    //output.Errors = new List<WebApiErrorWarningContract>();
                    //output.Errors.Add(new WebApiErrorWarningContract() { Message = ex.Message });
                    output.Data["state"]          = "UNKNOWN";
                    output.Data["country"]        = "UNKNOWN";
                    output.Data["reviews_rating"] = review;

                    resp.Values.Add(output);

                    return(req.CreateResponse(HttpStatusCode.OK, resp));
                }
            }



            log.Info($"Successful Run  returning {resp.Values.Count} records");
            return(req.CreateResponse(HttpStatusCode.OK, resp));
        }
예제 #28
0
        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 '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(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);
            });
예제 #29
0
        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));
        }
예제 #30
0
        public static async Task <IActionResult> RunCustomEntitySearch(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            if (preLoadedWords == null)
            {
                preLoadedWords = WordLinker.WordLink(executionContext.FunctionAppDirectory, "csv").Words;
            }

            log.LogInformation("Custom Entity Search function: 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) => {
                if (!inRecord.Data.ContainsKey("text") || inRecord.Data["text"] == null)
                {
                    outRecord.Errors.Add(new WebApiErrorWarningContract {
                        Message = "Cannot process record without the given key 'text' with a string value"
                    });
                    return(outRecord);
                }
                if (!inRecord.Data.ContainsKey("words") &&
                    (inRecord.Data.ContainsKey("synonyms") || inRecord.Data.ContainsKey("exactMatches") || inRecord.Data.ContainsKey("fuzzyMatchOffset")))
                {
                    outRecord.Errors.Add(new WebApiErrorWarningContract
                    {
                        Message = "Cannot process record without the given key 'words' in the dictionary"
                    });
                    return(outRecord);
                }
                string text = inRecord.Data["text"] as string;
                IList <string> words;
                if (inRecord.Data.ContainsKey("words") == true)
                {
                    words = inRecord.GetOrCreateList <List <string> >("words");
                }
                else
                {
                    outRecord.Warnings.Add(new WebApiErrorWarningContract
                    {
                        Message = "Used predefined key words from customLookupSkill configuration file " +
                                  "since no 'words' parameter was supplied in web request"
                    });
                    words = preLoadedWords;
                }
                Dictionary <string, string[]> synonyms = inRecord.GetOrCreateDictionary <Dictionary <string, string[]> >("synonyms");
                IList <string> exactMatches            = inRecord.GetOrCreateList <List <string> >("exactMatches");
                int offset         = (inRecord.Data.ContainsKey("fuzzyMatchOffset")) ? Math.Max(0, Convert.ToInt32(inRecord.Data["fuzzyMatchOffset"])) : 0;
                bool caseSensitive = (inRecord.Data.ContainsKey("caseSensitive")) ? (bool)inRecord.Data.ContainsKey("caseSensitive") : false;
                if (words.Count == 0 || (words.Count(word => !String.IsNullOrEmpty(word)) == 0))
                {
                    try
                    {
                        WordLinker userInput = WordLinker.WordLink(executionContext.FunctionDirectory, "json");
                        words         = userInput.Words;
                        synonyms      = userInput.Synonyms;
                        exactMatches  = userInput.ExactMatch;
                        offset        = (userInput.FuzzyMatchOffset >= 0) ? userInput.FuzzyMatchOffset : 0;
                        caseSensitive = userInput.CaseSensitive;
                        outRecord.Warnings.Add(new WebApiErrorWarningContract
                        {
                            Message = "Used predefined key words from customLookupSkill configuration file " +
                                      "since no 'words' parameter was supplied in web request"
                        });
                    }
                    catch (Exception)
                    {
                        outRecord.Errors.Add(new WebApiErrorWarningContract
                        {
                            Message = "Could not parse predefined words.json"
                        });
                        return(outRecord);
                    }
                }

                var entities      = new List <Entity>();
                var entitiesFound = new HashSet <string>();
                if (!string.IsNullOrWhiteSpace(text))
                {
                    foreach (string word in words)
                    {
                        if (string.IsNullOrEmpty(word))
                        {
                            continue;
                        }
                        int leniency         = (exactMatches != null && exactMatches.Contains(word)) ? 0 : offset;
                        string wordCharArray = (caseSensitive) ? CreateWordArray(word) : CreateWordArray(word.ToLower(CultureInfo.CurrentCulture));
                        if (leniency >= wordCharArray.Length)
                        {
                            outRecord.Warnings.Add(new WebApiErrorWarningContract
                            {
                                Message = @"The provided fuzzy offset of " + leniency + @", is larger than the length of the provided word, """ + word + @"""."
                            });
                            leniency = Math.Max(0, wordCharArray.Length - 1);
                        }
                        AddValues(word, text, wordCharArray, entities, entitiesFound, leniency, caseSensitive);
                        if (synonyms.TryGetValue(word, out string[] wordSynonyms))
                        {
                            foreach (string synonym in wordSynonyms)
                            {
                                leniency = (exactMatches != null && exactMatches.Contains(synonym)) ? 0 : offset;
                                string synonymCharArray = (caseSensitive) ? CreateWordArray(synonym) : CreateWordArray(synonym.ToLower(CultureInfo.CurrentCulture));
                                if (leniency >= synonym.Length)
                                {
                                    outRecord.Warnings.Add(new WebApiErrorWarningContract
                                    {
                                        Message = @"The provided fuzzy offset of " + leniency + @", is larger than the length of the provided synonym, """ + synonym + @"""."
                                    });
                                    leniency = Math.Max(0, synonymCharArray.Length - 1);
                                }
                                AddValues(synonym, text, synonymCharArray, entities, entitiesFound, leniency, caseSensitive);
                            }
                        }
                    }