e_plat 0.3.0 → 0.4.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +630 -131
  3. data/lib/active_resource/connection_error.rb +29 -0
  4. data/lib/active_resource/formats.rb +27 -0
  5. data/lib/current.rb +1 -1
  6. data/lib/e_plat/client/default_request_args.rb +27 -3
  7. data/lib/e_plat/client.rb +114 -87
  8. data/lib/e_plat/connection.rb +4 -0
  9. data/lib/e_plat/mapping/base.rb +119 -12
  10. data/lib/e_plat/mapping/bigcommerce/v_3/metafield.rb +62 -0
  11. data/lib/e_plat/mapping/bigcommerce/v_3/order/billing_address.rb +14 -0
  12. data/lib/e_plat/mapping/bigcommerce/v_3/order/line_item.rb +85 -0
  13. data/lib/e_plat/mapping/bigcommerce/v_3/order/shipping_address.rb +73 -0
  14. data/lib/e_plat/mapping/bigcommerce/v_3/order.rb +160 -0
  15. data/lib/e_plat/mapping/bigcommerce/v_3/product/image.rb +12 -12
  16. data/lib/e_plat/mapping/bigcommerce/v_3/product/variant.rb +1 -1
  17. data/lib/e_plat/mapping/bigcommerce/v_3/script_tag.rb +78 -0
  18. data/lib/e_plat/mapping/bigcommerce/v_3/shop.rb +11 -6
  19. data/lib/e_plat/mapping/bigcommerce/v_3/webhook.rb +54 -0
  20. data/lib/e_plat/mapping/request_body_root.rb +38 -0
  21. data/lib/e_plat/mapping/shopify/v_2024_01/metafield.rb +26 -0
  22. data/lib/e_plat/mapping/shopify/v_2024_01/order/billing_address.rb +14 -0
  23. data/lib/e_plat/mapping/shopify/v_2024_01/order/shipping_address.rb +30 -0
  24. data/lib/e_plat/mapping/shopify/{v_2022_07/product.rb → v_2024_01/order.rb} +3 -3
  25. data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/product/image.rb +1 -1
  26. data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/product/variant.rb +2 -1
  27. data/lib/e_plat/mapping/shopify/v_2024_01/product.rb +26 -0
  28. data/lib/e_plat/mapping/shopify/v_2024_01/script_tag.rb +26 -0
  29. data/lib/e_plat/mapping/shopify/{v_2022_07 → v_2024_01}/shop.rb +1 -1
  30. data/lib/e_plat/mapping/shopify/v_2024_01/webhook.rb +29 -0
  31. data/lib/e_plat/mapping/virtual_collection/base.rb +14 -0
  32. data/lib/e_plat/mapping/virtual_collection/bigcommerce/order_line_items.rb +297 -0
  33. data/lib/e_plat/mapping.rb +3 -3
  34. data/lib/e_plat/resource/attribute_interface.rb +28 -28
  35. data/lib/e_plat/resource/base.rb +107 -66
  36. data/lib/e_plat/resource/collection.rb +92 -0
  37. data/lib/e_plat/resource/concerns/aliases.rb +102 -18
  38. data/lib/e_plat/resource/concerns/dirty.rb +54 -0
  39. data/lib/e_plat/resource/concerns/metafieldable.rb +43 -0
  40. data/lib/e_plat/resource/concerns/overwrite_instance_methods.rb +108 -6
  41. data/lib/e_plat/resource/concerns/overwrite_request_methods.rb +73 -37
  42. data/lib/e_plat/resource/countable.rb +43 -0
  43. data/lib/e_plat/resource/metafield.rb +70 -0
  44. data/lib/e_plat/resource/order/Consignment.rb +8 -0
  45. data/lib/e_plat/resource/order/billing_address.rb +6 -0
  46. data/lib/e_plat/resource/order/fulfillment.rb +1 -0
  47. data/lib/e_plat/resource/order/line_item.rb +1 -0
  48. data/lib/e_plat/resource/order/shipping_address.rb +44 -0
  49. data/lib/e_plat/resource/order/shipping_line.rb +15 -14
  50. data/lib/e_plat/resource/order.rb +32 -0
  51. data/lib/e_plat/resource/paginated/link_headers.rb +42 -0
  52. data/lib/e_plat/resource/paginated/link_params.rb +26 -0
  53. data/lib/e_plat/resource/product/image.rb +6 -0
  54. data/lib/e_plat/resource/product/option.rb +1 -0
  55. data/lib/e_plat/resource/product/variant.rb +8 -12
  56. data/lib/e_plat/resource/product.rb +8 -2
  57. data/lib/e_plat/resource/script_tag.rb +56 -0
  58. data/lib/e_plat/resource/shop.rb +17 -13
  59. data/lib/e_plat/resource/shopify_only/recurring_application_charge/usage_charge.rb +32 -0
  60. data/lib/e_plat/resource/shopify_only/recurring_application_charge.rb +47 -0
  61. data/lib/e_plat/resource/webhook.rb +50 -0
  62. data/lib/e_plat/session.rb +10 -7
  63. data/lib/e_plat/type_coercer.rb +15 -17
  64. data/lib/e_plat/version.rb +1 -1
  65. data/lib/e_plat.rb +4 -3
  66. metadata +55 -9
  67. data/lib/e_plat/resource/order/customer.rb +0 -37
