public async Task CanWriteAndReadAFile(FileArtifactType type)
        {
            // Arrange
            var expected = new MemoryStream(Encoding.UTF8.GetBytes("Hello, world!"));

            // Act & Assert
            await _target.StoreStreamAsync(
                Id,
                Version,
                type,
                dest => expected.CopyToAsync(dest));

            _blobStorageService.Verify(
                x => x.UploadStreamAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <Stream>()),
                Times.Once);
            _blobStorageService.Verify(
                x => x.GetStreamOrNullAsync(It.IsAny <string>()),
                Times.Never);

            _blobStorageService.ResetCalls();

            using (var actual = await _target.GetStreamOrNullAsync(Id, Version, type))
            {
                AssertSameStreams(expected, actual);

                _blobStorageService.Verify(
                    x => x.UploadStreamAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <Stream>()),
                    Times.Never);
                _blobStorageService.Verify(
                    x => x.GetStreamOrNullAsync(It.IsAny <string>()),
                    Times.Never);
            }
        }
Example #2
0
        private static IEnumerable <string> EnumerateFileOrDirectories(
            string directoryPath,
            FileArtifactType fileArtifactType,
            string searchPattern,
            SearchOption searchOption = SearchOption.TopDirectoryOnly)
        {
            var enumeration = new List <string>();

            // The search pattern and path gets normalized so we always use backslashes
            searchPattern = NormalizePathToWindowsStyle(searchPattern);
            directoryPath = NormalizePathToWindowsStyle(directoryPath);

            var result = CustomEnumerateDirectoryEntries(
                directoryPath,
                fileArtifactType,
                searchPattern,
                searchOption,
                enumeration);

            // If the result indicates that the enumeration succeeded or the directory does not exist, then the result is considered success.
            // In particular, if the globed directory does not exist, then we want to return the empty file, and track for the anti-dependency.
            if (
                !(result.Status == WindowsNative.EnumerateDirectoryStatus.Success ||
                  result.Status == WindowsNative.EnumerateDirectoryStatus.SearchDirectoryNotFound))
            {
                throw result.CreateExceptionForError();
            }

            return(enumeration);
        }
Example #3
0
        public async Task <Stream> GetStreamOrNullAsync(string id, string version, FileArtifactType type)
        {
            var cacheKey = GetCacheKey(id, version, type);

            if (_memoryCache.TryGetValue <byte[]>(cacheKey, out var cachedValue))
            {
                return(new MemoryStream(cachedValue));
            }

            var blobName     = _blobNameProvider.GetLatestBlobName(id, version, type);
            var outputStream = new MemoryStream();

            using (var blobStream = await _blobStorageService.GetStreamOrNullAsync(blobName))
            {
                if (blobStream == null)
                {
                    return(null);
                }

                await blobStream.CopyToAsync(outputStream);
            }

            CacheMemoryStream(cacheKey, outputStream);

            outputStream.Position = 0;
            return(outputStream);
        }
Example #4
0
        public async Task StoreStreamAsync(string id, string version, FileArtifactType type, Func <Stream, Task> writeAsync)
        {
            using (var memoryStream = new MemoryStream())
            {
                await writeAsync(memoryStream);

                var blobName    = _blobNameProvider.GetLatestBlobName(id, version, type);
                var contentType = GetContentType(type);
                memoryStream.Position = 0;

                var cacheKey = GetCacheKey(id, version, type);
                try
                {
                    await _blobStorageService.UploadStreamAsync(blobName, contentType, memoryStream);
                }
                catch
                {
                    // Clear the cache since we don't know what happened.
                    _memoryCache.Remove(cacheKey);
                    throw;
                }

                CacheMemoryStream(cacheKey, memoryStream);
            }
        }
Example #5
0
        public string GetLatestBlobName(string id, string version, FileArtifactType type)
        {
            var lowerId      = id.ToLowerInvariant();
            var lowerVersion = version.ToLowerInvariant();
            var extension    = GetExtension(type);

            var blobName = $"{lowerId}/{lowerVersion}/latest.{extension}";

            return(blobName);
        }
        public async Task ReturnsNullWithNonExistentFile(FileArtifactType type)
        {
            // Arrange & Act
            using (var actual = await _target.GetStreamOrNullAsync(Id, Version, type))
            {
                // Assert
                Assert.Null(actual);

                _blobStorageService.Verify(
                    x => x.UploadStreamAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <Stream>()),
                    Times.Never);
                _blobStorageService.Verify(
                    x => x.GetStreamOrNullAsync(It.IsAny <string>()),
                    Times.Once);
            }
        }
Example #7
0
        private static string GetExtension(FileArtifactType type)
        {
            string extension;

            switch (type)
            {
            case FileArtifactType.Nuspec:
                extension = "nuspec";
                break;

            case FileArtifactType.MZip:
                extension = "mzip";
                break;

            default:
                throw new NotSupportedException($"The file artifact type {type} is not supported.");
            }

            return(extension);
        }
