feat(be): allow filter entities by ownership

This commit is contained in:
Denis Gukov 2024-12-17 20:13:30 +05:00
parent a047b7dd36
commit dc347442c4
No known key found for this signature in database
GPG Key ID: 044381366A5D4731
8 changed files with 113 additions and 24 deletions

View File

@ -56,7 +56,9 @@ func GetInventory(w http.ResponseWriter, r *http.Request) {
project := context.Get(r, "project").(db.Project)
inventories, err := helpers.Store(r).GetInventories(project.ID, helpers.QueryParams(r.URL))
params := helpers.QueryParams(r.URL)
params.Ownership.WithoutOwnerOnly = true
inventories, err := helpers.Store(r).GetInventories(project.ID, params)
if err != nil {
helpers.WriteError(w, err)

View File

@ -5,9 +5,9 @@ import (
"github.com/semaphoreui/semaphore/util"
"net/http"
"github.com/gorilla/context"
"github.com/semaphoreui/semaphore/api/helpers"
"github.com/semaphoreui/semaphore/db"
"github.com/gorilla/context"
log "github.com/sirupsen/logrus"
)
@ -93,11 +93,11 @@ func AddTemplate(w http.ResponseWriter, r *http.Request) {
if newTemplate.InventoryID == nil {
inv, err = helpers.Store(r).CreateInventory(db.Inventory{
Name: newTemplate.Name + " - default",
ProjectID: project.ID,
HolderID: &newTemplate.ID,
Type: db.InventoryTerraformWorkspace,
Inventory: "default",
Name: newTemplate.Name + " - default",
ProjectID: project.ID,
TemplateID: &newTemplate.ID,
Type: db.InventoryTerraformWorkspace,
Inventory: "default",
})
if err != nil {
@ -115,7 +115,7 @@ func AddTemplate(w http.ResponseWriter, r *http.Request) {
return
}
inv.HolderID = &newTemplate.ID
inv.TemplateID = &newTemplate.ID
err = helpers.Store(r).UpdateInventory(inv)
}

View File

@ -29,11 +29,11 @@ type Inventory struct {
// static/file
Type InventoryType `db:"type" json:"type"`
// HolderID is an ID of template which holds the inventory
// TemplateID is an ID of template which holds the inventory
// It is not used now but can be used in feature for
// inventories which can not be used more than one template
// at once.
HolderID *int `db:"holder_id" json:"holder_id" backup:"-"`
TemplateID *int `db:"template_id" json:"template_id" backup:"-"`
// RepositoryID is an ID of repo where inventory stored.
// If null than inventory will be got from template repository.

View File

@ -38,12 +38,19 @@ func ObjectToJSON(obj interface{}) *string {
return &str
}
type OwnershipFilter struct {
WithoutOwnerOnly bool
TemplateID *int
EnvironmentID *int
}
type RetrieveQueryParams struct {
Offset int
Count int
SortBy string
SortInverted bool
Filter string
Ownership OwnershipFilter
}
type ObjectReferrer struct {
@ -68,6 +75,17 @@ type IntegrationExtractorChildReferrers struct {
Integrations []ObjectReferrer `json:"integrations"`
}
func (f OwnershipFilter) GetOwnerID(ownership ObjectProps) *int {
switch ownership.ReferringColumnSuffix {
case "template_id":
return f.TemplateID
case "environment_id":
return f.EnvironmentID
default:
return nil
}
}
// ObjectProps describe database entities.
// It mainly used for NoSQL implementations (currently BoltDB) to preserve same
// data structure of different implementations and easy change it if required.
@ -80,6 +98,7 @@ type ObjectProps struct {
SortableColumns []string
DefaultSortingColumn string
SortInverted bool // sort from high to low object ID by default. It is useful for some NoSQL implementations.
Ownerships []*ObjectProps
}
var ErrNotFound = errors.New("no rows in result set")
@ -350,6 +369,7 @@ var InventoryProps = ObjectProps{
ReferringColumnSuffix: "inventory_id",
SortableColumns: []string{"name"},
DefaultSortingColumn: "name",
Ownerships: []*ObjectProps{&TemplateProps},
}
var RepositoryProps = ObjectProps{
@ -369,12 +389,6 @@ var TemplateProps = ObjectProps{
DefaultSortingColumn: "name",
}
var ScheduleProps = ObjectProps{
TableName: "project__schedule",
Type: reflect.TypeOf(Schedule{}),
PrimaryColumnName: "id",
}
var ProjectUserProps = ObjectProps{
TableName: "project__user",
Type: reflect.TypeOf(ProjectUser{}),
@ -390,6 +404,13 @@ var ProjectProps = ObjectProps{
IsGlobal: true,
}
var ScheduleProps = ObjectProps{
TableName: "project__schedule",
Type: reflect.TypeOf(Schedule{}),
PrimaryColumnName: "id",
Ownerships: []*ObjectProps{&ProjectProps},
}
var UserProps = ObjectProps{
TableName: "user",
Type: reflect.TypeOf(User{}),
@ -520,8 +541,8 @@ func ValidateInventory(store Store, inventory *Inventory) (err error) {
return
}
if inventory.HolderID != nil {
_, err = store.GetTemplate(inventory.ProjectID, *inventory.HolderID)
if inventory.TemplateID != nil {
_, err = store.GetTemplate(inventory.ProjectID, *inventory.TemplateID)
}
return

View File

@ -389,6 +389,30 @@ func apply(
return
}
if len(props.Ownerships) > 0 {
ownershipMatched := true
for _, ownership := range props.Ownerships {
if params.Ownership.WithoutOwnerOnly {
if f, ok := getReferredValue(*ownership, obj); ok && !f.IsZero() {
ownershipMatched = false
break
}
} else {
ownerID := params.Ownership.GetOwnerID(*ownership)
if ownerID != nil && !isObjectReferredBy(*ownership, intObjectID(*ownerID), obj) {
ownershipMatched = false
break
}
}
}
if !ownershipMatched {
continue
}
}
if filter != nil && !filter(obj) {
continue
}
@ -780,18 +804,41 @@ func (d *BoltDb) getObjectRefsFrom(projectID int, objProps db.ObjectProps, objID
return
}
func isObjectReferredBy(props db.ObjectProps, objID objectID, referringObj interface{}) bool {
func getReferredValue(props db.ObjectProps, referringObj interface{}) (f reflect.Value, ok bool) {
if props.ReferringColumnSuffix == "" {
return false
ok = false
return
}
fieldName, err := getFieldNameByTagSuffix(reflect.TypeOf(referringObj), "db", props.ReferringColumnSuffix)
if err != nil {
ok = false
return
}
f = reflect.ValueOf(referringObj).FieldByName(fieldName)
ok = true
return
}
func isObjectReferredBy(props db.ObjectProps, objID objectID, referringObj interface{}) bool {
f, ok := getReferredValue(props, referringObj)
if !ok {
return false
}
f := reflect.ValueOf(referringObj).FieldByName(fieldName)
//if props.ReferringColumnSuffix == "" {
// return false
//}
//
//fieldName, err := getFieldNameByTagSuffix(reflect.TypeOf(referringObj), "db", props.ReferringColumnSuffix)
//
//if err != nil {
// return false
//}
//
//f := reflect.ValueOf(referringObj).FieldByName(fieldName)
if f.IsZero() {
return false

View File

@ -210,6 +210,23 @@ func (d *SqlDb) makeObjectsQuery(projectID int, props db.ObjectProps, params db.
q = q.Where("pe.project_id=?", projectID)
}
if len(props.Ownerships) > 0 {
for _, ownership := range props.Ownerships {
if params.Ownership.WithoutOwnerOnly {
q = q.Where(squirrel.Eq{
"pe." + string(ownership.ReferringColumnSuffix): nil,
})
} else {
ownerID := params.Ownership.GetOwnerID(*ownership)
if ownerID != nil {
q = q.Where(squirrel.Eq{
"pe." + string(ownership.ReferringColumnSuffix): *ownerID,
})
}
}
}
}
orderDirection := "ASC"
if params.SortInverted {
orderDirection = "DESC"

View File

@ -35,7 +35,7 @@ func (d *SqlDb) UpdateInventory(inventory db.Inventory) error {
inventory.SSHKeyID,
inventory.Inventory,
inventory.BecomeKeyID,
inventory.HolderID,
inventory.TemplateID,
inventory.RepositoryID,
inventory.ID)
@ -53,7 +53,7 @@ func (d *SqlDb) CreateInventory(inventory db.Inventory) (newInventory db.Invento
inventory.SSHKeyID,
inventory.Inventory,
inventory.BecomeKeyID,
inventory.HolderID,
inventory.TemplateID,
inventory.RepositoryID)
if err != nil {

View File

@ -18,4 +18,6 @@ create table project__terraform_inventory_state(
foreign key (`task_id`) references task(`id`) on delete set null,
foreign key (`project_id`) references project(`id`) on delete cascade,
foreign key (`inventory_id`) references project__inventory(`id`) on delete cascade
);
);
alter table `project__inventory` add `template_id` int null references project__template(`id`);