apiwork 0.6.1 → 0.7.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/apiwork/adapter/serializer/error/default/api_builder.rb +6 -1
  3. data/lib/apiwork/adapter/standard/capability/writing/contract_builder.rb +1 -0
  4. data/lib/apiwork/api/base.rb +31 -0
  5. data/lib/apiwork/api/object.rb +12 -12
  6. data/lib/apiwork/contract/object.rb +74 -74
  7. data/lib/apiwork/controller.rb +6 -2
  8. data/lib/apiwork/export/apiwork.rb +20 -0
  9. data/lib/apiwork/export/apiwork_mapper.rb +232 -0
  10. data/lib/apiwork/export/open_api.rb +2 -0
  11. data/lib/apiwork/export/registry.rb +1 -1
  12. data/lib/apiwork/export/sorbus.rb +1 -5
  13. data/lib/apiwork/export/type_script.rb +1 -4
  14. data/lib/apiwork/export/type_script_mapper.rb +12 -4
  15. data/lib/apiwork/export/zod.rb +0 -9
  16. data/lib/apiwork/export/zod_mapper.rb +22 -1
  17. data/lib/apiwork/export.rb +1 -0
  18. data/lib/apiwork/introspection/api/resource.rb +9 -0
  19. data/lib/apiwork/introspection/dump/param.rb +9 -10
  20. data/lib/apiwork/introspection/dump/resource.rb +3 -1
  21. data/lib/apiwork/introspection/dump/type.rb +15 -4
  22. data/lib/apiwork/introspection/enum.rb +9 -0
  23. data/lib/apiwork/introspection/param/array.rb +3 -0
  24. data/lib/apiwork/introspection/param/base.rb +11 -0
  25. data/lib/apiwork/introspection/param/binary.rb +3 -0
  26. data/lib/apiwork/introspection/param/boolean.rb +3 -0
  27. data/lib/apiwork/introspection/param/date.rb +3 -0
  28. data/lib/apiwork/introspection/param/date_time.rb +3 -0
  29. data/lib/apiwork/introspection/param/decimal.rb +3 -0
  30. data/lib/apiwork/introspection/param/integer.rb +3 -0
  31. data/lib/apiwork/introspection/param/number.rb +3 -0
  32. data/lib/apiwork/introspection/param/record.rb +3 -0
  33. data/lib/apiwork/introspection/param/string.rb +3 -0
  34. data/lib/apiwork/introspection/param/time.rb +3 -0
  35. data/lib/apiwork/introspection/param/uuid.rb +3 -0
  36. data/lib/apiwork/introspection/type.rb +9 -0
  37. data/lib/apiwork/issue.rb +1 -1
  38. data/lib/apiwork/object.rb +99 -99
  39. data/lib/apiwork/reference_generator.rb +43 -0
  40. data/lib/apiwork/representation/attribute.rb +86 -1
  41. data/lib/apiwork/representation/base.rb +4 -0
  42. data/lib/apiwork/version.rb +1 -1
  43. data/lib/apiwork.rb +4 -0
  44. metadata +4 -3
  45. data/lib/apiwork/export/builder_mapper.rb +0 -184
@@ -7,13 +7,10 @@ module Apiwork
7
7
  output :string
8
8
  file_extension '.ts'
9
9
 
10
- option :builders, default: false, type: :boolean
11
10
  option :version, default: '5', enum: %w[4 5], type: :string
12
11
 
13
12
  def generate
14
- output = TypeScriptMapper.map(self, surface)
15
- output += "\n\n#{BuilderMapper.map(self, surface)}" if options[:builders]
16
- output
13
+ TypeScriptMapper.map(self, surface)
17
14
  end
18
15
 
19
16
  private
@@ -28,7 +28,7 @@ module Apiwork
28
28
  properties = type.shape.sort_by { |name, _param| name.to_s }.map do |name, param|
29
29
  key = @export.transform_key(name)
30
30
  ts_type = map_field(param)
31
- optional_marker = param.optional? ? '?' : ''
31
+ optional_marker = optional_in_output?(param) ? '?' : ''
32
32
 
33
33
  prop_jsdoc = jsdoc(description: param.description, example: param.concrete? ? param.example : nil)
34
34
  if prop_jsdoc
@@ -85,7 +85,7 @@ module Apiwork
85
85
  properties = query_params.sort_by { |name, _param| name.to_s }.map do |param_name, param|
