/// <summary>
        /// Builds (and validates) a <see cref="DeploymentManager"/> asynchronously.
        /// </summary>
        public async Task <IUpdateManager> BuildAsync()
        {
            XDeployAPI api = new XDeployAPI(_config);

            await ValidateAPIAsync(api);

            var apps = await GetApplicationDetailsAsync(api, _config.Apps);

            ISyncSignalNotifier notifier;
            IEnumerable <IApplicationSynchronizer> synchronizers;

            switch (_config.Mode)
            {
            case ApplicationMode.Deployer:
                notifier      = new SyncSignalServer(_config.SyncServerPort);
                synchronizers = apps.Select(x => new ApplicationDeployer(api, x));
                break;

            case ApplicationMode.Updater:
                if (_config.Proxy != null)
                {
                    notifier = new WebSocketsSignalNotifier(apps, _config.Endpoint, _config.Email, _config.APIKey, _config.Proxy);
                }
                else
                {
                    notifier = new WebSocketsSignalNotifier(apps, _config.Endpoint, _config.Email, _config.APIKey);
                }
                synchronizers = apps.Select(x => new ApplicationUpdater(api, x));
                break;

            default:
                throw new NotImplementedException($"Mode {_config.Mode} is not supported.");
            }
            return(new UpdateManager <IApplicationSynchronizer>(api, notifier, synchronizers));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="UpdateManagerBase"/> class.
        /// </summary>
        /// <param name="api">The API.</param>
        /// <exception cref="ArgumentNullException">api</exception>
        public UpdateManager(XDeployAPI api, ISyncSignalNotifier signalNotifier, IEnumerable <T> applicationSynchronizers)
        {
            if (api is null)
            {
                throw new ArgumentNullException(nameof(api));
            }
            if (signalNotifier is null)
            {
                throw new ArgumentNullException(nameof(signalNotifier));
            }
            if (applicationSynchronizers is null)
            {
                throw new ArgumentNullException(nameof(applicationSynchronizers));
            }

            _api      = api;
            _notifier = signalNotifier;
            _applicationSynchronizers     = applicationSynchronizers;
            _notifier.SyncSignalReceived += async(_, id) =>
            {
                Func <T, bool> idSelector = x => x.ApplicationID == id;
                if (_applicationSynchronizers.Any(idSelector))
                {
                    _ = await _applicationSynchronizers.First(idSelector).SynchronizeAsync();
                }
            };
        }
 private static async Task ValidateAPIAsync(XDeployAPI api)
 {
     try
     {
         _ = await api.ValidateCredentialsAsync();
     }
     catch (Exception e)
     {
         throw new StartupException($"Invalid API credentials provided.", e);
     }
 }
        /// <summary>
        /// Initializes a new instance of the <see cref="ApplicationSynchronizerBase"/> class.
        /// </summary>
        /// <param name="api">The API.</param>
        /// <param name="app">The application.</param>
        /// <exception cref="ArgumentNullException">
        /// api
        /// or
        /// app
        /// </exception>
        protected ApplicationSynchronizerBase(XDeployAPI api, ApplicationInfo app)
        {
            if (api is null)
            {
                throw new ArgumentNullException(nameof(api));
            }
            if (app is null)
            {
                throw new ArgumentNullException(nameof(app));
            }

            _api = api;
            _app = app;
            ;
            _fileManager = new DiskFileManager(app.Location);
        }
 private static async Task <IEnumerable <ApplicationInfo> > GetApplicationDetailsAsync(XDeployAPI api, IEnumerable <ApplicationInfo> apps)
 {
     foreach (var app in apps)
     {
         try
         {
             var anon    = new { encrypted = false, preDeployment = string.Empty, postDeployment = string.Empty };
             var details = JsonConvert.DeserializeAnonymousType(await api.GetAppDetailsAsync(app.ID), anon);
             app.Encrypted         = details.encrypted;
             app.PredeployActions  = details.preDeployment?.ToString().Replace("\r", string.Empty).Split('\n').ToList();
             app.PostdeployActions = details.postDeployment?.ToString().Replace("\r", string.Empty).Split('\n').ToList();
         }
         catch (Exception e)
         {
             throw new StartupException($"Invalid app ID \"{app.ID}\" or unauthorized IP.", e);
         }
         if (!Directory.Exists(app.Location))
         {
             throw new StartupException($"Invalid application path: {app.Location}", new DirectoryNotFoundException(app.Location));
         }
     }
     return(apps);
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="ApplicationDeployer"/> class.
 /// </summary>
 /// <param name="api">The API.</param>
 /// <param name="app">The application.</param>
 public ApplicationDeployer(XDeployAPI api, ApplicationInfo app) : base(api, app)
 {
 }