shopify_api 8.1.0 → 9.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +22 -10
- data/docs/graphql.md +191 -0
- data/lib/shopify_api.rb +2 -0
- data/lib/shopify_api/api_version.rb +1 -1
- data/lib/shopify_api/graphql.rb +79 -0
- data/lib/shopify_api/graphql/http_client.rb +22 -0
- data/lib/shopify_api/graphql/railtie.rb +17 -0
- data/lib/shopify_api/graphql/task.rake +100 -0
- data/lib/shopify_api/resources/assigned_fulfillment_order.rb +16 -0
- data/lib/shopify_api/resources/base.rb +8 -0
- data/lib/shopify_api/resources/fulfillment.rb +34 -0
- data/lib/shopify_api/resources/fulfillment_order.rb +137 -0
- data/lib/shopify_api/resources/fulfillment_order_locations_for_move.rb +4 -0
- data/lib/shopify_api/resources/fulfillment_v2.rb +20 -0
- data/lib/shopify_api/resources/order.rb +7 -0
- data/lib/shopify_api/session.rb +3 -3
- data/lib/shopify_api/version.rb +1 -1
- data/test/assigned_fulfillment_order_test.rb +77 -0
- data/test/base_test.rb +14 -0
- data/test/fixtures/assigned_fulfillment_orders.json +78 -0
- data/test/fixtures/fulfillment_order.json +38 -0
- data/test/fixtures/fulfillment_order_locations_for_move.json +18 -0
- data/test/fixtures/fulfillment_orders.json +78 -0
- data/test/fixtures/fulfillments.json +53 -0
- data/test/fixtures/graphql/2019-10.json +1083 -0
- data/test/fixtures/graphql/dummy_schema.rb +16 -0
- data/test/fixtures/graphql/unstable.json +1083 -0
- data/test/fulfillment_order_test.rb +462 -0
- data/test/fulfillment_order_test_helper.rb +7 -0
- data/test/fulfillment_test.rb +164 -1
- data/test/fulfillment_v2_test.rb +62 -0
- data/test/graphql/http_client_test.rb +26 -0
- data/test/graphql_test.rb +147 -0
- data/test/order_test.rb +50 -0
- data/test/session_test.rb +26 -13
- data/test/test_helper.rb +4 -1
- metadata +25 -3
- data/lib/shopify_api/resources/graphql.rb +0 -22
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
namespace :shopify_api do
|
5
|
+
namespace :graphql do
|
6
|
+
desc 'Dumps a local JSON schema file of the Shopify Admin API'
|
7
|
+
task :dump do
|
8
|
+
usage = <<~USAGE
|
9
|
+
|
10
|
+
Usage: rake shopify_api:graphql:dump [<args>]
|
11
|
+
|
12
|
+
Dumps a local JSON schema file of the Shopify Admin API. The schema is specific to an
|
13
|
+
API version and authentication is required (either OAuth or private app).
|
14
|
+
|
15
|
+
Dump the schema file for the 2020-01 API version using private app authentication:
|
16
|
+
$ rake shopify_api:graphql:dump SHOP_URL="https://API_KEY:PASSWORD@SHOP_NAME.myshopify.com" API_VERSION=2020-01
|
17
|
+
|
18
|
+
Dump the schema file for the unstable API version using an OAuth access token:
|
19
|
+
$ rake shopify_api:graphql:dump SHOP_DOMAIN=SHOP_NAME.myshopify.com ACCESS_TOKEN=abc API_VERSION=unstable
|
20
|
+
|
21
|
+
See https://github.com/Shopify/shopify_api#getting-started for more
|
22
|
+
details on getting started with authenticated API calls.
|
23
|
+
|
24
|
+
Arguments:
|
25
|
+
ACCESS_TOKEN OAuth access token (shop specific)
|
26
|
+
API_VERSION API version handle [example: 2020-01]
|
27
|
+
SHOP_DOMAIN Shop domain (without path) [example: SHOP_NAME.myshopify.com]
|
28
|
+
SHOP_URL Shop URL for private apps [example: https://API_KEY:PASSWORD@SHOP_NAME.myshopify.com]
|
29
|
+
USAGE
|
30
|
+
|
31
|
+
access_token = ENV['ACCESS_TOKEN'] || ENV['access_token']
|
32
|
+
api_version = ENV['API_VERSION'] || ENV['api_version']
|
33
|
+
shop_url = ENV['SHOP_URL'] || ENV['shop_url']
|
34
|
+
shop_domain = ENV['SHOP_DOMAIN'] || ENV['shop_domain']
|
35
|
+
|
36
|
+
unless access_token || api_version || shop_url || shop_domain
|
37
|
+
puts usage
|
38
|
+
exit(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
unless shop_url || shop_domain
|
42
|
+
puts 'Error: either SHOP_DOMAIN or SHOP_URL is required for authentication'
|
43
|
+
puts usage
|
44
|
+
exit(1)
|
45
|
+
end
|
46
|
+
|
47
|
+
if shop_url && shop_domain
|
48
|
+
puts 'Error: SHOP_DOMAIN and SHOP_URL cannot be used together. Use one or the other for authentication.'
|
49
|
+
puts usage
|
50
|
+
exit(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
if shop_domain && !access_token
|
54
|
+
puts 'Error: ACCESS_TOKEN required when SHOP_DOMAIN is used'
|
55
|
+
puts usage
|
56
|
+
exit(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
unless api_version
|
60
|
+
puts 'Error: API_VERSION required. Example: 2020-01'
|
61
|
+
puts usage
|
62
|
+
exit(1)
|
63
|
+
end
|
64
|
+
|
65
|
+
Rake::Task['environment'].invoke if Rake::Task.task_defined?('environment')
|
66
|
+
|
67
|
+
ShopifyAPI::ApiVersion.fetch_known_versions
|
68
|
+
ShopifyAPI::ApiVersion.version_lookup_mode = :raise_on_unknown
|
69
|
+
|
70
|
+
shopify_session = ShopifyAPI::Session.new(domain: shop_domain, token: access_token, api_version: api_version)
|
71
|
+
ShopifyAPI::Base.activate_session(shopify_session)
|
72
|
+
|
73
|
+
if shop_url
|
74
|
+
ShopifyAPI::Base.site = shop_url
|
75
|
+
end
|
76
|
+
|
77
|
+
puts "Fetching schema for #{ShopifyAPI::Base.api_version.handle} API version..."
|
78
|
+
|
79
|
+
client = ShopifyAPI::GraphQL::HTTPClient.new(ShopifyAPI::Base.api_version)
|
80
|
+
document = GraphQL.parse('{ __schema { queryType { name } } }')
|
81
|
+
response = client.execute(document: document).to_h
|
82
|
+
|
83
|
+
unless response['data'].present?
|
84
|
+
puts "Error: failed to query the API."
|
85
|
+
puts "Response: #{response}"
|
86
|
+
puts 'Ensure your SHOP_DOMAIN or SHOP_URL are valid and you have valid authentication credentials.'
|
87
|
+
puts usage
|
88
|
+
exit(1)
|
89
|
+
end
|
90
|
+
|
91
|
+
schema_location = ShopifyAPI::GraphQL.schema_location
|
92
|
+
FileUtils.mkdir_p(schema_location) unless Dir.exist?(schema_location)
|
93
|
+
|
94
|
+
schema_file = schema_location.join("#{api_version}.json")
|
95
|
+
GraphQL::Client.dump_schema(client, schema_file.to_s)
|
96
|
+
|
97
|
+
puts "Wrote file #{schema_file}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ShopifyAPI
|
2
|
+
class AssignedFulfillmentOrder < Base
|
3
|
+
|
4
|
+
def initialize(attributes = {}, persisted = false)
|
5
|
+
ShopifyAPI::Base.version_validation!(FulfillmentOrder::MINIMUM_VERSION)
|
6
|
+
super(attributes, persisted)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.find(scope, *args)
|
10
|
+
ShopifyAPI::Base.version_validation!(FulfillmentOrder::MINIMUM_VERSION)
|
11
|
+
|
12
|
+
assigned_fulfillment_orders = super(scope, *args)
|
13
|
+
assigned_fulfillment_orders.map { |afo| FulfillmentOrder.new(afo.attributes) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -132,6 +132,14 @@ module ShopifyAPI
|
|
132
132
|
!!early_july_pagination
|
133
133
|
end
|
134
134
|
|
135
|
+
def version_validation!(minimum_version)
|
136
|
+
available_in_version = ShopifyAPI::ApiVersion.find_version(minimum_version)
|
137
|
+
|
138
|
+
unless ShopifyAPI::Base.api_version >= available_in_version
|
139
|
+
raise NotImplementedError, "The minimum supported version is #{minimum_version}."
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
135
143
|
private
|
136
144
|
|
137
145
|
attr_accessor :early_july_pagination
|
@@ -9,5 +9,39 @@ module ShopifyAPI
|
|
9
9
|
def cancel; load_attributes_from_response(post(:cancel, {}, only_id)); end
|
10
10
|
def complete; load_attributes_from_response(post(:complete, {}, only_id)); end
|
11
11
|
def open; load_attributes_from_response(post(:open, {}, only_id)); end
|
12
|
+
|
13
|
+
def order_id=(order_id)
|
14
|
+
prefix_options[:order_id] = order_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def load(attributes, remove_root = false, persisted = false)
|
18
|
+
order_id = attributes['order_id']
|
19
|
+
prefix_options[:order_id] = order_id if order_id
|
20
|
+
super(attributes, remove_root, persisted)
|
21
|
+
end
|
22
|
+
|
23
|
+
def save
|
24
|
+
if prefix_options[:order_id].present?
|
25
|
+
super
|
26
|
+
else
|
27
|
+
line_items = attributes['line_items_by_fulfillment_order'] || attributes[:line_items_by_fulfillment_order]
|
28
|
+
if line_items.blank?
|
29
|
+
raise ShopifyAPI::ValidationException,
|
30
|
+
"either 'line_items_by_fulfillment_order' or prefix_options[:order_id] is required"
|
31
|
+
end
|
32
|
+
|
33
|
+
fulfillmentV2 = FulfillmentV2.new(attributes)
|
34
|
+
result = fulfillmentV2.save
|
35
|
+
load(fulfillmentV2.attributes, false, true)
|
36
|
+
result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def update_tracking(tracking_info:, notify_customer:)
|
41
|
+
fulfillmentV2 = FulfillmentV2.new(attributes)
|
42
|
+
result = fulfillmentV2.update_tracking(tracking_info: tracking_info, notify_customer: notify_customer)
|
43
|
+
load(fulfillmentV2.attributes, false, true)
|
44
|
+
result
|
45
|
+
end
|
12
46
|
end
|
13
47
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module ShopifyAPI
|
2
|
+
class FulfillmentOrder < Base
|
3
|
+
MINIMUM_VERSION = '2020-01'
|
4
|
+
|
5
|
+
def initialize(attributes = {}, persisted = false)
|
6
|
+
ShopifyAPI::Base.version_validation!(MINIMUM_VERSION)
|
7
|
+
super(attributes, persisted)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.find(scope, *args)
|
11
|
+
ShopifyAPI::Base.version_validation!(MINIMUM_VERSION)
|
12
|
+
|
13
|
+
if scope == :all
|
14
|
+
order_id = args.first&.dig(:params, :order_id)
|
15
|
+
raise ShopifyAPI::ValidationException, "'order_id' is required" if order_id.blank?
|
16
|
+
|
17
|
+
order = ::ShopifyAPI::Order.new(id: order_id)
|
18
|
+
order.fulfillment_orders(args.first[:params].except(:order_id))
|
19
|
+
else
|
20
|
+
super(scope, *args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def fulfillments(options = {})
|
25
|
+
fulfillment_hashes = get(:fulfillments, options)
|
26
|
+
fulfillment_hashes.map { |fulfillment_hash| Fulfillment.new(fulfillment_hash) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def locations_for_move
|
30
|
+
locations_for_move_hashes = get(:locations_for_move, {})
|
31
|
+
|
32
|
+
locations_for_move_hashes.map do |locations_for_move_hash|
|
33
|
+
FulfillmentOrderLocationsForMove.new(locations_for_move_hash)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def move(new_location_id:)
|
38
|
+
body = {
|
39
|
+
fulfillment_order: {
|
40
|
+
new_location_id: new_location_id
|
41
|
+
}
|
42
|
+
}
|
43
|
+
keyed_fulfillment_orders = keyed_fulfillment_orders_from_response(post(:move, {}, body.to_json))
|
44
|
+
load_keyed_fulfillment_order(keyed_fulfillment_orders, 'original_fulfillment_order')
|
45
|
+
keyed_fulfillment_orders
|
46
|
+
end
|
47
|
+
|
48
|
+
def cancel
|
49
|
+
keyed_fulfillment_orders = keyed_fulfillment_orders_from_response(post(:cancel, {}, only_id))
|
50
|
+
load_keyed_fulfillment_order(keyed_fulfillment_orders, 'fulfillment_order')
|
51
|
+
keyed_fulfillment_orders
|
52
|
+
end
|
53
|
+
|
54
|
+
def close(message: nil)
|
55
|
+
body = {
|
56
|
+
fulfillment_order: {
|
57
|
+
message: message
|
58
|
+
}
|
59
|
+
}
|
60
|
+
load_attributes_from_response(post(:close, {}, body.to_json))
|
61
|
+
end
|
62
|
+
|
63
|
+
def request_fulfillment(fulfillment_order_line_items: nil, message: nil)
|
64
|
+
body = {
|
65
|
+
fulfillment_request: {
|
66
|
+
fulfillment_order_line_items: fulfillment_order_line_items,
|
67
|
+
message: message
|
68
|
+
}
|
69
|
+
}
|
70
|
+
keyed_fulfillment_orders = keyed_fulfillment_orders_from_response(post(:fulfillment_request, {}, body.to_json))
|
71
|
+
load_keyed_fulfillment_order(keyed_fulfillment_orders, 'original_fulfillment_order')
|
72
|
+
keyed_fulfillment_orders
|
73
|
+
end
|
74
|
+
|
75
|
+
def accept_fulfillment_request(message: nil)
|
76
|
+
body = {
|
77
|
+
fulfillment_request: {
|
78
|
+
message: message
|
79
|
+
}
|
80
|
+
}
|
81
|
+
load_attributes_from_response(post('fulfillment_request/accept', {}, body.to_json))
|
82
|
+
end
|
83
|
+
|
84
|
+
def reject_fulfillment_request(message: nil)
|
85
|
+
body = {
|
86
|
+
fulfillment_request: {
|
87
|
+
message: message
|
88
|
+
}
|
89
|
+
}
|
90
|
+
load_attributes_from_response(post('fulfillment_request/reject', {}, body.to_json))
|
91
|
+
end
|
92
|
+
|
93
|
+
def request_cancellation(message: nil)
|
94
|
+
body = {
|
95
|
+
cancellation_request: {
|
96
|
+
message: message
|
97
|
+
}
|
98
|
+
}
|
99
|
+
load_attributes_from_response(post(:cancellation_request, {}, body.to_json))
|
100
|
+
end
|
101
|
+
|
102
|
+
def accept_cancellation_request(message: nil)
|
103
|
+
body = {
|
104
|
+
cancellation_request: {
|
105
|
+
message: message
|
106
|
+
}
|
107
|
+
}
|
108
|
+
load_attributes_from_response(post('cancellation_request/accept', {}, body.to_json))
|
109
|
+
end
|
110
|
+
|
111
|
+
def reject_cancellation_request(message: nil)
|
112
|
+
body = {
|
113
|
+
cancellation_request: {
|
114
|
+
message: message
|
115
|
+
}
|
116
|
+
}
|
117
|
+
load_attributes_from_response(post('cancellation_request/reject', {}, body.to_json))
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def load_keyed_fulfillment_order(keyed_fulfillment_orders, key)
|
123
|
+
if keyed_fulfillment_orders[key]&.attributes
|
124
|
+
load(keyed_fulfillment_orders[key].attributes, false, true)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def keyed_fulfillment_orders_from_response(response)
|
129
|
+
return load_attributes_from_response(response) if response.code != '200'
|
130
|
+
|
131
|
+
keyed_fulfillment_orders = ActiveSupport::JSON.decode(response.body)
|
132
|
+
keyed_fulfillment_orders.transform_values do |fulfillment_order_attributes|
|
133
|
+
FulfillmentOrder.new(fulfillment_order_attributes) if fulfillment_order_attributes
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ShopifyAPI
|
2
|
+
class FulfillmentV2 < Base
|
3
|
+
self.element_name = 'fulfillment'
|
4
|
+
|
5
|
+
def initialize(attributes = {}, persisted = false)
|
6
|
+
ShopifyAPI::Base.version_validation!(FulfillmentOrder::MINIMUM_VERSION)
|
7
|
+
super(attributes, persisted)
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_tracking(tracking_info:, notify_customer:)
|
11
|
+
body = {
|
12
|
+
fulfillment: {
|
13
|
+
tracking_info: tracking_info,
|
14
|
+
notify_customer: notify_customer
|
15
|
+
}
|
16
|
+
}
|
17
|
+
load_attributes_from_response(post(:update_tracking, {}, body.to_json))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -30,6 +30,13 @@ module ShopifyAPI
|
|
30
30
|
Transaction.create(capture_transaction)
|
31
31
|
end
|
32
32
|
|
33
|
+
def fulfillment_orders(options = {})
|
34
|
+
ShopifyAPI::Base.version_validation!(FulfillmentOrder::MINIMUM_VERSION)
|
35
|
+
|
36
|
+
fulfillment_order_hashes = get(:fulfillment_orders, options)
|
37
|
+
fulfillment_order_hashes.map { |fulfillment_order_hash| FulfillmentOrder.new(fulfillment_order_hash) }
|
38
|
+
end
|
39
|
+
|
33
40
|
class ClientDetails < Base
|
34
41
|
end
|
35
42
|
end
|
data/lib/shopify_api/session.rb
CHANGED
@@ -91,9 +91,9 @@ module ShopifyAPI
|
|
91
91
|
self.extra = extra
|
92
92
|
end
|
93
93
|
|
94
|
-
def create_permission_url(scope, redirect_uri =
|
95
|
-
params = {:
|
96
|
-
params[:
|
94
|
+
def create_permission_url(scope, redirect_uri, options = {})
|
95
|
+
params = { client_id: api_key, scope: scope.join(','), redirect_uri: redirect_uri }
|
96
|
+
params[:state] = options[:state] if options[:state]
|
97
97
|
construct_oauth_url("authorize", params)
|
98
98
|
end
|
99
99
|
|
data/lib/shopify_api/version.rb
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'fulfillment_order_test_helper'
|
3
|
+
|
4
|
+
class AssignedFulFillmentOrderTest < Test::Unit::TestCase
|
5
|
+
include FulfillmentOrderTestHelper
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
@url_prefix = url_prefix_for_activated_session_for('2020-01')
|
10
|
+
@fulfillment_order_fixture = load_fixture('assigned_fulfillment_orders')
|
11
|
+
end
|
12
|
+
|
13
|
+
context "AssignedFulfillmentOrder" do
|
14
|
+
context ".new" do
|
15
|
+
should "raise NotImplementedError when api_version is older than 2020-01" do
|
16
|
+
url_prefix_for_activated_session_for('2019-10')
|
17
|
+
|
18
|
+
assert_raises NotImplementedError do
|
19
|
+
ShopifyAPI::AssignedFulfillmentOrder.new(ActiveSupport::JSON.decode(@fulfillment_order_fixture))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "#all" do
|
25
|
+
should "raise NotImplementedError when api_version is older than 2020-01" do
|
26
|
+
@url_prefix = url_prefix_for_activated_session_for('2019-10')
|
27
|
+
|
28
|
+
fake 'assigned_fulfillment_orders',
|
29
|
+
url: "#{@url_prefix}/assigned_fulfillment_orders.json",
|
30
|
+
method: :get,
|
31
|
+
body: @fulfillment_order_fixture,
|
32
|
+
extension: false
|
33
|
+
|
34
|
+
assert_raises NotImplementedError do
|
35
|
+
ShopifyAPI::AssignedFulfillmentOrder.all(params: { assigned_status: 'cancellation_requested' })
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
should "list assigned fulfillment orders by assigned_status" do
|
40
|
+
fake 'assigned_fulfillment_orders',
|
41
|
+
url: "#{@url_prefix}/assigned_fulfillment_orders.json?assigned_status=cancellation_requested",
|
42
|
+
method: :get,
|
43
|
+
body: @fulfillment_order_fixture,
|
44
|
+
extension: false
|
45
|
+
|
46
|
+
assigned_fulfillment_orders = ShopifyAPI::AssignedFulfillmentOrder.all(
|
47
|
+
params: { assigned_status: 'cancellation_requested' }
|
48
|
+
)
|
49
|
+
|
50
|
+
assert_equal 2, assigned_fulfillment_orders.count
|
51
|
+
assigned_fulfillment_orders.each do |fulfillment_order|
|
52
|
+
assert fulfillment_order.is_a?(ShopifyAPI::FulfillmentOrder)
|
53
|
+
assert_equal 'in_progress', fulfillment_order.status
|
54
|
+
assert_equal 'cancellation_accepted', fulfillment_order.request_status
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
should "be able to list assigned fulfillment orders by location_ids" do
|
59
|
+
assigned_location_id = 905684977
|
60
|
+
fake 'assigned_fulfillment_orders',
|
61
|
+
url: "#{@url_prefix}/assigned_fulfillment_orders.json?location_ids%5B%5D=#{assigned_location_id}",
|
62
|
+
method: :get,
|
63
|
+
body: @fulfillment_order_fixture, extension: false
|
64
|
+
|
65
|
+
assigned_fulfillment_orders = ShopifyAPI::AssignedFulfillmentOrder.all(
|
66
|
+
params: { location_ids: [assigned_location_id] }
|
67
|
+
)
|
68
|
+
|
69
|
+
assert_equal 2, assigned_fulfillment_orders.count
|
70
|
+
assigned_fulfillment_orders.each do |fulfillment_order|
|
71
|
+
assert fulfillment_order.is_a?(ShopifyAPI::FulfillmentOrder)
|
72
|
+
assert_equal assigned_location_id, fulfillment_order.assigned_location_id
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|