AllowedQueryOptions = (AllowedQueryOptions)(AllowedQueryOptions.Supported - AllowedQueryOptions.Skip - AllowedQueryOptions.Count))] // NOTE: This just tells OData to return well-formed errors when features not supported by DocumentDB are attempted on the OData endpoint
        public async Task <SingleResult <HouseDto> > Get([FromODataUri] string key, ODataQueryOptions <HouseDto> odataOptions)
        {
            // get a standard IQueryable from the standard DocumentDB client
            await EnsureClientIsConnected();

            // apply the clause for getting the document by key
            // because our collection contains multiple document types, filter on that as well
            // execute the query safely with continuation and retries - you could also wrap the IQueryable using InterceptQuery and simply call ".ToArray()"
            var results = await DocumentDbExtensions.ExecuteQueryWithContinuationAndRetryAsync(
                client.CreateDocumentQuery <HouseDto>(collectionLink)
                .Where(x => x.DocumentType == DocumentType.House)
                .Where(x => x.Id == key));

            if (results.Count == 0)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            // return "not modified" if supplied etag matches
            if (odataOptions.IfNoneMatch != null)
            {
                if (results.Single().ETag == (string)odataOptions.IfNoneMatch["ETag"])
                {
                    throw new HttpResponseException(HttpStatusCode.NotModified);
                }
            }

            return(SingleResult.Create(results.AsQueryable()));
        }
Esempio n. 2
0
        public async Task <Tuple <Guid, bool> > IsAuthorized(string userName, string userPassword)
        {
            var userTokens             = userName.Split("@".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            Tuple <Guid, bool> dataObj = new Tuple <Guid, bool>(Guid.Empty, false);
            List <dynamic>     coll    = await _dataAccess.QueryAsync(@"Select * from 
                                                                accounts a 
                                                                where a.userName = @userName   
                                                                AND a.userEmail = @userEmail
                                                                ",
                                                                      DocumentDbExtensions.ToSqlParams(new
            {
                userName  = userTokens[0],
                userEmail = string.Format("{0}@{1}", userTokens[0], userTokens[1])
            }));

            var account = coll.FirstOrDefault();

            if (account != null)
            {
                var validPass = PasswordHash.ValidatePassword(userPassword, account.userPassword.ToString());
                dataObj = new Tuple <Guid, bool>(account.id, validPass);
            }

            return(dataObj);
        }
             AllowedQueryOptions = (AllowedQueryOptions)(AllowedQueryOptions.Supported - AllowedQueryOptions.Skip - AllowedQueryOptions.Count))] // NOTE: This just tells OData to return well-formed errors when features not supported by DocumentDB are attempted on the OData endpoint
        public async Task <IQueryable <HouseDto> > Get()
        {
            // get a standard IQueryable from the standard DocumentDB client
            await EnsureClientIsConnected();

            // configure the wrapped queryable to translate exceptions on enumeration (normally you would log this as well)
            EnumerationExceptionHandler enumerationExceptionHandler = (FeedResponseContext context, Exception exception) =>
            {
                throw new HttpResponseException(new HttpResponseMessage()
                {
                    StatusCode = HttpStatusCode.InternalServerError,
                    Content    = new StringContent(exception.ToString()),
                });
            };
            // this IQueryable will now work with DateTime/Offset types, follow result paging links, retry on error, and swallow any exceptions
            var safeWrappedQueryable = DocumentDbExtensions.InterceptQuery(
                client.CreateDocumentQuery <HouseDto>(collectionLink),
                enumerationExceptionHandler: enumerationExceptionHandler);

            // because our collection contains multiple document types, filter on that first
            safeWrappedQueryable = safeWrappedQueryable
                                   .Where(x => x.DocumentType == DocumentType.House);

            // OData will apply the query from the URL to the returned IQueryable
            return(safeWrappedQueryable);
        }
