lms-api 1.22.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,604 @@
1
+ module CanvasApi
2
+ module GoHelpers
3
+ def struct_fields(model, resource_name)
4
+ if !model["properties"]
5
+ puts "NO properties for #{resource_name} !!!!!!!!!!!!!!!!!!!!!"
6
+ return []
7
+ end
8
+
9
+ time_required = false
10
+ fields = model["properties"].map do |name, property|
11
+ description = ""
12
+ description << "#{safe_rb(property['description'].gsub("\n", "\n //"))}." if property["description"].present?
13
+ description << "Example: #{safe_rb(property['example'])}".gsub("..", "").gsub("\n", " ") if property["example"].present?
14
+
15
+ # clean up name
16
+ name = nested_arg(name)
17
+
18
+ if type = go_type(name, property, false, model, "")
19
+ if type.include? "time.Time"
20
+ time_required = true
21
+ end
22
+ go_declaration(name, type) + " // #{description}"
23
+ else
24
+ raise "Unable to determine type for #{name}"
25
+ end
26
+ end.compact
27
+
28
+ [fields, time_required]
29
+ end
30
+
31
+ # go_struct_fields handles the various forms of parameters documented in the Canvas API
32
+ # Examples:
33
+ #
34
+ # notification_preferences[<X>][frequency]
35
+ # Generates:
36
+ # NotificationPreferences map[string]string `json:"notification_preferences"`
37
+ #
38
+ # calendar_event[child_event_data][X][start_at]
39
+ # Generates:
40
+ # type ChildEventData struct {
41
+ # StartAt time.Time `json:"start_at"` // (Optional)
42
+ # EndAt time.Time `json:"end_at"` // (Optional)
43
+ # ContextCode string `json:"context_code"` // (Optional)
44
+ # }
45
+ # CalendarEvent struct {
46
+ # ChildEventData map[string]*ChildEventData
47
+ # }
48
+ #
49
+ # module_item[completion_requirement][type]
50
+ #
51
+ # events[start_at]
52
+ #
53
+ # Nested param
54
+ # account_notification[subject]
55
+ # Generates:
56
+ # AccountNotification struct {
57
+ # Subject string `json:"subject"` // (Required)
58
+ # Message string `json:"message"` // (Required)
59
+ # StartAt time.Time `json:"start_at"` // (Required)
60
+ # EndAt time.Time `json:"end_at"` // (Required)
61
+ # Icon string `json:"icon"` // (Optional) . Must be one of warning, information, question, error, calendar
62
+ # } `json:"account_notification"`
63
+ #
64
+ def go_struct_fields(nickname, params)
65
+ nested = {}
66
+ params.each do |p|
67
+ structs, name = split_nested(p)
68
+ go_to_tree(nickname, nested, structs, name, p)
69
+ end
70
+ go_render_params(nested)
71
+ end
72
+
73
+ def go_render_params(nested)
74
+ out = ""
75
+ nested.each do |name, val|
76
+ if val["paramType"]
77
+ out << "\n" + go_param_to_field(val, name)
78
+ elsif val[:array_of]
79
+ out << "\n#{struct_name(name)} #{val[:array_of]}"
80
+ elsif val[:map_of]
81
+ out << "\n#{struct_name(name)} #{val[:map_of]}"
82
+ else
83
+ out << "\n#{struct_name(name)} struct {"
84
+ out << go_render_params(val)
85
+ out << "\n} `json:\"#{name.underscore.gsub("`", "")}\" url:\"#{name.underscore.gsub("`", "")},omitempty\"`\n"
86
+ end
87
+ end
88
+ out
89
+ end
90
+
91
+ def go_render_child_structs
92
+ out = ""
93
+ @child_structs&.each do |name, params|
94
+ out << "\ntype #{struct_name(name)} struct {"
95
+ params.each do |n, p|
96
+ out << "\n" + go_param_to_field(p, n)
97
+ end
98
+ out << "\n}\n"
99
+ end
100
+ out
101
+ end
102
+
103
+ # HACK for https://canvas.instructure.com/doc/api/quiz_submissions.html
104
+ # update_student_question_scores_and_comments has a param with the following form
105
+ # {"paramType"=>"form", "name"=>"quiz_submissions[questions]", "type"=>"array", "format"=>nil, "required"=>false, "deprecated"=>false, "items"=>{"$ref"=>"Hash"}}
106
+ QuizSubmissionOverrides = "QuizSubmissionOverrides"
107
+
108
+ def go_to_tree(nickname, nested, structs, name, param)
109
+ @child_structs ||= {}
110
+
111
+ # HACK for https://canvas.instructure.com/doc/api/quiz_submissions.html
112
+ if nickname == "update_student_question_scores_and_comments"
113
+ @child_structs[QuizSubmissionOverrides] = {
114
+ "score" => {"name"=>"score", "type"=>"float"},
115
+ "comment" => {"name"=>"comment", "type"=>"string"},
116
+ }
117
+ end
118
+
119
+ if structs.length > 0
120
+ struct, rest = structs.first, structs[1..-1]
121
+ nested[struct] ||= {}
122
+ if rest
123
+ if is_x_param?(rest[1])
124
+ type = go_property_type(name, param)
125
+ child_name = rest[0]
126
+ child_struct = "#{struct_name(nickname)}#{struct_name(child_name)}"
127
+ @child_structs[child_struct] ||= {}
128
+ @child_structs[child_struct][name] = param
129
+ nested[struct][child_name] = {
130
+ "name" => "#{struct}[#{child_name}]",
131
+ "type" => "map[string]#{child_struct}",
132
+ "paramType" => param["paramType"],
133
+ "keep_type" => true,
134
+ }
135
+ rest.shift
136
+ rest.shift
137
+ elsif is_x_param?(rest[0])
138
+ if rest[0] == structs[1]
139
+ child_name = structs[0]
140
+ child_struct = "#{struct_name(nickname)}#{struct_name(child_name)}"
141
+ nested[struct][:map_of] = "map[string]#{child_struct}"
142
+ @child_structs[child_struct] ||= {}
143
+ @child_structs[child_struct][name] = param
144
+ else
145
+ type = go_property_type(name, param)
146
+ nested[struct][:map_of] = "map[string]#{type}"
147
+ end
148
+ rest.shift
149
+ end
150
+ end
151
+
152
+ if rest && rest.length > 0
153
+ go_to_tree(nickname, nested[struct], rest, name, param)
154
+ else
155
+ if @child_structs && child_struct = nested[struct][:array_of]
156
+ @child_structs[child_struct][name] = param
157
+ else
158
+ nested[struct][name] = param
159
+ end
160
+ end
161
+ else
162
+ nested[name] = param
163
+ if param["type"] == "array" && ["events"].include?(param["name"])
164
+ child_struct = "#{struct_name(nickname)}#{struct_name(param["name"])}"
165
+ @child_structs ||= {}
166
+ @child_structs[child_struct] ||= {}
167
+ nested[name][:array_of] = child_struct
168
+ puts "******** Using custom struct #{child_struct}"
169
+ end
170
+ end
171
+ end
172
+
173
+ def go_param_path(param)
174
+ if is_x_param?(param["name"])
175
+ "#{go_param_kind(param)}.#{go_name(param["name"])}"
176
+ elsif is_nested?(param)
177
+ structs, name = split_nested(param)
178
+ "#{go_param_kind(param)}.#{structs.map{|s| struct_name(s)}.join('.')}.#{go_name(name)}"
179
+ else
180
+ "#{go_param_kind(param)}.#{go_name(param["name"])}"
181
+ end
182
+ end
183
+
184
+ def is_x_param?(name)
185
+ if name
186
+ name.include?("[<X>]") ||
187
+ name.include?("<X>") ||
188
+ name.include?("X") ||
189
+ name.include?("<student_id>") ||
190
+ name.include?("0")
191
+ end
192
+ end
193
+
194
+ def is_nested?(param)
195
+ param["name"].include?("[")
196
+ end
197
+
198
+ def is_array(param)
199
+ param["type"] == "array"
200
+ end
201
+
202
+ def split_nested(param)
203
+ parts = param["name"].split("[").map{|p| p.gsub("]", "")}
204
+ [
205
+ parts[0...-1],
206
+ parts.last,
207
+ ]
208
+ end
209
+
210
+ def go_param_to_field(parameter, name = nil)
211
+ name ||= parameter["name"]
212
+ type = go_type(name, parameter)
213
+ go_declaration(name, type) + " // " + go_comments(parameter, false)
214
+ end
215
+
216
+ def go_comments(parameter, include_description = true)
217
+ out = " (#{parameter["required"] ? 'Required' : 'Optional'}) "
218
+ if parameter["enum"]
219
+ out << ". Must be one of #{parameter["enum"].join(', ')}"
220
+ end
221
+ if include_description && parameter["description"]
222
+ out << parameter["description"].gsub("\n", "\n// ")
223
+ end
224
+ out
225
+ end
226
+
227
+ def go_parameter_doc(parameter)
228
+ name = parameter["name"]
229
+ out = "# #{go_param_path(parameter)}"
230
+ out << go_comments(parameter)
231
+ out
232
+ end
233
+
234
+ def go_param_empty_value(parameter)
235
+ name = parameter["name"]
236
+ if is_x_param?(name)
237
+ return "nil"
238
+ end
239
+ type = go_type(name, parameter)
240
+ case type
241
+ when "int64"
242
+ when "int"
243
+ when "float64"
244
+ "0"
245
+ when "string"
246
+ '""'
247
+ when "time.Time"
248
+ "nil"
249
+ when "map[string](interface{})"
250
+ "nil"
251
+ else
252
+ if type.include?("[]")
253
+ "nil"
254
+ else
255
+ "need empty value for #{type}"
256
+ end
257
+ end
258
+ end
259
+
260
+ def is_required_field(parameter)
261
+ parameter["required"] && !["bool", "int64", "int", "float64"].include?(go_type(parameter["name"], parameter))
262
+ end
263
+
264
+ def go_declaration(name, type)
265
+ json = name.underscore.split("[")[0].gsub("`rlid`", "rlid")
266
+ out = "#{go_name(name)} #{type} `json:\"#{json}\" url:\"#{json},omitempty\"`"
267
+ end
268
+
269
+ def go_name(name)
270
+ parts = name.split("[")
271
+ parts[0].camelize.gsub("-", "").gsub("_", "")
272
+ .gsub("`rlid`", "RLID")
273
+ .gsub("Id", "ID")
274
+ .gsub("url", "URL")
275
+ .gsub("Sis", "SIS")
276
+ .gsub("MediaTrackk", "MediaTrack")
277
+ .gsub("Https:::::Canvas.instructure.com::Lti::Submission", "CanvasLTISubmission")
278
+ end
279
+
280
+ def struct_name(type)
281
+ # Remove chars and fix spelling errors
282
+ cleaned = type.split('|').first.strip.gsub(" ", "_")
283
+ go_name(cleaned)
284
+ end
285
+
286
+ def go_require_models(parameters, nickname, return_type)
287
+ parameters.any? { |p| go_type(p["name"], p).include?("models") } ||
288
+ ["assign_unassigned_members"].include?(@nickname) ||
289
+ (return_type &&
290
+ return_type != "bool" &&
291
+ !return_type.include?("string") &&
292
+ !return_type.include?("SuccessResponse") &&
293
+ !return_type.include?("UnreadCount")
294
+ )
295
+ end
296
+
297
+ def time_required?(parameters)
298
+ parameters.any? { |p| go_type(p["name"], p).include?("time.Time") }
299
+ end
300
+
301
+ def go_type(name, property, return_type = false, model = nil, namespace = "models.")
302
+ if property["$ref"]
303
+ "*#{namespace}#{struct_name(property['$ref'])}"
304
+ else
305
+ go_property_type(name, property, return_type, model, namespace)
306
+ end
307
+ end
308
+
309
+ def go_property_type(name, property, return_type = false, model = nil, namespace = "models.")
310
+ return property["type"] if property["keep_type"]
311
+ return property[:array_of] if property[:array_of]
312
+
313
+ # Canvas API docs are wrong for these so we HACK in the right type
314
+ return "float64" if name.downcase == "points_possible"
315
+
316
+ type = property["type"].downcase
317
+ case type
318
+ when "{success: true}"
319
+ "canvasapi.SuccessResponse"
320
+ when "integer", "string", "boolean", "datetime", "number", "date"
321
+ go_primitive(name, type, property["format"])
322
+ when "void"
323
+ "bool" # TODO this doesn't seem right?
324
+ when "array"
325
+ go_ref_property_type(property, namespace)
326
+ when "object"
327
+ puts "Using string type for '#{name}' ('#{property}') of type object."
328
+ "map[string](interface{})"
329
+ else
330
+ if property["type"] == "list of content items"
331
+ # HACK There's no list of content items object so we return an array of string
332
+ "[]string"
333
+ elsif property["type"].include?('{ "unread_count": "integer" }')
334
+ "canvasapi.UnreadCount"
335
+ elsif return_type
336
+ "*#{namespace}#{struct_name(property["type"])}"
337
+ elsif property["type"] == "Hash"
338
+ "map[string](interface{})"
339
+ elsif property["type"] == "String[]"
340
+ "[]string"
341
+ elsif property["type"] == "[Answer]"
342
+ "[]*models.Answer"
343
+ elsif property["type"] == "QuizUserConversation"
344
+ "canvasapi.QuizUserConversation"
345
+ elsif [
346
+ "Assignment",
347
+ "BlueprintRestriction",
348
+ "RubricAssessment",
349
+ ].include?(property["type"])
350
+ "*models.#{property["type"]}"
351
+ elsif property["type"] == "multiple BlueprintRestrictions"
352
+ "[]*models.BlueprintRestriction"
353
+ elsif property["type"] == "File"
354
+ # This won't work. If we ever need to use this type we'll need to do some refactoring
355
+ "string"
356
+ elsif property["type"] == "Deprecated"
357
+ "string"
358
+ elsif property["type"] == "SerializedHash"
359
+ # Not sure this will work
360
+ "map[string](interface{})"
361
+ elsif property["type"].downcase == "json"
362
+ "map[string](interface{})"
363
+ elsif ["Numeric", "float"].include?(property["type"])
364
+ "float64"
365
+ elsif property["type"] == "GroupMembership | Progress"
366
+ "no-op" # this is handled further up the stack
367
+ elsif property["type"] == "URL"
368
+ "string"
369
+ else
370
+ raise "Unable to match '#{name}' requested property '#{property}' to Go Type."
371
+ end
372
+ end
373
+ end
374
+
375
+ def go_ref_property_type(property, namespace)
376
+ ref_type = property.dig("items", "$ref")
377
+ if ref_type == nil
378
+ if property["type"] == "array"
379
+ "[]string"
380
+ else
381
+ "string"
382
+ end
383
+ elsif ref_type == "Hash"
384
+ # HACK for https://canvas.instructure.com/doc/api/quiz_submissions.html
385
+ if property["name"] == "quiz_submissions[questions]"
386
+ "map[string]QuizSubmissionOverrides"
387
+ else
388
+ raise "No type available for #{property}"
389
+ end
390
+ elsif ref_type == "[Integer]"
391
+ "[]int"
392
+ elsif ref_type == "Array"
393
+ "[]string"
394
+ elsif ref_type == "[String]"
395
+ "[]string"
396
+ elsif ref_type == "DateTime" || ref_type == "Date"
397
+ "[]time.Time"
398
+ elsif ref_type == "object"
399
+ "map[string](interface{})"
400
+ elsif ref_type
401
+ # HACK on https://canvas.instructure.com/doc/api/submissions.json
402
+ # the ref value is set to a full sentence rather than a
403
+ # simple type, so we look for that specific value
404
+ if ref_type.include?("UserDisplay if anonymous grading is not enabled")
405
+ "[]*#{namespace}UserDisplay"
406
+ elsif ref_type.include?("Url String The url to the result that was created")
407
+ "string"
408
+ else
409
+ "[]*#{namespace}#{struct_name(ref_type)}"
410
+ end
411
+ else
412
+ "[]#{go_primitive(name, property["items"]["type"].downcase, property["items"]["format"])}"
413
+ end
414
+ rescue
415
+ raise "Unable to discover Go list type for '#{name}' ('#{property}')."
416
+ end
417
+
418
+ def go_primitive(name, type, format)
419
+ case type
420
+ when "integer"
421
+ if name.end_with?("_ids")
422
+ "[]int64"
423
+ else
424
+ "int64"
425
+ end
426
+ when "number"
427
+ if format == "Float"
428
+ "float64"
429
+ else
430
+ # TODO many of the LMS types with 'number' don't indicate a type so we have to guess
431
+ # Hopefully that changes. For now we go with float
432
+ "float64"
433
+ end
434
+ when "string"
435
+ "string"
436
+ when "boolean"
437
+ "bool"
438
+ when "datetime"
439
+ "time.Time"
440
+ when "date"
441
+ "time.Time"
442
+ else
443
+ raise "Unable to match requested primitive '#{type}' to Go Type."
444
+ end
445
+ end
446
+
447
+ def go_field_validation(model)
448
+ return nil unless model["properties"]
449
+ allowable = {}
450
+ model["properties"].each do |name, property|
451
+ if property["allowableValues"]
452
+ values = property["allowableValues"]["values"].map do |value|
453
+ "\"#{value}\""
454
+ end
455
+ allowable[name] = {
456
+ values: values,
457
+ type: property["type"],
458
+ }
459
+ end
460
+ end
461
+ allowable
462
+ end
463
+
464
+ def go_param_kind(parmeter)
465
+ case parmeter["paramType"]
466
+ when "path"
467
+ "Path"
468
+ when "query"
469
+ "Query"
470
+ when "form"
471
+ "Form"
472
+ else
473
+ "Unknown paramType"
474
+ end
475
+ end
476
+
477
+ def go_path_params(params)
478
+ select_params("path", params)
479
+ end
480
+
481
+ def go_query_params(params)
482
+ select_params("query", params)
483
+ end
484
+
485
+ def go_form_params(params)
486
+ select_params("form", params)
487
+ end
488
+
489
+ def select_params(type, parameters)
490
+ params = parameters.select{|p| p["paramType"] == type}
491
+ if params && !params.nil? && params.length > 0
492
+ params
493
+ else
494
+ nil
495
+ end
496
+ end
497
+
498
+ def go_api_url
499
+ url = @api_url
500
+ @args.each do |arg|
501
+ url.gsub(arg, "+\"#{go_name(arg)}\"+")
502
+ end
503
+ url
504
+ end
505
+
506
+ def is_paged?(operation)
507
+ operation["type"] == "array"
508
+ end
509
+
510
+ def next_param(operation)
511
+ if is_paged?(operation)
512
+ ", next *url.URL"
513
+ end
514
+ end
515
+
516
+ def go_do_return_statement(operation, nickname)
517
+ if nickname == "assign_unassigned_members" || is_paged?(operation)
518
+ "return nil, nil, err"
519
+ elsif type = go_return_type(operation)
520
+ if type == "bool"
521
+ "return false, err"
522
+ elsif type == "string"
523
+ 'return "", err'
524
+ elsif type == "integer"
525
+ "return 0, err"
526
+ else
527
+ "return nil, err"
528
+ end
529
+ else
530
+ "return err"
531
+ end
532
+ end
533
+
534
+ def go_do_final_return_statement(operation, nickname)
535
+ if nickname == "assign_unassigned_members"
536
+ "return &groupMembership, &progress, nil"
537
+ elsif go_return_type(operation)
538
+ if is_paged?(operation)
539
+ "return ret, pagedResource, nil"
540
+ elsif operation["type"] == "boolean" || operation["type"] == "string" || operation["type"] == "integer"
541
+ "return ret, nil"
542
+ else
543
+ "return &ret, nil"
544
+ end
545
+ else
546
+ "return nil"
547
+ end
548
+ end
549
+
550
+ def go_do_return_value(operation, nickname)
551
+ if nickname == "assign_unassigned_members"
552
+ # HACK. harded coded because Assign unassigned members returns different values based on input
553
+ # see https://canvas.instructure.com/doc/api/group_categories.html#method.group_categories.assign_unassigned_members
554
+ "(*models.GroupMembership, *models.Progress, error)"
555
+ elsif type = go_return_type(operation)
556
+ if is_paged?(operation)
557
+ "(#{type}, *canvasapi.PagedResource, error)"
558
+ else
559
+ "(#{type}, error)"
560
+ end
561
+ else
562
+ "error"
563
+ end
564
+ end
565
+
566
+ def go_return_type(operation, is_decl = false)
567
+ prefix = is_decl ? "" : "*"
568
+ suffix = is_decl ? "{}" : ""
569
+ if operation["type"] == "void"
570
+ nil
571
+ elsif is_paged?(operation)
572
+ model = operation.dig("items", "$ref")
573
+ if model.include?(" ")
574
+ # Handle cases with spaces using go_property_type
575
+ type = go_property_type(operation["nickname"], operation)
576
+ if type == "string"
577
+ type = "[]#{type}"
578
+ end
579
+ "#{type}#{suffix}"
580
+ else
581
+ "[]*models.#{go_name(model)}#{suffix}"
582
+ end
583
+ elsif operation["type"] == "boolean"
584
+ "bool"
585
+ elsif operation["type"] == "integer"
586
+ "int64"
587
+ elsif model = operation["type"]
588
+ if model.include?(" ")
589
+ # Handle cases with spaces using go_property_type
590
+ type = go_property_type(operation["nickname"], operation)
591
+ if type == "string"
592
+ type
593
+ else
594
+ "#{prefix}#{type}#{suffix}"
595
+ end
596
+ else
597
+ "#{prefix}models.#{go_name(model)}#{suffix}"
598
+ end
599
+ else
600
+ raise "No return type found for #{operation}"
601
+ end
602
+ end
603
+ end
604
+ end
@@ -0,0 +1,17 @@
1
+ module CanvasApi
2
+ def nested_arg(str)
3
+ # TODO/HACK we are replacing values from the string here to get things to work for now.
4
+ # However, removing these symbols means that the methods that use the arguments
5
+ # generated herein will have bugs and be unusable.
6
+ str.gsub("[", "_").
7
+ gsub("]", "").
8
+ gsub("*", "star").
9
+ gsub("<", "_").
10
+ gsub(">", "_").
11
+ gsub("`", "").
12
+ gsub("https://canvas.instructure.com/lti/", "").
13
+ gsub("https://www.instructure.com/", "").
14
+ gsub("https://purl.imsglobal.org/spec/lti/claim/", "").
15
+ gsub(".", "")
16
+ end
17
+ end
@@ -93,6 +93,13 @@ module CanvasApi
93
93
  return str unless str.is_a?(String)
