/// <summary>
 /// Assertion that the provided string is a valid <see cref="Uri"/> to connect to
 /// a data-source with the current implementation of the <see cref="IDal"/>.
 /// </summary>
 /// <param name="uri">
 /// a string representing a <see cref="Uri"/>.
 /// </param>
 /// <returns>
 /// true when valid, false when invalid.
 /// </returns>
 /// <remarks>
 /// Only HTTP and HTTPS are valid.
 /// </remarks>
 public override bool IsValidUri(string uri)
 {
     try
     {
         var validUriAssertion = new Uri(uri);
         UriExtensions.AssertUriIsHttpOrHttpsSchema(validUriAssertion);
         return(true);
     }
     catch (Exception)
     {
         return(false);
     }
 }
        /// <summary>
        /// Opens a connection to a data source <see cref="Uri"/> speci1fied by the provided <see cref="Credentials"/>
        /// </summary>
        /// <param name="credentials">
        /// The <see cref="Dal.Credentials"/> that are used to connect to the data source such as username, password and <see cref="Uri"/>
        /// </param>
        /// <param name="cancellationToken">
        /// The cancellation Token.
        /// </param>
        /// <returns>
        /// The <see cref="IEnumerable{T}"/> that the services return when connecting to the <see cref="CDP4Common.SiteDirectoryData.SiteDirectory"/>.
        /// </returns>
        public override async Task <IEnumerable <Thing> > Open(Credentials credentials, CancellationToken cancellationToken)
        {
            if (credentials == null)
            {
                throw new ArgumentNullException(nameof(credentials), $"The {nameof(credentials)} may not be null");
            }

            if (credentials.Uri == null)
            {
                throw new ArgumentNullException(nameof(credentials.Uri), $"The Credentials URI may not be null");
            }

            UriExtensions.AssertUriIsHttpOrHttpsSchema(credentials.Uri);

            var queryAttributes = new QueryAttributes
            {
                Extent = ExtentQueryAttribute.deep,
                IncludeReferenceData = false
            };

            var resourcePath = $"SiteDirectory{queryAttributes}";

            var openToken = CDP4Common.Helpers.TokenGenerator.GenerateRandomToken();

            this.httpClient = this.CreateHttpClient(credentials);

            var watch = Stopwatch.StartNew();

            var uriBuilder = this.GetUriBuilder(credentials.Uri, ref resourcePath);

            Logger.Debug("Resource Path {0}: {1}", openToken, resourcePath);
            Logger.Debug("CDP4Services Open {0}: {1}", openToken, uriBuilder);

            var requestsw = Stopwatch.StartNew();

            var requestMessage = new HttpRequestMessage(HttpMethod.Get, resourcePath);

            requestMessage.Headers.Add(Headers.CDPToken, openToken);

            using (var httpResponseMessage = await this.httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken: cancellationToken))
            {
                Logger.Info("CDP4 Services responded in {0} [ms] to Open {1}", requestsw.ElapsedMilliseconds, openToken);
                requestsw.Stop();

                if (httpResponseMessage.StatusCode != HttpStatusCode.OK)
                {
                    var msg = $"The data-source replied with code {httpResponseMessage.StatusCode}: {httpResponseMessage.ReasonPhrase}";
                    Logger.Error(msg);
                    throw new DalReadException(msg);
                }

                watch.Stop();
                Logger.Info("CDP4Services Open {0}: {1} completed in {2} [ms]", openToken, uriBuilder, watch.ElapsedMilliseconds);

                this.ProcessHeaders(httpResponseMessage);

                watch = Stopwatch.StartNew();

                using (var resultStream = await httpResponseMessage.Content.ReadAsStreamAsync())
                {
                    var returned = this.Serializer.Deserialize(resultStream);

                    watch.Stop();
                    Logger.Info("JSON Deserializer completed in {0} [ms]", watch.ElapsedMilliseconds);

                    var returnedPerson = returned.OfType <CDP4Common.DTO.Person>().SingleOrDefault(x => x.ShortName == credentials.UserName);
                    if (returnedPerson == null)
                    {
                        throw new InvalidOperationException("User not found.");
                    }

                    this.Credentials = credentials;

                    return(returned);
                }
            }
        }