Esempio n. 4
0
             AllowedQueryOptions = (AllowedQueryOptions)(AllowedQueryOptions.Supported - AllowedQueryOptions.Skip - AllowedQueryOptions.Count))] // NOTE: This just tells OData to return well-formed errors when features not supported by DocumentDB are attempted on the OData endpoint
        public async Task <IQueryable <HouseHistoryDto> > Get([FromODataUri] string key)
        {
            // get a standard IQueryable from the standard DocumentDB client
            await EnsureClientIsConnected();

            // configure the wrapped queryable to translate exceptions on enumeration (normally you would log this as well)
            EnumerationExceptionHandler enumerationExceptionHandler = (Exception exception) =>
            {
                throw new HttpResponseException(new HttpResponseMessage()
                {
                    StatusCode = HttpStatusCode.InternalServerError,
                    Content    = new StringContent(exception.ToString()),
                });
            };
            // this IQueryable will now work with DateTime/Offset types, follow result paging links, retry on error, and swallow any exceptions
            var safeWrappedQueryable = DocumentDbExtensions.InterceptQuery(
                client.CreateDocumentQuery <HouseHistoryDto>(collectionLink),
                enumerationExceptionHandler);

            // apply the clause for getting the document by key
            // because our collection contains multiple document types, filter on that as well
            // execute the query safely with continuation and retries - you could also wrap the IQueryable using InterceptQuery and simply call ".ToArray()"
            safeWrappedQueryable = safeWrappedQueryable
                                   .Where(x => x.DocumentType == DocumentType.HouseHistory)
                                   .Where(x => x.ModifiedId == key);

            // todo: bug bug
            //if (results.Count == 0)
            //    throw new HttpResponseException(HttpStatusCode.NotFound);

            return(safeWrappedQueryable);
        }
        public void CanRetrieveNullCollectionProperty()
        {
            /* todo: The service can return a null collection, but apparently the client needs that feature added as well
             *       I suppose it's not that big a deal though, since you have control over the object at this point
             * var create = House.CreateHouse(Guid.NewGuid().ToString("D"));
             * create.TestWindows = null;
             * client.AddToHouses(create);
             * client.SaveChanges();
             * client.Detach(create);*/

            // because of the above bug, we must create the document directly, but this is the more important path anyway
            var connectionString   = ConfigurationManager.AppSettings["connectionString"];
            var databaseName       = ConfigurationManager.AppSettings["databaseName"];
            var collectionName     = ConfigurationManager.AppSettings["collectionName"];
            var collectionLink     = string.Format("dbs/{0}/colls/{1}", databaseName, collectionName);
            var documentLinkFormat = collectionLink + "/docs/{0}";
            var task = DocumentDB.GetDocumentClient(connectionString, databaseName, collectionName);

            task.Wait();
            var docClient = task.Result;

            // make test document
            HouseDocument houseDocument = new HouseDocument();

            houseDocument.Id          = Guid.NewGuid().ToString("D");
            houseDocument.TestWindows = null;

            // insert it directly to DocumentDB
            DocumentDbExtensions.ExecuteResultWithRetry(() =>
                                                        docClient.UpsertDocumentAsync(collectionLink, houseDocument));

            // retrieve it directly from DocumentDB
            var retrievedDocument = DocumentDbExtensions.ExecuteQueryWithContinuationAndRetry(
                docClient.CreateDocumentQuery <HouseDocument>(collectionLink)
                .Where(x => x.Id == houseDocument.Id))
                                    .Single();

            // is the test set up properly?
            Assert.IsNull(retrievedDocument.TestWindows);

            // finally, test retrieval through OData
            var house = odataClient.Houses.ByKey(houseDocument.Id).GetValue();

            Assert.IsNotNull(house);
            // note: house.TestWindows will actually be deserialized as an empty collection here, but the test is
            //       that the OData service didn't throw an error when encountering that in the source document
        }
        public async Task <IHttpActionResult> Put([FromODataUri] string key, HouseDto houseDto, ODataQueryOptions <HouseDto> odataOptions)
        {
            // get a standard DocumentDB client
            await EnsureClientIsConnected();

            // convert the DTO into the Document type
            var houseDoc = JsonConvert.DeserializeObject <HouseDocument>(JsonConvert.SerializeObject(houseDto));

            // DocumentDB Triggers are "opt-in", so opt-in
            var docdbOptions = new RequestOptions();

            docdbOptions.PreTriggerInclude = new List <string> {
                "MaintainHistoryAndTimestamps"
            };

            // DocumentDB ETag checks are "opt-in", so opt-in if requested
            if (odataOptions.IfMatch != null)
            {
                docdbOptions.AccessCondition = new AccessCondition()
                {
                    Type      = AccessConditionType.IfMatch,
                    Condition = (string)odataOptions.IfMatch["ETag"],
                };
            }

            // execute the replace document call safely with retries
            string documentLink = string.Format(documentLinkFormat, key);
            ResourceResponse <Document> replaced = null;

            try
            {
                replaced = await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                                  client.ReplaceDocumentAsync(documentLink, houseDoc, docdbOptions));
            }
            catch (DocumentDbNonRetriableResponse e)
            {
                return(StatusCode((e.InnerException as DocumentClientException).StatusCode ?? HttpStatusCode.InternalServerError));
            }

            // get the resulting document (it is returned above, but in non-typed form - probably should really be using AutoMapper or something like that here)
            var result = await DocumentDbExtensions.ExecuteQueryWithContinuationAndRetryAsync(
                client.CreateDocumentQuery <HouseDto>(collectionLink)
                .Where(x => x.DocumentType == DocumentType.House)
                .Where(x => x.Id == replaced.Resource.Id));

            return(Updated(result.Single()));
        }
