Trying to get object from CosmosDb via MVC app via client.CreateDocumentQuery - c#

First attempt at accessing Cosmos db.
All this is in a DAL project layer of the solution (n-tier).
EndPointURI and PrimaryKey and DBName are setup by the class constructor.
I execute the client.CreateDocumentQuery
But not seeing the expected cast to the out param of the function i.e. "user".
I see the generated query string only.
I'm basing my code on this
public void GetUserByEmail(string email, out IQueryable<Shared.User> user)
{
FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1 };
user = null;
try
{
user = this.client.CreateDocumentQuery<Shared.User>(
UriFactory.CreateDocumentCollectionUri(DBName, "Users"), queryOptions)
.Where(f => f.Email == email);
}
catch (Exception ex)
{
}
}
If trying ReadDocumentAsync, I get the following

Try this, you need to return the first item
var response = this.client.CreateDocumentQuery<eUser>( UriFactory.CreateDocumentCollectionUri("demo", "Users"), queryOptions)
.Where(f => f.Email == email).AsEnumerable().ToArray().FirstOrDefault();

Related

How to test a controller POST method which returns no data in response content in .NET Core 3.1?

i am new to integration tests. I have a controller method which adds a user to the database, as shown below:
[HttpPost]
public async Task<IActionResult> CreateUserAsync([FromBody] CreateUserRequest request)
{
try
{
var command = new CreateUserCommand
{
Login = request.Login,
Password = request.Password,
FirstName = request.FirstName,
LastName = request.LastName,
MailAddress = request.MailAddress,
TokenOwnerInformation = User
};
await CommandBus.SendAsync(command);
return Ok();
}
catch (Exception e)
{
await HandleExceptionAsync(e);
return StatusCode(StatusCodes.Status500InternalServerError,
new {e.Message});
}
}
As you have noticed my method returns no information about the user which has been added to the database - it informs about the results of handling a certain request using the status codes. I have written an integration test to check is it working properly:
[Fact]
public async Task ShouldCreateUser()
{
// Arrange
var createUserRequest = new CreateUserRequest
{
Login = "testowyLogin",
Password = "testoweHaslo",
FirstName = "Aleksander",
LastName = "Kowalski",
MailAddress = "akowalski#onet.poczta.pl"
};
var serializedCreateUserRequest = SerializeObject(createUserRequest);
// Act
var response = await HttpClient.PostAsync(ApiRoutes.CreateUserAsyncRoute,
serializedCreateUserRequest);
// Assert
response
.StatusCode
.Should()
.Be(HttpStatusCode.OK);
}
I am not sure is it enough to assert just a status code of response returned from the server. I am confused because, i don't know, shall i attach to assert section code, which would get all the users and check does it contain created user for example. I don't even have any id of such a user because my application finds a new id for the user while adding him/her to the database. I also have no idea how to test methods like that:
[HttpGet("{userId:int}")]
public async Task<IActionResult> GetUserAsync([FromRoute] int userId)
{
try
{
var query = new GetUserQuery
{
UserId = userId,
TokenOwnerInformation = User
};
var user = await QueryBus
.SendAsync<GetUserQuery, UserDto>(query);
var result = user is null
? (IActionResult) NotFound(new
{
Message = (string) _stringLocalizer[UserConstants.UserNotFoundMessageKey]
})
: Ok(user);
return result;
}
catch (Exception e)
{
await HandleExceptionAsync(e);
return StatusCode(StatusCodes.Status500InternalServerError,
new {e.Message});
}
}
I believe i should somehow create a user firstly in Arrange section, get it's id and then use it in Act section with the GetUserAsync method called with the request sent by HttpClient. Again the same problem - no information about user is returned, after creation (by the way - it is not returned, because of my CQRS design in whole application - commands return no information). Could you please explain me how to write such a tests properly? Have i missed anything? Thanks for any help.
This is how I do it:
var response = (CreatedResult) await _controller.Post(createUserRequest);
response.StatusCode.Should().Be(StatusCodes.Status201Created);
The second line above is not necessary, just there for illustration.
Also, your response it's better when you return a 201 (Created) instead of the 200(OK) on Post verbs, like:
return Created($"api/users/{user.id}", user);
To test NotFound's:
var result = (NotFoundObjectResult) await _controller.Get(id);
result.StatusCode.Should().Be(StatusCodes.Status404NotFound);
The NotFoundObjectResult assumes you are returning something. If you are just responding with a 404 and no explanation, replace NotFoundObjectResult with a NotFoundResult.
And finally InternalServerErrors:
var result = (ObjectResult) await _controller.Get(id);
result.StatusCode.Should().Be(StatusCodes.Status500InternalServerError);
You can use integrationFixture for that using this NuGet package. This is an AutoFixture alternative for integration tests.
The documented examples use Get calls but you can do other calls too. Logically, you should test for the status code (OkObjectResult means 200) value and the response (which could be an empty string, that is no problem at all).
Here is the documented example for a normal Get call.
[Fact]
public async Task GetTest()
{
// arrange
using (var fixture = new Fixture<Startup>())
{
using (var mockServer = fixture.FreezeServer("Google"))
{
SetupStableServer(mockServer, "Response");
var controller = fixture.Create<SearchEngineController>();
// act
var response = await controller.GetNumberOfCharacters("Hoi");
// assert
var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
Assert.Contains("Hoi", request.RawQuery);
Assert.Equal(8, ((OkObjectResult)response.Result).Value);
}
}
}
private void SetupStableServer(FluentMockServer fluentMockServer, string response)
{
fluentMockServer.Given(Request.Create().UsingGet())
.RespondWith(Response.Create().WithBody(response, encoding: Encoding.UTF8)
.WithStatusCode(HttpStatusCode.OK));
}
In the example above, the controller is resolved using the DI described in your Startup class.
You can also do an actual REST call using using Refit. The application is self hosted inside your test.
using (var fixture = new RefitFixture<Startup, ISearchEngine>(RestService.For<ISearchEngine>))
{
using (var mockServer = fixture.FreezeServer("Google"))
{
SetupStableServer(mockServer, "Response");
var refitClient = fixture.GetRefitClient();
var response = await refitClient.GetNumberOfCharacters("Hoi");
await response.EnsureSuccessStatusCodeAsync();
var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
Assert.Contains("Hoi", request.RawQuery);
}
}

