app_rail-airtable 0.3.2 → 0.3.3

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: 98fa16c4b212cd84ea0d9cd5caa37100c3f3bf9d97919cb1814d914d45fdf2c6
4
- data.tar.gz: d16166e491328eaa897086cd43940e11b52e8345bfd8bef3bcb547537304e2cd
3
+ metadata.gz: 6a8973236f81565186f4a9cf047fbe516cd7f3f452b551ab0e02c9406fce1c88
4
+ data.tar.gz: 313a3e40f5eb79bd17c6561e2f65f4946ae4df80afdf16d901cf2bad21c23ad0
5
5
  SHA512:
6
- metadata.gz: 2532b3183fba2e26b117e232a54d65120f9e08a874d46a1a08e6331bc11ee10de6a8460622d68c7bc46ec633090beb023d7de7b4a34dc2546c6013469b2157d6
7
- data.tar.gz: ae372f1540f615fffd90045c6e2c6b4f1e4252278dcb256d4262d04a6356b50e1510676ed94f6d0358c4608f967caec00214fa6df4ebb6b9334f41846ec5a85d
6
+ metadata.gz: dc54fefbcb0f1ef89868e249b2c9b456322a3f78a39fe550e45c20716d1b723f989556281b91d5f437fdf21de0de1a1fbbe6bbd65dff14982d7aa93f570e2b26
7
+ data.tar.gz: b11d2a98504953e57c171b38f13f3e618bdcf8bb42b014e62c883c4deeb3d92dbf1bb20e70a30b70394ed5e83f9c330e25ce6da5627f46509d23f4da81ddc1d7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- app_rail-airtable (0.3.2)
4
+ app_rail-airtable (0.3.3)
5
5
  activesupport
6
6
  airrecord
7
7
  bcrypt
@@ -44,7 +44,7 @@ GEM
44
44
  faraday-net_http_persistent (1.2.0)
45
45
  faraday-patron (1.0.0)
46
46
  faraday-rack (1.0.0)
47
- i18n (1.8.10)
47
+ i18n (1.8.11)
48
48
  concurrent-ruby (~> 1.0)
49
49
  minitest (5.14.4)
50
50
  multipart-post (2.1.1)
@@ -81,7 +81,7 @@ GEM
81
81
  tilt (2.0.10)
82
82
  tzinfo (2.0.4)
83
83
  concurrent-ruby (~> 1.0)
84
- zeitwerk (2.4.2)
84
+ zeitwerk (2.5.1)
85
85
 
86
86
  PLATFORMS
87
87
  ruby
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/string/inflections'
2
4
  require 'app_rail/airtable/string_ext'
3
5
  require 'thor'
@@ -11,21 +13,48 @@ module AppRail
11
13
  argument :output_directory
12
14
  argument :api_key
13
15
  argument :base_id
14
- argument :tables
16
+ argument :schema
15
17
 
16
18
  def self.source_root
17
- File.join(File.dirname(__FILE__), "..", "..", "..")
19
+ File.join(File.dirname(__FILE__), '..', '..', '..')
18
20
  end
19
21
 
20
22
  def create_project
21
- # Build tables
22
- @table_definitions = tables.split(",").map {|t| Airrecord.table(api_key, base_id, t.strip) }
23
+ validate_airtable_schema
23
24
  directory('templates/project', output_directory)
24
25
  end
25
-
26
+
26
27
  def self.exit_on_failure?
27
28
  true
28
29
  end
30
+
31
+ private
32
+
33
+ def validate_airtable_schema
34
+ tables.each do |table|
35
+ table_name = table['name']
36
+ table_fields = table['fields'].concat(table['associations']).map { |field| field['name'] }.compact
37
+
38
+ airtable_table = find_table(table_name)
39
+ compare_fields(table_fields, airtable_table)
40
+ end
41
+ end
42
+
43
+ def tables
44
+ @tables ||= JSON.parse(schema)['tables']
45
+ end
46
+
47
+ def find_table(table_name)
48
+ Airrecord.table(api_key, base_id, table_name)
49
+ end
50
+
51
+ def compare_fields(table_fields, airtable_table)
52
+ airtable_table.records.first.fields.each_key do |key|
53
+ next if table_fields.include? key
54
+
55
+ raise "ERROR! The key \"#{key}\" is not present in the schema definition."
56
+ end
57
+ end
29
58
  end
