• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MushroomObserver / mushroom-observer / 28292113828

27 Jun 2026 02:31PM UTC coverage: 98.442% (+0.05%) from 98.391%
28292113828

Pull #4612

github

JoeCohen
Add API2Controller#index to handle bad /api2 root requests
Pull Request #4612: Add API2Controller#index to handle bad /api2 root requests

6 of 6 new or added lines in 1 file covered. (100.0%)

3 existing lines in 1 file now uncovered.

49083 of 49860 relevant lines covered (98.44%)

745.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.77
/app/controllers/api2_controller.rb
1
# frozen_string_literal: true
2

3
#
4
#  = API2 Controller
5
#
6
#  This controller handles the JSON and XML interfaces
7
#
8
#  == Actions
9
#
10
#  observations, etc.::   Entry point for REST requests.
11
#
12
################################################################################
13
#
14
class API2Controller < ApplicationController
4✔
15
  require("xmlrpc/client")
4✔
16
  require("api2")
4✔
17

18
  disable_filters
4✔
19

20
  # wrapped parameters break JSON requests in the unit tests.
21
  wrap_parameters false
4✔
22

23
  def index
4✔
24
    render(json: { errors: ["Use /api2/{resource} — e.g. /api2/images"] },
1✔
25
           status: :bad_request)
26
  end
27

28
  # Standard entry point for REST requests.
29
  def api_keys
4✔
30
    rest_query(:api_key)
2✔
31
  end
32

33
  def collection_numbers
4✔
34
    rest_query(:collection_number)
6✔
35
  end
36

37
  def comments
4✔
38
    rest_query(:comment)
6✔
39
  end
40

41
  def external_links
4✔
42
    rest_query(:external_link)
6✔
43
  end
44

45
  def external_sites
4✔
46
    rest_query(:external_site)
6✔
47
  end
48

49
  def field_slips
4✔
50
    rest_query(:field_slip)
10✔
51
  end
52

53
  def herbaria
4✔
54
    rest_query(:herbarium)
6✔
55
  end
56

57
  def herbarium_records
4✔
58
    rest_query(:herbarium_record)
6✔
59
  end
60

61
  def images
4✔
62
    rest_query(:image)
11✔
63
  end
64

65
  def locations
4✔
66
    rest_query(:location)
7✔
67
  end
68

69
  def location_descriptions
4✔
70
    rest_query(:location_description)
6✔
71
  end
72

73
  def names
4✔
74
    rest_query(:name)
7✔
75
  end
76

77
  def name_descriptions
4✔
78
    rest_query(:name_description)
6✔
79
  end
80

81
  def observations
4✔
82
    rest_query(:observation)
30✔
83
  end
84

85
  def occurrences
4✔
86
    rest_query(:occurrence)
6✔
87
  end
88

89
  def projects
4✔
90
    rest_query(:project)
6✔
91
  end
92

93
  def sequences
4✔
94
    rest_query(:sequence)
7✔
95
  end
96

97
  def species_lists
4✔
98
    rest_query(:species_list)
6✔
99
  end
100

101
  def users
4✔
102
    rest_query(:user)
7✔
103
  end
104

105
  ##############################################################################
106

107
  private
4✔
108

109
  def rest_query(type)
4✔
110
    @start_time = Time.zone.now
147✔
111
    args = params_to_api_args(type)
147✔
112

113
    if request.method == "POST"
147✔
114
      if args[:upload].present?
13✔
115
        args[:upload] = upload_from_multipart_form_data(args[:upload])
2✔
116
        logger.warn("API UPLOAD: #{args[:upload].inspect}")
2✔
117
      elsif is_request_body_an_upload?
11✔
118
        args[:upload] = upload_from_request_body
2✔
119
      end
120
      # Special exception to let caller who creates new user to see that user's
121
      # new API keys.  Otherwise there is no way to get that info via the API.
122
      @show_api_keys_for_new_user = true if type == :user
13✔
123
    end
124

125
    render_api_results(args)
147✔
126
  end
127

128
  # Massage params hash to proper args hash for api
129
  def params_to_api_args(type)
4✔
130
    args = params.to_unsafe_h.symbolize_keys.except(:controller)
147✔
131
    args[:method] = request.method
147✔
132
    args[:action] = type
147✔
133
    args.delete(:format)
147✔
134
    args
147✔
135
  end
136

137
  def upload_from_multipart_form_data(data)
4✔
138
    API2::Upload.new(data: data)
2✔
139
  end
140

141
  def is_request_body_an_upload?
4✔
142
    request.content_length.positive? &&
11✔
143
      request.media_type.present? &&
144
      request.media_type != "application/x-www-form-urlencoded" &&
145
      request.media_type != "multipart/form-data" &&
146
      request.body.present?
147
  end
148

149
  def upload_from_request_body
4✔
150
    API2::Upload.new(
2✔
151
      data: request.body,
152
      length: request.content_length,
153
      content_type: request.media_type,
154
      checksum: request.headers["CONTENT_MD5"].to_s
155
    )
156
  end
157

158
  def render_api_results(args)
4✔
159
    @user = user_from_key(args[:api_key])
147✔
160
    @api = API2.execute(args)
147✔
161
    do_render
147✔
162
  rescue StandardError => e
UNCOV
163
    @api ||= API2.new
×
UNCOV
164
    @api.errors << API2::RenderFailed.new(e)
×
UNCOV
165
    do_render
×
166
  end
167

168
  def user_from_key(key)
4✔
169
    APIKey.find_by(key: key)&.user
147✔
170
  end
171

172
  def do_render
4✔
173
    set_cors_headers
147✔
174
    request.format = "json" if request.format == "html"
147✔
175
    respond_to do |format|
147✔
176
      format.xml  { do_render_xml  }
217✔
177
      format.json { do_render_json }
224✔
178
    end
179
  end
180

181
  def do_render_xml
4✔
182
    render(layout: false, template: "/api2/results")
70✔
183
  end
184

185
  def do_render_json
4✔
186
    render(layout: false, template: "/api2/results")
77✔
187
  end
188

189
  def set_cors_headers
4✔
190
    return unless request.method == "GET"
147✔
191

192
    response.set_header("Access-Control-Allow-Origin", "*")
134✔
193
    response.set_header("Access-Control-Allow-Headers",
134✔
194
                        "Origin, X-Requested-With, Content-Type, Accept")
195
    response.set_header("Access-Control-Allow-Methods", "GET")
134✔
196
  end
197
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc