lms-api 1.21.0 → 1.24.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.
@@ -6,10 +6,14 @@ module CanvasApi
6
6
  CONFLICTING_NAMES = ["end", "context", "next", "module"]
7
7
 
8
8
  def graphql_type(name, property, return_type = false, model = nil, input_type = false)
9
- if property["$ref"]
9
+ if property["nickname"] == "get_bulk_user_progress"
10
+ # custom type specific to the get_bulk_user_progress endpoint. The type from canvas is void but
11
+ # we have to use a custom type in order to return data.
12
+ "LMSGraphQL::Types::CanvasBespoke::CanvasModuleUser"
13
+ elsif property["$ref"]
10
14
  canvas_name(property['$ref'], input_type)
11
15
  elsif property["allowableValues"]
12
- enum_class_name(model, name)
16
+ enum_class_name(model, name, input_type)
13
17
  else
14
18
  type = property["type"].downcase
15
19
  case type
@@ -28,7 +32,7 @@ module CanvasApi
28
32
  elsif property["items"]["$ref"] == "[String]"
29
33
  "[String]"
30
34
  elsif property["items"]["$ref"] == "DateTime" || property["items"]["$ref"] == "Date"
31
- "[LMSGraphQL::Types::DateTimeType]"
35
+ "[GraphQL::Types::ISO8601DateTime]"
32
36
  elsif property["items"]["$ref"]
33
37
  # HACK on https://canvas.instructure.com/doc/api/submissions.json
34
38
  # the ref value is set to a full sentence rather than a
@@ -41,26 +45,36 @@ module CanvasApi
41
45
  "[#{canvas_name(property["items"]["$ref"], input_type)}]"
42
46
  end
43
47
  else
44
- graphql_primitive(name, property["items"]["type"].downcase, property["items"]["format"])
48
+ array_type = graphql_primitive(name, property["items"]["type"].downcase, property["items"]["format"])
49
+ array_type = "[#{array_type}]" if array_type != "[ID]"
50
+ array_type
45
51
  end
46
52
  rescue
47
53
  puts "Unable to discover list type for '#{name}' ('#{property}'). Defaulting to String"
48
- type = "String"
54
+ type = "[String]"
49
55
  end
50
56
  type
51
57
  when "object"
52
58
  puts "Using string type for '#{name}' ('#{property}') of type object."
53
59
  "String"
54
60
  else
55
- if property["type"] == "TermsOfService"
61
+ if property["type"] == "array of outcome ids"
62
+ "[String]"
63
+ elsif property["type"] == "TermsOfService"
56
64
  # HACK There's no TermsOfService object so we return a string
57
65
  "String"
58
66
  elsif property["type"] == "list of content items"
59
67
  # HACK There's no list of content items object so we return an array of string
60
68
  "[String]"
69
+ elsif property["type"] == "uuid"
70
+ # uuid is a string
71
+ "String"
61
72
  elsif property["type"].include?('{ "unread_count": "integer" }')
62
73
  # HACK TODO this should probably be a different type.
63
74
  "Int"
75
+ elsif property["type"].include?('{ "count": "integer" }')
76
+ # HACK TODO this should probably be a different type.
77
+ "Int"
64
78
  elsif return_type
65
79
  canvas_name(property["type"], input_type)
66
80
  else
@@ -74,6 +88,12 @@ module CanvasApi
74
88
  def canvas_name(type, input_type = false)
75
89
  # Remove chars and fix spelling errors
76
90
  name = type.split('|').first.strip.gsub(" ", "_").singularize.gsub("MediaTrackk", "MediaTrack")
91
+
92
+ # Handle comment in type
93
+ if name.include?("BlackoutDate_The_result_(which_should_match_the_input_with_maybe_some_different_IDs).")
94
+ name = "BlackoutDate"
95
+ end
96
+
77
97
  "LMSGraphQL::Types::Canvas::Canvas#{name}#{input_type ? 'Input' : ''}"
78
98
  end
79
99
 
@@ -96,19 +116,19 @@ module CanvasApi
96
116
  when "boolean"
97
117
  "Boolean"
98
118
  when "datetime"