Example #8
0
        private static string GetContentType(FileArtifactType type)
        {
            string contentType;

            switch (type)
            {
            case FileArtifactType.Nuspec:
                contentType = "application/xml";
                break;

            case FileArtifactType.MZip:
                contentType = "application/octet-stream";
                break;

            default:
                throw new NotSupportedException($"The file artifact type {type} is not supported.");
            }

            return(contentType);
        }
Example #9
0
        private static bool FileOrDirectoryExists(FileArtifactType fileArtifactType, string path)
        {
            // The path gets normalized so we always use backslashes
            path = NormalizePathToWindowsStyle(path);

            WindowsNative.Win32FindData findResult;
            using (var findHandle = WindowsNative.FindFirstFileW(path.TrimEnd('\\'), out findResult))
            {
                // Any error is interpreted as a file not found. This matches the managed Directory.Exists and File.Exists behavior
                if (findHandle.IsInvalid)
                {
                    return(false);
                }

                if (fileArtifactType == FileArtifactType.FileOrDirectory)
                {
                    return(true);
                }

                var isDirectory = (findResult.DwFileAttributes & FileAttributes.Directory) != 0;

                return(!(fileArtifactType == FileArtifactType.Directory ^ isDirectory));
            }
        }
Example #10
0
        private static WindowsNative.EnumerateDirectoryResult CustomEnumerateDirectoryEntries(
            string directoryPath,
            FileArtifactType fileArtifactType,
            string pattern,
            SearchOption searchOption,
            ICollection <string> result)
        {
            var searchDirectoryPath = Path.Combine(directoryPath.TrimEnd('\\'), "*");

            WindowsNative.Win32FindData findResult;
            using (var findHandle = WindowsNative.FindFirstFileW(searchDirectoryPath, out findResult))
            {
                if (findHandle.IsInvalid)
                {
                    int hr = Marshal.GetLastWin32Error();
                    Debug.Assert(hr != WindowsNative.ErrorFileNotFound);

                    WindowsNative.EnumerateDirectoryStatus findHandleOpenStatus;
                    switch (hr)
                    {
                    case WindowsNative.ErrorFileNotFound:
                        findHandleOpenStatus = WindowsNative.EnumerateDirectoryStatus.SearchDirectoryNotFound;
                        break;

                    case WindowsNative.ErrorPathNotFound:
                        findHandleOpenStatus = WindowsNative.EnumerateDirectoryStatus.SearchDirectoryNotFound;
                        break;

                    case WindowsNative.ErrorDirectory:
                        findHandleOpenStatus = WindowsNative.EnumerateDirectoryStatus.CannotEnumerateFile;
                        break;

                    case WindowsNative.ErrorAccessDenied:
                        findHandleOpenStatus = WindowsNative.EnumerateDirectoryStatus.AccessDenied;
                        break;

                    default:
                        findHandleOpenStatus = WindowsNative.EnumerateDirectoryStatus.UnknownError;
                        break;
                    }

                    return(new WindowsNative.EnumerateDirectoryResult(directoryPath, findHandleOpenStatus, hr));
                }

                while (true)
                {
                    var isDirectory = (findResult.DwFileAttributes & FileAttributes.Directory) != 0;

                    // There will be entries for the current and parent directories. Ignore those.
                    if (!isDirectory || (findResult.CFileName != "." && findResult.CFileName != ".."))
                    {
                        // Make sure pattern and directory/file filters are honored
                        // We special case the "*" pattern since it is the default when no pattern is specified
                        // so we avoid calling the matching function
                        if (pattern == "*" ||
                            WindowsNative.PathMatchSpecExW(findResult.CFileName, pattern, WindowsNative.DwFlags.PmsfNormal) ==
                            WindowsNative.ErrorSuccess)
                        {
                            if (fileArtifactType == FileArtifactType.FileOrDirectory ||
                                !(fileArtifactType == FileArtifactType.Directory ^ isDirectory))
                            {
                                result.Add(Path.Combine(directoryPath, findResult.CFileName));
                            }
                        }

                        // Recursively go into subfolders if specified
                        if (searchOption == SearchOption.AllDirectories && isDirectory)
                        {
                            var recurs = CustomEnumerateDirectoryEntries(
                                Path.Combine(directoryPath, findResult.CFileName),
                                fileArtifactType,
                                pattern,
                                searchOption,
                                result);

                            if (!recurs.Succeeded)
                            {
                                return(recurs);
                            }
                        }
                    }

                    if (!WindowsNative.FindNextFileW(findHandle, out findResult))
                    {
                        int hr = Marshal.GetLastWin32Error();
                        if (hr == WindowsNative.ErrorNoMoreFiles)
                        {
                            // Graceful completion of enumeration.
                            return(new WindowsNative.EnumerateDirectoryResult(
                                       directoryPath,
                                       WindowsNative.EnumerateDirectoryStatus.Success,
                                       hr));
                        }

                        Debug.Assert(hr != WindowsNative.ErrorSuccess);
                        return(new WindowsNative.EnumerateDirectoryResult(
                                   directoryPath,
                                   WindowsNative.EnumerateDirectoryStatus.UnknownError,
                                   hr));
                    }
                }
            }
        }
Example #11
0
 private static string GetCacheKey(string id, string version, FileArtifactType type)
 {
     return($"{id}/{version}/{type}".ToLowerInvariant());
 }