Esempio n. 7
0
        public static async Task <DocumentClient> GetDocumentClient(string connectionString, string databaseName, string collectionName)
        {
            string uriString = null;
            string authKey   = null;

            string[] pieces = connectionString.Split(";".ToArray(), StringSplitOptions.RemoveEmptyEntries);
            foreach (string piece in pieces)
            {
                string[] subpieces = piece.Split("=".ToArray());
                if (subpieces[0] == "AccountEndpoint")
                {
                    uriString = subpieces[1];
                }
                if (subpieces[0] == "AccountKey")
                {
                    authKey = subpieces[1];
                }
            }

            Uri serviceEndpoint = new Uri(uriString);

            var client = new DocumentClient(serviceEndpoint,
                                            authKey,
                                            new ConnectionPolicy
            {
                ConnectionMode = ConnectionMode.Direct,
                //ConnectionProtocol = Protocol.Tcp,
                RetryOptions = new RetryOptions()
                {
                    MaxRetryAttemptsOnThrottledRequests = 0
                }
            },
                                            ConsistencyLevel.Session);

            string collectionLink = string.Format("dbs/{0}/colls/{1}", databaseName, collectionName);

            var database = await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                                  GetOrCreateDatabase(client, databaseName));

            var collection = await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                                    GetOrCreateCollection(client, database, collectionName));

            var response = await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                                  client.UpsertStoredProcedureAsync(collectionLink, updateAndRecalculateDetailedStatusStoredProcedure));

            return(client);
        }
Esempio n. 8
0
        public async Task <IHttpActionResult> Delete([FromODataUri] string key)
        {
            // get a standard DocumentDB client
            await EnsureClientIsConnected();

            // execute the delete document call safely with retries
            string documentLink = string.Format(documentLinkFormat, key);

            try
            {
                await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                       client.DeleteDocumentAsync(documentLink));
            }
            catch (DocumentDbNonRetriableResponse e)
            {
                return(StatusCode((e.InnerException as DocumentClientException).StatusCode ?? HttpStatusCode.InternalServerError));
            }

            return(Ok());
        }
Esempio n. 9
0
             AllowedQueryOptions = (AllowedQueryOptions)(AllowedQueryOptions.Supported - AllowedQueryOptions.Skip - AllowedQueryOptions.Count))] // NOTE: This just tells OData to return well-formed errors when features not supported by DocumentDB are attempted on the OData endpoint
        public async Task <IEnumerable <HouseHistoryDto> > Get([FromODataUri] string key)
        {
            // get a standard IQueryable from the standard DocumentDB client
            await EnsureClientIsConnected();

            // apply the clause for getting the document by key
            // because our collection contains multiple document types, filter on that as well
            // execute the query safely with continuation and retries - you could also wrap the IQueryable using InterceptQuery and simply call ".ToArray()"
            var results = await DocumentDbExtensions.ExecuteQueryWithContinuationAndRetryAsync(
                client.CreateDocumentQuery <HouseHistoryDto>(collectionLink)
                .Where(x => x.DocumentType == DocumentType.HouseHistory)
                .Where(x => x.ModifiedId == key));

            if (results.Count == 0)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            return(results.AsQueryable());
        }
