diff --git a/.gitignore b/.gitignore index a9183b55b68a71ca47809af2e9361e03903aae0c..0a8e0559ee40f980c98cf9af85ceef89e5cdc385 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -################################################################################ +################################################################################ # This .gitignore file was automatically created by Microsoft(R) Visual Studio. ################################################################################ diff --git a/Tsi1.Api/Tsi1.Api/Controllers/CourseController.cs b/Tsi1.Api/Tsi1.Api/Controllers/CourseController.cs index 540ab08af66615f623811fa0d894ac7cb00aaea7..cb90fae29fb465e9298dddbe750edfbf5e5677c3 100644 --- a/Tsi1.Api/Tsi1.Api/Controllers/CourseController.cs +++ b/Tsi1.Api/Tsi1.Api/Controllers/CourseController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; @@ -19,9 +20,12 @@ namespace Tsi1.Api.Controllers { private readonly ICourseService _courseService; - public CourseController(ICourseService courseService) + private readonly IFileService _fileService; + + public CourseController(ICourseService courseService, IFileService fileService) { _courseService = courseService; + _fileService = fileService; } [Authorize(Roles = UserTypes.Student + ", " + UserTypes.Professor)] @@ -54,6 +58,10 @@ namespace Tsi1.Api.Controllers return BadRequest(result.Message); } + var path = Path.Combine(tenantId.ToString(), result.Data.Id.ToString()); + + _fileService.CreateFolder(path); + return Ok(); } @@ -171,6 +179,9 @@ namespace Tsi1.Api.Controllers [HttpPut("Modify/{courseId}")] public async Task<IActionResult> Modify(int courseId, CourseCreateDto courseDto) { + var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value); + courseDto.TenantId = tenantId; + var result = await _courseService.Modify(courseId, courseDto); if (result.HasError) @@ -197,11 +208,15 @@ namespace Tsi1.Api.Controllers return BadRequest(result.Message); } - if (result.Data == false) + if (result.Data == null) { return NotFound(result.Message); } + var path = Path.Combine(result.Data.TenantId.ToString(), result.Data.Id.ToString()); + + _fileService.DeleteFolder(path); + return Ok(); } diff --git a/Tsi1.Api/Tsi1.Api/Controllers/FileController.cs b/Tsi1.Api/Tsi1.Api/Controllers/FileController.cs new file mode 100644 index 0000000000000000000000000000000000000000..144a8298f82892a2caafe88b4957da0c01b96b24 --- /dev/null +++ b/Tsi1.Api/Tsi1.Api/Controllers/FileController.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Tsi1.BusinessLayer.Helpers; +using Tsi1.BusinessLayer.Interfaces; + +namespace Tsi1.Api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class FileController : ControllerBase + { + private readonly IFileService _fileService; + + public FileController(IFileService fileService) + { + _fileService = fileService; + } + + [Authorize(Roles = UserTypes.Professor)] + [HttpPost("Create/{courseId}")] + public async Task<IActionResult> Create(IFormFile file, int courseId) + { + var tenantId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "TenantId").Value); + + var result = await _fileService.Create(file, tenantId, courseId); + + if (result.HasError) + { + return BadRequest(result.Message); + } + + return Ok(result.Data); + } + + } +} diff --git a/Tsi1.Api/Tsi1.Api/Controllers/TenantController.cs b/Tsi1.Api/Tsi1.Api/Controllers/TenantController.cs index f37f044f4c36a08560c5a66bb439feade470bf58..92a0665ffad2a0892a1365f1b16fbbf9730a92e6 100644 --- a/Tsi1.Api/Tsi1.Api/Controllers/TenantController.cs +++ b/Tsi1.Api/Tsi1.Api/Controllers/TenantController.cs @@ -17,9 +17,12 @@ namespace Tsi1.Api.Controllers { private readonly ITenantService _tenantService; - public TenantController(ITenantService tenantService) + private readonly IFileService _fileService; + + public TenantController(ITenantService tenantService, IFileService fileService) { _tenantService = tenantService; + _fileService = fileService; } [Authorize(Roles = UserTypes.UdelarAdmin)] @@ -47,6 +50,8 @@ namespace Tsi1.Api.Controllers return BadRequest(result.Message); } + _fileService.CreateFolder(result.Data.Id.ToString()); + return Ok(result.Data); } @@ -85,6 +90,8 @@ namespace Tsi1.Api.Controllers return NotFound(result.Message); } + _fileService.DeleteFolder(tenantId.ToString()); + return Ok(); } diff --git a/Tsi1.Api/Tsi1.Api/Startup.cs b/Tsi1.Api/Tsi1.Api/Startup.cs index 8dd99046a3ee149eaf1ba5a977b024899657c536..854ba8a94b7a335050dcd3c5d35f1e8c2fa73939 100644 --- a/Tsi1.Api/Tsi1.Api/Startup.cs +++ b/Tsi1.Api/Tsi1.Api/Startup.cs @@ -1,17 +1,21 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using AutoMapper; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -71,12 +75,20 @@ namespace Tsi1.Api services.AddScoped<IPostService, PostService>(); services.AddScoped<IPostMessageService, PostMessageService>(); services.AddScoped<ITenantService, TenantService>(); + services.AddScoped<IFileService, FileService>(); services.Configure<MailSettings>(Configuration.GetSection("MailSettings")); services.AddScoped<IEmailService, EmailService>(); services.AddCors(); - + + services.AddAuthorization(options => + { + options.FallbackPolicy = new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .Build(); + }); + services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; @@ -155,6 +167,16 @@ namespace Tsi1.Api app.UseAuthentication(); app.UseAuthorization(); + var provider = new FileExtensionContentTypeProvider(); + app.UseStaticFiles(new StaticFileOptions() + { + ContentTypeProvider = provider, + FileProvider = new PhysicalFileProvider( + Path.Combine(env.ContentRootPath, "StaticFiles")), + RequestPath = "/static" + } + ); + app.UseEndpoints(endpoints => { endpoints.MapControllers(); diff --git a/Tsi1.Api/Tsi1.Api/Tsi1.Api.csproj b/Tsi1.Api/Tsi1.Api/Tsi1.Api.csproj index 1ed4b443527a84acaf750eab3427ac11002a946a..7fa4dab4f5b7683e724fdac0596793f7c778db45 100644 --- a/Tsi1.Api/Tsi1.Api/Tsi1.Api.csproj +++ b/Tsi1.Api/Tsi1.Api/Tsi1.Api.csproj @@ -14,5 +14,9 @@ <ProjectReference Include="..\Tsi1.BusinessLayer\Tsi1.BusinessLayer.csproj" /> </ItemGroup> + <ItemGroup> + <Folder Include="StaticFiles\" /> + </ItemGroup> + </Project> diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/FileDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/FileDto.cs new file mode 100644 index 0000000000000000000000000000000000000000..977a0596daafe431f74776ab1dca5530a569655b --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/FileDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tsi1.BusinessLayer.Dtos +{ + public class FileDto + { + public int Id { get; set; } + + public string Name { get; set; } + + public string Path { get; set; } + } +} diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs index e00a5824fae33c3235b76f2b3b27de5ea6e962ce..8dc7c9fb645af7ec4886187388f9ec2c5e662b8e 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs +++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs @@ -38,5 +38,9 @@ namespace Tsi1.BusinessLayer.Helpers public const string DuplicateTenantName = "Ya existe una Facultad con nombre '{0}'"; public const string InvalidUserType = "Tipo de usuario invalido '{0}'"; + + public const string ErrorSavingFile = "No se pudo guardar el archivo '{0}'"; + public const string ErrorDeletingFile = "No se pudo borrar el archivo '{0}'"; + public const string DuplicateFile = "Ya existe el archivo '{0}'"; } } diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs index f41ff864c4887ea8922e73442f9050233e10713a..00a67f4141fbdb357d1dc3552ed02951b200b20c 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs +++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/MappingProfile.cs @@ -31,6 +31,7 @@ namespace Tsi1.BusinessLayer.Helpers CreateMap<Tenant, TenantCreateDto>(); CreateMap<Tenant, TenantCourseDto>().ForMember(x => x.Courses, opt => opt.Ignore()); CreateMap<UserType, UserTypeDto>(); + CreateMap<File, FileDto>(); CreateMap<ForumCreateDto, Forum>(); CreateMap<ForumPreviewDto, Forum>(); @@ -45,12 +46,13 @@ namespace Tsi1.BusinessLayer.Helpers CreateMap<UserModifyDto, User>(); CreateMap<StudentPreviewDto, Student>(); CreateMap<ProfessorPreviewDto, Professor>(); - CreateMap<CourseCreateDto, Course>().ForMember(x => x.TenantId, opt => opt.Ignore()); + CreateMap<CourseCreateDto, Course>(); CreateMap<CoursePreviewDto, Course>(); CreateMap<TenantPreviewDto, Tenant>(); CreateMap<TenantCreateDto, Tenant>(); CreateMap<TenantCourseDto, Tenant>(); CreateMap<UserTypeDto, UserType>(); + CreateMap<FileDto, File>(); } } } diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ICourseService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ICourseService.cs index 885c3b3729b3a210b70990a747f1e808d6460d30..f13641f98f895d1f846b25bda95135e3a6b5be64 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ICourseService.cs +++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ICourseService.cs @@ -22,7 +22,7 @@ namespace Tsi1.BusinessLayer.Interfaces Task<ServiceResult<bool>> Modify(int courseId, CourseCreateDto courseDto); - Task<ServiceResult<bool>> Delete(int courseId); + Task<ServiceResult<Course>> Delete(int courseId); Task<ServiceResult<bool>> DropOutFromCourse(int userId, int courseId); diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IFileService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IFileService.cs new file mode 100644 index 0000000000000000000000000000000000000000..a42bbf693f410735a37715369574479ae29b6551 --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IFileService.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Tsi1.BusinessLayer.Dtos; +using Tsi1.BusinessLayer.Helpers; + +namespace Tsi1.BusinessLayer.Interfaces +{ + public interface IFileService + { + Task<ServiceResult<FileDto>> Create(IFormFile file, int tenantId, int courseId); + + void CreateFolder(string folderPath); + + void DeleteFolder(string folderPath); + } +} diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ITenantService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ITenantService.cs index 26c6c000930203ca3051ca38b142bde7cd1b4920..a1b88ed057e0435a0c6ff68901bdccd86d235ca4 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ITenantService.cs +++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/ITenantService.cs @@ -11,6 +11,8 @@ namespace Tsi1.BusinessLayer.Interfaces { Task<ServiceResult<int>> GetByName(string tenantName); + Task<ServiceResult<int>> GetById(int tenantId); + Task<ServiceResult<List<TenantPreviewDto>>> GetAll(); Task<ServiceResult<TenantPreviewDto>> Create(TenantCreateDto newTenant); diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/CourseService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/CourseService.cs index e385562b0bbb4c2986dfe06bd602e40860faa8ce..6930b541c7218a1262f4c772951d1d29ef787be3 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Services/CourseService.cs +++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/CourseService.cs @@ -41,7 +41,7 @@ namespace Tsi1.BusinessLayer.Services } var course = _mapper.Map<Course>(newCourse); - + _context.Courses.Add(course); await _context.SaveChangesAsync(); @@ -219,9 +219,9 @@ namespace Tsi1.BusinessLayer.Services return result; } - public async Task<ServiceResult<bool>> Delete(int courseId) + public async Task<ServiceResult<Course>> Delete(int courseId) { - var result = new ServiceResult<bool>(); + var result = new ServiceResult<Course>(); var course = await _context.Courses .FirstOrDefaultAsync(x => x.Id == courseId); @@ -236,7 +236,7 @@ namespace Tsi1.BusinessLayer.Services await _context.SaveChangesAsync(); - result.Data = true; + result.Data = course; return result; } diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/FileService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/FileService.cs new file mode 100644 index 0000000000000000000000000000000000000000..315250148761311cbd020f1da7863798800a7793 --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/FileService.cs @@ -0,0 +1,139 @@ +using AutoMapper; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Tsi1.BusinessLayer.Dtos; +using Tsi1.BusinessLayer.Helpers; +using Tsi1.BusinessLayer.Interfaces; +using Tsi1.DataLayer; +using Tsi1.DataLayer.Entities; + +namespace Tsi1.BusinessLayer.Services +{ + public class FileService : IFileService + { + private readonly Tsi1Context _context; + + private readonly IMapper _mapper; + + private readonly string _path; + + public FileService(Tsi1Context context, IMapper mapper, IHostingEnvironment hostingEnvironment) + { + _context = context; + _mapper = mapper; + _path = hostingEnvironment.ContentRootPath + "/StaticFiles"; + } + + public async Task<ServiceResult<string>> SaveFile(IFormFile file, string filePath) + { + var result = new ServiceResult<string>(); + + var fileStream = new FileStream(filePath, FileMode.Create); + + try + { + await file.CopyToAsync(fileStream); + } + catch (Exception) + { + result.HasError = true; + result.Message = string.Format(ErrorMessages.ErrorSavingFile, filePath); + return result; + } + finally + { + fileStream.Close(); + } + + return result; + } + + + + public async Task<ServiceResult<FileDto>> Create(IFormFile file, int tenantId, int courseId) + { + var result = new ServiceResult<FileDto>(); + + var tenant = await _context.Tenants + .FirstOrDefaultAsync(x => x.Id == tenantId); + + var course = await _context.Courses + .FirstOrDefaultAsync(x => x.Id == courseId); + + var filePath = Path.Combine(_path, tenant.Name, course.Name, file.FileName); + + var existingFile = await _context.Files.FirstOrDefaultAsync(x => x.Path == filePath); + if (existingFile != null) + { + result.HasError = true; + result.Message = string.Format(ErrorMessages.DuplicateFile, filePath); + return result; + } + + var newFile = new DataLayer.Entities.File + { + Name = file.FileName, + Path = filePath.Replace(_path, ""), + }; + + _context.Add(newFile); + await _context.SaveChangesAsync(); + + var resultSaveFile = await this.SaveFile(file, filePath); + if (resultSaveFile.HasError) + { + result.HasError = true; + result.Message = resultSaveFile.Message; + + _context.Remove(newFile); + await _context.SaveChangesAsync(); + + return result; + } + + var fileDto = _mapper.Map<FileDto>(newFile); + result.Data = fileDto; + + return result; + } + + + public async Task<ServiceResult<bool>> DeleteFile(string filePath) + { + var result = new ServiceResult<bool>(); + + try + { + using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose)) {} + } + catch (Exception) + { + result.HasError = true; + result.Message = string.Format(ErrorMessages.ErrorDeletingFile, filePath); + return result; + } + + return result; + } + + public void CreateFolder(string folderPath) + { + var path = Path.Combine(_path, folderPath); + + Directory.CreateDirectory(path); + } + + public void DeleteFolder(string folderPath) + { + var path = Path.Combine(_path, folderPath); + + Directory.Delete(path); + } + } +} diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/TenantService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/TenantService.cs index 71bd3c28acd1093aa04d0a982d31c0768fbc59df..17d6668ac6a2234260d9a64fb1007dcdeefa7437 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Services/TenantService.cs +++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/TenantService.cs @@ -175,5 +175,24 @@ namespace Tsi1.BusinessLayer.Services result.Data = tenantDtos; return result; } + + public async Task<ServiceResult<int>> GetById(int tenantId) + { + var result = new ServiceResult<int>(); + + var tenant = await _context.Tenants + .FirstOrDefaultAsync(x => x.Id == tenantId); + + if (tenant == null) + { + result.HasError = true; + result.Message = string.Format(ErrorMessages.TenantDoesNotExist, tenantId); + return result; + } + + result.Data = tenant.Id; + + return result; + } } } diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Tsi1.BusinessLayer.csproj b/Tsi1.Api/Tsi1.BusinessLayer/Tsi1.BusinessLayer.csproj index a21bff7c2af2796490606eab2c96c0b6c51598ae..a18735ed0c8c52887e85611f1b353106330f869f 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Tsi1.BusinessLayer.csproj +++ b/Tsi1.Api/Tsi1.BusinessLayer/Tsi1.BusinessLayer.csproj @@ -8,6 +8,8 @@ <PackageReference Include="AutoMapper" Version="10.1.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" /> <PackageReference Include="MailKit" Version="2.9.0" /> + <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" /> + <PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="3.1.9" /> <PackageReference Include="MimeKit" Version="2.9.2" /> </ItemGroup> diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/File.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/File.cs new file mode 100644 index 0000000000000000000000000000000000000000..4643d9c27c0f79917b66a41b0bff4435b6cfdc21 --- /dev/null +++ b/Tsi1.Api/Tsi1.DataLayer/Entities/File.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tsi1.DataLayer.Entities +{ + public class File + { + public int Id { get; set; } + + public string Name { get; set; } + + public string Path { get; set; } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/FileConfiguration.cs b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/FileConfiguration.cs new file mode 100644 index 0000000000000000000000000000000000000000..ee21323fe9a845cfc5d780b24877022c0b09b2c2 --- /dev/null +++ b/Tsi1.Api/Tsi1.DataLayer/EntityConfiguration/FileConfiguration.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Text; +using Tsi1.DataLayer.Entities; + +namespace Tsi1.DataLayer.EntityConfiguration +{ + public class FileConfiguration : IEntityTypeConfiguration<File> + { + public void Configure(EntityTypeBuilder<File> builder) + { + builder.HasKey(x => x.Id); + + builder.Property(x => x.Name) + .IsRequired() + .HasColumnType("character varying(50)"); + + builder.Property(x => x.Path) + .IsRequired() + .HasColumnType("character varying(1000)"); + + builder.HasIndex(x => x.Path) + .IsUnique(); + } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101163611_add-File-entity.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101163611_add-File-entity.Designer.cs new file mode 100644 index 0000000000000000000000000000000000000000..13074745a6636f6ee5f1fb3231bbc45b2c8aada6 --- /dev/null +++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101163611_add-File-entity.Designer.cs @@ -0,0 +1,479 @@ +// <auto-generated /> +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Tsi1.DataLayer; + +namespace Tsi1.DataLayer.Migrations +{ + [DbContext(typeof(Tsi1Context))] + [Migration("20201101163611_add-File-entity")] + partial class addFileentity + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<int>("TenantId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("Name", "TenantId") + .IsUnique(); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.File", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<int>("CourseId") + .HasColumnType("integer"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("CourseId", "Name") + .IsUnique(); + + b.ToTable("Forums"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b => + { + b.Property<int>("ForumId") + .HasColumnType("integer"); + + b.Property<int>("UserId") + .HasColumnType("integer"); + + b.HasKey("ForumId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("ForumUsers"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<DateTime>("Date") + .HasColumnType("timestamp without time zone"); + + b.Property<int>("ForumId") + .HasColumnType("integer"); + + b.Property<string>("Title") + .IsRequired() + .HasColumnType("character varying(100)"); + + b.Property<int>("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("ForumId", "Title") + .IsUnique(); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Content") + .IsRequired() + .HasColumnType("character varying(10485760)"); + + b.Property<DateTime>("Date") + .HasColumnType("timestamp without time zone"); + + b.Property<int>("PostId") + .HasColumnType("integer"); + + b.Property<int>("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + + b.ToTable("PostMessages"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("IdentityCard") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<int>("TenantId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("IdentityCard", "TenantId") + .IsUnique(); + + b.ToTable("Professors"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b => + { + b.Property<int>("ProfessorId") + .HasColumnType("integer"); + + b.Property<int>("CourseId") + .HasColumnType("integer"); + + b.HasKey("ProfessorId", "CourseId"); + + b.HasIndex("CourseId"); + + b.ToTable("ProfessorCourses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<int>("Age") + .HasColumnType("integer"); + + b.Property<string>("IdentityCard") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<int>("TenantId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("IdentityCard", "TenantId") + .IsUnique(); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b => + { + b.Property<int>("StudentId") + .HasColumnType("integer"); + + b.Property<int>("CourseId") + .HasColumnType("integer"); + + b.HasKey("StudentId", "CourseId"); + + b.HasIndex("CourseId"); + + b.ToTable("StudentCourses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Tenant", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Tenants"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Email") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("FirstName") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("LastName") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("Password") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<int?>("ProfessorId") + .HasColumnType("integer"); + + b.Property<int?>("StudentId") + .HasColumnType("integer"); + + b.Property<int>("TenantId") + .HasColumnType("integer"); + + b.Property<int>("UserTypeId") + .HasColumnType("integer"); + + b.Property<string>("Username") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ProfessorId") + .IsUnique(); + + b.HasIndex("StudentId") + .IsUnique(); + + b.HasIndex("TenantId"); + + b.HasIndex("UserTypeId"); + + b.HasIndex("Username", "TenantId") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("UserTypes"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant") + .WithMany("Courses") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Course", "Course") + .WithMany("Forums") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum") + .WithMany("ForumUsers") + .HasForeignKey("ForumId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.User", "User") + .WithMany("ForumUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum") + .WithMany("Posts") + .HasForeignKey("ForumId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.User", "User") + .WithMany("Posts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Post", "Post") + .WithMany("PostMessages") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.User", "User") + .WithMany("PostMessages") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant") + .WithMany("Professors") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Course", "Course") + .WithMany("ProfessorCourses") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor") + .WithMany("ProfessorCourses") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant") + .WithMany("Students") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Course", "Course") + .WithMany("StudentCourses") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.Student", "Student") + .WithMany("StudentCourses") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor") + .WithOne("User") + .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId"); + + b.HasOne("Tsi1.DataLayer.Entities.Student", "Student") + .WithOne("User") + .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId"); + + b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant") + .WithMany("Users") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType") + .WithMany() + .HasForeignKey("UserTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101163611_add-File-entity.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101163611_add-File-entity.cs new file mode 100644 index 0000000000000000000000000000000000000000..ace7d115279d1651eb0cfa5bc55035751cda87a2 --- /dev/null +++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101163611_add-File-entity.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Tsi1.DataLayer.Migrations +{ + public partial class addFileentity : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Files", + columns: table => new + { + Id = table.Column<int>(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column<string>(type: "character varying(50)", nullable: false), + Path = table.Column<string>(type: "character varying(100)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Files", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Files_Path", + table: "Files", + column: "Path", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Files"); + } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101164258_change-File-Path-Property.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101164258_change-File-Path-Property.Designer.cs new file mode 100644 index 0000000000000000000000000000000000000000..44695b906c006a5365bb3a1ea89a00e5d0b5425c --- /dev/null +++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101164258_change-File-Path-Property.Designer.cs @@ -0,0 +1,479 @@ +// <auto-generated /> +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Tsi1.DataLayer; + +namespace Tsi1.DataLayer.Migrations +{ + [DbContext(typeof(Tsi1Context))] + [Migration("20201101164258_change-File-Path-Property")] + partial class changeFilePathProperty + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<int>("TenantId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("Name", "TenantId") + .IsUnique(); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.File", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("character varying(1000)"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<int>("CourseId") + .HasColumnType("integer"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("CourseId", "Name") + .IsUnique(); + + b.ToTable("Forums"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b => + { + b.Property<int>("ForumId") + .HasColumnType("integer"); + + b.Property<int>("UserId") + .HasColumnType("integer"); + + b.HasKey("ForumId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("ForumUsers"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<DateTime>("Date") + .HasColumnType("timestamp without time zone"); + + b.Property<int>("ForumId") + .HasColumnType("integer"); + + b.Property<string>("Title") + .IsRequired() + .HasColumnType("character varying(100)"); + + b.Property<int>("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("ForumId", "Title") + .IsUnique(); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Content") + .IsRequired() + .HasColumnType("character varying(10485760)"); + + b.Property<DateTime>("Date") + .HasColumnType("timestamp without time zone"); + + b.Property<int>("PostId") + .HasColumnType("integer"); + + b.Property<int>("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + + b.ToTable("PostMessages"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("IdentityCard") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<int>("TenantId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("IdentityCard", "TenantId") + .IsUnique(); + + b.ToTable("Professors"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b => + { + b.Property<int>("ProfessorId") + .HasColumnType("integer"); + + b.Property<int>("CourseId") + .HasColumnType("integer"); + + b.HasKey("ProfessorId", "CourseId"); + + b.HasIndex("CourseId"); + + b.ToTable("ProfessorCourses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<int>("Age") + .HasColumnType("integer"); + + b.Property<string>("IdentityCard") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<int>("TenantId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.HasIndex("IdentityCard", "TenantId") + .IsUnique(); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b => + { + b.Property<int>("StudentId") + .HasColumnType("integer"); + + b.Property<int>("CourseId") + .HasColumnType("integer"); + + b.HasKey("StudentId", "CourseId"); + + b.HasIndex("CourseId"); + + b.ToTable("StudentCourses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Tenant", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Tenants"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Email") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("FirstName") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("LastName") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("Password") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<int?>("ProfessorId") + .HasColumnType("integer"); + + b.Property<int?>("StudentId") + .HasColumnType("integer"); + + b.Property<int>("TenantId") + .HasColumnType("integer"); + + b.Property<int>("UserTypeId") + .HasColumnType("integer"); + + b.Property<string>("Username") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ProfessorId") + .IsUnique(); + + b.HasIndex("StudentId") + .IsUnique(); + + b.HasIndex("TenantId"); + + b.HasIndex("UserTypeId"); + + b.HasIndex("Username", "TenantId") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("UserTypes"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant") + .WithMany("Courses") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Course", "Course") + .WithMany("Forums") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ForumUser", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum") + .WithMany("ForumUsers") + .HasForeignKey("ForumId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.User", "User") + .WithMany("ForumUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Post", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Forum", "Forum") + .WithMany("Posts") + .HasForeignKey("ForumId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.User", "User") + .WithMany("Posts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.PostMessage", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Post", "Post") + .WithMany("PostMessages") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.User", "User") + .WithMany("PostMessages") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant") + .WithMany("Professors") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Course", "Course") + .WithMany("ProfessorCourses") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor") + .WithMany("ProfessorCourses") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant") + .WithMany("Students") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Course", "Course") + .WithMany("StudentCourses") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.Student", "Student") + .WithMany("StudentCourses") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor") + .WithOne("User") + .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId"); + + b.HasOne("Tsi1.DataLayer.Entities.Student", "Student") + .WithOne("User") + .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId"); + + b.HasOne("Tsi1.DataLayer.Entities.Tenant", "Tenant") + .WithMany("Users") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType") + .WithMany() + .HasForeignKey("UserTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101164258_change-File-Path-Property.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101164258_change-File-Path-Property.cs new file mode 100644 index 0000000000000000000000000000000000000000..a348b6798398c41cc70117d6a0f4594c06784eee --- /dev/null +++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201101164258_change-File-Path-Property.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tsi1.DataLayer.Migrations +{ + public partial class changeFilePathProperty : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn<string>( + name: "Path", + table: "Files", + type: "character varying(1000)", + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(100)"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn<string>( + name: "Path", + table: "Files", + type: "character varying(100)", + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(1000)"); + } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs index ad82bacaa2af11e43014e891983a77d56bc0255b..4c6d381fbdcd5201077e62e722bd7916da810a66 100644 --- a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs +++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs @@ -43,6 +43,29 @@ namespace Tsi1.DataLayer.Migrations b.ToTable("Courses"); }); + modelBuilder.Entity("Tsi1.DataLayer.Entities.File", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("character varying(1000)"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Files"); + }); + modelBuilder.Entity("Tsi1.DataLayer.Entities.Forum", b => { b.Property<int>("Id") diff --git a/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs b/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs index c9fb14f74a1844a4fdc09fb7a9dea866975c2430..91401cfd792dbfc0ab8f62ebd7dd6e6f7277dda4 100644 --- a/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs +++ b/Tsi1.Api/Tsi1.DataLayer/Tsi1Context.cs @@ -21,6 +21,7 @@ namespace Tsi1.DataLayer public DbSet<PostMessage> PostMessages { get; set; } public DbSet<Tenant> Tenants { get; set; } public DbSet<ForumUser> ForumUsers { get; set; } + public DbSet<File> Files { get; set; } public Tsi1Context(DbContextOptions options) : base(options) { } @@ -39,6 +40,7 @@ namespace Tsi1.DataLayer modelBuilder.ApplyConfiguration(new PostMessageConfiguration()); modelBuilder.ApplyConfiguration(new TenantConfiguration()); modelBuilder.ApplyConfiguration(new ForumUserConfiguration()); + modelBuilder.ApplyConfiguration(new FileConfiguration()); } } }