cm-admin 0.7.7 → 0.7.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -5
  3. data/Gemfile.lock +28 -5
  4. data/app/assets/stylesheets/cm_admin/base/table.scss +1 -1
  5. data/app/controllers/cm_admin/resource_controller.rb +7 -4
  6. data/app/javascript/packs/cm_admin/exports.js +0 -1
  7. data/app/views/cm_admin/main/_associated_table.html.slim +3 -0
  8. data/app/views/cm_admin/main/_table.html.slim +1 -1
  9. data/app/views/cm_admin/main/_tabs.html.slim +1 -1
  10. data/cm_admin.gemspec +1 -1
  11. data/lib/cm_admin/models/form_field.rb +1 -1
  12. data/lib/cm_admin/version.rb +1 -1
  13. data/lib/cm_admin/view_helpers/form_field_helper.rb +6 -4
  14. data/lib/cm_admin/view_helpers.rb +18 -18
  15. data/lib/generators/cm_admin/add_graphql_generator.rb +25 -0
  16. data/lib/generators/cm_admin/templates/concerns/attachable.rb +63 -0
  17. data/lib/generators/cm_admin/templates/concerns/filtered_list.rb +22 -0
  18. data/lib/generators/cm_admin/templates/concerns/paginator.rb +12 -0
  19. data/lib/generators/cm_admin/templates/constants.rb +3 -0
  20. data/lib/generators/cm_admin/templates/exceptions/base_exception.rb +9 -0
  21. data/lib/generators/cm_admin/templates/graphql/enums/base/sort_column.rb +7 -0
  22. data/lib/generators/cm_admin/templates/graphql/enums/base/sort_direction.rb +8 -0
  23. data/lib/generators/cm_admin/templates/graphql/graphql_schema.rb +55 -0
  24. data/lib/generators/cm_admin/templates/graphql/inputs/base/attachment.rb +15 -0
  25. data/lib/generators/cm_admin/templates/graphql/inputs/base/filter.rb +15 -0
  26. data/lib/generators/cm_admin/templates/graphql/inputs/base/paging.rb +15 -0
  27. data/lib/generators/cm_admin/templates/graphql/inputs/base/sort.rb +15 -0
  28. data/lib/generators/cm_admin/templates/graphql/mutations/base_mutation.rb +8 -0
  29. data/lib/generators/cm_admin/templates/graphql/objects/base/attachment_type.rb +31 -0
  30. data/lib/generators/cm_admin/templates/graphql/objects/base/paging_type.rb +9 -0
  31. data/lib/generators/cm_admin/templates/graphql/queries/base_query.rb +4 -0
  32. data/package-lock.json +131 -35
  33. data/yarn.lock +5145 -6202
  34. metadata +27 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc4dad533a8a02bcc62cc27dd83d829b07794c32a2849e2eebc21de1c1f6cc84
4
- data.tar.gz: 30b772fbd67cf68633111b3290685e8d3d9d03c269d93313ab915d490c458311
3
+ metadata.gz: 2a03ed809475bd54b0365e020bb5d3ff1be6a2c16d2e2a2bf82b175af9ee26b8
4
+ data.tar.gz: 454ea27724b9f6e8c6297a81af181ce9e365fba0bbd777c68c9c1fdb6d5f47d0
5
5
  SHA512:
6
- metadata.gz: 31ec145cf1c7bb3c60a878b733fd251169d5403b6501f421180880ef56c7857f37e5d303ed4b93bd56a6ebfa4b7642f526db533864156a6e5a4e50af19b69d8a
7
- data.tar.gz: c45f05a62af30393e5d8bb458374afc51695c311310d6b3d497d6326913cbc45de634385d57a757e91748033de2765e321619c2ed64d1807ec612b763b98a8c1
6
+ metadata.gz: f6b7f5d92c8e26d67b3587fe3c1e0b674bf3bb49aca01d958495f1093b369ab1993383eda2ae744587dea723a8534a7bc3d4d51ef94cd0f6c9aa35bc2186525a
7
+ data.tar.gz: 2ccc66f2c64f05e584a4a33da9b0117868272d039e984e8822a90c6957fa48c2654ca3f9ad8b1a2a631a87f50bcd2c761de5d84e4f50b30915b1ad24f2c291ec
data/Gemfile CHANGED
@@ -1,11 +1,12 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- gem 'pagy', '~> 4.11.0'
4
- gem 'slim'
5
- gem "rake", "~> 12.0"
6
- gem "rspec", "~> 3.0"
7
3
  gem 'cocoon'
