upupa_dataist_ir/internal/api/handlers.go

252 lines
6.7 KiB
Go

package api
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"upupa_dataist_ir/internal/store"
"upupa_dataist_ir/pkg/models"
"github.com/go-chi/chi/v5"
)
type API struct {
Collections store.CollectionRepository
Records store.RecordRepository
}
func NewAPI(colRepo store.CollectionRepository, recRepo store.RecordRepository) *API {
return &API{
Collections: colRepo,
Records: recRepo,
}
}
func (a *API) HealthCheck(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("upupa_dataist_ir is running!"))
}
func (a *API) CreateRecordHandler(w http.ResponseWriter, r *http.Request) {
collectionName := chi.URLParam(r, "name")
collection, err := a.Collections.GetByName(collectionName)
if err != nil {
if err == sql.ErrNoRows {
http.Error(w, fmt.Sprintf("Collection '%s' not found.", collectionName), http.StatusNotFound)
return
}
log.Printf("Error retrieving collection: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
var recordData map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&recordData); err != nil {
http.Error(w, "Invalid JSON payload", http.StatusBadRequest)
return
}
newRecord := models.Record{
ID: models.NewID(),
CollectionID: collection.ID,
Data: recordData,
}
createdRecord, err := a.Records.CreateRecord(newRecord)
if err != nil {
log.Printf("Error creating record: %v", err)
http.Error(w, "Failed to create record", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(createdRecord)
}
func (a *API) GetAllRecordsHandler(w http.ResponseWriter, r *http.Request) {
collectionName := chi.URLParam(r, "name")
collection, err := a.Collections.GetByName(collectionName)
if err != nil {
if err == sql.ErrNoRows {
http.Error(w, fmt.Sprintf("Collection '%s' not found.", collectionName), http.StatusNotFound)
return
}
log.Printf("Error retrieving collection: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
queryParams := r.URL.Query()
filters := make(map[string]interface{})
for key, values := range queryParams {
if key != "limit" && key != "offset" && key != "orderBy" && len(values) > 0 {
filters[key] = values[0]
}
}
var finalFilters map[string]interface{} = nil
if len(filters) > 0 {
finalFilters = filters
}
limit := getIntQueryParam(queryParams.Get("limit"), 50)
offset := getIntQueryParam(queryParams.Get("offset"), 0)
orderBy := queryParams.Get("orderBy")
if limit < 0 || limit > 100 {
limit = 50
}
if offset < 0 {
offset = 0
}
records, err := a.Records.GetAll(collection.ID, finalFilters, orderBy, limit, offset)
if err != nil {
log.Printf("Error retrieving records: %v", err)
http.Error(w, "Failed to retrieve records", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
response := map[string]interface{}{
"items": records,
"totalItems": len(records),
"limit": limit,
"offset": offset,
"orderBy": orderBy,
}
json.NewEncoder(w).Encode(response)
}
func (a *API) GetRecordByIDHandler(w http.ResponseWriter, r *http.Request) {
collectionName := chi.URLParam(r, "name")
recordID := chi.URLParam(r, "id")
collection, err := a.Collections.GetByName(collectionName)
if err != nil {
if err == sql.ErrNoRows {
http.Error(w, fmt.Sprintf("Collection '%s' not found.", collectionName), http.StatusNotFound)
return
}
log.Printf("Error retrieving collection: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
record, err := a.Records.GetByID(collection.ID, recordID)
if err != nil {
if err == sql.ErrNoRows {
http.Error(w, fmt.Sprintf("Record with ID '%s' not found.", recordID), http.StatusNotFound)
return
}
log.Printf("Error retrieving record: %v", err)
http.Error(w, "Failed to retrieve record", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(record)
}
func (a *API) UpdateRecordHandler(w http.ResponseWriter, r *http.Request) {
collectionName := chi.URLParam(r, "name")
recordID := chi.URLParam(r, "id")
collection, err := a.Collections.GetByName(collectionName)
if err != nil {
if err == sql.ErrNoRows {
http.Error(w, fmt.Sprintf("Collection '%s' not found.", collectionName), http.StatusNotFound)
return
}
log.Printf("Error retrieving collection: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
var updatedData map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&updatedData); err != nil {
http.Error(w, "Invalid JSON payload", http.StatusBadRequest)
return
}
recordToUpdate := models.Record{
ID: recordID,
CollectionID: collection.ID,
Data: updatedData,
}
if err := a.Records.Update(recordToUpdate); err != nil {
if err == sql.ErrNoRows {
http.Error(w, fmt.Sprintf("Record with ID '%s' not found.", recordID), http.StatusNotFound)
return
}
log.Printf("Error updating record: %v", err)
http.Error(w, "Failed to update record", http.StatusInternalServerError)
return
}
updatedRecord, err := a.Records.GetByID(collection.ID, recordID)
if err != nil {
log.Printf("Error fetching updated record: %v", err)
http.Error(w, "Record updated, but failed to fetch final data.", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(updatedRecord)
}
func (a *API) DeleteRecordHandler(w http.ResponseWriter, r *http.Request) {
collectionName := chi.URLParam(r, "name")
recordID := chi.URLParam(r, "id")
collection, err := a.Collections.GetByName(collectionName)
if err != nil {
if err == sql.ErrNoRows {
http.Error(w, fmt.Sprintf("Collection '%s' not found.", collectionName), http.StatusNotFound)
return
}
log.Printf("Error retrieving collection: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
if err := a.Records.Delete(collection.ID, recordID); err != nil {
if err == sql.ErrNoRows {
http.Error(w, fmt.Sprintf("Record with ID '%s' not found.", recordID), http.StatusNotFound)
return
}
log.Printf("Error deleting record: %v", err)
http.Error(w, "Failed to delete record", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}
func getIntQueryParam(param string, defaultValue int) int {
if param == "" {
return defaultValue
}
val, err := strconv.Atoi(param)
if err != nil {
return defaultValue
}
return val
}