Spaces:
Running
Running
using System.Linq; | |
using System.Text.Json; | |
using Microsoft.EntityFrameworkCore; | |
var builder = WebApplication.CreateBuilder(args); | |
builder.WebHost.ConfigureKestrel(serverOptions => | |
{ | |
serverOptions.Listen(System.Net.IPAddress.Any, 7860); // Listen on all network interfaces on port 5000 | |
}); | |
builder.Services.AddDbContext<QuoteDbContext>(options => | |
options.UseSqlite("Data Source=quotes.db")); | |
builder.Services.AddEndpointsApiExplorer(); | |
builder.Services.AddSwaggerGen(); | |
var app = builder.Build(); | |
// Configure the HTTP request pipeline. | |
//if (app.Environment.IsDevelopment()) | |
{ | |
app.UseSwagger(); | |
app.UseSwaggerUI(); | |
} | |
const int MaxPageSize = 100; | |
app.UseHttpsRedirection(); | |
app.MapGet("/quotes", async (QuoteDbContext db, int pageNumber = 1, int pageSize = 10) => | |
{ | |
if (pageNumber < 1) pageNumber = 1; | |
if (pageSize < 1) pageSize = 10; | |
pageSize = Math.Min(pageSize, MaxPageSize); // Limit pageSize to MaxPageSize | |
// var quotes = await db.Quotes | |
// .Skip((pageNumber - 1) * pageSize) | |
// .Take(pageSize) | |
// .ToListAsync(); | |
var quotes = GlobalData.Quotes | |
.Skip((pageNumber - 1) * pageSize) | |
.Take(pageSize) | |
.ToList(); | |
return Results.Ok(quotes); | |
}); | |
app.MapGet("/quotes/{id}", async (int id, QuoteDbContext db) => | |
await db.Quotes.FindAsync(id) is Quote quote | |
? Results.Ok(quote) | |
: Results.NotFound("Quote not found")); | |
app.MapPost("/quotes", async (Quote newQuote, QuoteDbContext db) => | |
{ | |
db.Quotes.Add(newQuote); | |
await db.SaveChangesAsync(); | |
return Results.Created($"/quotes/{newQuote.Id}", newQuote); | |
}); | |
app.MapPut("/quotes/{id}", async (int id, Quote updatedQuote, QuoteDbContext db) => | |
{ | |
var quote = await db.Quotes.FindAsync(id); | |
if (quote is null) return Results.NotFound("Quote not found"); | |
quote.Author = updatedQuote.Author; | |
quote.QuoteText = updatedQuote.QuoteText; | |
quote.Source = updatedQuote.Source; | |
quote.Book = updatedQuote.Book; | |
quote.Categories = updatedQuote.Categories; | |
quote.Url = updatedQuote.Url; | |
quote.Isbn = updatedQuote.Isbn; | |
quote.Language = updatedQuote.Language; | |
quote.OriginalLanguage = updatedQuote.OriginalLanguage; | |
await db.SaveChangesAsync(); | |
return Results.NoContent(); | |
}); | |
app.MapDelete("/quotes/{id}", async (int id, QuoteDbContext db) => | |
{ | |
var quote = await db.Quotes.FindAsync(id); | |
if (quote is null) return Results.NotFound("Quote not found"); | |
db.Quotes.Remove(quote); | |
await db.SaveChangesAsync(); | |
return Results.NoContent(); | |
}); | |
// Random quote endpoint | |
app.MapGet("/quotes/random", async (QuoteDbContext db) => | |
{ | |
var quoteCount = GlobalData.Quotes.Count(); //await db.Quotes.CountAsync(); | |
if (quoteCount == 0) | |
return Results.NotFound("No quotes available."); | |
var random = new Random(); | |
var randomIndex = random.Next(quoteCount); | |
var randomQuote = GlobalData.Quotes[randomIndex]; //await db.Quotes.Skip(randomIndex).Take(1).FirstOrDefaultAsync(); | |
return randomQuote != null ? Results.Ok(randomQuote) : Results.NotFound("No quote found."); | |
}); | |
// Search quotes by author, categories, book, or quoteText with pagination | |
app.MapGet("/quotes/search", async (string query, QuoteDbContext db, int pageNumber = 1, int pageSize = 10) => | |
{ | |
var queryQuotes = GlobalData.Quotes.AsQueryable(); //db.Quotes.AsQueryable(); | |
if (!string.IsNullOrWhiteSpace(query)) | |
{ | |
query = query.ToLower(); | |
queryQuotes = queryQuotes.Where(q => | |
q.Author.ToLower().Contains(query) || | |
(q.Categories != null && q.Categories.ToLower().Contains(query)) || | |
(q.Book != null && q.Book.ToLower().Contains(query)) || | |
q.QuoteText.ToLower().Contains(query) | |
); | |
} | |
if (pageNumber < 1) pageNumber = 1; | |
if (pageSize < 1) pageSize = 10; | |
pageSize = Math.Min(pageSize, MaxPageSize); // Limit pageSize to MaxPageSize | |
var paginatedQuotes = queryQuotes | |
.Skip((pageNumber - 1) * pageSize) | |
.Take(pageSize) | |
.ToList(); | |
return paginatedQuotes.Any() ? Results.Ok(paginatedQuotes) : Results.NotFound("No matching quotes found."); | |
}); | |
app.MapGet("/health", () => Results.Ok(new { Status = "Healthy", Timestamp = DateTime.UtcNow })); | |
async Task SaveSource1Async(string jsonFilePath, QuoteDbContext db) | |
{ | |
var path = Path.Combine(Directory.GetCurrentDirectory(), "data", jsonFilePath); | |
if (!File.Exists(path)) | |
throw new FileNotFoundException("The JSON file for seeding is missing."); | |
// var jsonString = await File.ReadAllTextAsync(path); | |
// // Deserialize JSON to a list of dictionaries | |
// var dictionaryList = JsonSerializer.Deserialize<List<Dictionary<string, object>>>(jsonString); | |
// if (dictionaryList == null) | |
// throw new InvalidOperationException("Failed to deserialize JSON into dictionary list."); | |
// Fields | |
// ========== | |
// Quote (string) | |
// Author (string) | |
// Tags (list<string>) | |
// Category (string) | |
// foreach (var dictionary in dictionaryList) | |
// { | |
// var tags = ""; | |
// if(dictionary.TryGetValue("Tags", out var categories)) { | |
// var list = JsonSerializer.Deserialize<List<string>>(categories.ToString()); | |
// tags = String.Join(",", list.Select(x=> x.Trim().Trim('"'))); | |
// } | |
// var quote = new Quote | |
// { | |
// Author = dictionary.TryGetValue("Author", out var author) ? author.ToString() : null, | |
// QuoteText = dictionary.TryGetValue("Quote", out var quoteText) ? quoteText.ToString() : null, | |
// Categories = tags, | |
// }; | |
// db.Quotes.Add(quote); | |
// } | |
// TODO: import data and sanitize relevant fields: Trim and Trim('"') | |
//save for each seed file | |
await db.SaveChangesAsync(); | |
} | |
async Task SaveSource2Async(string jsonFile, QuoteDbContext db) | |
{ | |
var path = Path.Combine(Directory.GetCurrentDirectory(), "data", jsonFile); | |
if (!File.Exists(path)) | |
throw new FileNotFoundException("The JSON file for seeding is missing."); | |
// Fields | |
// ========== | |
// content (string) | |
// author (string) | |
// tags (list<string>) | |
// TODO: import data and sanitize relevant fields: Trim and Trim('"') | |
//save for each seed file | |
await db.SaveChangesAsync(); | |
} | |
async Task SaveSource3Async(string csvFile, QuoteDbContext db) | |
{ | |
var path = Path.Combine(Directory.GetCurrentDirectory(), "data", csvFile); | |
if (!File.Exists(path)) | |
throw new FileNotFoundException("The CSV file for seeding is missing."); | |
// Fields | |
// ========== | |
// quote (string) | |
// author (string) | |
// category (string) | |
// TODO: import data and sanitize relevant fields: Trim and Trim('"') | |
//save for each seed file | |
await db.SaveChangesAsync(); | |
} | |
// Seed database | |
async Task SeedDatabase(QuoteDbContext db) | |
{ | |
if (await db.Quotes.AnyAsync()) | |
return; // Database is already seeded | |
await SaveSource1Async("source1.json", db); | |
await SaveSource2Async("source2.json", db); | |
await SaveSource3Async("source3.csv", db); | |
} | |
using (var scope = app.Services.CreateScope()) | |
{ | |
var db = scope.ServiceProvider.GetRequiredService<QuoteDbContext>(); | |
db.Database.EnsureCreated(); | |
GlobalData.Quotes = await db.Quotes.ToListAsync(); | |
await SeedDatabase(db); | |
} | |
app.Run(); | |