feat: continued wip on supporting transactions
This commit is contained in:
Vendored
+4
-1
@@ -12,5 +12,8 @@
|
|||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"cSpell.words": ["Encryptor"],
|
"cSpell.words": ["Encryptor"],
|
||||||
"python-envs.defaultEnvManager": "ms-python.python:system"
|
"python-envs.defaultEnvManager": "ms-python.python:system",
|
||||||
|
"search.exclude": {
|
||||||
|
"**/Migrations": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using Going.Plaid.Converters;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using Going.Plaid;
|
|
||||||
using Going.Plaid.Entity;
|
|
||||||
|
|
||||||
namespace FiscalOS.API.Transactions.FireWebhook;
|
namespace FiscalOS.API.Transactions.FireWebhook;
|
||||||
|
|
||||||
internal static class Endpoint
|
internal static class Endpoint
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ global using FiscalOS.Core.Authentication;
|
|||||||
global using FiscalOS.Core.Identity;
|
global using FiscalOS.Core.Identity;
|
||||||
global using FiscalOS.Core.Queuing;
|
global using FiscalOS.Core.Queuing;
|
||||||
global using FiscalOS.Core.Security;
|
global using FiscalOS.Core.Security;
|
||||||
global using FiscalOS.Core.Transactions;
|
|
||||||
global using FiscalOS.Infra.Accounts.Plaid;
|
global using FiscalOS.Infra.Accounts.Plaid;
|
||||||
global using FiscalOS.Infra.Authentication;
|
global using FiscalOS.Infra.Authentication;
|
||||||
global using FiscalOS.Infra.Data;
|
global using FiscalOS.Infra.Data;
|
||||||
@@ -30,6 +29,8 @@ global using FiscalOS.Infra.Transactions.Plaid;
|
|||||||
global using FiscalOS.ServiceDefaults;
|
global using FiscalOS.ServiceDefaults;
|
||||||
|
|
||||||
global using Going.Plaid;
|
global using Going.Plaid;
|
||||||
|
global using Going.Plaid.Converters;
|
||||||
|
global using Going.Plaid.Entity;
|
||||||
global using Going.Plaid.Webhook;
|
global using Going.Plaid.Webhook;
|
||||||
|
|
||||||
global using Microsoft.AspNetCore.Mvc;
|
global using Microsoft.AspNetCore.Mvc;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using FiscalOS.Core.Transactions;
|
|
||||||
|
|
||||||
namespace FiscalOS.Core.Accounts;
|
namespace FiscalOS.Core.Accounts;
|
||||||
|
|
||||||
public sealed class Account : Entity
|
public sealed class Account : Entity
|
||||||
@@ -16,11 +14,17 @@ public sealed class Account : Entity
|
|||||||
private readonly List<Transaction> _transactions = [];
|
private readonly List<Transaction> _transactions = [];
|
||||||
public IEnumerable<Transaction> Transactions => _transactions;
|
public IEnumerable<Transaction> Transactions => _transactions;
|
||||||
|
|
||||||
public static Account From(Guid institutionId, string name, AccountMetadata accountMetadata)
|
private Account()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Account From(
|
||||||
|
string name,
|
||||||
|
AccountMetadata accountMetadata
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
InstitutionId = institutionId,
|
|
||||||
Name = name,
|
Name = name,
|
||||||
Metadata = accountMetadata,
|
Metadata = accountMetadata,
|
||||||
};
|
};
|
||||||
@@ -28,14 +32,14 @@ public sealed class Account : Entity
|
|||||||
|
|
||||||
public void AddBalance(Balance balance)
|
public void AddBalance(Balance balance)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(balance);
|
ArgumentNullException.ThrowIfNull(balance, nameof(balance));
|
||||||
|
|
||||||
_balances.Add(balance);
|
_balances.Add(balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTransaction(Transaction transaction)
|
public void AddTransaction(Transaction transaction)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(transaction);
|
ArgumentNullException.ThrowIfNull(transaction, nameof(transaction));
|
||||||
|
|
||||||
_transactions.Add(transaction);
|
_transactions.Add(transaction);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ public sealed class Balance : Entity
|
|||||||
string currencyCode
|
string currencyCode
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(currencyCode, nameof(currencyCode));
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
Current = current,
|
Current = current,
|
||||||
|
|||||||
@@ -25,4 +25,11 @@ public sealed class Institution : Entity
|
|||||||
Metadata = metadata
|
Metadata = metadata
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddAccount(Account account)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(account, nameof(account));
|
||||||
|
|
||||||
|
_accounts.Add(account);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,9 @@ public sealed class User : Entity
|
|||||||
private readonly List<Account> _accounts = [];
|
private readonly List<Account> _accounts = [];
|
||||||
public IEnumerable<Account> Accounts => _accounts;
|
public IEnumerable<Account> Accounts => _accounts;
|
||||||
|
|
||||||
|
private readonly List<Transaction> _transactions = [];
|
||||||
|
public IEnumerable<Transaction> Transactions => _transactions;
|
||||||
|
|
||||||
private User()
|
private User()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -60,4 +63,11 @@ public sealed class User : Entity
|
|||||||
|
|
||||||
_accounts.Add(account);
|
_accounts.Add(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddTransaction(Transaction transaction)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(transaction, nameof(transaction));
|
||||||
|
|
||||||
|
_transactions.Add(transaction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,17 @@ public sealed class Transaction : Entity
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Transaction From(
|
||||||
|
string merchantName,
|
||||||
|
string description,
|
||||||
|
decimal? amount,
|
||||||
|
DateTimeOffset? date,
|
||||||
|
TransactionMetadata metadata
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return From(Guid.Empty, Guid.Empty, merchantName, description, amount, date, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
public static Transaction From(
|
public static Transaction From(
|
||||||
Guid userId,
|
Guid userId,
|
||||||
Guid accountId,
|
Guid accountId,
|
||||||
@@ -29,8 +40,8 @@ public sealed class Transaction : Entity
|
|||||||
TransactionMetadata metadata
|
TransactionMetadata metadata
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(merchantName);
|
ArgumentNullException.ThrowIfNull(merchantName, nameof(merchantName));
|
||||||
ArgumentNullException.ThrowIfNull(description);
|
ArgumentNullException.ThrowIfNull(description, nameof(description));
|
||||||
|
|
||||||
if (amount.HasValue is false)
|
if (amount.HasValue is false)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
global using FiscalOS.Core.Accounts;
|
global using FiscalOS.Core.Accounts;
|
||||||
global using FiscalOS.Core.Data;
|
global using FiscalOS.Core.Data;
|
||||||
global using FiscalOS.Core.Identity;
|
global using FiscalOS.Core.Identity;
|
||||||
global using FiscalOS.Core.Security;
|
global using FiscalOS.Core.Security;
|
||||||
|
global using FiscalOS.Core.Transactions;
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
|
|
||||||
using FiscalOS.Infra.Common;
|
|
||||||
|
|
||||||
namespace FiscalOS.Infra.Accounts.Plaid;
|
namespace FiscalOS.Infra.Accounts.Plaid;
|
||||||
|
|
||||||
public sealed class PlaidAccountMetadata : AccountMetadata
|
public sealed class PlaidAccountMetadata : AccountMetadata
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using FiscalOS.Infra.Common;
|
|
||||||
|
|
||||||
namespace FiscalOS.Infra.Accounts.Plaid;
|
namespace FiscalOS.Infra.Accounts.Plaid;
|
||||||
|
|
||||||
public sealed class PlaidInstitutionMetadata : InstitutionMetadata
|
public sealed class PlaidInstitutionMetadata : InstitutionMetadata
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using FiscalOS.Core.Transactions;
|
|
||||||
using FiscalOS.Infra.Transactions.Plaid;
|
|
||||||
|
|
||||||
using Account = FiscalOS.Core.Accounts.Account;
|
using Account = FiscalOS.Core.Accounts.Account;
|
||||||
using Institution = FiscalOS.Core.Accounts.Institution;
|
using Institution = FiscalOS.Core.Accounts.Institution;
|
||||||
using Transaction = FiscalOS.Core.Transactions.Transaction;
|
using Transaction = FiscalOS.Core.Transactions.Transaction;
|
||||||
@@ -167,14 +164,14 @@ public sealed class AppDbContext(
|
|||||||
modelBuilder.Entity<Transaction>(static eb =>
|
modelBuilder.Entity<Transaction>(static eb =>
|
||||||
{
|
{
|
||||||
eb.HasOne(static t => t.User)
|
eb.HasOne(static t => t.User)
|
||||||
.WithMany()
|
.WithMany(static u => u.Transactions)
|
||||||
.HasForeignKey(static t => t.UserId)
|
.HasForeignKey(static t => t.UserId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
eb.HasOne(static t => t.Account)
|
eb.HasOne(static t => t.Account)
|
||||||
.WithMany()
|
.WithMany(static a => a.Transactions)
|
||||||
.HasForeignKey(static t => t.AccountId)
|
.HasForeignKey(static t => t.AccountId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
eb.Property(static t => t.MerchantName);
|
eb.Property(static t => t.MerchantName);
|
||||||
eb.Property(static t => t.Description);
|
eb.Property(static t => t.Description);
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
namespace FiscalOS.Infra.DependencyInjection;
|
namespace FiscalOS.Infra.DependencyInjection;
|
||||||
|
|
||||||
using FiscalOS.Core.Queuing;
|
|
||||||
using FiscalOS.Infra.Accounts.Plaid;
|
|
||||||
using FiscalOS.Infra.Queuing;
|
|
||||||
using FiscalOS.Infra.Transactions.Plaid;
|
|
||||||
|
|
||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services)
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
Generated
+518
@@ -0,0 +1,518 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FiscalOS.Infra.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FiscalOS.Infra.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20260303124406_AddTransactionsNavigationPropertyToUser")]
|
||||||
|
partial class AddTransactionsNavigationPropertyToUser
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("InstitutionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("InstitutionId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Account");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.AccountMetadata", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(21)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("AccountMetadata");
|
||||||
|
|
||||||
|
b.HasDiscriminator<string>("Type").HasValue("AccountMetadata");
|
||||||
|
|
||||||
|
b.UseTphMappingStrategy();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Balance", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<decimal>("Available")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CurrencyCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<decimal>("Current")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId");
|
||||||
|
|
||||||
|
b.ToTable("Balance");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Institutions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.InstitutionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("InstitutionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(21)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("InstitutionId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("InstitutionMetadata");
|
||||||
|
|
||||||
|
b.HasDiscriminator<string>("Type").HasValue("InstitutionMetadata");
|
||||||
|
|
||||||
|
b.UseTphMappingStrategy();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Identity.RefreshToken", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("ExpiresAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Revoked")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Token")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("RefreshTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Identity.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EncryptedDataKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EncryptionKeyId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HashedPassword")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Username")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AccountId1")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Date")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("MerchantName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("UserId1")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId1");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId1");
|
||||||
|
|
||||||
|
b.ToTable("Transactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.TransactionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("TransactionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(5)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TransactionId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("TransactionMetadata");
|
||||||
|
|
||||||
|
b.HasDiscriminator<string>("Type").HasValue("Plaid");
|
||||||
|
|
||||||
|
b.UseTphMappingStrategy();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Infra.Accounts.Plaid.PlaidAccountMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("FiscalOS.Core.Accounts.AccountMetadata");
|
||||||
|
|
||||||
|
b.Property<string>("Cursor")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("Plaid");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Infra.Accounts.Plaid.PlaidInstitutionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("FiscalOS.Core.Accounts.InstitutionMetadata");
|
||||||
|
|
||||||
|
b.Property<string>("EncryptedAccessToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("Plaid");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Infra.Transactions.Plaid.PlaidTransactionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("FiscalOS.Core.Transactions.TransactionMetadata");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("Plaid");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Institution", "Institution")
|
||||||
|
.WithMany("Accounts")
|
||||||
|
.HasForeignKey("InstitutionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", null)
|
||||||
|
.WithMany("Accounts")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Institution");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.AccountMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Account", null)
|
||||||
|
.WithOne("Metadata")
|
||||||
|
.HasForeignKey("FiscalOS.Core.Accounts.AccountMetadata", "AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Balance", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Account", null)
|
||||||
|
.WithMany("Balances")
|
||||||
|
.HasForeignKey("AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", "User")
|
||||||
|
.WithMany("Institutions")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.InstitutionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Institution", null)
|
||||||
|
.WithOne("Metadata")
|
||||||
|
.HasForeignKey("FiscalOS.Core.Accounts.InstitutionMetadata", "InstitutionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Identity.RefreshToken", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", "User")
|
||||||
|
.WithMany("RefreshTokens")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Account", "Account")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Account", null)
|
||||||
|
.WithMany("Transactions")
|
||||||
|
.HasForeignKey("AccountId1");
|
||||||
|
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", null)
|
||||||
|
.WithMany("Transactions")
|
||||||
|
.HasForeignKey("UserId1");
|
||||||
|
|
||||||
|
b.Navigation("Account");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.TransactionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Transactions.Transaction", null)
|
||||||
|
.WithOne("Metadata")
|
||||||
|
.HasForeignKey("FiscalOS.Core.Transactions.TransactionMetadata", "TransactionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Balances");
|
||||||
|
|
||||||
|
b.Navigation("Metadata");
|
||||||
|
|
||||||
|
b.Navigation("Transactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Accounts");
|
||||||
|
|
||||||
|
b.Navigation("Metadata");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Identity.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Accounts");
|
||||||
|
|
||||||
|
b.Navigation("Institutions");
|
||||||
|
|
||||||
|
b.Navigation("RefreshTokens");
|
||||||
|
|
||||||
|
b.Navigation("Transactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Metadata");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FiscalOS.Infra.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddTransactionsNavigationPropertyToUser : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "UserId1",
|
||||||
|
table: "Transactions",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Transactions_UserId1",
|
||||||
|
table: "Transactions",
|
||||||
|
column: "UserId1");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Transactions_Users_UserId1",
|
||||||
|
table: "Transactions",
|
||||||
|
column: "UserId1",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Transactions_Users_UserId1",
|
||||||
|
table: "Transactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Transactions_UserId1",
|
||||||
|
table: "Transactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserId1",
|
||||||
|
table: "Transactions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+500
@@ -0,0 +1,500 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FiscalOS.Infra.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FiscalOS.Infra.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20260303130637_UpdateAppDbContextToIncludeTransactionNavigationProperties")]
|
||||||
|
partial class UpdateAppDbContextToIncludeTransactionNavigationProperties
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("InstitutionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("InstitutionId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Account");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.AccountMetadata", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(21)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("AccountMetadata");
|
||||||
|
|
||||||
|
b.HasDiscriminator<string>("Type").HasValue("AccountMetadata");
|
||||||
|
|
||||||
|
b.UseTphMappingStrategy();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Balance", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<decimal>("Available")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CurrencyCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<decimal>("Current")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId");
|
||||||
|
|
||||||
|
b.ToTable("Balance");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Institutions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.InstitutionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("InstitutionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(21)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("InstitutionId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("InstitutionMetadata");
|
||||||
|
|
||||||
|
b.HasDiscriminator<string>("Type").HasValue("InstitutionMetadata");
|
||||||
|
|
||||||
|
b.UseTphMappingStrategy();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Identity.RefreshToken", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("ExpiresAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Revoked")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Token")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("RefreshTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Identity.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EncryptedDataKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("EncryptionKeyId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HashedPassword")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Username")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("AccountId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Date")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("MerchantName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Transactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.TransactionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("TransactionId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(5)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TransactionId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("TransactionMetadata");
|
||||||
|
|
||||||
|
b.HasDiscriminator<string>("Type").HasValue("Plaid");
|
||||||
|
|
||||||
|
b.UseTphMappingStrategy();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Infra.Accounts.Plaid.PlaidAccountMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("FiscalOS.Core.Accounts.AccountMetadata");
|
||||||
|
|
||||||
|
b.Property<string>("Cursor")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("Plaid");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Infra.Accounts.Plaid.PlaidInstitutionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("FiscalOS.Core.Accounts.InstitutionMetadata");
|
||||||
|
|
||||||
|
b.Property<string>("EncryptedAccessToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ItemId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("Plaid");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Infra.Transactions.Plaid.PlaidTransactionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasBaseType("FiscalOS.Core.Transactions.TransactionMetadata");
|
||||||
|
|
||||||
|
b.Property<string>("PlaidId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasDiscriminator().HasValue("Plaid");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Institution", "Institution")
|
||||||
|
.WithMany("Accounts")
|
||||||
|
.HasForeignKey("InstitutionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", null)
|
||||||
|
.WithMany("Accounts")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Institution");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.AccountMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Account", null)
|
||||||
|
.WithOne("Metadata")
|
||||||
|
.HasForeignKey("FiscalOS.Core.Accounts.AccountMetadata", "AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Balance", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Account", null)
|
||||||
|
.WithMany("Balances")
|
||||||
|
.HasForeignKey("AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", "User")
|
||||||
|
.WithMany("Institutions")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.InstitutionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Institution", null)
|
||||||
|
.WithOne("Metadata")
|
||||||
|
.HasForeignKey("FiscalOS.Core.Accounts.InstitutionMetadata", "InstitutionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Identity.RefreshToken", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", "User")
|
||||||
|
.WithMany("RefreshTokens")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Accounts.Account", "Account")
|
||||||
|
.WithMany("Transactions")
|
||||||
|
.HasForeignKey("AccountId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FiscalOS.Core.Identity.User", "User")
|
||||||
|
.WithMany("Transactions")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Account");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.TransactionMetadata", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FiscalOS.Core.Transactions.Transaction", null)
|
||||||
|
.WithOne("Metadata")
|
||||||
|
.HasForeignKey("FiscalOS.Core.Transactions.TransactionMetadata", "TransactionId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Account", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Balances");
|
||||||
|
|
||||||
|
b.Navigation("Metadata");
|
||||||
|
|
||||||
|
b.Navigation("Transactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Accounts.Institution", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Accounts");
|
||||||
|
|
||||||
|
b.Navigation("Metadata");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Identity.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Accounts");
|
||||||
|
|
||||||
|
b.Navigation("Institutions");
|
||||||
|
|
||||||
|
b.Navigation("RefreshTokens");
|
||||||
|
|
||||||
|
b.Navigation("Transactions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Metadata");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FiscalOS.Infra.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class UpdateAppDbContextToIncludeTransactionNavigationProperties : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Transactions_Account_AccountId1",
|
||||||
|
table: "Transactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Transactions_Users_UserId1",
|
||||||
|
table: "Transactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Transactions_AccountId1",
|
||||||
|
table: "Transactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Transactions_UserId1",
|
||||||
|
table: "Transactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AccountId1",
|
||||||
|
table: "Transactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserId1",
|
||||||
|
table: "Transactions");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "AccountId1",
|
||||||
|
table: "Transactions",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "UserId1",
|
||||||
|
table: "Transactions",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Transactions_AccountId1",
|
||||||
|
table: "Transactions",
|
||||||
|
column: "AccountId1");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Transactions_UserId1",
|
||||||
|
table: "Transactions",
|
||||||
|
column: "UserId1");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Transactions_Account_AccountId1",
|
||||||
|
table: "Transactions",
|
||||||
|
column: "AccountId1",
|
||||||
|
principalTable: "Account",
|
||||||
|
principalColumn: "Id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_Transactions_Users_UserId1",
|
||||||
|
table: "Transactions",
|
||||||
|
column: "UserId1",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -250,9 +250,6 @@ namespace FiscalOS.Infra.Migrations
|
|||||||
b.Property<Guid>("AccountId")
|
b.Property<Guid>("AccountId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<Guid?>("AccountId1")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<decimal>("Amount")
|
b.Property<decimal>("Amount")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
@@ -280,8 +277,6 @@ namespace FiscalOS.Infra.Migrations
|
|||||||
|
|
||||||
b.HasIndex("AccountId");
|
b.HasIndex("AccountId");
|
||||||
|
|
||||||
b.HasIndex("AccountId1");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
b.ToTable("Transactions");
|
b.ToTable("Transactions");
|
||||||
@@ -440,17 +435,13 @@ namespace FiscalOS.Infra.Migrations
|
|||||||
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("FiscalOS.Core.Accounts.Account", "Account")
|
b.HasOne("FiscalOS.Core.Accounts.Account", "Account")
|
||||||
.WithMany()
|
.WithMany("Transactions")
|
||||||
.HasForeignKey("AccountId")
|
.HasForeignKey("AccountId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("FiscalOS.Core.Accounts.Account", null)
|
|
||||||
.WithMany("Transactions")
|
|
||||||
.HasForeignKey("AccountId1");
|
|
||||||
|
|
||||||
b.HasOne("FiscalOS.Core.Identity.User", "User")
|
b.HasOne("FiscalOS.Core.Identity.User", "User")
|
||||||
.WithMany()
|
.WithMany("Transactions")
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
@@ -492,6 +483,8 @@ namespace FiscalOS.Infra.Migrations
|
|||||||
b.Navigation("Institutions");
|
b.Navigation("Institutions");
|
||||||
|
|
||||||
b.Navigation("RefreshTokens");
|
b.Navigation("RefreshTokens");
|
||||||
|
|
||||||
|
b.Navigation("Transactions");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
modelBuilder.Entity("FiscalOS.Core.Transactions.Transaction", b =>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace FiscalOS.Infra.Transactions.Plaid;
|
|||||||
public static class PlaidExtensions
|
public static class PlaidExtensions
|
||||||
{
|
{
|
||||||
#pragma warning disable CA1034
|
#pragma warning disable CA1034
|
||||||
extension(Transaction transaction)
|
extension(Going.Plaid.Entity.Transaction transaction)
|
||||||
{
|
{
|
||||||
public string Merchant => transaction.MerchantName ?? "Unknown merchant";
|
public string Merchant => transaction.MerchantName ?? "Unknown merchant";
|
||||||
public string Description => transaction.OriginalDescription ?? "";
|
public string Description => transaction.OriginalDescription ?? "";
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ internal sealed class PlaidRemovedTransactionHandler : IPlaidRemovedTransactionH
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PlaidRemovedTransactionHandler From(
|
||||||
|
AppDbContext appDbContext,
|
||||||
|
ILogger<PlaidRemovedTransactionHandler> logger
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return new(appDbContext, logger);
|
||||||
|
}
|
||||||
|
|
||||||
public static PlaidRemovedTransactionHandler From(IServiceProvider serviceProvider)
|
public static PlaidRemovedTransactionHandler From(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
return new(
|
return new(
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using FiscalOS.Core.Transactions;
|
|
||||||
using FiscalOS.Infra.Common;
|
|
||||||
|
|
||||||
namespace FiscalOS.Infra.Transactions.Plaid;
|
namespace FiscalOS.Infra.Transactions.Plaid;
|
||||||
|
|
||||||
public sealed class PlaidTransactionMetadata : TransactionMetadata
|
public sealed class PlaidTransactionMetadata : TransactionMetadata
|
||||||
@@ -14,7 +11,7 @@ public sealed class PlaidTransactionMetadata : TransactionMetadata
|
|||||||
|
|
||||||
public static PlaidTransactionMetadata From(string? plaidId)
|
public static PlaidTransactionMetadata From(string? plaidId)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(plaidId);
|
ArgumentNullException.ThrowIfNull(plaidId, nameof(plaidId));
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ global using FiscalOS.Core.Data;
|
|||||||
global using FiscalOS.Core.Identity;
|
global using FiscalOS.Core.Identity;
|
||||||
global using FiscalOS.Core.Queuing;
|
global using FiscalOS.Core.Queuing;
|
||||||
global using FiscalOS.Core.Security;
|
global using FiscalOS.Core.Security;
|
||||||
|
global using FiscalOS.Core.Transactions;
|
||||||
global using FiscalOS.Infra.Accounts.Plaid;
|
global using FiscalOS.Infra.Accounts.Plaid;
|
||||||
global using FiscalOS.Infra.Authentication;
|
global using FiscalOS.Infra.Authentication;
|
||||||
global using FiscalOS.Infra.Authorization;
|
global using FiscalOS.Infra.Authorization;
|
||||||
global using FiscalOS.Infra.Common;
|
global using FiscalOS.Infra.Common;
|
||||||
global using FiscalOS.Infra.Data;
|
global using FiscalOS.Infra.Data;
|
||||||
|
global using FiscalOS.Infra.Queuing;
|
||||||
global using FiscalOS.Infra.Security;
|
global using FiscalOS.Infra.Security;
|
||||||
|
global using FiscalOS.Infra.Transactions.Plaid;
|
||||||
|
|
||||||
global using Going.Plaid;
|
global using Going.Plaid;
|
||||||
global using Going.Plaid.Entity;
|
global using Going.Plaid.Entity;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public static class ServiceDefaultsExtensions
|
|||||||
|
|
||||||
public static WebApplication MapDefaultEndpoints(this WebApplication app)
|
public static WebApplication MapDefaultEndpoints(this WebApplication app)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(app);
|
ArgumentNullException.ThrowIfNull(app, nameof(app));
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using Aspire.Hosting;
|
|
||||||
using Aspire.Hosting.Testing;
|
|
||||||
|
|
||||||
namespace FiscalOS.AppHost.Tests.Infra;
|
namespace FiscalOS.AppHost.Tests.Infra;
|
||||||
|
|
||||||
public sealed class AspireFixture : IAsyncLifetime
|
public sealed class AspireFixture : IAsyncLifetime
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
global using Aspire.Hosting;
|
||||||
|
global using Aspire.Hosting.Testing;
|
||||||
|
|
||||||
global using FiscalOS.AppHost.Tests.Infra;
|
global using FiscalOS.AppHost.Tests.Infra;
|
||||||
|
|
||||||
global using Microsoft.Playwright;
|
global using Microsoft.Playwright;
|
||||||
global using Microsoft.Playwright.Xunit.v3;
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
namespace FiscalOS.Infra.Tests.Integration;
|
||||||
|
|
||||||
|
public abstract class IntegrationTest : IAsyncLifetime
|
||||||
|
{
|
||||||
|
private bool _isDisposed;
|
||||||
|
private readonly FileSystem _fileSystem = new();
|
||||||
|
protected AppDbContext AppDbContext { get; }
|
||||||
|
|
||||||
|
protected IntegrationTest()
|
||||||
|
{
|
||||||
|
var options = Options.Create(new AppDbContextOptions()
|
||||||
|
{
|
||||||
|
DatabaseFilePath = $"{Guid.NewGuid()}.db",
|
||||||
|
});
|
||||||
|
|
||||||
|
AppDbContext = new(options, _fileSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask InitializeAsync()
|
||||||
|
{
|
||||||
|
await AppDbContext.Database.MigrateAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await DisposeAsync(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual async Task DisposeAsync(bool disposing)
|
||||||
|
{
|
||||||
|
if (_isDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
await AppDbContext.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDisposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
namespace FiscalOS.Infra.Tests.Integration;
|
||||||
|
|
||||||
|
public class PlaidRemovedTransactionHandlerTests : IntegrationTest
|
||||||
|
{
|
||||||
|
private readonly Mock<ILogger<PlaidRemovedTransactionHandler>> _mockLogger = new();
|
||||||
|
private readonly PlaidRemovedTransactionHandler _sut;
|
||||||
|
|
||||||
|
public PlaidRemovedTransactionHandlerTests()
|
||||||
|
{
|
||||||
|
_sut = PlaidRemovedTransactionHandler.From(AppDbContext, _mockLogger.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HandleAsync_WhenCalled_ItShouldRemoveTheCorrectTransactions()
|
||||||
|
{
|
||||||
|
var user = User.From(
|
||||||
|
"username",
|
||||||
|
"hashedPassword",
|
||||||
|
EncryptedDataKey.From("keyUsed", "encryptedKey")
|
||||||
|
);
|
||||||
|
|
||||||
|
var institutionMetadata = PlaidInstitutionMetadata.From(
|
||||||
|
"plaidId",
|
||||||
|
"plaidName",
|
||||||
|
"encryptedAccessToken",
|
||||||
|
"itemId"
|
||||||
|
);
|
||||||
|
var institution = Institution.From("institutionName", institutionMetadata);
|
||||||
|
user.AddInstitution(institution);
|
||||||
|
|
||||||
|
var accountMetadata = PlaidAccountMetadata.From("plaidId", "plaidName");
|
||||||
|
var account = Account.From("some account", accountMetadata);
|
||||||
|
user.AddAccount(account);
|
||||||
|
institution.AddAccount(account);
|
||||||
|
|
||||||
|
var transactionMetadata = PlaidTransactionMetadata.From("plaidId");
|
||||||
|
var transaction = Transaction.From(
|
||||||
|
"merchantName",
|
||||||
|
"description",
|
||||||
|
100,
|
||||||
|
DateTimeOffset.UtcNow,
|
||||||
|
transactionMetadata
|
||||||
|
);
|
||||||
|
user.AddTransaction(transaction);
|
||||||
|
account.AddTransaction(transaction);
|
||||||
|
|
||||||
|
await AppDbContext.AddAsync(user, TestContext.Current.CancellationToken);
|
||||||
|
await AppDbContext.SaveChangesAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
|
var transactionToRemove = new Going.Plaid.Entity.RemovedTransaction()
|
||||||
|
{
|
||||||
|
TransactionId = transactionMetadata.PlaidId,
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _sut.HandleAsync([transactionToRemove], TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
|
result.Should().Be(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,19 +7,26 @@ global using System.Text.Json;
|
|||||||
|
|
||||||
global using AwesomeAssertions.Primitives;
|
global using AwesomeAssertions.Primitives;
|
||||||
|
|
||||||
|
global using FiscalOS.Core.Accounts;
|
||||||
global using FiscalOS.Core.Identity;
|
global using FiscalOS.Core.Identity;
|
||||||
global using FiscalOS.Core.Security;
|
global using FiscalOS.Core.Security;
|
||||||
|
global using FiscalOS.Core.Transactions;
|
||||||
|
global using FiscalOS.Infra.Accounts.Plaid;
|
||||||
global using FiscalOS.Infra.Authentication;
|
global using FiscalOS.Infra.Authentication;
|
||||||
global using FiscalOS.Infra.Authorization;
|
global using FiscalOS.Infra.Authorization;
|
||||||
|
global using FiscalOS.Infra.Data;
|
||||||
global using FiscalOS.Infra.Security;
|
global using FiscalOS.Infra.Security;
|
||||||
global using FiscalOS.Infra.Tests.Assertions;
|
global using FiscalOS.Infra.Tests.Assertions;
|
||||||
global using FiscalOS.Infra.Tests.Mocks;
|
global using FiscalOS.Infra.Tests.Mocks;
|
||||||
|
global using FiscalOS.Infra.Transactions.Plaid;
|
||||||
|
|
||||||
global using Microsoft.AspNetCore.Authentication.JwtBearer;
|
global using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
global using Microsoft.AspNetCore.Authorization;
|
global using Microsoft.AspNetCore.Authorization;
|
||||||
global using Microsoft.AspNetCore.Authorization.Policy;
|
global using Microsoft.AspNetCore.Authorization.Policy;
|
||||||
global using Microsoft.AspNetCore.Http;
|
global using Microsoft.AspNetCore.Http;
|
||||||
global using Microsoft.AspNetCore.Mvc;
|
global using Microsoft.AspNetCore.Mvc;
|
||||||
|
global using Microsoft.EntityFrameworkCore;
|
||||||
global using Microsoft.Extensions.Configuration;
|
global using Microsoft.Extensions.Configuration;
|
||||||
|
global using Microsoft.Extensions.Logging;
|
||||||
global using Microsoft.Extensions.Options;
|
global using Microsoft.Extensions.Options;
|
||||||
global using Microsoft.IdentityModel.Tokens;
|
global using Microsoft.IdentityModel.Tokens;
|
||||||
Reference in New Issue
Block a user