canvas_sync 0.10.0 → 0.10.2
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.
- checksums.yaml +4 -4
- data/lib/canvas_sync/api_syncable.rb +20 -1
- data/lib/canvas_sync/generators/templates/migrations/create_roles.rb +2 -0
- data/lib/canvas_sync/generators/templates/models/admin.rb +16 -10
- data/lib/canvas_sync/generators/templates/models/assignment_group.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/role.rb +12 -10
- data/lib/canvas_sync/generators/templates/models/term.rb +19 -14
- data/lib/canvas_sync/version.rb +1 -1
- data/spec/dummy/app/models/admin.rb +16 -10
- data/spec/dummy/app/models/role.rb +12 -10
- data/spec/dummy/app/models/term.rb +19 -14
- data/spec/dummy/db/migrate/{20190521003448_create_roles.rb → 20190606161037_create_roles.rb} +2 -0
- data/spec/dummy/db/schema.rb +3 -1
- data/spec/support/fixtures/canvas_responses/admins.json +49 -49
- data/spec/support/fixtures/canvas_responses/roles.json +743 -743
- metadata +4 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 50ea86e0116f11f92019daae4edd22aa3a402355e1d910947e391a0f3b6ac000
         | 
| 4 | 
            +
              data.tar.gz: 5712f275605f93eefc0f0ab5f1711b31e20dbc29d9a5e2b56d39853745610a49
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f914d9934b0b7c7c31fc3e3188d9884a07aeb104a4b89b9815f2455ada59bf74f6f43b3eb8fdc3fbd994dcca48166fd108dc45ae415f8715197ac96f33b3a2df
         | 
| 7 | 
            +
              data.tar.gz: b56ddedc9b422d64bf319f2f425cb209b1889fbfd4a5992eb403ab1901efd2b3aaeaa7463c3ca9b4d32924e6b44fc0b8479084c6462796ea547a8d6e5fd516b9
         | 
| @@ -1,7 +1,26 @@ | |
| 1 1 | 
             
            module CanvasSync::ApiSyncable
         | 
| 2 2 | 
             
              extend ActiveSupport::Concern
         | 
| 3 | 
            +
              NON_EXISTANT_ERRORS = [Faraday::Error::ResourceNotFound, Footrest::HttpError::NotFound]
         | 
| 3 4 |  | 
| 4 5 | 
             
              class_methods do
         | 
| 6 | 
            +
                def find_or_fetch(canvas_id, save: false, retries: 1)
         | 
| 7 | 
            +
                  inst = find_by(canvas_id: canvas_id)
         | 
| 8 | 
            +
                  return inst if inst.present?
         | 
| 9 | 
            +
                  inst = new(canvas_id: canvas_id)
         | 
| 10 | 
            +
                  api_response = inst.request_from_api(retries: retries)
         | 
| 11 | 
            +
                  inst.update_from_api_params(api_response)
         | 
| 12 | 
            +
                  inst.save! if save
         | 
| 13 | 
            +
                  inst
         | 
| 14 | 
            +
                rescue *NON_EXISTANT_ERRORS
         | 
| 15 | 
            +
                  nil
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def find_or_fetch!(*args)
         | 
| 19 | 
            +
                  inst = find_or_fetch(*args)
         | 
| 20 | 
            +
                  raise ActiveRecord::RecordNotFound unless inst.present?
         | 
| 21 | 
            +
                  inst
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 5 24 | 
             
                def api_sync_options=(opts)
         | 
| 6 25 | 
             
                  @api_sync_options = opts
         | 
| 7 26 | 
             
                end
         | 
| @@ -42,7 +61,7 @@ module CanvasSync::ApiSyncable | |
| 42 61 | 
             
                api_response = request_from_api(retries: retries)
         | 
| 43 62 | 
             
                update_from_api_params!(api_response)
         | 
| 44 63 | 
             
                api_response
         | 
| 45 | 
            -
              rescue  | 
| 64 | 
            +
              rescue *NON_EXISTANT_ERRORS
         | 
| 46 65 | 
             
                api_mark_deleted
         | 
| 47 66 | 
             
                save! if changed?
         | 
