Conditions | 45 |
Total Lines | 396 |
Code Lines | 208 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
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:
If many parameters/temporary variables are present:
Complex classes like cmd.validate 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 cmd |
||
65 | func validate() func(cmd *cobra.Command, args []string) error { |
||
66 | return func(cmd *cobra.Command, args []string) error { |
||
67 | // create an empty error list |
||
68 | list := &ErrList{ |
||
69 | Errors: []string{}, |
||
70 | } |
||
71 | |||
72 | // create a new context |
||
73 | ctx := context.Background() |
||
74 | |||
75 | // create a new development container |
||
76 | dev := development.NewContainer() |
||
77 | |||
78 | // parse the url from the first argument |
||
79 | u, err := url.Parse(args[0]) |
||
80 | if err != nil { |
||
81 | return err |
||
82 | } |
||
83 | |||
84 | // create a new decoder from the url |
||
85 | decoder, err := file.NewDecoderFromURL(u) |
||
86 | if err != nil { |
||
87 | return err |
||
88 | } |
||
89 | |||
90 | // create a new shape |
||
91 | s := &file.Shape{} |
||
92 | |||
93 | // decode the schema from the decoder |
||
94 | err = decoder.Decode(s) |
||
95 | if err != nil { |
||
96 | return err |
||
97 | } |
||
98 | |||
99 | // if debug is true, print schema is creating with color blue |
||
100 | color.Notice.Println("schema is creating... 🚀") |
||
101 | |||
102 | loader := schema.NewSchemaLoader() |
||
103 | loaded, err := loader.LoadSchema(s.Schema) |
||
104 | if err != nil { |
||
105 | return err |
||
106 | } |
||
107 | |||
108 | sch, err := parser.NewParser(loaded).Parse() |
||
109 | if err != nil { |
||
110 | return err |
||
111 | } |
||
112 | |||
113 | _, _, err = compiler.NewCompiler(true, sch).Compile() |
||
114 | if err != nil { |
||
115 | return err |
||
116 | } |
||
117 | |||
118 | version := xid.New().String() |
||
119 | |||
120 | cnf := make([]storage.SchemaDefinition, 0, len(sch.Statements)) |
||
121 | for _, st := range sch.Statements { |
||
122 | cnf = append(cnf, storage.SchemaDefinition{ |
||
123 | TenantID: "t1", |
||
124 | Version: version, |
||
125 | Name: st.GetName(), |
||
126 | SerializedDefinition: []byte(st.String()), |
||
127 | }) |
||
128 | } |
||
129 | |||
130 | // write the schema |
||
131 | err = dev.Container.SW.WriteSchema(ctx, cnf) |
||
132 | if err != nil { |
||
133 | list.Add(err.Error()) |
||
134 | color.Danger.Printf("fail: %s\n", validationError(err.Error())) |
||
135 | if len(list.Errors) != 0 { |
||
136 | list.Print() |
||
137 | os.Exit(1) |
||
138 | } |
||
139 | } |
||
140 | |||
141 | // if there are no errors and debug is true, print success with color success |
||
142 | if len(list.Errors) == 0 { |
||
143 | color.Success.Println(" success") |
||
144 | } |
||
145 | |||
146 | // if debug is true, print relationships are creating with color blue |
||
147 | color.Notice.Println("relationships are creating... 🚀") |
||
148 | |||
149 | // Iterate over all relationships in the subject |
||
150 | for _, t := range s.Relationships { |
||
151 | // Convert each relationship to a Tuple |
||
152 | var tup *base.Tuple |
||
153 | tup, err = tuple.Tuple(t) |
||
154 | // If an error occurs during the conversion, add the error message to the list and continue to the next iteration |
||
155 | if err != nil { |
||
156 | list.Add(err.Error()) |
||
157 | continue |
||
158 | } |
||
159 | |||
160 | // Retrieve the entity definition associated with the tuple's entity type |
||
161 | definition, _, err := dev.Container.SR.ReadEntityDefinition(ctx, "t1", tup.GetEntity().GetType(), version) |
||
162 | // If an error occurs while reading the entity definition, return the error |
||
163 | if err != nil { |
||
164 | return err |
||
165 | } |
||
166 | |||
167 | // Validate the tuple using the entity definition |
||
168 | err = serverValidation.ValidateTuple(definition, tup) |
||
169 | // If an error occurs during validation, return the error |
||
170 | if err != nil { |
||
171 | return err |
||
172 | } |
||
173 | |||
174 | // Write the validated tuple to the database |
||
175 | _, err = dev.Container.DW.Write(ctx, "t1", database.NewTupleCollection(tup), database.NewAttributeCollection()) |
||
176 | // If an error occurs while writing to the database, add an error message to the list, log the error and continue to the next iteration |
||
177 | if err != nil { |
||
178 | list.Add(fmt.Sprintf("%s failed %s", t, err.Error())) |
||
179 | color.Danger.Println(fmt.Sprintf("fail: %s failed %s", t, validationError(err.Error()))) |
||
180 | continue |
||
181 | } |
||
182 | |||
183 | // If the tuple was successfully written to the database, log a success message |
||
184 | color.Success.Println(fmt.Sprintf(" success: %s ", t)) |
||
185 | } |
||
186 | |||
187 | // if debug is true, print attributes are creating with color blue |
||
188 | color.Notice.Println("attributes are creating... 🚀") |
||
189 | |||
190 | // Iterate over all attributes in the subject |
||
191 | for _, a := range s.Attributes { |
||
192 | // Convert each attribute to an Attribute |
||
193 | var attr *base.Attribute |
||
194 | attr, err = attribute.Attribute(a) |
||
195 | // If an error occurs during the conversion, add the error message to the list and continue to the next iteration |
||
196 | if err != nil { |
||
197 | list.Add(err.Error()) |
||
198 | continue |
||
199 | } |
||
200 | |||
201 | // Retrieve the entity definition associated with the attribute's entity type |
||
202 | definition, _, err := dev.Container.SR.ReadEntityDefinition(ctx, "t1", attr.GetEntity().GetType(), version) |
||
203 | // If an error occurs while reading the entity definition, return the error |
||
204 | if err != nil { |
||
205 | return err |
||
206 | } |
||
207 | |||
208 | // Validate the attribute using the entity definition |
||
209 | err = serverValidation.ValidateAttribute(definition, attr) |
||
210 | // If an error occurs during validation, return the error |
||
211 | if err != nil { |
||
212 | return err |
||
213 | } |
||
214 | |||
215 | // Write the validated attribute to the database |
||
216 | _, err = dev.Container.DW.Write(ctx, "t1", database.NewTupleCollection(), database.NewAttributeCollection(attr)) |
||
217 | // If an error occurs while writing to the database, add an error message to the list, log the error and continue to the next iteration |
||
218 | if err != nil { |
||
219 | list.Add(fmt.Sprintf("%s failed %s", a, err.Error())) |
||
220 | color.Danger.Println(fmt.Sprintf("fail: %s failed %s", a, validationError(err.Error()))) |
||
221 | continue |
||
222 | } |
||
223 | |||
224 | // If the attribute was successfully written to the database, log a success message |
||
225 | color.Success.Println(fmt.Sprintf(" success: %s ", a)) |
||
226 | } |
||
227 | |||
228 | // if debug is true, print checking assertions with color blue |
||
229 | color.Notice.Println("checking scenarios... 🚀") |
||
230 | |||
231 | // Check Assertions |
||
232 | for sn, scenario := range s.Scenarios { |
||
233 | color.Notice.Printf("%v.scenario: %s - %s\n", sn+1, scenario.Name, scenario.Description) |
||
234 | |||
235 | // Start log output for checks |
||
236 | color.Notice.Println(" checks:") |
||
237 | |||
238 | // Iterate over all checks in the scenario |
||
239 | for _, check := range scenario.Checks { |
||
240 | // Extract entity from the check |
||
241 | entity, err := tuple.E(check.Entity) |
||
242 | if err != nil { |
||
243 | list.Add(err.Error()) |
||
244 | continue |
||
245 | } |
||
246 | |||
247 | // Extract entity-attribute-relation from the check's subject |
||
248 | ear, err := tuple.EAR(check.Subject) |
||
249 | if err != nil { |
||
250 | list.Add(err.Error()) |
||
251 | continue |
||
252 | } |
||
253 | |||
254 | // Define the subject based on the extracted entity-attribute-relation |
||
255 | subject := &base.Subject{ |
||
256 | Type: ear.GetEntity().GetType(), |
||
257 | Id: ear.GetEntity().GetId(), |
||
258 | Relation: ear.GetRelation(), |
||
259 | } |
||
260 | |||
261 | cont, err := Context(check.Context) |
||
262 | if err != nil { |
||
263 | list.Add(err.Error()) |
||
264 | continue |
||
265 | } |
||
266 | |||
267 | // Iterate over all assertions in the check |
||
268 | for permission, expected := range check.Assertions { |
||
269 | // Set expected result based on the assertion |
||
270 | exp := base.CheckResult_CHECK_RESULT_ALLOWED |
||
271 | if !expected { |
||
272 | exp = base.CheckResult_CHECK_RESULT_DENIED |
||
273 | } |
||
274 | |||
275 | // Perform a permission check based on the context, entity, permission, and subject |
||
276 | res, err := dev.Container.Invoker.Check(ctx, &base.PermissionCheckRequest{ |
||
277 | TenantId: "t1", |
||
278 | Context: cont, |
||
279 | Metadata: &base.PermissionCheckRequestMetadata{ |
||
280 | SchemaVersion: version, |
||
281 | SnapToken: token.NewNoopToken().Encode().String(), |
||
282 | Depth: 100, |
||
283 | }, |
||
284 | Entity: entity, |
||
285 | Permission: permission, |
||
286 | Subject: subject, |
||
287 | }) |
||
288 | if err != nil { |
||
289 | list.Add(err.Error()) |
||
290 | continue |
||
291 | } |
||
292 | |||
293 | // Formulate the query string for log output |
||
294 | query := tuple.SubjectToString(subject) + " " + permission + " " + tuple.EntityToString(entity) |
||
295 | |||
296 | // If the check result matches the expected result, log a success message |
||
297 | if res.Can == exp { |
||
298 | color.Success.Print(" success:") |
||
299 | fmt.Printf(" %s \n", query) |
||
300 | } else { |
||
301 | // If the check result does not match the expected result, log a failure message |
||
302 | color.Danger.Printf(" fail: %s ->", query) |
||
303 | if res.Can == base.CheckResult_CHECK_RESULT_ALLOWED { |
||
304 | color.Danger.Println(" expected: DENIED actual: ALLOWED ") |
||
305 | list.Add(fmt.Sprintf("%s -> expected: DENIED actual: ALLOWED ", query)) |
||
306 | } else { |
||
307 | color.Danger.Println(" expected: ALLOWED actual: DENIED ") |
||
308 | list.Add(fmt.Sprintf("%s -> expected: ALLOWED actual: DENIED ", query)) |
||
309 | } |
||
310 | } |
||
311 | } |
||
312 | } |
||
313 | |||
314 | // Start of the entity filter processing. |
||
315 | color.Notice.Println(" entity_filters:") |
||
316 | |||
317 | // Iterate over each entity filter in the scenario. |
||
318 | for _, filter := range scenario.EntityFilters { |
||
319 | |||
320 | // Convert the subject from the filter into a base.Subject. |
||
321 | ear, err := tuple.EAR(filter.Subject) |
||
322 | if err != nil { |
||
323 | // If an error occurs, add it to the list and continue to the next filter. |
||
324 | list.Add(err.Error()) |
||
325 | continue |
||
326 | } |
||
327 | |||
328 | // Create a new base.Subject from the Entity-Attribute-Relation (EAR). |
||
329 | subject := &base.Subject{ |
||
330 | Type: ear.GetEntity().GetType(), |
||
331 | Id: ear.GetEntity().GetId(), |
||
332 | Relation: ear.GetRelation(), |
||
333 | } |
||
334 | |||
335 | // Convert the filter context into a base.Context. |
||
336 | cont, err := Context(filter.Context) |
||
337 | if err != nil { |
||
338 | // If an error occurs, add it to the list and continue to the next filter. |
||
339 | list.Add(err.Error()) |
||
340 | continue |
||
341 | } |
||
342 | |||
343 | // Iterate over each assertion in the filter. |
||
344 | for permission, expected := range filter.Assertions { |
||
345 | // Perform a permission lookup for the entity. |
||
346 | res, err := dev.Container.Invoker.LookupEntity(ctx, &base.PermissionLookupEntityRequest{ |
||
347 | TenantId: "t1", |
||
348 | Context: cont, |
||
349 | Metadata: &base.PermissionLookupEntityRequestMetadata{ |
||
350 | SchemaVersion: version, |
||
351 | SnapToken: token.NewNoopToken().Encode().String(), |
||
352 | Depth: 100, |
||
353 | }, |
||
354 | EntityType: filter.EntityType, |
||
355 | Permission: permission, |
||
356 | Subject: subject, |
||
357 | }) |
||
358 | if err != nil { |
||
359 | // If an error occurs, add it to the list and continue to the next assertion. |
||
360 | list.Add(err.Error()) |
||
361 | continue |
||
362 | } |
||
363 | |||
364 | // Format the subject, permission, and entity type as a string for logging. |
||
365 | query := tuple.SubjectToString(subject) + " " + permission + " " + filter.EntityType |
||
366 | |||
367 | // Check if the actual result matches the expected result. |
||
368 | if isSameArray(res.GetEntityIds(), expected) { |
||
369 | // If the results match, log a success message. |
||
370 | color.Success.Print(" success:") |
||
371 | fmt.Printf(" %v\n", query) |
||
372 | } else { |
||
373 | // If the results don't match, log a failure message with the expected and actual results. |
||
374 | color.Danger.Printf(" fail: %s -> expected: %+v actual: %+v\n", query, expected, res.GetEntityIds()) |
||
375 | list.Add(fmt.Sprintf("%s -> expected: %+v actual: %+v", query, expected, res.GetEntityIds())) |
||
376 | } |
||
377 | } |
||
378 | } |
||
379 | |||
380 | // Print a message indicating the start of the subject filter processing. |
||
381 | color.Notice.Println(" subject_filters:") |
||
382 | |||
383 | // Iterate over each subject filter in the scenario. |
||
384 | for _, filter := range scenario.SubjectFilters { |
||
385 | |||
386 | // Convert the subject reference from the filter into a relation reference. |
||
387 | subjectReference := tuple.RelationReference(filter.SubjectReference) |
||
388 | |||
389 | // Convert the entity from the filter into a base.Entity. |
||
390 | var entity *base.Entity |
||
391 | entity, err = tuple.E(filter.Entity) |
||
392 | if err != nil { |
||
393 | // If an error occurs, add it to the list and continue to the next filter. |
||
394 | list.Add(err.Error()) |
||
395 | continue |
||
396 | } |
||
397 | |||
398 | // Convert the filter context into a base.Context. |
||
399 | cont, err := Context(filter.Context) |
||
400 | if err != nil { |
||
401 | // If an error occurs, add it to the list and continue to the next filter. |
||
402 | list.Add(err.Error()) |
||
403 | continue |
||
404 | } |
||
405 | |||
406 | // Iterate over each assertion in the filter. |
||
407 | for permission, expected := range filter.Assertions { |
||
408 | // Perform a permission lookup for the subject. |
||
409 | res, err := dev.Container.Invoker.LookupSubject(ctx, &base.PermissionLookupSubjectRequest{ |
||
410 | TenantId: "t1", |
||
411 | Context: cont, |
||
412 | Metadata: &base.PermissionLookupSubjectRequestMetadata{ |
||
413 | SchemaVersion: version, |
||
414 | SnapToken: token.NewNoopToken().Encode().String(), |
||
415 | Depth: 100, |
||
416 | }, |
||
417 | SubjectReference: subjectReference, |
||
418 | Permission: permission, |
||
419 | Entity: entity, |
||
420 | }) |
||
421 | if err != nil { |
||
422 | // If an error occurs, add it to the list and continue to the next assertion. |
||
423 | list.Add(err.Error()) |
||
424 | continue |
||
425 | } |
||
426 | |||
427 | // Format the entity, permission, and subject reference as a string for logging. |
||
428 | query := tuple.EntityToString(entity) + " " + permission + " " + filter.SubjectReference |
||
429 | |||
430 | // Check if the actual result matches the expected result. |
||
431 | if isSameArray(res.GetSubjectIds(), expected) { |
||
432 | // If the results match, log a success message. |
||
433 | color.Success.Print(" success:") |
||
434 | fmt.Printf(" %v\n", query) |
||
435 | } else { |
||
436 | // If the results don't match, log a failure message with the expected and actual results. |
||
437 | color.Danger.Printf(" fail: %s -> expected: %+v actual: %+v\n", query, expected, res.GetSubjectIds()) |
||
438 | list.Add(fmt.Sprintf("%s -> expected: %+v actual: %+v", query, expected, res.GetSubjectIds())) |
||
439 | } |
||
440 | } |
||
441 | } |
||
442 | } |
||
443 | |||
444 | // If the error list is not empty, there were some errors during processing. |
||
445 | if len(list.Errors) != 0 { |
||
446 | // Print the errors collected during processing. |
||
447 | list.Print() |
||
448 | // Exit the program with a status of 1 to indicate an error. |
||
449 | os.Exit(1) |
||
450 | } |
||
451 | |||
452 | // If there are no errors, print the success messages. |
||
453 | color.Notice.Println("schema successfully created") |
||
454 | color.Notice.Println("relationships successfully created") |
||
455 | color.Notice.Println("assertions successfully passed") |
||
456 | |||
457 | // Final success message to indicate everything completed successfully. |
||
458 | color.Success.Println("SUCCESS") |
||
459 | |||
460 | return nil |
||
461 | } |
||
540 |