Skip to content

Commit

Permalink
Enable navigating to a specific user profile page (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwilkins committed Apr 21, 2016
1 parent 986605c commit dea0afa
Show file tree
Hide file tree
Showing 33 changed files with 630 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,11 @@ public Task<UserContract> GetUser(string userId, string registrationReference =
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="continuationToken">Last captured ticks in the form of a string.</param>
/// <param name="includeNonActivePhotos">By default, false. If true, non-active photos are included.</param>
/// <returns>List of photos up to the page size.</returns>
public Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId, string continuationToken)
public Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId, string continuationToken, bool includeNonActivePhotos = false)
{
return _repository.GetUserPhotoStream(userId, continuationToken);
return _repository.GetUserPhotoStream(userId, continuationToken, includeNonActivePhotos);
}

/// <summary>
Expand Down Expand Up @@ -250,13 +251,23 @@ public Task ReinitializeDatabase(string serverPath)
}

/// <summary>
/// Update stored photo object.
/// Updates an existing photo object's category and description fields.
/// </summary>
/// <param name="photo">Photo Object.</param>
/// <param name="photoContract">Photo object.</param>
/// <returns>New PhotoContract containing updated data.</returns>
public Task<PhotoContract> UpdatePhoto(PhotoContract photo)
public Task<PhotoContract> UpdatePhoto(PhotoContract photoContract)
{
return _repository.UpdatePhoto(photoContract);
}

/// <summary>
/// Updates the status of the stored photo.
/// </summary>
/// <param name="photoContract">Photo object.</param>
/// <returns>PhotoContract containing updated data.</returns>
public Task<PhotoContract> UpdatePhotoStatus(PhotoContract photoContract)
{
return _repository.UpdatePhoto(photo);
return _repository.UpdatePhotoStatus(photoContract);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -806,8 +806,9 @@ private UserDocument GetUserDocumentByUserId(string userId)
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="continuationToken">Last captured ticks in the form of a string.</param>
/// <param name="includeNonActivePhotos">By default, false. If true, non-active photos are included.</param>
/// <returns>List of photos up to the page size.</returns>
public async Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId, string continuationToken)
public async Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId, string continuationToken, bool includeNonActivePhotos = false)
{
var feedOptions = new FeedOptions
{
Expand All @@ -819,11 +820,19 @@ public async Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId
feedOptions)
.Where(d => d.DocumentType == PhotoDocument.DocumentTypeIdentifier)
.Where(d => d.DocumentVersion == _currentDocumentVersion)
.Where(p => p.UserId == userId)
.Where(p => p.UserId == userId);

if (!includeNonActivePhotos)
{
query = query
.Where(p => p.Status == PhotoStatus.Active);
}

var documentQuery = query
.OrderByDescending(p => p.CreatedDateTime.Epoch)
.AsDocumentQuery();

var documentResponse = await query.ExecuteNextAsync<PhotoDocument>();
var documentResponse = await documentQuery.ExecuteNextAsync<PhotoDocument>();

var photoContracts = await CreatePhotoContractsAndLoadUserData(documentResponse.ToList());

Expand Down Expand Up @@ -1162,8 +1171,8 @@ private async Task UpdateAnnotationDocument(AnnotationDocument annotationDocumen
/// <summary>
/// Updates an existing photo object's category and description fields.
/// </summary>
/// <param name="photoContract">Photo Object.</param>
/// <returns>New PhotoContract containing updated data.</returns>
/// <param name="photoContract">Photo object.</param>
/// <returns>PhotoContract containing updated data.</returns>
public async Task<PhotoContract> UpdatePhoto(PhotoContract photoContract)
{
var photoDocument = GetPhotoDocument(photoContract.Id);
Expand All @@ -1176,6 +1185,22 @@ public async Task<PhotoContract> UpdatePhoto(PhotoContract photoContract)
return photoContract;
}

/// <summary>
/// Updates the status of the stored photo object.
/// </summary>
/// <param name="photoContract">Photo object.</param>
/// <returns>PhotoContract containing updated data.</returns>
public async Task<PhotoContract> UpdatePhotoStatus(PhotoContract photoContract)
{
var photoDocument = GetPhotoDocument(photoContract.Id);

photoDocument.Status = photoContract.Status;

await ReplacePhotoDocument(photoDocument);

return photoContract;
}

/// <summary>
/// Updates the user profile picture. User gold balance is also updated if it is the first time
/// the user is updating their profile picture.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ Task<LeaderboardContract> GetLeaderboard(int mostGoldCategoriesCount, int mostGo
/// </summary>
/// <param name="userId">The user id.</param>
/// <param name="continuationToken">Last captured ticks in the form of a string.</param>
/// <param name="includeNonActivePhotos">By default, false. If true, non-active photos are included.</param>
/// <returns>List of photos up to the page size.</returns>
Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId, string continuationToken);
Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId, string continuationToken, bool includeNonActivePhotos = false);