| 48 67 | 
             
                nil
         | 
| @@ -7,11 +7,13 @@ class CreateRoles < ActiveRecord::Migration[5.1] | |
| 7 7 | 
             
                  t.string :label, null: false
         | 
| 8 8 | 
             
                  t.string :base_role_type, null: false
         | 
| 9 9 | 
             
                  t.json :account
         | 
| 10 | 
            +
                  t.integer :canvas_account_id
         | 
| 10 11 | 
             
                  t.string :workflow_state, null: false
         | 
| 11 12 | 
             
                  t.json :permissions
         | 
| 12 13 |  | 
| 13 14 | 
             
                  t.timestamps
         | 
| 14 15 | 
             
                end
         | 
| 15 16 | 
             
                add_index :roles, :canvas_id, unique: true
         | 
| 17 | 
            +
                add_index :roles, :canvas_account_id
         | 
| 16 18 | 
             
              end
         | 
| 17 19 | 
             
            end
         | 
| @@ -1,20 +1,26 @@ | |
| 1 1 | 
             
            <%= autogenerated_model_warning %>
         | 
| 2 2 |  | 
| 3 3 | 
             
            class Admin < ApplicationRecord
         | 
| 4 | 
            +
              include CanvasSync::ApiSyncable
         | 
| 5 | 
            +
             | 
| 4 6 | 
             
              validates :canvas_id, uniqueness: true, presence: true
         | 
| 5 7 | 
             
              belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
         | 
| 6 8 | 
             
              belongs_to :role, primary_key: :canvas_id, foreign_key: :canvas_role_id, optional: true
         | 
| 7 9 |  | 
| 8 | 
            -
               | 
| 9 | 
            -
                 | 
| 10 | 
            +
              api_syncable({
         | 
| 11 | 
            +
                canvas_id: :id,
         | 
| 12 | 
            +
                role_name: :role,
         | 
| 13 | 
            +
                canvas_role_id: :role_id,
         | 
| 14 | 
            +
                user_data: :user,
         | 
| 15 | 
            +
                canvas_user_id: ->(r) { r['user']['id'] },
         | 
| 16 | 
            +
                workflow_state: :workflow_state,
         | 
| 17 | 
            +
              }, -> (api) {
         | 
| 18 | 
            +
                admins = api.account_admins('self').all_pages!
         | 
| 19 | 
            +
                admin_data = admins.find{|admin| admin['id'] == canvas_id }
         | 
| 20 | 
            +
                raise Footrest::HttpError::NotFound unless admin_data.present?
         | 
| 21 | 
            +
                admin_data
         | 
| 22 | 
            +
              })
         | 
| 10 23 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
                                        canvas_role_id: admin_params['role_id'],
         | 
| 13 | 
            -
                                        user_data: admin_params['user'],
         | 
| 14 | 
            -
                                        canvas_user_id: admin_params['user']['id'],
         | 
| 15 | 
            -
                                        workflow_state: admin_params['workflow_state'])
         | 
| 24 | 
            +
              def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
         | 
| 16 25 |  | 
| 17 | 
            -
                admin.save! if admin.changed?
         | 
| 18 | 
            -
                admin
         | 
| 19 | 
            -
              end
         | 
| 20 26 | 
             
            end
         | 
| @@ -15,5 +15,6 @@ class AssignmentGroup < ApplicationRecord | |
| 15 15 | 
             
                position: :position,
         | 
| 16 16 | 
             
                rules: :rules,
         | 
| 17 17 | 
             
                group_weight: :group_weight,
         | 
| 18 | 
            -
             | 
| 18 | 
            +
                workflow_state: ->(p) { 'available' },
         | 
| 19 | 
            +
              }, -> (api) { api.assignment_group(canvas_course_id, canvas_id) })
         | 
| 19 20 | 
             
            end
         | 
| @@ -1,19 +1,21 @@ | |
| 1 1 | 
             
            <%= autogenerated_model_warning %>
         | 
| 2 2 |  | 
| 3 3 | 
             
            class Role < ApplicationRecord
         | 
| 4 | 
            +
              include CanvasSync::ApiSyncable
         | 
