e_plat 0.3.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +631 -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 +14 -4
  66. metadata +67 -7
  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