-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create and Delete Profiles #3
Conversation
0171400
to
c119c1c
Compare
) | ||
# => { | ||
# code: 1, | ||
# message: 'Hup...want...buy.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Poor Hup got motion sickness: https://www.youtube.com/watch?v=Sqj8xY0_Q0s
# frozen_string_literal: true | ||
|
||
module Bambora | ||
class JSONRequest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is shared code for all JSON APIs. We'll also need an XMLRequest
for some of the endpoints.
|
||
```ruby | ||
client.profile.delete(1234) | ||
# => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return from these methods is the same as what the existing beanstream-ruby
library gives us.
lib/bambora/json_request.rb
Outdated
end | ||
|
||
def request(method:, path:, params: {}, body: {}) | ||
parse_response( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need any error handling here? If there is a network Error, we'll get an informative error from Excon.
Based on the existing gem, is possible that we'll get an error response back from Bambora, but those errors are in JSON as well, so I think we should handle them in the application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i guess if server error happens(not application which will form correct json with error as the body) you won't get to JSON response instead you'll get JSON parse exception and this is our responsibility to handle it. Possible solution is to check for response code first and if it signals about error wrap it in valid JSON.
19d302e
to
452f2cf
Compare
lib/bambora/client.rb
Outdated
# Summary: Payment profiles store confidential payment information. Transactions can be processed against profiles. | ||
# Docs: https://dev.na.bambora.com/docs/guides/payment_profiles/ | ||
# Endpoint: https://api.na.bambora.com/v1/profiles | ||
def profiles; end | ||
def profile | ||
@profile ||= Bambora::Profile.new(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing the Client here so that the same client instance can be used for profiles, payment, etc.
lib/bambora/json_request.rb
Outdated
end | ||
|
||
def symbolize_keys(obj) | ||
return obj unless obj.is_a?(Hash) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if you get a hash which holds a collection of hashes e.g.
obj = {
'user' => {
'accounts' => [
{
'number' => '123',
},
{
'number' => '456',
},
]
}
}
def symbolize_keys(obj)
return obj unless obj.is_a?(Hash)
obj.each_with_object({}) do |(k, v), hash|
hash[k.to_sym] = symbolize_keys(v)
hash
end
end
symbolize_keys(obj) => {:user=>{:accounts=>[{"number"=>"123"}, {"number"=>"456"}]}}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
took from rails source code:
def _deep_transform_keys_in_object(object, &block)
case object
when Hash
object.each_with_object({}) do |(key, value), result|
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
end
when Array
object.map { |e| _deep_transform_keys_in_object(e, &block) }
else
object
end
end
_deep_transform_keys_in_object(obj){ |key| key.to_sym } => {:USER=>{:ACCOUNTS=>[{:NUMBER=>"123"}, {:NUMBER=>"456"}]}}
You could wrap that method into your method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thanks @bilogub!
lib/bambora/profile.rb
Outdated
def initialize(client) | ||
super | ||
|
||
@path = '/v1/profiles' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would probably start to break into api versions here and namespacing the classes into Bambora::V1::
in case users would want to stay on old version if supported and someone would develop V2 if available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love watching this shape up, and I'm learning a lot from it. I had a few comments to consider.
@@ -7,10 +7,15 @@ PATH | |||
GEM | |||
remote: https://rubygems.org/ | |||
specs: | |||
addressable (2.7.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Were these changes added manually? I don't see any changes made to the Gemfile
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are different ways of managing dependencies for gems. The "bundler way" is to list all dependencies in the gemspec then call gemspec
in the Gemfile to load dependencies listed there.
https://bundler.io/v2.0/guides/creating_gem.html#testing-our-gem
I don't know much about this project, but hoe has another way of managing dependencies.
#### Create a Profile | ||
|
||
```ruby | ||
client.profile.create( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great example here!
README.md
Outdated
## Delete a Profile | ||
|
||
```ruby | ||
client.profile.delete(1234) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bambora remote customer codes contain letters, and are therefore strings. Use a string input instead for the example.
lib/bambora/profile.rb
Outdated
# frozen_string_literal: true | ||
|
||
module Bambora | ||
class Profile < JSONRequest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More of a semantic thing, I find it a bit confusing that Profile
inherits from JSONRequest
. I infer from this inheritance chain that Profile
is a type of JSONRequest
, when in reality it contains multiple methods that can make JSON requests. This seems to me to be more like a concern/module.
From the perspective of the caller though, when you are using this client, you are only ever using one API method at a time, so from the point of view of the caller, it does only represent one JSON request.
Now I've just argued with myself, haha.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going to call this Domain
since it represents the Bambora domain, but then I realized that the domain also uses XML. I could perhaps name this JSONDomain
and XMLDomain
.
lib/bambora/profile.rb
Outdated
request(method: :post, path: @path, body: card_data) | ||
end | ||
|
||
def delete(remote_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To better align with the Bambora terminology, call this parameter, customer_code
. Also, could this be a named parameter so that is clear? When looking at the documentation, I had to go to the implementation to determine what the input was. A named parameter would clear up that confusion immediately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multi-threading here we come!
README.md
Outdated
$ gem install bambora-client | ||
```bash | ||
gem install bambora-client | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the extra space needed? Not a blocker for this PR. This could be fixed when some Markdown linting is included 👀.
@@ -50,20 +54,89 @@ client = Bambora::Client.new( | |||
) | |||
``` | |||
|
|||
### Profiles | |||
|
|||
*Summary*: Payment profiles store confidential payment information. Transactions can be processed against profiles. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for cutting the line-length here.
end | ||
|
||
def deep_transform_keys_in_object(object, &block) | ||
case object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀
lib/bambora/json_request.rb
Outdated
|
||
def request(method:, path:, params: {}, body: {}) | ||
resp = client.request(method: method, path: path, params: params, body: body.to_json.to_s) | ||
return parse_response(resp) if resp.status <= 300 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i see here a problem - what if it is 404. As a json client i want to get a response with 404 and empty collection.
instead i get a generic error which i have to parse(raw text) to interpret it something meaningful in my app.
https://jsonplaceholder.typicode.com/posts/1 - this returns me an object and this returns me an empty object
https://jsonplaceholder.typicode.com/posts/12123123123 with a status in the response header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bilogub Do you think we should parse all 4xx level errors and only raise an erro on 5xx and 3xx?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cassidycodes i think i gave a falsey option in my prev comment regading possible solution here and misled you, sorry.
In nutshell we should relay anything we get from api back to our client app(as long as we don't have our own API doc to rely on - we are only sitting in the middle between API and client app passing bytes there and back) - that said parse response in begin/rescue block and pass it up the chain. In case we end up in rescue block compose 50x server response in json format. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good! I just pushed a change that passes a hash with the error back to the user.
This PR adds a class to interface with the
/profiles
endpoint. It implements bothcreate
anddelete
actions.See my review below for more details.