| 5 | 
            +
             | 
| 4 6 | 
             
              validates :canvas_id, uniqueness: true, presence: true
         | 
| 5 7 | 
             
              has_many :admins, foreign_key: :canvas_role_id, primary_key: :canvas_id
         | 
| 6 8 |  | 
| 7 | 
            -
               | 
| 8 | 
            -
                 | 
| 9 | 
            +
              api_syncable({
         | 
| 10 | 
            +
                canvas_id: :id,
         | 
| 11 | 
            +
                label: :label,
         | 
| 12 | 
            +
                base_role_type: :base_role_type,
         | 
| 13 | 
            +
                account: :account,
         | 
| 14 | 
            +
                canvas_account_id: ->(r) { r.dig('account', 'id') },
         | 
| 15 | 
            +
                permissions: :permissions,
         | 
| 16 | 
            +
                workflow_state: :workflow_state,
         | 
| 17 | 
            +
              }, -> (api) { api.get("/api/v1/accounts/#{canvas_account_id}/roles/#{canvas_id}") })
         | 
| 9 18 |  | 
| 10 | 
            -
             | 
| 11 | 
            -
                                       base_role_type: role_params['base_role_type'],
         | 
| 12 | 
            -
                                       account: role_params['account'],
         | 
| 13 | 
            -
                                       permissions: role_params['permissions'],
         | 
| 14 | 
            -
                                       workflow_state: role_params['workflow_state'])
         | 
| 19 | 
            +
              def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
         | 
| 15 20 |  | 
| 16 | 
            -
                role.save! if role.changed?
         | 
| 17 | 
            -
                role
         | 
| 18 | 
            -
              end
         | 
| 19 21 | 
             
            end
         | 
| @@ -1,9 +1,28 @@ | |
| 1 1 | 
             
            <%= autogenerated_model_warning %>
         | 
| 2 2 |  | 
| 3 3 | 
             
            class Term < ApplicationRecord
         | 
| 4 | 
            +
              include CanvasSync::ApiSyncable
         | 
| 5 | 
            +
             | 
| 4 6 | 
             
              validates :canvas_id, uniqueness: true, presence: true
         | 
| 5 7 | 
             
              has_many :courses, foreign_key: :canvas_term_id, primary_key: :canvas_id
         | 
| 6 8 |  | 
| 9 | 
            +
              api_syncable({
         | 
| 10 | 
            +
                canvas_id: :id,
         | 
| 11 | 
            +
                name: :name,
         | 
| 12 | 
            +
                sis_id: :sis_term_id,
         | 
| 13 | 
            +
                start_at: :start_at,
         | 
| 14 | 
            +
                end_at: :end_at,
         | 
| 15 | 
            +
                workflow_state: :workflow_state,
         | 
| 16 | 
            +
                grading_period_group_id: :grading_period_group_id,
         | 
| 17 | 
            +
              }, -> (api) {
         | 
| 18 | 
            +
                terms = api.terms('self').all_pages!
         | 
| 19 | 
            +
                term_data = terms.find{|term| term['id'] == canvas_id }
         | 
| 20 | 
            +
                raise Footrest::HttpError::NotFound unless term_data.present?
         | 
| 21 | 
            +
                term_data
         | 
| 22 | 
            +
              })
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
         | 
| 25 | 
            +
             | 
| 7 26 | 
             
              # This is a sample scope created by the CanvasSync gem; feel
         | 
| 8 27 | 
             
              # free to customize it for your tool's requirements.
         | 
| 9 28 | 
             
              scope :active, -> {
         | 
| @@ -12,18 +31,4 @@ class Term < ApplicationRecord | |
| 12 31 | 
             
                  .where("end_at >= ? OR end_at IS NULL", 15.days.ago)
         | 
| 13 32 | 
             
              }
         | 
| 14 33 |  | 
| 15 | 
            -
              def self.create_or_update(term_params)
         | 
