PricedItem evolved

This commit is contained in:
mr
2026-03-20 13:07:06 +01:00
parent 2abc035ec0
commit 1508cc3611
16 changed files with 162 additions and 96 deletions

View File

@@ -7,6 +7,7 @@ import (
"cloud.o-forge.io/core/oc-lib/dbs"
"cloud.o-forge.io/core/oc-lib/models/common/enum"
"cloud.o-forge.io/core/oc-lib/models/common/pricing"
"cloud.o-forge.io/core/oc-lib/models/order"
"cloud.o-forge.io/core/oc-lib/models/peer"
"cloud.o-forge.io/core/oc-lib/models/resources"
@@ -48,9 +49,10 @@ func DraftFirstBill(order *order.Order, request *tools.APIRequest) (*Bill, error
peers[p.DestPeerID] = []*PeerItemOrder{}
}
peers[p.DestPeerID] = append(peers[p.DestPeerID], &PeerItemOrder{
Purchase: p,
Item: p.PricedItem,
Quantity: 1,
ResourceType: p.ResourceType,
Purchase: p,
Item: p.PricedItem,
Quantity: 1,
})
}
for _, b := range order.Bookings {
@@ -69,7 +71,9 @@ func DraftFirstBill(order *order.Order, request *tools.APIRequest) (*Bill, error
peers[b.DestPeerID] = []*PeerItemOrder{}
}
peers[b.DestPeerID] = append(peers[b.DestPeerID], &PeerItemOrder{
Item: b.PricedItem,
ResourceType: b.ResourceType,
Quantity: 1,
Item: b.PricedItem,
})
}
peerOrders := map[string]*PeerOrder{}
@@ -135,6 +139,22 @@ type PeerOrder struct {
Total float64 `json:"total,omitempty" bson:"total,omitempty"`
}
func PricedByType(dt tools.DataType) pricing.PricedItemITF {
switch dt {
case tools.PROCESSING_RESOURCE:
return &resources.PricedProcessingResource{}
case tools.STORAGE_RESOURCE:
return &resources.PricedStorageResource{}
case tools.DATA_RESOURCE:
return &resources.PricedDataResource{}
case tools.COMPUTE_RESOURCE:
return &resources.PricedComputeResource{}
case tools.WORKFLOW_RESOURCE:
return &resources.PricedResource[*pricing.ExploitPricingProfile[pricing.TimePricingStrategy]]{}
}
return nil
}
func (d *PeerOrder) Pay(request *tools.APIRequest, response chan *PeerOrder, wg *sync.WaitGroup) {
d.Status = enum.PENDING
@@ -144,7 +164,7 @@ func (d *PeerOrder) Pay(request *tools.APIRequest, response chan *PeerOrder, wg
d.Status = enum.PAID // TO REMOVE LATER IT'S A MOCK
if d.Status == enum.PAID {
for _, b := range d.Items {
var priced *resources.PricedResource
priced := PricedByType(b.ResourceType)
bb, _ := json.Marshal(b.Item)
json.Unmarshal(bb, priced)
if !priced.IsPurchasable() {
@@ -178,9 +198,10 @@ func (d *PeerOrder) SumUpBill(request *tools.APIRequest) error {
}
type PeerItemOrder struct {
Quantity int `json:"quantity,omitempty" bson:"quantity,omitempty"`
Purchase *purchase_resource.PurchaseResource `json:"purchase,omitempty" bson:"purchase,omitempty"`
Item map[string]interface{} `json:"item,omitempty" bson:"item,omitempty"`
ResourceType tools.DataType `json:"datatype,omitempty" bson:"datatype,omitempty"`
Quantity int `json:"quantity,omitempty" bson:"quantity,omitempty"`
Purchase *purchase_resource.PurchaseResource `json:"purchase,omitempty" bson:"purchase,omitempty"`
Item map[string]interface{} `json:"item,omitempty" bson:"item,omitempty"`
}
func (d *PeerItemOrder) GetPriceHT(request *tools.APIRequest) (float64, error) {
@@ -189,7 +210,7 @@ func (d *PeerItemOrder) GetPriceHT(request *tools.APIRequest) (float64, error) {
return 0, nil
}
///////////
var priced *resources.PricedResource
priced := PricedByType(d.ResourceType)
b, _ := json.Marshal(d.Item)
err := json.Unmarshal(b, priced)
if err != nil {

View File

@@ -36,11 +36,11 @@ func (abs *ComputeResource) ConvertToPricedResource(t tools.DataType, selectedIn
if t != tools.COMPUTE_RESOURCE {
return nil, errors.New("not the proper type expected : cannot convert to priced resource : have " + t.String() + " wait Compute")
}
p, err := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, request)
p, err := ConvertToPricedResource[*ComputeResourcePricingProfile](t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, abs, request)
if err != nil {
return nil, err
}
priced := p.(*PricedResource)
priced := p.(*PricedResource[*ComputeResourcePricingProfile])
return &PricedComputeResource{
PricedResource: *priced,
}, nil
@@ -122,7 +122,10 @@ func (p *ComputeResourcePricingProfile) GetPriceHT(amountOfData float64, explici
return 0, errors.New("params must be set")
}
pp := float64(0)
model := params[1]
model := ""
if len(params) > 1 {
model = params[1]
}
if strings.Contains(params[0], "cpus") && len(params) > 1 {
if _, ok := p.CPUsPrices[model]; ok {
p.Pricing.Price = p.CPUsPrices[model]
@@ -158,7 +161,7 @@ func (p *ComputeResourcePricingProfile) GetPriceHT(amountOfData float64, explici
}
type PricedComputeResource struct {
PricedResource
PricedResource[*ComputeResourcePricingProfile]
CPUsLocated map[string]float64 `json:"cpus_in_use" bson:"cpus_in_use"` // CPUsInUse is the list of CPUs in use
GPUsLocated map[string]float64 `json:"gpus_in_use" bson:"gpus_in_use"` // GPUsInUse is the list of GPUs in use

View File

@@ -41,11 +41,11 @@ func (abs *DataResource) ConvertToPricedResource(t tools.DataType, selectedInsta
if t != tools.DATA_RESOURCE {
return nil, errors.New("not the proper type expected : cannot convert to priced resource : have " + t.String() + " wait Data")
}
p, err := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, request)
p, err := ConvertToPricedResource[*DataResourcePricingProfile](t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, abs, request)
if err != nil {
return nil, err
}
priced := p.(*PricedResource)
priced := p.(*PricedResource[*DataResourcePricingProfile])
return &PricedDataResource{
PricedResource: *priced,
}, nil
@@ -160,7 +160,7 @@ func (p *DataResourcePricingProfile) IsBooked() bool {
}
type PricedDataResource struct {
PricedResource
PricedResource[*DataResourcePricingProfile]
UsageStorageGB float64 `json:"storage_gb,omitempty" bson:"storage_gb,omitempty"`
}

View File

@@ -8,6 +8,10 @@ import (
"cloud.o-forge.io/core/oc-lib/tools"
)
type PricedResourceITF interface {
pricing.PricedItemITF
}
type ResourceInterface interface {
utils.DBObject
FilterPeer(peerID string) *dbs.Filters
@@ -15,7 +19,7 @@ type ResourceInterface interface {
ConvertToPricedResource(t tools.DataType, a *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, b *int, request *tools.APIRequest) (pricing.PricedItemITF, error)
GetType() string
ClearEnv() utils.DBObject
SetAllowedInstances(request *tools.APIRequest, instance_id ...string)
SetAllowedInstances(request *tools.APIRequest, instance_id ...string) []ResourceInstanceITF
AddInstances(instance ResourceInstanceITF)
GetSelectedInstance(index *int) ResourceInstanceITF
}

View File

@@ -37,12 +37,13 @@ func (d *NativeTool) ClearEnv() utils.DBObject {
return d
}
func (w *NativeTool) SetAllowedInstances(request *tools.APIRequest, ids ...string) {
func (w *NativeTool) SetAllowedInstances(request *tools.APIRequest, ids ...string) []ResourceInstanceITF {
/* EMPTY */
return []ResourceInstanceITF{}
}
func (w *NativeTool) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
return &PricedResource{
return &PricedResource[*pricing.ExploitPricingProfile[pricing.TimePricingStrategy]]{
Name: w.Name,
Logo: w.Logo,
ResourceID: w.UUID,

View File

@@ -16,11 +16,11 @@ type BookingConfiguration struct {
Mode booking.BookingMode `json:"mode,omitempty" bson:"mode,omitempty"`
}
type PricedResource struct {
type PricedResource[T pricing.PricingProfileITF] struct {
Name string `json:"name,omitempty" bson:"name,omitempty"`
Logo string `json:"logo,omitempty" bson:"logo,omitempty"`
InstancesRefs map[string]string `json:"instances_refs,omitempty" bson:"instances_refs,omitempty"`
SelectedPricing pricing.PricingProfileITF `json:"selected_pricing,omitempty" bson:"selected_pricing,omitempty"`
SelectedPricing T `json:"selected_pricing,omitempty" bson:"selected_pricing,omitempty"`
Quantity int `json:"quantity,omitempty" bson:"quantity,omitempty"`
BookingConfiguration *BookingConfiguration `json:"booking_configuration,omitempty" bson:"booking_configuration,omitempty"`
Variations []*pricing.PricingVariation `json:"pricing_variations" bson:"pricing_variations"`
@@ -31,56 +31,56 @@ type PricedResource struct {
ResourceType tools.DataType `json:"resource_type,omitempty" bson:"resource_type,omitempty"`
}
func (abs *PricedResource) GetQuantity() int {
func (abs *PricedResource[T]) GetQuantity() int {
return abs.Quantity
}
func (abs *PricedResource) AddQuantity(amount int) {
func (abs *PricedResource[T]) AddQuantity(amount int) {
abs.Quantity += amount
}
func (abs *PricedResource) SelectPricing() pricing.PricingProfileITF {
func (abs *PricedResource[T]) SelectPricing() pricing.PricingProfileITF {
return abs.SelectedPricing
}
func (abs *PricedResource) GetID() string {
func (abs *PricedResource[T]) GetID() string {
return abs.ResourceID
}
func (abs *PricedResource) GetInstanceID() string {
func (abs *PricedResource[T]) GetInstanceID() string {
return abs.InstanceID
}
func (abs *PricedResource) GetType() tools.DataType {
func (abs *PricedResource[T]) GetType() tools.DataType {
return abs.ResourceType
}
func (abs *PricedResource) GetCreatorID() string {
func (abs *PricedResource[T]) GetCreatorID() string {
return abs.CreatorID
}
func (abs *PricedResource) IsPurchasable() bool {
if abs.SelectedPricing == nil {
func (abs *PricedResource[T]) IsPurchasable() bool {
if any(abs.SelectedPricing) == nil {
return false
}
return (abs.SelectedPricing).IsPurchasable()
return abs.SelectedPricing.IsPurchasable()
}
func (abs *PricedResource) IsBooked() bool {
if abs.SelectedPricing == nil {
func (abs *PricedResource[T]) IsBooked() bool {
if any(abs.SelectedPricing) == nil {
return false
}
return (abs.SelectedPricing).IsBooked()
return abs.SelectedPricing.IsBooked()
}
func (abs *PricedResource) GetLocationEnd() *time.Time {
func (abs *PricedResource[T]) GetLocationEnd() *time.Time {
if abs.BookingConfiguration == nil {
return nil
}
return abs.BookingConfiguration.UsageEnd
}
func (abs *PricedResource) GetLocationStart() *time.Time {
func (abs *PricedResource[T]) GetLocationStart() *time.Time {
if abs.BookingConfiguration == nil {
now := time.Now().Add(2 * time.Minute)
return &now
@@ -88,34 +88,34 @@ func (abs *PricedResource) GetLocationStart() *time.Time {
return abs.BookingConfiguration.UsageStart
}
func (abs *PricedResource) SetLocationStart(start time.Time) {
func (abs *PricedResource[T]) SetLocationStart(start time.Time) {
if abs.BookingConfiguration == nil {
abs.BookingConfiguration = &BookingConfiguration{}
}
abs.BookingConfiguration.UsageStart = &start
}
func (abs *PricedResource) SetLocationEnd(end time.Time) {
func (abs *PricedResource[T]) SetLocationEnd(end time.Time) {
if abs.BookingConfiguration == nil {
abs.BookingConfiguration = &BookingConfiguration{}
}
abs.BookingConfiguration.UsageEnd = &end
}
func (abs *PricedResource) GetBookingMode() booking.BookingMode {
func (abs *PricedResource[T]) GetBookingMode() booking.BookingMode {
if abs.BookingConfiguration == nil {
return booking.WHEN_POSSIBLE
}
return abs.BookingConfiguration.Mode
}
func (abs *PricedResource) GetExplicitDurationInS() float64 {
func (abs *PricedResource[T]) GetExplicitDurationInS() float64 {
if abs.BookingConfiguration == nil {
abs.BookingConfiguration = &BookingConfiguration{}
}
if abs.BookingConfiguration.ExplicitBookingDurationS == 0 {
if abs.BookingConfiguration.UsageEnd == nil && abs.BookingConfiguration.UsageStart == nil {
return (5 * time.Minute).Seconds()
return (1 * time.Hour).Seconds()
}
if abs.BookingConfiguration.UsageEnd == nil {
add := abs.BookingConfiguration.UsageStart.Add(5 * time.Minute)
@@ -126,7 +126,7 @@ func (abs *PricedResource) GetExplicitDurationInS() float64 {
return abs.BookingConfiguration.ExplicitBookingDurationS
}
func (r *PricedResource) GetPriceHT() (float64, error) {
func (r *PricedResource[T]) GetPriceHT() (float64, error) {
now := time.Now()
if r.BookingConfiguration == nil {
r.BookingConfiguration = &BookingConfiguration{}
@@ -138,8 +138,8 @@ func (r *PricedResource) GetPriceHT() (float64, error) {
add := r.BookingConfiguration.UsageStart.Add(time.Duration(1 * time.Hour))
r.BookingConfiguration.UsageEnd = &add
}
if r.SelectedPricing == nil {
return 0, errors.New("pricing profile must be set on Priced Resource " + r.ResourceID)
if any(r.SelectedPricing) == nil {
return 0, errors.New("pricing profile must be set for resource " + r.ResourceID)
}
pricing := r.SelectedPricing
return pricing.GetPriceHT(1, 0, *r.BookingConfiguration.UsageStart, *r.BookingConfiguration.UsageEnd, r.Variations)

View File

@@ -1,6 +1,7 @@
package resources
import (
"errors"
"time"
"cloud.o-forge.io/core/oc-lib/models/common/enum"
@@ -65,7 +66,7 @@ type ProcessingResourcePartnership struct {
}
type PricedProcessingResource struct {
PricedResource
PricedResource[*ProcessingResourcePricingProfile]
IsService bool
}
@@ -82,7 +83,7 @@ func (a *PricedProcessingResource) GetExplicitDurationInS() float64 {
if a.IsService {
return -1
}
return (5 * time.Minute).Seconds()
return (1 * time.Hour).Seconds()
}
return a.BookingConfiguration.UsageEnd.Sub(*a.BookingConfiguration.UsageStart).Seconds()
}
@@ -93,6 +94,20 @@ func (d *ProcessingResource) GetAccessor(request *tools.APIRequest) utils.Access
return NewAccessor[*ProcessingResource](tools.PROCESSING_RESOURCE, request, func() utils.DBObject { return &ProcessingResource{} }) // Create a new instance of the accessor
}
func (abs *ProcessingResource) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
if t != tools.PROCESSING_RESOURCE {
return nil, errors.New("not the proper type expected : cannot convert to priced resource : have " + t.String() + " wait Data")
}
p, err := ConvertToPricedResource[*DataResourcePricingProfile](t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, abs, request)
if err != nil {
return nil, err
}
priced := p.(*PricedResource[*DataResourcePricingProfile])
return &PricedDataResource{
PricedResource: *priced,
}, nil
}
type ProcessingResourcePricingProfile struct {
pricing.AccessPricingProfile[pricing.TimePricingStrategy] // AccessPricingProfile is the pricing profile of a data it means that we can access the data for an amount of time
}

View File

@@ -73,9 +73,9 @@ func (abs *AbstractInstanciatedResource[T]) AddInstances(instance ResourceInstan
abs.Instances = append(abs.Instances, instance.(T))
}
func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.DataType,
func ConvertToPricedResource[T pricing.PricingProfileITF](t tools.DataType,
selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int,
selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
selectedBookingModeIndex *int, abs ResourceInterface, request *tools.APIRequest) (pricing.PricedItemITF, error) {
instances := map[string]string{}
var profile pricing.PricingProfileITF
var inst ResourceInstanceITF
@@ -84,7 +84,7 @@ func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.Data
instances[t.GetID()] = t.GetName()
profile = t.GetProfile(request.PeerID, selectedPartnership, selectedBuyingStrategy, selectedStrategy)
} else {
for i, instance := range abs.Instances { // TODO why it crush before ?
for i, instance := range abs.SetAllowedInstances(request) { // TODO why it crush before ?
if i == 0 {
inst = instance
}
@@ -106,8 +106,8 @@ func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.Data
}*/
}
variations := []*pricing.PricingVariation{}
if selectedBookingModeIndex != nil && abs.AllowedBookingModes[booking.BookingMode(*selectedBookingModeIndex)] != nil {
variations = append(variations, abs.AllowedBookingModes[booking.BookingMode(*selectedBookingModeIndex)])
if selectedBookingModeIndex != nil && abs.GetBookingModes()[booking.BookingMode(*selectedBookingModeIndex)] != nil {
variations = append(variations, abs.GetBookingModes()[booking.BookingMode(*selectedBookingModeIndex)])
}
// Seed the booking configuration with the instance's historical average duration
// so GetExplicitDurationInS() returns a realistic default out of the box.
@@ -117,17 +117,21 @@ func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.Data
bc = &BookingConfiguration{ExplicitBookingDurationS: avg}
}
}
return &PricedResource{
Name: abs.Name,
Logo: abs.Logo,
ResourceID: abs.UUID,
InstanceID: inst.GetID(),
instanceID := ""
if inst != nil {
instanceID = inst.GetID()
}
selectedPricing, _ := profile.(T)
return &PricedResource[T]{
Name: abs.GetName(),
ResourceID: abs.GetID(),
InstanceID: instanceID,
ResourceType: t,
Quantity: 1,
InstancesRefs: instances,
SelectedPricing: profile,
SelectedPricing: selectedPricing,
Variations: variations,
CreatorID: abs.CreatorID,
CreatorID: abs.GetCreatorID(),
BookingConfiguration: bc,
}, nil
}
@@ -149,11 +153,16 @@ func (r *AbstractInstanciatedResource[T]) GetSelectedInstance(selected *int) Res
return nil
}
func (abs *AbstractInstanciatedResource[T]) SetAllowedInstances(request *tools.APIRequest, instanceID ...string) {
if (request != nil && request.PeerID == abs.CreatorID && request.PeerID != "") || request.Admin {
return
func (abs *AbstractInstanciatedResource[T]) SetAllowedInstances(request *tools.APIRequest, instanceID ...string) []ResourceInstanceITF {
if !((request != nil && request.PeerID == abs.CreatorID && request.PeerID != "") || request.Admin) {
abs.Instances = VerifyAuthAction(abs.Instances, request, instanceID...)
}
abs.Instances = VerifyAuthAction(abs.Instances, request, instanceID...)
inst := []ResourceInstanceITF{}
for _, i := range abs.Instances {
inst = append(inst, i)
}
return inst
}
func (abs *AbstractInstanciatedResource[T]) VerifyAuth(callName string, request *tools.APIRequest) bool {

View File

@@ -35,11 +35,11 @@ func (abs *StorageResource) ConvertToPricedResource(t tools.DataType, selectedIn
if t != tools.STORAGE_RESOURCE {
return nil, errors.New("not the proper type expected : cannot convert to priced resource : have " + t.String() + " wait Storage")
}
p, err := abs.AbstractInstanciatedResource.ConvertToPricedResource(t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, request)
p, err := ConvertToPricedResource[*StorageResourcePricingProfile](t, selectedInstance, selectedPartnership, selectedBuyingStrategy, selectedStrategy, selectedBookingModeIndex, abs, request)
if err != nil {
return nil, err
}
priced := p.(*PricedResource)
priced := p.(*PricedResource[*StorageResourcePricingProfile])
return &PricedStorageResource{
PricedResource: *priced,
}, nil
@@ -181,7 +181,7 @@ func (p *StorageResourcePricingProfile) IsBooked() bool {
}
type PricedStorageResource struct {
PricedResource
PricedResource[*StorageResourcePricingProfile]
UsageStorageGB float64 `json:"storage_gb,omitempty" bson:"storage_gb,omitempty"`
}

View File

@@ -63,9 +63,16 @@ func TestPricedComputeResource_GetPriceHT(t *testing.T) {
start := time.Now()
end := start.Add(1 * time.Hour)
r := resources.PricedComputeResource{
PricedResource: resources.PricedResource{
ResourceID: "comp456",
SelectedPricing: &MockPricingProfile{ReturnCost: 1.0},
PricedResource: resources.PricedResource[*resources.ComputeResourcePricingProfile]{
ResourceID: "comp456",
SelectedPricing: &resources.ComputeResourcePricingProfile{
CPUsPrices: map[string]float64{"Xeon": 2.0},
ExploitPricingProfile: pricing.ExploitPricingProfile[pricing.TimePricingStrategy]{
AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{
Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{Price: 1.0},
},
},
},
BookingConfiguration: &resources.BookingConfiguration{
UsageStart: &start,
UsageEnd: &end,
@@ -73,8 +80,8 @@ func TestPricedComputeResource_GetPriceHT(t *testing.T) {
},
},
CPUsLocated: map[string]float64{"Xeon": 2},
GPUsLocated: map[string]float64{"Tesla": 1},
RAMLocated: 4,
GPUsLocated: map[string]float64{},
RAMLocated: 0,
}
price, err := r.GetPriceHT()
@@ -84,7 +91,7 @@ func TestPricedComputeResource_GetPriceHT(t *testing.T) {
func TestPricedComputeResource_GetPriceHT_MissingProfile(t *testing.T) {
r := resources.PricedComputeResource{
PricedResource: resources.PricedResource{
PricedResource: resources.PricedResource[*resources.ComputeResourcePricingProfile]{
ResourceID: "comp789",
},
}

View File

@@ -76,7 +76,7 @@ func TestDataResourcePricingStrategy_GetQuantity(t *testing.T) {
func TestDataResourcePricingProfile_IsPurchased(t *testing.T) {
profile := &resources.DataResourcePricingProfile{}
profile.Pricing.BuyingStrategy = pricing.SUBSCRIPTION
profile.Pricing.BuyingStrategy = pricing.PERMANENT
assert.True(t, profile.IsPurchasable())
}
@@ -91,7 +91,7 @@ func TestPricedDataResource_GetPriceHT(t *testing.T) {
pricingProfile.Pricing.OverrideStrategy = resources.PER_GB_DOWNLOADED
r := &resources.PricedDataResource{
PricedResource: resources.PricedResource{
PricedResource: resources.PricedResource[*resources.DataResourcePricingProfile]{
SelectedPricing: pricingProfile,
BookingConfiguration: &resources.BookingConfiguration{
UsageStart: &now,
@@ -107,7 +107,7 @@ func TestPricedDataResource_GetPriceHT(t *testing.T) {
func TestPricedDataResource_GetPriceHT_NoProfiles(t *testing.T) {
r := &resources.PricedDataResource{
PricedResource: resources.PricedResource{
PricedResource: resources.PricedResource[*resources.DataResourcePricingProfile]{
ResourceID: "test-resource",
},
}

View File

@@ -36,7 +36,7 @@ func (m *MockPricingProfile) GetPriceHT(amount float64, explicitDuration float64
// ---- Tests ----
func TestGetIDAndCreatorAndType(t *testing.T) {
r := resources.PricedResource{
r := resources.PricedResource[pricing.PricingProfileITF]{
ResourceID: "res-123",
CreatorID: "user-abc",
ResourceType: tools.DATA_RESOURCE,
@@ -48,19 +48,19 @@ func TestGetIDAndCreatorAndType(t *testing.T) {
func TestIsPurchased(t *testing.T) {
t.Run("nil selected pricing returns false", func(t *testing.T) {
r := &resources.PricedResource{}
r := &resources.PricedResource[pricing.PricingProfileITF]{}
assert.False(t, r.IsPurchasable())
})
t.Run("returns true if pricing profile is purchased", func(t *testing.T) {
mock := &MockPricingProfile{Purchased: true}
r := &resources.PricedResource{SelectedPricing: mock}
r := &resources.PricedResource[pricing.PricingProfileITF]{SelectedPricing: mock}
assert.True(t, r.IsPurchasable())
})
}
func TestGetAndSetLocationStartEnd(t *testing.T) {
r := &resources.PricedResource{}
r := &resources.PricedResource[pricing.PricingProfileITF]{}
now := time.Now()
r.SetLocationStart(now)
@@ -72,7 +72,7 @@ func TestGetAndSetLocationStartEnd(t *testing.T) {
func TestGetExplicitDurationInS(t *testing.T) {
t.Run("uses explicit duration if set", func(t *testing.T) {
r := &resources.PricedResource{BookingConfiguration: &resources.BookingConfiguration{
r := &resources.PricedResource[pricing.PricingProfileITF]{BookingConfiguration: &resources.BookingConfiguration{
ExplicitBookingDurationS: 3600,
},
}
@@ -82,7 +82,7 @@ func TestGetExplicitDurationInS(t *testing.T) {
t.Run("computes duration from start and end", func(t *testing.T) {
start := time.Now()
end := start.Add(2 * time.Hour)
r := &resources.PricedResource{
r := &resources.PricedResource[pricing.PricingProfileITF]{
BookingConfiguration: &resources.BookingConfiguration{
UsageStart: &start, UsageEnd: &end,
},
@@ -91,14 +91,14 @@ func TestGetExplicitDurationInS(t *testing.T) {
})
t.Run("defaults to 1 hour when times not set", func(t *testing.T) {
r := &resources.PricedResource{}
r := &resources.PricedResource[pricing.PricingProfileITF]{}
assert.InDelta(t, 3600.0, r.GetExplicitDurationInS(), 0.1)
})
}
func TestGetPriceHT(t *testing.T) {
t.Run("returns error if no pricing profile", func(t *testing.T) {
r := &resources.PricedResource{ResourceID: "no-profile"}
r := &resources.PricedResource[pricing.PricingProfileITF]{ResourceID: "no-profile"}
price, err := r.GetPriceHT()
require.Error(t, err)
assert.Contains(t, err.Error(), "pricing profile must be set")
@@ -107,7 +107,7 @@ func TestGetPriceHT(t *testing.T) {
t.Run("defaults BookingConfiguration when nil", func(t *testing.T) {
mock := &MockPricingProfile{ReturnCost: 42.0}
r := &resources.PricedResource{
r := &resources.PricedResource[pricing.PricingProfileITF]{
SelectedPricing: mock,
}
price, err := r.GetPriceHT()
@@ -119,7 +119,7 @@ func TestGetPriceHT(t *testing.T) {
start := time.Now()
end := start.Add(1 * time.Hour)
mock := &MockPricingProfile{ReturnErr: true}
r := &resources.PricedResource{
r := &resources.PricedResource[pricing.PricingProfileITF]{
SelectedPricing: mock,
BookingConfiguration: &resources.BookingConfiguration{
UsageStart: &start,
@@ -135,7 +135,7 @@ func TestGetPriceHT(t *testing.T) {
start := time.Now()
end := start.Add(1 * time.Hour)
mock := &MockPricingProfile{ReturnCost: 10.0}
r := &resources.PricedResource{
r := &resources.PricedResource[pricing.PricingProfileITF]{
SelectedPricing: mock,
BookingConfiguration: &resources.BookingConfiguration{
UsageStart: &start,

View File

@@ -40,7 +40,7 @@ func TestPricedProcessingResource_GetExplicitDurationInS(t *testing.T) {
{
name: "Nil start time, non-service",
input: PricedProcessingResource{
PricedResource: PricedResource{
PricedResource: PricedResource[*ProcessingResourcePricingProfile]{
BookingConfiguration: &resources.BookingConfiguration{
UsageStart: nil,
},
@@ -51,7 +51,7 @@ func TestPricedProcessingResource_GetExplicitDurationInS(t *testing.T) {
{
name: "Duration computed from start and end",
input: PricedProcessingResource{
PricedResource: PricedResource{
PricedResource: PricedResource[*ProcessingResourcePricingProfile]{
BookingConfiguration: &resources.BookingConfiguration{
UsageStart: &now,
UsageEnd: &after,
@@ -63,7 +63,7 @@ func TestPricedProcessingResource_GetExplicitDurationInS(t *testing.T) {
{
name: "Explicit duration takes precedence",
input: PricedProcessingResource{
PricedResource: PricedResource{
PricedResource: PricedResource[*ProcessingResourcePricingProfile]{
BookingConfiguration: &resources.BookingConfiguration{
ExplicitBookingDurationS: 1337,
},
@@ -96,7 +96,7 @@ func TestProcessingResourcePricingProfile_GetPriceHT(t *testing.T) {
},
}
profile := &ProcessingResourcePricingProfile{AccessPricingProfile: mockPricing}
price, err := profile.GetPriceHT(0, 0, start, end, []*pricing.PricingVariation{})
price, err := profile.GetPriceHT(1, 0, start, end, []*pricing.PricingVariation{})
assert.NoError(t, err)
assert.Equal(t, 100.0, price)
}

View File

@@ -81,8 +81,8 @@ func TestGetSelectedInstance_NoIndex(t *testing.T) {
}
func TestCanUpdate_WhenOnlyStateDiffers(t *testing.T) {
resource := &resources.AbstractResource{AbstractObject: utils.AbstractObject{IsDraft: false}}
set := &MockDBObject{isDraft: true}
resource := &resources.AbstractResource{AbstractObject: utils.AbstractObject{IsDraft: true}}
set := &MockDBObject{isDraft: false}
canUpdate, updated := resource.CanUpdate(set)
assert.True(t, canUpdate)
assert.Equal(t, set, updated)
@@ -105,8 +105,13 @@ type FakeResource struct {
resources.AbstractInstanciatedResource[*MockInstance]
}
func (f *FakeResource) SetAllowedInstances(*tools.APIRequest, ...string) {}
func (f *FakeResource) VerifyAuth(string, *tools.APIRequest) bool { return true }
func (f *FakeResource) SetAllowedInstances(req *tools.APIRequest, instance_id ...string) []resources.ResourceInstanceITF {
return nil
}
func (f *FakeResource) ConvertToPricedResource(t tools.DataType, a *int, b *int, c *int, d *int, e *int, req *tools.APIRequest) (pricing.PricedItemITF, error) {
return nil, nil
}
func (f *FakeResource) VerifyAuth(string, *tools.APIRequest) bool { return true }
func TestNewAccessor_ReturnsValid(t *testing.T) {
acc := resources.NewAccessor[*FakeResource](tools.COMPUTE_RESOURCE, &tools.APIRequest{}, func() utils.DBObject {

View File

@@ -96,7 +96,7 @@ func TestStorageResourcePricingStrategy_GetQuantity_Invalid(t *testing.T) {
func TestPricedStorageResource_GetPriceHT_NoProfiles(t *testing.T) {
res := &resources.PricedStorageResource{
PricedResource: resources.PricedResource{
PricedResource: resources.PricedResource[*resources.StorageResourcePricingProfile]{
ResourceID: "res-id",
},
}

View File

@@ -30,8 +30,9 @@ func (d *WorkflowResource) ClearEnv() utils.DBObject {
return d
}
func (w *WorkflowResource) SetAllowedInstances(request *tools.APIRequest, ids ...string) {
func (w *WorkflowResource) SetAllowedInstances(request *tools.APIRequest, ids ...string) []ResourceInstanceITF {
/* EMPTY */
return []ResourceInstanceITF{}
}
func (r *WorkflowResource) GetSelectedInstance(selected *int) ResourceInstanceITF {
@@ -39,7 +40,7 @@ func (r *WorkflowResource) GetSelectedInstance(selected *int) ResourceInstanceIT
}
func (w *WorkflowResource) ConvertToPricedResource(t tools.DataType, selectedInstance *int, selectedPartnership *int, selectedBuyingStrategy *int, selectedStrategy *int, selectedBookingModeIndex *int, request *tools.APIRequest) (pricing.PricedItemITF, error) {
return &PricedResource{
return &PricedResource[*pricing.ExploitPricingProfile[pricing.TimePricingStrategy]]{
Name: w.Name,
Logo: w.Logo,
ResourceID: w.UUID,