From c9bc8207edd849ce395195ba81422a575efb8bfa Mon Sep 17 00:00:00 2001 From: atnomoverflow Date: Thu, 18 Apr 2024 14:59:11 +0100 Subject: [PATCH] completing app api --- api/app/controllers/application.go | 186 +++++++++++------------- api/app/models/models.go | 34 ++--- api/app/types/types.go | 55 +++++++ api/infrastrucure/bunAppDatastore.go | 73 ++++++++++ api/infrastrucure/bunMemberDatastore.go | 6 +- api/main.go | 8 +- api/middleware/dumbauthz.go | 2 +- api/users/controllers/user.go | 22 +-- api/users/models/models.go | 36 ++--- api/users/types/types.go | 7 +- 10 files changed, 271 insertions(+), 158 deletions(-) create mode 100644 api/app/types/types.go create mode 100644 api/infrastrucure/bunAppDatastore.go diff --git a/api/app/controllers/application.go b/api/app/controllers/application.go index 8438b65..bb59514 100644 --- a/api/app/controllers/application.go +++ b/api/app/controllers/application.go @@ -2,17 +2,12 @@ package controllers import ( "context" - "database/sql" "encoding/json" "errors" - "fmt" "net/http" "time" "github.com/gorilla/mux" - "github.com/uptrace/bun" - "github.com/uptrace/bun/dialect/sqlitedialect" - "github.com/uptrace/bun/driver/sqliteshim" "go.uber.org/zap" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -23,6 +18,8 @@ import ( _ "github.com/swaggo/gin-swagger" appmodels "github.com/null-channel/eddington/api/app/models" + "github.com/null-channel/eddington/api/app/types" + "github.com/null-channel/eddington/api/core" pb "github.com/null-channel/eddington/api/proto/container" "github.com/null-channel/eddington/api/users/controllers" "github.com/null-channel/eddington/api/users/models" @@ -36,40 +33,35 @@ import ( kube "k8s.io/client-go/kubernetes" ) -// @BasePath /api/v1/ +type AppDatastore interface { + CreateNullApplication(ctx context.Context, nullApplication *appmodels.NullApplication) error + CreateNullApplicationService(ctx context.Context, nullApplication *appmodels.NullApplicationService) error + GetApplicationsByOrgID(ctx context.Context, orgId int64) ([]*appmodels.NullApplication, error) + GetApplicationByID(ctx context.Context, id int64) (*appmodels.NullApplication, error) + GetApplicationServiceByAppID(ctx context.Context, nullApplicationID int64) ([]*appmodels.NullApplicationService, error) + + GetAllAppSvc(ctx context.Context) ([]*appmodels.NullApplicationService, error) +} type ApplicationController struct { kube dynamic.Interface istioClient *versionedclient.Clientset userController *controllers.UserController - database *bun.DB + appDatastore AppDatastore containerServiceClient pb.ContainerServiceClient - logs *zap.SugaredLogger + logger *zap.SugaredLogger kubeClientset *kube.Clientset } -func NewApplicationController(kube dynamic.Interface, istio *versionedclient.Clientset, kcs *kube.Clientset, userContoller *controllers.UserController, containerBuildingService pb.ContainerServiceClient, logger *zap.Logger) *ApplicationController { +func NewApplicationController(kube dynamic.Interface, istio *versionedclient.Clientset, kcs *kube.Clientset, appDatastore AppDatastore, userContoller *controllers.UserController, containerBuildingService pb.ContainerServiceClient, logger *zap.Logger) *ApplicationController { // Set up a connection to the server. - sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared") - db := bun.NewDB(sqldb, sqlitedialect.New()) - if err != nil { - panic(err) - } - _, err = db.NewCreateTable().Model((*appmodels.NullApplication)(nil)).Exec(context.Background()) - if err != nil { - panic(err) - } - _, err = db.NewCreateTable().Model((*appmodels.NullApplicationService)(nil)).Exec(context.Background()) - if err != nil { - panic(err) - } return &ApplicationController{ kube: kube, userController: userContoller, - database: db, + appDatastore: appDatastore, containerServiceClient: containerBuildingService, - logs: logger.Sugar(), + logger: logger.Sugar(), istioClient: istio, kubeClientset: kcs, } @@ -77,16 +69,7 @@ func NewApplicationController(kube dynamic.Interface, istio *versionedclient.Cli func (a *ApplicationController) RegisterRoutes(router *mux.Router) { router.HandleFunc("", a.AppPOST).Methods("POST") - router.HandleFunc("/{}", a.AppGET).Methods("GET") -} - -type Application struct { - Name string `json:"name"` - Image string `json:"image"` - GitRepo string `json:"gitRepo"` - RepoType string `json:"repoType"` - ResourceGroup string `json:"resourceGroup"` - Directory string `json:"directory"` + router.HandleFunc("", a.AppGET).Methods("GET") } // AppPOST godoc @@ -100,86 +83,87 @@ type Application struct { // @Success 200 {string} Helloworld // @Router /apps/ [post] func (a ApplicationController) AppPOST(w http.ResponseWriter, r *http.Request) { + userId := r.Context().Value("user-id").(string) + + var appDTO types.Application + + err := json.NewDecoder(r.Body).Decode(&appDTO) - err := r.ParseForm() if err != nil { - http.Error(w, "Error parsing form data", http.StatusBadRequest) + a.logger.Error(err) + http.Error(w, "Decode error! Please check your JSON formatting.", http.StatusBadRequest) return } - app := Application{ - Name: r.FormValue("name"), - Image: r.FormValue("image"), - GitRepo: r.FormValue("gitRepo"), - RepoType: r.FormValue("repoType"), - ResourceGroup: r.FormValue("resourceGroup"), - Directory: r.FormValue("directory"), + if err := appDTO.Validate(); err != nil { + errorMessage := types.ConstructErrorMessages(err) + core.ValidationErrors(w, errorMessage) + return } - fmt.Println("app: ", app) - userId := r.Context().Value("user-id").(string) - - if err != nil { - a.logs.Errorf("Failed to parse user id", - "user-id:", r.Context().Value("user-id")) - } // get user namespace - userContext, err := a.userController.GetUserContext(r.Context(), userId) + org, err := a.userController.GetUserContext(r.Context(), userId) + if err != nil { - a.logs.Errorw("Failed to get user context for the user controller", + a.logger.Errorw("Failed to get user context for the user controller", "user-id", userId, "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } - fmt.Println(userContext) - resourceGroup, rgId, err := getResourceGroupName(userContext.ResourceGroups, app.ResourceGroup) + resourceGroup, rgId, err := getResourceGroupName(org.ResourceGroups, appDTO.ResourceGroup) if err != nil { - - a.logs.Warnw("Resource Group not found", "Resource Group:", resourceGroup) + a.logger.Warnw("Resource Group not found", "Resource Group:", resourceGroup) http.Error(w, "Resource group not found", http.StatusNotFound) return } //TODO: accept the revision _, err = a.containerServiceClient.CreateContainer(r.Context(), &pb.CreateContainerRequest{ - RepoURL: app.GitRepo, - Type: pb.Language(pb.Language_value[app.RepoType]), + RepoURL: appDTO.GitRepo, + Type: pb.Language(pb.Language_value[appDTO.RepoType.String()]), ResourceGroup: rgId, - Directory: app.Directory, + Directory: appDTO.Directory, Rev: "main", }) if err != nil { - a.logs.Errorf("Failed to create container", + a.logger.Errorf("Failed to create container", "user-id", userId, "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } - namespace := userContext.Name + resourceGroup + namespace := org.Name + resourceGroup //TODO: short sha for build id? - nullApplication := getNullApplication(app, userContext, rgId, namespace, "12345") - + nullApplication, nullApplicationService := getNullApplication(appDTO, org, rgId, namespace, "12345") //TODO: Save to database! - _, err = a.database.NewInsert().Model(nullApplication).Exec(context.Background()) - + err = a.appDatastore.CreateNullApplication(r.Context(), nullApplication) if err != nil { - a.logs.Errorf("Failed to create container", + a.logger.Errorf("Failed to create container", "user-id", userId, "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } - + nullApplicationService.NullApplicationID = nullApplication.ID + err = a.appDatastore.CreateNullApplicationService(r.Context(), nullApplicationService) + if err != nil { + a.logger.Errorf("Failed to create container", + "user-id", userId, + "error", err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + a.logger.Info(nullApplicationService.ID) go func() { keepChecking := true var status *pb.BuildStatusResponse for keepChecking { - status, err = a.containerServiceClient.BuildStatus(context.Background(), &pb.BuildStatusRequest{Repo: app.GitRepo, Directory: app.Directory}) + status, err = a.containerServiceClient.BuildStatus(context.Background(), &pb.BuildStatusRequest{Repo: appDTO.GitRepo, Directory: appDTO.Directory}) if err != nil { - a.logs.Errorw("Failed to get build status", "error", err) + a.logger.Errorw("Failed to get build status", "error", err) time.Sleep(5 * time.Second) continue } @@ -193,27 +177,27 @@ func (a ApplicationController) AppPOST(w http.ResponseWriter, r *http.Request) { // Create the services for our app. // TODO: This should all be in the null operator. // What was I thinking?!?! - serviceName := app.Name + "-service" - vitrualServiceName := app.Name + "-virtual-service" + serviceName := appDTO.Name + "-service" + vitrualServiceName := appDTO.Name + "-virtual-service" appService := createService(userId, serviceName, namespace) _, err = a.kubeClientset.CoreV1().Services(namespace).Apply(context.Background(), appService, metav1.ApplyOptions{FieldManager: "application/apply-patch"}) if err != nil { - a.logs.Errorw(err.Error()) + a.logger.Errorw(err.Error()) } - virtualService := getVirtualService(userId, app.Name, serviceName, vitrualServiceName, namespace) + virtualService := getVirtualService(userId, appDTO.Name, serviceName, vitrualServiceName, namespace) _, err = a.istioClient.NetworkingV1alpha3().VirtualServices(namespace).Apply(context.TODO(), virtualService, metav1.ApplyOptions{FieldManager: "application/apply-patch"}) if err != nil { - a.logs.Errorw(err.Error()) + a.logger.Errorw(err.Error()) } // Create the operator object - deployment := getApplication(app.Name, namespace, "nullchannel/"+app.Image) - _, err = a.kube.Resource(getDeploymentGVR()).Namespace(namespace).Apply(context.Background(), app.Name, deployment, v1.ApplyOptions{FieldManager: "application/apply-patch"}) + deployment := getApplication(appDTO.Name, namespace, "nullchannel/"+appDTO.Image) + _, err = a.kube.Resource(getDeploymentGVR()).Namespace(namespace).Apply(context.Background(), appDTO.Name, deployment, v1.ApplyOptions{FieldManager: "application/apply-patch"}) if err != nil { - a.logs.Errorw("Failed to apply CRD for new application", + a.logger.Errorw("Failed to apply CRD for new application", "user-id", userId, "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) @@ -271,30 +255,28 @@ func getVirtualService(userId, appName, service, virtualServiceName, namespace s }) } -func getNullApplication(app Application, org *models.Org, resourceGroupId int64, namespace string, buildID string) *appmodels.NullApplication { +func getNullApplication(app types.Application, org *models.Org, resourceGroupId int64, namespace string, buildID string) (*appmodels.NullApplication, *appmodels.NullApplicationService) { return &appmodels.NullApplication{ - OrgID: org.ID, - Name: app.Name, - ResourceGroupID: resourceGroupId, - Namespace: namespace, - NullApplicationService: []*appmodels.NullApplicationService{ - { - Type: appmodels.ContainerImage, - GitRepo: app.GitRepo, - Name: app.Name, - Image: app.Image, - Cpu: "100m", - Memory: "100Mi", - Storage: "1Gi", - BuildID: buildID, - }, - }, - } + OrgID: org.ID, + Name: app.Name, + ResourceGroupID: resourceGroupId, + Namespace: namespace, + }, &appmodels.NullApplicationService{ + Type: appmodels.ContainerImage, + GitRepo: app.GitRepo, + Name: app.Name, + Image: app.Image, + Cpu: "100m", + Memory: "100Mi", + Storage: "1Gi", + BuildID: buildID, + } + } func getResourceGroupName(resourceGroups []*models.ResourceGroup, requested string) (string, int64, error) { - if requested == "" { - requested = "default" + if requested == "" && len(resourceGroups) > 0 { + requested = "Default" } for _, group := range resourceGroups { @@ -329,7 +311,7 @@ func (a ApplicationController) AppGET(w http.ResponseWriter, r *http.Request) { // get user org org, err := a.userController.GetUserContext(r.Context(), userId) if err != nil { - a.logs.Errorw("Failed to get user org for the user controller", + a.logger.Errorw("Failed to get user org for the user controller", "user-id", userId, "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) @@ -338,7 +320,7 @@ func (a ApplicationController) AppGET(w http.ResponseWriter, r *http.Request) { nullApplications, err := a.GetApplications(r.Context(), org.ID) if err != nil { - a.logs.Errorw("Failed to get applications for the user", + a.logger.Errorw("Failed to get applications for the user", "user-id", userId, "error", err) http.Error(w, "Internal server error", http.StatusInternalServerError) @@ -350,8 +332,7 @@ func (a ApplicationController) AppGET(w http.ResponseWriter, r *http.Request) { } func (a ApplicationController) GetApplication(ctx context.Context, id int64) (*appmodels.NullApplication, error) { - nullApplication := &appmodels.NullApplication{} - err := a.database.NewSelect().Model(nullApplication).Where("id = ?", id).Scan(ctx) + nullApplication, err := a.appDatastore.GetApplicationByID(ctx, id) if err != nil { return nil, err } @@ -359,8 +340,7 @@ func (a ApplicationController) GetApplication(ctx context.Context, id int64) (*a } func (a ApplicationController) GetApplications(ctx context.Context, orgId int64) ([]*appmodels.NullApplication, error) { - nullApplications := []*appmodels.NullApplication{} - err := a.database.NewSelect().Model(&nullApplications).Where("org_id = ?", orgId).Scan(ctx) + nullApplications, err := a.appDatastore.GetApplicationsByOrgID(ctx, orgId) if err != nil { return nil, err } diff --git a/api/app/models/models.go b/api/app/models/models.go index 47760f9..2a46d8a 100644 --- a/api/app/models/models.go +++ b/api/app/models/models.go @@ -3,12 +3,12 @@ package models import "errors" type NullApplication struct { - ID int64 `bun:",pk,autoincrement"` - Name string `bun:"name"` - OrgID int64 `bun:"org_id"` - ResourceGroupID int64 `bun:"resource_group_id"` - Namespace string `bun:"namespace"` - NullApplicationService []*NullApplicationService `bun:"rel:has-many,join:id=null_application_id"` + ID int64 `bun:",pk,autoincrement" json:"id"` + Name string `bun:"name" json:"name"` + OrgID int64 `bun:"org_id" json:"org_id"` + ResourceGroupID int64 `bun:"resource_group_id" json:"resource_group_id"` + Namespace string `bun:"namespace" json:"namespace"` + NullApplicationService []*NullApplicationService `bun:"rel:has-many,join:id=null_application_id" json:"null_application_service"` } type BuildType int @@ -26,17 +26,17 @@ func (b BuildType) String() string { } type NullApplicationService struct { - ID int64 `bun:",pk,autoincrement"` - NullApplicationID int64 `bun:"null_application_id"` - Type BuildType `bun:"type"` - GitRepo string `bun:"gitRepo"` - GitSha string `bun:"gitSha"` - Name string `bun:"name"` - Image string `bun:"image"` - Cpu string `bun:"cpu"` - Memory string `bun:"memory"` - Storage string `bun:"storage"` - BuildID string `bun:"build_id"` + ID int64 `bun:",pk,autoincrement" json:"id"` + NullApplicationID int64 `bun:"null_application_id" json:"null_application_id"` + Type BuildType `bun:"type" json:"type"` + GitRepo string `bun:"gitRepo" json:"git_repo"` + GitSha string `bun:"gitSha" json:"git_sha"` + Name string `bun:"name" json:"name"` + Image string `bun:"image" json:"image"` + Cpu string `bun:"cpu" json:"cpu"` + Memory string `bun:"memory" json:"memory"` + Storage string `bun:"storage" json:"storage"` + BuildID string `bun:"build_id" json:"build_id"` } // Validate validates the NullApplicationService diff --git a/api/app/types/types.go b/api/app/types/types.go new file mode 100644 index 0000000..28d92db --- /dev/null +++ b/api/app/types/types.go @@ -0,0 +1,55 @@ +package types + +import ( + "fmt" + + "github.com/go-playground/validator/v10" + "github.com/null-channel/eddington/api/app/models" + userTypes "github.com/null-channel/eddington/api/users/types" +) + +var validate *validator.Validate + +type Application struct { + Name string `json:"name" validate:"required"` + Image string `json:"image"` + GitRepo string `json:"gitRepo" validate:"required"` + RepoType models.BuildType `json:"repoType" validate:"required,repoType"` + ResourceGroup string `json:"resourceGroup"` + Directory string `json:"directory" validate:"required"` +} + +func ConstructErrorMessages(err error) []userTypes.ApiError { + var apiErrors []userTypes.ApiError + for _, err := range err.(validator.ValidationErrors) { + fieldName := err.StructField() + var message string + + switch err.Tag() { + case "required": + message = fmt.Sprintf("%s is required", fieldName) + case "repoType": + message = fmt.Sprintf("%s unkown repo type.Please choose on of the following ['NodeJS', 'Go', 'Rust', 'Python', 'ContainerImage'].", err.Value()) + default: + message = fmt.Sprintf("%s is invalid", fieldName) + } + apiError := userTypes.ApiError{ + Param: fieldName, + Message: message, + } + + apiErrors = append(apiErrors, apiError) + } + return apiErrors +} +func validateRepoType(fl validator.FieldLevel) bool { + value := fl.Field().Interface().(models.BuildType) + return value.String() != "unknown" +} + +func (application *Application) Validate() error { + validate = validator.New() + validate.RegisterValidation("repoType", validateRepoType) + err := validate.Struct(application) + return err +} diff --git a/api/infrastrucure/bunAppDatastore.go b/api/infrastrucure/bunAppDatastore.go new file mode 100644 index 0000000..86de79f --- /dev/null +++ b/api/infrastrucure/bunAppDatastore.go @@ -0,0 +1,73 @@ +package infrastrucure + +import ( + "context" + + "github.com/null-channel/eddington/api/app/models" + "github.com/uptrace/bun" +) + +type BunAppDatastore struct { + db *bun.DB +} + +func NewBunAppDatastore(db *bun.DB) *BunAppDatastore { + + _, err := db.NewCreateTable().Model((*models.NullApplication)(nil)).Exec(context.Background()) + if err != nil { + panic(err) + } + + _, err = db.NewCreateTable().Model((*models.NullApplicationService)(nil)).Exec(context.Background()) + if err != nil { + panic(err) + } + + return &BunAppDatastore{ + db: db, + } +} + +func (datastore *BunAppDatastore) CreateNullApplication(ctx context.Context, nullApplication *models.NullApplication) error { + _, err := datastore.db.NewInsert().Model(nullApplication).Exec(context.Background()) + return err +} +func (datastore *BunAppDatastore) CreateNullApplicationService(ctx context.Context, nullApplicationSvc *models.NullApplicationService) error { + _, err := datastore.db.NewInsert().Model(nullApplicationSvc).Exec(context.Background()) + return err +} +func (datastore *BunAppDatastore) GetApplicationsByOrgID(ctx context.Context, orgId int64) ([]*models.NullApplication, error) { + nullApplications := []*models.NullApplication{} + err := datastore.db.NewSelect().Model(&nullApplications).Relation("NullApplicationService").Where("org_id = ?", orgId).Scan(ctx) + if err != nil { + return nil, err + } + return nullApplications, nil +} + +func (datastore *BunAppDatastore) GetApplicationByID(ctx context.Context, id int64) (*models.NullApplication, error) { + nullApplication := &models.NullApplication{} + err := datastore.db.NewSelect().Model(nullApplication).Relation("NullApplicationService").Where("id = ?", id).Scan(ctx) + if err != nil { + return nil, err + } + return nullApplication, nil +} +func (datastore *BunAppDatastore) GetAllAppSvc(ctx context.Context) ([]*models.NullApplicationService, error) { + nullApplicationService := []*models.NullApplicationService{} + err := datastore.db.NewSelect().Model(&nullApplicationService).Scan(ctx, &nullApplicationService) + if err != nil { + return nil, err + } + return nullApplicationService, nil +} + +func (datastore *BunAppDatastore) GetApplicationServiceByAppID(ctx context.Context, nullApplicationID int64) ([]*models.NullApplicationService, error) { + nullApplicationService := []*models.NullApplicationService{} + err := datastore.db.NewSelect().Model(&nullApplicationService).Where("null_application_id = ?", nullApplicationID).Scan(ctx, &nullApplicationService) + + if err != nil { + return nil, err + } + return nullApplicationService, nil +} diff --git a/api/infrastrucure/bunMemberDatastore.go b/api/infrastrucure/bunMemberDatastore.go index 2baa156..ee68650 100644 --- a/api/infrastrucure/bunMemberDatastore.go +++ b/api/infrastrucure/bunMemberDatastore.go @@ -101,8 +101,8 @@ func (bmd *BunMemberDatastore) UpdateOrg(ctx context.Context, org *models.Org) e return nil } -func (bmd *BunMemberDatastore) GetOrgByOwnerId(ctx context.Context, ownerId string) (*models.Org, error) { - var org models.Org +func (bmd *BunMemberDatastore) GetOrgByOwnerId(ctx context.Context, ownerId string) ([]*models.Org, error) { + var org []*models.Org err := bmd.Database.NewSelect(). Model(&org). Where("owner_id = ?", ownerId). @@ -111,7 +111,7 @@ func (bmd *BunMemberDatastore) GetOrgByOwnerId(ctx context.Context, ownerId stri if err != nil { return nil, err } - return &org, nil + return org, nil } func (bmd *BunMemberDatastore) CreateResourceGroup(ctx context.Context, resourceGroup *models.ResourceGroup) error { diff --git a/api/main.go b/api/main.go index 5c13634..b344aa2 100644 --- a/api/main.go +++ b/api/main.go @@ -104,7 +104,13 @@ func main() { config := dynamic.NewForConfigOrDie(clusterConfig) istioClient := versionedclient.NewForConfigOrDie(clusterConfig) - appController := app.NewApplicationController(config, istioClient, kubeClient, userController, client, logger) + appsqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared") + appdb := bun.NewDB(appsqldb, sqlitedialect.New()) + if err != nil { + panic(err) + } + bunAppDatastore := infrastrucure.NewBunAppDatastore(appdb) + appController := app.NewApplicationController(config, istioClient, kubeClient, bunAppDatastore, userController, client, logger) middlwares := []mux.MiddlewareFunc{ middleware.AddJwtHeaders, authzMiddleware.CheckAuthz, diff --git a/api/middleware/dumbauthz.go b/api/middleware/dumbauthz.go index 5d11629..a4f4f17 100644 --- a/api/middleware/dumbauthz.go +++ b/api/middleware/dumbauthz.go @@ -74,7 +74,7 @@ func (k *AuthzMiddleware) CheckAuthz(next http.Handler) http.Handler { } // check if org id matches org id in url - if orgId != fmt.Sprintf("%d", org.ID) { + if orgId != fmt.Sprintf("%d", org[0].ID) { fmt.Println("Org id in url does not match org id in database. Failing Authz.") http.Redirect(w, r, "/error", http.StatusSeeOther) } diff --git a/api/users/controllers/user.go b/api/users/controllers/user.go index 58441ee..2259d67 100644 --- a/api/users/controllers/user.go +++ b/api/users/controllers/user.go @@ -4,10 +4,11 @@ import ( "context" "encoding/json" "fmt" + "net/http" + "github.com/beevik/guid" "github.com/gorilla/mux" "go.uber.org/zap" - "net/http" pb "github.com/null-channel/eddington/proto/user" @@ -23,7 +24,7 @@ type MembersDatastore interface { CreateOrg(ctx context.Context, org *models.Org) error GetOrgByID(ctx context.Context, id int64) (*models.Org, error) UpdateOrg(ctx context.Context, org *models.Org) error - GetOrgByOwnerId(ctx context.Context, ownerId string) (*models.Org, error) + GetOrgByOwnerId(ctx context.Context, ownerId string) ([]*models.Org, error) CreateResourceGroup(ctx context.Context, resourceGroup *models.ResourceGroup) error GetResourceGroupByID(ctx context.Context, id int64) (*models.ResourceGroup, error) UpdateResourceGroup(ctx context.Context, resourceGroup *models.ResourceGroup) error @@ -105,8 +106,6 @@ func (u *UserController) UpsertUser(w http.ResponseWriter, r *http.Request) { var userDTO types.NewUserRequest err := json.NewDecoder(r.Body).Decode(&userDTO) - userDTO.ID = userID - u.logger.Info("Upserting user: ", userDTO) if err != nil { @@ -125,7 +124,7 @@ func (u *UserController) UpsertUser(w http.ResponseWriter, r *http.Request) { // Create or update the user based on userDTO user := &models.User{ - ID: userDTO.ID, + ID: userID, Name: userDTO.Name, Email: userDTO.Email, NewsLetterConsent: userDTO.NewsletterConsent, @@ -213,7 +212,7 @@ func (u *UserController) GetUserId(w http.ResponseWriter, r *http.Request) { } // return the UserGetResponse - userContext := &UserGetResponse{ + userContext := &types.UserGetResponse{ User: user, Org: org, } @@ -224,11 +223,6 @@ func (u *UserController) GetUserId(w http.ResponseWriter, r *http.Request) { } -type UserGetResponse struct { - User *models.User `json:"user"` - Org *models.Org `json:"org"` -} - func (u *UserController) GetUserContext(ctx context.Context, userId string) (*models.Org, error) { // This assumes that the user is the owner. This is bad... but works for now. // This is probably not even going to be an indext column in the future. @@ -241,13 +235,13 @@ func (u *UserController) GetUserContext(ctx context.Context, userId string) (*mo } var resGroups []*models.ResourceGroup - resGroups, _ = u.membersDatastore.GetResourceGroupByOrgID(ctx, &orgs.ID) + resGroups, _ = u.membersDatastore.GetResourceGroupByOrgID(ctx, &orgs[0].ID) - orgs.ResourceGroups = resGroups + orgs[0].ResourceGroups = resGroups fmt.Println(orgs) - return orgs, nil + return orgs[0], nil } func (u *UserController) GetUserByID(ctx context.Context, userID string) (*models.User, error) { diff --git a/api/users/models/models.go b/api/users/models/models.go index 2fa8f13..44228b0 100644 --- a/api/users/models/models.go +++ b/api/users/models/models.go @@ -6,33 +6,33 @@ import ( ) type User struct { - ID string `bun:",pk"` // primary key, same as ory. - Name string - Email string - NewsLetterConsent bool `bun:"newsletterConsent"` - DOB time.Time + ID string `bun:",pk" json:"id"` // primary key, same as ory. + Name string `json:"name"` + Email string `json:"email"` + NewsLetterConsent bool `bun:"newsletterConsent" json:"news_letter_consent"` + DOB time.Time `json:"dob"` } type Org struct { - ID int64 `bun:",pk,autoincrement"` - Name string - OwnerID string `bun:"owner_id"` - Owner *User `bun:"rel:belongs-to,join:owner_id=id"` - ResourceGroups []*ResourceGroup `bun:"rel:has-many,join:id=org_id"` + ID int64 `bun:",pk,autoincrement" json:"id"` + Name string `json:"name"` + OwnerID string `bun:"owner_id" json:"owner_id"` + Owner *User `bun:"rel:belongs-to,join:owner_id=id" json:"owner"` + ResourceGroups []*ResourceGroup `bun:"rel:has-many,join:id=org_id" json:"resource_groups"` } type ResourceGroup struct { - ID int64 `bun:",pk,autoincrement"` - OrgID int64 `bun:"org_id"` - Name string `bun:"name"` - Resources []*Resources `bun:"rel:has-many,join:id=resource_group_id"` + ID int64 `bun:",pk,autoincrement" json:"id"` + OrgID int64 `bun:"org_id" json:"org_id"` + Name string `bun:"name" json:"name"` + Resources []*Resources `bun:"rel:has-many,join:id=resource_group_id" json:"resources"` } type Resources struct { - ID int64 `bun:",pk,autoincrement"` - CreatedAt int64 - ResourceGroupID int64 - Type string + ID int64 `bun:",pk,autoincrement" json:"id"` + CreatedAt int64 `json:"created_at"` + ResourceGroupID int64 `json:"resource_group_id"` + Type string `json:"type"` } func (u User) String() string { diff --git a/api/users/types/types.go b/api/users/types/types.go index a96fa4c..333d510 100644 --- a/api/users/types/types.go +++ b/api/users/types/types.go @@ -6,6 +6,7 @@ import ( "time" "github.com/go-playground/validator/v10" + "github.com/null-channel/eddington/api/users/models" ) type CreateUserRequest struct { @@ -33,13 +34,17 @@ func ValidateAgeGT(fl validator.FieldLevel) bool { } type NewUserRequest struct { - ID string `json:"id"` Name string `json:"name" validate:"required"` Email string `json:"email" validate:"email"` NewsletterConsent bool `json:"newsletterConsent" validate:"boolean"` DOB time.Time `json:"dob" validate:"age=16"` } +type UserGetResponse struct { + User *models.User `json:"user"` + Org *models.Org `json:"org"` +} + // ApiError represents an API error containing parameter and message details. type ApiError struct { Param string `json:"param"`