<commit_before>﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using InfinniPlatform.DocumentStorage.Properties;
using InfinniPlatform.Sdk.Documents.Services;
using InfinniPlatform.Sdk.Logging;
using InfinniPlatform.Sdk.Services;

namespace InfinniPlatform.DocumentStorage.Services
{
    /// <summary>
    /// Базовый класс сервисов по работе с документами.
    /// </summary>
    internal abstract class DocumentHttpServiceBase : IHttpService
    {
        protected DocumentHttpServiceBase(IPerformanceLog performanceLog, ILog log)
        {
            _performanceLog = performanceLog;
            _log = log;
        }


        private readonly IPerformanceLog _performanceLog;
        private readonly ILog _log;


        void IHttpService.Load(IHttpServiceBuilder builder)
        {
            builder.ServicePath = DocumentHttpServiceConstants.DefaultServicePath;

            if (CanGet)
            {
                builder.Get[$"/{DocumentType}/{{{DocumentHttpServiceConstants.DocumentIdKey}?}}"] = Get;
            }

            if (CanPost)
            {
                builder.Post[$"/{DocumentType}"] = Post;
            }

            if (CanDelete)
            {
                builder.Delete[$"/{DocumentType}/{{{DocumentHttpServiceConstants.DocumentIdKey}?}}"] = Delete;
            }

            Load(builder);
        }


        /// <summary>
        /// Имя типа документа.
        /// </summary>
        public string DocumentType { get; protected set; }

        /// <summary>
        /// Разрешено ли получение документов.
        /// </summary>
        public bool CanGet { get; protected set; }

        /// <summary>
        /// Разрешено ли сохранение документов.
        /// </summary>
        public bool CanPost { get; protected set; }

        /// <summary>
        /// Разрешено ли удаление документов.
        /// </summary>
        public bool CanDelete { get; protected set; }


        /// <summary>
        /// Загружает настройки сервиса по работе с документами.
        /// </summary>
        protected abstract void Load(IHttpServiceBuilder builder);

        /// <summary>
        /// Обрабатывает запрос на получение документов.
        /// </summary>
        protected abstract Task<object> Get(IHttpRequest request);

        /// <summary>
        /// Обрабатывает запрос на сохранение документа.
        /// </summary>
        protected abstract Task<object> Post(IHttpRequest request);

        /// <summary>
        /// Обрабатывает запрос на удаление документа.
        /// </summary>
        protected abstract Task<object> Delete(IHttpRequest request);


