foobara-typescript-remote-command-generator 1.1.4 → 1.1.6

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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/src/remote_generator/services/aggregate_entity_generator.rb +1 -1
  4. data/src/remote_generator/services/aggregate_model_generator.rb +1 -1
  5. data/src/remote_generator/services/atom_entity_generator.rb +1 -1
  6. data/src/remote_generator/services/atom_model_generator.rb +1 -1
  7. data/src/remote_generator/services/command_cast_result_generator.rb +313 -0
  8. data/src/remote_generator/services/command_generator.rb +36 -0
  9. data/src/remote_generator/services/command_result_generator.rb +20 -4
  10. data/src/remote_generator/services/dependency_group.rb +0 -10
  11. data/src/remote_generator/services/domain_generator.rb +2 -1
  12. data/src/remote_generator/services/entity_variants_generator.rb +1 -1
  13. data/src/remote_generator/services/error_generator.rb +1 -1
  14. data/src/remote_generator/services/model_variants_generator.rb +5 -5
  15. data/src/remote_generator/services/type_generator.rb +15 -3
  16. data/src/remote_generator/services/typescript_from_manifest_base_generator.rb +6 -11
  17. data/src/remote_generator/write_typescript_to_disk.rb +0 -19
  18. data/templates/Command/castJsonResult.ts.erb +9 -0
  19. data/templates/Command.ts.erb +9 -0
  20. data/templates/Entity/Loaded.ts.erb +6 -0
  21. data/templates/Entity/Unloaded.ts.erb +4 -5
  22. data/templates/Foobara/Auth/utils/accessTokens.ts.erb +7 -13
  23. data/templates/base/Entity.ts +16 -4
  24. data/templates/base/QueryCache.ts +4 -0
  25. data/templates/base/RemoteCommand.ts +7 -1
  26. data/templates/hooks/useQuery.ts +11 -7
  27. metadata +5 -5
  28. data/src/remote_generator/services/auth/setup_generator.rb +0 -25
  29. data/templates/setup.ts.erb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5730e34976cb1769563181451bbde187218e84e4abbf6d48af05951dbab07b61
4
- data.tar.gz: b97e1b7a5e7ccc4c136bc722f81ca694b40f954779b6ad1c6e980a2d2d10a8aa
3
+ metadata.gz: 4ded6d4d4e950f079230273d922ef380455597c4dd0fcf01005396156871291e
4
+ data.tar.gz: 9f2ff8044bd23a32c5db7768a4783e17c3be1742f43edf7d6c60664935e9ff1f
5
5
  SHA512:
6
- metadata.gz: 67632d61f05b1a7613f40c22efbc90e77b828e3cbe89e9c1adaef4ff3627f645c3465714ddf9edb8e86853d762f0b5188ac7d6794d9eb0b6fc2d22aaf43c204c
7
- data.tar.gz: 513ad99446b01309740aba890c8faa53cb4047becfbc5a81a466fd536b7543c8e2c3a8fcd926a835b0f5cb9f6801e29c78680da039f6237ad57a06fbb70a0751
6
+ metadata.gz: 4447f6d43c30e80f514c8db191bbf8bb115ded65df4febaa030c877bffd88db7820db66d637185353e7bb7aab81d666dc38002d5413d99358f5a07e217fdcbfc
7
+ data.tar.gz: 4929a9a0ccb737f0df18979adcfc7d3d223c272c810f44aef208441b01077b77fcde6a114e0f822347530a39774c5bd06b0d2463ddb6503835a18d3853ea7449
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## [1.1.6] - 2025-10-17
2
+
3
+ - Dirty all queries on login/logout, not just GetCurrentUser
4
+ - Remove no-longer needed /setup.ts file generation
5
+
6
+ ## [1.1.5] - 2025-10-15
7
+
8
+ - Implement castJsonResult
9
+ - Fix various problems with Model and subclass constructors
10
+ - Memoize various #model_generators, #dependencies, #dependency_roots
11
+ - Cache generator creation
12
+ - Add ruby-prof and term trap spec support files
13
+
1
14
  ## [1.1.4] - 2025-10-02
2
15
 
3
16
  - Don't give warning about importing domain/setup.ts if it already has been added to index.tsx
@@ -25,7 +25,7 @@ module Foobara
25
25
  end
26
26
 
27
27
  def model_generators
28
- types_depended_on.select(&:model?).map do |model|
28
+ @model_generators ||= types_depended_on.select(&:model?).map do |model|
29
29
  Services::AggregateModelGenerator.new(model)
30
30
  end
31
31
  end
@@ -25,7 +25,7 @@ module Foobara
25
25
  end
26
26
 
27
27
  def model_generators