/// <summary>
/// Checks if the database exists and initializes it if it doesn't.
Expand Down Expand Up @@ -174,11 +175,18 @@ Task<LeaderboardContract> GetLeaderboard(int mostGoldCategoriesCount, int mostGo
Task ReinitializeDatabase(string serverPath);

/// <summary>
/// Update stored photo object.
/// Updates an existing photo object's category and description fields.
/// </summary>
/// <param name="photo">Photo Object.</param>
/// <returns>New PhotoContract containing updated data.</returns>
Task<PhotoContract> UpdatePhoto(PhotoContract photo);
/// <param name="photoContract">Photo object.</param>
/// <returns>PhotoContract containing updated data.</returns>
Task<PhotoContract> UpdatePhoto(PhotoContract photoContract);

/// <summary>
/// Updates the status of the stored photo.
/// </summary>
/// <param name="photoContract">Photo object.</param>
/// <returns>PhotoContract containing updated data.</returns>
Task<PhotoContract> UpdatePhotoStatus(PhotoContract photoContract);

/// <summary>
/// Updates the user profile picture. User gold balance is also updated if it is the first time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ public async Task UpdateUserAsDifferentUserTest()
_userRegistrationReferenceProviderMock.GetCurrentUserRegistrationReference =
() => user2.RegistrationReference;

user1.ProfilePhotoUrl = "http://northwind-traders.com";

// Act
await _userController.UpdateUserProfile(user1);
}
Expand All @@ -113,11 +111,14 @@ public async Task UpdateUserTest()
// Populate our db with necessary objects
var user = await _repository.CreateUser("Test User " + DateTime.UtcNow.Ticks);

var category1 = await _repository.CreateCategory("Test Category " + DateTime.UtcNow.Ticks);
var photo = await _repository.InsertPhoto(CreateTestPhoto(category1, user, "Test photo 1"), 1);

user.ProfilePhotoId = photo.Id;

_userRegistrationReferenceProviderMock.GetCurrentUserRegistrationReference =
() => user.RegistrationReference;

user.ProfilePhotoUrl = "http://northwind-traders.com";

// Act
var actionResult = await _userController.UpdateUserProfile(user);

Expand All @@ -134,5 +135,25 @@ public async Task UpdateUserTest()
Assert.AreEqual(user.ProfilePhotoUrl, actionResult.ProfilePhotoUrl,
"profile photo url update wasn't persisted after UpdateUserProfile service call");
}

[TestMethod]
[ExpectedException(typeof(HttpResponseException))]
public async Task UpdateUserWithInvalidPhotoTest()
{
// Populate our db with necessary objects
var user = await _repository.CreateUser("Test User " + DateTime.UtcNow.Ticks);
var user2 = await _repository.CreateUser("Test User 2" + DateTime.UtcNow.Ticks);

var category1 = await _repository.CreateCategory("Test Category " + DateTime.UtcNow.Ticks);
var photoByUser2 = await _repository.InsertPhoto(CreateTestPhoto(category1, user2, "Test Photo 1"), 1);

user.ProfilePhotoId = photoByUser2.Id;

_userRegistrationReferenceProviderMock.GetCurrentUserRegistrationReference =
() => user.RegistrationReference;

// Act
await _userController.UpdateUserProfile(user);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
// ---------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PhotoSharingApp.AppService.Controllers;
using PhotoSharingApp.AppService.Shared.Repositories;
using PhotoSharingApp.AppService.Tests.Helpers;
using PhotoSharingApp.AppService.Tests.Context;
using PhotoSharingApp.AppService.Tests.Helpers;
using PhotoSharingApp.Portable.DataContracts;

namespace PhotoSharingApp.AppService.Tests.Controllers
{
Expand All @@ -50,6 +52,36 @@ public void Init()
_userRegistrationReferenceProviderMock);
}

[TestMethod]
public async Task GetOtherUserPhotoStreamTest()
{
// Populate our db with necessary objects
var user = await _repository.CreateUser("Test User " + DateTime.UtcNow.Ticks);

var category1 = await _repository.CreateCategory("Test Category " + DateTime.UtcNow.Ticks);
var category2 = await _repository.CreateCategory("Test Category " + DateTime.UtcNow.Ticks);
var category3 = await _repository.CreateCategory("Test Category " + DateTime.UtcNow.Ticks);

await _repository.InsertPhoto(CreateTestPhoto(category1, user, "Test photo 1"), 1);
await _repository.InsertPhoto(CreateTestPhoto(category2, user, "Test photo 2"), 1);
await _repository.InsertPhoto(CreateTestPhoto(category3, user, "Test photo 3"), 1);

// Insert objectionable photo
var photo = CreateTestPhoto(category3, user, "Test photo 3");
var insertedPhoto = await _repository.InsertPhoto(photo, 1);

insertedPhoto.Status = PhotoStatus.ObjectionableContent;
await _repository.UpdatePhotoStatus(insertedPhoto);

// Act
var actionResult = await _userPhotoController.GetPagedAsync(user.UserId, null);

// Verify
Assert.IsNotNull(actionResult.Items, "null response from service");
Assert.AreEqual(3, actionResult.Items.Count, "photo count returned was not correct");
Assert.IsNull(actionResult.Items.FirstOrDefault(p => p.Status == PhotoStatus.ObjectionableContent));
}

