Class: DevicesController

Inherits:
ApplicationController show all
Defined in:
app/controllers/devices_controller.rb

Overview

DevicesController provides the mechanisim to authenticate a user, create a device associated to a user and remove devices

Constant Summary

Constant Summary

Constants inherited from ApplicationController

ApplicationController::SupportedClients

Instance Method Summary (collapse)

Methods inherited from ApplicationController

#absolutize_path, #clean_summary

Methods included from Authorization

included, ri?, #role_included?, #set_promotion_for_public_request

Instance Method Details

- (Integer) authenticate

Authenticates a device (or client) using an email and password

URL

/devices/authenticate [POST]

Response Codes
200 OK

Successful authentication

# Example response
1
401 Unauthorized

Failed authentication, invalid credentials

403 Forbidden

Failed authentication, organization does not allow access

400 Bad Request

Failed request, client failed to provide valid parameters

Parameters:

  • email (String)

    the email address of the user trying to authenticate

  • password (String)

    the password of the user trying to authenticate

Returns:

  • (Integer)


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'app/controllers/devices_controller.rb', line 35

def authenticate
  # Store the parameters
  email = params[:email]
  password = params[:password]
  
  # Verify that we actually have an email and password
  if email.nil? || password.nil?
    head :bad_request and return
  else
    # Have an email, so let's find the user
    user = User.find(:first, :include => [:contact, { :promotion => :organization} ], :conditions => ["contacts.email = ?", email], :order => "users.created_at DESC")
    if user
      # Verify that the organization allows access
      #if client_is_mobile? #&& user.promotion.is_mobile_enabled? == false
      #  head :forbidden and return
      # Organization allows access, so check for valid credentials
      #else
        # Make sure the password matches
        if verify_password_for_user(user,password)
          render :json => user.id, :status => :ok and return
        end
      #end
    end

    # If we've fallen all the way to this point, it's a 401
    head :unauthorized
  end
end

- (Device) create

Creates a device object for the specified `user_id`, password and device type (kind)

URL

/devices [POST]

Response Codes
201 Created

New device created for authenticated user

# Example response
{ "device":
   {
     "row_id": "3.device.1",
     "id":1, # (Integer) unique id of the device object
     "user_id":3, # (Integer) unique id of the user
     "key":"2cf4bffb13d746379d81bda95e498c1c", # (String) api key for the user
     "kind":"iPhone", # (String) type of device registered to the user
     "created_at":"2011-01-01T15:15:15Z", # (Time) when the device was created
     "updated_at":"2011-01-01T15:15:15Z", # (Time) when the device was modified
     "token":null # (String) RESERVED for future use
    }
 }
422 Unprocessable Entity

Failed to create the device

400 Bad Request

Failed request, client failed to provide valid parameters

Parameters:

  • user_id (String)

    the unique user id

  • password (String)

    the password of the user trying to authenticate

  • kind (String)

    the kind of `device` that is requesting access

Returns:

  • (Device)


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'app/controllers/devices_controller.rb', line 91

def create
  # Store the parameters
  user_id = params[:user_id].to_i rescue nil
  password = params[:password]
  kind = params[:kind]
  dmake = params[:make]
  dmodel = params[:model]
  os = params[:os]
  os_version = params[:os_version]    

  if user_id.nil? || password.nil? || kind.nil?
    head :bad_request and return
  else
    user = User.find_by_id(user_id)
    if user
      # found user
      if verify_password_for_user(user,password)
        device = user.devices.build(:kind => kind, :make => dmake, :model => dmodel, :os => os, :os_version => os_version, :key => Device.generate_key)
        if device.save
          @user = user #set this so unique_id_for_item can be assigned properly
          item = {
            :device => {
              :row_id => unique_id_for_item('device',device.id),
              :id => device.id,
              :user_id => device.user_id,
              :key => device.key,
              :kind => device.kind,
              :created_at => device.created_at,
              :updated_at => device.updated_at,
              :token => device.token
            }
          }
          respond_with(item, :location => :devices, :status => :created) and return
        end
      end
    end
  end

  # If we've fallen to here, the device was not created
  head :unprocessable_entity
end

- (Object) destroy

Deletes the specified device.

URL

/device/:id [DELETE]

Response Codes
200 OK

Device removed

400 Bad Request

Failed request, client failed to provide valid parameters

Parameters:

  • id (String)

    the id of the device entity



141
142
143
144
145
146
147
148
149
150
# File 'app/controllers/devices_controller.rb', line 141

def destroy
  device = Device.find_by_id(params[:id])
  if device
    device.destroy
    respond_with(device) and return
  end
  
  # If we've fallen to here, the device was not removed
  head :bad_request
end