86
86
  key = @export.transform_key(param_name)
87
87
  ts_type = map_field(param)
88
- optional_marker = param.optional? ? '?' : ''
88
+ optional_marker = optional_in_output?(param) ? '?' : ''
89
89
  " #{key}#{optional_marker}: #{ts_type};"
90
90
  end.join("\n")
91
91
 
@@ -96,7 +96,7 @@ module Apiwork
96
96
  properties = body_params.sort_by { |name, _param| name.to_s }.map do |param_name, param|
97
97
  key = @export.transform_key(param_name)
98
98
  ts_type = map_field(param)
99
- optional_marker = param.optional? ? '?' : ''
99
+ optional_marker = optional_in_output?(param) ? '?' : ''
100
100
  " #{key}#{optional_marker}: #{ts_type};"
101
101
  end.join("\n")
102
102
 
@@ -186,7 +186,7 @@ module Apiwork
186
186
  properties = param.shape.sort_by { |name, _field| name.to_s }.map do |name, field|
187
187
  key = @export.transform_key(name)
188
188
  ts_type = map_field(field)
189
- optional_marker = partial || field.optional? ? '?' : ''
189
+ optional_marker = partial || optional_in_output?(field) ? '?' : ''
190
190
  "#{key}#{optional_marker}: #{ts_type}"
191
191
  end.join('; ')
192
192
 
@@ -252,6 +252,14 @@ module Apiwork
252
252
  'unknown'
253
253
  end
254
254
 
255
+ def optional_in_output?(param)
256
+ return false unless param.optional?
257
+ return false if param.respond_to?(:default) && param.default?
258
+ return false if param.nullable?
259
+
260
+ true
261
+ end
262
+
255
263
  def type_reference(symbol)
256
264
  pascal_case(symbol)
257
265
  end
@@ -7,7 +7,6 @@ module Apiwork
7
7
  output :string
8
8
  file_extension '.ts'
9
9
 
10
- option :builders, default: false, type: :boolean
11
10
  option :version, default: '4', enum: %w[4], type: :string
12
11
 
13
12
  def generate
@@ -27,14 +26,6 @@ module Apiwork
27
26
  parts << ''
28
27
  end
29
28
 
30
- if options[:builders]
31
- builder_output = BuilderMapper.map(self, surface)
32
- if builder_output.present?
33
- parts << builder_output
34
- parts << ''
35
- end
36
- end
37
-
38
29
  parts.join("\n")
39
30
  end
40
31
 
@@ -458,10 +458,31 @@ module Apiwork
458
458
 
459
459
  def apply_modifiers(type, param, force_optional: nil)
460
460
  type += '.nullable()' if param.nullable?
461
+
462
+ has_default = param.respond_to?(:default) && param.default?
461
463
  optional = force_optional.nil? ? param.optional? : force_optional
462
- type += '.optional()' if optional
464
+
465
+ if has_default
466
+ type += ".default(#{serialize_default(param.default)})"
467
+ elsif optional
468
+ type += '.optional()'
469
+ end
470
+
463
471
  type
464
472
  end
473
+
474
+ def serialize_default(value)
475
+ case value
476
+ when String then "'#{value.gsub("'", "\\\\'")}'"
477
+ when Integer, Float then value.to_s
478
+ when BigDecimal then value.to_s('F')
479
+ when TrueClass, FalseClass then value.to_s
480
+ when Array then '[]'
481
+ when Hash then '{}'
482
+ when NilClass then 'null'
483
+ else value.to_s
484
+ end
485
+ end
465
486
  end
466
487
  end
467
488
  end
@@ -71,6 +71,7 @@ module Apiwork
71
71
  end
72
72
 
73
73
  def register_defaults!
74
+ register(Apiwork)
74
75
  register(OpenAPI)
75
76
  register(TypeScript)
76
77
  register(Zod)
@@ -23,6 +23,14 @@ module Apiwork
23
23
  @dump = dump
24
24
  end
25
25
 
26
+ # @api public
27
+ # The scope for this resource.
28
+ #
29
+ # @return [String, nil]
30
+ def scope
31
+ @dump[:scope]
32
+ end
33
+
26
34
  # @api public
27
35
  # The identifier for this resource.
28
36
  #
