Add API key management. #10

Merged
BobdaFett merged 22 commits from api-key-management into master 2024-01-08 19:07:18 +00:00
4 changed files with 68 additions and 34 deletions
Showing only changes of commit 435d01ef90 - Show all commits

No files matched your search

+4 -9
View File
@@ -1,12 +1,9 @@
using System;
using System.Data.Entity.Migrations.Model;
using Avalonia; using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using PcgsInvUi.Services; using PcgsInvUi.Services;
using PcgsInvUi.ViewModels; using PcgsInvUi.ViewModels;
using PcgsInvUi.Views; using PcgsInvUi.Views;
using PcgsInvUi.Models;
namespace PcgsInvUi; namespace PcgsInvUi;
@@ -20,17 +17,15 @@ public partial class App : Application {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
var coins = new CoinDatabase(); var coins = new CoinDatabase();
desktop.MainWindow = new MainWindow { desktop.MainWindow = new MainWindow {
DataContext = new MainWindowViewModel(coins), DataContext = new MainWindowViewModel(),
}; };
// Allow the application to save the database when it exits.
// The issue is that this will break if the database is not initialized here.
desktop.Exit += (sender, args) => { desktop.Exit += (sender, args) => {
coins.UpdateCollection("CollectionTable"); coins.UpdateCollection("CollectionTable");
}; };
} }
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();
} }
}
public void OnExit(object sender, ControlledApplicationLifetimeExitEventArgs e) {
// Save the database?
}
}
+53 -6
View File
@@ -16,15 +16,40 @@ public class CoinDatabase {
public CoinDatabase() { public CoinDatabase() {
// Create a connection to SQLite // Create a connection to SQLite
Connection = CreateConnection(); Connection = CreateConnection();
// Create a client to access the API.
_pcgsClient = new PcgsClient(
"eAb8gS0I2XAvT_5gJeiGJaglMia1Tk-oB4kJUK6kuafyrny_S61vIJY-Ikl4nCQM67wrdxzUqLVWTV2kBSxD3d5XNBHxHnYBhcSS6dOPug0hZaF3qAv56df3gYSzOGh9Tif5y0eP3Iw0LrqKDr1Hj-dk6SV6GKog2IIqCPQhhHH8FMTWBTYO-_O8cx7qLdM5GM8KlTsic6g3VRUhM8EA_4OO04dCfmNLGhqINRl3jGZ0Q4ziI8fng2bVWsIyteqiPzUn10rIQ3-OPpqVZG_DxeOmOejj4GzbUNyqUOajy-nr5rYY");
// Create a base table. // Create a base table.
CreateCollection("CollectionTable"); CreateCollection("CollectionTable");
CreateApiTable();
Console.WriteLine("Connected to database."); Console.WriteLine("Connected to database.");
Collection = new ObservableCollection<Coin>(); Collection = new ObservableCollection<Coin>();
GetCollection("CollectionTable"); GetCollection("CollectionTable");
Console.WriteLine("Got collection."); Console.WriteLine("Got collection.");
// Initialize the PCGS client. Must get the API key from the database.
// Check the database for an API key.
SQLiteCommand cmd = Connection.CreateCommand();
cmd.CommandText = "SELECT API_KEY FROM ApiTable";
using (var reader = cmd.ExecuteReader()) {
// If there's no API key, we need to get one from the user.
// Eventually there will be a window that pops up adking for the API key.
var apiKey = "";
if (reader.Read()) {
// Initialize the client.
apiKey = reader.GetString(0);
}
else {
// Get the API key from the user.
// TODO Create and open a window that asks for the API key.
// Since this needs to build before the main window is created, we can't do that yet.
Console.WriteLine("No API key found. Using default...");
do {
// Console.Write("Enter a valid API key: ");
// apiKey = Console.ReadLine();
// Eventually we actually want to throw an error here.
apiKey = "E8xivTVN_k4Q8ubk8Xv_uU11U91WX0wDht4S4m4BAf2HPmKnGrZ0J2f9nccM7Ns1VkGoT1SXYR74I7aNaMKqXlJGEy3oobseKV0nGiqu1IKvFkOTVjhV37SWUVExSDFMC6FLzs7xJQIHwkcxr5JFOQNmc4ZytEh_-hmCN3NL-z0fEk98tzBmPJ_F7bq_xNru86QMD2A9yalu_dWe-lCysfyE2bCA2EDqwvWenRYf-0XnNtp6qlxxCMlCd-TDqqRKLC3qefCwpXSemwBiNSP5gbC9pwcKrZugkruuJ0nyxxThZymm";
} while (apiKey == "" || apiKey == null);
}
_pcgsClient = new PcgsClient(apiKey);
}
} }
~CoinDatabase() { ~CoinDatabase() {
@@ -38,9 +63,9 @@ public class CoinDatabase {
private SQLiteConnection CreateConnection() { private SQLiteConnection CreateConnection() {
// Check if database exists. // Check if database exists.
if (!System.IO.File.Exists("coin_collection")) { if (!System.IO.File.Exists("coin_collection")) {
// Create the database. // If not, create the database.
Console.Write("Creating database... "); Console.Write("Creating database... ");
SQLiteConnection.CreateFile("coin_collection"); SQLiteConnection.CreateFile("coin_collection.sqlite");
Console.WriteLine("Done."); Console.WriteLine("Done.");
} }
@@ -60,6 +85,27 @@ public class CoinDatabase {
return connection; return connection;
} }
public void CreateApiTable() {
// Create a table to store the API key and the number of requests remaining.
if (!IsConnected) {
// TODO Create a NotConnectedToDatabaseException
Console.WriteLine("Not connected to database.");
return;
}
// Create a SQL command.
SQLiteCommand cmd = Connection.CreateCommand();
cmd.CommandText =
"CREATE TABLE IF NOT EXISTS ApiTable (" +
"API_KEY TEXT, " +
"REQUESTS_REMAINING INTEGER, " +
"PRIMARY KEY(API_KEY))";
// Execute the command
cmd.ExecuteNonQuery();
Console.WriteLine("API table initialized.");
}
public void CreateCollection(string collectionName) { public void CreateCollection(string collectionName) {
// Create a table to store the collection. // Create a table to store the collection.
if (!IsConnected) { if (!IsConnected) {
@@ -93,6 +139,7 @@ public class CoinDatabase {
// Execute the command. // Execute the command.
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
Console.WriteLine("Collection table initialized.");
} }
public void InsertCoin(Coin coin) { public void InsertCoin(Coin coin) {
@@ -297,4 +344,4 @@ public class CoinDatabase {
Console.WriteLine($"Grade is {actualGrade}"); Console.WriteLine($"Grade is {actualGrade}");
return actualGrade; return actualGrade;
} }
} }
+3 -6
View File
@@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
using PcgsInvUi.Models; using PcgsInvUi.Models;
using System.Text.Json; using System.Text.Json;
using DynamicData.Kernel;
using ReactiveUI; using ReactiveUI;
namespace PcgsInvUi.Services; namespace PcgsInvUi.Services;
@@ -28,9 +26,8 @@ public class PcgsClient : ReactiveObject {
private int _numApiRequests; private int _numApiRequests;
public PcgsClient(string apiKey) { public PcgsClient(string apiKey) {
// _apiKey = apiKey; _apiKey = apiKey;
_apiKey = // "E8xivTVN_k4Q8ubk8Xv_uU11U91WX0wDht4S4m4BAf2HPmKnGrZ0J2f9nccM7Ns1VkGoT1SXYR74I7aNaMKqXlJGEy3oobseKV0nGiqu1IKvFkOTVjhV37SWUVExSDFMC6FLzs7xJQIHwkcxr5JFOQNmc4ZytEh_-hmCN3NL-z0fEk98tzBmPJ_F7bq_xNru86QMD2A9yalu_dWe-lCysfyE2bCA2EDqwvWenRYf-0XnNtp6qlxxCMlCd-TDqqRKLC3qefCwpXSemwBiNSP5gbC9pwcKrZugkruuJ0nyxxThZymm";
"E8xivTVN_k4Q8ubk8Xv_uU11U91WX0wDht4S4m4BAf2HPmKnGrZ0J2f9nccM7Ns1VkGoT1SXYR74I7aNaMKqXlJGEy3oobseKV0nGiqu1IKvFkOTVjhV37SWUVExSDFMC6FLzs7xJQIHwkcxr5JFOQNmc4ZytEh_-hmCN3NL-z0fEk98tzBmPJ_F7bq_xNru86QMD2A9yalu_dWe-lCysfyE2bCA2EDqwvWenRYf-0XnNtp6qlxxCMlCd-TDqqRKLC3qefCwpXSemwBiNSP5gbC9pwcKrZugkruuJ0nyxxThZymm";
} }
public async Task<(ErrorType, Coin?)> GetCoinFactsByGrade(int pcgsNumber, string grade, bool plusGrade = false) { public async Task<(ErrorType, Coin?)> GetCoinFactsByGrade(int pcgsNumber, string grade, bool plusGrade = false) {
@@ -86,4 +83,4 @@ class CoinNotFoundException : Exception {
public CoinNotFoundException() { } public CoinNotFoundException() { }
public CoinNotFoundException(string message) : base(message) { } public CoinNotFoundException(string message) : base(message) { }
public CoinNotFoundException(string message, Exception inner) : base(message, inner) { } public CoinNotFoundException(string message, Exception inner) : base(message, inner) { }
} }
+8 -13
View File
@@ -10,18 +10,6 @@ using ReactiveUI;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
// TODO Create proper sums for total value.
// TODO Error handling for API calls.
// TODO Manual handling of API key.
// TODO Use CoinFacts link.
// TODO Splash screen during startup.
// TODO Take giant sheet of coins to integrate into the application.
// TODO Update all coins. Choose those that are older first.
// TODO API request tracker (1000 per day)
// TODO Ok button greys out while request is happening.
// TODO Track when coins were last updated.
// TODO Image for coin in notes view.
public class MainWindowViewModel : ViewModelBase { public class MainWindowViewModel : ViewModelBase {
public ViewModelBase SidebarContent { public ViewModelBase SidebarContent {
get => _sideContent; get => _sideContent;
@@ -48,7 +36,7 @@ public class MainWindowViewModel : ViewModelBase {
private Coin? _selectedCoin; private Coin? _selectedCoin;
private double _totalValue; private double _totalValue;
public MainWindowViewModel(CoinDatabase coins) { public MainWindowViewModel(CoinDatabase coins, bool askForApiKey) {
ConnectedCoinDatabase = coins; ConnectedCoinDatabase = coins;
var newViewModel = new NewViewModel(); var newViewModel = new NewViewModel();
_sideContent = newViewModel; _sideContent = newViewModel;
@@ -126,4 +114,11 @@ public class MainWindowViewModel : ViewModelBase {
TotalValue = ConnectedCoinDatabase.Collection.Sum(x => x.TotalPrice); TotalValue = ConnectedCoinDatabase.Collection.Sum(x => x.TotalPrice);
}; };
} }
// Create destructor to save the database when the application exits.
// This is necessary because the database is initialized in this class.
~MainWindowViewModel() {
Console.WriteLine("Running MainWindowViewModel destructor.");
ConnectedCoinDatabase.UpdateCollection("CollectionTable");
}
} }