예제 #1
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            var    resp        = await FHIRUtils.CallFHIRServer("", FHIRUtils.TransformBundle(requestBody, log), HttpMethod.Post, log);

            string r = "{}";

            if (resp != null)
            {
                r = resp.Content;
            }
            int sc = (int)resp.Status;

            return(new ContentResult()
            {
                Content = r, StatusCode = sc, ContentType = "application/json"
            });
        }
예제 #2
0
        public static string TransformBundle(string requestBody, ILogger log)
        {
            JObject result = JObject.Parse(requestBody);

            if (result == null || result["resourceType"] == null || result["type"] == null)
            {
                return(requestBody);
            }
            string rtt = result.FHIRResourceType();
            string bt  = (string)result["type"];

            if (rtt.Equals("Bundle") && bt.Equals("transaction"))
            {
                log.LogInformation($"TransformBundleProcess: looks like a valid transaction bundle");
                JArray entries = (JArray)result["entry"];
                if (entries.IsNullOrEmpty())
                {
                    return(result.ToString());
                }
                log.LogInformation($"TransformBundleProcess: Phase 1 searching for existing entries on FHIR Server...");
                foreach (JToken tok in entries)
                {
                    if (!tok.IsNullOrEmpty() && tok["request"]["ifNoneExist"] != null)
                    {
                        string resource = (string)tok["request"]["url"];
                        string query    = (string)tok["request"]["ifNoneExist"];
                        log.LogInformation($"TransformBundleProcess:Loading Resource {resource} with query {query}");
                        var r = FHIRUtils.CallFHIRServer($"{resource}?{query}", "", HttpMethod.Get, log).Result;
                        if (r.Success && r.Content != null)
                        {
                            var rs = JObject.Parse(r.Content);
                            if (!rs.IsNullOrEmpty() && ((string)rs["resourceType"]).Equals("Bundle") && !rs["entry"].IsNullOrEmpty())
                            {
                                JArray respentries = (JArray)rs["entry"];
                                string existingid  = "urn:uuid:" + (string)respentries[0]["resource"]["id"];
                                tok["fullUrl"] = existingid;
                            }
                        }
                    }
                }
                //reparse JSON with replacement of existing ids prepare to convert to Batch bundle with PUT to maintain relationships
                Dictionary <string, string> convert = new Dictionary <string, string>();
                result["type"] = "batch";
                entries        = (JArray)result["entry"];
                foreach (JToken tok in entries)
                {
                    string urn = (string)tok["fullUrl"];
                    if (!string.IsNullOrEmpty(urn) && !tok["resource"].IsNullOrEmpty())
                    {
                        string rt  = (string)tok["resource"]["resourceType"];
                        string rid = urn.Replace("urn:uuid:", "");
                        tok["resource"]["id"] = rid;
                        if (!convert.TryAdd(rid, rt))
                        {
                            //Duplicate catch
                            Guid g = Guid.NewGuid();
                            rid = g.ToString();
                            tok["resource"]["id"] = rid;
                        }
                        tok["request"]["method"] = "PUT";
                        tok["request"]["url"]    = $"{rt}?_id={rid}";
                    }
                }
                log.LogInformation($"TransformBundleProcess: Phase 2 Localizing {convert.Count} resource entries...");
                IEnumerable <JToken> refs = result.SelectTokens("$..reference");
                foreach (JToken item in refs)
                {
                    string s = item.ToString();
                    string t = "";
                    s = s.Replace("urn:uuid:", "");

                    if (convert.TryGetValue(s, out t))
                    {
                        item.Replace(t + "/" + s);
                    }
                }
                log.LogInformation($"TransformBundleProcess: Complete.");
                return(result.ToString());
            }
            return(requestBody);
        }
