diff --git a/src/Hl7.Fhir.Base/Specification/Source/ZipCacher.cs b/src/Hl7.Fhir.Base/Specification/Source/ZipCacher.cs index ce2bc21fa8..80b29bbeea 100644 --- a/src/Hl7.Fhir.Base/Specification/Source/ZipCacher.cs +++ b/src/Hl7.Fhir.Base/Specification/Source/ZipCacher.cs @@ -8,6 +8,7 @@ * available at https://github.com/FirelyTeam/firely-net-sdk/blob/master/LICENSE */ +using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; @@ -67,11 +68,11 @@ public bool IsActual() if (!dir.Exists) return false; - // Sometimes unzipping fails after creating the directory, try to fix that by - // checking if there are any files at all. - var dirIsEmpty = !dir.EnumerateFileSystemInfos().Any(); - if (dirIsEmpty) return false; - + // zip unpacking fails sometimes (issue #2164). if this cache is supposed to contain the specification, it should have at least a bunch of files in there (notably, we test for profiles-types.xml) + var isSpecificationZip = ZipPath.EndsWith(Path.Combine("specification.zip")); + var doesNotHaveCriticalFiles = !File.Exists(Path.Combine(CachePath, "specification", "profiles-types.xml")); + if (isSpecificationZip && doesNotHaveCriticalFiles) return false; + var currentZipFileTime = File.GetLastWriteTimeUtc(ZipPath); return dir.CreationTimeUtc >= currentZipFileTime; @@ -107,8 +108,14 @@ public void Refresh() dir.Create(); - - ZipFile.ExtractToDirectory(ZipPath, dir.FullName); + try + { + ZipFile.ExtractToDirectory(ZipPath, dir.FullName); + } + catch + { + Clear(); + } // and also extract the contained zip in there too with all the xsds in there if (File.Exists(Path.Combine(dir.FullName, "fhir-all-xsd.zip"))) @@ -130,9 +137,9 @@ public void Clear() /// - /// Gets the cache directory, but does not create one if it does not yet exist + /// Gets the cache directory, or creates an empty cache directory if it does not exist /// - /// + /// Information about the existing (or new) cache directory private DirectoryInfo getCachedZipDirectory() { // First, create the main "cache" directory. diff --git a/src/Hl7.Fhir.Conformance/Specification/Source/CommonZipSource.cs b/src/Hl7.Fhir.Conformance/Specification/Source/CommonZipSource.cs index 2c3dba0b87..4edd9b1eeb 100644 --- a/src/Hl7.Fhir.Conformance/Specification/Source/CommonZipSource.cs +++ b/src/Hl7.Fhir.Conformance/Specification/Source/CommonZipSource.cs @@ -22,7 +22,7 @@ namespace Hl7.Fhir.Specification.Source /// Reads FHIR artifacts (Profiles, ValueSets, ...) from a ZIP archive. Thread-safe. /// Extracts the ZIP archive to a temporary folder and delegates to the . [DebuggerDisplay(@"\{{DebuggerDisplay,nq}}")] - public class CommonZipSource : ISummarySource, ICommonConformanceSource, IArtifactSource, IResourceResolver, IAsyncResourceResolver + public class CommonZipSource : ISummarySource, ICommonConformanceSource, IArtifactSource, IAsyncResourceResolver { #pragma warning disable IDE1006 // Naming Styles - cannot fix this name because of bw-compatibility public const string SpecificationZipFileName = "specification.zip"; diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/Source/ZipSourceTests.cs b/src/Hl7.Fhir.Specification.Shared.Tests/Source/ZipSourceTests.cs index bb3f34164d..3884e1017c 100644 --- a/src/Hl7.Fhir.Specification.Shared.Tests/Source/ZipSourceTests.cs +++ b/src/Hl7.Fhir.Specification.Shared.Tests/Source/ZipSourceTests.cs @@ -1,9 +1,13 @@ using FluentAssertions; +using FluentAssertions.Extensions; +using Hl7.Fhir.Model; using Hl7.Fhir.Specification.Source; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.IO; +using System.IO.Compression; using System.Linq; +using Task = System.Threading.Tasks.Task; namespace Hl7.Fhir.Specification.Tests.Source { @@ -30,7 +34,7 @@ public void ListSummariesExcludingSubdirectories() { var zip = unpackTestData(new DirectorySourceSettings { IncludeSubDirectories = false }); var summaries = zip.ListSummaries(); - summaries.First().Origin.StartsWith(zip.ExtractPath); + summaries.First().Origin.StartsWith(zip.ExtractPath).Should().BeTrue(); Assert.IsNotNull(summaries, "Collection of summaries should not be null"); Assert.AreEqual(1, summaries.Count(), "In the zipfile there is 1 resource in the root folder."); @@ -52,5 +56,16 @@ public void UnpacksToSpecificDirectory() var summaries = zip.ListSummaries(); summaries.First().Origin.Should().StartWith(extractDir); } + + [TestMethod] + // If this test fails, the specification might have changed. This filename is hardcoded into the validation of a successful ZIP unpack + // (firely-net-sdk/src/Hl7.Fhir.Base/Specification/Source/ZipCacher.cs:74) + public void TestFilePrescence() + { + var zip = ZipSource.CreateValidationSource(); + zip.ListSummaries(); // make sure the zip is unpacked, we don't need the return value + // if extractpath is null, something went seriously wrong + File.Exists(Path.Combine(zip.ExtractPath!, "profiles-types.xml")).Should().BeTrue(); + } } }