From a86736e91aecc5dae22d4df502822d227666e960 Mon Sep 17 00:00:00 2001 From: Taekyu Date: Thu, 13 Jul 2023 18:04:25 +0900 Subject: [PATCH 01/17] feature. implementation --- .gitignore | 3 +- api/swagger/docs.go | 7 ++- api/swagger/swagger.json | 7 ++- api/swagger/swagger.yaml | 6 +- internal/delivery/http/alert.go | 9 +++ internal/helper/util.go | 10 +++ internal/pagination/pagination.go | 89 ++++++++++++++------------- internal/repository/alert.go | 22 +++---- internal/repository/app-group.go | 15 ++++- internal/repository/app-serve-app.go | 26 +++++--- internal/repository/cloud-account.go | 15 +++-- internal/repository/cluster.go | 59 +++++++----------- internal/repository/organization.go | 12 ++-- internal/repository/repository.go | 25 +++++++- internal/repository/stack-template.go | 44 ++++++++++--- internal/repository/user.go | 4 +- pkg/domain/pagination.go | 4 +- 17 files changed, 217 insertions(+), 140 deletions(-) diff --git a/.gitignore b/.gitignore index 634da0d9..7da128bd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,9 @@ *.njsproj *.sln *.sw? +*.sh web main output -vendor \ No newline at end of file +vendor diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 4211d035..e36031ba 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -4768,8 +4768,11 @@ const docTemplate = `{ "column": { "type": "string" }, - "value": { - "type": "string" + "values": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index d7df0b40..f49ea698 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -4761,8 +4761,11 @@ "column": { "type": "string" }, - "value": { - "type": "string" + "values": { + "type": "array", + "items": { + "type": "string" + } } } }, diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index ce331393..e78d1933 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -832,8 +832,10 @@ definitions: properties: column: type: string - value: - type: string + values: + items: + type: string + type: array type: object domain.FindIdRequest: properties: diff --git a/internal/delivery/http/alert.go b/internal/delivery/http/alert.go index 5e08e873..987dd89e 100644 --- a/internal/delivery/http/alert.go +++ b/internal/delivery/http/alert.go @@ -123,6 +123,15 @@ func (h *AlertHandler) GetAlerts(w http.ResponseWriter, r *http.Request) { if err := domain.Map(*pg, &out.Pagination); err != nil { log.InfoWithContext(r.Context(), err) } + /* + outFilters := make([]domain.FilterResponse, len(pg.Filters)) + for j, filter := range pg.Filters { + if err := domain.Map(filter, &outFilters[j]); err != nil { + log.InfoWithContext(r.Context(), err) + } + } + out.Pagination.Filters = outFilters + */ ResponseJSON(w, r, http.StatusOK, out) } diff --git a/internal/helper/util.go b/internal/helper/util.go index 4da8fa44..c6e92f2a 100644 --- a/internal/helper/util.go +++ b/internal/helper/util.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math/big" + "regexp" "strconv" "strings" "time" @@ -70,3 +71,12 @@ func SplitAddress(url string) (address string, port int) { return } + +var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") +var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") + +func ToSnakeCase(str string) string { + snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") + snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") + return strings.ToLower(snake) +} diff --git a/internal/pagination/pagination.go b/internal/pagination/pagination.go index b8b43d3d..7b839c16 100644 --- a/internal/pagination/pagination.go +++ b/internal/pagination/pagination.go @@ -1,11 +1,18 @@ package pagination import ( - "encoding/json" "net/url" "strconv" + "strings" + + "github.com/openinfradev/tks-api/internal/helper" ) +const SORT_COLUMN = "sortColumn" +const SORT_ORDER = "sortOrder" +const PAGE_NUMBER = "pageNumber" +const PAGE_SIZE = "pageSize" + type Pagination struct { Limit int Page int @@ -18,7 +25,7 @@ type Pagination struct { type Filter struct { Column string - Value string + Values []string } var DEFAULT_LIMIT = 10 @@ -50,54 +57,50 @@ func (p *Pagination) GetSortOrder() string { return p.SortOrder } -func (p *Pagination) GetFilter() []Filter { +func (p *Pagination) GetFilters() []Filter { return p.Filters } -/* - { - sortingColumn : "id", - order : "ASC", - page : 1, - limit : 10, - } -*/ func NewPagination(urlParams *url.Values) *Pagination { - var pg Pagination - - pg.SortColumn = urlParams.Get("sortColumn") - if pg.SortColumn == "" { - pg.SortColumn = "created_at" - } - pg.SortOrder = urlParams.Get("sortOrder") - if pg.SortOrder == "" { - pg.SortOrder = "ASC" - } - - page := urlParams.Get("pageNumber") - if page == "" { - pg.Page = 1 - } else { - pg.Page, _ = strconv.Atoi(page) - } - - limit := urlParams.Get("pageSize") - if limit == "" { - pg.Limit = DEFAULT_LIMIT - } else { - limitNum, err := strconv.Atoi(limit) - if err == nil { - pg.Limit = limitNum + pg := NewDefaultPagination() + + for key, value := range *urlParams { + switch key { + case SORT_COLUMN: + if value[0] == "" { + pg.SortColumn = "created_at" + } else { + pg.SortColumn = value[0] + } + case SORT_ORDER: + if value[0] == "" { + pg.SortOrder = "ASC" + } else { + pg.SortOrder = value[0] + } + case PAGE_NUMBER: + if value[0] == "" { + pg.Page = 1 + } else { + pg.Page, _ = strconv.Atoi(value[0]) + } + case PAGE_SIZE: + if value[0] == "" { + pg.Page = DEFAULT_LIMIT + } else { + if limitNum, err := strconv.Atoi(value[0]); err == nil { + pg.Limit = limitNum + } + } + default: + pg.Filters = append(pg.Filters, Filter{ + Column: helper.ToSnakeCase(strings.Replace(key, "[]", "", -1)), + Values: value, + }) } } - // [TODO] filter - filter := urlParams.Get("filter") - if filter != "" { - _ = json.Unmarshal([]byte(filter), &pg.Filters) - } - - return &pg + return pg } func NewDefaultPagination() *Pagination { diff --git a/internal/repository/alert.go b/internal/repository/alert.go index 2ab031e4..eef5b64a 100644 --- a/internal/repository/alert.go +++ b/internal/repository/alert.go @@ -12,7 +12,6 @@ import ( "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/log" ) // Interfaces @@ -105,29 +104,24 @@ func (r *AlertRepository) GetByName(organizationId string, name string) (out dom func (r *AlertRepository) Fetch(organizationId string, pg *pagination.Pagination) (out []domain.Alert, err error) { var alerts []Alert - var total int64 - if pg == nil { pg = pagination.NewDefaultPagination() } - r.db.Find(&alerts, "organization_id = ?", organizationId).Count(&total) - - pg.TotalRows = total - pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) - log.Info(total) - log.Info(pg.Limit) - - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - - res := r.db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery). + filterFunc := CombinedGormFilter("alerts", pg.GetFilters()) + db := filterFunc(r.db.Model(&Alert{}). Preload("AlertActions", func(db *gorm.DB) *gorm.DB { return db.Order("created_at ASC") }).Preload("AlertActions.Taker"). Preload("Cluster", "status = 2"). Preload("Organization"). Joins("join clusters on clusters.id = alerts.cluster_id AND clusters.status = 2"). - Find(&alerts, "alerts.organization_id = ?", organizationId) + Where("alerts.organization_id = ?", organizationId)) + db.Count(&pg.TotalRows) + + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&alerts) if res.Error != nil { return nil, res.Error } diff --git a/internal/repository/app-group.go b/internal/repository/app-group.go index 1d3afccd..ba95dac8 100644 --- a/internal/repository/app-group.go +++ b/internal/repository/app-group.go @@ -2,6 +2,7 @@ package repository import ( "fmt" + "math" "github.com/google/uuid" "gorm.io/datatypes" @@ -77,12 +78,22 @@ func (c *Application) BeforeCreate(tx *gorm.DB) (err error) { // Logics func (r *AppGroupRepository) Fetch(clusterId domain.ClusterId, pg *pagination.Pagination) (out []domain.AppGroup, err error) { var appGroups []AppGroup - out = []domain.AppGroup{} + if pg == nil { + pg = pagination.NewDefaultPagination() + } + + filterFunc := CombinedGormFilter("app_groups", pg.GetFilters()) + db := filterFunc(r.db.Model(&AppGroup{}). + Where("cluster_id = ?", clusterId)) + db.Count(&pg.TotalRows) - res := r.db.Find(&appGroups, "cluster_id = ?", clusterId) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&appGroups) if res.Error != nil { return nil, res.Error } + for _, appGroup := range appGroups { outAppGroup := reflectAppGroup(appGroup) out = append(out, outAppGroup) diff --git a/internal/repository/app-serve-app.go b/internal/repository/app-serve-app.go index 124901a4..44c474a9 100644 --- a/internal/repository/app-serve-app.go +++ b/internal/repository/app-serve-app.go @@ -2,6 +2,7 @@ package repository import ( "fmt" + "math" "time" "github.com/openinfradev/tks-api/internal/pagination" @@ -56,15 +57,20 @@ func (r *AppServeAppRepository) CreateTask( return task.ID, nil } -func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll bool, pg *pagination.Pagination) ([]domain.AppServeApp, error) { - var apps []domain.AppServeApp +func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll bool, pg *pagination.Pagination) (apps []domain.AppServeApp, err error) { var clusters []Cluster - - queryStr := fmt.Sprintf("organization_id = '%s' AND status <> 'DELETE_SUCCESS'", organizationId) - if showAll { - queryStr = fmt.Sprintf("organization_id = '%s'", organizationId) + if pg == nil { + pg = pagination.NewDefaultPagination() } - res := r.db.Order("created_at desc").Find(&apps, queryStr) + + filterFunc := CombinedGormFilter("app_serve_apps", pg.GetFilters()) + db := filterFunc(r.db.Model(&domain.AppServeApp{}). + Where("app_serve_apps.organization_id = ? AND status <> 'DELETE_SUCCESS'", organizationId)) + db.Count(&pg.TotalRows) + + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&apps) if res.Error != nil { return nil, fmt.Errorf("error while finding appServeApps with organizationId: %s", organizationId) } @@ -75,8 +81,8 @@ func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll b } // Add cluster names to apps list - queryStr = fmt.Sprintf("organization_id = '%s' AND status <> '%d'", organizationId, domain.ClusterStatus_DELETED) - res = r.db.Order("created_at desc").Find(&clusters, queryStr) + queryStr := fmt.Sprintf("organization_id = '%s' AND status <> '%d'", organizationId, domain.ClusterStatus_DELETED) + res = r.db.Find(&clusters, queryStr) if res.Error != nil { return nil, fmt.Errorf("error while fetching clusters with organizationId: %s", organizationId) } @@ -90,7 +96,7 @@ func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll b } } - return apps, nil + return } func (r *AppServeAppRepository) GetAppServeAppById(appId string) (*domain.AppServeApp, error) { diff --git a/internal/repository/cloud-account.go b/internal/repository/cloud-account.go index bfa052db..c8d4a206 100644 --- a/internal/repository/cloud-account.go +++ b/internal/repository/cloud-account.go @@ -96,19 +96,18 @@ func (r *CloudAccountRepository) GetByAwsAccountId(awsAccountId string) (out dom func (r *CloudAccountRepository) Fetch(organizationId string, pg *pagination.Pagination) (out []domain.CloudAccount, err error) { var cloudAccounts []CloudAccount - var total int64 - if pg == nil { pg = pagination.NewDefaultPagination() } - r.db.Find(&cloudAccounts, "organization_id = ? AND status != ?", organizationId, domain.CloudAccountStatus_DELETED).Count(&total) - - pg.TotalRows = total - pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) + filterFunc := CombinedGormFilter("cloud_accounts", pg.GetFilters()) + db := filterFunc(r.db.Model(&CloudAccount{}). + Preload(clause.Associations). + Where("organization_id = ? AND status != ?", organizationId, domain.CloudAccountStatus_DELETED)) + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := r.db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery). - Preload(clause.Associations).Find(&cloudAccounts, "organization_id = ? AND status != ?", organizationId, domain.CloudAccountStatus_DELETED) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&cloudAccounts) if res.Error != nil { return nil, res.Error } diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index 310b4d22..db8ff764 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -86,19 +86,17 @@ func (r *ClusterRepository) WithTrx(trxHandle *gorm.DB) IClusterRepository { func (r *ClusterRepository) Fetch(pg *pagination.Pagination) (out []domain.Cluster, err error) { var clusters []Cluster - var total int64 - if pg == nil { pg = pagination.NewDefaultPagination() } - r.db.Find(&clusters).Count(&total) + filterFunc := CombinedGormFilter("clusters", pg.GetFilters()) + db := filterFunc(r.db.Model(&Cluster{})) - pg.TotalRows = total - pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := r.db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery). - Preload(clause.Associations).Find(&clusters) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&clusters) if res.Error != nil { return nil, res.Error } @@ -106,71 +104,58 @@ func (r *ClusterRepository) Fetch(pg *pagination.Pagination) (out []domain.Clust outCluster := reflectCluster(cluster) out = append(out, outCluster) } - return out, nil + return } func (r *ClusterRepository) FetchByOrganizationId(organizationId string, pg *pagination.Pagination) (out []domain.Cluster, err error) { var clusters []Cluster - var total int64 - if pg == nil { pg = pagination.NewDefaultPagination() } - r.db.Find(&clusters, "organization_id = ? AND status != ?", organizationId, domain.ClusterStatus_DELETED).Count(&total) + pg.SortColumn = "updated_at" + pg.SortOrder = "DESC" + filterFunc := CombinedGormFilter("clusters", pg.GetFilters()) + db := filterFunc(r.db.Model(&Cluster{}).Preload(clause.Associations). + Where("organization_id = ? AND status != ?", organizationId, domain.ClusterStatus_DELETED)) - pg.TotalRows = total - pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := r.db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery). - Preload(clause.Associations).Order("updated_at desc, created_at desc"). - Find(&clusters, "organization_id = ? AND status != ?", organizationId, domain.ClusterStatus_DELETED) - + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&clusters) if res.Error != nil { return nil, res.Error } - - if res.RowsAffected == 0 { - return out, nil - } - for _, cluster := range clusters { outCluster := reflectCluster(cluster) out = append(out, outCluster) } - return } func (r *ClusterRepository) FetchByCloudAccountId(cloudAccountId uuid.UUID, pg *pagination.Pagination) (out []domain.Cluster, err error) { var clusters []Cluster - var total int64 - if pg == nil { pg = pagination.NewDefaultPagination() } - r.db.Find(&clusters, "cloud_account_id = ?", cloudAccountId).Count(&total) + pg.SortColumn = "updated_at" + pg.SortOrder = "DESC" + filterFunc := CombinedGormFilter("clusters", pg.GetFilters()) + db := filterFunc(r.db.Model(&Cluster{}).Preload("CloudAccount"). + Where("cloud_account_id = ?", cloudAccountId)) - pg.TotalRows = total - pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := r.db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery). - Preload("CloudAccount").Order("updated_at desc, created_at desc").Find(&clusters, "cloud_account_id = ?", cloudAccountId) - + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&clusters) if res.Error != nil { return nil, res.Error } - - if res.RowsAffected == 0 { - return out, nil - } - for _, cluster := range clusters { outCluster := reflectCluster(cluster) out = append(out, outCluster) } - return } diff --git a/internal/repository/organization.go b/internal/repository/organization.go index 3f4e8773..b66830d5 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -74,20 +74,18 @@ func (r *OrganizationRepository) Create(organizationId string, name string, crea func (r *OrganizationRepository) Fetch(pg *pagination.Pagination) (*[]domain.Organization, error) { var organizations []Organization var out []domain.Organization - var total int64 - if pg == nil { pg = pagination.NewDefaultPagination() } - r.db.Find(&organizations).Count(&total) - pg.TotalRows = total - pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) + filterFunc := CombinedGormFilter("organizations", pg.GetFilters()) + db := filterFunc(r.db.Model(&Organization{})) + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := r.db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&organizations) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&organizations) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) return nil, res.Error } for _, organization := range organizations { diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 01e45574..dc01804a 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -1,6 +1,11 @@ package repository -import "gorm.io/gorm" +import ( + "fmt" + "gorm.io/gorm" + + "github.com/openinfradev/tks-api/internal/pagination" +) type FilterFunc func(user *gorm.DB) *gorm.DB @@ -15,3 +20,21 @@ type Repository struct { StackTemplate IStackTemplateRepository Alert IAlertRepository } + +func CombinedGormFilter(baseTableName string, filters []pagination.Filter) FilterFunc { + return func(db *gorm.DB) *gorm.DB { + for _, filter := range filters { + if len(filter.Values) > 1 { + inQuery := fmt.Sprintf("%s.%s in (", baseTableName, filter.Column) + for _, val := range filter.Values { + inQuery = inQuery + fmt.Sprintf("'%s',", val) + } + inQuery = inQuery[:len(inQuery)-1] + ")" + db = db.Where(inQuery) + } else { + db = db.Where(fmt.Sprintf("%s.%s = '%s'", baseTableName, filter.Column, filter.Values[0])) + } + } + return db + } +} diff --git a/internal/repository/stack-template.go b/internal/repository/stack-template.go index 460b66c6..17b25292 100644 --- a/internal/repository/stack-template.go +++ b/internal/repository/stack-template.go @@ -71,21 +71,51 @@ func (r *StackTemplateRepository) Get(stackTemplateId uuid.UUID) (out domain.Sta } // [TODO] organizationId 별로 생성하지 않고, 하나의 stackTemplate 을 모든 organization 에서 재사용한다. ( 5월 한정, 추후 rearchitecture 필요) + +/* + var alerts []Alert + if pg == nil { + pg = pagination.NewDefaultPagination() + } + + filterFunc := CombinedGormFilter("alerts", pg.GetFilters()) + db := filterFunc(r.db.Model(&Alert{}). + Preload("AlertActions", func(db *gorm.DB) *gorm.DB { + return db.Order("created_at ASC") + }).Preload("AlertActions.Taker"). + Preload("Cluster", "status = 2"). + Preload("Organization"). + Joins("join clusters on clusters.id = alerts.cluster_id AND clusters.status = 2"). + Where("alerts.organization_id = ?", organizationId)) + db.Count(&pg.TotalRows) + + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) + orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&alerts) + if res.Error != nil { + return nil, res.Error + } + + for _, alert := range alerts { + out = append(out, reflectAlert(alert)) + } + return + +*/ + func (r *StackTemplateRepository) Fetch(pg *pagination.Pagination) (out []domain.StackTemplate, err error) { var stackTemplates []StackTemplate - var total int64 - if pg == nil { pg = pagination.NewDefaultPagination() } - r.db.Find(&stackTemplates).Count(&total) - pg.TotalRows = total - pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) + filterFunc := CombinedGormFilter("stack_templates", pg.GetFilters()) + db := filterFunc(r.db.Model(&StackTemplate{})) + db.Count(&pg.TotalRows) + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := r.db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery). - Preload(clause.Associations).Find(&stackTemplates) + res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&stackTemplates) if res.Error != nil { return nil, res.Error } diff --git a/internal/repository/user.go b/internal/repository/user.go index bf8857f2..5188c0d7 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -153,7 +153,7 @@ func (r *UserRepository) List(pg *pagination.Pagination, filters ...FilterFunc) } if filters == nil { - r.db.Find(&users).Count(&total) + r.db.Model(&User{}).Count(&total) pg.TotalRows = total pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) @@ -170,7 +170,7 @@ func (r *UserRepository) List(pg *pagination.Pagination, filters ...FilterFunc) } } cFunc := combinedFilter(filters...) - cFunc(r.db.Model(&User{})).Find(&total) + cFunc(r.db.Model(&User{})).Count(&total) pg.TotalRows = total pg.TotalPages = int(math.Ceil(float64(total) / float64(pg.Limit))) diff --git a/pkg/domain/pagination.go b/pkg/domain/pagination.go index e91e9c4a..33efaef7 100644 --- a/pkg/domain/pagination.go +++ b/pkg/domain/pagination.go @@ -11,6 +11,6 @@ type PaginationResponse struct { } type FilterResponse struct { - Column string `json:"column"` - Value string `json:"value"` + Column string `json:"column"` + Values []string `json:"values"` } From e90d6f0dcdd38734987b6b61ce64662d029718a3 Mon Sep 17 00:00:00 2001 From: Taekyu Date: Fri, 14 Jul 2023 14:18:01 +0900 Subject: [PATCH 02/17] trivial. minor changes --- internal/repository/alert.go | 2 +- internal/repository/app-group.go | 5 +++- internal/repository/app-serve-app.go | 2 +- internal/repository/cloud-account.go | 2 +- internal/repository/cluster.go | 6 ++--- internal/repository/organization.go | 2 +- internal/repository/repository.go | 6 ++--- internal/repository/stack-template.go | 34 +-------------------------- 8 files changed, 15 insertions(+), 44 deletions(-) diff --git a/internal/repository/alert.go b/internal/repository/alert.go index eef5b64a..8031e89f 100644 --- a/internal/repository/alert.go +++ b/internal/repository/alert.go @@ -108,7 +108,7 @@ func (r *AlertRepository) Fetch(organizationId string, pg *pagination.Pagination pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter("alerts", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&Alert{}). Preload("AlertActions", func(db *gorm.DB) *gorm.DB { return db.Order("created_at ASC") diff --git a/internal/repository/app-group.go b/internal/repository/app-group.go index ba95dac8..49a22cdf 100644 --- a/internal/repository/app-group.go +++ b/internal/repository/app-group.go @@ -82,11 +82,14 @@ func (r *AppGroupRepository) Fetch(clusterId domain.ClusterId, pg *pagination.Pa pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter("app_groups", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&AppGroup{}). Where("cluster_id = ?", clusterId)) db.Count(&pg.TotalRows) + r.db.Model(&AppGroup{}). + Where("cluster_id = ?", clusterId).Where("id").Where("app_groups.status").Where("app_groups.deleted") + pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&appGroups) diff --git a/internal/repository/app-serve-app.go b/internal/repository/app-serve-app.go index 44c474a9..46f32297 100644 --- a/internal/repository/app-serve-app.go +++ b/internal/repository/app-serve-app.go @@ -63,7 +63,7 @@ func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll b pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter("app_serve_apps", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&domain.AppServeApp{}). Where("app_serve_apps.organization_id = ? AND status <> 'DELETE_SUCCESS'", organizationId)) db.Count(&pg.TotalRows) diff --git a/internal/repository/cloud-account.go b/internal/repository/cloud-account.go index c8d4a206..21ef0d86 100644 --- a/internal/repository/cloud-account.go +++ b/internal/repository/cloud-account.go @@ -99,7 +99,7 @@ func (r *CloudAccountRepository) Fetch(organizationId string, pg *pagination.Pag if pg == nil { pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter("cloud_accounts", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&CloudAccount{}). Preload(clause.Associations). Where("organization_id = ? AND status != ?", organizationId, domain.CloudAccountStatus_DELETED)) diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index db8ff764..e131fdd6 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -89,7 +89,7 @@ func (r *ClusterRepository) Fetch(pg *pagination.Pagination) (out []domain.Clust if pg == nil { pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter("clusters", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&Cluster{})) db.Count(&pg.TotalRows) @@ -114,7 +114,7 @@ func (r *ClusterRepository) FetchByOrganizationId(organizationId string, pg *pag } pg.SortColumn = "updated_at" pg.SortOrder = "DESC" - filterFunc := CombinedGormFilter("clusters", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&Cluster{}).Preload(clause.Associations). Where("organization_id = ? AND status != ?", organizationId, domain.ClusterStatus_DELETED)) @@ -140,7 +140,7 @@ func (r *ClusterRepository) FetchByCloudAccountId(cloudAccountId uuid.UUID, pg * } pg.SortColumn = "updated_at" pg.SortOrder = "DESC" - filterFunc := CombinedGormFilter("clusters", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&Cluster{}).Preload("CloudAccount"). Where("cloud_account_id = ?", cloudAccountId)) diff --git a/internal/repository/organization.go b/internal/repository/organization.go index b66830d5..0e3b8221 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -78,7 +78,7 @@ func (r *OrganizationRepository) Fetch(pg *pagination.Pagination) (*[]domain.Org pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter("organizations", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&Organization{})) db.Count(&pg.TotalRows) diff --git a/internal/repository/repository.go b/internal/repository/repository.go index dc01804a..4cf95fd1 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -21,18 +21,18 @@ type Repository struct { Alert IAlertRepository } -func CombinedGormFilter(baseTableName string, filters []pagination.Filter) FilterFunc { +func CombinedGormFilter(filters []pagination.Filter) FilterFunc { return func(db *gorm.DB) *gorm.DB { for _, filter := range filters { if len(filter.Values) > 1 { - inQuery := fmt.Sprintf("%s.%s in (", baseTableName, filter.Column) + inQuery := fmt.Sprintf("%s in (", filter.Column) for _, val := range filter.Values { inQuery = inQuery + fmt.Sprintf("'%s',", val) } inQuery = inQuery[:len(inQuery)-1] + ")" db = db.Where(inQuery) } else { - db = db.Where(fmt.Sprintf("%s.%s = '%s'", baseTableName, filter.Column, filter.Values[0])) + db = db.Where(fmt.Sprintf("%s = '%s'", filter.Column, filter.Values[0])) } } return db diff --git a/internal/repository/stack-template.go b/internal/repository/stack-template.go index 17b25292..e5bd5ec6 100644 --- a/internal/repository/stack-template.go +++ b/internal/repository/stack-template.go @@ -71,45 +71,13 @@ func (r *StackTemplateRepository) Get(stackTemplateId uuid.UUID) (out domain.Sta } // [TODO] organizationId 별로 생성하지 않고, 하나의 stackTemplate 을 모든 organization 에서 재사용한다. ( 5월 한정, 추후 rearchitecture 필요) - -/* - var alerts []Alert - if pg == nil { - pg = pagination.NewDefaultPagination() - } - - filterFunc := CombinedGormFilter("alerts", pg.GetFilters()) - db := filterFunc(r.db.Model(&Alert{}). - Preload("AlertActions", func(db *gorm.DB) *gorm.DB { - return db.Order("created_at ASC") - }).Preload("AlertActions.Taker"). - Preload("Cluster", "status = 2"). - Preload("Organization"). - Joins("join clusters on clusters.id = alerts.cluster_id AND clusters.status = 2"). - Where("alerts.organization_id = ?", organizationId)) - db.Count(&pg.TotalRows) - - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&alerts) - if res.Error != nil { - return nil, res.Error - } - - for _, alert := range alerts { - out = append(out, reflectAlert(alert)) - } - return - -*/ - func (r *StackTemplateRepository) Fetch(pg *pagination.Pagination) (out []domain.StackTemplate, err error) { var stackTemplates []StackTemplate if pg == nil { pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter("stack_templates", pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters()) db := filterFunc(r.db.Model(&StackTemplate{})) db.Count(&pg.TotalRows) From 28ced533803647e6c369afa5384d40b443f9155e Mon Sep 17 00:00:00 2001 From: Taekyu Date: Mon, 17 Jul 2023 13:15:32 +0900 Subject: [PATCH 03/17] feature. add combindedFilter to pagination --- api/swagger/docs.go | 9 ++--- api/swagger/swagger.json | 9 ++--- api/swagger/swagger.yaml | 8 ++--- internal/delivery/http/alert.go | 7 +++- internal/delivery/http/app-group.go | 6 +++- internal/delivery/http/app-serve-app.go | 6 +++- internal/delivery/http/cloud-account.go | 6 +++- internal/delivery/http/cluster.go | 6 +++- internal/delivery/http/organization.go | 6 +++- internal/delivery/http/stack-template.go | 6 +++- internal/delivery/http/stack.go | 8 +++-- internal/delivery/http/user.go | 6 +++- internal/pagination/pagination.go | 42 +++++++++++++++++++----- internal/repository/alert.go | 2 +- internal/repository/app-group.go | 2 +- internal/repository/app-serve-app.go | 2 +- internal/repository/cloud-account.go | 2 +- internal/repository/cluster.go | 6 ++-- internal/repository/organization.go | 2 +- internal/repository/repository.go | 15 ++++++++- internal/repository/stack-template.go | 2 +- 21 files changed, 112 insertions(+), 46 deletions(-) diff --git a/api/swagger/docs.go b/api/swagger/docs.go index e36031ba..cf803d99 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -2596,12 +2596,9 @@ const docTemplate = `{ "in": "query" }, { - "type": "array", - "items": { - "type": "string" - }, - "description": "filters", - "name": "filters", + "type": "string", + "description": "combinedFilter", + "name": "combinedFilter", "in": "query" } ], diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index f49ea698..2738fb7e 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -2589,12 +2589,9 @@ "in": "query" }, { - "type": "array", - "items": { - "type": "string" - }, - "description": "filters", - "name": "filters", + "type": "string", + "description": "combinedFilter", + "name": "combinedFilter", "in": "query" } ], diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index e78d1933..c23032ea 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -3395,12 +3395,10 @@ paths: in: query name: sortOrder type: string - - description: filters + - description: combinedFilter in: query - items: - type: string - name: filters - type: array + name: combinedFilter + type: string produces: - application/json responses: diff --git a/internal/delivery/http/alert.go b/internal/delivery/http/alert.go index 987dd89e..a749f4a0 100644 --- a/internal/delivery/http/alert.go +++ b/internal/delivery/http/alert.go @@ -94,7 +94,12 @@ func (h *AlertHandler) GetAlerts(w http.ResponseWriter, r *http.Request) { } urlParams := r.URL.Query() - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + alerts, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) diff --git a/internal/delivery/http/app-group.go b/internal/delivery/http/app-group.go index ae169b70..624a5779 100644 --- a/internal/delivery/http/app-group.go +++ b/internal/delivery/http/app-group.go @@ -82,7 +82,11 @@ func (h *AppGroupHandler) GetAppGroups(w http.ResponseWriter, r *http.Request) { ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid clusterId"), "C_INVALID_CLUSTER_ID", "")) return } - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } appGroups, err := h.usecase.Fetch(r.Context(), domain.ClusterId(clusterId), pg) if err != nil { diff --git a/internal/delivery/http/app-serve-app.go b/internal/delivery/http/app-serve-app.go index b30a3a40..06b8965f 100644 --- a/internal/delivery/http/app-serve-app.go +++ b/internal/delivery/http/app-serve-app.go @@ -197,7 +197,11 @@ func (h *AppServeAppHandler) GetAppServeApps(w http.ResponseWriter, r *http.Requ ErrorJSON(w, r, err) return } - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } apps, err := h.usecase.GetAppServeApps(organizationId, showAll, pg) if err != nil { diff --git a/internal/delivery/http/cloud-account.go b/internal/delivery/http/cloud-account.go index 32e61bc8..33c71eea 100644 --- a/internal/delivery/http/cloud-account.go +++ b/internal/delivery/http/cloud-account.go @@ -93,7 +93,11 @@ func (h *CloudAccountHandler) GetCloudAccounts(w http.ResponseWriter, r *http.Re } urlParams := r.URL.Query() - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } cloudAccounts, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) diff --git a/internal/delivery/http/cluster.go b/internal/delivery/http/cluster.go index 16d697fd..6b0613d9 100644 --- a/internal/delivery/http/cluster.go +++ b/internal/delivery/http/cluster.go @@ -41,7 +41,11 @@ func (h *ClusterHandler) GetClusters(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() organizationId := urlParams.Get("organizationId") - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } clusters, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) diff --git a/internal/delivery/http/organization.go b/internal/delivery/http/organization.go index ba6e1111..12e420fb 100644 --- a/internal/delivery/http/organization.go +++ b/internal/delivery/http/organization.go @@ -90,7 +90,11 @@ func (h *OrganizationHandler) CreateOrganization(w http.ResponseWriter, r *http. // @Security JWT func (h *OrganizationHandler) GetOrganizations(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } organizations, err := h.usecase.Fetch(pg) if err != nil { diff --git a/internal/delivery/http/stack-template.go b/internal/delivery/http/stack-template.go index f1a017dd..56ebb222 100644 --- a/internal/delivery/http/stack-template.go +++ b/internal/delivery/http/stack-template.go @@ -55,7 +55,11 @@ func (h *StackTemplateHandler) CreateStackTemplate(w http.ResponseWriter, r *htt // @Security JWT func (h *StackTemplateHandler) GetStackTemplates(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } stackTemplates, err := h.usecase.Fetch(r.Context(), pg) if err != nil { diff --git a/internal/delivery/http/stack.go b/internal/delivery/http/stack.go index 1c69d591..a85c52ed 100644 --- a/internal/delivery/http/stack.go +++ b/internal/delivery/http/stack.go @@ -81,7 +81,7 @@ func (h *StackHandler) CreateStack(w http.ResponseWriter, r *http.Request) { // @Param page query string false "pageNumber" // @Param soertColumn query string false "sortColumn" // @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" +// @Param combinedFilter query string false "combinedFilter" // @Success 200 {object} domain.GetStacksResponse // @Router /organizations/{organizationId}/stacks [get] // @Security JWT @@ -94,7 +94,11 @@ func (h *StackHandler) GetStacks(w http.ResponseWriter, r *http.Request) { } urlParams := r.URL.Query() - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } stacks, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) diff --git a/internal/delivery/http/user.go b/internal/delivery/http/user.go index 787d07dc..135393ed 100644 --- a/internal/delivery/http/user.go +++ b/internal/delivery/http/user.go @@ -175,7 +175,11 @@ func (u UserHandler) List(w http.ResponseWriter, r *http.Request) { } urlParams := r.URL.Query() - pg := pagination.NewPagination(&urlParams) + pg, err := pagination.NewPagination(&urlParams) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } users, err := u.usecase.List(r.Context(), organizationId, pg) if err != nil { if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { diff --git a/internal/pagination/pagination.go b/internal/pagination/pagination.go index 7b839c16..01f8f513 100644 --- a/internal/pagination/pagination.go +++ b/internal/pagination/pagination.go @@ -1,6 +1,7 @@ package pagination import ( + "fmt" "net/url" "strconv" "strings" @@ -12,15 +13,17 @@ const SORT_COLUMN = "sortColumn" const SORT_ORDER = "sortOrder" const PAGE_NUMBER = "pageNumber" const PAGE_SIZE = "pageSize" +const COMBINED_FILTER = "combinedFilter" type Pagination struct { - Limit int - Page int - SortColumn string - SortOrder string - Filters []Filter - TotalRows int64 - TotalPages int + Limit int + Page int + SortColumn string + SortOrder string + Filters []Filter + CombinedFilter CombinedFilter + TotalRows int64 + TotalPages int } type Filter struct { @@ -28,6 +31,11 @@ type Filter struct { Values []string } +type CombinedFilter struct { + Columns []string + Value string +} + var DEFAULT_LIMIT = 10 var MAX_LIMIT = 1000 @@ -61,7 +69,7 @@ func (p *Pagination) GetFilters() []Filter { return p.Filters } -func NewPagination(urlParams *url.Values) *Pagination { +func NewPagination(urlParams *url.Values) (*Pagination, error) { pg := NewDefaultPagination() for key, value := range *urlParams { @@ -92,6 +100,22 @@ func NewPagination(urlParams *url.Values) *Pagination { pg.Limit = limitNum } } + case COMBINED_FILTER: + if len(value[0]) > 0 { + //"combinedFilter=key1,key2:value" + filterArray := strings.Split(value[0], ":") + if len(filterArray) == 2 { + keys := strings.Split(filterArray[0], ",") + value := filterArray[1] + + pg.CombinedFilter = CombinedFilter{ + Columns: keys, + Value: value, + } + } else { + return nil, fmt.Errorf("Invalid query string : combinedFilter ") + } + } default: pg.Filters = append(pg.Filters, Filter{ Column: helper.ToSnakeCase(strings.Replace(key, "[]", "", -1)), @@ -100,7 +124,7 @@ func NewPagination(urlParams *url.Values) *Pagination { } } - return pg + return pg, nil } func NewDefaultPagination() *Pagination { diff --git a/internal/repository/alert.go b/internal/repository/alert.go index 8031e89f..4bd33de7 100644 --- a/internal/repository/alert.go +++ b/internal/repository/alert.go @@ -108,7 +108,7 @@ func (r *AlertRepository) Fetch(organizationId string, pg *pagination.Pagination pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Alert{}). Preload("AlertActions", func(db *gorm.DB) *gorm.DB { return db.Order("created_at ASC") diff --git a/internal/repository/app-group.go b/internal/repository/app-group.go index 49a22cdf..4d033e48 100644 --- a/internal/repository/app-group.go +++ b/internal/repository/app-group.go @@ -82,7 +82,7 @@ func (r *AppGroupRepository) Fetch(clusterId domain.ClusterId, pg *pagination.Pa pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&AppGroup{}). Where("cluster_id = ?", clusterId)) db.Count(&pg.TotalRows) diff --git a/internal/repository/app-serve-app.go b/internal/repository/app-serve-app.go index 46f32297..64b265a5 100644 --- a/internal/repository/app-serve-app.go +++ b/internal/repository/app-serve-app.go @@ -63,7 +63,7 @@ func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll b pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&domain.AppServeApp{}). Where("app_serve_apps.organization_id = ? AND status <> 'DELETE_SUCCESS'", organizationId)) db.Count(&pg.TotalRows) diff --git a/internal/repository/cloud-account.go b/internal/repository/cloud-account.go index 21ef0d86..a4fc7da8 100644 --- a/internal/repository/cloud-account.go +++ b/internal/repository/cloud-account.go @@ -99,7 +99,7 @@ func (r *CloudAccountRepository) Fetch(organizationId string, pg *pagination.Pag if pg == nil { pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&CloudAccount{}). Preload(clause.Associations). Where("organization_id = ? AND status != ?", organizationId, domain.CloudAccountStatus_DELETED)) diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index e131fdd6..9cc690da 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -89,7 +89,7 @@ func (r *ClusterRepository) Fetch(pg *pagination.Pagination) (out []domain.Clust if pg == nil { pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Cluster{})) db.Count(&pg.TotalRows) @@ -114,7 +114,7 @@ func (r *ClusterRepository) FetchByOrganizationId(organizationId string, pg *pag } pg.SortColumn = "updated_at" pg.SortOrder = "DESC" - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Cluster{}).Preload(clause.Associations). Where("organization_id = ? AND status != ?", organizationId, domain.ClusterStatus_DELETED)) @@ -140,7 +140,7 @@ func (r *ClusterRepository) FetchByCloudAccountId(cloudAccountId uuid.UUID, pg * } pg.SortColumn = "updated_at" pg.SortOrder = "DESC" - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Cluster{}).Preload("CloudAccount"). Where("cloud_account_id = ?", cloudAccountId)) diff --git a/internal/repository/organization.go b/internal/repository/organization.go index 0e3b8221..0a2920f6 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -78,7 +78,7 @@ func (r *OrganizationRepository) Fetch(pg *pagination.Pagination) (*[]domain.Org pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Organization{})) db.Count(&pg.TotalRows) diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 4cf95fd1..9f1dd0ed 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -21,8 +21,9 @@ type Repository struct { Alert IAlertRepository } -func CombinedGormFilter(filters []pagination.Filter) FilterFunc { +func CombinedGormFilter(filters []pagination.Filter, combinedFilter pagination.CombinedFilter) FilterFunc { return func(db *gorm.DB) *gorm.DB { + // and query for _, filter := range filters { if len(filter.Values) > 1 { inQuery := fmt.Sprintf("%s in (", filter.Column) @@ -35,6 +36,18 @@ func CombinedGormFilter(filters []pagination.Filter) FilterFunc { db = db.Where(fmt.Sprintf("%s = '%s'", filter.Column, filter.Values[0])) } } + + // or query + // id = '123' or description = '345' + if len(combinedFilter.Columns) > 0 { + orQuery := "" + for _, column := range combinedFilter.Columns { + orQuery = orQuery + fmt.Sprintf("%s like '%%%s%%' OR ", column, combinedFilter.Value) + } + orQuery = orQuery[:len(orQuery)-3] + db = db.Where(orQuery) + } + return db } } diff --git a/internal/repository/stack-template.go b/internal/repository/stack-template.go index e5bd5ec6..b027ec77 100644 --- a/internal/repository/stack-template.go +++ b/internal/repository/stack-template.go @@ -77,7 +77,7 @@ func (r *StackTemplateRepository) Fetch(pg *pagination.Pagination) (out []domain pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters()) + filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&StackTemplate{})) db.Count(&pg.TotalRows) From 49742cf3a8da66940cfe7cc8ebd7076d819aa7b3 Mon Sep 17 00:00:00 2001 From: Robert Choi Date: Mon, 17 Jul 2023 16:16:31 +0900 Subject: [PATCH 04/17] app-serving: add preprocessing for extraEnv param tks-issues-753 --- internal/usecase/app-serve-app.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/usecase/app-serve-app.go b/internal/usecase/app-serve-app.go index 77111f02..e54b165e 100644 --- a/internal/usecase/app-serve-app.go +++ b/internal/usecase/app-serve-app.go @@ -2,6 +2,7 @@ package usecase import ( "fmt" + "encoding/json" "strconv" "strings" @@ -88,6 +89,26 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, } } + // Preprocess extraEnv param + extEnv := app.AppServeAppTasks[0].ExtraEnv + log.Debug("extraEnv received: ", extEnv) + + tempMap := map[string]string{} + json.Unmarshal([]byte(extEnv), &tempMap) + log.Debugf("extraEnv marshalled: %v", tempMap) + + newExtEnv := map[string]string{} + for key, val := range tempMap { + newkey := "\"" + key + "\"" + newval := "\"" + val + "\"" + newExtEnv[newkey] = newval + } + + mJson, _ := json.Marshal(newExtEnv) + newExtEnvStr := string(mJson) + + log.Debug("After transform, extraEnv: ", newExtEnvStr) + // TODO: Validate PV params appId, taskId, err := u.repo.CreateAppServeApp(app) @@ -116,7 +137,7 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, "image_url=" + app.AppServeAppTasks[0].ImageUrl, "port=" + app.AppServeAppTasks[0].Port, "profile=" + app.AppServeAppTasks[0].Profile, - "extra_env=" + app.AppServeAppTasks[0].ExtraEnv, + "extra_env=" + newExtEnvStr, "app_config=" + app.AppServeAppTasks[0].AppConfig, "app_secret=" + app.AppServeAppTasks[0].AppSecret, "resource_spec=" + app.AppServeAppTasks[0].ResourceSpec, From 2452b63e55d4ee626a7884d945d88842ad1261f3 Mon Sep 17 00:00:00 2001 From: Robert Choi Date: Mon, 17 Jul 2023 16:33:55 +0900 Subject: [PATCH 05/17] trivial: fix lint err --- internal/usecase/app-serve-app.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/usecase/app-serve-app.go b/internal/usecase/app-serve-app.go index e54b165e..c7079cb5 100644 --- a/internal/usecase/app-serve-app.go +++ b/internal/usecase/app-serve-app.go @@ -1,8 +1,8 @@ package usecase import ( - "fmt" "encoding/json" + "fmt" "strconv" "strings" @@ -94,7 +94,11 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, log.Debug("extraEnv received: ", extEnv) tempMap := map[string]string{} - json.Unmarshal([]byte(extEnv), &tempMap) + err := json.Unmarshal([]byte(extEnv), &tempMap) + if err != nil { + log.Error(err) + return "", "", errors.Wrap(err, "Failed to process extraEnv param.") + } log.Debugf("extraEnv marshalled: %v", tempMap) newExtEnv := map[string]string{} From cc6753854a6716c2ba232e1f6b8d10db618f5591 Mon Sep 17 00:00:00 2001 From: Robert Choi Date: Mon, 17 Jul 2023 18:17:41 +0900 Subject: [PATCH 06/17] apply same stuff for update logic --- internal/usecase/app-serve-app.go | 47 +++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/internal/usecase/app-serve-app.go b/internal/usecase/app-serve-app.go index c7079cb5..1d010bc5 100644 --- a/internal/usecase/app-serve-app.go +++ b/internal/usecase/app-serve-app.go @@ -89,12 +89,20 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, } } - // Preprocess extraEnv param + appId, taskId, err := u.repo.CreateAppServeApp(app) + if err != nil { + log.Error(err) + return "", "", errors.Wrap(err, "Failed to create app.") + } + + fmt.Printf("appId = %s, taskId = %s", appId, taskId) + + /* Preprocess extraEnv param */ extEnv := app.AppServeAppTasks[0].ExtraEnv log.Debug("extraEnv received: ", extEnv) tempMap := map[string]string{} - err := json.Unmarshal([]byte(extEnv), &tempMap) + err = json.Unmarshal([]byte(extEnv), &tempMap) if err != nil { log.Error(err) return "", "", errors.Wrap(err, "Failed to process extraEnv param.") @@ -110,19 +118,10 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, mJson, _ := json.Marshal(newExtEnv) newExtEnvStr := string(mJson) - log.Debug("After transform, extraEnv: ", newExtEnvStr) // TODO: Validate PV params - appId, taskId, err := u.repo.CreateAppServeApp(app) - if err != nil { - log.Error(err) - return "", "", errors.Wrap(err, "Failed to create app.") - } - - fmt.Printf("appId = %s, taskId = %s", appId, taskId) - // Call argo workflow workflow := "serve-java-app" @@ -394,6 +393,30 @@ func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask return "", fmt.Errorf("failed to update app status on UpdateAppServeApp. Err: %s", err) } + // Preprocess extraEnv param + extEnv := appTask.ExtraEnv + log.Debug("extraEnv received: ", extEnv) + + tempMap := map[string]string{} + err = json.Unmarshal([]byte(extEnv), &tempMap) + if err != nil { + log.Error(err) + return "", errors.Wrap(err, "Failed to process extraEnv param.") + } + log.Debugf("extraEnv marshalled: %v", tempMap) + + newExtEnv := map[string]string{} + for key, val := range tempMap { + newkey := "\"" + key + "\"" + newval := "\"" + val + "\"" + newExtEnv[newkey] = newval + } + + mJson, _ := json.Marshal(newExtEnv) + newExtEnvStr := string(mJson) + + log.Debug("After transform, extraEnv: ", newExtEnvStr) + // Call argo workflow workflow := "serve-java-app" @@ -414,7 +437,7 @@ func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask "image_url=" + appTask.ImageUrl, "port=" + appTask.Port, "profile=" + appTask.Profile, - "extra_env=" + appTask.ExtraEnv, + "extra_env=" + newExtEnvStr, "app_config=" + appTask.AppConfig, "app_secret=" + appTask.AppSecret, "resource_spec=" + appTask.ResourceSpec, From 12106f4c1796dd4b1db54151e22922770392a421 Mon Sep 17 00:00:00 2001 From: Taekyu Date: Wed, 19 Jul 2023 10:19:04 +0900 Subject: [PATCH 07/17] feature. implementation filter on pagination. --- internal/delivery/http/alert.go | 17 +++++++++++++++++ internal/repository/alert.go | 13 ++++++++++++- internal/repository/app-group.go | 2 +- internal/repository/app-serve-app.go | 2 +- internal/repository/cloud-account.go | 6 +++--- internal/repository/cluster.go | 10 +++++----- internal/repository/organization.go | 2 +- internal/repository/repository.go | 12 +++++++----- internal/repository/stack-template.go | 6 +++--- internal/usecase/alert.go | 4 ++-- pkg/domain/app-serve-app.go | 4 ++-- 11 files changed, 54 insertions(+), 24 deletions(-) diff --git a/internal/delivery/http/alert.go b/internal/delivery/http/alert.go index a749f4a0..70b2208a 100644 --- a/internal/delivery/http/alert.go +++ b/internal/delivery/http/alert.go @@ -99,6 +99,23 @@ func (h *AlertHandler) GetAlerts(w http.ResponseWriter, r *http.Request) { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return } + // convert status + for i, filter := range pg.GetFilters() { + if filter.Column == "status" { + for j, value := range filter.Values { + switch value { + case "CREATED": + pg.GetFilters()[i].Values[j] = "0" + case "INPROGRESS": + pg.GetFilters()[i].Values[j] = "1" + case "CLOSED": + pg.GetFilters()[i].Values[j] = "2" + case "ERROR": + pg.GetFilters()[i].Values[j] = "3" + } + } + } + } alerts, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { diff --git a/internal/repository/alert.go b/internal/repository/alert.go index 4bd33de7..e82e1999 100644 --- a/internal/repository/alert.go +++ b/internal/repository/alert.go @@ -57,6 +57,7 @@ type Alert struct { Summary string AlertActions []AlertAction `gorm:"foreignKey:AlertId"` RawData datatypes.JSON + Status domain.AlertActionStatus `gorm:"index"` } func (c *Alert) BeforeCreate(tx *gorm.DB) (err error) { @@ -108,7 +109,7 @@ func (r *AlertRepository) Fetch(organizationId string, pg *pagination.Pagination pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("alerts", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Alert{}). Preload("AlertActions", func(db *gorm.DB) *gorm.DB { return db.Order("created_at ASC") @@ -117,6 +118,7 @@ func (r *AlertRepository) Fetch(organizationId string, pg *pagination.Pagination Preload("Organization"). Joins("join clusters on clusters.id = alerts.cluster_id AND clusters.status = 2"). Where("alerts.organization_id = ?", organizationId)) + db.Count(&pg.TotalRows) pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) @@ -161,6 +163,7 @@ func (r *AlertRepository) Create(dto domain.Alert) (alertId uuid.UUID, err error CheckPoint: dto.CheckPoint, Summary: dto.Summary, RawData: dto.RawData, + Status: domain.AlertActionStatus_CREATED, } res := r.db.Create(&alert) if res.Error != nil { @@ -198,6 +201,13 @@ func (r *AlertRepository) CreateAlertAction(dto domain.AlertAction) (alertAction if res.Error != nil { return uuid.Nil, res.Error } + res = r.db.Model(&Alert{}). + Where("id = ?", dto.AlertId). + Update("status", dto.Status) + if res.Error != nil { + return uuid.Nil, res.Error + } + return alert.ID, nil } @@ -223,6 +233,7 @@ func reflectAlert(alert Alert) domain.Alert { Summary: alert.Summary, AlertActions: outAlertActions, RawData: alert.RawData, + Status: alert.Status, CreatedAt: alert.CreatedAt, UpdatedAt: alert.UpdatedAt, } diff --git a/internal/repository/app-group.go b/internal/repository/app-group.go index 4d033e48..46f63d9a 100644 --- a/internal/repository/app-group.go +++ b/internal/repository/app-group.go @@ -82,7 +82,7 @@ func (r *AppGroupRepository) Fetch(clusterId domain.ClusterId, pg *pagination.Pa pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("app_groups", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&AppGroup{}). Where("cluster_id = ?", clusterId)) db.Count(&pg.TotalRows) diff --git a/internal/repository/app-serve-app.go b/internal/repository/app-serve-app.go index 64b265a5..1e0ae2fc 100644 --- a/internal/repository/app-serve-app.go +++ b/internal/repository/app-serve-app.go @@ -63,7 +63,7 @@ func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll b pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("app_serve_apps", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&domain.AppServeApp{}). Where("app_serve_apps.organization_id = ? AND status <> 'DELETE_SUCCESS'", organizationId)) db.Count(&pg.TotalRows) diff --git a/internal/repository/cloud-account.go b/internal/repository/cloud-account.go index a4fc7da8..95e4b89c 100644 --- a/internal/repository/cloud-account.go +++ b/internal/repository/cloud-account.go @@ -41,8 +41,8 @@ type CloudAccount struct { ID uuid.UUID `gorm:"primarykey"` OrganizationId string Organization Organization `gorm:"foreignKey:OrganizationId"` - Name string - Description string + Name string `gorm:"index"` + Description string `gorm:"index"` Resource string CloudService string WorkflowId string @@ -99,7 +99,7 @@ func (r *CloudAccountRepository) Fetch(organizationId string, pg *pagination.Pag if pg == nil { pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("cloud_accounts", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&CloudAccount{}). Preload(clause.Associations). Where("organization_id = ? AND status != ?", organizationId, domain.CloudAccountStatus_DELETED)) diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index 9cc690da..80ee8898 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -46,10 +46,10 @@ type Cluster struct { gorm.Model ID domain.ClusterId `gorm:"primarykey"` - Name string + Name string `gorm:"index"` OrganizationId string Organization Organization `gorm:"foreignKey:OrganizationId"` - Description string + Description string `gorm:"index"` WorkflowId string Status domain.ClusterStatus StatusDesc string @@ -89,7 +89,7 @@ func (r *ClusterRepository) Fetch(pg *pagination.Pagination) (out []domain.Clust if pg == nil { pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("clusters", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Cluster{})) db.Count(&pg.TotalRows) @@ -114,7 +114,7 @@ func (r *ClusterRepository) FetchByOrganizationId(organizationId string, pg *pag } pg.SortColumn = "updated_at" pg.SortOrder = "DESC" - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("clusters", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Cluster{}).Preload(clause.Associations). Where("organization_id = ? AND status != ?", organizationId, domain.ClusterStatus_DELETED)) @@ -140,7 +140,7 @@ func (r *ClusterRepository) FetchByCloudAccountId(cloudAccountId uuid.UUID, pg * } pg.SortColumn = "updated_at" pg.SortOrder = "DESC" - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("clusters", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Cluster{}).Preload("CloudAccount"). Where("cloud_account_id = ?", cloudAccountId)) diff --git a/internal/repository/organization.go b/internal/repository/organization.go index 0a2920f6..7e1312a0 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -78,7 +78,7 @@ func (r *OrganizationRepository) Fetch(pg *pagination.Pagination) (*[]domain.Org pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("organizations", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&Organization{})) db.Count(&pg.TotalRows) diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 9f1dd0ed..087579dd 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -21,19 +21,21 @@ type Repository struct { Alert IAlertRepository } -func CombinedGormFilter(filters []pagination.Filter, combinedFilter pagination.CombinedFilter) FilterFunc { +func CombinedGormFilter(table string, filters []pagination.Filter, combinedFilter pagination.CombinedFilter) FilterFunc { return func(db *gorm.DB) *gorm.DB { // and query for _, filter := range filters { if len(filter.Values) > 1 { - inQuery := fmt.Sprintf("%s in (", filter.Column) + inQuery := fmt.Sprintf("%s.%s::text in (", table, filter.Column) for _, val := range filter.Values { - inQuery = inQuery + fmt.Sprintf("'%s',", val) + inQuery = inQuery + fmt.Sprintf("LOWER('%s'),", val) } inQuery = inQuery[:len(inQuery)-1] + ")" db = db.Where(inQuery) } else { - db = db.Where(fmt.Sprintf("%s = '%s'", filter.Column, filter.Values[0])) + if len(filter.Values[0]) > 0 { + db = db.Where(fmt.Sprintf("%s.%s::text like LOWER('%%%s%%')", table, filter.Column, filter.Values[0])) + } } } @@ -42,7 +44,7 @@ func CombinedGormFilter(filters []pagination.Filter, combinedFilter pagination.C if len(combinedFilter.Columns) > 0 { orQuery := "" for _, column := range combinedFilter.Columns { - orQuery = orQuery + fmt.Sprintf("%s like '%%%s%%' OR ", column, combinedFilter.Value) + orQuery = orQuery + fmt.Sprintf("%s.%s::text like LOWER('%%%s%%') OR ", table, column, combinedFilter.Value) } orQuery = orQuery[:len(orQuery)-3] db = db.Where(orQuery) diff --git a/internal/repository/stack-template.go b/internal/repository/stack-template.go index b027ec77..dc27b967 100644 --- a/internal/repository/stack-template.go +++ b/internal/repository/stack-template.go @@ -39,8 +39,8 @@ type StackTemplate struct { ID uuid.UUID `gorm:"primarykey"` OrganizationId string Organization Organization `gorm:"foreignKey:OrganizationId"` - Name string - Description string + Name string `gorm:"index"` + Description string `gorm:"index"` Template string Version string CloudService string @@ -77,7 +77,7 @@ func (r *StackTemplateRepository) Fetch(pg *pagination.Pagination) (out []domain pg = pagination.NewDefaultPagination() } - filterFunc := CombinedGormFilter(pg.GetFilters(), pg.CombinedFilter) + filterFunc := CombinedGormFilter("stack_templates", pg.GetFilters(), pg.CombinedFilter) db := filterFunc(r.db.Model(&StackTemplate{})) db.Count(&pg.TotalRows) diff --git a/internal/usecase/alert.go b/internal/usecase/alert.go index fb46007f..99fafa9c 100644 --- a/internal/usecase/alert.go +++ b/internal/usecase/alert.go @@ -225,7 +225,7 @@ func (u *AlertUsecase) getOrganizationFromCluster(clusters *[]domain.Cluster, st func (u *AlertUsecase) makeAdditionalInfo(alert *domain.Alert) { alert.FiredAt = &alert.CreatedAt - alert.Status = domain.AlertActionStatus_CREATED + //alert.Status = domain.AlertActionStatus_CREATED if len(alert.AlertActions) > 0 { alert.TakedAt = &alert.AlertActions[0].CreatedAt @@ -238,7 +238,7 @@ func (u *AlertUsecase) makeAdditionalInfo(alert *domain.Alert) { alert.LastTaker = alert.AlertActions[len(alert.AlertActions)-1].Taker alert.TakedSec = int((alert.AlertActions[0].CreatedAt).Sub(alert.CreatedAt).Seconds()) - alert.Status = alert.AlertActions[len(alert.AlertActions)-1].Status + //alert.Status = alert.AlertActions[len(alert.AlertActions)-1].Status } } diff --git a/pkg/domain/app-serve-app.go b/pkg/domain/app-serve-app.go index e59c33d0..4edcc92d 100644 --- a/pkg/domain/app-serve-app.go +++ b/pkg/domain/app-serve-app.go @@ -9,7 +9,7 @@ import ( type AppServeApp struct { ID string `gorm:"primarykey" json:"id,omitempty"` - Name string `json:"name,omitempty"` // application name + Name string `gorm:"index" json:"name,omitempty"` // application name Namespace string `json:"namespace,omitempty"` // application namespace OrganizationId string `json:"organizationId,omitempty"` // contractId is a contract ID which this app belongs to Type string `json:"type,omitempty"` // type (build/deploy/all) @@ -18,7 +18,7 @@ type AppServeApp struct { PreviewEndpointUrl string `json:"previewEndpointUrl,omitempty"` // preview svc endpoint URL in B/G deployment TargetClusterId string `json:"targetClusterId,omitempty"` // target cluster to which the app is deployed TargetClusterName string `gorm:"-:all" json:"targetClusterName,omitempty"` // target cluster name - Status string `json:"status,omitempty"` // status is status of deployed app + Status string `gorm:"index" json:"status,omitempty"` // status is status of deployed app CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt" ` UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` DeletedAt *time.Time `json:"deletedAt"` From 7b2640cb7b2999bbf6eb6705bc86649998ecbcb0 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 19 Jul 2023 13:46:09 +0900 Subject: [PATCH 08/17] improve email contents with html code --- internal/aws/ses/contents/authcode.html | 107 +++++++++++++ .../ses/contents/organization_creation.html | 129 ++++++++++++++++ .../aws/ses/contents/temporary_password.html | 107 +++++++++++++ internal/aws/ses/ses.go | 146 +++++++++++------- 4 files changed, 432 insertions(+), 57 deletions(-) create mode 100644 internal/aws/ses/contents/authcode.html create mode 100644 internal/aws/ses/contents/organization_creation.html create mode 100644 internal/aws/ses/contents/temporary_password.html diff --git a/internal/aws/ses/contents/authcode.html b/internal/aws/ses/contents/authcode.html new file mode 100644 index 00000000..09f79474 --- /dev/null +++ b/internal/aws/ses/contents/authcode.html @@ -0,0 +1,107 @@ + + + + + 이메일인증 안내 + + + +
+ + + + + + + + + + + + + + + +
SKT Enterprise
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 이메일 인증 안내 +
안녕하세요.
항상 저희 TKS Cloud Service를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
+ 고객님께서 입력하신 이메일 주소 인증을 위해 아래 6자리 인증번호를 +
화면에 입력해 주세요.
이메일 인증코드
{{.AuthCode}}
더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
감사합니다.
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
본 메일은 발신 전용 메일로, 회신 되지 않습니다.
우편번호: 04539 서울특별시중구을지로65 (을지로2가) SK T-타워 SK텔레콤㈜ 대표이사 : 유영상
COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED.
+
+
+ + + diff --git a/internal/aws/ses/contents/organization_creation.html b/internal/aws/ses/contents/organization_creation.html new file mode 100644 index 00000000..988411d7 --- /dev/null +++ b/internal/aws/ses/contents/organization_creation.html @@ -0,0 +1,129 @@ + + + + + 조직 생성 + + + +
+ + + + + + + + + + + + + + + +
SKT Enterprise
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ​ + + + + + + + + + + + + +
+ 조직 생성 안내 +
안녕하세요.
항상 저희 TKS Cloud Service를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
+ 조직이 생성되었습니다. +
아래의 정보를 이용하여 로그인 해주시기 바랍니다.
로그인 정보
+ • 조직코드: {{.OrganizationId}} +
• 아이디: {{.Id}} +
• 비밀번호: {{.Password}} +
조직 정보
+ • 조직이름: {{.OrganizationName}} +
• 관리자 이름: {{.AdminName}} +
더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
감사합니다.
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
본 메일은 발신 전용 메일로, 회신 되지 않습니다.
우편번호: 04539 서울특별시중구을지로65 (을지로2가) SK T-타워 SK텔레콤㈜ 대표이사 : 유영상
COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED.
+
+
+ + + diff --git a/internal/aws/ses/contents/temporary_password.html b/internal/aws/ses/contents/temporary_password.html new file mode 100644 index 00000000..775f434a --- /dev/null +++ b/internal/aws/ses/contents/temporary_password.html @@ -0,0 +1,107 @@ + + + + + 임시 비밀번호 발급 + + + +
+ + + + + + + + + + + + + + + +
SKT Enterprise
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 임시 비밀번호 발급 안내 +
안녕하세요.
항상 저희 TKS Cloud Service를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
+ 임시 비밀번호가 발급되었습니다. +
로그인 후 비밀번호를 변경하여 사용해 주시기 바랍니다.
임시 비밀번호
{{.TemporaryPassword}}
더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
감사합니다.
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
본 메일은 발신 전용 메일로, 회신 되지 않습니다.
우편번호: 04539 서울특별시중구을지로65 (을지로2가) SK T-타워 SK텔레콤㈜ 대표이사 : 유영상
COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED.
+
+
+ + + diff --git a/internal/aws/ses/ses.go b/internal/aws/ses/ses.go index f1cce3a4..ec24c54e 100644 --- a/internal/aws/ses/ses.go +++ b/internal/aws/ses/ses.go @@ -1,8 +1,11 @@ package ses import ( + "bytes" "context" + "embed" "fmt" + "html/template" "os" "github.com/aws/aws-sdk-go-v2/aws" @@ -13,12 +16,13 @@ import ( "github.com/spf13/viper" ) +//go:embed contents/*.html +var templateFS embed.FS + var Client *awsSes.Client const ( senderEmailAddress = "tks-dev@sktelecom.com" - - thanksContent = "TKS Cloud Service를 이용해 주셔서 감사합니다.\nTKS Cloud Service Team 드림" ) func Initialize() error { @@ -55,90 +59,118 @@ func Initialize() error { return nil } func SendEmailForVerityIdentity(client *awsSes.Client, targetEmailAddress string, code string) error { - subject := "[TKS][인증번호:" + code + "] – 요청하신 인증번호를 알려드립니다." - body := "아래의 인증번호를 인증번호 입력창에 입력해 주세요.\n\n" + - "인증번호: " + code + "\n\n" + - thanksContent + subject := "[TKS] [인증번호:" + code + "] 인증번호가 발급되었습니다." - input := &awsSes.SendEmailInput{ - Destination: &types.Destination{ - ToAddresses: []string{targetEmailAddress}, - }, - Message: &types.Message{ - Body: &types.Body{ - Text: &types.Content{ - Data: aws.String(body), - }, - }, - Subject: &types.Content{ - Data: aws.String(subject), - }, - }, - Source: aws.String(senderEmailAddress), + tmpl, err := template.ParseFS(templateFS, "contents/authcode.html") + if err != nil { + log.Errorf("failed to parse template, %v", err) + return err } - if _, err := client.SendEmail(context.Background(), input); err != nil { - log.Errorf("failed to send email, %v", err) + type TemplateData struct { + AuthCode string + } + + data := TemplateData{ + AuthCode: fmt.Sprintf("%s", code), + } + + var tpl bytes.Buffer + if err := tmpl.Execute(&tpl, data); err != nil { + log.Errorf("failed to execute template, %v", err) return err } + body := tpl.String() + + err = sendEmail(client, targetEmailAddress, subject, body) + if err != nil { + return err + } return nil } func SendEmailForTemporaryPassword(client *awsSes.Client, targetEmailAddress string, randomPassword string) error { - subject := "[TKS] 임시 비밀번호 발급" - body := "임시 비밀번호가 발급되었습니다.\n" + - "로그인 후 비밀번호를 변경하여 사용하십시오.\n\n" + - "임시 비밀번호: " + randomPassword + "\n\n" + - thanksContent + subject := "[TKS] 임시 비밀번호가 발급되었습니다." - input := &awsSes.SendEmailInput{ - Destination: &types.Destination{ - ToAddresses: []string{targetEmailAddress}, - }, - Message: &types.Message{ - Body: &types.Body{ - Text: &types.Content{ - Data: aws.String(body), - }, - }, - Subject: &types.Content{ - Data: aws.String(subject), - }, - }, - Source: aws.String(senderEmailAddress), + tmpl, err := template.ParseFS(templateFS, "contents/temporary_password.html") + if err != nil { + log.Errorf("failed to parse template, %v", err) + return err } - if _, err := client.SendEmail(context.Background(), input); err != nil { - log.Errorf("failed to send email, %v", err) + type TemplateData struct { + TemporaryPassword string + } + + data := TemplateData{ + TemporaryPassword: fmt.Sprintf("%s", randomPassword), + } + + var tpl bytes.Buffer + if err := tmpl.Execute(&tpl, data); err != nil { + log.Errorf("failed to execute template, %v", err) return err } + body := tpl.String() + + err = sendEmail(client, targetEmailAddress, subject, body) + if err != nil { + return err + } return nil } func SendEmailForGeneratingOrganization(client *awsSes.Client, organizationId string, organizationName string, targetEmailAddress string, userAccountId string, randomPassword string) error { subject := "[TKS] 조직이 생성되었습니다." - body := "조직이 생성되었습니다. \n" + - "조직코드: " + organizationId + "\n" + - "이름: " + organizationName + "\n" + - "관리자 아이디: " + userAccountId + "\n" + - "관리자 이름: admin\n\n" + - "아래 관리자 계정 정보로 로그인 후 사용하시기 바랍니다.\n" + - "조직코드: " + organizationId + "\n" + - "아이디: " + userAccountId + "\n" + - "비밀번호: " + randomPassword + "\n\n" + - thanksContent + tmpl, err := template.ParseFS(templateFS, "contents/organization_creation.html") + if err != nil { + log.Errorf("failed to parse template, %v", err) + return err + } + + type TemplateData struct { + OrganizationId string + Id string + Password string + OrganizationName string + AdminName string + } + + data := TemplateData{ + OrganizationId: fmt.Sprintf("%s", organizationId), + Id: fmt.Sprintf("%s", userAccountId), + Password: fmt.Sprintf("%s", randomPassword), + OrganizationName: fmt.Sprintf("%s", organizationName), + AdminName: fmt.Sprintf("%s", userAccountId), + } + + var tpl bytes.Buffer + if err := tmpl.Execute(&tpl, data); err != nil { + log.Errorf("failed to execute template, %v", err) + return err + } + body := tpl.String() + + err = sendEmail(client, targetEmailAddress, subject, body) + if err != nil { + return err + } + return nil +} + +func sendEmail(client *awsSes.Client, targetEmailAddress string, subject string, htmlBody string) error { input := &awsSes.SendEmailInput{ Destination: &types.Destination{ ToAddresses: []string{targetEmailAddress}, }, Message: &types.Message{ Body: &types.Body{ - Text: &types.Content{ - Data: aws.String(body), + Html: &types.Content{ + Data: aws.String(htmlBody), }, }, Subject: &types.Content{ From 0bdbe338e1c5a8effb1f1d9e6bccacb693981c51 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 19 Jul 2023 13:58:43 +0900 Subject: [PATCH 09/17] lint fix --- internal/aws/ses/ses.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/aws/ses/ses.go b/internal/aws/ses/ses.go index ec24c54e..f99bdc42 100644 --- a/internal/aws/ses/ses.go +++ b/internal/aws/ses/ses.go @@ -72,7 +72,7 @@ func SendEmailForVerityIdentity(client *awsSes.Client, targetEmailAddress string } data := TemplateData{ - AuthCode: fmt.Sprintf("%s", code), + AuthCode: code, } var tpl bytes.Buffer @@ -104,7 +104,7 @@ func SendEmailForTemporaryPassword(client *awsSes.Client, targetEmailAddress str } data := TemplateData{ - TemporaryPassword: fmt.Sprintf("%s", randomPassword), + TemporaryPassword: randomPassword, } var tpl bytes.Buffer @@ -141,11 +141,11 @@ func SendEmailForGeneratingOrganization(client *awsSes.Client, organizationId st } data := TemplateData{ - OrganizationId: fmt.Sprintf("%s", organizationId), - Id: fmt.Sprintf("%s", userAccountId), - Password: fmt.Sprintf("%s", randomPassword), - OrganizationName: fmt.Sprintf("%s", organizationName), - AdminName: fmt.Sprintf("%s", userAccountId), + OrganizationId: organizationId, + Id: userAccountId, + Password: randomPassword, + OrganizationName: organizationName, + AdminName: userAccountId, } var tpl bytes.Buffer From 3200b83ebe304659b896ea7a113a2f0928584c50 Mon Sep 17 00:00:00 2001 From: Robert Choi Date: Thu, 20 Jul 2023 11:20:43 +0900 Subject: [PATCH 10/17] app-serving: handle case that extraEnv is not given --- internal/usecase/app-serve-app.go | 82 ++++++++++++++++--------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/internal/usecase/app-serve-app.go b/internal/usecase/app-serve-app.go index 1d010bc5..4b46f41f 100644 --- a/internal/usecase/app-serve-app.go +++ b/internal/usecase/app-serve-app.go @@ -97,29 +97,31 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, fmt.Printf("appId = %s, taskId = %s", appId, taskId) - /* Preprocess extraEnv param */ extEnv := app.AppServeAppTasks[0].ExtraEnv - log.Debug("extraEnv received: ", extEnv) + if extEnv != "" { + /* Preprocess extraEnv param */ + log.Debug("extraEnv received: ", extEnv) + + tempMap := map[string]string{} + err = json.Unmarshal([]byte(extEnv), &tempMap) + if err != nil { + log.Error(err) + return "", "", errors.Wrap(err, "Failed to process extraEnv param.") + } + log.Debugf("extraEnv marshalled: %v", tempMap) - tempMap := map[string]string{} - err = json.Unmarshal([]byte(extEnv), &tempMap) - if err != nil { - log.Error(err) - return "", "", errors.Wrap(err, "Failed to process extraEnv param.") - } - log.Debugf("extraEnv marshalled: %v", tempMap) + newExtEnv := map[string]string{} + for key, val := range tempMap { + newkey := "\"" + key + "\"" + newval := "\"" + val + "\"" + newExtEnv[newkey] = newval + } - newExtEnv := map[string]string{} - for key, val := range tempMap { - newkey := "\"" + key + "\"" - newval := "\"" + val + "\"" - newExtEnv[newkey] = newval + mJson, _ := json.Marshal(newExtEnv) + extEnv := string(mJson) + log.Debug("After transform, extraEnv: ", extEnv) } - mJson, _ := json.Marshal(newExtEnv) - newExtEnvStr := string(mJson) - log.Debug("After transform, extraEnv: ", newExtEnvStr) - // TODO: Validate PV params // Call argo workflow @@ -140,7 +142,7 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, "image_url=" + app.AppServeAppTasks[0].ImageUrl, "port=" + app.AppServeAppTasks[0].Port, "profile=" + app.AppServeAppTasks[0].Profile, - "extra_env=" + newExtEnvStr, + "extra_env=" + extEnv, "app_config=" + app.AppServeAppTasks[0].AppConfig, "app_secret=" + app.AppServeAppTasks[0].AppSecret, "resource_spec=" + app.AppServeAppTasks[0].ResourceSpec, @@ -393,29 +395,31 @@ func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask return "", fmt.Errorf("failed to update app status on UpdateAppServeApp. Err: %s", err) } - // Preprocess extraEnv param extEnv := appTask.ExtraEnv - log.Debug("extraEnv received: ", extEnv) - - tempMap := map[string]string{} - err = json.Unmarshal([]byte(extEnv), &tempMap) - if err != nil { - log.Error(err) - return "", errors.Wrap(err, "Failed to process extraEnv param.") - } - log.Debugf("extraEnv marshalled: %v", tempMap) + if extEnv != "" { + /* Preprocess extraEnv param */ + log.Debug("extraEnv received: ", extEnv) + + tempMap := map[string]string{} + err = json.Unmarshal([]byte(extEnv), &tempMap) + if err != nil { + log.Error(err) + return "", errors.Wrap(err, "Failed to process extraEnv param.") + } + log.Debugf("extraEnv marshalled: %v", tempMap) - newExtEnv := map[string]string{} - for key, val := range tempMap { - newkey := "\"" + key + "\"" - newval := "\"" + val + "\"" - newExtEnv[newkey] = newval - } + newExtEnv := map[string]string{} + for key, val := range tempMap { + newkey := "\"" + key + "\"" + newval := "\"" + val + "\"" + newExtEnv[newkey] = newval + } - mJson, _ := json.Marshal(newExtEnv) - newExtEnvStr := string(mJson) + mJson, _ := json.Marshal(newExtEnv) + extEnv = string(mJson) - log.Debug("After transform, extraEnv: ", newExtEnvStr) + log.Debug("After transform, extraEnv: ", extEnv) + } // Call argo workflow workflow := "serve-java-app" @@ -437,7 +441,7 @@ func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask "image_url=" + appTask.ImageUrl, "port=" + appTask.Port, "profile=" + appTask.Profile, - "extra_env=" + newExtEnvStr, + "extra_env=" + extEnv, "app_config=" + appTask.AppConfig, "app_secret=" + appTask.AppSecret, "resource_spec=" + appTask.ResourceSpec, From 788a731d441416dcaeb99a12e99128b66fb97487 Mon Sep 17 00:00:00 2001 From: Taekyu Date: Thu, 20 Jul 2023 11:25:24 +0900 Subject: [PATCH 11/17] feature. re-architecutring pod-restart calendar on dashboard. --- api/swagger/docs.go | 17 ++++ api/swagger/swagger.json | 17 ++++ api/swagger/swagger.yaml | 11 +++ internal/usecase/dashboard.go | 166 ++++------------------------------ pkg/domain/dashboard.go | 12 ++- 5 files changed, 73 insertions(+), 150 deletions(-) diff --git a/api/swagger/docs.go b/api/swagger/docs.go index cf803d99..77172154 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -3905,6 +3905,12 @@ const docTemplate = `{ "domain.ChartData": { "type": "object", "properties": { + "podCounts": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.PodCount" + } + }, "series": { "type": "array", "items": { @@ -5357,6 +5363,17 @@ const docTemplate = `{ } } }, + "domain.PodCount": { + "type": "object", + "properties": { + "day": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, "domain.Role": { "type": "object", "properties": { diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 2738fb7e..cd91d143 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -3898,6 +3898,12 @@ "domain.ChartData": { "type": "object", "properties": { + "podCounts": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.PodCount" + } + }, "series": { "type": "array", "items": { @@ -5350,6 +5356,17 @@ } } }, + "domain.PodCount": { + "type": "object", + "properties": { + "day": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, "domain.Role": { "type": "object", "properties": { diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index c23032ea..4227bbed 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -251,6 +251,10 @@ definitions: type: object domain.ChartData: properties: + podCounts: + items: + $ref: '#/definitions/domain.PodCount' + type: array series: items: $ref: '#/definitions/domain.Unit' @@ -1217,6 +1221,13 @@ definitions: totalRows: type: integer type: object + domain.PodCount: + properties: + day: + type: integer + value: + type: integer + type: object domain.Role: properties: createdAt: diff --git a/internal/usecase/dashboard.go b/internal/usecase/dashboard.go index 1c4b6d79..f91732ca 100644 --- a/internal/usecase/dashboard.go +++ b/internal/usecase/dashboard.go @@ -273,101 +273,6 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy query = "avg by (taco_cluster) (rate(container_network_receive_bytes_total[1h]))" case domain.ChartType_POD_CALENDAR.String(): - /* - // 입력받은 년,월 을 date 형식으로 - yearInt, _ := strconv.Atoi(year) - monthInt, _ := strconv.Atoi(month) - startDate := time.Date(yearInt, time.Month(monthInt), 1, 0, 0, 0, 0, time.UTC) - endDate := startDate.Add(time.Hour * 24 * 30) - - start := 0 - end := 0 - if now.Year() < yearInt { - return res, fmt.Errorf("Invalid year") - } else if now.Year() == yearInt && int(now.Month()) < monthInt { - return res, fmt.Errorf("Invalid month") - } else if now.Year() == yearInt && int(now.Month()) == monthInt { - start = int(startDate.Unix()) - end = int(now.Unix()) - } else { - start = int(startDate.Unix()) - end = int(endDate.Unix()) - } - - log.Debugf("S : %d E : %d", start, end) - - query = "sum by (__name__) ({__name__=~\"kube_pod_container_status_restarts_total|kube_pod_status_phase\"})" - - result, err := thanosClient.FetchRange(query, start, end, 60*60*24) - if err != nil { - return res, err - } - - for _, val := range result.Data.Result { - xAxisData := []string{} - yAxisData := []string{} - - for _, vals := range val.Values { - x := int(math.Round(vals.([]interface{})[0].(float64))) - y, err := strconv.ParseFloat(vals.([]interface{})[1].(string), 32) - if err != nil { - y = 0 - } - xAxisData = append(xAxisData, strconv.Itoa(x)) - yAxisData = append(yAxisData, fmt.Sprintf("%d", int(y))) - } - - if val.Metric.Name == "kube_pod_container_status_restarts_total" { - chartData.XAxis.Data = xAxisData - chartData.Series = append(chartData.Series, domain.Unit{ - Name: "date", - Data: xAxisData, - }) - chartData.Series = append(chartData.Series, domain.Unit{ - Name: "podRestartCount", - Data: yAxisData, - }) - } - - if val.Metric.Name == "kube_pod_status_phase" { - chartData.Series = append(chartData.Series, domain.Unit{ - Name: "totalPodCount", - Data: yAxisData, - }) - } - } - - - { - series : [ - { - name : date, - data : [ - "timestamp1", - "timestamp2" - "timestamp3" - ] - }, - { - name : podRestartCount, - data : [ - "1", - "2" - "3" - ] - }, - { - name : totalPodCount, - data : [ - "10", - "20" - "30" - ] - }, - ] - } - */ - // 입력받은 년,월 을 date 형식으로 yearInt, _ := strconv.Atoi(year) monthInt, _ := strconv.Atoi(month) @@ -385,20 +290,24 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy return res, err } - xAxisData := []string{} - yAxisData := []string{} + organization, err := u.organizationRepo.Get(organizationId) + if err != nil { + return res, err + } + + log.Info(organization.CreatedAt.Format("2006-01-02")) + podCounts := []domain.PodCount{} for day := rangeDate(startDate, endDate); ; { d := day() if d.IsZero() { break } - baseDate := d.Format("2006-01-02") - cntPodRestartStr := "" + baseDate := d.Format("2006-01-02") cntPodRestart := 0 - if baseDate <= now.Format("2006-01-02") { + if baseDate <= now.Format("2006-01-02") && baseDate >= organization.CreatedAt.Format("2006-01-02") { for _, alert := range alerts { strDate := alert.CreatedAt.Format("2006-01-02") @@ -406,55 +315,16 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy cntPodRestart += 1 } } - cntPodRestartStr = fmt.Sprintf("%d", int(cntPodRestart)) - } - - dd := time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, time.UTC) - xAxisData = append(xAxisData, strconv.Itoa(int(dd.Unix()))) - yAxisData = append(yAxisData, cntPodRestartStr) - } - - chartData.XAxis.Data = xAxisData - chartData.Series = append(chartData.Series, domain.Unit{ - Name: "podRestartCount", - Data: yAxisData, - }) - - /* - for _, alert := range alerts { - xAxisData := []string{} - yAxisData := []string{} - - for _, vals := range val.Values { - x := int(math.Round(vals.([]interface{})[0].(float64))) - y, err := strconv.ParseFloat(vals.([]interface{})[1].(string), 32) - if err != nil { - y = 0 - } - xAxisData = append(xAxisData, strconv.Itoa(x)) - yAxisData = append(yAxisData, fmt.Sprintf("%d", int(y))) - } - - if val.Metric.Name == "kube_pod_container_status_restarts_total" { - chartData.XAxis.Data = xAxisData - chartData.Series = append(chartData.Series, domain.Unit{ - Name: "date", - Data: xAxisData, - }) - chartData.Series = append(chartData.Series, domain.Unit{ - Name: "podRestartCount", - Data: yAxisData, - }) - } - - if val.Metric.Name == "kube_pod_status_phase" { - chartData.Series = append(chartData.Series, domain.Unit{ - Name: "totalPodCount", - Data: yAxisData, - }) + pd := domain.PodCount{ + Day: d.Day(), + Value: int(cntPodRestart), } + podCounts = append(podCounts, pd) } - */ + } + chartData.XAxis = nil + chartData.YAxis = nil + chartData.PodCounts = podCounts return domain.DashboardChart{ ChartType: domain.ChartType_POD_CALENDAR, @@ -508,7 +378,9 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy Name: clusterName, Data: yAxisData, }) + } + chartData.XAxis = &domain.Axis{} chartData.XAxis.Data = xAxisData return domain.DashboardChart{ diff --git a/pkg/domain/dashboard.go b/pkg/domain/dashboard.go index b27616a0..c7699fb7 100644 --- a/pkg/domain/dashboard.go +++ b/pkg/domain/dashboard.go @@ -81,10 +81,16 @@ type Axis struct { Data []string `json:"data"` } +type PodCount struct { + Day int `json:"day"` + Value int `json:"value"` +} + type ChartData struct { - XAxis Axis `json:"xAxis"` - YAxis Axis `json:"yAxis"` - Series []Unit `json:"series"` + XAxis *Axis `json:"xAxis,omitempty"` + YAxis *Axis `json:"yAxis,omitempty"` + Series []Unit `json:"series,omitempty"` + PodCounts []PodCount `json:"podCounts,omitempty"` } type DashboardChartResponse struct { From b0255bc2116f0c1d1bba2be9979f011985b967a5 Mon Sep 17 00:00:00 2001 From: Robert Choi Date: Thu, 20 Jul 2023 14:51:50 +0900 Subject: [PATCH 12/17] trivial: remove garbage log msg --- internal/repository/app-serve-app.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/repository/app-serve-app.go b/internal/repository/app-serve-app.go index 1e0ae2fc..d27adfb6 100644 --- a/internal/repository/app-serve-app.go +++ b/internal/repository/app-serve-app.go @@ -120,7 +120,6 @@ func (r *AppServeAppRepository) GetAppServeAppById(appId string) (*domain.AppSer // Add cluster name to app object r.db.Select("name").Where("id = ?", app.TargetClusterId).First(&cluster) app.TargetClusterName = cluster.Name - log.Infof("App struct with cluster name:\n%+v", app) return &app, nil } From 7a0c749b48c432bb9f8b824b8f12f60a61fa32ac Mon Sep 17 00:00:00 2001 From: Taekyu Date: Thu, 20 Jul 2023 15:07:20 +0900 Subject: [PATCH 13/17] feature. change organization paramter for deleting appgroup --- internal/delivery/http/app-group.go | 9 +-------- internal/usecase/app-group.go | 13 ++++++++----- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/internal/delivery/http/app-group.go b/internal/delivery/http/app-group.go index 624a5779..84667143 100644 --- a/internal/delivery/http/app-group.go +++ b/internal/delivery/http/app-group.go @@ -6,7 +6,6 @@ import ( "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/helper" - "github.com/openinfradev/tks-api/internal/middleware/auth/request" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" @@ -157,12 +156,6 @@ func (h *AppGroupHandler) GetAppGroup(w http.ResponseWriter, r *http.Request) { // @Router /app-groups [delete] // @Security JWT func (h *AppGroupHandler) DeleteAppGroup(w http.ResponseWriter, r *http.Request) { - user, ok := request.UserFrom(r.Context()) - if !ok { - ErrorJSON(w, r, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "")) - return - } - vars := mux.Vars(r) strId, ok := vars["appGroupId"] if !ok { @@ -176,7 +169,7 @@ func (h *AppGroupHandler) DeleteAppGroup(w http.ResponseWriter, r *http.Request) return } - err := h.usecase.Delete(r.Context(), user.GetOrganizationId(), appGroupId) + err := h.usecase.Delete(r.Context(), appGroupId) if err != nil { log.ErrorWithContext(r.Context(), "Failed to delete appGroup err : ", err) ErrorJSON(w, r, err) diff --git a/internal/usecase/app-group.go b/internal/usecase/app-group.go index 14eab49c..ce93c40b 100644 --- a/internal/usecase/app-group.go +++ b/internal/usecase/app-group.go @@ -20,7 +20,7 @@ type IAppGroupUsecase interface { Fetch(ctx context.Context, clusterId domain.ClusterId, pg *pagination.Pagination) ([]domain.AppGroup, error) Create(ctx context.Context, dto domain.AppGroup) (id domain.AppGroupId, err error) Get(ctx context.Context, id domain.AppGroupId) (out domain.AppGroup, err error) - Delete(ctx context.Context, organizationId string, id domain.AppGroupId) (err error) + Delete(ctx context.Context, id domain.AppGroupId) (err error) GetApplications(ctx context.Context, id domain.AppGroupId, applicationType domain.ApplicationType) (out []domain.Application, err error) UpdateApplication(ctx context.Context, dto domain.Application) (err error) } @@ -134,13 +134,16 @@ func (u *AppGroupUsecase) Get(ctx context.Context, id domain.AppGroupId) (out do return appGroup, nil } -func (u *AppGroupUsecase) Delete(ctx context.Context, organizationId string, id domain.AppGroupId) (err error) { +func (u *AppGroupUsecase) Delete(ctx context.Context, id domain.AppGroupId) (err error) { appGroup, err := u.repo.Get(id) if err != nil { return fmt.Errorf("No appGroup for deletiing : %s", id) } - - clusterId := appGroup.ClusterId + cluster, err := u.clusterRepo.Get(appGroup.ClusterId) + if err != nil { + return httpErrors.NewBadRequestError(err, "AG_NOT_FOUND_CLUSTER", "") + } + organizationId := cluster.OrganizationId // Call argo workflow template workflowTemplate := "" @@ -164,7 +167,7 @@ func (u *AppGroupUsecase) Delete(ctx context.Context, organizationId string, id "organization_id=" + organizationId, "app_group=" + appGroupName, "github_account=" + viper.GetString("git-account"), - "cluster_id=" + clusterId.String(), + "cluster_id=" + cluster.ID.String(), "app_group_id=" + id.String(), "keycloak_url=" + strings.TrimSuffix(viper.GetString("keycloak-address"), "/auth"), "base_repo_branch=" + viper.GetString("revision"), From c9183d9e126ff9006bbfe8a5c093d3e7bc35352d Mon Sep 17 00:00:00 2001 From: Taekyu Date: Wed, 26 Jul 2023 15:22:44 +0900 Subject: [PATCH 14/17] feature. add workflow paramter for supporting s3. --- internal/usecase/app-group.go | 37 +++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/internal/usecase/app-group.go b/internal/usecase/app-group.go index ce93c40b..ab1f74bd 100644 --- a/internal/usecase/app-group.go +++ b/internal/usecase/app-group.go @@ -26,16 +26,18 @@ type IAppGroupUsecase interface { } type AppGroupUsecase struct { - repo repository.IAppGroupRepository - clusterRepo repository.IClusterRepository - argo argowf.ArgoClient + repo repository.IAppGroupRepository + clusterRepo repository.IClusterRepository + cloudAccountRepo repository.ICloudAccountRepository + argo argowf.ArgoClient } func NewAppGroupUsecase(r repository.Repository, argoClient argowf.ArgoClient) IAppGroupUsecase { return &AppGroupUsecase{ - repo: r.AppGroup, - clusterRepo: r.Cluster, - argo: argoClient, + repo: r.AppGroup, + clusterRepo: r.Cluster, + cloudAccountRepo: r.CloudAccount, + argo: argoClient, } } @@ -75,6 +77,28 @@ func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id d } } + // check cloudAccount + cloudAccounts, err := u.cloudAccountRepo.Fetch(cluster.OrganizationId, nil) + if err != nil { + return "", httpErrors.NewBadRequestError(fmt.Errorf("Failed to get cloudAccounts"), "", "") + } + tksCloudAccountId := cluster.CloudAccountId.String() + isExist := false + for _, ca := range cloudAccounts { + if ca.ID == cluster.CloudAccountId { + + // FOR TEST. ADD MAGIC KEYWORD + if strings.Contains(ca.Name, domain.CLOUD_ACCOUNT_INCLUSTER) { + tksCloudAccountId = "NULL" + } + isExist = true + break + } + } + if !isExist { + return "", httpErrors.NewBadRequestError(fmt.Errorf("Not found cloudAccountId[%s] in organization[%s]", cluster.CloudAccountId, cluster.OrganizationId), "", "") + } + if dto.ID == "" { dto.ID, err = u.repo.Create(dto) } else { @@ -98,6 +122,7 @@ func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id d "console_url=" + viper.GetString("console-address"), "alert_tks=" + viper.GetString("external-address") + "/system-api/1.0/alerts", "alert_slack=" + viper.GetString("alert-slack"), + "cloud_account_id=" + tksCloudAccountId, } switch dto.AppGroupType { From 808835f419266edb7273cd8ccaa18e5b36d1509a Mon Sep 17 00:00:00 2001 From: Taekyu Date: Wed, 26 Jul 2023 17:59:09 +0900 Subject: [PATCH 15/17] trivial. change empty value "" for cloud_account_id --- internal/usecase/app-group.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/usecase/app-group.go b/internal/usecase/app-group.go index ab1f74bd..f23366ac 100644 --- a/internal/usecase/app-group.go +++ b/internal/usecase/app-group.go @@ -89,7 +89,7 @@ func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id d // FOR TEST. ADD MAGIC KEYWORD if strings.Contains(ca.Name, domain.CLOUD_ACCOUNT_INCLUSTER) { - tksCloudAccountId = "NULL" + tksCloudAccountId = "" } isExist = true break From d4e29bc5933e8abcce3df47fef88f57b14d6fb93 Mon Sep 17 00:00:00 2001 From: Taekyu Date: Thu, 27 Jul 2023 14:46:12 +0900 Subject: [PATCH 16/17] bugfix. add workflow parameter for deleing appgroup --- internal/usecase/app-group.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/internal/usecase/app-group.go b/internal/usecase/app-group.go index f23366ac..e17fa685 100644 --- a/internal/usecase/app-group.go +++ b/internal/usecase/app-group.go @@ -170,6 +170,28 @@ func (u *AppGroupUsecase) Delete(ctx context.Context, id domain.AppGroupId) (err } organizationId := cluster.OrganizationId + // check cloudAccount + cloudAccounts, err := u.cloudAccountRepo.Fetch(cluster.OrganizationId, nil) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("Failed to get cloudAccounts"), "", "") + } + tksCloudAccountId := cluster.CloudAccountId.String() + isExist := false + for _, ca := range cloudAccounts { + if ca.ID == cluster.CloudAccountId { + + // FOR TEST. ADD MAGIC KEYWORD + if strings.Contains(ca.Name, domain.CLOUD_ACCOUNT_INCLUSTER) { + tksCloudAccountId = "" + } + isExist = true + break + } + } + if !isExist { + return httpErrors.NewBadRequestError(fmt.Errorf("Not found cloudAccountId[%s] in organization[%s]", cluster.CloudAccountId, cluster.OrganizationId), "", "") + } + // Call argo workflow template workflowTemplate := "" appGroupName := "" @@ -196,6 +218,7 @@ func (u *AppGroupUsecase) Delete(ctx context.Context, id domain.AppGroupId) (err "app_group_id=" + id.String(), "keycloak_url=" + strings.TrimSuffix(viper.GetString("keycloak-address"), "/auth"), "base_repo_branch=" + viper.GetString("revision"), + "cloud_account_id=" + tksCloudAccountId, } workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflowTemplate, opts) From 80fed549bf90d5efc9cf928ce43c183e5c3285cb Mon Sep 17 00:00:00 2001 From: Taekyu Date: Fri, 28 Jul 2023 14:58:01 +0900 Subject: [PATCH 17/17] bugfix. fix filtering query as lower case. --- internal/repository/repository.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 087579dd..33142cd3 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -26,7 +26,7 @@ func CombinedGormFilter(table string, filters []pagination.Filter, combinedFilte // and query for _, filter := range filters { if len(filter.Values) > 1 { - inQuery := fmt.Sprintf("%s.%s::text in (", table, filter.Column) + inQuery := fmt.Sprintf("LOWER(%s.%s::text) in (", table, filter.Column) for _, val := range filter.Values { inQuery = inQuery + fmt.Sprintf("LOWER('%s'),", val) } @@ -34,7 +34,7 @@ func CombinedGormFilter(table string, filters []pagination.Filter, combinedFilte db = db.Where(inQuery) } else { if len(filter.Values[0]) > 0 { - db = db.Where(fmt.Sprintf("%s.%s::text like LOWER('%%%s%%')", table, filter.Column, filter.Values[0])) + db = db.Where(fmt.Sprintf("LOWER(%s.%s::text) like LOWER('%%%s%%')", table, filter.Column, filter.Values[0])) } } } @@ -44,7 +44,7 @@ func CombinedGormFilter(table string, filters []pagination.Filter, combinedFilte if len(combinedFilter.Columns) > 0 { orQuery := "" for _, column := range combinedFilter.Columns { - orQuery = orQuery + fmt.Sprintf("%s.%s::text like LOWER('%%%s%%') OR ", table, column, combinedFilter.Value) + orQuery = orQuery + fmt.Sprintf("LOWER(%s.%s::text) like LOWER('%%%s%%') OR ", table, column, combinedFilter.Value) } orQuery = orQuery[:len(orQuery)-3] db = db.Where(orQuery)