Files
oc-lib/models/resources/resource.go

325 lines
12 KiB
Go
Raw Normal View History

2024-07-18 11:51:12 +02:00
package resources
2024-07-19 10:54:58 +02:00
import (
2026-01-13 16:04:31 +01:00
"errors"
"slices"
"cloud.o-forge.io/core/oc-lib/config"
2026-01-13 16:04:31 +01:00
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/models"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/peer"
2024-11-28 11:05:54 +01:00
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/tools"
"github.com/biter777/countries"
2026-01-12 11:59:05 +01:00
"github.com/google/uuid"
2024-07-19 10:54:58 +02:00
)
2024-07-18 11:51:12 +02:00
// AbstractResource is the struct containing all of the attributes commons to all ressources
2025-01-14 09:15:50 +01:00
type AbstractResource struct {
2026-01-13 16:04:31 +01:00
utils.AbstractObject // AbstractObject contains the basic fields of an object (id, name)
Type string `json:"type,omitempty" bson:"type,omitempty"` // Type is the type of the resource
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
UsageRestrictions string `bson:"usage_restrictions,omitempty" json:"usage_restrictions,omitempty"`
AllowedBookingModes map[booking.BookingMode]*pricing.PricingVariation `bson:"allowed_booking_modes" json:"allowed_booking_modes"`
2025-01-13 14:52:41 +01:00
}
2026-01-13 16:04:31 +01:00
func (r *AbstractResource) GetBookingModes() map[booking.BookingMode]*pricing.PricingVariation {
if len(r.AllowedBookingModes) == 0 {
return map[booking.BookingMode]*pricing.PricingVariation{
booking.PLANNED: {
Percentage: 0,
}, booking.WHEN_POSSIBLE: {
Percentage: 0,
},
}
}
return r.AllowedBookingModes
}
func (r *AbstractResource) GetSelectedInstance(selected *int) ResourceInstanceITF {
return nil
}
2025-01-20 13:26:30 +01:00
func (r *AbstractResource) GetType() string {
2025-01-20 13:49:39 +01:00
return tools.INVALID.String()
2025-01-20 13:26:30 +01:00
}
2025-01-14 09:15:50 +01:00
func (r *AbstractResource) StoreDraftDefault() {
r.IsDraft = true
}
2025-01-15 09:20:26 +01:00
2025-01-14 09:15:50 +01:00
func (r *AbstractResource) 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
}
2025-01-14 09:15:50 +01:00
func (r *AbstractResource) CanDelete() bool {
return r.IsDraft // only draft bookings can be deleted
}
2025-02-13 15:11:23 +01:00
type AbstractInstanciatedResource[T ResourceInstanceITF] struct {
2025-01-14 09:15:50 +01:00
AbstractResource // AbstractResource contains the basic fields of an object (id, name)
Instances []T `json:"instances,omitempty" bson:"instances,omitempty"` // Bill is the bill of the resource // Bill is the bill of the resource
}
2026-01-12 11:59:05 +01:00
func (abs *AbstractInstanciatedResource[T]) AddInstances(instance ResourceInstanceITF) {
abs.Instances = append(abs.Instances, instance.(T))
}
2026-01-13 16:04:31 +01:00
func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
2025-01-13 11:24:07 +01:00
instances := map[string]string{}
2025-01-14 11:28:16 +01:00
profiles := []pricing.PricingProfileITF{}
2026-01-13 16:04:31 +01:00
for _, instance := range abs.Instances { // TODO why it crush before ?
2025-01-13 11:24:07 +01:00
instances[instance.GetID()] = instance.GetName()
2025-06-20 12:10:36 +02:00
profiles = instance.GetPricingsProfiles(request.PeerID, request.Groups)
}
var profile pricing.PricingProfileITF
2026-01-13 16:04:31 +01:00
if t := abs.GetSelectedInstance(selectedInstance); t != nil {
profile = t.GetProfile(request.PeerID, selectedPartnership, selectedBuyingStrategy, selectedStrategy)
}
if profile == nil {
if len(profiles) > 0 {
profile = profiles[0]
} else {
if ok, _ := peer.IsMySelf(request.PeerID); ok {
profile = pricing.GetDefaultPricingProfile()
} else {
return nil, errors.New("no pricing profile found")
}
}
2025-06-20 12:10:36 +02:00
}
2026-01-13 16:04:31 +01:00
variations := []*pricing.PricingVariation{}
if selectedBookingModeIndex != nil && abs.AllowedBookingModes[booking.BookingMode(*selectedBookingModeIndex)] != nil {
variations = append(variations, abs.AllowedBookingModes[booking.BookingMode(*selectedBookingModeIndex)])
2025-01-13 11:24:07 +01:00
}
return &PricedResource{
Name: abs.Name,
Logo: abs.Logo,
ResourceID: abs.UUID,
ResourceType: t,
2026-01-13 16:04:31 +01:00
Quantity: 1,
2025-01-13 11:24:07 +01:00
InstancesRefs: instances,
2025-06-20 12:10:36 +02:00
SelectedPricing: profile,
2026-01-13 16:04:31 +01:00
Variations: variations,
2025-01-13 11:24:07 +01:00
CreatorID: abs.CreatorID,
2026-01-13 16:04:31 +01:00
}, nil
}
2025-02-13 15:11:23 +01:00
func (abs *AbstractInstanciatedResource[T]) ClearEnv() utils.DBObject {
for _, instance := range abs.Instances {
instance.ClearEnv()
}
return abs
}
2026-01-13 16:04:31 +01:00
func (r *AbstractInstanciatedResource[T]) GetSelectedInstance(selected *int) ResourceInstanceITF {
if selected != nil && len(r.Instances) > *selected {
return r.Instances[*selected]
}
if len(r.Instances) > 0 {
return r.Instances[0]
}
return nil
2024-11-28 11:05:54 +01:00
}
2025-02-13 15:11:23 +01:00
func (abs *AbstractInstanciatedResource[T]) SetAllowedInstances(request *tools.APIRequest) {
2025-02-19 11:41:52 +01:00
if request != nil && request.PeerID == abs.CreatorID && request.PeerID != "" {
2025-02-18 12:55:49 +01:00
return
}
2025-06-16 13:48:32 +02:00
abs.Instances = VerifyAuthAction[T](abs.Instances, request)
2024-12-16 12:17:20 +01:00
}
2025-02-13 15:11:23 +01:00
func (d *AbstractInstanciatedResource[T]) Trim() {
2025-01-21 09:02:26 +01:00
d.Type = d.GetType()
2024-12-16 12:17:20 +01:00
if ok, _ := (&peer.Peer{AbstractObject: utils.AbstractObject{UUID: d.CreatorID}}).IsMySelf(); !ok {
for _, instance := range d.Instances {
instance.ClearPeerGroups()
}
}
}
2026-01-13 16:04:31 +01:00
func (abs *AbstractInstanciatedResource[T]) VerifyAuth(callName string, request *tools.APIRequest) bool {
return len(VerifyAuthAction[T](abs.Instances, request)) > 0 || abs.AbstractObject.VerifyAuth(callName, request)
2024-12-16 12:17:20 +01:00
}
2025-06-16 13:48:32 +02:00
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)
2025-01-22 11:16:35 +01:00
continue
}
for _, grp := range grps {
if slices.Contains(request.Groups, grp) {
instances = append(instances, instance)
}
}
}
}
}
return instances
}
2025-01-14 08:42:12 +01:00
type GeoPoint struct {
Latitude float64 `json:"latitude,omitempty" bson:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty" bson:"longitude,omitempty"`
}
type ResourceInstance[T ResourcePartnerITF] struct {
utils.AbstractObject
2026-01-13 16:04:31 +01:00
Location GeoPoint `json:"location,omitempty" bson:"location,omitempty"`
Country countries.CountryCode `json:"country,omitempty" bson:"country,omitempty"`
AccessProtocol string `json:"access_protocol,omitempty" bson:"access_protocol,omitempty"`
Env []models.Param `json:"env,omitempty" bson:"env,omitempty"`
Inputs []models.Param `json:"inputs,omitempty" bson:"inputs,omitempty"`
Outputs []models.Param `json:"outputs,omitempty" bson:"outputs,omitempty"`
// SelectedPartnershipIndex int `json:"selected_partnership_index,omitempty" bson:"selected_partnership_index,omitempty"`
// SelectedBuyingStrategy int `json:"selected_buying_strategy,omitempty" bson:"selected_buying_strategy,omitempty"`
// SelectedStrategy int `json:"selected_strategy,omitempty" bson:"selected_strategy,omitempty"`
Partnerships []T `json:"partnerships,omitempty" bson:"partnerships,omitempty"`
}
2026-01-13 16:04:31 +01:00
// TODO should kicks all selection
2026-01-12 11:59:05 +01:00
func NewInstance[T ResourcePartnerITF](name string) *ResourceInstance[T] {
return &ResourceInstance[T]{
AbstractObject: utils.AbstractObject{
UUID: uuid.New().String(),
Name: name,
},
Partnerships: []T{},
}
}
func (ri *ResourceInstance[T]) ClearEnv() {
ri.Env = []models.Param{}
ri.Inputs = []models.Param{}
ri.Outputs = []models.Param{}
}
2026-01-13 16:04:31 +01:00
func (ri *ResourceInstance[T]) GetProfile(peerID string, partnershipIndex *int, buyingIndex *int, strategyIndex *int) pricing.PricingProfileITF {
if partnershipIndex != nil && len(ri.Partnerships) > *partnershipIndex {
prts := ri.Partnerships[*partnershipIndex]
return prts.GetProfile(buyingIndex, strategyIndex)
2025-06-24 12:22:27 +02:00
}
2026-01-13 16:04:31 +01:00
if ok, _ := peer.IsMySelf(peerID); ok {
return pricing.GetDefaultPricingProfile()
2025-06-24 12:22:27 +02:00
}
return nil
2025-06-20 12:10:36 +02:00
}
func (ri *ResourceInstance[T]) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF {
2025-01-13 11:24:07 +01:00
pricings := []pricing.PricingProfileITF{}
for _, p := range ri.Partnerships {
2025-06-20 12:10:36 +02:00
pricings = append(pricings, p.GetPricingsProfiles(peerID, groups)...)
}
2026-01-13 16:04:31 +01:00
if len(pricings) == 0 {
if ok, _ := peer.IsMySelf(peerID); ok {
pricings = append(pricings, pricing.GetDefaultPricingProfile())
}
}
2025-01-13 11:24:07 +01:00
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())
}
2026-01-13 16:04:31 +01:00
if len(groups) == 0 {
_, id := peer.GetSelf()
groups = []map[string][]string{
{
id: {"*"},
},
}
// TODO make allow all only for self.
}
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"`
2025-06-19 08:11:11 +02:00
PricingProfiles map[int]map[int]T `json:"pricing_profiles,omitempty" bson:"pricing_profiles,omitempty"`
// to upgrade pricing profiles. to be a map BuyingStrategy, map of Strategy
}
2026-01-13 16:04:31 +01:00
func (ri *ResourcePartnerShip[T]) GetProfile(buying *int, strategy *int) pricing.PricingProfileITF {
if buying != nil && strategy != nil {
if strat, ok := ri.PricingProfiles[*buying]; ok {
if profile, ok := strat[*strategy]; ok {
return profile
}
2025-06-24 12:22:27 +02:00
}
}
2026-01-13 16:04:31 +01:00
2025-06-24 12:22:27 +02:00
return nil
}
2025-06-19 08:11:11 +02:00
/*
Le pricing doit être selectionné lors d'un scheduling...
le type de paiement défini le type de stratégie de paiement
note : il faut rajouté - une notion de facturation
Une order est l'ensemble de la commande... un booking une réservation, une purchase un acte d'achat.
Une bill (facture) représente alors... l'emission d'une facture à un instant T en but d'être honorée, envoyée ... etc.
*/
2025-06-20 12:10:36 +02:00
func (ri *ResourcePartnerShip[T]) GetPricingsProfiles(peerID string, groups []string) []pricing.PricingProfileITF {
2025-02-11 08:30:38 +01:00
profiles := []pricing.PricingProfileITF{}
2025-06-19 08:11:11 +02:00
if ri.PeerGroups[peerID] == nil {
return profiles
}
for _, p := range ri.PeerGroups[peerID] {
if slices.Contains(groups, p) || slices.Contains(groups, "*") {
2025-06-20 12:10:36 +02:00
for _, ri := range ri.PricingProfiles {
for _, i := range ri {
profiles = append(profiles, i)
2025-01-13 11:24:07 +01:00
}
}
2025-06-19 08:11:11 +02:00
return profiles
2025-01-13 11:24:07 +01:00
}
}
2026-01-13 16:04:31 +01:00
if len(profiles) == 0 {
if ok, _ := peer.IsMySelf(peerID); ok {
profiles = append(profiles, pricing.GetDefaultPricingProfile())
}
}
2025-02-11 08:30:38 +01:00
return profiles
}
func (rp *ResourcePartnerShip[T]) GetPeerGroups() map[string][]string {
2026-01-13 16:04:31 +01:00
if len(rp.PeerGroups) == 0 {
_, id := peer.GetSelf()
return map[string][]string{
id: {"*"},
}
}
return rp.PeerGroups
}
func (rp *ResourcePartnerShip[T]) ClearPeerGroups() {
rp.PeerGroups = map[string][]string{}
}