| 16 | 
            -
                term = Term.find_or_initialize_by(canvas_id: term_params['id'])
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                term.assign_attributes(name: term_params['name'],
         | 
| 19 | 
            -
                                       start_at: term_params['start_at'],
         | 
| 20 | 
            -
                                       end_at: term_params['end_at'],
         | 
| 21 | 
            -
                                       workflow_state: term_params['workflow_state'],
         | 
| 22 | 
            -
                                       grading_period_group_id: term_params['grading_period_group_id'],
         | 
| 23 | 
            -
                                       sis_id: term_params['sis_term_id'])
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                term.save! if term.changed?
         | 
| 26 | 
            -
                term
         | 
| 27 | 
            -
              end
         | 
| 28 | 
            -
             | 
| 29 34 | 
             
            end
         | 
    
        data/lib/canvas_sync/version.rb
    CHANGED
    
    
| @@ -7,20 +7,26 @@ | |
| 7 7 |  | 
| 8 8 |  | 
| 9 9 | 
             
            class Admin < ApplicationRecord
         | 
| 10 | 
            +
              include CanvasSync::ApiSyncable
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
              validates :canvas_id, uniqueness: true, presence: true
         | 
| 11 13 | 
             
              belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
         | 
| 12 14 | 
             
              belongs_to :role, primary_key: :canvas_id, foreign_key: :canvas_role_id, optional: true
         | 
| 13 15 |  | 
| 14 | 
            -
               | 
| 15 | 
            -
                 | 
| 16 | 
            +
              api_syncable({
         | 
| 17 | 
            +
                canvas_id: :id,
         | 
| 18 | 
            +
                role_name: :role,
         | 
| 19 | 
            +
                canvas_role_id: :role_id,
         | 
| 20 | 
            +
                user_data: :user,
         | 
| 21 | 
            +
                canvas_user_id: ->(r) { r['user']['id'] },
         | 
| 22 | 
            +
                workflow_state: :workflow_state,
         | 
| 23 | 
            +
              }, -> (api) {
         | 
| 24 | 
            +
                admins = api.account_admins('self').all_pages!
         | 
| 25 | 
            +
                admin_data = admins.find{|admin| admin['id'] == canvas_id }
         | 
| 26 | 
            +
                raise Footrest::HttpError::NotFound unless admin_data.present?
         | 
| 27 | 
            +
                admin_data
         | 
| 28 | 
            +
              })
         | 
| 16 29 |  | 
| 17 | 
            -
             | 
| 18 | 
            -
                                        canvas_role_id: admin_params['role_id'],
         | 
| 19 | 
            -
                                        user_data: admin_params['user'],
         | 
| 20 | 
            -
                                        canvas_user_id: admin_params['user']['id'],
         | 
| 21 | 
            -
                                        workflow_state: admin_params['workflow_state'])
         | 
| 30 | 
            +
              def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
         | 
| 22 31 |  | 
| 23 | 
            -
                admin.save! if admin.changed?
         | 
| 24 | 
            -
                admin
         | 
| 25 | 
            -
              end
         | 
| 26 32 | 
             
            end
         | 
| @@ -7,19 +7,21 @@ | |
| 7 7 |  | 
| 8 8 |  | 
| 9 9 | 
             
            class Role < ApplicationRecord
         | 
| 10 | 
            +
              include CanvasSync::ApiSyncable
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
              validates :canvas_id, uniqueness: true, presence: true
         | 
| 11 13 | 
             
              has_many :admins, foreign_key: :canvas_role_id, primary_key: :canvas_id
         | 
| 12 14 |  | 
| 13 | 
            -
               | 
| 14 | 
            -
                 | 
| 15 | 
            +
              api_syncable({
         | 
| 16 | 
            +
                canvas_id: :id,
         | 
| 17 | 
            +
                label: :label,
         | 
| 18 | 
            +
                base_role_type: :base_role_type,
         | 
| 19 | 
            +
                account: :account,
         | 
| 20 | 
            +
                canvas_account_id: ->(r) { r.dig('account', 'id') },
         | 
| 21 | 
            +
                permissions: :permissions,
         | 
| 22 | 
            +
                workflow_state: :workflow_state,
         | 
| 23 | 
            +
              }, -> (api) { api.get("/api/v1/accounts/#{canvas_account_id}/roles/#{canvas_id}") })
         | 
| 15 24 |  | 
| 16 | 
            -
             | 