Inserting document into CosmosDB with C#

I'm trying to insert a new document to a cosmos database running locally with SQLAPI in the Cosmos Emulator. Selecting documents works fine but when I try to insert with below code I get the following error:
Response status code does not indicate success: NotFound (404); Substatus: 1003; ActivityId: 10e20d81-559a-47b3-8eef-021ce4138279; Reason: (Message: {"Errors":["Owner resource does not exist"]}
Code:
async Task<Product> IProductRepository.Add(Product product)
{
var container = cosmosClient.GetContainer("Invertory", "Products");
product.Id = Guid.NewGuid().ToString();
product.Category = new Category() { Name = "test" };
var x = await container.UpsertItemAsync<Product>(product);
return product;
}
It seems that the recourse (database) I try to reach not exists, but when I do a query with the same database and container and with the following code I get the expected results.
async Task<Product> IProductRepository.Get(string Id)
{
var container = cosmosClient.GetContainer("Inventory", "Products");
var iterator = (from p in container.GetItemLinqQueryable<Product>()
where p.Id.Equals(Id)
select p).ToFeedIterator();
return iterator.HasMoreResults ? (await iterator.ReadNextAsync()).First() : null;
}
What am I missing here?
There is a typo in function "IProductRepository.Add". Invertory should be Inventory.

How to retain data gained from multiple requests in EF Core?

I have asp.net core web api with React client. I'm adding data through my user interface created in React. In my api, Db context is added as scoped service, and each time my request finishes and new one is started, all my data from previous request is lost.
This is how my Configure services looks like:
services.AddDbContext<TicketingContext>(o=>o.UseLazyLoadingProxies().UseSqlServer(connectionString));
Controller method for posting data looks like this:
[HttpPost("{id}/tickets")]
public IActionResult CreateNewTicket(string id,
[FromBody] TicketForCreationDto ticketForCreation)
{
if (ticketForCreation == null)
{
return BadRequest();
}
var ticketEntity = _mapper.Map<Ticket>(ticketForCreation);
_ticketRepository.AddNewTicket(ticketEntity);
_ticketRepository.AddTicketToClient(id, ticketEntity);
if (!_ticketRepository.Save())
{
throw new Exception("Creating ticket failed on save");
}
var ticketToReturn = _mapper.Map<TicketDto>(ticketEntity);
return CreatedAtRoute("GetTicket", new {id=id, ticketId = ticketToReturn.Id }, ticketToReturn);
}
and methods in repository like this:
AddNewTicket:
public void AddNewTicket(Ticket ticket)
{
if (ticket.Id == Guid.Empty)
{
ticket.Id = Guid.NewGuid();
}
var dispatcher = AssignTicketToDispatcher(ticket);
if (dispatcher == null)
{
throw new Exception("There are no dispatchers matching this ticket");
}
dispatcher.UserTickets.Add(new UserTicket()
{
IdentityUser = dispatcher,
Ticket = ticket,
UserId = dispatcher.Id,
TicketId = ticket.Id
});
_context.Tickets.Add(ticket);
}
AddTicketToClient:
public void AddTicketToClient(string id, Ticket ticket)
{
var client = _identityUserRepository.GetClient(id);
if (client == null)
{
client = _context.Users.Where(u => u.UserName == "username").FirstOrDefault();
}
client.UserTickets.Add(new UserTicket()
{
IdentityUser = client,
Ticket = ticket,
UserId = client.Id,
TicketId = ticket.Id
});
}
Save:
public bool Save()
{
return (_context.SaveChanges() >= 0);
}
I want to be able to store data gained through multiple requests.
Does anyone have idea how to do that?
Use the database as it's the best method you have for persisting your data.
So When you do a request - at the end of the request, after your latest data is saved - query for the data from previous requests that you need and return it.
e.g. retrieve the last 5 requests saved newest first (where id is example for your primary key field):
var latestSaved = _context.UserTickets.OrderByDescending(x => x.id).Take(5);
Or amend to return all relevant data for e.g. active user by passing a user id stored client side.
Pass through any params you need to request the relevant data.
Use joins / includes set up in your entities. Whatever you need to do - make use of your entity relationships to get what you need from you database. Why try and replicate what it already does? :)

