Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
cd91c26
Remove unused python stuff, we have a new client python repo now
May 12, 2025
ea871e9
Added client rate limiter (with test), scan diffraction data cache, d…
May 12, 2025
f417fd1
Added missing rate limit files, allow client lib to connect to local …
May 18, 2025
6125daf
Added deleteImage, fixed bugs in diffraction map func, loadMap, finis…
May 21, 2025
bc04a2c
Fix client lib compile
May 21, 2025
76974ef
Merge branch 'development' into feature/client
May 21, 2025
85c0c62
Merge pull request #424 from pixlise/feature/client
pnemere May 21, 2025
7f1d183
Added getTag client library function
May 22, 2025
7bd3266
Fixing crash if memoisation delete fails
May 22, 2025
9ce8702
Added auth0 batch cmd line tool
May 23, 2025
7825510
Added writer user id to memoise item, uses latest data-formats, sends…
May 29, 2025
2cc2543
Send notification for ROI updates in case any UIs are listening
May 29, 2025
a1b3f69
Allows viewers to add other viewers
RyanStonebraker May 29, 2025
ccfcd12
Fix tests
May 30, 2025
a01ee87
Merge pull request #425 from pixlise/bugfix/test
pnemere May 30, 2025
5f13daf
Added image beam location uploading via PIXLISE client library. Inclu…
May 30, 2025
78038f3
Merge pull request #426 from pixlise/feature/auth0-role-batch-tool
pnemere Jun 1, 2025
76b1a03
Merge pull request #427 from pixlise/feature/update-ui-object-changes
pnemere Jun 1, 2025
6097ee3
Merge branch 'development' into feature/client
Jun 1, 2025
c6c2556
Use latest data formats
Jun 1, 2025
4c6db98
Fixing tests
Jun 2, 2025
0cb4c02
Fixing tests
Jun 2, 2025
75d998f
Fixing tests, was not handling ROI notification update
Jun 2, 2025
e296ba7
Merge pull request #428 from pixlise/feature/client
pnemere Jun 2, 2025
52df2ea
Added deleteROI to client so tests could be written that dont rely on…
Jun 2, 2025
1609d33
Added client library function getTagByName
Jun 2, 2025
10b46c3
Fixing typo in data formats
Jun 3, 2025
7f2490c
Merge pull request #429 from pixlise/feature/client
pnemere Jun 3, 2025
bfbd45a
Added AssociatedROIId to ROIItem and ROISummary
Jun 4, 2025
53649b0
Added support for deleting all associated ROIs (in case they were cre…
Jun 4, 2025
4c3196c
Implemented use of NoGC flag in memoised items, and client-side saveM…
Jun 4, 2025
605b276
Added associatedROIId handling for bulk ROI writing
Jun 4, 2025
9dc8223
Fix build
Jun 5, 2025
e2d35f7
Fixed ROI bulk delete using associatedROIId and user permission on bu…
Jun 5, 2025
396c7ba
ROI delete: always returns deleted ids, in single and associated id d…
Jun 5, 2025
ba0a47a
Fix build
Jun 5, 2025
a93a542
Return deleted ROI id in single delete case. Also check bulk roi crea…
Jun 5, 2025
5c4ecbc
Adds widget metadata write and get endpoints
RyanStonebraker Jun 5, 2025
4428102
Fix test
Jun 6, 2025
5b9d4cb
Fix test
Jun 6, 2025
f73ab11
Fix test
Jun 6, 2025
775dcb9
Merge branch 'development' into feature/client
Jun 6, 2025
a81c168
Animate PIXLISE by client library
Jun 6, 2025
30ee8c9
Merge pull request #430 from pixlise/feature/client
pnemere Jun 6, 2025
a896f95
Regen msgs
Jun 6, 2025
d01fe91
Merge pull request #431 from pixlise/feature/client
pnemere Jun 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions api/endpoints/memoisation.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import (
"errors"
"fmt"
"io"
"strings"

"github.com/pixlise/core/v4/api/dbCollections"
apiRouter "github.com/pixlise/core/v4/api/router"
"github.com/pixlise/core/v4/api/ws/wsHelpers"
"github.com/pixlise/core/v4/core/client"
"github.com/pixlise/core/v4/core/errorwithstatus"
"github.com/pixlise/core/v4/core/utils"
protos "github.com/pixlise/core/v4/generated-protos"
Expand Down Expand Up @@ -123,6 +125,8 @@ func PutMemoise(params apiRouter.ApiHandlerGenericParams) error {
return err
}

isClientSavedMap := strings.HasPrefix(key, client.ClientMapKeyPrefix)

// Ensure key is either empty or the same as the key in the query param
if len(reqItem.Key) > 0 && key != reqItem.Key {
return errorwithstatus.MakeBadRequestError(errors.New("Memoisation item key doesn't match query parameter"))
Expand All @@ -147,6 +151,12 @@ func PutMemoise(params apiRouter.ApiHandlerGenericParams) error {
ExprId: reqItem.ExprId,
DataSize: uint32(len(reqItem.Data)),
LastReadTimeUnixSec: timestamp, // Right now this is the last time it was accessed. To be updated in future get calls
MemoWriterUserId: params.UserInfo.UserID,
}

// If we're a client-library side saved map, we don't want this item wiped out by garbage collection!
if isClientSavedMap {
item.NoGC = true
}

result, err := coll.UpdateByID(ctx, reqItem.Key, bson.D{{Key: "$set", Value: item}}, opt)
Expand All @@ -163,5 +173,10 @@ func PutMemoise(params apiRouter.ApiHandlerGenericParams) error {
ts := fmt.Sprintf(`{"timestamp": %v}`, timestamp)
params.Writer.Write([]byte(ts))

// If user just saved a client-side created map, notify that it changed as they may be viewing it in a PIXLISE UI instance
if isClientSavedMap {
params.Svcs.Notifier.SysNotifyMapChanged(key)
}

return nil
}
6 changes: 3 additions & 3 deletions api/memoisation/garbageCollection.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ func collectGarbage(mongoDB *mongo.Database, oldestAllowedSec uint32, ts timesta

ctx := context.TODO()
opts := options.Delete()
filter := bson.M{"lastreadtimeunixsec": bson.M{"$lt": oldestAllowedUnixSec}}
filter := bson.M{"lastreadtimeunixsec": bson.M{"$lt": oldestAllowedUnixSec}, "nogc": false}
coll := mongoDB.Collection(dbCollections.MemoisedItemsName)

delResult, err := coll.DeleteMany(ctx, filter, opts)
if err != nil {
log.Errorf("Memoisation GC delete error: %v", err)
} else {
log.Infof("Memoisation GC deleted %v items", delResult.DeletedCount)
}

log.Infof("Memoisation GC deleted %v items", delResult.DeletedCount)
}
22 changes: 21 additions & 1 deletion api/memoisation/garbageCollection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func Example_CollectGarbage() {

// Insert an item that's too old, and one that's newly accessed
ts := &timestamper.MockTimeNowStamper{
QueuedTimeStamps: []int64{1234567890, 1234567890, 1234567890, 1234567890},
QueuedTimeStamps: []int64{1234567890, 1234567890, 1234567890},
}

now := uint32(ts.GetTimeNowSec())
Expand Down Expand Up @@ -50,12 +50,32 @@ func Example_CollectGarbage() {
_, err = coll.UpdateByID(ctx, item.Key, bson.D{{Key: "$set", Value: item}}, opt)
fmt.Printf("Insert 2: %v\n", err)

item = &protos.MemoisedItem{
Key: "key789",
MemoTimeUnixSec: now - maxAge - 60,
Data: []byte{10, 20, 30},
ScanId: "scan222",
DataSize: uint32(5),
LastReadTimeUnixSec: now - 5,
NoGC: true,
}
_, err = coll.UpdateByID(ctx, item.Key, bson.D{{Key: "$set", Value: item}}, opt)
fmt.Printf("Insert 3: %v\n", err)

log := &logger.StdOutLogger{}
// Should delete one based on time
collectGarbage(db, maxAge, ts, log)

// Should delete the other based on time, no GC should stay
maxAge = 1
collectGarbage(db, maxAge, ts, log)

// Output:
// Insert 1: <nil>
// Insert 2: <nil>
// Insert 3: <nil>
// INFO: Memoisation GC starting...
// INFO: Memoisation GC deleted 1 items
// INFO: Memoisation GC starting...
// INFO: Memoisation GC deleted 1 items
}
22 changes: 22 additions & 0 deletions api/notificationSender/interfaceImplementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,28 @@ func (n *NotificationSender) SysNotifyScanChanged(scanId string) {
n.sendSysNotification(wsSysNotify)
}

func (n *NotificationSender) SysNotifyROIChanged(roiId string) {
wsSysNotify := &protos.NotificationUpd{
Notification: &protos.Notification{
NotificationType: protos.NotificationType_NT_SYS_DATA_CHANGED,
RoiId: roiId,
},
}

n.sendSysNotification(wsSysNotify)
}

func (n *NotificationSender) SysNotifyMapChanged(mapId string) {
wsSysNotify := &protos.NotificationUpd{
Notification: &protos.Notification{
NotificationType: protos.NotificationType_NT_SYS_DATA_CHANGED,
MapId: mapId,
},
}

n.sendSysNotification(wsSysNotify)
}

func (n *NotificationSender) NotifyNewScanImage(scanName string, scanId string, imageName string) {
notifMsg := &protos.NotificationUpd{
Notification: &protos.Notification{
Expand Down
6 changes: 6 additions & 0 deletions api/services/notifierInterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ type INotifier interface {
// NOTE: This does NOT send emails, it's of system-level interest only so UI can update caches as required
SysNotifyScanChanged(scanId string)

// When an ROI has changed
SysNotifyROIChanged(roiId string)

// When a map (created by client library saveMapData function) has changed
SysNotifyMapChanged(mapId string)

// When an image is added to a scan
NotifyNewScanImage(scanName string, scanId string, imageName string)

Expand Down
117 changes: 117 additions & 0 deletions api/ws/handlers/image-beam-location.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/pixlise/core/v4/api/dbCollections"
"github.com/pixlise/core/v4/api/ws/wsHelpers"
"github.com/pixlise/core/v4/core/errorwithstatus"
"github.com/pixlise/core/v4/core/utils"
protos "github.com/pixlise/core/v4/generated-protos"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
Expand Down Expand Up @@ -236,3 +237,119 @@ func HandleImageBeamLocationVersionsReq(req *protos.ImageBeamLocationVersionsReq
BeamVersionPerScan: vers,
}, nil
}

func HandleImageBeamLocationUploadReq(req *protos.ImageBeamLocationUploadReq, hctx wsHelpers.HandlerContext) (*protos.ImageBeamLocationUploadResp, error) {
ctx := context.TODO()

if err := wsHelpers.CheckStringField(&req.ImageName, "ImageName", 1, 255); err != nil {
return nil, err
}

if req.Location == nil {
return nil, fmt.Errorf("Request missing location")
}

// For now, we only allow uploading version 0 so we don't clash with FM versioning
if req.Location.BeamVersion != 0 {
return nil, fmt.Errorf("Currently only version 0 supported for image beam location upload")
}

// Check that it's a valid image name
coll := hctx.Svcs.MongoDB.Collection(dbCollections.ImagesName)
imgResult := coll.FindOne(ctx, bson.M{"_id": req.ImageName})
if imgResult.Err() != nil {
if imgResult.Err() == mongo.ErrNoDocuments {
return nil, errorwithstatus.MakeNotFoundError(req.ImageName)
}
return nil, imgResult.Err()
}

img := protos.ScanImage{}
err := imgResult.Decode(&img)
if err != nil {
return nil, err
}

// NOTE: Once beam location saving works, we will update the "associated scans" list for the image!!

// Check that the scan ID is valid too
coll = hctx.Svcs.MongoDB.Collection(dbCollections.ScansName)
scanResult := coll.FindOne(ctx, bson.M{"_id": req.Location.ScanId})

if scanResult.Err() != nil {
if scanResult.Err() == mongo.ErrNoDocuments {
return nil, errorwithstatus.MakeNotFoundError(req.Location.ScanId)
}
return nil, scanResult.Err()
}

// Now load beam locations if any already stored.
coll = hctx.Svcs.MongoDB.Collection(dbCollections.ImageBeamLocationsName)

// Read what's there now
var dbLocs *protos.ImageLocations

imgBeamResult := coll.FindOne(ctx, bson.M{"_id": req.ImageName})
if imgBeamResult.Err() != nil {
if imgBeamResult.Err() != mongo.ErrNoDocuments {
return nil, imgBeamResult.Err()
}

// Doesn't exist yet, that's OK - we're new, just start with a blank structure
dbLocs = &protos.ImageLocations{
ImageName: req.ImageName,
LocationPerScan: []*protos.ImageLocationsForScan{},
}
} else {
err := imgBeamResult.Decode(&dbLocs)
if err != nil {
return nil, err
}
}

// Add ours to the list. If there's an existing one, overwrite it
added := false
for _, locPerScan := range dbLocs.LocationPerScan {
if locPerScan.ScanId == req.Location.ScanId && locPerScan.BeamVersion == req.Location.BeamVersion {
// Overwriting this one
locPerScan.Instrument = req.Location.Instrument
locPerScan.Locations = req.Location.Locations
added = true
break
}
}

if !added {
// We need to add one
dbLocs.LocationPerScan = append(dbLocs.LocationPerScan, req.Location)
}

// Now we put it back
opt := options.Update().SetUpsert(true)

beamResult, err := coll.UpdateByID(ctx, req.ImageName, bson.D{{Key: "$set", Value: dbLocs}}, opt)
if err != nil {
return nil, err
}

if beamResult.MatchedCount == 0 && beamResult.ModifiedCount == 0 {
hctx.Svcs.Log.Errorf("ImageBeamLocationUploadReq got unexpected image beam location upsert result: %+v", beamResult)
}

// Update the associated scans of the image itself
if !utils.ItemInSlice(req.Location.ScanId, img.AssociatedScanIds) {
// Add it here
img.AssociatedScanIds = append(img.AssociatedScanIds, req.Location.ScanId)

coll := hctx.Svcs.MongoDB.Collection(dbCollections.ImagesName)
imgUpdResult, err := coll.UpdateByID(ctx, req.ImageName, bson.D{{Key: "$set", Value: dbLocs}}, opt)
if err != nil {
return nil, err
}
if imgUpdResult.MatchedCount == 0 && imgUpdResult.ModifiedCount == 0 {
hctx.Svcs.Log.Errorf("ImageBeamLocationUploadReq got unexpected image upsert result: %+v", imgUpdResult)
}
}

return &protos.ImageBeamLocationUploadResp{}, nil
}
16 changes: 12 additions & 4 deletions api/ws/handlers/ownership-access.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,18 @@ func HandleObjectEditAccessReq(req *protos.ObjectEditAccessReq, hctx wsHelpers.H

ctx := context.TODO()

viewOnlyAccess := false

// Determine if we have edit access to the object
owner, err := wsHelpers.CheckObjectAccess(true, req.ObjectId, req.ObjectType, hctx)
if err != nil {
return nil, err
owner, err = wsHelpers.CheckObjectAccess(false, req.ObjectId, req.ObjectType, hctx)
if err != nil {
return nil, err
}

// If we have view access, we can't add editors, but we can add viewers
viewOnlyAccess = true
}

viewerUsers := map[string]bool{}
Expand All @@ -74,7 +82,7 @@ func HandleObjectEditAccessReq(req *protos.ObjectEditAccessReq, hctx wsHelpers.H
}

// Add new ones
if req.AddEditors != nil {
if req.AddEditors != nil && !viewOnlyAccess {
readToMap(req.AddEditors.UserIds, &editorUsers)
readToMap(req.AddEditors.GroupIds, &editorGroups)
}
Expand All @@ -84,12 +92,12 @@ func HandleObjectEditAccessReq(req *protos.ObjectEditAccessReq, hctx wsHelpers.H
}

// Delete ones that need to be deleted
if req.DeleteEditors != nil {
if req.DeleteEditors != nil && !viewOnlyAccess {
deleteFromMap(req.DeleteEditors.UserIds, &editorUsers)
deleteFromMap(req.DeleteEditors.GroupIds, &editorGroups)
}

if req.DeleteViewers != nil {
if req.DeleteViewers != nil && !viewOnlyAccess {
deleteFromMap(req.DeleteViewers.UserIds, &viewerUsers)
deleteFromMap(req.DeleteViewers.GroupIds, &viewerGroups)
}
Expand Down
Loading
Loading