99
- "LMSGraphQL::Types::DateTimeType"
119
+ "GraphQL::Types::ISO8601DateTime"
100
120
  when "date"
101
- "LMSGraphQL::Types::DateTimeType"
121
+ "GraphQL::Types::ISO8601DateTime"
102
122
  else
103
123
  raise "Unable to match requested primitive '#{type}' to GraphQL Type."
104
124
  end
105
125
  end
106
126
 
107
- def enum_class_name(model, field_name)
108
- "#{model['id'].classify}#{field_name.classify}Enum"
127
+ def enum_class_name(model, field_name, input_type)
128
+ "#{model['id'].classify}#{input_type ? 'Input' : ''}#{field_name.classify}Enum"
109
129
  end
110
130
 
111
- def graphql_field_enums(model)
131
+ def graphql_field_enums(model, input_type = false)
112
132
  return unless model["properties"]
113
133
  enums = model["properties"].map do |name, property|
114
134
  if property["allowableValues"]
@@ -116,7 +136,7 @@ module CanvasApi
116
136
  "value \"#{value}\""
117
137
  end.join("\n ")
118
138
  <<-CODE
119
- class #{enum_class_name(model, name)} < ::GraphQL::Schema::Enum
139
+ class #{enum_class_name(model, name, input_type)} < ::GraphQL::Schema::Enum
120
140
  #{values}
121
141
  end
122
142
  CODE
@@ -190,7 +210,7 @@ field :#{name.underscore}, #{type}, "#{description}", resolver_method: :resolve_
190
210
  end
191
211
 
192
212
  def is_basic_type(type)
193
- ["Int", "String", "Boolean", "LMSGraphQL::Types::DateTimeType", "Float", "ID"].include?(type)
213
+ ["Int", "String", "Boolean", "GraphQL::Types::ISO8601DateTime", "Float", "ID"].include?(type)
194
214
  end
195
215
 
196
216
  def no_brackets_period(str)
@@ -198,12 +218,18 @@ field :#{name.underscore}, #{type}, "#{description}", resolver_method: :resolve_
198
218
  end
199
219
 
200
220
  def make_file_name(str)
201
- str.underscore.split("/").last.split("|").first.gsub("canvas_", "").gsub(" ", "_").strip.singularize
221
+ str.underscore.split("/").last.split("|").first.gsub(/^canvas_?/, "").gsub(" ", "_").strip.singularize
222
+ end
223
+
224
+ def make_bespoke_file_name(str)
225
+ str.underscore.split("/").last.split("|").first.gsub(" ", "_").strip.singularize
202
226
  end
203
227
 
204
228
  def require_from_operation(operation)
205
229
  type = no_brackets_period(type_from_operation(@operation))
206
- if !is_basic_type(type)
230
+ if type.include?("CanvasBespoke")
231
+ "require_relative \"../../types/canvas_bespoke/#{make_bespoke_file_name(type)}\""
232
+ elsif !is_basic_type(type)
207
233
  "require_relative \"../../types/canvas/#{make_file_name(type)}\""
208
234
  end
209
235
  end
@@ -230,22 +256,6 @@ field :#{name.underscore}, #{type}, "#{description}", resolver_method: :resolve_
230
256
  str.gsub('"', "'")
231
257
  end
232
258
 
233
- def nested_arg(str)
234
- # TODO/HACK we are replacing values from the string here to get things to work for now.
235
- # However, removing these symbols means that the methods that use the arguments
236
- # generated herein will have bugs and be unusable.
237
- str.gsub("[", "_").
238
- gsub("]", "").
239
- gsub("*", "star").
240
- gsub("<", "_").
241
- gsub(">", "_").
242
- gsub("`", "").
243
- gsub("https://canvas.instructure.com/lti/", "").
244
- gsub("https://www.instructure.com/", "").
245
- gsub("https://purl.imsglobal.org/spec/lti/claim/", "").
246
- gsub(".", "")
247
- end
248
-
249
259
  def params_as_string(parameters, paramTypes)
250
260
  filtered = parameters.select{ |p| paramTypes.include?(p["paramType"]) }
251
261
  if filtered && !filtered.empty?