How to use SQL query with CosmosDB client in C# .NET Core 2.1 API app

I am trying to use an Azure CosmosDB (former DocumentDB) repository in an API I am creating (I'm quite new to C#). I have managed to get a result with all documents using the client.CreateDocumentQuery method without passing a SQL query to it. However, when I pass a SQL query, program execution hangs and the controller responds with 404. I've tried to add a try-catch around it but I get no exception or anything.
Using Microsoft.Azure.DocumentDB.Core Version 1.9.1.
I've tried many things, other methods and also LINQ, but got nothing to work. If you have a look at this sample method, could you give me an example on how to correctly query and get a result from the document collection? I would really appreciate it!UPDATE: I updated the SQL a bit. Have tried with hard coded parameter and have tested it in the data explorer where it works. Execution seems to freeze on *var feedResponse = await documentQuery.ExecuteNextAsync<JObject>();*
// Document repository
public async Task<IEnumerable<JObject>> GetDocsFromCollectionAsync(string someId)
{
DocumentClient client = new DocumentClient(new Uri(EndpointUrl), PrimaryKey);
IDocumentQuery<JObject> documentQuery;
var documentCollectionUri = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var queryOptions = new FeedOptions { MaxItemCount = -1 };
if (someId.IsNullOrEmpty())
{
// This works
documentQuery = client.CreateDocumentQuery<JObject>(documentCollectionUri,queryOptions)
.AsDocumentQuery();
}
else
{
var query = new SqlQuerySpec(
"SELECT * FROM c WHERE c.someId = #someId",
new SqlParameterCollection(new SqlParameter[] { new SqlParameter { Name = "#someId", Value = someId } }));
// This hangs during execution / returns 404 in the API controller
documentQuery = client.CreateDocumentQuery<JObject>(documentCollectionUri, query, queryOptions)
.AsDocumentQuery();
}
List<JObject> documents = new List<JObject>();
while (documentQuery.HasMoreResults)
{
var feedResponse = await documentQuery.ExecuteNextAsync<JObject>();
documents.AddRange(feedResponse);
}
return documents; // Return documents to API controller
}

Specification Testing with EF - The ObjectContext instance has been disposed

I have the following SpecFlow scenario:
[When(#"the registration is submitted")]
public void WhenTheRegistrationIsSubmitted()
{
//var controller = _kernel.Get<AccountController>();
var factory = new HockeyDbContextFactory();
var userRepository = new Repository<User>(factory);
var cryptoService = new CryptoService();
var roleRepository = new Repository<Role>(factory);
var playerService = new Mock<IPlayerService>();
var leagueService = new Mock<ILeagueService>();
var userService = new UserService(userRepository, cryptoService, roleRepository);
var controller = new AccountController(userService, playerService.Object, leagueService.Object);
controller.Register(_registerModel);
}
Which eventually calls the following method through my controller:
public void RegisterUser(User user)
{
var salt = _cryptoService.GenerateSalt();
var hasedPassword = _cryptoService.HashPassword(user.Password, salt);
user.PasswordSalt = salt;
user.Password = hasedPassword;
var defaultRole = _roleRepository.GetAll().Single(x => x.RoleName == "User");
user.Roles.Add(defaultRole);
Insert(user);
}
All of my database calls are fine until I get to this line:
var defaultRole = _roleRepository.GetAll().Single(x => x.RoleName == "User");
When I breakpoint on that line and inspect the call to GetAll(), I have context and I can view the query. The exception occurs on the call to Single(). Now, if I stick a .Include(x => x.Users) on the call to GetAll(), I'm fine. This tells me it has something to do with lazy-loading.
The error i get is: error: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
When RegisterUser is called from my web application, I'm fine. When RegisterUser is called from my specification test, it fails. Does anyone have some incite?
UPDATE:
To add a little more information, here is the controller action being called:
[HttpPost]
[AllowAnonymous]
public ActionResult Register(RegisterModel model)
{
if (!_userService.EmailIsUnique(model.EmailAddress))
ModelState.AddModelError("EmailAddress", "Email Address is already in use.");
if (!_userService.UserNameIsUnique(model.UserName))
ModelState.AddModelError("UserName", "User Name is already in use");
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
var user = Mapper.Map<User>(model);
_userService.RegisterUser(user);
FormsAuthentication.SetAuthCookie(model.UserName, false);
return View("RegisterSuccess");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
stepping through the code, I never make it to FormsAuthentication.SetAuthCookie(model.UserName, false);
I figured out what the issue was. I was seeding my test database with this step:
[BeforeFeature]
public static void BeforeFeature()
{
MappingConfig.RegisterMappings();
Database.SetInitializer(new TestDatabaseInitializer());
}
The context in my TestDatabaseInitializer must have been conflicting with the context I created in my scenario. Thanks for the comment Gert, it gave me the idea to take a closer look at what was going on in the rest of my scenario.

Categories

Resources