4
+ gem 'pagy', '~> 4.11.0'
8
5
  gem 'pundit'
6
+ gem 'rake', '~> 12.0'
7
+ gem 'rspec', '~> 3.0'
8
+ gem 'rubocop'
9
+ gem 'slim'
9
10
 
10
11
  # Specify your gem's dependencies in cm_admin.gemspec
11
12
  gemspec
data/Gemfile.lock CHANGED
@@ -2,7 +2,7 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  cm-admin (0.7.7)
5
- axlsx_rails (~> 0.6.1)
5
+ caxlsx_rails
6
6
  cocoon (~> 1.2.15)
7
7
  local_time (~> 2.1.0)
8
8
  pagy (~> 4.11.0)
@@ -31,15 +31,16 @@ GEM
31
31
  i18n (>= 1.6, < 2)
32
32
  minitest (>= 5.1)
33
33
  tzinfo (~> 2.0)
34
- axlsx_rails (0.6.1)
35
- actionpack (>= 3.1)
36
- caxlsx (>= 3.0)
34
+ ast (2.4.2)
37
35
  builder (3.2.4)
38
36
  caxlsx (3.2.0)
39
37
  htmlentities (~> 4.3, >= 4.3.4)
40
38
  marcel (~> 1.0)
41
39
  nokogiri (~> 1.10, >= 1.10.4)
42
40
  rubyzip (>= 1.3.0, < 3)
41
+ caxlsx_rails (0.6.3)
42
+ actionpack (>= 3.1)
43
+ caxlsx (>= 3.0)
43
44
  cocoon (1.2.15)
44
45
  concurrent-ruby (1.1.10)
45
46
  crass (1.0.6)
@@ -54,10 +55,15 @@ GEM
54
55
  nokogiri (>= 1.5.9)
55
56
  marcel (1.0.2)
56
57
  method_source (1.0.0)
58
+ mini_portile2 (2.8.0)
57
59
  minitest (5.16.2)
58
- nokogiri (1.13.7-arm64-darwin)
60
+ nokogiri (1.13.7)
61
+ mini_portile2 (~> 2.8.0)
59
62
  racc (~> 1.4)
60
63
  pagy (4.11.0)
64
+ parallel (1.22.1)
65
+ parser (3.1.2.0)
66
+ ast (~> 2.4.1)
61
67
  pundit (2.2.0)
62
68
  activesupport (>= 3.0.0)
63
69
  racc (1.6.0)
@@ -78,7 +84,10 @@ GEM
78
84
  rake (>= 12.2)
79
85
  thor (~> 1.0)
80
86
  zeitwerk (~> 2.5)
87
+ rainbow (3.1.1)
81
88
  rake (12.3.3)
89
+ regexp_parser (2.5.0)
90
+ rexml (3.2.5)
82
91
  rspec (3.10.0)
83
92
  rspec-core (~> 3.10.0)
84
93
  rspec-expectations (~> 3.10.0)
@@ -92,6 +101,18 @@ GEM
92
101
  diff-lcs (>= 1.2.0, < 2.0)
93
102
  rspec-support (~> 3.10.0)
94
103
  rspec-support (3.10.2)
