brick 1.0.105 → 1.0.106

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 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