Пример #1
0
        private void Initialize(IPreprocessedRule preprocessedRule)
        {
            RuleDirectives       = preprocessedRule;
            ImpersonateExecution = RuleDirectives.Impersonate;

            var references = new HashSet <Assembly>(DefaultAssemblyReferences().Concat(RuleDirectives.LoadAssemblyReferences()));
            var imports    = new HashSet <string>(DefaultImports().Concat(RuleDirectives.Imports));

            var scriptOptions = ScriptOptions.Default
                                .WithEmitDebugInformation(true)
                                .WithReferences(references)
                                // Add namespaces
                                .WithImports(imports);

            if (RuleDirectives.IsCSharp())
            {
                _roslynScript = CSharpScript.Create <string>(
                    code: RuleDirectives.GetRuleCode(),
                    options: scriptOptions,
                    globalsType: typeof(RuleExecutionContext));
            }
            else
            {
                _logger.WriteError($"Cannot execute rule: language is not supported.");
            }
        }
Пример #2
0
        internal async Task <string> ExecuteAsync(dynamic data, CancellationToken cancellationToken)
        {
            string collectionUrl = data.resourceContainers.collection.baseUrl;
            string eventType     = data.eventType;
            int    workItemId    = (eventType != "workitem.updated")
                ? data.resource.id
                : data.resource.workItemId;
            Guid   teamProjectId   = data.resourceContainers.project.id;
            string teamProjectName = (eventType != "workitem.updated")
                ? data.resource.fields["System.TeamProject"]
                : data.resource.revision.fields["System.TeamProject"];

            logger.WriteVerbose($"Connecting to Azure DevOps using {configuration.DevOpsTokenType}...");
            var clientCredentials = default(VssCredentials);

            if (configuration.DevOpsTokenType == DevOpsTokenType.PAT)
            {
                clientCredentials = new VssBasicCredential(configuration.DevOpsTokenType.ToString(), configuration.DevOpsToken);
            }
            else
            {
                logger.WriteError($"Azure DevOps Token type {configuration.DevOpsTokenType} not supported!");
                throw new ArgumentOutOfRangeException(nameof(configuration.DevOpsTokenType));
            }

            cancellationToken.ThrowIfCancellationRequested();

            // TODO improve from https://github.com/Microsoft/vsts-work-item-migrator
            using (var devops = new VssConnection(new Uri(collectionUrl), clientCredentials))
            {
                await devops.ConnectAsync(cancellationToken);

                logger.WriteInfo($"Connected to Azure DevOps");
                using (var witClient = devops.GetClient <WorkItemTrackingHttpClient>())
                {
                    string ruleFilePath = Path.Combine(functionDirectory, $"{ruleName}.rule");
                    if (!File.Exists(ruleFilePath))
                    {
                        logger.WriteError($"Rule code not found at {ruleFilePath}");
                        return("Rule file not found!");
                    }

                    logger.WriteVerbose($"Rule code found at {ruleFilePath}");
                    string[] ruleCode;
                    using (var fileStream = File.OpenRead(ruleFilePath))
                    {
                        var reader = new StreamReader(fileStream);
                        ruleCode = await ReadAllLinesAsync(reader);
                    }

                    var engine = new Engine.RuleEngine(logger, ruleCode, configuration.SaveMode, configuration.DryRun);

                    return(await engine.ExecuteAsync(teamProjectId, teamProjectName, workItemId, witClient, cancellationToken));
                }
            }
        }
Пример #3
0
 public async Task <string> ExecuteAsync(IRule rule, WorkItemEventContext eventContext, CancellationToken cancellationToken)
 {
     logger.WriteVerbose($"Connecting to Azure DevOps using {configuration.DevOpsTokenType}...");
     if (configuration.DevOpsTokenType == DevOpsTokenType.PAT)
     {
         var clientCredentials = new VssBasicCredential(configuration.DevOpsTokenType.ToString(), configuration.DevOpsToken);
         // see https://rules.sonarsource.com/csharp/RSPEC-4457
         return(await ExecAsyncImpl(rule, eventContext, clientCredentials, cancellationToken));
     }
     else
     {
         logger.WriteError($"Azure DevOps Token type {configuration.DevOpsTokenType} not supported!");
         throw new ArgumentException($"Azure DevOps Token type {configuration.DevOpsTokenType} not supported.");
     }
 }