94
94
  str.gsub('"', "'")
95
95
  end
96
+
97
+ def graphql_resolver_class(name)
98
+ # HACK Some resolvers have both singular and plural versions, so keep the plural on those
99
+ return name.camelize if name == "get_custom_colors"
100
+
101
+ name.classify
102
+ end
96
103
  end
97
104
 
98
- end
105
+ end
@@ -9,7 +9,7 @@ module CanvasApi
9
9
  if property["$ref"]
10
10
  canvas_name(property['$ref'], input_type)
11
11
  elsif property["allowableValues"]
12
- enum_class_name(model, name)
12
+ enum_class_name(model, name, input_type)
13
13
  else
14
14
  type = property["type"].downcase
15
15
  case type
@@ -106,11 +106,11 @@ module CanvasApi
106
106
  end
107
107
  end
108
108
 
109
- def enum_class_name(model, field_name)
110
- "#{model['id'].classify}#{field_name.classify}Enum"
109
+ def enum_class_name(model, field_name, input_type)
110
+ "#{model['id'].classify}#{input_type ? 'Input' : ''}#{field_name.classify}Enum"
111
111
  end
112
112
 
113
- def graphql_field_enums(model)
113
+ def graphql_field_enums(model, input_type = false)
114
114
  return unless model["properties"]
