diff --git a/src/Orbit.Api/Controllers/FriendsController.cs b/src/Orbit.Api/Controllers/FriendsController.cs index ecaa4d3f2..8742c03da 100644 --- a/src/Orbit.Api/Controllers/FriendsController.cs +++ b/src/Orbit.Api/Controllers/FriendsController.cs @@ -24,6 +24,7 @@ public record BlockUserBody([property: JsonRequired] Guid BlockedUserId); public record ReportUserBody([property: JsonRequired] Guid ReportedUserId, [property: JsonRequired] ReportReason Reason, string? Details, Guid? CheerId); [HttpGet] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task GetFriends(CancellationToken cancellationToken) @@ -33,6 +34,7 @@ public async Task GetFriends(CancellationToken cancellationToken) } [HttpGet("{userId:guid}/profile")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -44,6 +46,7 @@ public async Task GetFriendProfile(Guid userId, CancellationToken } [HttpGet("invite-preview")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("invite-preview")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] @@ -58,6 +61,7 @@ public async Task GetInvitePreview( } [HttpGet("feed")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task GetFeed( @@ -71,6 +75,7 @@ public async Task GetFeed( } [HttpGet("cheers")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task GetCheers( @@ -83,6 +88,7 @@ public async Task GetCheers( } [HttpPost("requests")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("friend-requests")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -103,6 +109,7 @@ public async Task SendRequest( } [HttpPost("requests/{friendshipId:guid}/accept")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("friend-mutations")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] @@ -119,6 +126,7 @@ public async Task AcceptRequest(Guid friendshipId, CancellationTo } [HttpDelete("{friendUserId:guid}")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("friend-mutations")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] @@ -130,6 +138,7 @@ public async Task RemoveFriend(Guid friendUserId, CancellationTok } [HttpPost("cheers")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("cheers")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -149,6 +158,7 @@ public async Task SendCheer( } [HttpPost("block")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("block")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -167,6 +177,7 @@ public async Task Block( } [HttpDelete("block/{blockedUserId:guid}")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("unblock")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] @@ -177,6 +188,7 @@ public async Task Unblock(Guid blockedUserId, CancellationToken c } [HttpPost("report")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("reports")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] diff --git a/src/Orbit.Api/Controllers/ProfileController.cs b/src/Orbit.Api/Controllers/ProfileController.cs index b9a0c43dd..7923c30f6 100644 --- a/src/Orbit.Api/Controllers/ProfileController.cs +++ b/src/Orbit.Api/Controllers/ProfileController.cs @@ -305,6 +305,7 @@ public async Task ResetTour(CancellationToken cancellationToken) } [HttpPut("handle")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [DistributedRateLimit("set-handle")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -324,6 +325,7 @@ public async Task SetHandle( } [HttpPut("social-opt-in")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -358,6 +360,7 @@ public async Task UpdateMarketingConsent( } [HttpPut("public")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] diff --git a/src/Orbit.Api/Controllers/PublicProfileController.cs b/src/Orbit.Api/Controllers/PublicProfileController.cs index 8dcc45a85..f30609719 100644 --- a/src/Orbit.Api/Controllers/PublicProfileController.cs +++ b/src/Orbit.Api/Controllers/PublicProfileController.cs @@ -24,6 +24,7 @@ public class PublicProfileController(IMediator mediator, IDistributedCache cache }; [HttpGet("{slug}")] + [Obsolete("The social API is deprecated and will be removed in part 2 of ORB-233.")] [AllowAnonymous] [DistributedRateLimit("public-profile")] [ProducesResponseType(StatusCodes.Status200OK)] diff --git a/src/Orbit.Api/openapi.json b/src/Orbit.Api/openapi.json index 9aa0e48f0..17f481005 100644 --- a/src/Orbit.Api/openapi.json +++ b/src/Orbit.Api/openapi.json @@ -4708,7 +4708,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/{userId}/profile": { @@ -4778,7 +4779,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/invite-preview": { @@ -4846,7 +4848,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/feed": { @@ -4906,7 +4909,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/cheers": { @@ -4955,7 +4959,8 @@ } } } - } + }, + "deprecated": true }, "post": { "tags": [ @@ -5034,7 +5039,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/requests": { @@ -5135,7 +5141,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/requests/{friendshipId}/accept": { @@ -5205,7 +5212,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/{friendUserId}": { @@ -5255,7 +5263,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/block": { @@ -5336,7 +5345,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/block/{blockedUserId}": { @@ -5386,7 +5396,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/report": { @@ -5467,7 +5478,8 @@ } } } - } + }, + "deprecated": true } }, "/api/Gamification/profile": { @@ -12670,7 +12682,8 @@ } } } - } + }, + "deprecated": true } }, "/api/Profile/social-opt-in": { @@ -12751,7 +12764,8 @@ } } } - } + }, + "deprecated": true } }, "/api/Profile/marketing-consent": { @@ -12913,7 +12927,8 @@ } } } - } + }, + "deprecated": true } }, "/api/Profile/reset": { @@ -13104,7 +13119,8 @@ } } } - } + }, + "deprecated": true } }, "/api/referrals/code": { @@ -18768,10 +18784,12 @@ "type": [ "null", "string" - ] + ], + "deprecated": true }, "socialOptIn": { - "type": "boolean" + "type": "boolean", + "deprecated": true }, "uses24HourClock": { "type": "boolean", diff --git a/src/Orbit.Api/openapi_v2.json b/src/Orbit.Api/openapi_v2.json index 3aee97ecd..4844c870d 100644 --- a/src/Orbit.Api/openapi_v2.json +++ b/src/Orbit.Api/openapi_v2.json @@ -4775,7 +4775,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/{userId}/profile": { @@ -4846,7 +4847,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/invite-preview": { @@ -4915,7 +4917,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/feed": { @@ -4976,7 +4979,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/cheers": { @@ -5026,7 +5030,8 @@ } } } - } + }, + "deprecated": true }, "post": { "tags": [ @@ -5106,7 +5111,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/requests": { @@ -5208,7 +5214,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/requests/{friendshipId}/accept": { @@ -5279,7 +5286,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/{friendUserId}": { @@ -5330,7 +5338,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/block": { @@ -5412,7 +5421,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/block/{blockedUserId}": { @@ -5463,7 +5473,8 @@ } } } - } + }, + "deprecated": true } }, "/api/friends/report": { @@ -5545,7 +5556,8 @@ } } } - } + }, + "deprecated": true } }, "/api/Gamification/profile": { @@ -12836,7 +12848,8 @@ } } } - } + }, + "deprecated": true } }, "/api/Profile/social-opt-in": { @@ -12918,7 +12931,8 @@ } } } - } + }, + "deprecated": true } }, "/api/Profile/marketing-consent": { @@ -13082,7 +13096,8 @@ } } } - } + }, + "deprecated": true } }, "/api/Profile/reset": { @@ -13276,7 +13291,8 @@ } } } - } + }, + "deprecated": true } }, "/api/referrals/code": { @@ -18969,10 +18985,12 @@ "type": [ "null", "string" - ] + ], + "deprecated": true }, "socialOptIn": { - "type": "boolean" + "type": "boolean", + "deprecated": true }, "uses24HourClock": { "type": "boolean", diff --git a/src/Orbit.Application/Profile/Queries/GetProfileQuery.cs b/src/Orbit.Application/Profile/Queries/GetProfileQuery.cs index fe2218e02..ae3feb56f 100644 --- a/src/Orbit.Application/Profile/Queries/GetProfileQuery.cs +++ b/src/Orbit.Application/Profile/Queries/GetProfileQuery.cs @@ -54,7 +54,9 @@ public record ProfileResponse( GoogleCalendarAutoSyncStatus GoogleCalendarAutoSyncStatus, DateTime? GoogleCalendarLastSyncedAt, bool CanViewGamification, + [property: Obsolete("The social profile field is deprecated and will be removed in part 2 of ORB-233.")] string? Handle, + [property: Obsolete("The social profile field is deprecated and will be removed in part 2 of ORB-233.")] bool SocialOptIn, bool Uses24HourClock = true, PublicProfileSettings? PublicProfile = null, diff --git a/tests/Orbit.Infrastructure.Tests/Controllers/FriendsControllerTests.cs b/tests/Orbit.Infrastructure.Tests/Controllers/FriendsControllerTests.cs index ed065f033..3062c4225 100644 --- a/tests/Orbit.Infrastructure.Tests/Controllers/FriendsControllerTests.cs +++ b/tests/Orbit.Infrastructure.Tests/Controllers/FriendsControllerTests.cs @@ -34,6 +34,7 @@ public FriendsControllerTests() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetFriends_Success_ReturnsOk() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -45,6 +46,7 @@ public async Task GetFriends_Success_ReturnsOk() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetFriends_PayGateFailure_Returns403() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -56,6 +58,7 @@ public async Task GetFriends_PayGateFailure_Returns403() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetFriendProfile_Success_ReturnsOk() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -67,6 +70,7 @@ public async Task GetFriendProfile_Success_ReturnsOk() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetInvitePreview_Success_ReturnsOk() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -78,6 +82,7 @@ public async Task GetInvitePreview_Success_ReturnsOk() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetFeed_Success_ReturnsOk() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -89,6 +94,7 @@ public async Task GetFeed_Success_ReturnsOk() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetCheers_Success_ReturnsOk() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -100,6 +106,7 @@ public async Task GetCheers_Success_ReturnsOk() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task SendRequest_Success_ReturnsOk() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -112,6 +119,7 @@ public async Task SendRequest_Success_ReturnsOk() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task SendRequest_Failure_ReturnsBadRequest() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -124,6 +132,7 @@ public async Task SendRequest_Failure_ReturnsBadRequest() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task AcceptRequest_Success_ReturnsNoContent() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -135,6 +144,7 @@ public async Task AcceptRequest_Success_ReturnsNoContent() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task RemoveFriend_Success_ReturnsNoContent() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -146,6 +156,7 @@ public async Task RemoveFriend_Success_ReturnsNoContent() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task SendCheer_Success_ReturnsOk() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -158,6 +169,7 @@ public async Task SendCheer_Success_ReturnsOk() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task SendCheer_SocialDisabled_Returns403() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -170,6 +182,7 @@ public async Task SendCheer_SocialDisabled_Returns403() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task Block_Success_ReturnsNoContent() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -182,6 +195,7 @@ public async Task Block_Success_ReturnsNoContent() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task Unblock_Success_ReturnsNoContent() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -193,6 +207,7 @@ public async Task Unblock_Success_ReturnsNoContent() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task Report_Success_ReturnsOk() { _mediator.Send(Arg.Any(), Arg.Any()) diff --git a/tests/Orbit.Infrastructure.Tests/Controllers/PublicProfileControllerTests.cs b/tests/Orbit.Infrastructure.Tests/Controllers/PublicProfileControllerTests.cs index ebea21022..8b57b3c3e 100644 --- a/tests/Orbit.Infrastructure.Tests/Controllers/PublicProfileControllerTests.cs +++ b/tests/Orbit.Infrastructure.Tests/Controllers/PublicProfileControllerTests.cs @@ -23,6 +23,7 @@ public PublicProfileControllerTests() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetPublicProfile_CacheMissSuccess_ReturnsJsonContent() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -35,6 +36,7 @@ public async Task GetPublicProfile_CacheMissSuccess_ReturnsJsonContent() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetPublicProfile_CacheMissFailure_ReturnsNotFound() { _mediator.Send(Arg.Any(), Arg.Any()) @@ -46,6 +48,7 @@ public async Task GetPublicProfile_CacheMissFailure_ReturnsNotFound() } [Fact] + [Obsolete("Covers the deprecated social API routes retained for ORB-233 part 1.")] public async Task GetPublicProfile_CacheHit_ReturnsJsonWithoutQuerying() { _cache.GetAsync(Arg.Any(), Arg.Any()) diff --git a/tests/Orbit.Infrastructure.Tests/Controllers/SocialDeprecationTests.cs b/tests/Orbit.Infrastructure.Tests/Controllers/SocialDeprecationTests.cs index a31d80cb2..5f2e7b06c 100644 --- a/tests/Orbit.Infrastructure.Tests/Controllers/SocialDeprecationTests.cs +++ b/tests/Orbit.Infrastructure.Tests/Controllers/SocialDeprecationTests.cs @@ -5,6 +5,71 @@ namespace Orbit.Infrastructure.Tests.Controllers; public class SocialDeprecationTests { + public static TheoryData RetiringRoutes() + { + (string Path, string Method)[] routes = + [ + ("/api/friends", "get"), + ("/api/friends/{userId}/profile", "get"), + ("/api/friends/invite-preview", "get"), + ("/api/friends/feed", "get"), + ("/api/friends/cheers", "get"), + ("/api/friends/requests", "post"), + ("/api/friends/requests/{friendshipId}/accept", "post"), + ("/api/friends/{friendUserId}", "delete"), + ("/api/friends/cheers", "post"), + ("/api/friends/block", "post"), + ("/api/friends/block/{blockedUserId}", "delete"), + ("/api/friends/report", "post"), + ("/api/u/{slug}", "get"), + ("/api/Profile/handle", "put"), + ("/api/Profile/social-opt-in", "put"), + ("/api/Profile/public", "put") + ]; + + var cases = new TheoryData(); + foreach (var file in new[] { "openapi.json", "openapi_v2.json" }) + foreach (var (path, method) in routes) + cases.Add(file, path, method); + return cases; + } + + [Theory] + [MemberData(nameof(RetiringRoutes))] + public void GeneratedOpenApi_RetiringRouteRemainsAvailableAndDeprecated(string file, string path, string method) + { + using var document = ReadDocument(file); + var operation = document.RootElement.GetProperty("paths").GetProperty(path).GetProperty(method); + + operation.TryGetProperty("deprecated", out var deprecated).Should().BeTrue(); + deprecated.GetBoolean().Should().BeTrue(); + } + + [Theory] + [InlineData("openapi.json")] + [InlineData("openapi_v2.json")] + public void GeneratedOpenApi_RequiredSocialProfileFieldsRemainAvailableAndDeprecated(string file) + { + using var document = ReadDocument(file); + var paths = document.RootElement.GetProperty("paths"); + paths.GetProperty("/api/Profile").GetProperty("get") + .TryGetProperty("deprecated", out _).Should().BeFalse(); + + var profile = document.RootElement.GetProperty("components").GetProperty("schemas").GetProperty("ProfileResponse"); + var properties = profile.GetProperty("properties"); + foreach (var name in new[] { "handle", "socialOptIn" }) + { + var property = properties.GetProperty(name); + property.TryGetProperty("deprecated", out var deprecated).Should().BeTrue(); + deprecated.GetBoolean().Should().BeTrue(); + } + + properties.TryGetProperty("publicProfile", out _).Should().BeTrue(); + properties.GetProperty("uses24HourClock").TryGetProperty("deprecated", out _).Should().BeFalse(); + profile.GetProperty("required").EnumerateArray().Select(item => item.GetString()) + .Should().Contain(["handle", "socialOptIn"]).And.NotContain("publicProfile"); + } + [Theory] [InlineData("/api/challenges", "get")] [InlineData("/api/challenges", "post")] @@ -23,9 +88,7 @@ public void GeneratedOpenApi_RouteRemainsAvailableAndDeprecated(string route, st { foreach (var file in new[] { "openapi.json", "openapi_v2.json" }) { - var path = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, - "../../../../..", "src/Orbit.Api", file)); - using var document = JsonDocument.Parse(File.ReadAllText(path)); + using var document = ReadDocument(file); var operation = document.RootElement.GetProperty("paths").GetProperty(route).GetProperty(method); operation.TryGetProperty("deprecated", out var deprecated).Should().BeTrue( @@ -33,4 +96,11 @@ public void GeneratedOpenApi_RouteRemainsAvailableAndDeprecated(string route, st deprecated.GetBoolean().Should().BeTrue(); } } + + private static JsonDocument ReadDocument(string file) + { + var path = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, + "../../../../..", "src/Orbit.Api", file)); + return JsonDocument.Parse(File.ReadAllText(path)); + } }