brick 1.0.105 → 1.0.106

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48fc69654d7cbdf5c7feb9ba7803b7a953c6c0ccd3c176614fe9a61994bb39e1
4
- data.tar.gz: f3a4837d334cf97cb491cacd658cb6e6d8e3f3a5c640f80c151a083bf6811b7a
3
+ metadata.gz: 988907627ce35c257a41cbcf4b455ec852c9635fe4f8721c5af167e416fbb4ef
4
+ data.tar.gz: c4a2fe612aa26a628f347d830197f961cf21b67c825ff968fa589e6bd342d1d7
5
5
  SHA512:
6
- metadata.gz: e51134710bae78f033b9dcf7827cd7c087cfd642ef8bc6244e2429f22158a80e9f804022bc703f55fa55f85cbc009ff25440289205ade1b415f5ba0af3d44c77
7
- data.tar.gz: d6069676ad70e8401f7597b92b641f0f95e3c28fffa54747f630932500c778d7b4ac967c5bbd70059647d11d398598362d9c0bb5c82ef0500b1025371104a578
6
+ metadata.gz: 3488a28db2bf8f3c7f363e9b2eb4355e9de2d4021db9a39fb45656f896046bee412a7fd310861f522915bbc244af35a2707005757357ee6b9f9877ea331e7240
7
+ data.tar.gz: 8a44cbbadde246520b028d0e474226cdf2d43e8d51bed023e20f21a0d5b6cc0e690a397908bcdbdb9b7ef21c7743bec64130258b859d1583b6c0facffd8edf5f
data/lib/brick/config.rb CHANGED
@@ -95,20 +95,19 @@ module Brick
95
95
  end
96
96
 
97
97
  def api_roots
98
- ver = api_version
99
- @mutex.synchronize { @api_roots || ["/api/#{ver}/"] }
98
+ @mutex.synchronize { @api_roots || ["/api/v1/"] }
100
99
  end
101
100
 
102
101
  def api_roots=(path)
103
102
  @mutex.synchronize { @api_roots = path }
104
103
  end
105
104
 
106
- def api_version
107
- @mutex.synchronize { @api_version || 'v1' }
105
+ def api_filter
106
+ @mutex.synchronize { @api_filter }
108
107
  end
109
108
 
110
- def api_version=(ver)
111
- @mutex.synchronize { @api_version = ver }
109
+ def api_filter=(proc)
110
+ @mutex.synchronize { @api_filter = proc }
112
111
  end
113
112
 
114
113
  # Additional table associations to use (Think of these as virtual foreign keys perhaps)
@@ -1526,9 +1526,10 @@ class Object
1526
1526
  self.protect_from_forgery unless: -> { self.request.format.js? }
1527
1527
  unless is_avo
1528
1528
  self.define_method :index do
1529
+ request_ver = request.path.split('/')[-2]
1529
1530
  current_api_root = ::Brick.config.api_roots.find do |ar|
1530
1531
  request.path.start_with?(ar) || # Exact match?
1531
- request.path.split('/')[-2] == ar.split('/').last # Version at least matches?
1532
+ request_ver == ar.split('/').last # Version at least matches?
1532
1533
  end
1533
1534
  if (current_api_root || is_openapi) &&
1534
1535
  !params&.key?('_brick_schema') &&
@@ -1542,51 +1543,63 @@ class Object
1542
1543
  _schema, @_is_show_schema_list = ::Brick.set_db_schema(params || api_params)
1543
1544
 
1544
1545
  if is_openapi
1546
+ api_name = Rswag::Ui.config.config_object[:urls].find do |api_url|
1547
+ api_url[:url] == request.path
1548
+ end&.fetch(:name, 'API documentation')
1545
1549
  current_api_ver = current_api_root.split('/').last&.[](1..-1).to_i
