diff --git a/Dockerfile b/Dockerfile index 4e93964..98b7fb7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # Adapted from https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.chiseled # Build stage -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS build ARG TARGETARCH WORKDIR /source @@ -19,7 +19,7 @@ WORKDIR /source/Player.Api RUN dotnet publish -a $TARGETARCH --no-restore -o /app # Debug Stage -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS debug +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS debug ENV DOTNET_HOSTBUILDER__RELOADCONFIGCHANGE=false EXPOSE 8080 WORKDIR /app @@ -28,11 +28,11 @@ USER $APP_UID ENTRYPOINT ["./Player.Api"] # Production stage -FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble-chiseled AS prod +FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS prod ARG commit ENV COMMIT=$commit ENV DOTNET_HOSTBUILDER__RELOADCONFIGCHANGE=false EXPOSE 8080 WORKDIR /app COPY --link --from=build /app . -ENTRYPOINT ["./Player.Api"] \ No newline at end of file +ENTRYPOINT ["./Player.Api"] diff --git a/Player.Api.Data/Player.Api.Data.csproj b/Player.Api.Data/Player.Api.Data.csproj index cc7fec6..a399c0e 100644 --- a/Player.Api.Data/Player.Api.Data.csproj +++ b/Player.Api.Data/Player.Api.Data.csproj @@ -1,8 +1,9 @@ - net8.0 + net10.0 + latest - + \ No newline at end of file diff --git a/Player.Api.Migrations.PostgreSQL/Migrations/20251113222811_Version10UpgradeSync.Designer.cs b/Player.Api.Migrations.PostgreSQL/Migrations/20251113222811_Version10UpgradeSync.Designer.cs new file mode 100644 index 0000000..f0bb8f0 --- /dev/null +++ b/Player.Api.Migrations.PostgreSQL/Migrations/20251113222811_Version10UpgradeSync.Designer.cs @@ -0,0 +1,1208 @@ +// Copyright 2025 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 Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Player.Api.Data.Data; + +#nullable disable + +namespace Player.Api.Migrations.PostgreSQL.Migrations +{ + [DbContext(typeof(PlayerContext))] + [Migration("20251113222811_Version10UpgradeSync")] + partial class Version10UpgradeSync + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "uuid-ossp"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ApplicationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("ApplicationTemplateId") + .HasColumnType("uuid") + .HasColumnName("application_template_id"); + + 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("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Url") + .HasColumnType("text") + .HasColumnName("url"); + + b.Property("ViewId") + .HasColumnType("uuid") + .HasColumnName("view_id"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationTemplateId"); + + b.HasIndex("ViewId"); + + b.ToTable("applications"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ApplicationInstanceEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("ApplicationId") + .HasColumnType("uuid") + .HasColumnName("application_id"); + + b.Property("DisplayOrder") + .HasColumnType("real") + .HasColumnName("display_order"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.HasIndex("TeamId"); + + b.ToTable("application_instances"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ApplicationTemplateEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + 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("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Url") + .HasColumnType("text") + .HasColumnName("url"); + + b.HasKey("Id"); + + b.ToTable("application_templates"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.FileEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Path") + .HasColumnType("text") + .HasColumnName("path"); + + b.PrimitiveCollection>("TeamIds") + .HasColumnType("uuid[]") + .HasColumnName("team_ids"); + + b.Property("ViewId") + .HasColumnType("uuid") + .HasColumnName("view_id"); + + b.HasKey("Id"); + + b.HasIndex("ViewId"); + + b.ToTable("files"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.NotificationEntity", b => + { + b.Property("Key") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("key"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Key")); + + b.Property("BroadcastTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("broadcast_time"); + + b.Property("FromId") + .HasColumnType("uuid") + .HasColumnName("from_id"); + + b.Property("FromName") + .HasColumnType("text") + .HasColumnName("from_name"); + + b.Property("FromType") + .HasColumnType("integer") + .HasColumnName("from_type"); + + b.Property("Link") + .HasColumnType("text") + .HasColumnName("link"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("Subject") + .HasColumnType("text") + .HasColumnName("subject"); + + b.Property("Text") + .HasColumnType("text") + .HasColumnName("text"); + + b.Property("ToId") + .HasColumnType("uuid") + .HasColumnName("to_id"); + + b.Property("ToName") + .HasColumnType("text") + .HasColumnName("to_name"); + + b.Property("ToType") + .HasColumnType("integer") + .HasColumnName("to_type"); + + b.Property("ViewId") + .HasColumnType("uuid") + .HasColumnName("view_id"); + + b.HasKey("Key"); + + b.ToTable("notifications"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.PermissionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Immutable") + .HasColumnType("boolean") + .HasColumnName("immutable"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("permissions"); + + b.HasData( + new + { + Id = new Guid("06e2699d-21a9-4053-922a-411499b3e923"), + Description = "", + Immutable = true, + Name = "CreateViews" + }, + new + { + Id = new Guid("06da87d8-7636-4a50-929a-bbff2fbad548"), + Description = "", + Immutable = true, + Name = "ViewViews" + }, + new + { + Id = new Guid("6c407f81-ab2e-4b24-911b-78b7f424b965"), + Description = "", + Immutable = true, + Name = "EditViews" + }, + new + { + Id = new Guid("5597959a-ff30-4b73-9122-f21d17c19382"), + Description = "", + Immutable = true, + Name = "ManageViews" + }, + new + { + Id = new Guid("70f3e368-7e7a-4166-9698-5c96dbb19ceb"), + Description = "", + Immutable = true, + Name = "ViewUsers" + }, + new + { + Id = new Guid("5cc1e12e-7440-4bd8-9a54-ccb5bb0f3f1e"), + Description = "", + Immutable = true, + Name = "ManageUsers" + }, + new + { + Id = new Guid("0ef125ff-c493-476d-a041-0b6af54f4d36"), + Description = "", + Immutable = true, + Name = "ViewApplications" + }, + new + { + Id = new Guid("8dc72622-565d-4b86-b6d7-1692dc803815"), + Description = "", + Immutable = true, + Name = "ManageApplications" + }, + new + { + Id = new Guid("cfcc8ac3-6591-41b8-abe1-0456616b3798"), + Description = "", + Immutable = true, + Name = "ViewRoles" + }, + new + { + Id = new Guid("f1416f76-aa64-4edc-bfa8-6f234da85060"), + Description = "", + Immutable = true, + Name = "ManageRoles" + }, + new + { + Id = new Guid("e15b0177-5250-4886-b062-4029a9371a99"), + Description = "", + Immutable = true, + Name = "ViewWebhookSubscriptions" + }, + new + { + Id = new Guid("e1772ce2-eacb-478f-bac8-2e77d49c608a"), + Description = "", + Immutable = true, + Name = "ManageWebhookSubscriptions" + }); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.RoleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("AllPermissions") + .HasColumnType("boolean") + .HasColumnName("all_permissions"); + + b.Property("Immutable") + .HasColumnType("boolean") + .HasColumnName("immutable"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("roles"); + + b.HasData( + new + { + Id = new Guid("f6c07d62-4f2c-4bd5-82af-bf32c0daccc7"), + AllPermissions = true, + Immutable = true, + Name = "Administrator" + }, + new + { + Id = new Guid("7fd6aa3e-a765-47b8-a77e-f58eae53a82f"), + AllPermissions = false, + Immutable = false, + Name = "Content Developer" + }); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.RolePermissionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("PermissionId") + .HasColumnType("uuid") + .HasColumnName("permission_id"); + + b.Property("RoleId") + .HasColumnType("uuid") + .HasColumnName("role_id"); + + b.HasKey("Id"); + + b.HasIndex("PermissionId"); + + b.HasIndex("RoleId", "PermissionId") + .IsUnique(); + + b.ToTable("role_permissions"); + + b.HasData( + new + { + Id = new Guid("b5ef76d0-6257-4657-a51a-79d1e3850720"), + PermissionId = new Guid("06e2699d-21a9-4053-922a-411499b3e923"), + RoleId = new Guid("7fd6aa3e-a765-47b8-a77e-f58eae53a82f") + }); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("RoleId") + .HasColumnType("uuid") + .HasColumnName("role_id"); + + b.Property("ViewId") + .HasColumnType("uuid") + .HasColumnName("view_id"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ViewId"); + + b.ToTable("teams"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamMembershipEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("RoleId") + .HasColumnType("uuid") + .HasColumnName("role_id"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("ViewMembershipId") + .HasColumnType("uuid") + .HasColumnName("view_membership_id"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.HasIndex("ViewMembershipId"); + + b.HasIndex("TeamId", "UserId") + .IsUnique(); + + b.ToTable("team_memberships"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamPermissionAssignmentEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("PermissionId") + .HasColumnType("uuid") + .HasColumnName("permission_id"); + + b.Property("TeamId") + .HasColumnType("uuid") + .HasColumnName("team_id"); + + b.HasKey("Id"); + + b.HasIndex("PermissionId"); + + b.HasIndex("TeamId", "PermissionId") + .IsUnique(); + + b.ToTable("team_permission_assignments"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamPermissionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Immutable") + .HasColumnType("boolean") + .HasColumnName("immutable"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("team_permissions"); + + b.HasData( + new + { + Id = new Guid("f3ef9465-7f7c-43ef-9855-83798ce5bcd5"), + Description = "Allows viewing Team resources", + Immutable = true, + Name = "ViewTeam" + }, + new + { + Id = new Guid("fbabccc8-48c7-478a-bc30-d4bd8950e3d5"), + Description = "Allows editing basic Team resources, including making changes within Virtual Machines, if applicable.", + Immutable = true, + Name = "EditTeam" + }, + new + { + Id = new Guid("83e41563-8b7f-4f43-b9d0-2d8dc12fc0bf"), + Description = "Allows managing all Team resources, including adding and removing Users.", + Immutable = true, + Name = "ManageTeam" + }, + new + { + Id = new Guid("dedb382b-9d9f-43dc-b128-bb5f1ad94a15"), + Description = "Allows managing all resources for all Teams in the View", + Immutable = true, + Name = "ManageView" + }, + new + { + Id = new Guid("7be07cd5-104e-4770-800b-80ac26cda6d5"), + Description = "Allows viewing all resources in the View", + Immutable = true, + Name = "ViewView" + }, + new + { + Id = new Guid("5ae96619-b40b-4fdb-bbef-ad476c21553d"), + Description = "Allows editing all basic resources in the View, including making changes within Virtual Machines, if applicable.", + Immutable = true, + Name = "EditView" + }, + new + { + Id = new Guid("5da3014c-a6a5-4c3c-a658-e86672801313"), + Description = "Allows uploading ISOs that can be used by any Teams in the View", + Immutable = false, + Name = "UploadViewIsos" + }, + new + { + Id = new Guid("d7271fd0-e47f-4630-a5ef-744acc4dc004"), + Description = "Allows uploading ISOs that can be used by members of the Team", + Immutable = false, + Name = "UploadTeamIsos" + }, + new + { + Id = new Guid("3b135496-c7d9-4bef-b60c-fbcfa1af9c1b"), + Description = "Allows downloading files directly from Vms", + Immutable = false, + Name = "DownloadVmFiles" + }, + new + { + Id = new Guid("6e41449b-a5da-4ac0-9adb-432210a5541c"), + Description = "Allows uploading files directly to Vms", + Immutable = false, + Name = "UploadVmFiles" + }, + new + { + Id = new Guid("42da22ae-ca0f-440f-87e0-5742799f60e1"), + Description = "Allows reverting a Vm to it's current snapshot", + Immutable = false, + Name = "RevertVms" + }); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamRoleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("AllPermissions") + .HasColumnType("boolean") + .HasColumnName("all_permissions"); + + b.Property("Immutable") + .HasColumnType("boolean") + .HasColumnName("immutable"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("team_roles"); + + b.HasData( + new + { + Id = new Guid("b65ce1b0-f995-45e1-93fc-47a09542cee5"), + AllPermissions = true, + Immutable = true, + Name = "View Admin" + }, + new + { + Id = new Guid("c875dcce-2488-4e73-8585-8375b4730151"), + AllPermissions = false, + Immutable = false, + Name = "Observer" + }, + new + { + Id = new Guid("a721a3bf-0ae1-4cd3-9d6f-e56d07260f22"), + AllPermissions = false, + Immutable = false, + Name = "View Member" + }); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamRolePermissionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("PermissionId") + .HasColumnType("uuid") + .HasColumnName("permission_id"); + + b.Property("RoleId") + .HasColumnType("uuid") + .HasColumnName("role_id"); + + b.HasKey("Id"); + + b.HasIndex("PermissionId"); + + b.HasIndex("RoleId", "PermissionId") + .IsUnique(); + + b.ToTable("team_role_permissions"); + + b.HasData( + new + { + Id = new Guid("d0715a73-16c3-4e44-be57-6bbf2e25e7b3"), + PermissionId = new Guid("f3ef9465-7f7c-43ef-9855-83798ce5bcd5"), + RoleId = new Guid("c875dcce-2488-4e73-8585-8375b4730151") + }, + new + { + Id = new Guid("5939d9cf-6f4b-4136-907c-0e878da2241b"), + PermissionId = new Guid("7be07cd5-104e-4770-800b-80ac26cda6d5"), + RoleId = new Guid("c875dcce-2488-4e73-8585-8375b4730151") + }, + new + { + Id = new Guid("d1252d24-c25d-4a80-a91c-4ad23efa9f89"), + PermissionId = new Guid("f3ef9465-7f7c-43ef-9855-83798ce5bcd5"), + RoleId = new Guid("a721a3bf-0ae1-4cd3-9d6f-e56d07260f22") + }, + new + { + Id = new Guid("f83d8368-1839-44d4-ad8c-dfa7fae56565"), + PermissionId = new Guid("fbabccc8-48c7-478a-bc30-d4bd8950e3d5"), + RoleId = new Guid("a721a3bf-0ae1-4cd3-9d6f-e56d07260f22") + }, + new + { + Id = new Guid("8a2d8db9-cb8f-4952-9f3b-1377140f0c11"), + PermissionId = new Guid("d7271fd0-e47f-4630-a5ef-744acc4dc004"), + RoleId = new Guid("a721a3bf-0ae1-4cd3-9d6f-e56d07260f22") + }, + new + { + Id = new Guid("aba4f8e0-298e-4e10-b0d4-ec6447baad6b"), + PermissionId = new Guid("6e41449b-a5da-4ac0-9adb-432210a5541c"), + RoleId = new Guid("a721a3bf-0ae1-4cd3-9d6f-e56d07260f22") + }); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.UserEntity", b => + { + b.Property("Key") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("key"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Key")); + + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("RoleId") + .HasColumnType("uuid") + .HasColumnName("role_id"); + + b.HasKey("Key"); + + b.HasIndex("Id") + .IsUnique(); + + b.HasIndex("RoleId"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ViewEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("DefaultTeamId") + .HasColumnType("uuid") + .HasColumnName("default_team_id"); + + b.Property("Description") + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("IsTemplate") + .HasColumnType("boolean") + .HasColumnName("is_template"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("ParentViewId") + .HasColumnType("uuid") + .HasColumnName("parent_view_id"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id"); + + b.HasIndex("DefaultTeamId"); + + b.HasIndex("ParentViewId"); + + b.ToTable("views"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ViewMembershipEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("PrimaryTeamMembershipId") + .HasColumnType("uuid") + .HasColumnName("primary_team_membership_id"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("ViewId") + .HasColumnType("uuid") + .HasColumnName("view_id"); + + b.HasKey("Id"); + + b.HasIndex("PrimaryTeamMembershipId"); + + b.HasIndex("UserId"); + + b.HasIndex("ViewId", "UserId") + .IsUnique(); + + b.ToTable("view_memberships"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.Webhooks.PendingEventEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("EventType") + .HasColumnType("integer") + .HasColumnName("event_type"); + + b.Property("Payload") + .HasColumnType("text") + .HasColumnName("payload"); + + b.Property("SubscriptionId") + .HasColumnType("uuid") + .HasColumnName("subscription_id"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("pending_events"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.Webhooks.WebhookSubscriptionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("CallbackUri") + .HasColumnType("text") + .HasColumnName("callback_uri"); + + b.Property("ClientId") + .HasColumnType("text") + .HasColumnName("client_id"); + + b.Property("ClientSecret") + .HasColumnType("text") + .HasColumnName("client_secret"); + + b.Property("LastError") + .HasColumnType("text") + .HasColumnName("last_error"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("webhooks"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.Webhooks.WebhookSubscriptionEventTypeEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id") + .HasDefaultValueSql("uuid_generate_v4()"); + + b.Property("EventType") + .HasColumnType("integer") + .HasColumnName("event_type"); + + b.Property("SubscriptionId") + .HasColumnType("uuid") + .HasColumnName("subscription_id"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId", "EventType") + .IsUnique(); + + b.ToTable("webhook_subscription_event_types"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ApplicationEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.ApplicationTemplateEntity", "Template") + .WithMany() + .HasForeignKey("ApplicationTemplateId"); + + b.HasOne("Player.Api.Data.Data.Models.ViewEntity", "View") + .WithMany("Applications") + .HasForeignKey("ViewId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + + b.Navigation("View"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ApplicationInstanceEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.ApplicationEntity", "Application") + .WithMany() + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Player.Api.Data.Data.Models.TeamEntity", "Team") + .WithMany("Applications") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Application"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.FileEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.ViewEntity", "View") + .WithMany("Files") + .HasForeignKey("ViewId"); + + b.Navigation("View"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.RolePermissionEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.PermissionEntity", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Player.Api.Data.Data.Models.RoleEntity", "Role") + .WithMany("Permissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.TeamRoleEntity", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Player.Api.Data.Data.Models.ViewEntity", "View") + .WithMany("Teams") + .HasForeignKey("ViewId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("View"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamMembershipEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.TeamRoleEntity", "Role") + .WithMany() + .HasForeignKey("RoleId"); + + b.HasOne("Player.Api.Data.Data.Models.TeamEntity", "Team") + .WithMany("Memberships") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Player.Api.Data.Data.Models.UserEntity", "User") + .WithMany("TeamMemberships") + .HasForeignKey("UserId") + .HasPrincipalKey("Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Player.Api.Data.Data.Models.ViewMembershipEntity", "ViewMembership") + .WithMany("TeamMemberships") + .HasForeignKey("ViewMembershipId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("Team"); + + b.Navigation("User"); + + b.Navigation("ViewMembership"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamPermissionAssignmentEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.TeamPermissionEntity", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Player.Api.Data.Data.Models.TeamEntity", "Team") + .WithMany("Permissions") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamRolePermissionEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.TeamPermissionEntity", "Permission") + .WithMany() + .HasForeignKey("PermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Player.Api.Data.Data.Models.TeamRoleEntity", "Role") + .WithMany("Permissions") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Permission"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.UserEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.RoleEntity", "Role") + .WithMany() + .HasForeignKey("RoleId"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ViewEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.TeamEntity", "DefaultTeam") + .WithMany() + .HasForeignKey("DefaultTeamId"); + + b.HasOne("Player.Api.Data.Data.Models.ViewEntity", "ParentView") + .WithMany() + .HasForeignKey("ParentViewId"); + + b.Navigation("DefaultTeam"); + + b.Navigation("ParentView"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ViewMembershipEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.TeamMembershipEntity", "PrimaryTeamMembership") + .WithMany() + .HasForeignKey("PrimaryTeamMembershipId"); + + b.HasOne("Player.Api.Data.Data.Models.UserEntity", "User") + .WithMany("ViewMemberships") + .HasForeignKey("UserId") + .HasPrincipalKey("Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Player.Api.Data.Data.Models.ViewEntity", "View") + .WithMany("Memberships") + .HasForeignKey("ViewId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PrimaryTeamMembership"); + + b.Navigation("User"); + + b.Navigation("View"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.Webhooks.PendingEventEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.Webhooks.WebhookSubscriptionEntity", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.Webhooks.WebhookSubscriptionEventTypeEntity", b => + { + b.HasOne("Player.Api.Data.Data.Models.Webhooks.WebhookSubscriptionEntity", "Subscription") + .WithMany("EventTypes") + .HasForeignKey("SubscriptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.RoleEntity", b => + { + b.Navigation("Permissions"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamEntity", b => + { + b.Navigation("Applications"); + + b.Navigation("Memberships"); + + b.Navigation("Permissions"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.TeamRoleEntity", b => + { + b.Navigation("Permissions"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.UserEntity", b => + { + b.Navigation("TeamMemberships"); + + b.Navigation("ViewMemberships"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ViewEntity", b => + { + b.Navigation("Applications"); + + b.Navigation("Files"); + + b.Navigation("Memberships"); + + b.Navigation("Teams"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.ViewMembershipEntity", b => + { + b.Navigation("TeamMemberships"); + }); + + modelBuilder.Entity("Player.Api.Data.Data.Models.Webhooks.WebhookSubscriptionEntity", b => + { + b.Navigation("EventTypes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Player.Api.Migrations.PostgreSQL/Migrations/20251113222811_Version10UpgradeSync.cs b/Player.Api.Migrations.PostgreSQL/Migrations/20251113222811_Version10UpgradeSync.cs new file mode 100644 index 0000000..e6e934c --- /dev/null +++ b/Player.Api.Migrations.PostgreSQL/Migrations/20251113222811_Version10UpgradeSync.cs @@ -0,0 +1,52 @@ +// Copyright 2025 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; + +#nullable disable + +namespace Player.Api.Migrations.PostgreSQL.Migrations +{ + /// + public partial class Version10UpgradeSync : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "default_team_id", + table: "views", + type: "uuid", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_views_default_team_id", + table: "views", + column: "default_team_id"); + + migrationBuilder.AddForeignKey( + name: "FK_views_teams_default_team_id", + table: "views", + column: "default_team_id", + principalTable: "teams", + principalColumn: "id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_views_teams_default_team_id", + table: "views"); + + migrationBuilder.DropIndex( + name: "IX_views_default_team_id", + table: "views"); + + migrationBuilder.DropColumn( + name: "default_team_id", + table: "views"); + } + } +} diff --git a/Player.Api.Migrations.PostgreSQL/Migrations/PlayerContextModelSnapshot.cs b/Player.Api.Migrations.PostgreSQL/Migrations/PlayerContextModelSnapshot.cs index 4fac0bc..cc52f77 100644 --- a/Player.Api.Migrations.PostgreSQL/Migrations/PlayerContextModelSnapshot.cs +++ b/Player.Api.Migrations.PostgreSQL/Migrations/PlayerContextModelSnapshot.cs @@ -21,7 +21,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "uuid-ossp"); @@ -150,7 +150,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text") .HasColumnName("path"); - b.Property>("TeamIds") + b.PrimitiveCollection>("TeamIds") .HasColumnType("uuid[]") .HasColumnName("team_ids"); @@ -773,6 +773,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnName("id") .HasDefaultValueSql("uuid_generate_v4()"); + b.Property("DefaultTeamId") + .HasColumnType("uuid") + .HasColumnName("default_team_id"); + b.Property("Description") .HasColumnType("text") .HasColumnName("description"); @@ -795,6 +799,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("DefaultTeamId"); + b.HasIndex("ParentViewId"); b.ToTable("views"); @@ -1086,10 +1092,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Player.Api.Data.Data.Models.ViewEntity", b => { + b.HasOne("Player.Api.Data.Data.Models.TeamEntity", "DefaultTeam") + .WithMany() + .HasForeignKey("DefaultTeamId"); + b.HasOne("Player.Api.Data.Data.Models.ViewEntity", "ParentView") .WithMany() .HasForeignKey("ParentViewId"); + b.Navigation("DefaultTeam"); + b.Navigation("ParentView"); }); diff --git a/Player.Api.Migrations.PostgreSQL/Player.Api.Migrations.PostgreSQL.csproj b/Player.Api.Migrations.PostgreSQL/Player.Api.Migrations.PostgreSQL.csproj index 1c71d77..df20fa3 100644 --- a/Player.Api.Migrations.PostgreSQL/Player.Api.Migrations.PostgreSQL.csproj +++ b/Player.Api.Migrations.PostgreSQL/Player.Api.Migrations.PostgreSQL.csproj @@ -1,9 +1,10 @@ - net8.0 + net10.0 + latest - + diff --git a/Player.Api/Infrastructure/DocumentFilters/ModelDocumentFilter.cs b/Player.Api/Infrastructure/DocumentFilters/ModelDocumentFilter.cs index 5df026c..f8304a2 100644 --- a/Player.Api/Infrastructure/DocumentFilters/ModelDocumentFilter.cs +++ b/Player.Api/Infrastructure/DocumentFilters/ModelDocumentFilter.cs @@ -1,7 +1,7 @@ // Copyright 2022 Carnegie Mellon University. All Rights Reserved. // Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Player.Api.Data.Data.Models; using Player.Api.ViewModels.Webhooks; using Swashbuckle.AspNetCore.SwaggerGen; @@ -20,4 +20,4 @@ public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) context.SchemaGenerator.GenerateSchema(typeof(TeamPermission), context.SchemaRepository); } } -} \ No newline at end of file +} diff --git a/Player.Api/Infrastructure/Extensions/ServiceCollectionExtensions.cs b/Player.Api/Infrastructure/Extensions/ServiceCollectionExtensions.cs index 30c33d0..37b63f3 100644 --- a/Player.Api/Infrastructure/Extensions/ServiceCollectionExtensions.cs +++ b/Player.Api/Infrastructure/Extensions/ServiceCollectionExtensions.cs @@ -10,9 +10,9 @@ using System.Reflection; using Microsoft.CodeAnalysis; using System.Text.Json; -using Microsoft.OpenApi.Models; using Player.Api.Infrastructure.DocumentFilters; using System.Runtime.Serialization; +using Microsoft.OpenApi; namespace Player.Api.Infrastructure.Extensions { @@ -47,27 +47,32 @@ public static void AddSwagger(this IServiceCollection services, AuthorizationOpt } }); - c.AddSecurityRequirement(new OpenApiSecurityRequirement() + c.AddSecurityRequirement((document) => new OpenApiSecurityRequirement { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "oauth2" - }, - Scheme = "oauth2" - }, - new[] {authOptions.AuthorizationScope} - } + { new OpenApiSecuritySchemeReference("oauth2", document), [authOptions.AuthorizationScope] } }); + c.IncludeXmlComments(commentsFile); c.EnableAnnotations(); c.OperationFilter(); c.DocumentFilter(); - c.MapType>(() => new OpenApiSchema { Type = "string", Format = "uuid" }); - c.MapType(() => new OpenApiSchema { Type = "object", Nullable = true }); + c.MapType>(() => new OpenApiSchema + { + OneOf = new List + { + new OpenApiSchema { Type = JsonSchemaType.String, Format = "uuid" }, + new OpenApiSchema { Type = JsonSchemaType.Null } + } + }); + + c.MapType(() => new OpenApiSchema + { + OneOf = new List + { + new OpenApiSchema { Type = JsonSchemaType.Object }, + new OpenApiSchema { Type = JsonSchemaType.Null } + } + }); }); } diff --git a/Player.Api/Infrastructure/OperationFilters/DefaultResponse.cs b/Player.Api/Infrastructure/OperationFilters/DefaultResponse.cs index 3f61533..08cfdd8 100644 --- a/Player.Api/Infrastructure/OperationFilters/DefaultResponse.cs +++ b/Player.Api/Infrastructure/OperationFilters/DefaultResponse.cs @@ -3,8 +3,7 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; -using Swashbuckle.AspNetCore.Swagger; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Player.Api.Infrastructure.OperationFilters diff --git a/Player.Api/Player.Api.csproj b/Player.Api/Player.Api.csproj index 91df81b..ed2a357 100644 --- a/Player.Api/Player.Api.csproj +++ b/Player.Api/Player.Api.csproj @@ -1,45 +1,45 @@ - net8.0 + net10.0 bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 1591 + latest - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - + + + + - + diff --git a/global.json b/global.json index 4570606..dcf907b 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.304", + "version": "10.0", "allowPrerelease": false, "rollForward": "latestMinor" }