From 675f6fc4d01c5a142486ed4772596710da9e1ffa Mon Sep 17 00:00:00 2001 From: Kasun333 Date: Tue, 17 Dec 2024 14:57:54 +0530 Subject: [PATCH 1/6] updated done --- api/Controllers/AccountController.cs | 98 ----- api/Controllers/CommentController.cs | 130 ------ api/Controllers/PortfolioController.cs | 111 ------ api/Controllers/StockController.cs | 106 ----- api/Data/ApplicationDBContext.cs | 57 --- api/Dtos/Account/LoginDto.cs | 16 - api/Dtos/Account/NewUserDto.cs | 14 - api/Dtos/Account/RegisterDto.cs | 19 - api/Dtos/Comment/CommentDto.cs | 17 - api/Dtos/Comment/CreateCommentDto.cs | 20 - api/Dtos/Comment/UpdateCommentRequestDto.cs | 20 - api/Dtos/Stock/CreateStockRequestDto.cs | 29 -- api/Dtos/Stock/FMPStock.cs | 47 --- api/Dtos/Stock/StockDto.cs | 20 - api/Dtos/Stock/UpdateStockRequestDto.cs | 29 -- api/Extensions/ClaimsExtensions.cs | 16 - api/Helpers/CommentQueryObject.cs | 13 - api/Helpers/QueryObject.cs | 17 - api/Interfaces/ICommentRepository.cs | 18 - api/Interfaces/IFMPService.cs | 13 - api/Interfaces/IPortfolioRepository.cs | 15 - api/Interfaces/IStockRepository.cs | 21 - api/Interfaces/ITokenService.cs | 13 - api/Mappers/CommentMapper.cs | 46 --- api/Mappers/StockMappers.cs | 53 --- .../20231202125736_init.Designer.cs | 107 ----- api/Migrations/20231202125736_init.cs | 69 ---- .../20231228175321_Identity.Designer.cs | 356 ----------------- api/Migrations/20231228175321_Identity.cs | 224 ----------- .../20231230165657_SeedRole.Designer.cs | 370 ------------------ api/Migrations/20231230165657_SeedRole.cs | 58 --- .../ApplicationDBContextModelSnapshot.cs | 367 ----------------- api/Models/AppUser.cs | 13 - api/Models/Comment.cs | 21 - api/Models/Portfolio.cs | 17 - api/Models/Stock.cs | 26 -- api/Program.cs | 103 +---- api/Repository/CommentRepository.cs | 79 ---- api/Repository/PortfolioRepository.cs | 56 --- api/Repository/StockRepository.cs | 107 ----- api/Service/FMPService.cs | 47 --- api/Service/TokenService.cs | 50 --- 42 files changed, 2 insertions(+), 3026 deletions(-) delete mode 100644 api/Controllers/AccountController.cs delete mode 100644 api/Controllers/CommentController.cs delete mode 100644 api/Controllers/PortfolioController.cs delete mode 100644 api/Controllers/StockController.cs delete mode 100644 api/Data/ApplicationDBContext.cs delete mode 100644 api/Dtos/Account/LoginDto.cs delete mode 100644 api/Dtos/Account/NewUserDto.cs delete mode 100644 api/Dtos/Account/RegisterDto.cs delete mode 100644 api/Dtos/Comment/CommentDto.cs delete mode 100644 api/Dtos/Comment/CreateCommentDto.cs delete mode 100644 api/Dtos/Comment/UpdateCommentRequestDto.cs delete mode 100644 api/Dtos/Stock/CreateStockRequestDto.cs delete mode 100644 api/Dtos/Stock/FMPStock.cs delete mode 100644 api/Dtos/Stock/StockDto.cs delete mode 100644 api/Dtos/Stock/UpdateStockRequestDto.cs delete mode 100644 api/Extensions/ClaimsExtensions.cs delete mode 100644 api/Helpers/CommentQueryObject.cs delete mode 100644 api/Helpers/QueryObject.cs delete mode 100644 api/Interfaces/ICommentRepository.cs delete mode 100644 api/Interfaces/IFMPService.cs delete mode 100644 api/Interfaces/IPortfolioRepository.cs delete mode 100644 api/Interfaces/IStockRepository.cs delete mode 100644 api/Interfaces/ITokenService.cs delete mode 100644 api/Mappers/CommentMapper.cs delete mode 100644 api/Mappers/StockMappers.cs delete mode 100644 api/Migrations/20231202125736_init.Designer.cs delete mode 100644 api/Migrations/20231202125736_init.cs delete mode 100644 api/Migrations/20231228175321_Identity.Designer.cs delete mode 100644 api/Migrations/20231228175321_Identity.cs delete mode 100644 api/Migrations/20231230165657_SeedRole.Designer.cs delete mode 100644 api/Migrations/20231230165657_SeedRole.cs delete mode 100644 api/Migrations/ApplicationDBContextModelSnapshot.cs delete mode 100644 api/Models/AppUser.cs delete mode 100644 api/Models/Comment.cs delete mode 100644 api/Models/Portfolio.cs delete mode 100644 api/Models/Stock.cs delete mode 100644 api/Repository/CommentRepository.cs delete mode 100644 api/Repository/PortfolioRepository.cs delete mode 100644 api/Repository/StockRepository.cs delete mode 100644 api/Service/FMPService.cs delete mode 100644 api/Service/TokenService.cs diff --git a/api/Controllers/AccountController.cs b/api/Controllers/AccountController.cs deleted file mode 100644 index 182fa135..00000000 --- a/api/Controllers/AccountController.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Dtos.Account; -using api.Interfaces; -using api.Models; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace api.Controllers -{ - [Route("api/account")] - [ApiController] - public class AccountController : ControllerBase - { - private readonly UserManager _userManager; - private readonly ITokenService _tokenService; - private readonly SignInManager _signinManager; - public AccountController(UserManager userManager, ITokenService tokenService, SignInManager signInManager) - { - _userManager = userManager; - _tokenService = tokenService; - _signinManager = signInManager; - } - - [HttpPost("login")] - public async Task Login(LoginDto loginDto) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var user = await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == loginDto.Username.ToLower()); - - if (user == null) return Unauthorized("Invalid username!"); - - var result = await _signinManager.CheckPasswordSignInAsync(user, loginDto.Password, false); - - if (!result.Succeeded) return Unauthorized("Username not found and/or password incorrect"); - - return Ok( - new NewUserDto - { - UserName = user.UserName, - Email = user.Email, - Token = _tokenService.CreateToken(user) - } - ); - } - - [HttpPost("register")] - public async Task Register([FromBody] RegisterDto registerDto) - { - try - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var appUser = new AppUser - { - UserName = registerDto.Username, - Email = registerDto.Email - }; - - var createdUser = await _userManager.CreateAsync(appUser, registerDto.Password); - - if (createdUser.Succeeded) - { - var roleResult = await _userManager.AddToRoleAsync(appUser, "User"); - if (roleResult.Succeeded) - { - return Ok( - new NewUserDto - { - UserName = appUser.UserName, - Email = appUser.Email, - Token = _tokenService.CreateToken(appUser) - } - ); - } - else - { - return StatusCode(500, roleResult.Errors); - } - } - else - { - return StatusCode(500, createdUser.Errors); - } - } - catch (Exception e) - { - return StatusCode(500, e); - } - } - } -} \ No newline at end of file diff --git a/api/Controllers/CommentController.cs b/api/Controllers/CommentController.cs deleted file mode 100644 index ad524c8f..00000000 --- a/api/Controllers/CommentController.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Dtos.Comment; -using api.Extensions; -using api.Helpers; -using api.Interfaces; -using api.Mappers; -using api.Models; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; - -namespace api.Controllers -{ - [Route("api/comment")] - [ApiController] - public class CommentController : ControllerBase - { - private readonly ICommentRepository _commentRepo; - private readonly IStockRepository _stockRepo; - private readonly UserManager _userManager; - private readonly IFMPService _fmpService; - public CommentController(ICommentRepository commentRepo, - IStockRepository stockRepo, UserManager userManager, - IFMPService fmpService) - { - _commentRepo = commentRepo; - _stockRepo = stockRepo; - _userManager = userManager; - _fmpService = fmpService; - } - - [HttpGet] - [Authorize] - public async Task GetAll([FromQuery] CommentQueryObject queryObject) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var comments = await _commentRepo.GetAllAsync(queryObject); - - var commentDto = comments.Select(s => s.ToCommentDto()); - - return Ok(commentDto); - } - - [HttpGet("{id:int}")] - public async Task GetById([FromRoute] int id) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var comment = await _commentRepo.GetByIdAsync(id); - - if (comment == null) - { - return NotFound(); - } - - return Ok(comment.ToCommentDto()); - } - - [HttpPost] - [Route("{symbol:alpha}")] - public async Task Create([FromRoute] string symbol, CreateCommentDto commentDto) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var stock = await _stockRepo.GetBySymbolAsync(symbol); - - if (stock == null) - { - stock = await _fmpService.FindStockBySymbolAsync(symbol); - if (stock == null) - { - return BadRequest("Stock does not exists"); - } - else - { - await _stockRepo.CreateAsync(stock); - } - } - - var username = User.GetUsername(); - var appUser = await _userManager.FindByNameAsync(username); - - var commentModel = commentDto.ToCommentFromCreate(stock.Id); - commentModel.AppUserId = appUser.Id; - await _commentRepo.CreateAsync(commentModel); - return CreatedAtAction(nameof(GetById), new { id = commentModel.Id }, commentModel.ToCommentDto()); - } - - [HttpPut] - [Route("{id:int}")] - public async Task Update([FromRoute] int id, [FromBody] UpdateCommentRequestDto updateDto) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var comment = await _commentRepo.UpdateAsync(id, updateDto.ToCommentFromUpdate(id)); - - if (comment == null) - { - return NotFound("Comment not found"); - } - - return Ok(comment.ToCommentDto()); - } - - [HttpDelete] - [Route("{id:int}")] - public async Task Delete([FromRoute] int id) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var commentModel = await _commentRepo.DeleteAsync(id); - - if (commentModel == null) - { - return NotFound("Comment does not exist"); - } - - return Ok(commentModel); - } - } -} \ No newline at end of file diff --git a/api/Controllers/PortfolioController.cs b/api/Controllers/PortfolioController.cs deleted file mode 100644 index 57e02fff..00000000 --- a/api/Controllers/PortfolioController.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Extensions; -using api.Interfaces; -using api.Models; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; - -namespace api.Controllers -{ - [Route("api/portfolio")] - [ApiController] - public class PortfolioController : ControllerBase - { - private readonly UserManager _userManager; - private readonly IStockRepository _stockRepo; - private readonly IPortfolioRepository _portfolioRepo; - private readonly IFMPService _fmpService; - public PortfolioController(UserManager userManager, - IStockRepository stockRepo, IPortfolioRepository portfolioRepo, - IFMPService fmpService) - { - _userManager = userManager; - _stockRepo = stockRepo; - _portfolioRepo = portfolioRepo; - _fmpService = fmpService; - } - - [HttpGet] - [Authorize] - public async Task GetUserPortfolio() - { - var username = User.GetUsername(); - var appUser = await _userManager.FindByNameAsync(username); - var userPortfolio = await _portfolioRepo.GetUserPortfolio(appUser); - return Ok(userPortfolio); - } - - [HttpPost] - [Authorize] - public async Task AddPortfolio(string symbol) - { - var username = User.GetUsername(); - var appUser = await _userManager.FindByNameAsync(username); - var stock = await _stockRepo.GetBySymbolAsync(symbol); - - if (stock == null) - { - stock = await _fmpService.FindStockBySymbolAsync(symbol); - if (stock == null) - { - return BadRequest("Stock does not exists"); - } - else - { - await _stockRepo.CreateAsync(stock); - } - } - - if (stock == null) return BadRequest("Stock not found"); - - var userPortfolio = await _portfolioRepo.GetUserPortfolio(appUser); - - if (userPortfolio.Any(e => e.Symbol.ToLower() == symbol.ToLower())) return BadRequest("Cannot add same stock to portfolio"); - - var portfolioModel = new Portfolio - { - StockId = stock.Id, - AppUserId = appUser.Id - }; - - await _portfolioRepo.CreateAsync(portfolioModel); - - if (portfolioModel == null) - { - return StatusCode(500, "Could not create"); - } - else - { - return Created(); - } - } - - [HttpDelete] - [Authorize] - public async Task DeletePortfolio(string symbol) - { - var username = User.GetUsername(); - var appUser = await _userManager.FindByNameAsync(username); - - var userPortfolio = await _portfolioRepo.GetUserPortfolio(appUser); - - var filteredStock = userPortfolio.Where(s => s.Symbol.ToLower() == symbol.ToLower()).ToList(); - - if (filteredStock.Count() == 1) - { - await _portfolioRepo.DeletePortfolio(appUser, symbol); - } - else - { - return BadRequest("Stock not in your portfolio"); - } - - return Ok(); - } - - } -} \ No newline at end of file diff --git a/api/Controllers/StockController.cs b/api/Controllers/StockController.cs deleted file mode 100644 index eed8ce86..00000000 --- a/api/Controllers/StockController.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Data; -using api.Dtos.Stock; -using api.Helpers; -using api.Interfaces; -using api.Mappers; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace api.Controllers -{ - [Route("api/stock")] - [ApiController] - public class StockController : ControllerBase - { - private readonly ApplicationDBContext _context; - private readonly IStockRepository _stockRepo; - public StockController(ApplicationDBContext context, IStockRepository stockRepo) - { - _stockRepo = stockRepo; - _context = context; - } - - [HttpGet] - [Authorize] - public async Task GetAll([FromQuery] QueryObject query) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var stocks = await _stockRepo.GetAllAsync(query); - - var stockDto = stocks.Select(s => s.ToStockDto()).ToList(); - - return Ok(stockDto); - } - - [HttpGet("{id:int}")] - public async Task GetById([FromRoute] int id) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var stock = await _stockRepo.GetByIdAsync(id); - - if (stock == null) - { - return NotFound(); - } - - return Ok(stock.ToStockDto()); - } - - [HttpPost] - public async Task Create([FromBody] CreateStockRequestDto stockDto) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var stockModel = stockDto.ToStockFromCreateDTO(); - - await _stockRepo.CreateAsync(stockModel); - - return CreatedAtAction(nameof(GetById), new { id = stockModel.Id }, stockModel.ToStockDto()); - } - - [HttpPut] - [Route("{id:int}")] - public async Task Update([FromRoute] int id, [FromBody] UpdateStockRequestDto updateDto) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var stockModel = await _stockRepo.UpdateAsync(id, updateDto); - - if (stockModel == null) - { - return NotFound(); - } - - return Ok(stockModel.ToStockDto()); - } - - [HttpDelete] - [Route("{id:int}")] - public async Task Delete([FromRoute] int id) - { - if (!ModelState.IsValid) - return BadRequest(ModelState); - - var stockModel = await _stockRepo.DeleteAsync(id); - - if (stockModel == null) - { - return NotFound(); - } - - return NoContent(); - } - - } -} \ No newline at end of file diff --git a/api/Data/ApplicationDBContext.cs b/api/Data/ApplicationDBContext.cs deleted file mode 100644 index aab429f7..00000000 --- a/api/Data/ApplicationDBContext.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Models; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; - -namespace api.Data -{ - public class ApplicationDBContext : IdentityDbContext - { - public ApplicationDBContext(DbContextOptions dbContextOptions) - : base(dbContextOptions) - { - - } - - public DbSet Stocks { get; set; } - public DbSet Comments { get; set; } - public DbSet Portfolios { get; set; } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - - builder.Entity(x => x.HasKey(p => new { p.AppUserId, p.StockId })); - - builder.Entity() - .HasOne(u => u.AppUser) - .WithMany(u => u.Portfolios) - .HasForeignKey(p => p.AppUserId); - - builder.Entity() - .HasOne(u => u.Stock) - .WithMany(u => u.Portfolios) - .HasForeignKey(p => p.StockId); - - - List roles = new List - { - new IdentityRole - { - Name = "Admin", - NormalizedName = "ADMIN" - }, - new IdentityRole - { - Name = "User", - NormalizedName = "USER" - }, - }; - builder.Entity().HasData(roles); - } - } -} \ No newline at end of file diff --git a/api/Dtos/Account/LoginDto.cs b/api/Dtos/Account/LoginDto.cs deleted file mode 100644 index 78b3fda5..00000000 --- a/api/Dtos/Account/LoginDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Account -{ - public class LoginDto - { - [Required] - public string Username { get; set; } - [Required] - public string Password { get; set; } - } -} \ No newline at end of file diff --git a/api/Dtos/Account/NewUserDto.cs b/api/Dtos/Account/NewUserDto.cs deleted file mode 100644 index 37648db9..00000000 --- a/api/Dtos/Account/NewUserDto.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Account -{ - public class NewUserDto - { - public string UserName { get; set; } - public string Email { get; set; } - public string Token { get; set; } - } -} \ No newline at end of file diff --git a/api/Dtos/Account/RegisterDto.cs b/api/Dtos/Account/RegisterDto.cs deleted file mode 100644 index 0599460b..00000000 --- a/api/Dtos/Account/RegisterDto.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Account -{ - public class RegisterDto - { - [Required] - public string? Username { get; set; } - [Required] - [EmailAddress] - public string? Email { get; set; } - [Required] - public string? Password { get; set; } - } -} \ No newline at end of file diff --git a/api/Dtos/Comment/CommentDto.cs b/api/Dtos/Comment/CommentDto.cs deleted file mode 100644 index 48333cef..00000000 --- a/api/Dtos/Comment/CommentDto.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Comment -{ - public class CommentDto - { - public int Id { get; set; } - public string Title { get; set; } = string.Empty; - public string Content { get; set; } = string.Empty; - public DateTime CreatedOn { get; set; } = DateTime.Now; - public string CreatedBy { get; set; } = string.Empty; - public int? StockId { get; set; } - } -} \ No newline at end of file diff --git a/api/Dtos/Comment/CreateCommentDto.cs b/api/Dtos/Comment/CreateCommentDto.cs deleted file mode 100644 index 3dd6da5a..00000000 --- a/api/Dtos/Comment/CreateCommentDto.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Comment -{ - public class CreateCommentDto - { - [Required] - [MinLength(5, ErrorMessage = "Title must be 5 characters")] - [MaxLength(280, ErrorMessage = "Title cannot be over 280 characters")] - public string Title { get; set; } = string.Empty; - [Required] - [MinLength(5, ErrorMessage = "Content must be 5 characters")] - [MaxLength(280, ErrorMessage = "Content cannot be over 280 characters")] - public string Content { get; set; } = string.Empty; - } -} \ No newline at end of file diff --git a/api/Dtos/Comment/UpdateCommentRequestDto.cs b/api/Dtos/Comment/UpdateCommentRequestDto.cs deleted file mode 100644 index 6080eb6c..00000000 --- a/api/Dtos/Comment/UpdateCommentRequestDto.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Comment -{ - public class UpdateCommentRequestDto - { - [Required] - [MinLength(5, ErrorMessage = "Title must be 5 characters")] - [MaxLength(280, ErrorMessage = "Title cannot be over 280 characters")] - public string Title { get; set; } = string.Empty; - [Required] - [MinLength(5, ErrorMessage = "Content must be 5 characters")] - [MaxLength(280, ErrorMessage = "Content cannot be over 280 characters")] - public string Content { get; set; } = string.Empty; - } -} \ No newline at end of file diff --git a/api/Dtos/Stock/CreateStockRequestDto.cs b/api/Dtos/Stock/CreateStockRequestDto.cs deleted file mode 100644 index 14ae7d76..00000000 --- a/api/Dtos/Stock/CreateStockRequestDto.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Stock -{ - public class CreateStockRequestDto - { - [Required] - [MaxLength(10, ErrorMessage = "Symbol cannot be over 10 over characters")] - public string Symbol { get; set; } = string.Empty; - [Required] - [MaxLength(10, ErrorMessage = "Company Name cannot be over 10 over characters")] - public string CompanyName { get; set; } = string.Empty; - [Required] - [Range(1, 1000000000)] - public decimal Purchase { get; set; } - [Required] - [Range(0.001, 100)] - public decimal LastDiv { get; set; } - [Required] - [MaxLength(10, ErrorMessage = "Industry cannot be over 10 characters")] - public string Industry { get; set; } = string.Empty; - [Range(1, 5000000000)] - public long MarketCap { get; set; } - } -} \ No newline at end of file diff --git a/api/Dtos/Stock/FMPStock.cs b/api/Dtos/Stock/FMPStock.cs deleted file mode 100644 index 2cc95547..00000000 --- a/api/Dtos/Stock/FMPStock.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Stock -{ - public class FMPStock - { - public string symbol { get; set; } - public double price { get; set; } - public double beta { get; set; } - public int volAvg { get; set; } - public long mktCap { get; set; } - public double lastDiv { get; set; } - public string range { get; set; } - public double changes { get; set; } - public string companyName { get; set; } - public string currency { get; set; } - public string cik { get; set; } - public string isin { get; set; } - public string cusip { get; set; } - public string exchange { get; set; } - public string exchangeShortName { get; set; } - public string industry { get; set; } - public string website { get; set; } - public string description { get; set; } - public string ceo { get; set; } - public string sector { get; set; } - public string country { get; set; } - public string fullTimeEmployees { get; set; } - public string phone { get; set; } - public string address { get; set; } - public string city { get; set; } - public string state { get; set; } - public string zip { get; set; } - public double dcfDiff { get; set; } - public double dcf { get; set; } - public string image { get; set; } - public string ipoDate { get; set; } - public bool defaultImage { get; set; } - public bool isEtf { get; set; } - public bool isActivelyTrading { get; set; } - public bool isAdr { get; set; } - public bool isFund { get; set; } - } -} \ No newline at end of file diff --git a/api/Dtos/Stock/StockDto.cs b/api/Dtos/Stock/StockDto.cs deleted file mode 100644 index f525b8d6..00000000 --- a/api/Dtos/Stock/StockDto.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Dtos.Comment; - -namespace api.Dtos.Stock -{ - public class StockDto - { - public int Id { get; set; } - public string Symbol { get; set; } = string.Empty; - public string CompanyName { get; set; } = string.Empty; - public decimal Purchase { get; set; } - public decimal LastDiv { get; set; } - public string Industry { get; set; } = string.Empty; - public long MarketCap { get; set; } - public List Comments { get; set; } - } -} \ No newline at end of file diff --git a/api/Dtos/Stock/UpdateStockRequestDto.cs b/api/Dtos/Stock/UpdateStockRequestDto.cs deleted file mode 100644 index a14421d5..00000000 --- a/api/Dtos/Stock/UpdateStockRequestDto.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Dtos.Stock -{ - public class UpdateStockRequestDto - { - [Required] - [MaxLength(10, ErrorMessage = "Symbol cannot be over 10 over characters")] - public string Symbol { get; set; } = string.Empty; - [Required] - [MaxLength(10, ErrorMessage = "Company Name cannot be over 10 over characters")] - public string CompanyName { get; set; } = string.Empty; - [Required] - [Range(1, 1000000000)] - public decimal Purchase { get; set; } - [Required] - [Range(0.001, 100)] - public decimal LastDiv { get; set; } - [Required] - [MaxLength(10, ErrorMessage = "Industry cannot be over 10 characters")] - public string Industry { get; set; } = string.Empty; - [Range(1, 5000000000)] - public long MarketCap { get; set; } - } -} \ No newline at end of file diff --git a/api/Extensions/ClaimsExtensions.cs b/api/Extensions/ClaimsExtensions.cs deleted file mode 100644 index eb895f34..00000000 --- a/api/Extensions/ClaimsExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace api.Extensions -{ - public static class ClaimsExtensions - { - public static string GetUsername(this ClaimsPrincipal user) - { - return user.Claims.SingleOrDefault(x => x.Type.Equals("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname")).Value; - } - } -} \ No newline at end of file diff --git a/api/Helpers/CommentQueryObject.cs b/api/Helpers/CommentQueryObject.cs deleted file mode 100644 index acbc3786..00000000 --- a/api/Helpers/CommentQueryObject.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Helpers -{ - public class CommentQueryObject - { - public string Symbol { get; set; } - public bool IsDecsending { get; set; } = true; - } -} \ No newline at end of file diff --git a/api/Helpers/QueryObject.cs b/api/Helpers/QueryObject.cs deleted file mode 100644 index 3e292508..00000000 --- a/api/Helpers/QueryObject.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Helpers -{ - public class QueryObject - { - public string? Symbol { get; set; } = null; - public string? CompanyName { get; set; } = null; - public string? SortBy { get; set; } = null; - public bool IsDecsending { get; set; } = false; - public int PageNumber { get; set; } = 1; - public int PageSize { get; set; } = 20; - } -} \ No newline at end of file diff --git a/api/Interfaces/ICommentRepository.cs b/api/Interfaces/ICommentRepository.cs deleted file mode 100644 index c64179a1..00000000 --- a/api/Interfaces/ICommentRepository.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Helpers; -using api.Models; - -namespace api.Interfaces -{ - public interface ICommentRepository - { - Task> GetAllAsync(CommentQueryObject queryObject); - Task GetByIdAsync(int id); - Task CreateAsync(Comment commentModel); - Task UpdateAsync(int id, Comment commentModel); - Task DeleteAsync(int id); - } -} \ No newline at end of file diff --git a/api/Interfaces/IFMPService.cs b/api/Interfaces/IFMPService.cs deleted file mode 100644 index 159d0756..00000000 --- a/api/Interfaces/IFMPService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Models; - -namespace api.Interfaces -{ - public interface IFMPService - { - Task FindStockBySymbolAsync(string symbol); - } -} \ No newline at end of file diff --git a/api/Interfaces/IPortfolioRepository.cs b/api/Interfaces/IPortfolioRepository.cs deleted file mode 100644 index 31fb4f09..00000000 --- a/api/Interfaces/IPortfolioRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Models; - -namespace api.Interfaces -{ - public interface IPortfolioRepository - { - Task> GetUserPortfolio(AppUser user); - Task CreateAsync(Portfolio portfolio); - Task DeletePortfolio(AppUser appUser, string symbol); - } -} \ No newline at end of file diff --git a/api/Interfaces/IStockRepository.cs b/api/Interfaces/IStockRepository.cs deleted file mode 100644 index 83e6667a..00000000 --- a/api/Interfaces/IStockRepository.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Dtos.Stock; -using api.Helpers; -using api.Models; - -namespace api.Interfaces -{ - public interface IStockRepository - { - Task> GetAllAsync(QueryObject query); - Task GetByIdAsync(int id); - Task GetBySymbolAsync(string symbol); - Task CreateAsync(Stock stockModel); - Task UpdateAsync(int id, UpdateStockRequestDto stockDto); - Task DeleteAsync(int id); - Task StockExists(int id); - } -} \ No newline at end of file diff --git a/api/Interfaces/ITokenService.cs b/api/Interfaces/ITokenService.cs deleted file mode 100644 index dc185e4b..00000000 --- a/api/Interfaces/ITokenService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Models; - -namespace api.Interfaces -{ - public interface ITokenService - { - string CreateToken(AppUser user); - } -} \ No newline at end of file diff --git a/api/Mappers/CommentMapper.cs b/api/Mappers/CommentMapper.cs deleted file mode 100644 index 44d1b8c5..00000000 --- a/api/Mappers/CommentMapper.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Dtos.Comment; -using api.Models; - -namespace api.Mappers -{ - public static class CommentMapper - { - public static CommentDto ToCommentDto(this Comment commentModel) - { - return new CommentDto - { - Id = commentModel.Id, - Title = commentModel.Title, - Content = commentModel.Content, - CreatedOn = commentModel.CreatedOn, - CreatedBy = commentModel.AppUser.UserName, - StockId = commentModel.StockId - }; - } - - public static Comment ToCommentFromCreate(this CreateCommentDto commentDto, int stockId) - { - return new Comment - { - Title = commentDto.Title, - Content = commentDto.Content, - StockId = stockId - }; - } - - public static Comment ToCommentFromUpdate(this UpdateCommentRequestDto commentDto, int stockId) - { - return new Comment - { - Title = commentDto.Title, - Content = commentDto.Content, - StockId = stockId - }; - } - - } -} \ No newline at end of file diff --git a/api/Mappers/StockMappers.cs b/api/Mappers/StockMappers.cs deleted file mode 100644 index e6b94b47..00000000 --- a/api/Mappers/StockMappers.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Dtos.Stock; -using api.Models; - -namespace api.Mappers -{ - public static class StockMappers - { - public static StockDto ToStockDto(this Stock stockModel) - { - return new StockDto - { - Id = stockModel.Id, - Symbol = stockModel.Symbol, - CompanyName = stockModel.CompanyName, - Purchase = stockModel.Purchase, - LastDiv = stockModel.LastDiv, - Industry = stockModel.Industry, - MarketCap = stockModel.MarketCap, - Comments = stockModel.Comments.Select(c => c.ToCommentDto()).ToList() - }; - } - - public static Stock ToStockFromCreateDTO(this CreateStockRequestDto stockDto) - { - return new Stock - { - Symbol = stockDto.Symbol, - CompanyName = stockDto.CompanyName, - Purchase = stockDto.Purchase, - LastDiv = stockDto.LastDiv, - Industry = stockDto.Industry, - MarketCap = stockDto.MarketCap - }; - } - - public static Stock ToStockFromFMP(this FMPStock fmpStock) - { - return new Stock - { - Symbol = fmpStock.symbol, - CompanyName = fmpStock.companyName, - Purchase = (decimal)fmpStock.price, - LastDiv = (decimal)fmpStock.lastDiv, - Industry = fmpStock.industry, - MarketCap = fmpStock.mktCap - }; - } - } -} \ No newline at end of file diff --git a/api/Migrations/20231202125736_init.Designer.cs b/api/Migrations/20231202125736_init.Designer.cs deleted file mode 100644 index 33723d16..00000000 --- a/api/Migrations/20231202125736_init.Designer.cs +++ /dev/null @@ -1,107 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using api.Data; - -#nullable disable - -namespace api.Migrations -{ - [DbContext(typeof(ApplicationDBContext))] - [Migration("20231202125736_init")] - partial class init - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("api.Models.Comment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("StockId") - .HasColumnType("int"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("StockId"); - - b.ToTable("Comments"); - }); - - modelBuilder.Entity("api.Models.Stock", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CompanyName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Industry") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("LastDiv") - .HasColumnType("decimal(18,2)"); - - b.Property("MarketCap") - .HasColumnType("bigint"); - - b.Property("Purchase") - .HasColumnType("decimal(18,2)"); - - b.Property("Symbol") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Stocks"); - }); - - modelBuilder.Entity("api.Models.Comment", b => - { - b.HasOne("api.Models.Stock", "Stock") - .WithMany("Comments") - .HasForeignKey("StockId"); - - b.Navigation("Stock"); - }); - - modelBuilder.Entity("api.Models.Stock", b => - { - b.Navigation("Comments"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/api/Migrations/20231202125736_init.cs b/api/Migrations/20231202125736_init.cs deleted file mode 100644 index 8887e0b5..00000000 --- a/api/Migrations/20231202125736_init.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace api.Migrations -{ - /// - public partial class init : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Stocks", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Symbol = table.Column(type: "nvarchar(max)", nullable: false), - CompanyName = table.Column(type: "nvarchar(max)", nullable: false), - Purchase = table.Column(type: "decimal(18,2)", nullable: false), - LastDiv = table.Column(type: "decimal(18,2)", nullable: false), - Industry = table.Column(type: "nvarchar(max)", nullable: false), - MarketCap = table.Column(type: "bigint", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Stocks", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Comments", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Title = table.Column(type: "nvarchar(max)", nullable: false), - Content = table.Column(type: "nvarchar(max)", nullable: false), - CreatedOn = table.Column(type: "datetime2", nullable: false), - StockId = table.Column(type: "int", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Comments", x => x.Id); - table.ForeignKey( - name: "FK_Comments_Stocks_StockId", - column: x => x.StockId, - principalTable: "Stocks", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_Comments_StockId", - table: "Comments", - column: "StockId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Comments"); - - migrationBuilder.DropTable( - name: "Stocks"); - } - } -} diff --git a/api/Migrations/20231228175321_Identity.Designer.cs b/api/Migrations/20231228175321_Identity.Designer.cs deleted file mode 100644 index 948a0d52..00000000 --- a/api/Migrations/20231228175321_Identity.Designer.cs +++ /dev/null @@ -1,356 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using api.Data; - -#nullable disable - -namespace api.Migrations -{ - [DbContext(typeof(ApplicationDBContext))] - [Migration("20231228175321_Identity")] - partial class Identity - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("api.Models.AppUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("api.Models.Comment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("StockId") - .HasColumnType("int"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("StockId"); - - b.ToTable("Comments"); - }); - - modelBuilder.Entity("api.Models.Stock", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CompanyName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Industry") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("LastDiv") - .HasColumnType("decimal(18,2)"); - - b.Property("MarketCap") - .HasColumnType("bigint"); - - b.Property("Purchase") - .HasColumnType("decimal(18,2)"); - - b.Property("Symbol") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Stocks"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("api.Models.Comment", b => - { - b.HasOne("api.Models.Stock", "Stock") - .WithMany("Comments") - .HasForeignKey("StockId"); - - b.Navigation("Stock"); - }); - - modelBuilder.Entity("api.Models.Stock", b => - { - b.Navigation("Comments"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/api/Migrations/20231228175321_Identity.cs b/api/Migrations/20231228175321_Identity.cs deleted file mode 100644 index 4dca9007..00000000 --- a/api/Migrations/20231228175321_Identity.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace api.Migrations -{ - /// - public partial class Identity : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AspNetRoles", - columns: table => new - { - Id = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetUsers", - columns: table => new - { - Id = table.Column(type: "nvarchar(450)", nullable: false), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "bit", nullable: false), - PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), - SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false), - LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), - LockoutEnabled = table.Column(type: "bit", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetRoleClaims", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(type: "nvarchar(450)", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserClaims", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(type: "nvarchar(450)", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetUserClaims_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserLogins", - columns: table => new - { - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), - ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), - UserId = table.Column(type: "nvarchar(450)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_AspNetUserLogins_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserRoles", - columns: table => new - { - UserId = table.Column(type: "nvarchar(450)", nullable: false), - RoleId = table.Column(type: "nvarchar(450)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserTokens", - columns: table => new - { - UserId = table.Column(type: "nvarchar(450)", nullable: false), - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - Value = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AspNetUserTokens_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AspNetRoleClaims_RoleId", - table: "AspNetRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "AspNetRoles", - column: "NormalizedName", - unique: true, - filter: "[NormalizedName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserClaims_UserId", - table: "AspNetUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserLogins_UserId", - table: "AspNetUserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserRoles_RoleId", - table: "AspNetUserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "AspNetUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "AspNetUsers", - column: "NormalizedUserName", - unique: true, - filter: "[NormalizedUserName] IS NOT NULL"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AspNetRoleClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserLogins"); - - migrationBuilder.DropTable( - name: "AspNetUserRoles"); - - migrationBuilder.DropTable( - name: "AspNetUserTokens"); - - migrationBuilder.DropTable( - name: "AspNetRoles"); - - migrationBuilder.DropTable( - name: "AspNetUsers"); - } - } -} diff --git a/api/Migrations/20231230165657_SeedRole.Designer.cs b/api/Migrations/20231230165657_SeedRole.Designer.cs deleted file mode 100644 index 283c3157..00000000 --- a/api/Migrations/20231230165657_SeedRole.Designer.cs +++ /dev/null @@ -1,370 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using api.Data; - -#nullable disable - -namespace api.Migrations -{ - [DbContext(typeof(ApplicationDBContext))] - [Migration("20231230165657_SeedRole")] - partial class SeedRole - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles", (string)null); - - b.HasData( - new - { - Id = "79f392bf-21e3-4e59-a899-f2a066f3884a", - Name = "Admin", - NormalizedName = "ADMIN" - }, - new - { - Id = "5849bcbd-fa3c-46ad-8ac1-00ff8cd9a6d7", - Name = "User", - NormalizedName = "USER" - }); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("api.Models.AppUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("api.Models.Comment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("StockId") - .HasColumnType("int"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("StockId"); - - b.ToTable("Comments"); - }); - - modelBuilder.Entity("api.Models.Stock", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CompanyName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Industry") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("LastDiv") - .HasColumnType("decimal(18,2)"); - - b.Property("MarketCap") - .HasColumnType("bigint"); - - b.Property("Purchase") - .HasColumnType("decimal(18,2)"); - - b.Property("Symbol") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Stocks"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("api.Models.Comment", b => - { - b.HasOne("api.Models.Stock", "Stock") - .WithMany("Comments") - .HasForeignKey("StockId"); - - b.Navigation("Stock"); - }); - - modelBuilder.Entity("api.Models.Stock", b => - { - b.Navigation("Comments"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/api/Migrations/20231230165657_SeedRole.cs b/api/Migrations/20231230165657_SeedRole.cs deleted file mode 100644 index 8ea2ab98..00000000 --- a/api/Migrations/20231230165657_SeedRole.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional - -namespace api.Migrations -{ - /// - public partial class SeedRole : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DeleteData( - table: "AspNetRoles", - keyColumn: "Id", - keyValue: "7bdc0619-6bfd-476d-804d-7add9a1f75af"); - - migrationBuilder.DeleteData( - table: "AspNetRoles", - keyColumn: "Id", - keyValue: "8811e782-4262-4abf-b967-ef2ace5505c6"); - - migrationBuilder.InsertData( - table: "AspNetRoles", - columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, - values: new object[,] - { - { "5849bcbd-fa3c-46ad-8ac1-00ff8cd9a6d7", null, "User", "USER" }, - { "79f392bf-21e3-4e59-a899-f2a066f3884a", null, "Admin", "ADMIN" } - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DeleteData( - table: "AspNetRoles", - keyColumn: "Id", - keyValue: "5849bcbd-fa3c-46ad-8ac1-00ff8cd9a6d7"); - - migrationBuilder.DeleteData( - table: "AspNetRoles", - keyColumn: "Id", - keyValue: "79f392bf-21e3-4e59-a899-f2a066f3884a"); - - migrationBuilder.InsertData( - table: "AspNetRoles", - columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, - values: new object[,] - { - { "7bdc0619-6bfd-476d-804d-7add9a1f75af", null, "User", "USER" }, - { "8811e782-4262-4abf-b967-ef2ace5505c6", null, "Admin", "ADMIN" } - }); - } - } -} diff --git a/api/Migrations/ApplicationDBContextModelSnapshot.cs b/api/Migrations/ApplicationDBContextModelSnapshot.cs deleted file mode 100644 index dbc498ed..00000000 --- a/api/Migrations/ApplicationDBContextModelSnapshot.cs +++ /dev/null @@ -1,367 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using api.Data; - -#nullable disable - -namespace api.Migrations -{ - [DbContext(typeof(ApplicationDBContext))] - partial class ApplicationDBContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles", (string)null); - - b.HasData( - new - { - Id = "79f392bf-21e3-4e59-a899-f2a066f3884a", - Name = "Admin", - NormalizedName = "ADMIN" - }, - new - { - Id = "5849bcbd-fa3c-46ad-8ac1-00ff8cd9a6d7", - Name = "User", - NormalizedName = "USER" - }); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("api.Models.AppUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("api.Models.Comment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("StockId") - .HasColumnType("int"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("StockId"); - - b.ToTable("Comments"); - }); - - modelBuilder.Entity("api.Models.Stock", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CompanyName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Industry") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("LastDiv") - .HasColumnType("decimal(18,2)"); - - b.Property("MarketCap") - .HasColumnType("bigint"); - - b.Property("Purchase") - .HasColumnType("decimal(18,2)"); - - b.Property("Symbol") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Stocks"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("api.Models.AppUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("api.Models.Comment", b => - { - b.HasOne("api.Models.Stock", "Stock") - .WithMany("Comments") - .HasForeignKey("StockId"); - - b.Navigation("Stock"); - }); - - modelBuilder.Entity("api.Models.Stock", b => - { - b.Navigation("Comments"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/api/Models/AppUser.cs b/api/Models/AppUser.cs deleted file mode 100644 index 2692b0eb..00000000 --- a/api/Models/AppUser.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Identity; - -namespace api.Models -{ - public class AppUser : IdentityUser - { - public List Portfolios { get; set; } = new List(); - } -} \ No newline at end of file diff --git a/api/Models/Comment.cs b/api/Models/Comment.cs deleted file mode 100644 index c2618fb9..00000000 --- a/api/Models/Comment.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Models -{ - [Table("Comments")] - public class Comment - { - public int Id { get; set; } - public string Title { get; set; } = string.Empty; - public string Content { get; set; } = string.Empty; - public DateTime CreatedOn { get; set; } = DateTime.Now; - public int? StockId { get; set; } - public Stock? Stock { get; set; } - public string AppUserId { get; set; } - public AppUser AppUser { get; set; } - } -} \ No newline at end of file diff --git a/api/Models/Portfolio.cs b/api/Models/Portfolio.cs deleted file mode 100644 index 29ec73e3..00000000 --- a/api/Models/Portfolio.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Models -{ - [Table("Portfolios")] - public class Portfolio - { - public string AppUserId { get; set; } - public int StockId { get; set; } - public AppUser AppUser { get; set; } - public Stock Stock { get; set; } - } -} \ No newline at end of file diff --git a/api/Models/Stock.cs b/api/Models/Stock.cs deleted file mode 100644 index d34120d8..00000000 --- a/api/Models/Stock.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Threading.Tasks; - -namespace api.Models -{ - [Table("Stocks")] - public class Stock - { - public int Id { get; set; } - public string Symbol { get; set; } = string.Empty; - public string CompanyName { get; set; } = string.Empty; - [Column(TypeName = "decimal(18,2)")] - public decimal Purchase { get; set; } - [Column(TypeName = "decimal(18,2)")] - public decimal LastDiv { get; set; } - public string Industry { get; set; } = string.Empty; - public long MarketCap { get; set; } - - public List Comments { get; set; } = new List(); - public List Portfolios { get; set; } = new List(); - - } -} \ No newline at end of file diff --git a/api/Program.cs b/api/Program.cs index bdc5fd66..a0711f81 100644 --- a/api/Program.cs +++ b/api/Program.cs @@ -1,101 +1,13 @@ -using api.Data; -using api.Interfaces; -using api.Models; -using api.Repository; -using api.Service; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.IdentityModel.Tokens; -using Microsoft.OpenApi.Models; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddControllers(); + builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); -builder.Services.AddSwaggerGen(option => -{ - option.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo API", Version = "v1" }); - option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme - { - In = ParameterLocation.Header, - Description = "Please enter a valid token", - Name = "Authorization", - Type = SecuritySchemeType.Http, - BearerFormat = "JWT", - Scheme = "Bearer" - }); - option.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type=ReferenceType.SecurityScheme, - Id="Bearer" - } - }, - new string[]{} - } - }); -}); - -builder.Services.AddControllers().AddNewtonsoftJson(options => -{ - options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; -}); - -builder.Services.AddDbContext(options => -{ - options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")); -}); - -builder.Services.AddIdentity(options => -{ - options.Password.RequireDigit = true; - options.Password.RequireLowercase = true; - options.Password.RequireUppercase = true; - options.Password.RequireNonAlphanumeric = true; - options.Password.RequiredLength = 12; -}) -.AddEntityFrameworkStores(); - -builder.Services.AddAuthentication(options => -{ - options.DefaultAuthenticateScheme = - options.DefaultChallengeScheme = - options.DefaultForbidScheme = - options.DefaultScheme = - options.DefaultSignInScheme = - options.DefaultSignOutScheme = JwtBearerDefaults.AuthenticationScheme; -}).AddJwtBearer(options => -{ - options.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuer = true, - ValidIssuer = builder.Configuration["JWT:Issuer"], - ValidateAudience = true, - ValidAudience = builder.Configuration["JWT:Audience"], - ValidateIssuerSigningKey = true, - IssuerSigningKey = new SymmetricSecurityKey( - System.Text.Encoding.UTF8.GetBytes(builder.Configuration["JWT:SigningKey"]) - ) - }; -}); - - -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddHttpClient(); - var app = builder.Build(); @@ -108,17 +20,6 @@ app.UseHttpsRedirection(); -app.UseCors(x => x - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials() - //.WithOrigins("https://localhost:44351)) - .SetIsOriginAllowed(origin => true)); - -app.UseAuthentication(); -app.UseAuthorization(); - -app.MapControllers(); app.Run(); diff --git a/api/Repository/CommentRepository.cs b/api/Repository/CommentRepository.cs deleted file mode 100644 index ed588d4c..00000000 --- a/api/Repository/CommentRepository.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Data; -using api.Helpers; -using api.Interfaces; -using api.Models; -using Microsoft.EntityFrameworkCore; - -namespace api.Repository -{ - public class CommentRepository : ICommentRepository - { - private readonly ApplicationDBContext _context; - public CommentRepository(ApplicationDBContext context) - { - _context = context; - } - - public async Task CreateAsync(Comment commentModel) - { - await _context.Comments.AddAsync(commentModel); - await _context.SaveChangesAsync(); - return commentModel; - } - - public async Task DeleteAsync(int id) - { - var commentModel = await _context.Comments.FirstOrDefaultAsync(x => x.Id == id); - - if (commentModel == null) - { - return null; - } - - _context.Comments.Remove(commentModel); - await _context.SaveChangesAsync(); - return commentModel; - } - - public async Task> GetAllAsync(CommentQueryObject queryObject) - { - var comments = _context.Comments.Include(a => a.AppUser).AsQueryable(); - - if (!string.IsNullOrWhiteSpace(queryObject.Symbol)) - { - comments = comments.Where(s => s.Stock.Symbol == queryObject.Symbol); - }; - if (queryObject.IsDecsending == true) - { - comments = comments.OrderByDescending(c => c.CreatedOn); - } - return await comments.ToListAsync(); - } - - public async Task GetByIdAsync(int id) - { - return await _context.Comments.Include(a => a.AppUser).FirstOrDefaultAsync(c => c.Id == id); - } - - public async Task UpdateAsync(int id, Comment commentModel) - { - var existingComment = await _context.Comments.FindAsync(id); - - if (existingComment == null) - { - return null; - } - - existingComment.Title = commentModel.Title; - existingComment.Content = commentModel.Content; - - await _context.SaveChangesAsync(); - - return existingComment; - } - } -} \ No newline at end of file diff --git a/api/Repository/PortfolioRepository.cs b/api/Repository/PortfolioRepository.cs deleted file mode 100644 index 61275029..00000000 --- a/api/Repository/PortfolioRepository.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Data; -using api.Interfaces; -using api.Models; -using Microsoft.EntityFrameworkCore; - -namespace api.Repository -{ - public class PortfolioRepository : IPortfolioRepository - { - private readonly ApplicationDBContext _context; - public PortfolioRepository(ApplicationDBContext context) - { - _context = context; - } - - public async Task CreateAsync(Portfolio portfolio) - { - await _context.Portfolios.AddAsync(portfolio); - await _context.SaveChangesAsync(); - return portfolio; - } - - public async Task DeletePortfolio(AppUser appUser, string symbol) - { - var portfolioModel = await _context.Portfolios.FirstOrDefaultAsync(x => x.AppUserId == appUser.Id && x.Stock.Symbol.ToLower() == symbol.ToLower()); - - if (portfolioModel == null) - { - return null; - } - - _context.Portfolios.Remove(portfolioModel); - await _context.SaveChangesAsync(); - return portfolioModel; - } - - public async Task> GetUserPortfolio(AppUser user) - { - return await _context.Portfolios.Where(u => u.AppUserId == user.Id) - .Select(stock => new Stock - { - Id = stock.StockId, - Symbol = stock.Stock.Symbol, - CompanyName = stock.Stock.CompanyName, - Purchase = stock.Stock.Purchase, - LastDiv = stock.Stock.LastDiv, - Industry = stock.Stock.Industry, - MarketCap = stock.Stock.MarketCap - }).ToListAsync(); - } - } -} \ No newline at end of file diff --git a/api/Repository/StockRepository.cs b/api/Repository/StockRepository.cs deleted file mode 100644 index 8d896c4f..00000000 --- a/api/Repository/StockRepository.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Data; -using api.Dtos.Stock; -using api.Helpers; -using api.Interfaces; -using api.Models; -using Microsoft.EntityFrameworkCore; - -namespace api.Repository -{ - public class StockRepository : IStockRepository - { - private readonly ApplicationDBContext _context; - public StockRepository(ApplicationDBContext context) - { - _context = context; - } - - public async Task CreateAsync(Stock stockModel) - { - await _context.Stocks.AddAsync(stockModel); - await _context.SaveChangesAsync(); - return stockModel; - } - - public async Task DeleteAsync(int id) - { - var stockModel = await _context.Stocks.FirstOrDefaultAsync(x => x.Id == id); - - if (stockModel == null) - { - return null; - } - - _context.Stocks.Remove(stockModel); - await _context.SaveChangesAsync(); - return stockModel; - } - - public async Task> GetAllAsync(QueryObject query) - { - var stocks = _context.Stocks.Include(c => c.Comments).ThenInclude(a => a.AppUser).AsQueryable(); - - if (!string.IsNullOrWhiteSpace(query.CompanyName)) - { - stocks = stocks.Where(s => s.CompanyName.Contains(query.CompanyName)); - } - - if (!string.IsNullOrWhiteSpace(query.Symbol)) - { - stocks = stocks.Where(s => s.Symbol.Contains(query.Symbol)); - } - - if (!string.IsNullOrWhiteSpace(query.SortBy)) - { - if (query.SortBy.Equals("Symbol", StringComparison.OrdinalIgnoreCase)) - { - stocks = query.IsDecsending ? stocks.OrderByDescending(s => s.Symbol) : stocks.OrderBy(s => s.Symbol); - } - } - - var skipNumber = (query.PageNumber - 1) * query.PageSize; - - - return await stocks.Skip(skipNumber).Take(query.PageSize).ToListAsync(); - } - - public async Task GetByIdAsync(int id) - { - return await _context.Stocks.Include(c => c.Comments).FirstOrDefaultAsync(i => i.Id == id); - } - - public async Task GetBySymbolAsync(string symbol) - { - return await _context.Stocks.FirstOrDefaultAsync(s => s.Symbol == symbol); - } - - public Task StockExists(int id) - { - return _context.Stocks.AnyAsync(s => s.Id == id); - } - - public async Task UpdateAsync(int id, UpdateStockRequestDto stockDto) - { - var existingStock = await _context.Stocks.FirstOrDefaultAsync(x => x.Id == id); - - if (existingStock == null) - { - return null; - } - - existingStock.Symbol = stockDto.Symbol; - existingStock.CompanyName = stockDto.CompanyName; - existingStock.Purchase = stockDto.Purchase; - existingStock.LastDiv = stockDto.LastDiv; - existingStock.Industry = stockDto.Industry; - existingStock.MarketCap = stockDto.MarketCap; - - await _context.SaveChangesAsync(); - - return existingStock; - } - } -} \ No newline at end of file diff --git a/api/Service/FMPService.cs b/api/Service/FMPService.cs deleted file mode 100644 index 2cdafebc..00000000 --- a/api/Service/FMPService.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using api.Dtos.Stock; -using api.Interfaces; -using api.Mappers; -using api.Models; -using Newtonsoft.Json; - -namespace api.Service -{ - public class FMPService : IFMPService - { - private HttpClient _httpClient; - private IConfiguration _config; - public FMPService(HttpClient httpClient, IConfiguration config) - { - _httpClient = httpClient; - _config = config; - } - public async Task FindStockBySymbolAsync(string symbol) - { - try - { - var result = await _httpClient.GetAsync($"https://financialmodelingprep.com/api/v3/profile/{symbol}?apikey={_config["FMPKey"]}"); - if (result.IsSuccessStatusCode) - { - var content = await result.Content.ReadAsStringAsync(); - var tasks = JsonConvert.DeserializeObject(content); - var stock = tasks[0]; - if (stock != null) - { - return stock.ToStockFromFMP(); - } - return null; - } - return null; - } - catch (Exception e) - { - Console.WriteLine(e); - return null; - } - } - } -} \ No newline at end of file diff --git a/api/Service/TokenService.cs b/api/Service/TokenService.cs deleted file mode 100644 index 7944a429..00000000 --- a/api/Service/TokenService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.Linq; -using System.Security.Claims; -using System.Text; -using System.Threading.Tasks; -using api.Interfaces; -using api.Models; -using Microsoft.IdentityModel.Tokens; - -namespace api.Service -{ - public class TokenService : ITokenService - { - private readonly IConfiguration _config; - private readonly SymmetricSecurityKey _key; - - public TokenService(IConfiguration config) - { - _config = config; - _key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JWT:SigningKey"])); - } - public string CreateToken(AppUser user) - { - var claims = new List - { - new Claim(JwtRegisteredClaimNames.Email, user.Email), - new Claim(JwtRegisteredClaimNames.GivenName, user.UserName) - }; - - var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature); - - var tokenDescriptor = new SecurityTokenDescriptor - { - Subject = new ClaimsIdentity(claims), - Expires = DateTime.Now.AddDays(7), - SigningCredentials = creds, - Issuer = _config["JWT:Issuer"], - Audience = _config["JWT:Audience"] - }; - - var tokenHandler = new JwtSecurityTokenHandler(); - - var token = tokenHandler.CreateToken(tokenDescriptor); - - return tokenHandler.WriteToken(token); - } - } -} \ No newline at end of file From 9f3f6a763e45c6464f349d644956ff24a7e0f898 Mon Sep 17 00:00:00 2001 From: Kasun333 Date: Tue, 17 Dec 2024 15:38:01 +0530 Subject: [PATCH 2/6] comments and Stock --- api/Models/Comment.cs | 16 ++++++++++++++++ api/Models/Stock.cs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 api/Models/Comment.cs create mode 100644 api/Models/Stock.cs diff --git a/api/Models/Comment.cs b/api/Models/Comment.cs new file mode 100644 index 00000000..2bc494b4 --- /dev/null +++ b/api/Models/Comment.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace api.Models +{ + public class Comment + { public int Id { get; set; } + public string Title { get; set; }=string.Empty; + public string Content { get; set; }=string.Empty; + public DateTime CreatedOn { get; set; }=DateTime.Now; + public int? StockId { get; set; } + public Stock? Stock { get; set; } + } +} \ No newline at end of file diff --git a/api/Models/Stock.cs b/api/Models/Stock.cs new file mode 100644 index 00000000..b9139d65 --- /dev/null +++ b/api/Models/Stock.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Threading.Tasks; + +namespace api.Models +{ + public class Stock + { + public int Id { get; set; } + public string Symbol { get; set; }=string.Empty; + public string CompanyName { get; set; }=string.Empty; + [Column(TypeName = "decimail(18,2)")] + + public decimal Purchase { get; set; } + + [Column(TypeName = "decimail(18,2)")] + + public decimal Lastdiv { get; set; } + public string Industry { get; set; }=string.Empty; + public long MarketCap { get; set; } + + public List Comments { get; set; }=new List(); + + + + + } +} \ No newline at end of file From c5d0ef251d80450f37d85108f41c5e5c758598c2 Mon Sep 17 00:00:00 2001 From: Kasun333 Date: Tue, 17 Dec 2024 19:25:03 +0530 Subject: [PATCH 3/6] migration folder done --- .vscode/settings.json | 1 + api/Data/ApplicationDBContext.cs | 21 ++++ .../20241217134339_init.Designer.cs | 107 ++++++++++++++++++ api/Migrations/20241217134339_init.cs | 69 +++++++++++ .../ApplicationDBContextModelSnapshot.cs | 104 +++++++++++++++++ api/Models/Stock.cs | 4 +- api/Program.cs | 8 ++ api/appsettings.json | 2 +- 8 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 api/Data/ApplicationDBContext.cs create mode 100644 api/Migrations/20241217134339_init.Designer.cs create mode 100644 api/Migrations/20241217134339_init.cs create mode 100644 api/Migrations/ApplicationDBContextModelSnapshot.cs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/api/Data/ApplicationDBContext.cs b/api/Data/ApplicationDBContext.cs new file mode 100644 index 00000000..31c4dbe1 --- /dev/null +++ b/api/Data/ApplicationDBContext.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using api.Models; +using Microsoft.EntityFrameworkCore; + +namespace api.Data +{ + public class ApplicationDBContext : DbContext + { public ApplicationDBContext(DbContextOptions dbContextOptions) + : base(dbContextOptions) + { + + } + + public DbSet Stock {get;set;} + public DbSet Comments {get;set;} + + } +} \ No newline at end of file diff --git a/api/Migrations/20241217134339_init.Designer.cs b/api/Migrations/20241217134339_init.Designer.cs new file mode 100644 index 00000000..cf190915 --- /dev/null +++ b/api/Migrations/20241217134339_init.Designer.cs @@ -0,0 +1,107 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using api.Data; + +#nullable disable + +namespace api.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + [Migration("20241217134339_init")] + partial class init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("api.Models.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("StockId") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("StockId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("api.Models.Stock", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CompanyName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Industry") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Lastdiv") + .HasColumnType("decimal(18,2)"); + + b.Property("MarketCap") + .HasColumnType("bigint"); + + b.Property("Purchase") + .HasColumnType("decimal(18,2)"); + + b.Property("Symbol") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Stock"); + }); + + modelBuilder.Entity("api.Models.Comment", b => + { + b.HasOne("api.Models.Stock", "Stock") + .WithMany("Comments") + .HasForeignKey("StockId"); + + b.Navigation("Stock"); + }); + + modelBuilder.Entity("api.Models.Stock", b => + { + b.Navigation("Comments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api/Migrations/20241217134339_init.cs b/api/Migrations/20241217134339_init.cs new file mode 100644 index 00000000..ce35da7e --- /dev/null +++ b/api/Migrations/20241217134339_init.cs @@ -0,0 +1,69 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api.Migrations +{ + /// + public partial class init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Stock", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Symbol = table.Column(type: "nvarchar(max)", nullable: false), + CompanyName = table.Column(type: "nvarchar(max)", nullable: false), + Purchase = table.Column(type: "decimal(18,2)", nullable: false), + Lastdiv = table.Column(type: "decimal(18,2)", nullable: false), + Industry = table.Column(type: "nvarchar(max)", nullable: false), + MarketCap = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Stock", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Comments", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Title = table.Column(type: "nvarchar(max)", nullable: false), + Content = table.Column(type: "nvarchar(max)", nullable: false), + CreatedOn = table.Column(type: "datetime2", nullable: false), + StockId = table.Column(type: "int", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Comments", x => x.Id); + table.ForeignKey( + name: "FK_Comments_Stock_StockId", + column: x => x.StockId, + principalTable: "Stock", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Comments_StockId", + table: "Comments", + column: "StockId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Comments"); + + migrationBuilder.DropTable( + name: "Stock"); + } + } +} diff --git a/api/Migrations/ApplicationDBContextModelSnapshot.cs b/api/Migrations/ApplicationDBContextModelSnapshot.cs new file mode 100644 index 00000000..464b3aa5 --- /dev/null +++ b/api/Migrations/ApplicationDBContextModelSnapshot.cs @@ -0,0 +1,104 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using api.Data; + +#nullable disable + +namespace api.Migrations +{ + [DbContext(typeof(ApplicationDBContext))] + partial class ApplicationDBContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("api.Models.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedOn") + .HasColumnType("datetime2"); + + b.Property("StockId") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("StockId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("api.Models.Stock", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CompanyName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Industry") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Lastdiv") + .HasColumnType("decimal(18,2)"); + + b.Property("MarketCap") + .HasColumnType("bigint"); + + b.Property("Purchase") + .HasColumnType("decimal(18,2)"); + + b.Property("Symbol") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Stock"); + }); + + modelBuilder.Entity("api.Models.Comment", b => + { + b.HasOne("api.Models.Stock", "Stock") + .WithMany("Comments") + .HasForeignKey("StockId"); + + b.Navigation("Stock"); + }); + + modelBuilder.Entity("api.Models.Stock", b => + { + b.Navigation("Comments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api/Models/Stock.cs b/api/Models/Stock.cs index b9139d65..863470ee 100644 --- a/api/Models/Stock.cs +++ b/api/Models/Stock.cs @@ -11,11 +11,11 @@ public class Stock public int Id { get; set; } public string Symbol { get; set; }=string.Empty; public string CompanyName { get; set; }=string.Empty; - [Column(TypeName = "decimail(18,2)")] + [Column(TypeName = "decimal(18,2)")] public decimal Purchase { get; set; } - [Column(TypeName = "decimail(18,2)")] + [Column(TypeName = "decimal(18,2)")] public decimal Lastdiv { get; set; } public string Industry { get; set; }=string.Empty; diff --git a/api/Program.cs b/api/Program.cs index a0711f81..1c2807a4 100644 --- a/api/Program.cs +++ b/api/Program.cs @@ -1,5 +1,8 @@ +using api.Data; +using Microsoft.EntityFrameworkCore; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. @@ -8,6 +11,11 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddDbContext(options =>{ + options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")); + +}); + var app = builder.Build(); diff --git a/api/appsettings.json b/api/appsettings.json index c4abd065..457c5fb0 100644 --- a/api/appsettings.json +++ b/api/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Data Source=DESKTOP-U39R90D\\SQLEXPRESS;Initial Catalog=finshark;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" + "DefaultConnection": "Data Source=DESKTOP-DJBNJJD\\SQLEXPRESS;Initial Catalog=finshark;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" }, "Logging": { "LogLevel": { From e005f74413d69b472b1b2ee1c0c4c1a842cb3852 Mon Sep 17 00:00:00 2001 From: Kasun333 Date: Wed, 18 Dec 2024 10:01:21 +0530 Subject: [PATCH 4/6] updated --- api/Controllers/StockController.cs | 38 ++++++++++++++++++++++++++++++ api/Program.cs | 4 +++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 api/Controllers/StockController.cs diff --git a/api/Controllers/StockController.cs b/api/Controllers/StockController.cs new file mode 100644 index 00000000..a1bcaa2f --- /dev/null +++ b/api/Controllers/StockController.cs @@ -0,0 +1,38 @@ +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using api.Data; + +namespace api.Controllers +{ + [Route("api/stock")] + [ApiController] + public class StockController : ControllerBase + { + private readonly ApplicationDBContext _context; + + public StockController(ApplicationDBContext context) + { + _context = context; + } + + [HttpGet] + public IActionResult GetAll() + { + var stocks = _context.Stock.ToList(); + return Ok(stocks); + } + + [HttpGet("{id}")] + public IActionResult GetById([FromRoute] int id) + { + var stock = _context.Stock.Find(id); + + if (stock == null) + { + return NotFound(); + } + + return Ok(stock); + } + } +} diff --git a/api/Program.cs b/api/Program.cs index 1c2807a4..17c35287 100644 --- a/api/Program.cs +++ b/api/Program.cs @@ -7,7 +7,7 @@ // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle - +builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); @@ -28,6 +28,8 @@ app.UseHttpsRedirection(); +app.MapControllers(); + app.Run(); From 73aa4d87e8fb654f04fcb97cb6bc32fb5e13b45c Mon Sep 17 00:00:00 2001 From: Kasun333 Date: Wed, 18 Dec 2024 12:42:08 +0530 Subject: [PATCH 5/6] stock mapper done --- api/Controllers/StockController.cs | 6 ++++-- api/Dtos/Stock/StockDto.cs | 26 ++++++++++++++++++++++++++ api/Mappers/StockMappers.cs | 26 ++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 api/Dtos/Stock/StockDto.cs create mode 100644 api/Mappers/StockMappers.cs diff --git a/api/Controllers/StockController.cs b/api/Controllers/StockController.cs index a1bcaa2f..a13bc7a4 100644 --- a/api/Controllers/StockController.cs +++ b/api/Controllers/StockController.cs @@ -1,6 +1,7 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using api.Data; +using api.Mappers; namespace api.Controllers { @@ -18,7 +19,8 @@ public StockController(ApplicationDBContext context) [HttpGet] public IActionResult GetAll() { - var stocks = _context.Stock.ToList(); + var stocks = _context.Stock.ToList() + .Select(s => s.ToStockDto()); return Ok(stocks); } @@ -32,7 +34,7 @@ public IActionResult GetById([FromRoute] int id) return NotFound(); } - return Ok(stock); + return Ok(stock.ToStockDto()); } } } diff --git a/api/Dtos/Stock/StockDto.cs b/api/Dtos/Stock/StockDto.cs new file mode 100644 index 00000000..23639b66 --- /dev/null +++ b/api/Dtos/Stock/StockDto.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace api.Dtos.Stock +{ + public class StockDto + { + public int Id { get; set; } + public string Symbol { get; set; }=string.Empty; + public string CompanyName { get; set; }=string.Empty; + + + public decimal Purchase { get; set; } + + + + public decimal Lastdiv { get; set; } + public string Industry { get; set; }=string.Empty; + public long MarketCap { get; set; } + + + + } +} \ No newline at end of file diff --git a/api/Mappers/StockMappers.cs b/api/Mappers/StockMappers.cs new file mode 100644 index 00000000..64cfb3f0 --- /dev/null +++ b/api/Mappers/StockMappers.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using api.Dtos.Stock; +using api.Models; + +namespace api.Mappers +{ + public static class StockMappers + { + + public static StockDto ToStockDto(this Stock stockModel){ + return new StockDto{ + Id=stockModel.Id, + Symbol=stockModel.Symbol, + CompanyName=stockModel.CompanyName, + Purchase=stockModel.Purchase, + Lastdiv=stockModel.Lastdiv, + Industry=stockModel.Industry, + MarketCap=stockModel.MarketCap + }; + } + + } +} \ No newline at end of file From 5629a18984de7d372e52e399bc9dca7bc1f0c1aa Mon Sep 17 00:00:00 2001 From: Kasun333 Date: Wed, 18 Dec 2024 20:29:26 +0530 Subject: [PATCH 6/6] as --- api/Controllers/StockController.cs | 36 ++++++++++++++++++++++ api/Dtos/Stock/CreateStockRequestDto.cs | 23 ++++++++++++++ api/Dtos/Stock/UpdateStockRequestDto.cs | 26 ++++++++++++++++ api/Mappers/StockMappers.cs | 40 ++++++++++++++++--------- api/Models/Stock.cs | 4 +-- 5 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 api/Dtos/Stock/CreateStockRequestDto.cs create mode 100644 api/Dtos/Stock/UpdateStockRequestDto.cs diff --git a/api/Controllers/StockController.cs b/api/Controllers/StockController.cs index a13bc7a4..c10ae5e5 100644 --- a/api/Controllers/StockController.cs +++ b/api/Controllers/StockController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using api.Data; using api.Mappers; +using api.Dtos.Stock; namespace api.Controllers { @@ -35,6 +36,41 @@ public IActionResult GetById([FromRoute] int id) } return Ok(stock.ToStockDto()); + + + } + + [HttpPost] + public IActionResult Create([FromBody] CreateStockRequestDto stockDto){ + var stockModel = stockDto.ToStockFromCreateDTO(); + _context.Stock.Add(stockModel); + _context.SaveChanges(); + return CreatedAtAction(nameof(GetById),new {id=stockModel.Id},stockModel.ToStockDto()); + + } + [HttpPut] + [Route("{id}")] + public IActionResult Update([FromRoute] int id,[FromBody] UpdateStockRequestDto updateDto ){ + var stockModel=_context.Stock.FirstOrDefault(x => x.Id==id); + if(stockModel==null){ + return NotFound(); + } + + stockModel.Symbol=updateDto.Symbol; + stockModel.CompanyName=updateDto.CompanyName; + stockModel.Purchase=updateDto.Purchase; + stockModel.Lastdiv=updateDto.Lastdiv; + stockModel.Industry=updateDto.Industry; + stockModel.MarketCap=updateDto.MarketCap; + + _context.SaveChanges(); + + return Ok(stockModel.ToStockDto()); + + + } + + } } diff --git a/api/Dtos/Stock/CreateStockRequestDto.cs b/api/Dtos/Stock/CreateStockRequestDto.cs new file mode 100644 index 00000000..f6554a1f --- /dev/null +++ b/api/Dtos/Stock/CreateStockRequestDto.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace api.Dtos.Stock +{ + public class CreateStockRequestDto + { + public string Symbol { get; set; }=string.Empty; + public string CompanyName { get; set; }=string.Empty; + + + public decimal Purchase { get; set; } + + + + public decimal Lastdiv { get; set; } + public string Industry { get; set; }=string.Empty; + public long MarketCap { get; set; } + + } +} \ No newline at end of file diff --git a/api/Dtos/Stock/UpdateStockRequestDto.cs b/api/Dtos/Stock/UpdateStockRequestDto.cs new file mode 100644 index 00000000..a15dcc7e --- /dev/null +++ b/api/Dtos/Stock/UpdateStockRequestDto.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace api.Dtos.Stock +{ + public class UpdateStockRequestDto + { + + public int Id { get; set; } + public string Symbol { get; set; }=string.Empty; + public string CompanyName { get; set; }=string.Empty; + + + public decimal Purchase { get; set; } + + + + public decimal Lastdiv { get; set; } + public string Industry { get; set; }=string.Empty; + public long MarketCap { get; set; } + + + } +} \ No newline at end of file diff --git a/api/Mappers/StockMappers.cs b/api/Mappers/StockMappers.cs index 64cfb3f0..f7ba4fbe 100644 --- a/api/Mappers/StockMappers.cs +++ b/api/Mappers/StockMappers.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using api.Dtos.Stock; using api.Models; @@ -9,18 +6,33 @@ namespace api.Mappers { public static class StockMappers { + // Maps a Stock model to a StockDto + public static StockDto ToStockDto(this Stock stockModel) + { + return new StockDto + { + Id = stockModel.Id, + Symbol = stockModel.Symbol, + CompanyName = stockModel.CompanyName, + Purchase = stockModel.Purchase, + Lastdiv = stockModel.Lastdiv, + Industry = stockModel.Industry, + MarketCap = stockModel.MarketCap + }; + } - public static StockDto ToStockDto(this Stock stockModel){ - return new StockDto{ - Id=stockModel.Id, - Symbol=stockModel.Symbol, - CompanyName=stockModel.CompanyName, - Purchase=stockModel.Purchase, - Lastdiv=stockModel.Lastdiv, - Industry=stockModel.Industry, - MarketCap=stockModel.MarketCap + // Maps a CreateStockRequestDto to a Stock model + public static Stock ToStockFromCreateDTO(this CreateStockRequestDto stockDto) + { + return new Stock + { + Symbol = stockDto.Symbol, + CompanyName = stockDto.CompanyName, + Purchase = stockDto.Purchase, + Lastdiv = stockDto.Lastdiv, + Industry = stockDto.Industry, + MarketCap = stockDto.MarketCap }; } - } -} \ No newline at end of file +} diff --git a/api/Models/Stock.cs b/api/Models/Stock.cs index 863470ee..613b03d9 100644 --- a/api/Models/Stock.cs +++ b/api/Models/Stock.cs @@ -8,7 +8,7 @@ namespace api.Models { public class Stock { - public int Id { get; set; } + public int Id { get; set; } public string Symbol { get; set; }=string.Empty; public string CompanyName { get; set; }=string.Empty; [Column(TypeName = "decimal(18,2)")] @@ -22,7 +22,7 @@ public class Stock public long MarketCap { get; set; } public List Comments { get; set; }=new List(); - +