[server][core] Include links

This commit is contained in:
Abhishek Shroff
2025-04-05 18:51:58 +05:30
parent 4db4411f46
commit 059fa1c5a3
9 changed files with 97 additions and 49 deletions
@@ -46,5 +46,6 @@ func ResourceFromFS(r fs.Resource) Resource {
ContentSHA256: r.ContentSHA256(),
Permissions: string(r.Permissions()),
Grants: string(r.Grants()),
Links: string(r.Links()),
}
}
@@ -25,6 +25,7 @@ type Resource struct {
ContentSHA256 string `json:"c_sha256"`
Permissions string `json:"permissions"`
Grants string `json:"grants"`
Links string `json:"links"`
}
type ResourceFull struct {
+15 -18
View File
@@ -9,23 +9,6 @@ import (
"github.com/shroff/phylum/server/internal/core/fs"
)
func formatRow(id string, dir bool, size int, sha256, name, permissions string, deleted bool) string {
sizeStr := " -"
if dir {
sha256 = "- "
} else {
sizeStr = FormatSize(size)
}
if sha256 == "" {
sha256 = "xxxxxxxx"
}
delStr := " "
if deleted {
delStr = "*"
}
return fmt.Sprintf("%s %s %s %s%-24s %s", id, sizeStr, sha256[0:8], delStr, name, permissions)
}
func FormatResourceSummary(r fs.Resource, name string, deleted pgtype.Timestamp) string {
if name == "" {
name = r.Name()
@@ -33,7 +16,21 @@ func FormatResourceSummary(r fs.Resource, name string, deleted pgtype.Timestamp)
if r.Dir() {
name += "/"
}
return formatRow(r.ID().String(), r.Dir(), int(r.ContentLength()), r.ContentSHA256(), name, formatGrantsJson(r.Grants()), deleted != r.Deleted())
sha256 := r.ContentSHA256()
sizeStr := " -"
if r.Dir() {
sha256 = "- "
} else {
sizeStr = FormatSize(r.ContentLength())
}
if sha256 == "" {
sha256 = "xxxxxxxx"
}
delStr := " "
if deleted != r.Deleted() {
delStr = "*"
}
return fmt.Sprintf("%s %s %s %s%-24s %s %s", r.ID().String(), sizeStr, sha256[0:8], delStr, name, formatGrantsJson(r.Grants()), r.Links())
}
func FormatSize(size int) string {
@@ -0,0 +1,7 @@
CREATE VIEW linked_resources AS
SELECT r.*, (SELECT jsonb_agg(l.name) FROM publinks l WHERE l.root = r.id AND l.deleted IS NULL) AS links
FROM resources r;
---- create above / drop below ----
DROP VIEW linked_resources;
+4 -4
View File
@@ -116,7 +116,7 @@ func (f filesystem) createResource(
case ResourceBindConflictResolutionError:
err = ErrResourceNameConflict
case ResourceBindConflictResolutionEnsure:
res, err = f.ChildResourceByName(parent, name)
res, err = f.childResourceByName(parent, name)
if err == nil && res.dir != dir {
err = ErrResourceNameConflict
}
@@ -150,7 +150,7 @@ func (f filesystem) createResource(
}
}
case ResourceBindConflictResolutionOverwrite:
res, err = f.ChildResourceByName(parent, name)
res, err = f.childResourceByName(parent, name)
if err == nil {
deleted = true
if res.dir == dir {
@@ -185,7 +185,7 @@ func (f filesystem) createResource(
}
}
case ResourceBindConflictResolutionDelete:
res, err = f.ChildResourceByName(parent, name)
res, err = f.childResourceByName(parent, name)
if err == nil {
deleted = true
err = f.deleteRecursive(res.id, parent, true, false)
@@ -229,6 +229,6 @@ func (f filesystem) insertResource(id, parent uuid.UUID, name string, dir bool,
if rows, err := f.db.Query(query, args...); err != nil {
return Resource{}, err
} else {
return f.collectResource(rows)
return f.collectResource(rows, false)
}
}
+7 -6
View File
@@ -23,7 +23,8 @@ func (f filesystem) ResourceByPath(path string) (Resource, error) {
Where(resources.Col("id").Eq(goqu.V(f.rootID))).
UnionAll(sub)
q := pg.Select(resources.All()).
l := goqu.T("publinks").As("l")
q := pg.Select(resources.All(), pg.Select(goqu.L("jsonb_agg(?)", l.Col("name"))).From(l).Where(l.Col("root").Eq(resources.Col("id")), l.Col("deleted").IsNull())).
From(resources).
WithRecursive("nodes(id, parent, search, depth)", rec).
Join(nodes, goqu.On(resources.Col("id").Eq(nodes.Col("id")))).
@@ -34,25 +35,25 @@ func (f filesystem) ResourceByPath(path string) (Resource, error) {
if rows, err := f.db.Query(query, args...); err != nil {
return Resource{}, err
} else {
return f.collectResource(rows)
return f.collectResource(rows, true)
}
}
func (f filesystem) ResourceByID(id uuid.UUID) (Resource, error) {
const query = "SELECT * FROM resources WHERE id = $1::UUID"
const query = "SELECT r.*, (SELECT jsonb_agg(l.name) FROM publinks l WHERE l.root = r.id AND l.deleted IS NULL) AS links FROM resources r WHERE id = $1::UUID"
if rows, err := f.db.Query(query, id); err != nil {
return Resource{}, err
} else {
return f.collectResource(rows)
return f.collectResource(rows, true)
}
}
func (f filesystem) ChildResourceByName(parentID uuid.UUID, name string) (Resource, error) {
func (f filesystem) childResourceByName(parentID uuid.UUID, name string) (Resource, error) {
const query = "SELECT * FROM resources WHERE parent = $1::UUID AND name = $2::TEXT AND deleted IS NULL"
if rows, err := f.db.Query(query, parentID, name); err != nil {
return Resource{}, err
} else {
return f.collectResource(rows)
return f.collectResource(rows, false)
}
}
+59 -2
View File
@@ -1,6 +1,7 @@
package fs
import (
"encoding/json"
"time"
"github.com/google/uuid"
@@ -22,6 +23,7 @@ type Resource struct {
contentSHA256 string
permissions []byte
grants []byte
links []byte
userPermission Permission
}
@@ -37,13 +39,18 @@ func (r Resource) ContentSHA256() string { return r.contentSHA256 }
func (r Resource) ContentType() string { return r.contentType }
func (r Resource) Permissions() []byte { return r.permissions }
func (r Resource) Grants() []byte { return r.grants }
func (r Resource) Links() []byte { return r.links }
func (r Resource) hasPermission(p Permission) bool {
return r.userPermission&p != 0
}
func (f filesystem) collectResource(rows pgx.Rows) (Resource, error) {
if r, err := pgx.CollectExactlyOneRow(rows, f.scanResource); err == nil {
func (f filesystem) collectResource(rows pgx.Rows, includeLinks bool) (Resource, error) {
scanner := f.scanLinkedResource
if !includeLinks {
scanner = f.scanResource
}
if r, err := pgx.CollectExactlyOneRow(rows, scanner); err == nil {
return r, nil
} else {
if err == pgx.ErrNoRows {
@@ -54,6 +61,41 @@ func (f filesystem) collectResource(rows pgx.Rows) (Resource, error) {
}
func (f filesystem) scanLinkedResource(row pgx.CollectableRow) (Resource, error) {
r := Resource{f: f}
err := row.Scan(
&r.id,
&r.name,
&r.parentID,
&r.dir,
&r.created,
&r.modified,
&r.deleted,
&r.contentLength,
&r.contentType,
&r.contentSHA256,
&r.permissions,
&r.grants,
&r.links,
)
if err != nil {
return r, err
}
permission := Permission(0)
if f.fullAccess {
permission = -1
} else if p, err := readPermissionFromJson(r.permissions, f.username); err != nil {
return Resource{}, err
} else {
permission = p
}
if permission&PermissionRead == 0 {
return Resource{}, ErrResourceNotFound
}
r.userPermission = permission
return r, err
}
func (f filesystem) scanResource(row pgx.CollectableRow) (Resource, error) {
r := Resource{f: f}
err := row.Scan(
@@ -87,3 +129,18 @@ func (f filesystem) scanResource(row pgx.CollectableRow) (Resource, error) {
r.userPermission = permission
return r, err
}
func readPermissionFromJson(j []byte, username string) (Permission, error) {
if j == nil {
return PermissionNone, nil
}
p := make(map[string]Permission)
err := json.Unmarshal(j, &p)
if err != nil {
return PermissionNone, err
}
if p, ok := p[username]; ok {
return p, nil
}
return PermissionNone, nil
}
+3 -3
View File
@@ -18,7 +18,7 @@ func (r Resource) DeleteChildRecursive(name string) (Resource, error) {
return Resource{}, ErrInsufficientPermissions
}
c, err := r.f.ChildResourceByName(r.ID(), name)
c, err := r.f.childResourceByName(r.ID(), name)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
err = ErrResourceNotFound
@@ -90,14 +90,14 @@ func (r Resource) RestoreDeleted(parentPathOrUUID string, name string, autoRenam
if name == "" {
name = r.name
}
_, err = r.f.ChildResourceByName(p.id, name)
_, err = r.f.childResourceByName(p.id, name)
if autoRename && err == nil {
ext := path.Ext(name)
basename := name[:len(name)-len(ext)]
counter := 1
for {
name = fmt.Sprintf("%s (%d)%s", basename, counter, ext)
if _, err = r.f.ChildResourceByName(p.id, name); err == nil {
if _, err = r.f.childResourceByName(p.id, name); err == nil {
counter++
} else {
break
-16
View File
@@ -1,7 +1,6 @@
package fs
import (
"encoding/json"
"slices"
"strings"
@@ -70,21 +69,6 @@ func selectDirectChildren(id uuid.UUID, deleted pgtype.Timestamp, includeDeleted
return r, q
}
func readPermissionFromJson(j []byte, username string) (Permission, error) {
if j == nil {
return PermissionNone, nil
}
p := make(map[string]Permission)
err := json.Unmarshal(j, &p)
if err != nil {
return PermissionNone, err
}
if p, ok := p[username]; ok {
return p, nil
}
return PermissionNone, nil
}
func collectNonDirResourceIDs(rows pgx.Rows, e error) (total int, ids []uuid.UUID, err error) {
if e != nil {
return 0, nil, e