foobara-typescript-remote-command-generator 0.0.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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/LICENSE-APACHE.txt +202 -0
  4. data/LICENSE-MIT.txt +21 -0
  5. data/LICENSE.txt +8 -0
  6. data/README.md +55 -0
  7. data/lib/foobara/typescript_remote_command_generator.rb +13 -0
  8. data/src/remote_generator/association_depth.rb +11 -0
  9. data/src/remote_generator/generate_typescript.rb +85 -0
  10. data/src/remote_generator/services/aggregate_entity_generator.rb +49 -0
  11. data/src/remote_generator/services/aggregate_model_generator.rb +53 -0
  12. data/src/remote_generator/services/atom_entity_generator.rb +47 -0
  13. data/src/remote_generator/services/atom_model_generator.rb +49 -0
  14. data/src/remote_generator/services/command_errors_generator.rb +36 -0
  15. data/src/remote_generator/services/command_errors_index_generator.rb +31 -0
  16. data/src/remote_generator/services/command_generator.rb +41 -0
  17. data/src/remote_generator/services/command_inputs_generator.rb +33 -0
  18. data/src/remote_generator/services/command_manifest_generator.rb +13 -0
  19. data/src/remote_generator/services/command_result_generator.rb +80 -0
  20. data/src/remote_generator/services/dependency_group.rb +137 -0
  21. data/src/remote_generator/services/domain_config_generator.rb +21 -0
  22. data/src/remote_generator/services/domain_generator.rb +63 -0
  23. data/src/remote_generator/services/domain_manifest_generator.rb +17 -0
  24. data/src/remote_generator/services/entity_generator.rb +43 -0
  25. data/src/remote_generator/services/entity_manifest_generator.rb +10 -0
  26. data/src/remote_generator/services/entity_variants_generator.rb +47 -0
  27. data/src/remote_generator/services/error_generator.rb +68 -0
  28. data/src/remote_generator/services/loaded_entity_generator.rb +27 -0
  29. data/src/remote_generator/services/manifest_generator.rb +13 -0
  30. data/src/remote_generator/services/model_generator.rb +84 -0
  31. data/src/remote_generator/services/model_manifest_generator.rb +11 -0
  32. data/src/remote_generator/services/model_variants_generator.rb +35 -0
  33. data/src/remote_generator/services/organization_config_generator.rb +21 -0
  34. data/src/remote_generator/services/organization_generator.rb +41 -0
  35. data/src/remote_generator/services/organization_manifest_generator.rb +17 -0
  36. data/src/remote_generator/services/processor_class_generator.rb +18 -0
  37. data/src/remote_generator/services/root_manifest_generator.rb +13 -0
  38. data/src/remote_generator/services/typescript_from_manifest_base_generator.rb +293 -0
  39. data/src/remote_generator/services/unloaded_entity_generator.rb +25 -0
  40. data/src/remote_generator/write_typescript_to_disk.rb +78 -0
  41. data/src/remote_generator.rb +5 -0
  42. data/templates/Command/Errors.ts.erb +11 -0
  43. data/templates/Command/Inputs.ts.erb +5 -0
  44. data/templates/Command/Result.ts.erb +7 -0
  45. data/templates/Command/errors/index.ts.erb +3 -0
  46. data/templates/Command.ts.erb +11 -0
  47. data/templates/Domain/config.ts.erb +16 -0
  48. data/templates/Domain.ts.erb +17 -0
  49. data/templates/Entity/Aggregate.ts.erb +18 -0
  50. data/templates/Entity/Ambiguous.ts.erb +30 -0
  51. data/templates/Entity/Atom.ts.erb +18 -0
  52. data/templates/Entity/Loaded.ts.erb +14 -0
  53. data/templates/Entity/Unloaded.ts.erb +18 -0
  54. data/templates/EntityVariants.ts.erb +40 -0
  55. data/templates/Error.ts.erb +8 -0
  56. data/templates/Model/Aggregate.ts.erb +12 -0
  57. data/templates/Model/Atom.ts.erb +12 -0
  58. data/templates/Model/Model.ts.erb +18 -0
  59. data/templates/ModelVariants.ts.erb +26 -0
  60. data/templates/Organization/config.ts.erb +16 -0
  61. data/templates/Organization.ts.erb +8 -0
  62. data/templates/base/DataPath.ts +49 -0
  63. data/templates/base/Entity.ts +87 -0
  64. data/templates/base/Error.ts +29 -0
  65. data/templates/base/Model.ts +24 -0
  66. data/templates/base/Outcome.ts +42 -0
  67. data/templates/base/RemoteCommand.ts +87 -0
  68. metadata +142 -0
