Skip to content

Commit

Permalink
feat: SJRA-51 user set model and get user set by id endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
celinepelletier committed Mar 1, 2025
1 parent 112614c commit 47566f9
Show file tree
Hide file tree
Showing 13 changed files with 542 additions and 16 deletions.
21 changes: 21 additions & 0 deletions backend/cmd/api/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,27 @@ func assertPostInterpretationSomatic(t *testing.T, repo repository.Interpretatio
return nil
}

func assertGetUserSet(t *testing.T, repo repository.UserSetsDAO, userSetId string, status int, expected string) {
router := gin.Default()
router.GET("/users/sets/:user_set_id", server.GetUserSet(repo))

req, _ := http.NewRequest("GET", fmt.Sprintf("/users/sets/%s", userSetId), bytes.NewBuffer([]byte("{}")))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)

assert.Equal(t, status, w.Code)
assert.JSONEq(t, expected, w.Body.String())
}

func Test_GetUserSet(t *testing.T) {
testutils.ParallelPostgresTestWithDb(t, func(t *testing.T, db *gorm.DB) {
pubmedService := &MockExternalClient{}
repo := repository.NewPostgresRepository(db, pubmedService)
// not found
assertGetUserSet(t, repo.UserSets, "bce3b031-c691-4680-878f-f43d661f9a9f", http.StatusNotFound, `{"error":"not found"}`)
})
}

func TestMain(m *testing.M) {
testutils.StartAllContainers()
code := m.Run()
Expand Down
7 changes: 5 additions & 2 deletions backend/cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func init() {
func main() {
flag.Parse()
defer glog.Flush()

database.MigrateWithEnvDefault()

// Initialize database connection
Expand Down Expand Up @@ -77,7 +77,7 @@ func main() {
AllowHeaders: []string{"Accept", "Authorization", "Content-Type"},
AllowCredentials: true, // Enable cookies/auth
}))

occurrencesGroup := r.Group("/occurrences")

role := os.Getenv("KEYCLOAK_CLIENT_ROLE")
Expand All @@ -104,6 +104,9 @@ func main() {
interpretationsSomaticGroup.GET("", server.GetInterpretationSomatic(repoPostgres.Interpretations))
interpretationsSomaticGroup.POST("", server.PostInterpretationSomatic(repoPostgres.Interpretations))

usersGroup := r.Group("/users")
usersGroup.GET("/sets/:user_set_id", server.GetUserSet(repoPostgres.UserSets))

r.Use(gin.Recovery())
r.Run(":8090")
}
4 changes: 2 additions & 2 deletions backend/docs/docs.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions backend/docs/swagger.json

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions backend/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,26 @@ components:
- all
type: string
type: object
UserSet:
properties:
active:
type: boolean
id:
type: string
ids:
items:
type: string
type: array
uniqueItems: false
name:
type: string
type:
type: string
updated_at:
type: string
user_id:
type: string
type: object
securitySchemes:
bearerauth:
bearerFormat: JWT
Expand Down Expand Up @@ -576,5 +596,36 @@ paths:
summary: Get API status
tags:
- status
/users/sets/{user_set_id}:
get:
description: Get user set
operationId: GetUserSet
parameters:
- description: UserSet ID
in: path
name: user_set_id
required: true
schema:
type: string
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/UserSet'
description: OK
"404":
content:
application/json:
schema:
additionalProperties:
type: string
type: object
description: Not Found
security:
- bearerauth: []
summary: Get user set by id
tags:
- user_sets
servers:
- url: /
5 changes: 3 additions & 2 deletions backend/internal/repository/postgres_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ import (
)

type PostgresRepository struct {
db *gorm.DB
db *gorm.DB
Interpretations *InterpretationsRepository
UserSets *UserSetsRepository
}

type PostgresDAO interface {
CheckDatabaseConnection() string
}

func NewPostgresRepository(db *gorm.DB, pubmedClient client.PubmedClientService) *PostgresRepository {
return &PostgresRepository{db: db, Interpretations: NewInterpretationsRepository(db, pubmedClient)}
return &PostgresRepository{db: db, Interpretations: NewInterpretationsRepository(db, pubmedClient), UserSets: NewUserSetsRepository(db)}
}

func (r *PostgresRepository) CheckDatabaseConnection() string {
Expand Down
101 changes: 101 additions & 0 deletions backend/internal/repository/user_sets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package repository

import (
"errors"
"fmt"
"github.com/Ferlab-Ste-Justine/radiant-api/internal/types"
"gorm.io/gorm"
)

type UserSetsRepository struct {
db *gorm.DB
}

type UserSetsDAO interface {
GetUserSet(userSetId string) (*types.UserSet, error)
}

func NewUserSetsRepository(db *gorm.DB) *UserSetsRepository {
return &UserSetsRepository{db: db}
}

func (r *UserSetsRepository) GetUserSet(userSetId string) (*types.UserSet, error) {
var dao types.UserSetDAO
if result := r.db.
Table(types.UserSetTable.Name).
Preload("ParticipantIds").
Preload("FileIds").
Preload("BiospecimenIds").
Preload("VariantIds").
Where("id = ?", userSetId).
First(&dao); result.Error != nil {
err := result.Error
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("error while fetching user set: %w", err)
} else {
return nil, nil
}
}
mapped, err := r.mapToUserSet(&dao)
if err != nil {
return nil, err
}
return mapped, nil
}

func (r *UserSetsRepository) mapToUserSet(dao *types.UserSetDAO) (*types.UserSet, error) {
ids := make([]string, 0)
if len(dao.ParticipantIds) > 0 {
ids = r.ParticipantIds(dao.ParticipantIds)
} else if len(dao.FileIds) > 0 {
ids = r.FileIds(dao.FileIds)
} else if len(dao.BiospecimenIds) > 0 {
ids = r.BiospecimenIds(dao.BiospecimenIds)
} else if len(dao.VariantIds) > 0 {
ids = r.VariantIds(dao.VariantIds)
}

userSet := &types.UserSet{
ID: dao.ID,
UserId: dao.UserId,
Name: dao.Name,
Type: dao.Type,
Active: dao.Active,
CreatedAt: dao.CreatedAt,
UpdatedAt: dao.UpdatedAt,
Ids: ids,
}
return userSet, nil
}

func (r *UserSetsRepository) ParticipantIds(dao []types.UserSetParticipantDAO) []string {
var list []string
for _, elem := range dao {
list = append(list, elem.ParticipantId)
}
return list
}

func (r *UserSetsRepository) FileIds(dao []types.UserSetFileDAO) []string {
var list []string
for _, elem := range dao {
list = append(list, elem.FileId)
}
return list
}

func (r *UserSetsRepository) BiospecimenIds(dao []types.UserSetBiospecimenDAO) []string {
var list []string
for _, elem := range dao {
list = append(list, elem.BiospecimenId)
}
return list
}

func (r *UserSetsRepository) VariantIds(dao []types.UserSetVariantDAO) []string {
var list []string
for _, elem := range dao {
list = append(list, elem.VariantId)
}
return list
}
40 changes: 36 additions & 4 deletions backend/internal/server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ func extractInterpretationParams(c *gin.Context) (string, string, string) {
return sequencingId, locusId, transcriptId
}

func extractUserSetParams(c *gin.Context) string {
userSetId := c.Param("user_set_id")
return userSetId
}

func fillInterpretationCommonWithContext(c *gin.Context, interpretation *types.InterpretationCommon) {
sequencingId, locusId, transcriptId := extractInterpretationParams(c)

Expand Down Expand Up @@ -222,7 +227,7 @@ func GetInterpretationGermline(repo repository.InterpretationsDAO) gin.HandlerFu
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
if (interpretation == nil) {
if interpretation == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
return
}
Expand Down Expand Up @@ -290,7 +295,7 @@ func GetInterpretationSomatic(repo repository.InterpretationsDAO) gin.HandlerFun
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
if (interpretation == nil) {
if interpretation == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
return
}
Expand Down Expand Up @@ -355,10 +360,37 @@ func GetPubmedCitation(pubmedClient client.PubmedClientService) gin.HandlerFunc
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
if (citation == nil) {
if citation == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
return
}
c.JSON(http.StatusOK, citation)
}
}
}