28
- types_depended_on.select(&:model?).map do |model|
28
+ @model_generators ||= types_depended_on.select(&:model?).map do |model|
29
29
  if model.detached_entity?
30
30
  Services::AggregateEntityGenerator.new(model)
31
31
  else
@@ -23,7 +23,7 @@ module Foobara
23
23
  end
24
24
 
25
25
  def model_generators
26
- types_depended_on.select(&:model?).map do |model|
26
+ @model_generators ||= types_depended_on.select(&:model?).map do |model|
27
27
  if model.detached_entity?
28
28
  Services::UnloadedEntityGenerator.new(model)
29
29
  else
@@ -25,7 +25,7 @@ module Foobara
25
25
  end
26
26
 
27
27
  def model_generators
28
- types_depended_on.select(&:model?).map do |model|
28
+ @model_generators ||= types_depended_on.select(&:model?).map do |model|
29
29
  if model.detached_entity?
30
30
  Services::UnloadedEntityGenerator.new(model)
31
31
  else
@@ -0,0 +1,313 @@
1
+ require_relative "command_result_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class CommandCastResultGenerator < CommandResultGenerator
7
+ class CastTree
8
+ attr_accessor :children, :declaration_to_cast, :past_first_model
9
+
10
+ def initialize(children: nil, declaration_to_cast: nil, past_first_model: false)
11
+ self.children = children
12
+ self.declaration_to_cast = declaration_to_cast
13
+ self.past_first_model = past_first_model
14
+ end
15
+
16
+ def empty?
17
+ (children.nil? || children.empty?) && declaration_to_cast.nil?
18
+ end
19
+ end
20
+
21
+ alias command_manifest relevant_manifest
22
+
23
+ def result_type
24
+ command_manifest.result_type
25
+ end
26
+
27
+ def target_path
28
+ [*scoped_full_path, "castJsonResult.ts"]
29
+ end
30
+
31
+ def template_path
32
+ "Command/castJsonResult.ts.erb"
33
+ end
34
+
35
+ def applicable?
36
+ result_json_requires_cast?
37
+ end
38
+
39
+ def nested_model_generators
40
+ return @nested_model_generators if defined?(@nested_model_generators)
41
+
42
+ nested_model_generators = []
43
+
44
+ generators = model_generators
45
+
46
+ generators.each do |generator|
47
+ _models_reachable_from_declaration(
48
+ generator.relevant_manifest
49
+ )&.each do |(model, past_first)|
50
+ generator_class = if atom?
51
+ if model.detached_entity? && past_first
52
+ Services::UnloadedEntityGenerator
53
+ else
54
+ Services::AtomModelGenerator
55
+ end
56
+ elsif aggregate?
57
+ Services::AggregateModelGenerator
58
+ else
59
+ Services::ModelGenerator
60
+ end
61
+
62
+ new_generator = generator_class.new(model)
63
+
64
+ unless generators.any? do |g|
65
+ g.relevant_manifest == model && g.class == new_generator.class
66
+ end
67
+ nested_model_generators << new_generator
68
+ end
69
+ end
70
+ end
71
+
72
+ @nested_model_generators = nested_model_generators
73
+ end
74
+
75
+ def atom?
76
+ serializers&.any? { |s| s == "Foobara::CommandConnectors::Serializers::AtomicSerializer" }
77
+ end
78
+
79
+ def aggregate?
80
+ serializers&.any? { |s| s == "Foobara::CommandConnectors::Serializers::AggregateSerializer" }
81
+ end
82
+
83
+ def dependencies
84
+ @dependencies ||= model_generators + nested_model_generators
85
+ end
86
+
87
+ def cast_json_result_function
88
+ "#{cast_json_result_function_body}\nreturn json"
89
+ end
90
+
91
+ private
92
+
93
+ # TODO: need to make use of initial?
94
+ def cast_json_result_function_body(
95
+ cast_tree = _construct_cast_tree(result_type),
96
+ parent: "json",
97
+ property: nil,
98
+ value: parent
99
+ )
100
+ if cast_tree.nil? || cast_tree.empty?
101
+ return
102
+ end
103
+
104
+ result = []
105
+
106
+ case cast_tree
107
+ when CastTree
108
+ result << cast_json_result_function_body(cast_tree.children, parent: value)
109
+ result << _ts_cast_expression(cast_tree, parent:, property:, value:)
110
+ when ::Hash
111
+ cast_tree.each_pair do |path_part, child_cast_tree|
112
+ if path_part == :"#"
113
+ result << "#{parent}?.forEach((element: any, index: number, array: any[]) => {"
114
+ result << cast_json_result_function_body(child_cast_tree,
115
+ parent: "array",
116
+ property: "index",
117
+ value: "element")
118
+ result << "})"
119
+ elsif child_cast_tree.is_a?(::Hash)
120
+ # TODO: either test this codepath or delete it
121
+ # :nocov:
122
+ property = path_part =~ /\A\d+\z/ ? path_part.to_i : "\"#{path_part}\""
123
+
124
+ result << cast_json_result_function_body(child_cast_tree,
125
+ parent:,
126
+ property:,
127
+ value: "#{parent}.#{path_part}")
128
+ # :nocov:
129
+ elsif child_cast_tree.is_a?(CastTree)
130
+ result << cast_json_result_function_body(child_cast_tree.children,
131
+ parent: "#{parent}.#{path_part}")
132
+
133
+ property = path_part =~ /\A\d+\z/ ? path_part.to_i : "\"#{path_part}\""
134
+ result << _ts_cast_expression(child_cast_tree,
135
+ parent:,
136
+ property:,
137
+ value: "#{parent}.#{path_part}")
138
+ else
139
+ # :nocov:
140
+ raise "Not sure how to handle a #{cast_tree.class}: #{cast_tree}"
141
+ # :nocov:
142
+ end
143
+ end
144
+ else
145
+ # :nocov:
146
+ raise "Not sure how to handle a #{cast_tree.class}: #{cast_tree}"
147
+ # :nocov:
148
+ end
149
+
150
+ result.compact.join("\n")
151
+ end
152
+
153
+ def _ts_cast_expression(cast_tree, value:, parent: nil, property: nil)
154
+ unless cast_tree.is_a?(CastTree)
155
+ # :nocov:
156
+ raise "Expected a CastTree but got a #{cast_tree.class}: #{cast_tree}"
157
+ # :nocov:
158
+ end
159
+
160
+ type_declaration = cast_tree.declaration_to_cast
161
+
162
+ return unless type_declaration
163
+
164
+ lvalue = if parent
165
+ if property.nil?
166
+ parent
167
+ elsif property =~ /\A"(.*)"\z/
168
+ "#{parent}.#{$1}"
169
+ else
170
+ "#{parent}[#{property}]"
171
+ end
172
+ else
173
+ # TODO: either test this path or raise
174
+ # :nocov:
175
+ value
176
+ # :nocov:
177
+ end
178
+
179
+ type = if type_declaration.is_a?(Manifest::TypeDeclaration)
180
+ type_declaration.to_type
181
+ else
182
+ type_declaration
183
+ end
184
+
185
+ type_symbol = type.type_symbol
186
+
187
+ value ||= lvalue
188
+
189
+ if type_symbol == :date || type_symbol == :datetime
190
+ "#{lvalue} = new Date(#{value})"
191
+ elsif type.model?
192
+ ts_model_name = model_to_ts_model_name(type, association_depth:,
193
+ initial: !cast_tree.past_first_model)
194
+
195
+ "#{lvalue} = new #{ts_model_name}(#{value})"
196
+ else
197
+ # :nocov:
198
+ raise "Not sure how to cast type #{type} to a Typescript expression"
199
+ # :nocov:
200
+ end
201
+ end
202
+
203
+ def _construct_cast_tree(type_declaration, past_first_model: false)
204
+ if type_declaration.is_a?(Manifest::Attributes)
205
+ return unless type_declaration.has_attribute_declarations?
206
+ return if type_declaration.attribute_declarations.empty?
207
+
208
+ path_tree = {}
209
+
210
+ type_declaration.attribute_declarations.each_pair do |attribute_name, attribute_declaration|
211
+ if type_requires_cast?(attribute_declaration)
212
+ path_tree[attribute_name] = _construct_cast_tree(attribute_declaration, past_first_model:)
213
+ end
214
+ end
215
+
216
+ unless path_tree.empty?
217
+ CastTree.new(children: path_tree, past_first_model:)
218
+ end
219
+ elsif type_declaration.is_a?(Manifest::Array)
220
+ element_type = type_declaration.element_type
221
+
222
+ if element_type && type_requires_cast?(element_type)
223
+ CastTree.new(children: { "#": _construct_cast_tree(element_type, past_first_model:) })
224
+ end
225
+ elsif type_declaration.type.to_sym == :date || type_declaration.type.to_sym == :datetime
226
+ CastTree.new(declaration_to_cast: type_declaration, past_first_model:)
227
+ elsif type_declaration.model?
228
+ type_declaration = type_declaration.to_type
229
+
230
+ children = if type_declaration.detached_entity? && atom?
231
+ nil
232
+ else
233
+ _construct_cast_tree(type_declaration.attributes_type, past_first_model: true)
234
+ end
235
+
236
+ CastTree.new(children:, declaration_to_cast: type_declaration, past_first_model:)
237
+ # TODO: either test this code path or raise or delete it
238
+ # :nocov:
239
+ elsif type_declaration.custom?
240
+ if type_requires_cast?(type_declaration.base_type.to_type_declaration)
241
+ tree = _construct_cast_tree(type_declaration.base_type.to_type_declaration, past_first_model:)
242
+
243
+ if tree && !tree.empty?
244
+ CastTree.new(children: tree, past_first_model:)
245
+ end
246
+ end
247
+ end
248
+ # :nocov:
249
+ end
250
+
251
+ # TODO: Feels like similar complicated logic is popping up in many places? How to find/converge such logic
252
+ def _models_reachable_from_declaration(type_declaration, past_first_model: false)
253
+ if type_declaration.is_a?(Manifest::Attributes)
254
+ return unless type_declaration.has_attribute_declarations?
255
+ return if type_declaration.attribute_declarations.empty?
256
+
257
+ models = nil
258
+
259
+ type_declaration.attribute_declarations.each_value do |attribute_declaration|
260
+ if type_requires_cast?(attribute_declaration)
261
+ models ||= Set.new
262
+
263
+ _models_reachable_from_declaration(
264
+ attribute_declaration,
265
+ past_first_model:
266
+ )&.each do |pair|
267
+ models << pair
268
+ end
269
+ end
270
+ end
271
+
272
+ models
273
+ elsif type_declaration.is_a?(Manifest::Array)
274
+ element_type = type_declaration.element_type
275
+
276
+ if element_type && type_requires_cast?(element_type)
277
+ _models_reachable_from_declaration(element_type, past_first_model:)
278
+ end
279
+ elsif type_declaration.model?
280
+ if type_declaration.is_a?(Manifest::TypeDeclaration)
281
+ type_declaration = type_declaration.to_type
282
+ end
283
+
284
+ models = Set[[type_declaration, past_first_model]]
285
+
286
+ if atom? && type_declaration.detached_entity?
287
+ return models
288
+ end
289
+
290
+ _models_reachable_from_declaration(
291
+ type_declaration.attributes_type,
292
+ past_first_model: true
293
+ )&.each do |pair|
294
+ models << pair
295
+ end
296
+
297
+ models
298
+ elsif type_declaration.custom?
299
+ # TODO: either test this code path or raise or delete it
300
+ # :nocov:
301
+ if type_requires_cast?(type_declaration.base_type.to_type_declaration)
302
+ _models_reachable_from_declaration(
303
+ type_declaration.base_type.to_type_declaration,
304
+ past_first_model:
305
+ )
306
+ end
307
+ # :nocov:
308
+ end
309
+ end
310
+ end
311
+ end
312
+ end
313
+ end
@@ -43,6 +43,42 @@ module Foobara
43
43
  def base_class_name