예제 #3
0
        public static async Task Run([EventGridTrigger] EventGridEvent blobCreatedEvent,
                                     [Blob("{data.url}", FileAccess.Read, Connection = "FBI-STORAGEACCT")] Stream myBlob,
                                     ILogger log)
        {
            bool   trbundles  = true;
            string strbundles = System.Environment.GetEnvironmentVariable("FBI-TRANSFORMBUNDLES");

            if (!string.IsNullOrEmpty(strbundles) && (strbundles.ToLower().Equals("no") || strbundles.ToLower().Equals("false")))
            {
                trbundles = false;
            }
            StorageBlobCreatedEventData createdEvent = ((JObject)blobCreatedEvent.Data).ToObject <StorageBlobCreatedEventData>();
            string name = createdEvent.Url.Substring(createdEvent.Url.LastIndexOf('/') + 1);

            if (myBlob == null)
            {
                return;
            }
            log.LogInformation($"ImportFHIRBUndles: Processing file Name:{name} \n Size: {myBlob.Length}");
            var          cbclient = StorageUtils.GetCloudBlobClient(System.Environment.GetEnvironmentVariable("FBI-STORAGEACCT"));
            StreamReader reader   = new StreamReader(myBlob);
            var          text     = await reader.ReadToEndAsync();

            var trtext     = (trbundles ? FHIRUtils.TransformBundle(text, log) : text);
            var fhirbundle = await FHIRUtils.CallFHIRServer("", trtext, HttpMethod.Post, log);

            var result = LoadErrorsDetected(trtext, fhirbundle, name, log);

            //Bundle Post was Throttled we can retry
            if (!fhirbundle.Success && fhirbundle.Status == System.Net.HttpStatusCode.TooManyRequests)
            {
                //Currently cannot use retry hints with EventGrid Trigger function bindings so we will throw and exception to enter eventgrid retry logic for FHIR Server throttling and do
                //our own dead letter for internal errors or unrecoverable conditions
                log.LogInformation($"ImportFHIRBUndles File Name:{name} is throttled...");
                throw new Exception($"ImportFHIRBUndles: Transient Error File: {name}...Entering eventgrid retry process until success or ultimate failure to dead letter if configured.");
            }
            //No Errors move to processed container
            if (fhirbundle.Success && ((JArray)result["errors"]).Count == 0 && ((JArray)result["throttled"]).Count == 0)
            {
                await StorageUtils.MoveTo(cbclient, "bundles", "bundlesprocessed", name, $"{name}.processed", log);

                await StorageUtils.WriteStringToBlob(cbclient, "bundlesprocessed", $"{name}.processed.result", fhirbundle.Content, log);

                log.LogInformation($"ImportFHIRBUndles Processed file Name:{name}");
            }
            //Handle Errors from FHIR Server of proxy
            if (!fhirbundle.Success || ((JArray)result["errors"]).Count > 0)
            {
                await StorageUtils.MoveTo(cbclient, "bundles", "bundleserr", name, $"{name}.err", log);

                await StorageUtils.WriteStringToBlob(cbclient, "bundleserr", $"{name}.err.response", fhirbundle.Content, log);

                await StorageUtils.WriteStringToBlob(cbclient, "bundleserr", $"{name}.err.actionneeded", result.ToString(), log);

                log.LogInformation($"ImportFHIRBUndles File Name:{name} had errors. Moved to deadletter bundleserr directory");
            }
            //Handle Throttled Requests inside of bundle so we will create a new bundle to retry
            if (fhirbundle.Success && ((JArray)result["throttled"]).Count > 0)
            {
                var nb = NDJSONConverter.initBundle();
                nb["entry"] = result["throttled"];
                string fn = $"retry{Guid.NewGuid().ToString().Replace("-","")}.json";
                await StorageUtils.MoveTo(cbclient, "bundles", "bundlesprocessed", name, $"{name}.processed", log);

                await StorageUtils.WriteStringToBlob(cbclient, "bundlesprocessed", $"{name}.processed.result", fhirbundle.Content, log);

                await StorageUtils.WriteStringToBlob(cbclient, "bundles", fn, nb.ToString(), log);

                log.LogInformation($"ImportFHIRBUndles File Name:{name} had throttled resources in response bundle. Moved to processed..Created retry bunde {fn}");
            }
        }