Пример #4
0
        internal async Task <string> Execute(dynamic data)
        {
            string collectionUrl = data.resourceContainers.collection.baseUrl;
            string eventType     = data.eventType;
            int    workItemId    = (eventType != "workitem.updated")
                ? data.resource.id
                : data.resource.workItemId;
            Guid   teamProjectId   = data.resourceContainers.project.id;
            string teamProjectName = (eventType != "workitem.updated")
                ? data.resource.fields["System.TeamProject"]
                : data.resource.revision.fields["System.TeamProject"];

            logger.WriteVerbose($"Connecting to Azure DevOps using {configuration.DevOpsTokenType}...");
            var clientCredentials = default(VssCredentials);

            if (configuration.DevOpsTokenType == DevOpsTokenType.PAT)
            {
                clientCredentials = new VssBasicCredential(configuration.DevOpsTokenType.ToString(), configuration.DevOpsToken);
            }
            else
            {
                logger.WriteError($"Azure DevOps Token type {configuration.DevOpsTokenType} not supported!");
                throw new ArgumentOutOfRangeException(nameof(configuration.DevOpsTokenType));
            }

            using (var devops = new VssConnection(new Uri(collectionUrl), clientCredentials))
            {
                await devops.ConnectAsync();

                logger.WriteInfo($"Connected to Azure DevOps");
                using (var witClient = devops.GetClient <WorkItemTrackingHttpClient>())
                {
                    string ruleFilePath = Path.Combine(functionDirectory, $"{ruleName}.rule");
                    if (!File.Exists(ruleFilePath))
                    {
                        logger.WriteError($"Rule code not found at {ruleFilePath}");
                        return("Rule file not found!");
                    }

                    logger.WriteVerbose($"Rule code found at {ruleFilePath}");
                    string[] ruleCode = File.ReadAllLines(ruleFilePath);

                    var engine = new Engine.RuleEngine(logger, ruleCode, configuration.SaveMode);

                    return(await engine.ExecuteAsync(collectionUrl, teamProjectId, teamProjectName, configuration.DevOpsToken, workItemId, witClient));
                }
            }
        }
Пример #5
0
        public async Task <string> ExecuteAsync(IRule rule, WorkItemEventContext eventContext, CancellationToken cancellationToken)
        {
            logger.WriteVerbose($"Connecting to Azure DevOps using {configuration.DevOpsTokenType}...");
            var clientCredentials = default(VssCredentials);

            if (configuration.DevOpsTokenType == DevOpsTokenType.PAT)
            {
                clientCredentials = new VssBasicCredential(configuration.DevOpsTokenType.ToString(), configuration.DevOpsToken);
            }
            else
            {
                logger.WriteError($"Azure DevOps Token type {configuration.DevOpsTokenType} not supported!");
                throw new ArgumentOutOfRangeException(nameof(configuration.DevOpsTokenType));
            }

            cancellationToken.ThrowIfCancellationRequested();

            // TODO improve from https://github.com/Microsoft/vsts-work-item-migrator
            using (var devops = new VssConnection(eventContext.CollectionUri, clientCredentials))
            {
                await devops.ConnectAsync(cancellationToken);

                logger.WriteInfo($"Connected to Azure DevOps");
                using (var clientsContext = new AzureDevOpsClientsContext(devops))
                {
                    var engine = new RuleEngine(logger, configuration.SaveMode, configuration.DryRun);

                    var ruleResult = await engine.RunAsync(rule, eventContext.ProjectId, eventContext.WorkItemPayload, clientsContext, cancellationToken);

                    logger.WriteInfo(ruleResult);
                    return(ruleResult);
                }
            }
        }
Пример #6
0
        internal async Task <string> ExecuteAsync(WorkItemEventContext eventContext, CancellationToken cancellationToken)
        {
            logger.WriteVerbose($"Connecting to Azure DevOps using {configuration.DevOpsTokenType}...");
            var clientCredentials = default(VssCredentials);

            if (configuration.DevOpsTokenType == DevOpsTokenType.PAT)
            {
                clientCredentials = new VssBasicCredential(configuration.DevOpsTokenType.ToString(), configuration.DevOpsToken);
            }
            else
            {
                logger.WriteError($"Azure DevOps Token type {configuration.DevOpsTokenType} not supported!");
                throw new ArgumentOutOfRangeException(nameof(configuration.DevOpsTokenType));
            }

            cancellationToken.ThrowIfCancellationRequested();

            // TODO improve from https://github.com/Microsoft/vsts-work-item-migrator
            using (var devops = new VssConnection(eventContext.CollectionUri, clientCredentials))
            {
                await devops.ConnectAsync(cancellationToken);

                logger.WriteInfo($"Connected to Azure DevOps");
                using (var clientsContext = new AzureDevOpsClientsContext(devops))
                {
                    string ruleFilePath = Path.Combine(functionDirectory, $"{ruleName}.rule");
                    if (!File.Exists(ruleFilePath))
                    {
                        logger.WriteError($"Rule code not found at {ruleFilePath}");
                        return("Rule file not found!");
                    }

                    logger.WriteVerbose($"Rule code found at {ruleFilePath}");
                    string[] ruleCode;
                    using (var fileStream = File.OpenRead(ruleFilePath))
                    {
                        var reader = new StreamReader(fileStream);
                        ruleCode = await ReadAllLinesAsync(reader);
                    }

                    var engine = new Engine.RuleEngine(logger, ruleCode, configuration.SaveMode, configuration.DryRun);

                    return(await engine.ExecuteAsync(eventContext.ProjectId, eventContext.WorkItemPayload, clientsContext, cancellationToken));
                }
            }
        }