@@ -0,0 +1,70 @@
1
+ module EPlat
2
+ class Metafield < Base
3
+ BIGCOMMERCE_DEFAULT_PERMISSION_SET = "read_and_sf_access"
4
+ READ_ONLY_ATTRIBUTES = [:created_at, :id, :updated_at, :date_created, :date_modified]
5
+
6
+ before_save :normalize_attributes
7
+
8
+ schema do
9
+ datetime :created_at
10
+ string :description
11
+ integer :id
12
+ string :key
13
+ string :namespace
14
+ integer :owner_id
15
+ string :owner_resource
16
+ datetime :updated_at
17
+ string :value
18
+ string :type
19
+ end
20
+
21
+
22
+ def element_path(options = {})
23
+ raise "owner_id is not defined" unless owner_id.present?
24
+
25
+ parents_metafield_path super(options)
26
+ end
27
+
28
+ def collection_path(options = {})
29
+ raise "owner_id is not defined" unless owner_id.present?
30
+
31
+ parents_metafield_path super(options)
32
+ end
33
+
34
+ def normalize_attributes
35
+ attributes.each {|k,v| attribute_will_change!(k) if READ_ONLY_ATTRIBUTES.exclude?(k.to_sym) }
36
+
37
+ return unless client.bigcommerce?
38
+
39
+ if permission_set.nil?
40
+ self.permission_set = BIGCOMMERCE_DEFAULT_PERMISSION_SET
41
+ end
42
+
43
+ if value?
44
+ self.value = value.to_s
45
+ self.value = "" if value == "false"
46
+ end
47
+ end
48
+
49
+ def parent_class
50
+ raise "owner_resource is not defined" unless owner_resource.present?
51
+
52
+ "EPlat::#{ owner_resource.classify }".constantize # e.g EPlat::Product
53
+ end
54
+
55
+
56
+ private
57
+
58
+ def parents_metafield_path(path)
59
+ id_suffix = id.present? ? "/#{ id }" : ""
60
+ uri = URI.parse(path)
61
+
62
+ uri.path = parent_class.element_path(owner_id).gsub(".json", "") + "/metafields#{ id_suffix }"
63
+ uri.path += ".json" if self.client.include_format_in_path?
64
+ uri.path.gsub!('v2', 'v3') if client.bigcommerce? # metafield uses v3 API, even though orders are only available in v2
65
+
66
+ uri.to_s
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module EPlat
3
+ class Order::Consignment < Base
4
+ belongs_to :order, class_name: "EPlat::Order"
5
+ exclude_from_json true
6
+ end
7
+ end
8
+
@@ -0,0 +1,6 @@
1
+
2
+ module EPlat
3
+ class Order::BillingAddress < Order::ShippingAddress; end
4
+ end
5
+
6
+
@@ -1,6 +1,7 @@
1
1
 
