From f70a87cc75f3260be74bfa3be5362ed24a415101 Mon Sep 17 00:00:00 2001 From: WWWNASTY Date: Mon, 5 Aug 2019 16:30:22 +0300 Subject: [PATCH 1/2] Done architecture of project. --- .../BusinessLogicLayer.Abstraction.csproj | 12 +++ .../Interfaces/ICurrencyProvider.cs | 11 +++ .../Interfaces/IResponseClient.cs | 10 +++ .../BusinessLogicLayer.Implementation.csproj | 20 +++++ .../Services/CbrCurrencyProviderService.cs | 44 +++++++++++ .../Services/VkResponseClientService.cs | 76 +++++++++++++++++++ .../BusinessLogicLayer.Objects.csproj | 11 +++ .../Dtos/CbrResponse.cs | 18 +++++ .../Dtos}/Currency.cs | 11 +-- .../Dtos}/Valute.cs | 8 +- ConsoleAppCourseValut.sln | 57 ++++++++++++-- ConsoleAppCourseValut/CbrResponse.cs | 16 ---- .../ConsoleAppCourseCurrency.csproj | 17 ----- .../DataAccessLayer.Abstraction.csproj | 11 +++ .../DataAccessLayer.Implementation.csproj | 11 +++ .../DataAccessLayer.Models.csproj | 16 ++++ .../Entities/CurrencyDataResponse.cs | 18 +++++ DataAccessLayer.Models/Entities/Updates.cs | 29 +++++++ DataAccessLayer.Models/Enums/SourceType.cs | 9 +++ .../MapperProfiles/MapperProfiles.cs | 33 ++++++++ .../Controllers/CallbackController.cs" | 54 ++----------- "Parser\320\241urrency/Models/Updates.cs" | 33 -------- .../Parser\320\241urrency.csproj" | 10 ++- "Parser\320\241urrency/Startup.cs" | 23 +++--- 24 files changed, 414 insertions(+), 144 deletions(-) create mode 100644 BusinessLogicLayer.Abstraction/BusinessLogicLayer.Abstraction.csproj create mode 100644 BusinessLogicLayer.Abstraction/Interfaces/ICurrencyProvider.cs create mode 100644 BusinessLogicLayer.Abstraction/Interfaces/IResponseClient.cs create mode 100644 BusinessLogicLayer.Implementation/BusinessLogicLayer.Implementation.csproj create mode 100644 BusinessLogicLayer.Implementation/Services/CbrCurrencyProviderService.cs create mode 100644 BusinessLogicLayer.Implementation/Services/VkResponseClientService.cs create mode 100644 BusinessLogicLayer.Objects/BusinessLogicLayer.Objects.csproj create mode 100644 BusinessLogicLayer.Objects/Dtos/CbrResponse.cs rename {ConsoleAppCourseValut => BusinessLogicLayer.Objects/Dtos}/Currency.cs (63%) rename {ConsoleAppCourseValut => BusinessLogicLayer.Objects/Dtos}/Valute.cs (60%) delete mode 100644 ConsoleAppCourseValut/CbrResponse.cs delete mode 100644 ConsoleAppCourseValut/ConsoleAppCourseCurrency.csproj create mode 100644 DataAccessLayer.Abstraction/DataAccessLayer.Abstraction.csproj create mode 100644 DataAccessLayer.Implementation/DataAccessLayer.Implementation.csproj create mode 100644 DataAccessLayer.Models/DataAccessLayer.Models.csproj create mode 100644 DataAccessLayer.Models/Entities/CurrencyDataResponse.cs create mode 100644 DataAccessLayer.Models/Entities/Updates.cs create mode 100644 DataAccessLayer.Models/Enums/SourceType.cs create mode 100644 DataAccessLayer.Models/MapperProfiles/MapperProfiles.cs delete mode 100644 "Parser\320\241urrency/Models/Updates.cs" diff --git a/BusinessLogicLayer.Abstraction/BusinessLogicLayer.Abstraction.csproj b/BusinessLogicLayer.Abstraction/BusinessLogicLayer.Abstraction.csproj new file mode 100644 index 0000000..c34e107 --- /dev/null +++ b/BusinessLogicLayer.Abstraction/BusinessLogicLayer.Abstraction.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp2.2 + Abstraction + + + + + + + diff --git a/BusinessLogicLayer.Abstraction/Interfaces/ICurrencyProvider.cs b/BusinessLogicLayer.Abstraction/Interfaces/ICurrencyProvider.cs new file mode 100644 index 0000000..425df5b --- /dev/null +++ b/BusinessLogicLayer.Abstraction/Interfaces/ICurrencyProvider.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using DataAccessLayer.Models.Entities; + +namespace Abstraction.Interfaces +{ + public interface ICurrencyProvider + { + Task> GetAnswerAsync(); + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Abstraction/Interfaces/IResponseClient.cs b/BusinessLogicLayer.Abstraction/Interfaces/IResponseClient.cs new file mode 100644 index 0000000..dcc2c70 --- /dev/null +++ b/BusinessLogicLayer.Abstraction/Interfaces/IResponseClient.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using DataAccessLayer.Models.Entities; + +namespace Abstraction.Interfaces +{ + public interface IResponseClient + { + Task SendMessage(Updates updates); + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Implementation/BusinessLogicLayer.Implementation.csproj b/BusinessLogicLayer.Implementation/BusinessLogicLayer.Implementation.csproj new file mode 100644 index 0000000..679045a --- /dev/null +++ b/BusinessLogicLayer.Implementation/BusinessLogicLayer.Implementation.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + + + + diff --git a/BusinessLogicLayer.Implementation/Services/CbrCurrencyProviderService.cs b/BusinessLogicLayer.Implementation/Services/CbrCurrencyProviderService.cs new file mode 100644 index 0000000..71bd211 --- /dev/null +++ b/BusinessLogicLayer.Implementation/Services/CbrCurrencyProviderService.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Abstraction.Interfaces; +using AutoMapper; +using BusinessLogicLayer.Objects.Dtos; +using DataAccessLayer.Models.Entities; +using Flurl.Http; + +namespace BusinessLogicLayer.Implementation.Services +{ + public class CbrCurrencyProviderService : ICurrencyProvider + { + private readonly IMapper _mapper; + + public CbrCurrencyProviderService(IMapper mapper) + { + _mapper = mapper; + } + + public async Task> GetAnswerAsync() + { + CbrResponse cbrResponse = await "https://www.cbr-xml-daily.ru/daily_json.js".GetJsonAsync(); + + Currency[] currencies = + { + cbrResponse.Currencies.EUR, + cbrResponse.Currencies.UAH, + cbrResponse.Currencies.USD + }; +// +// return currencies.Select(currency => new CurrencyDataResponse +// { +// DataSource = SourceType.Official, +// Date = cbrResponse.Date, +// Code = currency.CharCode, +// Name = currency.Name, +// Value = currency.Value / currency.Nominal +// }) +// .ToList(); + return _mapper.Map>(currencies, + options => options.Items["Date"] = cbrResponse.Date); + } + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Implementation/Services/VkResponseClientService.cs b/BusinessLogicLayer.Implementation/Services/VkResponseClientService.cs new file mode 100644 index 0000000..614068f --- /dev/null +++ b/BusinessLogicLayer.Implementation/Services/VkResponseClientService.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Abstraction.Interfaces; +using DataAccessLayer.Models.Entities; +using Microsoft.Extensions.Configuration; +using VkNet.Abstractions; +using VkNet.Model; +using VkNet.Model.RequestParams; +using VkNet.Utils; + +namespace BusinessLogicLayer.Implementation.Services +{ + public class VkResponseClientService : IResponseClient + { + private readonly IConfiguration _configuration; + + private readonly IVkApi _vkApi; + + private readonly ICurrencyProvider _currencyProviderService; + + public VkResponseClientService(IVkApi vkApi, IConfiguration configuration, + ICurrencyProvider currencyProviderService) + { + _vkApi = vkApi; + _configuration = configuration; + _currencyProviderService = currencyProviderService; + } + + public async Task SendMessage(Updates updates) + { + // Проверяем, что находится в поле "type" + + switch (updates.Type) + { + // Если это уведомление для подтверждения адреса + case "confirmation": + // Отправляем строку для подтверждения + return _configuration["Config:Confirmation"]; + + case "message_new": + { + // Десериализация + var msg = Message.FromJson(new VkResponse(updates.Object)); + + // Отправим в ответ полученный от пользователя текст + IEnumerable currencies = await _currencyProviderService.GetAnswerAsync(); + + StringBuilder message = new StringBuilder(String.Empty); + + foreach (CurrencyDataResponse currencyData in currencies) + { + message.Append($"{currencyData.Name}: {currencyData.Value} ₽\n"); + } + + if (msg.PeerId != null) + { + _vkApi.Messages.Send(new MessagesSendParams + { + RandomId = new DateTime().Millisecond, + + PeerId = msg.PeerId.Value, + + Message = message.ToString() + }); + } + + break; + } + } + + return string.Empty; + } + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Objects/BusinessLogicLayer.Objects.csproj b/BusinessLogicLayer.Objects/BusinessLogicLayer.Objects.csproj new file mode 100644 index 0000000..ef31712 --- /dev/null +++ b/BusinessLogicLayer.Objects/BusinessLogicLayer.Objects.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp2.2 + + + + + + + diff --git a/BusinessLogicLayer.Objects/Dtos/CbrResponse.cs b/BusinessLogicLayer.Objects/Dtos/CbrResponse.cs new file mode 100644 index 0000000..b418f2a --- /dev/null +++ b/BusinessLogicLayer.Objects/Dtos/CbrResponse.cs @@ -0,0 +1,18 @@ +using System; +using Newtonsoft.Json; + +namespace BusinessLogicLayer.Objects.Dtos +{ + public class CbrResponse + { + public DateTime Date { get; set; } + + public DateTime PreviousDate { get; set; } + + /// + /// Валюты + /// + [JsonProperty("Valute")] + public Valute Currencies { get; set; } + } +} \ No newline at end of file diff --git a/ConsoleAppCourseValut/Currency.cs b/BusinessLogicLayer.Objects/Dtos/Currency.cs similarity index 63% rename from ConsoleAppCourseValut/Currency.cs rename to BusinessLogicLayer.Objects/Dtos/Currency.cs index 077f478..f40d223 100644 --- a/ConsoleAppCourseValut/Currency.cs +++ b/BusinessLogicLayer.Objects/Dtos/Currency.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ConsoleAppCourseCurrency +namespace BusinessLogicLayer.Objects.Dtos { public class Currency { @@ -20,7 +16,6 @@ public class Currency public double Previous { get; set; } - public override string ToString() => $"Курс {Name}: {Value / Nominal} ₽"; + // public override string ToString() => $"Курс {Name}: {Value / Nominal} ₽"; } - -} +} \ No newline at end of file diff --git a/ConsoleAppCourseValut/Valute.cs b/BusinessLogicLayer.Objects/Dtos/Valute.cs similarity index 60% rename from ConsoleAppCourseValut/Valute.cs rename to BusinessLogicLayer.Objects/Dtos/Valute.cs index 91d8e0d..f3eef72 100644 --- a/ConsoleAppCourseValut/Valute.cs +++ b/BusinessLogicLayer.Objects/Dtos/Valute.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ConsoleAppCourseCurrency +namespace BusinessLogicLayer.Objects.Dtos { public class Valute { @@ -12,4 +8,4 @@ public class Valute public Currency EUR { get; set; } } -} +} \ No newline at end of file diff --git a/ConsoleAppCourseValut.sln b/ConsoleAppCourseValut.sln index 3e29777..dc182c8 100644 --- a/ConsoleAppCourseValut.sln +++ b/ConsoleAppCourseValut.sln @@ -3,24 +3,60 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28803.452 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleAppCourseCurrency", "ConsoleAppCourseValut\ConsoleAppCourseCurrency.csproj", "{08DA201E-DC1C-4DF9-9A71-55E32B3D0483}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParserСurrency", "ParserСurrency\ParserСurrency.csproj", "{10A225C1-5EAA-40D0-A7E9-48774C1DBFE5}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BusinessLogicLayer", "BusinessLogicLayer", "{723CCBD0-6040-4095-B01B-85F234E7B03D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLogicLayer.Abstraction", "BusinessLogicLayer.Abstraction\BusinessLogicLayer.Abstraction.csproj", "{50731D10-9144-4FFC-A0AF-6FCB1C8693B4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLogicLayer.Implementation", "BusinessLogicLayer.Implementation\BusinessLogicLayer.Implementation.csproj", "{827E6D5F-C334-4954-BD6D-00AB758C5555}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLogicLayer.Objects", "BusinessLogicLayer.Objects\BusinessLogicLayer.Objects.csproj", "{658F15E6-8BB1-4CEE-8F3A-52AEB335C0A8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataAccessLayer", "DataAccessLayer", "{5DEA6744-5DC4-4C77-903A-1A0E32A347F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAccessLayer.Abstraction", "DataAccessLayer.Abstraction\DataAccessLayer.Abstraction.csproj", "{31C8B387-847F-499B-9B1B-F3B87FF65AC3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAccessLayer.Implementation", "DataAccessLayer.Implementation\DataAccessLayer.Implementation.csproj", "{0A6427A3-7CC5-4089-930A-485D1CED818A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAccessLayer.Models", "DataAccessLayer.Models\DataAccessLayer.Models.csproj", "{EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PresentationLayer", "PresentationLayer", "{1800E55A-1231-4FB8-8B06-96D0A7FAEB0C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {08DA201E-DC1C-4DF9-9A71-55E32B3D0483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08DA201E-DC1C-4DF9-9A71-55E32B3D0483}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08DA201E-DC1C-4DF9-9A71-55E32B3D0483}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08DA201E-DC1C-4DF9-9A71-55E32B3D0483}.Release|Any CPU.Build.0 = Release|Any CPU {10A225C1-5EAA-40D0-A7E9-48774C1DBFE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10A225C1-5EAA-40D0-A7E9-48774C1DBFE5}.Debug|Any CPU.Build.0 = Debug|Any CPU {10A225C1-5EAA-40D0-A7E9-48774C1DBFE5}.Release|Any CPU.ActiveCfg = Release|Any CPU {10A225C1-5EAA-40D0-A7E9-48774C1DBFE5}.Release|Any CPU.Build.0 = Release|Any CPU + {50731D10-9144-4FFC-A0AF-6FCB1C8693B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50731D10-9144-4FFC-A0AF-6FCB1C8693B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50731D10-9144-4FFC-A0AF-6FCB1C8693B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50731D10-9144-4FFC-A0AF-6FCB1C8693B4}.Release|Any CPU.Build.0 = Release|Any CPU + {827E6D5F-C334-4954-BD6D-00AB758C5555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {827E6D5F-C334-4954-BD6D-00AB758C5555}.Debug|Any CPU.Build.0 = Debug|Any CPU + {827E6D5F-C334-4954-BD6D-00AB758C5555}.Release|Any CPU.ActiveCfg = Release|Any CPU + {827E6D5F-C334-4954-BD6D-00AB758C5555}.Release|Any CPU.Build.0 = Release|Any CPU + {658F15E6-8BB1-4CEE-8F3A-52AEB335C0A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {658F15E6-8BB1-4CEE-8F3A-52AEB335C0A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {658F15E6-8BB1-4CEE-8F3A-52AEB335C0A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {658F15E6-8BB1-4CEE-8F3A-52AEB335C0A8}.Release|Any CPU.Build.0 = Release|Any CPU + {31C8B387-847F-499B-9B1B-F3B87FF65AC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31C8B387-847F-499B-9B1B-F3B87FF65AC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31C8B387-847F-499B-9B1B-F3B87FF65AC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31C8B387-847F-499B-9B1B-F3B87FF65AC3}.Release|Any CPU.Build.0 = Release|Any CPU + {0A6427A3-7CC5-4089-930A-485D1CED818A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A6427A3-7CC5-4089-930A-485D1CED818A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A6427A3-7CC5-4089-930A-485D1CED818A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A6427A3-7CC5-4089-930A-485D1CED818A}.Release|Any CPU.Build.0 = Release|Any CPU + {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -28,4 +64,13 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AEC23FCF-85CF-43B0-B3BA-D4FFD2B8D264} EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50731D10-9144-4FFC-A0AF-6FCB1C8693B4} = {723CCBD0-6040-4095-B01B-85F234E7B03D} + {827E6D5F-C334-4954-BD6D-00AB758C5555} = {723CCBD0-6040-4095-B01B-85F234E7B03D} + {658F15E6-8BB1-4CEE-8F3A-52AEB335C0A8} = {723CCBD0-6040-4095-B01B-85F234E7B03D} + {31C8B387-847F-499B-9B1B-F3B87FF65AC3} = {5DEA6744-5DC4-4C77-903A-1A0E32A347F6} + {0A6427A3-7CC5-4089-930A-485D1CED818A} = {5DEA6744-5DC4-4C77-903A-1A0E32A347F6} + {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9} = {5DEA6744-5DC4-4C77-903A-1A0E32A347F6} + {10A225C1-5EAA-40D0-A7E9-48774C1DBFE5} = {1800E55A-1231-4FB8-8B06-96D0A7FAEB0C} + EndGlobalSection EndGlobal diff --git a/ConsoleAppCourseValut/CbrResponse.cs b/ConsoleAppCourseValut/CbrResponse.cs deleted file mode 100644 index cea76f3..0000000 --- a/ConsoleAppCourseValut/CbrResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ConsoleAppCourseCurrency -{ - public class CbrResponse - { - public DateTime Date { get; set; } - - public DateTime PreviousDate { get; set; } - - public Valute Valute { get; set; } - - } -} diff --git a/ConsoleAppCourseValut/ConsoleAppCourseCurrency.csproj b/ConsoleAppCourseValut/ConsoleAppCourseCurrency.csproj deleted file mode 100644 index 94b81ab..0000000 --- a/ConsoleAppCourseValut/ConsoleAppCourseCurrency.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - Library - netcoreapp2.2 - latest - - - - - - - - - - - diff --git a/DataAccessLayer.Abstraction/DataAccessLayer.Abstraction.csproj b/DataAccessLayer.Abstraction/DataAccessLayer.Abstraction.csproj new file mode 100644 index 0000000..8b21c23 --- /dev/null +++ b/DataAccessLayer.Abstraction/DataAccessLayer.Abstraction.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp2.2 + + + + + + + diff --git a/DataAccessLayer.Implementation/DataAccessLayer.Implementation.csproj b/DataAccessLayer.Implementation/DataAccessLayer.Implementation.csproj new file mode 100644 index 0000000..fbb67b0 --- /dev/null +++ b/DataAccessLayer.Implementation/DataAccessLayer.Implementation.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp2.2 + + + + + + + diff --git a/DataAccessLayer.Models/DataAccessLayer.Models.csproj b/DataAccessLayer.Models/DataAccessLayer.Models.csproj new file mode 100644 index 0000000..72b0981 --- /dev/null +++ b/DataAccessLayer.Models/DataAccessLayer.Models.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + diff --git a/DataAccessLayer.Models/Entities/CurrencyDataResponse.cs b/DataAccessLayer.Models/Entities/CurrencyDataResponse.cs new file mode 100644 index 0000000..bd7a8f5 --- /dev/null +++ b/DataAccessLayer.Models/Entities/CurrencyDataResponse.cs @@ -0,0 +1,18 @@ +using System; +using DataAccessLayer.Models.Enums; + +namespace DataAccessLayer.Models.Entities +{ + public class CurrencyDataResponse + { + public SourceType DataSource { get; set; } + + public string Code { get; set; } + + public string Name { get; set; } + + public double Value { get; set; } + + public DateTime Date { get; set; } + } +} \ No newline at end of file diff --git a/DataAccessLayer.Models/Entities/Updates.cs b/DataAccessLayer.Models/Entities/Updates.cs new file mode 100644 index 0000000..b45e2a5 --- /dev/null +++ b/DataAccessLayer.Models/Entities/Updates.cs @@ -0,0 +1,29 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace DataAccessLayer.Models.Entities +{ + [Serializable] + public class Updates + { + /// + /// Тип события + /// + [JsonProperty("type")] + public string Type { get; set; } + + /// + /// Объект, инициировавший событие + /// Структура объекта зависит от типа уведомления + /// + [JsonProperty("object")] + public JObject Object { get; set; } + + /// + /// ID сообщества, в котором произошло событие + /// + [JsonProperty("group_id")] + public long GroupId { get; set; } + } +} \ No newline at end of file diff --git a/DataAccessLayer.Models/Enums/SourceType.cs b/DataAccessLayer.Models/Enums/SourceType.cs new file mode 100644 index 0000000..0fe40c2 --- /dev/null +++ b/DataAccessLayer.Models/Enums/SourceType.cs @@ -0,0 +1,9 @@ +namespace DataAccessLayer.Models.Enums +{ + public enum SourceType + { + Exchange, + + Official + } +} \ No newline at end of file diff --git a/DataAccessLayer.Models/MapperProfiles/MapperProfiles.cs b/DataAccessLayer.Models/MapperProfiles/MapperProfiles.cs new file mode 100644 index 0000000..adb1fd1 --- /dev/null +++ b/DataAccessLayer.Models/MapperProfiles/MapperProfiles.cs @@ -0,0 +1,33 @@ +using System; +using AutoMapper; +using BusinessLogicLayer.Objects.Dtos; +using DataAccessLayer.Models.Entities; +using DataAccessLayer.Models.Enums; + +namespace DataAccessLayer.Models.MapperProfiles +{ + public class MapperProfiles : Profile + { + public MapperProfiles() + { + //opt => opt.MapFrom(dto => dto.SubCategory.Id) + //cbr resp в мою х у й н ю (отправка пльзователю ответа) + CreateMap() + .ForMember(dest => dest.Date, options => options + .MapFrom()) + .ForMember(dest => dest.DataSource, options => options + .MapFrom(dto => SourceType.Official)) + .ForMember(dest => dest.Code, options => options + .MapFrom(dto => dto.CharCode)) + .ForMember(dest => dest.Value, options => options + .MapFrom(dto => dto.Value / dto.Nominal)); + } + } + + public class DateResolver : IValueResolver + { + public DateTime Resolve(Currency source, CurrencyDataResponse destination, DateTime destMember, + ResolutionContext context) => + context.Items["Date"] as DateTime? ?? DateTime.Today; + } +} \ No newline at end of file diff --git "a/Parser\320\241urrency/Controllers/CallbackController.cs" "b/Parser\320\241urrency/Controllers/CallbackController.cs" index 8e2f691..c51f71e 100644 --- "a/Parser\320\241urrency/Controllers/CallbackController.cs" +++ "b/Parser\320\241urrency/Controllers/CallbackController.cs" @@ -1,20 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; +using Abstraction.Interfaces; +using DataAccessLayer.Models.Entities; using Microsoft.AspNetCore.Mvc; -using VkNet.Abstractions; -using VkNet.Model; -using VkNet.Model.RequestParams; -using VkNet.Utils; -using ParserСurrency.Models; -using Flurl.Http; -using ConsoleAppCourseCurrency; namespace ParserСurrency.Controllers { - [Route("api/[controller]")] [ApiController] public class CallbackController : ControllerBase @@ -22,47 +11,20 @@ public class CallbackController : ControllerBase /// /// Конфигурация приложения /// - private readonly IConfiguration _configuration; + private readonly IResponseClient _vkResponseClientService; - private readonly IVkApi _vkApi; - - public CallbackController(IVkApi vkApi, IConfiguration configuration) + public CallbackController(IResponseClient vkResponseClientService) { - _vkApi = vkApi; - _configuration = configuration; + _vkResponseClientService = vkResponseClientService; } [HttpPost] - public async Task CallbackAsync([FromBody] Updates updates) + public IActionResult CallbackAsync(Updates updates) { - // Проверяем, что находится в поле "type" - switch (updates.Type) - { - // Если это уведомление для подтверждения адреса - case "confirmation": - // Отправляем строку для подтверждения - return Ok(_configuration["Config:Confirmation"]); - - case "message_new": - { - // Десериализация - var msg = Message.FromJson(new VkResponse(updates.Object)); + var response = _vkResponseClientService.SendMessage(updates); - CbrResponse cbrResponse = await "https://www.cbr-xml-daily.ru/daily_json.js".GetJsonAsync(); - var currencies = cbrResponse.Valute; - // Отправим в ответ полученный от пользователя текст - _vkApi.Messages.Send(new MessagesSendParams - { - RandomId = new DateTime().Millisecond, - PeerId = msg.PeerId.Value, - Message = $"{currencies.USD}\n{currencies.EUR}\n{currencies.UAH}" - }); - break; - } - } // Возвращаем "ok" серверу Callback API - return Ok("ok"); + return Ok(response); } - } } \ No newline at end of file diff --git "a/Parser\320\241urrency/Models/Updates.cs" "b/Parser\320\241urrency/Models/Updates.cs" deleted file mode 100644 index a16247c..0000000 --- "a/Parser\320\241urrency/Models/Updates.cs" +++ /dev/null @@ -1,33 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ParserСurrency.Models -{ - [Serializable] - public class Updates - { - /// - /// Тип события - /// - [JsonProperty("type")] - public string Type { get; set; } - - /// - /// Объект, инициировавший событие - /// Структура объекта зависит от типа уведомления - /// - [JsonProperty("object")] - public JObject Object { get; set; } - - /// - /// ID сообщества, в котором произошло событие - /// - [JsonProperty("group_id")] - public long GroupId { get; set; } - } - -} diff --git "a/Parser\320\241urrency/Parser\320\241urrency.csproj" "b/Parser\320\241urrency/Parser\320\241urrency.csproj" index 6147f1d..93737ab 100644 --- "a/Parser\320\241urrency/Parser\320\241urrency.csproj" +++ "b/Parser\320\241urrency/Parser\320\241urrency.csproj" @@ -1,4 +1,4 @@ - + netcoreapp2.2 @@ -6,13 +6,19 @@ + - + + + + + + diff --git "a/Parser\320\241urrency/Startup.cs" "b/Parser\320\241urrency/Startup.cs" index 7eacdff..fa975ca 100644 --- "a/Parser\320\241urrency/Startup.cs" +++ "b/Parser\320\241urrency/Startup.cs" @@ -1,15 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using Abstraction.Interfaces; +using AutoMapper; +using BusinessLogicLayer.Implementation.Services; +using DataAccessLayer.Models.MapperProfiles; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using VkNet; using VkNet.Abstractions; using VkNet.Model; @@ -30,7 +27,8 @@ public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); - services.AddSingleton(sp => { + services.AddSingleton(sp => + { var api = new VkApi(); api.Authorize(new ApiAuthParams { @@ -38,8 +36,13 @@ public void ConfigureServices(IServiceCollection services) }); return api; }); + + services.AddSingleton(); + + services.AddSingleton(); + + services.AddAutoMapper(options => options.AddProfile()); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -59,4 +62,4 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) app.UseMvc(); } } -} +} \ No newline at end of file From 85face55e712c18ee32cb3d622585c45e7c82f84 Mon Sep 17 00:00:00 2001 From: WWWNASTY Date: Sat, 17 Aug 2019 00:25:29 +0300 Subject: [PATCH 2/2] Added currate service, refactoring. --- .../Interfaces/ICurrencyProvider.cs | 2 +- .../Interfaces/IOfficialSource.cs | 6 + .../Interfaces/IResponseClient.cs | 2 +- .../Interfaces/IUnofficialSource.cs | 6 + ...rService.cs => CbrExchangeRateProvider.cs} | 21 ++- .../CurrateCurrencyProviderService.cs | 58 ++++++++ .../Services/VkResponseClientService.cs | 34 ++--- .../Constants/CbrConstants.cs | 7 + .../Constants/VkMessagesTypes.cs | 9 ++ .../Dtos/{Valute.cs => Cbr/CbrCurrencies.cs} | 4 +- .../Dtos/{ => Cbr}/CbrResponse.cs | 4 +- .../Dtos/{ => Cbr}/Currency.cs | 4 +- .../Dtos/Currate/CurrateResponse.cs | 15 +++ .../Dtos/Currate/Currencies.cs | 9 ++ .../BusinessLogicLayer.Tests.csproj | 20 +++ .../CbrExchangeRateProviderTests.cs | 126 ++++++++++++++++++ .../JsonSerializedComparer.cs | 21 +++ .../VkResponseClientServiceTests.cs | 117 ++++++++++++++++ ConsoleAppCourseValut.sln | 7 + ...ataResponse.cs => CurrencyExchangeRate.cs} | 2 +- DataAccessLayer.Models/Entities/Updates.cs | 2 - DataAccessLayer.Models/Enums/SourceType.cs | 1 + .../MapperProfiles/DateResolver.cs | 15 +++ ...files.cs => ExchangeRateMappingProfile.cs} | 18 +-- .../Controllers/CallbackController.cs" | 2 +- "Parser\320\241urrency/Startup.cs" | 6 +- "Parser\320\241urrency/appsettings.json" | 12 +- 27 files changed, 470 insertions(+), 60 deletions(-) create mode 100644 BusinessLogicLayer.Abstraction/Interfaces/IOfficialSource.cs create mode 100644 BusinessLogicLayer.Abstraction/Interfaces/IUnofficialSource.cs rename BusinessLogicLayer.Implementation/Services/{CbrCurrencyProviderService.cs => CbrExchangeRateProvider.cs} (59%) create mode 100644 BusinessLogicLayer.Implementation/Services/CurrateCurrencyProviderService.cs create mode 100644 BusinessLogicLayer.Objects/Constants/CbrConstants.cs create mode 100644 BusinessLogicLayer.Objects/Constants/VkMessagesTypes.cs rename BusinessLogicLayer.Objects/Dtos/{Valute.cs => Cbr/CbrCurrencies.cs} (64%) rename BusinessLogicLayer.Objects/Dtos/{ => Cbr}/CbrResponse.cs (73%) rename BusinessLogicLayer.Objects/Dtos/{ => Cbr}/Currency.cs (71%) create mode 100644 BusinessLogicLayer.Objects/Dtos/Currate/CurrateResponse.cs create mode 100644 BusinessLogicLayer.Objects/Dtos/Currate/Currencies.cs create mode 100644 BusinessLogicLayer.Tests/BusinessLogicLayer.Tests.csproj create mode 100644 BusinessLogicLayer.Tests/CbrExchangeRateProviderTests.cs create mode 100644 BusinessLogicLayer.Tests/JsonSerializedComparer.cs create mode 100644 BusinessLogicLayer.Tests/VkResponseClientServiceTests.cs rename DataAccessLayer.Models/Entities/{CurrencyDataResponse.cs => CurrencyExchangeRate.cs} (89%) create mode 100644 DataAccessLayer.Models/MapperProfiles/DateResolver.cs rename DataAccessLayer.Models/MapperProfiles/{MapperProfiles.cs => ExchangeRateMappingProfile.cs} (50%) diff --git a/BusinessLogicLayer.Abstraction/Interfaces/ICurrencyProvider.cs b/BusinessLogicLayer.Abstraction/Interfaces/ICurrencyProvider.cs index 425df5b..52d6cf3 100644 --- a/BusinessLogicLayer.Abstraction/Interfaces/ICurrencyProvider.cs +++ b/BusinessLogicLayer.Abstraction/Interfaces/ICurrencyProvider.cs @@ -6,6 +6,6 @@ namespace Abstraction.Interfaces { public interface ICurrencyProvider { - Task> GetAnswerAsync(); + Task> GetExchangeRateAsync(); } } \ No newline at end of file diff --git a/BusinessLogicLayer.Abstraction/Interfaces/IOfficialSource.cs b/BusinessLogicLayer.Abstraction/Interfaces/IOfficialSource.cs new file mode 100644 index 0000000..a06a83e --- /dev/null +++ b/BusinessLogicLayer.Abstraction/Interfaces/IOfficialSource.cs @@ -0,0 +1,6 @@ +namespace Abstraction.Interfaces +{ + public interface IOfficialSource : ICurrencyProvider + { + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Abstraction/Interfaces/IResponseClient.cs b/BusinessLogicLayer.Abstraction/Interfaces/IResponseClient.cs index dcc2c70..133e25b 100644 --- a/BusinessLogicLayer.Abstraction/Interfaces/IResponseClient.cs +++ b/BusinessLogicLayer.Abstraction/Interfaces/IResponseClient.cs @@ -5,6 +5,6 @@ namespace Abstraction.Interfaces { public interface IResponseClient { - Task SendMessage(Updates updates); + Task SendResponse(Updates updates); } } \ No newline at end of file diff --git a/BusinessLogicLayer.Abstraction/Interfaces/IUnofficialSource.cs b/BusinessLogicLayer.Abstraction/Interfaces/IUnofficialSource.cs new file mode 100644 index 0000000..daa1b93 --- /dev/null +++ b/BusinessLogicLayer.Abstraction/Interfaces/IUnofficialSource.cs @@ -0,0 +1,6 @@ +namespace Abstraction.Interfaces +{ + public interface IUnofficialSource : ICurrencyProvider + { + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Implementation/Services/CbrCurrencyProviderService.cs b/BusinessLogicLayer.Implementation/Services/CbrExchangeRateProvider.cs similarity index 59% rename from BusinessLogicLayer.Implementation/Services/CbrCurrencyProviderService.cs rename to BusinessLogicLayer.Implementation/Services/CbrExchangeRateProvider.cs index 71bd211..8c05740 100644 --- a/BusinessLogicLayer.Implementation/Services/CbrCurrencyProviderService.cs +++ b/BusinessLogicLayer.Implementation/Services/CbrExchangeRateProvider.cs @@ -3,23 +3,31 @@ using Abstraction.Interfaces; using AutoMapper; using BusinessLogicLayer.Objects.Dtos; +using BusinessLogicLayer.Objects.Dtos.Cbr; using DataAccessLayer.Models.Entities; using Flurl.Http; +using Microsoft.Extensions.Configuration; namespace BusinessLogicLayer.Implementation.Services { - public class CbrCurrencyProviderService : ICurrencyProvider + public class CbrExchangeRateProvider : IOfficialSource { private readonly IMapper _mapper; - public CbrCurrencyProviderService(IMapper mapper) + private readonly IConfiguration _configuration; + + public CbrExchangeRateProvider(IMapper mapper, IConfiguration configuration) { _mapper = mapper; + + _configuration = configuration; } - public async Task> GetAnswerAsync() + + public async Task> GetExchangeRateAsync() { - CbrResponse cbrResponse = await "https://www.cbr-xml-daily.ru/daily_json.js".GetJsonAsync(); + CbrResponse cbrResponse = await _configuration["Config:LinqCbr"] + .GetJsonAsync(); Currency[] currencies = { @@ -37,8 +45,9 @@ public async Task> GetAnswerAsync() // Value = currency.Value / currency.Nominal // }) // .ToList(); - return _mapper.Map>(currencies, - options => options.Items["Date"] = cbrResponse.Date); + + return _mapper.Map>(currencies, + options => options.Items[CbrConstants.Date] = cbrResponse.Date); } } } \ No newline at end of file diff --git a/BusinessLogicLayer.Implementation/Services/CurrateCurrencyProviderService.cs b/BusinessLogicLayer.Implementation/Services/CurrateCurrencyProviderService.cs new file mode 100644 index 0000000..56220bb --- /dev/null +++ b/BusinessLogicLayer.Implementation/Services/CurrateCurrencyProviderService.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Abstraction.Interfaces; +using BusinessLogicLayer.Objects.Dtos.Currate; +using DataAccessLayer.Models.Entities; +using DataAccessLayer.Models.Enums; +using Flurl.Http; +using Microsoft.Extensions.Configuration; + +namespace BusinessLogicLayer.Implementation.Services +{ + public class CurrateCurrencyProviderService : IUnofficialSource + { + private readonly IConfiguration _configuration; + + public CurrateCurrencyProviderService(IConfiguration configuration) + { + _configuration = configuration; + } + + public async Task> GetExchangeRateAsync() + { + CurrateResponse currateResponse = + await _configuration["Config:LinqCurrate"] + .GetJsonAsync(); + + return new[] + { + new CurrencyExchangeRate + { + DataSource = SourceType.Exchange, + + Code = "EUR", + + Date = DateTime.UtcNow, + + Name = "Евро", + + Value = currateResponse.Data.EURRUB + }, + + new CurrencyExchangeRate + { + DataSource = SourceType.Exchange, + + Code = "USD", + + Date = DateTime.UtcNow, + + Name = "Доллар США", + + Value = currateResponse.Data.USDRUB + } + }; + } + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Implementation/Services/VkResponseClientService.cs b/BusinessLogicLayer.Implementation/Services/VkResponseClientService.cs index 614068f..a40dbe0 100644 --- a/BusinessLogicLayer.Implementation/Services/VkResponseClientService.cs +++ b/BusinessLogicLayer.Implementation/Services/VkResponseClientService.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; using System.Threading.Tasks; using Abstraction.Interfaces; +using BusinessLogicLayer.Objects.Dtos; using DataAccessLayer.Models.Entities; using Microsoft.Extensions.Configuration; using VkNet.Abstractions; @@ -28,45 +29,46 @@ public VkResponseClientService(IVkApi vkApi, IConfiguration configuration, _currencyProviderService = currencyProviderService; } - public async Task SendMessage(Updates updates) + public async Task SendResponse(Updates updates) { + if (updates == null) + { + return string.Empty; + } // Проверяем, что находится в поле "type" switch (updates.Type) { // Если это уведомление для подтверждения адреса - case "confirmation": + case VkMessagesTypes.Confirmation: // Отправляем строку для подтверждения return _configuration["Config:Confirmation"]; - case "message_new": + case VkMessagesTypes.MessageNew: { // Десериализация - var msg = Message.FromJson(new VkResponse(updates.Object)); + var vkMessage = Message.FromJson(new VkResponse(updates.Object)); // Отправим в ответ полученный от пользователя текст - IEnumerable currencies = await _currencyProviderService.GetAnswerAsync(); - - StringBuilder message = new StringBuilder(String.Empty); + IEnumerable currencies = await _currencyProviderService + .GetExchangeRateAsync(); - foreach (CurrencyDataResponse currencyData in currencies) - { - message.Append($"{currencyData.Name}: {currencyData.Value} ₽\n"); - } + string responseMessage = currencies.Aggregate(string.Empty, + (previous, current) => previous + $"{current.Name}: {current.Value} ₽\n"); - if (msg.PeerId != null) + if (vkMessage.PeerId != null) { _vkApi.Messages.Send(new MessagesSendParams { RandomId = new DateTime().Millisecond, - PeerId = msg.PeerId.Value, + PeerId = vkMessage.PeerId.Value, - Message = message.ToString() + Message = responseMessage }); } - break; + return responseMessage; } } diff --git a/BusinessLogicLayer.Objects/Constants/CbrConstants.cs b/BusinessLogicLayer.Objects/Constants/CbrConstants.cs new file mode 100644 index 0000000..fe3e787 --- /dev/null +++ b/BusinessLogicLayer.Objects/Constants/CbrConstants.cs @@ -0,0 +1,7 @@ +namespace BusinessLogicLayer.Objects.Dtos +{ + public class CbrConstants + { + public const string Date = "Date"; + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Objects/Constants/VkMessagesTypes.cs b/BusinessLogicLayer.Objects/Constants/VkMessagesTypes.cs new file mode 100644 index 0000000..7577b7f --- /dev/null +++ b/BusinessLogicLayer.Objects/Constants/VkMessagesTypes.cs @@ -0,0 +1,9 @@ +namespace BusinessLogicLayer.Objects.Dtos +{ + public class VkMessagesTypes + { + public const string Confirmation = "confirmation"; + + public const string MessageNew = "message_new"; + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Objects/Dtos/Valute.cs b/BusinessLogicLayer.Objects/Dtos/Cbr/CbrCurrencies.cs similarity index 64% rename from BusinessLogicLayer.Objects/Dtos/Valute.cs rename to BusinessLogicLayer.Objects/Dtos/Cbr/CbrCurrencies.cs index f3eef72..53213fa 100644 --- a/BusinessLogicLayer.Objects/Dtos/Valute.cs +++ b/BusinessLogicLayer.Objects/Dtos/Cbr/CbrCurrencies.cs @@ -1,6 +1,6 @@ -namespace BusinessLogicLayer.Objects.Dtos +namespace BusinessLogicLayer.Objects.Dtos.Cbr { - public class Valute + public class CbrCurrencies { public Currency USD { get; set; } diff --git a/BusinessLogicLayer.Objects/Dtos/CbrResponse.cs b/BusinessLogicLayer.Objects/Dtos/Cbr/CbrResponse.cs similarity index 73% rename from BusinessLogicLayer.Objects/Dtos/CbrResponse.cs rename to BusinessLogicLayer.Objects/Dtos/Cbr/CbrResponse.cs index b418f2a..941e999 100644 --- a/BusinessLogicLayer.Objects/Dtos/CbrResponse.cs +++ b/BusinessLogicLayer.Objects/Dtos/Cbr/CbrResponse.cs @@ -1,7 +1,7 @@ using System; using Newtonsoft.Json; -namespace BusinessLogicLayer.Objects.Dtos +namespace BusinessLogicLayer.Objects.Dtos.Cbr { public class CbrResponse { @@ -13,6 +13,6 @@ public class CbrResponse /// Валюты /// [JsonProperty("Valute")] - public Valute Currencies { get; set; } + public CbrCurrencies Currencies { get; set; } } } \ No newline at end of file diff --git a/BusinessLogicLayer.Objects/Dtos/Currency.cs b/BusinessLogicLayer.Objects/Dtos/Cbr/Currency.cs similarity index 71% rename from BusinessLogicLayer.Objects/Dtos/Currency.cs rename to BusinessLogicLayer.Objects/Dtos/Cbr/Currency.cs index f40d223..bd7ace3 100644 --- a/BusinessLogicLayer.Objects/Dtos/Currency.cs +++ b/BusinessLogicLayer.Objects/Dtos/Cbr/Currency.cs @@ -1,4 +1,4 @@ -namespace BusinessLogicLayer.Objects.Dtos +namespace BusinessLogicLayer.Objects.Dtos.Cbr { public class Currency { @@ -15,7 +15,5 @@ public class Currency public double Value { get; set; } public double Previous { get; set; } - - // public override string ToString() => $"Курс {Name}: {Value / Nominal} ₽"; } } \ No newline at end of file diff --git a/BusinessLogicLayer.Objects/Dtos/Currate/CurrateResponse.cs b/BusinessLogicLayer.Objects/Dtos/Currate/CurrateResponse.cs new file mode 100644 index 0000000..11f7383 --- /dev/null +++ b/BusinessLogicLayer.Objects/Dtos/Currate/CurrateResponse.cs @@ -0,0 +1,15 @@ +namespace BusinessLogicLayer.Objects.Dtos.Currate +{ + public class CurrateResponse + { + public Currencies Data { get; set; } +// { +// "status": "200", +// "message": "rates", +// "data": { +// "EURRUB": "71.3846", +// "USDRUB": "58.059" +// } +// } + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Objects/Dtos/Currate/Currencies.cs b/BusinessLogicLayer.Objects/Dtos/Currate/Currencies.cs new file mode 100644 index 0000000..ac11305 --- /dev/null +++ b/BusinessLogicLayer.Objects/Dtos/Currate/Currencies.cs @@ -0,0 +1,9 @@ +namespace BusinessLogicLayer.Objects.Dtos.Currate +{ + public class Currencies + { + public double EURRUB { get; set; } + + public double USDRUB { get; set; } + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Tests/BusinessLogicLayer.Tests.csproj b/BusinessLogicLayer.Tests/BusinessLogicLayer.Tests.csproj new file mode 100644 index 0000000..e453a1d --- /dev/null +++ b/BusinessLogicLayer.Tests/BusinessLogicLayer.Tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + diff --git a/BusinessLogicLayer.Tests/CbrExchangeRateProviderTests.cs b/BusinessLogicLayer.Tests/CbrExchangeRateProviderTests.cs new file mode 100644 index 0000000..130f00b --- /dev/null +++ b/BusinessLogicLayer.Tests/CbrExchangeRateProviderTests.cs @@ -0,0 +1,126 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using BusinessLogicLayer.Implementation.Services; +using BusinessLogicLayer.Objects.Dtos.Cbr; +using DataAccessLayer.Models.Entities; +using DataAccessLayer.Models.Enums; +using DataAccessLayer.Models.MapperProfiles; +using Flurl.Http.Testing; +using Microsoft.Extensions.Configuration; +using Moq; +using Xunit; + +namespace BusinessLogicLayer.Tests +{ + public class CbrExchangeRateProviderTests : IDisposable + { + Mock iConfiguration = new Mock(); + + public void Dispose() + { + _httpTest?.Dispose(); + } + + IMapper mapper = + new Mapper(new MapperConfiguration(config => config.AddProfile(new ExchangeRateMappingProfile()))); + + private HttpTest _httpTest; + + public CbrExchangeRateProviderTests() + { + _httpTest = new HttpTest(); + } + + [Fact] + public async Task GetAnswerAsync_cbr() + { + //Arrange + +// _httpTest.RespondWith("{ \"Date\": \"2019-08-14T11:30:00+03:00\",\n \"PreviousDate\": \"2019-08-13T11:30:00+03:00\",\n \"PreviousURL\": \"\\/\\/www.cbr-xml-daily.ru\\/archive\\/2019\\/08\\/13\\/daily_json.js\",\n \"Timestamp\": \"2019-08-13T23:00:00+03:00\",\n \"Valute\": {\n " + +// "\"EUR\": {\n \"ID\": \"R01010\",\n \"NumCode\": \"036\",\n \"CharCode\": \"EUR\",\n \"Nominal\": 1,\n \"Name\": \"Австралийский доллар\",\n \"Value\": 44.3664,\n \"Previous\": 44.2756\n },\n " + +// " \"UAH\": {\n \"ID\": \"R01020A\",\n \"NumCode\": \"944\",\n \"CharCode\": \"UAH\",\n \"Nominal\": 1,\n \"Name\": \"Азербайджанский манат\",\n \"Value\": 38.6688,\n \"Previous\": 38.5669\n },\n " + +// " \"USD\": {\n \"ID\": \"R01035\",\n \"NumCode\": \"826\",\n \"CharCode\": \"USD\",\n \"Nominal\": 1,\n \"Name\": \"Фунт стерлингов Соединенного королевства\",\n \"Value\": 79.1091,\n \"Previous\": 78.9005\n }}}"); +// + iConfiguration.SetupGet(configuration => configuration[It.IsAny()]) + .Returns("http://a.com"); + + _httpTest.RespondWithJson(new CbrResponse + { + Date = DateTime.MinValue, + PreviousDate = DateTime.MinValue.AddDays(1), + Currencies = new CbrCurrencies + { + EUR = new Currency + { + ID = "R01010", + NumCode = "036", + CharCode = "EUR", + Nominal = 1, + Name = "Австралийский доллар", + Value = 44.3664, + Previous = 44.2756 + }, + UAH = new Currency + { + ID = "R01020A", + NumCode = "944", + CharCode = "UAH", + Nominal = 1, + Name = "Азербайджанский манат", + Value = 38.6688, + Previous = 38.5669 + }, + USD = new Currency + { + ID = "R01035", + NumCode = "826", + CharCode = "USD", + Nominal = 1, + Name = "Фунт стерлингов Соединенного королевства", + Value = 79.1091, + Previous = 78.900 + } + } + }); + + var cbrCurrencyProviderService = new CbrExchangeRateProvider(mapper, iConfiguration.Object); + + CurrencyExchangeRate[] currencyExchangeRates = + { + new CurrencyExchangeRate + { + DataSource = SourceType.Official, + Code = "EUR", + Name = "Австралийский доллар", + Value = 44.3664, + Date = DateTime.MinValue + }, + new CurrencyExchangeRate + { + DataSource = SourceType.Official, + Code = "UAH", + Name = "Азербайджанский манат", + Value = 38.6688, + Date = DateTime.MinValue + }, + new CurrencyExchangeRate + { + DataSource = SourceType.Official, + Code = "USD", + Name = "Фунт стерлингов Соединенного королевства", + Value = 79.1091, + Date = DateTime.MinValue + } + }; + + //Act + + var actualResult = await cbrCurrencyProviderService.GetExchangeRateAsync(); + + //Assert + + Assert.Equal(currencyExchangeRates, actualResult, new JsonSerializedComparer()); + } + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Tests/JsonSerializedComparer.cs b/BusinessLogicLayer.Tests/JsonSerializedComparer.cs new file mode 100644 index 0000000..5ee3c67 --- /dev/null +++ b/BusinessLogicLayer.Tests/JsonSerializedComparer.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace BusinessLogicLayer.Tests +{ + public class JsonSerializedComparer : IEqualityComparer + { + public bool Equals(T first, T second) + { + var serializeFirstObject = JsonConvert.SerializeObject(first); + var serializeSecondObject = JsonConvert.SerializeObject(second); + + return serializeSecondObject == serializeFirstObject; + } + + public int GetHashCode(T obj) + { + return JsonConvert.SerializeObject(obj).GetHashCode(); + } + } +} \ No newline at end of file diff --git a/BusinessLogicLayer.Tests/VkResponseClientServiceTests.cs b/BusinessLogicLayer.Tests/VkResponseClientServiceTests.cs new file mode 100644 index 0000000..bdcf318 --- /dev/null +++ b/BusinessLogicLayer.Tests/VkResponseClientServiceTests.cs @@ -0,0 +1,117 @@ +using System.Threading.Tasks; +using Abstraction.Interfaces; +using BusinessLogicLayer.Implementation.Services; +using DataAccessLayer.Models.Entities; +using Microsoft.Extensions.Configuration; +using Moq; +using Newtonsoft.Json.Linq; +using VkNet.Abstractions; +using Xunit; + +namespace BusinessLogicLayer.Tests +{ + public class VkResponseClientServiceTests + { + Mock iVkApi = new Mock(); + Mock iConfiguration = new Mock(); + Mock iCurrencyProvider = new Mock(); + + [Fact] + public async Task SendMessage_case1() + { + //Arrange + + Updates updates = new Updates + { + Type = "confirmation" + }; + + const string confirmationMessage = "uhyuhu"; + + iConfiguration.SetupGet(configuration => configuration[It.IsAny()]) + .Returns(confirmationMessage); + + var vkResponseClientService = new VkResponseClientService(iVkApi.Object, + iConfiguration.Object, iCurrencyProvider.Object); + + //Act + + var actualResult = await vkResponseClientService.SendResponse(updates); + + //Assert + + Assert.Equal(actualResult, confirmationMessage); + } + + [Fact] + public async Task SendMessageNull() + { + //Arrange + + Updates updates = null; + + var vkResponseClientService = new VkResponseClientService(iVkApi.Object, + iConfiguration.Object, iCurrencyProvider.Object); + + //Act + + var actualResult = await vkResponseClientService.SendResponse(updates); + + //Assert + + Assert.Equal(string.Empty, actualResult); + } + + [Fact] + public async Task SendMessage_case2() + { + //Arrange + + Updates updates = new Updates + { + Type = "message_new", + Object = new JObject() + }; + + //iVkApi.SetupGet(iVkApi => iVkApi[It.IsAny()]).Returns(); + + CurrencyExchangeRate[] currencyExchangeRates = + { + new CurrencyExchangeRate + { + Name = "ggg", + Value = 2.4 + }, + new CurrencyExchangeRate + { + Name = "qqq", + Value = 2.4 + }, + new CurrencyExchangeRate + { + Name = "aaa", + Value = 2.4 + } + }; + + iCurrencyProvider.Setup(currencyProvider => currencyProvider.GetExchangeRateAsync()).ReturnsAsync( + currencyExchangeRates); + + + string expectedResult = + $"{currencyExchangeRates[0].Name}: {currencyExchangeRates[0].Value} ₽\n{currencyExchangeRates[1].Name}: {currencyExchangeRates[1].Value} ₽\n{currencyExchangeRates[2].Name}: {currencyExchangeRates[2].Value} ₽\n"; + + var vkResponseClientService = new VkResponseClientService(iVkApi.Object, + iConfiguration.Object, iCurrencyProvider.Object); + + //Act + + string actualResult = await vkResponseClientService.SendResponse(updates); + + + //Assert + + Assert.Equal(expectedResult, actualResult); + } + } +} \ No newline at end of file diff --git a/ConsoleAppCourseValut.sln b/ConsoleAppCourseValut.sln index dc182c8..9f8bac5 100644 --- a/ConsoleAppCourseValut.sln +++ b/ConsoleAppCourseValut.sln @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAccessLayer.Models", "D EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PresentationLayer", "PresentationLayer", "{1800E55A-1231-4FB8-8B06-96D0A7FAEB0C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLogicLayer.Tests", "BusinessLogicLayer.Tests\BusinessLogicLayer.Tests.csproj", "{B161200F-7E83-4067-8D2E-B0BED561C72A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,6 +59,10 @@ Global {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9}.Debug|Any CPU.Build.0 = Debug|Any CPU {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9}.Release|Any CPU.Build.0 = Release|Any CPU + {B161200F-7E83-4067-8D2E-B0BED561C72A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B161200F-7E83-4067-8D2E-B0BED561C72A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B161200F-7E83-4067-8D2E-B0BED561C72A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B161200F-7E83-4067-8D2E-B0BED561C72A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -72,5 +78,6 @@ Global {0A6427A3-7CC5-4089-930A-485D1CED818A} = {5DEA6744-5DC4-4C77-903A-1A0E32A347F6} {EEE62B96-2CBB-4ED0-B3BC-319DBE43A8C9} = {5DEA6744-5DC4-4C77-903A-1A0E32A347F6} {10A225C1-5EAA-40D0-A7E9-48774C1DBFE5} = {1800E55A-1231-4FB8-8B06-96D0A7FAEB0C} + {B161200F-7E83-4067-8D2E-B0BED561C72A} = {723CCBD0-6040-4095-B01B-85F234E7B03D} EndGlobalSection EndGlobal diff --git a/DataAccessLayer.Models/Entities/CurrencyDataResponse.cs b/DataAccessLayer.Models/Entities/CurrencyExchangeRate.cs similarity index 89% rename from DataAccessLayer.Models/Entities/CurrencyDataResponse.cs rename to DataAccessLayer.Models/Entities/CurrencyExchangeRate.cs index bd7a8f5..e0670cf 100644 --- a/DataAccessLayer.Models/Entities/CurrencyDataResponse.cs +++ b/DataAccessLayer.Models/Entities/CurrencyExchangeRate.cs @@ -3,7 +3,7 @@ namespace DataAccessLayer.Models.Entities { - public class CurrencyDataResponse + public class CurrencyExchangeRate { public SourceType DataSource { get; set; } diff --git a/DataAccessLayer.Models/Entities/Updates.cs b/DataAccessLayer.Models/Entities/Updates.cs index b45e2a5..6397489 100644 --- a/DataAccessLayer.Models/Entities/Updates.cs +++ b/DataAccessLayer.Models/Entities/Updates.cs @@ -10,14 +10,12 @@ public class Updates /// /// Тип события /// - [JsonProperty("type")] public string Type { get; set; } /// /// Объект, инициировавший событие /// Структура объекта зависит от типа уведомления /// - [JsonProperty("object")] public JObject Object { get; set; } /// diff --git a/DataAccessLayer.Models/Enums/SourceType.cs b/DataAccessLayer.Models/Enums/SourceType.cs index 0fe40c2..da2666b 100644 --- a/DataAccessLayer.Models/Enums/SourceType.cs +++ b/DataAccessLayer.Models/Enums/SourceType.cs @@ -4,6 +4,7 @@ public enum SourceType { Exchange, +//959d1d77b858e5a8e51c3c4ed3ca06f6 https://currate.ru/api/?get=rates&pairs=USDRUB,EURRUB&key=959d1d77b858e5a8e51c3c4ed3ca06f6 Official } } \ No newline at end of file diff --git a/DataAccessLayer.Models/MapperProfiles/DateResolver.cs b/DataAccessLayer.Models/MapperProfiles/DateResolver.cs new file mode 100644 index 0000000..207d6e3 --- /dev/null +++ b/DataAccessLayer.Models/MapperProfiles/DateResolver.cs @@ -0,0 +1,15 @@ +using System; +using AutoMapper; +using BusinessLogicLayer.Objects.Dtos; +using BusinessLogicLayer.Objects.Dtos.Cbr; +using DataAccessLayer.Models.Entities; + +namespace DataAccessLayer.Models.MapperProfiles +{ + public class DateResolver : IValueResolver + { + public DateTime Resolve(Currency source, CurrencyExchangeRate destination, DateTime destMember, + ResolutionContext context) => + context.Items[CbrConstants.Date] as DateTime? ?? DateTime.Today; + } +} \ No newline at end of file diff --git a/DataAccessLayer.Models/MapperProfiles/MapperProfiles.cs b/DataAccessLayer.Models/MapperProfiles/ExchangeRateMappingProfile.cs similarity index 50% rename from DataAccessLayer.Models/MapperProfiles/MapperProfiles.cs rename to DataAccessLayer.Models/MapperProfiles/ExchangeRateMappingProfile.cs index adb1fd1..475570e 100644 --- a/DataAccessLayer.Models/MapperProfiles/MapperProfiles.cs +++ b/DataAccessLayer.Models/MapperProfiles/ExchangeRateMappingProfile.cs @@ -1,18 +1,15 @@ -using System; using AutoMapper; -using BusinessLogicLayer.Objects.Dtos; +using BusinessLogicLayer.Objects.Dtos.Cbr; using DataAccessLayer.Models.Entities; using DataAccessLayer.Models.Enums; namespace DataAccessLayer.Models.MapperProfiles { - public class MapperProfiles : Profile + public class ExchangeRateMappingProfile : Profile { - public MapperProfiles() + public ExchangeRateMappingProfile() { - //opt => opt.MapFrom(dto => dto.SubCategory.Id) - //cbr resp в мою х у й н ю (отправка пльзователю ответа) - CreateMap() + CreateMap() .ForMember(dest => dest.Date, options => options .MapFrom()) .ForMember(dest => dest.DataSource, options => options @@ -23,11 +20,4 @@ public MapperProfiles() .MapFrom(dto => dto.Value / dto.Nominal)); } } - - public class DateResolver : IValueResolver - { - public DateTime Resolve(Currency source, CurrencyDataResponse destination, DateTime destMember, - ResolutionContext context) => - context.Items["Date"] as DateTime? ?? DateTime.Today; - } } \ No newline at end of file diff --git "a/Parser\320\241urrency/Controllers/CallbackController.cs" "b/Parser\320\241urrency/Controllers/CallbackController.cs" index c51f71e..a16f577 100644 --- "a/Parser\320\241urrency/Controllers/CallbackController.cs" +++ "b/Parser\320\241urrency/Controllers/CallbackController.cs" @@ -21,7 +21,7 @@ public CallbackController(IResponseClient vkResponseClientService) [HttpPost] public IActionResult CallbackAsync(Updates updates) { - var response = _vkResponseClientService.SendMessage(updates); + var response = _vkResponseClientService.SendResponse(updates); // Возвращаем "ok" серверу Callback API return Ok(response); diff --git "a/Parser\320\241urrency/Startup.cs" "b/Parser\320\241urrency/Startup.cs" index fa975ca..1b44df7 100644 --- "a/Parser\320\241urrency/Startup.cs" +++ "b/Parser\320\241urrency/Startup.cs" @@ -37,11 +37,13 @@ public void ConfigureServices(IServiceCollection services) return api; }); - services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); services.AddSingleton(); - services.AddAutoMapper(options => options.AddProfile()); + services.AddAutoMapper(options => options.AddProfile()); } diff --git "a/Parser\320\241urrency/appsettings.json" "b/Parser\320\241urrency/appsettings.json" index 533af71..acd02fe 100644 --- "a/Parser\320\241urrency/appsettings.json" +++ "b/Parser\320\241urrency/appsettings.json" @@ -1,15 +1,9 @@ -//{ -// "Logging": { -// "LogLevel": { -// "Default": "Warning" -// } -// }, -// "AllowedHosts": "*" -//} { "Config": { "AccessToken": "2cc72f081f78376c9bb645e7cd1d28c4a26edb0011e87c012fc776ae6552ae10c8051405f51605b633a3d", - "Confirmation": "72706f26" + "Confirmation": "72706f26", + "LinqCbr": "https://www.cbr-xml-daily.ru/daily_json.js", + "LinqCurrate": "https://currate.ru/api/?get=rates&pairs=USDRUB,EURRUB&key=959d1d77b858e5a8e51c3c4ed3ca06f6" }, "Logging": { "LogLevel": {