atomic_admin 1.1.1 → 2.0.0.beta.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/atomic_admin/api/admin/v1/application_instances_controller.rb +3 -0
  3. data/app/controllers/atomic_admin/api/admin/v1/applications_controller.rb +3 -0
  4. data/app/controllers/atomic_admin/api/admin/v1/lti_installs_controller.rb +3 -0
  5. data/app/controllers/atomic_admin/api/admin/v1/lti_platforms_controller.rb +3 -0
  6. data/app/controllers/atomic_admin/api/admin/v1/sites_controller.rb +3 -0
  7. data/app/controllers/atomic_admin/api/admin/v1/tenant_client_id_strategies_controller.rb +3 -0
  8. data/app/controllers/atomic_admin/api/admin/v1/tenant_deployments_controller.rb +3 -0
  9. data/app/controllers/atomic_admin/api/admin/v1/tenant_platform_guid_strategies_controller.rb +3 -0
  10. data/app/controllers/atomic_admin/v1/admin_controller.rb +49 -0
  11. data/app/controllers/atomic_admin/v1/application_instances_controller.rb +127 -0
  12. data/app/controllers/atomic_admin/v1/applications_controller.rb +49 -0
  13. data/app/controllers/atomic_admin/{atomic_lti_install_controller.rb → v1/lti_installs_controller.rb} +18 -12
  14. data/app/controllers/atomic_admin/v1/lti_platforms_controller.rb +50 -0
  15. data/app/controllers/atomic_admin/v1/sites_controller.rb +46 -0
  16. data/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb +48 -0
  17. data/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb +47 -0
  18. data/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb +63 -0
  19. data/app/controllers/concerns/filtering.rb +54 -0
  20. data/app/controllers/concerns/require_jwt_token.rb +77 -0
  21. data/config/routes.rb +25 -9
  22. data/lib/atomic_admin/interaction.rb +48 -0
  23. data/lib/atomic_admin/jwt_token/jwks_decoder.rb +41 -0
  24. data/lib/atomic_admin/jwt_token/secret_decoder.rb +29 -0
  25. data/lib/atomic_admin/jwt_token.rb +5 -65
  26. data/lib/atomic_admin/schema/application_instance_configuration_schema.rb +61 -0
  27. data/lib/atomic_admin/schema/application_instance_create_schema.rb +77 -0
  28. data/lib/atomic_admin/schema/application_instance_general_settings_schema.rb +156 -0
  29. data/lib/atomic_admin/schema/application_instance_license_details_schema.rb +99 -0
  30. data/lib/atomic_admin/schema/application_instance_schema.rb +18 -0
  31. data/lib/atomic_admin/schema/application_instance_trial_details_schema.rb +67 -0
  32. data/lib/atomic_admin/schema/application_instance_xml_config_schema.rb +66 -0
  33. data/lib/atomic_admin/schema/atomic_application_update_schema.rb +57 -0
  34. data/lib/atomic_admin/schema.rb +13 -0
  35. data/lib/atomic_admin/version.rb +1 -1
  36. data/lib/atomic_admin.rb +10 -6
  37. metadata +35 -10
  38. data/app/controllers/atomic_admin/application_controller.rb +0 -10
  39. data/app/controllers/atomic_admin/atomic_lti_platform_controller.rb +0 -43
  40. data/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb +0 -59
  41. data/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb +0 -79
  42. data/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb +0 -58
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b625a67a9d862fc3a6de28702d21bcff133db94852681e71e53fb5550dc34b7b
4
- data.tar.gz: 70ba323f076abdfcf245005cbfcdb55c882866bb1626ceab0909591b8afd1904
3
+ metadata.gz: 2877b41feec63fee9094438e6f3567435f0be06d1fd38935c572b17e607a8591
4
+ data.tar.gz: 83ff3c84cddad21e5f3799934df38ccd3b54d7b0457388bf6d34f8314da788fc
5
5
  SHA512:
6
- metadata.gz: 405ba65b9b8ad2d229b89a3d4f176173af15cc42005685d403b9b1524b0902701103ee8a49b2bb6c1013b37d097cc3495c70dec02e9a1a6266fbd3a52193720e
7
- data.tar.gz: b2006e527199b2619ba4809b99d41e99e999fff209bd731413ae7647bcba13f5a9fb4beb95af9a1c69308120fda89fd7603d16eee97f01c07cf7a04f15dfff80
6
+ metadata.gz: 0ce24f660fc4df2bce27318395eacd4debae6dc22a190271dd039b611902139ea1a9c6b6dba3d9e4191d1f864379e55d9567e8bd1298324d6567bd837e262975
7
+ data.tar.gz: 93c96f5d173f20a59af47ed1740f1f5a0c9bbe5f03b9eed2111f88bf4b1e0cae42224eb554734bfb5942bb6de9e6d67dbb3951b7d00a56041ca56d4b133b1702
@@ -0,0 +1,3 @@
1
+ module AtomicAdmin::Api::Admin::V1
2
+ ApplicationInstancesController = AtomicAdmin::V1::ApplicationInstancesController
3
+ end
@@ -0,0 +1,3 @@
1
+ module AtomicAdmin::Api::Admin::V1
2
+ ApplicationsController = AtomicAdmin::V1::ApplicationsController
3
+ end
@@ -0,0 +1,3 @@
1
+ module AtomicAdmin::Api::Admin::V1
2
+ LtiInstallsController = AtomicAdmin::V1::LtiInstallsController
3
+ end
@@ -0,0 +1,3 @@
1
+ module AtomicAdmin::Api::Admin::V1
2
+ LtiPlatformsController = AtomicAdmin::V1::LtiPlatformsController
3
+ end
@@ -0,0 +1,3 @@
1
+ module AtomicAdmin::Api::Admin::V1
2
+ SitesController = AtomicAdmin::V1::SitesController
3
+ end
@@ -0,0 +1,3 @@
1
+ module AtomicAdmin::Api::Admin::V1
2
+ TenantClientIdStrategiesController = AtomicAdmin::V1::TenantClientIdStrategiesController
3
+ end
@@ -0,0 +1,3 @@
1
+ module AtomicAdmin::Api::Admin::V1
2
+ TenantDeploymentsController = AtomicAdmin::V1::TenantDeploymentsController
3
+ end
@@ -0,0 +1,3 @@
1
+ module AtomicAdmin::Api::Admin::V1
2
+ TenantPlatformGuidStrategiesController = AtomicAdmin::V1::TenantPlatformGuidStrategiesController
3
+ end
@@ -0,0 +1,49 @@
1
+ module AtomicAdmin::V1
2
+ class AdminController < ActionController::API
3
+ include RequireJwtToken
4
+ before_action :only_admins!
5
+
6
+ rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
7
+ def record_not_found
8
+ render_error(:not_found)
9
+ end
10
+
11
+
12
+ protected
13
+
14
+ def json_for(resource)
15
+ resource.as_json
16
+ end
17
+
18
+ def json_for_collection(collection)
19
+ collection.map { |resource| json_for(resource) }
20
+ end
21
+
22
+ private
23
+
24
+ def render_error(type, message: nil)
25
+ case type
26
+ when :not_found
27
+ [404, { type: "not_found", message: "Record not found" }]
28
+ else
29
+ [500, { type: "unknown", message: "An error occurred" }]
30
+ end => [status, error]
31
+
32
+ if message.present?
33
+ error[:message] = message
34
+ end
35
+
36
+ render json: error, status: status
37
+ end
38
+
39
+ def only_admins!
40
+ return if is_atomic_admin?
41
+
42
+ user_not_authorized if current_user.blank? && !current_user.admin?
43
+ end
44
+
45
+ def user_not_authorized(message = "Not Authorized")
46
+ render json: { message: message, }, status: 401
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,127 @@
1
+ module AtomicAdmin::V1
2
+ class ApplicationInstancesController < AdminController
3
+ include Filtering
4
+
5
+ allowed_sort_columns %w[nickname]
6
+ allowed_search_columns %w[nickname]
7
+
8
+ def index
9
+ @application_instances = ApplicationInstance.where(application_id: params[:application_id])
10
+ @application_instances =
11
+ if type == "paid"
12
+ @application_instances.where.not(paid_at: nil)
13
+ else
14
+ @application_instances.where(paid_at: nil)
15
+ end
16
+
17
+ @application_instances, meta = filter(@application_instances)
18
+
19
+ render json: {
20
+ application_instances: json_for_collection(@application_instances),
21
+ meta:
22
+ }
23
+ end
24
+
25
+ def stats
26
+ render json: { stats: [] }
27
+ end
28
+
29
+ def show
30
+ @application_instance = ApplicationInstance.find(params[:id])
31
+ render json: { application_instance: json_for(@application_instance) }
32
+ end
33
+
34
+ def create
35
+ application = Application.find(params[:application_id])
36
+ instance = application.application_instances.new(create_params)
37
+
38
+ if instance.save
39
+ render json: { application_instance: json_for(instance) }
40
+ else
41
+ render json: { errors: instance.errors }, status: 422
42
+ end
43
+ end
44
+
45
+ def update
46
+ instance = ApplicationInstance.find(params[:id])
47
+ instance.update(update_params)
48
+
49
+ if params[:application_instance][:is_paid] && instance.paid_at.nil?
50
+ instance.paid_at = DateTime.now
51
+ elsif params[:application_instance][:is_paid] == false && instance.paid_at.present?
52
+ instance.paid_at = nil
53
+ end
54
+
55
+ if instance.save
56
+ render json: { application_instance: json_for(instance) }
57
+ else
58
+ render json: { errors: instance.errors }, status: 422
59
+ end
60
+ end
61
+
62
+ def destroy
63
+ instance = ApplicationInstance.find(params[:id])
64
+ instance.destroy
65
+ render json: { success: true }
66
+ end
67
+
68
+ def interactions
69
+ instance = ApplicationInstance.find(params[:id])
70
+ interactions = AtomicAdmin.application_instance_interactions.resolve(application_instance: instance)
71
+ render json: { interactions: interactions }
72
+ end
73
+
74
+ def json_for(instance)
75
+ json = instance.as_json(include: [:application, :site])
76
+
77
+ json["trial_start_date"] = instance.trial_start_date&.strftime("%Y-%m-%d") if instance.respond_to?(:trial_start_date)
78
+ json["trial_end_date"] = instance.trial_end_date&.strftime("%Y-%m-%d") if instance.respond_to?(:trial_end_date)
79
+ json["license_start_date"] = instance.license_start_date&.strftime("%Y-%m-%d") if instance.respond_to?(:license_start_date)
80
+ json["license_end_date"] = instance.license_end_date&.strftime("%Y-%m-%d") if instance.respond_to?(:license_end_date)
81
+ json["is_paid"] = instance.paid_at.present? if instance.respond_to?(:paid_at)
82
+ json["lti_config_xml"] = instance.lti_config_xml if instance.respond_to?(:lti_config_xml)
83
+
84
+ json
85
+ end
86
+
87
+ protected
88
+
89
+ def sortable_columns
90
+ [
91
+ "created_at",
92
+ "trial_end_date",
93
+ "trial_users",
94
+ "license_end_date",
95
+ "licensed_users",
96
+ "nickname",
97
+ ]
98
+ end
99
+
100
+ def sort_column
101
+ sortable_columns.include?(params[:sort_on]) ? params[:sort_on] : "created_at"
102
+ end
103
+
104
+ def sort_direction
105
+ {
106
+ "ascending" => "asc",
107
+ "descending" => "desc",
108
+ }.fetch(params[:sort_direction], "desc")
109
+ end
110
+
111
+ def type
112
+ params[:type] == "paid" ? "paid" : "evals"
113
+ end
114
+
115
+ def search
116
+ params[:search]
117
+ end
118
+
119
+ def create_params
120
+ params.require(:application_instance).except(:is_paid, :lti_config_xml, :site, :application).permit!
121
+ end
122
+
123
+ def update_params
124
+ create_params
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,49 @@
1
+ module AtomicAdmin::V1
2
+ class ApplicationsController < AdminController
3
+ include Filtering
4
+
5
+ allowed_sort_columns %w[name]
6
+ allowed_search_columns %w[name]
7
+
8
+ def index
9
+ @applications, meta = filter(Application.all.lti)
10
+ render json: { applications: json_for_collection(@applications), meta: }
11
+ end
12
+
13
+ def show
14
+ @application = Application.find(params[:id])
15
+ render json: { application: json_for(@application) }
16
+ end
17
+
18
+ def update
19
+ @application = Application.find(params[:id])
20
+
21
+ # Strong params doesn't allow abritrary json, so we need to set the values manually
22
+ @application.default_config = params[:default_config]
23
+ @application.canvas_api_permissions = params[:canvas_api_permissions]
24
+
25
+ @application.update!(update_params)
26
+ render json: { application: json_for(@application) }
27
+ end
28
+
29
+ def interactions
30
+ application = Application.find(params[:id])
31
+ interactions = AtomicAdmin.application_interactions.resolve(application: application)
32
+ render json: { interactions: interactions }
33
+ end
34
+
35
+ def json_for(application)
36
+ json = application.as_json.with_indifferent_access
37
+ secret = json[:oauth_secret]
38
+ json[:oauth_secret_preview] = secret[0..2] + '*' * (secret.length - 3) if secret
39
+
40
+ json
41
+ end
42
+
43
+ protected
44
+
45
+ def update_params
46
+ params.require(:application).permit!
47
+ end
48
+ end
49
+ end
@@ -1,19 +1,11 @@
1
- module AtomicAdmin
2
- class AtomicLtiInstallController < ApplicationController
3
- def install_params
4
- params.permit(:iss, :client_id)
5
- end
6
-
7
- def find_install
8
- AtomicLti::Install.find_by(id: params[:id])
9
- end
10
-
1
+ module AtomicAdmin::V1
2
+ class LtiInstallsController < AdminController
11
3
  def index
