private void LoadPatch(
            IIncludeContext includeContext,
            string path,
            List <PolicyPatch> policySet,
            JObject patch)
        {
            PolicyPatch p = patch.ToObject <PolicyPatch>();

            p.ConditionDelegates = _conditionDelegates;

            string patchPath = path;

            p.PatchSourcePath = Path.GetRelativePath(includeContext.IncludeRoot, path) + "=>" + patch.Path;

            // ref patches will be added in a nested call in the else statement
            if (p.IncludeFile != null)
            {
                string includePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(patchPath), p.IncludeFile));

                LoadPatchFile(includeContext, includePath, File.ReadAllText(includePath), policySet);
            }
            else
            {
                policySet.Add(p);
            }
        }
        private void LoadPatchFile(
            IIncludeContext includeContext,
            string path,
            string json,
            List <PolicyPatch> policySet)
        {
            JToken token = JToken.Parse(json);

            if (token is JArray patchArray)
            {
                patchArray
                .Children <JObject>()
                .ToList()
                .ForEach(patch =>
                {
                    LoadPatch(includeContext, path, policySet, patch);
                });

                return;
            }

            if (token is JObject patchObj)
            {
                LoadPatch(includeContext, path, policySet, patchObj);
            }
        }
        public bool Build(IIncludeContext includeContext)
        {
            CleanPreviousOutput(includeContext);

            _logMessageSink.Message($"{nameof(includeContext.OutputRoot)}:{Path.GetFullPath(includeContext.OutputRoot)}");
            _logMessageSink.Message($"{nameof(includeContext.ConfigRoot)}:{Path.GetFullPath(includeContext.ConfigRoot)}");
            _logMessageSink.Message($"{nameof(includeContext.IncludeRoot)}:{Path.GetFullPath(includeContext.IncludeRoot)}");
            _logMessageSink.Message($"Resolved include paths: \n{string.Join("\n", includeContext.ResolvedIncludeRoots)}");

            Stopwatch sw = new Stopwatch();

            _logMessageSink.Message("Beginning generation process...");

            sw.Start();

            List <Task> tasks = new List <Task>();

            // The TransformationContext has caching capabilities that are based on a concurrent dictionary.
            // Create it here to share things like resolved files while applying transformations to different output files
            ConcurrentDictionary <string, object> objectCache = new ConcurrentDictionary <string, object>();

            foreach (string role in _environmentDescription.Roles)
            {
                foreach (StageDefinition stage in _environmentDescription.Stages)
                {
                    foreach (ServerDefinition server in stage.Servers)
                    {
                        if (!server.Roles.Contains(role))
                        {
                            continue;
                        }

                        if (!Directory.Exists(server.OutputPath))
                        {
                            Directory.CreateDirectory(server.OutputPath);
                        }

                        if (_initConfig.Debug)
                        {
                            PerformTransformations(objectCache, includeContext, server, role, stage);
                            continue;
                        }

                        tasks.Add(Task.Run(() =>
                        {
                            PerformTransformations(objectCache, includeContext, server, role, stage);
                        }));
                    }
                }
            }

            if (!_initConfig.Debug)
            {
                Task.WaitAll(tasks.ToArray());
            }

            sw.Stop();

            return(DetermineResult(sw));
        }
 private static void CleanPreviousOutput(IIncludeContext includeContext)
 {
     if (Directory.Exists(includeContext.OutputRoot))
     {
         Directory.Delete(includeContext.OutputRoot, true);
         Directory.CreateDirectory(includeContext.OutputRoot);
     }
 }
        private void PerformTransformations(
            ConcurrentDictionary <string, object> objectCache,
            IIncludeContext includeContext,
            ServerDefinition server,
            string role,
            StageDefinition stage)
        {
            _logMessageSink.EnterBlock(sink =>
            {
                string environmentPath = Path.Combine(server.OutputPath, $"{role}.json");

                try
                {
                    sink.Message($"------------------------------------------------------------------------------------------------------\nStarting {environmentPath}");

                    TargetingInformation targetingInformation = new TargetingInformation
                    {
                        Replication = server.Replication,
                        Role        = role,
                        Stage       = stage.Stage,
                        Server      = server.Server
                    };

                    // This needs to be created per file creation thread
                    TransformationContext transformationContext = new TransformationContext();

                    // use the shared cache created in the wrapping service method to share resource reads.
                    transformationContext.Cache = objectCache;

                    PatchingTemplate template = _templateProvider.GetTemplate(transformationContext, targetingInformation.Role);
                    JArray runtimeTemplate    = JArray.Parse(template.String);

                    transformationContext.SetPluginPolicy(includeContext);
                    transformationContext.SetPluginPolicy(targetingInformation);

                    transformationContext.SetPluginPolicy(sink);

                    _transformationEngine.EvaluateMakros(transformationContext, runtimeTemplate);

                    File.WriteAllText(environmentPath, JsonConvert.SerializeObject(runtimeTemplate, Formatting.Indented));

                    sink.Message($"Finished {environmentPath}\n------------------------------------------------------------------------------------------------------");
                }
                catch (Exception e)
                {
                    sink.Error($"Error while generating {environmentPath}...\n{e.ToString()}");
                }
            });
        }
 public IEnumerable <IPolicyPatch> GetTargetedPatches(
     ITransformationContext transformationContext,
     IIncludeContext includeContext,
     string patchExtension)
 {
     return(CollectFromIncludeLocations(
                transformationContext,
                includeContext,
                patchExtension)
            // patch applies by targeting information
            .Where(p => p.CanApply(transformationContext))
            // and has additional conditions (match property)
            // this indicates that this is not a base patch
            .Where(p => p.Match?.Properties().Any() ?? false));
 }
 public IEnumerable <IPolicyPatch> GetBasePatches(
     ITransformationContext transformationContext,
     IIncludeContext includeContext,
     string patchExtension)
 {
     return(CollectFromIncludeLocations(
                transformationContext,
                includeContext,
                patchExtension)
            .Where(p => p.CanApply(transformationContext))
            .Where(p => !p.Match?.Properties()
                   .Any() ??
                   false)
            .ToArray());
 }
        public bool TryHandle(ITransformationContext transformationContext, JToken makro, string[] makroArguments)
        {
            if (makroArguments.Length != 2 || makroArguments[0] != "PATCH")
            {
                return(false);
            }

            JArray array = (JArray)makro.Parent;

            int index = array.IndexOf(makro);

            array.RemoveAt(index);

            string extensionToken = makroArguments[1];

            IIncludeContext includeContext = transformationContext.GetPluginPolicy <IIncludeContext>();

            ILogMessageSink logger = transformationContext.GetPluginPolicy <ILogMessageSink>();

            IEnumerable <(IPolicyPatch, IEnumerable <JObject>)> basePatches =
                _patchCollector
                .GetBasePatches(transformationContext, includeContext, $"{extensionToken}.json")
                .Select(p => (p, p.GetPolicies()));

            foreach ((IPolicyPatch, IEnumerable <JObject>)patch in basePatches)
            {
                foreach (JObject policy in patch.Item2)
                {
                    string sourceTerm = CreateSourceTerm(policy, patch.Item1.PatchSourcePath, "Base");
                    AppendPatchSource(policy, sourceTerm);
                    logger.Message(sourceTerm);

                    array.Insert(index, policy);
                    index++;
                }
            }

            IEnumerable <IPolicyPatch> targetedPatches =
                _patchCollector
                .GetTargetedPatches(transformationContext, includeContext, $"{extensionToken}.json");

            foreach (IPolicyPatch policy in targetedPatches)
            {
                ApplyPatch(logger, policy, array);
            }

            return(true);
        }
 private ImmutableSortedDictionary <string, string> GetPatchCache(
     ITransformationContext transformationContext,
     IIncludeContext includeContext,
     string patchToken)
 {
     return(transformationContext
            .GetOrAddCacheEntry(
                includeContext.IncludeRoot + "|" + patchToken,
                () => includeContext
                .ResolvedIncludeRoots
                .SelectMany(r => Directory.GetFiles(r, $"*{patchToken}", SearchOption.AllDirectories))
                .Distinct()
                .Select(path => (path, File.ReadAllText(path)))
                .OrderBy(p => Path.GetRelativePath(includeContext.IncludeRoot, p.Item1))
                .ToImmutableSortedDictionary(k => k.Item1, v => v.Item2)));
 }
        private IEnumerable <IPolicyPatch> CollectFromIncludeLocations(
            ITransformationContext transformationContext,
            IIncludeContext includeContext,
            string patchToken)
        {
            List <PolicyPatch> policySet = new List <PolicyPatch>();

            ImmutableSortedDictionary <string, string> patches = GetPatchCache(transformationContext, includeContext, patchToken);

            foreach (KeyValuePair <string, string> file in patches)
            {
                LoadPatchFile(includeContext, file.Key, file.Value, policySet);
            }

            return(policySet);
        }