Skip to content

Commit e271ed8

Browse files
Merge pull request #6 from CS3219-AY2425S1/titus/add-id-ref
feat: add question id, ref, createdAt
2 parents 061ba70 + 1aec715 commit e271ed8

File tree

7 files changed

+113
-27
lines changed

7 files changed

+113
-27
lines changed
Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package handlers
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
7+
"log"
68
"net/http"
79
"question-service/models"
810
"question-service/utils"
11+
12+
"cloud.google.com/go/firestore"
13+
"google.golang.org/api/iterator"
914
)
1015

1116
func (s *Service) CreateQuestion(w http.ResponseWriter, r *http.Request) {
@@ -18,23 +23,71 @@ func (s *Service) CreateQuestion(w http.ResponseWriter, r *http.Request) {
1823
return
1924
}
2025

21-
// Update database
22-
docRef, _, err := s.Client.Collection("questions").Add(ctx, map[string]interface{}{
23-
"title": question.Title,
24-
"description": question.Description,
25-
"complexity": question.Complexity,
26-
"categories": question.Categories,
26+
// Validation
27+
// TODO: Duplicate checking for question name
28+
29+
// Reference to the document where we store the last ID
30+
counterDocRef := s.Client.Collection("counters").Doc("questions")
31+
32+
// Firestore transaction to implement auto-increment
33+
err := s.Client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
34+
doc, err := tx.Get(counterDocRef)
35+
if err != nil {
36+
return err
37+
}
38+
39+
// Get the current value of the counter
40+
currentCounter := doc.Data()["count"].(int64)
41+
newCounter := currentCounter + 1
42+
43+
// Update the counter in Firestore
44+
if err := tx.Set(counterDocRef, map[string]interface{}{
45+
"count": newCounter,
46+
}); err != nil {
47+
return err
48+
}
49+
50+
// Use the newCounter as the ID for the new document
51+
docRef, _, err := s.Client.Collection("questions").Add(ctx, map[string]interface{}{
52+
"id": newCounter,
53+
"title": question.Title,
54+
"description": question.Description,
55+
"complexity": question.Complexity,
56+
"categories": question.Categories,
57+
"createdAt": firestore.ServerTimestamp,
58+
})
59+
if err != nil {
60+
http.Error(w, "Error adding question", http.StatusInternalServerError)
61+
return err
62+
}
63+
64+
// Get data
65+
doc, err = docRef.Get(ctx)
66+
if err != nil {
67+
if err != iterator.Done {
68+
http.Error(w, "Question not found", http.StatusNotFound)
69+
return err
70+
}
71+
http.Error(w, "Failed to get question", http.StatusInternalServerError)
72+
return err
73+
}
74+
75+
// Map data
76+
question.DocRefID = doc.Ref.ID
77+
if err := doc.DataTo(&question); err != nil {
78+
http.Error(w, "Failed to map question data", http.StatusInternalServerError)
79+
return err
80+
}
81+
82+
return nil
2783
})
2884
if err != nil {
29-
http.Error(w, "Error adding question", http.StatusInternalServerError)
85+
log.Fatalf("Trasaction failed: %v", err)
3086
return
3187
}
3288

33-
// Map data
34-
question.ID = docRef.ID
35-
3689
w.Header().Set("Content-Type", "application/json")
3790
json.NewEncoder(w).Encode(question)
3891

39-
fmt.Fprintf(w, "Question with ID %s created successfully", question.ID)
92+
fmt.Fprintf(w, "Question with ID %s created successfully", question.DocRefID)
4093
}