12
4
  render json: AtomicLti::Install.all.order(:id).paginate(page: params[:page], per_page: 30)
13
5
  end
14
6
 
15
7
  def create
16
- AtomicLti::Install.create!(install_params)
8
+ AtomicLti::Install.create!(create_params)
17
9
  end
18
10
 
19
11
  def show
@@ -23,7 +15,7 @@ module AtomicAdmin
23
15
 
24
16
  def update
25
17
  install = find_install
26
- result = install.update!(install_params)
18
+ result = install.update!(update_params)
27
19
  render json: result
28
20
  end
29
21
 
@@ -32,5 +24,19 @@ module AtomicAdmin
32
24
  install.destroy
33
25
  render json: install
34
26
  end
27
+
28
+ protected
29
+
30
+ def find_install
31
+ AtomicLti::Install.find_by(id: params[:id])
32
+ end
33
+
34
+ def create_params
35
+ params.require(:install).permit!
36
+ end
37
+
38
+ def update_params
39
+ params.require(:install).permit!
40
+ end
35
41
  end
36
42
  end
@@ -0,0 +1,50 @@
1
+ module AtomicAdmin::V1
2
+ class LtiPlatformsController < AdminController
3
+ include Filtering
4
+
5
+ allowed_search_columns %w[iss]
6
+ allowed_sort_columns %w[iss]
7
+
8
+ def index
9
+ platforms, meta = filter(AtomicLti::Platform.all)
10
+
11
+ render json: { platforms:, meta: }
12
+ end
13
+
14
+ def create
15
+ platform = AtomicLti::Platform.create!(create_params)
16
+ render json: { platform: platform }
17
+ end
18
+
19
+ def show
20
+ platform = find_platform
21
+ render json: platform
22
+ end
23
+
24
+ def update
25
+ platform = find_platform
26
+ platform.update!(update_params)
27
+ render json: { platform: find_platform }
28
+ end
29
+
30
+ def destroy
31
+ platform = find_platform
32
+ platform.destroy
33
+ render json: platform
34
+ end
35
+
36
+ protected
37
+
38
+ def create_params
39
+ params.require(:platform).permit!
40
+ end
41
+
42
+ def update_params
43
+ params.require(:platform).permit!
44
+ end
45
+
46
+ def find_platform
47
+ AtomicLti::Platform.find_by(id: params[:id])
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,46 @@
1
+ module AtomicAdmin::V1
2
+ class SitesController < AdminController
3
+ include Filtering
4
+
5
+ allowed_search_columns %w[url]
6
+ allowed_sort_columns %w[url]
7
+
8
+ def index
9
+ @sites = Site.all
10
+ sites, meta = filter(@sites)
11
+ render json: { sites: json_for_collection(sites), meta: }
12
+ end
13
+
14
+ def create
15
+ @site = Site.create!(create_params)
16
+ render json: { site: json_for(@site) }
17
+ end
18
+
19
+ def update
20
+ @site = Site.find(params[:id])
21
+ @site.update!(update_params)
22
+ render json: { site: json_for(@site) }
23
+ end
24
+
25
+ def destroy
26
+ @site = Site.find(params[:id])
27
+ @site.destroy!
28
+ render json: { site: json_for(@site) }
29
+ end
30
+
31
+ protected
32
+
33
+ def json_for(site)
34
+ site.as_json
35
+ end
36
+
37
+ def create_params
38
+ params.require(:site).permit!
39
+ end
40
+
41
+ def update_params
42
+ binding.break
43
+ params.require(:site).permit!
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,48 @@
1
+ module AtomicAdmin::V1
2
+ class TenantClientIdStrategiesController < AdminController
3
+ include Filtering
4
+
5
+ allowed_search_columns %w[client_id, iss]
6
+ allowed_sort_columns %w[client_id, iss]
7
+
8
+ def index
9
+ query = AtomicTenant::PinnedClientId.where(application_instance_id:)
10
+ page, meta = filter(query)
11
+
12
+ render json: {
13
+ pinned_client_ids: page,
14
+ meta:
15
+ }
16
+ end
17
+
18
+ def show
19
+ pinned_client_id = find_pinned_client_id
20
+ render json: {pinned_client_id: pinned_client_id}
21
+ end
22
+
23
+ def create
24
+ result = AtomicTenant::PinnedClientId.create!({**create_params, application_instance_id:})
25
+ render json: { pinned_client_id: result }
26
+ end
27
+
28
+ def destroy
29
+ pinned_client_id = find_pinned_client_id
30
+ pinned_client_id.destroy
31
+ render json: { pinned_client_id: pinned_client_id }
32
+ end
33
+
34
+ protected
35
+
36
+ def application_instance_id
37
+ params[:application_instance_id] || params[:application_instance_id]
38
+ end
39
+
40
+ def create_params
41
+ params.require(:pinned_client_id).permit!
42
+ end
43
+
44
+ def find_pinned_client_id
45
+ AtomicTenant::PinnedClientId.find_by(id: params[:id])
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,47 @@
1
+ module AtomicAdmin::V1
2
+ class TenantDeploymentsController < AdminController
3
+ include Filtering
4
+
5
+ allowed_search_columns %w[deployment_id, iss]
6
+ allowed_sort_columns %w[deployment_id, iss]
7
+
8
+ def index
9
+ page, meta = filter(AtomicTenant::LtiDeployment.where(application_instance_id:))
10
+
11
+ render json: {
12
+ deployments: page,
13
+ meta:
14
+ }
15
+ end
16
+
17
+ def create
18
+ result = AtomicTenant::LtiDeployment.create!({**create_params, application_instance_id:})
19
+ render json: { deployment: result }
20
+ end
21
+
22
+ def show
23
+ deployment = find_deployment
24
+ render json: { deployment: deployment }
25
+ end
26
+
27
+ def destroy
28
+ deployment = find_deployment
29
+ deployment.destroy
30
+ render json: { deployment: deployment }
31
+ end
32
+
33
+ protected
34
+
35
+ def application_instance_id
36
+ params[:application_instance_id]
37
+ end
38
+
39
+ def create_params
40
+ params.require(:deployment).permit!
41
+ end
42
+
43
+ def find_deployment
44
+ AtomicTenant::LtiDeployment.find_by(id: params[:id])
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,63 @@
1
+ module AtomicAdmin::V1
2
+ class TenantPlatformGuidStrategiesController < AdminController
3
+ include Filtering
4
+
5
+ allowed_search_columns %w[platform_guid, iss]
6
+ allowed_sort_columns %w[platform_guid, iss]
7
+
8
+ def index
9
+ query = AtomicTenant::PinnedPlatformGuid.where(application_instance_id:)
10
+ page, meta = filter(query)
11
+
12
+ render json: {
13
+ pinned_platform_guids: page,
14
+ meta:
15
+ }
16
+ end
17
+
18
+ def create
19
+ result = AtomicTenant::PinnedPlatformGuid.create!({**create_params, application_instance_id:, application_id:})
20
+ render json: { pinned_platform_guid: result }
21
+ end
22
+
23
+ def show
24
+ pinned_platform_guid = find_pinned_platform_guid
25
+ render json: {pinned_platform_guid: pinned_platform_guid}
26
+ end
27
+
28
+ def update
29
+ pinned_platform_guid = find_pinned_platform_guid
30
+ pinned_platform_guid.update!(update_params)
31
+
32
+ render json: {pinned_platform_guid: find_pinned_platform_guid}
33
+ end
34
+
35
+ def destroy
36
+ pinned_platform_guid = find_pinned_platform_guid
37
+ pinned_platform_guid.destroy
38
+ render json: { pinned_platform_guid: pinned_platform_guid }
39
+ end
40
+
41
+ protected
42
+
43
+ def application_id
44
+ params[:application_id]
45
+ end
46
+
47
+ def application_instance_id
48
+ params[:application_instance_id]
49
+ end
50
+
51
+ def create_params
52
+ params.require(:pinned_platform_guid).permit!
53
+ end
54
+
55
+ def update_params
56
+ params.require(:pinned_platform_guid).permit!
57
+ end
58
+
59
+ def find_pinned_platform_guid
60
+ AtomicTenant::PinnedPlatformGuid.find(params[:id])
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,54 @@
1
+ module Filtering
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ class_variable_set(:@@allowed_sort_columns, [])
6
+ class_variable_set(:@@allowed_search_columns, [])
7
+ end
8
+
9
+ class_methods do
10
+ def allowed_sort_columns(names)
11
+ class_variable_set(:@@allowed_sort_columns, names)
12
+ end
13
+
14
+ def allowed_search_columns(names)
15
+ class_variable_set(:@@allowed_search_columns, names)
16
+ end
17
+ end
18
+
19
+ def query_params
20
+ params.
21
+ permit(:search, :sort_on, :sort_direction, :page, :per_page, :search_on).
22
+ with_defaults(sort_direction: "asc", page: 1, per_page: 30)
23
+ end
24
+
25
+ def filter(relation)
26
+ params = query_params
27
+ allowed_search_columns = self.class.class_variable_get(:@@allowed_search_columns)
28
+
29
+ if params[:search].present? && params[:search_on].present? && allowed_search_columns.include?(params[:search_on])
30
+ relation = relation.where("lower(#{params[:search_on]}) LIKE ?", "%#{params[:search].downcase}%")
31
+ end
32
+
33
+ allowed_sort_columns = self.class.class_variable_get(:@@allowed_sort_columns)
34
+ if params[:sort_on].present? && allowed_sort_columns.include?(params[:sort_on])
35
+ sort_col = params[:sort_on]
36
+ sort_dir = params[:sort_direction]
37
+ sort_dir = "asc" if sort_dir == "ascending"
38
+ sort_dir = "desc" if sort_dir == "descending"
39
+ relation = relation.order({sort_col => sort_dir})
40
+ end
41
+
42
+ relation = relation.paginate(page: params[:page], per_page: params[:per_page])
43
+
44
+ meta = {
45
+ current_page: relation.current_page,
46
+ next_page: relation.next_page,
47
+ prev_page: relation.previous_page,
48
+ total_pages: relation.total_pages,
49
+ total_items: relation.total_entries,
50
+ }
51
+
52
+ [relation, meta]
53
+ end
54
+ end