44
44
  base_class_path.split("/").last
45
45
  end
46
+
47
+ def result_json_requires_cast?
48
+ # What types require a cast?
49
+ # :date and :datetime, :model, custom type declaration (check #custom?)
50
+ result_type && type_requires_cast?(result_type)
51
+ end
52
+
53
+ private
54
+
55
+ def type_requires_cast?(type_declaration)
56
+ if type_declaration.is_a?(Manifest::Attributes)
57
+ return false unless type_declaration.has_attribute_declarations?
58
+ return false if type_declaration.attribute_declarations.empty?
59
+
60
+ type_declaration.attribute_declarations.values.any? do |attribute_declaration|
61
+ type_requires_cast?(attribute_declaration)
62
+ end
63
+ elsif type_declaration.is_a?(Manifest::Array)
64
+ element_type = type_declaration.element_type
65
+ element_type && type_requires_cast?(element_type)
66
+ else
67
+ return true if type_declaration.model?
68
+
69
+ type_symbol = type_declaration.type_symbol
70
+
71
+ if type_symbol == :date || type_symbol == :datetime
72
+ return true
73
+ end
74
+
75
+ if type_declaration.custom?
76
+ type_declaration = type_declaration.to_type if type_declaration.is_a?(Manifest::TypeDeclaration)
77
+ base_type = type_declaration.base_type
78
+ type_requires_cast?(base_type)
79
+ end
80
+ end
81
+ end
46
82
  end
