my_data 0.1.0 → 0.9.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +37 -0
  3. data/.rubocop.yml +12 -2
  4. data/Gemfile +9 -4
  5. data/Guardfile +7 -0
  6. data/README.md +55 -8
  7. data/lib/my_data.rb +9 -10
  8. data/lib/my_data/client.rb +34 -11
  9. data/lib/my_data/resource.rb +43 -29
  10. data/lib/my_data/resources/continuation_token_type.rb +7 -0
  11. data/lib/my_data/resources/ecls/expenses_classification.rb +7 -0
  12. data/lib/my_data/resources/ecls/expenses_classification_type.rb +1 -1
  13. data/lib/my_data/resources/ecls/invoice_expenses_classification_type.rb +1 -1
  14. data/lib/my_data/resources/ecls/invoices_expenses_classification_detail_type.rb +1 -1
  15. data/lib/my_data/resources/error_type.rb +1 -2
  16. data/lib/my_data/resources/icls/income_classification.rb +7 -0
  17. data/lib/my_data/resources/icls/income_classification_type.rb +1 -1
  18. data/lib/my_data/resources/icls/invoice_income_classification_type.rb +1 -1
  19. data/lib/my_data/resources/icls/invoices_income_classification_detail_type.rb +1 -1
  20. data/lib/my_data/resources/inv/aade_book_invoice_type.rb +1 -1
  21. data/lib/my_data/resources/inv/address_type.rb +1 -1
  22. data/lib/my_data/resources/inv/cancelled_invoice_type.rb +7 -0
  23. data/lib/my_data/resources/inv/continuation_token_type.rb +7 -0
  24. data/lib/my_data/resources/inv/invoice_header_type.rb +1 -1
  25. data/lib/my_data/resources/inv/invoice_row_type.rb +1 -1
  26. data/lib/my_data/resources/inv/invoice_summary_type.rb +1 -1
  27. data/lib/my_data/resources/inv/invoices_doc.rb +1 -11
  28. data/lib/my_data/resources/inv/party_type.rb +1 -1
  29. data/lib/my_data/resources/inv/payment_method_detail_type.rb +1 -1
  30. data/lib/my_data/resources/inv/request_doc.rb +7 -0
  31. data/lib/my_data/resources/inv/requested_invoices_doc.rb +7 -0
  32. data/lib/my_data/resources/inv/ship_type.rb +1 -1
  33. data/lib/my_data/resources/inv/tax_totals_type.rb +1 -1
  34. data/lib/my_data/resources/invoice_provider_type.rb +7 -0
  35. data/lib/my_data/resources/requested_provider_doc.rb +7 -0
  36. data/lib/my_data/resources/response.rb +7 -0
  37. data/lib/my_data/resources/response_type.rb +1 -13
  38. data/lib/my_data/response_parser.rb +42 -0
  39. data/lib/my_data/type_caster.rb +5 -4
  40. data/lib/my_data/version.rb +1 -1
  41. data/lib/my_data/xml_generator.rb +29 -17
  42. data/lib/my_data/xml_parser.rb +37 -76
  43. data/lib/my_data/xsd/complex_type.rb +11 -3
  44. data/lib/my_data/xsd/doc.rb +77 -0
  45. data/lib/my_data/xsd/element.rb +20 -8
  46. data/lib/my_data/xsd/resource_generator.rb +26 -13
  47. data/lib/my_data/xsd/structure.rb +41 -35
  48. data/my_data.gemspec +4 -3
  49. metadata +41 -19
  50. data/lib/my_data/resources.rb +0 -10
  51. data/lib/my_data/resources/ecls.rb +0 -7
  52. data/lib/my_data/resources/icls.rb +0 -7
  53. data/lib/my_data/resources/inv.rb +0 -14
  54. data/lib/my_data/resources/responses_doc.rb +0 -8
  55. data/lib/my_data/xsd.rb +0 -7
@@ -3,6 +3,5 @@
3
3
  class MyData::Resources::ErrorType
4
4
  include MyData::Resource
5
5
 