        /// <summary>
        /// Обрабатывает запрос с использованием указанных правил.
        /// </summary>
        protected async Task<object> ProcessRequestAsync<TQuery, TResult>(
            IHttpRequest request,
            Func<IHttpRequest, TQuery> queryFunc,
            Func<TQuery, Task<TResult>> handlerFunc,
            Func<TQuery, Task<TResult>> onBefore,
            Func<TQuery, TResult, Exception, Task> onAfter) where TResult : DocumentQueryResult
        {
            var startTime = DateTime.Now;

            object response;
            TResult result = null;
            Exception error = null;

            try
            {
                // Разбор входящего запроса
                var queryResult = TryExecute(() => queryFunc(request));
                var query = queryResult.Item1;
                error = queryResult.Item2;

                if (error == null)
                {
                    // Выполнение предобработчика
                    var onBeforeResult = await TryExecuteAsync(() => onBefore(query));
                    result = onBeforeResult.Item1;
                    error = onBeforeResult.Item2;

                    if (error == null)
                    {
                        // Если нет результата
                        if (result == null)
                        {
                            // Выполнение обработчика
                            var handlerResult = await TryExecuteAsync(() => handlerFunc(query));
                            result = handlerResult.Item1;
                            error = handlerResult.Item2;
                        }

                        var resultClosure = result;
                        var errorClosure = error;

                        // Выполнение постобработчика
                        var onAfterError = await TryExecuteAsync(() => onAfter(query, resultClosure, errorClosure));

                        if (onAfterError != null)
                        {
                            error = (error == null) ? onAfterError : new AggregateException(error, onAfterError);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                error = exception;
            }
            finally
            {
                var success = (error == null && (result?.ValidationResult == null || result.ValidationResult.IsValid));

                var requestResult = new ServiceResult<TResult>
                {
                    Success = success,
                    Result = result,
                    Error = error?.GetFullMessage()
                };

                response = new JsonHttpResponse(requestResult)
                {
                    StatusCode = success ? 200 : 500
                };
            }

            // Запись в журнал

            var method = $"{request.Method}::{request.Path}";

            if (error != null)
            {
                _log.Error(Resources.RequestProcessedWithException, new Dictionary<string, object> { { "method", method } }, error);
            }<commit_msg>Status code changed to 400 in case of validation error<commit_after>﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using InfinniPlatform.DocumentStorage.Properties;
using InfinniPlatform.Sdk.Documents.Services;
using InfinniPlatform.Sdk.Logging;
using InfinniPlatform.Sdk.Services;

namespace InfinniPlatform.DocumentStorage.Services
{
    /// <summary>
    /// Базовый класс сервисов по работе с документами.
    /// </summary>
    internal abstract class DocumentHttpServiceBase : IHttpService
    {
        protected DocumentHttpServiceBase(IPerformanceLog performanceLog, ILog log)
        {
            _performanceLog = performanceLog;
            _log = log;
        }


        private readonly IPerformanceLog _performanceLog;
        private readonly ILog _log;


        void IHttpService.Load(IHttpServiceBuilder builder)
        {
            builder.ServicePath = DocumentHttpServiceConstants.DefaultServicePath;

            if (CanGet)
            {
                builder.Get[$"/{DocumentType}/{{{DocumentHttpServiceConstants.DocumentIdKey}?}}"] = Get;
            }

            if (CanPost)
            {
                builder.Post[$"/{DocumentType}"] = Post;
            }

            if (CanDelete)
            {
                builder.Delete[$"/{DocumentType}/{{{DocumentHttpServiceConstants.DocumentIdKey}?}}"] = Delete;
            }

            Load(builder);
        }


        /// <summary>
        /// Имя типа документа.
        /// </summary>
        public string DocumentType { get; protected set; }

        /// <summary>
        /// Разрешено ли получение документов.
        /// </summary>
        public bool CanGet { get; protected set; }

        /// <summary>
        /// Разрешено ли сохранение документов.
        /// </summary>
        public bool CanPost { get; protected set; }

        /// <summary>
        /// Разрешено ли удаление документов.
        /// </summary>
        public bool CanDelete { get; protected set; }


        /// <summary>
        /// Загружает настройки сервиса по работе с документами.
        /// </summary>
        protected abstract void Load(IHttpServiceBuilder builder);

        /// <summary>
        /// Обрабатывает запрос на получение документов.
        /// </summary>
        protected abstract Task<object> Get(IHttpRequest request);

        /// <summary>
        /// Обрабатывает запрос на сохранение документа.
        /// </summary>
        protected abstract Task<object> Post(IHttpRequest request);

        /// <summary>
        /// Обрабатывает запрос на удаление документа.
        /// </summary>
        protected abstract Task<object> Delete(IHttpRequest request);


        /// <summary>
        /// Обрабатывает запрос с использованием указанных правил.
        /// </summary>
        protected async Task<object> ProcessRequestAsync<TQuery, TResult>(
            IHttpRequest request,
            Func<IHttpRequest, TQuery> queryFunc,
            Func<TQuery, Task<TResult>> handlerFunc,
            Func<TQuery, Task<TResult>> onBefore,
            Func<TQuery, TResult, Exception, Task> onAfter) where TResult : DocumentQueryResult
        {
            var startTime = DateTime.Now;

            object response;
            TResult result = null;
            Exception error = null;

            try
            {
                // Разбор входящего запроса
                var queryResult = TryExecute(() => queryFunc(request));
                var query = queryResult.Item1;
                error = queryResult.Item2;

                if (error == null)
                {
                    // Выполнение предобработчика
                    var onBeforeResult = await TryExecuteAsync(() => onBefore(query));
                    result = onBeforeResult.Item1;
                    error = onBeforeResult.Item2;

                    if (error == null)
                    {
                        // Если нет результата
                        if (result == null)
                        {
                            // Выполнение обработчика
                            var handlerResult = await TryExecuteAsync(() => handlerFunc(query));
                            result = handlerResult.Item1;
                            error = handlerResult.Item2;
                        }

                        var resultClosure = result;
                        var errorClosure = error;

                        // Выполнение постобработчика
                        var onAfterError = await TryExecuteAsync(() => onAfter(query, resultClosure, errorClosure));

                        if (onAfterError != null)
                        {
                            error = (error == null) ? onAfterError : new AggregateException(error, onAfterError);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                error = exception;
            }
            finally
            {
                var success = (error == null && (result?.ValidationResult == null || result.ValidationResult.IsValid));

                var serverError = error != null;
                var validationError = result?.ValidationResult != null && result.ValidationResult.IsValid == false;

                var requestResult = new ServiceResult<TResult>
                {
                    Success = success,
                    Result = result,
                    Error = error?.GetFullMessage()
                };

                var statusCode = 200;

                if (!success)
                {
                    if (serverError)
                    {
                        statusCode = 500;
                    }
                    else
                    {
                        if (validationError)
                        {
                            statusCode = 400;
                        }
                    }
                }

                response = new JsonHttpResponse(requestResult)
                           {
                               StatusCode = statusCode
                           };
            }

            // Запись в журнал

            var method = $"{request.Method}::{request.Path}";

            if (error != null)
            {
                _log.Error(Resources.RequestProcessedWithException, new Dictionary<string, object> { { "method", method } }, error);
            }
{'fraction_non_alphanumeric': 0.09081231575499508, 'fraction_numerical': 0.002456600065509335, 'mean_word_length': 1.3432463545663853, 'pattern_counts': {'":': 0, '<': 69, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4044730', 'n_tokens_mistral': 3071, 'n_tokens_neox': 2995, 'n_words': 677}