Esempio n. 10
0
        public static async Task <DocumentClient> GetDocumentClient(string connectionString, string databaseName, string collectionName)
        {
            string uriString = null;
            string authKey   = null;

            string[] pieces = connectionString.Split(";".ToArray(), StringSplitOptions.RemoveEmptyEntries);
            foreach (string piece in pieces)
            {
                string[] subpieces = piece.Split("=".ToArray());
                if (subpieces[0] == "AccountEndpoint")
                {
                    uriString = subpieces[1];
                }
                if (subpieces[0] == "AccountKey")
                {
                    authKey = subpieces[1] + "==";
                }
            }

            Uri serviceEndpoint = new Uri(uriString);

            var client = new DocumentClient(serviceEndpoint,
                                            authKey,
                                            new ConnectionPolicy
            {
                ConnectionMode     = ConnectionMode.Gateway,
                ConnectionProtocol = Protocol.Tcp
            },
                                            ConsistencyLevel.Session);

            string collectionLink = string.Format("dbs/{0}/colls/{1}", databaseName, collectionName);

            var database = await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                                  GetOrCreateDatabase(client, databaseName));

            var collection = await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                                    GetOrCreateCollection(client, database, collectionName));

            return(client);
        }
        public async Task <IHttpActionResult> Delete([FromODataUri] string key, ODataQueryOptions <HouseDto> odataOptions)
        {
            // get a standard DocumentDB client
            await EnsureClientIsConnected();

            // DocumentDB Triggers are "opt-in", so opt-in
            var docdbOptions = new RequestOptions();

            docdbOptions.PreTriggerInclude = new List <string> {
                "MaintainHistoryAndTimestamps"
            };

            // DocumentDB ETag checks are "opt-in", so opt-in if requested
            if (odataOptions.IfMatch != null)
            {
                docdbOptions.AccessCondition = new AccessCondition()
                {
                    Type      = AccessConditionType.IfMatch,
                    Condition = (string)odataOptions.IfMatch["ETag"],
                };
            }

            // execute the delete document call safely with retries
            string documentLink = string.Format(documentLinkFormat, key);

            try
            {
                await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                       client.DeleteDocumentAsync(documentLink, docdbOptions));
            }
            catch (DocumentDbNonRetriableResponse e)
            {
                return(StatusCode((e.InnerException as DocumentClientException).StatusCode ?? HttpStatusCode.InternalServerError));
            }

            return(Ok());
        }
        public async Task <IHttpActionResult> Post(HouseDto houseDto)
        {
            // get a standard DocumentDB client
            await EnsureClientIsConnected();

            // convert the DTO into the Document type
            var houseDoc = JsonConvert.DeserializeObject <HouseDocument>(JsonConvert.SerializeObject(houseDto));

            // DocumentDB Triggers are "opt-in", so opt-in
            var docdbOptions = new RequestOptions();

            docdbOptions.PreTriggerInclude = new List <string> {
                "MaintainHistoryAndTimestamps"
            };

            // execute the create document call safely with retries
            ResourceResponse <Document> created = null;

            try
            {
                created = await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                                 client.CreateDocumentAsync(collectionLink, houseDoc, docdbOptions));
            }
            catch (DocumentDbNonRetriableResponse e)
            {
                return(StatusCode((e.InnerException as DocumentClientException).StatusCode ?? HttpStatusCode.InternalServerError));
            }

            // get the resulting document (it is returned above, but in non-typed form - probably should really be using AutoMapper or something like that here)
            var result = await DocumentDbExtensions.ExecuteQueryWithContinuationAndRetryAsync(
                client.CreateDocumentQuery <HouseDto>(collectionLink)
                .Where(x => x.DocumentType == DocumentType.House)
                .Where(x => x.Id == created.Resource.Id));

            return(Created(result.Single()));
        }
        public async Task <IHttpActionResult> Patch([FromODataUri] string key, Delta <HouseDto> dtoUpdate, ODataQueryOptions <HouseDto> odataOptions)
        {
            // get a standard IQueryable from the standard DocumentDB client
            await EnsureClientIsConnected();

            // apply the clause for getting the document by key
            // because our collection contains multiple document types, filter on that as well
            // execute the query safely with continuation and retries
            var results = await DocumentDbExtensions.ExecuteQueryWithContinuationAndRetryAsync(
                client.CreateDocumentQuery <HouseDto>(collectionLink)
                .Where(x => x.DocumentType == DocumentType.House)
                .Where(x => x.Id == key));

            if (results.Count == 0)
            {
                return(NotFound());
            }

            // patch the original with the delta
            var original = results.Single();

            dtoUpdate.Patch(original);

            // convert the updated DTO into the Document type
            var updatedDoc = JsonConvert.DeserializeObject <HouseDocument>(JsonConvert.SerializeObject(original));

            // DocumentDB Triggers are "opt-in", so opt-in
            var docdbOptions = new RequestOptions();

            docdbOptions.PreTriggerInclude = new List <string> {
                "MaintainHistoryAndTimestamps"
            };

            // DocumentDB ETag checks are "opt-in", so opt-in if requested
            if (odataOptions.IfMatch != null)
            {
                docdbOptions.AccessCondition = new AccessCondition()
                {
                    Type      = AccessConditionType.IfMatch,
                    Condition = (string)odataOptions.IfMatch["ETag"],
                };
            }

            ResourceResponse <Document> updated = null;

            try
            {
                // execute the replace document call safely with retries
                string documentLink = string.Format(documentLinkFormat, key);
                updated = await DocumentDbExtensions.ExecuteResultWithRetryAsync(() =>
                                                                                 client.ReplaceDocumentAsync(documentLink, updatedDoc, docdbOptions));
            }
            catch (DocumentDbNonRetriableResponse e)
            {
                return(StatusCode((e.InnerException as DocumentClientException).StatusCode ?? HttpStatusCode.InternalServerError));
            }

            // get the resulting document (it is returned above, but in non-typed form - probably should really be using AutoMapper or something like that here)
            var result = await DocumentDbExtensions.ExecuteQueryWithContinuationAndRetryAsync(
                client.CreateDocumentQuery <HouseDto>(collectionLink)
                .Where(x => x.DocumentType == DocumentType.House)
                .Where(x => x.Id == updated.Resource.Id));

            return(Updated(result.Single()));
        }