From c7cb7bb55a5f0dd2439554d87e09435ccf6f4f30 Mon Sep 17 00:00:00 2001 From: sei-tspencer Date: Wed, 5 Jun 2024 17:06:15 -0400 Subject: [PATCH 1/8] wip services --- Blueprint.Api.Data/BlueprintContext.cs | 5 + Blueprint.Api.Data/Enumerations.cs | 6 + Blueprint.Api.Data/Models/Catalog.cs | 39 +++ Blueprint.Api.Data/Models/CatalogInject.cs | 52 ++++ Blueprint.Api.Data/Models/CatalogUnit.cs | 52 ++++ Blueprint.Api.Data/Models/DataField.cs | 7 + Blueprint.Api.Data/Models/DataValue.cs | 11 +- Blueprint.Api.Data/Models/Inject.cs | 25 ++ Blueprint.Api.Data/Models/InjectType.cs | 31 +++ Blueprint.Api.Data/Models/ScenarioEvent.cs | 4 + Blueprint.Api.Data/Models/Unit.cs | 1 + .../Authorization/CatalogViewRequirement.cs | 37 +++ Blueprint.Api/Services/DataValueService.cs | 2 +- Blueprint.Api/Services/InjectService.cs | 231 ++++++++++++++++++ Blueprint.Api/Services/InjectTypeService.cs | 127 ++++++++++ .../Services/ScenarioEventService.cs | 1 - Blueprint.Api/Services/ServiceUtilities.cs | 16 ++ Blueprint.Api/ViewModels/Catalog.cs | 23 ++ Blueprint.Api/ViewModels/CatalogInject.cs | 26 ++ Blueprint.Api/ViewModels/CatalogUnit.cs | 26 ++ Blueprint.Api/ViewModels/DataField.cs | 1 + Blueprint.Api/ViewModels/DataValue.cs | 3 +- Blueprint.Api/ViewModels/Inject.cs | 19 ++ Blueprint.Api/ViewModels/InjectType.cs | 17 ++ 24 files changed, 757 insertions(+), 5 deletions(-) create mode 100644 Blueprint.Api.Data/Models/Catalog.cs create mode 100644 Blueprint.Api.Data/Models/CatalogInject.cs create mode 100644 Blueprint.Api.Data/Models/CatalogUnit.cs create mode 100644 Blueprint.Api.Data/Models/Inject.cs create mode 100644 Blueprint.Api.Data/Models/InjectType.cs create mode 100644 Blueprint.Api/Infrastructure/Authorization/CatalogViewRequirement.cs create mode 100644 Blueprint.Api/Services/InjectService.cs create mode 100644 Blueprint.Api/Services/InjectTypeService.cs create mode 100644 Blueprint.Api/ViewModels/Catalog.cs create mode 100644 Blueprint.Api/ViewModels/CatalogInject.cs create mode 100644 Blueprint.Api/ViewModels/CatalogUnit.cs create mode 100644 Blueprint.Api/ViewModels/Inject.cs create mode 100644 Blueprint.Api/ViewModels/InjectType.cs diff --git a/Blueprint.Api.Data/BlueprintContext.cs b/Blueprint.Api.Data/BlueprintContext.cs index 5b25503..668c2e7 100755 --- a/Blueprint.Api.Data/BlueprintContext.cs +++ b/Blueprint.Api.Data/BlueprintContext.cs @@ -38,11 +38,16 @@ public BlueprintContext(DbContextOptions options) : base(optio public DbSet Organizations { get; set; } public DbSet Cards { get; set; } public DbSet CardTeams { get; set; } + public DbSet Catalogs { get; set; } + public DbSet CatalogInjects { get; set; } + public DbSet CatalogUnits { get; set; } public DbSet CiteActions { get; set; } public DbSet CiteRoles { get; set; } + public DbSet InjectTypes { get; set; } public DbSet PlayerApplications { get; set; } public DbSet PlayerApplicationTeams { get; set; } public DbSet MselPages { get; set; } + public DbSet Injects { get; set; } public DbSet Invitations { get; set; } public DbSet Units { get; set; } public DbSet UnitUsers { get; set; } diff --git a/Blueprint.Api.Data/Enumerations.cs b/Blueprint.Api.Data/Enumerations.cs index 5b93374..9bb7a85 100755 --- a/Blueprint.Api.Data/Enumerations.cs +++ b/Blueprint.Api.Data/Enumerations.cs @@ -19,6 +19,12 @@ public enum IntegrationType { Connect = 30 } + public enum EventType { + Inject = 10, + Information = 20, + Facilitation = 30 + } + public enum EventExecutionStatus { Executed = 10, diff --git a/Blueprint.Api.Data/Models/Catalog.cs b/Blueprint.Api.Data/Models/Catalog.cs new file mode 100644 index 0000000..9f8e253 --- /dev/null +++ b/Blueprint.Api.Data/Models/Catalog.cs @@ -0,0 +1,39 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Blueprint.Api.Data.Models +{ + public class CatalogEntity : BaseEntity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public Guid InjectTypeId { get; set; } + public InjectTypeEntity InjectType { get; set; } + public bool IsPublic { get; set; } + public Guid? ParentId { get; set; } + public CatalogEntity Parent { get; set; } + public virtual ICollection Injects { get; set; } = new HashSet(); + public virtual ICollection CatalogUnits { get; set; } = new HashSet(); + public virtual ICollection CatalogInjects { get; set; } = new HashSet(); + } + + public class CatalogConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasIndex(x => new { x.Name }).IsUnique(); + } + } + +} + diff --git a/Blueprint.Api.Data/Models/CatalogInject.cs b/Blueprint.Api.Data/Models/CatalogInject.cs new file mode 100644 index 0000000..b8ab142 --- /dev/null +++ b/Blueprint.Api.Data/Models/CatalogInject.cs @@ -0,0 +1,52 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Blueprint.Api.Data.Models +{ + public class CatalogInjectEntity + { + public CatalogInjectEntity() { } + + public CatalogInjectEntity(Guid injectId, Guid catalogId) + { + InjectId = injectId; + CatalogId = catalogId; + } + + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid InjectId { get; set; } + public InjectEntity Inject { get; set; } + + public Guid CatalogId { get; set; } + public CatalogEntity Catalog { get; set; } + } + + public class CatalogInjectConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasIndex(x => new { x.InjectId, x.CatalogId }).IsUnique(); + + builder + .HasOne(u => u.Inject) + .WithMany(p => p.CatalogInjects) + .HasForeignKey(x => x.InjectId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(u => u.Catalog) + .WithMany(p => p.CatalogInjects) + .HasForeignKey(x => x.CatalogId) + .OnDelete(DeleteBehavior.Cascade); + } + } +} + diff --git a/Blueprint.Api.Data/Models/CatalogUnit.cs b/Blueprint.Api.Data/Models/CatalogUnit.cs new file mode 100644 index 0000000..adc47f4 --- /dev/null +++ b/Blueprint.Api.Data/Models/CatalogUnit.cs @@ -0,0 +1,52 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Blueprint.Api.Data.Models +{ + public class CatalogUnitEntity + { + public CatalogUnitEntity() { } + + public CatalogUnitEntity(Guid unitId, Guid catalogId) + { + UnitId = unitId; + CatalogId = catalogId; + } + + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + + public Guid UnitId { get; set; } + public UnitEntity Unit { get; set; } + + public Guid CatalogId { get; set; } + public CatalogEntity Catalog { get; set; } + } + + public class CatalogUnitConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasIndex(x => new { x.UnitId, x.CatalogId }).IsUnique(); + + builder + .HasOne(u => u.Unit) + .WithMany(p => p.CatalogUnits) + .HasForeignKey(x => x.UnitId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(u => u.Catalog) + .WithMany(p => p.CatalogUnits) + .HasForeignKey(x => x.CatalogId) + .OnDelete(DeleteBehavior.Cascade); + } + } +} + diff --git a/Blueprint.Api.Data/Models/DataField.cs b/Blueprint.Api.Data/Models/DataField.cs index 599881f..a576ce1 100644 --- a/Blueprint.Api.Data/Models/DataField.cs +++ b/Blueprint.Api.Data/Models/DataField.cs @@ -18,6 +18,8 @@ public class DataFieldEntity : BaseEntity public Guid Id { get; set; } public Guid? MselId { get; set; } public virtual MselEntity Msel { get; set; } + public Guid? InjectTypeId { get; set; } + public virtual InjectTypeEntity InjectType { get; set; } public string Name { get; set; } public DataFieldType DataType { get; set; } public int DisplayOrder { get; set; } @@ -37,10 +39,15 @@ public class DataFieldEntityConfiguration : IEntityTypeConfiguration builder) { + builder.HasCheckConstraint("data_field_msel_or_inject_type", "msel_id IS NOT NULL XOR inject_type_id IS NOT NULL"); builder .HasOne(d => d.Msel) .WithMany(d => d.DataFields) .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(d => d.InjectType) + .WithMany(d => d.DataFields) + .OnDelete(DeleteBehavior.Cascade); } } diff --git a/Blueprint.Api.Data/Models/DataValue.cs b/Blueprint.Api.Data/Models/DataValue.cs index 1222238..614eccc 100644 --- a/Blueprint.Api.Data/Models/DataValue.cs +++ b/Blueprint.Api.Data/Models/DataValue.cs @@ -15,8 +15,10 @@ public class DataValueEntity : BaseEntity [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } public string Value { get; set; } - public Guid ScenarioEventId { get; set; } + public Guid? ScenarioEventId { get; set; } public virtual ScenarioEventEntity ScenarioEvent { get; set; } + public Guid? InjectId { get; set; } + public virtual InjectEntity Inject { get; set; } public Guid DataFieldId { get; set; } public virtual DataFieldEntity DataField { get; set; } public string CellMetadata { get; set; } @@ -27,11 +29,16 @@ public class DataValueEntityConfiguration : IEntityTypeConfiguration builder) { builder.HasIndex(e => e.Id).IsUnique(); - builder.HasIndex(e => new { e.ScenarioEventId, e.DataFieldId }).IsUnique(); + builder.HasCheckConstraint("data_value_scenario_event_or_inject", "scenario_event_id IS NOT NULL XOR inject_id IS NOT NULL"); + builder.HasIndex(e => new { e.ScenarioEventId, e.InjectId, e.DataFieldId }).IsUnique(); builder .HasOne(d => d.ScenarioEvent) .WithMany(d => d.DataValues) .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(d => d.Inject) + .WithMany(d => d.DataValues) + .OnDelete(DeleteBehavior.Cascade); } } diff --git a/Blueprint.Api.Data/Models/Inject.cs b/Blueprint.Api.Data/Models/Inject.cs new file mode 100644 index 0000000..ed8d241 --- /dev/null +++ b/Blueprint.Api.Data/Models/Inject.cs @@ -0,0 +1,25 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Blueprint.Api.Data.Models +{ + public class InjectEntity : BaseEntity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + public Guid InjectTypeId { get; set; } + public InjectTypeEntity InjectType { get; set; } + public Guid? RequiresInjectId { get; set; } + public virtual InjectEntity RequiresInject { get; set; } + public virtual ICollection DataValues { get; set; } = new HashSet(); + public virtual ICollection CatalogInjects { get; set; } = new HashSet(); + } + +} + diff --git a/Blueprint.Api.Data/Models/InjectType.cs b/Blueprint.Api.Data/Models/InjectType.cs new file mode 100644 index 0000000..744e6e2 --- /dev/null +++ b/Blueprint.Api.Data/Models/InjectType.cs @@ -0,0 +1,31 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact injectType@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Blueprint.Api.Data.Models +{ + public class InjectTypeEntity : BaseEntity + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public virtual ICollection DataFields { get; set; } = new HashSet(); + } + + public class InjectTypeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasIndex(x => new { x.Name }).IsUnique(); + } + } +} + diff --git a/Blueprint.Api.Data/Models/ScenarioEvent.cs b/Blueprint.Api.Data/Models/ScenarioEvent.cs index 9f65e78..7c1e952 100755 --- a/Blueprint.Api.Data/Models/ScenarioEvent.cs +++ b/Blueprint.Api.Data/Models/ScenarioEvent.cs @@ -23,6 +23,10 @@ public class ScenarioEventEntity : BaseEntity public bool IsHidden { get; set; } // flag that hides the secenario event on the Exercise View shown to participants public string RowMetadata { get; set; } // comma separated values (row height number, integer R, integer G, integer B) public int DeltaSeconds { get; set; } // time from the start of the MSEL when this event should be executed + public EventType ScenarioEventType { get; set; } + public string Description { get; set; } + public Guid? InjectId { get; set; } + public InjectEntity Inject { get; set; } } public class ScenarioEventEntityConfiguration : IEntityTypeConfiguration diff --git a/Blueprint.Api.Data/Models/Unit.cs b/Blueprint.Api.Data/Models/Unit.cs index e57b191..f91b8b1 100644 --- a/Blueprint.Api.Data/Models/Unit.cs +++ b/Blueprint.Api.Data/Models/Unit.cs @@ -17,6 +17,7 @@ public class UnitEntity : BaseEntity public string ShortName { get; set; } public ICollection UnitUsers { get; set; } = new List(); public virtual ICollection MselUnits { get; set; } = new HashSet(); + public virtual ICollection CatalogUnits { get; set; } = new HashSet(); public Guid? OldTeamId { get; set; } } diff --git a/Blueprint.Api/Infrastructure/Authorization/CatalogViewRequirement.cs b/Blueprint.Api/Infrastructure/Authorization/CatalogViewRequirement.cs new file mode 100644 index 0000000..381725e --- /dev/null +++ b/Blueprint.Api/Infrastructure/Authorization/CatalogViewRequirement.cs @@ -0,0 +1,37 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Blueprint.Api.Data; +using Microsoft.CodeAnalysis.VisualBasic.Syntax; + +namespace Blueprint.Api.Infrastructure.Authorization +{ + public static class CatalogViewRequirement + { + public static async Task IsMet(Guid userId, Guid? catalogId, BlueprintContext blueprintContext) + { + var createdBy = (await blueprintContext.Catalogs.FirstOrDefaultAsync(m => m.Id == catalogId)).CreatedBy; + if (createdBy == userId) + { + return true; + } + else + { + var unitIdList = await blueprintContext.UnitUsers + .Where(m => m.UserId == userId) + .Select(m => m.UnitId) + .ToListAsync(); + var isSuccess = await blueprintContext.CatalogUnits + .Where(m => m.CatalogId == catalogId && unitIdList.Contains(m.UnitId)) + .AnyAsync(); + return isSuccess; + } + } + } +} + diff --git a/Blueprint.Api/Services/DataValueService.cs b/Blueprint.Api/Services/DataValueService.cs index 0a4bb8a..382c7d1 100644 --- a/Blueprint.Api/Services/DataValueService.cs +++ b/Blueprint.Api/Services/DataValueService.cs @@ -60,7 +60,7 @@ public DataValueService( .Select(se => se.Id) .ToListAsync(ct); var dataValueEntities = await _context.DataValues - .Where(dv => scenarioEventIdList.Contains(dv.ScenarioEventId)) + .Where(dv => scenarioEventIdList.Contains((Guid)dv.ScenarioEventId)) .ToListAsync(ct); return _mapper.Map>(dataValueEntities).ToList();; diff --git a/Blueprint.Api/Services/InjectService.cs b/Blueprint.Api/Services/InjectService.cs new file mode 100644 index 0000000..1d61538 --- /dev/null +++ b/Blueprint.Api/Services/InjectService.cs @@ -0,0 +1,231 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Blueprint.Api.Data; +using Blueprint.Api.Data.Models; +using Blueprint.Api.Infrastructure.Authorization; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.Infrastructure.Extensions; +using Blueprint.Api.Infrastructure.Options; +using Blueprint.Api.ViewModels; + +namespace Blueprint.Api.Services +{ + public interface IInjectService + { + Task> GetByCatalogAsync(Guid catalogId, CancellationToken ct); + Task GetAsync(Guid id, CancellationToken ct); + Task CreateAsync(Guid catalogId, Inject inject, CancellationToken ct); + Task UpdateAsync(Guid id, ViewModels.Inject inject, CancellationToken ct); + Task DeleteAsync(Guid id, CancellationToken ct); + } + + public class InjectService : IInjectService + { + private readonly BlueprintContext _context; + private readonly IAuthorizationService _authorizationService; + private readonly ClaimsPrincipal _user; + private readonly IMapper _mapper; + private readonly DatabaseOptions _options; + + + public InjectService( + BlueprintContext context, + IAuthorizationService authorizationService, + IPrincipal user, + IMapper mapper, + DatabaseOptions options) + { + _context = context; + _authorizationService = authorizationService; + _user = user as ClaimsPrincipal; + _mapper = mapper; + _options = options; + } + + public async Task> GetByCatalogAsync(Guid catalogId, CancellationToken ct) + { + // user must be a Content Developer or a Catalog viewer + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded && + !await CatalogViewRequirement.IsMet(_user.GetId(), catalogId, _context)) + throw new ForbiddenException(); + + var injects = await _context.CatalogInjects + .Where(i => i.CatalogId == catalogId) + .Select(m => m.Inject) + .ToListAsync(ct); + return _mapper.Map>(injects); + } + + public async Task GetAsync(Guid id, CancellationToken ct) + { + var item = await _context.Injects + .Include(se => se.DataValues) + .SingleAsync(a => a.Id == id, ct); + + if (item == null) + throw new EntityNotFoundException(); + + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + { + var userId = _user.GetId(); + var unitIdList = await _context.CatalogUnits + .Where(m => m.Id == userId) + .Select(m => m.UnitId) + .ToListAsync(ct); + var catalogIdList = await _context.CatalogUnits + .Where(m => unitIdList.Contains(m.UnitId)) + .Select(m => m.CatalogId) + .ToListAsync(ct); + var isAuthorized = await _context.CatalogInjects + .Where(m => catalogIdList.Contains(m.CatalogId)) + .AnyAsync(ct); + if (!isAuthorized) + throw new ForbiddenException(); + } + + return _mapper.Map(item); + } + + public async Task CreateAsync(Guid catalogId, Inject inject, CancellationToken ct) + { + // user must be a Content Developer + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + // start a transaction, because we may also update DataValues + await _context.Database.BeginTransactionAsync(); + // create the inject + inject.Id = inject.Id != Guid.Empty ? inject.Id : Guid.NewGuid(); + inject.DateCreated = DateTime.UtcNow; + inject.CreatedBy = _user.GetId(); + inject.DateModified = null; + inject.ModifiedBy = null; + var injectEntity = _mapper.Map(inject); + _context.Injects.Add(injectEntity); + await _context.SaveChangesAsync(ct); + // create the associated data values + var dataFieldList = await _context.InjectTypes + .Where(m => m.Id == inject.InjectTypeId) + .Select(m => m.DataFields) + .SingleOrDefaultAsync(ct); + foreach (var dataField in dataFieldList) + { + var dataValue = inject.DataValues + .FirstOrDefault(dv => dv.DataFieldId == dataField.Id); + if (dataValue == null) + { + dataValue = new DataValue(); + dataValue.DataFieldId = dataField.Id; + } + dataValue.Id = Guid.NewGuid(); + dataValue.CreatedBy = inject.CreatedBy; + dataValue.DateCreated = inject.DateCreated; + dataValue.DateModified = null; + dataValue.ModifiedBy = null; + var dataValueEntity = _mapper.Map(dataValue); + _context.DataValues.Add(dataValueEntity); + } + await _context.SaveChangesAsync(ct); + // update the Catalog modified info + await ServiceUtilities.SetCatalogModifiedAsync(catalogId, injectEntity.CreatedBy, injectEntity.DateCreated, _context, ct); + // commit the transaction + await _context.Database.CommitTransactionAsync(ct); + + return _mapper.Map(injectEntity); + } + + public async Task UpdateAsync(Guid id, ViewModels.Inject inject, CancellationToken ct) + { + // user must be a Content Developer + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + // make sure entity exists + var injectToUpdate = await _context.Injects.SingleOrDefaultAsync(v => v.Id == id, ct); + if (injectToUpdate == null) + throw new EntityNotFoundException($"Inject not found {id}."); + + // start a transaction, because we may also update DataValues and other injects + await _context.Database.BeginTransactionAsync(); + // determine if row indexes need updated for the other injects on this Catalog + // update this inject + inject.CreatedBy = injectToUpdate.CreatedBy; + inject.DateCreated = injectToUpdate.DateCreated; + inject.ModifiedBy = _user.GetId(); + inject.DateModified = DateTime.UtcNow; + _mapper.Map(inject, injectToUpdate); + _context.Injects.Update(injectToUpdate); + await _context.SaveChangesAsync(ct); + // get the DataField IDs for this Catalog + var dataFieldList = await _context.InjectTypes + .Where(m => m.Id == inject.InjectTypeId) + .Select(m => m.DataFields) + .SingleOrDefaultAsync(ct); + // update the data values + foreach (var dataField in dataFieldList) + { + var dataValueToUpdate = await _context.DataValues + .SingleOrDefaultAsync(dv => dv.InjectId == inject.Id && dv.DataFieldId == dataField.Id, ct); + var dataValue = inject.DataValues + .SingleOrDefault(dv => dv.InjectId == inject.Id && dv.DataFieldId == dataField.Id); + if (dataValueToUpdate == null) + { + dataValue.Id = Guid.NewGuid(); + dataValue.CreatedBy = (Guid)inject.ModifiedBy; + dataValue.DateCreated = (DateTime)inject.DateModified; + dataValue.DateModified = dataValue.DateCreated; + dataValue.ModifiedBy = dataValue.CreatedBy; + var dataValueEntity = _mapper.Map(dataValue); + _context.DataValues.Add(dataValueEntity); + } + else if (dataValue.Value != dataValueToUpdate.Value) + { + // update the DataValue + dataValueToUpdate.ModifiedBy = injectToUpdate.ModifiedBy; + dataValueToUpdate.DateModified = injectToUpdate.DateModified; + dataValueToUpdate.Value = dataValue.Value; + _context.DataValues.Update(dataValueToUpdate); + } + } + await _context.SaveChangesAsync(ct); + // commit the transaction + await _context.Database.CommitTransactionAsync(ct); + + return _mapper.Map(injectToUpdate); + } + + public async Task DeleteAsync(Guid id, CancellationToken ct) + { + // user must be a Content Developer + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + var injectToDelete = await _context.Injects.SingleOrDefaultAsync(v => v.Id == id, ct); + if (injectToDelete == null) + throw new EntityNotFoundException(); + + // start a transaction, because we may also update DataValues and other injects + await _context.Database.BeginTransactionAsync(); + _context.Injects.Remove(injectToDelete); + await _context.SaveChangesAsync(ct); + // commit the transaction + await _context.Database.CommitTransactionAsync(ct); + + return true; + } + + } + + } + diff --git a/Blueprint.Api/Services/InjectTypeService.cs b/Blueprint.Api/Services/InjectTypeService.cs new file mode 100644 index 0000000..c1c5e93 --- /dev/null +++ b/Blueprint.Api/Services/InjectTypeService.cs @@ -0,0 +1,127 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact injectType@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Blueprint.Api.Data; +using Blueprint.Api.Data.Models; +using Blueprint.Api.Infrastructure.Extensions; +using Blueprint.Api.Infrastructure.Authorization; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.ViewModels; + +namespace Blueprint.Api.Services +{ + public interface IInjectTypeService + { + Task> GetAsync(CancellationToken ct); + Task GetAsync(Guid id, CancellationToken ct); + // Task> GetByUserIdAsync(Guid userId, CancellationToken ct); + Task CreateAsync(ViewModels.InjectType injectType, CancellationToken ct); + Task UpdateAsync(Guid id, ViewModels.InjectType injectType, CancellationToken ct); + Task DeleteAsync(Guid id, CancellationToken ct); + } + + public class InjectTypeService : IInjectTypeService + { + private readonly BlueprintContext _context; + private readonly IAuthorizationService _authorizationService; + private readonly ClaimsPrincipal _user; + private readonly IMapper _mapper; + + public InjectTypeService(BlueprintContext context, IAuthorizationService authorizationService, IPrincipal user, IMapper mapper) + { + _context = context; + _authorizationService = authorizationService; + _user = user as ClaimsPrincipal; + _mapper = mapper; + } + + public async Task> GetAsync(CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + var items = await _context.InjectTypes + .ToListAsync(ct); + + return _mapper.Map>(items); + } + + public async Task GetAsync(Guid id, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + var item = await _context.InjectTypes + .SingleOrDefaultAsync(o => o.Id == id, ct); + + return _mapper.Map(item); + } + + public async Task CreateAsync(ViewModels.InjectType injectType, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + injectType.Id = injectType.Id != Guid.Empty ? injectType.Id : Guid.NewGuid(); + injectType.DateCreated = DateTime.UtcNow; + injectType.CreatedBy = _user.GetId(); + injectType.DateModified = null; + injectType.ModifiedBy = null; + var injectTypeEntity = _mapper.Map(injectType); + + _context.InjectTypes.Add(injectTypeEntity); + await _context.SaveChangesAsync(ct); + + return await GetAsync(injectTypeEntity.Id, ct); + } + + public async Task UpdateAsync(Guid id, ViewModels.InjectType injectType, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + var injectTypeToUpdate = await _context.InjectTypes.SingleOrDefaultAsync(v => v.Id == id, ct); + + if (injectTypeToUpdate == null) + throw new EntityNotFoundException(); + + injectType.CreatedBy = injectTypeToUpdate.CreatedBy; + injectType.DateCreated = injectTypeToUpdate.DateCreated; + injectType.DateModified = DateTime.UtcNow; + injectType.ModifiedBy = _user.GetId(); + _mapper.Map(injectType, injectTypeToUpdate); + + _context.InjectTypes.Update(injectTypeToUpdate); + await _context.SaveChangesAsync(ct); + + return _mapper.Map(injectTypeToUpdate, injectType); + } + + public async Task DeleteAsync(Guid id, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + var injectTypeToDelete = await _context.InjectTypes.SingleOrDefaultAsync(v => v.Id == id, ct); + + if (injectTypeToDelete == null) + throw new EntityNotFoundException(); + + _context.InjectTypes.Remove(injectTypeToDelete); + await _context.SaveChangesAsync(ct); + + return true; + } + + } +} + diff --git a/Blueprint.Api/Services/ScenarioEventService.cs b/Blueprint.Api/Services/ScenarioEventService.cs index b19bd9b..1ab751a 100644 --- a/Blueprint.Api/Services/ScenarioEventService.cs +++ b/Blueprint.Api/Services/ScenarioEventService.cs @@ -18,7 +18,6 @@ using Blueprint.Api.Infrastructure.Extensions; using Blueprint.Api.Infrastructure.Options; using Blueprint.Api.ViewModels; -using System.Security.Cryptography.X509Certificates; namespace Blueprint.Api.Services { diff --git a/Blueprint.Api/Services/ServiceUtilities.cs b/Blueprint.Api/Services/ServiceUtilities.cs index 0603740..794905b 100644 --- a/Blueprint.Api/Services/ServiceUtilities.cs +++ b/Blueprint.Api/Services/ServiceUtilities.cs @@ -29,6 +29,22 @@ public static async Task SetMselModifiedAsync(Guid? id, Guid? modifiedBy, DateTi } } + public static async Task SetCatalogModifiedAsync(Guid? id, Guid? modifiedBy, DateTime? dateModified, BlueprintContext context, CancellationToken ct) + { + if (id != null) + { + var catalogToUpdate = await context.Catalogs.SingleOrDefaultAsync(v => v.Id == id, ct); + if (catalogToUpdate == null) + throw new EntityNotFoundException(); + + // okay to update this catalog + catalogToUpdate.ModifiedBy = modifiedBy; + catalogToUpdate.DateModified = dateModified; + context.Catalogs.Update(catalogToUpdate); + await context.SaveChangesAsync(ct); + } + } + } } diff --git a/Blueprint.Api/ViewModels/Catalog.cs b/Blueprint.Api/ViewModels/Catalog.cs new file mode 100644 index 0000000..9c94dae --- /dev/null +++ b/Blueprint.Api/ViewModels/Catalog.cs @@ -0,0 +1,23 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using Blueprint.Api.Data.Enumerations; + +namespace Blueprint.Api.ViewModels +{ + public class Catalog : Base + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public Guid InjectTypeId { get; set; } + public bool IsPublic { get; set; } + public Guid? ParentId { get; set; } + public Catalog Parent { get; set; } + public virtual ICollection Injects { get; set; } = new HashSet(); + public virtual ICollection Units { get; set; } = new HashSet(); + } +} + diff --git a/Blueprint.Api/ViewModels/CatalogInject.cs b/Blueprint.Api/ViewModels/CatalogInject.cs new file mode 100644 index 0000000..f7b8711 --- /dev/null +++ b/Blueprint.Api/ViewModels/CatalogInject.cs @@ -0,0 +1,26 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. + +using System; + +namespace Blueprint.Api.ViewModels +{ + public class CatalogInject + { + public CatalogInject() { + } + + public CatalogInject(Guid catalogId, Guid injectId) + { + CatalogId = catalogId; + InjectId = injectId; + } + + public Guid Id { get; set; } + public Guid CatalogId { get; set; } + public Guid InjectId { get; set; } + public virtual Inject Inject { get; set; } + } + +} + diff --git a/Blueprint.Api/ViewModels/CatalogUnit.cs b/Blueprint.Api/ViewModels/CatalogUnit.cs new file mode 100644 index 0000000..7e038d4 --- /dev/null +++ b/Blueprint.Api/ViewModels/CatalogUnit.cs @@ -0,0 +1,26 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. + +using System; + +namespace Blueprint.Api.ViewModels +{ + public class CatalogUnit + { + public CatalogUnit() { + } + + public CatalogUnit(Guid catalogId, Guid unitId) + { + CatalogId = catalogId; + UnitId = unitId; + } + + public Guid Id { get; set; } + public Guid CatalogId { get; set; } + public Guid UnitId { get; set; } + public virtual Unit Unit { get; set; } + } + +} + diff --git a/Blueprint.Api/ViewModels/DataField.cs b/Blueprint.Api/ViewModels/DataField.cs index a69665a..1ac71cb 100644 --- a/Blueprint.Api/ViewModels/DataField.cs +++ b/Blueprint.Api/ViewModels/DataField.cs @@ -11,6 +11,7 @@ public class DataField : Base { public Guid Id { get; set; } public Guid? MselId { get; set; } + public Guid? InjectTypeId { get; set; } public string Name { get; set; } public DataFieldType DataType { get; set; } public int DisplayOrder { get; set; } diff --git a/Blueprint.Api/ViewModels/DataValue.cs b/Blueprint.Api/ViewModels/DataValue.cs index 71e7712..403635a 100644 --- a/Blueprint.Api/ViewModels/DataValue.cs +++ b/Blueprint.Api/ViewModels/DataValue.cs @@ -9,7 +9,8 @@ public class DataValue : Base { public Guid Id { get; set; } public string Value { get; set; } - public Guid ScenarioEventId { get; set; } + public Guid? ScenarioEventId { get; set; } + public Guid? InjectId { get; set; } public Guid DataFieldId { get; set; } public string CellMetadata { get; set; } } diff --git a/Blueprint.Api/ViewModels/Inject.cs b/Blueprint.Api/ViewModels/Inject.cs new file mode 100644 index 0000000..d591fda --- /dev/null +++ b/Blueprint.Api/ViewModels/Inject.cs @@ -0,0 +1,19 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using Blueprint.Api.Data.Enumerations; + +namespace Blueprint.Api.ViewModels +{ + public class Inject : Base + { + public Guid Id { get; set; } + public Guid InjectTypeId { get; set; } + public Guid? RequiresInjectId { get; set; } + public virtual Inject RequiresInject { get; set; } + public virtual ICollection DataValues { get; set; } = new HashSet(); + } +} + diff --git a/Blueprint.Api/ViewModels/InjectType.cs b/Blueprint.Api/ViewModels/InjectType.cs new file mode 100644 index 0000000..bf72670 --- /dev/null +++ b/Blueprint.Api/ViewModels/InjectType.cs @@ -0,0 +1,17 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; + +namespace Blueprint.Api.ViewModels +{ + public class InjectType : Base + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public virtual ICollection DataFields { get; set; } = new HashSet(); + } +} + From 795a05baeb1374638bc9c6262e6293f2b0b947c6 Mon Sep 17 00:00:00 2001 From: sei-tspencer Date: Thu, 6 Jun 2024 09:15:27 -0400 Subject: [PATCH 2/8] view model changes --- Blueprint.Api.Data/Models/Unit.cs | 1 - Blueprint.Api/ViewModels/ScenarioEvent.cs | 3 +++ Blueprint.Api/ViewModels/Unit.cs | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Blueprint.Api.Data/Models/Unit.cs b/Blueprint.Api.Data/Models/Unit.cs index f91b8b1..6070ac9 100644 --- a/Blueprint.Api.Data/Models/Unit.cs +++ b/Blueprint.Api.Data/Models/Unit.cs @@ -18,7 +18,6 @@ public class UnitEntity : BaseEntity public ICollection UnitUsers { get; set; } = new List(); public virtual ICollection MselUnits { get; set; } = new HashSet(); public virtual ICollection CatalogUnits { get; set; } = new HashSet(); - public Guid? OldTeamId { get; set; } } public class UnitConfiguration : IEntityTypeConfiguration diff --git a/Blueprint.Api/ViewModels/ScenarioEvent.cs b/Blueprint.Api/ViewModels/ScenarioEvent.cs index f22011a..a03197a 100644 --- a/Blueprint.Api/ViewModels/ScenarioEvent.cs +++ b/Blueprint.Api/ViewModels/ScenarioEvent.cs @@ -16,6 +16,9 @@ public class ScenarioEvent : Base public bool IsHidden { get; set; } public string RowMetadata { get; set; } public int DeltaSeconds { get; set; } // time from the start of the MSEL when this event should be executed + public EventType ScenarioEventType { get; set; } + public string Description { get; set; } + public Guid? InjectId { get; set; } } } diff --git a/Blueprint.Api/ViewModels/Unit.cs b/Blueprint.Api/ViewModels/Unit.cs index c6a0853..6a62ee6 100644 --- a/Blueprint.Api/ViewModels/Unit.cs +++ b/Blueprint.Api/ViewModels/Unit.cs @@ -12,7 +12,6 @@ public class Unit : Base public string Name { get; set; } public string ShortName { get; set; } public ICollection Users { get; set; } = new List(); - public Guid? OldTeamId { get; set; } } } From 4cc22d10b9eb6b0d2cbed252b63741f7ec36eda5 Mon Sep 17 00:00:00 2001 From: sei-tspencer Date: Wed, 12 Jun 2024 16:41:59 -0400 Subject: [PATCH 3/8] add services and some controllers --- .../Controllers/CatalogController.cs | 213 +++++++++++++ Blueprint.Api/Controllers/InjectController.cs | 133 ++++++++ .../Controllers/InjectTypeController.cs | 133 ++++++++ .../Services/CatalogInjectService.cs | 106 +++++++ Blueprint.Api/Services/CatalogService.cs | 300 ++++++++++++++++++ Blueprint.Api/Services/CatalogUnitService.cs | 169 ++++++++++ 6 files changed, 1054 insertions(+) create mode 100644 Blueprint.Api/Controllers/CatalogController.cs create mode 100644 Blueprint.Api/Controllers/InjectController.cs create mode 100644 Blueprint.Api/Controllers/InjectTypeController.cs create mode 100644 Blueprint.Api/Services/CatalogInjectService.cs create mode 100644 Blueprint.Api/Services/CatalogService.cs create mode 100644 Blueprint.Api/Services/CatalogUnitService.cs diff --git a/Blueprint.Api/Controllers/CatalogController.cs b/Blueprint.Api/Controllers/CatalogController.cs new file mode 100644 index 0000000..ad835db --- /dev/null +++ b/Blueprint.Api/Controllers/CatalogController.cs @@ -0,0 +1,213 @@ +// Copyright 2022 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.Data; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Blueprint.Api.Data.Enumerations; +using Blueprint.Api.Infrastructure.Extensions; +using Blueprint.Api.Infrastructure.QueryParameters; +using Blueprint.Api.Services; +using Blueprint.Api.ViewModels; +using Swashbuckle.AspNetCore.Annotations; + +namespace Blueprint.Api.Controllers +{ + public class CatalogController : BaseController + { + private readonly ICatalogService _catalogService; + private readonly IAuthorizationService _authorizationService; + + public CatalogController( + ICatalogService catalogService, + ICiteService citeService, + IPlayerService playerService, + IAuthorizationService authorizationService) + { + _catalogService = catalogService; + _authorizationService = authorizationService; + } + + /// + /// Gets Catalogs + /// + /// + /// Returns a list of Catalogs. + /// + /// + /// + [HttpGet("catalogs")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getCatalogs")] + public async Task Get(CancellationToken ct) + { + var list = await _catalogService.GetAsync(ct); + return Ok(list); + } + + /// + /// Gets Catalogs for the current user + /// + /// + /// Returns a list of the current user's active Catalogs. + /// + /// + /// + [HttpGet("my-catalogs")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getMyCatalogs")] + public async Task GetMine(CancellationToken ct) + { + var list = await _catalogService.GetMineAsync(ct); + return Ok(list); + } + + /// + /// Gets Catalogs for requested user + /// + /// + /// Returns a list of the requested user's active Catalogs. + /// + /// + /// + /// + [HttpGet("users/{userId}/catalogs")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getUserCatalogs")] + public async Task GetUserCatalogs(Guid userId, CancellationToken ct) + { + var list = await _catalogService.GetUserCatalogsAsync(userId, ct); + return Ok(list); + } + + /// + /// Gets a specific Catalog by id + /// + /// + /// Returns the Catalog with the id specified + /// + /// The id of the Catalog + /// + /// + [HttpGet("catalogs/{id}")] + [ProducesResponseType(typeof(Catalog), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getCatalog")] + public async Task Get(Guid id, CancellationToken ct) + { + var catalog = await _catalogService.GetAsync(id, ct); + return Ok(catalog); + } + + /// + /// Creates a new Catalog + /// + /// + /// Creates a new Catalog with the attributes specified + /// + /// Accessible only to a ContentDeveloper or an Administrator + /// + /// The data used to create the Catalog + /// + [HttpPost("catalogs")] + [ProducesResponseType(typeof(Catalog), (int)HttpStatusCode.Created)] + [SwaggerOperation(OperationId = "createCatalog")] + public async Task Create([FromBody] Catalog catalog, CancellationToken ct) + { + catalog.CreatedBy = User.GetId(); + var createdCatalog = await _catalogService.CreateAsync(catalog, ct); + return CreatedAtAction(nameof(this.Get), new { id = createdCatalog.Id }, createdCatalog); + } + + /// + /// Creates a new Catalog by copying an existing Catalog + /// + /// + /// Creates a new Catalog from the specified existing Catalog + /// + /// Accessible only to a ContentDeveloper or an Administrator + /// + /// The ID of the Catalog to be copied + /// + [HttpPost("catalogs/{id}/copy")] + [ProducesResponseType(typeof(Catalog), (int)HttpStatusCode.Created)] + [SwaggerOperation(OperationId = "copyCatalog")] + public async Task Copy(Guid id, CancellationToken ct) + { + var createdCatalog = await _catalogService.CopyAsync(id, ct); + return CreatedAtAction(nameof(this.Get), new { id = createdCatalog.Id }, createdCatalog); + } + + /// + /// Updates a Catalog + /// + /// + /// Updates a Catalog with the attributes specified. + /// The ID from the route MUST MATCH the ID contained in the catalog parameter + /// + /// Accessible only to a ContentDeveloper or an Administrator + /// + /// The Id of the Catalog to update + /// The updated Catalog values + /// + [HttpPut("catalogs/{id}")] + [ProducesResponseType(typeof(Catalog), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "updateCatalog")] + public async Task Update([FromRoute] Guid id, [FromBody] Catalog catalog, CancellationToken ct) + { + catalog.ModifiedBy = User.GetId(); + var updatedCatalog = await _catalogService.UpdateAsync(id, catalog, ct); + return Ok(updatedCatalog); + } + + /// + /// Deletes a Catalog + /// + /// + /// Deletes a Catalog with the specified id + /// + /// Accessible only to a ContentDeveloper or an Administrator + /// + /// The id of the Catalog to delete + /// + [HttpDelete("catalogs/{id}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [SwaggerOperation(OperationId = "deleteCatalog")] + public async Task Delete(Guid id, CancellationToken ct) + { + await _catalogService.DeleteAsync(id, ct); + return NoContent(); + } + + /// Upload a json Catalog file + /// The files to upload and their settings + /// + [HttpPost("catalogs/json")] + [ProducesResponseType(typeof(Catalog), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "uploadJsonFiles")] + public async Task UploadJsonAsync([FromForm] FileForm form, CancellationToken ct) + { + var result = await _catalogService.UploadJsonAsync(form, ct); + return Ok(result); + } + + /// Download a catalog by id as json file + /// The id of the catalog + /// + [HttpGet("catalogs/{id}/json")] + [ProducesResponseType(typeof(FileResult), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "downloadJson")] + public async Task DownloadJsonAsync(Guid id, CancellationToken ct) + { + (var stream, var fileName) = await _catalogService.DownloadJsonAsync(id, ct); + + // If this is wrapped in an Ok, it throws an exception + return File(stream, "application/octet-stream", fileName); + } + + } +} diff --git a/Blueprint.Api/Controllers/InjectController.cs b/Blueprint.Api/Controllers/InjectController.cs new file mode 100644 index 0000000..2d53977 --- /dev/null +++ b/Blueprint.Api/Controllers/InjectController.cs @@ -0,0 +1,133 @@ +// Copyright 2022 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Blueprint.Api.Infrastructure.Extensions; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.Infrastructure.QueryParameters; +using Blueprint.Api.Services; +using Swashbuckle.AspNetCore.Annotations; + +namespace Blueprint.Api.Controllers +{ + public class InjectController : BaseController + { + private readonly IInjectService _injectService; + private readonly IAuthorizationService _authorizationService; + + public InjectController(IInjectService injectService, IAuthorizationService authorizationService) + { + _injectService = injectService; + _authorizationService = authorizationService; + } + + /// + /// Gets Injects for a Catalog + /// + /// + /// Returns a list of Injects for the Catalog. + /// + /// The ID of the Catalog + /// + /// + [HttpGet("catalogs/{catalogId}/injects")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getInjectsByCatalog")] + public async Task GetByCatalog(Guid catalogId, CancellationToken ct) + { + var list = await _injectService.GetByCatalogAsync(catalogId, ct); + return Ok(list); + } + + /// + /// Gets a specific Inject by id + /// + /// + /// Returns the Inject with the id specified + /// + /// Accessible to a User that is a member of a Team within the specified Inject + /// + /// The id of the Inject + /// + /// + [HttpGet("injects/{id}")] + [ProducesResponseType(typeof(ViewModels.Inject), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getInject")] + public async Task Get(Guid id, CancellationToken ct) + { + var inject = await _injectService.GetAsync(id, ct); + + if (inject == null) + throw new EntityNotFoundException(); + + return Ok(inject); + } + + /// + /// Creates a new Inject + /// + /// + /// Creates a new Inject with the attributes specified + /// + /// Accessible only to a SuperUser or an Administrator + /// + /// The ID of the catalog where the inject will be added + /// The data to create the Inject with + /// + [HttpPost("catalog/{catalogId}/injects")] + [ProducesResponseType(typeof(ViewModels.Inject), (int)HttpStatusCode.Created)] + [SwaggerOperation(OperationId = "createInject")] + public async Task Create([FromRoute] Guid catalogId, [FromBody] ViewModels.Inject inject, CancellationToken ct) + { + var list = await _injectService.CreateAsync(catalogId, inject, ct); + return Ok(list); + } + + /// + /// Updates an Inject + /// + /// + /// Updates an Inject with the attributes specified + /// + /// Accessible only to a SuperUser or a User on an Admin Team within the specified Inject + /// + /// The Id of the Inject to update + /// The updated Inject values + /// + [HttpPut("injects/{id}")] + [ProducesResponseType(typeof(ViewModels.Inject), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "updateInject")] + public async Task Update([FromRoute] Guid id, [FromBody] ViewModels.Inject inject, CancellationToken ct) + { + inject.ModifiedBy = User.GetId(); + var list = await _injectService.UpdateAsync(id, inject, ct); + return Ok(list); + } + + /// + /// Deletes an Inject + /// + /// + /// Deletes an Inject with the specified id + /// + /// Accessible only to a SuperUser or a User on an Admin Team within the specified Inject + /// + /// The id of the Inject to delete + /// + [HttpDelete("injects/{id}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [SwaggerOperation(OperationId = "deleteInject")] + public async Task Delete(Guid id, CancellationToken ct) + { + var returnVal = await _injectService.DeleteAsync(id, ct); + return Ok(returnVal); + } + + } +} diff --git a/Blueprint.Api/Controllers/InjectTypeController.cs b/Blueprint.Api/Controllers/InjectTypeController.cs new file mode 100644 index 0000000..97fb1bf --- /dev/null +++ b/Blueprint.Api/Controllers/InjectTypeController.cs @@ -0,0 +1,133 @@ +// Copyright 2022 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Blueprint.Api.Infrastructure.Extensions; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.Services; +using Blueprint.Api.ViewModels; +using Swashbuckle.AspNetCore.Annotations; + +namespace Blueprint.Api.Controllers +{ + public class InjectTypeController : BaseController + { + private readonly IInjectTypeService _injectTypeService; + private readonly IAuthorizationService _authorizationService; + + public InjectTypeController(IInjectTypeService injectTypeService, IAuthorizationService authorizationService) + { + _injectTypeService = injectTypeService; + _authorizationService = authorizationService; + } + + /// + /// Gets all InjectType in the system + /// + /// + /// Returns a list of all of the InjectTypes in the system. + /// + /// Only accessible to a SuperUser + /// + /// + [HttpGet("injectTypes")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getInjectTypes")] + public async Task Get(CancellationToken ct) + { + var list = await _injectTypeService.GetAsync(ct); + return Ok(list); + } + + /// + /// Gets a specific InjectType by id + /// + /// + /// Returns the InjectType with the id specified + /// + /// Accessible to a SuperUser or a User that is a member of a Team within the specified InjectType + /// + /// The id of the InjectType + /// + /// + [HttpGet("injectTypes/{id}")] + [ProducesResponseType(typeof(InjectType), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getInjectType")] + public async Task Get(Guid id, CancellationToken ct) + { + var injectType = await _injectTypeService.GetAsync(id, ct); + + if (injectType == null) + throw new EntityNotFoundException(); + + return Ok(injectType); + } + + /// + /// Creates a new InjectType + /// + /// + /// Creates a new InjectType with the attributes specified + /// + /// Accessible only to a SuperUser or an Administrator + /// + /// The data to create the InjectType with + /// + [HttpPost("injectTypes")] + [ProducesResponseType(typeof(InjectType), (int)HttpStatusCode.Created)] + [SwaggerOperation(OperationId = "createInjectType")] + public async Task Create([FromBody] InjectType injectType, CancellationToken ct) + { + injectType.CreatedBy = User.GetId(); + var createdInjectType = await _injectTypeService.CreateAsync(injectType, ct); + return CreatedAtAction(nameof(this.Get), new { id = createdInjectType.Id }, createdInjectType); + } + + /// + /// Updates a InjectType + /// + /// + /// Updates a InjectType with the attributes specified + /// + /// Accessible only to a SuperUser or a User on an Admin Team within the specified InjectType + /// + /// The Id of the Exericse to update + /// The updated InjectType values + /// + [HttpPut("injectTypes/{id}")] + [ProducesResponseType(typeof(InjectType), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "updateInjectType")] + public async Task Update([FromRoute] Guid id, [FromBody] InjectType injectType, CancellationToken ct) + { + injectType.ModifiedBy = User.GetId(); + var updatedInjectType = await _injectTypeService.UpdateAsync(id, injectType, ct); + return Ok(updatedInjectType); + } + + /// + /// Deletes a InjectType + /// + /// + /// Deletes a InjectType with the specified id + /// + /// Accessible only to a SuperUser or a User on an Admin Team within the specified InjectType + /// + /// The id of the InjectType to delete + /// + [HttpDelete("injectTypes/{id}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [SwaggerOperation(OperationId = "deleteInjectType")] + public async Task Delete(Guid id, CancellationToken ct) + { + await _injectTypeService.DeleteAsync(id, ct); + return NoContent(); + } + + } +} diff --git a/Blueprint.Api/Services/CatalogInjectService.cs b/Blueprint.Api/Services/CatalogInjectService.cs new file mode 100644 index 0000000..d6cb545 --- /dev/null +++ b/Blueprint.Api/Services/CatalogInjectService.cs @@ -0,0 +1,106 @@ +// Copyright 2022 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact inject@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Blueprint.Api.Data; +using Blueprint.Api.Data.Models; +using Blueprint.Api.Infrastructure.Authorization; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.Infrastructure.Extensions; +using Blueprint.Api.ViewModels; + +namespace Blueprint.Api.Services +{ + public interface ICatalogInjectService + { + Task GetAsync(Guid id, CancellationToken ct); + Task CreateAsync(ViewModels.CatalogInject catalogInject, CancellationToken ct); + Task DeleteAsync(Guid id, CancellationToken ct); + Task DeleteByIdsAsync(Guid catalogId, Guid injectId, CancellationToken ct); + } + + public class CatalogInjectService : ICatalogInjectService + { + private readonly BlueprintContext _context; + private readonly IAuthorizationService _authorizationService; + private readonly ClaimsPrincipal _catalog; + private readonly IMapper _mapper; + private readonly ILogger _logger; + + public CatalogInjectService(BlueprintContext context, IAuthorizationService authorizationService, IPrincipal catalog, ILogger logger, IMapper mapper) + { + _context = context; + _authorizationService = authorizationService; + _catalog = catalog as ClaimsPrincipal; + _mapper = mapper; + _logger = logger; + } + + public async Task GetAsync(Guid id, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_catalog, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + var item = await _context.CatalogInjects + .SingleOrDefaultAsync(o => o.Id == id, ct); + + return _mapper.Map(item); + } + + public async Task CreateAsync(ViewModels.CatalogInject catalogInject, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_catalog, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + catalogInject.Id = catalogInject.Id != Guid.Empty ? catalogInject.Id : Guid.NewGuid(); + var catalogInjectEntity = _mapper.Map(catalogInject); + + _context.CatalogInjects.Add(catalogInjectEntity); + await _context.SaveChangesAsync(ct); + _logger.LogWarning($"CatalogInject created by {_catalog.GetId()} = catalog: {catalogInject.CatalogId} and inject: {catalogInject.InjectId}"); + return await GetAsync(catalogInjectEntity.Id, ct); + } + + public async Task DeleteAsync(Guid id, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_catalog, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + var catalogInjectToDelete = await _context.CatalogInjects.SingleOrDefaultAsync(v => v.Id == id, ct); + + if (catalogInjectToDelete == null) + throw new EntityNotFoundException(); + + _context.CatalogInjects.Remove(catalogInjectToDelete); + await _context.SaveChangesAsync(ct); + _logger.LogWarning($"CatalogInject deleted by {_catalog.GetId()} = catalog: {catalogInjectToDelete.CatalogId} and inject: {catalogInjectToDelete.InjectId}"); + return true; + } + + public async Task DeleteByIdsAsync(Guid catalogId, Guid injectId, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_catalog, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + + var catalogInjectToDelete = await _context.CatalogInjects.SingleOrDefaultAsync(v => v.CatalogId == catalogId && v.InjectId == injectId, ct); + + if (catalogInjectToDelete == null) + throw new EntityNotFoundException(); + + _context.CatalogInjects.Remove(catalogInjectToDelete); + await _context.SaveChangesAsync(ct); + _logger.LogWarning($"CatalogInject deleted by {_catalog.GetId()} = catalog: {catalogInjectToDelete.CatalogId} and inject: {catalogInjectToDelete.InjectId}"); + return true; + } + + } +} diff --git a/Blueprint.Api/Services/CatalogService.cs b/Blueprint.Api/Services/CatalogService.cs new file mode 100644 index 0000000..e7eaff7 --- /dev/null +++ b/Blueprint.Api/Services/CatalogService.cs @@ -0,0 +1,300 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Blueprint.Api.Data; +using Blueprint.Api.Data.Enumerations; +using Blueprint.Api.Data.Models; +using Blueprint.Api.Infrastructure.Authorization; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.Infrastructure.Extensions; +using Blueprint.Api.Infrastructure.Options; +using Blueprint.Api.Infrastructure.QueryParameters; +using Blueprint.Api.ViewModels; +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Spreadsheet; +using DocumentFormat.OpenXml.Packaging; + +namespace Blueprint.Api.Services +{ + public interface ICatalogService + { + Task> GetAsync(CancellationToken ct); + Task> GetMineAsync(CancellationToken ct); + Task> GetUserCatalogsAsync(Guid userId, CancellationToken ct); + Task GetAsync(Guid id, CancellationToken ct); + Task CreateAsync(ViewModels.Catalog catalog, CancellationToken ct); + Task CopyAsync(Guid catalogId, CancellationToken ct); + Task UpdateAsync(Guid id, ViewModels.Catalog catalog, CancellationToken ct); + Task DeleteAsync(Guid id, CancellationToken ct); + Task UploadJsonAsync(FileForm form, CancellationToken ct); + Task> DownloadJsonAsync(Guid catalogId, CancellationToken ct); + } + + public class CatalogService : ICatalogService + { + private readonly BlueprintContext _context; + private readonly IAuthorizationService _authorizationService; + private readonly ClaimsPrincipal _user; + private readonly IMapper _mapper; + private readonly ILogger _logger; + private readonly ClientOptions _clientOptions; + private readonly IScenarioEventService _scenarioEventService; + private readonly IIntegrationQueue _integrationQueue; + private readonly IPlayerService _playerService; + private readonly IJoinQueue _joinQueue; + + public CatalogService( + BlueprintContext context, + ClientOptions clientOptions, + IAuthorizationService authorizationService, + IScenarioEventService scenarioEventService, + IIntegrationQueue integrationQueue, + IPlayerService playerService, + IJoinQueue joinQueue, + IPrincipal user, + ILogger logger, + IMapper mapper) + { + _context = context; + _clientOptions = clientOptions; + _authorizationService = authorizationService; + _scenarioEventService = scenarioEventService; + _user = user as ClaimsPrincipal; + _mapper = mapper; + _logger = logger; + _integrationQueue = integrationQueue; + _playerService = playerService; + _joinQueue = joinQueue; + } + + public async Task> GetAsync(CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + + var catalogs = await _context.Catalogs.ToListAsync(ct); + + return _mapper.Map>(catalogs); + } + + public async Task> GetMineAsync(CancellationToken ct) + { + var userId = _user.GetId(); + return await GetUserCatalogsAsync(userId, ct); + } + + public async Task> GetUserCatalogsAsync(Guid userId, CancellationToken ct) + { + var currentUserId = _user.GetId(); + if (currentUserId == userId) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new BaseUserRequirement())).Succeeded) + throw new ForbiddenException(); + } + else + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new FullRightsRequirement())).Succeeded) + throw new ForbiddenException(); + } + // get the user's units + var unitIdList = await _context.UnitUsers + .Where(tu => tu.UserId == userId) + .Select(tu => tu.UnitId) + .ToListAsync(ct); + // get the units' catalogs + var unitCatalogList = await _context.CatalogUnits + .Where(mu => unitIdList.Contains(mu.UnitId)) + .Select(mu => mu.Catalog) + .ToListAsync(ct); + // get catalogs created by user + var myCatalogList = new List(); + if ((await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + { + myCatalogList = await _context.Catalogs + .Where(m => m.CreatedBy == userId) + .ToListAsync(ct); + } + // combine lists + var catalogList = unitCatalogList.Union(myCatalogList).OrderBy(m => m.Name); + + return _mapper.Map>(catalogList); + } + + public async Task GetAsync(Guid id, CancellationToken ct) + { + if (!await CatalogViewRequirement.IsMet(_user.GetId(), id, _context) && + !(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + var catalogEntity = await _context.Catalogs + .SingleOrDefaultAsync(sm => sm.Id == id, ct); + + return _mapper.Map(catalogEntity); + } + + public async Task CreateAsync(ViewModels.Catalog catalog, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + catalog.Id = catalog.Id != Guid.Empty ? catalog.Id : Guid.NewGuid(); + catalog.DateCreated = DateTime.UtcNow; + catalog.CreatedBy = _user.GetId(); + catalog.DateModified = catalog.DateCreated; + catalog.ModifiedBy = catalog.CreatedBy; + var catalogEntity = _mapper.Map(catalog); + + _context.Catalogs.Add(catalogEntity); + await _context.SaveChangesAsync(ct); + catalog = await GetAsync(catalogEntity.Id, ct); + + return catalog; + } + + public async Task CopyAsync(Guid catalogId, CancellationToken ct) + { + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + var newCatalogEntity = await _context.Catalogs + .Include(m => m.CatalogInjects) + .AsNoTracking() + .SingleOrDefaultAsync(m => m.Id == catalogId); + newCatalogEntity = await privateCatalogCopyAsync(newCatalogEntity, ct); + var catalog = _mapper.Map(newCatalogEntity); + + return catalog; + } + + private async Task privateCatalogCopyAsync(CatalogEntity catalogEntity, CancellationToken ct) + { + // assign a new ID + catalogEntity.Id = Guid.NewGuid(); + // update all CatalogInjects to have a new ID and the new catalog ID + foreach (var ci in catalogEntity.CatalogInjects) + { + ci.Id = Guid.NewGuid(); + ci.CatalogId = catalogEntity.Id; + } + await _context.Catalogs.AddAsync(catalogEntity); + await _context.SaveChangesAsync(); + return catalogEntity; + } + + public async Task UpdateAsync(Guid id, ViewModels.Catalog catalog, CancellationToken ct) + { + // user must be a Content Developer or a Catalog owner + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + var catalogToUpdate = await _context.Catalogs.SingleOrDefaultAsync(v => v.Id == id, ct); + if (catalogToUpdate == null) + throw new EntityNotFoundException(); + + // okay to update this catalog + catalog.CreatedBy = catalogToUpdate.CreatedBy; + catalog.DateCreated = catalogToUpdate.DateCreated; + catalog.ModifiedBy = _user.GetId(); + catalog.DateModified = DateTime.UtcNow; + _mapper.Map(catalog, catalogToUpdate); + + _context.Catalogs.Update(catalogToUpdate); + await _context.SaveChangesAsync(ct); + + catalog = await GetAsync(catalogToUpdate.Id, ct); + + return catalog; + } + + public async Task DeleteAsync(Guid id, CancellationToken ct) + { + // user must be a Content Developer or a Catalog owner + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + // delete the Catalog + var catalogToDelete = await _context.Catalogs.SingleOrDefaultAsync(v => v.Id == id, ct); + if (catalogToDelete == null) + throw new EntityNotFoundException(); + + _context.Catalogs.Remove(catalogToDelete); + await _context.SaveChangesAsync(ct); + + return true; + } + + public async Task> DownloadJsonAsync(Guid catalogId, CancellationToken ct) + { + // user must be a Content Developer + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + var catalog = await _context.Catalogs + .SingleOrDefaultAsync(m => m.Id == catalogId); + if (catalog == null) + { + throw new EntityNotFoundException("Catalog not found " + catalogId); + } + + var catalogJson = ""; + var options = new JsonSerializerOptions() + { + ReferenceHandler = ReferenceHandler.Preserve + }; + catalogJson = JsonSerializer.Serialize(catalog, options); + // convert string to stream + byte[] byteArray = Encoding.ASCII.GetBytes(catalogJson); + MemoryStream memoryStream = new MemoryStream(byteArray); + var filename = catalog.Name.ToLower().EndsWith(".json") ? catalog.Name : catalog.Name + ".json"; + + return System.Tuple.Create(memoryStream, filename); + } + + public async Task UploadJsonAsync(FileForm form, CancellationToken ct) + { + // user must be a Content Developer + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + var uploadItem = form.ToUpload; + var catalogJson = ""; + using (StreamReader reader = new StreamReader(uploadItem.OpenReadStream())) + { + // convert stream to string + catalogJson = reader.ReadToEnd(); + } + var options = new JsonSerializerOptions() + { + ReferenceHandler = ReferenceHandler.Preserve + }; + var catalogEntity = JsonSerializer.Deserialize(catalogJson, options); + // create copies of the injects, if they don't already exist + foreach (var inject in catalogEntity.CatalogInjects) + { + // TODO: create new injects and replace InjectId on CatalogInject + } + // make a copy of the catalog and add it to the database + catalogEntity = await privateCatalogCopyAsync(catalogEntity, ct); + + return _mapper.Map(catalogEntity); + } + + } +} diff --git a/Blueprint.Api/Services/CatalogUnitService.cs b/Blueprint.Api/Services/CatalogUnitService.cs new file mode 100644 index 0000000..89760ae --- /dev/null +++ b/Blueprint.Api/Services/CatalogUnitService.cs @@ -0,0 +1,169 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Blueprint.Api.Data; +using Blueprint.Api.Data.Models; +using Blueprint.Api.Infrastructure.Authorization; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.Infrastructure.Extensions; +using Blueprint.Api.ViewModels; + +namespace Blueprint.Api.Services +{ + public interface ICatalogUnitService + { + Task> GetByCatalogAsync(Guid catalogId, CancellationToken ct); + Task GetAsync(Guid id, CancellationToken ct); + Task CreateAsync(ViewModels.CatalogUnit catalogUnit, CancellationToken ct); + Task UpdateAsync(Guid id, ViewModels.CatalogUnit catalogUnit, CancellationToken ct); + Task DeleteAsync(Guid id, CancellationToken ct); + Task DeleteByIdsAsync(Guid catalogId, Guid unitId, CancellationToken ct); + } + + public class CatalogUnitService : ICatalogUnitService + { + private readonly BlueprintContext _context; + private readonly IAuthorizationService _authorizationService; + private readonly ClaimsPrincipal _user; + private readonly IMapper _mapper; + private readonly ILogger _logger; + + public CatalogUnitService(BlueprintContext context, IAuthorizationService authorizationService, IPrincipal user, ILogger logger, IMapper mapper) + { + _context = context; + _authorizationService = authorizationService; + _user = user as ClaimsPrincipal; + _mapper = mapper; + _logger = logger; + } + + public async Task> GetByCatalogAsync(Guid catalogId, CancellationToken ct) + { + var catalog = await _context.Catalogs.SingleOrDefaultAsync(v => v.Id == catalogId, ct); + if (catalog == null) + throw new EntityNotFoundException(); + + // user must be a Content Developer or a Catalog owner + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + var items = await _context.CatalogUnits + .Where(tc => tc.CatalogId == catalogId) + .Include(mt => mt.Unit) + .ThenInclude(t => t.UnitUsers) + .ThenInclude(tu => tu.User) + .ToListAsync(ct); + + return _mapper.Map>(items); + } + + public async Task GetAsync(Guid id, CancellationToken ct) + { + var catalogUnit = await _context.CatalogUnits.SingleOrDefaultAsync(v => v.Id == id, ct); + if (catalogUnit == null) + throw new EntityNotFoundException(); + + // user must be a Content Developer or a Catalog Viewer + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded && + !(await CatalogViewRequirement.IsMet(_user.GetId(), catalogUnit.CatalogId, _context))) + throw new ForbiddenException(); + + var item = await _context.CatalogUnits + .Include(mt => mt.Unit) + .ThenInclude(t => t.UnitUsers) + .ThenInclude(tu => tu.User) + .SingleOrDefaultAsync(o => o.Id == id, ct); + + return _mapper.Map(item); + } + + public async Task CreateAsync(ViewModels.CatalogUnit catalogUnit, CancellationToken ct) + { + var catalog = await _context.Catalogs.SingleOrDefaultAsync(v => v.Id == catalogUnit.CatalogId, ct); + if (catalog == null) + throw new EntityNotFoundException(); + + var unit = await _context.Units.SingleOrDefaultAsync(v => v.Id == catalogUnit.UnitId, ct); + if (unit == null) + throw new EntityNotFoundException(); + + // user must be a Content Developer or a Catalog owner + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + if (await _context.CatalogUnits.AnyAsync(mt => mt.UnitId == unit.Id && mt.CatalogId == catalog.Id)) + throw new ArgumentException("Catalog Unit already exists."); + + var catalogUnitEntity = _mapper.Map(catalogUnit); + catalogUnitEntity.Id = catalogUnitEntity.Id != Guid.Empty ? catalogUnitEntity.Id : Guid.NewGuid(); + + _context.CatalogUnits.Add(catalogUnitEntity); + await _context.SaveChangesAsync(ct); + _logger.LogWarning($"Unit {catalogUnit.UnitId} added to Catalog {catalogUnit.CatalogId} by {_user.GetId()}"); + return await GetAsync(catalogUnitEntity.Id, ct); + } + + public async Task UpdateAsync(Guid id, ViewModels.CatalogUnit catalogUnit, CancellationToken ct) + { + // user must be a Content Developer or a Catalog owner + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + var catalogUnitToUpdate = await _context.CatalogUnits.SingleOrDefaultAsync(v => v.Id == id, ct); + if (catalogUnitToUpdate == null) + throw new EntityNotFoundException(); + + _mapper.Map(catalogUnit, catalogUnitToUpdate); + + _context.CatalogUnits.Update(catalogUnitToUpdate); + await _context.SaveChangesAsync(ct); + + catalogUnit = await GetAsync(catalogUnitToUpdate.Id, ct); + return catalogUnit; + } + + public async Task DeleteAsync(Guid id, CancellationToken ct) + { + var catalogUnitToDelete = await _context.CatalogUnits.SingleOrDefaultAsync(v => v.Id == id, ct); + if (catalogUnitToDelete == null) + throw new EntityNotFoundException(); + + // user must be a Content Developer or a Catalog owner + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + _context.CatalogUnits.Remove(catalogUnitToDelete); + await _context.SaveChangesAsync(ct); + _logger.LogWarning($"Unit {catalogUnitToDelete.UnitId} removed from Catalog {catalogUnitToDelete.CatalogId} by {_user.GetId()}"); + return true; + } + + public async Task DeleteByIdsAsync(Guid catalogId, Guid unitId, CancellationToken ct) + { + var catalogUnitToDelete = await _context.CatalogUnits.SingleOrDefaultAsync(v => (v.UnitId == unitId) && (v.CatalogId == catalogId), ct); + if (catalogUnitToDelete == null) + throw new EntityNotFoundException(); + + // user must be a Content Developer or a Catalog owner + if (!(await _authorizationService.AuthorizeAsync(_user, null, new ContentDeveloperRequirement())).Succeeded) + throw new ForbiddenException(); + + _context.CatalogUnits.Remove(catalogUnitToDelete); + await _context.SaveChangesAsync(ct); + _logger.LogWarning($"Unit {catalogUnitToDelete.UnitId} removed from Catalog {catalogUnitToDelete.CatalogId} by {_user.GetId()}"); + return true; + } + + } +} From f332c3fe3e7886474643dc7b9fa5383e49257ff4 Mon Sep 17 00:00:00 2001 From: sei-tspencer Date: Thu, 13 Jun 2024 08:47:18 -0400 Subject: [PATCH 4/8] last controllers --- .../Controllers/CatalogInjectController.cs | 112 ++++++++++++++ .../Controllers/CatalogUnitController.cs | 145 ++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 Blueprint.Api/Controllers/CatalogInjectController.cs create mode 100644 Blueprint.Api/Controllers/CatalogUnitController.cs diff --git a/Blueprint.Api/Controllers/CatalogInjectController.cs b/Blueprint.Api/Controllers/CatalogInjectController.cs new file mode 100644 index 0000000..61f199d --- /dev/null +++ b/Blueprint.Api/Controllers/CatalogInjectController.cs @@ -0,0 +1,112 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. + +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.Services; +using Blueprint.Api.ViewModels; +using Swashbuckle.AspNetCore.Annotations; + +namespace Blueprint.Api.Controllers +{ + public class CatalogInjectController : BaseController + { + private readonly ICatalogInjectService _catalogInjectService; + private readonly IAuthorizationService _authorizationService; + + public CatalogInjectController(ICatalogInjectService catalogInjectService, IAuthorizationService authorizationService) + { + _catalogInjectService = catalogInjectService; + _authorizationService = authorizationService; + } + + /// + /// Gets a specific CatalogInject by id + /// + /// + /// Returns the CatalogInject with the id specified + /// + /// Only accessible to a SuperCatalog + /// + /// The id of the CatalogInject + /// + /// + [HttpGet("cataloginjects/{id}")] + [ProducesResponseType(typeof(CatalogInject), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getCatalogInject")] + public async Task Get(Guid id, CancellationToken ct) + { + var inject = await _catalogInjectService.GetAsync(id, ct); + + if (inject == null) + throw new EntityNotFoundException(); + + return Ok(inject); + } + + /// + /// Creates a new CatalogInject + /// + /// + /// Creates a new CatalogInject with the attributes specified + /// + /// Accessible only to a SuperCatalog + /// + /// The data to create the CatalogInject with + /// + [HttpPost("cataloginjects")] + [ProducesResponseType(typeof(CatalogInject), (int)HttpStatusCode.Created)] + [SwaggerOperation(OperationId = "createCatalogInject")] + public async Task Create([FromBody] CatalogInject inject, CancellationToken ct) + { + var createdCatalogInject = await _catalogInjectService.CreateAsync(inject, ct); + return CreatedAtAction(nameof(this.Get), new { id = createdCatalogInject.Id }, createdCatalogInject); + } + + /// + /// Deletes a CatalogInject + /// + /// + /// Deletes a CatalogInject with the specified id + /// + /// Accessible only to a SuperCatalog + /// + /// The id of the CatalogInject to delete + /// + [HttpDelete("cataloginjects/{id}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [SwaggerOperation(OperationId = "deleteCatalogInject")] + public async Task Delete(Guid id, CancellationToken ct) + { + await _catalogInjectService.DeleteAsync(id, ct); + return NoContent(); + } + + /// + /// Deletes a CatalogInject by catalog ID and inject ID + /// + /// + /// Deletes a CatalogInject with the specified catalog ID and inject ID + /// + /// Accessible only to a SuperCatalog + /// + /// ID of a catalog. + /// ID of a inject. + /// + [HttpDelete("catalogs/{catalogId}/injects/{injectId}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [SwaggerOperation(OperationId = "deleteCatalogInjectByIds")] + public async Task Delete(Guid catalogId, Guid injectId, CancellationToken ct) + { + await _catalogInjectService.DeleteByIdsAsync(catalogId, injectId, ct); + return NoContent(); + } + + } +} diff --git a/Blueprint.Api/Controllers/CatalogUnitController.cs b/Blueprint.Api/Controllers/CatalogUnitController.cs new file mode 100644 index 0000000..5704491 --- /dev/null +++ b/Blueprint.Api/Controllers/CatalogUnitController.cs @@ -0,0 +1,145 @@ +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. +// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Blueprint.Api.Infrastructure.Exceptions; +using Blueprint.Api.Services; +using Blueprint.Api.ViewModels; +using Swashbuckle.AspNetCore.Annotations; + +namespace Blueprint.Api.Controllers +{ + public class CatalogUnitController : BaseController + { + private readonly ICatalogUnitService _catalogUnitService; + private readonly IAuthorizationService _authorizationService; + + public CatalogUnitController(ICatalogUnitService catalogUnitService, IAuthorizationService authorizationService) + { + _catalogUnitService = catalogUnitService; + _authorizationService = authorizationService; + } + + /// + /// Gets all CatalogUnits for a Catalog + /// + /// + /// Returns a list of all of the CatalogUnits for the catalog. + /// + /// The id of the Catalog + /// + /// + [HttpGet("catalogs/{catalogId}/catalogunits")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getCatalogUnits")] + public async Task GetByCatalog(Guid catalogId, CancellationToken ct) + { + var list = await _catalogUnitService.GetByCatalogAsync(catalogId, ct); + return Ok(list); + } + + /// + /// Gets a specific CatalogUnit by id + /// + /// + /// Returns the CatalogUnit with the id specified + /// + /// + /// The id of the CatalogUnit + /// + /// + [HttpGet("catalogunits/{id}")] + [ProducesResponseType(typeof(CatalogUnit), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "getCatalogUnit")] + public async Task Get(Guid id, CancellationToken ct) + { + var unit = await _catalogUnitService.GetAsync(id, ct); + + if (unit == null) + throw new EntityNotFoundException(); + + return Ok(unit); + } + + /// + /// Creates a new CatalogUnit + /// + /// + /// Creates a new CatalogUnit with the attributes specified + /// + /// + /// The data to create the CatalogUnit with + /// + [HttpPost("catalogunits")] + [ProducesResponseType(typeof(CatalogUnit), (int)HttpStatusCode.Created)] + [SwaggerOperation(OperationId = "createCatalogUnit")] + public async Task Create([FromBody] CatalogUnit catalogUnit, CancellationToken ct) + { + var createdCatalogUnit = await _catalogUnitService.CreateAsync(catalogUnit, ct); + return CreatedAtAction(nameof(this.Get), new { id = createdCatalogUnit.Id }, createdCatalogUnit); + } + + /// + /// Updates a CatalogUnit + /// + /// + /// Updates a CatalogUnit with the attributes specified + /// + /// + /// The Id of the CatalogUnit to update + /// The updated CatalogUnit values + /// + [HttpPut("catalogunits/{id}")] + [ProducesResponseType(typeof(Unit), (int)HttpStatusCode.OK)] + [SwaggerOperation(OperationId = "updateCatalogUnit")] + public async Task Update([FromRoute] Guid id, [FromBody] CatalogUnit catalogUnit, CancellationToken ct) + { + var updatedUnit = await _catalogUnitService.UpdateAsync(id, catalogUnit, ct); + return Ok(updatedUnit); + } + + /// + /// Deletes a CatalogUnit + /// + /// + /// Deletes a CatalogUnit with the specified id + /// + /// + /// The id of the CatalogUnit to delete + /// + [HttpDelete("catalogunits/{id}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [SwaggerOperation(OperationId = "deleteCatalogUnit")] + public async Task Delete(Guid id, CancellationToken ct) + { + await _catalogUnitService.DeleteAsync(id, ct); + return NoContent(); + } + + /// + /// Deletes a CatalogUnit by catalog ID and unit ID + /// + /// + /// Deletes a CatalogUnit with the specified catalog ID and unit ID + /// + /// + /// ID of a catalog. + /// ID of a unit. + /// + [HttpDelete("catalogs/{catalogId}/units/{unitId}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [SwaggerOperation(OperationId = "deleteCatalogUnitByIds")] + public async Task Delete(Guid catalogId, Guid unitId, CancellationToken ct) + { + await _catalogUnitService.DeleteByIdsAsync(catalogId, unitId, ct); + return NoContent(); + } + + } +} From 39298db9fa4130686e6e85f87b76016f7cd8e4c3 Mon Sep 17 00:00:00 2001 From: sei-tspencer Date: Thu, 13 Jun 2024 08:51:49 -0400 Subject: [PATCH 5/8] clean up --- Blueprint.Api/Controllers/CatalogController.cs | 5 +---- Blueprint.Api/Controllers/CatalogInjectController.cs | 1 - Blueprint.Api/Controllers/InjectController.cs | 3 +-- Blueprint.Api/Controllers/InjectTypeController.cs | 2 +- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Blueprint.Api/Controllers/CatalogController.cs b/Blueprint.Api/Controllers/CatalogController.cs index ad835db..74a9b15 100644 --- a/Blueprint.Api/Controllers/CatalogController.cs +++ b/Blueprint.Api/Controllers/CatalogController.cs @@ -1,17 +1,14 @@ -// Copyright 2022 Carnegie Mellon University. All Rights Reserved. +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. // Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. using System; using System.Collections.Generic; -using System.Data; using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Blueprint.Api.Data.Enumerations; using Blueprint.Api.Infrastructure.Extensions; -using Blueprint.Api.Infrastructure.QueryParameters; using Blueprint.Api.Services; using Blueprint.Api.ViewModels; using Swashbuckle.AspNetCore.Annotations; diff --git a/Blueprint.Api/Controllers/CatalogInjectController.cs b/Blueprint.Api/Controllers/CatalogInjectController.cs index 61f199d..144c8dd 100644 --- a/Blueprint.Api/Controllers/CatalogInjectController.cs +++ b/Blueprint.Api/Controllers/CatalogInjectController.cs @@ -2,7 +2,6 @@ // Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. using System; -using System.Collections.Generic; using System.Net; using System.Threading; using System.Threading.Tasks; diff --git a/Blueprint.Api/Controllers/InjectController.cs b/Blueprint.Api/Controllers/InjectController.cs index 2d53977..9a7479c 100644 --- a/Blueprint.Api/Controllers/InjectController.cs +++ b/Blueprint.Api/Controllers/InjectController.cs @@ -1,4 +1,4 @@ -// Copyright 2022 Carnegie Mellon University. All Rights Reserved. +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. // Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. using System; @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Mvc; using Blueprint.Api.Infrastructure.Extensions; using Blueprint.Api.Infrastructure.Exceptions; -using Blueprint.Api.Infrastructure.QueryParameters; using Blueprint.Api.Services; using Swashbuckle.AspNetCore.Annotations; diff --git a/Blueprint.Api/Controllers/InjectTypeController.cs b/Blueprint.Api/Controllers/InjectTypeController.cs index 97fb1bf..55ce05f 100644 --- a/Blueprint.Api/Controllers/InjectTypeController.cs +++ b/Blueprint.Api/Controllers/InjectTypeController.cs @@ -1,4 +1,4 @@ -// Copyright 2022 Carnegie Mellon University. All Rights Reserved. +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. // Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact permission@sei.cmu.edu for full terms. using System; From aeeeea820d7de55376b327697ee8d2b70254dbc8 Mon Sep 17 00:00:00 2001 From: sei-tspencer Date: Thu, 13 Jun 2024 11:17:22 -0400 Subject: [PATCH 6/8] clean up --- Blueprint.Api/Blueprint.Api.csproj | 2 +- Blueprint.Api/Services/CatalogInjectService.cs | 3 +-- Blueprint.Api/Services/CatalogService.cs | 5 ----- Blueprint.Api/ViewModels/Catalog.cs | 2 -- Blueprint.Api/ViewModels/Inject.cs | 2 -- 5 files changed, 2 insertions(+), 12 deletions(-) diff --git a/Blueprint.Api/Blueprint.Api.csproj b/Blueprint.Api/Blueprint.Api.csproj index 40a6763..4fbafee 100755 --- a/Blueprint.Api/Blueprint.Api.csproj +++ b/Blueprint.Api/Blueprint.Api.csproj @@ -1,7 +1,7 @@  - 1.1.1 + 1.2.0-rc1 net6.0 bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml CS1591 diff --git a/Blueprint.Api/Services/CatalogInjectService.cs b/Blueprint.Api/Services/CatalogInjectService.cs index d6cb545..46e187f 100644 --- a/Blueprint.Api/Services/CatalogInjectService.cs +++ b/Blueprint.Api/Services/CatalogInjectService.cs @@ -1,8 +1,7 @@ -// Copyright 2022 Carnegie Mellon University. All Rights Reserved. +// Copyright 2024 Carnegie Mellon University. All Rights Reserved. // Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact inject@sei.cmu.edu for full terms. using System; -using System.Collections.Generic; using System.Security.Claims; using System.Security.Principal; using System.Threading; diff --git a/Blueprint.Api/Services/CatalogService.cs b/Blueprint.Api/Services/CatalogService.cs index e7eaff7..5f97bb7 100644 --- a/Blueprint.Api/Services/CatalogService.cs +++ b/Blueprint.Api/Services/CatalogService.cs @@ -18,17 +18,12 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Blueprint.Api.Data; -using Blueprint.Api.Data.Enumerations; using Blueprint.Api.Data.Models; using Blueprint.Api.Infrastructure.Authorization; using Blueprint.Api.Infrastructure.Exceptions; using Blueprint.Api.Infrastructure.Extensions; using Blueprint.Api.Infrastructure.Options; -using Blueprint.Api.Infrastructure.QueryParameters; using Blueprint.Api.ViewModels; -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Spreadsheet; -using DocumentFormat.OpenXml.Packaging; namespace Blueprint.Api.Services { diff --git a/Blueprint.Api/ViewModels/Catalog.cs b/Blueprint.Api/ViewModels/Catalog.cs index 9c94dae..f23221e 100644 --- a/Blueprint.Api/ViewModels/Catalog.cs +++ b/Blueprint.Api/ViewModels/Catalog.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Blueprint.Api.Data.Enumerations; namespace Blueprint.Api.ViewModels { @@ -20,4 +19,3 @@ public class Catalog : Base public virtual ICollection Units { get; set; } = new HashSet(); } } - diff --git a/Blueprint.Api/ViewModels/Inject.cs b/Blueprint.Api/ViewModels/Inject.cs index d591fda..1e9c6d6 100644 --- a/Blueprint.Api/ViewModels/Inject.cs +++ b/Blueprint.Api/ViewModels/Inject.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Blueprint.Api.Data.Enumerations; namespace Blueprint.Api.ViewModels { @@ -16,4 +15,3 @@ public class Inject : Base public virtual ICollection DataValues { get; set; } = new HashSet(); } } - From 8c5005408529b262f1b13a4974a74fcf3a0a150a Mon Sep 17 00:00:00 2001 From: sei-tspencer Date: Thu, 13 Jun 2024 11:46:51 -0400 Subject: [PATCH 7/8] db migration --- Blueprint.Api.Data/Models/DataField.cs | 4 +- Blueprint.Api.Data/Models/DataValue.cs | 4 +- ...40613154406_catalog-and-inject.Designer.cs | 2087 +++++++++++++++++ .../20240613154406_catalog-and-inject.cs | 401 ++++ .../BlueprintContextModelSnapshot.cs | 360 ++- 5 files changed, 2839 insertions(+), 17 deletions(-) create mode 100644 Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.Designer.cs create mode 100644 Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.cs diff --git a/Blueprint.Api.Data/Models/DataField.cs b/Blueprint.Api.Data/Models/DataField.cs index a576ce1..abf1707 100644 --- a/Blueprint.Api.Data/Models/DataField.cs +++ b/Blueprint.Api.Data/Models/DataField.cs @@ -39,7 +39,8 @@ public class DataFieldEntityConfiguration : IEntityTypeConfiguration builder) { - builder.HasCheckConstraint("data_field_msel_or_inject_type", "msel_id IS NOT NULL XOR inject_type_id IS NOT NULL"); + builder.HasCheckConstraint("data_field_msel_or_inject_type", + "(msel_id IS NOT NULL AND inject_type_id IS NULL) OR (msel_id IS NULL AND inject_type_id IS NOT NULL)"); builder .HasOne(d => d.Msel) .WithMany(d => d.DataFields) @@ -52,4 +53,3 @@ public void Configure(EntityTypeBuilder builder) } } - diff --git a/Blueprint.Api.Data/Models/DataValue.cs b/Blueprint.Api.Data/Models/DataValue.cs index 614eccc..d344541 100644 --- a/Blueprint.Api.Data/Models/DataValue.cs +++ b/Blueprint.Api.Data/Models/DataValue.cs @@ -29,7 +29,8 @@ public class DataValueEntityConfiguration : IEntityTypeConfiguration builder) { builder.HasIndex(e => e.Id).IsUnique(); - builder.HasCheckConstraint("data_value_scenario_event_or_inject", "scenario_event_id IS NOT NULL XOR inject_id IS NOT NULL"); + builder.HasCheckConstraint("data_value_scenario_event_or_inject", + "(scenario_event_id IS NOT NULL AND inject_id IS NULL) OR (scenario_event_id IS NULL AND inject_id IS NOT NULL)"); builder.HasIndex(e => new { e.ScenarioEventId, e.InjectId, e.DataFieldId }).IsUnique(); builder .HasOne(d => d.ScenarioEvent) @@ -43,4 +44,3 @@ public void Configure(EntityTypeBuilder builder) } } - diff --git a/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.Designer.cs b/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.Designer.cs new file mode 100644 index 0000000..3054e2d --- /dev/null +++ b/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.Designer.cs @@ -0,0 +1,2087 @@ +// +using System; +using Blueprint.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Blueprint.Api.Migrations.PostgreSQL.Migrations +{ + [DbContext(typeof(BlueprintContext))] + [Migration("20240613154406_catalog-and-inject")] + partial class catalogandinject + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "uuid-ossp"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CardEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("GalleryId") + .HasColumnType("uuid") + .HasColumnName("gallery_id"); + + b.Property("Inject") + .HasColumnType("integer") + .HasColumnName("inject"); + + b.Property("IsTemplate") + .HasColumnType("boolean") + .HasColumnName("is_template"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Move") + .HasColumnType("integer") + .HasColumnName("move"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("MselId"); + + b.ToTable("cards"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CardTeamEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CanPostArticles") + .HasColumnType("boolean") + .HasColumnName("can_post_articles"); + + b.Property("CardId") + .HasColumnType("uuid") + .HasColumnName("card_id"); + + b.Property("IsShownOnWall") + .HasColumnType("boolean") + .HasColumnName("is_shown_on_wall"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.HasKey("Id"); + + b.HasIndex("CardId"); + + b.HasIndex("TeamId", "CardId") + .IsUnique(); + + b.ToTable("card_teams"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("InjectTypeId") + .HasColumnType("uuid") + .HasColumnName("inject_type_id"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("is_public"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("ParentId") + .HasColumnType("uuid") + .HasColumnName("parent_id"); + + b.HasKey("Id"); + + b.HasIndex("InjectTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("ParentId"); + + b.ToTable("catalogs"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogInjectEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CatalogId") + .HasColumnType("uuid") + .HasColumnName("catalog_id"); + + b.Property("InjectId") + .HasColumnType("uuid") + .HasColumnName("inject_id"); + + b.HasKey("Id"); + + b.HasIndex("CatalogId"); + + b.HasIndex("InjectId", "CatalogId") + .IsUnique(); + + b.ToTable("catalog_injects"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogUnitEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CatalogId") + .HasColumnType("uuid") + .HasColumnName("catalog_id"); + + b.Property("UnitId") + .HasColumnType("uuid") + .HasColumnName("unit_id"); + + b.HasKey("Id"); + + b.HasIndex("CatalogId"); + + b.HasIndex("UnitId", "CatalogId") + .IsUnique(); + + b.ToTable("catalog_units"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CiteActionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("ActionNumber") + .HasColumnType("integer") + .HasColumnName("action_number"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("InjectNumber") + .HasColumnType("integer") + .HasColumnName("inject_number"); + + b.Property("IsTemplate") + .HasColumnType("boolean") + .HasColumnName("is_template"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MoveNumber") + .HasColumnType("integer") + .HasColumnName("move_number"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.HasKey("Id"); + + b.HasIndex("MselId"); + + b.HasIndex("TeamId"); + + b.ToTable("cite_actions"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CiteRoleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("IsTemplate") + .HasColumnType("boolean") + .HasColumnName("is_template"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.HasKey("Id"); + + b.HasIndex("MselId"); + + b.HasIndex("TeamId"); + + b.ToTable("cite_roles"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.DataFieldEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CellMetadata") + .HasColumnType("text") + .HasColumnName("cell_metadata"); + + b.Property("ColumnMetadata") + .HasColumnType("text") + .HasColumnName("column_metadata"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DataType") + .HasColumnType("integer") + .HasColumnName("data_type"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("DisplayOrder") + .HasColumnType("integer") + .HasColumnName("display_order"); + + b.Property("GalleryArticleParameter") + .HasColumnType("text") + .HasColumnName("gallery_article_parameter"); + + b.Property("InjectTypeId") + .HasColumnType("uuid") + .HasColumnName("inject_type_id"); + + b.Property("IsChosenFromList") + .HasColumnType("boolean") + .HasColumnName("is_chosen_from_list"); + + b.Property("IsInitiallyHidden") + .HasColumnType("boolean") + .HasColumnName("is_initially_hidden"); + + b.Property("IsOnlyShownToOwners") + .HasColumnType("boolean") + .HasColumnName("is_only_shown_to_owners"); + + b.Property("IsTemplate") + .HasColumnType("boolean") + .HasColumnName("is_template"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("OnExerciseView") + .HasColumnType("boolean") + .HasColumnName("on_exercise_view"); + + b.Property("OnScenarioEventList") + .HasColumnType("boolean") + .HasColumnName("on_scenario_event_list"); + + b.HasKey("Id"); + + b.HasIndex("InjectTypeId"); + + b.HasIndex("MselId"); + + b.ToTable("data_fields"); + + b.HasCheckConstraint("data_field_msel_or_inject_type", "(msel_id IS NOT NULL AND inject_type_id IS NULL) OR (msel_id IS NULL AND inject_type_id IS NOT NULL)"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.DataOptionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DataFieldId") + .HasColumnType("uuid") + .HasColumnName("data_field_id"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("DisplayOrder") + .HasColumnType("integer") + .HasColumnName("display_order"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("OptionName") + .HasColumnType("text") + .HasColumnName("option_name"); + + b.Property("OptionValue") + .HasColumnType("text") + .HasColumnName("option_value"); + + b.HasKey("Id"); + + b.HasIndex("DataFieldId"); + + b.ToTable("data_options"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.DataValueEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CellMetadata") + .HasColumnType("text") + .HasColumnName("cell_metadata"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DataFieldId") + .HasColumnType("uuid") + .HasColumnName("data_field_id"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("InjectId") + .HasColumnType("uuid") + .HasColumnName("inject_id"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("ScenarioEventId") + .HasColumnType("uuid") + .HasColumnName("scenario_event_id"); + + b.Property("Value") + .HasColumnType("text") + .HasColumnName("value"); + + b.HasKey("Id"); + + b.HasIndex("DataFieldId"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("InjectId"); + + b.HasIndex("ScenarioEventId", "InjectId", "DataFieldId") + .IsUnique(); + + b.ToTable("data_values"); + + b.HasCheckConstraint("data_value_scenario_event_or_inject", "(scenario_event_id IS NOT NULL AND inject_id IS NULL) OR (scenario_event_id IS NULL AND inject_id IS NOT NULL)"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CatalogEntityId") + .HasColumnType("uuid") + .HasColumnName("catalog_entity_id"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("InjectTypeId") + .HasColumnType("uuid") + .HasColumnName("inject_type_id"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("RequiresInjectId") + .HasColumnType("uuid") + .HasColumnName("requires_inject_id"); + + b.HasKey("Id"); + + b.HasIndex("CatalogEntityId"); + + b.HasIndex("InjectTypeId"); + + b.HasIndex("RequiresInjectId"); + + b.ToTable("injects"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectTypeEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("inject_types"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InvitationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("EmailDomain") + .HasColumnType("text") + .HasColumnName("email_domain"); + + b.Property("ExpirationDateTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_date_time"); + + b.Property("IsTeamLeader") + .HasColumnType("boolean") + .HasColumnName("is_team_leader"); + + b.Property("MaxUsersAllowed") + .HasColumnType("integer") + .HasColumnName("max_users_allowed"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.Property("UserCount") + .HasColumnType("integer") + .HasColumnName("user_count"); + + b.Property("WasDeactivated") + .HasColumnType("boolean") + .HasColumnName("was_deactivated"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MselId"); + + b.HasIndex("TeamId"); + + b.ToTable("invitations"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MoveEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("DeltaSeconds") + .HasColumnType("integer") + .HasColumnName("delta_seconds"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MoveNumber") + .HasColumnType("integer") + .HasColumnName("move_number"); + + b.Property("MoveStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("move_start_time"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("SituationDescription") + .HasColumnType("text") + .HasColumnName("situation_description"); + + b.Property("SituationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("situation_time"); + + b.HasKey("Id"); + + b.HasIndex("MselId", "MoveNumber") + .IsUnique(); + + b.ToTable("moves"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MselEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CiteEvaluationId") + .HasColumnType("uuid") + .HasColumnName("cite_evaluation_id"); + + b.Property("CiteIntegrationType") + .HasColumnType("integer") + .HasColumnName("cite_integration_type"); + + b.Property("CiteScoringModelId") + .HasColumnType("uuid") + .HasColumnName("cite_scoring_model_id"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("DurationSeconds") + .HasColumnType("integer") + .HasColumnName("duration_seconds"); + + b.Property("GalleryCollectionId") + .HasColumnType("uuid") + .HasColumnName("gallery_collection_id"); + + b.Property("GalleryExhibitId") + .HasColumnType("uuid") + .HasColumnName("gallery_exhibit_id"); + + b.Property("GalleryIntegrationType") + .HasColumnType("integer") + .HasColumnName("gallery_integration_type"); + + b.Property("HeaderRowMetadata") + .HasColumnType("text") + .HasColumnName("header_row_metadata"); + + b.Property("IsTemplate") + .HasColumnType("boolean") + .HasColumnName("is_template"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("PlayerIntegrationType") + .HasColumnType("integer") + .HasColumnName("player_integration_type"); + + b.Property("PlayerViewId") + .HasColumnType("uuid") + .HasColumnName("player_view_id"); + + b.Property("ShowGroupOnExerciseView") + .HasColumnType("boolean") + .HasColumnName("show_group_on_exercise_view"); + + b.Property("ShowGroupOnScenarioEventList") + .HasColumnType("boolean") + .HasColumnName("show_group_on_scenario_event_list"); + + b.Property("ShowMoveOnExerciseView") + .HasColumnType("boolean") + .HasColumnName("show_move_on_exercise_view"); + + b.Property("ShowMoveOnScenarioEventList") + .HasColumnType("boolean") + .HasColumnName("show_move_on_scenario_event_list"); + + b.Property("ShowTimeOnExerciseView") + .HasColumnType("boolean") + .HasColumnName("show_time_on_exercise_view"); + + b.Property("ShowTimeOnScenarioEventList") + .HasColumnType("boolean") + .HasColumnName("show_time_on_scenario_event_list"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("SteamfitterIntegrationType") + .HasColumnType("integer") + .HasColumnName("steamfitter_integration_type"); + + b.Property("SteamfitterScenarioId") + .HasColumnType("uuid") + .HasColumnName("steamfitter_scenario_id"); + + b.Property("UseCite") + .HasColumnType("boolean") + .HasColumnName("use_cite"); + + b.Property("UseGallery") + .HasColumnType("boolean") + .HasColumnName("use_gallery"); + + b.Property("UsePlayer") + .HasColumnType("boolean") + .HasColumnName("use_player"); + + b.Property("UseSteamfitter") + .HasColumnType("boolean") + .HasColumnName("use_steamfitter"); + + b.HasKey("Id"); + + b.ToTable("msels"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MselPageEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("AllCanView") + .HasColumnType("boolean") + .HasColumnName("all_can_view"); + + b.Property("Content") + .HasColumnType("text") + .HasColumnName("content"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MselId"); + + b.ToTable("msel_pages"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MselTeamEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CiteTeamTypeId") + .HasColumnType("uuid") + .HasColumnName("cite_team_type_id"); + + b.Property("Email") + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.HasKey("Id"); + + b.HasIndex("MselId"); + + b.HasIndex("TeamId", "MselId") + .IsUnique(); + + b.ToTable("msel_teams"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MselUnitEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("UnitId") + .HasColumnType("uuid") + .HasColumnName("unit_id"); + + b.HasKey("Id"); + + b.HasIndex("MselId"); + + b.HasIndex("UnitId", "MselId") + .IsUnique(); + + b.ToTable("msel_units"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.OrganizationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Email") + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("IsTemplate") + .HasColumnType("boolean") + .HasColumnName("is_template"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("ShortName") + .HasColumnType("text") + .HasColumnName("short_name"); + + b.Property("Summary") + .HasColumnType("text") + .HasColumnName("summary"); + + b.HasKey("Id"); + + b.HasIndex("MselId"); + + b.ToTable("organizations"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.PermissionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Key") + .HasColumnType("text") + .HasColumnName("key"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("ReadOnly") + .HasColumnType("boolean") + .HasColumnName("read_only"); + + b.Property("Value") + .HasColumnType("text") + .HasColumnName("value"); + + b.HasKey("Id"); + + b.HasIndex("Key", "Value") + .IsUnique(); + + b.ToTable("permissions"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.PlayerApplicationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Embeddable") + .HasColumnType("boolean") + .HasColumnName("embeddable"); + + b.Property("Icon") + .HasColumnType("text") + .HasColumnName("icon"); + + b.Property("LoadInBackground") + .HasColumnType("boolean") + .HasColumnName("load_in_background"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Url") + .HasColumnType("text") + .HasColumnName("url"); + + b.HasKey("Id"); + + b.HasIndex("MselId"); + + b.ToTable("player_applications"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.PlayerApplicationTeamEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("PlayerApplicationId") + .HasColumnType("uuid") + .HasColumnName("player_application_id"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.HasKey("Id"); + + b.HasIndex("PlayerApplicationId"); + + b.HasIndex("TeamId", "PlayerApplicationId") + .IsUnique(); + + b.ToTable("player_application_teams"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.ScenarioEventEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("DeltaSeconds") + .HasColumnType("integer") + .HasColumnName("delta_seconds"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("GroupOrder") + .HasColumnType("integer") + .HasColumnName("group_order"); + + b.Property("InjectId") + .HasColumnType("uuid") + .HasColumnName("inject_id"); + + b.Property("IsHidden") + .HasColumnType("boolean") + .HasColumnName("is_hidden"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("RowMetadata") + .HasColumnType("text") + .HasColumnName("row_metadata"); + + b.Property("ScenarioEventType") + .HasColumnType("integer") + .HasColumnName("scenario_event_type"); + + b.HasKey("Id"); + + b.HasIndex("InjectId"); + + b.HasIndex("MselId"); + + b.ToTable("scenario_events"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.TeamEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CiteTeamId") + .HasColumnType("uuid") + .HasColumnName("cite_team_id"); + + b.Property("CiteTeamTypeId") + .HasColumnType("uuid") + .HasColumnName("cite_team_type_id"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Email") + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("GalleryTeamId") + .HasColumnType("uuid") + .HasColumnName("gallery_team_id"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("PlayerTeamId") + .HasColumnType("uuid") + .HasColumnName("player_team_id"); + + b.Property("ShortName") + .HasColumnType("text") + .HasColumnName("short_name"); + + b.Property("canTeamLeaderInvite") + .HasColumnType("boolean") + .HasColumnName("can_team_leader_invite"); + + b.Property("canTeamMemberInvite") + .HasColumnType("boolean") + .HasColumnName("can_team_member_invite"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("MselId"); + + b.ToTable("teams"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.TeamUserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId", "TeamId") + .IsUnique(); + + b.ToTable("team_users"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UnitEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("ShortName") + .HasColumnType("text") + .HasColumnName("short_name"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("units"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UnitUserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("UnitId") + .HasColumnType("uuid") + .HasColumnName("unit_id"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("UnitId"); + + b.HasIndex("UserId", "UnitId") + .IsUnique(); + + b.ToTable("unit_users"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UserEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .IsUnique(); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UserMselRoleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("MselId") + .HasColumnType("uuid") + .HasColumnName("msel_id"); + + b.Property("Role") + .HasColumnType("integer") + .HasColumnName("role"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("MselId", "UserId", "Role") + .IsUnique(); + + b.ToTable("user_msel_roles"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UserPermissionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("PermissionId") + .HasColumnType("uuid") + .HasColumnName("permission_id"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("PermissionId"); + + b.HasIndex("UserId", "PermissionId") + .IsUnique(); + + b.ToTable("user_permissions"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UserTeamRoleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Role") + .HasColumnType("integer") + .HasColumnName("role"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("TeamId", "UserId", "Role") + .IsUnique(); + + b.ToTable("user_team_roles"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CardEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("Cards") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Msel"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CardTeamEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.CardEntity", "Card") + .WithMany("CardTeams") + .HasForeignKey("CardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.TeamEntity", "Team") + .WithMany("CardTeams") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Card"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.InjectTypeEntity", "InjectType") + .WithMany() + .HasForeignKey("InjectTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.CatalogEntity", "Parent") + .WithMany() + .HasForeignKey("ParentId"); + + b.Navigation("InjectType"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogInjectEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.CatalogEntity", "Catalog") + .WithMany("CatalogInjects") + .HasForeignKey("CatalogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.InjectEntity", "Inject") + .WithMany("CatalogInjects") + .HasForeignKey("InjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Catalog"); + + b.Navigation("Inject"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogUnitEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.CatalogEntity", "Catalog") + .WithMany("CatalogUnits") + .HasForeignKey("CatalogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.UnitEntity", "Unit") + .WithMany("CatalogUnits") + .HasForeignKey("UnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Catalog"); + + b.Navigation("Unit"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CiteActionEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("CiteActions") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Blueprint.Api.Data.Models.TeamEntity", "Team") + .WithMany("CiteActions") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Msel"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CiteRoleEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("CiteRoles") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Blueprint.Api.Data.Models.TeamEntity", "Team") + .WithMany("CiteRoles") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Msel"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.DataFieldEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.InjectTypeEntity", "InjectType") + .WithMany("DataFields") + .HasForeignKey("InjectTypeId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("DataFields") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("InjectType"); + + b.Navigation("Msel"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.DataOptionEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.DataFieldEntity", "DataField") + .WithMany("DataOptions") + .HasForeignKey("DataFieldId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DataField"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.DataValueEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.DataFieldEntity", "DataField") + .WithMany() + .HasForeignKey("DataFieldId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.InjectEntity", "Inject") + .WithMany("DataValues") + .HasForeignKey("InjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Blueprint.Api.Data.Models.ScenarioEventEntity", "ScenarioEvent") + .WithMany("DataValues") + .HasForeignKey("ScenarioEventId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("DataField"); + + b.Navigation("Inject"); + + b.Navigation("ScenarioEvent"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.CatalogEntity", null) + .WithMany("Injects") + .HasForeignKey("CatalogEntityId"); + + b.HasOne("Blueprint.Api.Data.Models.InjectTypeEntity", "InjectType") + .WithMany() + .HasForeignKey("InjectTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.InjectEntity", "RequiresInject") + .WithMany() + .HasForeignKey("RequiresInjectId"); + + b.Navigation("InjectType"); + + b.Navigation("RequiresInject"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InvitationEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("Invitations") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.TeamEntity", "Team") + .WithMany("Invitations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Msel"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MoveEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("Moves") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Msel"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MselPageEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("Pages") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Msel"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MselTeamEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany() + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.TeamEntity", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Msel"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MselUnitEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("MselUnits") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.UnitEntity", "Unit") + .WithMany("MselUnits") + .HasForeignKey("UnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Msel"); + + b.Navigation("Unit"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.OrganizationEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("Organizations") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Msel"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.PlayerApplicationEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("PlayerApplications") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Msel"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.PlayerApplicationTeamEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.PlayerApplicationEntity", "PlayerApplication") + .WithMany("PlayerApplicationTeams") + .HasForeignKey("PlayerApplicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.TeamEntity", "Team") + .WithMany("PlayerApplicationTeams") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerApplication"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.ScenarioEventEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.InjectEntity", "Inject") + .WithMany() + .HasForeignKey("InjectId"); + + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("ScenarioEvents") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Inject"); + + b.Navigation("Msel"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.TeamEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("Teams") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Msel"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.TeamUserEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.TeamEntity", "Team") + .WithMany("TeamUsers") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.UserEntity", "User") + .WithMany("TeamUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UnitUserEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.UnitEntity", "Unit") + .WithMany("UnitUsers") + .HasForeignKey("UnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.UserEntity", "User") + .WithMany("UnitUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Unit"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UserMselRoleEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") + .WithMany("UserMselRoles") + .HasForeignKey("MselId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Msel"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UserPermissionEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.PermissionEntity", "Permission") + .WithMany("UserPermissions") + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.UserEntity", "User") + .WithMany("UserPermissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UserTeamRoleEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.TeamEntity", "Team") + .WithMany("UserTeamRoles") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.UserEntity", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CardEntity", b => + { + b.Navigation("CardTeams"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogEntity", b => + { + b.Navigation("CatalogInjects"); + + b.Navigation("CatalogUnits"); + + b.Navigation("Injects"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.DataFieldEntity", b => + { + b.Navigation("DataOptions"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectEntity", b => + { + b.Navigation("CatalogInjects"); + + b.Navigation("DataValues"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectTypeEntity", b => + { + b.Navigation("DataFields"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.MselEntity", b => + { + b.Navigation("Cards"); + + b.Navigation("CiteActions"); + + b.Navigation("CiteRoles"); + + b.Navigation("DataFields"); + + b.Navigation("Invitations"); + + b.Navigation("Moves"); + + b.Navigation("MselUnits"); + + b.Navigation("Organizations"); + + b.Navigation("Pages"); + + b.Navigation("PlayerApplications"); + + b.Navigation("ScenarioEvents"); + + b.Navigation("Teams"); + + b.Navigation("UserMselRoles"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.PermissionEntity", b => + { + b.Navigation("UserPermissions"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.PlayerApplicationEntity", b => + { + b.Navigation("PlayerApplicationTeams"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.ScenarioEventEntity", b => + { + b.Navigation("DataValues"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.TeamEntity", b => + { + b.Navigation("CardTeams"); + + b.Navigation("CiteActions"); + + b.Navigation("CiteRoles"); + + b.Navigation("Invitations"); + + b.Navigation("PlayerApplicationTeams"); + + b.Navigation("TeamUsers"); + + b.Navigation("UserTeamRoles"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UnitEntity", b => + { + b.Navigation("CatalogUnits"); + + b.Navigation("MselUnits"); + + b.Navigation("UnitUsers"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.UserEntity", b => + { + b.Navigation("TeamUsers"); + + b.Navigation("UnitUsers"); + + b.Navigation("UserPermissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.cs b/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.cs new file mode 100644 index 0000000..8ee9fc9 --- /dev/null +++ b/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.cs @@ -0,0 +1,401 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Blueprint.Api.Migrations.PostgreSQL.Migrations +{ + public partial class catalogandinject : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_data_values_scenario_event_id_data_field_id", + table: "data_values"); + + migrationBuilder.DropColumn( + name: "old_team_id", + table: "units"); + + migrationBuilder.AddColumn( + name: "description", + table: "scenario_events", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "inject_id", + table: "scenario_events", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "scenario_event_type", + table: "scenario_events", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AlterColumn( + name: "scenario_event_id", + table: "data_values", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AddColumn( + name: "inject_id", + table: "data_values", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "inject_type_id", + table: "data_fields", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "inject_types", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "uuid_generate_v4()"), + name = table.Column(type: "text", nullable: true), + description = table.Column(type: "text", nullable: true), + date_created = table.Column(type: "timestamp with time zone", nullable: false), + date_modified = table.Column(type: "timestamp with time zone", nullable: true), + created_by = table.Column(type: "uuid", nullable: false), + modified_by = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_inject_types", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "catalogs", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "uuid_generate_v4()"), + name = table.Column(type: "text", nullable: true), + description = table.Column(type: "text", nullable: true), + inject_type_id = table.Column(type: "uuid", nullable: false), + is_public = table.Column(type: "boolean", nullable: false), + parent_id = table.Column(type: "uuid", nullable: true), + date_created = table.Column(type: "timestamp with time zone", nullable: false), + date_modified = table.Column(type: "timestamp with time zone", nullable: true), + created_by = table.Column(type: "uuid", nullable: false), + modified_by = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_catalogs", x => x.id); + table.ForeignKey( + name: "FK_catalogs_catalogs_parent_id", + column: x => x.parent_id, + principalTable: "catalogs", + principalColumn: "id"); + table.ForeignKey( + name: "FK_catalogs_inject_types_inject_type_id", + column: x => x.inject_type_id, + principalTable: "inject_types", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "catalog_units", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "uuid_generate_v4()"), + unit_id = table.Column(type: "uuid", nullable: false), + catalog_id = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_catalog_units", x => x.id); + table.ForeignKey( + name: "FK_catalog_units_catalogs_catalog_id", + column: x => x.catalog_id, + principalTable: "catalogs", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_catalog_units_units_unit_id", + column: x => x.unit_id, + principalTable: "units", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "injects", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "uuid_generate_v4()"), + inject_type_id = table.Column(type: "uuid", nullable: false), + requires_inject_id = table.Column(type: "uuid", nullable: true), + catalog_entity_id = table.Column(type: "uuid", nullable: true), + date_created = table.Column(type: "timestamp with time zone", nullable: false), + date_modified = table.Column(type: "timestamp with time zone", nullable: true), + created_by = table.Column(type: "uuid", nullable: false), + modified_by = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_injects", x => x.id); + table.ForeignKey( + name: "FK_injects_catalogs_catalog_entity_id", + column: x => x.catalog_entity_id, + principalTable: "catalogs", + principalColumn: "id"); + table.ForeignKey( + name: "FK_injects_inject_types_inject_type_id", + column: x => x.inject_type_id, + principalTable: "inject_types", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_injects_injects_requires_inject_id", + column: x => x.requires_inject_id, + principalTable: "injects", + principalColumn: "id"); + }); + + migrationBuilder.CreateTable( + name: "catalog_injects", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false, defaultValueSql: "uuid_generate_v4()"), + inject_id = table.Column(type: "uuid", nullable: false), + catalog_id = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_catalog_injects", x => x.id); + table.ForeignKey( + name: "FK_catalog_injects_catalogs_catalog_id", + column: x => x.catalog_id, + principalTable: "catalogs", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_catalog_injects_injects_inject_id", + column: x => x.inject_id, + principalTable: "injects", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_scenario_events_inject_id", + table: "scenario_events", + column: "inject_id"); + + migrationBuilder.CreateIndex( + name: "IX_data_values_inject_id", + table: "data_values", + column: "inject_id"); + + migrationBuilder.CreateIndex( + name: "IX_data_values_scenario_event_id_inject_id_data_field_id", + table: "data_values", + columns: new[] { "scenario_event_id", "inject_id", "data_field_id" }, + unique: true); + + migrationBuilder.AddCheckConstraint( + name: "CK_data_values_data_value_scenario_event_or_inject", + table: "data_values", + sql: "(scenario_event_id IS NOT NULL AND inject_id IS NULL) OR (scenario_event_id IS NULL AND inject_id IS NOT NULL)"); + + migrationBuilder.CreateIndex( + name: "IX_data_fields_inject_type_id", + table: "data_fields", + column: "inject_type_id"); + + migrationBuilder.AddCheckConstraint( + name: "CK_data_fields_data_field_msel_or_inject_type", + table: "data_fields", + sql: "(msel_id IS NOT NULL AND inject_type_id IS NULL) OR (msel_id IS NULL AND inject_type_id IS NOT NULL)"); + + migrationBuilder.CreateIndex( + name: "IX_catalog_injects_catalog_id", + table: "catalog_injects", + column: "catalog_id"); + + migrationBuilder.CreateIndex( + name: "IX_catalog_injects_inject_id_catalog_id", + table: "catalog_injects", + columns: new[] { "inject_id", "catalog_id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_catalog_units_catalog_id", + table: "catalog_units", + column: "catalog_id"); + + migrationBuilder.CreateIndex( + name: "IX_catalog_units_unit_id_catalog_id", + table: "catalog_units", + columns: new[] { "unit_id", "catalog_id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_catalogs_inject_type_id", + table: "catalogs", + column: "inject_type_id"); + + migrationBuilder.CreateIndex( + name: "IX_catalogs_name", + table: "catalogs", + column: "name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_catalogs_parent_id", + table: "catalogs", + column: "parent_id"); + + migrationBuilder.CreateIndex( + name: "IX_inject_types_name", + table: "inject_types", + column: "name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_injects_catalog_entity_id", + table: "injects", + column: "catalog_entity_id"); + + migrationBuilder.CreateIndex( + name: "IX_injects_inject_type_id", + table: "injects", + column: "inject_type_id"); + + migrationBuilder.CreateIndex( + name: "IX_injects_requires_inject_id", + table: "injects", + column: "requires_inject_id"); + + migrationBuilder.AddForeignKey( + name: "FK_data_fields_inject_types_inject_type_id", + table: "data_fields", + column: "inject_type_id", + principalTable: "inject_types", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_data_values_injects_inject_id", + table: "data_values", + column: "inject_id", + principalTable: "injects", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_scenario_events_injects_inject_id", + table: "scenario_events", + column: "inject_id", + principalTable: "injects", + principalColumn: "id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_data_fields_inject_types_inject_type_id", + table: "data_fields"); + + migrationBuilder.DropForeignKey( + name: "FK_data_values_injects_inject_id", + table: "data_values"); + + migrationBuilder.DropForeignKey( + name: "FK_scenario_events_injects_inject_id", + table: "scenario_events"); + + migrationBuilder.DropTable( + name: "catalog_injects"); + + migrationBuilder.DropTable( + name: "catalog_units"); + + migrationBuilder.DropTable( + name: "injects"); + + migrationBuilder.DropTable( + name: "catalogs"); + + migrationBuilder.DropTable( + name: "inject_types"); + + migrationBuilder.DropIndex( + name: "IX_scenario_events_inject_id", + table: "scenario_events"); + + migrationBuilder.DropIndex( + name: "IX_data_values_inject_id", + table: "data_values"); + + migrationBuilder.DropIndex( + name: "IX_data_values_scenario_event_id_inject_id_data_field_id", + table: "data_values"); + + migrationBuilder.DropCheckConstraint( + name: "CK_data_values_data_value_scenario_event_or_inject", + table: "data_values"); + + migrationBuilder.DropIndex( + name: "IX_data_fields_inject_type_id", + table: "data_fields"); + + migrationBuilder.DropCheckConstraint( + name: "CK_data_fields_data_field_msel_or_inject_type", + table: "data_fields"); + + migrationBuilder.DropColumn( + name: "description", + table: "scenario_events"); + + migrationBuilder.DropColumn( + name: "inject_id", + table: "scenario_events"); + + migrationBuilder.DropColumn( + name: "scenario_event_type", + table: "scenario_events"); + + migrationBuilder.DropColumn( + name: "inject_id", + table: "data_values"); + + migrationBuilder.DropColumn( + name: "inject_type_id", + table: "data_fields"); + + migrationBuilder.AddColumn( + name: "old_team_id", + table: "units", + type: "uuid", + nullable: true); + + migrationBuilder.AlterColumn( + name: "scenario_event_id", + table: "data_values", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_data_values_scenario_event_id_data_field_id", + table: "data_values", + columns: new[] { "scenario_event_id", "data_field_id" }, + unique: true); + } + } +} diff --git a/Blueprint.Api.Migrations.PostgreSQL/Migrations/BlueprintContextModelSnapshot.cs b/Blueprint.Api.Migrations.PostgreSQL/Migrations/BlueprintContextModelSnapshot.cs index f86636a..8e8bd16 100644 --- a/Blueprint.Api.Migrations.PostgreSQL/Migrations/BlueprintContextModelSnapshot.cs +++ b/Blueprint.Api.Migrations.PostgreSQL/Migrations/BlueprintContextModelSnapshot.cs @@ -1,8 +1,3 @@ -/* - Copyright 2024 Carnegie Mellon University. All Rights Reserved. - Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. -*/ - // using System; using Blueprint.Api.Data; @@ -121,6 +116,114 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("card_teams"); }); + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("InjectTypeId") + .HasColumnType("uuid") + .HasColumnName("inject_type_id"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("is_public"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("ParentId") + .HasColumnType("uuid") + .HasColumnName("parent_id"); + + b.HasKey("Id"); + + b.HasIndex("InjectTypeId"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("ParentId"); + + b.ToTable("catalogs"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogInjectEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CatalogId") + .HasColumnType("uuid") + .HasColumnName("catalog_id"); + + b.Property("InjectId") + .HasColumnType("uuid") + .HasColumnName("inject_id"); + + b.HasKey("Id"); + + b.HasIndex("CatalogId"); + + b.HasIndex("InjectId", "CatalogId") + .IsUnique(); + + b.ToTable("catalog_injects"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogUnitEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CatalogId") + .HasColumnType("uuid") + .HasColumnName("catalog_id"); + + b.Property("UnitId") + .HasColumnType("uuid") + .HasColumnName("unit_id"); + + b.HasKey("Id"); + + b.HasIndex("CatalogId"); + + b.HasIndex("UnitId", "CatalogId") + .IsUnique(); + + b.ToTable("catalog_units"); + }); + modelBuilder.Entity("Blueprint.Api.Data.Models.CiteActionEntity", b => { b.Property("Id") @@ -271,6 +374,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text") .HasColumnName("gallery_article_parameter"); + b.Property("InjectTypeId") + .HasColumnType("uuid") + .HasColumnName("inject_type_id"); + b.Property("IsChosenFromList") .HasColumnType("boolean") .HasColumnName("is_chosen_from_list"); @@ -309,9 +416,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("InjectTypeId"); + b.HasIndex("MselId"); b.ToTable("data_fields"); + + b.HasCheckConstraint("data_field_msel_or_inject_type", "(msel_id IS NOT NULL AND inject_type_id IS NULL) OR (msel_id IS NULL AND inject_type_id IS NOT NULL)"); }); modelBuilder.Entity("Blueprint.Api.Data.Models.DataOptionEntity", b => @@ -389,11 +500,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasColumnName("date_modified"); + b.Property("InjectId") + .HasColumnType("uuid") + .HasColumnName("inject_id"); + b.Property("ModifiedBy") .HasColumnType("uuid") .HasColumnName("modified_by"); - b.Property("ScenarioEventId") + b.Property("ScenarioEventId") .HasColumnType("uuid") .HasColumnName("scenario_event_id"); @@ -408,10 +523,101 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Id") .IsUnique(); - b.HasIndex("ScenarioEventId", "DataFieldId") + b.HasIndex("InjectId"); + + b.HasIndex("ScenarioEventId", "InjectId", "DataFieldId") .IsUnique(); b.ToTable("data_values"); + + b.HasCheckConstraint("data_value_scenario_event_or_inject", "(scenario_event_id IS NOT NULL AND inject_id IS NULL) OR (scenario_event_id IS NULL AND inject_id IS NOT NULL)"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CatalogEntityId") + .HasColumnType("uuid") + .HasColumnName("catalog_entity_id"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("InjectTypeId") + .HasColumnType("uuid") + .HasColumnName("inject_type_id"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("RequiresInjectId") + .HasColumnType("uuid") + .HasColumnName("requires_inject_id"); + + b.HasKey("Id"); + + b.HasIndex("CatalogEntityId"); + + b.HasIndex("InjectTypeId"); + + b.HasIndex("RequiresInjectId"); + + b.ToTable("injects"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectTypeEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CreatedBy") + .HasColumnType("uuid") + .HasColumnName("created_by"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_modified"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("ModifiedBy") + .HasColumnType("uuid") + .HasColumnName("modified_by"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("inject_types"); }); modelBuilder.Entity("Blueprint.Api.Data.Models.InvitationEntity", b => @@ -969,10 +1175,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("integer") .HasColumnName("delta_seconds"); + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + b.Property("GroupOrder") .HasColumnType("integer") .HasColumnName("group_order"); + b.Property("InjectId") + .HasColumnType("uuid") + .HasColumnName("inject_id"); + b.Property("IsHidden") .HasColumnType("boolean") .HasColumnName("is_hidden"); @@ -989,8 +1203,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text") .HasColumnName("row_metadata"); + b.Property("ScenarioEventType") + .HasColumnType("integer") + .HasColumnName("scenario_event_type"); + b.HasKey("Id"); + b.HasIndex("InjectId"); + b.HasIndex("MselId"); b.ToTable("scenario_events"); @@ -1124,10 +1344,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text") .HasColumnName("name"); - b.Property("OldTeamId") - .HasColumnType("uuid") - .HasColumnName("old_team_id"); - b.Property("ShortName") .HasColumnType("text") .HasColumnName("short_name"); @@ -1349,6 +1565,61 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Team"); }); + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.InjectTypeEntity", "InjectType") + .WithMany() + .HasForeignKey("InjectTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.CatalogEntity", "Parent") + .WithMany() + .HasForeignKey("ParentId"); + + b.Navigation("InjectType"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogInjectEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.CatalogEntity", "Catalog") + .WithMany("CatalogInjects") + .HasForeignKey("CatalogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.InjectEntity", "Inject") + .WithMany("CatalogInjects") + .HasForeignKey("InjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Catalog"); + + b.Navigation("Inject"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogUnitEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.CatalogEntity", "Catalog") + .WithMany("CatalogUnits") + .HasForeignKey("CatalogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.UnitEntity", "Unit") + .WithMany("CatalogUnits") + .HasForeignKey("UnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Catalog"); + + b.Navigation("Unit"); + }); + modelBuilder.Entity("Blueprint.Api.Data.Models.CiteActionEntity", b => { b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") @@ -1385,11 +1656,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Blueprint.Api.Data.Models.DataFieldEntity", b => { + b.HasOne("Blueprint.Api.Data.Models.InjectTypeEntity", "InjectType") + .WithMany("DataFields") + .HasForeignKey("InjectTypeId") + .OnDelete(DeleteBehavior.Cascade); + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") .WithMany("DataFields") .HasForeignKey("MselId") .OnDelete(DeleteBehavior.Cascade); + b.Navigation("InjectType"); + b.Navigation("Msel"); }); @@ -1412,17 +1690,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("Blueprint.Api.Data.Models.InjectEntity", "Inject") + .WithMany("DataValues") + .HasForeignKey("InjectId") + .OnDelete(DeleteBehavior.Cascade); + b.HasOne("Blueprint.Api.Data.Models.ScenarioEventEntity", "ScenarioEvent") .WithMany("DataValues") .HasForeignKey("ScenarioEventId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .OnDelete(DeleteBehavior.Cascade); b.Navigation("DataField"); + b.Navigation("Inject"); + b.Navigation("ScenarioEvent"); }); + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectEntity", b => + { + b.HasOne("Blueprint.Api.Data.Models.CatalogEntity", null) + .WithMany("Injects") + .HasForeignKey("CatalogEntityId"); + + b.HasOne("Blueprint.Api.Data.Models.InjectTypeEntity", "InjectType") + .WithMany() + .HasForeignKey("InjectTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blueprint.Api.Data.Models.InjectEntity", "RequiresInject") + .WithMany() + .HasForeignKey("RequiresInjectId"); + + b.Navigation("InjectType"); + + b.Navigation("RequiresInject"); + }); + modelBuilder.Entity("Blueprint.Api.Data.Models.InvitationEntity", b => { b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") @@ -1544,12 +1849,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Blueprint.Api.Data.Models.ScenarioEventEntity", b => { + b.HasOne("Blueprint.Api.Data.Models.InjectEntity", "Inject") + .WithMany() + .HasForeignKey("InjectId"); + b.HasOne("Blueprint.Api.Data.Models.MselEntity", "Msel") .WithMany("ScenarioEvents") .HasForeignKey("MselId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.Navigation("Inject"); + b.Navigation("Msel"); }); @@ -1664,11 +1975,32 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("CardTeams"); }); + modelBuilder.Entity("Blueprint.Api.Data.Models.CatalogEntity", b => + { + b.Navigation("CatalogInjects"); + + b.Navigation("CatalogUnits"); + + b.Navigation("Injects"); + }); + modelBuilder.Entity("Blueprint.Api.Data.Models.DataFieldEntity", b => { b.Navigation("DataOptions"); }); + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectEntity", b => + { + b.Navigation("CatalogInjects"); + + b.Navigation("DataValues"); + }); + + modelBuilder.Entity("Blueprint.Api.Data.Models.InjectTypeEntity", b => + { + b.Navigation("DataFields"); + }); + modelBuilder.Entity("Blueprint.Api.Data.Models.MselEntity", b => { b.Navigation("Cards"); @@ -1732,6 +2064,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Blueprint.Api.Data.Models.UnitEntity", b => { + b.Navigation("CatalogUnits"); + b.Navigation("MselUnits"); b.Navigation("UnitUsers"); From 208e24cea99c8723efa7ab6ee5b59569e4ab09bb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:51:00 +0000 Subject: [PATCH 8/8] Add missing document markings --- .../Migrations/20240613154406_catalog-and-inject.Designer.cs | 5 +++++ .../Migrations/20240613154406_catalog-and-inject.cs | 5 +++++ .../Migrations/BlueprintContextModelSnapshot.cs | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.Designer.cs b/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.Designer.cs index 3054e2d..bddd90a 100644 --- a/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.Designer.cs +++ b/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.Designer.cs @@ -1,3 +1,8 @@ +/* + Copyright 2024 Carnegie Mellon University. All Rights Reserved. + Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. +*/ + // using System; using Blueprint.Api.Data; diff --git a/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.cs b/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.cs index 8ee9fc9..c163981 100644 --- a/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.cs +++ b/Blueprint.Api.Migrations.PostgreSQL/Migrations/20240613154406_catalog-and-inject.cs @@ -1,3 +1,8 @@ +/* + Copyright 2024 Carnegie Mellon University. All Rights Reserved. + Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. +*/ + using System; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/Blueprint.Api.Migrations.PostgreSQL/Migrations/BlueprintContextModelSnapshot.cs b/Blueprint.Api.Migrations.PostgreSQL/Migrations/BlueprintContextModelSnapshot.cs index 8e8bd16..db07d6d 100644 --- a/Blueprint.Api.Migrations.PostgreSQL/Migrations/BlueprintContextModelSnapshot.cs +++ b/Blueprint.Api.Migrations.PostgreSQL/Migrations/BlueprintContextModelSnapshot.cs @@ -1,3 +1,8 @@ +/* + Copyright 2024 Carnegie Mellon University. All Rights Reserved. + Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. +*/ + // using System; using Blueprint.Api.Data;