47
83
  end
48
84
  end
@@ -3,7 +3,7 @@ require_relative "typescript_from_manifest_base_generator"
3
3
  module Foobara
4
4
  module RemoteGenerator
5
5
  class Services
6
- class CommandResultGenerator < TypescriptFromManifestBaseGenerator
6
+ class CommandResultGenerator < CommandGenerator
7
7
  alias command_manifest relevant_manifest
8
8
 
9
9
  def result_type
@@ -18,10 +18,20 @@ module Foobara
18
18
  "Command/Result.ts.erb"
19
19
  end
20
20
 
21
- def model_generators(type = result_type, initial = true)
22
- return [] if type.nil?
21
+ def model_generators(*args)
22
+ type, initial = if args.empty?
23
+ return @model_generators if defined?(@model_generators)
23
24
 
24
- if type.detached_entity?
25
+ use_cache = true
26
+
27
+ [result_type, true]
28
+ else
29
+ args
30
+ end
31
+
32
+ if type.nil?
33
+ []
34
+ elsif type.detached_entity?
25
35
  generator_class = if atom?
26
36
  if initial
27
37
  AtomEntityGenerator
@@ -57,9 +67,15 @@ module Foobara
57
67
  end.flatten.uniq
58
68
  elsif type.array?
59
69
  model_generators(type.element_type, false)
