using FastEndpoints; using Microsoft.EntityFrameworkCore; using Socialize.Api.Data; using Socialize.Api.Infrastructure.Security; using Socialize.Api.Modules.Assets.Data; using Socialize.Api.Modules.ContentItems.Contracts; using Socialize.Api.Modules.ContentItems.Data; using Socialize.Api.Modules.Notifications.Contracts; using System.Text.Json; namespace Socialize.Api.Modules.Assets.Handlers; public record CreateGoogleDriveAssetRequest( Guid WorkspaceId, Guid ContentItemId, string AssetType, string DisplayName, string GoogleDriveFileId, string GoogleDriveLink, string? PreviewUrl); public class CreateGoogleDriveAssetRequestValidator : Validator { public CreateGoogleDriveAssetRequestValidator() { RuleFor(x => x.WorkspaceId).NotEmpty(); RuleFor(x => x.ContentItemId).NotEmpty(); RuleFor(x => x.AssetType).NotEmpty().MaximumLength(64); RuleFor(x => x.DisplayName).NotEmpty().MaximumLength(256); RuleFor(x => x.GoogleDriveFileId).NotEmpty().MaximumLength(256); RuleFor(x => x.GoogleDriveLink).NotEmpty().MaximumLength(2048); RuleFor(x => x.PreviewUrl).MaximumLength(2048); } } public class CreateGoogleDriveAssetHandler( AppDbContext dbContext, AccessScopeService accessScopeService, IContentItemActivityWriter activityWriter, INotificationEventWriter notificationEventWriter) : Endpoint { public override void Configure() { Post("/api/assets/google-drive"); Options(o => o.WithTags("Assets")); } public override async Task HandleAsync(CreateGoogleDriveAssetRequest request, CancellationToken ct) { ContentItem? contentItem = await dbContext.ContentItems .SingleOrDefaultAsync( candidate => candidate.Id == request.ContentItemId && candidate.WorkspaceId == request.WorkspaceId, ct); if (contentItem is null) { AddError(request => request.ContentItemId, "The selected content item does not exist in the active workspace."); await SendErrorsAsync(StatusCodes.Status400BadRequest, ct); return; } if (!await accessScopeService.CanContributeToCampaignAsync(User, contentItem.WorkspaceId, contentItem.ClientId, contentItem.CampaignId, ct)) { await SendForbiddenAsync(ct); return; } Asset asset = new() { Id = Guid.NewGuid(), WorkspaceId = request.WorkspaceId, ContentItemId = request.ContentItemId, AssetType = request.AssetType.Trim(), SourceType = "GoogleDrive", DisplayName = request.DisplayName.Trim(), GoogleDriveFileId = request.GoogleDriveFileId.Trim(), GoogleDriveLink = request.GoogleDriveLink.Trim(), PreviewUrl = string.IsNullOrWhiteSpace(request.PreviewUrl) ? null : request.PreviewUrl.Trim(), CurrentRevisionNumber = 1, CreatedAt = DateTimeOffset.UtcNow, }; AssetRevision revision = new() { Id = Guid.NewGuid(), AssetId = asset.Id, RevisionNumber = 1, SourceReference = asset.GoogleDriveLink, PreviewUrl = asset.PreviewUrl, CreatedAt = DateTimeOffset.UtcNow, }; dbContext.Assets.Add(asset); dbContext.AssetRevisions.Add(revision); await dbContext.SaveChangesAsync(ct); await activityWriter.WriteAsync( new ContentItemActivityWriteModel( asset.WorkspaceId, asset.ContentItemId, "asset.google-drive-linked", "Asset", asset.Id, $"Google Drive asset {asset.DisplayName} was linked to {contentItem.Title}.", User.GetUserId(), User.GetEmail(), JsonSerializer.Serialize(new { assetType = asset.AssetType, sourceType = asset.SourceType, googleDriveFileId = asset.GoogleDriveFileId, currentRevisionNumber = asset.CurrentRevisionNumber, })), ct); await notificationEventWriter.WriteAsync( new NotificationEventWriteModel( asset.WorkspaceId, asset.ContentItemId, "asset.google-drive-linked", "Asset", asset.Id, $"Google Drive asset {asset.DisplayName} was linked to {contentItem.Title}.", null, null, $$"""{"googleDriveFileId":"{{asset.GoogleDriveFileId}}"}"""), ct); AssetDto dto = new( asset.Id, asset.WorkspaceId, asset.ContentItemId, asset.AssetType, asset.SourceType, asset.DisplayName, asset.GoogleDriveFileId, asset.GoogleDriveLink, asset.PreviewUrl, asset.CurrentRevisionNumber, asset.CreatedAt, [ new AssetRevisionDto( revision.Id, revision.AssetId, revision.RevisionNumber, revision.SourceReference, revision.PreviewUrl, revision.Notes, revision.CreatedByUserId, revision.CreatedAt) ]); await SendAsync(dto, StatusCodes.Status201Created, ct); } }