Skip to content

Add CRUD operations for Problems #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
7 changes: 7 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ func (app *App) setRouters() {
app.Get("/api/subjects/{name}", app.handleRequest(handler.GetSubject))
app.Put("/api/subjects/{name}", app.handleRequest(handler.UpdateSubject))
app.Delete("/api/subjects/{name}", app.handleRequest(handler.DeleteSubject))

// Routing for problems
app.Get("/api/problems", app.handleRequest(handler.GetProblems))
app.Get("/api/problems/{name}", app.handleRequest(handler.GetProblem))
app.Post("/api/problems", app.handleRequest(handler.CreateProblem))
app.Put("/api/problems/{name}", app.handleRequest(handler.UpdateProblem))
app.Delete("/api/problems/{name}", app.handleRequest(handler.DeleteProblem))
}

// Get wraps the router for GET method
Expand Down
154 changes: 154 additions & 0 deletions app/dbOperations/problems.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package dbOperations

import (
"errors"
"fmt"

gremcos "github.com/supplyon/gremcos"
"github.com/supplyon/gremcos/api"
"github.com/supplyon/gremcos/interfaces"

modelStorage "github.com/imeplusplus/dont-panic-api/app/modelStorage"
)

func CreateProblem(cosmos gremcos.Cosmos, problem modelStorage.Problem) (modelStorage.Problem, error) {
_, err := GetProblemByName(cosmos, problem.Name)

if err == nil {
return modelStorage.Problem{}, fmt.Errorf("there is already a problem with name %v", problem.Name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: consider using typed errors, as they might make your functions easier to test and to reuse the error for other use cases:

type MyAwesomeError struct {
    SomeField string
}

func (e MyAwesomeError) Error() string {
    return fmt.Errorf("some error occurred and the field is %s", e.SomeField)
}

func (e MyAwesomeError) Is(err error) bool {
    _, ok := err.(MyAwesomeError)
    return ok
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the tips to our notes so we can learn about them and replace in the code =]

}

g := api.NewGraph("g")

query := g.AddV("problem").Property("partitionKey", "problem")
query = addProblemVertexProperties(query, problem)

res, err := cosmos.ExecuteQuery(query)
if err != nil {
fmt.Println("Failed to execute a gremlin command", query.String())
return problem, err
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another tip here that might improve debugability:
consider using xerrors. This way you can have a builtin "stacktrace" of errors.
when you build an error like:

// imagine that err is fmt.Errorf("some stuff happened")
return xerrors.Errorf("loading some stuff: %w", err)

the final error returns "loading some stuff: some stuff happened"
and the xerrors.Is(err, target error) call returns true if either the error is either of both, so it's also useful for testing

}

problems, err := getProblemsFromResponse(res)
if len(problems) == 0 {
return modelStorage.Problem{}, err
}

return problems[0], err
}

func GetProblems(cosmos gremcos.Cosmos) ([]modelStorage.Problem, error) {
g := api.NewGraph("g")

query := g.V().HasLabel("problem")
res, err := cosmos.ExecuteQuery(query)
if err != nil {
fmt.Println("Failed to execute a gremlin command", query.String())
return nil, err
}

return getProblemsFromResponse(res)
}

func GetProblemByName(cosmos gremcos.Cosmos, name string) (modelStorage.Problem, error) {
var problem modelStorage.Problem
g := api.NewGraph("g")
query := g.V().HasLabel("problem").Has("name", name)

res, err := cosmos.ExecuteQuery(query)
if err != nil {
fmt.Println("Failed to execute a gremlin command", query.String())
return problem, err
}

problems, err := getProblemsFromResponse(res)
if len(problems) == 0 {
return modelStorage.Problem{}, err
}

return problems[0], err
}

func UpdateProblem(cosmos gremcos.Cosmos, problem modelStorage.Problem, name string) (modelStorage.Problem, error) {
if problem.Name != name {
_, err := GetProblemByName(cosmos, problem.Name)
if err == nil {
return modelStorage.Problem{}, fmt.Errorf("there is already a problem with name %v. can't rename in this case", problem.Name)
}
}

oldProblem, err := GetProblemByName(cosmos, name)
if err != nil {
return oldProblem, fmt.Errorf("there is no problem with name '%v' to update in the database", name)
}

g := api.NewGraph("g")
query := addProblemVertexProperties(g.VByStr(oldProblem.Id), problem)

res, err := cosmos.ExecuteQuery(query)
if err != nil {
fmt.Println("Failed to execute a gremlin command", query.String())
return problem, err
}

problems, err := getProblemsFromResponse(res)
if len(problems) == 0 {
return modelStorage.Problem{}, err
}

return problems[0], err
}

func DeleteProblem(cosmos gremcos.Cosmos, name string) error {
_, err := GetProblemByName(cosmos, name)

if err != nil {
return fmt.Errorf("there is no problem with name '%v' to delete in the database", name)
}

g := api.NewGraph("g")
query := g.V().HasLabel("problem").Has("name", name).Drop()

_, err = cosmos.ExecuteQuery(query)

return err
}

func addProblemVertexProperties(vertex interfaces.Vertex, problem modelStorage.Problem) interfaces.Vertex {
vertex = vertex.
Property("name", problem.Name).
Property("difficulty", problem.Difficulty).
Property("link", problem.Link)

return vertex
}

func getProblemsFromResponse(res []interfaces.Response) ([]modelStorage.Problem, error) {
var problems []modelStorage.Problem
response := api.ResponseArray(res)
vertices, _ := response.ToVertices()

if len(vertices) == 0 {
return problems, errors.New("there is no data with type 'api.vertex' in the response. the graph query didn't return any vertex")
}

for _, v := range vertices {
problems = append(problems, vertexToProblem(v))
}

return problems, nil
}

func vertexToProblem(vertex api.Vertex) modelStorage.Problem {
var problem modelStorage.Problem

problem.Id = vertex.ID

properties := vertex.Properties
problem.Name = properties["name"][0].Value.AsString()
problem.Difficulty = int(properties["difficulty"][0].Value.AsInt32())
problem.Link = properties["link"][0].Value.AsString()
problem.PartitionKey = properties["partitionKey"][0].Value.AsString()

return problem
}
42 changes: 21 additions & 21 deletions app/dbOperations/subjects.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (
"github.com/supplyon/gremcos/api"
"github.com/supplyon/gremcos/interfaces"

"github.com/imeplusplus/dont-panic-api/app/model"
modelStorage "github.com/imeplusplus/dont-panic-api/app/modelStorage"
)

func GetSubjects(cosmos gremcos.Cosmos) ([]model.Subject, error) {
func GetSubjects(cosmos gremcos.Cosmos) ([]modelStorage.Subject, error) {
g := api.NewGraph("g")
query := g.V().HasLabel("subject")

Expand All @@ -34,32 +34,32 @@ func GetSubjects(cosmos gremcos.Cosmos) ([]model.Subject, error) {
return subjects, nil
}

func GetSubjectByName(cosmos gremcos.Cosmos, name string) (model.Subject, error) {
var subject model.Subject
func GetSubjectByName(cosmos gremcos.Cosmos, name string) (modelStorage.Subject, error) {
var subject modelStorage.Subject
g := api.NewGraph("g")
query := g.V().HasLabel("subject").Has("name", name)

res, err := cosmos.ExecuteQuery(query)
if err != nil {
fmt.Println("Failed to execute a gremlin command " + query.String())
//logger.Error().Err(err).Msg("Failed to execute a gremlin command")
// logger.Error().Err(err).Msg("Failed to execute a gremlin command")
return subject, err
}

return getSubjectFromResponse(res)
}

func CreateSubject(cosmos gremcos.Cosmos, subject model.Subject) (model.Subject, error) {
func CreateSubject(cosmos gremcos.Cosmos, subject modelStorage.Subject) (modelStorage.Subject, error) {
_, err := GetSubjectByName(cosmos, subject.Name)

if err == nil {
return model.Subject{}, errors.New("There is already a subject with name " + subject.Name)
return modelStorage.Subject{}, errors.New("There is already a subject with name " + subject.Name)
}

g := api.NewGraph("g")

query := g.AddV("subject").Property("partitionKey", "subject")
query = addVertexProperties(query, subject)
query = addSubjectVertexProperties(query, subject)

res, err := cosmos.ExecuteQuery(query)
if err != nil {
Expand All @@ -71,15 +71,15 @@ func CreateSubject(cosmos gremcos.Cosmos, subject model.Subject) (model.Subject,
return getSubjectFromResponse(res)
}

func UpdateSubject(cosmos gremcos.Cosmos, subject model.Subject, name string) (model.Subject, error) {
func UpdateSubject(cosmos gremcos.Cosmos, subject modelStorage.Subject, name string) (modelStorage.Subject, error) {
oldSubject, err := GetSubjectByName(cosmos, name)

if err != nil {
return model.Subject{}, errors.New("There is no subject with name " + oldSubject.Name)
return modelStorage.Subject{}, errors.New("There is no subject with name " + oldSubject.Name)
}

g := api.NewGraph("g")
query := addVertexProperties(g.VByStr(oldSubject.Id), subject)
query := addSubjectVertexProperties(g.VByStr(oldSubject.Id), subject)

res, err := cosmos.ExecuteQuery(query)
if err != nil {
Expand All @@ -100,7 +100,7 @@ func DeleteSubject(cosmos gremcos.Cosmos, name string) error {
return err
}

func addVertexProperties(vertex interfaces.Vertex, subject model.Subject) interfaces.Vertex {
func addSubjectVertexProperties(vertex interfaces.Vertex, subject modelStorage.Subject) interfaces.Vertex {
vertex = vertex.
Property("name", subject.Name).
Property("difficulty", subject.Difficulty).
Expand All @@ -117,25 +117,25 @@ func addVertexProperties(vertex interfaces.Vertex, subject model.Subject) interf
return vertex
}

func getSubjectFromResponse(res []interfaces.Response) (model.Subject, error) {
var subject model.Subject
func getSubjectFromResponse(res []interfaces.Response) (modelStorage.Subject, error) {
var subject modelStorage.Subject
response := api.ResponseArray(res)
vertices, err := response.ToVertices()
vertices, _ := response.ToVertices()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity: why is the second return ignored?


if len(vertices) == 0 {
return subject, errors.New("There is no vertex in the response")
return subject, errors.New("there is no vertex in the response")
}

subject, err = vertexToSubject(vertices[0])
subject, err := vertexToSubject(vertices[0])
if err != nil {
return subject, err
}

return subject, nil
}

func verticesToSubjects(vertices []api.Vertex) []model.Subject {
subjects := []model.Subject{}
func verticesToSubjects(vertices []api.Vertex) []modelStorage.Subject {
subjects := []modelStorage.Subject{}

for _, v := range vertices {
subject, err := vertexToSubject(v)
Expand All @@ -147,8 +147,8 @@ func verticesToSubjects(vertices []api.Vertex) []model.Subject {
return subjects
}

func vertexToSubject(vertex api.Vertex) (model.Subject, error) {
var subject model.Subject
func vertexToSubject(vertex api.Vertex) (modelStorage.Subject, error) {
var subject modelStorage.Subject

subject.Id = vertex.ID

Expand Down
Loading