104
+ rubocop (1.30.1)
105
+ parallel (~> 1.10)
106
+ parser (>= 3.1.0.0)
107
+ rainbow (>= 2.2.2, < 4.0)
108
+ regexp_parser (>= 1.8, < 3.0)
109
+ rexml (>= 3.2.5, < 4.0)
110
+ rubocop-ast (>= 1.18.0, < 2.0)
111
+ ruby-progressbar (~> 1.7)
112
+ unicode-display_width (>= 1.4.0, < 3.0)
113
+ rubocop-ast (1.18.0)
114
+ parser (>= 3.1.1.0)
115
+ ruby-progressbar (1.11.0)
95
116
  rubyzip (2.3.2)
96
117
  semantic_range (3.0.0)
97
118
  slim (4.1.0)
@@ -102,6 +123,7 @@ GEM
102
123
  tilt (2.0.10)
103
124
  tzinfo (2.0.4)
104
125
  concurrent-ruby (~> 1.0)
126
+ unicode-display_width (2.1.0)
105
127
  webpacker (5.4.3)
106
128
  activesupport (>= 5.2)
107
129
  rack-proxy (>= 0.6.1)
@@ -119,6 +141,7 @@ DEPENDENCIES
119
141
  pundit
120
142
  rake (~> 12.0)
121
143
  rspec (~> 3.0)
144
+ rubocop
122
145
  slim
123
146
 
124
147
  BUNDLED WITH
@@ -222,7 +222,7 @@
222
222
  width: calc(100% - 285px);
223
223
  left: 245px;
224
224
  background-color: #fff;
