diff --git a/models/bill/bill.go b/models/bill/bill.go index b73d5aa..0442cd5 100644 --- a/models/bill/bill.go +++ b/models/bill/bill.go @@ -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 { diff --git a/models/resources/compute.go b/models/resources/compute.go index e40fa23..0f8067b 100755 --- a/models/resources/compute.go +++ b/models/resources/compute.go @@ -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 diff --git a/models/resources/data.go b/models/resources/data.go index ce9aa0f..25bf83c 100755 --- a/models/resources/data.go +++ b/models/resources/data.go @@ -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"` } diff --git a/models/resources/interfaces.go b/models/resources/interfaces.go index 8a2f855..68b80c2 100755 --- a/models/resources/interfaces.go +++ b/models/resources/interfaces.go @@ -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 } diff --git a/models/resources/native_tools.go b/models/resources/native_tools.go index fdfc435..6f18ba8 100644 --- a/models/resources/native_tools.go +++ b/models/resources/native_tools.go @@ -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, diff --git a/models/resources/priced_resource.go b/models/resources/priced_resource.go index 8d0d3de..a471748 100755 --- a/models/resources/priced_resource.go +++ b/models/resources/priced_resource.go @@ -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) diff --git a/models/resources/processing.go b/models/resources/processing.go index 1d4d3bf..d813797 100755 --- a/models/resources/processing.go +++ b/models/resources/processing.go @@ -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 } diff --git a/models/resources/resource.go b/models/resources/resource.go index e1c255c..86511e3 100755 --- a/models/resources/resource.go +++ b/models/resources/resource.go @@ -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 { diff --git a/models/resources/storage.go b/models/resources/storage.go index 0b6ff1e..aa8a694 100755 --- a/models/resources/storage.go +++ b/models/resources/storage.go @@ -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"` } diff --git a/models/resources/tests/compute_test.go b/models/resources/tests/compute_test.go index 1e1a8b2..9ea7330 100644 --- a/models/resources/tests/compute_test.go +++ b/models/resources/tests/compute_test.go @@ -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", }, } diff --git a/models/resources/tests/data_test.go b/models/resources/tests/data_test.go index e8be97f..aaa6751 100644 --- a/models/resources/tests/data_test.go +++ b/models/resources/tests/data_test.go @@ -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", }, } diff --git a/models/resources/tests/priced_resource_test.go b/models/resources/tests/priced_resource_test.go index 72e3140..57f66e0 100644 --- a/models/resources/tests/priced_resource_test.go +++ b/models/resources/tests/priced_resource_test.go @@ -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, diff --git a/models/resources/tests/processing_test.go b/models/resources/tests/processing_test.go index 59ba5aa..6895b5a 100644 --- a/models/resources/tests/processing_test.go +++ b/models/resources/tests/processing_test.go @@ -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) } diff --git a/models/resources/tests/resource_test.go b/models/resources/tests/resource_test.go index 83f5239..db50c8f 100644 --- a/models/resources/tests/resource_test.go +++ b/models/resources/tests/resource_test.go @@ -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 { diff --git a/models/resources/tests/storage_test.go b/models/resources/tests/storage_test.go index 22da67e..11a82d0 100644 --- a/models/resources/tests/storage_test.go +++ b/models/resources/tests/storage_test.go @@ -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", }, } diff --git a/models/resources/workflow.go b/models/resources/workflow.go index 6ee6018..b8045d0 100755 --- a/models/resources/workflow.go +++ b/models/resources/workflow.go @@ -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,