6
- attribute :message, :string
7
- attribute :code, :integer
6
+ xsd_complex_type
8
7
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::Resources::Icls::IncomeClassification
4
+ include MyData::Resource
5
+
6
+ xsd_doc
7
+ end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Icls::IncomeClassificationType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Icls::InvoiceIncomeClassificationType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Icls::InvoicesIncomeClassificationDetailType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::AadeBookInvoiceType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::AddressType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::Resources::Inv::CancelledInvoiceType
4
+ include MyData::Resource
5
+
6
+ xsd_complex_type
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::Resources::Inv::ContinuationTokenType
4
+ include MyData::Resource
5
+
6
+ xsd_complex_type
7
+ end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::InvoiceHeaderType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::InvoiceRowType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::InvoiceSummaryType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,15 +3,5 @@
3
3
  class MyData::Resources::Inv::InvoicesDoc
4
4
  include MyData::Resource
5
5
 
6
- container_tag "InvoicesDoc", {
7
- xmlns: "http://www.aade.gr/myDATA/invoice/v1.0",
8
- "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
9
- "xsi:schemaLocation": "http://www.aade.gr/myDATA/invoice/v1.0 schema.xsd",
10
- "xmlns:icls": "https://www.aade.gr/myDATA/incomeClassificaton/v1.0"
11
- }
12
-
13
- attribute :invoice, :resource,
14
- class_name: "Inv::AadeBookInvoiceType", collection: true
15
-
16
- validates_presence_of :invoice
6
+ xsd_doc
17
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::PartyType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::PaymentMethodDetailType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::Resources::Inv::RequestDoc
4
+ include MyData::Resource
5
+
6
+ xsd_doc
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::Resources::Inv::RequestedInvoicesDoc
4
+ include MyData::Resource
5
+
6
+ xsd_doc
7
+ end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::ShipType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -3,5 +3,5 @@
3
3
  class MyData::Resources::Inv::TaxTotalsType
4
4
  include MyData::Resource
5
5
 
6
- xsd_structure
6
+ xsd_complex_type
7
7
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::Resources::InvoiceProviderType
4
+ include MyData::Resource
5
+
6
+ xsd_complex_type
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::Resources::RequestedProviderDoc
4
+ include MyData::Resource
5
+
6
+ xsd_doc
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::Resources::Response
4
+ include MyData::Resource
5
+
6
+ xsd_doc
7
+ end
@@ -3,17 +3,5 @@
3
3
  class MyData::Resources::ResponseType
4
4
  include MyData::Resource
5
5
 
6
- attribute :index, :integer
7
-
8
- attribute :invoice_uid, :string
9
- attribute :invoice_mark, :integer
10
- attribute :classification_mark, :integer
11
- attribute :cancellation_mark, :integer
12
- attribute :authentication_code, :string
13
-
14
- attribute :errors, :resource,
15
- class_name: "ErrorType",
16
- collection: true, collection_element_name: "error"
17
-
18
- attribute :status_code, :string
6
+ xsd_complex_type
19
7
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyData::ResponseParser
4
+ def initialize(response, resource: nil, root: nil)
5
+ @original_response = response
6
+ @resource = resource
7
+ @root = root
8
+ end
9
+
10
+ def success?
11
+ original_response.status == 200 &&
12
+ (!response_type? || response.response.all? { |r| r.status_code == "Success" })
13
+ end
14
+
15
+ def errors
16
+ @errors ||=
17
+ if success?
18
+ []
19
+ elsif response_type?
20
+ response.response.map(&:errors).flatten
21
+ else
22
+ [MyData::Resources::ErrorType.new(JSON.parse(original_response.body))]
23
+ end
24
+ end
25
+
26
+ def response
27
+ return @response if defined? @response
28
+
29
+ @response =
30
+ if original_response.status == 200
31
+ MyData::XmlParser.xml_to_resource(xml: original_response.body, resource: resource, root: root)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def response_type?
38
+ response&.is_a?(MyData::Resources::Response)
39
+ end
40
+
41
+ attr_reader :original_response, :resource, :root
42
+ end
@@ -11,8 +11,7 @@ module MyData
11
11
  :date,
