8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
# File 'lib/generators/brick/airtable_api_caller.rb', line 8
def pick_tables(is_airtable_id, usage = :migrations)
puts "In order to reference Airtable data you will need a Personal Access Token (PAT) which can be generated by referencing this URL:
https://airtable.com/create/tokens
You need only #{usage == :migrations ? 'this scope:' : "these three scopes:
data.records:read
data.recordComments:read"}
schema.bases:read
Please provide your Airtable PAT:"
pat = gets_password
pat = ENV['AIRTABLE_PAT'] if pat.blank?
require 'net/http'
bases = https_get('https://api.airtable.com/v0/meta/bases', pat)
if (error = bases.fetch('error', nil))
puts error.fetch('message', nil) || 'Error'
return
end
base = gets_list(bases.fetch('bases', nil)&.map { |z| AirtableTable.new(z['id'], z['name']) })
puts
objects = https_get("https://api.airtable.com/v0/meta/bases/#{base.id}/tables", pat).fetch('tables', nil)
if objects.blank?
puts "No tables found in base #{base.name}."
return
end
tables = objects.map { |z| AirtableTable.new(z['id'], z['name'], z['primaryFieldId'], z['fields'], z['views'], base.id) }
chosen = gets_list(tables, tables.dup)
puts
fks = []
associatives = {}
relations = chosen.each_with_object({base_name: base.name}) do |table, s|
tbl_name = sane_table_name(table.name)
cols = {}
col_order = []
table.fields.each do |col|
col_name = sane_name(col['name'])
if col['type'] == 'multipleRecordLinks'
if (frn_tbl = sane_table_name(
chosen.find { |t| t.id == col['options']['linkedTableId'] }&.name
))
if col['options']['prefersSingleRecordLink'] fks << [frn_tbl, (fk_col_name = "#{col_name}_id"), tbl_name, col_name]
col_order << fk_col_name
else camelized = (assoc_name = "#{tbl_name}_#{col_name}_#{frn_tbl}").camelize
if associatives.keys.any? { |a| a.camelize == camelized }
puts "Strangely have found two columns in \"#{table.name}\" with a name similar to \"#{col_name}\". Skipping one to avoid a conflict."
next
end
associatives[assoc_name] = [col_name, frn_tbl, tbl_name]
fks << [assoc_name, frn_tbl, tbl_name, col_name.underscore, tbl_name]
end
end
else
dt = case col['type']
when 'singleLineText', 'url', 'email', 'singleSelect', 'phoneNumber'
'string'
when 'multilineText'
'text'
when 'number', 'currency'
col['options']['precision'] == 0 ? 'integer' : 'decimal'
when 'checkbox'
'boolean'
when 'date'
'date'
when 'multipleSelects', 'singleCollaborator'
'json'
when 'formula', 'count', 'rollup', 'multipleAttachments'
next
end
cols[col_name] = [dt, nil, true, false] col_order << col_name
end
end
pkey = if is_airtable_id
cols['airtable_id'] = ['string', nil, true, false]
'airtable_id'
else
table.fields.find { |f| f['id'] == table.primary_key }['name']
end
s[tbl_name] = {
pkey: { "#{tbl_name}_pkey" => [sane_name(pkey)] },
cols: cols,
col_order: col_order,
fks: {},
airtable_table: table
}
end
associatives.each do |k, v|
pri_fk_name = "#{v[1]}_id"
frn_fk_base = (v[1] == v[2]) ? "#{v[2]}_2" : v[2] frn_fk_name = "#{frn_fk_base}_id"
relations[k] = {
pkey: { "#{k}_pkey" => ['id'] },
cols: { 'id' => ['integer', nil, false, false] }
}
fks << [v[1], pri_fk_name, k, v[0].underscore]
fks << [v[2], frn_fk_name, k, frn_fk_base.underscore]
end
fk_idx = 0
fks.each do |pri_tbl, fk_col, frn_tbl, airtable_col, assoc_tbl|
pri_pk_col = relations[pri_tbl][:pkey].first.last.first
unless assoc_tbl relations[frn_tbl][:cols][fk_col] = [relations[pri_tbl][:cols][pri_pk_col][0], nil, true, false]
end
frn_fks = ((relations[frn_tbl] ||= {})[:fks] ||= {})
this_fk = frn_fks["fk_airtable_#{fk_idx += 1}"] = {
is_bt: !assoc_tbl, fk: fk_col,
assoc_name: airtable_col,
inverse_table: pri_tbl
}
this_fk[:assoc_tbl] = assoc_tbl if assoc_tbl
end
relations
end
|