Skip to content

Commit

Permalink
list related resources
Browse files Browse the repository at this point in the history
Expose a method to list resources related to a given resource.

Signed-off-by: Mike Mason <[email protected]>
  • Loading branch information
mikemrm committed Jul 14, 2023
1 parent 972fec5 commit bb7845e
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 22 deletions.
18 changes: 12 additions & 6 deletions internal/api/relationships.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (r *Router) relationshipsCreate(c echo.Context) error {

func (r *Router) relationshipsList(c echo.Context) error {
resourceIDStr := c.Param("id")
resourceTypePrefix := c.QueryParam("resourceType")

ctx, span := tracer.Start(c.Request().Context(), "api.relationshipsList", trace.WithAttributes(attribute.String("id", resourceIDStr)))
defer span.End()
Expand All @@ -102,20 +103,25 @@ func (r *Router) relationshipsList(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "error listing relationships").SetInternal(err)
}

rels, err := r.engine.ListRelationships(ctx, resource, "")
rels, err := r.engine.ListRelationships(ctx, resource, resourceTypePrefix, "")
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "error listing relationships").SetInternal(err)
}

items := make([]relationshipItem, len(rels))

for i, rel := range rels {
item := relationshipItem{
Relation: rel.Relation,
SubjectID: rel.Subject.ID.String(),
if resourceTypePrefix != "" {
items[i] = relationshipItem{
ResourceID: rel.Resource.ID.String(),
Relation: rel.Relation,
}
} else {
items[i] = relationshipItem{
Relation: rel.Relation,
SubjectID: rel.Subject.ID.String(),
}
}

items[i] = item
}

out := listRelationshipsResponse{
Expand Down
5 changes: 3 additions & 2 deletions internal/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ type deleteRelationshipsResponse struct {
}

type relationshipItem struct {
Relation string `json:"relation"`
SubjectID string `json:"subject_id"`
ResourceID string `json:"resource_id,omitempty"`
Relation string `json:"relation"`
SubjectID string `json:"subject_id,omitempty"`
}

type listRelationshipsResponse struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/query/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (e *Engine) ListAssignments(ctx context.Context, role types.Role, queryToke
}

// ListRelationships returns nothing but satisfies the Engine interface.
func (e *Engine) ListRelationships(ctx context.Context, resource types.Resource, queryToken string) ([]types.Relationship, error) {
func (e *Engine) ListRelationships(ctx context.Context, resource types.Resource, optionalRelationIDPrefix, queryToken string) ([]types.Relationship, error) {
return nil, nil
}

Expand Down
43 changes: 34 additions & 9 deletions internal/query/relations.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,20 +412,30 @@ func relationshipsToRoles(rels []*pb.Relationship) []types.Role {
return out
}

func (e *engine) relationshipsToNonRoles(rels []*pb.Relationship, res types.Resource) ([]types.Relationship, error) {
func (e *engine) relationshipsToNonRoles(rels []*pb.Relationship) ([]types.Relationship, error) {
var out []types.Relationship

for _, rel := range rels {
if rel.Subject.Object.ObjectType == e.namespace+"/role" {
continue
}

id, err := gidx.Parse(rel.Subject.Object.ObjectId)
resID, err := gidx.Parse(rel.Resource.ObjectId)
if err != nil {
return nil, err
}

res, err := e.NewResourceFromID(resID)
if err != nil {
return nil, err
}

subjID, err := gidx.Parse(rel.Subject.Object.ObjectId)
if err != nil {
return nil, err
}

subj, err := e.NewResourceFromID(id)
subj, err := e.NewResourceFromID(subjID)
if err != nil {
return nil, err
}
Expand All @@ -443,20 +453,35 @@ func (e *engine) relationshipsToNonRoles(rels []*pb.Relationship, res types.Reso
}

// ListRelationships returns all non-role relationships bound to a given resource.
func (e *engine) ListRelationships(ctx context.Context, resource types.Resource, queryToken string) ([]types.Relationship, error) {
resType := e.namespace + "/" + resource.Type
func (e *engine) ListRelationships(ctx context.Context, resource types.Resource, optionalRelationIDPrefix, queryToken string) ([]types.Relationship, error) {
var filter *pb.RelationshipFilter

filter := &pb.RelationshipFilter{
ResourceType: resType,
OptionalResourceId: resource.ID.String(),
if optionalRelationIDPrefix != "" {
relationType, ok := e.schemaPrefixMap[optionalRelationIDPrefix]
if !ok {
return nil, ErrInvalidType
}

filter = &pb.RelationshipFilter{
ResourceType: e.namespace + "/" + relationType.Name,
OptionalSubjectFilter: &pb.SubjectFilter{
SubjectType: e.namespace + "/" + resource.Type,
OptionalSubjectId: resource.ID.String(),
},
}
} else {
filter = &pb.RelationshipFilter{
ResourceType: e.namespace + "/" + resource.Type,
OptionalResourceId: resource.ID.String(),
}
}

relationships, err := e.readRelationships(ctx, filter, queryToken)
if err != nil {
return nil, err
}

return e.relationshipsToNonRoles(relationships, resource)
return e.relationshipsToNonRoles(relationships)
}

// ListRoles returns all roles bound to a given resource.
Expand Down
6 changes: 3 additions & 3 deletions internal/query/relations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func TestRelationships(t *testing.T) {
}
}

rels, err := e.ListRelationships(ctx, input.Resource, queryToken)
rels, err := e.ListRelationships(ctx, input.Resource, "", queryToken)

return testingx.TestResult[[]types.Relationship]{
Success: rels,
Expand Down Expand Up @@ -449,7 +449,7 @@ func TestRelationshipDelete(t *testing.T) {
queryToken, err := e.CreateRelationships(ctx, []types.Relationship{relReq})
require.NoError(t, err)

createdResources, err := e.ListRelationships(ctx, childRes, queryToken)
createdResources, err := e.ListRelationships(ctx, childRes, "", queryToken)
require.NoError(t, err)
require.NotEmpty(t, createdResources)

Expand Down Expand Up @@ -487,7 +487,7 @@ func TestRelationshipDelete(t *testing.T) {
}
}

rels, err := e.ListRelationships(ctx, input.Resource, queryToken)
rels, err := e.ListRelationships(ctx, input.Resource, "", queryToken)

return testingx.TestResult[[]types.Relationship]{
Success: rels,
Expand Down
2 changes: 1 addition & 1 deletion internal/query/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Engine interface {
CreateRelationships(ctx context.Context, rels []types.Relationship) (string, error)
CreateRole(ctx context.Context, res types.Resource, actions []string) (types.Role, string, error)
ListAssignments(ctx context.Context, role types.Role, queryToken string) ([]types.Resource, error)
ListRelationships(ctx context.Context, resource types.Resource, queryToken string) ([]types.Relationship, error)
ListRelationships(ctx context.Context, resource types.Resource, optionalRelationIDPrefix, queryToken string) ([]types.Relationship, error)
ListRoles(ctx context.Context, resource types.Resource, queryToken string) ([]types.Role, error)
DeleteRelationship(ctx context.Context, rel types.Relationship) (string, error)
DeleteRole(ctx context.Context, roleResource types.Resource, queryToken string) (string, error)
Expand Down

0 comments on commit bb7845e

Please sign in to comment.