30
59
  end
31
- end
60
+ end
@@ -1,5 +1,5 @@
1
1
  module AppRail
2
2
  module Airtable
3
- VERSION = "0.3.2"
3
+ VERSION = "0.3.3"
4
4
  end
5
5
  end
@@ -5,17 +5,17 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
5
 
6
6
  ruby '2.7.4'
7
7
 
8
- gem 'app_rail-airtable'
8
+ gem 'app_rail-airtable', '~> 0.3.3'
9
9
 
10
10
  group :development do
11
- gem 'dotenv'
11
+ gem 'dotenv'
12
12
  end
13
13
 
14
14
  group :test do
15
- gem "rspec"
16
- gem "rack-test"
15
+ gem 'rack-test'
16
+ gem 'rspec'
17
17
  end
18
18
 
19
19
  group :development, :test do
20
- gem 'byebug'
21
- end
20
+ gem 'byebug'
21
+ end
@@ -10,10 +10,14 @@ GEM
10
10
  airrecord (1.0.7)
11
11
  faraday (>= 0.10, < 2.0)
12
12
  net-http-persistent
13
- app_rail-airtable (0.2.2)
13
+ app_rail-airtable (0.3.3)
14
14
  activesupport
15
15
  airrecord
16
+ bcrypt
16
17
  sinatra
18
+ thor
19
+ bcrypt (3.1.16)
20
+ byebug (11.1.3)
17
21
  concurrent-ruby (1.1.9)
18
22
  connection_pool (2.2.5)
19
23
  diff-lcs (1.4.4)
@@ -37,7 +41,7 @@ GEM
37
41
  faraday-net_http_persistent (1.2.0)
38
42
  faraday-patron (1.0.0)
39
43
  faraday-rack (1.0.0)
40
- i18n (1.8.10)
44
+ i18n (1.8.11)
41
45
  concurrent-ruby (~> 1.0)
42
46
  minitest (5.14.4)
43
47
  multipart-post (2.1.1)
@@ -62,23 +66,25 @@ GEM
62
66
  rspec-mocks (3.10.2)
63
67
  diff-lcs (>= 1.2.0, < 2.0)
64
68
  rspec-support (~> 3.10.0)
65
- rspec-support (3.10.2)
69
+ rspec-support (3.10.3)
66
70
  ruby2_keywords (0.0.5)
67
71
  sinatra (2.1.0)
68
72
  mustermann (~> 1.0)
69
73
  rack (~> 2.2)
70
74
  rack-protection (= 2.1.0)
71
75
  tilt (~> 2.0)
76
+ thor (1.1.0)
72
77
  tilt (2.0.10)
73
78
  tzinfo (2.0.4)
74
79
  concurrent-ruby (~> 1.0)
75
- zeitwerk (2.4.2)
80
+ zeitwerk (2.5.1)
76
81
 
77
82
  PLATFORMS
78
83
  ruby
79
84
 
80
85
  DEPENDENCIES
81
- app_rail-airtable
86
+ app_rail-airtable (~> 0.3.3)
87
+ byebug
82
88
  dotenv
83
89
  rack-test
84
90
  rspec
@@ -1,15 +1,62 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'app_rail/airtable'
4
+ require 'active_support/inflector'
5
+
6
+ <% @tables.each do |table| -%>
7
+ <% keys = table['fields'].map { |field| field['name'] if field['type'] != 'association' }.compact -%>
8
+ class <%= table['name'].singularize %> < AppRail::Airtable::ApplicationRecord
9
+ <% if table['authenticatable'] == 'True' -%>
10
+ include AppRail::Airtable::Authenticatable
2
11
 