2
2
  module EPlat
3
3
  class Order::Fulfillment < Base
4
+ belongs_to :order, class_name: "EPlat::Order"
4
5
 
5
6
  schema do
6
7
  string :created_at
@@ -1,6 +1,7 @@
1
1
 
2
2
  module EPlat
3
3
  class Order::LineItem < Base
4
+ belongs_to :order, class_name: "EPlat::Order"
4
5
 
5
6
  schema do
6
7
  integer :id
@@ -0,0 +1,44 @@
1
+
2
+ module EPlat
3
+ class Order::ShippingAddress < Base
4
+ belongs_to :order, class_name: "EPlat::Order" # only include if belongs_to is needed
5
+
6
+ schema do
7
+ string :address1
8
+ string :address2
9
+ string :city
10
+ string :company
11
+ string :country
12
+ string :first_name
13
+ string :last_name
14
+ string :latitude
15
+ string :longitude
16
+ string :phone
17
+ string :province
18
+ string :zip
19
+ string :name
20
+ string :country_code
21
+ string :province_code
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+
28
+ # "shipping_address": {
29
+ # "address1": "123 Amoebobacterieae St",
30
+ # "address2": "",
31
+ # "city": "Ottawa",
32
+ # "company": null,
33
+ # "country": "Canada",
34
+ # "first_name": "Bob",
35
+ # "last_name": "Bobsen",
36
+ # "latitude": "45.41634",
37
+ # "longitude": "-75.6868",
38
+ # "phone": "555-625-1199",
39
+ # "province": "Ontario",
40
+ # "zip": "K2P0V6",
41
+ # "name": "Bob Bobsen",
42
+ # "country_code": "CA",
43
+ # "province_code": "ON"
44
+ # },
@@ -1,22 +1,23 @@
1
1
 
2
2
  module EPlat
3
3
  class Order::ShippingLine < Base
4
+ belongs_to :order, class_name: "EPlat::Order"
4
5
 
5
6
  schema do
6
- integer :id
7
- string :carrier_identifier
8
- string :code
9
- string :delivery_category
10
- string :discounted_price
11
- hash :discounted_price_set
12
- string :phone
13
- string :price
14
- hash :price_set
15
- integer :requested_fulfillment_service_id
16
- string :source
17
- string :title
18
- array :tax_lines
19
- array :discount_allocations
7
+ integer :id
8
+ string :carrier_identifier
9
+ string :code
10
+ string :delivery_category
11
+ string :discounted_price
12
+ hash :discounted_price_set
13
+ string :phone
14
+ string :price
15
+ hash :price_set
16
+ integer :requested_fulfillment_service_id
17
+ string :source
18
+ string :title
19
+ array :tax_lines
20
+ array :discount_allocations
20
21
  end
21
22
 
22
23
  end
@@ -1,8 +1,12 @@
1
1
 
2
2
  module EPlat
3
3
  class Order < Base
4
+ has_many :line_items, class_name: "EPlat::Order::LineItem"
5
+
6
+ include Concerns::Metafieldable
4
7
 
5
8
  schema do
9
+ integer :id
6
10
  integer :app_id
7
11
  string :billing_address_address1
8
12
  string :billing_address_address2
@@ -99,6 +103,34 @@ module EPlat
99
103
  array :shipping_lines
100
104
  end
101
105
 
106
+ def cancel(options = {})
107
+ if client.bigcommerce?
108
+ self.status_id = 5
109
+ save
110
+ else
111
+ load_attributes_from_response(post(:cancel, {}, options.to_json))
112
+ end
113
+ end
114
+
115
+ def cancelled?
116
+ case client.platform
117
+ when :shopify
118
+ cancelled_at.present?
119
+ when :bigcommerce
120
+ status_id == 5
121
+ end
122
+ end
123
+
124
+ # abstract to class method call
125
+ # def consignments=(consignment_data)
126
+ # @consignments = consignment_data.map do |consignment_attrs|
127
+ # EPlat::Order::LineItem.new(consignment_attrs)
128
+ # end
129
+ # end
130
+
131
+ # alias is virtual interfact, so it can pick and push from/to the various native interfaces, whether nested or not
132
+
102
133
  end
