From 0999c3497a146a45f7bbf01fcf36ff5d3a3cad8f Mon Sep 17 00:00:00 2001 From: Joshua Hunsche Jones Date: Fri, 5 May 2023 13:46:38 -0500 Subject: [PATCH 1/8] Move interactive prompt to generate access token into client --- lib/pcloud/client.rb | 35 +++++++++++++++++++++++++++++++++++ lib/pcloud_api.rb | 3 +++ 2 files changed, 38 insertions(+) diff --git a/lib/pcloud/client.rb b/lib/pcloud/client.rb index c49c6c4..97aedb4 100644 --- a/lib/pcloud/client.rb +++ b/lib/pcloud/client.rb @@ -33,6 +33,41 @@ def execute(method, query: {}, body: {}) json_response end + def generate_access_token + puts "=== Follow these steps to generate a pCloud app and access token: ===" + puts "1. Register an app at `https://docs.pcloud.com/my_apps/`" + + puts "2. Enter the client id and secret for the app:" + print "Client ID > " + client_id = $stdin.gets.chomp + + print "Client Secret > " + client_secret = $stdin.gets.chomp + + puts "3. Enter the data region of your pCloud account [EU/US]:" + print "> " + region_specific_api_base = $stdin.gets.chomp == "EU" ? "eapi.pcloud.com" : "api.pcloud.com" + + puts "4. Navigate to this URL to start the access code flow:" + puts "`https://my.pcloud.com/oauth2/authorize?client_id=#{client_id}&response_type=code`" + puts "5. After logging in, enter the access code provided below:" + print "> " + access_code = $stdin.gets.chomp + + puts "6. Requesting access token from pCloud..." + query = { client_id: client_id, client_secret: client_secret, code: access_code } + uri = URI.parse("https://#{region_specific_api_base}/oauth2_token?#{URI.encode_www_form(query)}") + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + request = Net::HTTP::Post.new(uri.request_uri) + request["Accept"] = "application/json" + response = http.request(request) + + json_response = JSON.parse(response.body) + raise json_response["error"] if json_response["error"] + puts "Done! Your access token is: #{json_response["access_token"]}" + end + private def data_region_from_config_or_env diff --git a/lib/pcloud_api.rb b/lib/pcloud_api.rb index 621359d..f4df1c4 100644 --- a/lib/pcloud_api.rb +++ b/lib/pcloud_api.rb @@ -2,6 +2,9 @@ require "tzinfo" require "json" require "time" +require "json" +require "uri" +require "net/http" require "pcloud/version" require "pcloud/time_helper" From aa3725656ebc966c171a7abd7be86fe0b56f4d1a Mon Sep 17 00:00:00 2001 From: jhunschejones Date: Fri, 5 May 2023 14:27:03 -0500 Subject: [PATCH 2/8] Update script copy --- lib/pcloud/client.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pcloud/client.rb b/lib/pcloud/client.rb index 97aedb4..f6f562b 100644 --- a/lib/pcloud/client.rb +++ b/lib/pcloud/client.rb @@ -34,7 +34,7 @@ def execute(method, query: {}, body: {}) end def generate_access_token - puts "=== Follow these steps to generate a pCloud app and access token: ===" + puts "=== Follow these steps to generate a pCloud app and access token ===" puts "1. Register an app at `https://docs.pcloud.com/my_apps/`" puts "2. Enter the client id and secret for the app:" @@ -65,7 +65,9 @@ def generate_access_token json_response = JSON.parse(response.body) raise json_response["error"] if json_response["error"] - puts "Done! Your access token is: #{json_response["access_token"]}" + puts "Done! Your access token is: \n#{json_response["access_token"]}" + puts "\nStore this value somewhere secure as it can be used to access your" + puts "pCloud account data and it will not be shown again." end private From d9d73fddd6c61b3b4b4cfd10394782deeef35fbb Mon Sep 17 00:00:00 2001 From: jhunschejones Date: Fri, 5 May 2023 14:32:10 -0500 Subject: [PATCH 3/8] Update README instructions --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f977b7c..280e77d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,17 @@ Or install it yourself as: ### Generating an access token -To use the `pcloud_api` client, you will need to first generate an access token. You may do this by cloning and running the script in [`./bin/generate_access_token`](https://github.com/jhunschejones/pcloud_api/blob/master/bin/generate_access_token). It will take you through the process of setting up a pCloud app and completing the OAuth2 flow in the browser. +To use the `pcloud_api` client, you will need to first generate an access token. You may do this by opening a Rails console or IRB session and entering the following code: + +```ruby +irb(main):001:0> require "pcloud_api" +=> true +irb(main):002:0> Pcloud::Client.generate_access_token +=== Follow these steps to generate a pCloud app and access token === +... +``` + +You will be presented with an interactive prompt which will take you through the process of setting up a pCloud app and completing the OAuth2 flow in the browser. At the end of the prompt, your new access token will be displayed. You should save this value in a secure location as it can be used to access your pCloud account data. ### Configuration From beb9d52fb8ff07c690c74c9f5ee1827059b1cb55 Mon Sep 17 00:00:00 2001 From: jhunschejones Date: Fri, 5 May 2023 14:32:34 -0500 Subject: [PATCH 4/8] Remove bin/generate_access_token script --- bin/generate_access_token | 46 --------------------------------------- 1 file changed, 46 deletions(-) delete mode 100755 bin/generate_access_token diff --git a/bin/generate_access_token b/bin/generate_access_token deleted file mode 100755 index 6c61f2b..0000000 --- a/bin/generate_access_token +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env ruby - -require "json" -require "uri" -require "net/http" - -class String - def green; "\033[32m#{self}\033[0m" end - def cyan; "\033[36m#{self}\033[0m" end -end - -puts "1. Register an app at `https://docs.pcloud.com/my_apps/`".cyan - - -puts "2. Enter the client id and secret for the app:".cyan -print "Client ID > " -client_id = $stdin.gets.chomp.freeze -print "Client Secret > " -client_secret = $stdin.gets.chomp.freeze - - -puts "3. Enter the data region of your pCloud account [EU/US]:".cyan -print "> " -region_specific_api_base = $stdin.gets.chomp == "EU" ? "eapi.pcloud.com" : "api.pcloud.com" - - -puts "4. Navigate to this URL to start the access code flow:".cyan -puts "`https://my.pcloud.com/oauth2/authorize?client_id=#{client_id}&response_type=code`" - - -puts "5. After logging in, enter the access code provided below:".cyan -print "> " -access_code = $stdin.gets.chomp.freeze - - -puts "6. Requesting access token from pCloud...".cyan -query = { client_id: client_id, client_secret: client_secret, code: access_code } -uri = URI.parse("https://#{region_specific_api_base}/oauth2_token?#{URI.encode_www_form(query)}") -http = Net::HTTP.new(uri.host, uri.port) -http.use_ssl = true -request = Net::HTTP::Post.new(uri.request_uri) -request["Accept"] = "application/json" -response = http.request(request) -json_response = JSON.parse(response.body) -raise json_response["error"] if json_response["error"] -puts "Done! Your access token is: #{json_response["access_token"]}".green From f9eb0ef74007ae315f081405fc18f96b5126509c Mon Sep 17 00:00:00 2001 From: jhunschejones Date: Tue, 8 Oct 2024 15:52:45 +0900 Subject: [PATCH 5/8] Add unit test for access token generation --- pcloud_api.gemspec | 1 + spec/pcloud/client_spec.rb | 31 +++++++++++++++++++++++++++++++ spec/spec_helper.rb | 1 + 3 files changed, 33 insertions(+) diff --git a/pcloud_api.gemspec b/pcloud_api.gemspec index fc2dc9b..c074118 100644 --- a/pcloud_api.gemspec +++ b/pcloud_api.gemspec @@ -27,6 +27,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler", ">= 1.17", "< 3.0" spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "webmock", "~> 3.23" spec.add_development_dependency "pry", "~> 0.13" spec.add_dependency "httparty", "~> 0.16" diff --git a/spec/pcloud/client_spec.rb b/spec/pcloud/client_spec.rb index eed2e36..fff655c 100644 --- a/spec/pcloud/client_spec.rb +++ b/spec/pcloud/client_spec.rb @@ -1,5 +1,7 @@ RSpec.describe Pcloud::Client do before(:each) do + WebMock.reset! + Pcloud::Client.remove_class_variable(:@@data_region) if Pcloud::Client.class_variable_defined?(:@@data_region) Pcloud::Client.remove_class_variable(:@@access_token) if Pcloud::Client.class_variable_defined?(:@@access_token) Pcloud::Client.remove_class_variable(:@@timeout_seconds) if Pcloud::Client.class_variable_defined?(:@@timeout_seconds) @@ -73,6 +75,35 @@ end end + describe ".generate_access_token" do + before do + # silence console output from the interactive CLI + allow(Pcloud::Client).to receive(:puts) + allow(Pcloud::Client).to receive(:print) + end + + it "makes the expected web request to get an access token" do + client_id = "my_client_id" + client_secret = "my_client_secret" + access_code = "pcloud_access_code" + + allow($stdin).to receive(:gets).and_return( + client_id, + client_secret, + "EU", # user specified data region + access_code, # access code provided by pCloud + ) + pcloud_post_request = stub_request( + :post, + "https://eapi.pcloud.com/oauth2_token?client_id=#{client_id}&client_secret=#{client_secret}&code=#{access_code}" + ).to_return(body: { access_token: "Here's your access token!" }.to_json) + + Pcloud::Client.generate_access_token + + expect(pcloud_post_request).to have_been_requested + end + end + describe ".data_region_from_config_or_env" do it "reads from module configuration" do Pcloud::Client.configure(access_token: "test-token", data_region: "EU") diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b9c224d..34cb6db 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ require "bundler/setup" require "pcloud_api" +require "webmock/rspec" RSpec.configure do |config| # Enable flags like --only-failures and --next-failure From 5c71e52472ebc4eb66b43863f07cc128663d1ea5 Mon Sep 17 00:00:00 2001 From: jhunschejones Date: Tue, 8 Oct 2024 16:00:16 +0900 Subject: [PATCH 6/8] Replace Net::HTTP post with HTTParty post --- lib/pcloud/client.rb | 10 ++++------ lib/pcloud_api.rb | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/pcloud/client.rb b/lib/pcloud/client.rb index f6f562b..0b12e90 100644 --- a/lib/pcloud/client.rb +++ b/lib/pcloud/client.rb @@ -56,12 +56,10 @@ def generate_access_token puts "6. Requesting access token from pCloud..." query = { client_id: client_id, client_secret: client_secret, code: access_code } - uri = URI.parse("https://#{region_specific_api_base}/oauth2_token?#{URI.encode_www_form(query)}") - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - request = Net::HTTP::Post.new(uri.request_uri) - request["Accept"] = "application/json" - response = http.request(request) + response = HTTParty.post( + "https://#{region_specific_api_base}/oauth2_token?#{URI.encode_www_form(query)}", + headers: { "Accept" => "application/json" } + ) json_response = JSON.parse(response.body) raise json_response["error"] if json_response["error"] diff --git a/lib/pcloud_api.rb b/lib/pcloud_api.rb index f4df1c4..db762ec 100644 --- a/lib/pcloud_api.rb +++ b/lib/pcloud_api.rb @@ -4,7 +4,6 @@ require "time" require "json" require "uri" -require "net/http" require "pcloud/version" require "pcloud/time_helper" From 9d726f552a075ce5c92ca8102273c741ffcac8a7 Mon Sep 17 00:00:00 2001 From: jhunschejones Date: Tue, 8 Oct 2024 16:14:10 +0900 Subject: [PATCH 7/8] Just mock HTTParty and remove webmock --- spec/pcloud/client_spec.rb | 19 +++++++++++-------- spec/spec_helper.rb | 1 - 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/spec/pcloud/client_spec.rb b/spec/pcloud/client_spec.rb index fff655c..ac4f643 100644 --- a/spec/pcloud/client_spec.rb +++ b/spec/pcloud/client_spec.rb @@ -1,7 +1,5 @@ RSpec.describe Pcloud::Client do before(:each) do - WebMock.reset! - Pcloud::Client.remove_class_variable(:@@data_region) if Pcloud::Client.class_variable_defined?(:@@data_region) Pcloud::Client.remove_class_variable(:@@access_token) if Pcloud::Client.class_variable_defined?(:@@access_token) Pcloud::Client.remove_class_variable(:@@timeout_seconds) if Pcloud::Client.class_variable_defined?(:@@timeout_seconds) @@ -76,10 +74,15 @@ end describe ".generate_access_token" do + let(:httparty_response) { double(HTTParty::Response) } + before do # silence console output from the interactive CLI allow(Pcloud::Client).to receive(:puts) allow(Pcloud::Client).to receive(:print) + + allow(httparty_response).to receive(:body).and_return({ access_token: "Here's your access token!" }.to_json) + allow(HTTParty).to receive(:post).and_return(httparty_response) end it "makes the expected web request to get an access token" do @@ -93,14 +96,14 @@ "EU", # user specified data region access_code, # access code provided by pCloud ) - pcloud_post_request = stub_request( - :post, - "https://eapi.pcloud.com/oauth2_token?client_id=#{client_id}&client_secret=#{client_secret}&code=#{access_code}" - ).to_return(body: { access_token: "Here's your access token!" }.to_json) + expect(HTTParty) + .to receive(:post) + .with( + "https://eapi.pcloud.com/oauth2_token?client_id=#{client_id}&client_secret=#{client_secret}&code=#{access_code}", + { headers: { "Accept" => "application/json" } } + ).and_return(httparty_response) Pcloud::Client.generate_access_token - - expect(pcloud_post_request).to have_been_requested end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 34cb6db..b9c224d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,5 @@ require "bundler/setup" require "pcloud_api" -require "webmock/rspec" RSpec.configure do |config| # Enable flags like --only-failures and --next-failure From 6cd0434a70506205990a14e6add21cd65c41e874 Mon Sep 17 00:00:00 2001 From: jhunschejones Date: Tue, 8 Oct 2024 16:15:33 +0900 Subject: [PATCH 8/8] Remove webmock from pcloud_api.gemspec --- pcloud_api.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/pcloud_api.gemspec b/pcloud_api.gemspec index c074118..fc2dc9b 100644 --- a/pcloud_api.gemspec +++ b/pcloud_api.gemspec @@ -27,7 +27,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler", ">= 1.17", "< 3.0" spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "rspec", "~> 3.0" - spec.add_development_dependency "webmock", "~> 3.23" spec.add_development_dependency "pry", "~> 0.13" spec.add_dependency "httparty", "~> 0.16"