115
115
  enums = model["properties"].map do |name, property|
116
116
  if property["allowableValues"]
@@ -118,7 +118,7 @@ module CanvasApi
118
118
  "value \"#{value}\""
119
119
  end.join("\n ")
120
120
  <<-CODE
121
- class #{enum_class_name(model, name)} < ::GraphQL::Schema::Enum
121
+ class #{enum_class_name(model, name, input_type)} < ::GraphQL::Schema::Enum
122
122
  #{values}
123
123
  end
124
124
  CODE
@@ -200,7 +200,7 @@ field :#{name.underscore}, #{type}, "#{description}", resolver_method: :resolve_
200
200
  end
201
201
 
202
202
  def make_file_name(str)
203
- str.underscore.split("/").last.split("|").first.gsub("canvas_", "").gsub(" ", "_").strip.singularize
203
+ str.underscore.split("/").last.split("|").first.gsub(/^canvas_?/, "").gsub(" ", "_").strip.singularize
204
204
  end
205
205
 
206
206
  def require_from_operation(operation)
@@ -232,22 +232,6 @@ field :#{name.underscore}, #{type}, "#{description}", resolver_method: :resolve_
232
232
  str.gsub('"', "'")
233
233
  end
234
234
 
235
- def nested_arg(str)
236
- # TODO/HACK we are replacing values from the string here to get things to work for now.
237
- # However, removing these symbols means that the methods that use the arguments
238
- # generated herein will have bugs and be unusable.
239
- str.gsub("[", "_").
240
- gsub("]", "").
241
- gsub("*", "star").
242
- gsub("<", "_").
243
- gsub(">", "_").
244
- gsub("`", "").
245
- gsub("https://canvas.instructure.com/lti/", "").
246
- gsub("https://www.instructure.com/", "").
247
- gsub("https://purl.imsglobal.org/spec/lti/claim/", "").
248
- gsub(".", "")
249
- end
250
-
251
235
  def params_as_string(parameters, paramTypes)
252
236
  filtered = parameters.select{ |p| paramTypes.include?(p["paramType"]) }
253
237
  if filtered && !filtered.empty?