103
134
  end
104
135
 
136
+
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module EPlat
6
+ module Paginated
7
+ class LinkHeaders
8
+ LinkHeader = Struct.new(:url, :rel)
9
+ attr_reader :previous_link, :next_link
10
+
11
+ def initialize(link_header)
12
+ links = parse_link_header(link_header)
13
+
14
+ @previous_link = links.find { |link| link.rel == :previous }
15
+ @next_link = links.find { |link| link.rel == :next }
16
+ end
17
+
18
+
19
+ private
20
+
21
+ def parse_link_header(link_header)
22
+ return [] unless link_header.present?
23
+
24
+ links = link_header[0].split(',')
25
+ links.map do |link|
26
+ parts = link.split('; ')
27
+
28
+ unless parts.length == 2
29
+ raise "Invalid link header: url and rel expected"
30
+ end
31
+
32
+ url = parts[0][/<(.*)>/, 1]
33
+ rel = parts[1][/rel="(.*)"/, 1]&.to_sym
34
+
35
+ url = URI.parse(url).request_uri
36
+ LinkHeader.new(url, rel)
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ module EPlat
6
+ module Paginated
7
+ class LinkParams
8
+ LinkParam = Struct.new(:rel, :url)
9
+ attr_reader :previous_link, :next_link
10
+
11
+ def initialize(link_params) #{"next"=>"?include=variants%2Cimages&limit=2&page=2", "current"=>"?include=variants%2Cimages&limit=2&page=1"}
12
+ links = link_params.map do |key, value|
13
+ if key == "next"
14
+ LinkParam.new(rel: key.to_sym, url: value)
15
+ else key == "previous"
16
+ LinkParam.new(rel: key.to_sym, url: value)
17
+ end
18
+ end
19
+
20
+ @previous_link = links.find { |link| link.rel == :previous }
21
+ @next_link = links.find { |link| link.rel == :next }
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -2,6 +2,7 @@
2
2
  module EPlat
3
3
  class Product
4
4
  class Image < Base
5
+ belongs_to :product, class_name: "EPlat::Product"
5
6
 
6
7
  class << self
7
8
  end
@@ -19,6 +20,11 @@ module EPlat
19
20
  array :variant_ids
20
21
  string :admin_graphql_api_id
21
22
  end
23
+
24
+ def save
25
+ prefix_options[:product] ||= product.id
26
+ super
27
+ end
22
28
 
23
29
  end
24
30
  end
@@ -2,6 +2,7 @@
2
2
  module EPlat
3
3
  class Product
4
4
  class Option < Base
5
+ belongs_to :product, class_name: "EPlat::Product"
5
6
 
6
7
  schema do
7
8
  integer :id
@@ -1,11 +1,11 @@
1
-
2
1
  module EPlat
3
2
  class Product
4
3
  class Variant < Base
5
-
4
+ belongs_to :product, class_name: "EPlat::Product"
5
+
6
6
  class << self
7
7
  end
8
-
8
+
9
9
  schema do
10
10
  integer :id
11
11
  integer :product_id
@@ -35,17 +35,13 @@ module EPlat
35
35
  string :tax_code
36
36
  boolean :requires_shipping
37
37
  string :admin_graphql_api_id
38
-
39
- hash :e_plat_option_hash
40
38
  end
41
-
42
-
43
- def testing
44
- puts "Im just testing"
39
+
40
+ def save
41
+ prefix_options[:product] ||= product.id
42
+ super
45
43
  end
46
-
44
+
47
45
  end
48
46
  end
49
47
  end