| 17 | 
            -
                                       base_role_type: role_params['base_role_type'],
         | 
| 18 | 
            -
                                       account: role_params['account'],
         | 
| 19 | 
            -
                                       permissions: role_params['permissions'],
         | 
| 20 | 
            -
                                       workflow_state: role_params['workflow_state'])
         | 
| 25 | 
            +
              def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
         | 
| 21 26 |  | 
| 22 | 
            -
                role.save! if role.changed?
         | 
| 23 | 
            -
                role
         | 
| 24 | 
            -
              end
         | 
| 25 27 | 
             
            end
         | 
| @@ -7,9 +7,28 @@ | |
| 7 7 |  | 
| 8 8 |  | 
| 9 9 | 
             
            class Term < ApplicationRecord
         | 
| 10 | 
            +
              include CanvasSync::ApiSyncable
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
              validates :canvas_id, uniqueness: true, presence: true
         | 
| 11 13 | 
             
              has_many :courses, foreign_key: :canvas_term_id, primary_key: :canvas_id
         | 
| 12 14 |  | 
| 15 | 
            +
              api_syncable({
         | 
| 16 | 
            +
                canvas_id: :id,
         | 
| 17 | 
            +
                name: :name,
         | 
| 18 | 
            +
                sis_id: :sis_term_id,
         | 
| 19 | 
            +
                start_at: :start_at,
         | 
| 20 | 
            +
                end_at: :end_at,
         | 
| 21 | 
            +
                workflow_state: :workflow_state,
         | 
| 22 | 
            +
                grading_period_group_id: :grading_period_group_id,
         | 
| 23 | 
            +
              }, -> (api) {
         | 
| 24 | 
            +
                terms = api.terms('self').all_pages!
         | 
| 25 | 
            +
                term_data = terms.find{|term| term['id'] == canvas_id }
         | 
| 26 | 
            +
                raise Footrest::HttpError::NotFound unless term_data.present?
         | 
| 27 | 
            +
                term_data
         | 
| 28 | 
            +
              })
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
         | 
| 31 | 
            +
             | 
| 13 32 | 
             
              # This is a sample scope created by the CanvasSync gem; feel
         | 
| 14 33 | 
             
              # free to customize it for your tool's requirements.
         | 
| 15 34 | 
             
              scope :active, -> {
         | 
| @@ -18,18 +37,4 @@ class Term < ApplicationRecord | |
| 18 37 | 
             
                  .where("end_at >= ? OR end_at IS NULL", 15.days.ago)
         | 
| 19 38 | 
             
              }
         | 
| 20 39 |  | 
| 21 | 
            -
              def self.create_or_update(term_params)
         | 
| 22 | 
            -
                term = Term.find_or_initialize_by(canvas_id: term_params['id'])
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                term.assign_attributes(name: term_params['name'],
         | 
| 25 | 
            -
                                       start_at: term_params['start_at'],
         | 
| 26 | 
            -
                                       end_at: term_params['end_at'],
         | 
| 27 | 
            -
                                       workflow_state: term_params['workflow_state'],
         | 
| 28 | 
            -
                                       grading_period_group_id: term_params['grading_period_group_id'],
         | 
| 29 | 
            -
                                       sis_id: term_params['sis_term_id'])
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                term.save! if term.changed?
         | 
| 32 | 
            -
                term
         | 
| 33 | 
            -
              end
         | 
| 34 | 
            -
             | 
| 35 40 | 
             
            end
         | 
    
        data/spec/dummy/db/migrate/{20190521003448_create_roles.rb → 20190606161037_create_roles.rb}
    RENAMED
    
    | @@ -13,11 +13,13 @@ class CreateRoles < ActiveRecord::Migration[5.1] | |
| 13 13 | 
             
                  t.string :label, null: false
         | 
| 14 14 | 
             
                  t.string :base_role_type, null: false
         | 
| 15 15 | 
             
                  t.json :account
         | 
| 16 | 
            +
                  t.integer :canvas_account_id
         | 
| 16 17 | 
             
                  t.string :workflow_state, null: false
         | 
| 17 18 | 
             
                  t.json :permissions
         | 