Пример #7
0
        public async Task <string> ExecuteAsync(string collectionUrl, Guid projectId, string projectName, string personalAccessToken, int workItemId, WorkItemTrackingHttpClientBase witClient, CancellationToken cancellationToken)
        {
            if (State == EngineState.Error)
            {
                return(string.Empty);
            }

            var context = new EngineContext(witClient, projectId, projectName, personalAccessToken, logger);
            var store   = new WorkItemStore(context);
            var self    = store.GetWorkItem(workItemId);

            logger.WriteInfo($"Initial WorkItem {workItemId} retrieved from {collectionUrl}");

            var globals = new Globals
            {
                self  = self,
                store = store
            };

            logger.WriteInfo($"Executing Rule...");
            var result = await roslynScript.RunAsync(globals, cancellationToken);

            if (result.Exception != null)
            {
                logger.WriteError($"Rule failed with {result.Exception}");
                State = EngineState.Error;
            }
            else if (result.ReturnValue != null)
            {
                logger.WriteInfo($"Rule succeeded with {result.ReturnValue}");
            }
            else
            {
                logger.WriteInfo($"Rule succeeded, no return value");
            }

            State = EngineState.Success;

            logger.WriteVerbose($"Post-execution, save any change (mode {saveMode})...");
            var saveRes = await store.SaveChanges(saveMode, !DryRun, cancellationToken);

            if (saveRes.created + saveRes.updated > 0)
            {
                logger.WriteInfo($"Changes saved to Azure DevOps (mode {saveMode}): {saveRes.created} created, {saveRes.updated} updated.");
            }
            else
            {
                logger.WriteInfo($"No changes saved to Azure DevOps.");
            }

            return(result.ReturnValue);
        }
Пример #8
0
        public async Task <string> ExecuteAsync(Guid projectId, WorkItemData workItemPayload, IClientsContext clients, CancellationToken cancellationToken)
        {
            if (State == EngineState.Error)
            {
                return(string.Empty);
            }

            var workItem    = workItemPayload.WorkItem;
            var context     = new EngineContext(clients, projectId, workItem.GetTeamProject(), logger);
            var store       = new WorkItemStore(context, workItem);
            var self        = store.GetWorkItem(workItem.Id.Value);
            var selfChanges = new WorkItemUpdateWrapper(workItemPayload.WorkItemUpdate);

            logger.WriteInfo($"Initial WorkItem {self.Id} retrieved from {clients.WitClient.BaseAddress}");

            var globals = new Globals
            {
                self        = self,
                selfChanges = selfChanges,
                store       = store,
                logger      = logger
            };

            logger.WriteInfo($"Executing Rule...");
            var result = await roslynScript.RunAsync(globals, cancellationToken);

            if (result.Exception != null)
            {
                logger.WriteError($"Rule failed with {result.Exception}");
                State = EngineState.Error;
            }
            else
            {
                logger.WriteInfo($"Rule succeeded with {result.ReturnValue ?? "no return value"}");
                State = EngineState.Success;
            }

            logger.WriteVerbose($"Post-execution, save any change (mode {saveMode})...");
            var saveRes = await store.SaveChanges(saveMode, !DryRun, cancellationToken);

            if (saveRes.created + saveRes.updated > 0)
            {
                logger.WriteInfo($"Changes saved to Azure DevOps (mode {saveMode}): {saveRes.created} created, {saveRes.updated} updated.");
            }
            else
            {
                logger.WriteInfo($"No changes saved to Azure DevOps.");
            }

            return(result.ReturnValue);
        }
