Skip to content

Commit

Permalink
Added search, fixed refresh token not saving for google oAuth etc
Browse files Browse the repository at this point in the history
  • Loading branch information
AlenGeoAlex committed Oct 15, 2024
1 parent cca560d commit 33c8012
Show file tree
Hide file tree
Showing 19 changed files with 188 additions and 14 deletions.
12 changes: 12 additions & 0 deletions MyServe.Backend.App.Application/Dto/Me/SearchDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using MyServe.Backend.Common.Constants;

namespace MyServe.Backend.App.Application.Dto.Me;

public class SearchDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Service { get; set; }
public Dictionary<string, string?> Metadata { get; set; } = new();
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public async Task<OAuthResponse> Handle(OAuthCommand request, CancellationToken
var refreshTokenTask = refreshTokenService.CreateRefreshTokenAsync(user.Id);

await Task.WhenAll(accessTokenTask, refreshTokenTask);

await uow.CommitAsync();
return new OAuthResponse()
{
Success = true,
Expand All @@ -64,6 +64,7 @@ public async Task<OAuthResponse> Handle(OAuthCommand request, CancellationToken
catch (Exception e)
{
logger.Error(e, "An unknown error occured while generating the user access for {EmailAddress}", userIdentificationDto.Email);
await uow.RollbackAsync();
return new OAuthResponse()
{
Success = false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using MyServe.Backend.App.Application.Abstract;

namespace MyServe.Backend.App.Application.Features.Profile.Search;

public class MeSearchQuery : IAppRequest<MeSearchResponse>
{
public Guid UserId { get; set; }

public string Search { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using MediatR;
using MyServe.Backend.App.Application.Services;
using MyServe.Backend.App.Domain.Abstracts;

namespace MyServe.Backend.App.Application.Features.Profile.Search;

public class MeSearchQueryHandler(
IProfileService profileService,
IReadOnlyUnitOfWork readOnlyUnitOfWork
) : IRequestHandler<MeSearchQuery, MeSearchResponse>
{
public async Task<MeSearchResponse> Handle(MeSearchQuery request, CancellationToken cancellationToken)
{
await using var uow = await readOnlyUnitOfWork.StartTransactionAsync();
try
{
var matchedList = await profileService.SearchAsync(request, cancellationToken);
return new MeSearchResponse()
{
Matched = matchedList
};
}
catch (Exception e)
{
await uow.RollbackAsync();
return new MeSearchResponse();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MyServe.Backend.App.Application.Dto.Me;

namespace MyServe.Backend.App.Application.Features.Profile.Search;

public class MeSearchResponse
{
public List<SearchDto> Matched = [];
}
4 changes: 3 additions & 1 deletion MyServe.Backend.App.Application/Services/IProfileService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using MyServe.Backend.App.Application.Dto.Me;
using MyServe.Backend.App.Application.Dto.Profile;
using MyServe.Backend.App.Application.Features.Profile.Create;
using MyServe.Backend.App.Application.Features.Profile.Search;

namespace MyServe.Backend.App.Application.Services;

public interface IProfileService
{
Task<ProfileDto?> GetProfileAsync(Guid userId, CancellationToken cancellationToken = default);

Task<ProfileDto> CreateNewProfileAsync(CreateProfileCommand command, CancellationToken cancellationToken = default);
Task<List<SearchDto>> SearchAsync(MeSearchQuery searchQuery, CancellationToken cancellationToken = default);
}
10 changes: 10 additions & 0 deletions MyServe.Backend.App.Common/Constants/ServiceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace MyServe.Backend.Common.Constants;

public enum ServiceType
{
File,
Password,
Calendar,
Notes,
ShareableLink
}
2 changes: 1 addition & 1 deletion MyServe.Backend.App.Domain/Models/Files/File.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace MyServe.Backend.App.Domain.Models.Files;

public class File
public class File : IEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
Expand Down
6 changes: 6 additions & 0 deletions MyServe.Backend.App.Domain/Models/IEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MyServe.Backend.App.Domain.Models;

public interface IEntity
{
public Guid Id { get; set; }
}
2 changes: 1 addition & 1 deletion MyServe.Backend.App.Domain/Models/Profile/Profile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace MyServe.Backend.App.Domain.Models.Profile;

public class Profile
public class Profile : IEntity
{
public Profile()
{
Expand Down
2 changes: 1 addition & 1 deletion MyServe.Backend.App.Domain/Models/User/User.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace MyServe.Backend.App.Domain.Models.User;

public class User
public class User : IEntity
{
public Guid Id { get; set; }
public string EmailAddress { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions MyServe.Backend.App.Domain/Repositories/IProfileRepository.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using MyServe.Backend.App.Domain.Abstracts;
using MyServe.Backend.App.Domain.Models;
using MyServe.Backend.App.Domain.Models.Profile;

namespace MyServe.Backend.App.Domain.Repositories;

public interface IProfileRepository : IAppRepository<Profile>
{
Task<bool> ExistsAsync(string emailAddress);
Task<List<IEntity>> SearchAcrossProfileAsync(string search, Guid userId);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using Dapper;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.Extensions.DependencyInjection;
using MyServe.Backend.App.Domain.Extensions;
using MyServe.Backend.App.Domain.Models;
using MyServe.Backend.App.Domain.Models.Profile;
using MyServe.Backend.App.Domain.Repositories;
using MyServe.Backend.App.Infrastructure.Abstract;
using MyServe.Backend.App.Infrastructure.Database.NpgSql;
using Newtonsoft.Json;
using Npgsql;
using File = MyServe.Backend.App.Domain.Models.Files.File;

namespace MyServe.Backend.App.Infrastructure.Repositories;

Expand Down Expand Up @@ -70,6 +73,30 @@ public async Task<bool> ExistsAsync(string emailAddress)
return emailList.Any();
}

public async Task<List<IEntity>> SearchAcrossProfileAsync(string search, Guid userId)
{
List<IEntity> entities = [];
var searchParam = search.Split(" ").Select(x => $"%{x}%").ToList();
var gridReader = await readOnlyConnection.QueryMultipleAsync(ProfileSql.SearchEntities, new
{
UserId = userId,
@Search = searchParam,
});

entities.AddRange(from file in await gridReader.ReadAsync()
let fileTypeRaw = file.type.ToString()
select new File()
{
Id = file.id,
Name = file.name,
ParentId = file.parent,
MimeType = file.mime_type,
TargetSize = file.target_size,
Type = ((string)fileTypeRaw).GetFileTypeFromString()!.Value
});
return entities;
}

private static class ProfileSql
{
public const string SelectById = """
Expand All @@ -80,5 +107,15 @@ private static class ProfileSql
INSERT INTO public.profile (id, first_name, last_name, "profile_settings", "created_at", "profile_image", "encryption_key") VALUES (@Id, @FirstName, @LastName, @Settings, @CreatedAt, @ProfileImage, @EncryptionKey);;
""";
public const string Exists = "SELECT 1 FROM profile WHERE id = @Email LIMIT 1";

public const string SearchEntities = """
SELECT id AS "id", "name" AS name, parent AS "parent", "mime_type", "type", target_size
FROM files.file f WHERE f.owner = @UserId AND is_deleted = false AND (
"name" ILIKE ANY(@Search) OR mime_type ILIKE ANY(@Search)
)
ORDER BY f.name, f.created
""";
}
}
30 changes: 30 additions & 0 deletions MyServe.Backend.App.Infrastructure/Services/ProfileService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Net.Mime;
using Microsoft.Extensions.DependencyInjection;
using MyServe.Backend.App.Application.Client;
using MyServe.Backend.App.Application.Dto.Me;
using MyServe.Backend.App.Application.Dto.Profile;
using MyServe.Backend.App.Application.Features.Profile.Create;
using MyServe.Backend.App.Application.Features.Profile.Search;
using MyServe.Backend.App.Application.Services;
using MyServe.Backend.App.Domain.Models.Profile;
using MyServe.Backend.App.Domain.Repositories;
Expand All @@ -11,6 +13,7 @@
using MyServe.Backend.Common.Constants;
using MyServe.Backend.Common.Constants.StorageConstants;
using MyServe.Backend.Common.Models;
using File = MyServe.Backend.App.Domain.Models.Files.File;

namespace MyServe.Backend.App.Infrastructure.Services;

Expand Down Expand Up @@ -64,4 +67,31 @@ public async Task<ProfileDto> CreateNewProfileAsync(CreateProfileCommand command

return ProfileMapper.ToProfileDto(profile);;
}

public async Task<List<SearchDto>> SearchAsync(MeSearchQuery searchQuery, CancellationToken cancellationToken = default)
{
var entities = await profileRepository.SearchAcrossProfileAsync(searchQuery.Search, searchQuery.UserId);
List<SearchDto> responses = [];
foreach (var entity in entities)
{
if (entity is File file)
{
responses.Add(new SearchDto()
{
Service = ServiceType.File.ToString(),
Name = file.Name,
Id = file.Id,
Description = "",
Metadata =
{
{"ParentId", file.ParentId?.ToString() },
{"Type", file.Type.ToString()},
{"MimeType", file.MimeType},
{"TargetSize", file.TargetSize.ToString()}
}
});
}
}
return responses;
}
}
13 changes: 11 additions & 2 deletions MyServe.Backend.Http.Api/Controllers/MeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using MyServe.Backend.App.Application.Client;
using MyServe.Backend.App.Application.Features.Profile.Create;
using MyServe.Backend.App.Application.Features.Profile.Me;
using MyServe.Backend.App.Application.Features.Profile.Search;
using Newtonsoft.Json;
using ILogger = Serilog.ILogger;

Expand All @@ -18,7 +19,7 @@ public class MeController(ICacheService cacheService, ILogger logger, IRequestCo

[HttpGet]
[Authorize]
public async Task<IActionResult> Get()
public async Task<ActionResult<MeResponse>> Get()
{
var requesterUserId = requestContext.Requester.UserId;
requestContext.CacheControl.FrameEndpointCacheKey(CacheConstants.UserCacheKey, requesterUserId.ToString());
Expand All @@ -38,7 +39,7 @@ public async Task<IActionResult> Get()

[HttpPost]
[Authorize]
public async Task<IActionResult> Post()
public async Task<ActionResult<CreateProfileResponse>> Post()
{
var formCollection = await Request.ReadFormAsync();
if (!formCollection.ContainsKey("body"))
Expand All @@ -65,5 +66,13 @@ public async Task<IActionResult> Post()

return CreatedAtRoute("GetUser", new { controller = "User", id = createProfileResponse.Id }, null);
}

[HttpGet("search")]
public async Task<ActionResult<MeSearchResponse>> Search([FromQuery] MeSearchQuery searchQuery)
{
searchQuery.UserId = requestContext.Requester.UserId;
var response = await mediator.Send(searchQuery);
return Ok(response);
}

}
16 changes: 16 additions & 0 deletions MyServe.Backend.Http.Api/Validators/MeValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using FluentValidation;
using MyServe.Backend.App.Application.Features.Profile.Search;

namespace MyServe.Backend.Api.Validators;

public class MeSearchQueryValidator : AbstractValidator<MeSearchQuery>
{

public MeSearchQueryValidator()
{
RuleFor(x => x.Search)
.MinimumLength(3)
.WithMessage("Search length must be between 3");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,9 @@ $$ LANGUAGE plpgsql;

CREATE OR REPLACE TRIGGER validate_parent_type
BEFORE INSERT OR UPDATE ON files.file
FOR EACH ROW EXECUTE FUNCTION check_file_validation();
FOR EACH ROW EXECUTE FUNCTION check_file_validation();

CREATE INDEX idx_name_mime_type ON files.file USING gin (
name gin_trgm_ops,
mime_type gin_trgm_ops
);
4 changes: 3 additions & 1 deletion MyServe.Backend.Infrastructure.Database/Tables/init.sql
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
CREATE SCHEMA IF NOT EXISTS "public";
CREATE SCHEMA IF NOT EXISTS "files";
CREATE SCHEMA IF NOT EXISTS "files";

CREATE EXTENSION IF NOT EXISTS pg_trgm;
5 changes: 0 additions & 5 deletions MyServe.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyServe.Backend.Http.Api", "MyServe.Backend.Http.Api\MyServe.Backend.Http.Api.csproj", "{5013C947-CDE3-4CF6-8767-330481288FAC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MyServe.Backend.Infrastructure", "MyServe.Backend.Infrastructure", "{619C9F09-AF0F-4A47-8DCC-FB93D6CEFA3C}"
ProjectSection(SolutionItems) = preProject
docker-compose.yml = docker-compose.yml
Dockerfile-Consumer-Job = Dockerfile-Consumer-Job
.dockerignore = .dockerignore
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyServe.Backend.App.Common", "MyServe.Backend.App.Common\MyServe.Backend.App.Common.csproj", "{2E4F2FF1-2C96-4D1F-937C-9B2634915C40}"
EndProject
Expand Down

0 comments on commit 33c8012

Please sign in to comment.