Passed
Push — master ( c86536...67f069 )
by Tolga
01:12 queued 17s
created

servers.*DataServer.Write   D

Complexity

Conditions 13

Size

Total Lines 93
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 59
nop 2
dl 0
loc 93
rs 4.2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

Complexity

Complex classes like servers.*DataServer.Write often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
package servers
2
3
import (
4
	"log/slog"
5
6
	otelCodes "go.opentelemetry.io/otel/codes"
7
	"golang.org/x/net/context"
8
	"google.golang.org/grpc/status"
9
10
	"github.com/Permify/permify/internal/storage"
11
	"github.com/Permify/permify/internal/validation"
12
	"github.com/Permify/permify/pkg/attribute"
13
	"github.com/Permify/permify/pkg/database"
14
	v1 "github.com/Permify/permify/pkg/pb/base/v1"
15
	"github.com/Permify/permify/pkg/tuple"
16
)
17
18
// DataServer - Structure for Data Server
19
type DataServer struct {
20
	v1.UnimplementedDataServer
21
22
	sr storage.SchemaReader
23
	dr storage.DataReader
24
	br storage.BundleReader
25
	dw storage.DataWriter
26
}
27
28
// NewDataServer - Creates new Data Server
29
func NewDataServer(
30
	dr storage.DataReader,
31
	dw storage.DataWriter,
32
	br storage.BundleReader,
33
	sr storage.SchemaReader,
34
) *DataServer {
35
	return &DataServer{
36
		dr: dr,
37
		dw: dw,
38
		br: br,
39
		sr: sr,
40
	}
41
}
42
43
// ReadRelationships - Allows directly querying the stored engines data to display and filter stored relational tuples
44
func (r *DataServer) ReadRelationships(ctx context.Context, request *v1.RelationshipReadRequest) (*v1.RelationshipReadResponse, error) {
45
	ctx, span := tracer.Start(ctx, "data.read.relationships")
46
	defer span.End()
47
48
	v := request.Validate()
49
	if v != nil {
50
		return nil, status.Error(GetStatus(v), v.Error())
51
	}
52
53
	snap := request.GetMetadata().GetSnapToken()
54
	if snap == "" {
55
		st, err := r.dr.HeadSnapshot(ctx, request.GetTenantId())
56
		if err != nil {
57
			return nil, status.Error(GetStatus(err), err.Error())
58
		}
59
		snap = st.Encode().String()
60
	}
61
62
	collection, ct, err := r.dr.ReadRelationships(
63
		ctx,
64
		request.GetTenantId(),
65
		request.GetFilter(),
66
		snap,
67
		database.NewPagination(
68
			database.Size(request.GetPageSize()),
69
			database.Token(request.GetContinuousToken()),
70
		),
71
	)
72
	if err != nil {
73
		span.RecordError(err)
74
		span.SetStatus(otelCodes.Error, err.Error())
75
		slog.Error(err.Error())
76
		return nil, status.Error(GetStatus(err), err.Error())
77
	}
78
79
	return &v1.RelationshipReadResponse{
80
		Tuples:          collection.GetTuples(),
81
		ContinuousToken: ct.String(),
82
	}, nil
83
}
84
85
// ReadAttributes - Allows directly querying the stored engines data to display and filter stored attribute tuples
86
func (r *DataServer) ReadAttributes(ctx context.Context, request *v1.AttributeReadRequest) (*v1.AttributeReadResponse, error) {
87
	ctx, span := tracer.Start(ctx, "data.read.attributes")
88
	defer span.End()
89
90
	v := request.Validate()
91
	if v != nil {
92
		return nil, status.Error(GetStatus(v), v.Error())
93
	}
94
95
	snap := request.GetMetadata().GetSnapToken()
96
	if snap == "" {
97
		st, err := r.dr.HeadSnapshot(ctx, request.GetTenantId())
98
		if err != nil {
99
			return nil, status.Error(GetStatus(err), err.Error())
100
		}
101
		snap = st.Encode().String()
102
	}
103
104
	collection, ct, err := r.dr.ReadAttributes(
105
		ctx,
106
		request.GetTenantId(),
107
		request.GetFilter(),
108
		snap,
109
		database.NewPagination(
110
			database.Size(request.GetPageSize()),
111
			database.Token(request.GetContinuousToken()),
112
		),
113
	)
114
	if err != nil {
115
		span.RecordError(err)
116
		span.SetStatus(otelCodes.Error, err.Error())
117
		slog.Error(err.Error())
118
		return nil, status.Error(GetStatus(err), err.Error())
119
	}
120
121
	return &v1.AttributeReadResponse{
122
		Attributes:      collection.GetAttributes(),
123
		ContinuousToken: ct.String(),
124
	}, nil
125
}
126
127
// Write - Write relationships and attributes to writeDB
128
func (r *DataServer) Write(ctx context.Context, request *v1.DataWriteRequest) (*v1.DataWriteResponse, error) {
129
	ctx, span := tracer.Start(ctx, "data.write")
130
	defer span.End()
131
132
	v := request.Validate()
133
	if v != nil {
134
		return nil, status.Error(GetStatus(v), v.Error())
135
	}
136
137
	version := request.GetMetadata().GetSchemaVersion()
138
	if version == "" {
139
		v, err := r.sr.HeadVersion(ctx, request.GetTenantId())
140
		if err != nil {
141
			span.RecordError(err)
142
			span.SetStatus(otelCodes.Error, err.Error())
143
			return nil, status.Error(GetStatus(err), err.Error())
144
		}
145
		version = v
146
	}
147
148
	relationships := make([]*v1.Tuple, 0, len(request.GetTuples()))
149
150
	relationshipsMap := map[string]struct{}{}
151
152
	for _, tup := range request.GetTuples() {
153
154
		key := tuple.ToString(tup)
155
156
		if _, ok := relationshipsMap[key]; ok {
157
			continue
158
		}
159
160
		relationshipsMap[key] = struct{}{}
161
162
		definition, _, err := r.sr.ReadEntityDefinition(ctx, request.GetTenantId(), tup.GetEntity().GetType(), version)
163
		if err != nil {
164
			span.RecordError(err)
165
			span.SetStatus(otelCodes.Error, err.Error())
166
			return nil, status.Error(GetStatus(err), err.Error())
167
		}
168
169
		err = validation.ValidateTuple(definition, tup)
170
		if err != nil {
171
			span.RecordError(err)
172
			span.SetStatus(otelCodes.Error, err.Error())
173
			return nil, status.Error(GetStatus(err), err.Error())
174
		}
175
176
		relationships = append(relationships, tup)
177
	}
178
179
	attrs := make([]*v1.Attribute, 0, len(request.GetAttributes()))
180
181
	attributesMap := map[string]struct{}{}
182
183
	for _, attr := range request.GetAttributes() {
184
185
		key := attribute.EntityAndAttributeToString(attr.GetEntity(), attr.GetAttribute())
186
187
		if _, ok := attributesMap[key]; ok {
188
			continue
189
		}
190
191
		attributesMap[key] = struct{}{}
192
193
		definition, _, err := r.sr.ReadEntityDefinition(ctx, request.GetTenantId(), attr.GetEntity().GetType(), version)
194
		if err != nil {
195
			span.RecordError(err)
196
			span.SetStatus(otelCodes.Error, err.Error())
197
			return nil, status.Error(GetStatus(err), err.Error())
198
		}
199
200
		err = validation.ValidateAttribute(definition, attr)
201
		if err != nil {
202
			span.RecordError(err)
203
			span.SetStatus(otelCodes.Error, err.Error())
204
			return nil, status.Error(GetStatus(err), err.Error())
205
		}
206
207
		attrs = append(attrs, attr)
208
	}
209
210
	snap, err := r.dw.Write(ctx, request.GetTenantId(), database.NewTupleCollection(relationships...), database.NewAttributeCollection(attrs...))
211
	if err != nil {
212
		span.RecordError(err)
213
		span.SetStatus(otelCodes.Error, err.Error())
214
		slog.Error(err.Error())
215
		return nil, status.Error(GetStatus(err), err.Error())
216
	}
217
218
	return &v1.DataWriteResponse{
219
		SnapToken: snap.String(),
220
	}, nil
221
}
222
223
// WriteRelationships - Write relation tuples to writeDB
224
func (r *DataServer) WriteRelationships(ctx context.Context, request *v1.RelationshipWriteRequest) (*v1.RelationshipWriteResponse, error) {
225
	ctx, span := tracer.Start(ctx, "relationships.write")
226
	defer span.End()
227
228
	v := request.Validate()
229
	if v != nil {
230
		return nil, status.Error(GetStatus(v), v.Error())
231
	}
232
233
	version := request.GetMetadata().GetSchemaVersion()
234
	if version == "" {
235
		v, err := r.sr.HeadVersion(ctx, request.GetTenantId())
236
		if err != nil {
237
			span.RecordError(err)
238
			span.SetStatus(otelCodes.Error, err.Error())
239
			return nil, status.Error(GetStatus(err), err.Error())
240
		}
241
		version = v
242
	}
243
244
	relationships := make([]*v1.Tuple, 0, len(request.GetTuples()))
245
246
	relationshipsMap := map[string]struct{}{}
247
248
	for _, tup := range request.GetTuples() {
249
250
		key := tuple.ToString(tup)
251
252
		if _, ok := relationshipsMap[key]; ok {
253
			continue
254
		}
255
256
		relationshipsMap[key] = struct{}{}
257
258
		definition, _, err := r.sr.ReadEntityDefinition(ctx, request.GetTenantId(), tup.GetEntity().GetType(), version)
259
		if err != nil {
260
			span.RecordError(err)
261
			span.SetStatus(otelCodes.Error, err.Error())
262
			return nil, status.Error(GetStatus(err), err.Error())
263
		}
264
265
		err = validation.ValidateTuple(definition, tup)
266
		if err != nil {
267
			span.RecordError(err)
268
			span.SetStatus(otelCodes.Error, err.Error())
269
			return nil, status.Error(GetStatus(err), err.Error())
270
		}
271
272
		relationships = append(relationships, tup)
273
	}
274
275
	snap, err := r.dw.Write(ctx, request.GetTenantId(), database.NewTupleCollection(relationships...), database.NewAttributeCollection())
276
	if err != nil {
277
		span.RecordError(err)
278
		span.SetStatus(otelCodes.Error, err.Error())
279
		slog.Error(err.Error())
280
		return nil, status.Error(GetStatus(err), err.Error())
281
	}
282
283
	return &v1.RelationshipWriteResponse{
284
		SnapToken: snap.String(),
285
	}, nil
286
}
287
288
// Delete - Delete relationships and attributes from writeDB
289
func (r *DataServer) Delete(ctx context.Context, request *v1.DataDeleteRequest) (*v1.DataDeleteResponse, error) {
290
	ctx, span := tracer.Start(ctx, "data.delete")
291
	defer span.End()
292
293
	v := request.Validate()
294
	if v != nil {
295
		return nil, status.Error(GetStatus(v), v.Error())
296
	}
297
298
	err := validation.ValidateFilters(request.GetTupleFilter(), request.GetAttributeFilter())
299
	if err != nil {
300
		return nil, status.Error(GetStatus(v), v.Error())
301
	}
302
303
	snap, err := r.dw.Delete(ctx, request.GetTenantId(), request.GetTupleFilter(), request.GetAttributeFilter())
304
	if err != nil {
305
		span.RecordError(err)
306
		span.SetStatus(otelCodes.Error, err.Error())
307
		slog.Error(err.Error())
308
		return nil, status.Error(GetStatus(err), err.Error())
309
	}
310
311
	return &v1.DataDeleteResponse{
312
		SnapToken: snap.String(),
313
	}, nil
314
}
315
316
// DeleteRelationships - Delete relationships from writeDB
317
func (r *DataServer) DeleteRelationships(ctx context.Context, request *v1.RelationshipDeleteRequest) (*v1.RelationshipDeleteResponse, error) {
318
	ctx, span := tracer.Start(ctx, "relationships.delete")
319
	defer span.End()
320
321
	v := request.Validate()
322
	if v != nil {
323
		return nil, status.Error(GetStatus(v), v.Error())
324
	}
325
326
	err := validation.ValidateTupleFilter(request.GetFilter())
327
	if err != nil {
328
		return nil, status.Error(GetStatus(v), v.Error())
329
	}
330
331
	snap, err := r.dw.Delete(ctx, request.GetTenantId(), request.GetFilter(), &v1.AttributeFilter{})
332
	if err != nil {
333
		span.RecordError(err)
334
		span.SetStatus(otelCodes.Error, err.Error())
335
		slog.Error(err.Error())
336
		return nil, status.Error(GetStatus(err), err.Error())
337
	}
338
339
	return &v1.RelationshipDeleteResponse{
340
		SnapToken: snap.String(),
341
	}, nil
342
}
343
344
// RunBundle executes a bundle and returns its snapshot token.
345
func (r *DataServer) RunBundle(ctx context.Context, request *v1.BundleRunRequest) (*v1.BundleRunResponse, error) {
346
	ctx, span := tracer.Start(ctx, "bundle.run")
347
	defer span.End()
348
349
	v := request.Validate()
350
	if v != nil {
351
		return nil, status.Error(GetStatus(v), v.Error())
352
	}
353
354
	bundle, err := r.br.Read(ctx, request.GetTenantId(), request.GetName())
355
	if err != nil {
356
		span.RecordError(err)
357
		span.SetStatus(otelCodes.Error, err.Error())
358
		slog.Error(err.Error())
359
		return nil, status.Error(GetStatus(err), err.Error())
360
	}
361
362
	err = validation.ValidateBundleArguments(bundle.GetArguments(), request.GetArguments())
363
	if err != nil {
364
		return nil, status.Error(GetStatus(err), err.Error())
365
	}
366
367
	snap, err := r.dw.RunBundle(ctx, request.GetTenantId(), request.GetArguments(), bundle)
368
	if err != nil {
369
		span.RecordError(err)
370
		span.SetStatus(otelCodes.Error, err.Error())
371
		slog.Error(err.Error())
372
		return nil, status.Error(GetStatus(err), err.Error())
373
	}
374
375
	return &v1.BundleRunResponse{
376
		SnapToken: snap.String(),
377
	}, nil
378
}
379