@@ -1,20 +1,26 @@
1
+ require "canvas_api/helpers"
1
2
  require "canvas_api/js_graphql_helpers"
2
3
  require "canvas_api/js_helpers"
3
4
  require "canvas_api/ruby_helpers"
4
5
  require "canvas_api/rb_graphql_helpers"
6
+ require "canvas_api/go_helpers"
5
7
  require "byebug"
8
+
6
9
  module CanvasApi
7
10
 
8
11
  class Render
12
+ include CanvasApi
9
13
  include CanvasApi::GraphQLHelpers
10
14
  include CanvasApi::JsHelpers
11
15
  include CanvasApi::RubyHelpers
16
+ include CanvasApi::GoHelpers
12
17
  attr_accessor :template, :description, :resource, :api_url, :operation,
13
18
  :args, :method, :api, :name, :resource_name, :resource_api,
14
19
  :nickname, :notes, :content, :summary, :model, :model_name
15
20
 
16
- def initialize(template, api, resource, resource_api, operation, parameters, content, model)
21
+ def initialize(template, api, resource, resource_api, operation, parameters, content, model, api_path)
17
22
  @template = File.read(File.expand_path(template, __dir__))
23
+ @api_path = api_path
18
24
  if api
19
25
  @api = api
20
26
  @name = @api["path"].gsub("/", "").gsub(".json", "")
@@ -31,6 +37,11 @@ module CanvasApi
31
37
  end
32
38
  if operation
33
39
  nickname = operation["nickname"]
