/// <summary>Queue messages for subscription.</summary> /// <param name="subscription">The subscription.</param> /// <param name="json"> The resource.</param> public static void QueueMessagesForSubscription( r5.Subscription subscription, string json) { if (subscription == null) { return; } if (!_instance._subscriptionInfosDict.ContainsKey(subscription.Id)) { return; } foreach (WebsocketClientInformation client in _instance._subscriptionInfosDict[subscription.Id]) { // add this message to this client's queue (caller should have set it up correctly) client.MessageQ.Enqueue(json); //string clientMessage = string.Empty; //// determine the type of message this client wants //switch (client.PayloadType) //{ // case WebsocketClientInformation.WebsocketPayloadTypes.Empty: // case WebsocketClientInformation.WebsocketPayloadTypes.FullResource: // case WebsocketClientInformation.WebsocketPayloadTypes.IdOnly: // // serialize our bundle as our message // clientMessage = json; // break; // case WebsocketClientInformation.WebsocketPayloadTypes.R4: // // send a notification // clientMessage = $"ping {subscription.Id}"; // break; //} //// add this message to this client's queue //client.MessageQ.Enqueue(clientMessage); } }
/// <summary>Converts a different FHIR version subscription to R5.</summary> /// <param name="s4">The subscription.</param> /// <returns>R5 Subscription.</returns> public static r5.Subscription SubscriptionToR5(r4.Subscription s4) { if (s4 == null) { return(null); } r5.Subscription s5 = new r5.Subscription() { Id = s4.Id, ImplicitRules = s4.ImplicitRules, Language = s4.Language, End = s4.End, Reason = s4.Reason, Endpoint = s4.Channel.Endpoint, Header = s4.Channel.Header, ContentType = s4.Channel.Payload, }; if (s4.Meta != null) { s5.Meta = new Meta(); if (s4.Meta.LastUpdated != null) { s5.Meta.LastUpdated = s4.Meta.LastUpdated; } if (s4.Meta.Profile != null) { s5.Meta.Profile = s4.Meta.Profile; } if (s4.Meta.Security != null) { s5.Meta.Security = new List <Coding>(); foreach (Coding c4 in s4.Meta.Security) { s5.Meta.Security.Add((Coding)c4.DeepCopy()); } } if (s4.Meta.Source != null) { s5.Meta.Source = s4.Meta.Source; } if (s4.Meta.Tag != null) { s5.Meta.Tag = new List <Coding>(); foreach (Coding c4 in s4.Meta.Tag) { s5.Meta.Tag.Add((Coding)c4.DeepCopy()); } } } switch (s4.Status) { case r4.Subscription.SubscriptionStatus.Active: s5.Status = r5.SubscriptionState.Active; break; case r4.Subscription.SubscriptionStatus.Error: s5.Status = r5.SubscriptionState.Error; break; case r4.Subscription.SubscriptionStatus.Off: s5.Status = r5.SubscriptionState.Off; break; case r4.Subscription.SubscriptionStatus.Requested: s5.Status = r5.SubscriptionState.Requested; break; default: Console.WriteLine($"Invalid R4 Subscription.Status: {s4.Status}"); return(null); } switch (s4.Channel.Type) { case r4.Subscription.SubscriptionChannelType.Email: s5.ChannelType = new Coding() { System = CanonicalChannelType, Code = "email", }; break; case r4.Subscription.SubscriptionChannelType.Message: s5.ChannelType = new Coding() { System = CanonicalChannelType, Code = "message", }; break; case r4.Subscription.SubscriptionChannelType.RestHook: s5.ChannelType = new Coding() { System = CanonicalChannelType, Code = "rest-hook", }; break; case r4.Subscription.SubscriptionChannelType.Websocket: s5.ChannelType = new Coding() { System = CanonicalChannelType, Code = "websocket", }; break; default: Console.WriteLine($"Invalid R4 Subscription.Channel.Type: {s4.Channel.Type}"); return(null); } if ((s4.Extension == null) || (s4.Extension.Count == 0)) { return(null); } foreach (Extension ext in s4.Extension) { if (ext.Url == ExtensionUrlTopic) { if (ext.Value is FhirUri) { s5.Topic = new ResourceReference(((FhirUri)ext.Value).Value); break; } else if (ext.Value is Canonical) { s5.Topic = new ResourceReference(((Canonical)ext.Value).Value); break; } } } if (s5.Topic == null) { #pragma warning disable CA1303 // Do not pass literals as localized parameters Console.WriteLine($"R4 Subscription requires Extension: {ExtensionUrlTopic}"); #pragma warning restore CA1303 // Do not pass literals as localized parameters return(null); } if (!SubscriptionTopicManager.TryGetTopic(s5.Topic.Reference, out r5.SubscriptionTopic topic)) { Console.WriteLine($"Unknown R4 SubscriptionTopic: {s5.Topic.Reference}"); return(null); } if (topic.ResourceTrigger == null) { Console.WriteLine($"SubscriptionTopic: {topic.Url} requires resourceTrigger"); return(null); } if ((topic.ResourceTrigger.ResourceType == null) || (!topic.ResourceTrigger.ResourceType.Any())) { Console.WriteLine($"SubscriptionTopic: {topic.Url} requires resourceTrigger.resourceType"); return(null); } if (s4.Channel.Extension != null) { foreach (Extension ext in s4.Channel.Extension) { switch (ext.Url) { case ExtensionUrlHeartbeat: if ((ext.Value != null) && (ext.Value is UnsignedInt)) { s5.HeartbeatPeriod = ((UnsignedInt)ext.Value).Value; } break; case ExtensionUrlTimeout: if ((ext.Value != null) && (ext.Value is UnsignedInt)) { s5.Timeout = ((UnsignedInt)ext.Value).Value; } break; case ExtensionNotificationUrlLocation: if ((ext.Value != null) && (ext.Value is Code)) { // TODO: Need December 2020 R5 build } break; case ExtensionMaxCount: if ((ext.Value != null) && (ext.Value is PositiveInt)) { // TODO: Need December 2020 R5 Build } break; } } } if ((s4.Channel.PayloadElement.Extension != null) && (s4.Channel.PayloadElement.Extension.Count > 0)) { foreach (Extension ext in s4.Channel.PayloadElement.Extension) { if ((ext.Url == ExtensionUrlContent) && (ext.Value is Code)) { switch (((Code)ext.Value).Value) { case "empty": s5.Content = r5.Subscription.SubscriptionPayloadContent.Empty; break; case "id-only": s5.Content = r5.Subscription.SubscriptionPayloadContent.IdOnly; break; case "full-resource": s5.Content = r5.Subscription.SubscriptionPayloadContent.FullResource; break; default: Console.WriteLine($"Invalid R4 Subscription.Channel.Payload Content: {((Code)ext.Value).Value}"); return(null); } } } } if (!string.IsNullOrEmpty(s4.Criteria)) { string criteria = s4.Criteria; string resource = topic.ResourceTrigger.ResourceType.First().Value.ToString(); if (!criteria.StartsWith(resource, StringComparison.Ordinal)) { Console.WriteLine( $"R4 Subscription Criteria: {criteria}" + $" must match SubscriptionTopic.resourceTrigger.resourceType: {resource}"); return(null); } // remove initial resource type plus the ? criteria = criteria.Substring(resource.Length + 1); string[] components = criteria.Split('&'); if (components.Length > 0) { s5.FilterBy = new List <r5.Subscription.FilterByComponent>(); } foreach (string component in components) { if (TryExpandFilter(component, out r5.Subscription.FilterByComponent filter)) { s5.FilterBy.Add(filter); } } } return(s5); }
/// <summary>Converts an R5 subscription to another FHIR version.</summary> /// <param name="s5">The subscription.</param> /// <returns>The desired version of a subscription.</returns> public static r4.Subscription SubscriptionFromR5(r5.Subscription s5) { if (s5 == null) { return(null); } if (s5.Topic == null) { return(null); } if (!SubscriptionTopicManager.TryGetTopic(s5.Topic.Reference, out r5.SubscriptionTopic topic)) { Console.WriteLine($"Unknown R5 SubscriptionTopic: {s5.Topic.Reference}"); return(null); } if (topic.ResourceTrigger == null) { Console.WriteLine($"SubscriptionTopic: {topic.Url} requires resourceTrigger"); return(null); } if ((topic.ResourceTrigger.ResourceType == null) || (!topic.ResourceTrigger.ResourceType.Any())) { Console.WriteLine($"SubscriptionTopic: {topic.Url} requires resourceTrigger.resourceType"); return(null); } r4.Subscription s4 = new r4.Subscription() { Id = s5.Id, ImplicitRules = s5.ImplicitRules, Language = s5.Language, End = s5.End, Reason = s5.Reason, Channel = new r4.Subscription.ChannelComponent() { Endpoint = s5.Endpoint, Header = s5.Header, Payload = s5.ContentType, }, }; if (s5.Meta != null) { s4.Meta = new Meta(); if (s5.Meta.LastUpdated != null) { s4.Meta.LastUpdated = s5.Meta.LastUpdated; } if (s5.Meta.Profile != null) { s4.Meta.Profile = s5.Meta.Profile; } if (s5.Meta.Security != null) { s4.Meta.Security = new List <Coding>(); foreach (Coding c5 in s5.Meta.Security) { s4.Meta.Security.Add((Coding)c5.DeepCopy()); } } if (s5.Meta.Source != null) { s4.Meta.Source = s5.Meta.Source; } if (s5.Meta.Tag != null) { s4.Meta.Tag = new List <Coding>(); foreach (Coding c5 in s5.Meta.Tag) { s4.Meta.Tag.Add((Coding)c5.DeepCopy()); } } } switch (s5.Status) { case r5.SubscriptionState.Active: s4.Status = r4.Subscription.SubscriptionStatus.Active; break; case r5.SubscriptionState.Error: s4.Status = r4.Subscription.SubscriptionStatus.Error; break; case r5.SubscriptionState.Off: s4.Status = r4.Subscription.SubscriptionStatus.Off; break; case r5.SubscriptionState.Requested: s4.Status = r4.Subscription.SubscriptionStatus.Requested; break; } switch (s5.ChannelType.Code) { case "email": s4.Channel.Type = r4.Subscription.SubscriptionChannelType.Email; break; case "message": s4.Channel.Type = r4.Subscription.SubscriptionChannelType.Message; break; case "rest-hook": s4.Channel.Type = r4.Subscription.SubscriptionChannelType.RestHook; break; case "websocket": s4.Channel.Type = r4.Subscription.SubscriptionChannelType.Websocket; break; } s4.Extension.Add(new Extension() { Url = ExtensionUrlTopic, Value = new FhirUri(s5.Topic.Url), }); if (s5.HeartbeatPeriod != null) { s4.Channel.Extension.Add(new Extension() { Url = ExtensionUrlHeartbeat, Value = new UnsignedInt(s5.HeartbeatPeriod), }); } if (s5.Timeout != null) { s4.Channel.Extension.Add(new Extension() { Url = ExtensionUrlTimeout, Value = new UnsignedInt(s5.Timeout), }); } switch (s5.Content) { case r5.Subscription.SubscriptionPayloadContent.Empty: s4.Channel.PayloadElement.Extension = new List <Extension>() { new Extension() { Url = ExtensionUrlContent, Value = new Code("empty"), }, }; break; case r5.Subscription.SubscriptionPayloadContent.IdOnly: s4.Channel.PayloadElement.Extension = new List <Extension>() { new Extension() { Url = ExtensionUrlContent, Value = new Code("id-only"), }, }; break; case r5.Subscription.SubscriptionPayloadContent.FullResource: s4.Channel.PayloadElement.Extension = new List <Extension>() { new Extension() { Url = ExtensionUrlContent, Value = new Code("full-resource"), }, }; break; } if ((s5.FilterBy != null) && (s5.FilterBy.Count > 0)) { StringBuilder sb = new StringBuilder(); sb.Append($"{topic.ResourceTrigger.ResourceType.First().Value}"); int addedCount = 0; foreach (r5.Subscription.FilterByComponent filter in s5.FilterBy) { if (TryCondenseFilter(filter, out string value)) { if (addedCount++ == 0) { sb.Append('?'); } else { sb.Append('&'); } sb.Append(value); } } s4.Criteria = sb.ToString(); } // TODO: Need December 2020 R5 build to add NotificationUrlLocation s4.Channel.AddExtension(ExtensionNotificationUrlLocation, new Code("full-url")); // TODO: Need December 2020 R5 build to add MaxCount s4.Channel.AddExtension(ExtensionMaxCount, new PositiveInt(10)); return(s4); }