3
- <% @table_definitions.each do |td| -%>
4
- <% keys = td.all.first.fields.keys -%>
5
- class <%= td.table_name.singularize %> < AppRail::Airtable::ApplicationRecord
12
+ <% end -%>
6
13
  airtable_attr <%= keys.map{|f| "\"#{f}\""}.join(", ") %>
7
- ar_list_item text: :<%= keys.first.snake_case %>
14
+ <% table['ar_class_methods']&.each do |method| -%>
15
+ <%= method['name'] %> <%= method['properties'].map {|prop| "#{prop['type']}: :#{prop['value']}" }.join(', ') %>
16
+ <% end -%>
17
+
18
+ <% table['associations']&.each do |association| -%>
19
+ <% word_ending_method = association['type'] == 'has_many' ? :pluralize : :singularize -%>
20
+ <%= association['type'] %> <%= ":#{association['model'].snake_case.send(word_ending_method)}, class: \"#{association['model'].camelize.singularize}\", column: \"#{association['model'].camelize.send(word_ending_method)}\"" %>
21
+ <% end -%>
22
+ <% if table['authenticatable'] == 'True' -%>
23
+
24
+ def self.create_as_json(params:, current_user:)
25
+ record = <%= table['name'].singularize %>.create(email: params["payload"]["email"], password: params["payload"]["password"])
26
+ { response: { id: record.id }, oauth_session: record.oauth_session}
27
+ end
28
+ <% end -%>
8
29
  end
9
30
 
10
31
  <% end -%>
32
+
33
+ <% authenticatable_table = @tables.select { |table| table['authenticatable'] == 'True' }&.first -%>
34
+ <% authenticatable_resource = authenticatable_table['name'] if authenticatable_table -%>
11
35
  class Server < AppRail::Airtable::Sinatra
12
- <% @table_definitions.each do |td| -%>
13
- resources :<%= td.table_name.snake_case %>
36
+ <% if authenticatable_resource -%>
37
+ <% model_name = authenticatable_resource.singularize -%>
38
+ helpers do
39
+ def find_authenticatable_resource(access_token:)
40
+ <%= model_name %>.find_by_access_token(access_token)
41
+ end
42
+ end
43
+
44
+ authenticatable_resources :<%= authenticatable_resource.snake_case %> do
45
+ <% @tables.each do |table| -%>
46
+ <% next if table['name'] == authenticatable_resource -%>
47
+ resources :<%= table['name'].snake_case %>
48
+ <% end -%>
49
+ end
50
+
51
+ post "/sessions" do
52
+ oauth_session = <%= model_name %>.create_session_as_json(email: params["username"], password: params["password"])
53
+ halt 401 unless oauth_session
54
+
55
+ oauth_session.to_json
56
+ end
57
+ <% else -%>
58
+ <% @tables.each do |table| -%>
59
+ resources :<%= table['name'].snake_case %>
60
+ <% end -%>
14
61
  <% end -%>