services/question-service/handlers/delete.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ func (s *Service) DeleteQuestion(w http.ResponseWriter, r *http.Request) {
1313
ctx := r.Context()
1414

1515
// Parse request
16-
id := chi.URLParam(r, "id")
16+
docRefID := chi.URLParam(r, "docRefID")
1717

18+
// Reference document
19+
docRef := s.Client.Collection("questions").Doc(docRefID)
20+
21+
// Validation
1822
// Check if exists
19-
_, err := s.Client.Collection("questions").Doc(id).Get(ctx)
23+
_, err := docRef.Get(ctx)
2024
if err != nil {
2125
if status.Code(err) == codes.NotFound {
2226
http.Error(w, "Question not found", http.StatusNotFound)
@@ -27,11 +31,11 @@ func (s *Service) DeleteQuestion(w http.ResponseWriter, r *http.Request) {
2731
}
2832

2933
// Update database
30-
_, err = s.Client.Collection("questions").Doc(id).Delete(ctx)
34+
_, err = docRef.Delete(ctx)
3135
if err != nil {
3236
http.Error(w, "Error deleting question", http.StatusInternalServerError)
3337
return
3438
}
3539

36-
fmt.Fprintf(w, "Question with ID %s deleted successfully", id)
40+
fmt.Fprintf(w, "Question with ID %s deleted successfully", docRefID)
3741
}

services/question-service/handlers/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func (s *Service) ListQuestions(w http.ResponseWriter, r *http.Request) {
2727

2828
// Map data
2929
var question models.Question
30-
question.ID = doc.Ref.ID
30+
question.DocRefID = doc.Ref.ID
3131
if err := doc.DataTo(&question); err != nil {
3232
http.Error(w, "Failed to parse question", http.StatusInternalServerError)
3333
return

services/question-service/handlers/read.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ func (s *Service) ReadQuestion(w http.ResponseWriter, r *http.Request) {
1313
ctx := r.Context()
1414

1515
// Parse request
16-
id := chi.URLParam(r, "id")
16+
docRefID := chi.URLParam(r, "docRefID")
17+
18+
// Reference document
19+
docRef := s.Client.Collection("questions").Doc(docRefID)
1720

1821
// Get data
19-
doc, err := s.Client.Collection("questions").Doc(id).Get(ctx)
22+
doc, err := docRef.Get(ctx)
2023
if err != nil {
2124
if err != iterator.Done {
2225
http.Error(w, "Question not found", http.StatusNotFound)
@@ -28,7 +31,7 @@ func (s *Service) ReadQuestion(w http.ResponseWriter, r *http.Request) {
2831

2932
// Map data
3033
var question models.Question
31-
question.ID = doc.Ref.ID
34+
question.DocRefID = doc.Ref.ID
3235
if err := doc.DataTo(&question); err != nil {
3336
http.Error(w, "Failed to map question data", http.StatusInternalServerError)
3437
return

services/question-service/handlers/update.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"cloud.google.com/go/firestore"
1111
"github.com/go-chi/chi/v5"
12+
"google.golang.org/api/iterator"
1213
"google.golang.org/grpc/codes"
1314
"google.golang.org/grpc/status"
1415
)
@@ -17,15 +18,19 @@ func (s *Service) UpdateQuestion(w http.ResponseWriter, r *http.Request) {
1718
ctx := r.Context()
1819

1920
// Parse request
20-
id := chi.URLParam(r, "id")
21+
docRefID := chi.URLParam(r, "docRefID")
2122
var question models.Question
2223
if err := utils.DecodeJSONBody(w, r, &question); err != nil {
2324
http.Error(w, err.Error(), http.StatusBadRequest)
2425
return
2526
}
2627

28+
// Reference document
29+
docRef := s.Client.Collection("questions").Doc(docRefID)
30+
31+
// Validation
2732
// Check if exists
28-
_, err := s.Client.Collection("questions").Doc(id).Get(ctx)
33+
_, err := docRef.Get(ctx)
2934
if err != nil {
3035
if status.Code(err) == codes.NotFound {
3136
http.Error(w, "Question not found", http.StatusNotFound)
@@ -35,7 +40,7 @@ func (s *Service) UpdateQuestion(w http.ResponseWriter, r *http.Request) {
3540
return
3641
}
3742

38-
// Prepare the update data
43+
// Prepare the update data.
3944
updates := []firestore.Update{
4045
{Path: "title", Value: question.Title},
4146
{Path: "description", Value: question.Description},
@@ -44,17 +49,32 @@ func (s *Service) UpdateQuestion(w http.ResponseWriter, r *http.Request) {
4449
}
4550

4651
// Update database
47-
_, err = s.Client.Collection("questions").Doc(id).Update(ctx, updates)
52+
_, err = docRef.Update(ctx, updates)
4853
if err != nil {
4954
http.Error(w, "Error updating question", http.StatusInternalServerError)
5055
return
5156
}
5257

58+
// Get data
59+
doc, err := docRef.Get(ctx)
60+
if err != nil {
61+
if err != iterator.Done {
62+
http.Error(w, "Question not found", http.StatusNotFound)
63+
return
64+
}
65+
http.Error(w, "Failed to get question", http.StatusInternalServerError)
66+
return
67+
}
68+
5369
// Map data
54-
question.ID = id
70+
question.DocRefID = doc.Ref.ID
71+
if err := doc.DataTo(&question); err != nil {
72+
http.Error(w, "Failed to map question data", http.StatusInternalServerError)
73+
return
74+
}
5575

5676
w.Header().Set("Content-Type", "application/json")
5777
json.NewEncoder(w).Encode(question)
5878

59-
fmt.Fprintf(w, "Question with ID %s updated successfully", id)
79+
fmt.Fprintf(w, "Question with ID %s updated successfully", question.DocRefID)
6080
}

services/question-service/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func main() {
5252
r.Get("/", service.ListQuestions)
5353
r.Post("/", service.CreateQuestion)
5454

55-
r.Route("/{id}", func(r chi.Router) {
55+
r.Route("/{docRefID}", func(r chi.Router) {
5656
r.Get("/", service.ReadQuestion)
5757
r.Put("/", service.UpdateQuestion)
5858
r.Delete("/", service.DeleteQuestion)
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package models
22

3+
import "time"
4+
35
// TODO: currently the Question model is a simplified model
46
type Question struct {
5-
ID string `json:"id"`
67
Title string `json:"title"`
78
Description string `json:"description"`
89
Categories []string `json:"categories"`
910
Complexity string `json:"complexity"`
11+
12+
// Special DB fields
13+
ID int64 `json:"id"`
14+
DocRefID string `json:"docRefId"` // The firestore document reference ID
15+
CreatedAt time.Time `json:"createdAt"`
1016
}

0 commit comments

Comments
 (0)