@ -0,0 +1,29 @@ | |||
apiVersion: v1 | |||
kind: Service | |||
metadata: | |||
labels: | |||
app: eshop | |||
component: keystore-data | |||
name: keystore-data | |||
spec: | |||
ports: | |||
- port: 6379 | |||
selector: | |||
app: eshop | |||
component: keystore-data | |||
--- | |||
apiVersion: extensions/v1beta1 | |||
kind: Deployment | |||
metadata: | |||
name: keystore-data | |||
spec: | |||
template: | |||
metadata: | |||
labels: | |||
app: eshop | |||
component: keystore-data | |||
spec: | |||
containers: | |||
- name: keystore-data | |||
image: redis:3.2-alpine | |||
@ -0,0 +1,13 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp1.1</TargetFramework> | |||
<RootNamespace>Microsoft.eShopOnContainers.BuildingBlocks</RootNamespace> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="1.1.2" /> | |||
<PackageReference Include="StackExchange.Redis" Version="1.2.3" /> | |||
</ItemGroup> | |||
</Project> |
@ -0,0 +1,95 @@ | |||
namespace Microsoft.eShopOnContainers.BuildingBlocks | |||
{ | |||
using Microsoft.AspNetCore.DataProtection; | |||
using Microsoft.AspNetCore.DataProtection.Repositories; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using System; | |||
using System.Linq; | |||
using System.Net; | |||
/// <summary> | |||
/// Extension methods for <see cref="IDataProtectionBuilder"/> for configuring | |||
/// data protection options. | |||
/// </summary> | |||
public static class DataProtectionBuilderExtensions | |||
{ | |||
/// <summary> | |||
/// Sets up data protection to persist session keys in Redis. | |||
/// </summary> | |||
/// <param name="builder">The <see cref="IDataProtectionBuilder"/> used to set up data protection options.</param> | |||
/// <param name="redisConnectionString">The connection string specifying the Redis instance and database for key storage.</param> | |||
/// <returns> | |||
/// The <paramref name="builder" /> for continued configuration. | |||
/// </returns> | |||
/// <exception cref="System.ArgumentNullException"> | |||
/// Thrown if <paramref name="builder" /> or <paramref name="redisConnectionString" /> is <see langword="null" />. | |||
/// </exception> | |||
/// <exception cref="System.ArgumentException"> | |||
/// Thrown if <paramref name="redisConnectionString" /> is empty. | |||
/// </exception> | |||
public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, string redisConnectionString) | |||
{ | |||
if (builder == null) | |||
{ | |||
throw new ArgumentNullException(nameof(builder)); | |||
} | |||
if (redisConnectionString == null) | |||
{ | |||
throw new ArgumentNullException(nameof(redisConnectionString)); | |||
} | |||
if (redisConnectionString.Length == 0) | |||
{ | |||
throw new ArgumentException("Redis connection string may not be empty.", nameof(redisConnectionString)); | |||
} | |||
var ips = Dns.GetHostAddressesAsync(redisConnectionString).Result; | |||
return builder.Use(ServiceDescriptor.Singleton<IXmlRepository>(services => | |||
new RedisXmlRepository(ips.First().ToString(), services.GetRequiredService<ILogger<RedisXmlRepository>>()))); | |||
} | |||
/// <summary> | |||
/// Updates an <see cref="IDataProtectionBuilder"/> to use the service of | |||
/// a specific type, removing all other services of that type. | |||
/// </summary> | |||
/// <param name="builder">The <see cref="IDataProtectionBuilder"/> that should use the specified service.</param> | |||
/// <param name="descriptor">The <see cref="ServiceDescriptor"/> with the service the <paramref name="builder" /> should use.</param> | |||
/// <returns> | |||
/// The <paramref name="builder" /> for continued configuration. | |||
/// </returns> | |||
/// <exception cref="System.ArgumentNullException"> | |||
/// Thrown if <paramref name="builder" /> or <paramref name="descriptor" /> is <see langword="null" />. | |||
/// </exception> | |||
public static IDataProtectionBuilder Use(this IDataProtectionBuilder builder, ServiceDescriptor descriptor) | |||
{ | |||
// This algorithm of removing all other services of a specific type | |||
// before adding the new/replacement service is how the base ASP.NET | |||
// DataProtection bits work. Due to some of the differences in how | |||
// that base set of bits handles DI, it's better to follow suit | |||
// and work in the same way than to try and debug weird issues. | |||
if (builder == null) | |||
{ | |||
throw new ArgumentNullException(nameof(builder)); | |||
} | |||
if (descriptor == null) | |||
{ | |||
throw new ArgumentNullException(nameof(descriptor)); | |||
} | |||
for (int i = builder.Services.Count - 1; i >= 0; i--) | |||
{ | |||
if (builder.Services[i]?.ServiceType == descriptor.ServiceType) | |||
{ | |||
builder.Services.RemoveAt(i); | |||
} | |||
} | |||
builder.Services.Add(descriptor); | |||
return builder; | |||
} | |||
} | |||
} |
@ -0,0 +1,210 @@ | |||
namespace Microsoft.eShopOnContainers.BuildingBlocks | |||
{ | |||
using Microsoft.AspNetCore.DataProtection.Repositories; | |||
using Microsoft.Extensions.Logging; | |||
using StackExchange.Redis; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text.RegularExpressions; | |||
using System.Xml.Linq; | |||
/// <summary> | |||
/// Key repository that stores XML encrypted keys in a Redis distributed cache. | |||
/// </summary> | |||
/// <remarks> | |||
/// <para> | |||
/// The values stored in Redis are XML documents that contain encrypted session | |||
/// keys used for the protection of things like session state. The document contents | |||
/// are double-encrypted - first with a changing session key; then by a master key. | |||
/// As such, there's no risk in storing the keys in Redis - even if someone can crack | |||
/// the master key, they still need to also crack the session key. (Other solutions | |||
/// for sharing keys across a farm environment include writing them to files | |||
/// on a file share.) | |||
/// </para> | |||
/// <para> | |||
/// While the repository uses a hash to keep the set of encrypted keys separate, you | |||
/// can further separate these items from other items in Redis by specifying a unique | |||
/// database in the connection string. | |||
/// </para> | |||
/// <para> | |||
/// Consumers of the repository are responsible for caching the XML items as needed. | |||
/// Typically repositories are consumed by things like <see cref="Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingProvider"/> | |||
/// which generates <see cref="Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.CacheableKeyRing"/> | |||
/// values that get cached. The mechanism is already optimized for caching so there's | |||
/// no need to create a redundant cache. | |||
/// </para> | |||
/// </remarks> | |||
/// <seealso cref="Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository" /> | |||
/// <seealso cref="System.IDisposable" /> | |||
public class RedisXmlRepository : IXmlRepository, IDisposable | |||
{ | |||
/// <summary> | |||
/// The root cache key for XML items stored in Redis | |||
/// </summary> | |||
public static readonly string RedisHashKey = "DataProtectionXmlRepository"; | |||
/// <summary> | |||
/// The connection to the Redis backing store. | |||
/// </summary> | |||
private IConnectionMultiplexer _connection; | |||
/// <summary> | |||
/// Flag indicating whether the object has been disposed. | |||
/// </summary> | |||
private bool _disposed = false; | |||
/// <summary> | |||
/// Initializes a new instance of the <see cref="RedisXmlRepository"/> class. | |||
/// </summary> | |||
/// <param name="connectionString"> | |||
/// The Redis connection string. | |||
/// </param> | |||
/// <param name="logger"> | |||
/// The <see cref="ILogger{T}"/> used to log diagnostic messages. | |||
/// </param> | |||
/// <exception cref="System.ArgumentNullException"> | |||
/// Thrown if <paramref name="connectionString" /> or <paramref name="logger" /> is <see langword="null" />. | |||
/// </exception> | |||
public RedisXmlRepository(string connectionString, ILogger<RedisXmlRepository> logger) | |||
: this(ConnectionMultiplexer.Connect(connectionString), logger) | |||
{ | |||
} | |||
/// <summary> | |||
/// Initializes a new instance of the <see cref="RedisXmlRepository"/> class. | |||
/// </summary> | |||
/// <param name="connection"> | |||
/// The Redis database connection. | |||
/// </param> | |||
/// <param name="logger"> | |||
/// The <see cref="ILogger{T}"/> used to log diagnostic messages. | |||
/// </param> | |||
/// <exception cref="System.ArgumentNullException"> | |||
/// Thrown if <paramref name="connection" /> or <paramref name="logger" /> is <see langword="null" />. | |||
/// </exception> | |||
public RedisXmlRepository(IConnectionMultiplexer connection, ILogger<RedisXmlRepository> logger) | |||
{ | |||
if (connection == null) | |||
{ | |||
throw new ArgumentNullException(nameof(connection)); | |||
} | |||
if (logger == null) | |||
{ | |||
throw new ArgumentNullException(nameof(logger)); | |||
} | |||
this._connection = connection; | |||
this.Logger = logger; | |||
// Mask the password so it doesn't get logged. | |||
var configuration = Regex.Replace(this._connection.Configuration, @"password\s*=\s*[^,]*", "password=****", RegexOptions.IgnoreCase); | |||
this.Logger.LogDebug("Storing data protection keys in Redis: {RedisConfiguration}", configuration); | |||
} | |||
/// <summary> | |||
/// Gets the logger. | |||
/// </summary> | |||
/// <value> | |||
/// The <see cref="ILogger{T}"/> used to log diagnostic messages. | |||
/// </value> | |||
public ILogger<RedisXmlRepository> Logger { get; private set; } | |||
/// <summary> | |||
/// Performs application-defined tasks associated with freeing, releasing, | |||
/// or resetting unmanaged resources. | |||
/// </summary> | |||
public void Dispose() | |||
{ | |||
this.Dispose(true); | |||
} | |||
/// <summary> | |||
/// Gets all top-level XML elements in the repository. | |||
/// </summary> | |||
/// <returns> | |||
/// An <see cref="IReadOnlyCollection{T}"/> with the set of elements | |||
/// stored in the repository. | |||
/// </returns> | |||
public IReadOnlyCollection<XElement> GetAllElements() | |||
{ | |||
var database = this._connection.GetDatabase(); | |||
var hash = database.HashGetAll(RedisHashKey); | |||
var elements = new List<XElement>(); | |||
if (hash == null || hash.Length == 0) | |||
{ | |||
return elements.AsReadOnly(); | |||
} | |||
foreach (var item in hash.ToStringDictionary()) | |||
{ | |||
elements.Add(XElement.Parse(item.Value)); | |||
} | |||
this.Logger.LogDebug("Read {XmlElementCount} XML elements from Redis.", elements.Count); | |||
return elements.AsReadOnly(); | |||
} | |||
/// <summary> | |||
/// Adds a top-level XML element to the repository. | |||
/// </summary> | |||
/// <param name="element">The element to add.</param> | |||
/// <param name="friendlyName"> | |||
/// An optional name to be associated with the XML element. | |||
/// For instance, if this repository stores XML files on disk, the friendly name may | |||
/// be used as part of the file name. Repository implementations are not required to | |||
/// observe this parameter even if it has been provided by the caller. | |||
/// </param> | |||
/// <remarks> | |||
/// The <paramref name="friendlyName" /> parameter must be unique if specified. | |||
/// For instance, it could be the ID of the key being stored. | |||
/// </remarks> | |||
/// <exception cref="System.ArgumentNullException"> | |||
/// Thrown if <paramref name="element" /> is <see langword="null" />. | |||
/// </exception> | |||
public void StoreElement(XElement element, string friendlyName) | |||
{ | |||
if (element == null) | |||
{ | |||
throw new ArgumentNullException(nameof(element)); | |||
} | |||
if (string.IsNullOrEmpty(friendlyName)) | |||
{ | |||
// The framework always passes in a name, but | |||
// the contract indicates this may be null or empty. | |||
friendlyName = Guid.NewGuid().ToString(); | |||
} | |||
this.Logger.LogDebug("Storing XML element with friendly name {XmlElementFriendlyName}.", friendlyName); | |||
this._connection.GetDatabase().HashSet(RedisHashKey, friendlyName, element.ToString()); | |||
} | |||
/// <summary> | |||
/// Releases unmanaged and - optionally - managed resources. | |||
/// </summary> | |||
/// <param name="disposing"> | |||
/// <see langword="true" /> to release both managed and unmanaged resources; | |||
/// <see langword="false" /> to release only unmanaged resources. | |||
/// </param> | |||
protected virtual void Dispose(bool disposing) | |||
{ | |||
if (!this._disposed) | |||
{ | |||
if (disposing) | |||
{ | |||
if (this._connection != null) | |||
{ | |||
this._connection.Close(); | |||
this._connection.Dispose(); | |||
} | |||
} | |||
this._connection = null; | |||
this._disposed = true; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
namespace eShopOnContainers.Core.Models.Location | |||
{ | |||
public class LocationRequest | |||
{ | |||
public double Longitude { get; set; } | |||
public double Latitude { get; set; } | |||
} | |||
} |
@ -0,0 +1,10 @@ | |||
namespace eShopOnContainers.Core.Services.Location | |||
{ | |||
using System.Threading.Tasks; | |||
using eShopOnContainers.Core.Models.Location; | |||
public interface ILocationService | |||
{ | |||
Task UpdateUserLocation(LocationRequest newLocReq); | |||
} | |||
} |
@ -0,0 +1,28 @@ | |||
namespace eShopOnContainers.Core.Services.Location | |||
{ | |||
using eShopOnContainers.Core.Models.Location; | |||
using eShopOnContainers.Core.Services.RequestProvider; | |||
using System; | |||
using System.Threading.Tasks; | |||
public class LocationService : ILocationService | |||
{ | |||
private readonly IRequestProvider _requestProvider; | |||
public LocationService(IRequestProvider requestProvider) | |||
{ | |||
_requestProvider = requestProvider; | |||
} | |||
public async Task UpdateUserLocation(LocationRequest newLocReq) | |||
{ | |||
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.LocationEndpoint); | |||
builder.Path = "api/v1/locations"; | |||
string uri = builder.ToString(); | |||
var result = await _requestProvider.PostAsync(uri, newLocReq); | |||
} | |||
} | |||
} |
@ -0,0 +1,52 @@ | |||
using System; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Metadata; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using IdentityServer4.EntityFramework.DbContexts; | |||
namespace Identity.API.Migrations | |||
{ | |||
[DbContext(typeof(PersistedGrantDbContext))] | |||
[Migration("20170604151240_Init-persisted-grant")] | |||
partial class Initpersistedgrant | |||
{ | |||
protected override void BuildTargetModel(ModelBuilder modelBuilder) | |||
{ | |||
modelBuilder | |||
.HasAnnotation("ProductVersion", "1.1.2") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => | |||
{ | |||
b.Property<string>("Key") | |||
.HasMaxLength(200); | |||
b.Property<string>("ClientId") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<DateTime>("CreationTime"); | |||
b.Property<string>("Data") | |||
.IsRequired() | |||
.HasMaxLength(50000); | |||
b.Property<DateTime?>("Expiration"); | |||
b.Property<string>("SubjectId") | |||
.HasMaxLength(200); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(50); | |||
b.HasKey("Key"); | |||
b.HasIndex("SubjectId", "ClientId", "Type"); | |||
b.ToTable("PersistedGrants"); | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,40 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
namespace Identity.API.Migrations | |||
{ | |||
public partial class Initpersistedgrant : Migration | |||
{ | |||
protected override void Up(MigrationBuilder migrationBuilder) | |||
{ | |||
migrationBuilder.CreateTable( | |||
name: "PersistedGrants", | |||
columns: table => new | |||
{ | |||
Key = table.Column<string>(maxLength: 200, nullable: false), | |||
ClientId = table.Column<string>(maxLength: 200, nullable: false), | |||
CreationTime = table.Column<DateTime>(nullable: false), | |||
Data = table.Column<string>(maxLength: 50000, nullable: false), | |||
Expiration = table.Column<DateTime>(nullable: true), | |||
SubjectId = table.Column<string>(maxLength: 200, nullable: true), | |||
Type = table.Column<string>(maxLength: 50, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_PersistedGrants", x => x.Key); | |||
}); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_PersistedGrants_SubjectId_ClientId_Type", | |||
table: "PersistedGrants", | |||
columns: new[] { "SubjectId", "ClientId", "Type" }); | |||
} | |||
protected override void Down(MigrationBuilder migrationBuilder) | |||
{ | |||
migrationBuilder.DropTable( | |||
name: "PersistedGrants"); | |||
} | |||
} | |||
} |
@ -0,0 +1,539 @@ | |||
using System; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Metadata; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using IdentityServer4.EntityFramework.DbContexts; | |||
namespace Identity.API.Migrations.ConfigurationDb | |||
{ | |||
[DbContext(typeof(ConfigurationDbContext))] | |||
[Migration("20170604151338_Init-configuration")] | |||
partial class Initconfiguration | |||
{ | |||
protected override void BuildTargetModel(ModelBuilder modelBuilder) | |||
{ | |||
modelBuilder | |||
.HasAnnotation("ProductVersion", "1.1.2") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(1000); | |||
b.Property<string>("DisplayName") | |||
.HasMaxLength(200); | |||
b.Property<bool>("Enabled"); | |||
b.Property<string>("Name") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("Name") | |||
.IsUnique(); | |||
b.ToTable("ApiResources"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ApiResourceId") | |||
.IsRequired(); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("ApiResourceId"); | |||
b.ToTable("ApiClaims"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ApiResourceId") | |||
.IsRequired(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(1000); | |||
b.Property<string>("DisplayName") | |||
.HasMaxLength(200); | |||
b.Property<bool>("Emphasize"); | |||
b.Property<string>("Name") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<bool>("Required"); | |||
b.Property<bool>("ShowInDiscoveryDocument"); | |||
b.HasKey("Id"); | |||
b.HasIndex("ApiResourceId"); | |||
b.HasIndex("Name") | |||
.IsUnique(); | |||
b.ToTable("ApiScopes"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ApiScopeId") | |||
.IsRequired(); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("ApiScopeId"); | |||
b.ToTable("ApiScopeClaims"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ApiResourceId") | |||
.IsRequired(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(1000); | |||
b.Property<DateTime?>("Expiration"); | |||
b.Property<string>("Type") | |||
.HasMaxLength(250); | |||
b.Property<string>("Value") | |||
.HasMaxLength(2000); | |||
b.HasKey("Id"); | |||
b.HasIndex("ApiResourceId"); | |||
b.ToTable("ApiSecrets"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int>("AbsoluteRefreshTokenLifetime"); | |||
b.Property<int>("AccessTokenLifetime"); | |||
b.Property<int>("AccessTokenType"); | |||
b.Property<bool>("AllowAccessTokensViaBrowser"); | |||
b.Property<bool>("AllowOfflineAccess"); | |||
b.Property<bool>("AllowPlainTextPkce"); | |||
b.Property<bool>("AllowRememberConsent"); | |||
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken"); | |||
b.Property<bool>("AlwaysSendClientClaims"); | |||
b.Property<int>("AuthorizationCodeLifetime"); | |||
b.Property<string>("ClientId") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<string>("ClientName") | |||
.HasMaxLength(200); | |||
b.Property<string>("ClientUri") | |||
.HasMaxLength(2000); | |||
b.Property<bool>("EnableLocalLogin"); | |||
b.Property<bool>("Enabled"); | |||
b.Property<int>("IdentityTokenLifetime"); | |||
b.Property<bool>("IncludeJwtId"); | |||
b.Property<string>("LogoUri"); | |||
b.Property<bool>("LogoutSessionRequired"); | |||
b.Property<string>("LogoutUri"); | |||
b.Property<bool>("PrefixClientClaims"); | |||
b.Property<string>("ProtocolType") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<int>("RefreshTokenExpiration"); | |||
b.Property<int>("RefreshTokenUsage"); | |||
b.Property<bool>("RequireClientSecret"); | |||
b.Property<bool>("RequireConsent"); | |||
b.Property<bool>("RequirePkce"); | |||
b.Property<int>("SlidingRefreshTokenLifetime"); | |||
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh"); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId") | |||
.IsUnique(); | |||
b.ToTable("Clients"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(250); | |||
b.Property<string>("Value") | |||
.IsRequired() | |||
.HasMaxLength(250); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientClaims"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Origin") | |||
.IsRequired() | |||
.HasMaxLength(150); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientCorsOrigins"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("GrantType") | |||
.IsRequired() | |||
.HasMaxLength(250); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientGrantTypes"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Provider") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientIdPRestrictions"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("PostLogoutRedirectUri") | |||
.IsRequired() | |||
.HasMaxLength(2000); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientPostLogoutRedirectUris"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("RedirectUri") | |||
.IsRequired() | |||
.HasMaxLength(2000); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientRedirectUris"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Scope") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientScopes"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(2000); | |||
b.Property<DateTime?>("Expiration"); | |||
b.Property<string>("Type") | |||
.HasMaxLength(250); | |||
b.Property<string>("Value") | |||
.IsRequired() | |||
.HasMaxLength(2000); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientSecrets"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("IdentityResourceId") | |||
.IsRequired(); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("IdentityResourceId"); | |||
b.ToTable("IdentityClaims"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(1000); | |||
b.Property<string>("DisplayName") | |||
.HasMaxLength(200); | |||
b.Property<bool>("Emphasize"); | |||
b.Property<bool>("Enabled"); | |||
b.Property<string>("Name") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<bool>("Required"); | |||
b.Property<bool>("ShowInDiscoveryDocument"); | |||
b.HasKey("Id"); | |||
b.HasIndex("Name") | |||
.IsUnique(); | |||
b.ToTable("IdentityResources"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||
.WithMany("UserClaims") | |||
.HasForeignKey("ApiResourceId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||
.WithMany("Scopes") | |||
.HasForeignKey("ApiResourceId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope") | |||
.WithMany("UserClaims") | |||
.HasForeignKey("ApiScopeId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||
.WithMany("Secrets") | |||
.HasForeignKey("ApiResourceId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("Claims") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("AllowedCorsOrigins") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("AllowedGrantTypes") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("IdentityProviderRestrictions") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("PostLogoutRedirectUris") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("RedirectUris") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("AllowedScopes") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("ClientSecrets") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") | |||
.WithMany("UserClaims") | |||
.HasForeignKey("IdentityResourceId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,501 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using Microsoft.EntityFrameworkCore.Metadata; | |||
namespace Identity.API.Migrations.ConfigurationDb | |||
{ | |||
public partial class Initconfiguration : Migration | |||
{ | |||
protected override void Up(MigrationBuilder migrationBuilder) | |||
{ | |||
migrationBuilder.CreateTable( | |||
name: "ApiResources", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
Description = table.Column<string>(maxLength: 1000, nullable: true), | |||
DisplayName = table.Column<string>(maxLength: 200, nullable: true), | |||
Enabled = table.Column<bool>(nullable: false), | |||
Name = table.Column<string>(maxLength: 200, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ApiResources", x => x.Id); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "Clients", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
AbsoluteRefreshTokenLifetime = table.Column<int>(nullable: false), | |||
AccessTokenLifetime = table.Column<int>(nullable: false), | |||
AccessTokenType = table.Column<int>(nullable: false), | |||
AllowAccessTokensViaBrowser = table.Column<bool>(nullable: false), | |||
AllowOfflineAccess = table.Column<bool>(nullable: false), | |||
AllowPlainTextPkce = table.Column<bool>(nullable: false), | |||
AllowRememberConsent = table.Column<bool>(nullable: false), | |||
AlwaysIncludeUserClaimsInIdToken = table.Column<bool>(nullable: false), | |||
AlwaysSendClientClaims = table.Column<bool>(nullable: false), | |||
AuthorizationCodeLifetime = table.Column<int>(nullable: false), | |||
ClientId = table.Column<string>(maxLength: 200, nullable: false), | |||
ClientName = table.Column<string>(maxLength: 200, nullable: true), | |||
ClientUri = table.Column<string>(maxLength: 2000, nullable: true), | |||
EnableLocalLogin = table.Column<bool>(nullable: false), | |||
Enabled = table.Column<bool>(nullable: false), | |||
IdentityTokenLifetime = table.Column<int>(nullable: false), | |||
IncludeJwtId = table.Column<bool>(nullable: false), | |||
LogoUri = table.Column<string>(nullable: true), | |||
LogoutSessionRequired = table.Column<bool>(nullable: false), | |||
LogoutUri = table.Column<string>(nullable: true), | |||
PrefixClientClaims = table.Column<bool>(nullable: false), | |||
ProtocolType = table.Column<string>(maxLength: 200, nullable: false), | |||
RefreshTokenExpiration = table.Column<int>(nullable: false), | |||
RefreshTokenUsage = table.Column<int>(nullable: false), | |||
RequireClientSecret = table.Column<bool>(nullable: false), | |||
RequireConsent = table.Column<bool>(nullable: false), | |||
RequirePkce = table.Column<bool>(nullable: false), | |||
SlidingRefreshTokenLifetime = table.Column<int>(nullable: false), | |||
UpdateAccessTokenClaimsOnRefresh = table.Column<bool>(nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_Clients", x => x.Id); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "IdentityResources", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
Description = table.Column<string>(maxLength: 1000, nullable: true), | |||
DisplayName = table.Column<string>(maxLength: 200, nullable: true), | |||
Emphasize = table.Column<bool>(nullable: false), | |||
Enabled = table.Column<bool>(nullable: false), | |||
Name = table.Column<string>(maxLength: 200, nullable: false), | |||
Required = table.Column<bool>(nullable: false), | |||
ShowInDiscoveryDocument = table.Column<bool>(nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_IdentityResources", x => x.Id); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ApiClaims", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ApiResourceId = table.Column<int>(nullable: false), | |||
Type = table.Column<string>(maxLength: 200, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ApiClaims", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ApiClaims_ApiResources_ApiResourceId", | |||
column: x => x.ApiResourceId, | |||
principalTable: "ApiResources", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ApiScopes", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ApiResourceId = table.Column<int>(nullable: false), | |||
Description = table.Column<string>(maxLength: 1000, nullable: true), | |||
DisplayName = table.Column<string>(maxLength: 200, nullable: true), | |||
Emphasize = table.Column<bool>(nullable: false), | |||
Name = table.Column<string>(maxLength: 200, nullable: false), | |||
Required = table.Column<bool>(nullable: false), | |||
ShowInDiscoveryDocument = table.Column<bool>(nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ApiScopes", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ApiScopes_ApiResources_ApiResourceId", | |||
column: x => x.ApiResourceId, | |||
principalTable: "ApiResources", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ApiSecrets", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ApiResourceId = table.Column<int>(nullable: false), | |||
Description = table.Column<string>(maxLength: 1000, nullable: true), | |||
Expiration = table.Column<DateTime>(nullable: true), | |||
Type = table.Column<string>(maxLength: 250, nullable: true), | |||
Value = table.Column<string>(maxLength: 2000, nullable: true) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ApiSecrets", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ApiSecrets_ApiResources_ApiResourceId", | |||
column: x => x.ApiResourceId, | |||
principalTable: "ApiResources", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ClientClaims", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ClientId = table.Column<int>(nullable: false), | |||
Type = table.Column<string>(maxLength: 250, nullable: false), | |||
Value = table.Column<string>(maxLength: 250, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ClientClaims", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ClientClaims_Clients_ClientId", | |||
column: x => x.ClientId, | |||
principalTable: "Clients", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ClientCorsOrigins", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ClientId = table.Column<int>(nullable: false), | |||
Origin = table.Column<string>(maxLength: 150, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ClientCorsOrigins", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ClientCorsOrigins_Clients_ClientId", | |||
column: x => x.ClientId, | |||
principalTable: "Clients", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ClientGrantTypes", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ClientId = table.Column<int>(nullable: false), | |||
GrantType = table.Column<string>(maxLength: 250, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ClientGrantTypes", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ClientGrantTypes_Clients_ClientId", | |||
column: x => x.ClientId, | |||
principalTable: "Clients", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ClientIdPRestrictions", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ClientId = table.Column<int>(nullable: false), | |||
Provider = table.Column<string>(maxLength: 200, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ClientIdPRestrictions", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ClientIdPRestrictions_Clients_ClientId", | |||
column: x => x.ClientId, | |||
principalTable: "Clients", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ClientPostLogoutRedirectUris", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ClientId = table.Column<int>(nullable: false), | |||
PostLogoutRedirectUri = table.Column<string>(maxLength: 2000, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ClientPostLogoutRedirectUris", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ClientPostLogoutRedirectUris_Clients_ClientId", | |||
column: x => x.ClientId, | |||
principalTable: "Clients", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ClientRedirectUris", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ClientId = table.Column<int>(nullable: false), | |||
RedirectUri = table.Column<string>(maxLength: 2000, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ClientRedirectUris", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ClientRedirectUris_Clients_ClientId", | |||
column: x => x.ClientId, | |||
principalTable: "Clients", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ClientScopes", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ClientId = table.Column<int>(nullable: false), | |||
Scope = table.Column<string>(maxLength: 200, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ClientScopes", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ClientScopes_Clients_ClientId", | |||
column: x => x.ClientId, | |||
principalTable: "Clients", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ClientSecrets", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ClientId = table.Column<int>(nullable: false), | |||
Description = table.Column<string>(maxLength: 2000, nullable: true), | |||
Expiration = table.Column<DateTime>(nullable: true), | |||
Type = table.Column<string>(maxLength: 250, nullable: true), | |||
Value = table.Column<string>(maxLength: 2000, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ClientSecrets", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ClientSecrets_Clients_ClientId", | |||
column: x => x.ClientId, | |||
principalTable: "Clients", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "IdentityClaims", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
IdentityResourceId = table.Column<int>(nullable: false), | |||
Type = table.Column<string>(maxLength: 200, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_IdentityClaims", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_IdentityClaims_IdentityResources_IdentityResourceId", | |||
column: x => x.IdentityResourceId, | |||
principalTable: "IdentityResources", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "ApiScopeClaims", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false) | |||
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), | |||
ApiScopeId = table.Column<int>(nullable: false), | |||
Type = table.Column<string>(maxLength: 200, nullable: false) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_ApiScopeClaims", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_ApiScopeClaims_ApiScopes_ApiScopeId", | |||
column: x => x.ApiScopeId, | |||
principalTable: "ApiScopes", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ApiResources_Name", | |||
table: "ApiResources", | |||
column: "Name", | |||
unique: true); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ApiClaims_ApiResourceId", | |||
table: "ApiClaims", | |||
column: "ApiResourceId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ApiScopes_ApiResourceId", | |||
table: "ApiScopes", | |||
column: "ApiResourceId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ApiScopes_Name", | |||
table: "ApiScopes", | |||
column: "Name", | |||
unique: true); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ApiScopeClaims_ApiScopeId", | |||
table: "ApiScopeClaims", | |||
column: "ApiScopeId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ApiSecrets_ApiResourceId", | |||
table: "ApiSecrets", | |||
column: "ApiResourceId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_Clients_ClientId", | |||
table: "Clients", | |||
column: "ClientId", | |||
unique: true); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ClientClaims_ClientId", | |||
table: "ClientClaims", | |||
column: "ClientId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ClientCorsOrigins_ClientId", | |||
table: "ClientCorsOrigins", | |||
column: "ClientId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ClientGrantTypes_ClientId", | |||
table: "ClientGrantTypes", | |||
column: "ClientId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ClientIdPRestrictions_ClientId", | |||
table: "ClientIdPRestrictions", | |||
column: "ClientId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ClientPostLogoutRedirectUris_ClientId", | |||
table: "ClientPostLogoutRedirectUris", | |||
column: "ClientId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ClientRedirectUris_ClientId", | |||
table: "ClientRedirectUris", | |||
column: "ClientId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ClientScopes_ClientId", | |||
table: "ClientScopes", | |||
column: "ClientId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_ClientSecrets_ClientId", | |||
table: "ClientSecrets", | |||
column: "ClientId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_IdentityClaims_IdentityResourceId", | |||
table: "IdentityClaims", | |||
column: "IdentityResourceId"); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_IdentityResources_Name", | |||
table: "IdentityResources", | |||
column: "Name", | |||
unique: true); | |||
} | |||
protected override void Down(MigrationBuilder migrationBuilder) | |||
{ | |||
migrationBuilder.DropTable( | |||
name: "ApiClaims"); | |||
migrationBuilder.DropTable( | |||
name: "ApiScopeClaims"); | |||
migrationBuilder.DropTable( | |||
name: "ApiSecrets"); | |||
migrationBuilder.DropTable( | |||
name: "ClientClaims"); | |||
migrationBuilder.DropTable( | |||
name: "ClientCorsOrigins"); | |||
migrationBuilder.DropTable( | |||
name: "ClientGrantTypes"); | |||
migrationBuilder.DropTable( | |||
name: "ClientIdPRestrictions"); | |||
migrationBuilder.DropTable( | |||
name: "ClientPostLogoutRedirectUris"); | |||
migrationBuilder.DropTable( | |||
name: "ClientRedirectUris"); | |||
migrationBuilder.DropTable( | |||
name: "ClientScopes"); | |||
migrationBuilder.DropTable( | |||
name: "ClientSecrets"); | |||
migrationBuilder.DropTable( | |||
name: "IdentityClaims"); | |||
migrationBuilder.DropTable( | |||
name: "ApiScopes"); | |||
migrationBuilder.DropTable( | |||
name: "Clients"); | |||
migrationBuilder.DropTable( | |||
name: "IdentityResources"); | |||
migrationBuilder.DropTable( | |||
name: "ApiResources"); | |||
} | |||
} | |||
} |
@ -0,0 +1,538 @@ | |||
using System; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Metadata; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using IdentityServer4.EntityFramework.DbContexts; | |||
namespace Identity.API.Migrations.ConfigurationDb | |||
{ | |||
[DbContext(typeof(ConfigurationDbContext))] | |||
partial class ConfigurationDbContextModelSnapshot : ModelSnapshot | |||
{ | |||
protected override void BuildModel(ModelBuilder modelBuilder) | |||
{ | |||
modelBuilder | |||
.HasAnnotation("ProductVersion", "1.1.2") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(1000); | |||
b.Property<string>("DisplayName") | |||
.HasMaxLength(200); | |||
b.Property<bool>("Enabled"); | |||
b.Property<string>("Name") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("Name") | |||
.IsUnique(); | |||
b.ToTable("ApiResources"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ApiResourceId") | |||
.IsRequired(); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("ApiResourceId"); | |||
b.ToTable("ApiClaims"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ApiResourceId") | |||
.IsRequired(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(1000); | |||
b.Property<string>("DisplayName") | |||
.HasMaxLength(200); | |||
b.Property<bool>("Emphasize"); | |||
b.Property<string>("Name") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<bool>("Required"); | |||
b.Property<bool>("ShowInDiscoveryDocument"); | |||
b.HasKey("Id"); | |||
b.HasIndex("ApiResourceId"); | |||
b.HasIndex("Name") | |||
.IsUnique(); | |||
b.ToTable("ApiScopes"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ApiScopeId") | |||
.IsRequired(); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("ApiScopeId"); | |||
b.ToTable("ApiScopeClaims"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ApiResourceId") | |||
.IsRequired(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(1000); | |||
b.Property<DateTime?>("Expiration"); | |||
b.Property<string>("Type") | |||
.HasMaxLength(250); | |||
b.Property<string>("Value") | |||
.HasMaxLength(2000); | |||
b.HasKey("Id"); | |||
b.HasIndex("ApiResourceId"); | |||
b.ToTable("ApiSecrets"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int>("AbsoluteRefreshTokenLifetime"); | |||
b.Property<int>("AccessTokenLifetime"); | |||
b.Property<int>("AccessTokenType"); | |||
b.Property<bool>("AllowAccessTokensViaBrowser"); | |||
b.Property<bool>("AllowOfflineAccess"); | |||
b.Property<bool>("AllowPlainTextPkce"); | |||
b.Property<bool>("AllowRememberConsent"); | |||
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken"); | |||
b.Property<bool>("AlwaysSendClientClaims"); | |||
b.Property<int>("AuthorizationCodeLifetime"); | |||
b.Property<string>("ClientId") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<string>("ClientName") | |||
.HasMaxLength(200); | |||
b.Property<string>("ClientUri") | |||
.HasMaxLength(2000); | |||
b.Property<bool>("EnableLocalLogin"); | |||
b.Property<bool>("Enabled"); | |||
b.Property<int>("IdentityTokenLifetime"); | |||
b.Property<bool>("IncludeJwtId"); | |||
b.Property<string>("LogoUri"); | |||
b.Property<bool>("LogoutSessionRequired"); | |||
b.Property<string>("LogoutUri"); | |||
b.Property<bool>("PrefixClientClaims"); | |||
b.Property<string>("ProtocolType") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<int>("RefreshTokenExpiration"); | |||
b.Property<int>("RefreshTokenUsage"); | |||
b.Property<bool>("RequireClientSecret"); | |||
b.Property<bool>("RequireConsent"); | |||
b.Property<bool>("RequirePkce"); | |||
b.Property<int>("SlidingRefreshTokenLifetime"); | |||
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh"); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId") | |||
.IsUnique(); | |||
b.ToTable("Clients"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(250); | |||
b.Property<string>("Value") | |||
.IsRequired() | |||
.HasMaxLength(250); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientClaims"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Origin") | |||
.IsRequired() | |||
.HasMaxLength(150); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientCorsOrigins"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("GrantType") | |||
.IsRequired() | |||
.HasMaxLength(250); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientGrantTypes"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Provider") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientIdPRestrictions"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("PostLogoutRedirectUri") | |||
.IsRequired() | |||
.HasMaxLength(2000); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientPostLogoutRedirectUris"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("RedirectUri") | |||
.IsRequired() | |||
.HasMaxLength(2000); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientRedirectUris"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Scope") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientScopes"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("ClientId") | |||
.IsRequired(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(2000); | |||
b.Property<DateTime?>("Expiration"); | |||
b.Property<string>("Type") | |||
.HasMaxLength(250); | |||
b.Property<string>("Value") | |||
.IsRequired() | |||
.HasMaxLength(2000); | |||
b.HasKey("Id"); | |||
b.HasIndex("ClientId"); | |||
b.ToTable("ClientSecrets"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<int?>("IdentityResourceId") | |||
.IsRequired(); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.HasKey("Id"); | |||
b.HasIndex("IdentityResourceId"); | |||
b.ToTable("IdentityClaims"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd(); | |||
b.Property<string>("Description") | |||
.HasMaxLength(1000); | |||
b.Property<string>("DisplayName") | |||
.HasMaxLength(200); | |||
b.Property<bool>("Emphasize"); | |||
b.Property<bool>("Enabled"); | |||
b.Property<string>("Name") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<bool>("Required"); | |||
b.Property<bool>("ShowInDiscoveryDocument"); | |||
b.HasKey("Id"); | |||
b.HasIndex("Name") | |||
.IsUnique(); | |||
b.ToTable("IdentityResources"); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||
.WithMany("UserClaims") | |||
.HasForeignKey("ApiResourceId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||
.WithMany("Scopes") | |||
.HasForeignKey("ApiResourceId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope") | |||
.WithMany("UserClaims") | |||
.HasForeignKey("ApiScopeId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource") | |||
.WithMany("Secrets") | |||
.HasForeignKey("ApiResourceId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("Claims") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("AllowedCorsOrigins") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("AllowedGrantTypes") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("IdentityProviderRestrictions") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("PostLogoutRedirectUris") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("RedirectUris") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("AllowedScopes") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client") | |||
.WithMany("ClientSecrets") | |||
.HasForeignKey("ClientId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b => | |||
{ | |||
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource") | |||
.WithMany("UserClaims") | |||
.HasForeignKey("IdentityResourceId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,51 @@ | |||
using System; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Metadata; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using IdentityServer4.EntityFramework.DbContexts; | |||
namespace Identity.API.Migrations | |||
{ | |||
[DbContext(typeof(PersistedGrantDbContext))] | |||
partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot | |||
{ | |||
protected override void BuildModel(ModelBuilder modelBuilder) | |||
{ | |||
modelBuilder | |||
.HasAnnotation("ProductVersion", "1.1.2") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => | |||
{ | |||
b.Property<string>("Key") | |||
.HasMaxLength(200); | |||
b.Property<string>("ClientId") | |||
.IsRequired() | |||
.HasMaxLength(200); | |||
b.Property<DateTime>("CreationTime"); | |||
b.Property<string>("Data") | |||
.IsRequired() | |||
.HasMaxLength(50000); | |||
b.Property<DateTime?>("Expiration"); | |||
b.Property<string>("SubjectId") | |||
.HasMaxLength(200); | |||
b.Property<string>("Type") | |||
.IsRequired() | |||
.HasMaxLength(50); | |||
b.HasKey("Key"); | |||
b.HasIndex("SubjectId", "ClientId", "Type"); | |||
b.ToTable("PersistedGrants"); | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,3 @@ | |||
* | |||
!obj/Docker/publish/* | |||
!obj/Docker/empty/ |
@ -0,0 +1,19 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Mvc; | |||
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Controllers | |||
{ | |||
public class HomeController : Controller | |||
{ | |||
// GET: /<controller>/ | |||
public IActionResult Index() | |||
{ | |||
return new RedirectResult("~/swagger"); | |||
} | |||
} | |||
} |
@ -0,0 +1,62 @@ | |||
using Microsoft.AspNetCore.Authorization; | |||
using Microsoft.AspNetCore.Mvc; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel; | |||
using System; | |||
using System.Threading.Tasks; | |||
namespace Locations.API.Controllers | |||
{ | |||
[Route("api/v1/[controller]")] | |||
[Authorize] | |||
public class LocationsController : ControllerBase | |||
{ | |||
private readonly ILocationsService _locationsService; | |||
private readonly IIdentityService _identityService; | |||
public LocationsController(ILocationsService locationsService, IIdentityService identityService) | |||
{ | |||
_locationsService = locationsService ?? throw new ArgumentNullException(nameof(locationsService)); | |||
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); | |||
} | |||
//GET api/v1/[controller]/user/1 | |||
[Route("user/{userId:int}")] | |||
[HttpGet] | |||
public async Task<IActionResult> GetUserLocation(int userId) | |||
{ | |||
var userLocation = await _locationsService.GetUserLocation(userId); | |||
return Ok(userLocation); | |||
} | |||
//GET api/v1/[controller]/ | |||
[Route("")] | |||
[HttpGet] | |||
public async Task<IActionResult> GetAllLocations() | |||
{ | |||
var locations = await _locationsService.GetAllLocation(); | |||
return Ok(locations); | |||
} | |||
//GET api/v1/[controller]/1 | |||
[Route("{locationId}")] | |||
[HttpGet] | |||
public async Task<IActionResult> GetLocation(string locationId) | |||
{ | |||
var location = await _locationsService.GetLocation(locationId); | |||
return Ok(location); | |||
} | |||
//POST api/v1/[controller]/ | |||
[Route("")] | |||
[HttpPost] | |||
public async Task<IActionResult> CreateOrUpdateUserLocation([FromBody]LocationRequest newLocReq) | |||
{ | |||
var userId = _identityService.GetUserIdentity(); | |||
var result = await _locationsService.AddOrUpdateUserLocation(userId, newLocReq); | |||
return result ? | |||
(IActionResult)Ok() : | |||
(IActionResult)BadRequest(); | |||
} | |||
} | |||
} |
@ -0,0 +1,6 @@ | |||
FROM microsoft/aspnetcore:1.1 | |||
ARG source | |||
WORKDIR /app | |||
EXPOSE 80 | |||
COPY ${source:-obj/Docker/publish} . | |||
ENTRYPOINT ["dotnet", "Locations.API.dll"] |
@ -0,0 +1,14 @@ | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.AspNetCore.Mvc; | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.ActionResults | |||
{ | |||
public class InternalServerErrorObjectResult : ObjectResult | |||
{ | |||
public InternalServerErrorObjectResult(object error) | |||
: base(error) | |||
{ | |||
StatusCode = StatusCodes.Status500InternalServerError; | |||
} | |||
} | |||
} |
@ -0,0 +1,21 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Exceptions | |||
{ | |||
using System; | |||
/// <summary> | |||
/// Exception type for app exceptions | |||
/// </summary> | |||
public class LocationDomainException : Exception | |||
{ | |||
public LocationDomainException() | |||
{ } | |||
public LocationDomainException(string message) | |||
: base(message) | |||
{ } | |||
public LocationDomainException(string message, Exception innerException) | |||
: base(message, innerException) | |||
{ } | |||
} | |||
} |
@ -0,0 +1,67 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Filters | |||
{ | |||
using AspNetCore.Mvc; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.AspNetCore.Mvc.Filters; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.ActionResults; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Exceptions; | |||
using Microsoft.Extensions.Logging; | |||
using System.Net; | |||
public class HttpGlobalExceptionFilter : IExceptionFilter | |||
{ | |||
private readonly IHostingEnvironment env; | |||
private readonly ILogger<HttpGlobalExceptionFilter> logger; | |||
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger) | |||
{ | |||
this.env = env; | |||
this.logger = logger; | |||
} | |||
public void OnException(ExceptionContext context) | |||
{ | |||
logger.LogError(new EventId(context.Exception.HResult), | |||
context.Exception, | |||
context.Exception.Message); | |||
if (context.Exception.GetType() == typeof(LocationDomainException)) | |||
{ | |||
var json = new JsonErrorResponse | |||
{ | |||
Messages = new[] { context.Exception.Message } | |||
}; | |||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 | |||
//It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information | |||
context.Result = new BadRequestObjectResult(json); | |||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; | |||
} | |||
else | |||
{ | |||
var json = new JsonErrorResponse | |||
{ | |||
Messages = new[] { "An error occur.Try it again." } | |||
}; | |||
if (env.IsDevelopment()) | |||
{ | |||
json.DeveloperMessage = context.Exception; | |||
} | |||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 | |||
// It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information | |||
context.Result = new InternalServerErrorObjectResult(json); | |||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; | |||
} | |||
context.ExceptionHandled = true; | |||
} | |||
private class JsonErrorResponse | |||
{ | |||
public string[] Messages { get; set; } | |||
public object DeveloperMessage { get; set; } | |||
} | |||
} | |||
} |
@ -0,0 +1,34 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure | |||
{ | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Model; | |||
using Microsoft.Extensions.Options; | |||
using MongoDB.Driver; | |||
public class LocationsContext | |||
{ | |||
private readonly IMongoDatabase _database = null; | |||
public LocationsContext(IOptions<LocationSettings> settings) | |||
{ | |||
var client = new MongoClient(settings.Value.ConnectionString); | |||
if (client != null) | |||
_database = client.GetDatabase(settings.Value.Database); | |||
} | |||
public IMongoCollection<UserLocation> UserLocation | |||
{ | |||
get | |||
{ | |||
return _database.GetCollection<UserLocation>("UserLocation"); | |||
} | |||
} | |||
public IMongoCollection<Locations> Locations | |||
{ | |||
get | |||
{ | |||
return _database.GetCollection<Locations>("Locations"); | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,145 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure | |||
{ | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Model; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
using MongoDB.Driver; | |||
using MongoDB.Driver.GeoJsonObjectModel; | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
public class LocationsContextSeed | |||
{ | |||
private static LocationsContext ctx; | |||
public static async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory) | |||
{ | |||
var config = applicationBuilder | |||
.ApplicationServices.GetRequiredService<IOptions<LocationSettings>>(); | |||
ctx = new LocationsContext(config); | |||
if (!ctx.Locations.Database.GetCollection<Locations>(nameof(Locations)).AsQueryable().Any()) | |||
{ | |||
await SetIndexes(); | |||
await SetUSLocations(); | |||
} | |||
} | |||
static async Task SetUSLocations() | |||
{ | |||
var us = new Locations() | |||
{ | |||
Code = "US", | |||
Description = "United States" | |||
}; | |||
us.SetLocation(-101.357386, 41.650455); | |||
us.SetArea(GetUSPoligon()); | |||
await ctx.Locations.InsertOneAsync(us); | |||
await SetWashingtonLocations(us.Id); | |||
} | |||
static async Task SetWashingtonLocations(string parentId) | |||
{ | |||
var wht = new Locations() | |||
{ | |||
Parent_Id = parentId, | |||
Code = "WHT", | |||
Description = "Washington" | |||
}; | |||
wht.SetLocation(-119.542781, 47.223652); | |||
wht.SetArea(GetWashingtonPoligon()); | |||
await ctx.Locations.InsertOneAsync(wht); | |||
await SetSeattleLocations(wht.Id); | |||
await SetRedmondLocations(wht.Id); | |||
} | |||
static async Task SetSeattleLocations(string parentId) | |||
{ | |||
var stl = new Locations() | |||
{ | |||
Parent_Id = parentId, | |||
Code = "SEAT", | |||
Description = "Seattle" | |||
}; | |||
stl.SetArea(GetSeattlePoligon()); | |||
stl.SetLocation(-122.330747, 47.603111); | |||
await ctx.Locations.InsertOneAsync(stl); | |||
} | |||
static async Task SetRedmondLocations(string parentId) | |||
{ | |||
var rdm = new Locations() | |||
{ | |||
Parent_Id = parentId, | |||
Code = "REDM", | |||
Description = "Redmond" | |||
}; | |||
rdm.SetLocation(-122.122887, 47.674961); | |||
rdm.SetArea(GetRedmondPoligon()); | |||
await ctx.Locations.InsertOneAsync(rdm); | |||
} | |||
static async Task SetIndexes() | |||
{ | |||
// Set location indexes | |||
var builder = Builders<Locations>.IndexKeys; | |||
var keys = builder.Geo2DSphere(prop => prop.Location); | |||
await ctx.Locations.Indexes.CreateOneAsync(keys); | |||
} | |||
static List<GeoJson2DGeographicCoordinates> GetUSPoligon() | |||
{ | |||
return new List<GeoJson2DGeographicCoordinates>() | |||
{ | |||
new GeoJson2DGeographicCoordinates(-62.88205, 48.7985), | |||
new GeoJson2DGeographicCoordinates(-129.3132, 48.76513), | |||
new GeoJson2DGeographicCoordinates(-120.9496, 30.12256), | |||
new GeoJson2DGeographicCoordinates(-111.3944, 30.87114), | |||
new GeoJson2DGeographicCoordinates(-78.11975, 24.24979), | |||
new GeoJson2DGeographicCoordinates(-62.88205, 48.7985) | |||
}; | |||
} | |||
static List<GeoJson2DGeographicCoordinates> GetSeattlePoligon() | |||
{ | |||
return new List<GeoJson2DGeographicCoordinates>() | |||
{ | |||
new GeoJson2DGeographicCoordinates(-122.36238,47.82929), | |||
new GeoJson2DGeographicCoordinates(-122.42091,47.6337), | |||
new GeoJson2DGeographicCoordinates(-122.37371,47.45224), | |||
new GeoJson2DGeographicCoordinates(-122.20788,47.50259), | |||
new GeoJson2DGeographicCoordinates(-122.26934,47.73644), | |||
new GeoJson2DGeographicCoordinates(-122.36238,47.82929) | |||
}; | |||
} | |||
static List<GeoJson2DGeographicCoordinates> GetRedmondPoligon() | |||
{ | |||
return new List<GeoJson2DGeographicCoordinates>() | |||
{ | |||
new GeoJson2DGeographicCoordinates(-122.15432, 47.73148), | |||
new GeoJson2DGeographicCoordinates(-122.17673, 47.72559), | |||
new GeoJson2DGeographicCoordinates(-122.16904, 47.67851), | |||
new GeoJson2DGeographicCoordinates(-122.16136, 47.65036), | |||
new GeoJson2DGeographicCoordinates(-122.15604, 47.62746), | |||
new GeoJson2DGeographicCoordinates(-122.01562, 47.63463), | |||
new GeoJson2DGeographicCoordinates(-122.04961, 47.74244), | |||
new GeoJson2DGeographicCoordinates(-122.15432, 47.73148) | |||
}; | |||
} | |||
static List<GeoJson2DGeographicCoordinates> GetWashingtonPoligon() | |||
{ | |||
return new List<GeoJson2DGeographicCoordinates>() | |||
{ | |||
new GeoJson2DGeographicCoordinates(-124.68633, 48.8943), | |||
new GeoJson2DGeographicCoordinates(-124.32962, 45.66613), | |||
new GeoJson2DGeographicCoordinates(-116.73824, 45.93384), | |||
new GeoJson2DGeographicCoordinates(-116.96912, 49.04282), | |||
new GeoJson2DGeographicCoordinates(-124.68633, 48.8943) | |||
}; | |||
} | |||
} | |||
} |
@ -0,0 +1,23 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories | |||
{ | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Model; | |||
using ViewModel; | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
public interface ILocationsRepository | |||
{ | |||
Task<Locations> GetAsync(string locationId); | |||
Task<List<Locations>> GetLocationListAsync(); | |||
Task<UserLocation> GetUserLocationAsync(int userId); | |||
Task<List<Locations>> GetCurrentUserRegionsListAsync(LocationRequest currentPosition); | |||
Task AddUserLocationAsync(UserLocation location); | |||
Task UpdateUserLocationAsync(UserLocation userLocation); | |||
} | |||
} |
@ -0,0 +1,66 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories | |||
{ | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Model; | |||
using Microsoft.Extensions.Options; | |||
using MongoDB.Bson; | |||
using MongoDB.Driver; | |||
using MongoDB.Driver.GeoJsonObjectModel; | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
using ViewModel; | |||
public class LocationsRepository | |||
: ILocationsRepository | |||
{ | |||
private readonly LocationsContext _context; | |||
public LocationsRepository(IOptions<LocationSettings> settings) | |||
{ | |||
_context = new LocationsContext(settings); | |||
} | |||
public async Task<Locations> GetAsync(string locationId) | |||
{ | |||
var filter = Builders<Locations>.Filter.Eq("Id", ObjectId.Parse(locationId)); | |||
return await _context.Locations | |||
.Find(filter) | |||
.FirstOrDefaultAsync(); | |||
} | |||
public async Task<UserLocation> GetUserLocationAsync(int userId) | |||
{ | |||
var filter = Builders<UserLocation>.Filter.Eq("UserId", userId); | |||
return await _context.UserLocation | |||
.Find(filter) | |||
.FirstOrDefaultAsync(); | |||
} | |||
public async Task<List<Locations>> GetLocationListAsync() | |||
{ | |||
return await _context.Locations.Find(new BsonDocument()).ToListAsync(); | |||
} | |||
public async Task<List<Locations>> GetCurrentUserRegionsListAsync(LocationRequest currentPosition) | |||
{ | |||
var point = GeoJson.Point(GeoJson.Geographic(currentPosition.Longitude, currentPosition.Latitude)); | |||
var orderByDistanceQuery = new FilterDefinitionBuilder<Locations>().Near(x => x.Location, point); | |||
var withinAreaQuery = new FilterDefinitionBuilder<Locations>().GeoIntersects("Polygon", point); | |||
var filter = Builders<Locations>.Filter.And(orderByDistanceQuery, withinAreaQuery); | |||
return await _context.Locations.Find(filter).ToListAsync(); | |||
} | |||
public async Task AddUserLocationAsync(UserLocation location) | |||
{ | |||
await _context.UserLocation.InsertOneAsync(location); | |||
} | |||
public async Task UpdateUserLocationAsync(UserLocation userLocation) | |||
{ | |||
await _context.UserLocation.ReplaceOneAsync( | |||
doc => doc.UserId == userLocation.UserId, | |||
userLocation, | |||
new UpdateOptions { IsUpsert = true }); | |||
} | |||
} | |||
} |
@ -0,0 +1,12 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services | |||
{ | |||
public interface IIdentityService | |||
{ | |||
string GetUserIdentity(); | |||
} | |||
} |
@ -0,0 +1,18 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services | |||
{ | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Model; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel; | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
public interface ILocationsService | |||
{ | |||
Task<Locations> GetLocation(string locationId); | |||
Task<UserLocation> GetUserLocation(int id); | |||
Task<List<Locations>> GetAllLocation(); | |||
Task<bool> AddOrUpdateUserLocation(string userId, LocationRequest locRequest); | |||
} | |||
} |
@ -0,0 +1,23 @@ | |||
using Microsoft.AspNetCore.Http; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services | |||
{ | |||
public class IdentityService : IIdentityService | |||
{ | |||
private IHttpContextAccessor _context; | |||
public IdentityService(IHttpContextAccessor context) | |||
{ | |||
_context = context ?? throw new ArgumentNullException(nameof(context)); | |||
} | |||
public string GetUserIdentity() | |||
{ | |||
return _context.HttpContext.User.FindFirst("sub").Value; | |||
} | |||
} | |||
} |
@ -0,0 +1,63 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services | |||
{ | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Model; | |||
using System; | |||
using System.Threading.Tasks; | |||
using System.Linq; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Exceptions; | |||
using System.Collections.Generic; | |||
public class LocationsService : ILocationsService | |||
{ | |||
private ILocationsRepository _locationsRepository; | |||
public LocationsService(ILocationsRepository locationsRepository) | |||
{ | |||
_locationsRepository = locationsRepository ?? throw new ArgumentNullException(nameof(locationsRepository)); | |||
} | |||
public async Task<Locations> GetLocation(string locationId) | |||
{ | |||
return await _locationsRepository.GetAsync(locationId); | |||
} | |||
public async Task<UserLocation> GetUserLocation(int id) | |||
{ | |||
return await _locationsRepository.GetUserLocationAsync(id); | |||
} | |||
public async Task<List<Locations>> GetAllLocation() | |||
{ | |||
return await _locationsRepository.GetLocationListAsync(); | |||
} | |||
public async Task<bool> AddOrUpdateUserLocation(string id, LocationRequest currentPosition) | |||
{ | |||
if (!int.TryParse(id, out int userId)) | |||
{ | |||
throw new ArgumentException("Not valid userId"); | |||
} | |||
// Get the list of ordered regions the user currently is within | |||
var currentUserAreaLocationList = await _locationsRepository.GetCurrentUserRegionsListAsync(currentPosition); | |||
if(currentUserAreaLocationList is null) | |||
{ | |||
throw new LocationDomainException("User current area not found"); | |||
} | |||
// If current area found, then update user location | |||
var locationAncestors = new List<string>(); | |||
var userLocation = await _locationsRepository.GetUserLocationAsync(userId); | |||
userLocation = userLocation ?? new UserLocation(); | |||
userLocation.UserId = userId; | |||
userLocation.LocationId = currentUserAreaLocationList[0].Id; | |||
userLocation.UpdateDate = DateTime.UtcNow; | |||
await _locationsRepository.UpdateUserLocationAsync(userLocation); | |||
return true; | |||
} | |||
} | |||
} |
@ -0,0 +1,15 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API | |||
{ | |||
public class LocationSettings | |||
{ | |||
public string ExternalCatalogBaseUrl { get; set; } | |||
public string EventBusConnection { get; set; } | |||
public string ConnectionString { get; set; } | |||
public string Database { get; set; } | |||
} | |||
} |
@ -0,0 +1,41 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp1.1</TargetFramework> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
<RootNamespace>Microsoft.eShopOnContainers.Services.Locations.API</RootNamespace> | |||
<UserSecretsId>aspnet-Locations.API-20161122013619</UserSecretsId> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Folder Include="wwwroot\" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="1.2.0" /> | |||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" /> | |||
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" /> | |||
<PackageReference Include="mongocsharpdriver" Version="2.4.3" /> | |||
<PackageReference Include="MongoDB.Bson" Version="2.4.3" /> | |||
<PackageReference Include="MongoDB.Driver" Version="2.4.3" /> | |||
<PackageReference Include="MongoDB.Driver.Core" Version="2.4.3" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" /> | |||
</ItemGroup> | |||
</Project> |
@ -0,0 +1,37 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Model | |||
{ | |||
using MongoDB.Bson; | |||
using MongoDB.Driver.GeoJsonObjectModel; | |||
using System.Collections.Generic; | |||
using MongoDB.Bson.Serialization.Attributes; | |||
public class Locations | |||
{ | |||
[BsonRepresentation(BsonType.ObjectId)] | |||
public string Id { get; set; } | |||
public string Code { get; set; } | |||
[BsonRepresentation(BsonType.ObjectId)] | |||
public string Parent_Id { get; set; } | |||
public string Description { get; set; } | |||
public double Latitude { get; set; } | |||
public double Longitude { get; set; } | |||
public GeoJsonPoint<GeoJson2DGeographicCoordinates> Location { get; private set; } | |||
public GeoJsonPolygon<GeoJson2DGeographicCoordinates> Polygon { get; private set; } | |||
public void SetLocation(double lon, double lat) => SetPosition(lon, lat); | |||
public void SetArea(List<GeoJson2DGeographicCoordinates> coordinatesList) => SetPolygon(coordinatesList); | |||
private void SetPosition(double lon, double lat) | |||
{ | |||
Latitude = lat; | |||
Longitude = lon; | |||
Location = new GeoJsonPoint<GeoJson2DGeographicCoordinates>( | |||
new GeoJson2DGeographicCoordinates(lon, lat)); | |||
} | |||
private void SetPolygon(List<GeoJson2DGeographicCoordinates> coordinatesList) | |||
{ | |||
Polygon = new GeoJsonPolygon<GeoJson2DGeographicCoordinates>(new GeoJsonPolygonCoordinates<GeoJson2DGeographicCoordinates>( | |||
new GeoJsonLinearRingCoordinates<GeoJson2DGeographicCoordinates>(coordinatesList))); | |||
} | |||
} | |||
} |
@ -0,0 +1,17 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.Model | |||
{ | |||
using MongoDB.Bson; | |||
using MongoDB.Bson.Serialization.Attributes; | |||
using System; | |||
public class UserLocation | |||
{ | |||
[BsonIgnoreIfDefault] | |||
[BsonRepresentation(BsonType.ObjectId)] | |||
public string Id { get; set; } | |||
public int UserId { get; set; } = 0; | |||
[BsonRepresentation(BsonType.ObjectId)] | |||
public string LocationId { get; set; } | |||
public DateTime UpdateDate { get; set; } | |||
} | |||
} |
@ -0,0 +1,25 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API | |||
{ | |||
public class Program | |||
{ | |||
public static void Main(string[] args) | |||
{ | |||
var host = new WebHostBuilder() | |||
.UseKestrel() | |||
.UseContentRoot(Directory.GetCurrentDirectory()) | |||
.UseStartup<Startup>() | |||
.UseApplicationInsights() | |||
.Build(); | |||
host.Run(); | |||
} | |||
} | |||
} |
@ -0,0 +1,29 @@ | |||
{ | |||
"iisSettings": { | |||
"windowsAuthentication": false, | |||
"anonymousAuthentication": true, | |||
"iisExpress": { | |||
"applicationUrl": "http://localhost:3278/", | |||
"sslPort": 0 | |||
} | |||
}, | |||
"profiles": { | |||
"IIS Express": { | |||
"commandName": "IISExpress", | |||
"launchBrowser": true, | |||
"launchUrl": "api/values", | |||
"environmentVariables": { | |||
"ASPNETCORE_ENVIRONMENT": "Development" | |||
} | |||
}, | |||
"Locations.API": { | |||
"commandName": "Project", | |||
"launchBrowser": true, | |||
"launchUrl": "api/values", | |||
"environmentVariables": { | |||
"ASPNETCORE_ENVIRONMENT": "Development" | |||
}, | |||
"applicationUrl": "http://localhost:3279" | |||
} | |||
} | |||
} |
@ -0,0 +1,113 @@ | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure; | |||
using Microsoft.Extensions.Configuration; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using System.Reflection; | |||
using System; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Services; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Repositories; | |||
using Microsoft.AspNetCore.Http; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Infrastructure.Filters; | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API | |||
{ | |||
public class Startup | |||
{ | |||
public IConfigurationRoot Configuration { get; } | |||
public Startup(IHostingEnvironment env) | |||
{ | |||
var builder = new ConfigurationBuilder() | |||
.SetBasePath(env.ContentRootPath) | |||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) | |||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); | |||
if (env.IsDevelopment()) | |||
{ | |||
builder.AddUserSecrets(typeof(Startup).GetTypeInfo().Assembly); | |||
} | |||
builder.AddEnvironmentVariables(); | |||
Configuration = builder.Build(); | |||
} | |||
// This method gets called by the runtime. Use this method to add services to the container. | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
// Add framework services. | |||
services.AddMvc(options => | |||
{ | |||
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | |||
}).AddControllersAsServices(); | |||
services.Configure<LocationSettings>(Configuration); | |||
// Add framework services. | |||
services.AddSwaggerGen(options => | |||
{ | |||
options.DescribeAllEnumsAsStrings(); | |||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info | |||
{ | |||
Title = "eShopOnContainers - Location HTTP API", | |||
Version = "v1", | |||
Description = "The Location Microservice HTTP API. This is a Data-Driven/CRUD microservice sample", | |||
TermsOfService = "Terms Of Service" | |||
}); | |||
}); | |||
services.AddCors(options => | |||
{ | |||
options.AddPolicy("CorsPolicy", | |||
builder => builder.AllowAnyOrigin() | |||
.AllowAnyMethod() | |||
.AllowAnyHeader() | |||
.AllowCredentials()); | |||
}); | |||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||
services.AddTransient<IIdentityService, IdentityService>(); | |||
services.AddTransient<ILocationsService, LocationsService>(); | |||
services.AddTransient<ILocationsRepository, LocationsRepository>(); | |||
} | |||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||
{ | |||
//Configure logs | |||
loggerFactory.AddConsole(Configuration.GetSection("Logging")); | |||
loggerFactory.AddDebug(); | |||
app.UseCors("CorsPolicy"); | |||
ConfigureAuth(app); | |||
app.UseMvcWithDefaultRoute(); | |||
app.UseSwagger() | |||
.UseSwaggerUI(c => | |||
{ | |||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); | |||
}); | |||
LocationsContextSeed.SeedAsync(app, loggerFactory) | |||
.Wait(); | |||
} | |||
protected virtual void ConfigureAuth(IApplicationBuilder app) | |||
{ | |||
var identityUrl = Configuration.GetValue<string>("IdentityUrl"); | |||
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions | |||
{ | |||
Authority = identityUrl.ToString(), | |||
ApiName = "locations", | |||
RequireHttpsMetadata = false | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,13 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Services.Locations.API.ViewModel | |||
{ | |||
public class LocationRequest | |||
{ | |||
public double Longitude { get; set; } | |||
public double Latitude { get; set; } | |||
} | |||
} |
@ -0,0 +1,10 @@ | |||
{ | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Debug", | |||
"System": "Information", | |||
"Microsoft": "Information" | |||
} | |||
} | |||
} |
@ -0,0 +1,13 @@ | |||
{ | |||
"ConnectionString": "mongodb://nosql.data", | |||
"Database": "LocationsDb", | |||
"IdentityUrl": "http://localhost:5105", | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Debug", | |||
"System": "Information", | |||
"Microsoft": "Information" | |||
} | |||
} | |||
} |
@ -0,0 +1,3 @@ | |||
* | |||
!obj/Docker/publish/* | |||
!obj/Docker/empty/ |
@ -0,0 +1,151 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Controllers | |||
{ | |||
using Microsoft.AspNetCore.Mvc; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; | |||
using System.Threading.Tasks; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Model; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; | |||
using System.Collections.Generic; | |||
using Microsoft.AspNetCore.Authorization; | |||
[Route("api/v1/[controller]")] | |||
[Authorize] | |||
public class CampaignsController : Controller | |||
{ | |||
private readonly MarketingContext _context; | |||
public CampaignsController(MarketingContext context) | |||
{ | |||
_context = context; | |||
} | |||
[HttpGet] | |||
public async Task<IActionResult> GetAllCampaigns() | |||
{ | |||
var campaignList = await _context.Campaigns | |||
.ToListAsync(); | |||
if (campaignList is null) | |||
{ | |||
return Ok(); | |||
} | |||
var campaignDtoList = MapCampaignModelListToDtoList(campaignList); | |||
return Ok(campaignDtoList); | |||
} | |||
[HttpGet("{id:int}")] | |||
public async Task<IActionResult> GetCampaignById(int id) | |||
{ | |||
var campaign = await _context.Campaigns | |||
.SingleOrDefaultAsync(c => c.Id == id); | |||
if (campaign is null) | |||
{ | |||
return NotFound(); | |||
} | |||
var campaignDto = MapCampaignModelToDto(campaign); | |||
return Ok(campaignDto); | |||
} | |||
[HttpPost] | |||
public async Task<IActionResult> CreateCampaign([FromBody] CampaignDTO campaignDto) | |||
{ | |||
if (campaignDto is null) | |||
{ | |||
return BadRequest(); | |||
} | |||
var campaign = MapCampaignDtoToModel(campaignDto); | |||
await _context.Campaigns.AddAsync(campaign); | |||
await _context.SaveChangesAsync(); | |||
return CreatedAtAction(nameof(GetCampaignById), new { id = campaign.Id }, null); | |||
} | |||
[HttpPut("{id:int}")] | |||
public async Task<IActionResult> UpdateCampaign(int id, [FromBody] CampaignDTO campaignDto) | |||
{ | |||
if (id < 1 || campaignDto is null) | |||
{ | |||
return BadRequest(); | |||
} | |||
var campaignToUpdate = await _context.Campaigns.FindAsync(id); | |||
if (campaignToUpdate is null) | |||
{ | |||
return NotFound(); | |||
} | |||
campaignToUpdate.Description = campaignDto.Description; | |||
campaignToUpdate.From = campaignDto.From; | |||
campaignToUpdate.To = campaignDto.To; | |||
campaignToUpdate.Url = campaignDto.Url; | |||
await _context.SaveChangesAsync(); | |||
return CreatedAtAction(nameof(GetCampaignById), new { id = campaignToUpdate.Id }, null); | |||
} | |||
[HttpDelete("{id:int}")] | |||
public async Task<IActionResult> Delete(int id) | |||
{ | |||
if (id < 1) | |||
{ | |||
return BadRequest(); | |||
} | |||
var campaignToDelete = await _context.Campaigns.FindAsync(id); | |||
if (campaignToDelete is null) | |||
{ | |||
return NotFound(); | |||
} | |||
_context.Campaigns.Remove(campaignToDelete); | |||
await _context.SaveChangesAsync(); | |||
return NoContent(); | |||
} | |||
private List<CampaignDTO> MapCampaignModelListToDtoList(List<Campaign> campaignList) | |||
{ | |||
var campaignDtoList = new List<CampaignDTO>(); | |||
campaignList.ForEach(campaign => campaignDtoList | |||
.Add(MapCampaignModelToDto(campaign))); | |||
return campaignDtoList; | |||
} | |||
private CampaignDTO MapCampaignModelToDto(Campaign campaign) | |||
{ | |||
return new CampaignDTO | |||
{ | |||
Id = campaign.Id, | |||
Description = campaign.Description, | |||
From = campaign.From, | |||
To = campaign.To, | |||
Url = campaign.Url, | |||
}; | |||
} | |||
private Campaign MapCampaignDtoToModel(CampaignDTO campaignDto) | |||
{ | |||
return new Campaign | |||
{ | |||
Id = campaignDto.Id, | |||
Description = campaignDto.Description, | |||
From = campaignDto.From, | |||
To = campaignDto.To, | |||
Url = campaignDto.Url | |||
}; | |||
} | |||
} | |||
} |
@ -0,0 +1,13 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Controllers | |||
{ | |||
using Microsoft.AspNetCore.Mvc; | |||
// GET: /<controller>/ | |||
public class HomeController : Controller | |||
{ | |||
public IActionResult Index() | |||
{ | |||
return new RedirectResult("~/swagger"); | |||
} | |||
} | |||
} |
@ -0,0 +1,146 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Controllers | |||
{ | |||
using Microsoft.AspNetCore.Authorization; | |||
using Microsoft.AspNetCore.Mvc; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Model; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
[Authorize] | |||
public class LocationsController : Controller | |||
{ | |||
private readonly MarketingContext _context; | |||
public LocationsController(MarketingContext context) | |||
{ | |||
_context = context; | |||
} | |||
[HttpGet] | |||
[Route("api/v1/campaigns/{campaignId:int}/locations/{userLocationRuleId:int}")] | |||
public IActionResult GetLocationByCampaignAndLocationRuleId(int campaignId, | |||
int userLocationRuleId) | |||
{ | |||
if (campaignId < 1 || userLocationRuleId < 1) | |||
{ | |||
return BadRequest(); | |||
} | |||
var location = _context.Rules | |||
.OfType<UserLocationRule>() | |||
.SingleOrDefault(c => c.CampaignId == campaignId && c.Id == userLocationRuleId); | |||
if (location is null) | |||
{ | |||
return NotFound(); | |||
} | |||
var locationDto = MapUserLocationRuleModelToDto(location); | |||
return Ok(locationDto); | |||
} | |||
[HttpGet] | |||
[Route("api/v1/campaigns/{campaignId:int}/locations")] | |||
public IActionResult GetAllLocationsByCampaignId(int campaignId) | |||
{ | |||
if (campaignId < 1) | |||
{ | |||
return BadRequest(); | |||
} | |||
var locationList = _context.Rules | |||
.OfType<UserLocationRule>() | |||
.Where(c => c.CampaignId == campaignId) | |||
.ToList(); | |||
if(locationList is null) | |||
{ | |||
return Ok(); | |||
} | |||
var locationDtoList = MapUserLocationRuleModelListToDtoList(locationList); | |||
return Ok(locationDtoList); | |||
} | |||
[HttpPost] | |||
[Route("api/v1/campaigns/{campaignId:int}/locations")] | |||
public async Task<IActionResult> CreateLocation(int campaignId, | |||
[FromBody] UserLocationRuleDTO locationRuleDto) | |||
{ | |||
if (campaignId < 1 || locationRuleDto is null) | |||
{ | |||
return BadRequest(); | |||
} | |||
var locationRule = MapUserLocationRuleDtoToModel(locationRuleDto); | |||
locationRule.CampaignId = campaignId; | |||
await _context.Rules.AddAsync(locationRule); | |||
await _context.SaveChangesAsync(); | |||
return CreatedAtAction(nameof(GetLocationByCampaignAndLocationRuleId), | |||
new { campaignId = campaignId, locationRuleId = locationRule.Id }, null); | |||
} | |||
[HttpDelete] | |||
[Route("api/v1/campaigns/{campaignId:int}/locations/{userLocationRuleId:int}")] | |||
public async Task<IActionResult> DeleteLocationById(int campaignId, int userLocationRuleId) | |||
{ | |||
if (campaignId < 1 || userLocationRuleId < 1) | |||
{ | |||
return BadRequest(); | |||
} | |||
var locationToDelete = _context.Rules | |||
.OfType<UserLocationRule>() | |||
.SingleOrDefault(c => c.CampaignId == campaignId && c.Id == userLocationRuleId); | |||
if (locationToDelete is null) | |||
{ | |||
return NotFound(); | |||
} | |||
_context.Rules.Remove(locationToDelete); | |||
await _context.SaveChangesAsync(); | |||
return NoContent(); | |||
} | |||
private List<UserLocationRuleDTO> MapUserLocationRuleModelListToDtoList(List<UserLocationRule> userLocationRuleList) | |||
{ | |||
var userLocationRuleDtoList = new List<UserLocationRuleDTO>(); | |||
userLocationRuleList.ForEach(userLocationRule => userLocationRuleDtoList | |||
.Add(MapUserLocationRuleModelToDto(userLocationRule))); | |||
return userLocationRuleDtoList; | |||
} | |||
private UserLocationRuleDTO MapUserLocationRuleModelToDto(UserLocationRule userLocationRule) | |||
{ | |||
return new UserLocationRuleDTO | |||
{ | |||
Id = userLocationRule.Id, | |||
Description = userLocationRule.Description, | |||
LocationId = userLocationRule.LocationId | |||
}; | |||
} | |||
private UserLocationRule MapUserLocationRuleDtoToModel(UserLocationRuleDTO userLocationRuleDto) | |||
{ | |||
return new UserLocationRule | |||
{ | |||
Id = userLocationRuleDto.Id, | |||
Description = userLocationRuleDto.Description, | |||
LocationId = userLocationRuleDto.LocationId | |||
}; | |||
} | |||
} | |||
} |
@ -0,0 +1,6 @@ | |||
FROM microsoft/aspnetcore:1.1.2 | |||
ARG source | |||
WORKDIR /app | |||
EXPOSE 80 | |||
COPY ${source:-obj/Docker/publish} . | |||
ENTRYPOINT ["dotnet", "Marketing.API.dll"] |
@ -0,0 +1,17 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto | |||
{ | |||
using System; | |||
public class CampaignDTO | |||
{ | |||
public int Id { get; set; } | |||
public string Description { get; set; } | |||
public DateTime From { get; set; } | |||
public DateTime To { get; set; } | |||
public string Url { get; set; } | |||
} | |||
} |
@ -0,0 +1,11 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto | |||
{ | |||
public class UserLocationRuleDTO | |||
{ | |||
public int Id { get; set; } | |||
public int LocationId { get; set; } | |||
public string Description { get; set; } | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.ActionResults | |||
{ | |||
using AspNetCore.Http; | |||
using Microsoft.AspNetCore.Mvc; | |||
public class InternalServerErrorObjectResult : ObjectResult | |||
{ | |||
public InternalServerErrorObjectResult(object error) | |||
: base(error) | |||
{ | |||
StatusCode = StatusCodes.Status500InternalServerError; | |||
} | |||
} | |||
} |
@ -0,0 +1,21 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions | |||
{ | |||
using System; | |||
/// <summary> | |||
/// Exception type for app exceptions | |||
/// </summary> | |||
public class MarketingDomainException : Exception | |||
{ | |||
public MarketingDomainException() | |||
{ } | |||
public MarketingDomainException(string message) | |||
: base(message) | |||
{ } | |||
public MarketingDomainException(string message, Exception innerException) | |||
: base(message, innerException) | |||
{ } | |||
} | |||
} |
@ -0,0 +1,67 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Filters | |||
{ | |||
using AspNetCore.Mvc; | |||
using global::Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.AspNetCore.Mvc.Filters; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.ActionResults; | |||
using Microsoft.Extensions.Logging; | |||
using System.Net; | |||
public class HttpGlobalExceptionFilter : IExceptionFilter | |||
{ | |||
private readonly IHostingEnvironment env; | |||
private readonly ILogger<HttpGlobalExceptionFilter> logger; | |||
public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger<HttpGlobalExceptionFilter> logger) | |||
{ | |||
this.env = env; | |||
this.logger = logger; | |||
} | |||
public void OnException(ExceptionContext context) | |||
{ | |||
logger.LogError(new EventId(context.Exception.HResult), | |||
context.Exception, | |||
context.Exception.Message); | |||
if (context.Exception.GetType() == typeof(MarketingDomainException)) | |||
{ | |||
var json = new JsonErrorResponse | |||
{ | |||
Messages = new[] { context.Exception.Message } | |||
}; | |||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 | |||
//It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information | |||
context.Result = new BadRequestObjectResult(json); | |||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; | |||
} | |||
else | |||
{ | |||
var json = new JsonErrorResponse | |||
{ | |||
Messages = new[] { "An error occur.Try it again." } | |||
}; | |||
if (env.IsDevelopment()) | |||
{ | |||
json.DeveloperMessage = context.Exception; | |||
} | |||
// Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 | |||
// It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information | |||
context.Result = new InternalServerErrorObjectResult(json); | |||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; | |||
} | |||
context.ExceptionHandled = true; | |||
} | |||
private class JsonErrorResponse | |||
{ | |||
public string[] Messages { get; set; } | |||
public object DeveloperMessage { get; set; } | |||
} | |||
} | |||
} |
@ -0,0 +1,83 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure | |||
{ | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Metadata.Builders; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Model; | |||
public class MarketingContext : DbContext | |||
{ | |||
public MarketingContext(DbContextOptions<MarketingContext> options) : base(options) | |||
{ | |||
} | |||
public DbSet<Campaign> Campaigns { get; set; } | |||
public DbSet<Rule> Rules { get; set; } | |||
protected override void OnModelCreating(ModelBuilder builder) | |||
{ | |||
builder.Entity<Campaign>(ConfigureCampaigns); | |||
builder.Entity<Rule>(ConfigureRules); | |||
builder.Entity<UserLocationRule>(ConfigureUserLocationRules); | |||
} | |||
void ConfigureCampaigns(EntityTypeBuilder<Campaign> builder) | |||
{ | |||
builder.ToTable("Campaign"); | |||
builder.HasKey(m => m.Id); | |||
builder.Property(m => m.Id) | |||
.ForSqlServerUseSequenceHiLo("campaign_hilo") | |||
.IsRequired(); | |||
builder.Property(m => m.Description) | |||
.HasColumnName("Description") | |||
.IsRequired(); | |||
builder.Property(m => m.From) | |||
.HasColumnName("From") | |||
.IsRequired(); | |||
builder.Property(m => m.To) | |||
.HasColumnName("To") | |||
.IsRequired(); | |||
builder.Property(m => m.Description) | |||
.HasColumnName("Description") | |||
.IsRequired(); | |||
builder.HasMany(m => m.Rules) | |||
.WithOne(r => r.Campaign) | |||
.HasForeignKey(r => r.CampaignId) | |||
.IsRequired(); | |||
} | |||
void ConfigureRules(EntityTypeBuilder<Rule> builder) | |||
{ | |||
builder.ToTable("Rule"); | |||
builder.HasKey(r => r.Id); | |||
builder.Property(r => r.Id) | |||
.ForSqlServerUseSequenceHiLo("rule_hilo") | |||
.IsRequired(); | |||
builder.HasDiscriminator<int>("RuleTypeId") | |||
.HasValue<UserProfileRule>((int)RuleTypeEnum.UserProfileRule) | |||
.HasValue<PurchaseHistoryRule>((int)RuleTypeEnum.PurchaseHistoryRule) | |||
.HasValue<UserLocationRule>((int)RuleTypeEnum.UserLocationRule); | |||
builder.Property(r => r.Description) | |||
.HasColumnName("Description") | |||
.IsRequired(); | |||
} | |||
void ConfigureUserLocationRules(EntityTypeBuilder<UserLocationRule> builder) | |||
{ | |||
builder.Property(r => r.LocationId) | |||
.HasColumnName("LocationId") | |||
.IsRequired(); | |||
} | |||
} | |||
} |
@ -0,0 +1,67 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure | |||
{ | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Model; | |||
using Microsoft.Extensions.Logging; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
public class MarketingContextSeed | |||
{ | |||
public static async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, int? retry = 0) | |||
{ | |||
var context = (MarketingContext)applicationBuilder | |||
.ApplicationServices.GetService(typeof(MarketingContext)); | |||
context.Database.Migrate(); | |||
if (!context.Campaigns.Any()) | |||
{ | |||
context.Campaigns.AddRange( | |||
GetPreconfiguredMarketings()); | |||
await context.SaveChangesAsync(); | |||
} | |||
} | |||
static List<Campaign> GetPreconfiguredMarketings() | |||
{ | |||
return new List<Campaign> | |||
{ | |||
new Campaign | |||
{ | |||
Description = "Campaign1", | |||
From = DateTime.Now, | |||
To = DateTime.Now.AddDays(7), | |||
Url = "http://CampaignUrl.test/12f09ed3cef54187123f500ad", | |||
Rules = new List<Rule> | |||
{ | |||
new UserLocationRule | |||
{ | |||
Description = "UserLocationRule1", | |||
LocationId = 1 | |||
} | |||
} | |||
}, | |||
new Campaign | |||
{ | |||
Description = "Campaign2", | |||
From = DateTime.Now.AddDays(7), | |||
To = DateTime.Now.AddDays(14), | |||
Url = "http://CampaignUrl.test/02a59eda65f241871239000ff", | |||
Rules = new List<Rule> | |||
{ | |||
new UserLocationRule | |||
{ | |||
Description = "UserLocationRule2", | |||
LocationId = 3 | |||
} | |||
} | |||
} | |||
}; | |||
} | |||
} | |||
} |
@ -0,0 +1,111 @@ | |||
using System; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Metadata; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations | |||
{ | |||
[DbContext(typeof(MarketingContext))] | |||
[Migration("20170609104915_Initial")] | |||
partial class Initial | |||
{ | |||
protected override void BuildTargetModel(ModelBuilder modelBuilder) | |||
{ | |||
modelBuilder | |||
.HasAnnotation("ProductVersion", "1.1.2") | |||
.HasAnnotation("SqlServer:Sequence:.campaign_hilo", "'campaign_hilo', '', '1', '10', '', '', 'Int64', 'False'") | |||
.HasAnnotation("SqlServer:Sequence:.rule_hilo", "'rule_hilo', '', '1', '10', '', '', 'Int64', 'False'") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd() | |||
.HasAnnotation("SqlServer:HiLoSequenceName", "campaign_hilo") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); | |||
b.Property<string>("Description") | |||
.IsRequired() | |||
.HasColumnName("Description"); | |||
b.Property<DateTime>("From") | |||
.HasColumnName("From"); | |||
b.Property<DateTime>("To") | |||
.HasColumnName("To"); | |||
b.Property<string>("Url"); | |||
b.HasKey("Id"); | |||
b.ToTable("Campaign"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd() | |||
.HasAnnotation("SqlServer:HiLoSequenceName", "rule_hilo") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); | |||
b.Property<int>("CampaignId"); | |||
b.Property<string>("Description") | |||
.IsRequired() | |||
.HasColumnName("Description"); | |||
b.Property<int>("RuleTypeId"); | |||
b.HasKey("Id"); | |||
b.HasIndex("CampaignId"); | |||
b.ToTable("Rule"); | |||
b.HasDiscriminator<int>("RuleTypeId"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.PurchaseHistoryRule", b => | |||
{ | |||
b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); | |||
b.ToTable("PurchaseHistoryRule"); | |||
b.HasDiscriminator().HasValue(2); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserLocationRule", b => | |||
{ | |||
b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); | |||
b.Property<int>("LocationId") | |||
.HasColumnName("LocationId"); | |||
b.ToTable("UserLocationRule"); | |||
b.HasDiscriminator().HasValue(3); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserProfileRule", b => | |||
{ | |||
b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); | |||
b.ToTable("UserProfileRule"); | |||
b.HasDiscriminator().HasValue(1); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => | |||
{ | |||
b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", "Campaign") | |||
.WithMany("Rules") | |||
.HasForeignKey("CampaignId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,76 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations | |||
{ | |||
public partial class Initial : Migration | |||
{ | |||
protected override void Up(MigrationBuilder migrationBuilder) | |||
{ | |||
migrationBuilder.CreateSequence( | |||
name: "campaign_hilo", | |||
incrementBy: 10); | |||
migrationBuilder.CreateSequence( | |||
name: "rule_hilo", | |||
incrementBy: 10); | |||
migrationBuilder.CreateTable( | |||
name: "Campaign", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false), | |||
Description = table.Column<string>(nullable: false), | |||
From = table.Column<DateTime>(nullable: false), | |||
To = table.Column<DateTime>(nullable: false), | |||
Url = table.Column<string>(nullable: true) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_Campaign", x => x.Id); | |||
}); | |||
migrationBuilder.CreateTable( | |||
name: "Rule", | |||
columns: table => new | |||
{ | |||
Id = table.Column<int>(nullable: false), | |||
CampaignId = table.Column<int>(nullable: false), | |||
Description = table.Column<string>(nullable: false), | |||
RuleTypeId = table.Column<int>(nullable: false), | |||
LocationId = table.Column<int>(nullable: true) | |||
}, | |||
constraints: table => | |||
{ | |||
table.PrimaryKey("PK_Rule", x => x.Id); | |||
table.ForeignKey( | |||
name: "FK_Rule_Campaign_CampaignId", | |||
column: x => x.CampaignId, | |||
principalTable: "Campaign", | |||
principalColumn: "Id", | |||
onDelete: ReferentialAction.Cascade); | |||
}); | |||
migrationBuilder.CreateIndex( | |||
name: "IX_Rule_CampaignId", | |||
table: "Rule", | |||
column: "CampaignId"); | |||
} | |||
protected override void Down(MigrationBuilder migrationBuilder) | |||
{ | |||
migrationBuilder.DropTable( | |||
name: "Rule"); | |||
migrationBuilder.DropTable( | |||
name: "Campaign"); | |||
migrationBuilder.DropSequence( | |||
name: "campaign_hilo"); | |||
migrationBuilder.DropSequence( | |||
name: "rule_hilo"); | |||
} | |||
} | |||
} |
@ -0,0 +1,110 @@ | |||
using System; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Metadata; | |||
using Microsoft.EntityFrameworkCore.Migrations; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations | |||
{ | |||
[DbContext(typeof(MarketingContext))] | |||
partial class MarketingContextModelSnapshot : ModelSnapshot | |||
{ | |||
protected override void BuildModel(ModelBuilder modelBuilder) | |||
{ | |||
modelBuilder | |||
.HasAnnotation("ProductVersion", "1.1.2") | |||
.HasAnnotation("SqlServer:Sequence:.campaign_hilo", "'campaign_hilo', '', '1', '10', '', '', 'Int64', 'False'") | |||
.HasAnnotation("SqlServer:Sequence:.rule_hilo", "'rule_hilo', '', '1', '10', '', '', 'Int64', 'False'") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd() | |||
.HasAnnotation("SqlServer:HiLoSequenceName", "campaign_hilo") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); | |||
b.Property<string>("Description") | |||
.IsRequired() | |||
.HasColumnName("Description"); | |||
b.Property<DateTime>("From") | |||
.HasColumnName("From"); | |||
b.Property<DateTime>("To") | |||
.HasColumnName("To"); | |||
b.Property<string>("Url"); | |||
b.HasKey("Id"); | |||
b.ToTable("Campaign"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => | |||
{ | |||
b.Property<int>("Id") | |||
.ValueGeneratedOnAdd() | |||
.HasAnnotation("SqlServer:HiLoSequenceName", "rule_hilo") | |||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); | |||
b.Property<int>("CampaignId"); | |||
b.Property<string>("Description") | |||
.IsRequired() | |||
.HasColumnName("Description"); | |||
b.Property<int>("RuleTypeId"); | |||
b.HasKey("Id"); | |||
b.HasIndex("CampaignId"); | |||
b.ToTable("Rule"); | |||
b.HasDiscriminator<int>("RuleTypeId"); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.PurchaseHistoryRule", b => | |||
{ | |||
b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); | |||
b.ToTable("PurchaseHistoryRule"); | |||
b.HasDiscriminator().HasValue(2); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserLocationRule", b => | |||
{ | |||
b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); | |||
b.Property<int>("LocationId") | |||
.HasColumnName("LocationId"); | |||
b.ToTable("UserLocationRule"); | |||
b.HasDiscriminator().HasValue(3); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserProfileRule", b => | |||
{ | |||
b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); | |||
b.ToTable("UserProfileRule"); | |||
b.HasDiscriminator().HasValue(1); | |||
}); | |||
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => | |||
{ | |||
b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", "Campaign") | |||
.WithMany("Rules") | |||
.HasForeignKey("CampaignId") | |||
.OnDelete(DeleteBehavior.Cascade); | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,50 @@ | |||
<Project Sdk="Microsoft.NET.Sdk.Web"> | |||
<PropertyGroup> | |||
<TargetFramework>netcoreapp1.1</TargetFramework> | |||
<RuntimeFrameworkVersion>1.1.2</RuntimeFrameworkVersion> | |||
<OutputType>Exe</OutputType> | |||
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | |||
<RootNamespace>Microsoft.eShopOnContainers.Services.Marketing.API</RootNamespace> | |||
<PackageTargetFallback>portable-net45+win8</PackageTargetFallback> | |||
<UserSecretsId>aspnet-Marketing.API-20161122013619</UserSecretsId> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Folder Include="Infrastructure\MarketingMigrations\" /> | |||
<Folder Include="IntegrationEvents\EventHandling\" /> | |||
<Folder Include="IntegrationEvents\Events\" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="1.2.0" /> | |||
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" /> | |||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.1.1" /> | |||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" /> | |||
</ItemGroup> | |||
</Project> |
@ -0,0 +1,7 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API | |||
{ | |||
public class MarketingSettings | |||
{ | |||
public string ConnectionString { get; set; } | |||
} | |||
} |
@ -0,0 +1,26 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model | |||
{ | |||
using System; | |||
using System.Collections.Generic; | |||
public class Campaign | |||
{ | |||
public int Id { get; set; } | |||
public string Description { get; set; } | |||
public DateTime From { get; set; } | |||
public DateTime To { get; set; } | |||
public string Url { get; set; } | |||
public List<Rule> Rules { get; set; } | |||
public Campaign() | |||
{ | |||
Rules = new List<Rule>(); | |||
} | |||
} | |||
} |
@ -0,0 +1,27 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model | |||
{ | |||
public abstract class Rule | |||
{ | |||
public int Id { get; set; } | |||
public int CampaignId { get; set; } | |||
public Campaign Campaign { get; set; } | |||
public string Description { get; set; } | |||
} | |||
public class UserProfileRule : Rule | |||
{ | |||
} | |||
public class PurchaseHistoryRule : Rule | |||
{ | |||
} | |||
public class UserLocationRule : Rule | |||
{ | |||
public int LocationId { get; set; } | |||
} | |||
} |
@ -0,0 +1,20 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model | |||
{ | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions; | |||
using System; | |||
public enum RuleTypeEnum { UserProfileRule = 1, PurchaseHistoryRule = 2, UserLocationRule = 3 } | |||
public static class RuleType | |||
{ | |||
public static RuleTypeEnum From(int id) | |||
{ | |||
if (!Enum.IsDefined(typeof(RuleTypeEnum), id)) | |||
{ | |||
throw new MarketingDomainException($"Invalid value for RuleType, RuleTypeId: {id}"); | |||
} | |||
return (RuleTypeEnum)id; | |||
} | |||
} | |||
} |
@ -0,0 +1,20 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API | |||
{ | |||
using System.IO; | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
public class Program | |||
{ | |||
public static void Main(string[] args) | |||
{ | |||
var host = new WebHostBuilder() | |||
.UseKestrel() | |||
.UseContentRoot(Directory.GetCurrentDirectory()) | |||
.UseStartup<Startup>() | |||
.Build(); | |||
host.Run(); | |||
} | |||
} | |||
} |
@ -0,0 +1,29 @@ | |||
{ | |||
"iisSettings": { | |||
"windowsAuthentication": false, | |||
"anonymousAuthentication": true, | |||
"iisExpress": { | |||
"applicationUrl": "http://localhost:5110", | |||
"sslPort": 0 | |||
} | |||
}, | |||
"profiles": { | |||
"IIS Express": { | |||
"commandName": "IISExpress", | |||
"launchBrowser": true, | |||
"launchUrl": "api/values", | |||
"environmentVariables": { | |||
"ASPNETCORE_ENVIRONMENT": "Development" | |||
} | |||
}, | |||
"Marketing.API": { | |||
"commandName": "Project", | |||
"launchBrowser": true, | |||
"launchUrl": "api/values", | |||
"environmentVariables": { | |||
"ASPNETCORE_ENVIRONMENT": "Development" | |||
}, | |||
"applicationUrl": "http://localhost:52059" | |||
} | |||
} | |||
} |
@ -0,0 +1,119 @@ | |||
namespace Microsoft.eShopOnContainers.Services.Marketing.API | |||
{ | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; | |||
using Microsoft.Extensions.Configuration; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using System.Reflection; | |||
using System; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Filters; | |||
public class Startup | |||
{ | |||
public Startup(IHostingEnvironment env) | |||
{ | |||
var builder = new ConfigurationBuilder() | |||
.SetBasePath(env.ContentRootPath) | |||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) | |||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) | |||
.AddEnvironmentVariables(); | |||
if (env.IsDevelopment()) | |||
{ | |||
builder.AddUserSecrets(typeof(Startup).GetTypeInfo().Assembly); | |||
} | |||
builder.AddEnvironmentVariables(); | |||
Configuration = builder.Build(); | |||
} | |||
public IConfigurationRoot Configuration { get; } | |||
// This method gets called by the runtime. Use this method to add services to the container. | |||
public void ConfigureServices(IServiceCollection services) | |||
{ | |||
// Add framework services. | |||
services.AddMvc(options => | |||
{ | |||
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | |||
}).AddControllersAsServices(); //Injecting Controllers themselves thru DIFor further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services | |||
services.AddDbContext<MarketingContext>(options => | |||
{ | |||
options.UseSqlServer(Configuration["ConnectionString"], | |||
sqlServerOptionsAction: sqlOptions => | |||
{ | |||
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); | |||
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency | |||
sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | |||
}); | |||
// Changing default behavior when client evaluation occurs to throw. | |||
// Default in EF Core would be to log a warning when client evaluation is performed. | |||
options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); | |||
//Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval | |||
}); | |||
// Add framework services. | |||
services.AddSwaggerGen(options => | |||
{ | |||
options.DescribeAllEnumsAsStrings(); | |||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info | |||
{ | |||
Title = "Marketing HTTP API", | |||
Version = "v1", | |||
Description = "The Marketing Service HTTP API", | |||
TermsOfService = "Terms Of Service" | |||
}); | |||
}); | |||
services.AddCors(options => | |||
{ | |||
options.AddPolicy("CorsPolicy", | |||
builder => builder.AllowAnyOrigin() | |||
.AllowAnyMethod() | |||
.AllowAnyHeader() | |||
.AllowCredentials()); | |||
}); | |||
} | |||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) | |||
{ | |||
loggerFactory.AddConsole(Configuration.GetSection("Logging")); | |||
loggerFactory.AddDebug(); | |||
app.UseCors("CorsPolicy"); | |||
ConfigureAuth(app); | |||
app.UseMvcWithDefaultRoute(); | |||
app.UseSwagger() | |||
.UseSwaggerUI(c => | |||
{ | |||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); | |||
}); | |||
MarketingContextSeed.SeedAsync(app, loggerFactory) | |||
.Wait(); | |||
} | |||
protected virtual void ConfigureAuth(IApplicationBuilder app) | |||
{ | |||
var identityUrl = Configuration.GetValue<string>("IdentityUrl"); | |||
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions | |||
{ | |||
Authority = identityUrl.ToString(), | |||
ApiName = "marketing", | |||
RequireHttpsMetadata = false | |||
}); | |||
} | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
{ | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Warning" | |||
} | |||
} | |||
} |
@ -0,0 +1,10 @@ | |||
{ | |||
"Logging": { | |||
"IncludeScopes": false, | |||
"LogLevel": { | |||
"Default": "Warning" | |||
} | |||
}, | |||
"ConnectionString": "127.0.0.1", | |||
"IdentityUrl": "http://localhost:5105" | |||
} |
@ -0,0 +1,41 @@ | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.AspNetCore.TestHost; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Text; | |||
namespace IntegrationTests.Services.Locations | |||
{ | |||
public class LocationsScenarioBase | |||
{ | |||
public TestServer CreateServer() | |||
{ | |||
var webHostBuilder = new WebHostBuilder(); | |||
webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory() + "\\Services\\Locations"); | |||
webHostBuilder.UseStartup<LocationsTestsStartup>(); | |||
return new TestServer(webHostBuilder); | |||
} | |||
public static class Get | |||
{ | |||
public static string Locations = "api/v1/locations"; | |||
public static string LocationBy(string id) | |||
{ | |||
return $"api/v1/locations/{id}"; | |||
} | |||
public static string UserLocationBy(int id) | |||
{ | |||
return $"api/v1/locations/user/{id}"; | |||
} | |||
} | |||
public static class Post | |||
{ | |||
public static string AddNewLocation = "api/v1/locations/"; | |||
} | |||
} | |||
} |
@ -0,0 +1,138 @@ | |||
using Microsoft.eShopOnContainers.Services.Locations.API.Model; | |||
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel; | |||
using Location = Microsoft.eShopOnContainers.Services.Locations.API.Model.Locations; | |||
using System.Collections.Generic; | |||
using Newtonsoft.Json; | |||
using System.Net.Http; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Xunit; | |||
namespace IntegrationTests.Services.Locations | |||
{ | |||
public class LocationsScenarios | |||
: LocationsScenarioBase | |||
{ | |||
[Fact] | |||
public async Task Set_new_user_seattle_location_response_ok_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var userId = 1234; | |||
var content = new StringContent(BuildLocationsRequest(-122.315752, 47.604610), UTF8Encoding.UTF8, "application/json"); | |||
// Expected result | |||
var expectedLocation = "SEAT"; | |||
// Act | |||
var response = await server.CreateClient() | |||
.PostAsync(Post.AddNewLocation, content); | |||
var userLocationResponse = await server.CreateClient() | |||
.GetAsync(Get.UserLocationBy(userId)); | |||
var responseBody = await userLocationResponse.Content.ReadAsStringAsync(); | |||
var userLocation = JsonConvert.DeserializeObject<UserLocation>(responseBody); | |||
var locationResponse = await server.CreateClient() | |||
.GetAsync(Get.LocationBy(userLocation.LocationId)); | |||
responseBody = await locationResponse.Content.ReadAsStringAsync(); | |||
var location = JsonConvert.DeserializeObject<Location>(responseBody); | |||
// Assert | |||
Assert.Equal(expectedLocation, location.Code); | |||
} | |||
} | |||
[Fact] | |||
public async Task Set_new_user_readmond_location_response_ok_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var userId = 1234; | |||
var content = new StringContent(BuildLocationsRequest(-122.119998, 47.690876), UTF8Encoding.UTF8, "application/json"); | |||
// Expected result | |||
var expectedLocation = "REDM"; | |||
// Act | |||
var response = await server.CreateClient() | |||
.PostAsync(Post.AddNewLocation, content); | |||
var userLocationResponse = await server.CreateClient() | |||
.GetAsync(Get.UserLocationBy(userId)); | |||
var responseBody = await userLocationResponse.Content.ReadAsStringAsync(); | |||
var userLocation = JsonConvert.DeserializeObject<UserLocation>(responseBody); | |||
var locationResponse = await server.CreateClient() | |||
.GetAsync(Get.LocationBy(userLocation.LocationId)); | |||
responseBody = await locationResponse.Content.ReadAsStringAsync(); | |||
var location = JsonConvert.DeserializeObject<Location>(responseBody); | |||
// Assert | |||
Assert.Equal(expectedLocation, location.Code); | |||
} | |||
} | |||
[Fact] | |||
public async Task Set_new_user_Washington_location_response_ok_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var userId = 1234; | |||
var content = new StringContent(BuildLocationsRequest(-121.040360, 48.091631), UTF8Encoding.UTF8, "application/json"); | |||
// Expected result | |||
var expectedLocation = "WHT"; | |||
// Act | |||
var response = await server.CreateClient() | |||
.PostAsync(Post.AddNewLocation, content); | |||
var userLocationResponse = await server.CreateClient() | |||
.GetAsync(Get.UserLocationBy(userId)); | |||
var responseBody = await userLocationResponse.Content.ReadAsStringAsync(); | |||
var userLocation = JsonConvert.DeserializeObject<UserLocation>(responseBody); | |||
var locationResponse = await server.CreateClient() | |||
.GetAsync(Get.LocationBy(userLocation.LocationId)); | |||
responseBody = await locationResponse.Content.ReadAsStringAsync(); | |||
var location = JsonConvert.DeserializeObject<Location>(responseBody); | |||
// Assert | |||
Assert.Equal(expectedLocation, location.Code); | |||
} | |||
} | |||
[Fact] | |||
public async Task Get_all_locations_response_ok_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var response = await server.CreateClient() | |||
.GetAsync(Get.Locations); | |||
var responseBody = await response.Content.ReadAsStringAsync(); | |||
var locations = JsonConvert.DeserializeObject<List<Location>>(responseBody); | |||
// Assert | |||
Assert.NotEmpty(locations); | |||
} | |||
} | |||
string BuildLocationsRequest(double lon, double lat) | |||
{ | |||
var location = new LocationRequest() | |||
{ | |||
Longitude = lon, | |||
Latitude = lat | |||
}; | |||
return JsonConvert.SerializeObject(location); | |||
} | |||
} | |||
} |
@ -0,0 +1,26 @@ | |||
namespace IntegrationTests.Services.Locations | |||
{ | |||
using IntegrationTests.Middleware; | |||
using Microsoft.AspNetCore.Builder; | |||
using Microsoft.AspNetCore.Hosting; | |||
using Microsoft.eShopOnContainers.Services.Locations.API; | |||
public class LocationsTestsStartup : Startup | |||
{ | |||
public LocationsTestsStartup(IHostingEnvironment env) : base(env) | |||
{ | |||
} | |||
protected override void ConfigureAuth(IApplicationBuilder app) | |||
{ | |||
if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant()) | |||
{ | |||
app.UseMiddleware<AutoAuthorizeMiddleware>(); | |||
} | |||
else | |||
{ | |||
base.ConfigureAuth(app); | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
{ | |||
"ConnectionString": "mongodb://localhost:27017", | |||
"Database": "LocationsDb", | |||
"ExternalCatalogBaseUrl": "http://localhost:5101", | |||
"IdentityUrl": "http://localhost:5105", | |||
"isTest": "true", | |||
"EventBusConnection": "localhost" | |||
} |
@ -0,0 +1,30 @@ | |||
namespace IntegrationTests.Services.Marketing | |||
{ | |||
public class CampaignScenarioBase : MarketingScenarioBase | |||
{ | |||
public static class Get | |||
{ | |||
public static string Campaigns = CampaignsUrlBase; | |||
public static string CampaignBy(int id) | |||
=> $"{CampaignsUrlBase}/{id}"; | |||
} | |||
public static class Post | |||
{ | |||
public static string AddNewCampaign = CampaignsUrlBase; | |||
} | |||
public static class Put | |||
{ | |||
public static string CampaignBy(int id) | |||
=> $"{CampaignsUrlBase}/{id}"; | |||
} | |||
public static class Delete | |||
{ | |||
public static string CampaignBy(int id) | |||
=> $"{CampaignsUrlBase}/{id}"; | |||
} | |||
} | |||
} |
@ -0,0 +1,127 @@ | |||
namespace IntegrationTests.Services.Marketing | |||
{ | |||
using System.Net.Http; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Xunit; | |||
using System; | |||
using Newtonsoft.Json; | |||
using System.Net; | |||
using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; | |||
public class CampaignScenarios | |||
: CampaignScenarioBase | |||
{ | |||
[Fact] | |||
public async Task Get_get_all_campaigns_and_response_ok_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var response = await server.CreateClient() | |||
.GetAsync(Get.Campaigns); | |||
response.EnsureSuccessStatusCode(); | |||
} | |||
} | |||
[Fact] | |||
public async Task Get_get_campaign_by_id_and_response_ok_status_code() | |||
{ | |||
var campaignId = 1; | |||
using (var server = CreateServer()) | |||
{ | |||
var response = await server.CreateClient() | |||
.GetAsync(Get.CampaignBy(campaignId)); | |||
response.EnsureSuccessStatusCode(); | |||
} | |||
} | |||
[Fact] | |||
public async Task Get_get_campaign_by_id_and_response_not_found_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var response = await server.CreateClient() | |||
.GetAsync(Get.CampaignBy(int.MaxValue)); | |||
Assert.True(response.StatusCode == HttpStatusCode.NotFound); | |||
} | |||
} | |||
[Fact] | |||
public async Task Post_add_new_campaign_and_response_ok_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var fakeCampaignDto = GetFakeCampaignDto(); | |||
var content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); | |||
var response = await server.CreateClient() | |||
.PostAsync(Post.AddNewCampaign, content); | |||
response.EnsureSuccessStatusCode(); | |||
} | |||
} | |||
[Fact] | |||
public async Task Delete_delete_campaign_and_response_not_content_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var fakeCampaignDto = GetFakeCampaignDto(); | |||
var content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); | |||
//add campaign | |||
var campaignResponse = await server.CreateClient() | |||
.PostAsync(Post.AddNewCampaign, content); | |||
if (int.TryParse(campaignResponse.Headers.Location.Segments[4], out int id)) | |||
{ | |||
var response = await server.CreateClient() | |||
.DeleteAsync(Delete.CampaignBy(id)); | |||
Assert.True(response.StatusCode == HttpStatusCode.NoContent); | |||
} | |||
campaignResponse.EnsureSuccessStatusCode(); | |||
} | |||
} | |||
[Fact] | |||
public async Task Put_update_campaign_and_response_not_content_status_code() | |||
{ | |||
using (var server = CreateServer()) | |||
{ | |||
var fakeCampaignDto = GetFakeCampaignDto(); | |||
var content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); | |||
//add campaign | |||
var campaignResponse = await server.CreateClient() | |||
.PostAsync(Post.AddNewCampaign, content); | |||
if (int.TryParse(campaignResponse.Headers.Location.Segments[4], out int id)) | |||
{ | |||
fakeCampaignDto.Description = "FakeCampaignUpdatedDescription"; | |||
content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); | |||
var response = await server.CreateClient() | |||
.PutAsync(Put.CampaignBy(id), content); | |||
Assert.True(response.StatusCode == HttpStatusCode.Created); | |||
} | |||
campaignResponse.EnsureSuccessStatusCode(); | |||
} | |||
} | |||
private static CampaignDTO GetFakeCampaignDto() | |||
{ | |||
return new CampaignDTO() | |||
{ | |||
Description = "FakeCampaignDescription", | |||
From = DateTime.Now, | |||
To = DateTime.Now.AddDays(7), | |||
Url = "http://CampaignUrl.test/fdaf91ad0cef5419719f50198", | |||
}; | |||
} | |||
} | |||
} |