This commit is contained in:
mr
2026-05-28 08:15:02 +02:00
parent 3be023b9af
commit 55c1b70064
7 changed files with 97 additions and 19 deletions
+62 -12
View File
@@ -9,6 +9,7 @@ import (
oclib "cloud.o-forge.io/core/oc-lib"
"cloud.o-forge.io/core/oc-lib/dbs"
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/peer"
beego "github.com/beego/beego/v2/server/web"
"github.com/gorilla/websocket"
"go.mongodb.org/mongo-driver/bson/primitive"
@@ -21,41 +22,77 @@ type BookingController struct {
var BookingExample booking.Booking
// bookingDestFilter builds a dest_peer_id filter for booking queries.
// Default: filter by self (master's own bookings).
// With nanoID: verify the ID belongs to a NANO peer, then filter by it.
// Returns nil when nanoID is provided but does not resolve to a NANO peer
// (caller should return HTTP 404 or empty result).
func bookingDestFilter(nanoID string) *dbs.Filters {
targetID := ""
if nanoID != "" {
d := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil).Search(&dbs.Filters{
And: map[string][]dbs.Filter{
"id": {{Operator: dbs.EQUAL.String(), Value: nanoID}},
"relation": {{Operator: dbs.EQUAL.String(), Value: peer.NANO}},
},
}, "", false, 0, 1)
if len(d.Data) == 0 {
return nil
}
targetID = nanoID
} else {
if self, err := oclib.GetMySelf(); err == nil && self != nil {
targetID = self.GetID()
}
}
if targetID == "" {
return nil
}
return &dbs.Filters{
And: map[string][]dbs.Filter{
"dest_peer_id": {{Operator: dbs.EQUAL.String(), Value: targetID}},
},
}
}
// @Title Search
// @Description search bookings
// @Param start_date path string true "the word search you want to get"
// @Param end_date path string true "the word search you want to get"
// @Param is_draft query string false "draft wished"
// @Param nano_id query string false "nano peer UUID — if set, return bookings for that nano instead of self"
// @Param offset query string false
// @Param limit query string false
// @Success 200 {workspace} models.workspace
// @router /search/:start_date/:end_date [get]
func (o *BookingController) Search() {
/*
* This is a sample of how to use the search function
* The search function is used to search for data in the database
* The search function takes in a filter and a data type
* The filter is a struct that contains the search parameters
* The data type is an enum that specifies the type of data to search for
* The search function returns a list of data that matches the filter
* The data is then returned as a json object
*/
// store and return Id or post with UUID
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset"))
limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit"))
start_date, _ := time.ParseInLocation("2006-01-02", o.Ctx.Input.Param(":start_date"), time.UTC)
end_date, _ := time.ParseInLocation("2006-01-02", o.Ctx.Input.Param(":end_date"), time.UTC)
isDraft := o.Ctx.Input.Query("is_draft")
nanoID := o.Ctx.Input.Query("nano_id")
sd := primitive.NewDateTimeFromTime(start_date)
ed := primitive.NewDateTimeFromTime(end_date)
fmt.Println("SEARCH START END", start_date, end_date)
df := bookingDestFilter(nanoID)
if nanoID != "" && df == nil {
o.Ctx.Output.SetStatus(http.StatusNotFound)
o.Data["json"] = map[string]string{"error": "nano peer not found: " + nanoID}
o.ServeJSON()
return
}
f := dbs.Filters{
And: map[string][]dbs.Filter{
"expected_start_date": {{Operator: "gte", Value: sd}, {Operator: "lte", Value: ed}},
},
}
if df != nil {
for k, v := range df.And {
f.And[k] = v
}
}
o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), user, peerID, groups, nil).Search(&f, "", isDraft == "true", int64(offset), int64(limit))
o.ServeJSON()
}
@@ -63,6 +100,7 @@ func (o *BookingController) Search() {
// @Title GetAll
// @Description find booking by id
// @Param is_draft query string false "draft wished"
// @Param nano_id query string false "nano peer UUID — if set, return bookings for that nano instead of self"
// @Param offset query string false
// @Param limit query string false
// @Success 200 {booking} models.booking
@@ -72,7 +110,19 @@ func (o *BookingController) GetAll() {
offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset"))
limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit"))
isDraft := o.Ctx.Input.Query("is_draft")
o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), user, peerID, groups, nil).LoadAll(isDraft == "true", int64(offset), int64(limit))
nanoID := o.Ctx.Input.Query("nano_id")
df := bookingDestFilter(nanoID)
if nanoID != "" && df == nil {
o.Ctx.Output.SetStatus(http.StatusNotFound)
o.Data["json"] = map[string]string{"error": "nano peer not found: " + nanoID}
o.ServeJSON()
return
}
if df != nil {
o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), user, peerID, groups, nil).Search(df, "", isDraft == "true", int64(offset), int64(limit))
} else {
o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.BOOKING), user, peerID, groups, nil).LoadAll(isDraft == "true", int64(offset), int64(limit))
}
o.ServeJSON()
}
+1 -1
View File
@@ -3,7 +3,7 @@ module oc-scheduler
go 1.25.0
require (
cloud.o-forge.io/core/oc-lib v0.0.0-20260429050913-47d487ea8011
cloud.o-forge.io/core/oc-lib v0.0.0-20260527135023-cef23b5f307b
github.com/beego/beego/v2 v2.3.8
github.com/google/uuid v1.6.0
github.com/robfig/cron v1.2.0
+2
View File
@@ -6,6 +6,8 @@ cloud.o-forge.io/core/oc-lib v0.0.0-20260428065508-e3fbe7688ad5 h1:CVwlE1JgIcTAv
cloud.o-forge.io/core/oc-lib v0.0.0-20260428065508-e3fbe7688ad5/go.mod h1:JynnOb3eMr9VZW1mHq+Vsl3tzx6gPhPsGKpQD/dtEBc=
cloud.o-forge.io/core/oc-lib v0.0.0-20260429050913-47d487ea8011 h1:owV5pQ+mS5xDCKEcGTO+BgsyYrKjkISL8LDsmjEb/3s=
cloud.o-forge.io/core/oc-lib v0.0.0-20260429050913-47d487ea8011/go.mod h1:JynnOb3eMr9VZW1mHq+Vsl3tzx6gPhPsGKpQD/dtEBc=
cloud.o-forge.io/core/oc-lib v0.0.0-20260527135023-cef23b5f307b h1:TWhmHeurbBmdyevREh4+mHWOBehO2AK587RCIjCfvOc=
cloud.o-forge.io/core/oc-lib v0.0.0-20260527135023-cef23b5f307b/go.mod h1:JynnOb3eMr9VZW1mHq+Vsl3tzx6gPhPsGKpQD/dtEBc=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+2 -2
View File
@@ -82,7 +82,7 @@ func handleCreateResource(resp tools.NATSResponse) {
if bk.FromNano != "" {
access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil)
pp := access.LoadOne(bk.FromNano)
if p := pp.ToPeer(); p == nil || p.Relation == peer.NANO {
if p := pp.ToPeer(); p == nil || p.Relation != peer.NANO {
return
}
access = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.BOOKING), nil)
@@ -107,7 +107,7 @@ func handleCreateResource(resp tools.NATSResponse) {
if pr.FromNano != "" {
access := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil)
pp := access.LoadOne(pr.FromNano)
if p := pp.ToPeer(); p == nil || p.Relation == peer.NANO {
if p := pp.ToPeer(); p == nil || p.Relation != peer.NANO {
return
}
access = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PURCHASE_RESOURCE), nil)
+2
View File
@@ -226,6 +226,8 @@ func (ws *WorkflowSchedule) GenerateExecutions(wf *workflow.Workflow, isPreempti
SelectedPartnerships: ws.SelectedPartnerships,
SelectedBuyings: ws.SelectedBuyings,
SelectedStrategies: ws.SelectedStrategies,
SelectedPaymentMode: ws.SelectedPaymentType,
SelectedBillingStrategy: ws.SelectedBillingStrategy,
SelectedEmbeddedStorages: ws.SelectedEmbeddedStorages,
Priority: 1,
ExecutionsID: ws.UUID,
@@ -356,6 +356,25 @@ func (s *SchedulingResourcesService) HandleCreateBooking(bk *booking.Booking, ad
return !bk.IsDraft
}
// Booking destined for a known NANO — store as local proxy without planner
// check (the resource belongs to the NANO, not us). oc-discovery will
// forward to the NANO via a DTN-critical stream.
if bk.DestPeerID != "" && bk.DestPeerID != self.GetID() {
d := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.PEER), nil).Search(&dbs.Filters{
And: map[string][]dbs.Filter{
"id": {{Operator: dbs.EQUAL.String(), Value: bk.DestPeerID}},
"relation": {{Operator: dbs.EQUAL.String(), Value: peer.NANO}},
},
}, "", false, 0, 1)
if len(d.Data) > 0 {
bk.IsDraft = true
if stored, _, err := booking.NewAccessor(adminReq).StoreOne(bk); err == nil {
time.AfterFunc(10*time.Minute, func() { DraftTimeout(stored.GetID(), tools.BOOKING) })
}
}
return false
}
// New booking
if !bk.ExpectedStartDate.IsZero() && bk.ExpectedStartDate.Before(time.Now().UTC()) {
fmt.Println("HandleCreateBooking: start date in the past, discarding")
+9 -4
View File
@@ -9,9 +9,10 @@ import (
"time"
"cloud.o-forge.io/core/oc-lib/dbs"
"cloud.o-forge.io/core/oc-lib/models/bill"
"cloud.o-forge.io/core/oc-lib/models/billing"
"cloud.o-forge.io/core/oc-lib/models/booking"
"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/resources/purchase_resource"
"cloud.o-forge.io/core/oc-lib/models/utils"
@@ -261,6 +262,7 @@ func GenerateOrder(
ExecutionsID: executionsID,
Purchases: []*purchase_resource.PurchaseResource{},
Bookings: []*booking.Booking{},
Billing: map[pricing.BillingStrategy][]*booking.Booking{},
Status: enum.PENDING,
}
for _, purch := range purchases {
@@ -268,14 +270,17 @@ func GenerateOrder(
scheduling_resources.FromSchedulerObject(tools.PURCHASE_RESOURCE, purch).(*purchase_resource.PurchaseResource))
}
for _, b := range bookings {
newOrder.Bookings = append(newOrder.Bookings,
scheduling_resources.FromSchedulerObject(tools.BOOKING, b).(*booking.Booking))
bk := scheduling_resources.FromSchedulerObject(tools.BOOKING, b).(*booking.Booking)
newOrder.Bookings = append(newOrder.Bookings, bk)
// Groupe les bookings par stratégie de facturation pour permettre
// à DraftFirstBill de savoir quand et comment facturer chaque item.
newOrder.Billing[bk.BillingStrategy] = append(newOrder.Billing[bk.BillingStrategy], bk)
}
res, _, err := order.NewAccessor(request).StoreOne(newOrder)
if err != nil {
return "", err
}
if _, err := bill.DraftFirstBill(res.(*order.Order), request); err != nil {
if _, err := billing.DraftFirstBill(res.(*order.Order), request); err != nil {
return res.GetID(), err
}
return res.GetID(), nil