1546
- json = { 'openapi': '3.0.1', 'info': { 'title': Rswag::Ui.config.config_object[:urls].last&.fetch(:name, 'API documentation'), 'version': ::Brick.config.api_version },
1550
+ json = { 'openapi': '3.0.1', 'info': { 'title': api_name, 'version': request_ver },
1547
1551
  'servers': [
1548
- { 'url': '{scheme}://{defaultHost}', 'variables': {
1549
- 'scheme': { 'default': request.env['rack.url_scheme'] },
1550
- 'defaultHost': { 'default': request.env['HTTP_HOST'] }
1551
- } }
1552
+ { 'url': '{scheme}://{defaultHost}',
1553
+ 'variables': {
1554
+ 'scheme': { 'default': request.env['rack.url_scheme'] },
1555
+ 'defaultHost': { 'default': request.env['HTTP_HOST'] }
1556
+ }
1557
+ }
1552
1558
  ]
1553
1559
  }
1554
- json['paths'] = relations.each_with_object({}) do |relation, s|
1555
- unless ::Brick.config.enable_api == false
1560
+ unless ::Brick.config.enable_api == false
1561
+ json['paths'] = relations.each_with_object({}) do |relation, s|
1556
1562
  next if (api_vers = relation.last.fetch(:api, nil)) &&
1557
- !(api_ver_path = api_vers[current_api_ver])
1558
-
1559
- relation_name = api_ver_path || relation.first.tr('.', '/')
1560
- table_description = relation.last[:description]
1561
- s["#{current_api_root}#{relation_name}"] = {
1562
- 'get': {
1563
- 'summary': "list #{relation.first}",
1564
- 'description': table_description,
1565
- 'parameters': relation.last[:cols].map do |k, v|
1566
- param = { in: 'query', 'name' => k, 'schema': { 'type': v.first } }
1567
- if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
1568
- param['description'] = col_descrip
1569
- end
1570
- param
1571
- end,
1572
- 'responses': { '200': { 'description': 'successful' } }
1573
- }
1574
- }
1575
-
1576
- s["#{current_api_root}#{relation_name}/{id}"] = {
1577
- 'patch': {
1578
- 'summary': "update a #{relation.first.singularize}",
1579
- 'description': table_description,
1580
- 'parameters': relation.last[:cols].reject { |k, v| Brick.config.metadata_columns.include?(k) }.map do |k, v|
1581
- param = { 'name' => k, 'schema': { 'type': v.first } }
1582
- if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
1583
- param['description'] = col_descrip
1584
- end
1585
- param
1586
- end,
1587
- 'responses': { '200': { 'description': 'successful' } }
1588
- }
1589
- } unless relation.last.fetch(:isView, nil)
1563
+ !(api_ver_paths = api_vers[current_api_ver])
1564
+
1565
+ # binding.pry if relation.last.fetch(:isView, nil) && api_ver_paths != { relation.first => ::Brick::ALL_API_ACTIONS }
1566
+ (api_ver_paths || { relation.first => ::Brick::ALL_API_ACTIONS }).each do |api_ver_path, actions|
1567
+ relation_name = api_ver_path || relation.first.tr('.', '/')
1568
+ table_description = relation.last[:description]
1569
+ unless actions&.exclude?(:index)
1570
+ s["#{current_api_root}#{relation_name}"] = {
1571
+ 'get': {
1572
+ 'summary': "list #{relation.first}",
1573
+ 'description': table_description,
1574
+ 'parameters': relation.last[:cols].map do |k, v|
1575
+ param = { in: 'query', 'name' => k, 'schema': { 'type': v.first } }
1576
+ if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
1577
+ param['description'] = col_descrip
1578
+ end
1579
+ param
1580
+ end,
1581
+ 'responses': { '200': { 'description': 'successful' } }
1582
+ }
1583
+ }
1584
+ end
1585
+
1586
+ unless actions&.exclude?(:update)
1587
+ s["#{current_api_root}#{relation_name}/{id}"] = {
1588
+ 'patch': {
1589
+ 'summary': "update a #{relation.first.singularize}",
1590
+ 'description': table_description,
1591
+ 'parameters': relation.last[:cols].reject { |k, v| Brick.config.metadata_columns.include?(k) }.map do |k, v|
1592
+ param = { 'name' => k, 'schema': { 'type': v.first } }
1593
+ if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
1594
+ param['description'] = col_descrip
1595
+ end
1596
+ param
1597
+ end,
1598
+ 'responses': { '200': { 'description': 'successful' } }
1599
+ }
1600
+ } unless relation.last.fetch(:isView, nil)
1601
+ end
1602
+ end # Do multiple api_ver_paths
1590
1603
  end
1591
1604
  end
1592
1605
  render inline: json.to_json, content_type: request.format
@@ -1600,11 +1613,11 @@ class Object
1600
1613
  end
1601
1614
  render inline: exported_csv, content_type: request.format
1602
1615
  return
1603
- elsif request.format == :js || current_api_root # Asking for JSON?
1604
- # %%% Add: where, order, page, page_size, offset, limit
1605
- data = (model.is_view? || !Object.const_defined?('DutyFree')) ? model.limit(1000) : model.df_export(model.brick_import_template)
1606
- render inline: { data: data }.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
1607
- return
1616
+ # elsif request.format == :js || current_api_root # Asking for JSON?
1617
+ # # %%% Add: where, order, page, page_size, offset, limit
1618
+ # data = (model.is_view? || !Object.const_defined?('DutyFree')) ? model.limit(1000) : model.df_export(model.brick_import_template)
1619
+ # render inline: { data: data }.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
1620
+ # return
1608
1621
  end
1609
1622
 
1610
1623
  # Normal (not swagger or CSV) request
@@ -1616,8 +1629,15 @@ class Object
1616
1629
 
1617
1630
  ar_relation = ActiveRecord.version < Gem::Version.new('4') ? model.preload : model.all
1618
1631
  @_brick_params = ar_relation.brick_select(params, (selects ||= []), order_by,
1619
- translations = {},
1620
- join_array = ::Brick::JoinArray.new)
1632
+ translations = {},
1633
+ join_array = ::Brick::JoinArray.new)
1634
+
1635
+ if request.format == :js || current_api_root # Asking for JSON?
1636
+ data = ar_relation.respond_to?(:_select!) ? ar_relation.dup._select!(*selects) : ar_relation.select(selects)
1637
+ render inline: { data: data }.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
1638
+ return
1639
+ end
1640
+
1621
1641
  # %%% Add custom HM count columns
