From acc99cdd271de07d7b2bcdfa2972e5a2de689fce Mon Sep 17 00:00:00 2001 From: Seungkyu Ahn Date: Tue, 6 Feb 2024 19:15:48 +0900 Subject: [PATCH] Add Namespace management --- internal/delivery/http/project.go | 499 ++++++++++++++++++++++-------- internal/repository/project.go | 155 ++++++---- internal/route/route.go | 19 +- internal/usecase/project.go | 109 ++++--- pkg/domain/project.go | 153 +++++---- 5 files changed, 646 insertions(+), 289 deletions(-) diff --git a/internal/delivery/http/project.go b/internal/delivery/http/project.go index 8350a88f..3972e43f 100644 --- a/internal/delivery/http/project.go +++ b/internal/delivery/http/project.go @@ -3,6 +3,7 @@ package http import ( "fmt" "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/serializer" "net/http" "strings" "time" @@ -29,6 +30,7 @@ type IProjectHandler interface { RemoveProjectMember(w http.ResponseWriter, r *http.Request) RemoveProjectMembers(w http.ResponseWriter, r *http.Request) UpdateProjectMemberRole(w http.ResponseWriter, r *http.Request) + UpdateProjectMembersRole(w http.ResponseWriter, r *http.Request) CreateProjectNamespace(w http.ResponseWriter, r *http.Request) IsProjectNamespaceExist(w http.ResponseWriter, r *http.Request) @@ -78,9 +80,6 @@ func (p ProjectHandler) CreateProject(w http.ResponseWriter, r *http.Request) { return } - log.Infof("projectReq: name = %s, description = %s, projectLeaderId = %s", - projectReq.Name, projectReq.Description, projectReq.ProjectLeaderId) - now := time.Now() project := &domain.Project{ OrganizationId: organizationId, @@ -96,8 +95,6 @@ func (p ProjectHandler) CreateProject(w http.ResponseWriter, r *http.Request) { return } - log.Infof("Project Id: %s", projectId) - project.ID = projectId ProjectLeaderId, err := uuid.Parse(projectReq.ProjectLeaderId) if err != nil { @@ -105,8 +102,11 @@ func (p ProjectHandler) CreateProject(w http.ResponseWriter, r *http.Request) { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "Failed to parse uuid to string")) return } + //Don't add ProjectUser Object because of Cascading pm := &domain.ProjectMember{ - ProjectId: projectId, + ProjectId: projectId, + //ProjectUser: &domain.ProjectUser{ID: ProjectLeaderId}, + //ProjectRole: &domain.ProjectRole{ID: projectReq.ProjectRoleId}, ProjectUserId: ProjectLeaderId, ProjectRoleId: projectReq.ProjectRoleId, CreatedAt: now, @@ -114,27 +114,29 @@ func (p ProjectHandler) CreateProject(w http.ResponseWriter, r *http.Request) { projectMemberId, err := p.usecase.AddProjectMember(pm) if err != nil { + log.Errorf("projectMemberId: %v", projectMemberId) ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } - pr, err := p.usecase.GetProjectRoles(usecase.ProjectLeader) - if err != nil { - ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) - return - } - - pms := make([]domain.ProjectMember, 0) - pm.ID = projectMemberId - pm.ProjectRole = pr[0] - pms = append(pms, *pm) - - project.ProjectMembers = pms - projectRes := domain.CreateProjectResponse{ - Project: *project, - } - - ResponseJSON(w, r, http.StatusOK, projectRes) + //pr, err := p.usecase.GetProjectRoles(usecase.ProjectLeader) + //if err != nil { + // ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + // return + //} + // + //pms := make([]domain.ProjectMember, 0) + //pm.ID = projectMemberId + //pm.ProjectRole = pr[0] + //pms = append(pms, *pm) + // + //project.ProjectMembers = pms + //projectRes := domain.CreateProjectResponse{ + // Project: *project, + //} + + out := domain.CreateProjectResponse{ProjectId: projectId} + ResponseJSON(w, r, http.StatusOK, out) } @@ -144,8 +146,8 @@ func (p ProjectHandler) CreateProject(w http.ResponseWriter, r *http.Request) { // @Description Get projects // @Accept json // @Produce json -// @Param organizationId path string true "Organization ID" -// @Success 200 {object} domain.GetProjectsResponse +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.GetProjectsResponse // @Router /organizations/{organizationId}/projects [get] // @Security JWT func (p ProjectHandler) GetProjects(w http.ResponseWriter, r *http.Request) { @@ -164,8 +166,35 @@ func (p ProjectHandler) GetProjects(w http.ResponseWriter, r *http.Request) { return } + // TODO: get myUserId from login component + myUserId := "58568003-9812-42d6-b10e-61a60e52cb06" + prs := make([]domain.ProjectResponse, 0) + for _, project := range ps { + var projectRoleId, projectRoleName string + for _, pm := range project.ProjectMembers { + if myUserId == pm.ProjectUserId.String() { + projectRoleId = pm.ProjectRoleId + projectRoleName = pm.ProjectRole.Name + } + } + // TODO: implement AppCount + pr := domain.ProjectResponse{ + ID: project.ID, + OrganizationId: project.OrganizationId, + Name: project.Name, + Description: project.Description, + ProjectRoleId: projectRoleId, + ProjectRoleName: projectRoleName, + NamespaceCount: len(project.ProjectNamespaces), + AppCount: 0, + MemberCount: len(project.ProjectMembers), + CreatedAt: project.CreatedAt, + } + prs = append(prs, pr) + } + var out domain.GetProjectsResponse - out.Projects = ps + out.Projects = prs if ps == nil { ResponseJSON(w, r, http.StatusNotFound, out) @@ -174,12 +203,127 @@ func (p ProjectHandler) GetProjects(w http.ResponseWriter, r *http.Request) { } } +// GetProject godoc +// @Tags Projects +// @Summary Get projects +// @Description Get projects +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Success 200 {object} domain.GetProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId} [get] +// @Security JWT func (p ProjectHandler) GetProject(w http.ResponseWriter, r *http.Request) { - //TODO implement me + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + project, err := p.usecase.GetProject(organizationId, projectId) + if err != nil { + log.ErrorWithContext(r.Context(), "Failed to retrieve project", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectResponse + if project == nil { + ResponseJSON(w, r, http.StatusNotFound, out) + return + } + + var projectLeaderId, projectLeaderName, projectLeaderAccountId, projectLeaderDepartment string + for _, pu := range project.ProjectMembers { + if pu.ProjectRole.Name == "project-leader" { + projectLeaderId = pu.ProjectUser.ID.String() + projectLeaderName = pu.ProjectUser.Name + projectLeaderAccountId = pu.ProjectUser.AccountId + projectLeaderDepartment = pu.ProjectUser.Department + } + } + + var pdr domain.ProjectDetailResponse + if err = serializer.Map(*project, &pdr); err != nil { + log.Error(err) + ErrorJSON(w, r, err) + return + } + pdr.ProjectLeaderId = projectLeaderId + pdr.ProjectLeaderName = projectLeaderName + pdr.ProjectLeaderAccountId = projectLeaderAccountId + pdr.ProjectLeaderDepartment = projectLeaderDepartment + pdr.NamespaceCount = len(project.ProjectNamespaces) + pdr.MemberCount = len(project.ProjectMembers) + //TODO implement AppCount + pdr.AppCount = 0 + + out.Project = &pdr + ResponseJSON(w, r, http.StatusOK, out) } +// UpdateProject godoc +// @Tags Projects +// @Summary Update project +// @Description Update project +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param request body domain.UpdateProjectRequest true "Request body to update project" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId} [put] +// @Security JWT func (p ProjectHandler) UpdateProject(w http.ResponseWriter, r *http.Request) { - //TODO implement me + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf("organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + projectId, ok := vars["projectId"] + log.Debugf("projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + var projectReq domain.UpdateProjectRequest + if err := UnmarshalRequestInput(r, &projectReq); err != nil { + ErrorJSON(w, r, err) + return + } + + now := time.Now() + project, err := p.usecase.GetProject(organizationId, projectId) + if err != nil { + + } + + project.Name = projectReq.Name + project.Description = projectReq.Description + project.UpdatedAt = &now + project.ProjectNamespaces = nil + project.ProjectMembers = nil + + if err := p.usecase.UpdateProject(project); err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) } func (p ProjectHandler) DeleteProject(w http.ResponseWriter, r *http.Request) { @@ -192,10 +336,10 @@ func (p ProjectHandler) DeleteProject(w http.ResponseWriter, r *http.Request) { // @Description Get project role by id // @Accept json // @Produce json -// @Param organizationId path string true "Organization ID" -// @Param projectId path string true "Project ID" +// @Param organizationId path string true "Organization ID" +// @Param projectRoleId path string true "Project Role ID" // @Success 200 {object} domain.GetProjectRoleResponse -// @Router /organizations/{organizationId}/projects/project-roles/{projectRoleId} [get] +// @Router /organizations/{organizationId}/projects/pass/project-roles/{projectRoleId} [get] // @Security JWT func (p ProjectHandler) GetProjectRole(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -234,10 +378,10 @@ func (p ProjectHandler) GetProjectRole(w http.ResponseWriter, r *http.Request) { // @Description Get project roles by giving params // @Accept json // @Produce json -// @Param organizationId path string true "Organization ID" +// @Param organizationId path string true "Organization ID" // @Param query query string false "project role search by query (query=all), (query=leader), (query=member), (query=viewer)" // @Success 200 {object} domain.GetProjectRolesResponse -// @Router /organizations/{organizationId}/projects/project-roles [get] +// @Router /organizations/{organizationId}/projects/pass/project-roles [get] // @Security JWT func (p ProjectHandler) GetProjectRoles(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() @@ -294,7 +438,6 @@ func (p ProjectHandler) AddProjectMember(w http.ResponseWriter, r *http.Request) return } projectId, ok := vars["projectId"] - log.Debugf("projectId = [%v]\n", projectId) if !ok { ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), "C_INVALID_PROJECT_ID", "")) @@ -307,41 +450,46 @@ func (p ProjectHandler) AddProjectMember(w http.ResponseWriter, r *http.Request) return } - projectMemberResponse := domain.AddProjectMemberResponse{ - ProjectMembers: make([]domain.ProjectMember, 0), - } now := time.Now() for _, pmr := range projectMemberReq.ProjectMemberRequests { - ProjectUserId, err := uuid.Parse(pmr.ProjectUserId) + pu, err := p.usecase.GetProjectUser(pmr.ProjectUserId) if err != nil { log.Error(err) - ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "Failed to parse uuid to string")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectUserId"), + "C_INVALID_PROJECT_USER_ID", "")) return } - pm := &domain.ProjectMember{ - ProjectId: projectId, - ProjectUserId: ProjectUserId, - ProjectRoleId: pmr.ProjectRoleId, - CreatedAt: now, - } - pmId, err := p.usecase.AddProjectMember(pm) + pr, err := p.usecase.GetProjectRole(pmr.ProjectRoleId) if err != nil { + log.Error(err) ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } + if pr == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectRoleId"), + "C_INVALID_PROJECT_ROLE_ID", "")) + return + } - pr, err := p.usecase.GetProjectRole(pm.ProjectRoleId) + pm := &domain.ProjectMember{ + ProjectId: projectId, + ProjectUserId: pu.ID, + ProjectUser: nil, + ProjectRoleId: pr.ID, + ProjectRole: nil, + CreatedAt: now, + } + pmId, err := p.usecase.AddProjectMember(pm) if err != nil { + log.Errorf("projectMemberId: %s", pmId) ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } - pm.ID = pmId - pm.ProjectRole = *pr - projectMemberResponse.ProjectMembers = append(projectMemberResponse.ProjectMembers, *pm) } - ResponseJSON(w, r, http.StatusOK, projectMemberResponse) + out := domain.CommonProjectResponse{Result: "OK"} + ResponseJSON(w, r, http.StatusOK, out) } // GetProjectMember godoc @@ -351,7 +499,7 @@ func (p ProjectHandler) AddProjectMember(w http.ResponseWriter, r *http.Request) // @Accept json // @Produce json // @Param organizationId path string true "Organization ID" -// @Param projectId path string true "Project ID" +// @Param projectId path string true "Project ID" // @Param projectMemberId path string true "Project Member ID" // @Success 200 {object} domain.GetProjectMemberResponse // @Router /organizations/{organizationId}/projects/{projectId}/members/{projectMemberId} [get] @@ -382,7 +530,7 @@ func (p ProjectHandler) GetProjectMember(w http.ResponseWriter, r *http.Request) return } - pm, err := p.usecase.GetProjectMemberById(projectMemberId) + pm, err := p.usecase.GetProjectMember(projectMemberId) if err != nil { log.ErrorWithContext(r.Context(), "Failed to get project member ", err) ErrorJSON(w, r, err) @@ -446,7 +594,7 @@ func (p ProjectHandler) GetProjectMembers(w http.ResponseWriter, r *http.Request // @Param organizationId path string true "Organization ID" // @Param projectId path string true "Project ID" // @Param projectMemberId path string true "Project Member ID" -// @Success 200 {object} domain.CommonProjectResponse +// @Success 200 {object} domain.CommonProjectResponse // @Router /organizations/{organizationId}/projects/{projectId}/members/{projectMemberId} [delete] // @Security JWT func (p ProjectHandler) RemoveProjectMember(w http.ResponseWriter, r *http.Request) { @@ -518,6 +666,7 @@ func (p ProjectHandler) RemoveProjectMembers(w http.ResponseWriter, r *http.Requ return } + // TODO: change multi row delete for _, pm := range projectMemberReq.ProjectMember { if err := p.usecase.RemoveProjectMember(pm.ProjectMemberId); err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) @@ -571,11 +720,93 @@ func (p ProjectHandler) UpdateProjectMemberRole(w http.ResponseWriter, r *http.R return } - if err := p.usecase.UpdateProjectMemberRole(projectMemberId, pmrReq.ProjectRoleId); err != nil { + now := time.Now() + pm, err := p.usecase.GetProjectMember(projectMemberId) + if err != nil { + log.Error(err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if pm == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectMemberId"), + "C_INVALID_PROJECT_MEMBER_ID", "")) + return + } + + pm.ProjectRoleId = pmrReq.ProjectRoleId + pm.ProjectUser = nil + pm.ProjectRole = nil + pm.UpdatedAt = &now + + if err := p.usecase.UpdateProjectMemberRole(pm); err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) +} + +// UpdateProjectMembersRole godoc +// @Tags Projects +// @Summary Update project member Role +// @Description Update project member Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param request body domain.UpdateProjectMembersRoleRequest true "Request body to update project member role" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members [put] +// @Security JWT +func (p ProjectHandler) UpdateProjectMembersRole(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf("organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + log.Debugf("projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + now := time.Now() + var projectMemberReq domain.UpdateProjectMembersRoleRequest + if err := UnmarshalRequestInput(r, &projectMemberReq); err != nil { ErrorJSON(w, r, err) return } + for _, pmr := range projectMemberReq.ProjectMemberRoleRequests { + pm, err := p.usecase.GetProjectMember(pmr.ProjectMemberId) + if err != nil { + log.Error(err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if pm == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectMemberId"), + "C_INVALID_PROJECT_MEMBER_ID", "")) + return + } + + pm.ProjectRoleId = pmr.ProjectRoleId + pm.ProjectUser = nil + pm.ProjectRole = nil + pm.UpdatedAt = &now + + if err := p.usecase.UpdateProjectMemberRole(pm); err != nil { + ErrorJSON(w, r, err) + return + } + } + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) } @@ -589,13 +820,12 @@ func (p ProjectHandler) UpdateProjectMemberRole(w http.ResponseWriter, r *http.R // @Param projectId path string true "Project ID" // @Param stackId path string true "Stack ID" // @Param request body domain.CreateProjectNamespaceRequest true "Request body to create project namespace" -// @Success 200 {object} domain.CreateProjectNamespaceResponse -// @Router /organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces [post] +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces [post] // @Security JWT func (p ProjectHandler) CreateProjectNamespace(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] - log.Debugf("organizationId = [%v]\n", organizationId) if !ok { ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) @@ -607,12 +837,6 @@ func (p ProjectHandler) CreateProjectNamespace(w http.ResponseWriter, r *http.Re "C_INVALID_PROJECT_ID", "")) return } - stackId, ok := vars["stackId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackId"), - "C_INVALID_STACK_ID", "")) - return - } var projectNamespaceReq domain.CreateProjectNamespaceRequest if err := UnmarshalRequestInput(r, &projectNamespaceReq); err != nil { @@ -622,20 +846,20 @@ func (p ProjectHandler) CreateProjectNamespace(w http.ResponseWriter, r *http.Re now := time.Now() pn := &domain.ProjectNamespace{ - ProjectId: projectId, - StackId: stackId, + StackId: projectNamespaceReq.StackId, Namespace: projectNamespaceReq.Namespace, + ProjectId: projectId, + Stack: nil, Description: projectNamespaceReq.Description, Status: "CREATING", CreatedAt: now, } - pnId, err := p.usecase.CreateProjectNamespace(pn) - if err != nil { + if err := p.usecase.CreateProjectNamespace(organizationId, pn); err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } - out := domain.CreateProjectNamespaceResponse{ProjectNamesapceId: pnId} + out := domain.CommonProjectResponse{Result: "OK"} ResponseJSON(w, r, http.StatusOK, out) } @@ -650,7 +874,7 @@ func (p ProjectHandler) CreateProjectNamespace(w http.ResponseWriter, r *http.Re // @Param stackId path string true "Project Stack ID" // @Param projectNamespace path string true "Project Namespace" // @Success 200 {object} domain.CheckExistedResponse -// @Router /organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces/{projectNamespace}/existence [get] +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/existence [get] // @Security JWT func (p ProjectHandler) IsProjectNamespaceExist(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -701,7 +925,7 @@ func (p ProjectHandler) IsProjectNamespaceExist(w http.ResponseWriter, r *http.R // @Param projectId path string true "Project ID" // @Param stackId path string true "Project Stack ID" // @Success 200 {object} domain.GetProjectNamespacesResponse -// @Router /organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces [get] +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces [get] // @Security JWT func (p ProjectHandler) GetProjectNamespaces(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -717,15 +941,8 @@ func (p ProjectHandler) GetProjectNamespaces(w http.ResponseWriter, r *http.Requ "C_INVALID_PROJECT_ID", "")) return } - stackId, ok := vars["stackId"] - log.Debugf("stackId = [%v]\n", stackId) - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackId"), - "C_INVALID_STACK_ID", "")) - return - } - pns, err := p.usecase.GetProjectNamespaces(organizationId, projectId, stackId) + pns, err := p.usecase.GetProjectNamespaces(organizationId, projectId) if err != nil { log.ErrorWithContext(r.Context(), "Failed to get project namespaces.", err) ErrorJSON(w, r, err) @@ -733,8 +950,26 @@ func (p ProjectHandler) GetProjectNamespaces(w http.ResponseWriter, r *http.Requ } var out domain.GetProjectNamespacesResponse - out.ProjectNamespaces = pns + if pns == nil { + ResponseJSON(w, r, http.StatusNotFound, out) + return + } + pnrs := make([]domain.ProjectNamespaceResponse, 0) + for _, pn := range pns { + var pnr domain.ProjectNamespaceResponse + if err = serializer.Map(pn, &pnr); err != nil { + log.Error(err) + ErrorJSON(w, r, err) + return + } + pnr.StackName = pn.Stack.Name + //TODO: implement AppCount + pnr.AppCount = 0 + + pnrs = append(pnrs, pnr) + } + out.ProjectNamespaces = pnrs ResponseJSON(w, r, http.StatusOK, out) } @@ -747,10 +982,10 @@ func (p ProjectHandler) GetProjectNamespaces(w http.ResponseWriter, r *http.Requ // @Produce json // @Param organizationId path string true "Organization ID" // @Param projectId path string true "Project ID" +// @Param projectNamespace path string true "Project Namespace" // @Param stackId path string true "Project Stack ID" -// @Param projectNamespaceId path string true "Project Namespace ID" // @Success 200 {object} domain.GetProjectNamespaceResponse -// @Router /organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces/{projectNamespaceId} [get] +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId} [get] // @Security JWT func (p ProjectHandler) GetProjectNamespace(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -766,20 +1001,20 @@ func (p ProjectHandler) GetProjectNamespace(w http.ResponseWriter, r *http.Reque "C_INVALID_PROJECT_ID", "")) return } - stackId, ok := vars["stackId"] + projectNamespace, ok := vars["projectNamespace"] if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackId"), - "C_INVALID_STACK_ID", "")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectNamespace"), + "C_INVALID_PROJECT_NAMESPACE", "")) return } - projectNamespaceId, ok := vars["projectNamespaceId"] + stackId, ok := vars["stackId"] if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectNamespaceId"), - "C_INVALID_PROJECT_NAMESPACE_ID", "")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackId"), + "C_INVALID_STACK_ID", "")) return } - pn, err := p.usecase.GetProjectNamespace(organizationId, projectId, stackId, projectNamespaceId) + pn, err := p.usecase.GetProjectNamespace(organizationId, projectId, projectNamespace, stackId) if err != nil { log.ErrorWithContext(r.Context(), "Failed to get project namespace.", err) ErrorJSON(w, r, err) @@ -787,12 +1022,24 @@ func (p ProjectHandler) GetProjectNamespace(w http.ResponseWriter, r *http.Reque } var out domain.GetProjectNamespaceResponse - out.ProjectNamespace = pn if pn == nil { ResponseJSON(w, r, http.StatusNotFound, out) - } else { - ResponseJSON(w, r, http.StatusOK, out) + return + } + + var pnr domain.ProjectNamespaceResponse + if err = serializer.Map(*pn, &pnr); err != nil { + log.Error(err) + ErrorJSON(w, r, err) + return } + pnr.StackName = pn.Stack.Name + //TODO: implement AppCount + pnr.AppCount = 0 + + out.ProjectNamespace = &pnr + ResponseJSON(w, r, http.StatusOK, out) + } // DeleteProjectNamespace godoc @@ -804,43 +1051,45 @@ func (p ProjectHandler) GetProjectNamespace(w http.ResponseWriter, r *http.Reque // @Param organizationId path string true "Organization ID" // @Param projectId path string true "Project ID" // @Param stackId path string true "Stack ID" -// @Param projectNamespaceId path string true "Project Namespace ID" +// @Param projectNamespace path string true "Project Namespace" // @Success 200 {object} domain.CommonProjectResponse -// @Router /organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces/{projectNamespaceId} [delete] +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId} [delete] // @Security JWT func (p ProjectHandler) DeleteProjectNamespace(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - organizationId, ok := vars["organizationId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("organizationId not found in path"), - "C_INVALID_ORGANIZATION_ID", "")) - return - } - projectId, ok := vars["projectId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), - "C_INVALID_PROJECT_ID", "")) - return - } - stackId, ok := vars["stackId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackId"), - "C_INVALID_STACK_ID", "")) - return - } - projectNamespaceId, ok := vars["projectNamespaceId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectNamespaceId"), - "C_INVALID_PROJECT_NAMESPACE_ID", "")) - return - } - - if err := p.usecase.DeleteProjectNamespace(organizationId, projectId, stackId, projectNamespaceId); err != nil { - ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) - return + //TODO implement me - } - ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) + //vars := mux.Vars(r) + //organizationId, ok := vars["organizationId"] + //if !ok { + // ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("organizationId not found in path"), + // "C_INVALID_ORGANIZATION_ID", "")) + // return + //} + //projectId, ok := vars["projectId"] + //if !ok { + // ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + // "C_INVALID_PROJECT_ID", "")) + // return + //} + //projectNamespace, ok := vars["projectNamespace"] + //if !ok { + // ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectNamespace"), + // "C_INVALID_PROJECT_NAMESPACE", "")) + // return + //} + //stackId, ok := vars["stackId"] + //if !ok { + // ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackId"), + // "C_INVALID_STACK_ID", "")) + // return + //} + // + //if err := p.usecase.DeleteProjectNamespace(organizationId, projectId, projectNamespace, stackId); err != nil { + // ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + // return + // + //} + //ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) } func (p ProjectHandler) SetFavoriteProject(w http.ResponseWriter, r *http.Request) { diff --git a/internal/repository/project.go b/internal/repository/project.go index 96cbd117..dfa6293f 100644 --- a/internal/repository/project.go +++ b/internal/repository/project.go @@ -10,6 +10,8 @@ import ( type IProjectRepository interface { CreateProject(p *domain.Project) (string, error) GetProjects(organizationId string) ([]domain.Project, error) + GetProjectById(organizationId string, projectId string) (*domain.Project, error) + UpdateProject(p *domain.Project) error GetAllProjectRoles() ([]domain.ProjectRole, error) GetProjectRoleByName(name string) (*domain.ProjectRole, error) GetProjectRoleById(id string) (*domain.ProjectRole, error) @@ -17,12 +19,13 @@ type IProjectRepository interface { GetProjectMembersByProjectId(projectId string) ([]domain.ProjectMember, error) GetProjectMemberById(projectMemberId string) (*domain.ProjectMember, error) RemoveProjectMember(projectMemberId string) error - UpdateProjectMemberRole(projectMemberId string, projectRoleId string) error - CreateProjectNamespace(*domain.ProjectNamespace) (string, error) + //UpdateProjectMemberRole(projectMemberId string, projectRoleId string) error + UpdateProjectMemberRole(pm *domain.ProjectMember) error + CreateProjectNamespace(organizationId string, pn *domain.ProjectNamespace) error GetProjectNamespaceByName(organizationId string, projectId string, stackId string, projectNamespace string) (*domain.ProjectNamespace, error) - GetProjectNamespaces(organizationId string, projectId string, stackId string) ([]domain.ProjectNamespace, error) - GetProjectNamespaceById(organizationId string, projectId string, stackId string, projectNamespaceId string) (*domain.ProjectNamespace, error) - DeleteProjectNamespace(organizationId string, projectId string, stackId string, projectNamespaceId string) error + GetProjectNamespaces(organizationId string, projectId string) ([]domain.ProjectNamespace, error) + GetProjectNamespaceByPrimaryKey(organizationId string, projectId string, projectNamespace string, stackId string) (*domain.ProjectNamespace, error) + DeleteProjectNamespace(organizationId string, projectId string, projectNamespace string, stackId string) error } type ProjectRepository struct { @@ -63,44 +66,75 @@ func (r *ProjectRepository) GetProjects(organizationId string) (ps []domain.Proj return ps, nil } +func (r *ProjectRepository) GetProjectById(organizationId string, projectId string) (p *domain.Project, err error) { + res := r.db.Limit(1).Where("organization_id = ? and id = ?", organizationId, projectId). + Preload("ProjectMembers"). + Preload("ProjectMembers.ProjectRole"). + Preload("ProjectMembers.ProjectUser"). + First(&p) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info("Cannot find project") + return nil, nil + } else { + log.Error(res.Error) + return nil, res.Error + } + } + + return p, nil +} + +func (r *ProjectRepository) UpdateProject(p *domain.Project) error { + res := r.db.Model(&p).Updates(domain.Project{Name: p.Name, Description: p.Description, UpdatedAt: p.UpdatedAt}) + if res.Error != nil { + return res.Error + } + + return nil +} + func (r *ProjectRepository) GetProjectRoleById(id string) (*domain.ProjectRole, error) { var pr = &domain.ProjectRole{ID: id} - result := r.db.First(pr) - if result.Error != nil { - log.Error(result.Error) - return pr, result.Error - } - if result.RowsAffected == 0 { - log.Info("There is no project_roles table data") - return pr, nil + res := r.db.First(pr) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info("Cannot find project role") + return nil, nil + } else { + log.Error(res.Error) + return nil, res.Error + } } return pr, nil } func (r *ProjectRepository) GetAllProjectRoles() (prs []domain.ProjectRole, err error) { - result := r.db.Find(&prs) - if result.Error != nil { - log.Error(result.Error) - return nil, result.Error - } - if result.RowsAffected == 0 { - log.Info("There is no project_roles table data") - return prs, nil + res := r.db.Find(&prs) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info("Cannot find project roles") + return nil, nil + } else { + log.Error(res.Error) + return nil, res.Error + } } return prs, nil } func (r *ProjectRepository) GetProjectRoleByName(name string) (pr *domain.ProjectRole, err error) { - result := r.db.Where("name = ?", name).First(&pr) - if result.Error != nil { - log.Error(result.Error) - return nil, result.Error - } - if result.RowsAffected == 0 { - log.Info("There is no project_roles table data") - return pr, nil + res := r.db.Where("name = ?", name).First(&pr) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info("Cannot find project roles") + return nil, nil + } else { + log.Error(res.Error) + return nil, res.Error + } } return pr, nil @@ -156,8 +190,17 @@ func (r *ProjectRepository) RemoveProjectMember(projectMemberId string) error { return nil } -func (r *ProjectRepository) UpdateProjectMemberRole(projectMemberId string, projectRoleId string) error { - res := r.db.Model(&domain.ProjectMember{ID: projectMemberId}).Update("project_role_id", projectRoleId) +//func (r *ProjectRepository) UpdateProjectMemberRole(projectMemberId string, projectRoleId string) error { +// res := r.db.Model(&domain.ProjectMember{ID: projectMemberId}).Update("project_role_id", projectRoleId) +// if res.Error != nil { +// return res.Error +// } +// +// return nil +//} + +func (r *ProjectRepository) UpdateProjectMemberRole(pm *domain.ProjectMember) error { + res := r.db.Model(&pm).Updates(domain.ProjectMember{ProjectRoleId: pm.ProjectRoleId, UpdatedAt: pm.UpdatedAt}) if res.Error != nil { return res.Error } @@ -165,18 +208,21 @@ func (r *ProjectRepository) UpdateProjectMemberRole(projectMemberId string, proj return nil } -func (r *ProjectRepository) CreateProjectNamespace(pn *domain.ProjectNamespace) (string, error) { +func (r *ProjectRepository) CreateProjectNamespace(organizationId string, pn *domain.ProjectNamespace) error { res := r.db.Create(&pn) if res.Error != nil { - return "", res.Error + return res.Error } - return pn.ID, nil + //return pn.ID, nil + return nil } func (r *ProjectRepository) GetProjectNamespaceByName(organizationId string, projectId string, stackId string, projectNamespace string) (pn *domain.ProjectNamespace, err error) { - res := r.db.Limit(1).Where("project_id = ? and stack_id = ? and namespace = ?", projectId, stackId, projectNamespace).First(&pn) + res := r.db.Limit(1). + Where("stack_id = ? and namespace = ? and project_id = ?", stackId, projectNamespace, projectId). + First(&pn) if res.Error != nil { if errors.Is(res.Error, gorm.ErrRecordNotFound) { log.Info("Not found project namespace") @@ -190,24 +236,29 @@ func (r *ProjectRepository) GetProjectNamespaceByName(organizationId string, pro return pn, nil } -func (r *ProjectRepository) GetProjectNamespaces(organizationId string, projectId string, - stackId string) (pns []domain.ProjectNamespace, err error) { - result := r.db.Where("project_id = ? and stack_id = ?", projectId, stackId).Find(&pns) - if result.Error != nil { - log.Error(result.Error) - return nil, result.Error - } - if result.RowsAffected == 0 { - log.Info("Cannot find project namespace") - return pns, nil +func (r *ProjectRepository) GetProjectNamespaces(organizationId string, projectId string) (pns []domain.ProjectNamespace, err error) { + res := r.db.Where("project_id = ?", projectId). + Preload("Stack"). + Find(&pns) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info("Not found project namespaces") + return nil, nil + } else { + log.Error(res.Error) + return nil, res.Error + } } return pns, nil } -func (r *ProjectRepository) GetProjectNamespaceById(organizationId string, projectId string, stackId string, - projectNamespaceId string) (pn *domain.ProjectNamespace, err error) { - res := r.db.Limit(1).Where("id = ? and project_id = ? and stack_id = ?", projectNamespaceId, projectId, stackId).First(&pn) +func (r *ProjectRepository) GetProjectNamespaceByPrimaryKey(organizationId string, projectId string, + projectNamespace string, stackId string) (pn *domain.ProjectNamespace, err error) { + res := r.db.Limit(1). + Where("stack_id = ? and namespace = ? and project_id = ?", stackId, projectNamespace, projectId). + Preload("Stack"). + First(&pn) if res.Error != nil { if errors.Is(res.Error, gorm.ErrRecordNotFound) { log.Info("Not found project namespace") @@ -221,10 +272,10 @@ func (r *ProjectRepository) GetProjectNamespaceById(organizationId string, proje return pn, nil } -func (r *ProjectRepository) DeleteProjectNamespace(organizationId string, projectId string, - stackId string, projectNamespaceId string) error { - res := r.db.Where("project_id = ? and stack_id = ?", projectId, stackId). - Delete(&domain.ProjectNamespace{ID: projectNamespaceId}) +func (r *ProjectRepository) DeleteProjectNamespace(organizationId string, projectId string, projectNamespace string, + stackId string) error { + res := r.db.Where("stack_id = ? and namespace = ? and project_id = ?", stackId, projectNamespace, projectId). + Delete(&domain.ProjectNamespace{StackId: stackId, Namespace: projectNamespace}) if res.Error != nil { return res.Error } diff --git a/internal/route/route.go b/internal/route/route.go index 4161ad37..fa536594 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -199,22 +199,23 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa projectHandler := delivery.NewProjectHandler(usecase.NewProjectUsecase(repoFactory, argoClient)) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects", customMiddleware.Handle(internalApi.CreateProject, http.HandlerFunc(projectHandler.CreateProject))).Methods(http.MethodPost) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects", customMiddleware.Handle(internalApi.GetProjects, http.HandlerFunc(projectHandler.GetProjects))).Methods(http.MethodGet) - //r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}", customMiddleware.Handle(internalApi.GetProject, http.HandlerFunc(projectHandler.GetProject))).Methods(http.MethodGet) - //r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}", customMiddleware.Handle(internalApi.UpdateProject, http.HandlerFunc(projectHandler.UpdateProject))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}", customMiddleware.Handle(internalApi.GetProject, http.HandlerFunc(projectHandler.GetProject))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}", customMiddleware.Handle(internalApi.UpdateProject, http.HandlerFunc(projectHandler.UpdateProject))).Methods(http.MethodPut) //r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}", customMiddleware.Handle(internalApi.DeleteProject, http.HandlerFunc(projectHandler.DeleteProject))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/project-roles", customMiddleware.Handle(internalApi.GetProjectRoles, http.HandlerFunc(projectHandler.GetProjectRoles))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/project-roles/{projectRoleId}", customMiddleware.Handle(internalApi.GetProjectRole, http.HandlerFunc(projectHandler.GetProjectRole))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/pass/project-roles", customMiddleware.Handle(internalApi.GetProjectRoles, http.HandlerFunc(projectHandler.GetProjectRoles))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/pass/project-roles/{projectRoleId}", customMiddleware.Handle(internalApi.GetProjectRole, http.HandlerFunc(projectHandler.GetProjectRole))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members", customMiddleware.Handle(internalApi.AddProjectMember, http.HandlerFunc(projectHandler.AddProjectMember))).Methods(http.MethodPost) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}", customMiddleware.Handle(internalApi.GetProjectMember, http.HandlerFunc(projectHandler.GetProjectMember))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members", customMiddleware.Handle(internalApi.GetProjectMembers, http.HandlerFunc(projectHandler.GetProjectMembers))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}", customMiddleware.Handle(internalApi.RemoveProjectMember, http.HandlerFunc(projectHandler.RemoveProjectMember))).Methods(http.MethodDelete) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members", customMiddleware.Handle(internalApi.RemoveProjectMember, http.HandlerFunc(projectHandler.RemoveProjectMembers))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members", customMiddleware.Handle(internalApi.UpdateProjectMemberRole, http.HandlerFunc(projectHandler.UpdateProjectMembersRole))).Methods(http.MethodPut) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}/role", customMiddleware.Handle(internalApi.UpdateProjectMemberRole, http.HandlerFunc(projectHandler.UpdateProjectMemberRole))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces", customMiddleware.Handle(internalApi.CreateProjectNamespace, http.HandlerFunc(projectHandler.CreateProjectNamespace))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces/{projectNamespace}/existence", customMiddleware.Handle(internalApi.GetProjectNamespace, http.HandlerFunc(projectHandler.IsProjectNamespaceExist))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces", customMiddleware.Handle(internalApi.GetProjectNamespaces, http.HandlerFunc(projectHandler.GetProjectNamespaces))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces/{projectNamespaceId}", customMiddleware.Handle(internalApi.GetProjectNamespace, http.HandlerFunc(projectHandler.GetProjectNamespace))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/stacks/{stackId}/namespaces/{projectNamespaceId}", customMiddleware.Handle(internalApi.DeleteProjectNamespace, http.HandlerFunc(projectHandler.DeleteProjectNamespace))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces", customMiddleware.Handle(internalApi.CreateProjectNamespace, http.HandlerFunc(projectHandler.CreateProjectNamespace))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/existence", customMiddleware.Handle(internalApi.GetProjectNamespace, http.HandlerFunc(projectHandler.IsProjectNamespaceExist))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces", customMiddleware.Handle(internalApi.GetProjectNamespaces, http.HandlerFunc(projectHandler.GetProjectNamespaces))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}", customMiddleware.Handle(internalApi.GetProjectNamespace, http.HandlerFunc(projectHandler.GetProjectNamespace))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}", customMiddleware.Handle(internalApi.DeleteProjectNamespace, http.HandlerFunc(projectHandler.DeleteProjectNamespace))).Methods(http.MethodDelete) r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", alertHandler.CreateAlert).Methods(http.MethodPost) // assets diff --git a/internal/usecase/project.go b/internal/usecase/project.go index 159f9bf2..39fd33b5 100644 --- a/internal/usecase/project.go +++ b/internal/usecase/project.go @@ -1,7 +1,9 @@ package usecase import ( + "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/internal/serializer" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/log" @@ -18,18 +20,21 @@ const ( type IProjectUsecase interface { CreateProject(*domain.Project) (string, error) GetProjects(organizationId string) ([]domain.Project, error) + GetProject(organizationId string, projectId string) (*domain.Project, error) + UpdateProject(p *domain.Project) error GetProjectRole(id string) (*domain.ProjectRole, error) GetProjectRoles(int) ([]domain.ProjectRole, error) AddProjectMember(pm *domain.ProjectMember) (string, error) - GetProjectMemberById(projectMemberId string) (*domain.ProjectMember, error) + GetProjectUser(projectUserId string) (*domain.ProjectUser, error) + GetProjectMember(projectMemberId string) (*domain.ProjectMember, error) GetProjectMembersByProjectId(projectId string) ([]domain.ProjectMember, error) RemoveProjectMember(projectMemberId string) error - UpdateProjectMemberRole(projectMemberId string, projectRoleId string) error - CreateProjectNamespace(pn *domain.ProjectNamespace) (string, error) + UpdateProjectMemberRole(pm *domain.ProjectMember) error + CreateProjectNamespace(organizationId string, pn *domain.ProjectNamespace) error IsProjectNamespaceExist(organizationId string, projectId string, stackId string, projectNamespace string) (bool, error) - GetProjectNamespaces(organizationId string, projectId string, stackId string) ([]domain.ProjectNamespace, error) - GetProjectNamespace(organizationId string, projectId string, stackId string, projectNamespaceId string) (*domain.ProjectNamespace, error) - DeleteProjectNamespace(organizationId string, projectId string, stackId string, projectNamespaceId string) error + GetProjectNamespaces(organizationId string, projectId string) ([]domain.ProjectNamespace, error) + GetProjectNamespace(organizationId string, projectId string, projectNamespace string, stackId string) (*domain.ProjectNamespace, error) + DeleteProjectNamespace(organizationId string, projectId string, projectNamespace string, stackId string) error } type ProjectUsecase struct { @@ -72,6 +77,23 @@ func (u *ProjectUsecase) GetProjects(organizationId string) (ps []domain.Project return ps, err } +func (u *ProjectUsecase) GetProject(organizationId string, projectId string) (*domain.Project, error) { + p, err := u.projectRepo.GetProjectById(organizationId, projectId) + if err != nil { + log.Error(err) + return nil, errors.Wrap(err, "Failed to get projects.") + } + return p, err +} + +func (u *ProjectUsecase) UpdateProject(p *domain.Project) error { + if err := u.projectRepo.UpdateProject(p); err != nil { + log.Error(err) + return errors.Wrap(err, "Failed to update project.") + } + return nil +} + func (u *ProjectUsecase) GetProjectRole(id string) (*domain.ProjectRole, error) { pr, err := u.projectRepo.GetProjectRoleById(id) if err != nil { @@ -115,32 +137,34 @@ func (u *ProjectUsecase) AddProjectMember(pm *domain.ProjectMember) (string, err return projectMemberId, nil } -func (u *ProjectUsecase) GetProjectMemberById(projectMemberId string) (pm *domain.ProjectMember, err error) { +func (u *ProjectUsecase) GetProjectUser(projectUserId string) (*domain.ProjectUser, error) { + var uid uuid.UUID + uid, err := uuid.Parse(projectUserId) + if err != nil { + log.Error(err) + return nil, errors.Wrap(err, "Failed to parse uuid to string") + } + + user, err := u.userRepository.GetByUuid(uid) + if err != nil { + log.Error(err) + return nil, errors.Wrap(err, "Failed to retrieve user by id") + } + var pu domain.ProjectUser + if err = serializer.Map(user, &pu); err != nil { + log.Error(err) + return nil, err + } + return &pu, nil +} + +func (u *ProjectUsecase) GetProjectMember(projectMemberId string) (pm *domain.ProjectMember, err error) { pm, err = u.projectRepo.GetProjectMemberById(projectMemberId) if err != nil { log.Error(err) return pm, errors.Wrap(err, "Failed to get project member.") } - //var uid uuid.UUID - //uid, err = uuid.Parse(pm.ProjectUserId) - //if err != nil { - // log.Error(err) - // return pm, errors.Wrap(err, "Failed to parse uuid to string") - //} - - //user, err := u.userRepository.GetByUuid(pm.ProjectUserId) - //if err != nil { - // log.Error(err) - // return pm, errors.Wrap(err, "Failed to retrieve user by id") - //} - //var pu domain.ProjectUser - //if err = serializer.Map(user, &pu); err != nil { - // log.Error(err) - // return pm, err - //} - // - //pm.ProjectUser = pu return pm, nil } @@ -151,9 +175,6 @@ func (u *ProjectUsecase) GetProjectMembersByProjectId(projectId string) ([]domai return nil, errors.Wrap(err, "Failed to get project members.") } - //var uid uuid.UUID - //s - return pms, nil } @@ -165,21 +186,21 @@ func (u *ProjectUsecase) RemoveProjectMember(projectMemberId string) error { return nil } -func (u *ProjectUsecase) UpdateProjectMemberRole(projectMemberId string, projectRoleId string) error { - if err := u.projectRepo.UpdateProjectMemberRole(projectMemberId, projectRoleId); err != nil { +func (u *ProjectUsecase) UpdateProjectMemberRole(pm *domain.ProjectMember) error { + + if err := u.projectRepo.UpdateProjectMemberRole(pm); err != nil { log.Error(err) return errors.Wrap(err, "Failed to remove project member to project.") } return nil } -func (u *ProjectUsecase) CreateProjectNamespace(pn *domain.ProjectNamespace) (string, error) { - projectNamespaceId, err := u.projectRepo.CreateProjectNamespace(pn) - if err != nil { +func (u *ProjectUsecase) CreateProjectNamespace(organizationId string, pn *domain.ProjectNamespace) error { + if err := u.projectRepo.CreateProjectNamespace(organizationId, pn); err != nil { log.Error(err) - return "", errors.Wrap(err, "Failed to create project namespace.") + return errors.Wrap(err, "Failed to create project namespace.") } - return projectNamespaceId, nil + return nil } func (u *ProjectUsecase) IsProjectNamespaceExist(organizationId string, projectId string, stackId string, projectNamespace string) (bool, error) { @@ -196,8 +217,8 @@ func (u *ProjectUsecase) IsProjectNamespaceExist(organizationId string, projectI return exist, nil } -func (u *ProjectUsecase) GetProjectNamespaces(organizationId string, projectId string, stackId string) ([]domain.ProjectNamespace, error) { - pns, err := u.projectRepo.GetProjectNamespaces(organizationId, projectId, stackId) +func (u *ProjectUsecase) GetProjectNamespaces(organizationId string, projectId string) ([]domain.ProjectNamespace, error) { + pns, err := u.projectRepo.GetProjectNamespaces(organizationId, projectId) if err != nil { log.Error(err) return nil, errors.Wrap(err, "Failed to retrieve project namespaces.") @@ -206,19 +227,19 @@ func (u *ProjectUsecase) GetProjectNamespaces(organizationId string, projectId s return pns, nil } -func (u *ProjectUsecase) GetProjectNamespace(organizationId string, projectId string, stackId string, projectNamespaceId string) (*domain.ProjectNamespace, error) { - pn, err := u.projectRepo.GetProjectNamespaceById(organizationId, projectId, stackId, projectNamespaceId) +func (u *ProjectUsecase) GetProjectNamespace(organizationId string, projectId string, projectNamespace string, stackId string) (*domain.ProjectNamespace, error) { + pn, err := u.projectRepo.GetProjectNamespaceByPrimaryKey(organizationId, projectId, projectNamespace, stackId) if err != nil { log.Error(err) - return nil, errors.Wrap(err, "Failed to retrieve project namespaces.") + return nil, errors.Wrap(err, "Failed to retrieve project namespace.") } return pn, nil } -func (u *ProjectUsecase) DeleteProjectNamespace(organizationId string, projectId string, stackId string, - projectNamespaceId string) error { - if err := u.projectRepo.DeleteProjectNamespace(organizationId, projectId, stackId, projectNamespaceId); err != nil { +func (u *ProjectUsecase) DeleteProjectNamespace(organizationId string, projectId string, + stackId string, projectNamespace string) error { + if err := u.projectRepo.DeleteProjectNamespace(organizationId, projectId, projectNamespace, stackId); err != nil { log.Error(err) return errors.Wrap(err, "Failed to delete project namespace.") } diff --git a/pkg/domain/project.go b/pkg/domain/project.go index 6eb5148b..0ce775e2 100644 --- a/pkg/domain/project.go +++ b/pkg/domain/project.go @@ -22,8 +22,12 @@ func (t *ProjectMember) BeforeCreate(*gorm.DB) (err error) { return nil } +//func (t *ProjectNamespace) BeforeCreate(*gorm.DB) (err error) { +// t.ID = uuid.New().String() +// return nil +//} + func (t *ProjectNamespace) BeforeCreate(*gorm.DB) (err error) { - t.ID = uuid.New().String() return nil } @@ -39,38 +43,41 @@ type Project struct { ProjectNamespaces []ProjectNamespace `gorm:"foreignKey:ProjectId" json:"projectNamespaces,omitempty"` } -//type ProjectList struct { -// ID string `json:"id"` -// OrganizationId string `json:"organizationId"` -// Name string `json:"name"` -// Description string `json:"description"` -// ProjectMembers []struct { -// ID string `json:"id"` -// UserId string `json:"userId"` -// AccountId string `json:"accountId"` -// Name string `json:"name"` -// Email string `json:"email"` -// ProjectRoleId string `json:"projectId"` -// ProjectRoleName string `json:"projectRoleName"` -// CreatedAt time.Time `json:"createdAt"` -// UpdatedAt *time.Time `json:"updatedAt"` -// } `json:"projectMembers"` -// ProjectNamespaces []struct { -// ID string `json:"id"` -// StackId string `json:"stackId"` -// StackName string `json:"stackName"` -// Namespace string `json:"namespace"` -// Description string `json:"description"` -// Status string `json:"status"` -// CreatedAt time.Time `json:"createdAt"` -// UpdatedAt *time.Time `json:"updatedAt"` -// } `json:"projectNamespaces"` -// CreatedAt time.Time `json:"createdAt"` -// UpdatedAt *time.Time `json:"updatedAt"` -//} +type ProjectResponse struct { + ID string `json:"id"` + OrganizationId string `json:"organizationId"` + Name string `json:"name"` + Description string `json:"description"` + ProjectRoleId string `json:"projectRoleId"` + ProjectRoleName string `json:"projectRoleName"` + NamespaceCount int `json:"namespaceCount"` + AppCount int `json:"appCount"` + MemberCount int `json:"memberCount"` + CreatedAt time.Time `json:"createdAt"` +} type GetProjectsResponse struct { - Projects []Project `json:"projects"` + Projects []ProjectResponse `json:"projects"` +} + +type ProjectDetailResponse struct { + ID string `json:"id"` + OrganizationId string `json:"organizationId"` + Name string `json:"name"` + Description string `json:"description"` + ProjectLeaderId string `json:"projectLeaderId"` + ProjectLeaderName string `json:"projectLeaderName"` + ProjectLeaderAccountId string `json:"projectLeaderAccountId"` + ProjectLeaderDepartment string `json:"projectLeaderDepartment"` + NamespaceCount int `json:"namespaceCount"` + AppCount int `json:"appCount"` + MemberCount int `json:"memberCount"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt *time.Time `json:"updatedAt"` +} + +type GetProjectResponse struct { + Project *ProjectDetailResponse `json:"project"` } type ProjectRole struct { @@ -98,28 +105,37 @@ func (ProjectUser) TableName() string { } type ProjectMember struct { - ID string `gorm:"primarykey" json:"id"` - ProjectId string `gorm:"not null" json:"projectId"` - ProjectUserId uuid.UUID `json:"projectUserId"` - ProjectUser ProjectUser `gorm:"foreignKey:ProjectUserId;references:ID" json:"projectUser"` - ProjectRoleId string `json:"projectRoleId"` - ProjectRole ProjectRole `gorm:"foreignKey:ProjectRoleId" json:"projectRole"` - CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` - UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` - DeletedAt *time.Time `json:"deletedAt"` + ID string `gorm:"primarykey" json:"id"` + ProjectId string `gorm:"not null" json:"projectId"` + ProjectUserId uuid.UUID `json:"projectUserId"` + ProjectUser *ProjectUser `gorm:"foreignKey:ProjectUserId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectUser"` + ProjectRoleId string `json:"projectRoleId"` + ProjectRole *ProjectRole `gorm:"foreignKey:ProjectRoleId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectRole"` + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} + +type ProjectStack struct { + ID string `gorm:"primarykey" json:"id"` + OrganizationId string `json:"organizationId"` + Name string `json:"name"` +} + +func (ProjectStack) TableName() string { + return "clusters" } type ProjectNamespace struct { - ID string `gorm:"primarykey" json:"id"` - ProjectId string `gorm:"not null" json:"projectId"` - StackId string `gorm:"uniqueIndex:idx_stackid_namespace" json:"stackId"` - Namespace string `gorm:"uniqueIndex:idx_stackid_namespace" json:"namespace"` - StackName string `gorm:"-:all" json:"stackName,omitempty"` - Description string `json:"description,omitempty"` - Status string `json:"status,omitempty"` - CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` - UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` - DeletedAt *time.Time `json:"deletedAt"` + StackId string `gorm:"primarykey" json:"stackId"` + Namespace string `gorm:"primarykey" json:"namespace"` + Stack *ProjectStack `gorm:"foreignKey:StackId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"stack"` + ProjectId string `gorm:"not null" json:"projectId"` + Description string `json:"description,omitempty"` + Status string `json:"status,omitempty"` + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` } type CreateProjectRequest struct { @@ -130,7 +146,14 @@ type CreateProjectRequest struct { } type CreateProjectResponse struct { - Project Project `json:"project"` + ProjectId string `json:"projectId"` +} + +type UpdateProjectRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description"` + //ProjectLeaderId string `json:"projectLeaderId"` + //ProjectRoleId string `json:"projectRoleId"` } type GetProjectRoleResponse struct { @@ -149,10 +172,6 @@ type AddProjectMemberRequest struct { ProjectMemberRequests []ProjectMemberRequest `json:"projectMembers"` } -type AddProjectMemberResponse struct { - ProjectMembers []ProjectMember `json:"projectMembers"` -} - type GetProjectMemberResponse struct { ProjectMember *ProjectMember `json:"projectMember"` } @@ -175,19 +194,35 @@ type UpdateProjectMemberRoleRequest struct { ProjectRoleId string `json:"projectRoleId"` } +type UpdateProjectMembersRoleRequest struct { + ProjectMemberRoleRequests []struct { + ProjectMemberId string `json:"projectMemberId"` + ProjectRoleId string `json:"projectRoleId"` + } `json:"projectMembers"` +} + type CreateProjectNamespaceRequest struct { + StackId string `json:"stackId"` Namespace string `json:"namespace"` Description string `json:"description"` } -type CreateProjectNamespaceResponse struct { - ProjectNamesapceId string `json:"projectNamespaceId"` +type ProjectNamespaceResponse struct { + StackId string `json:"stackId"` + Namespace string `json:"namespace"` + StackName string `json:"stackName"` + ProjectId string `json:"projectId"` + Description string `json:"description"` + Status string `json:"status"` + AppCount int `json:"appCount"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt *time.Time `json:"updatedAt"` } type GetProjectNamespacesResponse struct { - ProjectNamespaces []ProjectNamespace `json:"projectNamespaces"` + ProjectNamespaces []ProjectNamespaceResponse `json:"projectNamespaces"` } type GetProjectNamespaceResponse struct { - ProjectNamespace *ProjectNamespace `json:"projectNamespace"` + ProjectNamespace *ProjectNamespaceResponse `json:"projectNamespace"` }