From 33760d7c7dc70d404dcfce9eb2bc1ac11894c5f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BD=D1=8E=D1=82=D0=B8=D0=BD=20=D0=9C=D0=B0=D0=BA?= =?UTF-8?q?=D1=81=D0=B8=D0=BC=20=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B5?= =?UTF-8?q?=D0=B2=D0=B8=D1=87?= Date: Mon, 19 Feb 2024 00:02:43 +0300 Subject: [PATCH] =?UTF-8?q?#1609.=20=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D1=82=20=D0=B1=D0=B0=D0=B3=20=D1=81=D0=B5?= =?UTF-8?q?=D1=80=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VkNet.Tests/Categories/Polls/GetVotersTest.cs | 35 ++++ .../TestData/Categories/Polls/GetVoters.json | 167 ++++++++++++++++++ .../Category/Async/IPollsCategoryAsync.cs | 7 +- VkNet/Abstractions/Category/IPollsCategory.cs | 114 ++++++++++-- VkNet/Categories/Async/PollsCategoryAsync.cs | 41 ++--- VkNet/Categories/PollsCategory.cs | 64 +++---- VkNet/Model/PollAnswerVoters.cs | 5 +- .../Utils/JsonConverter/UserJsonConverter.cs | 23 +-- .../VkCollectionJsonConverter.cs | 28 +-- 9 files changed, 378 insertions(+), 106 deletions(-) create mode 100644 VkNet.Tests/Categories/Polls/GetVotersTest.cs create mode 100644 VkNet.Tests/TestData/Categories/Polls/GetVoters.json diff --git a/VkNet.Tests/Categories/Polls/GetVotersTest.cs b/VkNet.Tests/Categories/Polls/GetVotersTest.cs new file mode 100644 index 000000000..186bc71bf --- /dev/null +++ b/VkNet.Tests/Categories/Polls/GetVotersTest.cs @@ -0,0 +1,35 @@ +using FluentAssertions; +using VkNet.Model; +using VkNet.Tests.Infrastructure; +using Xunit; + +namespace VkNet.Tests.Categories.Polls; + +public class GetVotersTest : CategoryBaseTest +{ + protected override string Folder => "Polls"; + + [Fact(DisplayName = "Баг 1609. Ошибка при получении голосований")] + public void GetVoters() + { + Url = "https://api.vk.com/method/polls.getVoters"; + + ReadCategoryJsonPath(nameof(GetVoters)); + + var pollsGetVotersParams = new PollsGetVotersParams + { + OwnerId = -145005178, + PollId = 928754744, + AnswersIds = + [ + 2501159906, + 2501159907 + ] + }; + + var pollAnswerVotersList = Api.PollsCategory.GetVoters(pollsGetVotersParams); + + pollAnswerVotersList.Should() + .NotBeEmpty(); + } +} \ No newline at end of file diff --git a/VkNet.Tests/TestData/Categories/Polls/GetVoters.json b/VkNet.Tests/TestData/Categories/Polls/GetVoters.json new file mode 100644 index 000000000..bf8e0dd0b --- /dev/null +++ b/VkNet.Tests/TestData/Categories/Polls/GetVoters.json @@ -0,0 +1,167 @@ +{ + "response": [ + { + "answer_id": 2501159906, + "users": { + "count": 60, + "items": [ + 318635224, + 778883494, + 94201706, + 623350854, + 201488398, + 213530408, + 434528365, + 217467544, + 311704080, + 247818861, + 215639484, + 66934532, + 447883684, + 619432159, + 254058506, + 151870764, + 257425714, + 295098246, + 350472868, + 54817590, + 238305686, + 205425121, + 429253967, + 447844502, + 453500207, + 561997370, + 194918815, + 479187060, + 427521444, + 193032397, + 266230043, + 519014563, + 170520086, + 110523624, + 143000777, + 220348312, + 472684673, + 294508579, + 366986220, + 188342596, + 206575863, + 229296066, + 145777129, + 392153898, + 312602842, + 102304468, + 421298985, + 504854790, + 502775095, + 175214268, + 171952781, + 319997504, + 176266488, + 512806749, + 139403830, + 337996293, + 251943149, + 180493831, + 210044998, + 628795343 + ] + } + }, + { + "answer_id": 2501159907, + "users": { + "count": 87, + "items": [ + 168465174, + 274754699, + 306611621, + 135598472, + 191759022, + 521684931, + 324136148, + 171739713, + 200442287, + 320036357, + 147545346, + 175523367, + 137423888, + 242220494, + 256326397, + 430378781, + 445995892, + 233548445, + 731203998, + 617413957, + 94573753, + 433750147, + 366048147, + 301464586, + 143520524, + 471605790, + 161913415, + 407063258, + 463254275, + 387067154, + 630966769, + 413158803, + 144293813, + 189123462, + 175418693, + 265750836, + 252748506, + 192714066, + 257907920, + 147834724, + 323306622, + 503557478, + 239667963, + 227911949, + 215597196, + 401622393, + 497503979, + 325164033, + 368533700, + 331192219, + 120454286, + 502763971, + 138445466, + 174823688, + 216573589, + 143004393, + 342860417, + 539544387, + 328868093, + 294170655, + 34477394, + 199984489, + 280874358, + 421912776, + 305732877, + 160791608, + 153607802, + 397393793, + 383192662, + 205835602, + 503548267, + 35310705, + 276256203, + 151401740, + 251902560, + 143394843, + 165269567, + 261407405, + 205981670, + 200423347, + 311391398, + 468732163, + 68688528, + 197419808, + 147513930, + 350810139, + 58810322 + ] + } + } + ] +} \ No newline at end of file diff --git a/VkNet/Abstractions/Category/Async/IPollsCategoryAsync.cs b/VkNet/Abstractions/Category/Async/IPollsCategoryAsync.cs index 9354a5e0d..4b3c1a2a7 100644 --- a/VkNet/Abstractions/Category/Async/IPollsCategoryAsync.cs +++ b/VkNet/Abstractions/Category/Async/IPollsCategoryAsync.cs @@ -2,7 +2,6 @@ using System.Threading; using System.Threading.Tasks; using VkNet.Model; -using VkNet.Utils; namespace VkNet.Abstractions; @@ -84,8 +83,8 @@ Task DeleteVoteAsync(PollsDeleteVoteParams @params, /// /// Страница документации ВКонтакте https://vk.com/dev/polls.GetVoters /// - Task> GetVotersAsync(PollsGetVotersParams @params, - CancellationToken token = default); + Task> GetVotersAsync(PollsGetVotersParams @params, + CancellationToken token = default); /// /// Позволяет создавать опросы, которые впоследствии можно прикреплять к записям на @@ -119,7 +118,7 @@ Task CreateAsync(PollsCreateParams @params, /// /// Получает адрес сервера для загрузки фоновой фотографии в опрос. /// - /// Идеентификатор пользователя или сообщества + /// Идентификатор пользователя или сообщества /// Токен отмены операции /// /// Возвращает объект с полем содержащим URL для загрузки фотографии diff --git a/VkNet/Abstractions/Category/IPollsCategory.cs b/VkNet/Abstractions/Category/IPollsCategory.cs index 82bf354ab..ada66706e 100644 --- a/VkNet/Abstractions/Category/IPollsCategory.cs +++ b/VkNet/Abstractions/Category/IPollsCategory.cs @@ -1,36 +1,126 @@ using System.Collections.ObjectModel; using VkNet.Model; -using VkNet.Utils; namespace VkNet.Abstractions; -/// +/// +/// Категория методов для работы с опросами +/// public interface IPollsCategory : IPollsCategoryAsync { - /// + /// + /// Возвращает детальную информацию об опросе по его идентификатору. + /// + /// Параметры + /// + /// Опрос + /// + /// + /// Страница документации ВКонтакте https://vk.com/dev/polls.GetById + /// Poll GetById(PollsGetByIdParams @params); - /// + /// + /// Позволяет редактировать созданные опросы. + /// + /// Параметры + /// + /// Признак успешного редактирования + /// + /// + /// Страница документации ВКонтакте https://vk.com/dev/polls.Edit + /// bool Edit(PollsEditParams @params); - /// + /// + /// Отдает голос текущего пользователя за выбранный вариант ответа в указанном + /// опросе. + /// + /// Параметры + /// + /// 1 — если голос текущего пользователя был отдан за выбранный вариант ответа; + /// 0 — если текущий пользователь уже голосовал в указанном опросе + /// + /// + /// Страница документации ВКонтакте https://vk.com/dev/polls.addVote + /// bool AddVote(PollsAddVoteParams @params); - /// + /// + /// Снимает голос текущего пользователя с выбранного варианта ответа в указанном + /// опросе. + /// + /// Параметры + /// + /// 1 — если голос текущего пользователя был снят с выбранного варианта ответа + /// 0 — если текущий пользователь еще не голосовал в указанном опросе или указан не + /// выбранный им вариант ответа + /// + /// + /// Страница документации ВКонтакте https://vk.com/dev/polls.DeleteVote + /// bool DeleteVote(PollsDeleteVoteParams @params); - /// - VkCollection GetVoters(PollsGetVotersParams @params); + /// + /// Получает список идентификаторов пользователей, которые выбрали определенные + /// варианты ответа в опросе. + /// + /// Параметры + /// + /// Список ответов + /// + /// + /// Страница документации ВКонтакте https://vk.com/dev/polls.GetVoters + /// + ReadOnlyCollection GetVoters(PollsGetVotersParams @params); - /// + /// + /// Позволяет создавать опросы, которые впоследствии можно прикреплять к записям на + /// странице пользователя или + /// сообщества. + /// + /// Параметры + /// + /// В случае успешного создания опроса в качестве результата возвращается объект + /// опроса. + /// + /// + /// Страница документации ВКонтакте https://vk.com/dev/polls.create + /// Poll Create(PollsCreateParams @params); - /// + /// + /// Получает варианты фонового изображения для опросов. + /// + /// + /// Возвращает массив объектов, описывающих фоновое изображение опроса. + /// + /// + /// Страница документации ВКонтакте https://vk.com/dev/polls.getBackgrounds + /// ReadOnlyCollection GetBackgrounds(); - /// + /// + /// Получает адрес сервера для загрузки фоновой фотографии в опрос. + /// + /// Идентификатор пользователя или сообщества + /// + /// Возвращает объект с полем содержащим URL для загрузки фотографии + /// + /// + /// Страница документации ВКонтакте http://vk.com/dev.php?method=polls.getPhotoUploadServer + /// UploadServer GetPhotoUploadServer(long ownerId); - /// + /// + /// Сохраняет фотографию, загруженную в опрос. + /// + /// Параметры + /// + /// В случае успешного сохранения возвращает объект описывающий фотографию + /// + /// + /// Страница документации ВКонтакте http://vk.com/dev/polls.savePhoto + /// SavePhotoResult SavePhoto(SavePhotoParams @params); } \ No newline at end of file diff --git a/VkNet/Categories/Async/PollsCategoryAsync.cs b/VkNet/Categories/Async/PollsCategoryAsync.cs index 1129c6fa3..7010438b0 100644 --- a/VkNet/Categories/Async/PollsCategoryAsync.cs +++ b/VkNet/Categories/Async/PollsCategoryAsync.cs @@ -12,39 +12,34 @@ public partial class PollsCategory { /// public Task GetByIdAsync(PollsGetByIdParams @params, - CancellationToken token = default) => - TypeHelper.TryInvokeMethodAsync(() => - GetById(@params), token); + CancellationToken token = default) => TypeHelper.TryInvokeMethodAsync(() => + GetById(@params), token); /// public Task EditAsync(PollsEditParams @params, - CancellationToken token = default) => - TypeHelper.TryInvokeMethodAsync(() => - Edit(@params), token); + CancellationToken token = default) => TypeHelper.TryInvokeMethodAsync(() => + Edit(@params), token); /// public Task AddVoteAsync(PollsAddVoteParams @params, - CancellationToken token = default) => - TypeHelper.TryInvokeMethodAsync(() => - AddVote(@params), token); + CancellationToken token = default) => TypeHelper.TryInvokeMethodAsync(() => + AddVote(@params), token); /// public Task DeleteVoteAsync(PollsDeleteVoteParams @params, - CancellationToken token = default) => - TypeHelper.TryInvokeMethodAsync(() => - DeleteVote(@params), token); + CancellationToken token = default) => TypeHelper.TryInvokeMethodAsync(() => + DeleteVote(@params), token); /// - public Task> GetVotersAsync(PollsGetVotersParams @params, - CancellationToken token = default) => - TypeHelper.TryInvokeMethodAsync(() => + public Task> GetVotersAsync(PollsGetVotersParams @params, + CancellationToken token = default) => TypeHelper.TryInvokeMethodAsync( + () => GetVoters(@params), token); /// public Task CreateAsync(PollsCreateParams @params, - CancellationToken token = default) => - TypeHelper.TryInvokeMethodAsync(() => - Create(@params), token); + CancellationToken token = default) => TypeHelper.TryInvokeMethodAsync(() => + Create(@params), token); /// public Task> GetBackgroundsAsync(CancellationToken token = default) => @@ -52,13 +47,11 @@ public Task> GetBackgroundsAsync(Cancel /// public Task GetPhotoUploadServerAsync(long ownerId, - CancellationToken token = default) => - TypeHelper.TryInvokeMethodAsync(() => - GetPhotoUploadServer(ownerId), token); + CancellationToken token = default) => TypeHelper.TryInvokeMethodAsync(() => + GetPhotoUploadServer(ownerId), token); /// public Task SavePhotoAsync(SavePhotoParams @params, - CancellationToken token = default) => - TypeHelper.TryInvokeMethodAsync(() => - SavePhoto(@params), token); + CancellationToken token = default) => TypeHelper.TryInvokeMethodAsync(() => + SavePhoto(@params), token); } \ No newline at end of file diff --git a/VkNet/Categories/PollsCategory.cs b/VkNet/Categories/PollsCategory.cs index 9bd14cd02..a638958ef 100644 --- a/VkNet/Categories/PollsCategory.cs +++ b/VkNet/Categories/PollsCategory.cs @@ -93,8 +93,39 @@ public partial class PollsCategory : IPollsCategory }); /// - public VkCollection GetVoters(PollsGetVotersParams @params) + public ReadOnlyCollection GetVoters(PollsGetVotersParams @params) { + return _vk.Call>("polls.getVoters", new() + { + { + "owner_id", @params.OwnerId + }, + { + "is_board", @params.IsBoard + }, + { + "poll_id", @params.PollId + }, + { + "answer_ids", FormatList(answersIds: @params.AnswersIds) + }, + { + "friends_only", @params.FriendsOnly + }, + { + "offset", @params.Offset + }, + { + "count", @params.Count + }, + { + "fields", @params.Fields + }, + { + "name_case", @params.NameCase + } + }); + object FormatList(IList answersIds) { if (answersIds is null) @@ -116,37 +147,6 @@ object FormatList(IList answersIds) return stringBuilder.ToString(); } - - return _vk.Call>("polls.getVoters", new() - { - { - "owner_id", @params.OwnerId - }, - { - "is_board", @params.IsBoard - }, - { - "poll_id", @params.PollId - }, - { - "answer_ids", FormatList(answersIds: @params.AnswersIds) - }, - { - "friends_only", @params.FriendsOnly - }, - { - "offset", @params.Offset - }, - { - "count", @params.Count - }, - { - "fields", @params.Fields - }, - { - "name_case", @params.NameCase - } - }); } /// diff --git a/VkNet/Model/PollAnswerVoters.cs b/VkNet/Model/PollAnswerVoters.cs index 0ffee1eb4..3d59a8b55 100644 --- a/VkNet/Model/PollAnswerVoters.cs +++ b/VkNet/Model/PollAnswerVoters.cs @@ -5,11 +5,9 @@ namespace VkNet.Model; /// -/// Объект для перечисления пользователей, которые выбрали определенные варианты -/// ответа в опросе. +/// Объект для перечисления пользователей, которые выбрали определенные варианты ответа в опросе. /// [Serializable] -//[JsonConverter(typeof(PollAnswerVotersJsonConverter))] public class PollAnswerVoters { /// @@ -27,5 +25,6 @@ public class PollAnswerVoters /// /// Коллекция идентификаторов пользователей, только если Fields = null /// + [JsonProperty("items")] public VkCollection UsersIds { get; set; } } \ No newline at end of file diff --git a/VkNet/Utils/JsonConverter/UserJsonConverter.cs b/VkNet/Utils/JsonConverter/UserJsonConverter.cs index 46aaae379..9d703158b 100644 --- a/VkNet/Utils/JsonConverter/UserJsonConverter.cs +++ b/VkNet/Utils/JsonConverter/UserJsonConverter.cs @@ -9,20 +9,18 @@ namespace VkNet.Utils.JsonConverter; /// -public class UserJsonConverter : Newtonsoft.Json.JsonConverter +public class UserJsonConverter : JsonConverter { /// - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, User value, JsonSerializer serializer) { - var user = (User) value; - var jObj = new JObject { { - "first_name", JToken.FromObject(user.FirstName) + "first_name", JToken.FromObject(value.FirstName) }, { - "last_name", JToken.FromObject(user.LastName) + "last_name", JToken.FromObject(value.LastName) } }; @@ -31,13 +29,21 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s /// /// - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override User ReadJson(JsonReader reader, Type objectType, User existingValue, bool hasExistingValue, JsonSerializer serializer) { if (objectType.IsGenericType) { throw new TypeAccessException(); } + if (reader.TokenType is JsonToken.Integer) + { + return new() + { + Id = (long) reader.Value + }; + } + var obj = JObject.Load(reader); var responseJToken = obj["response"] ?? obj; var response = new VkResponse(responseJToken); @@ -282,7 +288,4 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist return user; } - - /// - public override bool CanConvert(Type objectType) => throw new NotImplementedException(); } \ No newline at end of file diff --git a/VkNet/Utils/JsonConverter/VkCollectionJsonConverter.cs b/VkNet/Utils/JsonConverter/VkCollectionJsonConverter.cs index 6a2b5078c..c5636d638 100644 --- a/VkNet/Utils/JsonConverter/VkCollectionJsonConverter.cs +++ b/VkNet/Utils/JsonConverter/VkCollectionJsonConverter.cs @@ -12,6 +12,8 @@ namespace VkNet.Utils.JsonConverter; /// public class VkCollectionJsonConverter : Newtonsoft.Json.JsonConverter { + private const string ResponsePropertyKey = "response"; + /// /// Инициализация /// @@ -28,20 +30,14 @@ public VkCollectionJsonConverter() : this("items") /// /// Количество /// - private static string CountField => "count"; + private static string CountPropertyKey => "count"; /// /// Поле с коллекцией данных /// private string CollectionField { get; } - /// - /// Сериализация объекта в Json - /// - /// Json writer - /// Значение - /// Сериализатор - /// + /// public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var vkCollectionType = value.GetType(); @@ -71,17 +67,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s serializer.Serialize(writer, vkCollectionSurrogate); } - /// - /// Преобразование JSON в VkCollection - /// - /// Json reader - /// Тип объекта - /// Существующее значение - /// Serializer - /// - /// Объект - /// - /// + /// public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (!objectType.IsGenericType) @@ -103,9 +89,9 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist var vkCollection = typeof(VkCollection<>).MakeGenericType(keyType); var obj = JObject.Load(reader); - var response = obj["response"] ?? obj; + var response = obj[ResponsePropertyKey] ?? obj; - var totalCount = response[CountField] + var totalCount = response[CountPropertyKey] .Value(); var converter =