Initial commit
This commit is contained in:
25
.dockerignore
Normal file
25
.dockerignore
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
223
.gitignore
vendored
Normal file
223
.gitignore
vendored
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
build/
|
||||||
|
bld/
|
||||||
|
[Oo]bj/
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
!"DEV server.pubxml"
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Elmah Error Log Files
|
||||||
|
error-*.xml
|
||||||
|
|
||||||
|
# MSBuild Community Tasks dll
|
||||||
|
!.build/MSBuild.Community.Tasks.dll
|
||||||
|
|
||||||
|
# NLog Results
|
||||||
|
logs/
|
||||||
|
|
||||||
|
*.DotSettings
|
||||||
|
|
||||||
|
#SpecFlow Autogenerated feature code behind files
|
||||||
|
*.feature.cs
|
||||||
23
Readme.md
Normal file
23
Readme.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# YABA: Yet Another Bookmark App
|
||||||
|
|
||||||
|
## Developer Guides
|
||||||
|
|
||||||
|
### Running Migrations
|
||||||
|
When running migrations, .NET seems to be ignoring dependency injection settings. In order to get around this, be sure to add the connection string to the command like. For example, when adding the migration:
|
||||||
|
|
||||||
|
```
|
||||||
|
dotnet ef migrations add InitialMigration -p YABA.Data -s YABA.API --context YABABaseContext -- {CONNECTION_STRING_HERE}
|
||||||
|
```
|
||||||
|
|
||||||
|
When removing last migration:
|
||||||
|
```
|
||||||
|
dotnet ef migrations remove InitialMigration -p YABA.Data -s YABA.API --context YABABaseContext -- {CONNECTION_STRING_HERE}
|
||||||
|
```
|
||||||
|
|
||||||
|
When applying migrations:
|
||||||
|
```
|
||||||
|
dotnet ef database update -p YABA.Data -s YABA.API -c YABABaseContext -- { CONNECTION_STRING_HERE }
|
||||||
|
```
|
||||||
|
|
||||||
|
As per the documentation [on MSDN](https://learn.microsoft.com/en-ca/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli#from-application-services):
|
||||||
|
> The -- token directs dotnet ef to treat everything that follows as an argument and not try to parse them as options. Any extra arguments not used by dotnet ef are forwarded to the app.
|
||||||
35
YABA.API/Controllers/WeatherForecastController.cs
Normal file
35
YABA.API/Controllers/WeatherForecastController.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace YABA.API.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[ApiVersion("1")]
|
||||||
|
[Authorize, Route("api/v{version:apiVersion}/[controller]")]
|
||||||
|
public class WeatherForecastController : ControllerBase
|
||||||
|
{
|
||||||
|
private static readonly string[] Summaries = new[]
|
||||||
|
{
|
||||||
|
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly ILogger<WeatherForecastController> _logger;
|
||||||
|
|
||||||
|
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet(Name = "GetWeatherForecast")]
|
||||||
|
public IEnumerable<WeatherForecast> Get()
|
||||||
|
{
|
||||||
|
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||||
|
{
|
||||||
|
Date = DateTime.Now.AddDays(index),
|
||||||
|
TemperatureC = Random.Shared.Next(-20, 55),
|
||||||
|
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||||
|
})
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
YABA.API/Dockerfile
Normal file
22
YABA.API/Dockerfile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 80
|
||||||
|
EXPOSE 443
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["YABA.API/YABA.API.csproj", "YABA.API/"]
|
||||||
|
RUN dotnet restore "YABA.API/YABA.API.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/YABA.API"
|
||||||
|
RUN dotnet build "YABA.API.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "YABA.API.csproj" -c Release -o /app/publish
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "YABA.API.dll"]
|
||||||
91
YABA.API/Program.cs
Normal file
91
YABA.API/Program.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Versioning;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using YABA.API.Settings;
|
||||||
|
using YABA.API.Settings.Swashbuckle;
|
||||||
|
using YABA.Data.Configuration;
|
||||||
|
using YABA.Data.Context;
|
||||||
|
using YABA.Service.Configuration;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
var configuration = builder.Configuration;
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
var auth0Section = configuration.GetSection("Authentication").GetSection("Auth0");
|
||||||
|
var auth0Settings = auth0Section.Get<Auth0Settings>();
|
||||||
|
var domain = $"https://{auth0Settings.Domain}/";
|
||||||
|
|
||||||
|
builder.Services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
}).AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.Authority = domain;
|
||||||
|
options.Audience = auth0Settings.Identifier;
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
NameClaimType = ClaimTypes.NameIdentifier
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddApiVersioning(setup =>
|
||||||
|
{
|
||||||
|
setup.DefaultApiVersion = new ApiVersion(1, 0);
|
||||||
|
setup.AssumeDefaultVersionWhenUnspecified = true;
|
||||||
|
setup.ReportApiVersions = true;
|
||||||
|
setup.ApiVersionReader = new UrlSegmentApiVersionReader();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
builder.Services.AddServiceProjectDependencyInjectionConfiguration(configuration);
|
||||||
|
builder.Services.AddDataProjectDependencyInjectionConfiguration(configuration);
|
||||||
|
builder.Services.AddControllers().AddNewtonsoftJson();
|
||||||
|
|
||||||
|
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen(
|
||||||
|
c =>
|
||||||
|
{
|
||||||
|
c.SwaggerDoc(
|
||||||
|
"v1",
|
||||||
|
new OpenApiInfo
|
||||||
|
{
|
||||||
|
Title = "YABA.API",
|
||||||
|
Version = "v1"
|
||||||
|
});
|
||||||
|
c.OperationFilter<RemoveVersionParameterFilter>();
|
||||||
|
c.DocumentFilter<ReplaceVersionWithExactValueInPathFilter>();
|
||||||
|
c.ResolveConflictingActions(apiDescription => apiDescription.First());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Run database migrations
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var yabaDbContext = scope.ServiceProvider.GetRequiredService<YABAReadWriteContext>();
|
||||||
|
yabaDbContext.Database.Migrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
38
YABA.API/Properties/launchSettings.json
Normal file
38
YABA.API/Properties/launchSettings.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:7922",
|
||||||
|
"sslPort": 44310
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"YABA.API": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:7032;http://localhost:5032",
|
||||||
|
"dotnetRunMessages": true
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Docker": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||||
|
"publishAllPorts": true,
|
||||||
|
"useSSL": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
YABA.API/Settings/Auth0Settings.cs
Normal file
10
YABA.API/Settings/Auth0Settings.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace YABA.API.Settings
|
||||||
|
{
|
||||||
|
public class Auth0Settings
|
||||||
|
{
|
||||||
|
public string Domain { get; set; }
|
||||||
|
public string ClientSecret { get; set; }
|
||||||
|
public string ClientId { get; set; }
|
||||||
|
public string Identifier { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace YABA.API.Settings.Swashbuckle
|
||||||
|
{
|
||||||
|
public class RemoveVersionParameterFilter : IOperationFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||||
|
{
|
||||||
|
var versionParameter = operation.Parameters.Single(p => p.Name == "version");
|
||||||
|
operation.Parameters.Remove(versionParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace YABA.API.Settings.Swashbuckle
|
||||||
|
{
|
||||||
|
public class ReplaceVersionWithExactValueInPathFilter : IDocumentFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||||
|
{
|
||||||
|
var paths = new OpenApiPaths();
|
||||||
|
foreach (var path in swaggerDoc.Paths)
|
||||||
|
{
|
||||||
|
paths.Add(path.Key.Replace("v{version}", swaggerDoc.Info.Version), path.Value);
|
||||||
|
}
|
||||||
|
swaggerDoc.Paths = paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
YABA.API/WeatherForecast.cs
Normal file
13
YABA.API/WeatherForecast.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace YABA.API
|
||||||
|
{
|
||||||
|
public class WeatherForecast
|
||||||
|
{
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
|
||||||
|
public int TemperatureC { get; set; }
|
||||||
|
|
||||||
|
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||||
|
|
||||||
|
public string? Summary { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
30
YABA.API/YABA.API.csproj
Normal file
30
YABA.API/YABA.API.csproj
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>726eb626-1514-45b8-8521-cd7353303edb</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AutoMapper" Version="12.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.13" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.17">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\YABA.Data\YABA.Data.csproj" />
|
||||||
|
<ProjectReference Include="..\YABA.Service\YABA.Service.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
8
YABA.API/appsettings.Development.json
Normal file
8
YABA.API/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
YABA.API/appsettings.json
Normal file
10
YABA.API/appsettings.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
41
YABA.Data/Configuration/DependencyInjectionConfiguration.cs
Normal file
41
YABA.Data/Configuration/DependencyInjectionConfiguration.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using YABA.Data.Context;
|
||||||
|
|
||||||
|
namespace YABA.Data.Configuration
|
||||||
|
{
|
||||||
|
public static class DependencyInjectionConfiguration
|
||||||
|
{
|
||||||
|
public static void AddDataProjectDependencyInjectionConfiguration(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddScoped(x =>
|
||||||
|
{
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<YABABaseContext>();
|
||||||
|
optionsBuilder.UseNpgsql(configuration.GetConnectionString("YABAReadOnlyDbConnectionString")).UseSnakeCaseNamingConvention();
|
||||||
|
return new YABAReadOnlyContext(optionsBuilder.Options);
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddScoped(x => {
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<YABABaseContext>();
|
||||||
|
optionsBuilder.UseNpgsql(configuration.GetConnectionString("YABAReadWriteDbConnectionString")).UseSnakeCaseNamingConvention();
|
||||||
|
return new YABAReadWriteContext(optionsBuilder.Options);
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddDbContext<YABABaseContext>(options => options
|
||||||
|
.UseNpgsql(configuration.GetConnectionString("YABAReadWriteDbConnectionString"))
|
||||||
|
.UseSnakeCaseNamingConvention()
|
||||||
|
.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll));
|
||||||
|
|
||||||
|
services.AddDbContext<YABAReadWriteContext>(options => options
|
||||||
|
.UseNpgsql(configuration.GetConnectionString("YABAReadWriteDbConnectionString"))
|
||||||
|
.UseSnakeCaseNamingConvention()
|
||||||
|
.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll));
|
||||||
|
|
||||||
|
services.AddDbContext<YABAReadOnlyContext>(options => options
|
||||||
|
.UseNpgsql(configuration.GetConnectionString("YABAReadOnlyDbConnectionString"))
|
||||||
|
.UseSnakeCaseNamingConvention()
|
||||||
|
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
YABA.Data/Context/YABABaseContext.cs
Normal file
59
YABA.Data/Context/YABABaseContext.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Query;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
using YABA.Models;
|
||||||
|
using YABA.Models.Interfaces;
|
||||||
|
|
||||||
|
namespace YABA.Data.Context
|
||||||
|
{
|
||||||
|
public class YABABaseContext : DbContext
|
||||||
|
{
|
||||||
|
public YABABaseContext(DbContextOptions<YABABaseContext> options) : base(options) { }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
// Add lookup backed data here
|
||||||
|
// SAMPLE
|
||||||
|
// var lookupBackedData = Enum.GetValues(typeof(LookupEnum)).Cast<LookupEnum>();
|
||||||
|
// modelBuilder.Entity<LookupModel>().HasData(lookupBackedData.Select(x => new LookupModel(x)));
|
||||||
|
|
||||||
|
|
||||||
|
modelBuilder.Model.GetEntityTypes()
|
||||||
|
.Where(entityType => typeof(ISoftDeletable).IsAssignableFrom(entityType.ClrType))
|
||||||
|
.ToList()
|
||||||
|
.ForEach(entityType =>
|
||||||
|
{
|
||||||
|
modelBuilder.Entity(entityType.ClrType)
|
||||||
|
.HasQueryFilter(ConvertFilterExpression<ISoftDeletable>(e => !e.IsDeleted, entityType.ClrType));
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<BookmarkTag>()
|
||||||
|
.HasIndex(x => new { x.BookmarkId, x.TagId })
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
modelBuilder.Entity<User>()
|
||||||
|
.HasIndex(x => x.Auth0Id)
|
||||||
|
.IsUnique();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<Bookmark> Bookmarks { get; set; }
|
||||||
|
public DbSet<Tag> Tags { get; set; }
|
||||||
|
public DbSet<BookmarkTag> BookmarkTags { get; set; }
|
||||||
|
public DbSet<User> Users { get; set; }
|
||||||
|
|
||||||
|
private static LambdaExpression ConvertFilterExpression<TInterface>(
|
||||||
|
Expression<Func<TInterface, bool>> filterExpression,
|
||||||
|
Type entityType)
|
||||||
|
{
|
||||||
|
// SOURCE: https://stackoverflow.com/questions/47673524/ef-core-soft-delete-with-shadow-properties-and-query-filters/48744644#48744644
|
||||||
|
var newParam = Expression.Parameter(entityType);
|
||||||
|
var newBody = ReplacingExpressionVisitor.Replace(filterExpression.Parameters.Single(), newParam, filterExpression.Body);
|
||||||
|
|
||||||
|
return Expression.Lambda(newBody, newParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
YABA.Data/Context/YABABaseContextFactory.cs
Normal file
18
YABA.Data/Context/YABABaseContextFactory.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
|
||||||
|
namespace YABA.Data.Context
|
||||||
|
{
|
||||||
|
public class YABABaseContextFactory : IDesignTimeDbContextFactory<YABABaseContext>
|
||||||
|
{
|
||||||
|
public YABABaseContext CreateDbContext(string[] args)
|
||||||
|
{
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<YABABaseContext>();
|
||||||
|
optionsBuilder.UseNpgsql(args[0])
|
||||||
|
.UseSnakeCaseNamingConvention()
|
||||||
|
.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll);
|
||||||
|
return new YABABaseContext(optionsBuilder.Options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
YABA.Data/Context/YABAReadOnlyContext.cs
Normal file
12
YABA.Data/Context/YABAReadOnlyContext.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace YABA.Data.Context
|
||||||
|
{
|
||||||
|
public class YABAReadOnlyContext : YABABaseContext
|
||||||
|
{
|
||||||
|
public YABAReadOnlyContext(DbContextOptions<YABABaseContext> options) : base(options)
|
||||||
|
{
|
||||||
|
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
YABA.Data/Context/YABAReadWriteContext.cs
Normal file
55
YABA.Data/Context/YABAReadWriteContext.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using YABA.Models.Interfaces;
|
||||||
|
|
||||||
|
namespace YABA.Data.Context
|
||||||
|
{
|
||||||
|
public class YABAReadWriteContext : YABABaseContext
|
||||||
|
{
|
||||||
|
public YABAReadWriteContext(DbContextOptions<YABABaseContext> options) : base(options)
|
||||||
|
{
|
||||||
|
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int SaveChanges()
|
||||||
|
{
|
||||||
|
var dateCreatedTrackableEntries = ChangeTracker
|
||||||
|
.Entries()
|
||||||
|
.Where(e => e.Entity is IDateCreatedTrackable && e.State == EntityState.Added);
|
||||||
|
|
||||||
|
foreach (var entry in dateCreatedTrackableEntries)
|
||||||
|
((IDateCreatedTrackable)entry.Entity).CreatedOn = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
var dateModifiedTrackableItems = ChangeTracker
|
||||||
|
.Entries()
|
||||||
|
.Where(e => e.Entity is IDateModifiedTrackable && (e.State == EntityState.Modified || e.State == EntityState.Added));
|
||||||
|
|
||||||
|
foreach (var entry in dateModifiedTrackableItems)
|
||||||
|
((IDateModifiedTrackable)entry.Entity).LastModified = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
return base.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var dateCreatedTrackableEntries = ChangeTracker
|
||||||
|
.Entries()
|
||||||
|
.Where(e => e.Entity is IDateCreatedTrackable && e.State == EntityState.Added);
|
||||||
|
|
||||||
|
foreach (var entry in dateCreatedTrackableEntries)
|
||||||
|
((IDateCreatedTrackable)entry.Entity).CreatedOn = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
var dateModifiedTrackableItems = ChangeTracker
|
||||||
|
.Entries()
|
||||||
|
.Where(e => e.Entity is IDateModifiedTrackable && (e.State == EntityState.Modified || e.State == EntityState.Added));
|
||||||
|
|
||||||
|
foreach (var entry in dateModifiedTrackableItems)
|
||||||
|
((IDateModifiedTrackable)entry.Entity).LastModified = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
return base.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
YABA.Data/Extensions/GenericDbSetExtensions.cs
Normal file
35
YABA.Data/Extensions/GenericDbSetExtensions.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using YABA.Models.Interfaces;
|
||||||
|
|
||||||
|
namespace YABA.Data.Extensions
|
||||||
|
{
|
||||||
|
public static class GenericDbSetExtensions
|
||||||
|
{
|
||||||
|
public static void Upsert<T>(this DbSet<T> dbSet, T entity) where T : class, IIdentifiable
|
||||||
|
{
|
||||||
|
var entityInList = new List<T>() { entity };
|
||||||
|
dbSet.UpsertRange(entityInList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpsertRange<T>(this DbSet<T> dbSet, IEnumerable<T> entities) where T : class, IIdentifiable
|
||||||
|
{
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
var entityExists = dbSet.Any(x => x.Id == entity.Id);
|
||||||
|
|
||||||
|
if (entityExists)
|
||||||
|
{
|
||||||
|
EntityEntry<T> attachedEntity = dbSet.Attach(entity);
|
||||||
|
attachedEntity.State = EntityState.Modified;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dbSet.Add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
221
YABA.Data/Migrations/20230125055348_InitialMigration.Designer.cs
generated
Normal file
221
YABA.Data/Migrations/20230125055348_InitialMigration.Designer.cs
generated
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using YABA.Data.Context;
|
||||||
|
|
||||||
|
namespace YABA.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(YABABaseContext))]
|
||||||
|
[Migration("20230125055348_InitialMigration")]
|
||||||
|
partial class InitialMigration
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||||
|
.HasAnnotation("ProductVersion", "5.0.17")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.Bookmark", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedOn")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_on");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("description");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_deleted");
|
||||||
|
|
||||||
|
b.Property<bool>("IsHidden")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_hidden");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("LastModified")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last_modified");
|
||||||
|
|
||||||
|
b.Property<string>("Note")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("note");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("title");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user_id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_bookmarks");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("ix_bookmarks_user_id");
|
||||||
|
|
||||||
|
b.ToTable("bookmarks");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.BookmarkTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
b.Property<int>("BookmarkId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("bookmark_id");
|
||||||
|
|
||||||
|
b.Property<int>("TagId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("tag_id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_bookmark_tags");
|
||||||
|
|
||||||
|
b.HasIndex("TagId")
|
||||||
|
.HasDatabaseName("ix_bookmark_tags_tag_id");
|
||||||
|
|
||||||
|
b.HasIndex("BookmarkId", "TagId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_bookmark_tags_bookmark_id_tag_id");
|
||||||
|
|
||||||
|
b.ToTable("bookmark_tags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.Tag", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_deleted");
|
||||||
|
|
||||||
|
b.Property<bool>("IsHidden")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_hidden");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user_id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_tags");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("ix_tags_user_id");
|
||||||
|
|
||||||
|
b.ToTable("tags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
b.Property<string>("Auth0Id")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("auth0id");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedOn")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_on");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_deleted");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("LastModified")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last_modified");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_users");
|
||||||
|
|
||||||
|
b.HasIndex("Auth0Id")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_users_auth0id");
|
||||||
|
|
||||||
|
b.ToTable("users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.Bookmark", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("YABA.Models.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.HasConstraintName("fk_bookmarks_users_user_id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.BookmarkTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("YABA.Models.Bookmark", "Bookmark")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("BookmarkId")
|
||||||
|
.HasConstraintName("fk_bookmark_tags_bookmarks_bookmark_id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("YABA.Models.Tag", "Tag")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.HasConstraintName("fk_bookmark_tags_tags_tag_id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Bookmark");
|
||||||
|
|
||||||
|
b.Navigation("Tag");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.Tag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("YABA.Models.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.HasConstraintName("fk_tags_users_user_id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
144
YABA.Data/Migrations/20230125055348_InitialMigration.cs
Normal file
144
YABA.Data/Migrations/20230125055348_InitialMigration.cs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
namespace YABA.Data.Migrations
|
||||||
|
{
|
||||||
|
public partial class InitialMigration : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "users",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
is_deleted = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
created_on = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
last_modified = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
auth0id = table.Column<string>(type: "text", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_users", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "bookmarks",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
is_deleted = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
created_on = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
last_modified = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
title = table.Column<string>(type: "text", nullable: false),
|
||||||
|
description = table.Column<string>(type: "text", nullable: false),
|
||||||
|
note = table.Column<string>(type: "text", nullable: false),
|
||||||
|
is_hidden = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
user_id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_bookmarks", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_bookmarks_users_user_id",
|
||||||
|
column: x => x.user_id,
|
||||||
|
principalTable: "users",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "tags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
is_deleted = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
name = table.Column<string>(type: "text", nullable: false),
|
||||||
|
is_hidden = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
user_id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_tags", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_tags_users_user_id",
|
||||||
|
column: x => x.user_id,
|
||||||
|
principalTable: "users",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "bookmark_tags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
bookmark_id = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
tag_id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_bookmark_tags", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_bookmark_tags_bookmarks_bookmark_id",
|
||||||
|
column: x => x.bookmark_id,
|
||||||
|
principalTable: "bookmarks",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_bookmark_tags_tags_tag_id",
|
||||||
|
column: x => x.tag_id,
|
||||||
|
principalTable: "tags",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_bookmark_tags_bookmark_id_tag_id",
|
||||||
|
table: "bookmark_tags",
|
||||||
|
columns: new[] { "bookmark_id", "tag_id" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_bookmark_tags_tag_id",
|
||||||
|
table: "bookmark_tags",
|
||||||
|
column: "tag_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_bookmarks_user_id",
|
||||||
|
table: "bookmarks",
|
||||||
|
column: "user_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_tags_user_id",
|
||||||
|
table: "tags",
|
||||||
|
column: "user_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_users_auth0id",
|
||||||
|
table: "users",
|
||||||
|
column: "auth0id",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "bookmark_tags");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "bookmarks");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "tags");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
219
YABA.Data/Migrations/YABABaseContextModelSnapshot.cs
Normal file
219
YABA.Data/Migrations/YABABaseContextModelSnapshot.cs
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using YABA.Data.Context;
|
||||||
|
|
||||||
|
namespace YABA.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(YABABaseContext))]
|
||||||
|
partial class YABABaseContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63)
|
||||||
|
.HasAnnotation("ProductVersion", "5.0.17")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.Bookmark", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedOn")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_on");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("description");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_deleted");
|
||||||
|
|
||||||
|
b.Property<bool>("IsHidden")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_hidden");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("LastModified")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last_modified");
|
||||||
|
|
||||||
|
b.Property<string>("Note")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("note");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("title");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user_id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_bookmarks");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("ix_bookmarks_user_id");
|
||||||
|
|
||||||
|
b.ToTable("bookmarks");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.BookmarkTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
b.Property<int>("BookmarkId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("bookmark_id");
|
||||||
|
|
||||||
|
b.Property<int>("TagId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("tag_id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_bookmark_tags");
|
||||||
|
|
||||||
|
b.HasIndex("TagId")
|
||||||
|
.HasDatabaseName("ix_bookmark_tags_tag_id");
|
||||||
|
|
||||||
|
b.HasIndex("BookmarkId", "TagId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_bookmark_tags_bookmark_id_tag_id");
|
||||||
|
|
||||||
|
b.ToTable("bookmark_tags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.Tag", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_deleted");
|
||||||
|
|
||||||
|
b.Property<bool>("IsHidden")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_hidden");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user_id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_tags");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("ix_tags_user_id");
|
||||||
|
|
||||||
|
b.ToTable("tags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||||
|
|
||||||
|
b.Property<string>("Auth0Id")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("auth0id");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedOn")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_on");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is_deleted");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("LastModified")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last_modified");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_users");
|
||||||
|
|
||||||
|
b.HasIndex("Auth0Id")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_users_auth0id");
|
||||||
|
|
||||||
|
b.ToTable("users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.Bookmark", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("YABA.Models.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.HasConstraintName("fk_bookmarks_users_user_id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.BookmarkTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("YABA.Models.Bookmark", "Bookmark")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("BookmarkId")
|
||||||
|
.HasConstraintName("fk_bookmark_tags_bookmarks_bookmark_id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("YABA.Models.Tag", "Tag")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagId")
|
||||||
|
.HasConstraintName("fk_bookmark_tags_tags_tag_id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Bookmark");
|
||||||
|
|
||||||
|
b.Navigation("Tag");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YABA.Models.Tag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("YABA.Models.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.HasConstraintName("fk_tags_users_user_id")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
YABA.Data/YABA.Data.csproj
Normal file
24
YABA.Data/YABA.Data.csproj
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="EFCore.NamingConventions" Version="5.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.17" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.17">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.10" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\YABA.Models\YABA.Models.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
24
YABA.Models/Bookmark.cs
Normal file
24
YABA.Models/Bookmark.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using YABA.Models.Interfaces;
|
||||||
|
|
||||||
|
namespace YABA.Models
|
||||||
|
{
|
||||||
|
public class Bookmark : IIdentifiable, ISoftDeletable, IDateCreatedTrackable, IDateModifiedTrackable
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
public DateTimeOffset CreatedOn { get; set; }
|
||||||
|
public DateTimeOffset LastModified { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Note { get; set; }
|
||||||
|
public bool IsHidden { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(User))]
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public virtual User User { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
21
YABA.Models/BookmarkTag.cs
Normal file
21
YABA.Models/BookmarkTag.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using YABA.Models.Interfaces;
|
||||||
|
|
||||||
|
namespace YABA.Models
|
||||||
|
{
|
||||||
|
public class BookmarkTag : IIdentifiable
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Bookmark))]
|
||||||
|
public int BookmarkId { get; set; }
|
||||||
|
public virtual Bookmark Bookmark { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(Tag))]
|
||||||
|
public int TagId { get; set; }
|
||||||
|
public virtual Tag Tag { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
9
YABA.Models/Interfaces/IDateCreatedTrackable.cs
Normal file
9
YABA.Models/Interfaces/IDateCreatedTrackable.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace YABA.Models.Interfaces
|
||||||
|
{
|
||||||
|
public interface IDateCreatedTrackable
|
||||||
|
{
|
||||||
|
public DateTimeOffset CreatedOn { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
9
YABA.Models/Interfaces/IDateModifiedTrackable.cs
Normal file
9
YABA.Models/Interfaces/IDateModifiedTrackable.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace YABA.Models.Interfaces
|
||||||
|
{
|
||||||
|
public interface IDateModifiedTrackable
|
||||||
|
{
|
||||||
|
public DateTimeOffset LastModified { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
8
YABA.Models/Interfaces/IIdentifiable.cs
Normal file
8
YABA.Models/Interfaces/IIdentifiable.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
namespace YABA.Models.Interfaces
|
||||||
|
{
|
||||||
|
public interface IIdentifiable
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
8
YABA.Models/Interfaces/ISoftDeletable.cs
Normal file
8
YABA.Models/Interfaces/ISoftDeletable.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
namespace YABA.Models.Interfaces
|
||||||
|
{
|
||||||
|
public interface ISoftDeletable
|
||||||
|
{
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
YABA.Models/Tag.cs
Normal file
19
YABA.Models/Tag.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using YABA.Models.Interfaces;
|
||||||
|
|
||||||
|
namespace YABA.Models
|
||||||
|
{
|
||||||
|
public class Tag : IIdentifiable, ISoftDeletable
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public bool IsHidden { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey(nameof(User))]
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public virtual User User { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
YABA.Models/User.cs
Normal file
17
YABA.Models/User.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using YABA.Models.Interfaces;
|
||||||
|
|
||||||
|
namespace YABA.Models
|
||||||
|
{
|
||||||
|
public class User : IIdentifiable, ISoftDeletable, IDateCreatedTrackable, IDateModifiedTrackable
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
public DateTimeOffset CreatedOn { get; set; }
|
||||||
|
public DateTimeOffset LastModified { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Auth0Id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
YABA.Models/YABA.Models.csproj
Normal file
12
YABA.Models/YABA.Models.csproj
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
9
YABA.Service/Class1.cs
Normal file
9
YABA.Service/Class1.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace YABA.Service
|
||||||
|
{
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace YABA.Service.Configuration
|
||||||
|
{
|
||||||
|
public static class DependencyInjectionConfiguration
|
||||||
|
{
|
||||||
|
public static void AddServiceProjectDependencyInjectionConfiguration(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
YABA.Service/YABA.Service.csproj
Normal file
13
YABA.Service/YABA.Service.csproj
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
43
YABA.sln
Normal file
43
YABA.sln
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.2.32505.173
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YABA.API", "YABA.API\YABA.API.csproj", "{97FC2A4F-B663-43B0-987D-DC3EA7ADD66C}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YABA.Models", "YABA.Models\YABA.Models.csproj", "{DDA30925-F844-426B-8B90-3E6E258BD407}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YABA.Data", "YABA.Data\YABA.Data.csproj", "{461E9D5A-3C06-4CCB-A466-76BBB9BB7BF5}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YABA.Service", "YABA.Service\YABA.Service.csproj", "{0098D0A8-0273-46F1-9FE7-B0409442251A}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{97FC2A4F-B663-43B0-987D-DC3EA7ADD66C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{97FC2A4F-B663-43B0-987D-DC3EA7ADD66C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{97FC2A4F-B663-43B0-987D-DC3EA7ADD66C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{97FC2A4F-B663-43B0-987D-DC3EA7ADD66C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DDA30925-F844-426B-8B90-3E6E258BD407}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DDA30925-F844-426B-8B90-3E6E258BD407}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DDA30925-F844-426B-8B90-3E6E258BD407}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DDA30925-F844-426B-8B90-3E6E258BD407}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{461E9D5A-3C06-4CCB-A466-76BBB9BB7BF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{461E9D5A-3C06-4CCB-A466-76BBB9BB7BF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{461E9D5A-3C06-4CCB-A466-76BBB9BB7BF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{461E9D5A-3C06-4CCB-A466-76BBB9BB7BF5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0098D0A8-0273-46F1-9FE7-B0409442251A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0098D0A8-0273-46F1-9FE7-B0409442251A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0098D0A8-0273-46F1-9FE7-B0409442251A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0098D0A8-0273-46F1-9FE7-B0409442251A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {8B677B7D-5CF6-460E-B4F6-7AF3BD655B12}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
Reference in New Issue
Block a user