1622
1642
  # %%% What happens when the PK is composite?
1623
1643
  counts = model._br_hm_counts.each_with_object([]) do |v, s|
@@ -2127,7 +2147,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
2127
2147
  # AND kcu2.TABLE_NAME = ?;", Apartment::Tenant.current, table_name
2128
2148
  fk_references = ActiveRecord::Base.execute_sql(sql)
2129
2149
  when 'SQLite'
2130
- sql = "SELECT m.name, fkl.\"from\", fkl.\"table\", m.name || '_' || fkl.\"from\" AS constraint_name
2150
+ sql = "SELECT NULL AS constraint_schema, m.name, fkl.\"from\", NULL AS primary_schema, fkl.\"table\", m.name || '_' || fkl.\"from\" AS constraint_name
2131
2151
  FROM sqlite_master m
2132
2152
  INNER JOIN pragma_foreign_key_list(m.name) fkl ON m.type = 'table'
2133
2153
  ORDER BY m.name, fkl.seq"
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 105
8
+ TINY = 106
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/brick.rb CHANGED
@@ -124,6 +124,8 @@ if Gem::Specification.all_names.any? { |g| g.start_with?('rails-') }
124
124
  require 'brick/frameworks/rails'
125
125
  end
126
126
  module Brick
127
+ ALL_API_ACTIONS = [:index, :show, :create, :update, :destroy]
128
+
127
129
  class << self
128
130
  def sti_models
129
131
  @sti_models ||= {}
@@ -347,6 +349,16 @@ module Brick
347
349
  Brick.config.api_roots
348
350
  end
349
351
 
352
+ # @api public
353
+ def api_filter=(proc)
354
+ Brick.config.api_filter = proc
355
+ end
356
+
357
+ # @api public
358
+ def api_filter
359
+ Brick.config.api_filter
360
+ end
361
+
350
362
  # @api public
351
363
  def skip_database_views=(value)
352
364
  Brick.config.skip_database_views = value
@@ -675,6 +687,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
675
687
  api_done_views = (versioned_views[api_root] ||= {})
676
688
  found = nil
677
689
  view_relation = nil
690
+ actions = ::Brick::ALL_API_ACTIONS # By default all actions are allowed
678
691
  # If it's a view then see if there's a versioned one available by searching for resource names
679
692
  # versioned with the closest number (equal to or less than) compared with our API version number.
680
693
  if v.key?(:isView) && (ver = k.match(/^v([\d_]*)/).captures.first)[-1] == '_'
@@ -687,15 +700,15 @@ In config/initializers/brick.rb appropriate entries would look something like:
687
700
  api_ver = api_root.split('/')[-1]&.gsub('_', '.')
688
701
  vn_idx = api_ver.rindex(/[^\d._]/) # Position of the first numeric digit at the end of the version number
689
702
  # Was: .to_d
690
- api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
703
+ test_ver_num = api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
691
704
  # puts [api_ver, vn_idx, api_ver_num, unversioned].inspect
692
705
 
693
706
  next if ver.to_i > api_ver_num # Don't surface any newer views in an older API
694
707
 
695
- api_ver_num -= 1 until api_ver_num.zero? ||
696
- (view_relation = ::Brick.relations.fetch(
697
- found = "v#{api_ver_num}_#{k[ver.length + 1..-1]}", nil
698
- ))
708
+ test_ver_num -= 1 until test_ver_num.zero? ||
709
+ (view_relation = ::Brick.relations.fetch(
710
+ found = "v#{test_ver_num}_#{k[ver.length + 1..-1]}", nil
711
+ ))
699
712
  api_done_views[unversioned] = nil # Mark that for this API version this view is done
