Skip to content
This repository has been archived by the owner on Aug 21, 2023. It is now read-only.

Incremental activities #36

Merged
merged 5 commits into from
Dec 7, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/hackerone/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
require_relative "client/structured_scope"
require_relative "client/swag"
require_relative "client/bounty"
require_relative "client/incremental/activities"

module HackerOne
module Client
Expand Down Expand Up @@ -119,14 +120,14 @@ def get(endpoint, params = nil)
self.class.parse_response(response)
end

def self.parse_response(response)
def self.parse_response(response, extract_data: true)
if response.status.to_s.start_with?("4")
raise ArgumentError, "API called failed, probably your fault: #{response.body}"
elsif response.status.to_s.start_with?("5")
raise RuntimeError, "API called failed, probably their fault: #{response.body}"
elsif response.success?
response_body_json = JSON.parse(response.body, :symbolize_names => true)
if response_body_json.key?(:data)
if extract_data && response_body_json.key?(:data)
response_body_json[:data]
else
response_body_json
Expand Down
101 changes: 101 additions & 0 deletions lib/hackerone/client/incremental/activities.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
module HackerOne
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this file in general, I think the tiny methods might work better as inline code with a comment rather than a separate one line method that's only called in one place. I think avoiding ivars in favor of locals will help readability. Double emphasis on think.

module Client
module Incremental
class Activities
include ResourceHelper

DOTFILE = '.hackerone_client_incremental_activities'.freeze
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is your plan to store the latest fetched thing in this file? If so, that isn't going to work if you have more than one machine that uses this lib and I would let this up to the user how to store this data.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's the plan! We need some kind of persistent storage to store the updated_at_after value, which essentially acts like a cursor.

I think you have a point, let's remove this logic?


attr_reader :program, :updated_at_after, :page_size

def initialize(program, updated_at_after: nil, page_size: 25)
@program = program
@updated_at_after = updated_at_after
@page_size = page_size
end

def loop_through_activities
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

traverse is the word cool kids use

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😎

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flatmap :trollface:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oreoshake wow, you're so cool

load_dotfile

loop do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't while next_page what you want?

fetch_current_page

activities.each do |activity_json|
activity = HackerOne::Client::Activities
.build(activity_json)
yield activity
end

break if next_page.nil?
end
end

def activities
current_page[:data]
end

def next_page?
next_updated_at_after.present?
end

def next_page
return nil unless next_page?

@updated_at_after = next_updated_at_after
fetch_current_page
end

def load_dotfile
return nil unless File.exist?(dotfile_filepath)
dotfile_content = JSON.parse(
File.read(dotfile_filepath)
)

return nil unless dotfile_content.key?(program.handle)

@updated_at_after = dotfile_content
.fetch(program.handle)
.fetch('updated_at_after')
end

def store_dotfile
new_dotfile_content = {
program.handle => {
updated_at_after: updated_at_after
}
}
File.open(dotfile_filepath, 'w') do |f|
f.puts(JSON.pretty_generate(new_dotfile_content))
end
end

private

def fetch_current_page
@current_page = nil
current_page
end

def current_page
@current_page ||= make_get_request(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think avoiding memoization here can clean up some of the state introduced by the ivars.

'incremental/activities',
extract_data: false,
params: {
handle: program.handle,
first: page_size,
updated_at_after: updated_at_after
}
)
end

def dotfile_filepath
File.join(Dir.home, DOTFILE)
end

def next_updated_at_after
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method name is a little hard to grok.

current_page[:meta][:max_updated_at]
end
end
end
end
end
8 changes: 8 additions & 0 deletions lib/hackerone/client/program.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ def id
@program[:id]
end

def incremental_activities(updated_at_after: nil, page_size: 25)
HackerOne::Client::Incremental::Activities.new(
self,
updated_at_after: updated_at_after,
page_size: page_size
)
end

def attributes
OpenStruct.new(@program[:attributes])
end
Expand Down
15 changes: 9 additions & 6 deletions lib/hackerone/client/resource_helper.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
module HackerOne
module Client
module ResourceHelper
def parse_response(response)
HackerOne::Client::Api.parse_response(response)
def parse_response(response, extract_data: true)
HackerOne::Client::Api.parse_response(
response,
extract_data: extract_data,
)
end

def make_post_request(url, request_body:)
def make_post_request(url, request_body:, extract_data: true)
response = HackerOne::Client::Api.hackerone_api_connection.post do |req|
req.headers['Content-Type'] = 'application/json'
req.url url
req.body = { data: request_body }.to_json
end

parse_response(response)
parse_response(response, extract_data: extract_data)
end

def make_get_request(url, params: {})
def make_get_request(url, params: {}, extract_data: true)
response = HackerOne::Client::Api.hackerone_api_connection.get do |req|
req.headers['Content-Type'] = 'application/json'
req.url url
req.params = params
end

parse_response(response)
parse_response(response, extract_data: extract_data)
end

private
Expand Down