Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 31 additions & 0 deletions admin/canvases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package admin

import (
"context"

"github.com/rilldata/rill/admin/database"
runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1"
"github.com/rilldata/rill/runtime"
)

// LookupCanvas fetches a canvas's spec from a runtime deployment.
func (s *Service) LookupCanvas(ctx context.Context, depl *database.Deployment, canvasName string) (*runtimev1.CanvasSpec, error) {
rt, err := s.OpenRuntimeClient(depl)
if err != nil {
return nil, err
}
defer rt.Close()

res, err := rt.GetResource(ctx, &runtimev1.GetResourceRequest{
InstanceId: depl.RuntimeInstanceID,
Name: &runtimev1.ResourceName{
Kind: runtime.ResourceKindCanvas,
Name: canvasName,
},
})
if err != nil {
return nil, err
}

return res.Resource.Resource.(*runtimev1.Resource_Canvas).Canvas.Spec, nil
}
8 changes: 6 additions & 2 deletions admin/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ type DB interface {

FindVirtualFiles(ctx context.Context, projectID, environment string, afterUpdatedOn time.Time, afterPath string, limit int) ([]*VirtualFile, error)
FindVirtualFile(ctx context.Context, projectID, environment, path string) (*VirtualFile, error)
// FindVirtualFilesByOwner TODO: pagination
FindVirtualFilesByOwner(ctx context.Context, projectID, environment, ownerID string) ([]*VirtualFile, error)
UpsertVirtualFile(ctx context.Context, opts *InsertVirtualFileOptions) error
UpdateVirtualFileDeleted(ctx context.Context, projectID, environment, path string) error
DeleteExpiredVirtualFiles(ctx context.Context, retention time.Duration) error
Expand Down Expand Up @@ -1225,6 +1227,7 @@ type UpdateBookmarkOptions struct {
type VirtualFile struct {
Path string `db:"path"`
Data []byte `db:"data"`
OwnerID *string `db:"owner_id"`
Deleted bool `db:"deleted"`
UpdatedOn time.Time `db:"updated_on"`
}
Expand All @@ -1233,8 +1236,9 @@ type VirtualFile struct {
type InsertVirtualFileOptions struct {
ProjectID string
Environment string
Path string `validate:"required"`
Data []byte `validate:"max=131072"` // 128kb
Path string `validate:"required"`
OwnerID *string `db:"owner_id"`
Data []byte `validate:"max=131072"` // 128kb
}

// Asset represents a user-uploaded file asset.
Expand Down
1 change: 1 addition & 0 deletions admin/database/postgres/migrations/0094.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE virtual_files ADD COLUMN owner_id UUID;
25 changes: 20 additions & 5 deletions admin/database/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -2882,7 +2882,7 @@ func (c *connection) DeleteBookmark(ctx context.Context, bookmarkID string) erro
func (c *connection) FindVirtualFiles(ctx context.Context, projectID, environment string, afterUpdatedOn time.Time, afterPath string, limit int) ([]*database.VirtualFile, error) {
var res []*database.VirtualFile
err := c.getDB(ctx).SelectContext(ctx, &res, `
SELECT path, data, deleted, updated_on
SELECT path, data, owner_id, deleted, updated_on
FROM virtual_files
WHERE project_id=$1 AND environment=$2 AND (updated_on>$3 OR updated_on=$3 AND path>$4)
ORDER BY updated_on, path LIMIT $5
Expand All @@ -2896,7 +2896,7 @@ func (c *connection) FindVirtualFiles(ctx context.Context, projectID, environmen
func (c *connection) FindVirtualFile(ctx context.Context, projectID, environment, path string) (*database.VirtualFile, error) {
res := &database.VirtualFile{}
err := c.getDB(ctx).QueryRowxContext(ctx, `
SELECT path, data, deleted, updated_on
SELECT path, data, owner_id, deleted, updated_on
FROM virtual_files
WHERE project_id=$1 AND environment=$2 AND path=$3
`, projectID, environment, path).StructScan(res)
Expand All @@ -2906,19 +2906,34 @@ func (c *connection) FindVirtualFile(ctx context.Context, projectID, environment
return res, nil
}

func (c *connection) FindVirtualFilesByOwner(ctx context.Context, projectID, environment, ownerID string) ([]*database.VirtualFile, error) {
var res []*database.VirtualFile
err := c.getDB(ctx).SelectContext(ctx, &res, `
SELECT path, data, owner_id, deleted, updated_on
FROM virtual_files
WHERE project_id=$1 AND environment=$2 AND deleted=FALSE AND owner_id=$3
ORDER BY path
`, projectID, environment, ownerID)
if err != nil {
return nil, parseErr("virtual files", err)
}
return res, nil
}

func (c *connection) UpsertVirtualFile(ctx context.Context, opts *database.InsertVirtualFileOptions) error {
if err := database.Validate(opts); err != nil {
return err
}

_, err := c.getDB(ctx).ExecContext(ctx, `
INSERT INTO virtual_files (project_id, environment, path, data, deleted)
VALUES ($1, $2, $3, $4, FALSE)
INSERT INTO virtual_files (project_id, environment, owner_id, path, data, deleted)
VALUES ($1, $2, $3, $4, $5, FALSE)
ON CONFLICT (project_id, environment, path) DO UPDATE SET
data = EXCLUDED.data,
owner_id = EXCLUDED.owner_id,
deleted = FALSE,
updated_on = now()
`, opts.ProjectID, opts.Environment, opts.Path, opts.Data)
`, opts.ProjectID, opts.Environment, opts.OwnerID, opts.Path, opts.Data)
if err != nil {
return parseErr("virtual file", err)
}
Expand Down
48 changes: 4 additions & 44 deletions admin/server/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
"errors"
"fmt"
"path"
"regexp"
"slices"
"strconv"
"strings"
"time"

"github.com/google/uuid"
"github.com/rilldata/rill/admin"
"github.com/rilldata/rill/admin/database"
"github.com/rilldata/rill/admin/server/auth"
Expand Down Expand Up @@ -155,17 +153,17 @@
return nil, err
}

name, err := s.generateAlertName(ctx, depl, req.Options.DisplayName)
if err != nil {
return nil, err
}
name, err := s.generateVirtualFileName(ctx, req.Options.DisplayName, func(ctx context.Context, name string) error {

Check failure on line 156 in admin/server/alerts.go

View workflow job for this annotation

GitHub Actions / lint

ineffectual assignment to err (ineffassign)
_, err := s.admin.LookupAlert(ctx, depl, name)
return err
})

data, err := s.yamlForManagedAlert(req.Options, claims.OwnerID())
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to generate alert YAML: %s", err.Error())
}

err = s.admin.DB.UpsertVirtualFile(ctx, &database.InsertVirtualFileOptions{

Check failure on line 166 in admin/server/alerts.go

View workflow job for this annotation

GitHub Actions / lint

database.InsertVirtualFileOptions is missing field OwnerID (exhaustruct)
ProjectID: proj.ID,
Environment: "prod",
Path: virtualFilePathForManagedAlert(name),
Expand Down Expand Up @@ -235,7 +233,7 @@
return nil, status.Errorf(codes.InvalidArgument, "failed to generate alert YAML: %s", err.Error())
}

err = s.admin.DB.UpsertVirtualFile(ctx, &database.InsertVirtualFileOptions{

Check failure on line 236 in admin/server/alerts.go

View workflow job for this annotation

GitHub Actions / lint

database.InsertVirtualFileOptions is missing field OwnerID (exhaustruct)
ProjectID: proj.ID,
Environment: "prod",
Path: virtualFilePathForManagedAlert(req.Name),
Expand Down Expand Up @@ -379,7 +377,7 @@
return nil, status.Errorf(codes.InvalidArgument, "failed to generate alert YAML: %s", err.Error())
}

err = s.admin.DB.UpsertVirtualFile(ctx, &database.InsertVirtualFileOptions{

Check failure on line 380 in admin/server/alerts.go

View workflow job for this annotation

GitHub Actions / lint

database.InsertVirtualFileOptions is missing field OwnerID (exhaustruct)
ProjectID: proj.ID,
Environment: "prod",
Path: virtualFilePathForManagedAlert(req.Name),
Expand Down Expand Up @@ -587,44 +585,6 @@
return yaml.Marshal(res)
}

// generateAlertName generates a random alert name with the display name as a seed.
// Example: "My alert!" -> "my-alert-5b3f7e1a".
// It verifies that the name is not taken (the random component makes any collision unlikely, but we check to be sure).
func (s *Server) generateAlertName(ctx context.Context, depl *database.Deployment, displayName string) (string, error) {
for i := 0; i < 5; i++ {
name := randomAlertName(displayName)

_, err := s.admin.LookupAlert(ctx, depl, name)
if err != nil {
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
// Success! Name isn't taken
return name, nil
}
return "", fmt.Errorf("failed to check alert name: %w", err)
}
}

// Fail-safe in case all names we tried were taken
return uuid.New().String(), nil
}

var alertNameToDashCharsRegexp = regexp.MustCompile(`[ _]+`)

var alertNameExcludeCharsRegexp = regexp.MustCompile(`[^a-zA-Z0-9-]+`)

func randomAlertName(displayName string) string {
name := alertNameToDashCharsRegexp.ReplaceAllString(displayName, "-")
name = alertNameExcludeCharsRegexp.ReplaceAllString(name, "")
name = strings.ToLower(name)
name = strings.Trim(name, "-")
if name == "" {
name = uuid.New().String()
} else {
name = name + "-" + uuid.New().String()[0:8]
}
return name
}

// alertYAML is derived from runtime/parser.AlertYAML, but adapted for generating (as opposed to parsing) the alert YAML.
type alertYAML struct {
Type string `yaml:"type"`
Expand Down
45 changes: 4 additions & 41 deletions admin/server/reports.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ import (
"errors"
"fmt"
"path"
"regexp"
"strconv"
"strings"
"time"

"github.com/google/uuid"
"github.com/rilldata/rill/admin"
"github.com/rilldata/rill/admin/database"
"github.com/rilldata/rill/admin/server/auth"
Expand Down Expand Up @@ -202,7 +200,10 @@ func (s *Server) CreateReport(ctx context.Context, req *adminv1.CreateReportRequ
return nil, err
}

name, err := s.generateReportName(ctx, depl, req.Options.DisplayName)
name, err := s.generateVirtualFileName(ctx, req.Options.DisplayName, func(ctx context.Context, name string) error {
_, err := s.admin.LookupReport(ctx, depl, name)
return err
})
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -644,27 +645,6 @@ func (s *Server) yamlForCommittedReport(opts *adminv1.ReportOptions) ([]byte, er
return yaml.Marshal(res)
}

// generateReportName generates a random report name with the display name as a seed.
// Example: "My report!" -> "my-report-5b3f7e1a".
// It verifies that the name is not taken (the random component makes any collision unlikely, but we check to be sure).
func (s *Server) generateReportName(ctx context.Context, depl *database.Deployment, displayName string) (string, error) {
for i := 0; i < 5; i++ {
name := randomReportName(displayName)

_, err := s.admin.LookupReport(ctx, depl, name)
if err != nil {
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
// Success! Name isn't taken
return name, nil
}
return "", fmt.Errorf("failed to check report name: %w", err)
}
}

// Fail-safe in case all names we tried were taken
return uuid.New().String(), nil
}

func (s *Server) createMagicTokens(ctx context.Context, orgID, projectID, reportName, ownerID string, emails []string, resources []*adminv1.ResourceName) (map[string]string, error) {
var createdByUserID *string
if ownerID != "" {
Expand Down Expand Up @@ -842,23 +822,6 @@ func (s *Server) getAttributesForProjectMember(ctx context.Context, email, orgID
return attr, id, nil
}

var reportNameToDashCharsRegexp = regexp.MustCompile(`[ _]+`)

var reportNameExcludeCharsRegexp = regexp.MustCompile(`[^a-zA-Z0-9-]+`)

func randomReportName(displayName string) string {
name := reportNameToDashCharsRegexp.ReplaceAllString(displayName, "-")
name = reportNameExcludeCharsRegexp.ReplaceAllString(name, "")
name = strings.ToLower(name)
name = strings.Trim(name, "-")
if name == "" {
name = uuid.New().String()
} else {
name = name + "-" + uuid.New().String()[0:8]
}
return name
}

// reportYAML is derived from runtime/parser.ReportYAML, but adapted for generating (as opposed to parsing) the report YAML.
type reportYAML struct {
Type string `yaml:"type"`
Expand Down
Loading
Loading