Passed
Push — master ( 76638c...1fdb58 )
by Tolga
01:06 queued 12s
created

graph.buildPermissionGraph   C

Complexity

Conditions 9

Size

Total Lines 55
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 36
nop 3
dl 0
loc 55
rs 6.6666
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
package graph
2
3
import (
4
	"errors"
5
	"fmt"
6
	"strings"
7
8
	"github.com/rs/xid"
9
10
	"github.com/Permify/permify/internal/schema"
11
	base "github.com/Permify/permify/pkg/pb/base/v1"
12
	"github.com/Permify/permify/pkg/tuple"
13
)
14
15
// SchemaToGraph takes a schema definition and converts it into a graph
16
// representation, returning the created graph and an error if any occurs.
17
func SchemaToGraph(schema *base.SchemaDefinition) (g Graph, err error) {
18
	// Iterate through entity definitions in the schema
19
	for _, en := range schema.GetEntityDefinitions() {
20
		// Convert each entity into a graph
21
		eg, err := EntityToGraph(en)
22
		if err != nil {
23
			return Graph{}, err
24
		}
25
		// Add the nodes and edges from the entity graph to the schema graph
26
		g.AddNodes(eg.Nodes())
27
		g.AddEdges(eg.Edges())
28
	}
29
	return
30
}
31
32
// EntityToGraph takes an entity definition and converts it into a graph
33
// representation, returning the created graph and an error if any occurs.
34
func EntityToGraph(entity *base.EntityDefinition) (g Graph, err error) {
35
	// Create a node for the entity
36
	enNode := &Node{
37
		Type:  "entity",
38
		ID:    entity.GetName(),
39
		Label: entity.GetName(),
40
	}
41
	g.AddNode(enNode)
42
43
	// Iterate through the relations in the entity
44
	for _, re := range entity.GetRelations() {
45
		// Create a node for each relation
46
		reNode := &Node{
47
			Type:  "relation",
48
			ID:    fmt.Sprintf("%s#%s", entity.GetName(), re.GetName()),
49
			Label: re.Name,
50
		}
51
52
		// Iterate through the relation references
53
		for _, ref := range re.GetRelationReferences() {
54
			if ref.GetRelation() != "" {
55
				g.AddEdge(reNode, &Node{
56
					Type:  "relation",
57
					ID:    fmt.Sprintf("%s#%s", ref.GetType(), ref.GetRelation()),
58
					Label: re.Name,
59
				}, nil)
60
			} else {
61
				g.AddEdge(reNode, &Node{
62
					Type:  "entity",
63
					ID:    fmt.Sprintf("%s", ref.GetType()),
64
					Label: re.Name,
65
				}, nil)
66
			}
67
		}
68
69
		// Add relation node and edge to the graph
70
		g.AddNode(reNode)
71
		g.AddEdge(enNode, reNode, nil)
72
	}
73
74
	// Iterate through the permissions in the entity
75
	for _, permission := range entity.GetPermissions() {
76
		// Create a node for each permission
77
		acNode := &Node{
78
			Type:  "permission",
79
			ID:    fmt.Sprintf("%s#%s", entity.GetName(), permission.GetName()),
80
			Label: permission.GetName(),
81
		}
82
		g.AddNode(acNode)
83
		g.AddEdge(enNode, acNode, nil)
84
		// Build permission graph for each permission
85
		ag, err := buildPermissionGraph(entity, acNode, []*base.Child{permission.GetChild()})
86
		if err != nil {
87
			return Graph{}, err
88
		}
89
		// Add nodes and edges from permission graph to entity graph
90
		g.AddNodes(ag.Nodes())
91
		g.AddEdges(ag.Edges())
92
	}
93
	return
94
}
95
96
// buildActionGraph creates a permission graph for the given entity and node,
97
// and recursively processes the children of the node. Returns the created
98
// graph and an error if any occurs.
99
func buildPermissionGraph(entity *base.EntityDefinition, from *Node, children []*base.Child) (g Graph, err error) {
100
	// Iterate through the children
101
	for _, child := range children {
102
		switch child.GetType().(type) {
103
		case *base.Child_Rewrite:
104
			// Create a node for the rewrite operation
105
			rw := &Node{
106
				Type:  "operation",
107
				ID:    xid.New().String(),
108
				Label: child.GetRewrite().GetRewriteOperation().String(),
109
			}
110
111
			// Add the rewrite node to the graph and connect it to the parent node
112
			g.AddNode(rw)
113
			g.AddEdge(from, rw, nil)
114
			// Recursively process the children of the rewrite node
115
			ag, err := buildPermissionGraph(entity, rw, child.GetRewrite().GetChildren())
116
			if err != nil {
117
				return Graph{}, err
118
			}
119
			// Add the nodes and edges from the child graph to the current graph
120
			g.AddNodes(ag.Nodes())
121
			g.AddEdges(ag.Edges())
122
		case *base.Child_Leaf:
123
			// Process the leaf node
124
			leaf := child.GetLeaf()
125
126
			switch leaf.GetType().(type) {
127
			case *base.Leaf_TupleToUserSet:
128
				// Find the relation in the entity definition
129
				re, err := schema.GetRelationByNameInEntityDefinition(entity, leaf.GetTupleToUserSet().GetTupleSet().GetRelation())
130
				if err != nil {
131
					return Graph{}, errors.New(base.ErrorCode_ERROR_CODE_RELATION_DEFINITION_NOT_FOUND.String())
132
				}
133
134
				// Add an edge between the parent node and the tuple set relation node
135
				g.AddEdge(from, &Node{
136
					Type:  "relation",
137
					ID:    fmt.Sprintf("%s#%s", GetTupleSetReferenceReference(re), leaf.GetTupleToUserSet().GetComputed().GetRelation()),
0 ignored issues
show
introduced by
can't check non-constant format in call to Sprintf
Loading history...
138
					Label: leaf.GetTupleToUserSet().GetComputed().GetRelation(),
139
				}, leaf.GetExclusion())
140
141
			case *base.Leaf_ComputedUserSet:
142
				// Add an edge between the parent node and the computed user set relation node
143
				g.AddEdge(from, &Node{
144
					Type:  "relation",
145
					ID:    fmt.Sprintf("%s#%s", entity.GetName(), leaf.GetComputedUserSet().GetRelation()),
0 ignored issues
show
introduced by
can't check non-constant format in call to Sprintf
Loading history...
146
					Label: leaf.GetComputedUserSet().GetRelation(),
147
				}, leaf.GetExclusion())
148
			default:
149
				break
150
			}
151
		}
152
	}
153
	return
154
}
155
156
// GetTupleSetReferenceReference iterates through the relation references
157
// and returns the first reference that doesn't contain a "#" symbol.
158
// If no such reference is found, it returns the tuple.USER constant.
159
func GetTupleSetReferenceReference(definition *base.RelationDefinition) string {
160
	for _, ref := range definition.GetRelationReferences() {
161
		if !strings.Contains(ref.String(), "#") {
162
			return ref.GetType()
163
		}
164
	}
165
	return tuple.USER
166
}
167