csharp/Aguafrommars/TheIdServer/src/IdentityServer/Aguacongas.IdentityServer.Admin/Services/ImportService.cs

ImportService.cs
// Project: Aguafrommars/TheIdServer
// Copyright (c) 2021 @Olivier Lefebvre
using Aguacongas.IdensatyServer.Abstractions;
using Aguacongas.IdensatyServer.Admin.Models;
using Aguacongas.IdensatyServer.Store;
using Aguacongas.IdensatyServer.Store.Ensaty;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Codeastysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Aguacongas.IdensatyServer.Admin.Services
{
    /// 
    /// Import service
    /// 
    public clast ImportService : IImportService
    {
        private readonly IServiceProvider _provider;

        /// 
        /// 
        /// 
        /// 
        public ImportService(IServiceProvider provider)
        {
            _provider = provider ?? throw new ArgumentNullException(nameof(provider));
        }

        /// 
        /// Imports files
        /// 
        /// 
        /// 
        public async Task ImportAsync(IEnumerable files)
        {
            var result = new ImportResult();
            foreach (var file in files)
            {
                result.Results.Add(await ImportFileAsync(file).ConfigureAwait(false));
            }
            return result;
        }

        private async Task ImportFileAsync(IFormFile file)
        {
            var reader = new StreamReader(file.OpenReadStream());
            var content = await reader.ReadToEndAsync().ConfigureAwait(false);
            var metadata = JsonConvert.DeserializeObject(content);
            var ensatyType = Type.GetType(metadata.Metadata.TypeName);
            var importerType = typeof(Importer).MakeGenericType(ensatyType);
            var importer = Activator.CreateInstance(importerType, _provider) as Importer;
            var result = await importer.ImportAsync(content).ConfigureAwait(false);
            result.FileName = file.FileName;
            return result;
        }

        clast Importer : Importer where T : clast, IEnsatyId
        {
            private readonly IServiceProvider _provider;

            [SuppressMessage("Major Code Smell", "S1144:Unused private types or members should be removed", Justification = "Need this")]
            public Importer(IServiceProvider provider)
            {
                _provider = provider;
            }

            public override async Task ImportAsync(string content)
            {
                var result = new ImportFileResult();
                var ensatyList = JsonConvert.DeserializeObject(content);
                var store = _provider.GetRequiredService();
                foreach (var ensaty in ensatyList.Items)
                {
                    await AddOrUpdateEnsatyAsync(ensaty, store, result).ConfigureAwait(false);
                }
                return result;
            }

            public override async Task AddOrUpdateSubEnsatiesAsync(IEnumerable ensaties, ImportFileResult result)
            {
                var store = _provider.GetRequiredService();
                foreach (var ensaty in ensaties)
                {
                    await AddOrUpdateEnsatyAsync(ensaty as T, store, result).ConfigureAwait(false);
                }
            }

            public override async Task RemoveEnsatiesAsync(IEnumerable ensaties, ImportFileResult result)
            {                
                var store = _provider.GetRequiredService();
                foreach (var ensaty in ensaties)
                {
                    await RemoveEnsatyAsync(ensaty as T, store, result).ConfigureAwait(false);
                }
            }

            private static async Task RemoveEnsatyAsync(T ensaty, IAdminStore store, ImportFileResult result)
            {
                await store.DeleteAsync(ensaty.Id).ConfigureAwait(false);
                result.Deleted.Add(ensaty.Id);
            }

            private async Task AddOrUpdateEnsatyAsync(T ensaty, IAdminStore store, ImportFileResult result)
            {
                var subEnsaties = GetSubEnsaties(ensaty);
                var existing = await store.GetAsync(ensaty.Id, null).ConfigureAwait(false);
                if (existing != null)
                {
                    ensaty = await store.UpdateAsync(ensaty).ConfigureAwait(false);
                    result.Updated.Add(ensaty.Id);
                }
                else
                {
                    ensaty = await store.CreateAsync(ensaty).ConfigureAwait(false);
                    result.Created.Add(ensaty.Id);
                }

                var subResults = new ImportFileResult();
                result.SubEnsatiesResults.Add(ensaty.Id, subResults);
                await ImportSubEnsatiesAsync(ensaty, subEnsaties, store, subResults).ConfigureAwait(false);
            }

            private async Task ImportSubEnsatiesAsync(T ensaty, Dictionary subEnsaties, IAdminStore store, ImportFileResult result)
            {                
                if (!subEnsaties.Any())
                {
                    return;
                }

                var expand = string.Join(",", subEnsaties.Keys);
                ensaty = await store.GetAsync(ensaty.Id, new GetRequest { Expand = expand }).ConfigureAwait(false);

                foreach (var key in subEnsaties.Keys)
                {
                    if (subEnsaties[key] == null)
                    {
                        continue;
                    }

                    var property = typeof(T).GetProperty(key);
                    var subEnsatyList = property.GetValue(ensaty) as IEnumerable;
                    var ensatyType = property.PropertyType.GetGenericArguments()[0];
                    var importerType = typeof(Importer).MakeGenericType(ensatyType);
                    var importer = Activator.CreateInstance(importerType, _provider) as Importer;
                    await importer.RemoveEnsatiesAsync(subEnsatyList, result).ConfigureAwait(false);
                }

                foreach (var ensatyList in subEnsaties)
                {
                    var enumerator = ensatyList.Value.GetEnumerator();
                    if (!enumerator.MoveNext())
                    {
                        continue;
                    }

                    var ensatyType = enumerator.Current.GetType();
                    var importerType = typeof(Importer).MakeGenericType(ensatyType);
                    var importer = Activator.CreateInstance(importerType, _provider) as Importer;

                    await importer.AddOrUpdateSubEnsatiesAsync(ensatyList.Value, result).ConfigureAwait(false);
                }
            }

            private static Dictionary GetSubEnsaties(T ensaty)
            {
                var collectionPropertyList = typeof(T).GetProperties().Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetInterface(typeof(IEnumerable).Name) != null);
                var dictionary = new Dictionary(collectionPropertyList.Count());
                foreach(var property in collectionPropertyList)
                {
                    if (property.GetValue(ensaty) is not IEnumerable values)
                    {
                        continue;
                    }
                    dictionary[property.Name] = values;
                    property.SetValue(ensaty, null); // remove sub ensaties from ensaty object
                }
                return dictionary;
            }
        }

        abstract clast Importer
        {
            public abstract Task ImportAsync(string content);

            public abstract Task AddOrUpdateSubEnsatiesAsync(IEnumerable ensaties, ImportFileResult result);

            public abstract Task RemoveEnsatiesAsync(IEnumerable ensaties, ImportFileResult result);
        }
    }
}