private async Task <(Stream stream, bool fromStorage)> GetContextStreamAsync( TournamentBracketRequest request, TournamentBracketContent content, CompositeDisposable disposables, CancellationToken cancellationToken ) { // Validate parameters. if (request == null) { throw new ArgumentNullException(nameof(request)); } if (disposables == null) { throw new ArgumentNullException(nameof(disposables)); } // The algorithm is this: // If there is content, use that. // If there is not content, then try to get from challonge. // If there is no content, try to get from persistence // If it can't be retrieved, throw. // Check the request for content. if (content != null && content.MediaType.Type == "text" && content.MediaType.SubTypeWithoutSuffix == "html" ) { // Return a streamreader. return(content.Content, false); } // Try and get it from challonge. Stream stream = await GetStreamFromChallongeAsync( request, disposables, cancellationToken ) .ConfigureAwait(false); // If not null, return. if (stream != null) { return(stream, false); } // Get the reader from storage. stream = await GetStreamFromStorageAsync( request, disposables, cancellationToken ).ConfigureAwait(false); // If there is no stream at this point, throw. if (stream == null) { throw new InvalidOperationException( $"Could not retrieve content for URL \"{request.Url}\" from user-sumbmitted content, Challonge, or storage."); } // Return the reader. return(stream, true); }
public async Task <TournamentBracketResponse> GetTournamentBracketAsync( TournamentBracketRequest request, TournamentBracketContent content, CancellationToken cancellationToken) { // Validate parameters. // Note: The content parameter can be null. if (request == null) { throw new ArgumentNullException(nameof(request)); } // If the url is null, throw. if (request.Url == null) { throw new InvalidOperationException( $"The {nameof(request.Url)} property on the {nameof(request)} parameter cannot be null."); } // The disposables. using var disposables = new CompositeDisposable(); // The page content. TournamentBracketPage page; // Get the content stream. (Stream rawStream, bool fromStorage) = await GetContextStreamAsync( request, content, disposables, cancellationToken ).ConfigureAwait(false); // Add the raw stream to the disposables. disposables.Add(rawStream); // If there is no stream, copy it. if (!(rawStream is MemoryStream stream)) { // Create a new one. stream = new MemoryStream(); // Copy. await rawStream.CopyToAsync(stream, 4096, cancellationToken) .ConfigureAwait(false); } // Add the stream to the disposables. disposables.Add(stream); // Set the position of the stream back. stream.Position = 0; // Create a reader. using TextReader reader = new StreamReader(stream); // Parse. page = await _parser.ParseTournamentBracketPageAsync(reader, cancellationToken) .ConfigureAwait(false); // Everything was successful at this point, reset the stream and persist. stream.Position = 0; // Persist if this wasn't from storage to begin with. if (!fromStorage) { await PersistContentAsync(request, stream, cancellationToken) .ConfigureAwait(false); } // Return. return(new TournamentBracketResponse { Request = request, PageContent = page.PageContent, Bracket = page.Bracket, TargetingKeyValues = page.TargetingKeyValues, MetaList = page.MetaList }); }