12
12
  :time,
13
13
  :boolean,
14
- :resource,
15
- :ignore
14
+ :resource
16
15
  ].freeze
17
16
 
18
17
  def valid_type?(type)
@@ -28,7 +27,7 @@ module MyData
28
27
  private
29
28
 
30
29
  def type_cast_string(value)
31
- value.to_s.strip
30
+ value ? value.to_s.strip : nil
32
31
  end
33
32
 
34
33
  def type_cast_integer(value)
@@ -48,7 +47,9 @@ module MyData
48
47
  end
49
48
 
50
49
  def type_cast_boolean(value)
51
- value.downcase == "true"
50
+ return value if value.nil?
51
+
52
+ value.is_a?(String) ? value.downcase == "true" : value == true
52
53
  end
53
54
 
54
55
  def type_cast_resource(value, resource)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MyData
4
- VERSION = "0.1.0"
4
+ VERSION = "0.9.0"
5
5
  end
@@ -22,28 +22,40 @@ module MyData
22
22
 
23
23
  def to_xml_structure
24
24
  resource.attributes.each_with_object([]) do |(key, value), structure|
25
- attr_mappings = mappings[key]
26
- next if value.nil?
27
-
28
- if attr_mappings[:collection]
29
- val = value.map { |v| attr_mappings[:resource] ? self.class.new(v, parent_namespace: namespace).to_xml_structure : v.to_s }
30
-
31
- if attr_mappings[:collection_element_name]
32
- struct = val.map { |v| attr_structure(attr_mappings[:collection_element_name], v) }
33
- structure.push(attr_structure(key, struct))
34
- else
35
- struct = val.map { |v| attr_structure(key, v) }
36
- structure.push(*struct)
37
- end
38
- else
39
- val = attr_mappings[:resource] ? self.class.new(value, parent_namespace: namespace).to_xml_structure : value.to_s
40
- structure.push(attr_structure(key, val))
41
- end
25
+ next if value.nil? || (value.respond_to?(:empty?) && value.empty?)
26
+
27
+ value = value_to_xml_structure(key, value)
28
+
29
+ structure.push(*extract_attributes(key, value))
42
30
  end
43
31
  end
44
32
 
45
33
  private
46
34
 
35
+ def value_to_xml_structure(key, value)
36
+ is_resource = mappings[key][:resource].present?
37
+
38
+ transform = lambda do |v|
39
+ is_resource ? self.class.new(v, parent_namespace: namespace).to_xml_structure : v.to_s
40
+ end
41
+
42
+ value.is_a?(Array) ? value.map(&transform) : transform.call(value)
43
+ end
44
+
45
+ def extract_attributes(key, value)
46
+ collection, collection_element_name = mappings[key].values_at(:collection, :collection_element_name)
47
+
48
+ if !collection
49
+ [attr_structure(key, value)]
50
+ elsif collection_element_name
51
+ attrs = value.map { |v| attr_structure(collection_element_name, v) }
52
+
53
+ [attr_structure(key, attrs)]
54
+ else
55
+ value.map { |v| attr_structure(key, v) }
56
+ end
57
+ end
58
+
47
59
  def attr_structure(key, value)
48
60
  prefix = parent_namespace && namespace != parent_namespace ? "#{namespace}:" : ""
49
61
 
@@ -1,96 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module MyData
4
- class XmlParser
5
- attr_reader :doc, :mappings, :class_name
3
+ module MyData::XmlParser
4
+ extend self
6
5
 
7
- def initialize(doc, mappings, class_name)
8
- @doc = doc
9
- @mappings = mappings
10
- @class_name = class_name
11
- end
12
-
13
- def parse_xml
14
- doc.children.select(&:element?).each_with_object({}) do |element, parse_attrs|
15
- attr_names = extract_attr_names(element)
16
-
17
- attr_names.each do |attr_name|
18
- value = extract_value(element, attr_name)
19
- parse_attrs[attr_name] = value if value
20
- end
21
- end
22
- end
6
+ def xml_to_resource(xml:, resource:, root: nil)
7
+ h = transofrm_xml_to_hash(fix_xml(xml))
8
+ h = h[root] if root
23
9
 