15
62
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% authenticatable_table = @tables.select { |table| table['authenticatable'] == 'True' }&.first -%>
4
+ <% authenticatable_resource = authenticatable_table['name'] if authenticatable_table -%>
5
+ require 'spec_helper'
6
+
7
+ RSpec.describe Server do
8
+ let(:json_response) { JSON.parse(response.body, symbolize_names: true) }
9
+ let(:access_token) { SecureRandom.hex }
10
+ let(:auth_headers) { { 'HTTP_AUTHORIZATION' => "Bearer: #{access_token}" } }
11
+
12
+ def app
13
+ Server
14
+ end
15
+
16
+ <% if authenticatable_resource -%>
17
+ <% auth_model_name = authenticatable_resource.singularize -%>
18
+ <% auth_path_name = authenticatable_resource.snake_case.pluralize -%>
19
+ describe 'POST /<%= auth_path_name %>' do
20
+ let(:params) { { payload: { email: 'test@test.com', password: 'Secret000' } } }
21
+ let(:response) { post "/<%= auth_path_name %>", params.to_json }
22
+ let(:mock_resource) { double(<%= auth_model_name %>) }
23
+
24
+ before { allow_any_instance_of(<%= auth_model_name %>).to receive(:create) { mock_resource } }
25
+
26
+ it { expect(response.status).to eq 201 }
27
+ it { expect(json_response[:oauth_session][:access_token]).to_not be_nil }
28
+ end
29
+
30
+ describe 'POST /sessions' do
31
+ let(:params) { { username: 'test@test.com', password: 'Secret000' } }
32
+ let(:response) { post "/sessions", params }
33
+
34
+ context 'ok' do
35
+ let(:oauth_session) { { access_token: SecureRandom.hex } }
36
+
37
+ before { allow(<%= auth_model_name %>).to receive(:create_session_as_json).with({ email: params[:username], password: params[:password] }) { oauth_session } }
38
+
39
+ it { expect(response.status).to eq 200 }
40
+ it { expect(json_response[:access_token]).to_not be_nil }
41
+ end
42
+
43
+ context 'invalid' do
44
+ before { allow(<%= auth_model_name %>).to receive(:create_session_as_json).with({ email: params[:username], password: params[:password] }) { nil } }
45
+
46
+ it { expect(response.status).to eq 401 }
47
+ it { expect(response.body).to eq '' }
48
+ end
49
+ end
50
+
51
+ <% @tables.each do |table| -%>
52
+ <% next if table['name'] == authenticatable_resource -%>
53
+ <% model_name = table['name'].singularize -%>
54
+ <% path_name = table['name'].snake_case.pluralize -%>
55
+ describe 'GET /<%= path_name %>' do
56
+ let(:params) { {} }
57
+ let(:response) { get '/<%= path_name %>', params.to_json, auth_headers}
58
+ let(:mock_auth_resource) { double(<%= auth_model_name %>) }
59
+ let(:mock_resource) { double(<%= model_name %>, id: '1') }
60
+ let(:resource_to_json) { { id: '1' } }
61
+
62
+ before do
63
+ allow(<%= auth_model_name %>).to receive(:find_by_access_token).with(access_token) { mock_auth_resource }
64
+ allow(<%= model_name %>).to receive(:all) { [mock_resource] }
65
+ allow(mock_resource).to receive(:ar_list_item_as_json) { resource_to_json }
66
+ end
67
+
68
+ it { expect(response.status).to eq 200 }
69
+ it { expect(json_response).to eq [resource_to_json] }
70
+ end
71
+
72
+ <% end -%>
73
+ <% else -%>
74
+ <% @tables.each do |table| -%>
75
+ <% model_name = table['name'].singularize -%>
76
+ <% path_name = table['name'].snake_case.pluralize -%>
77
+ describe 'GET /<%= path_name %>' do
78
+ let(:params) { {} }
79
+ let(:response) { get '/<%= path_name %>', params.to_json, auth_headers}
80
+ let(:mock_resource) { double(<%= model_name %>, id: '1') }
81
+ let(:resource_to_json) { { id: '1' } }
82
+
83
+ before do
84
+ allow(<%= model_name %>).to receive(:all) { [mock_resource] }
85
+ allow(mock_resource).to receive(:ar_list_item_as_json) { resource_to_json }
86
+ end
87
+
88
+ it { expect(response.status).to eq 200 }
89
+ it { expect(json_response).to eq [resource_to_json] }
90
+ end
91
+
92
+ <% end -%>
93
+ <% end -%>
94
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app_rail-airtable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Brooke-Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-13 00:00:00.000000000 Z
11
+ date: 2021-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -143,7 +143,7 @@ files:
143
143
  - templates/project/app.json
144
144
  - templates/project/config.ru
145
145
  - templates/project/lib/server.rb.tt
146
- - templates/project/spec/server_spec.rb
146
+ - templates/project/spec/server_spec.rb.tt
147
147
  - templates/project/spec/spec_helper.rb
148
148
  homepage: https://github.com/FutureWorkshops/app_rail-airtable
149
149
  licenses:
@@ -1,9 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe Server do
4
-
5
- def app
6
- Server
7
- end
8
-
9
- end