252 lines
6.7 KiB
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
|
|
}
|