Merge pull request #6 from Azure/pdimit/DomainInRoot

Added domain and shared logic to root for use as submodule
This commit is contained in:
Maggie Pint 2017-10-25 15:37:35 -07:00 коммит произвёл GitHub
Родитель 119aa01eec 600dc3e46c
Коммит 46ad89eb58
52 изменённых файлов: 1171 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Sia.Domain.ApiModels
{
public class NewEngagement
{
[Required]
public Participant Participant { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,19 @@
using Sia.Shared.Data;
using System;
using System.ComponentModel.DataAnnotations;
using System.Dynamic;
namespace Sia.Domain.ApiModels
{
public class NewEvent
:IJsonDataObject
{
[Required]
public long? EventTypeId { get; set; }
[Required]
public DateTime? Occurred { get; set; }
[Required]
public DateTime? EventFired { get; set; }
public object Data { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace Sia.Domain.ApiModels
{
public class NewIncident
{
[Required]
public string Title { get; set; }
[Required]
public Ticket PrimaryTicket
{
get
{
return Tickets.FirstOrDefault(ticket => ticket.IsPrimary);
}
set
{
if (Tickets == null) Tickets = new List<Ticket>();
foreach (var ticket in Tickets.Where(ticket => ticket.IsPrimary))
{
ticket.IsPrimary = false;
}
if (value == null) return;
if (!Tickets.Contains(value)) Tickets.Add(value);
value.IsPrimary = true;
}
}
public IList<Ticket> Tickets { get; set; }
= new List<Ticket>();
public IList<NewEvent> Events { get; set; }
= new List<NewEvent>();
public IList<Participant> Engagements { get; set; }
= new List<Participant>();
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using Sia.Domain.Playbook;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.ApiModels.Playbooks
{
public class CreateAction
{
public CreateActionTemplate NewActionTemplate { get; set; }
public long? ExistingActionTemplateId { get; set; }
public ICollection<CreateConditionSet> NewConditionSets { get; set; }
= new List<CreateConditionSet>();
public ICollection<long> ExistingConditionSetIds { get; set; }
= new List<long>();
}
}

Просмотреть файл

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Sia.Domain.ApiModels.Playbooks
{
public class CreateActionTemplate
{
[Required]
public string Name { get; set; }
public bool IsUrl { get; set; }
public string Template { get; set; }
public ICollection<CreateActionTemplateSource> NewSources { get; set; }
= new List<CreateActionTemplateSource>();
public ICollection<long> ExistingSourceIds { get; set; }
= new List<long>();
}
}

Просмотреть файл

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.ApiModels.Playbooks
{
public class CreateActionTemplateSource : CreateSource
{
}
}

Просмотреть файл

@ -0,0 +1,21 @@
using Sia.Domain.Playbook;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.ApiModels.Playbooks
{
public class CreateCondition
{
public string Name { get; set; }
public AssertionType AssertionType { get; set; }
= AssertionType.IsOrDoes;
public ConditionType ConditionType { get; set; }
public DataFormat DataFormat { get; set; }
= DataFormat.String;
public string ComparisonValue { get; set; }
public long? IntegerComparisonValue { get; set; }
public DateTime DateTimeComparisonValue { get; set; }
public long? ExistingConditionSourceId { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using Sia.Domain.Playbook;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.ApiModels.Playbooks
{
public class CreateConditionSet
{
public string Name { get; set; }
public ConditionSetType Type { get; set; }
public ICollection<CreateCondition> NewConditions { get; set; }
= new List<CreateCondition>();
public ICollection<long> ExistingConditionIds { get; set; }
= new List<long>();
}
}

Просмотреть файл

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.ApiModels.Playbooks
{
public class CreateConditionSource : CreateSource
{
}
}

Просмотреть файл

@ -0,0 +1,16 @@
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Sia.Domain.ApiModels.Playbooks
{
public class CreateEventType : IJsonDataObject
{
[Required]
public string Name { get; set; }
[Required]
public object Data { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,14 @@
using Sia.Domain.Playbook;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.ApiModels.Playbooks
{
public abstract class CreateSource
{
public SourceObject SourceObject { get; set; }
public string Key { get; set; }
public string Name { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>Microsoft.Sia.Domain.ApiModels</PackageId>
<Version>1.1.1-alpha</Version>
<Authors>pdimit, magpint, jache, chtownes</Authors>
<Company>Microsoft</Company>
<Product>SRE Incident Assistant</Product>
<Description>API operation models for the SRE Incident Assistant</Description>
<Copyright>Copyright 2017</Copyright>
<PackageReleaseNotes>Initial open source release, intended for prototyping.</PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.DataAnnotations" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sia.Domain\Sia.Domain.csproj" />
<ProjectReference Include="..\Sia.Shared\Sia.Shared.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,10 @@
using System;
namespace Sia.Domain.ApiModels
{
public class UpdateEngagement
{
public DateTime? TimeEngaged { get; set; }
public DateTime? TimeDisengaged { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,12 @@
using Sia.Shared.Data;
using System.ComponentModel.DataAnnotations;
namespace Sia.Domain.ApiModels
{
public class UpdateIncident
{
public string Title { get; set; }
[Required]
public Ticket PrimaryTicket { get; set; }
}
}

37
Domain/Sia.Domain.sln Normal file
Просмотреть файл

@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sia.Domain", "Sia.Domain\Sia.Domain.csproj", "{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sia.Domain.ApiModels", "Sia.Domain.ApiModels\Sia.Domain.ApiModels.csproj", "{23F482C9-DBFE-4E14-A128-BF78B9FC8702}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sia.Shared", "Sia.Shared\Sia.Shared.csproj", "{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}.Release|Any CPU.Build.0 = Release|Any CPU
{23F482C9-DBFE-4E14-A128-BF78B9FC8702}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23F482C9-DBFE-4E14-A128-BF78B9FC8702}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23F482C9-DBFE-4E14-A128-BF78B9FC8702}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23F482C9-DBFE-4E14-A128-BF78B9FC8702}.Release|Any CPU.Build.0 = Release|Any CPU
{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C4632DD2-4898-45B0-B3C5-FCBA12370F09}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,16 @@
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain
{
public class Engagement : IEntity
{
public long Id { get; set; }
public long IncidentId { get; set; }
public DateTime TimeEngaged { get; set; }
public DateTime? TimeDisengaged { get; set; }
public Participant Participant {get; set;}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
using Sia.Domain;
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Text;
namespace Sia.Domain
{
public class Event : IEntity, IJsonDataObject
{
public long Id { get; set; }
public long? IncidentId { get; set; }
public long EventTypeId { get; set; }
public DateTime Occurred { get; set; }
public DateTime EventFired { get; set; }
public object Data { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,39 @@
using Sia.Shared.Data;
using System.Collections.Generic;
using System.Linq;
namespace Sia.Domain
{
public class Incident : IEntity
{
public long Id { get; set; }
public string Title { get; set; }
public Ticket PrimaryTicket
{
get
{
return Tickets.FirstOrDefault(ticket => ticket.IsPrimary);
}
set
{
if (Tickets == null) Tickets = new List<Ticket>();
foreach (var ticket in Tickets.Where(ticket => ticket.IsPrimary))
{
ticket.IsPrimary = false;
}
if (value == null) return;
if (!Tickets.Contains(value)) Tickets.Add(value);
value.IsPrimary = true;
}
}
public ICollection<Ticket> Tickets { get; set; }
= new List<Ticket>();
public ICollection<Event> Events { get; set; }
= new List<Event>();
public ICollection<Engagement> Engagements { get; set; }
= new List<Engagement>();
}
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace Sia.Domain
{
public class Participant
{
public string Alias { get; set; }
public string Team { get; set; }
public string Role { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,15 @@
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.Playbook
{
public class Action : IEntity
{
public long Id { get; set; }
public ActionTemplate ActionTemplate { get; set; }
public ICollection<ConditionSet> ConditionSets { get; set; }
= new List<ConditionSet>();
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.Playbook
{
public class ActionTemplate : IEntity
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsUrl { get; set; }
public string Template { get; set; }
public ICollection<ActionTemplateSource> Sources { get; set; }
= new List<ActionTemplateSource>();
}
}

Просмотреть файл

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.Playbook
{
public class ActionTemplateSource : Source
{
}
}

Просмотреть файл

@ -0,0 +1,44 @@
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.Playbook
{
public class Condition : IEntity
{
public long Id { get; set; }
public string Name { get; set; }
public AssertionType AssertionType { get; set; }
= AssertionType.IsOrDoes;
public ConditionType ConditionType { get; set; }
public DataFormat DataFormat { get; set; }
= DataFormat.String;
public string ComparisonValue { get; set; }
public long? IntegerComparisonValue { get; set; }
public DateTime DateTimeComparisonValue { get; set; }
public ConditionSource ConditionSource { get; set; }
}
public enum AssertionType
{
IsOrDoes,
IsNotOrDoesNot
}
public enum ConditionType
{
Equal,
Contain,
HaveValue,
GreaterThan,
LessThan
}
public enum DataFormat
{
String,
DateTime,
Integer
}
}

Просмотреть файл

@ -0,0 +1,23 @@
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.Playbook
{
public class ConditionSet : IEntity
{
public long Id { get; set; }
public string Name { get; set; }
public ConditionSetType Type { get; set; }
public ICollection<Condition> Conditions { get; set; }
= new List<Condition>();
}
public enum ConditionSetType
{
AnyOf,
AllOf,
NoneOf
}
}

Просмотреть файл

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.Playbook
{
public class ConditionSource : Source
{
}
}

Просмотреть файл

@ -0,0 +1,15 @@
using Sia.Shared.Data;
using System.Collections.Generic;
namespace Sia.Domain.Playbook
{
public class EventType : IEntity, IJsonDataObject
{
public long Id { get; set; }
public string Name { get; set; }
public object Data { get; set; }
public ICollection<Action> Actions { get; set; }
= new List<Action>();
}
}

Просмотреть файл

@ -0,0 +1,23 @@
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Domain.Playbook
{
public abstract class Source : IEntity
{
public long Id { get; set; }
public string Name { get; set; }
public SourceObject SourceObject { get; set; }
public string Key { get; set; }
}
public enum SourceObject
{
Event,
Ticket,
EventType,
Engagement
}
}

Просмотреть файл

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Microsoft.Sia.Domain</PackageId>
<Version>1.1.3-alpha</Version>
<Authors>pdimit, magpint, jache, chtownes</Authors>
<Company>Microsoft</Company>
<Product>SRE Incident Assistant</Product>
<Description>Domain models for the SRE Incident Assistant</Description>
<Copyright>Copyright 2017</Copyright>
<PackageReleaseNotes>Initial open source release, intended for prototyping.</PackageReleaseNotes>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.DataAnnotations" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sia.Shared\Sia.Shared.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,14 @@
using Sia.Shared.Data;
namespace Sia.Domain
{
public class Ticket : IEntity
{
public long Id { get; set; }
public long IncidentId { get; set; }
public string OriginId { get; set; }
public long TicketingSystemId { get; set; }
public string OriginUri { get; set; }
public bool IsPrimary { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,10 @@
using Sia.Shared.Data;
namespace Sia.Domain
{
public class TicketingSystem : IEntity
{
public long Id { get; set; }
public string Name { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,72 @@
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.KeyVault.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Sia.Shared.Validation;
using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace Sia.Shared.Authentication
{
public interface ISecretVault
{
Task<string> Get(string secretName);
}
public class AzureSecretVault : ISecretVault
{
private readonly KeyVaultConfiguration _config;
private const string _secretsEndpoint = "/secrets/";
private const string _keysEndpoint = "/keys/";
private const string _certificatesEndpoint = "/certificates/";
public AzureSecretVault(KeyVaultConfiguration configuration)
{
_config = ThrowIf.Null(configuration, nameof(configuration));
}
public async Task<string> Get(string secretName)
{
try
{
var secret = await GetKeyVaultClient().GetSecretAsync(_config.Vault + _secretsEndpoint + secretName).ConfigureAwait(false);
return secret.Value;
}
catch (KeyVaultErrorException ex)
{
return string.Empty;
}
}
public async Task<X509Certificate2> GetCertificate(string certificateName)
{
try
{
var cert = await GetKeyVaultClient()
.GetCertificateAsync(_config.Vault, certificateName)
.ConfigureAwait(false);
return new X509Certificate2(cert.Cer);
}
catch (KeyVaultErrorException ex)
{
return null;
}
}
private async Task<string> GetToken(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(_config.ClientId, _config.ClientSecret);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken;
}
private KeyVaultClient GetKeyVaultClient() => new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
}
}

Просмотреть файл

@ -0,0 +1,20 @@
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
namespace Sia.Shared.Authentication
{
public abstract class CertificateRetriever : IHttpClientFactory
{
protected X509Certificate2 _cert;
public abstract X509Certificate2 Certificate { get; }
public virtual HttpClient GetClient()
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(Certificate);
return new HttpClient(handler);
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
using System.Net.Http;
namespace Sia.Shared.Authentication
{
public interface IHttpClientFactory
{
HttpClient GetClient();
}
}

Просмотреть файл

@ -0,0 +1,29 @@
using Sia.Shared.Validation;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace Sia.Shared.Authentication
{
public class KeyVaultCertificateRetriever
: CertificateRetriever
{
private readonly X509Certificate2 _certificate;
public KeyVaultCertificateRetriever(AzureSecretVault certificateVault, string certificateName)
{
ThrowIf.NullOrWhiteSpace(certificateName, nameof(certificateName));
var certTask = certificateVault.GetCertificate(certificateName);
Task.WaitAll(new Task[] { certTask });
if (certTask.IsCompleted)
{
_certificate = certTask.Result;
}
}
public override X509Certificate2 Certificate => _certificate;
}
}

Просмотреть файл

@ -0,0 +1,24 @@
using Microsoft.Extensions.Configuration;
using Sia.Shared.Validation;
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Shared.Authentication
{
public class KeyVaultConfiguration
{
public KeyVaultConfiguration(string clientId, string clientSecret, string vault)
{
ClientId = ThrowIf.NullOrWhiteSpace(clientId, nameof(clientId));
ClientSecret = ThrowIf.NullOrWhiteSpace(clientSecret, nameof(clientSecret));
Vault = String.Format(secretUriBase, ThrowIf.NullOrWhiteSpace(vault, nameof(vault)));
}
private const string secretUriBase = "https://{0}.vault.azure.net";
public readonly string Vault;
public readonly string ClientId;
public readonly string ClientSecret;
}
}

Просмотреть файл

@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
namespace Sia.Shared.Authentication
{
public class LocalCertificateRetriever
: CertificateRetriever
{
private readonly string _certThumbprint;
private List<StoreLocation> _storeLocations
=> new List<StoreLocation>
{
StoreLocation.CurrentUser,
StoreLocation.LocalMachine
};
public LocalCertificateRetriever(string certThumbprint)
{
_certThumbprint = certThumbprint;
}
public override X509Certificate2 Certificate
{
get
{
if (_cert == null)
{
_cert = GetCertFromStore();
}
return _cert;
}
}
private X509Certificate2 GetCertFromStore()
{
foreach (var location in _storeLocations)
{
using (X509Store store = new X509Store("My", location))
{
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindByThumbprint, _certThumbprint, true);
if (certificates.Count > 0)
return certificates[0];
}
}
throw new KeyNotFoundException("Could not find valid certificate");
}
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using System.Net.Http;
namespace Sia.Shared.Authentication
{
public class UnauthenticatedClientFactory : IHttpClientFactory
{
HttpClient _httpClient;
public HttpClient GetClient()
{
if (_httpClient == null)
{
_httpClient = new HttpClient();
}
return _httpClient;
}
}
}

Просмотреть файл

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
namespace Sia.Shared.Data
{
public interface IAssociation<TLeft, TRight>
{
void Associate(TLeft left, TRight right);
KeyValuePair<TLeft, TRight> AssociatesBetween();
}
public abstract class Association<TLeft, TRight>
: IAssociation<TLeft, TRight>
where TLeft : IEntity
where TRight : IEntity
{
protected internal abstract long _leftId { get; set; }
protected internal abstract long _rightId { get; set; }
protected internal abstract TLeft _left { get; set; }
protected internal abstract TRight _right { get; set; }
public void Associate(TLeft left, TRight right)
{
_left = left;
_right = right;
_leftId = left.Id;
_rightId = right.Id;
}
public KeyValuePair<TLeft, TRight> AssociatesBetween()
=> new KeyValuePair<TLeft, TRight>(_left, _right);
}
public abstract class BidrectionalAssociation<TLeft, TRight>
: Association<TLeft, TRight>,
IAssociation<TRight, TLeft>
where TLeft : IEntity
where TRight : IEntity
{
protected BidrectionalAssociation()
{
}
void IAssociation<TRight, TLeft>.Associate(TRight left, TLeft right)
=> Associate(right, left);
KeyValuePair<TRight, TLeft> IAssociation<TRight, TLeft>.AssociatesBetween()
=> new KeyValuePair<TRight, TLeft>(_right, _left);
}
}

Просмотреть файл

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Shared.Data
{
public interface IEntity
{
long Id { get; }
}
}

Просмотреть файл

@ -0,0 +1,75 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Sia.Shared.Data
{
public class ManyToManyCollection<TItem, TAssociation, TAssociated> : ICollection<TAssociated>
where TAssociation : IAssociation<TItem, TAssociated>, new()
{
private TItem _parent;
private ICollection<TAssociation> _associations;
private IEnumerable<TAssociated> _associated => _associations.Select(assoc => assoc.AssociatesBetween().Value);
public ManyToManyCollection(TItem parent, ICollection<TAssociation> associations)
{
_parent = parent;
_associations = associations;
}
public int Count => _associations.Count;
public bool IsReadOnly => false;
public void Add(TAssociated item)
{
var associationToAdd = new TAssociation();
associationToAdd.Associate(_parent, item);
_associations.Add(associationToAdd);
}
public void Clear()
{
_associations.Clear();
}
public bool Contains(TAssociated item)
{
return _associated.Contains(item);
}
public void CopyTo(TAssociated[] array, int arrayIndex)
{
if (array == null) throw new ArgumentNullException(nameof(arrayIndex));
if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, "Array Index cannot be less than 0!");
if (array.GetLength(0) - arrayIndex > Count) throw new ArgumentException("The number of elements in the source System.Collections.Generic.ICollection`1 is greater than the available space from arrayIndex to the end of the destination array.", nameof(arrayIndex));
_associated.ToList().CopyTo(array, arrayIndex);
}
public IEnumerator<TAssociated> GetEnumerator()
{
return _associated.GetEnumerator();
}
public bool Remove(TAssociated item)
{
var relevantAssociations = _associations.Where(assoc => assoc.AssociatesBetween().Value.Equals(item));
bool success = true;
foreach (var association in relevantAssociations)
{
if (!_associations.Remove(association))
{
success = false;
}
}
return success;
}
IEnumerator IEnumerable.GetEnumerator()
{
return _associated.GetEnumerator();
}
}
}

Просмотреть файл

@ -0,0 +1,31 @@
using Sia.Shared.Data;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
namespace Microsoft.EntityFrameworkCore
{
public static class ModelBuilderExtensions
{
public static void WithManyToManyAssociation<TLeft, TRight, TAssociation>(this ModelBuilder builder
, Expression<Func<TLeft, IEnumerable<TAssociation>>> associationsFromLeft
, Expression<Func<TRight, IEnumerable<TAssociation>>> associationsFromRight
)
where TAssociation : BidrectionalAssociation<TLeft, TRight>
where TLeft : class, IEntity
where TRight : class, IEntity
{
builder.Entity<TAssociation>()
.HasOne(assoc => assoc._left)
.WithMany(associationsFromLeft)
.HasForeignKey(assoc => assoc._leftId);
builder.Entity<TAssociation>()
.HasOne(assoc => assoc._right)
.WithMany(associationsFromRight)
.HasForeignKey(assoc => assoc._rightId);
builder.Entity<TAssociation>()
.HasKey(assoc => new { assoc._leftId, assoc._rightId });
}
}
}

Просмотреть файл

@ -0,0 +1,39 @@
using AutoMapper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Threading.Tasks;
namespace Sia.Shared.Data
{
public interface IJsonDataString
{
string Data { get; set; }
}
public interface IJsonDataObject
{
object Data { get; set; }
}
public class ResolveJsonToString<TSource, TDestination>
: IValueResolver<TSource, TDestination, string>
where TSource: IJsonDataObject
where TDestination: IJsonDataString
{
public string Resolve(TSource source, TDestination destination, string destMember, ResolutionContext context)
=> source.Data is null ? null : JsonConvert.SerializeObject(source.Data);
}
public class ResolveStringToJson<TSource, TDestination>
: IValueResolver<TSource, TDestination, object>
where TSource : IJsonDataString
where TDestination : IJsonDataObject
{
public object Resolve(TSource source, TDestination destination, object destMember, ResolutionContext context)
=> source.Data is null ? null : JsonConvert.DeserializeObject(source.Data);
}
}

Просмотреть файл

@ -0,0 +1,11 @@
namespace Sia.Shared.Exceptions
{
public class BadRequestException : BaseException
{
public BadRequestException(string message) : base(message)
{
}
public override int StatusCode => 400;
}
}

Просмотреть файл

@ -0,0 +1,13 @@
using System;
namespace Sia.Shared.Exceptions
{
public abstract class BaseException : Exception
{
public BaseException(string message) : base(message)
{
}
public abstract int StatusCode { get; }
}
}

Просмотреть файл

@ -0,0 +1,11 @@
namespace Sia.Shared.Exceptions
{
public class ConflictException : BaseException
{
public ConflictException(string message) : base(message)
{
}
public override int StatusCode => 409;
}
}

Просмотреть файл

@ -0,0 +1,32 @@
using Sia.Shared.Transactions;
using System;
namespace Sia.Shared.Exceptions
{
public static class ConvertStatusToException
{
public static void ThrowExceptionOnUnsuccessfulStatus<T>(this IResponse<T> response)
{
if (response.IsSuccessStatusCode)
{
return;
}
var message = response.Content;
switch (response.StatusCode)
{
case System.Net.HttpStatusCode.BadRequest:
throw new BadRequestException(message);
case System.Net.HttpStatusCode.Conflict:
throw new ConflictException(message);
case System.Net.HttpStatusCode.Forbidden:
throw new UnauthorizedException(message);
case System.Net.HttpStatusCode.NotFound:
throw new NotFoundException(message);
case System.Net.HttpStatusCode.Unauthorized:
throw new UnauthorizedException(message);
default:
throw new Exception(message);
}
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
namespace Sia.Shared.Exceptions
{
public class NotFoundException : BaseException
{
public NotFoundException(string message) : base(message)
{
}
public override int StatusCode => 404;
}
}

Просмотреть файл

@ -0,0 +1,11 @@
namespace Sia.Shared.Exceptions
{
public class UnauthorizedException : BaseException
{
public UnauthorizedException(string message) : base(message)
{
}
public override int StatusCode => 403;
}
}

Просмотреть файл

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>1.1.0-alpha</Version>
<PackageId>Microsoft.Sia.Shared</PackageId>
<Authors>pdimit, magpint, jache, chtownes</Authors>
<Company>Microsoft</Company>
<Product>SRE Incident Assistant</Product>
<Description>SIA utility and shared code</Description>
<PackageReleaseNotes>Initial open source release, intended for prototyping.</PackageReleaseNotes>
<Copyright>Copyright 2017</Copyright>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="6.1.1" />
<PackageReference Include="Microsoft.Azure.KeyVault" Version="2.3.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="3.16.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,12 @@
using System.Net;
namespace Sia.Shared.Transactions
{
public interface IResponse<T>
{
HttpStatusCode StatusCode { get; }
bool IsSuccessStatusCode { get; }
string Content { get; }
T Value { get; }
}
}

Просмотреть файл

@ -0,0 +1,31 @@
using Newtonsoft.Json;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
namespace Sia.Shared.Transactions
{
public class Response<T> : IResponse<T>
{
public HttpStatusCode StatusCode { get; private set; }
public bool IsSuccessStatusCode { get; private set; }
public string Content { get; private set; }
public T Value { get; private set; }
public static async Task<Response<T>> Create(HttpResponseMessage message)
{
var response = new Response<T>();
response.IsSuccessStatusCode = message.IsSuccessStatusCode;
response.StatusCode = message.StatusCode;
var content = await message.Content.ReadAsStringAsync();
if (message.IsSuccessStatusCode)
{
response.Value = JsonConvert.DeserializeObject<T>(content);
}
else
{
response.Content = content;
}
return response;
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
using System;
namespace Sia.Shared.Validation
{
public static class ThrowIf
{
public static TParam Null<TParam>(TParam o, string paramName)
where TParam : class
=> o ?? throw new ArgumentNullException(paramName);
public static string NullOrWhiteSpace(string input, string paramName)
{
if (string.IsNullOrWhiteSpace(input)) throw new ArgumentException("Value cannot be null or white space", paramName);
return input;
}
}
}