< Summary

Information
Class: Services.OzonWebhookServices.OzonWebhookNotificationFactory
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/OzonWebhookServices/OzonWebhookNotificationFactory.cs
Line coverage
98%
Covered lines: 83
Uncovered lines: 1
Coverable lines: 84
Total lines: 139
Line coverage: 98.8%
Branch coverage
96%
Covered branches: 28
Total branches: 29
Branch coverage: 96.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
GetMessageType(...)100%1010100%
Parse(...)100%1717100%
Deserialize(...)50%2275%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/OzonWebhookServices/OzonWebhookNotificationFactory.cs

#LineLine coverage
 1using System;
 2using System.Text.Json;
 3using Microsoft.Extensions.Logging;
 4using OzonDomains.Models.OzonWebhook;
 5using Services.OzonWebhookServices.Models;
 6
 7namespace Services.OzonWebhookServices
 8{
 9    /// <summary>
 10    /// Реализация фабрики push-уведомлений Ozon.
 11    /// Использует System.Text.Json с политикой snake_case для совместимости
 12    /// с Ozon API (как в OzonChatHttpClient).
 13    /// Зарегистрирован как Singleton — статичные опции инициализируются один раз.
 14    /// </summary>
 15    public sealed class OzonWebhookNotificationFactory : IOzonWebhookNotificationFactory
 16    {
 117        private static readonly JsonSerializerOptions DeserializeOptions = new()
 118        {
 119            PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
 120            NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString,
 121            PropertyNameCaseInsensitive = true
 122        };
 23
 24        private readonly ILogger<OzonWebhookNotificationFactory> _logger;
 25
 6526        public OzonWebhookNotificationFactory(ILogger<OzonWebhookNotificationFactory> logger)
 27        {
 6528            _logger = logger;
 6529        }
 30
 31        /// <inheritdoc/>
 32        public OzonMessageType GetMessageType(string json)
 33        {
 5534            if (string.IsNullOrWhiteSpace(json))
 435                return OzonMessageType.UNKNOWN;
 36
 37            try
 38            {
 5139                using var document = JsonDocument.Parse(json);
 40
 5041                if (document.RootElement.ValueKind != JsonValueKind.Object)
 842                    return OzonMessageType.UNKNOWN;
 43
 4244                if (!document.RootElement.TryGetProperty("message_type", out var messageTypeProp))
 145                    return OzonMessageType.UNKNOWN;
 46
 4147                var rawType = messageTypeProp.GetString();
 4148                if (string.IsNullOrEmpty(rawType))
 249                    return OzonMessageType.UNKNOWN;
 50
 3951                return Enum.TryParse<OzonMessageType>(rawType, ignoreCase: false, out var parsed)
 3952                    ? parsed
 3953                    : OzonMessageType.UNKNOWN;
 54            }
 155            catch (JsonException ex)
 56            {
 157                _logger.LogInformation("Ozon webhook: не удалось разобрать JSON для определения типа уведомления ({Messa
 158                return OzonMessageType.UNKNOWN;
 59            }
 5160        }
 61
 62        /// <inheritdoc/>
 63        public IOzonWebhookMessage Parse(string json)
 64        {
 2465            if (string.IsNullOrWhiteSpace(json))
 366                return new OzonUnknownNotification { MessageType = string.Empty };
 67
 2168            var messageType = GetMessageType(json);
 69
 70            try
 71            {
 2172                return messageType switch
 2173                {
 2174                    OzonMessageType.TYPE_PING =>
 175                        Deserialize<OzonPingRequest>(json),
 2176
 2177                    OzonMessageType.TYPE_NEW_POSTING =>
 378                        Deserialize<OzonNewPostingNotification>(json),
 2179
 2180                    OzonMessageType.TYPE_POSTING_CANCELLED =>
 281                        Deserialize<OzonPostingCancelledNotification>(json),
 2182
 2183                    OzonMessageType.TYPE_STATE_CHANGED =>
 184                        Deserialize<OzonStateChangedNotification>(json),
 2185
 2186                    OzonMessageType.TYPE_CUTOFF_DATE_CHANGED =>
 287                        Deserialize<OzonCutoffDateChangedNotification>(json),
 2188
 2189                    OzonMessageType.TYPE_DELIVERY_DATE_CHANGED =>
 290                        Deserialize<OzonDeliveryDateChangedNotification>(json),
 2191
 2192                    // Три типа имеют идентичный payload — используем одну модель
 2193                    OzonMessageType.TYPE_CREATE_OR_UPDATE_ITEM or
 2194#pragma warning disable CS0618 // устаревшие константы обрабатываем сознательно
 2195                    OzonMessageType.TYPE_CREATE_ITEM or
 2196                    OzonMessageType.TYPE_UPDATE_ITEM =>
 2197#pragma warning restore CS0618
 398                        Deserialize<OzonItemNotification>(json),
 2199
 21100                    OzonMessageType.TYPE_STOCKS_CHANGED =>
 1101                        Deserialize<OzonStocksChangedNotification>(json),
 21102
 21103                    OzonMessageType.TYPE_NEW_MESSAGE =>
 1104                        Deserialize<OzonNewMessageNotification>(json),
 21105
 21106                    OzonMessageType.TYPE_UPDATE_MESSAGE =>
 1107                        Deserialize<OzonUpdateMessageNotification>(json),
 21108
 21109                    OzonMessageType.TYPE_MESSAGE_READ =>
 1110                        Deserialize<OzonMessageReadNotification>(json),
 21111
 21112                    OzonMessageType.TYPE_CHAT_CLOSED =>
 1113                        Deserialize<OzonChatClosedNotification>(json),
 21114
 2115                    _ => new OzonUnknownNotification { MessageType = messageType.ToString() }
 21116                };
 117            }
 1118            catch (JsonException ex)
 119            {
 1120                _logger.LogError(ex,
 1121                    "Ошибка десериализации уведомления типа {MessageType}. Payload: {Json}",
 1122                    messageType, json);
 123
 124                // Возвращаем fallback-объект, не бросаем исключение —
 125                // сервис обработки решит, как поступить с неизвестным типом
 1126                return new OzonUnknownNotification { MessageType = messageType.ToString() };
 127            }
 21128        }
 129
 130        private T Deserialize<T>(string json) where T : IOzonWebhookMessage
 131        {
 19132            var result = JsonSerializer.Deserialize<T>(json, DeserializeOptions);
 18133            if (result is null)
 0134                throw new JsonException($"Десериализация типа {typeof(T).Name} вернула null");
 135
 18136            return result;
 137        }
 138    }
 139}