Added comments to CoinCollection code, and finished a basic framework for API key entry.
This commit is contained in:
1 parent
683a2cf87b
commit
ba04d31c3e
4 files changed
+88
-29
No files matched your search
@@ -17,11 +17,10 @@ public partial class App : Application {
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
|
||||
var coins = new CoinDatabase();
|
||||
desktop.MainWindow = new MainWindow {
|
||||
DataContext = new MainWindowViewModel(coins, !coins.TryInitApiClient()),
|
||||
DataContext = new MainWindowViewModel(coins),
|
||||
};
|
||||
|
||||
// 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) => {
|
||||
coins.UpdateCollection("CollectionTable");
|
||||
};
|
||||
|
||||
@@ -30,6 +30,9 @@ public class CoinDatabase {
|
||||
Console.WriteLine("Got collection.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor. Currently not used, due to the strange nature of desktop applications.
|
||||
/// </summary>
|
||||
~CoinDatabase() {
|
||||
Console.WriteLine("Destructor called, saving collection.");
|
||||
// Save collection.
|
||||
@@ -42,7 +45,7 @@ public class CoinDatabase {
|
||||
/// Attempts to intitialize the API client by selecting the API key from the database.
|
||||
/// </summary>
|
||||
/// <returns>True if the API key was found, false otherwise.</returns>
|
||||
public bool TryInitApiClient() {
|
||||
public async Task<bool> TryInitApiClient() {
|
||||
Console.Write("Attempting to initialize API client... ");
|
||||
SQLiteCommand cmd = Connection.CreateCommand();
|
||||
cmd.CommandText = "SELECT API_KEY FROM ApiTable";
|
||||
@@ -50,7 +53,7 @@ public class CoinDatabase {
|
||||
using (var reader = cmd.ExecuteReader()) {
|
||||
if (reader.Read()) {
|
||||
var apiKey = reader.GetString(0);
|
||||
_pcgsClient = PcgsClient.CreateClient(apiKey);
|
||||
_pcgsClient = await PcgsClient.CreateClient(apiKey);
|
||||
|
||||
// Check that the client is valid.
|
||||
if (_pcgsClient is null) {
|
||||
@@ -59,14 +62,8 @@ public class CoinDatabase {
|
||||
return IsClientConnected;
|
||||
}
|
||||
|
||||
// This only runs if the key is valid. Insert the key into the database.
|
||||
// TODO Create checks to ensure that the database doesn't already contain a key.
|
||||
var insertCmd = Connection.CreateCommand();
|
||||
insertCmd.CommandText = $"INSERT INTO ApiTable (API_KEY, REQUESTS_REMAINING) VALUE ({apiKey}, 1000)";
|
||||
insertCmd.ExecuteNonQuery();
|
||||
|
||||
IsClientConnected = true;
|
||||
Console.WriteLine("Done!");
|
||||
Console.WriteLine("Done! API key was found from database.");
|
||||
}
|
||||
else {
|
||||
IsClientConnected = false;
|
||||
@@ -76,9 +73,13 @@ public class CoinDatabase {
|
||||
return IsClientConnected;
|
||||
}
|
||||
|
||||
public bool TryInitApiClient(string apiKey) {
|
||||
/// <summary>
|
||||
/// Atttempts to initialize the PCGS API client.
|
||||
/// </summary>
|
||||
/// <returns>A Task with a return value that returns true if the client intializes properly.</returns>
|
||||
public async Task<bool> TryInitApiClient(string apiKey) {
|
||||
Console.Write("Attempting to initialize API client with key param... ");
|
||||
_pcgsClient = PcgsClient.CreateClient(apiKey);
|
||||
_pcgsClient = await PcgsClient.CreateClient(apiKey);
|
||||
// TODO Check if the API key is valid and if it is, then add to the database.
|
||||
if (_pcgsClient is null) {
|
||||
IsClientConnected = false;
|
||||
@@ -87,13 +88,19 @@ public class CoinDatabase {
|
||||
}
|
||||
|
||||
var cmd = Connection.CreateCommand();
|
||||
cmd.CommandText = $"INSERT INTO ApiTable (API_KEY, REQUESTS_REMAINING) VALUES ({apiKey}, 1000)";
|
||||
// cmd.ExecuteNonQuery();
|
||||
cmd.CommandText = $"INSERT INTO ApiTable (API_KEY, REQUESTS_REMAINING) VALUES (\"{apiKey}\", 1000)";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
|
||||
IsClientConnected = true;
|
||||
Console.WriteLine("Done!");
|
||||
Console.WriteLine("Done! API key was added to database.");
|
||||
return IsClientConnected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection from the database and stores it in the Collection property.
|
||||
/// Creates the collection table if it does not exist already.
|
||||
/// </summary>
|
||||
/// <returns>The SQLiteConnection that corresponds to the connected database.</returns>
|
||||
private SQLiteConnection CreateConnection() {
|
||||
// Check if database exists.
|
||||
if (!System.IO.File.Exists("coin_collection")) {
|
||||
@@ -119,6 +126,9 @@ public class CoinDatabase {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the table that stores the API key and the number of requests remaining.
|
||||
/// </summary>
|
||||
public void CreateApiTable() {
|
||||
// Create a table to store the API key and the number of requests remaining.
|
||||
if (!IsConnected) {
|
||||
@@ -176,6 +186,9 @@ public class CoinDatabase {
|
||||
Console.WriteLine("Collection table initialized.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a Coin object into the database.
|
||||
/// </summary>
|
||||
public void InsertCoin(Coin coin) {
|
||||
Console.WriteLine("Inserting coin...");
|
||||
// Use our existing connection to insert a coin to the database.
|
||||
@@ -202,6 +215,13 @@ public class CoinDatabase {
|
||||
Console.WriteLine("Coin inserted.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Coin object and saves it to the database.
|
||||
/// </summary>
|
||||
/// <param name="pcgsNumber">The PCGS number of the coin.</param>
|
||||
/// <param name="grade">The grade of the coin.</param>
|
||||
/// <param name="quantity">The quantity of the coin.</param>
|
||||
/// <returns>The error type returned by the API.</returns>
|
||||
public async Task<PcgsClient.ErrorType> CreateCoin(int pcgsNumber, string grade, int quantity) {
|
||||
Console.WriteLine("Creating coin...");
|
||||
// Create a coin by calling the API, then insert it into the database.
|
||||
@@ -227,6 +247,12 @@ public class CoinDatabase {
|
||||
return apiResult.Item1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a coin from the database.
|
||||
/// </summary>
|
||||
/// <param name="pcgsNumber">The PCGS number of the coin.</param>
|
||||
/// <param name="grade">The grade of the coin.</param>
|
||||
/// <returns>The coin if it exists, null otherwise.</returns>
|
||||
public Coin? GetCoin(int pcgsNumber, string grade) {
|
||||
Console.WriteLine($"Getting coin {pcgsNumber}, {grade}...");
|
||||
int gradeInt = GetGradeFromString(grade);
|
||||
@@ -246,6 +272,10 @@ public class CoinDatabase {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to get all Coin objects within the database.
|
||||
/// </summary>
|
||||
/// <param name="collectionName">The name of the collection table.</param>
|
||||
public void GetCollection(string collectionName) {
|
||||
Console.WriteLine("Getting collection...");
|
||||
SQLiteCommand cmd = Connection.CreateCommand();
|
||||
@@ -259,6 +289,11 @@ public class CoinDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a CSV formatted string from a collection.
|
||||
/// </summary>
|
||||
/// <param name="collectionName">The name of the collection table.</param>
|
||||
/// <returns>A CSV formatted string.</returns>
|
||||
public String CollectionToCSV(string collectionName) {
|
||||
var cmd = Connection.CreateCommand();
|
||||
cmd.CommandText = $"SELECT * FROM {collectionName}";
|
||||
@@ -307,6 +342,11 @@ public class CoinDatabase {
|
||||
return resultString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a Coin object to the database.
|
||||
/// </summary>
|
||||
/// <param name="collectionName">The name of the collection table.</param>
|
||||
/// <param name="coin">The coin to save.</param>
|
||||
public void SaveCoin(string collectionName, Coin coin) {
|
||||
// Get and replace the coin from the database.
|
||||
Console.WriteLine("Saving coin...");
|
||||
@@ -333,6 +373,10 @@ public class CoinDatabase {
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the database with the current contents of the specified collection.
|
||||
/// </summary>
|
||||
/// <param name="collectionName">The name of the collection table.</param>
|
||||
public void UpdateCollection(string collectionName) {
|
||||
// Update the database with the contents of the collection.
|
||||
// TODO This is a very inefficient way of doing this. I should be able to do this in one command.
|
||||
@@ -341,6 +385,10 @@ public class CoinDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Coin from the information in a reader specifically from this database.
|
||||
/// </summary>
|
||||
/// <param name="reader">The reader to get the information from.</param>
|
||||
private Coin CoinFromReader(SQLiteDataReader reader) {
|
||||
// Create a coin while filtering out any possible null values - they throw exceptions.
|
||||
Coin coin = new Coin();
|
||||
@@ -364,6 +412,10 @@ public class CoinDatabase {
|
||||
return coin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a Coin object from the database.
|
||||
/// </summary>
|
||||
/// <param name="delCoin">The coin to delete.</param>
|
||||
public void DeleteCoin(Coin delCoin) {
|
||||
SQLiteCommand cmd = Connection.CreateCommand();
|
||||
cmd.CommandText = "DELETE FROM CollectionTable WHERE PCGS_NUMBER = @PcgsNumber AND GRADE = @Grade";
|
||||
@@ -373,6 +425,11 @@ public class CoinDatabase {
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the grade from a string returned from the main window.
|
||||
/// These are always in a format similar to "MS-65", so we can just get the last two characters.
|
||||
/// </summary>
|
||||
/// <param name="grade">The grade string.</param>
|
||||
private int GetGradeFromString(string grade) {
|
||||
var actualGrade = int.Parse(grade.Substring(grade.Length - 2));
|
||||
Console.WriteLine($"Grade is {actualGrade}");
|
||||
|
||||
@@ -39,18 +39,19 @@ public class PcgsClient : ReactiveObject {
|
||||
/// </summary>
|
||||
/// <param name="apiKey">The api key to use for requests.</param>
|
||||
/// <returns>A new instance of the PcgsClient class, or null if the key is invalid.</returns>
|
||||
public static PcgsClient? CreateClient(string apiKey) {
|
||||
public static async Task<PcgsClient?> CreateClient(string apiKey) {
|
||||
// Check if the api key is valid.
|
||||
var testClient = new PcgsClient(apiKey);
|
||||
if (!testClient.VerifyCredentials()) return null;
|
||||
var validCreds = await testClient.VerifyCredentials();
|
||||
if (!validCreds) return null;
|
||||
return testClient;
|
||||
}
|
||||
|
||||
public bool VerifyCredentials() {
|
||||
public async Task<bool> VerifyCredentials() {
|
||||
// Create a way to check if the api key given is valid
|
||||
// I think the best way to do this is to make a request to the api and see if it returns the expected response.
|
||||
var result = this.GetCoinFactsByGrade(1, "1"); // This is a known invalid coin id - this is because the api is very fast with invalid requests.
|
||||
if (result.Result.Item1 == ErrorType.ApiKeyInvalid) return false;
|
||||
var result = await this.GetCoinFactsByGrade(1, "AG-03"); // This is a known invalid coin id - this is because the api is very fast with invalid requests.
|
||||
if (result.Item1 == ErrorType.ApiKeyInvalid) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -66,6 +67,7 @@ public class PcgsClient : ReactiveObject {
|
||||
Console.WriteLine("Got response.");
|
||||
|
||||
if (response.IsSuccessStatusCode) {
|
||||
// Check response for errors.
|
||||
string jsonResponse = await response.Content.ReadAsStringAsync();
|
||||
var parsedResponse = ParseJsonResponse(jsonResponse);
|
||||
return parsedResponse;
|
||||
@@ -76,13 +78,11 @@ public class PcgsClient : ReactiveObject {
|
||||
}
|
||||
|
||||
private int GetGradeFromString(string grade) {
|
||||
var actualGrade = int.Parse(grade.Substring(grade.Length - 2));
|
||||
var actualGrade = int.Parse(grade.Substring(grade.Length - 2)); // Throws an exception if the grade doesn't exist.
|
||||
Console.WriteLine($"Grade is {actualGrade}");
|
||||
return actualGrade;
|
||||
}
|
||||
|
||||
// TODO Will eventually create the other methods that are included in the Public API.
|
||||
|
||||
private (ErrorType, Coin?) ParseJsonResponse(string json) {
|
||||
Console.WriteLine(json);
|
||||
// TODO Check for errors in the response
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Reactive.Linq;
|
||||
using PcgsInvUi.Models;
|
||||
using PcgsInvUi.Services;
|
||||
using ReactiveUI;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PcgsInvUi.ViewModels;
|
||||
|
||||
@@ -36,8 +37,10 @@ public class MainWindowViewModel : ViewModelBase {
|
||||
private Coin? _selectedCoin;
|
||||
private double _totalValue;
|
||||
|
||||
public MainWindowViewModel(CoinDatabase coins, bool askForApiKey) {
|
||||
public MainWindowViewModel(CoinDatabase coins) {
|
||||
ConnectedCoinDatabase = coins;
|
||||
var keyFound = Task.Run(() => coins.TryInitApiClient()).Result;
|
||||
Console.WriteLine("Function completed.");
|
||||
var newViewModel = new NewViewModel();
|
||||
var apiKeyViewModel = new ApiKeyViewModel();
|
||||
// DisplayedList = CoinCollection = new ObservableCollection<Coin>(coins.GetItems());
|
||||
@@ -45,13 +48,13 @@ public class MainWindowViewModel : ViewModelBase {
|
||||
TotalValue = DisplayedList.Sum(x => x.TotalPrice);
|
||||
|
||||
// Setup interaction to show ApiKeyWindow
|
||||
apiKeyViewModel.OkCommand.Subscribe(apiKey => {
|
||||
if (ConnectedCoinDatabase.TryInitApiClient(apiKey))
|
||||
SidebarContent = newViewModel; // Must assign this to the observable property.
|
||||
apiKeyViewModel.OkCommand.Subscribe(async apiKey => {
|
||||
if (await ConnectedCoinDatabase.TryInitApiClient(apiKey))
|
||||
SidebarContent = newViewModel; // Must assign this to the observable property rather than private variable.
|
||||
});
|
||||
|
||||
// Check if API key must be entered.
|
||||
if (askForApiKey) {
|
||||
if (!keyFound) {
|
||||
Console.WriteLine("API key must be entered by user.");
|
||||
// Show the API key input view.
|
||||
_sideContent = apiKeyViewModel;
|
||||
|
||||
Reference in new issue
Block a user