diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..1ff0c423042b46cb1d617b81efb715defbe8054d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2df1da5b5077d597142e85e19812ba900a4e81c --- /dev/null +++ b/.gitignore @@ -0,0 +1,270 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Database connection for t4 class generation from database +**/*.generated.cs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user +Resharper + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +#cake +tools/* +!tools/packages.config +dist/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..b339a45c43a1dcb4b80ab99e6224bc7232abd723 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,55 @@ +stages: + - test + - release + - releasetrigger + +cake:Test: + stage: test + script: + - PowerShell .\build.ps1 -Target Test -Configuration Debug + variables: + GIT_STRATEGY: clone + artifacts: + reports: + junit: "./Artifacts/TestResults.xml" + paths: + - "./Artifacts/*" + except: + - master + - tags + +cake:Release: + stage: release + script: + - PowerShell .\build.ps1 -Target Release -Configuration Release --nugetApiKey="${NUGET_API_KEY}" + variables: + GIT_STRATEGY: clone + dependencies: + - cake:Test + artifacts: + paths: + - "./Artifacts/*" + only: + - tags + +cake:Prerelease: + stage: release + script: + - PowerShell .\build.ps1 -Target Prerelease -Configuration Release + variables: + GIT_STRATEGY: clone + dependencies: + - cake:Test + artifacts: + paths: + - "./Artifacts/*" + except: + - tags + - master + +cake:GitlabRelease: + stage: releasetrigger + script: + - PowerShell .\build.ps1 -Target GitlabRelease --GitlabProjectPath="${CI_PROJECT_PATH}" --gitlabProjectId="${CI_PROJECT_ID}" --gitlabToken="${GITLAB_TOKEN}" + only: + - master \ No newline at end of file diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000000000000000000000000000000000000..1bdc0fe219f5f35d8a024a66d9fd715f44539640 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,7 @@ +mode: ContinuousDeployment +next-version: 1.0.0 +major-version-bump-message: 'Breaking:' +minor-version-bump-message: '(Update|New):' +patch-version-bump-message: 'Fix:' +no-bump-message: '.*' +commit-message-incrementing: Enabled \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..b8cd48a4db77aeee8a31fbec89cd7c8c4fb0b5ff --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 RWTH Aachen University + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/build.cake b/build.cake new file mode 100644 index 0000000000000000000000000000000000000000..81648ff61bbee492e1bc7b4390eb8e0cc33d3056 --- /dev/null +++ b/build.cake @@ -0,0 +1,351 @@ +#tool nuget:?package=NUnit.ConsoleRunner&version=3.10.0 +#tool nuget:?package=vswhere&version=2.8.4 +#tool nuget:?package=GitVersion.CommandLine&version=5.1.3 + +#addin nuget:https://api.nuget.org/v3/index.json?package=Cake.Json&version=4.0.0 +#addin nuget:https://api.nuget.org/v3/index.json?package=Newtonsoft.Json&version=11.0.2 +#addin nuget:https://api.nuget.org/v3/index.json?package=Cake.FileHelpers&version=3.2.1 + +using System.Net; +using System.Net.Http; + +// Commandline arguments +var target = Argument("target", "Default"); +var configuration = Argument("configuration", "Release"); +var nugetApiKey = Argument<string>("nugetApiKey", null); +var version = Argument("nugetVersion", ""); +var gitlabProjectPath = Argument("gitlabProjectPath", ""); +var gitlabProjectId = Argument("gitlabProjectId", ""); +var gitlabToken = Argument("gitlabToken", ""); + +// Define directories +var projects = GetFiles("./**/*.csproj"); +var artifactsDir = Directory("./Artifacts"); +string nupkgDir; +var solutionFile = GetFiles("./**/*.sln").First(); +var projectName = solutionFile.GetFilenameWithoutExtension().ToString(); +var nugetSource = "https://api.nuget.org/v3/index.json"; +var assemblyInfoSubPath = "Properties/AssemblyInfo.cs"; +var semanticVersion = ""; +string localNugetFeed; + +// get latest MSBuild version +var vsLatest = VSWhereLatest(); +var msBuildPathX64 = (vsLatest == null) ? null : vsLatest.CombineWithFilePath("./MSBuild/Current/Bin/MSBuild.exe"); + +Setup(context =>{ + nupkgDir = $"{artifactsDir.ToString()}/nupkg"; + var branch = GitVersion(new GitVersionSettings { + UpdateAssemblyInfo = false + }).BranchName.Replace("/", "-"); + + localNugetFeed = $"C:\\coscine\\LocalNugetFeeds\\{branch}"; + Information("{0}", branch); + Information("Started at {0}", DateTime.Now); +}); + +Teardown(context =>{ + Information("Finished at {0}", DateTime.Now); +}); + +Task("Clean") +.Description("Cleans all build and artifacts directories") +.Does(() =>{ + var settings = new DeleteDirectorySettings { + Recursive = true, + Force = true + }; + + var directoriesToClean = new List<DirectoryPath>(); + + foreach(var project in projects) { + directoriesToClean.Add(Directory($"{project.GetDirectory()}/obj")); + directoriesToClean.Add(Directory($"{project.GetDirectory()}/bin")); + } + + directoriesToClean.Add(artifactsDir); + + foreach(var dir in directoriesToClean) { + Information("Cleaning {0}", dir.ToString()); + if (DirectoryExists(dir)) { + DeleteDirectory(dir, settings); + CreateDirectory(dir); + } else { + CreateDirectory(dir); + } + } +}); + +Task("Restore") +.Does(() =>{ + NuGetRestore(solutionFile, new NuGetRestoreSettings { + NoCache = true, + FallbackSource = new List<string>{ localNugetFeed }, + }); +}); + +Task("Test") +.IsDependentOn("Build") +.Does(() =>{ + NUnit3($"./src/**/bin/{configuration}/*.Tests.dll", new NUnit3Settings { + // generate the xml file + NoResults = false, + Results = new NUnit3Result[] { + new NUnit3Result() { + FileName = $"{artifactsDir}/TestResults.xml", + Transform = $"{Context.Environment.WorkingDirectory}/nunit3-junit.xslt" + } + } + }); +}); + +Task("GitVersion") +.Does(() => { + if(string.IsNullOrWhiteSpace(version)) { + version = GitVersion(new GitVersionSettings { + UpdateAssemblyInfo = false + }).NuGetVersionV2; + } + var index = version.IndexOf("-"); + semanticVersion = index > 0 ? version.Substring(0, index) : version; + Information("Version: {0}, SemanticVersion: {1}", version, semanticVersion); +}); + +Task("UpdateAssemblyInfo") +.Does(() =>{ + var index = version.IndexOf("-"); + var semanticVersion = index > 0 ? version.Substring(0, index) : version; + + foreach(var project in projects) { + CreateAssemblyInfo($"{project.GetDirectory()}/{assemblyInfoSubPath}", new AssemblyInfoSettings { + Product = project.GetFilenameWithoutExtension().ToString(), + Title = project.GetFilenameWithoutExtension().ToString(), + Company = "IT Center, RWTH Aachen University", + Version = semanticVersion, + FileVersion = semanticVersion, + InformationalVersion = version, + Copyright = $"{DateTime.Now.Year} IT Center, RWTH Aachen University", + Description = $"{project.GetFilenameWithoutExtension().ToString()} is a part of the CoScInE group." + }); + } +}); + +Task("GitlabRelease") +.IsDependentOn("GitVersion") +.Does(() => { + var client = new HttpClient(); + client.DefaultRequestHeaders.Add("PRIVATE-TOKEN", gitlabToken); + + // get the latest tag + var result = client.GetAsync($"https://git.rwth-aachen.de/api/v4/projects/{gitlabProjectId}/repository/tags").Result; + if(!result.IsSuccessStatusCode) { + throw new Exception("Tag query failed."); + } + + var tagList = result.Content.ReadAsStringAsync().Result; + var jArray = JArray.Parse(tagList); + // null if not tags exists yet + var lastTag = jArray.Select(x => x["name"]).FirstOrDefault(); + + var url = $"https://git.rwth-aachen.de/{gitlabProjectPath}"; + + if(url.EndsWith(".git")) { + url = url.Substring(0, url.Length - ".git".Length); + } + + if(url.EndsWith("/")) { + url = url.Substring(0, url.Length - 1); + } + + var description = ""; + // First line of description + // Gitlab compare url, if something can be compared + if(lastTag == null) { + description = $"# {semanticVersion} ({DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day})\n\n\n"; + } else { + description = $"# [{semanticVersion}]({url}/compare/{lastTag}...v{semanticVersion}) ({DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day})\n\n\n"; + } + + // From when will messages be parsed, null results in all messages + var logParam = ""; + if(lastTag != null) { + logParam = $"{lastTag}..Head"; + } + + Information(lastTag); + + IEnumerable<string> redirectedStandardOutput; + var exitCodeWithArgument = + StartProcess( + "git", + new ProcessSettings { + Arguments = $"log {logParam} --pretty=format:HASH%h:%B", + RedirectStandardOutput = true + }, + out redirectedStandardOutput + ); + + var prefixList = new Dictionary<string, List<string>>{ + {"Fix", new List<string>()}, + {"Update", new List<string>()}, + {"New", new List<string>()}, + {"Breaking", new List<string>()}, + {"Docs", new List<string>()}, + {"Build", new List<string>()}, + {"Upgrade", new List<string>()}, + {"Chore", new List<string>()}, + }; + + var currentHash = ""; + // Output last line of process output. + foreach(var line in redirectedStandardOutput) { + var commitMessage = ""; + if(line.StartsWith("HASH")) { + currentHash = line.Substring("HASH".Length); + currentHash = currentHash.Substring(0, currentHash.IndexOf(":")); + commitMessage = line.Substring(currentHash.Length + line.IndexOf(currentHash) + 1); + } else { + commitMessage = line; + } + + foreach(var kv in prefixList) { + if(commitMessage.StartsWith($"{kv.Key}:")) { + kv.Value.Add($"* {commitMessage.Substring(kv.Key.Length + 1).Trim()} {currentHash}"); + break; + } + }; + } + + foreach(var kv in prefixList) { + if(kv.Value.Any()) { + description += $" ### {kv.Key}\n\n"; + foreach(var line in kv.Value) { + description += $"{line}\n"; + } + description += "\n"; + } + } + // correctly escape the json newlines + description = description.Replace("\n", "\\n"); + Information("Description: {0}", description); + + // create tag + result = client.PostAsync($"https://git.rwth-aachen.de/api/v4/projects/{gitlabProjectId}/repository/tags?tag_name=v{semanticVersion}&ref=master", null).Result; + Information("Create tag: {0}", result.Content.ReadAsStringAsync().Result); + if(!result.IsSuccessStatusCode) { + throw new Exception("Tag creation failed."); + } + + // create release + var json = $"{{\"name\": \"v{semanticVersion}\", \"tag_name\": \"v{semanticVersion}\", \"description\": \"{description}\"}}"; + var content = new StringContent(json, Encoding.UTF8, "application/json"); + result = client.PostAsync($"https://git.rwth-aachen.de/api/v4/projects/{gitlabProjectId}/releases", content).Result; + Information("Create release: {0}", result.Content.ReadAsStringAsync().Result); + if(!result.IsSuccessStatusCode) { + throw new Exception("Release creation failed."); + } +}); + +Task("Build") +.IsDependentOn("Clean") +.IsDependentOn("GitVersion") +.IsDependentOn("UpdateAssemblyInfo") +.IsDependentOn("Restore") +.Does(() =>{ + var frameworkSettingsWindows = new MSBuildSettings { + Configuration = configuration + }; + + frameworkSettingsWindows.ToolPath = msBuildPathX64; + frameworkSettingsWindows.WorkingDirectory = Context.Environment.WorkingDirectory; + + if (configuration.Equals("Release")) { + frameworkSettingsWindows.WithProperty("DebugSymbols", "false"); + frameworkSettingsWindows.WithProperty("DebugType", "None"); + } + + // Use MSBuild + Information("Building {0}", solutionFile); + MSBuild(solutionFile, frameworkSettingsWindows); +}); + +Task("NugetPack") +.IsDependentOn("Build") +.Does(() =>{ + foreach(var project in projects) { + var nuspec = $"{project.GetDirectory()}/{project.GetFilenameWithoutExtension()}.nuspec"; + if(!project.ToString().EndsWith(".Tests") && FileExists(nuspec)) + { + var settings = new NuGetPackSettings + { + OutputDirectory = nupkgDir, + Version = version, + Properties = new Dictionary<string, string> + { + { "Configuration", configuration} + } + }; + NuGetPack(project.ToString(), settings); + } + } +}); + +Task("NugetPush") +.IsDependentOn("NugetPack") +.Does(() =>{ + var nupkgs = GetFiles($"{nupkgDir}/*.nupkg"); + Information("Need to push {0} packages", nupkgs.Count); + if(!String.IsNullOrWhiteSpace(nugetApiKey)) { + foreach(var nupkg in nupkgs) { + Information("Pushing {0}", nupkg); + NuGetPush(nupkg, new NuGetPushSettings { + Source = nugetSource, + ApiKey = nugetApiKey + }); + } + } else { + Information("NugetApiKey is not set. Can't push."); + throw new Exception("NugetApiKey is not set. Can't push."); + } +}); + +Task("CopyToArtifacts") +.Does(() =>{ + foreach(var project in projects) { + if(!project.GetDirectory().ToString().EndsWith(".Tests") + && !FileExists($"{project.GetDirectory()}/{project.GetFilenameWithoutExtension()}.nuspec") + && DirectoryExists(project.GetDirectory())) + { + Information("Copying {0}/* to {1}", $"{project.GetDirectory()}/bin/{configuration}", artifactsDir); + CopyDirectory($"{project.GetDirectory()}/bin/{configuration}/", artifactsDir); + } + } +}); + +Task("NugetPushLocal") +.IsDependentOn("NugetPack") +.Does(() =>{ + var nupkgs = GetFiles($"{nupkgDir}/*.nupkg"); + foreach(var nupkg in nupkgs) { + if(!DirectoryExists(localNugetFeed)) { + CreateDirectory(localNugetFeed); + } + CopyFile(nupkg.ToString(), $"{localNugetFeed}\\{nupkg.GetFilename()}"); + } +}); + +Task("Prerelease") +.IsDependentOn("Build") +.IsDependentOn("CopyToArtifacts") +.IsDependentOn("NugetPushLocal"); + +Task("Release") +.IsDependentOn("NugetPack") +.IsDependentOn("CopyToArtifacts") +.IsDependentOn("NugetPushLocal") +.IsDependentOn("NugetPush"); + +Task("Default") +.IsDependentOn("Test"); + +RunTarget(target); diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..f83382e8e4092ba6cf32a47f9dfa4e211c878430 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,255 @@ +#The MIT License (MIT) +# +#Copyright (c) 2014 - 2016 Patrik Svensson, Mattias Karlsson, Gary Ewan Park and contributors +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in +#the Software without restriction, including without limitation the rights to +#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +#the Software, and to permit persons to whom the Software is furnished to do so, +#subject to the following conditions: +# +#The above copyright notice and this permission notice shall be included in all +#copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +#FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +#COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +#IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +########################################################################## +# This is the Cake bootstrapper script for PowerShell. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +<# + +.SYNOPSIS +This is a Powershell script to bootstrap a Cake build. + +.DESCRIPTION +This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) +and execute your Cake build script with the parameters you provide. + +.PARAMETER Script +The build script to execute. +.PARAMETER Target +The build script target to run. +.PARAMETER Configuration +The build configuration to use. +.PARAMETER Verbosity +Specifies the amount of information to be displayed. +.PARAMETER ShowDescription +Shows description about tasks. +.PARAMETER DryRun +Performs a dry run. +.PARAMETER Experimental +Uses the nightly builds of the Roslyn script engine. +.PARAMETER Mono +Uses the Mono Compiler rather than the Roslyn script engine. +.PARAMETER SkipToolPackageRestore +Skips restoring of packages. +.PARAMETER ScriptArgs +Remaining arguments are added here. + +.LINK +https://cakebuild.net + +#> + +[CmdletBinding()] +Param( + [string]$Script = "build.cake", + [string]$Target, + [string]$Configuration, + [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] + [string]$Verbosity, + [switch]$ShowDescription, + [Alias("WhatIf", "Noop")] + [switch]$DryRun, + [switch]$Experimental, + [switch]$Mono, + [switch]$SkipToolPackageRestore, + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$ScriptArgs +) + +[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null +function MD5HashFile([string] $filePath) +{ + if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) + { + return $null + } + + [System.IO.Stream] $file = $null; + [System.Security.Cryptography.MD5] $md5 = $null; + try + { + $md5 = [System.Security.Cryptography.MD5]::Create() + $file = [System.IO.File]::OpenRead($filePath) + return [System.BitConverter]::ToString($md5.ComputeHash($file)) + } + finally + { + if ($file -ne $null) + { + $file.Dispose() + } + } +} + +function GetProxyEnabledWebClient +{ + $wc = New-Object System.Net.WebClient + $proxy = [System.Net.WebRequest]::GetSystemWebProxy() + $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $wc.Proxy = $proxy + return $wc +} + +Write-Host "Preparing to run build script..." + +if(!$PSScriptRoot){ + $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent +} + +$TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" +$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" +$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" +$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" +$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" +$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" +$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" +$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" +$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" + +# Make sure tools folder exists +if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { + Write-Verbose -Message "Creating tools directory..." + New-Item -Path $TOOLS_DIR -Type directory | out-null +} + +# Make sure that packages.config exist. +if (!(Test-Path $PACKAGES_CONFIG)) { + Write-Verbose -Message "Downloading packages.config..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { + Throw "Could not download packages.config." + } +} + +# Try find NuGet.exe in path if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Trying to find nuget.exe in PATH..." + $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } + $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 + if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { + Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." + $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName + } +} + +# Try download NuGet.exe if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Downloading NuGet.exe..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile($NUGET_URL, $NUGET_EXE) + } catch { + Throw "Could not download NuGet.exe." + } +} + +# Save nuget.exe path to environment to be available to child processed +$ENV:NUGET_EXE = $NUGET_EXE + +# Restore tools from NuGet? +if(-Not $SkipToolPackageRestore.IsPresent) { + Push-Location + Set-Location $TOOLS_DIR + + # Check for changes in packages.config and remove installed tools if true. + [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) + if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or + ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { + Write-Verbose -Message "Missing or changed package.config hash..." + Remove-Item * -Recurse -Exclude packages.config,nuget.exe + } + + Write-Verbose -Message "Restoring tools from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet tools." + } + else + { + $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" + } + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore addins from NuGet +if (Test-Path $ADDINS_PACKAGES_CONFIG) { + Push-Location + Set-Location $ADDINS_DIR + + Write-Verbose -Message "Restoring addins from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet addins." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore modules from NuGet +if (Test-Path $MODULES_PACKAGES_CONFIG) { + Push-Location + Set-Location $MODULES_DIR + + Write-Verbose -Message "Restoring modules from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet modules." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Make sure that Cake has been installed. +if (!(Test-Path $CAKE_EXE)) { + Throw "Could not find Cake.exe at $CAKE_EXE" +} + + + +# Build Cake arguments +$cakeArguments = @("$Script"); +if ($Target) { $cakeArguments += "-target=$Target" } +if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } +if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } +if ($ShowDescription) { $cakeArguments += "-showdescription" } +if ($DryRun) { $cakeArguments += "-dryrun" } +if ($Experimental) { $cakeArguments += "-experimental" } +if ($Mono) { $cakeArguments += "-mono" } +$cakeArguments += $ScriptArgs + +# Start Cake +Write-Host "Running build script..." +&$CAKE_EXE $cakeArguments +exit $LASTEXITCODE diff --git a/build.sh b/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..d088917ed78538ff57cf654cf71aad6bf045d655 --- /dev/null +++ b/build.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash + +#The MIT License (MIT) +# +#Copyright (c) 2014 - 2016 Patrik Svensson, Mattias Karlsson, Gary Ewan Park and contributors +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in +#the Software without restriction, including without limitation the rights to +#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +#the Software, and to permit persons to whom the Software is furnished to do so, +#subject to the following conditions: +# +#The above copyright notice and this permission notice shall be included in all +#copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +#FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +#COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +#IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +########################################################################## +# This is the Cake bootstrapper script for Linux and OS X. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +# Define directories. +SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +TOOLS_DIR=$SCRIPT_DIR/tools +NUGET_EXE=$TOOLS_DIR/nuget.exe +CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe +PACKAGES_CONFIG=$TOOLS_DIR/packages.config +PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum + +# Define md5sum or md5 depending on Linux/OSX +MD5_EXE= +if [[ "$(uname -s)" == "Darwin" ]]; then + MD5_EXE="md5 -r" +else + MD5_EXE="md5sum" +fi + +# Define default arguments. +SCRIPT="build.cake" +TARGET="Default" +CONFIGURATION="Release" +VERBOSITY="verbose" +DRYRUN= +SHOW_VERSION=false +SCRIPT_ARGUMENTS=() + +# Parse arguments. +for i in "$@"; do + case $1 in + -s|--script) SCRIPT="$2"; shift ;; + -t|--target) TARGET="$2"; shift ;; + -c|--configuration) CONFIGURATION="$2"; shift ;; + -v|--verbosity) VERBOSITY="$2"; shift ;; + -d|--dryrun) DRYRUN="-dryrun" ;; + --version) SHOW_VERSION=true ;; + --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;; + *) SCRIPT_ARGUMENTS+=("$1") ;; + esac + shift +done + +# Make sure the tools folder exist. +if [ ! -d "$TOOLS_DIR" ]; then + mkdir "$TOOLS_DIR" +fi + +# Make sure that packages.config exist. +if [ ! -f "$TOOLS_DIR/packages.config" ]; then + echo "Downloading packages.config..." + curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages + if [ $? -ne 0 ]; then + echo "An error occurred while downloading packages.config." + exit 1 + fi +fi + +# Download NuGet if it does not exist. +if [ ! -f "$NUGET_EXE" ]; then + echo "Downloading NuGet..." + curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe + if [ $? -ne 0 ]; then + echo "An error occurred while downloading nuget.exe." + exit 1 + fi +fi + +# Restore tools from NuGet. +pushd "$TOOLS_DIR" >/dev/null +if [ ! -f $PACKAGES_CONFIG_MD5 ] || [ "$( cat $PACKAGES_CONFIG_MD5 | sed 's/\r$//' )" != "$( $MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' )" ]; then + find . -type d ! -name . | xargs rm -rf +fi + +mono "$NUGET_EXE" install -ExcludeVersion +if [ $? -ne 0 ]; then + echo "Could not restore NuGet packages." + exit 1 +fi + +$MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' >| $PACKAGES_CONFIG_MD5 + +popd >/dev/null + +# Make sure that Cake has been installed. +if [ ! -f "$CAKE_EXE" ]; then + echo "Could not find Cake.exe at '$CAKE_EXE'." + exit 1 +fi + +# Start Cake +if $SHOW_VERSION; then + exec mono "$CAKE_EXE" -version +else + exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -configuration=$CONFIGURATION -target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}" +fi \ No newline at end of file diff --git a/nunit3-junit.xslt b/nunit3-junit.xslt new file mode 100644 index 0000000000000000000000000000000000000000..bf69bdb1d2cd6fc2fdbeb915b4eb6732f720848e --- /dev/null +++ b/nunit3-junit.xslt @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Taken from: https://github.com/nunit/nunit-transforms/tree/master/nunit3-junit +--> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:output method="xml" indent="yes"/> + + <xsl:template match="/test-run"> + <testsuites tests="{@testcasecount}" failures="{@failed}" disabled="{@skipped}" time="{@duration}"> + <xsl:apply-templates/> + </testsuites> + </xsl:template> + + <xsl:template match="test-suite"> + <xsl:if test="test-case"> + <testsuite tests="{@testcasecount}" time="{@duration}" errors="{@testcasecount - @passed - @skipped - @failed}" failures="{@failed}" skipped="{@skipped}" timestamp="{@start-time}"> + <xsl:attribute name="name"> + <xsl:for-each select="ancestor-or-self::test-suite/@name"> + <xsl:value-of select="concat(., '.')"/> + </xsl:for-each> + </xsl:attribute> + <xsl:apply-templates select="test-case"/> + </testsuite> + <xsl:apply-templates select="test-suite"/> + </xsl:if> + <xsl:if test="not(test-case)"> + <xsl:apply-templates/> + </xsl:if> + </xsl:template> + + <xsl:template match="test-case"> + <testcase name="{@name}" assertions="{@asserts}" time="{@duration}" status="{@result}" classname="{@classname}"> + <xsl:if test="@runstate = 'Skipped' or @runstate = 'Ignored'"> + <skipped/> + </xsl:if> + + <xsl:apply-templates/> + </testcase> + </xsl:template> + + <xsl:template match="command-line"/> + <xsl:template match="settings"/> + + <xsl:template match="output"> + <system-out> + <xsl:value-of select="."/> + </system-out> + </xsl:template> + + <xsl:template match="stack-trace"> + </xsl:template> + + <xsl:template match="test-case/failure"> + <failure message="{./message}"> + <xsl:value-of select="./stack-trace"/> + </failure> + </xsl:template> + + <xsl:template match="test-suite/failure"/> + + <xsl:template match="test-case/reason"> + <skipped message="{./message}"/> + </xsl:template> + + <xsl:template match="test-case/assertions"> + </xsl:template> + + <xsl:template match="test-suite/reason"/> + + <xsl:template match="properties"/> +</xsl:stylesheet> \ No newline at end of file diff --git a/src/NotificationConfiguration.Test/NotificationConfiguration.Test.csproj b/src/NotificationConfiguration.Test/NotificationConfiguration.Test.csproj new file mode 100644 index 0000000000000000000000000000000000000000..a98032a96a2396e726b15a148ee4b0776d148e19 --- /dev/null +++ b/src/NotificationConfiguration.Test/NotificationConfiguration.Test.csproj @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props" Condition="Exists('..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props')" /> + <Import Project="..\packages\NUnit.3.12.0\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.12.0\build\NUnit.props')" /> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{C7499D7B-015F-47E3-95EA-ED16794D1319}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Coscine.NotificationConfiguration.Test</RootNamespace> + <AssemblyName>Coscine.NotificationConfiguration.Test</AssemblyName> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + <NuGetPackageImportStamp> + </NuGetPackageImportStamp> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> + </Reference> + <Reference Include="nunit.framework, Version=3.12.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL"> + <HintPath>..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="NotificationConfigurationTest.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\NotificationConfiguration\NotificationConfiguration.csproj"> + <Project>{F17D5354-2FC3-4E57-B06B-EB8C2A307DB2}</Project> + <Name>NotificationConfiguration</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\NUnit.3.12.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.12.0\build\NUnit.props'))" /> + <Error Condition="!Exists('..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props'))" /> + </Target> +</Project> \ No newline at end of file diff --git a/src/NotificationConfiguration.Test/NotificationConfigurationTest.cs b/src/NotificationConfiguration.Test/NotificationConfigurationTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..6b9a1b62b4fa961866528c116ef982ddf3d5de8c --- /dev/null +++ b/src/NotificationConfiguration.Test/NotificationConfigurationTest.cs @@ -0,0 +1,77 @@ + +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Coscine.NotificationConfiguration.Test +{ + [TestFixture] + public class NotificationConfigurationTest + { + private static readonly string _actionNameValid = "project_deleted"; + private static readonly string _actionNameInvalid = "hfauifhsdksndah"; + + private static readonly string _channelNameValid = "email"; + private static readonly string _channelNameInvalid = "hfauifhsdksndah"; + + public NotificationConfigurationTest() + { + } + + [OneTimeSetUp] + public void Setup() + { + + } + + [OneTimeTearDown] + public void End() + { + } + + [Test] + public void TestConstructor() + { + Coscine.NotificationConfiguration.NotificationConfiguration notificationConfiguration = new Coscine.NotificationConfiguration.NotificationConfiguration(); + } + + [Test] + public void TestIsValidAction() + { + Coscine.NotificationConfiguration.NotificationConfiguration notificationConfiguration = new Coscine.NotificationConfiguration.NotificationConfiguration(); + Assert.True(notificationConfiguration.IsValidAction(_actionNameValid)); + Assert.False(notificationConfiguration.IsValidAction(_actionNameInvalid)); + } + + [Test] + public void TestIsValidChannel() + { + Coscine.NotificationConfiguration.NotificationConfiguration notificationConfiguration = new Coscine.NotificationConfiguration.NotificationConfiguration(); + Assert.True(notificationConfiguration.IsValidChannel(_channelNameValid)); + Assert.False(notificationConfiguration.IsValidChannel(_channelNameInvalid)); + } + + [Test] + public void TestGetAction() + { + Coscine.NotificationConfiguration.NotificationConfiguration notificationConfiguration = new Coscine.NotificationConfiguration.NotificationConfiguration(); + Assert.True(notificationConfiguration.GetAction(_actionNameValid).GetType() == new ActionObject().GetType()); + Assert.True(notificationConfiguration.GetAction(_actionNameInvalid) == null); + } + + [Test] + public void TestGetChannel() + { + Coscine.NotificationConfiguration.NotificationConfiguration notificationConfiguration = new Coscine.NotificationConfiguration.NotificationConfiguration(); + Assert.True(notificationConfiguration.GetChannel(_channelNameValid).GetType() == new ChannelObject().GetType()); + Assert.True(notificationConfiguration.GetChannel(_channelNameInvalid) == null); + } + + [Test] + public void TestGetAllChannelsForAction() + { + Coscine.NotificationConfiguration.NotificationConfiguration notificationConfiguration = new Coscine.NotificationConfiguration.NotificationConfiguration(); + Assert.True(notificationConfiguration.GetAllChannelsForAction(_actionNameValid).Count > 0); + Assert.True(notificationConfiguration.GetAllChannelsForAction(_actionNameInvalid).Count == 0); + } + } +} diff --git a/src/NotificationConfiguration.Test/Properties/AssemblyInfo.cs b/src/NotificationConfiguration.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000000000000000000000000000000..dbd82eee9276e70c398441ca327ae16a4aead263 --- /dev/null +++ b/src/NotificationConfiguration.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by Cake. +// </auto-generated> +//------------------------------------------------------------------------------ +using System.Reflection; + +[assembly: AssemblyTitle("NotificationConfiguration.Test")] +[assembly: AssemblyDescription("NotificationConfiguration.Test is a part of the CoScInE group.")] +[assembly: AssemblyCompany("IT Center, RWTH Aachen University")] +[assembly: AssemblyProduct("NotificationConfiguration.Test")] +[assembly: AssemblyVersion("1.1.0")] +[assembly: AssemblyFileVersion("1.1.0")] +[assembly: AssemblyInformationalVersion("1.1.0-topic-828-notifi0009")] +[assembly: AssemblyCopyright("2020 IT Center, RWTH Aachen University")] + diff --git a/src/NotificationConfiguration.Test/packages.config b/src/NotificationConfiguration.Test/packages.config new file mode 100644 index 0000000000000000000000000000000000000000..0ba70060e0928ebafe11536b75bbf2982a748336 --- /dev/null +++ b/src/NotificationConfiguration.Test/packages.config @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net461" /> + <package id="NUnit" version="3.12.0" targetFramework="net461" /> + <package id="NUnit3TestAdapter" version="3.13.0" targetFramework="net461" /> +</packages> \ No newline at end of file diff --git a/src/NotificationConfiguration.sln b/src/NotificationConfiguration.sln new file mode 100644 index 0000000000000000000000000000000000000000..546c6ceacc2d34b2b4a2e6cb87f98545e9641563 --- /dev/null +++ b/src/NotificationConfiguration.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29911.84 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotificationConfiguration", "NotificationConfiguration\NotificationConfiguration.csproj", "{F17D5354-2FC3-4E57-B06B-EB8C2A307DB2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotificationConfiguration.Test", "NotificationConfiguration.Test\NotificationConfiguration.Test.csproj", "{C7499D7B-015F-47E3-95EA-ED16794D1319}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F17D5354-2FC3-4E57-B06B-EB8C2A307DB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F17D5354-2FC3-4E57-B06B-EB8C2A307DB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F17D5354-2FC3-4E57-B06B-EB8C2A307DB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F17D5354-2FC3-4E57-B06B-EB8C2A307DB2}.Release|Any CPU.Build.0 = Release|Any CPU + {C7499D7B-015F-47E3-95EA-ED16794D1319}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7499D7B-015F-47E3-95EA-ED16794D1319}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7499D7B-015F-47E3-95EA-ED16794D1319}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7499D7B-015F-47E3-95EA-ED16794D1319}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {781E1DD3-7C5E-4992-B026-A83B214FE9FC} + EndGlobalSection +EndGlobal diff --git a/src/NotificationConfiguration/ActionObject.cs b/src/NotificationConfiguration/ActionObject.cs new file mode 100644 index 0000000000000000000000000000000000000000..84f6a239bf1bea7bdd98730eb18e9d8a21ced388 --- /dev/null +++ b/src/NotificationConfiguration/ActionObject.cs @@ -0,0 +1,54 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Coscine.NotificationConfiguration +{ + public class ActionObject + { + + public JObject Template { get; set; } + + public Dictionary<string, string> Name { get; set; } + + public Dictionary<string, string> Description { get; set; } + + public List<string> Group { get; set; } + + public List<string> ChannelsAvailable { get; set; } + + public List<string> ChannelsAlways { get; set; } + + public List<string> ChannelsOnly { get; set; } + + public ActionObject() + { + + } + + public ActionObject(JObject template, Dictionary<string, string> name, Dictionary<string, string> description, List<string> group, List<string> channelsAvailable, List<string> channelsAlways, List<string> channelsOnly) + { + Template = template; + Name = name; + Description = description; + Group = group; + ChannelsAvailable = channelsAvailable; + ChannelsAlways = channelsAlways; + ChannelsOnly = channelsOnly; + } + + public ActionObject(JObject channelData) + { + Template = (JObject)channelData["template"]; + Name = channelData["name"].ToObject<Dictionary<string, string>>(); + Description = channelData["name"].ToObject<Dictionary<string, string>>(); + Group = channelData["group"].ToObject<List<string>>(); + ChannelsAvailable = channelData["channels"]["available"].ToObject<List<string>>(); + ChannelsAlways = channelData["channels"]["always"].ToObject<List<string>>(); + ChannelsOnly = channelData["channels"]["only"].ToObject<List<string>>(); + } + } +} diff --git a/src/NotificationConfiguration/Actions.json b/src/NotificationConfiguration/Actions.json new file mode 100644 index 0000000000000000000000000000000000000000..b1beffc5b6a77134c5955ce0a834f79f3453cf00 --- /dev/null +++ b/src/NotificationConfiguration/Actions.json @@ -0,0 +1,170 @@ +{ + "project_deleted": { + "channels": { + "available": [ "email" ], + "always": [], + "only": [] + }, + "template": { + "email": { + "de": { + "subject": "[CoScInE] Projekt gelöscht", + "body": "Das Projekt wurde gelöscht." + }, + "en": { + "subject": "[CoScInE] Project deleted", + "body": "Project was deleted." + } + } + }, + "name": { + "de": "Projekt gelöscht", + "en": "Project deleted" + }, + "description": { + "de": "Sie werden informiert, wenn Projekte in denen Sie Mitglied sind gelöscht werden.", + "en": "You will be informed when projects in which you are a member are deleted." + }, + "group": [] + }, + "project_created": { + "channels": { + "available": [ "email" ], + "always": [], + "only": [] + }, + "template": { + "email": { + "de": { + "subject": "[CoScInE] Projekt erstellt", + "body": "Ein Projekt wurde erstellt." + }, + "en": { + "subject": "[CoScInE] Project created", + "body": "A project was created." + } + } + }, + "name": { + "de": "Projekt erstellt", + "en": "Project created" + }, + "description": { + "de": "Sie werden informiert, wenn Sie ein Projekt erstellt haben.", + "en": "You will be informed when you have created a project." + }, + "group": [] + }, + "user_deleted": { + "channels": { + "available": [ "email" ], + "always": [], + "only": [] + }, + "template": { + "email": { + "de": { + "subject": "[CoScInE] Benutzer von Projekt entfernt", + "body": "Sie wurden von einem Projekt entfernt." + }, + "en": { + "subject": "[CoScInE] User removed from project", + "body": "You were removed from a project." + } + } + }, + "name": { + "de": "Als Mitglied entfernt", + "en": "Removed as a member" + }, + "description": { + "de": "Sie werden informiert, wenn Sie von einem Projekt als Mitglied entfernt werden.", + "en": "You will be informed when you are removed as a user from a project." + }, + "group": [] + }, + "user_role_changed": { + "channels": { + "available": [ "email" ], + "always": [], + "only": [] + }, + "template": { + "email": { + "de": { + "subject": "[CoScInE] Benutzerrolle in Projekt geändert", + "body": "Ihre Rolle in einem Projekt wurde geändert." + }, + "en": { + "subject": "[CoScInE] User role changed in project", + "body": "Your role in a project was changed." + } + } + }, + "name": { + "de": "Benutzerrolle geändert", + "en": "User role changed" + }, + "description": { + "de": "Sie werden informiert, wenn Ihre Rolle in einem Projekt geändert wird.", + "en": "You will be informed when your role in a project is changed." + }, + "group": [] + }, + "resource_deleted": { + "channels": { + "available": [ "email" ], + "always": [], + "only": [] + }, + "template": { + "email": { + "de": { + "subject": "[CoScInE] Ressource entfernt", + "body": "Es wurde eine Ressource aus einem Ihrer Projekte entfernt." + }, + "en": { + "subject": "[CoScInE] Resource removed", + "body": "A resource was removed from one of your projects." + } + } + }, + "name": { + "de": "Ressource gelöscht", + "en": "Resource deleted" + }, + "description": { + "de": "Sie werden informiert, wenn eine Ressource aus einem Projekt entfernt wird.", + "en": "You will be informed when a resource is removed from a project." + }, + "group": [] + }, + "resource_created": { + "channels": { + "available": [ "email" ], + "always": [], + "only": [] + }, + "template": { + "email": { + "de": { + "subject": "[CoScInE] Ressource erstellt", + "body": "Es wurde eine Ressource in einem Ihrer Projekte erstellt." + }, + "en": { + "subject": "[CoScInE] Resource created", + "body": "A resource was created in one of your projects." + } + } + }, + "name": { + "de": "Ressource erstellt", + "en": "Resource created" + }, + "description": { + "de": "Sie werden informiert, wenn eine Ressource in einem Projekt erstellt wird.", + "en": "You will be informed when a resource is created in a project." + }, + "group": [] + } +} diff --git a/src/NotificationConfiguration/ChannelObject.cs b/src/NotificationConfiguration/ChannelObject.cs new file mode 100644 index 0000000000000000000000000000000000000000..9e8fbcb7c4d65c48b933662aaf6c0a110a75a7b4 --- /dev/null +++ b/src/NotificationConfiguration/ChannelObject.cs @@ -0,0 +1,40 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Coscine.NotificationConfiguration +{ + public class ChannelObject + { + public string Path { get; set; } + public JObject Args { get; set; } + public Dictionary<string, string> Name { get; set; } + public Dictionary<string, string> Description { get; set; } + + public ChannelObject() + { + + } + + public ChannelObject(string path, JObject args, Dictionary<string, string> name, Dictionary<string, string> description) + { + Path = path; + Args = args; + Name = name; + Description = description; + } + + public ChannelObject(JObject channelData) + { + Path = channelData["path"].ToString(); + Args = (JObject)channelData["args"]; + Name = channelData["name"].ToObject<Dictionary<string, string>>(); + Description = channelData["name"].ToObject<Dictionary<string, string>>(); + } + + } +} diff --git a/src/NotificationConfiguration/Channels.json b/src/NotificationConfiguration/Channels.json new file mode 100644 index 0000000000000000000000000000000000000000..85ad14a168ee3b35592b8bdc6da162128f8961f5 --- /dev/null +++ b/src/NotificationConfiguration/Channels.json @@ -0,0 +1,16 @@ +{ + "email": { + "path": "NotificationChannelEmail\\Coscine.NotificationChannelEmail.dll", + "args": { + "from": "coscine@itc.rwth-aachen.de" + }, + "name": { + "de": "E-Mail", + "en": "Email" + }, + "description": { + "de": "Sie können auswählen, über welche Änderungen in CoScInE Sie per E-Mail informiert werden.", + "en": "You can choose which changes in CoScInE you will be informed about by email." + } + } +} diff --git a/src/NotificationConfiguration/NotificationConfiguration.cs b/src/NotificationConfiguration/NotificationConfiguration.cs new file mode 100644 index 0000000000000000000000000000000000000000..7b6b6973a7512f1d526a71ed6230a06c37e5ee8f --- /dev/null +++ b/src/NotificationConfiguration/NotificationConfiguration.cs @@ -0,0 +1,95 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace Coscine.NotificationConfiguration +{ + public class NotificationConfiguration + { + private readonly Dictionary<string, ActionObject> _actions = new Dictionary<string, ActionObject>(); + + private readonly Dictionary<string, ChannelObject> _channels = new Dictionary<string, ChannelObject>(); + + public NotificationConfiguration() + { + var actionsFileName = "Coscine.NotificationConfiguration.Actions.json"; + var channelsFileName = "Coscine.NotificationConfiguration.Channels.json"; + + var actionsJSON = GetJSONFromAssembly(actionsFileName); + var channelsJSON = GetJSONFromAssembly(channelsFileName); + + foreach(var action in actionsJSON) + { + _actions.Add(action.Key, new ActionObject((JObject)action.Value)); + } + foreach (var channel in channelsJSON) + { + _channels.Add(channel.Key, new ChannelObject((JObject)channel.Value)); + } + } + + public static JObject GetJSONFromAssembly(string fileName) + { + var assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream(fileName)) + { + using (StreamReader reader = new StreamReader(stream)) + { + string result = reader.ReadToEnd(); + return (JObject)JsonConvert.DeserializeObject(result); + } + } + } + + public bool IsValidAction(string action) + { + return _actions.ContainsKey(action); + } + + public bool IsValidChannel(string channel) + { + return _channels.ContainsKey(channel); + } + + public ActionObject GetAction (string action) + { + if (_actions.ContainsKey(action)) + { + return _actions[action]; + } + return null; + } + + public ChannelObject GetChannel(string channel) + { + if (_channels.ContainsKey(channel)) + { + return _channels[channel]; + } + return null; + } + + public List<string> GetAllChannelsForAction(string action) + { + ActionObject actionObject = GetAction(action); + if (actionObject != null) + { + var only = actionObject.ChannelsOnly; + var available = actionObject.ChannelsAvailable; + var always = actionObject.ChannelsOnly; + if (only.Count > 0) + { + return only; + } + else + { + return available.Union(always).ToList(); + } + } + return new List<string>(); + } + } +} diff --git a/src/NotificationConfiguration/NotificationConfiguration.csproj b/src/NotificationConfiguration/NotificationConfiguration.csproj new file mode 100644 index 0000000000000000000000000000000000000000..5b21435c8a37ac630b99616146fb6101f341a5ff --- /dev/null +++ b/src/NotificationConfiguration/NotificationConfiguration.csproj @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{F17D5354-2FC3-4E57-B06B-EB8C2A307DB2}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Coscine.NotificationConfiguration</RootNamespace> + <AssemblyName>Coscine.NotificationConfiguration</AssemblyName> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + <NuGetPackageImportStamp> + </NuGetPackageImportStamp> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> + <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="ActionObject.cs" /> + <Compile Include="ChannelObject.cs" /> + <Compile Include="NotificationConfiguration.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="Actions.json" /> + <EmbeddedResource Include="Channels.json" /> + <None Include="packages.config" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project> \ No newline at end of file diff --git a/src/NotificationConfiguration/NotificationConfiguration.nuspec b/src/NotificationConfiguration/NotificationConfiguration.nuspec new file mode 100644 index 0000000000000000000000000000000000000000..97934e29d167372c63f8e3d8b01548700e2cfa6f --- /dev/null +++ b/src/NotificationConfiguration/NotificationConfiguration.nuspec @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<package > + <metadata> + <id>$id$</id> + <version>$version$</version> + <title>$title$</title> + <authors>rwth-aachen</authors> + <owners>rwth-aachen</owners> + <license type="expression">MIT</license> + <projectUrl>https://git.rwth-aachen.de/coscine/cs/notificationconfiguration</projectUrl> + <requireLicenseAcceptance>false</requireLicenseAcceptance> + <description>$description$</description> + <copyright>$copyright$</copyright> + </metadata> +</package> \ No newline at end of file diff --git a/src/NotificationConfiguration/Properties/AssemblyInfo.cs b/src/NotificationConfiguration/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000000000000000000000000000000000..be67c58c5909ca5ce994cb7ee7299408800ade4b --- /dev/null +++ b/src/NotificationConfiguration/Properties/AssemblyInfo.cs @@ -0,0 +1,16 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by Cake. +// </auto-generated> +//------------------------------------------------------------------------------ +using System.Reflection; + +[assembly: AssemblyTitle("NotificationConfiguration")] +[assembly: AssemblyDescription("NotificationConfiguration is a part of the CoScInE group.")] +[assembly: AssemblyCompany("IT Center, RWTH Aachen University")] +[assembly: AssemblyProduct("NotificationConfiguration")] +[assembly: AssemblyVersion("1.1.0")] +[assembly: AssemblyFileVersion("1.1.0")] +[assembly: AssemblyInformationalVersion("1.1.0-topic-828-notifi0009")] +[assembly: AssemblyCopyright("2020 IT Center, RWTH Aachen University")] + diff --git a/src/NotificationConfiguration/packages.config b/src/NotificationConfiguration/packages.config new file mode 100644 index 0000000000000000000000000000000000000000..8ab13a9023dec3cf548ee61ba183013bd098a297 --- /dev/null +++ b/src/NotificationConfiguration/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net461" /> +</packages> \ No newline at end of file diff --git a/tools/packages.config b/tools/packages.config new file mode 100644 index 0000000000000000000000000000000000000000..8bfcb250c0244d439f7224ab4ab1089153a6243c --- /dev/null +++ b/tools/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Cake" version="0.38.2" /> +</packages>