[TestMethod]
public async Task GetUserPhotoStreamTest()
{
Expand All @@ -64,6 +96,13 @@ public async Task GetUserPhotoStreamTest()
await _repository.InsertPhoto(CreateTestPhoto(category2, user, "Test photo 2"), 1);
await _repository.InsertPhoto(CreateTestPhoto(category3, user, "Test photo 3"), 1);

// Insert objectionable photo
var photo = CreateTestPhoto(category3, user, "Test photo 3");
var insertedPhoto = await _repository.InsertPhoto(photo, 1);

insertedPhoto.Status = PhotoStatus.ObjectionableContent;
await _repository.UpdatePhotoStatus(insertedPhoto);

_userRegistrationReferenceProviderMock.GetCurrentUserRegistrationReference =
() => user.RegistrationReference;

Expand All @@ -72,7 +111,8 @@ public async Task GetUserPhotoStreamTest()

// Verify
Assert.IsNotNull(actionResult.Items, "null response from service");
Assert.AreEqual(3, actionResult.Items.Count, "photo count returned was not correct");
Assert.AreEqual(4, actionResult.Items.Count, "photo count returned was not correct");
Assert.IsNotNull(actionResult.Items.FirstOrDefault(p => p.Status == PhotoStatus.ObjectionableContent));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public Task<UserContract> GetUser(string userId, string registrationReference =
throw new NotImplementedException();
}

public Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId, string continuationToken)
public Task<PagedResponse<PhotoContract>> GetUserPhotoStream(string userId, string continuationToken, bool includeNonActivePhotos = false)
{
throw new NotImplementedException();
}
Expand Down Expand Up @@ -147,7 +147,12 @@ public Task ReinitializeDatabase(string serverPath)
throw new NotImplementedException();
}

public Task<PhotoContract> UpdatePhoto(PhotoContract photo)
public Task<PhotoContract> UpdatePhoto(PhotoContract photoContract)
{
throw new NotImplementedException();
}

public Task<PhotoContract> UpdatePhotoStatus(PhotoContract photoContract)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,23 @@ public async Task<UserContract> UpdateUserProfile(UserContract user)

var currentUserId = await ValidateAndReturnCurrentUserId();

// A user should only be able to update his/her own profile.
if (!currentUserId.Equals(user.RegistrationReference))
{
throw ServiceExceptions.NotAllowed();
}

// Check if the user owns the photo that is passed in as profile photo
var photo = await _repository.GetPhoto(user.ProfilePhotoId);

if (!photo.User.UserId.Equals(user.UserId))
{
throw ServiceExceptions.NotAllowed();
}

// Refreshing profile photo url
user.ProfilePhotoUrl = photo.ThumbnailUrl;

var existingUser = await _repository.UpdateUser(user);

return existingUser;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ public UserPhotoController(IRepository repository, TelemetryClient telemetryClie
}

/// <summary>
/// Gets a users paged photostream after provided continuation token.
/// Gets the paged photostream for the authorized user.
/// </summary>
/// <verb>GET</verb>
/// <url>http://{host}/api/userphoto?continuationToken={continuationToken}</url>
/// <param name="continuationToken">Continuation token for paged response.</param>
/// <returns>Paged photo contracts</returns>
/// <returns>Paged photo contracts which include flagged and non-flagged photos.</returns>
[Authorize]
[Route("api/userphoto")]
public async Task<PagedResponse<PhotoContract>> GetPagedAsync([FromUri] string continuationToken)
Expand All @@ -76,6 +76,40 @@ public async Task<PagedResponse<PhotoContract>> GetPagedAsync([FromUri] string c

var user = await _repository.GetUser(null, registrationReference);

var stream = await _repository.GetUserPhotoStream(user.UserId, continuationToken, true);

return stream;
}
catch (DataLayerException ex)
{
_telemetryClient.TrackException(ex);

if (ex.Error == DataLayerError.Unknown)
{
throw ServiceExceptions.UnknownInternalFailureException(ServiceExceptions.Source);
}

throw ServiceExceptions.DataLayerException(ex.Message);
}
}

/// <summary>
/// Gets a user's paged photostream.
/// </summary>
/// <verb>GET</verb>
/// <url>http://{host}/api/userphoto/{userId}?continuationToken={continuationToken}</url>
/// <param name="userId">The userId.</param>
/// <param name="continuationToken">Continuation token for paged response.</param>
/// <returns>Paged photo contracts which includes non-flagged photos only.</returns>
[Route("api/userphoto/{userId}")]
public async Task<PagedResponse<PhotoContract>> GetPagedAsync(string userId, [FromUri] string continuationToken)
{
try
{
_telemetryClient.TrackEvent("UserPhotoController GetPagedAsync invoked");

var user = await _repository.GetUser(userId);

var stream = await _repository.GetUserPhotoStream(user.UserId, continuationToken);

return stream;
Expand Down
Loading

0 comments on commit dea0afa

Please sign in to comment.