700
713
 
701
714
  # puts "Found #{found}" if view_relation
@@ -705,21 +718,69 @@ In config/initializers/brick.rb appropriate entries would look something like:
705
718
  view_relation ||= ::Brick.relations.fetch(found = unversioned,
706
719
  ::Brick.relations.fetch(found = unversioned, nil)
707
720
  )
708
- if found && view_relation && k != (found = unversioned)
709
- view_relation[:api][api_ver_num] = found
721
+ if found && view_relation && k != unversioned
722
+ # Call proc that limits which endpoints get surfaced based on version, table or view name, method (get list / get one / post / patch / delete)
723
+ # Returning nil makes it do nothing, false makes it skip creating this endpoint, and an array of up to
724
+ # these 3 things controls and changes the nature of the endpoint that gets built:
725
+ # (updated api_name, name of different relation to route to, allowed actions such as :index, :show, :create, etc)
726
+ proc_result = if (filter = ::Brick.config.api_filter).is_a?(Proc)
727
+ begin
728
+ filter.call(unversioned, k, actions, api_ver_num, found, test_ver_num)
729
+ rescue StandardError => e
730
+ puts "::Brick.api_filter Proc error: #{e.message}"
731
+ end
732
+ end
733
+ # proc_result expects to receive back: [updated_api_name, to_other_relation, allowed_actions]
734
+
735
+ case proc_result
736
+ when NilClass
737
+ # Do nothing differently than what normal behaviour would be
738
+ when FalseClass # Skip implementing this endpoint
739
+ view_relation[:api][api_ver_num] = nil
740
+ next
741
+ when Array # Did they give back an array of actions?
742
+ unless proc_result.any? { |pr| ::Brick::ALL_API_ACTIONS.exclude?(pr) }
743
+ proc_result = [unversioned, to_relation, proc_result]
744
+ end
745
+ # Otherwise don't change this array because it's probably legit
746
+ when String
747
+ proc_result = [proc_result] # Treat this as the surfaced api_name (path) they want to use for this endpoint
748
+ else
749
+ puts "::Brick.api_filter Proc warning: Unable to parse this result returned: \n #{proc_result.inspect}"
750
+ proc_result = nil # Couldn't understand what in the world was returned
751
+ end
752
+
753
+ if proc_result&.present?
754
+ if proc_result[1] # to_other_relation
755
+ if (new_view_relation = ::Brick.relations.fetch(proc_result[1], nil))
756
+ k = proc_result[1] # Route this call over to this different relation
757
+ view_relation = new_view_relation
758
+ else
759
+ puts "::Brick.api_filter Proc warning: Unable to find new suggested relation with name #{proc_result[1]} -- sticking with #{k} instead."
760
+ end
761
+ end
762
+ if proc_result.first&.!=(k) # updated_api_name -- a different name than this relation would normally have
763
+ found = proc_result.first
764
+ end
765
+ actions &= proc_result[2] if proc_result[2] # allowed_actions
766
+ end
710
767
  end
768
+ (view_relation[:api][api_ver_num] ||= {})[found] = actions # Add to the list of API paths this resource responds to
711
769
  end
712
770
 
713
771
  # view_ver_num = if (first_part = k.split('_').first) =~ /^v[\d_]+/
714
772
  # first_part[1..-1].gsub('_', '.').to_i
715
773
  # end
716
774
  controller_name = view_relation.fetch(:resource, nil)&.pluralize if view_relation
717
- if schema_name
718
- full_resource = "#{schema_name}/#{found || v[:resource]}"
719
- send(:get, "#{api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" })
720
- else
721
- # Normally goes to something like: /api/v1/employees
722
- send(:get, "#{api_root}#{found || v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" })
775
+ # %%% So far we can only surface the #index action
776
+ if actions.include?(:index)
777
+ if schema_name
778
+ full_resource = "#{schema_name}/#{found || v[:resource]}"
779
+ send(:get, "#{api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" })
780
+ else
781
+ # Normally goes to something like: /api/v1/employees
782
+ send(:get, "#{api_root}#{found || v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" })
783
+ end
723
784
  end
724
785
  end
725
786
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brick
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.105
4
+ version: 1.0.106
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-17 00:00:00.000000000 Z
11
+ date: 2023-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: 1.42.0
167
+ - !ruby/object:Gem::Dependency
168
+ name: rswag-ui
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: mysql2
169
183
  requirement: !ruby/object:Gem::Requirement