| 18 19 |  | 
| 19 20 | 
             
                  t.timestamps
         | 
| 20 21 | 
             
                end
         | 
| 21 22 | 
             
                add_index :roles, :canvas_id, unique: true
         | 
| 23 | 
            +
                add_index :roles, :canvas_account_id
         | 
| 22 24 | 
             
              end
         | 
| 23 25 | 
             
            end
         | 
    
        data/spec/dummy/db/schema.rb
    CHANGED
    
    | @@ -10,7 +10,7 @@ | |
| 10 10 | 
             
            #
         | 
| 11 11 | 
             
            # It's strongly recommended that you check this file into your version control system.
         | 
| 12 12 |  | 
| 13 | 
            -
            ActiveRecord::Schema.define(version:  | 
| 13 | 
            +
            ActiveRecord::Schema.define(version: 20190606161037) do
         | 
| 14 14 |  | 
| 15 15 | 
             
              # These are extensions that must be enabled in order to support this database
         | 
| 16 16 | 
             
              enable_extension "plpgsql"
         | 
| @@ -162,10 +162,12 @@ ActiveRecord::Schema.define(version: 20190604193942) do | |
| 162 162 | 
             
                t.string "label", null: false
         | 
| 163 163 | 
             
                t.string "base_role_type", null: false
         | 
| 164 164 | 
             
                t.json "account"
         | 
| 165 | 
            +
                t.integer "canvas_account_id"
         | 
| 165 166 | 
             
                t.string "workflow_state", null: false
         | 
| 166 167 | 
             
                t.json "permissions"
         | 
| 167 168 | 
             
                t.datetime "created_at", null: false
         | 
| 168 169 | 
             
                t.datetime "updated_at", null: false
         | 
| 170 | 
            +
                t.index ["canvas_account_id"], name: "index_roles_on_canvas_account_id"
         | 
| 169 171 | 
             
                t.index ["canvas_id"], name: "index_roles_on_canvas_id", unique: true
         | 
| 170 172 | 
             
              end
         | 
| 171 173 |  | 
| @@ -1,50 +1,50 @@ | |
| 1 1 | 
             
            [
         | 
| 2 | 
            -
            {
         | 
| 3 | 
            -
            "id": 2,
         | 
| 4 | 
            -
            "role": "AccountAdmin",
         | 
| 5 | 
            -
            "role_id": 1,
         | 
| 6 | 
            -
            "user": {
         | 
| 7 | 
            -
            "id": 1,
         | 
| 8 | 
            -
            "name": "mvalentine@instructure.com",
         | 
| 9 | 
            -
            "sortable_name": "mvalentine@instructure.com",
         | 
| 10 | 
            -
            "short_name": "mvalentine@instructure.com",
         | 
| 11 | 
            -
            "sis_user_id": null,
         | 
| 12 | 
            -
            "integration_id": null,
         | 
| 13 | 
            -
            "sis_import_id": null,
         | 
| 14 | 
            -
            "login_id": "mvalentine@instructure.com"
         | 
| 15 | 
            -
            },
         | 
| 16 | 
            -
            "workflow_state": "active"
         | 
| 17 | 
            -
            },
         | 
| 18 | 
            -
            {
         | 
| 19 | 
            -
            "id": 3,
         | 
| 20 | 
            -
            "role": "Custom Account Role",
         | 
| 21 | 
            -
            "role_id": 11,
         | 
| 22 | 
            -
            "user": {
         | 
| 23 | 
            -
            "id": 11,
         | 
| 24 | 
            -
            "name": "Test Account User",
         | 
| 25 | 
            -
            "sortable_name": "User, Test Account",
         | 
| 26 | 
            -
            "short_name": "Test Account User",
         | 
| 27 | 
            -
            "sis_user_id": null,
         | 
| 28 | 
            -
            "integration_id": null,
         | 
| 29 | 
            -
            "sis_import_id": null,
         | 
| 30 | 
            -
            "login_id": "testaccountuser@example.com"
         | 
| 31 | 
            -
            },
         | 
| 32 | 
            -
            "workflow_state": "active"
         | 
| 33 | 
            -
            },
         | 
| 34 | 
            -
            {
         | 
| 35 | 
            -
            "id": 4,
         | 
| 36 | 
            -
            "role": "Test",
         | 
| 37 | 
            -
            "role_id": 10,
         | 
| 38 | 
            -
            "user": {
         | 
| 39 | 
            -
            "id": 11,
         | 
| 40 | 
            -
            "name": "Test Account User",
         | 
| 41 | 
            -
            "sortable_name": "User, Test Account",
         | 
| 42 | 
            -
            "short_name": "Test Account User",
         | 
| 43 | 
            -
            "sis_user_id": null,
         | 
| 44 | 
            -
            "integration_id": null,
         | 
| 45 | 
            -
            "sis_import_id": null,
         | 
| 46 | 
            -
            "login_id": "testaccountuser@example.com"
         | 
| 47 | 
            -
            },
         | 
| 48 | 
            -
            "workflow_state": "active"
         | 
| 49 | 
            -
            }
         | 
| 50 | 
            -
            ]
         | 
| 2 | 
            +
                {
         | 
| 3 | 
            +
                    "id": 2,
         | 
| 4 | 
            +
                    "role": "AccountAdmin",
         | 
| 5 | 
            +
                    "role_id": 1,
         | 
| 6 | 
            +
                    "user": {
         | 
| 7 | 
            +
                        "id": 1,
         | 
| 8 | 
            +
                        "name": "mvalentine@instructure.com",
         | 
| 9 | 
            +
                        "sortable_name": "mvalentine@instructure.com",
         | 
| 10 | 
            +
                        "short_name": "mvalentine@instructure.com",
         | 
| 11 | 
            +
                        "sis_user_id": null,
         | 
| 12 | 
            +
                        "integration_id": null,
         | 
| 13 | 
            +
                        "sis_import_id": null,
         | 
| 14 | 
            +
                        "login_id": "mvalentine@instructure.com"
         | 
| 15 | 
            +
                    },
         | 
| 16 | 
            +
                    "workflow_state": "active"
         | 
| 17 | 
            +
                },
         | 
| 18 | 
            +
                {
         | 
| 19 | 
            +
                    "id": 3,
         | 
| 20 | 
            +
                    "role": "Custom Account Role",
         | 
| 21 | 
            +
                    "role_id": 11,
         | 
| 22 | 
            +
                    "user": {
         | 
| 23 | 
            +
                        "id": 11,
         | 
| 24 | 
            +
                        "name": "Test Account User",
         | 
| 25 | 
            +
                        "sortable_name": "User, Test Account",
         | 
| 26 | 
            +
                        "short_name": "Test Account User",
         | 
| 27 | 
            +
                        "sis_user_id": null,
         | 
| 28 | 
            +
                        "integration_id": null,
         | 
| 29 | 
            +
                        "sis_import_id": null,
         | 
| 30 | 
            +
                        "login_id": "testaccountuser@example.com"
         | 
| 31 | 
            +
                    },
         | 
| 32 | 
            +
                    "workflow_state": "active"
         | 
| 33 | 
            +
                },
         | 
| 34 | 
            +
                {
         | 
| 35 | 
            +
                    "id": 4,
         | 
| 36 | 
            +
                    "role": "Test",
         | 
| 37 | 
            +
                    "role_id": 10,
         | 
| 38 | 
            +
                    "user": {
         | 
| 39 | 
            +
                        "id": 11,
         | 
| 40 | 
            +
                        "name": "Test Account User",
         | 
| 41 | 
            +
                        "sortable_name": "User, Test Account",
         | 
| 42 | 
            +
                        "short_name": "Test Account User",
         | 
| 43 | 
            +
                        "sis_user_id": null,
         | 
| 44 | 
            +
                        "integration_id": null,
         | 
| 45 | 
            +
                        "sis_import_id": null,
         | 
| 46 | 
            +
                        "login_id": "testaccountuser@example.com"
         | 
| 47 | 
            +
                    },
         | 
| 48 | 
            +
                    "workflow_state": "active"
         | 
| 49 | 
            +
                }
         | 
| 50 | 
            +
            ]
         |