@@ -0,0 +1,31 @@
1
+ require_relative "command_errors_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class CommandErrorsIndexGenerator < CommandErrorsGenerator
7
+ alias command_manifest relevant_manifest
8
+
9
+ def target_path
10
+ [*super[0..-2], "errors", "index.ts"]
11
+ end
12
+
13
+ def template_path
14
+ "Command/errors/index.ts.erb"
15
+ end
16
+
17
+ def dependencies
18
+ []
19
+ end
20
+
21
+ def applicable?
22
+ errors_in_this_namespace.any?
23
+ end
24
+
25
+ def import_destructure
26
+ "* as #{scoped_name}Errors"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ require_relative "typescript_from_manifest_base_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class CommandGenerator < TypescriptFromManifestBaseGenerator
7
+ alias command_manifest relevant_manifest
8
+
9
+ def target_path
10
+ [*scoped_full_path, "index.ts"]
11
+ end
12
+
13
+ def template_path
14
+ "Command.ts.erb"
15
+ end
16
+
17
+ def domain_generator
18
+ @domain_generator ||= Services::DomainGenerator.new(domain)
19
+ end
20
+
21
+ foobara_delegate :organization_generator, :domain_name, :organization_name, to: :domain_generator
22
+
23
+ def errors_in_this_namespace
24
+ @errors_in_this_namespace ||= possible_errors.values.map(&:error).uniq.sort_by(&:error_name).select do |error|
25
+ error.parent&.path&.map(&:to_s) == path.map(&:to_s)
26
+ end.map do |error_manifest|
27
+ Services::ErrorGenerator.new(error_manifest)
28
+ end
29
+ end
30
+
31
+ def dependencies
32
+ errors_in_this_namespace
33
+ end
34
+
35
+ def command_errors_index_generator
36
+ Services::CommandErrorsIndexGenerator.new(command_manifest)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ require_relative "typescript_from_manifest_base_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class CommandInputsGenerator < TypescriptFromManifestBaseGenerator
7
+ alias command_manifest relevant_manifest
8
+
9
+ def target_path
10
+ [*scoped_full_path, "Inputs.ts"]
11
+ end
12
+
13
+ def template_path
14
+ "Command/Inputs.ts.erb"
15
+ end
16
+
17
+ def model_generators
18
+ @model_generators ||= inputs_types_depended_on.select(&:model?).uniq.map do |model|
19
+ if model.entity?
20
+ Services::EntityGenerator.new(model)
21
+ else
22
+ Services::ModelGenerator.new(model)
23
+ end
24
+ end
25
+ end
26
+
27
+ def dependencies
28
+ model_generators
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "manifest_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class CommandManifestGenerator < ManifestGenerator
7
+ def target_path
8
+ [*scoped_full_path, "manifest.json"]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,80 @@
1
+ require_relative "typescript_from_manifest_base_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class CommandResultGenerator < TypescriptFromManifestBaseGenerator
7
+ alias command_manifest relevant_manifest
8
+
9
+ def result_type
10
+ command_manifest.result_type
11
+ end
12
+
13
+ def target_path
14
+ [*scoped_full_path, "Result.ts"]
15
+ end
16
+
17
+ def template_path
18
+ "Command/Result.ts.erb"
19
+ end
20
+
21
+ def model_generators(type = result_type, initial = true)
22
+ if type.entity?
23
+ generator_class = if atom?
24
+ if initial
25
+ AtomEntityGenerator
26
+ else
27
+ UnloadedEntityGenerator
28
+ end
29
+ elsif aggregate?
30
+ AggregateEntityGenerator
31
+ else
32
+ EntityGenerator
33
+ end
34
+
35
+ [generator_class.new(type.to_entity)]
36
+ elsif type.model?
37
+ generator_class = if atom?
38
+ AtomModelGenerator
39
+ elsif aggregate?
40
+ AggregateModelGenerator
41
+ else
42
+ ModelGenerator
43
+ end
44
+
45
+ [generator_class.new(type.to_model)]
46
+ elsif type.type.to_sym == :attributes
47
+ type.attribute_declarations.values.map do |attribute_declaration|
48
+ model_generators(attribute_declaration, false)
49
+ end.flatten.uniq
50
+ else
51
+ # TODO: handle tuples, associative arrays, arrays
52
+ []
53
+ end
54
+ end
55
+
56
+ def atom?
57
+ serializers&.any? { |s| s == "Foobara::CommandConnectors::Serializers::AtomicSerializer" }
58
+ end
59
+
60
+ def aggregate?
61
+ serializers&.any? { |s| s == "Foobara::CommandConnectors::Serializers::AggregateSerializer" }
62
+ end
63
+
64
+ def association_depth
65
+ if atom?
66
+ AssociationDepth::ATOM
67
+ elsif aggregate?
68
+ AssociationDepth::AGGREGATE
69
+ else
70
+ AssociationDepth::AMBIGUOUS
71
+ end
72
+ end
73
+
74
+ def dependencies
75
+ model_generators
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,137 @@
1
+ module Foobara
2
+ module RemoteGenerator
3
+ class Services
4
+ class DependencyGroup
5
+ class CollisionData
6
+ attr_accessor :points
7
+
8
+ def collisions_for_points
9
+ @collisions_for_points ||= {}
10
+ end
11
+ end
12
+
13
+ attr_accessor :dependencies, :name
14
+
15
+ def initialize(dependencies, name:)
16
+ self.name = name
17
+ self.dependencies = dependencies.to_set
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
+ find_collisions
30
+ end
31
+
32
+ def collision_data_for(dep)
33
+ key = to_key(dep)
34
+
35
+ collision_data[key].tap do |cd|
36
+ unless cd
37
+ # :nocov:
38
+ raise "Dependency #{dep} is not part of this dependency group"
39
+ # :nocov:
40
+ end
41
+ end
42
+ end
43
+
44
+ def set_collision_data_for(dep, collision_data)
45
+ key = to_key(dep)
46
+ self.collision_data[key] = collision_data
47
+ end
48
+
49
+ def to_key(dep)
50
+ [dep.scoped_category, *dep.ts_instance_full_path].map(&:to_s)
51
+ end
52
+
53
+ def collision_data
54
+ @collision_data ||= {}
55
+ end
56
+
57
+ def non_colliding_dependency_roots
58
+ roots = Set.new
59
+
60
+ dependencies.each do |dep|
61
+ roots << non_colliding_root(dep)
62
+ end
63
+
64
+ roots
65
+ end
66
+
67
+ def non_colliding_root(dep)
68
+ root = dep
69
+ points = points_for(dep)
70
+ points_climbed = 0
71
+
72
+ until points_climbed >= points
73
+ # TODO: can't use scoped_path because sometimes we want UnloadedUser instead of User. How to fix??
74
+ points_climbed += dep.scoped_path.size
75
+ root = root.parent
76
+ end
77
+
78
+ root
79
+ end
80
+
81
+ def points_for(dep)
82
+ points = collision_data_for(dep).points
83
+
84
+ unless points
85
+ # :nocov:
86
+ raise "Dependency #{dep} has no collision data"
87
+ # :nocov:
88
+ end
89
+
90
+ points
91
+ end
92
+
93
+ def non_colliding_type_name(dep, points = points_for(dep))
94
+ non_colliding_type_path(dep, points).join(".")
95
+ end
96
+
97
+ def non_colliding_type(dep, points = points_for(dep))
98
+ non_colliding_type_name(dep, points)
99
+ end
100
+
101
+ def non_colliding_type_path(dep, points = points_for(dep))
102
+ start_at = dep.ts_type_full_path.size - points - 1
103
+ path = dep.ts_type_full_path[start_at..] || []
104
+
105
+ path.map(&:to_s)
106
+ end
107
+
108
+ private
109
+
110
+ def find_collisions
111
+ dependencies.each do |dep|
112
+ collision_data = CollisionData.new
113
+
114
+ points = 0
115
+
116
+ loop do
117
+ name = non_colliding_type_name(dep, points)
118
+
119
+ collisions = dependencies.select do |other_dep|
120
+ dep != other_dep && name == non_colliding_type_name(other_dep, points)
121
+ end
122
+
123
+ if collisions.empty?
124
+ collision_data.points = points
125
+ set_collision_data_for(dep, collision_data)
126
+ break
127
+ else
128
+ collision_data.collisions_for_points[points] = collisions
129
+ points += 1
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "domain_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class DomainConfigGenerator < DomainGenerator
7
+ def target_path
8
+ [*super[0..-2], "config.ts"]
9
+ end
10
+
11
+ def template_path
12
+ "Domain/config.ts.erb"
13
+ end
14
+
15
+ def dependencies
16
+ []
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,63 @@
1
+ require_relative "typescript_from_manifest_base_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class DomainGenerator < TypescriptFromManifestBaseGenerator
7
+ alias domain_manifest relevant_manifest
8
+
9
+ def import_destructure
10
+ "* as #{domain_name}"
11
+ end
12
+
13
+ def target_path
14
+ if global?
15
+ ["GlobalDomain", "index.ts"]
16
+ else
17
+ [*scoped_full_path, "index.ts"]
18
+ end
19
+ end
20
+
21
+ def template_path
22
+ "Domain.ts.erb"
23
+ end
24
+
25
+ def command_generators
26
+ @command_generators ||= domain_manifest.commands.map do |command_manifest|
27
+ CommandGenerator.new(command_manifest)
28
+ end
29
+ end
30
+
31
+ def entity_generators
32
+ @entity_generators ||= domain_manifest.entities.map do |entity_manifest|
33
+ EntityGenerator.new(entity_manifest)
34
+ end
35
+ end
36
+
37
+ def model_generators
38
+ @model_generators ||= begin
39
+ only_models = domain_manifest.models.reject(&:entity?)
40
+
41
+ only_models.map do |model_manifest|
42
+ ModelGenerator.new(model_manifest)
43
+ end
44
+ end
45
+ end
46
+
47
+ def dependencies
48
+ [*command_generators, *model_generators, *entity_generators, *organization]
49
+ end
50
+
51
+ def domain_name
52
+ scoped_short_name || "GlobalDomain"
53
+ end
54
+
55
+ def organization_generator
56
+ @organization_generator ||= OrganizationGenerator.new(domain_manifest.organization)
57
+ end
58
+
59
+ foobara_delegate :organization_name, to: :organization_generator
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "manifest_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class DomainManifestGenerator < ManifestGenerator
7
+ def target_path
8
+ if global?
9
+ ["GlobalDomain", "manifest.json"]
10
+ else
11
+ [*scoped_full_path, "manifest.json"]
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ require_relative "model_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class EntityGenerator < ModelGenerator
7
+ alias entity_manifest relevant_manifest
8
+
9
+ def target_path
10
+ [*domain.scoped_full_path, "types", entity_name, "Ambiguous.ts"]
11
+ end
12
+
13
+ def template_path
14
+ ["Entity", "Ambiguous.ts.erb"]
15
+ end
16
+
17
+ def entity_name(...)
18
+ model_name(...)
19
+ end
20
+
21
+ def primary_key_name
22
+ primary_key_attribute
23
+ end
24
+
25
+ def primary_key_ts_type
26
+ foobara_type_to_ts_type(primary_key_type, dependency_group:)
27
+ end
28
+
29
+ def entity_name_downcase
30
+ model_name_downcase
31
+ end
32
+
33
+ def attribute_names
34
+ super - [primary_key_name]
35
+ end
36
+
37
+ def association_property_paths_ts
38
+ associations.keys.map { |k| DataPath.parse(k).path.map(&:to_s) }.inspect
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,10 @@
1
+ require_relative "model_manifest_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class EntityManifestGenerator < ModelManifestGenerator
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ require_relative "model_variants_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class EntityVariantsGenerator < EntityGenerator
7
+ def target_path
8
+ [*domain.scoped_full_path, "types", "#{model_name}.ts"]
9
+ end
10
+
11
+ def template_path
12
+ "EntityVariants.ts.erb"
13
+ end
14
+
15
+ def entity_generator
16
+ EntityGenerator.new(entity_manifest)
17
+ end
18
+
19
+ def unloaded_entity_generator
20
+ UnloadedEntityGenerator.new(entity_manifest)
21
+ end
22
+
23
+ def loaded_entity_generator
24
+ LoadedEntityGenerator.new(entity_manifest)
25
+ end
26
+
27
+ def atom_entity_generator
28
+ AtomEntityGenerator.new(entity_manifest)
29
+ end
30
+
31
+ def aggregate_entity_generator
32
+ AggregateEntityGenerator.new(entity_manifest)
33
+ end
34
+
35
+ def dependencies
36
+ Set[
37
+ entity_generator,
38
+ unloaded_entity_generator,
39
+ loaded_entity_generator,
40
+ atom_entity_generator,
41
+ aggregate_entity_generator
42
+ ]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,68 @@
1
+ require_relative "typescript_from_manifest_base_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class ErrorGenerator < TypescriptFromManifestBaseGenerator
7
+ alias error_manifest relevant_manifest
8
+
9
+ def target_path
10
+ p = parent
11
+
12
+ basename = "#{error_name}.ts"
13
+
14
+ case parent
15
+ when OrganizationGenerator, DomainGenerator, CommandGenerator
16
+ [*p.target_dir, "errors", basename]
17
+ when nil
18
+ # :nocov:
19
+ raise "Expected #{error_name} to have a parent but it did not"
20
+ # :nocov:
21
+ else
22
+ [*p.target_dir, basename]
23
+ end
24
+ end
25
+
26
+ def error_base_class
27
+ case category
28
+ when "data"
29
+ "DataError"
30
+ else
31
+ "RuntimeError"
32
+ end
33
+ end
34
+
35
+ def template_path
36
+ "Error.ts.erb"
37
+ end
38
+
39
+ def context_type_declaration
40
+ @context_type_declaration ||= Manifest::TypeDeclaration.new(root_manifest, [*path, :context_type_declaration])
41
+ end
42
+
43
+ def context_ts_type
44
+ if context_type_declaration.is_a?(Manifest::Attributes) && context_type_declaration.empty?
45
+ # TODO: update other parts of the generator that disable the linting rule to use this instead...
46
+ "Record<string, never>"
47
+ else
48
+ foobara_type_to_ts_type(context_type_declaration, dependency_group:)
49
+ end
50
+ end
51
+
52
+ def dependencies
53
+ types_depended_on.select(&:entity?)
54
+ end
55
+
56
+ def ts_type_full_path
57
+ if parent.is_a?(CommandGenerator)
58
+ p = super.dup
59
+ p[-2] += "Errors"
60
+ p
61
+ else
62
+ super
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,27 @@
1
+ require_relative "entity_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class LoadedEntityGenerator < EntityGenerator
7
+ def target_path
8
+ [*domain.scoped_full_path, "types", entity_name, "Loaded.ts"]
9
+ end
10
+
11
+ def template_path
12
+ ["Entity", "Loaded.ts.erb"]
13
+ end
14
+
15
+ def ts_instance_path
16
+ *prefix, name = super
17
+ [*prefix, "Loaded#{name}"]
18
+ end
19
+
20
+ def ts_instance_full_path
21
+ *prefix, name = super
22
+ [*prefix, "Loaded#{name}"]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "typescript_from_manifest_base_generator"
2
+
3
+ module Foobara
4
+ module RemoteGenerator
5
+ class Services
6
+ class ManifestGenerator < TypescriptFromManifestBaseGenerator
7
+ def generate(_elements_to_generate)
8
+ JSON.pretty_generate(relevant_manifest.relevant_manifest)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end