70
+ # rubocop:disable Lint/DuplicateBranch
60
71
  else
61
72
  # TODO: handle tuples, associative arrays
62
73
  []
74
+ # rubocop:enable Lint/DuplicateBranch
75
+ end.tap do |result|
76
+ if use_cache
77
+ @model_generators = result
78
+ end
63
79
  end
64
80
  end
65
81
 
@@ -16,16 +16,6 @@ module Foobara
16
16
  self.name = name
17
17
  self.dependencies = dependencies.to_set
18
18
 
19
- dependencies.each do |dep|
20
- if dep.belongs_to_dependency_group
21
- # :nocov:
22
- raise "Dependency group #{dep} already belongs to dependency group #{dep.dependency_group}"
23
- # :nocov:
24
- end
25
-
26
- dep.belongs_to_dependency_group = self
27
- end
28
-
29
19
  find_collisions
30
20
  end
31
21
 
@@ -58,7 +58,8 @@ module Foobara
58
58
  end
59
59
 
60
60
  def dependencies
61
- [*command_generators, *model_generators, *entity_generators, *type_generators, *organization]
61
+ @dependencies ||= [*command_generators, *model_generators, *entity_generators, *type_generators,
62
+ *organization]
62
63
  end
63
64
 
64
65
  def domain_name
@@ -35,7 +35,7 @@ module Foobara
35
35
  end
36
36
 
37
37
  def dependencies