Пример #9
0
        public RuleEngine(IAggregatorLogger logger, string[] ruleCode, SaveMode mode, bool dryRun)
        {
            State = EngineState.Unknown;

            this.logger   = logger;
            this.saveMode = mode;
            this.DryRun   = dryRun;

            var directives = new DirectivesParser(logger, ruleCode);

            if (!directives.Parse())
            {
                State = EngineState.Error;
                return;
            }

            if (directives.Language == DirectivesParser.Languages.Csharp)
            {
                var types = new List <Type>()
                {
                    typeof(object),
                    typeof(System.Linq.Enumerable),
                    typeof(System.Collections.Generic.CollectionExtensions),
                    typeof(Microsoft.VisualStudio.Services.WebApi.IdentityRef)
                };
                var references = types.ConvertAll(t => t.Assembly).Distinct();

                var scriptOptions = ScriptOptions.Default
                                    .WithEmitDebugInformation(true)
                                    .WithReferences(references)
                                    // Add namespaces
                                    .WithImports(
                    "System",
                    "System.Linq",
                    "System.Collections.Generic",
                    "Microsoft.VisualStudio.Services.WebApi"
                    );

                this.roslynScript = CSharpScript.Create <string>(
                    code: directives.GetRuleCode(),
                    options: scriptOptions,
                    globalsType: typeof(Globals));
            }
            else
            {
                logger.WriteError($"Cannot execute rule: language is not supported.");
                State = EngineState.Error;
            }
        }
        private string GetRuleFilePath(string ruleName)
        {
            bool IsRequestedRule(IFileInfo info)
            {
                return(string.Equals(ruleName, Path.GetFileNameWithoutExtension(info.Name), StringComparison.OrdinalIgnoreCase));
            }

            string ruleFilePath = null;
            string rulesPath    = _configuration.GetValue <string>("Aggregator_RulesPath");

            if (string.IsNullOrEmpty(rulesPath))
            {
                rulesPath = _configuration.GetValue <string>(WebHostDefaults.ContentRootKey);
                _logger.WriteVerbose($"Searching '{ruleName}' in {rulesPath}");
                var provider = new PhysicalFileProvider(rulesPath);
                var contents = provider.GetDirectoryContents(SCRIPT_RULE_DIRECTORY).Where(f => f.Name.EndsWith(SCRIPT_RULE_NAME_PATTERN));

                ruleFilePath = contents.First(IsRequestedRule)?.PhysicalPath;
            }
            else
            {
                _logger.WriteVerbose($"Searching '{ruleName}' in {rulesPath}");
                string ruleFullPath = Path.Combine(rulesPath, $"{ruleName}{SCRIPT_RULE_NAME_PATTERN}");
                ruleFilePath = File.Exists(ruleFullPath) ? ruleFullPath : null;
            }

            if (ruleFilePath == null)
            {
                var errorMsg     = $"Rule code file '{ruleName}{SCRIPT_RULE_NAME_PATTERN}' not found at expected Path {rulesPath}";
                var ruleNotFound = new EventTelemetry()
                {
                    Name = "Rule code file not found",
                };
                ruleNotFound.Properties["rule"] = ruleName;
                Telemetry.TrackEvent(ruleNotFound);
                _logger.WriteError(errorMsg);
                throw new FileNotFoundException(errorMsg);
            }

            _logger.WriteVerbose($"Rule code found at {ruleFilePath}");
            return(ruleFilePath);
        }
Пример #11
0
        private string GetRuleFilePath(string ruleName)
        {
            bool IsRequestedRule(string filePath)
            {
                return(string.Equals(ruleName, Path.GetFileNameWithoutExtension(filePath), StringComparison.OrdinalIgnoreCase));
            }

            var ruleFilePath = Directory.EnumerateFiles(_rulesPath, SCRIPT_RULE_NAME_PATTERN, SearchOption.TopDirectoryOnly)
                               .First(IsRequestedRule);

            if (ruleFilePath == null)
            {
                var errorMsg = $"Rule code file '{ruleName}.rule' not found at expected Path {_rulesPath}";
                _logger.WriteError(errorMsg);
                throw new FileNotFoundException(errorMsg);
            }

            _logger.WriteVerbose($"Rule code found at {ruleFilePath}");
            return(ruleFilePath);
        }
Пример #12
0
        public RuleEngine(IAggregatorLogger logger, string[] ruleCode, SaveMode mode, bool dryRun)
        {
            State = EngineState.Unknown;

            this.logger   = logger;
            this.saveMode = mode;
            this.DryRun   = dryRun;

            var directives = new DirectivesParser(logger, ruleCode);

            if (!directives.Parse())
            {
                State = EngineState.Error;
                return;
            }

            if (directives.Language == DirectivesParser.Languages.Csharp)
            {
                var references = LoadReferences(directives);
                var imports    = GetImports(directives);

                var scriptOptions = ScriptOptions.Default
                                    .WithEmitDebugInformation(true)
                                    .WithReferences(references)
                                    // Add namespaces
                                    .WithImports(imports)
                ;

                this.roslynScript = CSharpScript.Create <string>(
                    code: directives.GetRuleCode(),
                    options: scriptOptions,
                    globalsType: typeof(Globals));
            }
            else
            {
                logger.WriteError($"Cannot execute rule: language is not supported.");
                State = EngineState.Error;
            }
        }