@@ -75,6 +83,7 @@ module Apiwork
75
83
  parent_identifiers: parent_identifiers,
76
84
  path: path,
77
85
  resources: resources.transform_values(&:to_h),
86
+ scope: scope,
78
87
  }
79
88
  end
80
89
  end
@@ -33,10 +33,9 @@ module Apiwork
33
33
 
34
34
  reference = resolve_type_reference(options[:type])
35
35
 
36
- {
36
+ result = {
37
37
  reference:,
38
38
  as: options[:as],
39
- default: options[:default],
40
39
  deprecated: options[:deprecated] == true,
41
40
  description: resolve_attribute_description(options),
42
41
  discriminator: nil,
@@ -55,14 +54,15 @@ module Apiwork
55
54
  value: options[:type] == :literal ? options[:value] : nil,
56
55
  variants: [],
57
56
  }
57
+ result[:default] = options[:default] if options.key?(:default)
58
+ result
58
59
  end
59
60
 
60
61
  def build_union_param(options)
61
62
  union_dump = build_union(options[:union])
62
63
 
63
- {
64
+ result = {
64
65
  as: options[:as],
65
- default: options[:default],
66
66
  deprecated: options[:deprecated] == true,
67
67
  description: resolve_attribute_description(options),
68
68
  discriminator: union_dump[:discriminator],
@@ -82,15 +82,16 @@ module Apiwork
82
82
  value: nil,
83
83
  variants: union_dump[:variants],
84
84
  }
85
+ result[:default] = options[:default] if options.key?(:default)
86
+ result
85
87
  end
86
88
 
87
89
  def build_custom_type_param(options)
88
90
  custom_type_name = options[:custom_type]
89
91
  custom_type_name = qualified_name(custom_type_name, @contract_param) if @contract_param.contract_class.resolve_custom_type(custom_type_name)
90
92
 
91
- {
93
+ result = {
92
94
  as: options[:as],
93
- default: options[:default],
94
95
  deprecated: options[:deprecated] == true,
95
96
  description: resolve_attribute_description(options),
96
97
  discriminator: nil,
@@ -110,6 +111,8 @@ module Apiwork
110
111
  value: nil,
111
112
  variants: [],
112
113
  }
114
+ result[:default] = options[:default] if options.key?(:default)
115
+ result
113
116
  end
114
117
 
115
118
  def resolve_type_reference(type_value)
@@ -163,7 +166,6 @@ module Apiwork
163
166
  result = {
164
167
  reference:,
165
168
  as: nil,
166
- default: nil,
167
169
  deprecated: false,
168
170
  description: nil,
169
171
  discriminator: nil,
@@ -205,7 +207,6 @@ module Apiwork
205
207
  {
206
208
  reference:,
207
209
  as: nil,
208
- default: nil,
209
210
  deprecated: false,
210
211
  description: nil,
211
212
  discriminator: nil,
@@ -312,7 +313,6 @@ module Apiwork
312
313
 
313
314
  result = {
314
315
  as: nil,
315
- default: nil,
316
316
  deprecated: false,
317
317
  description: nil,
318
318
  discriminator: union ? of.discriminator : nil,
@@ -343,7 +343,6 @@ module Apiwork
343
343
  def build_api_variant(variant)
344
344
  {
345
345
  as: nil,
346
- default: nil,
347
346
  deprecated: false,
348
347
  description: nil,
349
348
  discriminator: nil,
@@ -19,13 +19,15 @@ module Apiwork
19
19
  )
20
20
 
21
21
  resource_path = build_resource_path(formatted_segment)
22
+ contract_class = resolve_contract_class
22
23
 
23
24
  {
24
- actions: build_actions(resolve_contract_class, resource_path),
25
+ actions: build_actions(contract_class, resource_path),
25
26
  identifier: @resource.name.to_s,
26
27
  parent_identifiers: @parent_identifiers,
27
28
  path: resource_path,
28
29
  resources: build_nested_resources(resource_path),
30
+ scope: contract_class&.scope_prefix&.to_s,
29
31
  }
30
32
  end
31
33
 
@@ -39,8 +39,11 @@ module Apiwork
39
39
  end
40
40
 
41
41
  def build_type(qualified_name, type_definition)
42
+ scope = resolve_scope(type_definition.scope)
43
+
42
44
  if type_definition.union?
43
45
  {
46
+ scope:,
44
47
  deprecated: type_definition.deprecated?,
45
48
  description: resolve_type_description(qualified_name, type_definition),
46
49
  discriminator: type_definition.discriminator,
@@ -52,6 +55,7 @@ module Apiwork
52
55
  }
53
56
  else
54
57
  {
58
+ scope:,
55
59
  deprecated: type_definition.deprecated?,
56
60
  description: resolve_type_description(qualified_name, type_definition),
57
61
  discriminator: nil,
@@ -110,10 +114,9 @@ module Apiwork
110
114
  resolve_type_reference(options[:type], scope)
111
115
  end
112
116
 
113
- {
117
+ result = {
114
118
  reference:,
115
119
  as: options[:as],
116
- default: options[:default],
117
120
  deprecated: options[:deprecated] == true,
118
121
  description: resolve_param_description(name, options, scope),
119
122
  discriminator: nil,
@@ -132,6 +135,8 @@ module Apiwork
132
135
  value: options[:type] == :literal ? options[:value] : nil,
133
136
  variants: build_nested_variants(options[:shape]),
134
137
  }
138
+ result[:default] = options[:default] if options.key?(:default)
139
+ result
135
140
  end
136
141
 
137
142
  def build_variant(variant, scope)
@@ -141,7 +146,6 @@ module Apiwork
141
146
  {
142
147
  reference:,
143
148
  as: nil,
144
- default: nil,
145
149
  deprecated: false,
146
150
  description: nil,
147
151
  discriminator: nil,
@@ -217,7 +221,6 @@ module Apiwork
217
221
 
218
222
  result = {
219
223
  as: nil,
220
- default: nil,
221
224
  deprecated: false,
222
225
  description: nil,
223
226
  discriminator: union ? of.discriminator : nil,
@@ -260,12 +263,20 @@ module Apiwork
260
263
  deprecated: enum_definition.deprecated?,
261
264
  description: resolve_enum_description(qualified_name, enum_definition),
262
265
  example: enum_definition.example,
266
+ scope: resolve_scope(enum_definition.scope),
263
267
  values: enum_definition.values || [],
264
268
  }
265
269
  end
266
270
 
267
271
  private
268
272
 
273
+ def resolve_scope(scope)
274
+ return nil unless scope
275
+ return nil unless scope.respond_to?(:scope_prefix)
276
+
277
+ scope.scope_prefix.to_s
278
+ end
279
+
269
280
  def resolve_param_description(name, options, scope)
270
281
  return options[:description] if options[:description]
271
282
  return nil unless scope
@@ -38,6 +38,14 @@ module Apiwork
38
38
  @dump[:example]
39
39
  end
40
40
 
41
+ # @api public
42
+ # The scope for this enum.
43
+ #
44
+ # @return [String, nil]
45
+ def scope
46
+ @dump[:scope]
47
+ end
48
+
41
49
  # @api public
42
50
  # Whether this enum is deprecated.
43
51
  #
@@ -55,6 +63,7 @@ module Apiwork
55
63
  deprecated: deprecated?,
56
64
  description: description,
57
65
  example: example,
66
+ scope: scope,
58
67
  values: values,
59
68
  }
60
69
  end
@@ -23,6 +23,9 @@ module Apiwork
23
23
  # @api public
24
24
  # The default for this param.
25
25
  #
26
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
27
+ # Use {#default?} to distinguish these cases.
28
+ #
26
29
  # @return [Object, nil]
27
30
  def default
28
31
  @dump[:default]
@@ -48,6 +48,17 @@ module Apiwork
48
48
  @dump[:description]
49
49
  end
50
50
 
51
+ # @api public
52
+ # Whether this param has a default value.
53
+ #
54
+ # Use this to distinguish "no default" from "default is explicitly `nil`".
55
+ # The {#default} accessor returns `nil` in both cases.
56
+ #
57
+ # @return [Boolean]
58
+ def default?
59
+ @dump.key?(:default)
60
+ end
61
+
51
62
  # @api public
52
63
  # The tag for this param.
53
64
  #
@@ -23,6 +23,9 @@ module Apiwork
23
23
  # @api public
24
24
  # The default for this param.
25
25
  #
26
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
27
+ # Use {#default?} to distinguish these cases.
28
+ #
26
29
  # @return [Object, nil]
27
30
  def default
28
31
  @dump[:default]
@@ -23,6 +23,9 @@ module Apiwork
23
23
  # @api public
24
24
  # The default for this param.
25
25
  #
26
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
27
+ # Use {#default?} to distinguish these cases.
28
+ #
26
29
  # @return [Object, nil]
27
30
  def default
28
31
  @dump[:default]
@@ -23,6 +23,9 @@ module Apiwork
23
23
  # @api public
24
24
  # The default for this param.
25
25
  #
26
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
27
+ # Use {#default?} to distinguish these cases.
28
+ #
26
29
  # @return [Object, nil]
27
30
  def default
28
31
  @dump[:default]
@@ -23,6 +23,9 @@ module Apiwork
23
23
  # @api public
24
24
  # The default for this param.
25
25
  #
26
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
27
+ # Use {#default?} to distinguish these cases.
28
+ #
26
29
  # @return [Object, nil]
27
30
  def default
28
31
  @dump[:default]
@@ -27,6 +27,9 @@ module Apiwork
27
27
  # @api public
28
28
  # The default for this param.
29
29
  #
30
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
31
+ # Use {#default?} to distinguish these cases.
32
+ #
30
33
  # @return [Object, nil]
31
34
  def default
32
35
  @dump[:default]
@@ -28,6 +28,9 @@ module Apiwork
28
28
  # @api public
29
29
  # The default for this param.
30
30
  #
31
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
32
+ # Use {#default?} to distinguish these cases.
33
+ #
31
34
  # @return [Object, nil]
32
35
  def default
33
36
  @dump[:default]
@@ -27,6 +27,9 @@ module Apiwork
27
27
  # @api public
28
28
  # The default for this param.
29
29
  #
30
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
31
+ # Use {#default?} to distinguish these cases.
32
+ #
30
33
  # @return [Object, nil]
31
34
  def default
32
35
  @dump[:default]
@@ -17,6 +17,9 @@ module Apiwork
17
17
  # @api public
18
18
  # The default for this param.
19
19
  #
20
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
21
+ # Use {#default?} to distinguish these cases.
22
+ #
20
23
  # @return [Object, nil]
21
24
  def default
22
25
  @dump[:default]
@@ -27,6 +27,9 @@ module Apiwork
27
27
  # @api public
28
28
  # The default for this param.
29
29
  #
30
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
31
+ # Use {#default?} to distinguish these cases.
32
+ #
30
33
  # @return [Object, nil]
31
34
  def default
32
35
  @dump[:default]
@@ -23,6 +23,9 @@ module Apiwork
23
23
  # @api public
24
24
  # The default for this param.
25
25
  #
26
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
27
+ # Use {#default?} to distinguish these cases.
28
+ #
26
29
  # @return [Object, nil]
27
30
  def default
28
31
  @dump[:default]
@@ -23,6 +23,9 @@ module Apiwork
23
23
  # @api public
24
24
  # The default for this param.
25
25
  #
26
+ # Returns `nil` for both "no default" and "default is explicitly `nil`".
27
+ # Use {#default?} to distinguish these cases.
28
+ #
26
29
  # @return [Object, nil]
27
30
  def default
28
31
  @dump[:default]
@@ -100,6 +100,14 @@ module Apiwork
100
100
  @dump[:example]
101
101
  end
102
102
 
103
+ # @api public
104
+ # The scope for this type.
105
+ #
106
+ # @return [String, nil]
107
+ def scope
108
+ @dump[:scope]
109
+ end
110
+
103
111
  # @api public
104
112
  # Whether this type is deprecated.
105
113
  #
@@ -119,6 +127,7 @@ module Apiwork
119
127
  discriminator: discriminator,
120
128
  example: example,
121
129
  extends: extends,
130
+ scope: scope,
122
131
  shape: shape.transform_values(&:to_h),
123
132
  type: type,
124
133
  variants: variants.map(&:to_h),
data/lib/apiwork/issue.rb CHANGED
@@ -56,7 +56,7 @@ module Apiwork
56
56
  code: code,
57
57
  detail: detail,
58
58
  meta: meta,
59
- path: path.map(&:to_s),
59
+ path: path.map { |segment| segment.is_a?(Integer) ? segment : segment.to_s },
60
60
  pointer: pointer,
61
61
  }
62
62
  end