50
-
51
-
@@ -1,7 +1,9 @@
1
1
  module EPlat
2
2
  class Product < Base
3
- has_many :variant, class_name: "EPlat::Product::Variant"
4
- has_many :image, class_name: "EPlat::Product::Image"
3
+ has_many :variants, class_name: "EPlat::Product::Variant"
4
+ has_many :images, class_name: "EPlat::Product::Image"
5
+
6
+ include Concerns::Metafieldable
5
7
 
6
8
  schema do
7
9
  string :body_html
@@ -30,6 +32,10 @@ module EPlat
30
32
  def status
31
33
  super || "active"
32
34
  end
35
+
36
+ def find_variant(id)
37
+ variants.find { |v| v.id && v.id.to_i == id.to_i }
38
+ end
33
39
 
34
40
  end
35
41
  end
@@ -0,0 +1,56 @@
1
+
2
+ module EPlat
3
+ class ScriptTag < Base
4
+
5
+ schema do
6
+ datetime :created_at
7
+ datetime :updated_at
8
+ string :event
9
+ integer :id
10
+ string :src
11
+ string :display_scope
12
+ boolean :cache
13
+ end
14
+
15
+ def to_param
16
+ if client.bigcommerce?
17
+ uuid && uuid.to_s
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ def self.element_path(id, prefix_options = {}, query_options = nil)
24
+ if client.bigcommerce?
25
+ super(id, prefix_options, query_options).gsub("script_tags", "scripts")
26
+ else
27
+ super(id, prefix_options, query_options)
28
+ end
29
+ end
30
+
31
+ def self.collection_path(prefix_options = {}, query_options = nil)
32
+ if client.bigcommerce?
33
+ super(prefix_options, query_options).gsub("script_tags", "scripts")
34
+ else
35
+ super(prefix_options, query_options)
36
+ end
37
+ end
38
+
39
+ def element_path(options = {})
40
+ if client.bigcommerce?
41
+ super(options).gsub("script_tags", "scripts")
42
+ else
43
+ super(options)
44
+ end
45
+ end
46
+
47
+ def collection_path(options = {})
48
+ if client.bigcommerce?
49
+ super(options).gsub("script_tags", "scripts")
50
+ else
51
+ super(options)
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -1,6 +1,23 @@
1
1
 
2
2
  module EPlat
3
3
  class Shop < Base
4
+
5
+ class << self
6
+
7
+ # Need to override for BigCommerce, since it doesn't have a /shop endpoint
8
+ # Is threadsafe as the path is determined and used all within this method
9
+ def find_single(scope, options)
10
+ prefix_options, query_options = split_options(options[:params])
11
+ path = (client.bigcommerce?) ? "/stores/#{ client.store_hash }/v2/store.json" : "#{ client.url_prefix }shop.json"
12
+
13
+ instantiate_record(format.decode(connection.get(path, headers).body), prefix_options)
14
+ end
15
+
16
+ def current
17
+ find(1)
18
+ end
19
+ end
20
+
4
21
 
5
22
  schema do
6
23
  integer :id
@@ -62,18 +79,5 @@ module EPlat
62
79
  boolean :marketing_sms_consent_enabled_at_checkout
63
80
  end
64
81
 
65
- class << self
66
-
67
- def element_path(id, prefix_options = {}, query_options = {})
68
- "#{ Current.e_plat_session.url_prefix }shop.json"
69
- end
70
-
71
-
72
- def current
73
- find(1)
74
- end
75
-
76
- end
77
-
78
82
  end
79
83
  end