24
- private
25
-
26
- def extract_attr_names(element)
27
- posible_attrs = mappings.select { |_, opts| element.name == opts[:tag_name] }
10
+ resource.new hash_mapping(h, resource)
11
+ end
28
12
 
29
- if posible_attrs.present?
30
- attr_names = posible_attrs
31
- .select { |_name, opts| match_static_tag_attributes?(element, opts[:static_tag_attrs]) }
32
- else
33
- attr_names = []
34
- # raise "Not implemented: #{class_name}: #{element.name}"
35
- end
13
+ private
36
14
 
37
- if attr_names.empty?
38
- # raise("Tag attributes not matched: #{class_name}: #{element.name}, element attrs: #{element.attributes}")
39
- end
15
+ def hash_mapping(hash, resource)
16
+ flatten(hash, resource).each_with_object({}) do |(key, value), h|
17
+ mappings = resource.mappings[key]
40
18
 
41
- attr_names.map(&:first)
19
+ h[key] = value_mapping(value, mappings)
42
20
  end
21
+ end
43
22
 
44
- def extract_value(element, attr_name)
45
- value = element
46
- attr_opts = mappings[attr_name]
47
-
48
- # TODO: current we dont check that the attribute setting matcing the elements
49
- if attr_opts[:nested_tags].present?
50
- attr_opts[:nested_tags].each do |tag|
51
- return if value.nil?
23
+ def value_mapping(value, mappings)
24
+ return value if mappings[:resource].nil?
52
25
 
53
- value = value.children.select(&:element?).find { |element| element.name == tag[:name].to_s }
54
- end
26
+ if mappings[:collection]
27
+ value = value.is_a?(Array) ? value : [value]
28
+ value.map { |v| hash_mapping(v, mappings[:resource]) }
29
+ else
30
+ hash_mapping(value, mappings[:resource])
31
+ end
32
+ end
55
33
 
56
- return value if value.nil?
57
- end
34
+ def fix_xml(xml)
35
+ xml.strip.gsub("&lt;", "<").gsub("&gt;", ">")
36
+ end
58
37
 
59
- if attr_opts[:collection]
60
- value.children.select(&:element?).map { |val| get_value(val, attr_opts) }
61
- else
62
- get_value(value, attr_opts)
63
- end
64
- end
38
+ def transofrm_xml_to_hash(xml)
39
+ Hash
40
+ .from_xml(xml)
41
+ .deep_transform_keys(&:underscore)["string"]
42
+ end
65
43
 
66
- def get_value(value, attr_opts)
67
- if attr_opts[:type] == :model
68
- nested_parser = self.class.new(
69
- value,
70
- attr_opts[:class_name].mappings,
71
- attr_opts[:class_name].name
72
- )
73
- value = nested_parser.parse_xml
74
- else
75
- value = if attr_opts[:dynamic_tag_attr].present?
76
- value.attributes[attr_opts[:dynamic_tag_attr]].value
77
- else
78
- value.text
79
- end
80
- end
81
- end
44
+ def flatten(hash, resource)
45
+ return {} unless hash
82
46
 
83
- def match_static_tag_attributes?(element, static_tag_attributes)
84
- static_tag_attributes.all? do |tag_attribute|
85
- element_attr = element.attributes[tag_attribute.name]
47
+ hash.each_with_object({}) do |(k, v), h|
48
+ next if resource.attributes.none?(k) || !v
86
49
 
87
- next false unless element_attr
50
+ mappings = resource.mappings[k]
88
51
 
89
- matched = (element_attr.value == tag_attribute.value) &&
90
- (tag_attribute.namespace.nil? || tag_attribute.namespace == element_attr.namespace.prefix)
52
+ next h[k] = v unless mappings[:collection] && mappings[:collection_element_name]
91
53
 
92
- matched
93
- end
54
+ h[k] = v[mappings[:collection_element_name]]
94
55
  end
95
56
  end
96
57
  end