✨ Provide real user and posts data for the thinking
This commit is contained in:
		
							
								
								
									
										252
									
								
								DysonNetwork.Sphere/Post/PostServiceGrpc.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								DysonNetwork.Sphere/Post/PostServiceGrpc.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,252 @@ | ||||
| using DysonNetwork.Shared.Proto; | ||||
| using DysonNetwork.Shared.Models; | ||||
| using Grpc.Core; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
|  | ||||
| namespace DysonNetwork.Sphere.Post; | ||||
|  | ||||
| public class PostServiceGrpc(AppDatabase db, PostService ps) : Shared.Proto.PostService.PostServiceBase | ||||
| { | ||||
|     public override async Task<Shared.Proto.Post> GetPost(GetPostRequest request, ServerCallContext context) | ||||
|     { | ||||
|         if (!Guid.TryParse(request.Id, out var id)) | ||||
|             throw new RpcException(new Status(StatusCode.InvalidArgument, "invalid post id")); | ||||
|  | ||||
|         var post = await db.Posts | ||||
|             .Include(p => p.Publisher) | ||||
|             .Include(p => p.Tags) | ||||
|             .Include(p => p.Categories) | ||||
|             .Include(p => p.RepliedPost) | ||||
|             .Include(p => p.ForwardedPost) | ||||
|             .Include(p => p.FeaturedRecords) | ||||
|             .FilterWithVisibility(null, [], []) | ||||
|             .FirstOrDefaultAsync(p => p.Id == id); | ||||
|  | ||||
|         if (post == null) throw new RpcException(new Status(StatusCode.NotFound, "post not found")); | ||||
|  | ||||
|         post = await ps.LoadPostInfo(post); | ||||
|  | ||||
|         return post.ToProtoValue(); | ||||
|     } | ||||
|  | ||||
|     public override async Task<GetPostBatchResponse> GetPostBatch(GetPostBatchRequest request, ServerCallContext context) | ||||
|     { | ||||
|         var ids = request.Ids | ||||
|             .Where(s => !string.IsNullOrWhiteSpace(s) && Guid.TryParse(s, out _)) | ||||
|             .Select(Guid.Parse) | ||||
|             .ToList(); | ||||
|  | ||||
|         if (ids.Count == 0) return new GetPostBatchResponse(); | ||||
|  | ||||
|         var posts = await db.Posts | ||||
|             .Include(p => p.Publisher) | ||||
|             .Include(p => p.Tags) | ||||
|             .Include(p => p.Categories) | ||||
|             .Include(p => p.RepliedPost) | ||||
|             .Include(p => p.ForwardedPost) | ||||
|             .Include(p => p.FeaturedRecords) | ||||
|             .Include(p => p.Awards) | ||||
|             .Where(p => ids.Contains(p.Id)) | ||||
|             .FilterWithVisibility(null, [], []) | ||||
|             .ToListAsync(); | ||||
|  | ||||
|         posts = await ps.LoadPostInfo(posts, null); | ||||
|  | ||||
|         var resp = new GetPostBatchResponse(); | ||||
|         resp.Posts.AddRange(posts.Select(p => p.ToProtoValue())); | ||||
|         return resp; | ||||
|     } | ||||
|  | ||||
|     public override async Task<SearchPostsResponse> SearchPosts(SearchPostsRequest request, ServerCallContext context) | ||||
|     { | ||||
|         var query = db.Posts | ||||
|             .Include(p => p.Publisher) | ||||
|             .Include(p => p.Tags) | ||||
|             .Include(p => p.Categories) | ||||
|             .Include(p => p.RepliedPost) | ||||
|             .Include(p => p.ForwardedPost) | ||||
|             .Include(p => p.Attachments) | ||||
|             .Include(p => p.Awards) | ||||
|             .Include(p => p.Reactions) | ||||
|             .Include(p => p.FeaturedRecords) | ||||
|             .Where(p => p.DeletedAt == null) // Only active posts | ||||
|             .AsQueryable(); | ||||
|  | ||||
|         if (!string.IsNullOrWhiteSpace(request.Query)) | ||||
|         { | ||||
|             // Simple search, assuming full-text search or title/content contains | ||||
|             query = query.Where(p => | ||||
|                 EF.Functions.ILike(p.Title, $"%{request.Query}%") || | ||||
|                 EF.Functions.ILike(p.Content, $"%{request.Query}%") || | ||||
|                 EF.Functions.ILike(p.Description, $"%{request.Query}%")); | ||||
|         } | ||||
|  | ||||
|         if (!string.IsNullOrWhiteSpace(request.PublisherId) && Guid.TryParse(request.PublisherId, out var pid)) | ||||
|         { | ||||
|             query = query.Where(p => p.PublisherId == pid); | ||||
|         } | ||||
|  | ||||
|         if (!string.IsNullOrWhiteSpace(request.RealmId) && Guid.TryParse(request.RealmId, out var rid)) | ||||
|         { | ||||
|             query = query.Where(p => p.RealmId == rid); | ||||
|         } | ||||
|  | ||||
|         query = query.FilterWithVisibility(null, [], []); | ||||
|  | ||||
|         var totalSize = await query.CountAsync(); | ||||
|  | ||||
|         // Apply pagination | ||||
|         var pageSize = request.PageSize > 0 ? request.PageSize : 20; | ||||
|         var pageToken = request.PageToken; | ||||
|         var offset = string.IsNullOrEmpty(pageToken) ? 0 : int.Parse(pageToken); | ||||
|  | ||||
|         var posts = await query | ||||
|             .OrderByDescending(p => p.PublishedAt ?? p.CreatedAt) | ||||
|             .Skip(offset) | ||||
|             .Take(pageSize) | ||||
|             .ToListAsync(); | ||||
|  | ||||
|         posts = await ps.LoadPostInfo(posts, null, true); | ||||
|  | ||||
|         var nextToken = offset + pageSize < totalSize ? (offset + pageSize).ToString() : string.Empty; | ||||
|  | ||||
|         var resp = new SearchPostsResponse(); | ||||
|         resp.Posts.AddRange(posts.Select(p => p.ToProtoValue())); | ||||
|         resp.NextPageToken = nextToken; | ||||
|         resp.TotalSize = totalSize; | ||||
|  | ||||
|         return resp; | ||||
|     } | ||||
|  | ||||
|     public override async Task<ListPostsResponse> ListPosts(ListPostsRequest request, ServerCallContext context) | ||||
|     { | ||||
|         var query = db.Posts | ||||
|             .Include(p => p.Publisher) | ||||
|             .Include(p => p.Tags) | ||||
|             .Include(p => p.Categories) | ||||
|             .Include(p => p.RepliedPost) | ||||
|             .Include(p => p.ForwardedPost) | ||||
|             .Include(p => p.Awards) | ||||
|             .Include(p => p.FeaturedRecords) | ||||
|             .Where(p => p.DeletedAt == null) | ||||
|             .AsQueryable(); | ||||
|  | ||||
|         if (!string.IsNullOrWhiteSpace(request.PublisherId) && Guid.TryParse(request.PublisherId, out var pid)) | ||||
|         { | ||||
|             query = query.Where(p => p.PublisherId == pid); | ||||
|         } | ||||
|  | ||||
|         if (!string.IsNullOrWhiteSpace(request.RealmId) && Guid.TryParse(request.RealmId, out var rid)) | ||||
|         { | ||||
|             query = query.Where(p => p.RealmId == rid); | ||||
|         } | ||||
|  | ||||
|         if (request.Categories.Count > 0) | ||||
|         { | ||||
|             query = query.Where(p => p.Categories.Any(c => request.Categories.Contains(c.Slug))); | ||||
|         } | ||||
|  | ||||
|         if (request.Tags.Count > 0) | ||||
|         { | ||||
|             query = query.Where(p => p.Tags.Any(c => request.Tags.Contains(c.Slug))); | ||||
|         } | ||||
|  | ||||
|         // TODO: Add types filtering when proto is regenerated | ||||
|         // if (request.Types.Count > 0) | ||||
|         // { | ||||
|         //     var types = request.Types.Select(t => (Shared.Models.PostType)t).Distinct(); | ||||
|         //     query = query.Where(p => types.Contains(p.Type)); | ||||
|         // } | ||||
|  | ||||
|         if (request.OnlyMedia) | ||||
|         { | ||||
|             query = query.Where(e => e.Attachments.Count > 0); | ||||
|         } | ||||
|  | ||||
|         // Pinned filtering | ||||
|         switch (request.Pinned) | ||||
|         { | ||||
|             case Shared.Proto.PostPinMode.RealmPage when !string.IsNullOrWhiteSpace(request.RealmId): | ||||
|                 query = query.Where(p => p.PinMode == Shared.Models.PostPinMode.RealmPage); | ||||
|                 break; | ||||
|             case Shared.Proto.PostPinMode.PublisherPage when !string.IsNullOrWhiteSpace(request.PublisherId): | ||||
|                 query = query.Where(p => p.PinMode == Shared.Models.PostPinMode.PublisherPage); | ||||
|                 break; | ||||
|             case Shared.Proto.PostPinMode.ReplyPage: | ||||
|                 query = query.Where(p => p.PinMode == Shared.Models.PostPinMode.ReplyPage); | ||||
|                 break; | ||||
|             default: | ||||
|                 if (request.Pinned != null) | ||||
|                 { | ||||
|                     // Specific pinned mode but conditions not met, or unknown mode | ||||
|                     query = query.Where(p => p.PinMode == (Shared.Models.PostPinMode)request.Pinned); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     query = query.Where(p => p.PinMode == null); | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         // Include/exclude replies | ||||
|         if (request.IncludeReplies) | ||||
|         { | ||||
|             // Include both root and reply posts | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Exclude reply posts, only root posts | ||||
|             query = query.Where(e => e.RepliedPostId == null); | ||||
|         } | ||||
|  | ||||
|         // TODO: Time range filtering when proto fields are available | ||||
|         // if (request.After != null) | ||||
|         // { | ||||
|         //     var afterTime = request.After.ToDateTimeOffset(); | ||||
|         //     query = query.Where(p => (p.CreatedAt >= afterTime) || (p.PublishedAt >= afterTime)); | ||||
|         // } | ||||
|  | ||||
|         // if (request.Before != null) | ||||
|         // { | ||||
|         //     var beforeTime = request.Before.ToDateTimeOffset(); | ||||
|         //     query = query.Where(p => (p.CreatedAt <= beforeTime) || (p.PublishedAt <= beforeTime)); | ||||
|         // } | ||||
|  | ||||
|         // TODO: Query text search when proto field is available | ||||
|         // if (!string.IsNullOrWhiteSpace(request.Query)) | ||||
|         // { | ||||
|         //     query = query.Where(p => | ||||
|         //         EF.Functions.ILike(p.Title, $"%{request.Query}%") || | ||||
|         //         EF.Functions.ILike(p.Content, $"%{request.Query}%") || | ||||
|         //         EF.Functions.ILike(p.Description, $"%{request.Query}%")); | ||||
|         // } | ||||
|  | ||||
|         // Visibility filter (simplified for grpc - no user context) | ||||
|         query = query.FilterWithVisibility(null, [], []); | ||||
|  | ||||
|         var totalSize = await query.CountAsync(); | ||||
|  | ||||
|         var pageSize = request.PageSize > 0 ? request.PageSize : 20; | ||||
|         var pageToken = request.PageToken; | ||||
|         var offset = string.IsNullOrEmpty(pageToken) ? 0 : int.Parse(pageToken); | ||||
|  | ||||
|         // Ordering - TODO: Add shuffle when proto field is available | ||||
|         var orderedQuery = query.OrderByDescending(e => e.PublishedAt ?? e.CreatedAt); | ||||
|  | ||||
|         var posts = await orderedQuery | ||||
|             .Skip(offset) | ||||
|             .Take(pageSize) | ||||
|             .ToListAsync(); | ||||
|  | ||||
|         posts = await ps.LoadPostInfo(posts, null, true); | ||||
|  | ||||
|         var nextToken = offset + pageSize < totalSize ? (offset + pageSize).ToString() : string.Empty; | ||||
|  | ||||
|         var resp = new ListPostsResponse(); | ||||
|         resp.Posts.AddRange(posts.Select(p => p.ToProtoValue())); | ||||
|         resp.NextPageToken = nextToken; | ||||
|         resp.TotalSize = totalSize; | ||||
|  | ||||
|         return resp; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user