Moved projects to their own separate subdirectories
This commit is contained in:
280
API/YABA.Service/BookmarkService.cs
Normal file
280
API/YABA.Service/BookmarkService.cs
Normal file
@ -0,0 +1,280 @@
|
||||
using AutoMapper;
|
||||
using HtmlAgilityPack;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using YABA.Common.DTOs.Bookmarks;
|
||||
using YABA.Common.DTOs.Tags;
|
||||
using YABA.Common.Extensions;
|
||||
using YABA.Common.Interfaces;
|
||||
using YABA.Data.Context;
|
||||
using YABA.Data.Extensions;
|
||||
using YABA.Models;
|
||||
using YABA.Service.Interfaces;
|
||||
|
||||
namespace YABA.Service
|
||||
{
|
||||
public class BookmarkService : IBookmarkService
|
||||
{
|
||||
private readonly YABAReadOnlyContext _roContext;
|
||||
private readonly YABAReadWriteContext _context;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public BookmarkService(
|
||||
YABAReadOnlyContext roContext,
|
||||
YABAReadWriteContext context,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IMapper mapper)
|
||||
{
|
||||
_roContext = roContext;
|
||||
_context = context;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public IEnumerable<BookmarkDTO> GetAll(bool showHidden = false)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
var userBookmarkDTOs = new List<BookmarkDTO>();
|
||||
|
||||
|
||||
var showHiddenCondition = new Func<Bookmark, bool>(b => b.IsHidden || b.BookmarkTags.Where(x => x.Tag.UserId == currentUserId).Any(x => x.Tag.IsHidden));
|
||||
var showNotHiddenCondition = new Func<Bookmark, bool>(b => !b.IsHidden && b.BookmarkTags.Where(x => x.Tag.UserId == currentUserId).All(bt => !bt.Tag.IsHidden));
|
||||
|
||||
var filteredBookmarks = _roContext.Bookmarks
|
||||
.Include(b => b.BookmarkTags)
|
||||
.ThenInclude(bt => bt.Tag)
|
||||
.Where(b => b.UserId == currentUserId)
|
||||
.Where(showHidden ? showHiddenCondition : showNotHiddenCondition)
|
||||
.ToList();
|
||||
|
||||
foreach(var bookmark in filteredBookmarks)
|
||||
{
|
||||
var isBookmarkHidden = bookmark.IsHidden;
|
||||
var bookmarkDTO = _mapper.Map<BookmarkDTO>(bookmark);
|
||||
|
||||
var bookmarkTags = bookmark.BookmarkTags.Select(x => x.Tag);
|
||||
bookmarkDTO.Tags = _mapper.Map<List<TagDTO>>(bookmarkTags);
|
||||
|
||||
userBookmarkDTOs.Add(bookmarkDTO);
|
||||
}
|
||||
|
||||
return userBookmarkDTOs;
|
||||
}
|
||||
|
||||
public async Task<BookmarkDTO?> CreateBookmark(CreateBookmarkRequestDTO request)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (!_roContext.Users.UserExists(currentUserId)
|
||||
|| await _roContext.Bookmarks.AnyAsync(x => x.UserId == currentUserId && x.Url == request.Url)) return null;
|
||||
|
||||
var bookmark = _mapper.Map<Bookmark>(request);
|
||||
UpdateBookmarkWithMetaData(bookmark);
|
||||
bookmark.UserId = currentUserId;
|
||||
|
||||
var newEntity = await _context.Bookmarks.AddAsync(bookmark);
|
||||
|
||||
if (await _context.SaveChangesAsync() > 0)
|
||||
{
|
||||
var bookmarkDTO = _mapper.Map<BookmarkDTO>(newEntity.Entity);
|
||||
|
||||
if(request.Tags != null && request.Tags.Any())
|
||||
bookmarkDTO.Tags.AddRange(await UpdateBookmarkTags(bookmarkDTO.Id, request.Tags));
|
||||
return bookmarkDTO;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<BookmarkDTO?> UpdateBookmark(int id, UpdateBookmarkRequestDTO request)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
var bookmark = _context.Bookmarks.FirstOrDefault(x => x.UserId == currentUserId && x.Id == id);
|
||||
var tags = new List<TagDTO>();
|
||||
|
||||
if (request.Tags != null && request.Tags.Any())
|
||||
tags = (await UpdateBookmarkTags(id, request.Tags)).ToList();
|
||||
|
||||
|
||||
if (bookmark == null) return null;
|
||||
|
||||
bookmark.Title = !string.IsNullOrEmpty(request.Title) ? request.Title : bookmark.Title;
|
||||
bookmark.Description = !string.IsNullOrEmpty(request.Description) ? request.Description : bookmark.Description;
|
||||
bookmark.Note = !string.IsNullOrEmpty(request.Note) ? request.Note : bookmark.Note;
|
||||
bookmark.IsHidden = request.IsHidden;
|
||||
bookmark.Url = !string.IsNullOrEmpty(request.Url) ? request.Url : bookmark.Url;
|
||||
UpdateBookmarkWithMetaData(bookmark);
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
var bookmarkDTO = _mapper.Map<BookmarkDTO>(bookmark);
|
||||
bookmarkDTO.Tags = tags;
|
||||
return bookmarkDTO;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TagDTO>?> UpdateBookmarkTags(int id, IEnumerable<string> tags)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (!_roContext.Bookmarks.Any(x => x.Id == id && x.UserId == currentUserId)
|
||||
|| tags == null || !tags.Any()) return null;
|
||||
|
||||
// Add tags that are not yet in the database
|
||||
var savedUserTags = _context.Tags.Where(x => x.UserId == currentUserId).ToList();
|
||||
var tagsToSave = tags.Except(savedUserTags.Select(x => x.Name).ToHashSet()).Select(x => new Tag { Name = x, UserId = currentUserId }).ToList();
|
||||
await _context.Tags.AddRangeAsync(tagsToSave);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Add newly added tags to the lookup
|
||||
savedUserTags.AddRange(tagsToSave);
|
||||
|
||||
var existingBookmarkTags = _roContext.BookmarkTags.Include(x => x.Tag).Where(x => x.BookmarkId == id).ToList();
|
||||
var existingBookmarkTagsLookup = existingBookmarkTags.ToDictionary(k => k.TagId, v => v.Tag.Name);
|
||||
|
||||
var bookmarkTagsToRemove = existingBookmarkTagsLookup
|
||||
.Where(x => !tags.Contains(x.Value))
|
||||
.Select(x => new BookmarkTag { BookmarkId = id, TagId = x.Key });
|
||||
|
||||
var savedUserTagsByName = savedUserTags.ToDictionary(k => k.Name, v => v.Id);
|
||||
var bookmarkTagsToAdd = tags.Except(existingBookmarkTagsLookup.Values)
|
||||
.Select(x => new BookmarkTag { BookmarkId = id, TagId = savedUserTagsByName[x] });
|
||||
|
||||
_context.BookmarkTags.RemoveRange(bookmarkTagsToRemove);
|
||||
await _context.BookmarkTags.AddRangeAsync(bookmarkTagsToAdd);
|
||||
|
||||
if (await _context.SaveChangesAsync() >= 0)
|
||||
{
|
||||
var updatedBookmarkTags = _roContext.BookmarkTags
|
||||
.Include(x => x.Tag)
|
||||
.Where(x => x.BookmarkId == id)
|
||||
.Select(x => x.Tag);
|
||||
|
||||
return _mapper.Map<IEnumerable<TagDTO>>(updatedBookmarkTags);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<BookmarkDTO?> Get(int id)
|
||||
{
|
||||
int.TryParse(_httpContextAccessor.HttpContext.User.Identity.GetUserId(), out int userId);
|
||||
var bookmark = await _roContext.Bookmarks.FirstOrDefaultAsync(x => x.Id == id && x.UserId == userId);
|
||||
|
||||
if (bookmark == null) return null;
|
||||
|
||||
var bookmarkTags = _roContext.BookmarkTags
|
||||
.Include(x => x.Tag)
|
||||
.Where(x => x.BookmarkId == id)
|
||||
.Select(x => x.Tag)
|
||||
.ToList();
|
||||
|
||||
var bookmarkDTO = _mapper.Map<BookmarkDTO>(bookmark);
|
||||
bookmarkDTO.Tags = _mapper.Map<List<TagDTO>>(bookmarkTags);
|
||||
|
||||
return bookmarkDTO;
|
||||
}
|
||||
|
||||
public IEnumerable<TagDTO>? GetBookmarkTags(int id)
|
||||
{
|
||||
int.TryParse(_httpContextAccessor.HttpContext.User.Identity.GetUserId(), out int userId);
|
||||
if (!_roContext.Bookmarks.Any(x => x.Id == id && x.UserId == userId)) return null;
|
||||
|
||||
var bookmarkTags = _roContext.BookmarkTags
|
||||
.Include(x => x.Tag)
|
||||
.Where(x => x.BookmarkId == id)
|
||||
.Select(x => x.Tag)
|
||||
.ToList();
|
||||
|
||||
return _mapper.Map<IEnumerable<TagDTO>>(bookmarkTags);
|
||||
}
|
||||
|
||||
public async Task<int?> DeleteBookmark(int id)
|
||||
{
|
||||
var result = await DeleteBookmarks(new List<int> { id });
|
||||
return result.FirstOrDefault();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<int>?> DeleteBookmarks(IEnumerable<int> ids)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (!await _roContext.Users.UserExistsAsync(currentUserId)) return null;
|
||||
|
||||
var entriesToDelete = _context.Bookmarks.Where(x => x.UserId == currentUserId && ids.Contains(x.Id)).ToList();
|
||||
_context.Bookmarks.RemoveRange(entriesToDelete);
|
||||
|
||||
if (await _context.SaveChangesAsync() <= 0) return null;
|
||||
|
||||
return entriesToDelete.Select(x => x.Id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<int>?> HideBookmarks(IEnumerable<int> ids)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if (!await _roContext.Users.UserExistsAsync(currentUserId)) return null;
|
||||
|
||||
var entriesToHide = _context.Bookmarks.Where(x => x.UserId == currentUserId && ids.Contains(x.Id)).ToList();
|
||||
entriesToHide.ForEach((x) => { x.IsHidden = !x.IsHidden; });
|
||||
|
||||
if(await _context.SaveChangesAsync() <= 0) return null;
|
||||
|
||||
return entriesToHide.Select(x => x.Id);
|
||||
}
|
||||
|
||||
public IEnumerable<TagDTO> GetAllBookmarkTags(bool showHidden = false)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
var activeUserTags = _roContext.BookmarkTags
|
||||
.Include(x => x.Tag)
|
||||
.Where(x => x.Tag.UserId == currentUserId && x.Tag.IsHidden == showHidden)
|
||||
.ToList()
|
||||
.GroupBy(x => x.Tag.Id)
|
||||
.Select(g => g.First()?.Tag);
|
||||
|
||||
return _mapper.Map<IEnumerable<TagDTO>>(activeUserTags);
|
||||
}
|
||||
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
int.TryParse(_httpContextAccessor.HttpContext.User.Identity.GetUserId(), out int userId);
|
||||
return userId;
|
||||
}
|
||||
|
||||
private void UpdateBookmarkWithMetaData(IBookmark bookmark)
|
||||
{
|
||||
var webClient = new WebClient();
|
||||
webClient.Headers.Add("User-Agent", "APIClient");
|
||||
var sourceData = webClient.DownloadString(bookmark.Url);
|
||||
var title = Regex.Match(sourceData, @"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>", RegexOptions.IgnoreCase).Groups["Title"].Value;
|
||||
var description = string.Empty;
|
||||
|
||||
var getHtmlDoc = new HtmlWeb();
|
||||
var document = getHtmlDoc.Load(bookmark.Url);
|
||||
var metaTags = document.DocumentNode.SelectNodes("//meta");
|
||||
if (metaTags != null)
|
||||
{
|
||||
foreach (var sitetag in metaTags)
|
||||
{
|
||||
if (sitetag.Attributes["name"] != null && sitetag.Attributes["content"] != null && sitetag.Attributes["name"].Value == "description")
|
||||
|
||||
{
|
||||
|
||||
description = sitetag.Attributes["content"].Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bookmark.Title = !string.IsNullOrEmpty(bookmark.Title) ? bookmark.Title : string.IsNullOrEmpty(title) ? bookmark.Url : title;
|
||||
bookmark.Description = !string.IsNullOrEmpty(bookmark.Description) ? bookmark.Description : description;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
API/YABA.Service/Configuration/AutoMapperProfile.cs
Normal file
22
API/YABA.Service/Configuration/AutoMapperProfile.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using AutoMapper;
|
||||
using YABA.Common.DTOs;
|
||||
using YABA.Common.DTOs.Bookmarks;
|
||||
using YABA.Common.DTOs.Tags;
|
||||
using YABA.Models;
|
||||
|
||||
namespace YABA.Service.Configuration
|
||||
{
|
||||
public class AutoMapperProfile : Profile
|
||||
{
|
||||
public AutoMapperProfile()
|
||||
{
|
||||
CreateMap<User, UserDTO>();
|
||||
CreateMap<BookmarkDTO, Bookmark>();
|
||||
CreateMap<Bookmark, BookmarkDTO>();
|
||||
CreateMap<CreateBookmarkRequestDTO, Bookmark>();
|
||||
CreateMap<Tag, TagDTO>();
|
||||
CreateMap<CreateTagDTO, Tag>()
|
||||
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name.ToLowerInvariant()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using YABA.Service.Interfaces;
|
||||
|
||||
namespace YABA.Service.Configuration
|
||||
{
|
||||
public static class DependencyInjectionConfiguration
|
||||
{
|
||||
public static void AddServiceProjectDependencyInjectionConfiguration(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
services.AddScoped<IBookmarkService, BookmarkService>();
|
||||
services.AddScoped<IMiscService, MiscService>();
|
||||
services.AddScoped<ITagsService, TagsService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
22
API/YABA.Service/Interfaces/IBookmarkService.cs
Normal file
22
API/YABA.Service/Interfaces/IBookmarkService.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using YABA.Common.DTOs.Bookmarks;
|
||||
using YABA.Common.DTOs.Tags;
|
||||
|
||||
namespace YABA.Service.Interfaces
|
||||
{
|
||||
public interface IBookmarkService
|
||||
{
|
||||
Task<BookmarkDTO?> CreateBookmark(CreateBookmarkRequestDTO request);
|
||||
Task<BookmarkDTO?> UpdateBookmark(int id, UpdateBookmarkRequestDTO request);
|
||||
Task<IEnumerable<TagDTO>?> UpdateBookmarkTags(int id, IEnumerable<string> tags);
|
||||
IEnumerable<BookmarkDTO> GetAll(bool isHidden = false);
|
||||
Task<BookmarkDTO?> Get(int id);
|
||||
IEnumerable<TagDTO>? GetBookmarkTags(int id);
|
||||
Task<int?> DeleteBookmark(int id);
|
||||
Task<IEnumerable<int>?> DeleteBookmarks(IEnumerable<int> ids);
|
||||
Task<IEnumerable<int>?> HideBookmarks(IEnumerable<int> ids);
|
||||
IEnumerable<TagDTO> GetAllBookmarkTags(bool showHidden = false);
|
||||
|
||||
}
|
||||
}
|
||||
9
API/YABA.Service/Interfaces/IMiscService.cs
Normal file
9
API/YABA.Service/Interfaces/IMiscService.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using YABA.Common.DTOs;
|
||||
|
||||
namespace YABA.Service.Interfaces
|
||||
{
|
||||
public interface IMiscService
|
||||
{
|
||||
public WebsiteMetaDataDTO GetWebsiteMetaData(string url);
|
||||
}
|
||||
}
|
||||
18
API/YABA.Service/Interfaces/ITagsService.cs
Normal file
18
API/YABA.Service/Interfaces/ITagsService.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using YABA.Common.DTOs.Tags;
|
||||
|
||||
namespace YABA.Service.Interfaces
|
||||
{
|
||||
public interface ITagsService
|
||||
{
|
||||
Task<IEnumerable<TagDTO>?> GetAll();
|
||||
Task<TagDTO?> Get(int id);
|
||||
Task<IEnumerable<TagDTO>?> UpsertTags(IEnumerable<TagDTO> tags);
|
||||
Task<TagDTO?> UpsertTag(TagDTO tag);
|
||||
Task<IEnumerable<int>?> DeleteTags(IEnumerable<int> ids);
|
||||
Task<IEnumerable<int>?> HideTags(IEnumerable<int> ids);
|
||||
Task<TagDTO?> CreateTag(CreateTagDTO request);
|
||||
Task<TagDTO?> UpdateTag(int id, UpdateTagDTO request);
|
||||
}
|
||||
}
|
||||
12
API/YABA.Service/Interfaces/IUserService.cs
Normal file
12
API/YABA.Service/Interfaces/IUserService.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
using YABA.Common.DTOs;
|
||||
|
||||
namespace YABA.Service.Interfaces
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
bool IsUserRegistered(string authProviderId);
|
||||
Task<UserDTO> RegisterUser(string authProviderId);
|
||||
int GetUserId(string authProviderId);
|
||||
}
|
||||
}
|
||||
53
API/YABA.Service/MiscService.cs
Normal file
53
API/YABA.Service/MiscService.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using HtmlAgilityPack;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using YABA.Common.DTOs;
|
||||
using YABA.Service.Interfaces;
|
||||
|
||||
namespace YABA.Service
|
||||
{
|
||||
public class MiscService : IMiscService
|
||||
{
|
||||
public MiscService() { }
|
||||
|
||||
public WebsiteMetaDataDTO GetWebsiteMetaData(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
var webClient = new WebClient();
|
||||
webClient.Headers.Add("user-agent", "API-Application");
|
||||
var sourceData = webClient.DownloadString(url);
|
||||
var title = Regex.Match(sourceData, @"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>", RegexOptions.IgnoreCase).Groups["Title"].Value;
|
||||
var description = string.Empty;
|
||||
|
||||
var getHtmlDoc = new HtmlWeb();
|
||||
var document = getHtmlDoc.Load(url);
|
||||
var metaTags = document.DocumentNode.SelectNodes("//meta");
|
||||
if (metaTags != null)
|
||||
{
|
||||
foreach (var sitetag in metaTags)
|
||||
{
|
||||
if (sitetag.Attributes["name"] != null && sitetag.Attributes["content"] != null && sitetag.Attributes["name"].Value == "description")
|
||||
{
|
||||
description = sitetag.Attributes["content"].Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new WebsiteMetaDataDTO
|
||||
{
|
||||
Title = title,
|
||||
Description = description
|
||||
};
|
||||
} catch(Exception)
|
||||
{
|
||||
return new WebsiteMetaDataDTO
|
||||
{
|
||||
Title = url,
|
||||
Description = string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
API/YABA.Service/TagsService.cs
Normal file
133
API/YABA.Service/TagsService.cs
Normal file
@ -0,0 +1,133 @@
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using YABA.Common.DTOs.Tags;
|
||||
using YABA.Common.Extensions;
|
||||
using YABA.Data.Context;
|
||||
using YABA.Data.Extensions;
|
||||
using YABA.Models;
|
||||
using YABA.Service.Interfaces;
|
||||
|
||||
namespace YABA.Service
|
||||
{
|
||||
public class TagsService : ITagsService
|
||||
{
|
||||
private readonly YABAReadOnlyContext _roContext;
|
||||
private readonly YABAReadWriteContext _rwContext;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public TagsService(
|
||||
YABAReadOnlyContext roContext,
|
||||
YABAReadWriteContext rwContext,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IMapper mapper)
|
||||
{
|
||||
_roContext = roContext;
|
||||
_rwContext = rwContext;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TagDTO>?> GetAll()
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
if (!await _roContext.Users.UserExistsAsync(currentUserId)) return null;
|
||||
|
||||
var activeUserTags = _roContext.Tags
|
||||
.Where(x => x.UserId == currentUserId)
|
||||
.ToList();
|
||||
|
||||
return _mapper.Map<IEnumerable<TagDTO>>(activeUserTags);
|
||||
}
|
||||
|
||||
public async Task<TagDTO?> Get(int id)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
if (!await _roContext.Users.UserExistsAsync(currentUserId)) return null;
|
||||
|
||||
var bookmark = await _roContext.Tags.FirstOrDefaultAsync(x => x.Id == id && x.UserId == currentUserId);
|
||||
return _mapper.Map<TagDTO>(bookmark);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TagDTO>?> UpsertTags(IEnumerable<TagDTO> tags)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
if (!await _roContext.Users.UserExistsAsync(currentUserId)) return null;
|
||||
|
||||
var tagsToUpsert = tags.Select(x => new Tag { Id = x.Id, Name = x.Name.ToLower(), IsHidden = x.IsHidden, UserId = currentUserId});
|
||||
_rwContext.Tags.UpsertRange(tagsToUpsert);
|
||||
await _rwContext.SaveChangesAsync();
|
||||
|
||||
var newlyUpsertedTags = _roContext.Tags.Where(x => tagsToUpsert.Select(x => x.Name).Contains(x.Name)).ToList();
|
||||
return _mapper.Map<IEnumerable<TagDTO>>(newlyUpsertedTags);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<int>?> DeleteTags(IEnumerable<int> ids)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
if (!await _roContext.Users.UserExistsAsync(currentUserId)) return null;
|
||||
|
||||
var entriesToDelete = _rwContext.Tags.Where(x => x.UserId == currentUserId && ids.Contains(x.Id)).ToList();
|
||||
_rwContext.Tags.RemoveRange(entriesToDelete);
|
||||
|
||||
if (await _rwContext.SaveChangesAsync() <= 0) return null;
|
||||
|
||||
return entriesToDelete.Select(x => x.Id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<int>?> HideTags(IEnumerable<int> ids)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
if (!await _roContext.Users.UserExistsAsync(currentUserId)) return null;
|
||||
|
||||
var entriesToHide = _rwContext.Tags.Where(x => x.UserId == currentUserId && ids.Contains(x.Id)).ToList();
|
||||
entriesToHide.ForEach((x) => { x.IsHidden = !x.IsHidden; });
|
||||
|
||||
if (await _rwContext.SaveChangesAsync() <= 0) return null;
|
||||
|
||||
return entriesToHide.Select(x => x.Id);
|
||||
}
|
||||
|
||||
public async Task<TagDTO?> UpsertTag(TagDTO tag) => (await UpsertTags(new List<TagDTO>() { tag }))?.FirstOrDefault();
|
||||
|
||||
public async Task<TagDTO?> CreateTag(CreateTagDTO request)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
|
||||
if(!(await _roContext.Users.UserExistsAsync(currentUserId))
|
||||
|| await _roContext.Tags.AnyAsync(x => x.UserId == currentUserId && x.Name.ToLower() == request.Name.ToLower()))
|
||||
return null;
|
||||
|
||||
var tag = _mapper.Map<Tag>(request);
|
||||
tag.UserId = currentUserId;
|
||||
|
||||
var newEntity = await _rwContext.Tags.AddAsync(tag);
|
||||
|
||||
return await _rwContext.SaveChangesAsync() > 0 ? _mapper.Map<TagDTO>(newEntity.Entity) : null;
|
||||
}
|
||||
|
||||
public async Task<TagDTO?> UpdateTag(int id, UpdateTagDTO request)
|
||||
{
|
||||
var currentUserId = GetCurrentUserId();
|
||||
var tag = await _rwContext.Tags.FirstOrDefaultAsync(x => x.UserId == currentUserId && x.Id == id);
|
||||
|
||||
if (tag == null) return null;
|
||||
|
||||
tag.Name = !string.IsNullOrEmpty(request.Name) ? request.Name.ToLower() : tag.Name;
|
||||
tag.IsHidden = request.IsHidden.HasValue ? request.IsHidden.Value : tag.IsHidden;
|
||||
|
||||
return await _rwContext.SaveChangesAsync() > 0 ? _mapper.Map<TagDTO>(tag) : null;
|
||||
}
|
||||
|
||||
private int GetCurrentUserId()
|
||||
{
|
||||
int.TryParse(_httpContextAccessor.HttpContext.User.Identity.GetUserId(), out int userId);
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
API/YABA.Service/UserService.cs
Normal file
79
API/YABA.Service/UserService.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using AutoMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using YABA.Common.DTOs;
|
||||
using YABA.Data.Context;
|
||||
using YABA.Models;
|
||||
using YABA.Service.Interfaces;
|
||||
|
||||
namespace YABA.Service
|
||||
{
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private readonly YABAReadOnlyContext _roContext;
|
||||
private readonly YABAReadWriteContext _context;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly ILogger<UserService> _logger;
|
||||
|
||||
public UserService (YABAReadOnlyContext roContext, YABAReadWriteContext context, IMapper mapper, ILogger<UserService> logger)
|
||||
{
|
||||
_roContext = roContext;
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool IsUserRegistered(string authProviderId)
|
||||
{
|
||||
return _roContext.Users.Any(x => x.Auth0Id == authProviderId);
|
||||
}
|
||||
|
||||
public async Task<UserDTO> RegisterUser(string authProviderId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsUserRegistered(authProviderId))
|
||||
{
|
||||
var userToRegister = new User
|
||||
{
|
||||
Auth0Id = authProviderId
|
||||
};
|
||||
|
||||
var registedUser = _context.Users.Add(userToRegister);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if(ex.InnerException is PostgresException &&
|
||||
((PostgresException)ex.InnerException).Code == "23505")
|
||||
{
|
||||
var postgresException = (PostgresException)ex.InnerException;
|
||||
_logger.LogWarning("Swallowing constraint violation: {@ConstraintName} for {@AuthProviderId}", postgresException.ConstraintName, authProviderId);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
return await Get(authProviderId);
|
||||
}
|
||||
|
||||
public async Task<UserDTO> Get(string authProviderId)
|
||||
{
|
||||
var user = await _roContext.Users.FirstOrDefaultAsync(x => x.Auth0Id == authProviderId);
|
||||
return _mapper.Map<UserDTO>(user);
|
||||
}
|
||||
|
||||
public int GetUserId(string authProviderId)
|
||||
{
|
||||
var user = _roContext.Users.FirstOrDefault(x => x.Auth0Id == authProviderId);
|
||||
return user != null ? user.Id : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
API/YABA.Service/YABA.Service.csproj
Normal file
22
API/YABA.Service/YABA.Service.csproj
Normal file
@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="12.0.1" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\YABA.Common\YABA.Common.csproj" />
|
||||
<ProjectReference Include="..\YABA.Data\YABA.Data.csproj" />
|
||||
<ProjectReference Include="..\YABA.Models\YABA.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user