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."); } }
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)); } } }
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."); } }
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)); } } }
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); } } }
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)); } } }
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); }
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); }
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); }
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); }
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; } }