Skip to content
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