@@ -0,0 +1,32 @@
1
+
2
+ module EPlat
3
+ class RecurringApplicationCharge
4
+ class UsageCharge < Base
5
+ belongs_to :recurring_application_charge, class_name: "EPlat::RecurringApplicationCharge"
6
+
7
+ schema do
8
+ datetime :created_at
9
+ string :description
10
+ integer :id
11
+ string :price
12
+ integer :recurring_application_charge_id
13
+ datetime :updated_at
14
+ string :currency
15
+ end
16
+
17
+ def element_path(options = {})
18
+ options['recurring_application_charge'] = recurring_application_charge_id
19
+
20
+ super(options)
21
+ end
22
+
23
+ def collection_path(options = {})
24
+ options['recurring_application_charge'] = recurring_application_charge_id
25
+
26
+ super(options)
27
+ end
28
+
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,47 @@
1
+ module EPlat
2
+ class RecurringApplicationCharge < Base
3
+ has_many :usage_charges, class_name: "EPlat::RecurringApplicationCharge::UsageCharge"
4
+
5
+ schema do
6
+ datetime :activated_on
7
+ datetime :cancelled_on
8
+ string :capped_amount
9
+ string :confirmation_url
10
+ datetime :created_at
11
+ integer :id
12
+ string :name
13
+ string :price
14
+ string :return_url
15
+ string :status # pending | active | declined | expired | frozen | cancelled
16
+ string :terms
17
+ boolean :test
18
+ integer :trial_days
19
+ datetime :trial_ends_on
20
+ datetime :updated_at
21
+ string :currency
22
+ end
23
+
24
+ def self.current
25
+ (all || []).find { |c| c.status == 'active' }
26
+ end
27
+
28
+ def save
29
+ if new_record?
30
+ super
31
+ else
32
+ load_attributes_from_response(
33
+ put(:customize, {}, to_json)
34
+ )
35
+ end
36
+ end
37
+
38
+ def cancel
39
+ self.destroy
40
+ end
41
+
42
+ def usage_charges
43
+ RecurringApplicationCharge::UsageCharge.find(:all, params: {recurring_application_charge: id})
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,50 @@
1
+ module EPlat
2
+ class Webhook < Base
3
+
4
+ schema do
5
+ string :address
6
+ string :api_version
7
+ datetime :created_at
8
+ array :fields
9
+ string :format
10
+ integer :id
11
+ array :metafield_namespaces
12
+ array :private_metafield_namespaces
13
+ string :topic
14
+ datetime :updated_at
15
+ end
16
+
17
+ def self.element_path(id, prefix_options = {}, query_options = nil)
18
+ if client.bigcommerce?
19
+ super(id, prefix_options, query_options).gsub("webhooks", "hooks")
20
+ else
21
+ super(id, prefix_options, query_options)
22
+ end
23
+ end
24
+
25
+ def self.collection_path(prefix_options = {}, query_options = nil)
26
+ if client.bigcommerce?
27
+ super(prefix_options, query_options).gsub("webhooks", "hooks")
28
+ else
29
+ super(prefix_options, query_options)
30
+ end
31
+ end
32
+
33
+ def element_path(options = {})
34
+ if client.bigcommerce?
35
+ super(options).gsub("webhooks", "hooks")
36
+ else
37
+ super(options)
38
+ end
39
+ end
40
+
41
+ def collection_path(options = {})
42
+ if client.bigcommerce?
43
+ super(options).gsub("webhooks", "hooks")
44
+ else
45
+ super(options)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -4,13 +4,16 @@ module EPlat
4
4
  #the session is an instance of EPlat::Client made available via Rails Current::Attributes
5
5
 
6
6
  def initialize(platform:, store_url:, api_token:, store_hash: nil)
7
- Current.e_plat_session = EPlat::Client.new
8
- Current.e_plat_session.platform = platform
9
- Current.e_plat_session.store_url = store_url
10
- Current.e_plat_session.api_token = api_token
11
- Current.e_plat_session.store_hash = store_hash
12
-
13
- Current.e_plat_session.send(:get_api_version) if Current.e_plat_session.active?
7
+ Current.e_plat_session = EPlat::Client.new(
8
+ platform: platform,
9
+ store_url: store_url,
10
+ api_token: api_token,
11
+ store_hash: store_hash
12
+ ).freeze
13
+ end
14
+
15
+ def self.clear!
16
+ Current.e_plat_session = nil
14
17
  end
15
18
 
16
19
  end