225
- z-index: 4;
225
+ z-index: 3;
226
226
  .table-sticky-top {
227
227
  position: sticky;
228
228
  top: 254px;
@@ -77,7 +77,11 @@ module CmAdmin
77
77
  respond_to do |format|
78
78
  if @action.action_type == :custom
79
79
  if @action.child_records
80
- format.html { render @action.layout }
80
+ if request.xhr?
81
+ format.html { render partial: '/cm_admin/main/associated_table' }
82
+ else
83
+ format.html { render @action.layout }
84
+ end
81
85
  elsif @action.display_type == :page
82
86
  data = @action.parent == "index" ? @ar_object.data : @ar_object
83
87
  format.html { render @action.partial }
@@ -137,7 +141,7 @@ module CmAdmin
137
141
  child_records = @ar_object.send(@current_action.child_records)
138
142
  @associated_model = CmAdmin::Model.find_by(name: @model.ar_model.reflect_on_association(@current_action.child_records).klass.name)
139
143
  if child_records.is_a? ActiveRecord::Relation
140
- @associated_ar_object = filter_by(params, child_records)
144
+ @associated_ar_object = filter_by(params, child_records, @associated_model.filter_params(params))
141
145
  else
142
146
  @associated_ar_object = child_records
143
147
  end
@@ -155,8 +159,7 @@ module CmAdmin
155
159
 
156
160
  records = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve if records.nil?
157
161
  records = records.order("#{@current_action.sort_column} #{@current_action.sort_direction}")
158
-
159
- final_data = CmAdmin::Models::Filter.filtered_data(filter_params, records, @model.filters)
162
+ final_data = CmAdmin::Models::Filter.filtered_data(filter_params, records, @associated_model ? @associated_model.filters : @model.filters)
160
163
  pagy, records = pagy(final_data)
161
164
  filtered_result.data = records
162
165
  filtered_result.pagy = pagy
@@ -1,6 +1,5 @@
1
1
  $(document).on('click', '.export-to-file-btn', function(e) {
2
2
  e.preventDefault();
3
3
  query_param = window.location.href.split("?")[1]
4
- $('#export-to-file-form').get(0).setAttribute('action', '/cm_admin/export_to_file.js?' + query_param);
5
4
  $("#export-to-file-form").submit();
6
5
  });
@@ -1,5 +1,8 @@
1
1
  .admin-table-index
2
2
  .table-top
3
+ - if @associated_model.filters.present? && @action.partial.nil?
4
+ .index-page__filters
5
+ == render partial: 'cm_admin/main/filters', locals: { filters: @associated_model.filters }
3
6
  p.table-top__total-count = "#{@associated_ar_object.pagy.count} #{@action.child_records.to_s.gsub('_', ' ')} found"
4
7
  .table-top__column-action
5
8
  - if @associated_model && @associated_model.available_actions.map(&:name).include?('new')
@@ -27,7 +27,7 @@
27
27
  td.text-ellipsis
28
28
  span class="#{column.field_type.to_s} #{column.cm_css_class} "
29
29
  - if index == 0
30
- = link_to ar_object.send(column.field_name), "/cm_admin/#{ar_object.model_name.collection}/#{ar_object.id}"
30
+ = link_to ar_object.send(column.field_name), cm_admin.send("#{ar_object.model_name.singular}_show_path", ar_object.id)
31
31
  - else
32
32
  = show_field_value(ar_object, column)
33
33
  - if column.field_type == :drawer
@@ -4,4 +4,4 @@ ul.nav.nav-pills
4
4
  - if nav_item.custom_action.empty? || (nav_item.custom_action.present? && policy([:cm_admin, @model.name.classify.constantize]).send(:"#{nav_item.custom_action}?"))
5
5
  li.nav-item
6
6
  - nav_item_action_name = nav_item.custom_action.present? ? nav_item.custom_action : 'show'
7
- = link_to nav_item.nav_item_name.to_s.titleize, "/cm_admin/#{@model.name.underscore.pluralize}/#{@ar_object.id}/#{nav_item.custom_action}", class: "nav-link #{ nav_item_action_name == action_name ? 'active' : ''}"
7
+ = link_to nav_item.nav_item_name.to_s.titleize, cm_admin.send("#{@ar_object.model_name.singular}_#{nav_item_action_name}_path", @ar_object.id), class: "nav-link #{ nav_item_action_name == action_name ? 'active' : ''}"
data/cm_admin.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.bindir = "exe"
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
- spec.add_runtime_dependency 'axlsx_rails', '~> 0.6.1'
29
+ spec.add_runtime_dependency 'caxlsx_rails'
30
30
  spec.add_runtime_dependency 'cocoon', '~> 1.2.15'
31
31
  spec.add_runtime_dependency 'local_time', '~> 2.1.0'
32
32
  spec.add_runtime_dependency 'pagy', '~> 4.11.0'
@@ -1,7 +1,7 @@
1
1
  module CmAdmin
2
2
  module Models
3
3
  class FormField
4
- attr_accessor :field_name, :label, :header, :input_type, :collection, :custom_value, :disabled, :collection_method
4
+ attr_accessor :field_name, :label, :header, :input_type, :collection, :disabled, :helper_method
5
5
  VALID_INPUT_TYPES = [:integer, :decimal, :string, :single_select, :multi_select, :date, :date_time, :text, :single_file_upload, :multi_file_upload, :hidden, :rich_text].freeze
6
6
 
7
7
  def initialize(field_name, input_type, attributes = {})
@@ -1,3 +1,3 @@
1
1
  module CmAdmin
2
- VERSION = "0.7.7"
2
+ VERSION = "0.7.8"
3
3
  end
@@ -2,7 +2,7 @@ module CmAdmin
2
2
  module ViewHelpers
3
3
  module FormFieldHelper
4
4
  def input_field_for_column(f, field)
5
- value = field.custom_value || f.object.send(field.field_name)
5
+ value = field.helper_method ? send(field.helper_method) : f.object.send(field.field_name)
6
6
  is_required = f.object._validators[field.field_name].map(&:kind).include?(:presence)
7
7
  required_class = is_required ? 'required' : ''
8
8
  case field.input_type
@@ -29,13 +29,15 @@ module CmAdmin
29
29
  when :multi_file_upload
30
30
  return f.file_field field.field_name, multiple: true, class: "normal-input #{required_class}"
31
31
  when :hidden
32
- return f.hidden_field field.field_name, value: field.custom_value
32
+ return f.hidden_field field.field_name, value: value
33
33
  end
34
34
  end
35
35
 
36
+ # Refactor: Collection argument can be removed.
37
+ # helper_method argument will accept a method where value can be passed.
36
38
  def select_collection_value(field)
37
- if field.collection_method
38
- collection = send(field.collection_method)
39
+ if field.helper_method
40
+ collection = send(field.helper_method)
39
41
  elsif field.collection
40
42
  collection = field.collection
41
43
  else
@@ -1,6 +1,6 @@
1
1
  module CmAdmin
2
2
  module ViewHelpers
3
- Dir[File.expand_path("view_helpers", __dir__) + "/*.rb"].each { |f| require f }
3
+ Dir[File.expand_path('view_helpers', __dir__) + '/*.rb'].each { |f| require f }
4
4
 
5
5
  include ActionDropdownHelper
6
6
  include FieldDisplayHelper
@@ -14,17 +14,17 @@ module CmAdmin
14
14
  include ActionView::Helpers::FormTagHelper
15
15
  include ActionView::Helpers::TagHelper
16
16
 
17
- def exportable(klass, html_class: [])
18
- tag.a "Export as excel", class: html_class.append("filter-btn modal-btn mr-2"), data: {toggle: "modal", target: "#exportmodal"} do
17
+ def exportable(_klass, html_class: [])
18
+ tag.a 'Export as excel', class: html_class.append('filter-btn modal-btn mr-2'), data: { toggle: 'modal', target: '#exportmodal' } do
19
19
  concat tag.i class: 'fa fa-download'
20
- concat tag.span " Export"
20
+ concat tag.span ' Export'
21
21
  end
22
22
  end
23
23
 
24
24
  def column_pop_up(klass, required_filters = nil)
25
- tag.div class: "modal fade form-modal", id: "exportmodal", role: "dialog", aria: {labelledby: "exportModal"} do
26
- tag.div class: "modal-dialog modal-lg", role: "document" do
27
- tag.div class: "modal-content" do
25
+ tag.div class: 'modal fade form-modal', id: 'exportmodal', role: 'dialog', aria: { labelledby: 'exportModal' } do
26
+ tag.div class: 'modal-dialog modal-lg', role: 'document' do
27
+ tag.div class: 'modal-content' do
28
28
  concat pop_ups(klass, required_filters)
29
29
  end
30
30
  end
@@ -39,17 +39,17 @@ module CmAdmin
39
39
  end
40
40
 
41
41
  def pop_up_header
42
- tag.div class: "modal-header" do
43
- tag.button type: "button", class: "close", data: {dismiss: "modal"}, aria: {label: "Close"} do
44
- tag.span "X", aria: {hidden: "true"}
42
+ tag.div class: 'modal-header' do
43
+ tag.button type: 'button', class: 'close', data: { dismiss: 'modal' }, aria: { label: 'Close' } do
44
+ tag.span 'X', aria: { hidden: 'true' }
45
45
  end
46
- tag.h4 "Select columns to export", class: "modal-title", id: "exportModal"
46
+ tag.h4 'Select columns to export', class: 'modal-title', id: 'exportModal'
47
47
  end
48
48
  end
49
49
 
50
- def pop_up_body(klass, required_filters)
51
- tag.div class: "modal-body" do
52
- form_tag '/cm_admin/export_to_file.js', id: 'export-to-file-form', style: "width: 100%;", class:"cm-admin-csv-export-form" do
50
+ def pop_up_body(klass, _required_filters)
51
+ tag.div class: 'modal-body' do
52
+ form_tag cm_admin.send('export_to_file_path'), id: 'export-to-file-form', style: 'width: 100%;', class: 'cm-admin-csv-export-form' do
53
53
  concat hidden_field_tag 'class_name', klass.name.to_s, id: 'export-to-file-klass'
54
54
  concat checkbox_row(klass)
55
55
  concat tag.hr
@@ -59,7 +59,7 @@ module CmAdmin
59
59
  end
60
60
 
61
61
  def checkbox_row(klass)
62
- tag.div class: "row" do
62
+ tag.div class: 'row' do
63
63
  CmAdmin::Models::Export.exportable_columns(klass).each do |column_path|
64
64
  concat create_checkbox(column_path)
65
65
  end
@@ -67,9 +67,9 @@ module CmAdmin
67
67
  end
68
68
 
69
69
  def create_checkbox(column_path)
70
- tag.div class: "col-md-4" do
71
- concat check_box_tag "columns[]", column_path, id: column_path.to_s.gsub('/', '-')
72
- concat " " + column_path.to_s.gsub('/', '_').humanize
70
+ tag.div class: 'col-md-4' do
71
+ concat check_box_tag 'columns[]', column_path, id: column_path.to_s.gsub('/', '-')
72
+ concat " #{column_path.to_s.gsub('/', '_').humanize}"
73
73
  end
74
74
  end
75
75
  end
@@ -0,0 +1,25 @@
1
+ require 'rails/generators'
2
+
3
+ module CmAdmin
4
+ module Generators
5
+ class AddGraphqlGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ def add_graphql
9
+ gem 'graphql'
10
+ gem 'graphql-errors'
11
+ gem 'graphql-rails_logger'
12
+ generate 'graphql:install'
13
+ template 'graphql/graphql_schema.rb', "app/graphql/#{Rails.application.class.module_parent_name.underscore}_schema.rb"
14
+ directory 'graphql/inputs/base', 'app/graphql/types/inputs/base'
15
+ directory 'graphql/enums/base', 'app/graphql/types/enums/base'
16
+ directory 'graphql/objects/base', 'app/graphql/types/objects/base'
17
+ directory 'concerns', 'app/models/concerns'
18
+ copy_file 'graphql/mutations/base_mutation.rb', 'app/graphql/mutations/base_mutation.rb'
19
+ copy_file 'graphql/queries/base_query.rb', 'app/graphql/queries/base_query.rb'
20
+ copy_file 'exceptions/base_exception.rb', 'app/exceptions/base_exception.rb'
21
+ copy_file 'constants.rb', 'config/initializers/constants.rb'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,63 @@
1
+ module Attachable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ end
6
+
7
+ def save_with_attachments
8
+ save!
9
+ save_attachment
10
+ end
11
+
12
+ def update!(args)
13
+ add_accessors_for_attachment_types
14
+ super
15
+ save_attachment
16
+ end
17
+
18
+ def initialize(args)
19
+ add_accessors_for_attachment_types
20
+ super
21
+ end
22
+
23
+ def save_attachment
24
+ self.class.attachment_types.each do |attachment_type|
25
+ next if send("#{attachment_type}_file").blank?
26
+
27
+ arr = []
28
+ if send("#{attachment_type}_file").class.eql?(Array)
29
+ arr = send("#{attachment_type}_file")
30
+ else
31
+ arr << send("#{attachment_type}_file")
32
+ end
33
+ arr.each do |x|
34
+ regexp = %r{\Adata:([-\w]+\/[-\w\+\.]+)?;base64,(.*)}m
35
+ data_uri_parts = x[:content].match(regexp) || []
36
+ decoded_data = Base64.decode64(data_uri_parts[2])
37
+ filename = x[:filename]
38
+ filepath = "#{Rails.root}/tmp/#{filename}"
39
+ File.open(filepath, 'wb') do |f|
40
+ f.write(decoded_data)
41
+ end
42
+ send(attachment_type.to_s).attach(io: File.open(filepath), filename: filename, content_type: data_uri_parts[1])
43
+ File.delete(filepath)
44
+ end
45
+ end
46
+ end
47
+
48
+ def attached_url(attachment_type)
49
+ if send(attachment_type.to_s).attached? && send(attachment_type.to_s).class == ActiveStorage::Attached::One
50
+ Rails.application.routes.url_helpers.rails_blob_url(send(attachment_type.to_s))
51
+ elsif send(attachment_type.to_s).attached? && send(attachment_type.to_s).class == ActiveStorage::Attached::Many
52
+ send(attachment_type.to_s)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def add_accessors_for_attachment_types
59
+ self.class.attachment_types.each do |attachment_type|
60
+ singleton_class.class_eval { attr_accessor "#{attachment_type}_file" }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,22 @@
1
+ class FilteredList
2
+ attr_reader :data, :facets, :paging
3
+
4
+ def initialize(paginated_list)
5
+ self.data = paginated_list[:list]
6
+ self.paging = paginated_list
7
+ end
8
+
9
+ def data=(data)
10
+ @data = []
11
+ @data = data if data.present?
12
+ end
13
+
14
+ def paging=(paginated_list)
15
+ @paging = {
16
+ total_items: paginated_list[:list].total_count,
17
+ current_page: paginated_list[:list].current_page,
18
+ total_pages: paginated_list[:list].total_pages,
19
+ total_count: paginated_list[:total_count]
20
+ }
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ module Paginator
2
+ extend ActiveSupport::Concern
3
+ module ClassMethods
4
+ def list(per_page = DEFAULT_PER_PAGE, page = nil, _filter_params = nil, total_count = nil)
5
+ paginated_list = {}
6
+ per_page = DEFAULT_PER_PAGE if per_page == 0
7
+ paginated_list[:list] = self.page(page || 1).per(per_page)
8
+ paginated_list[:total_count] = total_count
9
+ FilteredList.new(paginated_list)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ DEFAULT_PER_PAGE = 20
2
+ DEFAULT_SORT_COLUMN = 'created_at'.freeze
3
+ DEFAULT_SORT_DIRECTION = 'desc'.freeze
@@ -0,0 +1,9 @@
1
+ class BaseException < StandardError
2
+ def initialize(message = nil)
3
+ @message = message
4
+ end
5
+
6
+ def message
7
+ @message || 'Hello!'
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Types
2
+ class Enums::Base::SortColumn < Types::BaseEnum
3
+ description 'Possible values for sort column'
4
+
5
+ value :created_at, 'Sort by created_at'
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Types
2
+ class Enums::Base::SortDirection < Types::BaseEnum
3
+ description 'Possible values for sort direction'
4
+
5
+ value :asc, 'Sort by ascending'
6
+ value :desc, 'Sort by descending'
7
+ end
8
+ end
@@ -0,0 +1,55 @@
1
+ class <%= Rails.application.class.module_parent_name %>Schema < GraphQL::Schema
2
+ mutation(Types::MutationType)
3
+ query(Types::QueryType)
4
+
5
+ # Union and Interface Resolution
6
+ def self.resolve_type(abstract_type, obj, ctx)
7
+ # TODO: Implement this function
8
+ # to return the correct object type for `obj`
9
+ raise(GraphQL::RequiredImplementationMissingError)
10
+ end
11
+
12
+ # Relay-style Object Identification:
13
+
14
+ # Return a string UUID for `object`
15
+ def self.id_from_object(object, type_definition, query_ctx)
16
+ # Here's a simple implementation which:
17
+ # - joins the type name & object.id
18
+ # - encodes it with base64:
19
+ # GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
20
+ end
21
+
22
+ # Given a string UUID, find the object
23
+ def self.object_from_id(id, query_ctx)
24
+ # For example, to decode the UUIDs generated above:
25
+ # type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
26
+ #
27
+ # Then, based on `type_name` and `id`
28
+ # find an object in your application
29
+ # ...
30
+ end
31
+
32
+ rescue_from ActiveRecord::RecordNotFound do |err, obj, args, ctx, field|
33
+ GraphQL::ExecutionError.new("#{field.type.unwrap.graphql_name} not found", extensions: {code: :unprocessable_entity, sub_code: :record_invalid, message: err.message})
34
+ end
35
+
36
+ rescue_from ActiveRecord::RecordInvalid do |err, obj, args, ctx, field|
37
+ GraphQL::ExecutionError.new(err.message, extensions: {code: :unprocessable_entity, sub_code: :record_invalid, message: err.message})
38
+ end
39
+
40
+ rescue_from BaseException do |err, obj, args, ctx, field|
41
+ GraphQL::ExecutionError.new(err.message, extensions: {code: err.code, sub_code: err.sub_code, message: err.message})
42
+ end
43
+
44
+ unless Rails.env.development?
45
+ rescue_from StandardError do |err, obj, args, ctx, field|
46
+ rollbar_error = Rollbar.error(err)
47
+ GraphQL::ExecutionError.new("Internal Server Error", extensions: {code: :internal_server_error, uuid: rollbar_error[:uuid]})
48
+ end
49
+ end
50
+
51
+ def self.unauthorized_object(error)
52
+ raise Unauthorized, I18n.t("graphql.unauthorized", error_type: error.type.graphql_name)
53
+ end
54
+
55
+ end
@@ -0,0 +1,15 @@
1
+ module Types
2
+ module Inputs
3
+ module Base
4
+ class Attachment < Types::BaseInputObject
5
+ graphql_name 'AttachmentInput'
6
+
7
+ description 'Attributes needed to attach a file'
8
+
9
+ argument :filename, String, nil, required: true
10
+ argument :content, String, nil, required: true
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+ module Types
2
+ module Inputs
3
+ module Base
4
+ class Filter < Types::BaseInputObject
5
+ graphql_name 'BaseFilterInput'
6
+
7
+ description 'Attributes needed for filtering items'
8
+
9
+ argument :ids, [Integer], nil, required: false
10
+ argument :q, String, nil, required: false
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+ module Types
2
+ module Inputs
3
+ module Base
4
+ class Paging < Types::BaseInputObject
5
+ graphql_name 'PagingInput'
6
+
7
+ description 'Attributes needed for paginating list of items'
8
+
9
+ argument :page_no, Integer, nil, required: true
10
+ argument :per_page, Integer, nil, required: false
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+ module Types
2
+ module Inputs
3
+ module Base
4
+ class Sort < Types::BaseInputObject
5
+ graphql_name 'SortInput'
6
+
7
+ description 'Attributes needed for sorting the list of items'
8
+
9
+ argument :column, Types::Enums::Base::SortColumn, nil, required: true
10
+ argument :direction, Types::Enums::Base::SortDirection, nil, required: true
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,8 @@
1
+ module Mutations
2
+ class BaseMutation < GraphQL::Schema::RelayClassicMutation
3
+ argument_class Types::BaseArgument
4
+ field_class Types::BaseField
5
+ input_object_class Types::BaseInputObject
6
+ object_class Types::BaseObject
7
+ end
8
+ end
@@ -0,0 +1,31 @@
1
+ module Types::Objects::Base
2
+ class AttachmentType < Types::BaseObject
3
+ field :id, Int, nil, null: false
4
+ field :filename, String, nil, null: false
5
+ field :url, String, nil, null: false
6
+
7
+ def id
8
+ if object.class.eql?(ActiveStorage::Variant)
9
+ object.blob.id
10
+ else
11
+ object.id
12
+ end
13
+ end
14
+
15
+ def filename
16
+ if object.class.eql?(ActiveStorage::Variant)
17
+ object.blob.filename.to_s + "-" + object.variation.transformations[:resize]
18
+ else
19
+ object.filename.to_s
20
+ end
21
+ end
22
+
23
+ def url
24
+ if object.class.eql?(ActiveStorage::Variant)
25
+ Rails.application.routes.url_helpers.rails_representation_url(object)
26
+ else
27
+ object.service_url
28
+ end
29
+ end
30
+ end
31
+ end