// GetUserSet
// @Summary Get user set by id
// @Id GetUserSet
// @Description Get user set
// @Tags user_sets
// @Security bearerauth
// @Param user_set_id path string true "UserSet ID"
// @Produce json
// @Success 200 {object} types.UserSet
// @Failure 404 {object} map[string]string
// @Router /users/sets/{user_set_id} [get]
func GetUserSet(repo repository.UserSetsDAO) gin.HandlerFunc {
return func(c *gin.Context) {
userSetId := extractUserSetParams(c)
userSet, err := repo.GetUserSet(userSetId)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
return
}
if userSet == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
return
}
c.JSON(http.StatusOK, userSet)
}
}
57 changes: 57 additions & 0 deletions backend/internal/server/handlers_user_sets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package server

import (
"bytes"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/Ferlab-Ste-Justine/radiant-api/internal/types"

"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)

func (m *MockRepository) GetUserSet(userSetId string) (*types.UserSet, error) {
if userSetId == "set1" {
return &types.UserSet{
ID: userSetId,
UserId: "user1",
Name: "set_name",
Type: "set_type",
Active: true,
UpdatedAt: time.Date(
2000, 1, 1, 0, 0, 0, 0, time.UTC),
}, nil
} else if userSetId == "set2" {
return nil, fmt.Errorf("error")
}
return nil, nil
}

func assertGetUserSet(t *testing.T, userSetId string, status int, expected string) {
repo := &MockRepository{}
router := gin.Default()
router.GET("/users/sets/:user_set_id", GetUserSet(repo))

req, _ := http.NewRequest("GET", fmt.Sprintf("/users/sets/%s", userSetId), bytes.NewBuffer([]byte("{}")))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)

assert.Equal(t, status, w.Code)
assert.JSONEq(t, expected, w.Body.String())
}

func Test_GetUserSet_ok(t *testing.T) {
assertGetUserSet(t, "set1", http.StatusOK, `{"id":"set1", "user_id":"user1", "name":"set_name", "type": "set_type", "active":true, "updated_at": "2000-01-01T00:00:00Z"}`)
}

func Test_GetUserSet_error(t *testing.T) {
assertGetUserSet(t, "set2", http.StatusInternalServerError, `{"error":"internal server error"}`)
}

func Test_GetUserSet_notFound(t *testing.T) {
assertGetUserSet(t, "set3", http.StatusNotFound, `{"error":"not found"}`)
}
Loading

0 comments on commit 47566f9

Please sign in to comment.