- (Object) log_events

Logs an array of events. This method processes the array of events into sessions automatically. It also registers device installs. The method returns the total number of events processed.

URL

/device/log_events

Response Codes
200 OK

Events logged

400 Bad Request

Failed request, client failed to provide valid parameters

Examples:

#POST /devices/log_events
{
  events: [
    { "key": "/tips", "app_uuid": "UUIDB1197F3348938A555D3FFBC0A617", "app_version": "i0.9", "os": "iPhone OS", "os_version": "5.0", "model": "iPhone 4S", "make": "Apple", "created_at": "2011-06-23T16:33:35Z", "device_id": 1 }
  ]
}

Parameters:

  • events (Array)

    the array of event hashes



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'app/controllers/devices_controller.rb', line 214

def log_events
  evs = params[:events]
  items = [] #so we can return the array size
  evs.each do |ev|
    
    uuid = ev[:app_uuid]
    app_version = ev[:app_version]
    cat = Time.parse(ev[:created_at])
    key = ev[:key]
    dvid = ev[:device_id].to_i
    
    # don't want to store empty or nil app or keys
    if app_version.to_s.empty? || key.to_s.empty?
      next # skip to the next event
    end

    AppInstall.transaction do
      # Make sure an app install instance exists
      ins = AppInstall.find_or_initialize_by_app_uuid(uuid)
      # Save it now (this ensures that new ones are created and existing ones are updated in case an os update occurs)
      ins.update_attributes(:make => ev[:make], :model => ev[:model], :os => ev[:os], :os_version => ev[:os_version])

      # Find an existing session for this user (if one exists)
      conditions = nil
      unless dvid.zero?
        conditions = ["device_id = ?", dvid]
      else
        conditions = ["device_id IS NULL AND app_install_id = ?", ins.id]
      end
      ms = MetricSession.find(:first, :conditions => conditions, :order => 'created_at DESC')
      
      # if no session was found OR it's been 15 minutes since the last activity, we'll create a new session
      if ms.nil? || (cat - ms.ended_at) > 15.minutes #new session
        ms = ins.metric_sessions.create(:device_id => dvid.zero? ? nil : dvid, :started_at => cat, :ended_at => cat, :make => ev[:make], :model => ev[:model], :os => ev[:os], :os_version => ev[:os_version])
      else #existing session
        ms.update_attributes(:ended_at => cat, :make => ev[:make], :model => ev[:model], :os => ev[:os], :os_version => ev[:os_version])
      end
    
      met = MetricEventType.find_or_create_by_name(key)
      mea = MetricEventApp.find_or_create_by_name(app_version)
      items << ms.events.create(:metric_event_type_id => met.id, :metric_event_app_id => mea.id, :app_install_id => ins.id)
    end
  end
  respond_with(items.size, :location => :log_events, :status => :ok) and return
rescue
  head :bad_request
end

- (Integer) update_required

Determines whether the participant’s current app version should be updated or not. When an app loads, it should check this api method. When the method returns true, the app should prompt the participant to update the app.

URL

/devices/update_required

Response Codes
200 OK

Returns boolean with true if an update is available, false otherwise.

400 Bad Request

Invalid params

Examples:

#POST /devices/update_required
{  os: 'ios', major: 0, minor: 9, revision: 2  }

Parameters:

  • os (String)

    (operating system) ‘android’ or ‘ios’, if not provided the API will use the supported-client http header

  • major (String)

    major version of the app

  • minor (String)

    minor version of the app

  • revision (String)

    revision version of the app

Returns:

  • (Integer)

    0 or 1 (1=true, so prompt the participant to update)



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'app/controllers/devices_controller.rb', line 169

def update_required

  h = { :os => params[:os] || @hes_supported_client.to_s, :major => params[:major].to_i, :minor => params[:minor].to_i, :revision => params[:revision].to_i }

  # if you don't pass a version above 0.0.0 (or the params you've passed result in 0.0.0), we shouldn't require an update
  if h[:major] == 0 && h[:minor] == 0 && h[:revision] == 0
    head :bad_request and return
  end

  # Set the minimum required version for supported clients
  rv = {}
  if h[:os] == 'ios'
    rv = { :major => 1, :minor => 0, :revision => 0 }
  elsif h[:os] == 'android'
    rv = { :major => 0, :minor => 0, :revision => 3 }
  end

  # check the minimum required version by doing fancy array comparision
  required = [rv[:major],rv[:minor],rv[:revision],0]
  posted = [h[:major],h[:minor],h[:revision],1]

  is_update_required = [required,posted].sort.first == posted
      
  render :json => {:update_required => is_update_required}, :status => :ok and return
rescue
  head :bad_request
end