/// <summary> /// Send a Bundle to a path on the server /// </summary> /// <param name="bundle">The contents of the Bundle to be sent</param> /// <param name="path">A path on the server to send the Bundle to</param> /// <returns>True if the bundle was successfully delivered, false otherwise</returns> /// <remarks>This method differs from Batch, in that it can be used to deliver a Bundle /// at the endpoint for messages, documents or binaries, instead of the batched update /// REST endpoint.</remarks> public bool DeliverBundle(Bundle bundle, string path) { if (Endpoint == null) { throw new InvalidOperationException("Endpoint must be provided using either the Endpoint property or the FhirClient constructor"); } byte[] data; string contentType = ContentType.BuildContentType(PreferredFormat, false); if (PreferredFormat == ContentType.ResourceFormat.Json) { data = FhirSerializer.SerializeBundleToJsonBytes(bundle); } else if (PreferredFormat == ContentType.ResourceFormat.Xml) { data = FhirSerializer.SerializeBundleToXmlBytes(bundle); } else { throw new ArgumentException("Cannot encode a batch into format " + PreferredFormat.ToString()); } var req = createRequest(Endpoint, true); req.Method = "POST"; req.ContentType = contentType; prepareRequest(req, data); return(doRequest(req, HttpStatusCode.OK, () => true)); }
public bool SaveAs(string fn, bool writeFreshly = false, PreferredFormat prefFmt = PreferredFormat.None, MemoryStream useMemoryStream = null) { Console.WriteLine("SaveAs: " + fn); if (fn.ToLower().EndsWith(".xml")) { // save only XML this.fn = fn; try { using (var s = new StreamWriter(this.fn)) { // TODO: use aasenv serialzers here! var serializer = new XmlSerializer(typeof(AdminShell.AdministrationShellEnv)); var nss = new XmlSerializerNamespaces(); nss.Add("xsi", System.Xml.Schema.XmlSchema.InstanceNamespace); nss.Add("aas", "http://www.admin-shell.io/aas/2/0"); nss.Add("IEC61360", "http://www.admin-shell.io/IEC61360/2/0"); serializer.Serialize(s, this.aasenv, nss); } } catch (Exception ex) { throw (new Exception(string.Format("While writing AAS {0} at {1} gave: {2}", fn, AdminShellUtil.ShortLocation(ex), ex.Message))); } return(true); } if (fn.ToLower().EndsWith(".json")) { // save only JSON // this funcitonality is a initial test this.fn = fn; try { using (var sw = new StreamWriter(fn)) { // TODO: use aasenv serialzers here! sw.AutoFlush = true; JsonSerializer serializer = new JsonSerializer() { NullValueHandling = NullValueHandling.Ignore, ReferenceLoopHandling = ReferenceLoopHandling.Serialize, Formatting = Newtonsoft.Json.Formatting.Indented }; using (JsonWriter writer = new JsonTextWriter(sw)) { serializer.Serialize(writer, this.aasenv); } } } catch (Exception ex) { throw (new Exception(string.Format("While writing AAS {0} at {1} gave: {2}", fn, AdminShellUtil.ShortLocation(ex), ex.Message))); } return(true); } if (fn.ToLower().EndsWith(".aasx")) { // save package AASX try { // we want existing contents to be preserved, but no possiblity to change file name // therefore: copy file to new name, re-open! // fn could be changed, therefore close "old" package first if (this.openPackage != null) { // ReSharper disable EmptyGeneralCatchClause try { this.openPackage.Close(); if (!writeFreshly) { if (this.tempFn != null) { System.IO.File.Copy(this.tempFn, fn); } else { System.IO.File.Copy(this.fn, fn); } } } catch { } // ReSharper enable EmptyGeneralCatchClause this.openPackage = null; } // approach is to utilize the existing package, if possible. If not, create from scratch Package package = null; if (useMemoryStream != null) { package = Package.Open(useMemoryStream, (writeFreshly) ? FileMode.Create : FileMode.OpenOrCreate); } else { package = Package.Open((this.tempFn != null) ? this.tempFn : fn, (writeFreshly) ? FileMode.Create : FileMode.OpenOrCreate); } this.fn = fn; // get the origin from the package PackagePart originPart = null; var xs = package.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aasx-origin"); foreach (var x in xs) { if (x.SourceUri.ToString() == "/") { originPart = package.GetPart(x.TargetUri); break; } } if (originPart == null) { // create, as not existing originPart = package.CreatePart(new Uri("/aasx/aasx-origin", UriKind.RelativeOrAbsolute), System.Net.Mime.MediaTypeNames.Text.Plain, CompressionOption.Maximum); using (var s = originPart.GetStream(FileMode.Create)) { var bytes = System.Text.Encoding.ASCII.GetBytes("Intentionally empty."); s.Write(bytes, 0, bytes.Length); } package.CreateRelationship(originPart.Uri, TargetMode.Internal, "http://www.admin-shell.io/aasx/relationships/aasx-origin"); } // get the specs from the package PackagePart specPart = null; PackageRelationship specRel = null; xs = originPart.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aas-spec"); foreach (var x in xs) { specRel = x; specPart = package.GetPart(x.TargetUri); break; } // check, if we have to change the spec part if (specPart != null && specRel != null) { var name = System.IO.Path.GetFileNameWithoutExtension(specPart.Uri.ToString()).ToLower().Trim(); var ext = System.IO.Path.GetExtension(specPart.Uri.ToString()).ToLower().Trim(); if ((ext == ".json" && prefFmt == PreferredFormat.Xml) || (ext == ".xml" && prefFmt == PreferredFormat.Json) || (name.StartsWith("aasenv-with-no-id"))) { // try kill specpart // ReSharper disable EmptyGeneralCatchClause try { originPart.DeleteRelationship(specRel.Id); package.DeletePart(specPart.Uri); } catch { } finally { specPart = null; specRel = null; } // ReSharper enable EmptyGeneralCatchClause } } if (specPart == null) { // create, as not existing var frn = "aasenv-with-no-id"; if (this.aasenv.AdministrationShells.Count > 0) { frn = this.aasenv.AdministrationShells[0].GetFriendlyName() ?? frn; } var aas_spec_fn = "/aasx/#/#.aas"; if (prefFmt == PreferredFormat.Json) { aas_spec_fn += ".json"; } else { aas_spec_fn += ".xml"; } aas_spec_fn = aas_spec_fn.Replace("#", "" + frn); specPart = package.CreatePart(new Uri(aas_spec_fn, UriKind.RelativeOrAbsolute), System.Net.Mime.MediaTypeNames.Text.Xml, CompressionOption.Maximum); originPart.CreateRelationship(specPart.Uri, TargetMode.Internal, "http://www.admin-shell.io/aasx/relationships/aas-spec"); } // now, specPart shall be != null! if (specPart.Uri.ToString().ToLower().Trim().EndsWith("json")) { using (var s = specPart.GetStream(FileMode.Create)) { JsonSerializer serializer = new JsonSerializer(); serializer.NullValueHandling = NullValueHandling.Ignore; serializer.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; serializer.Formatting = Newtonsoft.Json.Formatting.Indented; using (var sw = new StreamWriter(s)) { using (JsonWriter writer = new JsonTextWriter(sw)) { serializer.Serialize(writer, this.aasenv); } } } } else { using (var s = specPart.GetStream(FileMode.Create)) { var serializer = new XmlSerializer(typeof(AdminShell.AdministrationShellEnv)); var nss = new XmlSerializerNamespaces(); nss.Add("xsi", System.Xml.Schema.XmlSchema.InstanceNamespace); nss.Add("aas", "http://www.admin-shell.io/aas/2/0"); nss.Add("IEC61360", "http://www.admin-shell.io/IEC61360/2/0"); serializer.Serialize(s, this.aasenv, nss); } } // there might be pending files to be deleted (first delete, then add, in case of identical files in both categories) foreach (var psfDel in pendingFilesToDelete) { // try find an existing part for that file .. var found = false; // normal files xs = specPart.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aas-suppl"); foreach (var x in xs) { if (x.TargetUri == psfDel.uri) { // try to delete specPart.DeleteRelationship(x.Id); package.DeletePart(psfDel.uri); found = true; break; } } // thumbnails xs = package.GetRelationshipsByType("http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"); foreach (var x in xs) { if (x.TargetUri == psfDel.uri) { // try to delete package.DeleteRelationship(x.Id); package.DeletePart(psfDel.uri); found = true; break; } } if (!found) { throw (new Exception($"Not able to delete pending file {psfDel.uri} in saving package {fn}")); } } // after this, there are no more pending for delete files pendingFilesToDelete.Clear(); // write pending supplementary files foreach (var psfAdd in pendingFilesToAdd) { // make sure .. if ((psfAdd.sourceLocalPath == null && psfAdd.sourceGetBytesDel == null) || psfAdd.location != AdminShellPackageSupplementaryFile.LocationType.AddPending) { continue; } // normal file? if (psfAdd.specialHandling == AdminShellPackageSupplementaryFile.SpecialHandlingType.None || psfAdd.specialHandling == AdminShellPackageSupplementaryFile.SpecialHandlingType.EmbedAsThumbnail) { // try find an existing part for that file .. PackagePart filePart = null; if (psfAdd.specialHandling == AdminShellPackageSupplementaryFile.SpecialHandlingType.None) { xs = specPart.GetRelationshipsByType("http://www.admin-shell.io/aasx/relationships/aas-suppl"); foreach (var x in xs) { if (x.TargetUri == psfAdd.uri) { filePart = package.GetPart(x.TargetUri); break; } } } if (psfAdd.specialHandling == AdminShellPackageSupplementaryFile.SpecialHandlingType.EmbedAsThumbnail) { xs = package.GetRelationshipsByType("http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"); foreach (var x in xs) { if (x.SourceUri.ToString() == "/" && x.TargetUri == psfAdd.uri) { filePart = package.GetPart(x.TargetUri); break; } } } if (filePart == null) { // determine mimeType var mimeType = psfAdd.useMimeType; // reconcile mime if (mimeType == null && psfAdd.sourceLocalPath != null) { mimeType = AdminShellPackageEnv.GuessMimeType(psfAdd.sourceLocalPath); } // still null? if (mimeType == null) { // see: https://stackoverflow.com/questions/6783921/which-mime-type-to-use-for-a-binary-file-thats-specific-to-my-program mimeType = "application/octet-stream"; } // create new part and link filePart = package.CreatePart(psfAdd.uri, mimeType, CompressionOption.Maximum); if (psfAdd.specialHandling == AdminShellPackageSupplementaryFile.SpecialHandlingType.None) { specPart.CreateRelationship(filePart.Uri, TargetMode.Internal, "http://www.admin-shell.io/aasx/relationships/aas-suppl"); } if (psfAdd.specialHandling == AdminShellPackageSupplementaryFile.SpecialHandlingType.EmbedAsThumbnail) { package.CreateRelationship(filePart.Uri, TargetMode.Internal, "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"); } } // now should be able to write using (var s = filePart.GetStream(FileMode.Create)) { if (psfAdd.sourceLocalPath != null) { var bytes = System.IO.File.ReadAllBytes(psfAdd.sourceLocalPath); s.Write(bytes, 0, bytes.Length); } if (psfAdd.sourceGetBytesDel != null) { var bytes = psfAdd.sourceGetBytesDel(); if (bytes != null) { s.Write(bytes, 0, bytes.Length); } } } } } // after this, there are no more pending for add files pendingFilesToAdd.Clear(); // flush and close package.Flush(); this.openPackage = null; package.Close(); // if in temp fn, close the package, copy to original fn, re-open the package if (this.tempFn != null) { try { package.Close(); System.IO.File.Copy(this.tempFn, this.fn, overwrite: true); this.openPackage = Package.Open(this.tempFn, FileMode.OpenOrCreate); } catch (Exception ex) { throw (new Exception(string.Format("While write AASX {0} indirectly at {1} gave: {2}", fn, AdminShellUtil.ShortLocation(ex), ex.Message))); } } } catch (Exception ex) { throw (new Exception(string.Format("While write AASX {0} at {1} gave: {2}", fn, AdminShellUtil.ShortLocation(ex), ex.Message))); } return(true); } // Don't know to handle throw (new Exception(string.Format($"Not able to handle {fn}."))); }