40
+
41
+ if nickname == "save_enabled_account_calendars_creates_and_updates_enabled_account_calendars_and_mark_feature_as_seen_user_preferences_argument_mark_feature_as_seen_optional_boolean_flag_to_mark_account_calendars_feature_as_seen_argument_enabled_account_calendars_optional_array_array_of_account_ids_to_remember_in_calendars_list_of_user_curl_https_canvas_api_v_calendar_events_save_enabled_account_calendars_x_post_f_mark_feature_as_seen_true_f_enabled_account_calendars_f_enabled_account_calendars_h_authorization_bearer_token"
42
+ nickname = "save_enabled_account_calendars"
43
+ end
44
+
34
45
  nickname = "#{@name}_#{nickname}" if [
35
46
  "upload_file",
36
47
  "query_by_course",
@@ -47,7 +58,12 @@ module CanvasApi
47
58
  @summary = operation["summary"]
48
59
  end
49
60
  if parameters
50
- @parameters = parameters.map { |p| p.delete("description"); p }
61
+ # Keep a copy of full parameters for code that will include the description
62
+ @full_parameters = Marshal.load Marshal::dump(parameters)
63
+ # Strip description from parameters so that canvas_urls.rb
64
+ # doesn't error out on bad chars in the descriptions
65
+ tmp_params = Marshal.load Marshal::dump(parameters)
66
+ @parameters = tmp_params.map { |p| p.delete("description"); p }
51
67
  end
52
68
  @content = content
53
69
  @model = model
@@ -0,0 +1,241 @@
1
+ <% string_utils_required = @parameters.any?{|p| p["enum"] && !is_x_param?(p["name"]) } -%>
2
+ <% errors_required = @parameters.any?{|p| p["enum"] || is_required_field(p) }-%>
3
+ <% query_params = go_query_params(@parameters) -%>
4
+ <% path_params = go_path_params(@parameters) -%>
5
+ <% form_params = go_form_params(@parameters) -%>
6
+ <% return_type = go_return_type(operation) -%>
7
+ package requests
8
+
9
+ import (
10
+ <% if return_type && return_type != "bool" && return_type != "string" || @nickname == "assign_unassigned_members" || form_params -%>
11
+ "encoding/json"
12
+ <% end -%>
13
+ <% if path_params || errors_required -%>
14
+ "fmt"
15
+ <% end -%>
16
+ <% if !!return_type -%>
17
+ "io/ioutil"
18
+ <% end -%>
19
+ <% if is_paged?(@operation) -%>
20
+ "net/http"
21
+ <% end -%>
22
+ "net/url"
23
+ <% if return_type == "int64" -%>
24
+ "strconv"
25
+ <% end -%>
26
+ <% if errors_required || path_params -%>
27
+ "strings"
28
+ <% end -%>
29
+ <% if time_required?(@parameters) -%>
30
+ "time"
31
+ <% end -%>
32
+
33
+ <% if query_params || go_form_params(@parameters) -%>
34
+ "github.com/google/go-querystring/query"
35
+ <% end -%>
36
+
37
+ "github.com/atomicjolt/canvasapi"
38
+ <% if go_require_models(@parameters, @nickname, return_type)-%>
39
+ "github.com/atomicjolt/canvasapi/models"
40
+ <% end -%>
41
+ <% if string_utils_required -%>
42
+ "github.com/atomicjolt/string_utils"
43
+ <% end -%>
44
+ )
45
+
46
+ // <%= go_name(@nickname) %> <%= @notes %>
47
+ // https://canvas.instructure.com/doc/api<%=@api_path.gsub(".json", ".html")%>
48
+ <% if params = go_path_params(@full_parameters)-%>
49
+ //
50
+ // Path Parameters:
51
+ // <%= params.map { |p| go_parameter_doc(p) }.join("\n// ") %>
52
+ <% end -%>
53
+ <% if params = go_query_params(@full_parameters) -%>
54
+ //
55
+ // Query Parameters:
56
+ // <%= params.map { |p| go_parameter_doc(p) }.join("\n// ") %>
57
+ <% end -%>
58
+ <% if params = go_form_params(@full_parameters) -%>
59
+ //
60
+ // Form Parameters:
61
+ // <%= params.map { |p| go_parameter_doc(p) }.join("\n// ") %>
62
+ <% end -%>
63
+ //
64
+ type <%= struct_name(@nickname) %> struct {
65
+ <% if params = go_path_params(@parameters) -%>
66
+ Path struct {
67
+ <%= go_struct_fields(@nickname, params) %>
68
+ } `json:"path"`
69
+ <% end -%>
70
+
71
+ <% if query_params -%>
72
+ Query struct {
73
+ <%= go_struct_fields(@nickname, query_params) %>
74
+ } `json:"query"`
75
+ <% end -%>
76
+
77
+ <% if form_params -%>
78
+ Form struct {
79
+ <%= go_struct_fields(@nickname, form_params) %>
80
+ } `json:"form"`
81
+ <% end -%>
82
+ }
83
+
84
+ func (t *<%= struct_name(@nickname) %>) GetMethod() string {
85
+ return "<%=@method%>"
86
+ }
87
+
88
+ func (t *<%= struct_name(@nickname) %>) GetURLPath() string {
89
+ <% if params = go_path_params(@parameters) -%>
90
+ path := "<%= go_api_url %>"
91
+ <% params.each do |p| -%>
92
+ path = strings.ReplaceAll(path, "{<%=p["name"]%>}", fmt.Sprintf("%v", t.Path.<%=go_name(p["name"])%>))
93
+ <% end -%>
94
+ return path
95
+ <% else -%>
96
+ return ""
97
+ <% end -%>
98
+ }
99
+
100
+ func (t *<%= struct_name(@nickname) %>) GetQuery()(string, error) {
101
+ <% if query_params -%>
102
+ v, err := query.Values(t.Query)
103
+ if err != nil {
104
+ return "", err
105
+ }
106
+ return v.Encode(), nil
107
+ <% else -%>
108
+ return "", nil
109
+ <% end -%>
110
+ }
111
+
112
+ func (t *<%= struct_name(@nickname) %>) GetBody() (url.Values, error) {
113
+ <% if form_params -%>
114
+ return query.Values(t.Form)
115
+ <% else -%>
116
+ return nil, nil
117
+ <% end -%>
118
+ }
119
+
120
+ func (t *<%= struct_name(@nickname) %>) GetJSON() ([]byte, error) {
121
+ <% if form_params -%>
122
+ j, err := json.Marshal(t.Form)
123
+ if err != nil {
124
+ return nil, nil
125
+ }
126
+ return j, nil
127
+ <% else -%>
128
+ return nil, nil
129
+ <% end -%>
130
+ }
131
+
132
+ func (t *<%= struct_name(@nickname) %>) HasErrors() error {
133
+ <% if errors_required -%>
134
+ errs := []string{}
135
+ <% end -%>
136
+ <% @parameters.each do |p| -%>
137
+ <% type = go_type(p["name"], p) -%>
138
+ <% if is_required_field(p) -%>
139
+ <% if type == "time.Time" -%>
140
+ if t.<%=go_param_path(p)%>.IsZero() {
141
+ errs = append(errs, "'<%=go_param_path(p)%>' is required")
142
+ }
143
+ <% else -%>
144
+ if t.<%=go_param_path(p)%> == <%=go_param_empty_value(p)%> {
145
+ errs = append(errs, "'<%=go_param_path(p)%>' is required")
146
+ }
147
+ <% end -%>
148
+ <% end -%>
149
+ <% if p["enum"] && ["string", "[]string"].include?(type) -%>
150
+ <% if p["type"] == "array" || type == "[]string" -%>
151
+ for _, v := range t.<%=go_param_path(p)%> {
152
+ if v != "" && !string_utils.Include([]string{"<%= p["enum"].join("\", \"") %>"}, v) {
153
+ errs = append(errs, "<%=go_name(p["name"])%> must be one of <%= p["enum"].join(", ") %>")
154
+ }
155
+ }
156
+ <% elsif !is_x_param?(p["name"]) -%>
157
+ if t.<%=go_param_path(p)%> != "" && !string_utils.Include([]string{"<%= p["enum"].join("\", \"") %>"}, t.<%=go_param_path(p)%>) {
158
+ errs = append(errs, "<%=go_name(p["name"])%> must be one of <%= p["enum"].join(", ") %>")
159
+ }
160
+ <% end -%>
161
+ <% end -%>
162
+ <% end -%>
163
+ <% if errors_required -%>
164
+ if len(errs) > 0 {
165
+ return fmt.Errorf(strings.Join(errs, ", "))
166
+ }
167
+ <% end -%>
168
+ return nil
169
+ }
170
+
171
+ func (t *<%= struct_name(@nickname) %>) Do(c *canvasapi.Canvas<%=next_param(@operation)%>) <%=go_do_return_value(@operation, @nickname)%> {
172
+ <% ret_type = go_return_type(@operation, true) -%>
173
+ <% if ret_type -%>
174
+ <% if is_paged?(@operation) -%>
175
+ var err error
176
+ var response *http.Response
177
+ if next != nil {
178
+ response, err = c.Send(next, t.GetMethod(), nil)
179
+ } else {
180
+ response, err = c.SendRequest(t)
181
+ }
182
+
183
+ if err != nil {
184
+ return nil, nil, err
185
+ }
186
+ <% else -%>
187
+ response, err := c.SendRequest(t)
188
+ <% end -%>
189
+ <% else -%>
190
+ _, err := c.SendRequest(t)
191
+ <% end -%>
192
+ if err != nil {
193
+ <%= go_do_return_statement(operation, @nickname) %>
194
+ }
195
+
196
+ <% if ret_type -%>
197
+ body, err := ioutil.ReadAll(response.Body)
198
+ response.Body.Close()
199
+ if err != nil {
200
+ <%= go_do_return_statement(operation, @nickname) %>
201
+ }
202
+ <% if @nickname == "assign_unassigned_members" -%>
203
+ groupMembership := models.GroupMembership{}
204
+ progress := models.Progress{}
205
+ if t.Form.Sync {
206
+ err = json.Unmarshal(body, &groupMembership)
207
+ if err != nil {
208
+ return nil, nil, err
209
+ }
210
+ } else {
211
+ err = json.Unmarshal(body, &progress)
212
+ if err != nil {
213
+ return nil, nil, err
214
+ }
215
+ }
216
+ <% elsif ret_type == "bool" -%>
217
+ ret := string(body) == "true"
218
+ <% elsif ret_type == "string" -%>
219
+ ret := string(body)
220
+ <% elsif ret_type == "int64" -%>
221
+ ret := strconv.ParseInt(string(body), 10, 64)
222
+ <% else -%>
223
+ ret := <%=ret_type%>
224
+ err = json.Unmarshal(body, &ret)
225
+ if err != nil {
226
+ <%= go_do_return_statement(operation, @nickname) %>
227
+ }
228
+ <% end -%>
229
+ <% end -%>
230
+
231
+ <% if @operation["type"] == "array" -%>
232
+ pagedResource, err := canvasapi.ExtractPagedResource(response.Header)
233
+ if err != nil {
234
+ return nil, nil, err
235
+ }
236
+ <% end -%>
237
+
238
+ <%= go_do_final_return_statement(operation, @nickname) %>
239
+ }
240
+
241
+ <%= go_render_child_structs %>
@@ -0,0 +1,44 @@
1
+ <%
2
+ fields, time_required = struct_fields(@model, @resource_name)
3
+ field_validations = go_field_validation(@model)
4
+ -%>
5
+ package models
6
+
7
+ <% if time_required || field_validations -%>
8
+ import (
9
+ <% if time_required -%>
10
+ "time"
11
+ <% end -%>
12
+ <% if (field_validations && field_validations.length > 0) -%>
13
+ "fmt"
14
+
15
+ "github.com/atomicjolt/string_utils"
16
+ <% end -%>
17
+ )
18
+ <% end -%>
19
+
20
+ type <%=struct_name(@model['id'])%> struct {
21
+ <%=fields&.join("\n ")%>
22
+ }
23
+
24
+ func (t *<%=struct_name(@model['id'])%>) HasErrors() error {
25
+ <% if field_validations && field_validations.length > 0 -%>
26
+ var s []string
27
+ errs := []string{}
28
+ <%field_validations.each do |name, prop|-%>
29
+ s = []string{<%=prop[:values].join(",")%>}
30
+ <% if prop[:type] == "array" %>
31
+ for _, v := range t.<%=go_name(name)%> {
32
+ if v != "" && !string_utils.Include(s, v) {
33
+ errs = append(errs, fmt.Sprintf("expected '<%=go_name(name)%>' to be one of %v", s))
34
+ }
35
+ }
36
+ <% else -%>
37
+ if t.<%=go_name(name)%> != "" && !string_utils.Include(s, t.<%=go_name(name)%>) {
38
+ errs = append(errs, fmt.Sprintf("expected '<%=go_name(name)%>' to be one of %v", s))
39
+ }
40
+ <% end -%>
41
+ <% end -%>
42
+ <% end -%>
43
+ return nil
44
+ }
@@ -1,3 +1,3 @@
1
1
  field :<%= @nickname %>,
2
- resolver: LMSGraphQL::Resolvers::Canvas::<%= @nickname.classify %>,
3
- description: "<%= @summary %>. <%= @notes.gsub(/\n+/, " ").gsub("//", " ").gsub('"', "'") %>"
2
+ resolver: LMSGraphQL::Resolvers::Canvas::<%= graphql_resolver_class(@nickname) %>,
3
+ description: "<%= @summary %>. <%= @notes.gsub(/\n+/, " ").gsub("//", " ").gsub('"', "'") %>"
@@ -5,9 +5,9 @@ module LMSGraphQL
5
5
  module Types
6
6
  module Canvas
7
7
  class Canvas<%=@model['id'].singularize%>Input < BaseInputObject
8
- <%=graphql_field_enums(@model)-%>
8
+ <%=graphql_field_enums(@model, true)-%>
9
9
  description "<%=@description%>. API Docs: https://canvas.instructure.com/doc/api/<%=@name%>.html"
10
- <%=graphql_fields(@model, @resource_name, true, true).join(" ")%>
10
+ <%=graphql_fields(@model, @resource_name, true, true).join(" ")%>
11
11
  end
12
12
  end
13
13
  end
@@ -4,7 +4,7 @@ require_relative "../canvas_base_resolver"
4
4
  module LMSGraphQL
5
5
  module Resolvers
6
6
  module Canvas
7
- class <%= @nickname.classify %> < CanvasBaseResolver
7
+ class <%= graphql_resolver_class(@nickname) %> < CanvasBaseResolver
8
8
  type <%= type_from_operation(operation) %>, null: false
9
9
  <% if operation["type"] == "array"
10
10
  %> argument :get_all, Boolean, required: false