Skip to content

Commit c06a497

Browse files
committed
🔨 scheme_data_importer.rb - example script
1 parent 29a1ea4 commit c06a497

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

examples/scheme_data_importer.rb

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#!/usr/bin/env ruby -r rubygems
2+
# frozen_string_literal: true
3+
4+
# Usage examples:
5+
# ./scheme_data_importer.rb \
6+
# --script-id 8xx \
7+
# --deploy-id x \
8+
# --realm 7xxxxxxx \
9+
# --consumer-key 4xxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
10+
# --consumer-secret 2xxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
11+
# --token 7xxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
12+
# --token-secret 5xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
13+
#
14+
# This example demonstrates how to sign an OAuth 1.0 HMAC-SHA256 GET request
15+
# including oauth params and query params in the signature base string, and
16+
# send the Authorization header to a NetSuite-hosted endpoint.
17+
18+
require "oauth"
19+
require "net/http"
20+
require "uri"
21+
require "json"
22+
require "securerandom"
23+
require "openssl"
24+
require "base64"
25+
require "optparse"
26+
require "pp"
27+
28+
class SchemeDataImporter
29+
attr_reader :script_id, :deploy_id, :realm, :consumer, :token
30+
31+
def initialize(script_id, deploy_id, realm, consumer_key, consumer_secret, token_value, token_secret)
32+
@script_id = script_id
33+
@deploy_id = deploy_id
34+
@realm = realm
35+
@consumer = OAuth::Consumer.new(consumer_key, consumer_secret, {
36+
site: "https://#{realm}.xyz.netsuite.com",
37+
signature_method: "HMAC-SHA256",
38+
oauth_version: "1.0",
39+
})
40+
41+
# AccessToken must be constructed with the token and token_secret
42+
@token = OAuth::AccessToken.new(@consumer, token_value, token_secret)
43+
@token_secret = token_secret || ""
44+
end
45+
46+
def import_data
47+
base_uri = "https://#{realm}.xyz.netsuite.com/app/site/hosting/xyz.nz"
48+
request_path = "#{base_uri}?script=#{OAuth::Helper.escape(script_id)}&deploy=#{OAuth::Helper.escape(deploy_id)}"
49+
50+
auth_header, debug = generate_auth_header(base_uri, {"script" => script_id, "deploy" => deploy_id})
51+
52+
puts "Authorization header: #{auth_header}"
53+
puts "Base string: #{debug[:base_string]}"
54+
puts "Signing key: #{debug[:signing_key]}"
55+
puts "Signature: #{debug[:signature]}"
56+
57+
uri = URI(request_path)
58+
http = Net::HTTP.new(uri.host, uri.port)
59+
http.use_ssl = (uri.scheme == "https")
60+
req = Net::HTTP::Get.new(uri.request_uri)
61+
req["Authorization"] = auth_header
62+
req["Content-Type"] = "application/json"
63+
req["Accept"] = "application/json"
64+
65+
response = http.request(req)
66+
67+
puts "Response Code: #{response.code}"
68+
puts "Response Message: #{response.message}"
69+
puts "Response Body: #{response.body}"
70+
71+
if response.code.to_i == 200
72+
puts "Import process complete"
73+
begin
74+
JSON.parse(response.body)
75+
rescue
76+
response.body
77+
end
78+
else
79+
raise "Failed to fetch data: #{response.code} #{response.message} - #{response.body}"
80+
end
81+
end
82+
83+
private
84+
85+
def generate_auth_header(base_uri, query_params = {})
86+
oauth_params = {
87+
"oauth_consumer_key" => @consumer.key,
88+
"oauth_token" => @token.token,
89+
"oauth_signature_method" => "HMAC-SHA256",
90+
"oauth_timestamp" => Time.now.to_i.to_s,
91+
"oauth_nonce" => generate_nonce,
92+
"oauth_version" => "1.0",
93+
}
94+
95+
all_params = oauth_params.merge(query_params)
96+
97+
base_string = generate_base_string("GET", base_uri, all_params)
98+
99+
signing_key = "#{OAuth::Helper.escape(@consumer.secret)}&#{OAuth::Helper.escape(@token_secret.to_s)}"
100+
101+
digest = OpenSSL::HMAC.digest("sha256", signing_key, base_string)
102+
signature = Base64.strict_encode64(digest)
103+
104+
oauth_params["oauth_signature"] = signature
105+
106+
header_params = oauth_params.sort.map do |k, v|
107+
%(#{OAuth::Helper.escape(k)}="#{OAuth::Helper.escape(v)}")
108+
end
109+
header = "OAuth " + %(realm="#{OAuth::Helper.escape(realm)}") + ", " + header_params.join(", ")
110+
111+
debug = {
112+
base_string: base_string,
113+
signing_key: signing_key,
114+
signature: signature,
115+
}
116+
117+
[header, debug]
118+
end
119+
120+
def generate_nonce
121+
SecureRandom.hex(16)
122+
end
123+
124+
def generate_base_string(http_method, base_uri, params)
125+
encoded_pairs = params.map do |k, v|
126+
[OAuth::Helper.escape(k.to_s), OAuth::Helper.escape(v.to_s)]
127+
end
128+
129+
encoded_pairs.sort_by! { |k, v| [k, v] }
130+
131+
normalized = encoded_pairs.map { |k, v| "#{k}=#{v}" }.join("&")
132+
133+
method = http_method.upcase
134+
"#{method}&#{OAuth::Helper.escape(base_uri)}&#{OAuth::Helper.escape(normalized)}"
135+
end
136+
end
137+
138+
# ----------------
139+
# CLI / Example runner
140+
options = {}
141+
142+
op = OptionParser.new do |opts|
143+
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
144+
145+
opts.on("--script-id ID", "Script ID to call") { |v| options[:script_id] = v }
146+
opts.on("--deploy-id ID", "Deploy ID to call") { |v| options[:deploy_id] = v }
147+
opts.on("--realm REALM", "Account realm (subdomain)") { |v| options[:realm] = v }
148+
opts.on("--consumer-key KEY", "Consumer key") { |v| options[:consumer_key] = v }
149+
opts.on("--consumer-secret SECRET", "Consumer secret") { |v| options[:consumer_secret] = v }
150+
opts.on("--token TOKEN", "Access token") { |v| options[:token] = v }
151+
opts.on("--token-secret SECRET", "Access token secret") { |v| options[:token_secret] = v }
152+
end
153+
154+
op.parse!
155+
156+
required = %i[script_id deploy_id realm consumer_key consumer_secret token token_secret]
157+
missing = required.select { |k| options[k].nil? }
158+
if missing.any?
159+
puts op.help
160+
puts "Missing options: #{missing.join(", ")}"
161+
exit 1
162+
end
163+
164+
importer = SchemeDataImporter.new(
165+
options[:script_id],
166+
options[:deploy_id],
167+
options[:realm],
168+
options[:consumer_key],
169+
options[:consumer_secret],
170+
options[:token],
171+
options[:token_secret],
172+
)
173+
174+
begin
175+
result = importer.import_data
176+
pp(result)
177+
rescue => e
178+
warn("ERROR: #{e.message}")
179+
exit(1)
180+
end

0 commit comments

Comments
 (0)