38
- Set[
38
+ @dependencies ||= Set[
39
39
  entity_generator,
40
40
  unloaded_entity_generator,
41
41
  loaded_entity_generator,
@@ -54,7 +54,7 @@ module Foobara
54
54
  end
55
55
 
56
56
  def dependencies
57
- types_depended_on.select { |type| type.detached_entity? || type.custom? || type.model? }
57
+ @dependencies ||= types_depended_on.select { |type| type.detached_entity? || type.custom? || type.model? }
58
58
  end
59
59
 
60
60
  def ts_type_full_path
@@ -23,11 +23,11 @@ module Foobara
23
23
  end
24
24
 
25
25
  def dependencies
26
- if has_associations?
27
- [model_generator, atom_model_generator, aggregate_model_generator]
28
- else
29
- [model_generator]
30
- end
26
+ @dependencies ||= if has_associations?
27
+ [model_generator, atom_model_generator, aggregate_model_generator]
28
+ else
29
+ [model_generator]
30
+ end
31
31
  end
32
32
  end
33
33
  end
@@ -5,15 +5,27 @@ module Foobara
5
5
  class Services
6
6
  class TypeGenerator < TypescriptFromManifestBaseGenerator
7
7
  class << self
8
+ def lru_cache
9
+ @lru_cache ||= LruCache.new(1000)
10
+ end
11
+
8
12
  def new(relevant_manifest)
9
- return super unless self == TypeGenerator
13
+ unless self == TypeGenerator
14
+ generator = lru_cache.cached([self, relevant_manifest]) do
15
+ super
16
+ end
17
+
18
+ return generator
19
+ end
10
20
 
11
21
  if relevant_manifest.detached_entity?
12
22
  EntityGenerator.new(relevant_manifest)
13
23
  elsif relevant_manifest.model?
14
24
  ModelGenerator.new(relevant_manifest)
15
25
  else
16
- super
26
+ lru_cache.cached([self, relevant_manifest]) do
27
+ super
28
+ end
17
29
  end
18
30
  end
19
31
  end
@@ -99,7 +111,7 @@ module Foobara
99
111
  end
100
112
 
101
113
  def dependencies
102
- custom_type_generators + model_generators
114
+ @dependencies ||= custom_type_generators + model_generators
103
115
  end
104
116
 
105
117
  def type_name_downcase
@@ -36,10 +36,7 @@ module Foobara
36
36
  when "Foobara::Auth::Logout"
37
37
  Services::Auth::LogoutGenerator
38
38
  when /\bGetCurrentUser$/
39
- [
40
- Services::Auth::RequiresAuthGenerator,
41
- Services::Auth::SetupGenerator
42
- ]
39
+ Services::Auth::RequiresAuthGenerator
43
40
  else
44
41
  if manifest.requires_authentication?
45
42
  Services::Auth::RequiresAuthGenerator
@@ -52,6 +49,7 @@ module Foobara
52
49
  *generator_classes,
53
50
  Services::CommandInputsGenerator,
54
51
  Services::CommandResultGenerator,
52
+ Services::CommandCastResultGenerator,
55
53
  Services::CommandErrorsGenerator,
56
54
  Services::CommandErrorsIndexGenerator,
57
55
  Services::CommandManifestGenerator
@@ -127,6 +125,8 @@ module Foobara
127
125
  end
128
126
 
129
127
  def dependency_roots
128
+ return @dependency_roots if defined?(@dependency_roots)
129
+
130
130
  unless dependency_group
131
131
  # :nocov:
132
132
  raise "This generator was created without a " \
@@ -134,7 +134,7 @@ module Foobara
134
134
  # :nocov:
135
135
  end
136
136
 
137
- dependency_group.non_colliding_dependency_roots.sort_by(&:scoped_full_name)
137
+ @dependency_roots = dependency_group.non_colliding_dependency_roots.sort_by(&:scoped_full_name)
138
138
  end
139
139
 
140
140
  def ts_instance_path
@@ -380,12 +380,7 @@ module Foobara
380
380
 
381
381
  def path_to_root
382
382
  path = super
383
-
384
- if path.empty?
385
- "./"
386
- else
387
- path
388
- end
383
+ path.empty? ? "./" : path
389
384
  end
390
385
  end
391
386
  end
@@ -59,7 +59,6 @@ module Foobara
59
59
 
60
60
  def run_post_generation_tasks
61
61
  eslint_fix
62
- warn_about_adding_setup_to_index
63
62
  end
64
63
 
65
64
  def eslint_fix
@@ -74,24 +73,6 @@ module Foobara
74
73
  end
75
74
  end
76
75
  end
77
-
78
- def warn_about_adding_setup_to_index
79
- if paths_to_source_code.key?("setup.ts")
80
- index_tsx_path = "#{output_directory}/../index.tsx"
81
-
82
- if File.exist?(index_tsx_path)
83
- unless File.read(index_tsx_path) =~ /import.*domains\/setup/
84
- warn "WARNING: you should add the following to src/index.tsx:\n\n" \
85
- "import './domains/setup'"
86
- end
87
- else
88
- # :nocov:
89
- warn "WARNING: Make sure you add the following somewhere:\n\n" \
90
- "import './domains/setup'"
91
- # :nocov:
92
- end
93
- end
94
- end
95
76
  end
96
77
  end
97
78
  end
@@ -0,0 +1,9 @@
1
+ import Result from "./Result"
2
+
3
+ <% dependency_roots.each do |dependency_root| %>
4
+ import <%= dependency_root.import_destructure %> from "<%= path_to_root %><%= dependency_root.import_path %>"
5
+ <% end %>
6
+
7
+ export default function castJsonResult (json: any): Result {
8
+ <%= cast_json_result_function %>
9
+ }
@@ -3,9 +3,18 @@ import <%= base_class_name %> from "<%= path_to_root %><%= base_class_path %>"
3
3
  import Inputs from "./Inputs"
4
4
  import Result from "./Result"
5
5
  import { Error } from "./Errors"
6
+ <% if result_json_requires_cast? %>
7
+ import castJsonResult from "./castJsonResult"
8
+ <% end %>
6
9
 
7
10
  export class <%= command_name %> extends <%= base_class_name %><Inputs, Result, Error> {
8
11
  static readonly organizationName = "<%= organization_name %>"
9
12
  static readonly domainName = "<%= domain_name %>"
10
13
  static readonly commandName = "<%= command_name %>"
14
+
15
+ <% if result_json_requires_cast? %>
16
+ castJsonResult(json: any): Result {
17
+ return castJsonResult(json)
18
+ }
19
+ <% end %>
11
20
  }
@@ -5,6 +5,12 @@ import {
5
5
 
6
6
  export class Loaded<%= entity_short_name %><T extends <%= entity_short_name %>AttributesType = <%= entity_short_name %>AttributesType> extends <%= entity_short_name %><T> {
7
7
  readonly isLoaded: true = true
8
+
9
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
10
+ constructor(attributes: T) {
11
+ super(attributes)
12
+ }
13
+
8
14
  <% unless has_associations? %>
9
15
  /* eslint-disable @typescript-eslint/class-literal-property-style */
10
16
  get isAtom (): true { return true }
@@ -1,18 +1,17 @@
1
1
  import { Never } from "<%= path_to_root %>base/Entity"
2
2
  import {
3
3
  <%= entity_short_name %>,
4
- //<%= entity_short_name %>PrimaryKeyType
4
+ <%= entity_short_name %>PrimaryKeyType,
5
5
  <%= entity_short_name %>AttributesType
6
6
  } from "./Ambiguous"
7
7
 
8
8
  export type Unloaded<%= entity_short_name %>AttributesType = Never<<%= entity_short_name %>AttributesType>
9
9
 
10
10
  export class Unloaded<%= entity_short_name %> extends <%= entity_short_name %><Unloaded<%= entity_short_name %>AttributesType> {
11
- /*
12
- constructor(id: <%= entity_short_name %>PrimaryKeyType) {
13
- super(id, {})
11
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
12
+ constructor(primaryKey: <%= entity_short_name %>PrimaryKeyType) {
13
+ super(primaryKey)
14
14
  }
15
- */
16
15
 
17
16
  readonly isLoaded: false = false
18
17
  }
@@ -1,31 +1,25 @@
1
1
  import type Query from '../../../base/Query'
2
2
  import type RemoteCommand from '../../../base/RemoteCommand'
3
+ import { forEachQuery } from '../../../base/QueryCache'
3
4
 
4
- let getCurrentUserQuery: Query<RemoteCommand<any, any, any>> | undefined
5
-
6
- export function setGetCurrentUserQuery (query: Query<RemoteCommand<any, any, any>>): void {
7
- getCurrentUserQuery = query
8
- }
9
-
10
- function setCurrentUserDirty () {
11
- console.log('setCurrentUserDirty')
12
- if (getCurrentUserQuery != null) {
13
- getCurrentUserQuery.setDirty()
14
- }
5
+ function dirtyAllQueries () {
6
+ forEachQuery((query: Query<RemoteCommand<any, any, any>>) => {
7
+ query.setDirty()
8
+ })
15
9
  }
16
10
 
17
11
  const accessTokens = new Map<string, string>()
18
12
 
19
13
  const logout = (urlBase: string): void => {
20
14
  accessTokens.delete(urlBase)
21
- setCurrentUserDirty()
15
+ dirtyAllQueries()
22
16
  }
23
17
  let handleLogout: (baseUrl: string) => void = logout
24
18
 
25
19
  const tokenForUrl = (baseUrl: string): string | undefined => accessTokens.get(baseUrl)
26
20
  const handleLogin: (baseUrl: string, accessToken: string) => void = (baseUrl, accessToken) => {
27
21
  accessTokens.set(baseUrl, accessToken)
28
- setCurrentUserDirty()
22
+ dirtyAllQueries()
29
23
  }
30
24
 
31
25
  if (typeof BroadcastChannel !== 'undefined') {
@@ -17,10 +17,22 @@ export abstract class Entity<PrimaryKeyType extends EntityPrimaryKeyType, Attrib
17
17
  abstract get hasAssociations(): boolean
18
18
  abstract get associationPropertyPaths (): string[][]
19
19
 
20
- constructor(primaryKey: PrimaryKeyType, attributes: AttributesType) {
21
- super(attributes)
22
- this.primaryKey = primaryKey
23
- this.isLoaded = attributes !== undefined
20
+ constructor(primaryKeyOrAttributes: PrimaryKeyType | AttributesType) {
21
+ let attributes: AttributesType | undefined
22
+
23
+ const klass = new.target as typeof Entity<PrimaryKeyType, AttributesType>
24
+
25
+ if (typeof primaryKeyOrAttributes === "object") {
26
+ attributes = primaryKeyOrAttributes
27
+ super(attributes)
28
+ this.isLoaded = true
29
+ this.primaryKey = this.attributes[klass.primaryKeyAttributeName as keyof AttributesType] as PrimaryKeyType
30
+ } else {
31
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
32
+ super({[klass.primaryKeyAttributeName]: primaryKeyOrAttributes} as AttributesType)
33
+ this.isLoaded = false
34
+ this.primaryKey = primaryKeyOrAttributes as PrimaryKeyType
35
+ }
24
36
  }
25
37
 
26
38
  /* Can we make this work or not?
@@ -33,6 +33,10 @@ export function getQuery<CommandT extends RemoteCommand<any, any, any>> (
33
33
  return query
34
34
  }
35
35
 
36
+ export function forEachQuery (callback: (query: Query<RemoteCommand<any, any, any>>) => void): void {
37
+ queryCache.forEach(callback)
38
+ }
39
+
36
40
  function toKey<CommandT extends RemoteCommand<any, any, any>> (
37
41
  CommandClass: RemoteCommandConstructor<CommandT>,
38
42
  inputs: InputsOf<CommandT> | undefined
@@ -101,13 +101,19 @@ export default abstract class RemoteCommand<Inputs, Result, CommandError extends
101
101
  return await fetch(this._buildUrl(), this._buildRequestParams())
102
102
  }
103
103
 
104
+ castJsonResult (json: any): Result {
105
+ return json
106
+ }
107
+
104
108
  async _handleResponse (response: Response): Promise<Outcome<Result, CommandError>> {
105
109
  const text = await response.text()
106
110
  const body = JSON.parse(text)
107
111
 
108
112
  if (response.ok) {
113
+ const result = this.castJsonResult(body)
114
+
115
+ this.outcome = new SuccessfulOutcome<Result, CommandError>(result)
109
116
  this.commandState = 'succeeded'
110
- this.outcome = new SuccessfulOutcome<Result, CommandError>(body)
111
117
  } else if (response.status === 422 || response.status === 401 || response.status === 403) {
112
118
  this.commandState = 'errored'
113
119
  this.outcome = new ErrorOutcome<Result, CommandError>(body)
@@ -57,19 +57,23 @@ export default function useQuery<CommandT extends RemoteCommand<any, any, any>>
57
57
  queryRef.current = query
58
58
  }
59
59
 
60
+ const [queryState, setQueryState] = useState<QueryState<CommandT>>(
61
+ queryToQueryState<CommandT>(query)
62
+ )
63
+
60
64
  useEffect(() => {
61
- const unsubscribe = query?.onChange(() => {
62
- if (query != null) { // just here to satisfy type checker
65
+ if (query == null) {
66
+ return undefined
67
+ }
68
+
69
+ const removeListener = query.onChange(() => {
70
+ if (query != null) { // Just here to satisfy type check
63
71
  setQueryState(queryToQueryState<CommandT>(query))
64
72
  }
65
73
  })
66
74
 
67
- return unsubscribe
75
+ return removeListener
68
76
  }, [query])
69
77
 
70
- const [queryState, setQueryState] = useState<QueryState<CommandT>>(
71
- queryToQueryState<CommandT>(query)
72
- )
73
-
74
78
  return queryState
75
79
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara-typescript-remote-command-generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
@@ -15,7 +15,7 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 0.1.14
18
+ version: 0.1.16
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
21
  version: 2.0.0
@@ -25,7 +25,7 @@ dependencies:
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 0.1.14
28
+ version: 0.1.16
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: 2.0.0
@@ -70,7 +70,7 @@ files:
70
70
  - src/remote_generator/services/auth/refresh_login_generator.rb
71
71
  - src/remote_generator/services/auth/requires_auth_command_generator.rb
72
72
  - src/remote_generator/services/auth/requires_auth_generator.rb
73
- - src/remote_generator/services/auth/setup_generator.rb
73
+ - src/remote_generator/services/command_cast_result_generator.rb
74
74
  - src/remote_generator/services/command_errors_generator.rb
75
75
  - src/remote_generator/services/command_errors_index_generator.rb
76
76
  - src/remote_generator/services/command_generator.rb
@@ -103,6 +103,7 @@ files:
103
103
  - templates/Command/Errors.ts.erb
104
104
  - templates/Command/Inputs.ts.erb
105
105
  - templates/Command/Result.ts.erb
106
+ - templates/Command/castJsonResult.ts.erb
106
107
  - templates/Command/errors/index.ts.erb
107
108
  - templates/Domain.ts.erb
108
109
  - templates/Domain/config.ts.erb
@@ -134,7 +135,6 @@ files:
134
135
  - templates/base/RemoteCommand.ts
135
136
  - templates/base/RemoteCommandTypes.ts
136
137
  - templates/hooks/useQuery.ts
137
- - templates/setup.ts.erb
138
138
  homepage: https://github.com/foobara/typescript-remote-command-generator
139
139
  licenses:
140
140
  - Apache-2.0
@@ -1,25 +0,0 @@
1
- module Foobara
2
- module RemoteGenerator
3
- class Services
4
- module Auth
5
- class SetupGenerator < CommandGenerator
6
- def template_path
7
- "setup.ts.erb"
8
- end
9
-
10
- def target_path
11
- ["setup.ts"]
12
- end
13
-
14
- def command_generator
15
- generator_for(command_manifest)
16
- end
17
-
18
- def dependencies
19
- [command_generator]
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,6 +0,0 @@
1
- import { getQuery } from './base/QueryCache'
2
- import { setGetCurrentUserQuery } from './Foobara/Auth/utils/accessTokens'
3
-
4
- import <%= command_generator.import_destructure %> from "<%= path_to_root %><%= command_generator.import_path %>"
5
-
6
- setGetCurrentUserQuery(getQuery(<%= dependency_group.non_colliding_type(command_generator) %>, undefined))