Add project files
This commit is contained in:
21
src/ChainOfResponssibility/ChainOfResponssibility.xproj
Normal file
21
src/ChainOfResponssibility/ChainOfResponssibility.xproj
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>89536824-683f-4351-8789-406d7bdd922d</ProjectGuid>
|
||||
<RootNamespace>ChainOfResponssibility</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
19
src/ChainOfResponssibility/Properties/AssemblyInfo.cs
Normal file
19
src/ChainOfResponssibility/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Hewlett-Packard Company")]
|
||||
[assembly: AssemblyProduct("ChainOfResponssibility")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("89536824-683f-4351-8789-406d7bdd922d")]
|
||||
92
src/ChainOfResponssibility/PurchaseExample/CheckAuthority.cs
Normal file
92
src/ChainOfResponssibility/PurchaseExample/CheckAuthority.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
|
||||
namespace ChainOfResponssibility.PurchaseExample
|
||||
{
|
||||
public class CheckAuthority
|
||||
{
|
||||
ManagerPPower manager;
|
||||
DirectorPPower director;
|
||||
VicePresidentPPower vp;
|
||||
PresidentPPower president;
|
||||
|
||||
public CheckAuthority()
|
||||
{
|
||||
manager = new ManagerPPower();
|
||||
director = new DirectorPPower();
|
||||
vp = new VicePresidentPPower();
|
||||
president = new PresidentPPower();
|
||||
|
||||
manager.Successor = director;
|
||||
director.Successor = vp;
|
||||
vp.Successor = president;
|
||||
}
|
||||
|
||||
public void PrintHowMuchEachCanSpend()
|
||||
{
|
||||
manager.PrintHowMuchICanSpend();
|
||||
director.PrintHowMuchICanSpend();
|
||||
vp.PrintHowMuchICanSpend();
|
||||
president.PrintHowMuchICanSpend();
|
||||
}
|
||||
|
||||
public void SpendMoney()
|
||||
{
|
||||
string input = "";
|
||||
do
|
||||
{
|
||||
Console.WriteLine("Enter the amount to check who should approve your expenditure.");
|
||||
Console.Write(">");
|
||||
input = Console.ReadLine();
|
||||
|
||||
if (IsDoulbe(input))
|
||||
{
|
||||
double d = double.Parse(input);
|
||||
manager.ProcessRequest(new PurchaseRequest(d, "I am beautifull"));
|
||||
}
|
||||
|
||||
} while (!IsExitCode(input));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static bool IsExitCode(string input)
|
||||
{
|
||||
return "exit".Equals(input, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private bool IsDoulbe(string input)
|
||||
{
|
||||
double x = 0;
|
||||
return double.TryParse(input, out x);
|
||||
}
|
||||
|
||||
|
||||
public string GetDescriptionOfClass()
|
||||
{
|
||||
return @"CheckAuthority allows an employee to spend money
|
||||
if(manager can approve it) manager will process the request
|
||||
if (director can approve it) director will process the request
|
||||
if (vice president can approve it) vice president will process the request
|
||||
if (president can approve it) president will process the request";
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/ChainOfResponssibility/PurchaseExample/PurchasePower.cs
Normal file
38
src/ChainOfResponssibility/PurchaseExample/PurchasePower.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.PurchaseExample
|
||||
{
|
||||
public abstract class PurchasePower
|
||||
{
|
||||
protected const double BaseUnit = 500;
|
||||
|
||||
public PurchasePower Successor { get; set; }
|
||||
|
||||
protected abstract double MaximumToSpend { get; }
|
||||
|
||||
protected abstract string Role { get; }
|
||||
|
||||
public void ProcessRequest(PurchaseRequest request)
|
||||
{
|
||||
if (request.Ammount <= MaximumToSpend)
|
||||
{
|
||||
Console.WriteLine("{0} will approve ${1}", Role, request.Ammount);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Successor != null)
|
||||
Successor.ProcessRequest(request);
|
||||
else
|
||||
Console.WriteLine("No one has that much money");
|
||||
}
|
||||
}
|
||||
|
||||
public void PrintHowMuchICanSpend()
|
||||
{
|
||||
Console.WriteLine("As a {0} I can spend at most {1}", Role, MaximumToSpend);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace ChainOfResponssibility.PurchaseExample
|
||||
{
|
||||
public class ManagerPPower : PurchasePower
|
||||
{
|
||||
protected override double MaximumToSpend { get { return BaseUnit * 10; } }
|
||||
|
||||
protected override string Role { get { return "Manager"; } }
|
||||
}
|
||||
|
||||
public class DirectorPPower : PurchasePower
|
||||
{
|
||||
protected override double MaximumToSpend { get { return BaseUnit * 20; } }
|
||||
|
||||
protected override string Role { get { return "Director"; } }
|
||||
}
|
||||
|
||||
public class VicePresidentPPower : PurchasePower
|
||||
{
|
||||
protected override double MaximumToSpend { get { return BaseUnit * 40; } }
|
||||
|
||||
protected override string Role { get { return "VicePresident"; } }
|
||||
}
|
||||
|
||||
public class PresidentPPower : PurchasePower
|
||||
{
|
||||
protected override double MaximumToSpend { get { return BaseUnit * 60; } }
|
||||
|
||||
protected override string Role { get { return "President"; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace ChainOfResponssibility.PurchaseExample
|
||||
{
|
||||
public class PurchaseRequest
|
||||
{
|
||||
public PurchaseRequest(double ammount, string reason)
|
||||
{
|
||||
Ammount = ammount;
|
||||
|
||||
Reason = reason;
|
||||
}
|
||||
|
||||
public double Ammount { get; private set; }
|
||||
|
||||
public string Reason { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using ChainOfResponssibility.PurchaseExample;
|
||||
using ChainOfResponssibility.TransferFileExample;
|
||||
using ChainOfResponssibility.Validators.UserEntities;
|
||||
using System;
|
||||
|
||||
namespace ChainOfResponssibility
|
||||
{
|
||||
public class ChainOfResponsibillityExamples
|
||||
{
|
||||
public static void Run()
|
||||
{
|
||||
Console.WriteLine(GetPatternDescription());
|
||||
GoToNextStep();
|
||||
|
||||
Console.WriteLine(ExeucteFirstWhenConditionMatchesFlavorDescription());
|
||||
GoToNextStep();
|
||||
|
||||
CheckAuthority moneySpender = new CheckAuthority();
|
||||
|
||||
Console.WriteLine(moneySpender.GetDescriptionOfClass());
|
||||
GoToNextStep();
|
||||
|
||||
moneySpender.PrintHowMuchEachCanSpend();
|
||||
moneySpender.SpendMoney();
|
||||
GoToNextStep();
|
||||
|
||||
TransferFilesManager transferFilesManager = new TransferFilesManager();
|
||||
|
||||
Console.WriteLine(transferFilesManager.GetDescriptionOfClass());
|
||||
GoToNextStep();
|
||||
transferFilesManager.TransferFiles();
|
||||
|
||||
GoToNextStep();
|
||||
Console.WriteLine(ExecuteAllUntilConditionIsFalseFlavorDescription());
|
||||
Console.WriteLine(ExecuteAllFlavorDescritpion());
|
||||
GoToNextStep();
|
||||
|
||||
UserProcessor userProcessor = new UserProcessor();
|
||||
userProcessor.DoStuff();
|
||||
|
||||
GoToNextStep();
|
||||
Console.WriteLine(GetPitfalls());
|
||||
}
|
||||
|
||||
private static void GoToNextStep()
|
||||
{
|
||||
Console.ReadKey();
|
||||
Console.Clear();
|
||||
}
|
||||
|
||||
public static string GetPatternDescription()
|
||||
{
|
||||
return @"
|
||||
Decouples sender and receiver (as a sender you don't know who will handle the request/ as a receiver you don't know who the sender is necessary)
|
||||
Hierarchical in nature
|
||||
When using the Chain of Responsibility is more effective:
|
||||
More than one object can handle a command
|
||||
The handler is not known in advance
|
||||
The handler should be determined automatically
|
||||
It’s wished that the request is addressed to a group of objects without explicitly specifying its receiver
|
||||
The group of objects that may handle the command must be specified in a dynamic way.
|
||||
Examples in real life:
|
||||
-java.util.logging.Logger.#log()
|
||||
-javax.servlet.Filter#doFilter()
|
||||
-Spring Security Filter Chain";
|
||||
}
|
||||
|
||||
public static string GetPitfalls()
|
||||
{
|
||||
return @"
|
||||
Handling/Handler guarantee - you won't be sure that someone can process the request
|
||||
Runtime configuration risk - the order matters/and it might be that the chain is not configured correctly
|
||||
Chain length/performance issues - in theory you could see a chain that is too big, and it would be a bottleneck in performance";
|
||||
}
|
||||
|
||||
public static string ExeucteFirstWhenConditionMatchesFlavorDescription()
|
||||
{
|
||||
return @"Flavor 1: Execute first that matches the condition and exit";
|
||||
}
|
||||
|
||||
public static string ExecuteAllUntilConditionIsFalseFlavorDescription()
|
||||
{
|
||||
return @"Flavor 2 of chain of responssibility:Execute all elements of chain until the condition does not match";
|
||||
}
|
||||
|
||||
public static string ExecuteAllFlavorDescritpion()
|
||||
{
|
||||
return @"Flavor 3 of chain of responssibility: Execute all elements of chain";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ChainOfResponssibility.TransferFileExample
|
||||
{
|
||||
public class FileCopyClient : TransferClient
|
||||
{
|
||||
protected override bool CanTransferTo(string destination)
|
||||
{
|
||||
return destination.StartsWith("file://") || Directory.Exists(Path.GetDirectoryName(destination));
|
||||
}
|
||||
|
||||
protected override void Transfer(string source, string destination)
|
||||
{
|
||||
Console.WriteLine("File copy from: {0} to {1}", source, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.TransferFileExample
|
||||
{
|
||||
public class FtpTransferClient : TransferClient
|
||||
{
|
||||
protected override bool CanTransferTo(string destination)
|
||||
{
|
||||
return destination.StartsWith("ftp:");
|
||||
}
|
||||
|
||||
protected override void Transfer(string source, string destination)
|
||||
{
|
||||
Console.WriteLine("FTP transfer file from: {0} to {1}", source, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.TransferFileExample
|
||||
{
|
||||
public class SftpransferClient : TransferClient
|
||||
{
|
||||
protected override bool CanTransferTo(string destination)
|
||||
{
|
||||
return destination.StartsWith("sftp:");
|
||||
}
|
||||
|
||||
protected override void Transfer(string source, string destination)
|
||||
{
|
||||
Console.WriteLine("SFTP transfer file from: {0} to {1}", source, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.TransferFileExample
|
||||
{
|
||||
public class HttpTransferClient : TransferClient
|
||||
{
|
||||
protected override bool CanTransferTo(string destination)
|
||||
{
|
||||
return destination.StartsWith("http:") || destination.StartsWith("https:");
|
||||
}
|
||||
|
||||
protected override void Transfer(string source, string destination)
|
||||
{
|
||||
Console.WriteLine("Http transfer file from: {0} to {1}", source, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.TransferFileExample
|
||||
{
|
||||
public abstract class TransferClient
|
||||
{
|
||||
protected abstract bool CanTransferTo(string destination);
|
||||
|
||||
protected abstract void Transfer(string source, string destination);
|
||||
|
||||
protected TransferClient Successor { get; private set; }
|
||||
|
||||
public void TransferFile(string source, string destination)
|
||||
{
|
||||
if(CanTransferTo(destination))
|
||||
{
|
||||
Transfer(source, destination);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Successor != null)
|
||||
Successor.TransferFile(source, destination);
|
||||
else
|
||||
Console.WriteLine("Could not transfer file to: {0}", destination);
|
||||
}
|
||||
}
|
||||
|
||||
public TransferClient SetSuccessor(TransferClient successor)
|
||||
{
|
||||
return Successor = successor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.TransferFileExample
|
||||
{
|
||||
public class TransferFilesManager
|
||||
{
|
||||
TransferClient tranferClient;
|
||||
|
||||
public TransferFilesManager()
|
||||
{
|
||||
tranferClient = new FtpTransferClient();
|
||||
tranferClient
|
||||
.SetSuccessor(new HttpTransferClient())
|
||||
.SetSuccessor(new SftpransferClient())
|
||||
.SetSuccessor(new FileCopyClient());
|
||||
}
|
||||
|
||||
public void TransferFiles()
|
||||
{
|
||||
string src = "", dst = "";
|
||||
|
||||
do
|
||||
{
|
||||
Console.WriteLine("Source:");
|
||||
Console.Write(">");
|
||||
src = Console.ReadLine();
|
||||
Console.WriteLine("Destination:");
|
||||
Console.Write(">");
|
||||
dst = Console.ReadLine();
|
||||
|
||||
if (!IsExitCode(src) && !IsExitCode(dst))
|
||||
tranferClient.TransferFile(src, dst);
|
||||
|
||||
} while (!IsExitCode(src) && !IsExitCode(dst));
|
||||
}
|
||||
|
||||
private static bool IsExitCode(string input)
|
||||
{
|
||||
return "exit".Equals(input, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public string GetDescriptionOfClass()
|
||||
{
|
||||
return @"TransferFilesManager will try to transfer the file to the destination by trying FTP, SFTP, Http, and simple file copy";
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/ChainOfResponssibility/Validators/ChainValidation.cs
Normal file
33
src/ChainOfResponssibility/Validators/ChainValidation.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators
|
||||
{
|
||||
public abstract class ChainValidation<T>
|
||||
{
|
||||
public ChainValidation<T> Successor { get; private set; }
|
||||
|
||||
public ChainValidation<T> SetSuccessor(ChainValidation<T> successor)
|
||||
{
|
||||
return Successor = successor;
|
||||
}
|
||||
|
||||
protected abstract ValidationResult IsValid(T obj);
|
||||
|
||||
public ValidationResult Validate(T obj)
|
||||
{
|
||||
ValidationResult result = IsValid(obj);
|
||||
|
||||
if (!result.IsValid)
|
||||
return result;
|
||||
|
||||
if (Successor != null)
|
||||
return Successor.Validate(obj);
|
||||
else
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities
|
||||
{
|
||||
internal class CommandCouldNotBeParsedException : Exception
|
||||
{
|
||||
public CommandCouldNotBeParsedException()
|
||||
{
|
||||
}
|
||||
|
||||
public CommandCouldNotBeParsedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public CommandCouldNotBeParsedException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities
|
||||
{
|
||||
internal class DuplicateRecordException : Exception
|
||||
{
|
||||
public DuplicateRecordException()
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateRecordException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateRecordException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities.Validators
|
||||
{
|
||||
internal class ForbiddenException : Exception
|
||||
{
|
||||
public ForbiddenException()
|
||||
{
|
||||
}
|
||||
|
||||
public ForbiddenException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ForbiddenException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities
|
||||
{
|
||||
internal class NotFoundException : Exception
|
||||
{
|
||||
private int iD;
|
||||
|
||||
public NotFoundException()
|
||||
{
|
||||
}
|
||||
|
||||
public NotFoundException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public NotFoundException(int iD)
|
||||
{
|
||||
this.iD = iD;
|
||||
}
|
||||
|
||||
public NotFoundException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators
|
||||
{
|
||||
public class ValidationResult
|
||||
{
|
||||
protected ValidationResult(bool isValid, Exception e)
|
||||
{
|
||||
IsValid = isValid;
|
||||
Exception = e;
|
||||
}
|
||||
|
||||
public static ValidationResult GetValidResult()
|
||||
{
|
||||
return new ValidationResult(true, null);
|
||||
}
|
||||
|
||||
public static ValidationResult GetInvalidResult(Exception e)
|
||||
{
|
||||
return new ValidationResult(false, e);
|
||||
}
|
||||
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
public bool IsValid { get; set; }
|
||||
}
|
||||
|
||||
public class ValidationResult<T> : ValidationResult
|
||||
{
|
||||
public ValidationResult(bool isValid, Exception e, T model) : base(isValid, e)
|
||||
{
|
||||
}
|
||||
|
||||
public static ValidationResult<T> GetValidResult(T model)
|
||||
{
|
||||
return new ValidationResult<T>(true, null, model);
|
||||
}
|
||||
|
||||
public static ValidationResult<T> GetInvalidResult(Exception e)
|
||||
{
|
||||
return new ValidationResult<T>(false, e, default(T));
|
||||
}
|
||||
|
||||
public T Result { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities.Infrastructure
|
||||
{
|
||||
public class PrincipalHelper
|
||||
{
|
||||
User authenticatedUser;
|
||||
|
||||
public User GetAuthenticatedUser()
|
||||
{
|
||||
return authenticatedUser;
|
||||
}
|
||||
|
||||
public void SetAuthenticatedUser(User user)
|
||||
{
|
||||
authenticatedUser = user;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities
|
||||
{
|
||||
public class UserRepository
|
||||
{
|
||||
List<User> users;
|
||||
public UserRepository()
|
||||
{
|
||||
users = new List<User>();
|
||||
users.Add(new User { ID = 1, Email = "a@a.a", TenantId = 1, UserName = "a", Rights = Rights.Create | Rights.Update });
|
||||
users.Add(new User { ID = 2, Email = "b@a.a", TenantId = 1, UserName = "b", Rights = Rights.Read});
|
||||
users.Add(new User { ID = 2, Email = "c@a.a", TenantId = 2, UserName = "c", Rights = Rights.Create | Rights.Update | Rights.Read | Rights.Delete });
|
||||
}
|
||||
|
||||
public User Get(string email)
|
||||
{
|
||||
return users.First(u => u.Email == email);
|
||||
}
|
||||
|
||||
public bool Exists(string email)
|
||||
{
|
||||
return users.Any(u => u.Email.Equals(email, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public bool Exists(int id)
|
||||
{
|
||||
return users.Any(u => u.ID == id);
|
||||
}
|
||||
|
||||
public void Add(User user)
|
||||
{
|
||||
lock (users)
|
||||
{
|
||||
int maxId = users.Max(u => u.ID);
|
||||
user.ID = maxId + 1;
|
||||
users.Add(user);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Update(User user)
|
||||
{
|
||||
User dbUser = users.First(u => u.ID == user.ID);
|
||||
|
||||
dbUser.Email = user.Email;
|
||||
dbUser.TenantId = user.TenantId;
|
||||
dbUser.UserName = user.UserName;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
public string UserName { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public int TenantId { get; set; }
|
||||
|
||||
public Rights Rights { get; set; }
|
||||
}
|
||||
[Flags]
|
||||
public enum Rights
|
||||
{
|
||||
Create = 1 << 3,
|
||||
Read = 1 << 2,
|
||||
Update = 1 << 1,
|
||||
Delete = 1 << 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities.UserMenu
|
||||
{
|
||||
public class AuthenticateOperation : Operation
|
||||
{
|
||||
const string prefix = "authenticate as";
|
||||
Action<string> authenticateFunction;
|
||||
public AuthenticateOperation(Action<string> authenticateFunction)
|
||||
{
|
||||
this.authenticateFunction = authenticateFunction;
|
||||
}
|
||||
|
||||
protected override bool CanExecute(string command)
|
||||
{
|
||||
return command.ToLower().StartsWith(prefix);
|
||||
}
|
||||
|
||||
protected override void ExecuteSpecificOperation(string command)
|
||||
{
|
||||
string email = command.Substring(prefix.Length + 1);
|
||||
authenticateFunction(email);
|
||||
}
|
||||
|
||||
protected override string GetMessageToPrint()
|
||||
{
|
||||
return string.Format("To authenticate press: {0} <email>", prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities.UserMenu
|
||||
{
|
||||
public class CreateNewUserOperation : Operation
|
||||
{
|
||||
string prefix = "create user";
|
||||
Action<string, string, int> createUser;
|
||||
public CreateNewUserOperation(Action<string, string, int> createUser)
|
||||
{
|
||||
this.createUser = createUser;
|
||||
}
|
||||
|
||||
protected override bool CanExecute(string command)
|
||||
{
|
||||
var hasCorrectPrefix = command.ToLower().StartsWith(prefix) && command.Contains(",");
|
||||
return hasCorrectPrefix && IsInt(GetTenantId(command));
|
||||
}
|
||||
|
||||
protected override void ExecuteSpecificOperation(string command)
|
||||
{
|
||||
string commandWithoutPrefix = command.Substring(prefix.Length);
|
||||
|
||||
string email = new string(commandWithoutPrefix.TakeWhile(c => c != ',').Skip(1).ToArray()).Trim();
|
||||
|
||||
string userName = GetUserName(commandWithoutPrefix);
|
||||
|
||||
int tenantId = int.Parse(GetTenantId(commandWithoutPrefix));
|
||||
createUser(email, userName, tenantId);
|
||||
}
|
||||
|
||||
public static string GetUserName(string command)
|
||||
{
|
||||
string commandWithoutEmail = command.Substring(command.IndexOf(',') + 1);
|
||||
string userName = commandWithoutEmail.Substring(0, commandWithoutEmail.LastIndexOf(','));
|
||||
return userName.Trim();
|
||||
}
|
||||
|
||||
private static string GetTenantId(string command)
|
||||
{
|
||||
return command.Substring(command.LastIndexOf(',') + 1).Trim();
|
||||
}
|
||||
|
||||
protected override string GetMessageToPrint()
|
||||
{
|
||||
return string.Format("To create a new user press: create user <email>, <username>, <tenant id>");
|
||||
}
|
||||
|
||||
private bool IsInt(string tenantId)
|
||||
{
|
||||
int number;
|
||||
return int.TryParse(tenantId, out number);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities.UserMenu
|
||||
{
|
||||
public abstract class Operation
|
||||
{
|
||||
protected abstract bool CanExecute(string command);
|
||||
|
||||
protected abstract void ExecuteSpecificOperation(string command);
|
||||
|
||||
protected abstract string GetMessageToPrint();
|
||||
|
||||
public Operation Successor { get; private set; }
|
||||
|
||||
public Operation SetSuccessor(Operation successor)
|
||||
{
|
||||
return Successor = successor;
|
||||
}
|
||||
|
||||
public ValidationResult Execute(string command)
|
||||
{
|
||||
if (CanExecute(command))
|
||||
{
|
||||
ExecuteSpecificOperation(command);
|
||||
return ValidationResult.GetValidResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Successor != null)
|
||||
return Successor.Execute(command);
|
||||
else
|
||||
return GetInvalidResult(command);
|
||||
}
|
||||
}
|
||||
|
||||
public void PrintMenu()
|
||||
{
|
||||
Console.WriteLine(GetMessageToPrint());
|
||||
|
||||
if (Successor != null)
|
||||
Successor.PrintMenu();
|
||||
}
|
||||
|
||||
private static ValidationResult GetInvalidResult(string command)
|
||||
{
|
||||
return ValidationResult.GetInvalidResult(new CommandCouldNotBeParsedException(command));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities.UserMenu
|
||||
{
|
||||
public class UpdateUserOperation
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using ChainOfResponssibility.Validators.UserEntities.Infrastructure;
|
||||
using ChainOfResponssibility.Validators.UserEntities.UserMenu;
|
||||
using ChainOfResponssibility.Validators.UserEntities.Validators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities
|
||||
{
|
||||
public class UserProcessor
|
||||
{
|
||||
UserRepository userRepository;
|
||||
PrincipalHelper principalHelper;
|
||||
Operation operation;
|
||||
ChainValidation<User> userCreationValidation;
|
||||
ChainValidation<User> authenticateUserValidation;
|
||||
public UserProcessor()
|
||||
{
|
||||
userRepository = new UserRepository();
|
||||
principalHelper = new PrincipalHelper();
|
||||
operation = new AuthenticateOperation(AuthenticateUser);
|
||||
operation.SetSuccessor(new CreateNewUserOperation(CreateNewUser));
|
||||
|
||||
userCreationValidation = new IsAuthorisedToDoOperationsOnUser(principalHelper, Rights.Create);
|
||||
userCreationValidation.SetSuccessor(new ValidateNoDuplicateEmail(userRepository));
|
||||
|
||||
authenticateUserValidation = new ValidateUserExistsInDb(userRepository);
|
||||
|
||||
}
|
||||
|
||||
private void CreateNewUser(string email, string userName, int tenantId)
|
||||
{
|
||||
User user = new User { Email = email, UserName = userName, TenantId = tenantId };
|
||||
var result = userCreationValidation.Validate(user);
|
||||
|
||||
if (result.IsValid)
|
||||
userRepository.Add(user);
|
||||
else
|
||||
Console.WriteLine(result.Exception);
|
||||
}
|
||||
|
||||
private void AuthenticateUser(string email)
|
||||
{
|
||||
var result = authenticateUserValidation.Validate(new User { Email = email });
|
||||
|
||||
if (result.IsValid)
|
||||
{
|
||||
principalHelper.SetAuthenticatedUser(userRepository.Get(email));
|
||||
Console.WriteLine("Authentication successful");
|
||||
}
|
||||
else
|
||||
Console.WriteLine(result.Exception.Message);
|
||||
}
|
||||
|
||||
public void DoStuff()
|
||||
{
|
||||
string userInput;
|
||||
do
|
||||
{
|
||||
operation.PrintMenu();
|
||||
Console.Write(">");
|
||||
userInput = Console.ReadLine();
|
||||
|
||||
if (!IsExitCode(userInput))
|
||||
operation.Execute(userInput);
|
||||
|
||||
} while (!IsExitCode(userInput));
|
||||
}
|
||||
|
||||
private static bool IsExitCode(string input)
|
||||
{
|
||||
return "exit".Equals(input, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using ChainOfResponssibility.Validators.UserEntities.Infrastructure;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities.Validators
|
||||
{
|
||||
public class IsAuthorisedToDoOperationsOnUser : ChainValidation<User>
|
||||
{
|
||||
PrincipalHelper principalHelper;
|
||||
Rights rights;
|
||||
public IsAuthorisedToDoOperationsOnUser(PrincipalHelper principalHelper, Rights rights)
|
||||
{
|
||||
this.principalHelper = principalHelper;
|
||||
this.rights = rights;
|
||||
}
|
||||
|
||||
protected override ValidationResult IsValid(User obj)
|
||||
{
|
||||
User authenticatedUser = principalHelper.GetAuthenticatedUser();
|
||||
|
||||
if (authenticatedUser == null)
|
||||
return ValidationResult.GetInvalidResult(new ForbiddenException("Only authenticated users may create new users"));
|
||||
|
||||
if (!authenticatedUser.Rights.HasFlag(rights))
|
||||
return ValidationResult.GetInvalidResult(new ForbiddenException(string.Format("Unauthorised to do {0} on user", rights)));
|
||||
|
||||
if (authenticatedUser.TenantId != obj.TenantId)
|
||||
return ValidationResult.GetInvalidResult(new ForbiddenException("Cannot create user for another tenant"));
|
||||
|
||||
return ValidationResult.GetValidResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities
|
||||
{
|
||||
public class ValidateNoDuplicateEmail : ChainValidation<User>
|
||||
{
|
||||
UserRepository userRepository;
|
||||
public ValidateNoDuplicateEmail(UserRepository userRepository)
|
||||
{
|
||||
this.userRepository = userRepository;
|
||||
|
||||
}
|
||||
protected override ValidationResult IsValid(User obj)
|
||||
{
|
||||
if (userRepository.Exists(obj.Email))
|
||||
return ValidationResult.GetInvalidResult(new DuplicateRecordException(obj.Email));
|
||||
else
|
||||
return ValidationResult.GetValidResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChainOfResponssibility.Validators.UserEntities
|
||||
{
|
||||
public class ValidateUserExistsInDb : ChainValidation<User>
|
||||
{
|
||||
UserRepository userRepository;
|
||||
public ValidateUserExistsInDb(UserRepository userRepository)
|
||||
{
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
protected override ValidationResult IsValid(User obj)
|
||||
{
|
||||
bool userExists = userRepository.Exists(obj.Email);
|
||||
|
||||
if (userExists)
|
||||
return ValidationResult.GetValidResult();
|
||||
else
|
||||
return ValidationResult.GetInvalidResult(new NotFoundException(obj.ID));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/ChainOfResponssibility/project.json
Normal file
13
src/ChainOfResponssibility/project.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "1.0.0-*",
|
||||
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.5.0-rc2-24027"
|
||||
},
|
||||
|
||||
"frameworks": {
|
||||
"netstandard1.5": {
|
||||
"imports": "dnxcore50"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user