package resources import ( "slices" "cloud.o-forge.io/core/oc-lib/config" "cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/resources/resource_model" "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" "github.com/biter777/countries" "github.com/marcinwyszynski/geopoint" ) // AbstractResource is the struct containing all of the attributes commons to all ressources // Resource is the interface to be implemented by all classes inheriting from Resource to have the same behavior // http://www.inanzzz.com/index.php/post/wqbs/a-basic-usage-of-int-and-string-enum-types-in-golang /* * AbstractResource is a struct that represents a resource * it defines the resource data */ type AbsResource struct { utils.AbstractObject // AbstractObject contains the basic fields of an object (id, name) Logo string `json:"logo,omitempty" bson:"logo,omitempty" validate:"required"` // Logo is the logo of the resource Description string `json:"description,omitempty" bson:"description,omitempty"` // Description is the description of the resource ShortDescription string `json:"short_description,omitempty" bson:"short_description,omitempty" validate:"required"` // ShortDescription is the short description of the resource Owners []utils.Owner `json:"owners,omitempty" bson:"owners,omitempty"` // Owners is the list of owners of the resource ResourceModel *resource_model.ResourceModel `json:"resource_model,omitempty" bson:"resource_model,omitempty"` // ResourceModel is the model of the resource UsageRestrictions string `bson:"usage_restrictions,omitempty" json:"usage_restrictions,omitempty"` } func (r *AbsResource) StoreDraftDefault() { r.IsDraft = true } func (r *AbsResource) CanUpdate(set utils.DBObject) (bool, utils.DBObject) { if r.IsDraft != set.IsDrafted() && set.IsDrafted() { return true, set // only state can be updated } return r.IsDraft != set.IsDrafted() && set.IsDrafted(), set } func (r *AbsResource) CanDelete() bool { return r.IsDraft // only draft bookings can be deleted } func (ao *AbsResource) GetAccessor(request *tools.APIRequest) utils.Accessor { return nil } func (abs *AbsResource) SetResourceModel(model *resource_model.ResourceModel) { abs.ResourceModel = model } type AbstractResource[T ResourceInstanceITF] struct { AbsResource SelectedInstanceIndex int `json:"selected_instance_index,omitempty" bson:"selected_instance_index,omitempty"` // SelectedInstance is the selected instance Instances []T `json:"instances,omitempty" bson:"instances,omitempty"` // Bill is the bill of the resource // Bill is the bill of the resource } func (abs *AbstractResource[T]) ConvertToPricedResource( t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { instances := map[string]string{} profiles := map[string][]pricing.PricingProfileITF{} for _, instance := range abs.Instances { instances[instance.GetID()] = instance.GetName() profiles[instance.GetID()] = instance.GetPricingsProfiles(request.PeerID, request.Groups) } return &PricedResource{ Name: abs.Name, Logo: abs.Logo, ResourceID: abs.UUID, ResourceType: t, InstancesRefs: instances, PricingProfiles: profiles, CreatorID: abs.CreatorID, } } func (abs *AbstractResource[T]) SetAllowedInstances(request *tools.APIRequest) { abs.Instances = verifyAuthAction[T](abs.Instances, request) } func (d *AbstractResource[T]) Trim() { if ok, _ := (&peer.Peer{AbstractObject: utils.AbstractObject{UUID: d.CreatorID}}).IsMySelf(); !ok { for _, instance := range d.Instances { instance.ClearPeerGroups() } } } func (abs *AbstractResource[T]) VerifyAuth(request *tools.APIRequest) bool { return len(verifyAuthAction[T](abs.Instances, request)) > 0 || abs.AbstractObject.VerifyAuth(request) } func verifyAuthAction[T ResourceInstanceITF](baseInstance []T, request *tools.APIRequest) []T { instances := []T{} for _, instance := range baseInstance { _, peerGroups := instance.GetPeerGroups() for _, peers := range peerGroups { if request == nil { continue } if grps, ok := peers[request.PeerID]; ok || config.GetConfig().Whitelist { if (ok && slices.Contains(grps, "*")) || (!ok && config.GetConfig().Whitelist) { instances = append(instances, instance) } for _, grp := range grps { if slices.Contains(request.Groups, grp) { instances = append(instances, instance) } } } } } return instances } type ResourceInstance[T ResourcePartnerITF] struct { UUID string `json:"id,omitempty" bson:"id,omitempty"` Name string `json:"name,omitempty" bson:"name,omitempty"` Location geopoint.GeoPoint `json:"location,omitempty" bson:"location,omitempty"` Country countries.CountryCode `json:"country,omitempty" bson:"country,omitempty"` Source string `bson:"source,omitempty" json:"source,omitempty"` // Source is the source of the storage AccessProtocol string `json:"access_protocol,omitempty" bson:"access_protocol,omitempty"` Partnerships []T `json:"partner_resource,omitempty" bson:"partner_resource,omitempty"` } func (ri *ResourceInstance[T]) GetID() string { return ri.UUID } func (ri *ResourceInstance[T]) GetName() string { return ri.Name } func (ri *ResourceInstance[T]) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF { pricings := []pricing.PricingProfileITF{} for _, p := range ri.Partnerships { pricings = append(pricings, p.GetPricingsProfiles(peerID, groups)...) } return pricings } func (ri *ResourceInstance[T]) GetPeerGroups() ([]ResourcePartnerITF, []map[string][]string) { groups := []map[string][]string{} partners := []ResourcePartnerITF{} for _, p := range ri.Partnerships { partners = append(partners, p) groups = append(groups, p.GetPeerGroups()) } return partners, groups } func (ri *ResourceInstance[T]) ClearPeerGroups() { for _, p := range ri.Partnerships { p.ClearPeerGroups() } } type ResourcePartnerShip[T pricing.PricingProfileITF] struct { Namespace string `json:"namespace" bson:"namespace" default:"default-namespace"` PeerGroups map[string][]string `json:"peer_groups,omitempty" bson:"peer_groups,omitempty"` PricingProfiles map[string]T `json:"pricing,omitempty" bson:"pricing,omitempty"` } func (ri *ResourcePartnerShip[T]) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF { if ri.PeerGroups[peerID] != nil { for _, p := range ri.PeerGroups[peerID] { if slices.Contains(groups, p) { profiles := []pricing.PricingProfileITF{} for _, ri := range ri.PricingProfiles { profiles = append(profiles, ri) } return profiles } } } return []pricing.PricingProfileITF{} } func (rp *ResourcePartnerShip[T]) GetPeerGroups() map[string][]string { return rp.PeerGroups } func (rp *ResourcePartnerShip[T]) ClearPeerGroups() { rp.PeerGroups = map[string][]string{} } /* -> when a workflow should book a resource -> it must be able to book a resource for a particular time -> the resource should be available for the time -> we must be able to parameter the resource for the time -> before bookin' */