zero-rails-adapter 0.2.0 → 0.3.1
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 +4 -4
- data/CHANGELOG.md +150 -0
- data/README.md +178 -22
- data/app/controllers/zero_rails_adapter/mutations_controller.rb +5 -1
- data/examples/nextjs/README.md +4 -2
- data/lib/generators/zero_rails_adapter/install/templates/initializer.rb +28 -9
- data/lib/generators/zero_rails_adapter/mutator/templates/mutator.rb +4 -1
- data/lib/generators/zero_rails_adapter/publication/publication_generator.rb +21 -0
- data/lib/zero_rails_adapter/configuration.rb +13 -17
- data/lib/zero_rails_adapter/crud/dispatcher.rb +12 -6
- data/lib/zero_rails_adapter/errors.rb +2 -0
- data/lib/zero_rails_adapter/mutator.rb +3 -1
- data/lib/zero_rails_adapter/postgresql/publication_generator.rb +36 -0
- data/lib/zero_rails_adapter/processor.rb +35 -7
- data/lib/zero_rails_adapter/published_schema.rb +143 -0
- data/lib/zero_rails_adapter/relationship.rb +129 -0
- data/lib/zero_rails_adapter/type_script/generator.rb +110 -52
- data/lib/zero_rails_adapter/version.rb +1 -1
- data/lib/zero_rails_adapter.rb +3 -0
- metadata +7 -2
|
@@ -37,13 +37,31 @@ module ZeroRailsAdapter
|
|
|
37
37
|
jsonb: "z.json()"
|
|
38
38
|
}.freeze
|
|
39
39
|
|
|
40
|
-
attr_reader :models
|
|
40
|
+
attr_reader :models, :crud_models
|
|
41
|
+
|
|
42
|
+
def initialize(published_schema: nil, crud_models: nil, manual_relationships: nil)
|
|
43
|
+
schema = published_schema || ZeroRailsAdapter.configuration.published_schema.call
|
|
44
|
+
@published_schema = PublishedSchema.new(
|
|
45
|
+
schema,
|
|
46
|
+
zero_key: ZeroRailsAdapter.configuration.zero_key
|
|
47
|
+
)
|
|
48
|
+
@models = @published_schema.models.sort_by(&:table_name)
|
|
49
|
+
relationship_definitions = manual_relationships ||
|
|
50
|
+
ZeroRailsAdapter.configuration.relationship_provider.call
|
|
51
|
+
@manual_relationships = Array(relationship_definitions).map do |definition|
|
|
52
|
+
Relationship.from_definition(definition, published_schema: @published_schema)
|
|
53
|
+
end
|
|
54
|
+
requested_crud_models = Array(
|
|
55
|
+
crud_models || ZeroRailsAdapter.configuration.crud_model_provider.call
|
|
56
|
+
)
|
|
57
|
+
unpublished = requested_crud_models - models
|
|
58
|
+
if unpublished.any?
|
|
59
|
+
labels = unpublished.map { |model| model.respond_to?(:name) ? model.name : model.inspect }
|
|
60
|
+
raise UnsafePublicationError,
|
|
61
|
+
"CRUD models must also be published: #{labels.join(', ')}"
|
|
62
|
+
end
|
|
41
63
|
|
|
42
|
-
|
|
43
|
-
@models = Array(models || ZeroRailsAdapter.configuration.model_provider.call)
|
|
44
|
-
.select { |model| active_record_model?(model) }
|
|
45
|
-
.uniq
|
|
46
|
-
.sort_by(&:table_name)
|
|
64
|
+
@crud_models = requested_crud_models.uniq.sort_by(&:table_name)
|
|
47
65
|
end
|
|
48
66
|
|
|
49
67
|
def schema
|
|
@@ -75,7 +93,7 @@ module ZeroRailsAdapter
|
|
|
75
93
|
|
|
76
94
|
def table_definitions
|
|
77
95
|
models.map do |model|
|
|
78
|
-
columns = model.
|
|
96
|
+
columns = columns_for(model).map do |column|
|
|
79
97
|
" #{property_name(column.name)}: #{zero_type(model, column)},"
|
|
80
98
|
end.join("\n")
|
|
81
99
|
keys = primary_keys(model).map { |key| quote(key) }.join(", ")
|
|
@@ -91,22 +109,20 @@ module ZeroRailsAdapter
|
|
|
91
109
|
end
|
|
92
110
|
|
|
93
111
|
def relationship_definitions
|
|
94
|
-
relationships.group_by(&:
|
|
95
|
-
kinds = definitions.map
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
"}),"
|
|
109
|
-
].join("\n")
|
|
112
|
+
relationships.group_by(&:source).map do |model, definitions|
|
|
113
|
+
kinds = definitions.map(&:kind).uniq.sort.join(", ")
|
|
114
|
+
entries = definitions.map do |relationship|
|
|
115
|
+
hops = relationship.hops.map do |hop|
|
|
116
|
+
[
|
|
117
|
+
"{",
|
|
118
|
+
" sourceField: [#{hop.source_fields.map { |field| quote(field) }.join(', ')}],",
|
|
119
|
+
" destSchema: #{variable_name(hop.destination)},",
|
|
120
|
+
" destField: [#{hop.destination_fields.map { |field| quote(field) }.join(', ')}],",
|
|
121
|
+
"}"
|
|
122
|
+
].join("\n")
|
|
123
|
+
end.join(", ")
|
|
124
|
+
|
|
125
|
+
"#{property_name(relationship.name)}: #{relationship.kind}(#{hops}),"
|
|
110
126
|
end.join("\n")
|
|
111
127
|
|
|
112
128
|
[
|
|
@@ -119,7 +135,7 @@ module ZeroRailsAdapter
|
|
|
119
135
|
|
|
120
136
|
def schema_export
|
|
121
137
|
table_vars = models.map { |model| variable_name(model) }.join(", ")
|
|
122
|
-
relation_vars = relationships.map(&:
|
|
138
|
+
relation_vars = relationships.map(&:source).uniq.map do |model|
|
|
123
139
|
relationship_variable_name(model)
|
|
124
140
|
end.join(", ")
|
|
125
141
|
|
|
@@ -140,7 +156,7 @@ module ZeroRailsAdapter
|
|
|
140
156
|
end
|
|
141
157
|
|
|
142
158
|
def argument_schemas
|
|
143
|
-
|
|
159
|
+
crud_models.flat_map do |model|
|
|
144
160
|
[
|
|
145
161
|
zod_schema(model, :create),
|
|
146
162
|
zod_schema(model, :update),
|
|
@@ -162,7 +178,7 @@ module ZeroRailsAdapter
|
|
|
162
178
|
end
|
|
163
179
|
|
|
164
180
|
def mutator_export
|
|
165
|
-
tables =
|
|
181
|
+
tables = crud_models.map do |model|
|
|
166
182
|
variable = variable_name(model)
|
|
167
183
|
table = property_name(model.table_name)
|
|
168
184
|
create_values = create_insert_values(model)
|
|
@@ -193,8 +209,8 @@ module ZeroRailsAdapter
|
|
|
193
209
|
|
|
194
210
|
def create_insert_values(model)
|
|
195
211
|
additions = []
|
|
196
|
-
additions << "created_at: now" if
|
|
197
|
-
additions << "updated_at: now" if
|
|
212
|
+
additions << "created_at: now" if published_column?(model, "created_at")
|
|
213
|
+
additions << "updated_at: now" if published_column?(model, "updated_at")
|
|
198
214
|
defaulted_columns(model).each do |column|
|
|
199
215
|
value = if generated_attribute_names(model, :create).include?(column.name)
|
|
200
216
|
"args.#{property_name(column.name)} ?? #{typescript_default(column)}"
|
|
@@ -208,7 +224,7 @@ module ZeroRailsAdapter
|
|
|
208
224
|
|
|
209
225
|
def update_insert_values(model)
|
|
210
226
|
additions = []
|
|
211
|
-
additions << "updated_at: Date.now()" if
|
|
227
|
+
additions << "updated_at: Date.now()" if published_column?(model, "updated_at")
|
|
212
228
|
object_with_additions(additions)
|
|
213
229
|
end
|
|
214
230
|
|
|
@@ -220,21 +236,24 @@ module ZeroRailsAdapter
|
|
|
220
236
|
|
|
221
237
|
def mutation_columns(model, action)
|
|
222
238
|
primary = primary_keys(model)
|
|
239
|
+
immutable = (primary + Array(model.primary_key).compact.map(&:to_s)).uniq
|
|
223
240
|
case action
|
|
224
241
|
when :create
|
|
225
242
|
allowed = generated_attribute_names(model, action)
|
|
226
|
-
model.
|
|
243
|
+
columns_for(model).select do |column|
|
|
227
244
|
!timestamp_column?(column) &&
|
|
228
245
|
(primary.include?(column.name) || allowed.include?(column.name))
|
|
229
246
|
end
|
|
230
247
|
when :update
|
|
231
248
|
allowed = generated_attribute_names(model, action)
|
|
232
|
-
model.
|
|
249
|
+
columns_for(model).select do |column|
|
|
233
250
|
primary.include?(column.name) ||
|
|
234
|
-
(!
|
|
251
|
+
(!immutable.include?(column.name) &&
|
|
252
|
+
!timestamp_column?(column) &&
|
|
253
|
+
allowed.include?(column.name))
|
|
235
254
|
end
|
|
236
255
|
when :destroy
|
|
237
|
-
model.
|
|
256
|
+
columns_for(model).select { |column| primary.include?(column.name) }
|
|
238
257
|
end
|
|
239
258
|
end
|
|
240
259
|
|
|
@@ -250,7 +269,7 @@ module ZeroRailsAdapter
|
|
|
250
269
|
|
|
251
270
|
def defaulted_columns(model)
|
|
252
271
|
primary = primary_keys(model)
|
|
253
|
-
model.
|
|
272
|
+
columns_for(model).select do |column|
|
|
254
273
|
!primary.include?(column.name) &&
|
|
255
274
|
!timestamp_column?(column) &&
|
|
256
275
|
!column.null &&
|
|
@@ -264,7 +283,7 @@ module ZeroRailsAdapter
|
|
|
264
283
|
return column.null ? "#{type}.optional()" : type
|
|
265
284
|
end
|
|
266
285
|
|
|
267
|
-
type =
|
|
286
|
+
type = native_enum_values(model, column)&.then do |values|
|
|
268
287
|
"enumeration<#{values.map { |value| quote(value) }.join(' | ')}>()"
|
|
269
288
|
end
|
|
270
289
|
type ||= mapped_type(SCHEMA_TYPES, model, column)
|
|
@@ -273,7 +292,7 @@ module ZeroRailsAdapter
|
|
|
273
292
|
end
|
|
274
293
|
|
|
275
294
|
def zod_type(model, column, action)
|
|
276
|
-
values =
|
|
295
|
+
values = native_enum_values(model, column)
|
|
277
296
|
type = if column.respond_to?(:array) && column.array
|
|
278
297
|
"z.array(#{mapped_type(ZOD_TYPES, model, column)})"
|
|
279
298
|
elsif values
|
|
@@ -300,18 +319,24 @@ module ZeroRailsAdapter
|
|
|
300
319
|
end
|
|
301
320
|
end
|
|
302
321
|
|
|
303
|
-
def
|
|
322
|
+
def native_enum_values(model, column)
|
|
323
|
+
return unless column.type.to_sym == :enum
|
|
324
|
+
|
|
304
325
|
definition = model.defined_enums[column.name]
|
|
305
|
-
definition
|
|
326
|
+
return definition.keys if definition
|
|
327
|
+
|
|
328
|
+
enum_types = model.connection.enum_types.to_h
|
|
329
|
+
enum_types[column.sql_type] ||
|
|
330
|
+
raise(UnsupportedColumnTypeError,
|
|
331
|
+
"Cannot read PostgreSQL enum values for #{model.name}.#{column.name}")
|
|
306
332
|
end
|
|
307
333
|
|
|
308
334
|
def schema_imports
|
|
309
335
|
imports = %w[createSchema table]
|
|
310
336
|
imports << "relationships" if relationships.any?
|
|
311
|
-
imports << "enumeration" if models.any? { |model| model.defined_enums.any? }
|
|
312
337
|
models.each do |model|
|
|
313
|
-
model.
|
|
314
|
-
imports << if
|
|
338
|
+
columns_for(model).each do |column|
|
|
339
|
+
imports << if native_enum_values(model, column)
|
|
315
340
|
"enumeration"
|
|
316
341
|
elsif column.respond_to?(:array) && column.array
|
|
317
342
|
"json"
|
|
@@ -334,14 +359,42 @@ module ZeroRailsAdapter
|
|
|
334
359
|
end
|
|
335
360
|
|
|
336
361
|
def relationships
|
|
337
|
-
@relationships ||=
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
362
|
+
@relationships ||= begin
|
|
363
|
+
automatic = models.flat_map do |model|
|
|
364
|
+
model.reflect_on_all_associations.filter_map do |reflection|
|
|
365
|
+
next if reflection.polymorphic? || reflection.options[:through]
|
|
366
|
+
|
|
367
|
+
destination = reflection.klass
|
|
368
|
+
next unless models.include?(destination)
|
|
369
|
+
|
|
370
|
+
source_fields, destination_fields =
|
|
371
|
+
relationship_fields(model, reflection, destination)
|
|
372
|
+
if source_fields.all? { |field| published_column?(model, field) } &&
|
|
373
|
+
destination_fields.all? { |field| published_column?(destination, field) }
|
|
374
|
+
Relationship.new(
|
|
375
|
+
source: model,
|
|
376
|
+
name: reflection.name,
|
|
377
|
+
kind: reflection.collection? ? :many : :one,
|
|
378
|
+
hops: [{
|
|
379
|
+
source_fields:,
|
|
380
|
+
destination:,
|
|
381
|
+
destination_fields:
|
|
382
|
+
}],
|
|
383
|
+
published_schema: @published_schema
|
|
384
|
+
)
|
|
385
|
+
end
|
|
386
|
+
rescue NameError
|
|
387
|
+
nil
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
manual_keys = @manual_relationships.map do |relationship|
|
|
392
|
+
[relationship.source, relationship.name]
|
|
393
|
+
end
|
|
394
|
+
(automatic.reject do |relationship|
|
|
395
|
+
manual_keys.include?([relationship.source, relationship.name])
|
|
396
|
+
end + @manual_relationships).sort_by do |relationship|
|
|
397
|
+
[relationship.source.table_name, relationship.name]
|
|
345
398
|
end
|
|
346
399
|
end
|
|
347
400
|
end
|
|
@@ -366,10 +419,15 @@ module ZeroRailsAdapter
|
|
|
366
419
|
end
|
|
367
420
|
|
|
368
421
|
def primary_keys(model)
|
|
369
|
-
|
|
370
|
-
|
|
422
|
+
@published_schema.zero_keys_for(model)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def columns_for(model)
|
|
426
|
+
@published_schema.columns_for(model)
|
|
427
|
+
end
|
|
371
428
|
|
|
372
|
-
|
|
429
|
+
def published_column?(model, name)
|
|
430
|
+
columns_for(model).any? { |column| column.name == name.to_s }
|
|
373
431
|
end
|
|
374
432
|
|
|
375
433
|
def relationship_variable_name(model)
|
data/lib/zero_rails_adapter.rb
CHANGED
|
@@ -11,6 +11,8 @@ require_relative "zero_rails_adapter/errors"
|
|
|
11
11
|
require_relative "zero_rails_adapter/identity"
|
|
12
12
|
require_relative "zero_rails_adapter/context"
|
|
13
13
|
require_relative "zero_rails_adapter/configuration"
|
|
14
|
+
require_relative "zero_rails_adapter/published_schema"
|
|
15
|
+
require_relative "zero_rails_adapter/relationship"
|
|
14
16
|
require_relative "zero_rails_adapter/request_verifiers/api_key"
|
|
15
17
|
require_relative "zero_rails_adapter/registry"
|
|
16
18
|
require_relative "zero_rails_adapter/mutator"
|
|
@@ -18,6 +20,7 @@ require_relative "zero_rails_adapter/mutation"
|
|
|
18
20
|
require_relative "zero_rails_adapter/request"
|
|
19
21
|
require_relative "zero_rails_adapter/crud/dispatcher"
|
|
20
22
|
require_relative "zero_rails_adapter/type_script/generator"
|
|
23
|
+
require_relative "zero_rails_adapter/postgresql/publication_generator"
|
|
21
24
|
require_relative "zero_rails_adapter/storage/zero_schema"
|
|
22
25
|
require_relative "zero_rails_adapter/processor"
|
|
23
26
|
require_relative "zero_rails_adapter/engine"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zero-rails-adapter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zero Rails Adapter contributors
|
|
@@ -35,6 +35,7 @@ executables: []
|
|
|
35
35
|
extensions: []
|
|
36
36
|
extra_rdoc_files: []
|
|
37
37
|
files:
|
|
38
|
+
- CHANGELOG.md
|
|
38
39
|
- LICENSE.txt
|
|
39
40
|
- README.md
|
|
40
41
|
- app/controllers/zero_rails_adapter/mutations_controller.rb
|
|
@@ -46,6 +47,7 @@ files:
|
|
|
46
47
|
- lib/generators/zero_rails_adapter/install/templates/initializer.rb
|
|
47
48
|
- lib/generators/zero_rails_adapter/mutator/mutator_generator.rb
|
|
48
49
|
- lib/generators/zero_rails_adapter/mutator/templates/mutator.rb
|
|
50
|
+
- lib/generators/zero_rails_adapter/publication/publication_generator.rb
|
|
49
51
|
- lib/generators/zero_rails_adapter/typescript/typescript_generator.rb
|
|
50
52
|
- lib/zero_rails_adapter.rb
|
|
51
53
|
- lib/zero_rails_adapter/configuration.rb
|
|
@@ -56,8 +58,11 @@ files:
|
|
|
56
58
|
- lib/zero_rails_adapter/identity.rb
|
|
57
59
|
- lib/zero_rails_adapter/mutation.rb
|
|
58
60
|
- lib/zero_rails_adapter/mutator.rb
|
|
61
|
+
- lib/zero_rails_adapter/postgresql/publication_generator.rb
|
|
59
62
|
- lib/zero_rails_adapter/processor.rb
|
|
63
|
+
- lib/zero_rails_adapter/published_schema.rb
|
|
60
64
|
- lib/zero_rails_adapter/registry.rb
|
|
65
|
+
- lib/zero_rails_adapter/relationship.rb
|
|
61
66
|
- lib/zero_rails_adapter/request.rb
|
|
62
67
|
- lib/zero_rails_adapter/request_verifiers/api_key.rb
|
|
63
68
|
- lib/zero_rails_adapter/storage/zero_schema.rb
|
|
@@ -83,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
83
88
|
- !ruby/object:Gem::Version
|
|
84
89
|
version: '0'
|
|
85
90
|
requirements: []
|
|
86
|
-
rubygems_version: 4.0.
|
|
91
|
+
rubygems_version: 4.0.16
|
|
87
92
|
specification_version: 4
|
|
88
93
|
summary: A Rails 8 Active Record adapter for Rocicorp Zero
|
|
89
94
|
test_files: []
|