freckle_io 0.0.3
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 +7 -0
- data/.codeclimate.yml +5 -0
- data/.env.sample +3 -0
- data/.env.test.sample +3 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +67 -0
- data/.travis.yml +20 -0
- data/Gemfile +8 -0
- data/README.md +95 -0
- data/Rakefile +5 -0
- data/freckle-io.gemspec +37 -0
- data/icon.svg +192 -0
- data/lib/freckle_io/authentication.rb +26 -0
- data/lib/freckle_io/client/entries.rb +53 -0
- data/lib/freckle_io/client/project_groups.rb +36 -0
- data/lib/freckle_io/client/projects.rb +42 -0
- data/lib/freckle_io/client/tags.rb +33 -0
- data/lib/freckle_io/client/users.rb +35 -0
- data/lib/freckle_io/configuration.rb +48 -0
- data/lib/freckle_io/connection.rb +80 -0
- data/lib/freckle_io/errors/configuration.rb +5 -0
- data/lib/freckle_io/errors/connection.rb +9 -0
- data/lib/freckle_io/errors/params.rb +8 -0
- data/lib/freckle_io/paginator.rb +70 -0
- data/lib/freckle_io/params.rb +46 -0
- data/lib/freckle_io/request/multiple_pages.rb +62 -0
- data/lib/freckle_io/request/single_page.rb +73 -0
- data/lib/freckle_io/validator/entry.rb +57 -0
- data/lib/freckle_io/validator/project.rb +46 -0
- data/lib/freckle_io/validator/project_group.rb +30 -0
- data/lib/freckle_io/validator/restricted_hash.rb +11 -0
- data/lib/freckle_io/validator/tag.rb +30 -0
- data/lib/freckle_io/validator/user.rb +43 -0
- data/lib/freckle_io/validator/validation.yml +14 -0
- data/lib/freckle_io.rb +35 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Entries/with_configuration/_all/result_must_be_a_Request_MultiplePages.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Entries/with_configuration/_all/returns_a_entry_for_each_response.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Entries/with_configuration/with_valid_params/params_should_be_valid.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Entries/with_configuration/with_validator/call_entry_s_validator.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_ProjectGroups/with_configuration/_all/result_must_be_a_Request_MultiplePages.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_ProjectGroups/with_configuration/_all/returns_a_project_group_for_each_response.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_ProjectGroups/with_configuration/with_valid_params/params_should_be_valid.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_ProjectGroups/with_configuration/with_validator/call_project_group_s_validator.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Projects/with_configuration/_all/result_must_be_a_Request_MultiplePages.yml +491 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Projects/with_configuration/_all/returns_a_project_for_each_response.yml +491 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Projects/with_configuration/_show/get_a_spacific_project.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Projects/with_configuration/_show/raw_links_should_be_a_empty_array.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Projects/with_configuration/with_valid_params/params_should_be_valid.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Projects/with_configuration/with_validator/call_project_s_validator.yml +491 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Tags/with_configuration/_all/result_must_be_a_Request_MultiplePages.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Tags/with_configuration/_all/returns_a_tag_for_each_response.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Tags/with_configuration/with_valid_params/params_should_be_valid.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Tags/with_configuration/with_validator/call_tag_s_validator.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Users/with_configuration/_all/result_must_be_a_Request_MultiplePages.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Users/with_configuration/_all/returns_a_user_for_each_response.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Users/with_configuration/_show/get_a_spacific_user.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Users/with_configuration/_show/raw_links_should_be_a_empty_array.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Users/with_configuration/with_valid_params/params_should_be_valid.yml +48 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Client_Users/with_configuration/with_validator/call_user_s_validator.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Connection/with_default_configuration/with_a_valid_response/must_be_a_json_format.yml +51 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Connection/with_default_configuration/with_freckle_token_authentication/set_X-FreckleToken.yml +51 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Connection/with_default_configuration/with_header/set_user_agent.yml +51 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Connection/with_default_configuration/with_invalid_url/raises_a_resource_not_found_error_for_invalid_resource.yml +40 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Connection/with_default_configuration/with_page_param/response_must_be_success.yml +51 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Connection/with_default_configuration/with_per_page_param/response_must_have_per_page_elements.yml +51 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_MultiplePages/with_configuration/_get/returns_an_array_of_faraday_response.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_MultiplePages/with_configuration/_get/returns_freckle_default_per_page_elements.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_MultiplePages/with_per_page/_get/return_per_page_number_elements.yml +148 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_first/next_page_should_be_a_SinglePage.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_first/prev_page_should_be_nil.yml +51 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_first/when_get_last_page_from_the_first_page/next_should_be_nil.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_get/returns_correct_raw_links_for_the_first_page.yml +51 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_last/next_page_should_be_nil.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_last/returns_correct_raw_links_for_the_last_page.yml +99 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_last/when_get_prev_page_from_the_last_page/prev_should_be_nil.yml +147 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_next/previous_page_is_a_SinglePage.yml +147 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_next/previous_page_should_be_not_nil.yml +147 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_prev/next_page_is_a_SinglePage.yml +195 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_prev/when_get_prev_page_from_the_first_page/prev_should_be_nil.yml +147 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_total_page/with_first_page/returns_the_range_of_pages.yml +51 -0
- data/spec/fixtures/vcr_cassettes/FreckleIO_Request_SinglePage/with_configuration/_total_page/with_next_page/returns_an_empty_array.yml +99 -0
- data/spec/freckle_io/client/entries_spec.rb +359 -0
- data/spec/freckle_io/client/project_groups_spec.rb +132 -0
- data/spec/freckle_io/client/projects_spec.rb +183 -0
- data/spec/freckle_io/client/tags_spec.rb +132 -0
- data/spec/freckle_io/client/users_spec.rb +150 -0
- data/spec/freckle_io/configuration_spec.rb +70 -0
- data/spec/freckle_io/connection_spec.rb +116 -0
- data/spec/freckle_io/paginator_spec.rb +105 -0
- data/spec/freckle_io/params_spec.rb +15 -0
- data/spec/freckle_io/request/multiple_pages_spec.rb +39 -0
- data/spec/freckle_io/request/single_page_spec.rb +107 -0
- data/spec/spec_helper.rb +69 -0
- data/spec/support/anonymize/entries.rb +60 -0
- data/spec/support/anonymize/project_groups.rb +55 -0
- data/spec/support/anonymize/projects.rb +60 -0
- data/spec/support/anonymize/tags.rb +47 -0
- data/spec/support/anonymize/users.rb +59 -0
- data/spec/support/anonymize_interaction.rb +60 -0
- data/spec/support/entry_info.rb +19 -0
- data/spec/support/project_group_info.rb +13 -0
- data/spec/support/project_info.rb +26 -0
- data/spec/support/shared/links/raw_links.rb +13 -0
- data/spec/support/tag_info.rb +11 -0
- data/spec/support/user_info.rb +21 -0
- metadata +382 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "freckle_io/authentication"
|
|
2
|
+
|
|
3
|
+
module FreckleIO
|
|
4
|
+
class Configuration
|
|
5
|
+
include Authentication
|
|
6
|
+
|
|
7
|
+
DEFAULT_URL = "https://api.letsfreckle.com/v2".freeze
|
|
8
|
+
|
|
9
|
+
attr_writer :auth_type
|
|
10
|
+
attr_writer :token
|
|
11
|
+
attr_writer :url
|
|
12
|
+
attr_writer :per_page
|
|
13
|
+
attr_writer :max_concurrency
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@token = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def auth_type
|
|
20
|
+
unless valid_auth?(@auth_type)
|
|
21
|
+
raise(
|
|
22
|
+
Errors::Configuration,
|
|
23
|
+
"#{@auth_type} isn't valid type authentication"
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@auth_type || raise(Errors::Configuration, "Authentication type missing!")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def token
|
|
31
|
+
@token || raise(Errors::Configuration, "Token missing!")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def url
|
|
35
|
+
@url || self.class::DEFAULT_URL
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def per_page
|
|
39
|
+
@per_page || nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def max_concurrency
|
|
43
|
+
@max_concurrency || raise(
|
|
44
|
+
Errors::Configuration, "Max concurrency missing!"
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require "freckle_io/authentication"
|
|
2
|
+
require "faraday"
|
|
3
|
+
require "faraday_middleware"
|
|
4
|
+
|
|
5
|
+
module FreckleIO
|
|
6
|
+
class Connection
|
|
7
|
+
include FreckleIO::Authentication
|
|
8
|
+
|
|
9
|
+
# rubocop:disable Metrics/MethodLength
|
|
10
|
+
def get(path, params: {}, request_options: {})
|
|
11
|
+
response = connection.get do |request|
|
|
12
|
+
authorize_request(request)
|
|
13
|
+
set_request_options(request, request_options)
|
|
14
|
+
request.url path, params
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
response
|
|
18
|
+
rescue Faraday::ConnectionFailed => e
|
|
19
|
+
raise Errors::Connection::Failed.new(e), e.message
|
|
20
|
+
rescue Faraday::ResourceNotFound => e
|
|
21
|
+
raise Errors::Connection::ResourceNotFound.new(e), e.message
|
|
22
|
+
rescue Faraday::ClientError => e
|
|
23
|
+
raise Errors::Connection::ClientError.new(e), e.message
|
|
24
|
+
end
|
|
25
|
+
# rubocop:enable Metrics/MethodLength
|
|
26
|
+
|
|
27
|
+
def get_in_parallel(
|
|
28
|
+
path,
|
|
29
|
+
from_page_number,
|
|
30
|
+
to_page_number,
|
|
31
|
+
params: {},
|
|
32
|
+
request_options: {}
|
|
33
|
+
)
|
|
34
|
+
responses = []
|
|
35
|
+
|
|
36
|
+
connection.in_parallel(manager) do
|
|
37
|
+
(from_page_number..to_page_number).each do |page|
|
|
38
|
+
merged_params = {page: page}.merge(params)
|
|
39
|
+
|
|
40
|
+
responses << get(
|
|
41
|
+
path, params: merged_params, request_options: request_options
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
responses
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def connection
|
|
52
|
+
@connection ||= Faraday.new(default_options) do |connection|
|
|
53
|
+
connection.request :json
|
|
54
|
+
connection.response :json, content_type: /\bjson$/
|
|
55
|
+
connection.response :raise_error
|
|
56
|
+
connection.adapter :typhoeus
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def default_options
|
|
61
|
+
{
|
|
62
|
+
url: FreckleIO.configuration.url,
|
|
63
|
+
headers: {
|
|
64
|
+
user_agent: "MyFreckleBot/1.0"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def set_request_options(request, options)
|
|
70
|
+
request.options.timeout = options[:timeout]
|
|
71
|
+
request.options.open_timeout = options[:open_timeout]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def manager
|
|
75
|
+
@manager ||= Typhoeus::Hydra.new(
|
|
76
|
+
max_concurrency: FreckleIO.configuration.max_concurrency
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
require "cgi"
|
|
3
|
+
|
|
4
|
+
module FreckleIO
|
|
5
|
+
class Paginator
|
|
6
|
+
attr_reader :raw_links
|
|
7
|
+
|
|
8
|
+
def initialize(raw_links)
|
|
9
|
+
@raw_links = raw_links
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def next
|
|
13
|
+
find("next")&.dig(:url)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def prev
|
|
17
|
+
find("prev")&.dig(:url)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def first
|
|
21
|
+
find("first")&.dig(:url)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def last
|
|
25
|
+
find("last")&.dig(:url)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def total_pages
|
|
29
|
+
find("last")&.dig(:number_page)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def find(rel)
|
|
35
|
+
pages.find { |page| page[:rel] == rel }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def pages
|
|
39
|
+
return {} if raw_links.empty?
|
|
40
|
+
|
|
41
|
+
@pages ||=
|
|
42
|
+
raw_links.split(",").map do |link|
|
|
43
|
+
url, rel, number_page = split_and_clean_link(link)
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
url: "#{url.path}?#{url.query}",
|
|
47
|
+
rel: rel,
|
|
48
|
+
number_page: number_page
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Example of link response headers:
|
|
54
|
+
# "<https://api.letsfreckle.com/v2/users?page=2>; rel=\"last\""
|
|
55
|
+
#
|
|
56
|
+
# The first element is an URI. "Rel" means the relative position
|
|
57
|
+
# of the current page, in this case is last page.
|
|
58
|
+
|
|
59
|
+
def split_and_clean_link(link)
|
|
60
|
+
raw_url, rel = link.split(";").map(&:strip)
|
|
61
|
+
|
|
62
|
+
raw_url = raw_url.gsub(/<|>/, "")
|
|
63
|
+
rel = rel.gsub(/(rel=)?"/, "")
|
|
64
|
+
url = URI.parse(raw_url)
|
|
65
|
+
page = CGI.parse(url.query)["page"].first
|
|
66
|
+
|
|
67
|
+
[url, rel, page]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module FreckleIO
|
|
2
|
+
class Params
|
|
3
|
+
attr_reader :params
|
|
4
|
+
attr_reader :allowed_keys
|
|
5
|
+
attr_reader :validator_module
|
|
6
|
+
|
|
7
|
+
def initialize(
|
|
8
|
+
params,
|
|
9
|
+
allowed_keys,
|
|
10
|
+
validator_module
|
|
11
|
+
)
|
|
12
|
+
@params = params
|
|
13
|
+
@allowed_keys = allowed_keys
|
|
14
|
+
@validator_module = validator_module
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
return validator.output if valid?
|
|
19
|
+
|
|
20
|
+
raise Errors::Params::InvalidParams.new, validator_messages
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def valid?
|
|
26
|
+
validator.errors.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def validator
|
|
30
|
+
@validator ||= which_validator_module.validation(params, allowed_keys)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def which_validator_module
|
|
34
|
+
Kernel.const_get(validator_module)
|
|
35
|
+
rescue NameError => e
|
|
36
|
+
raise Errors::Params::InvalidModule.new(e), e.message
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def validator_messages
|
|
40
|
+
messages = validator.messages(full: true)
|
|
41
|
+
return messages.values.join(",") if messages.is_a?(Hash)
|
|
42
|
+
|
|
43
|
+
messages.join(",")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require "faraday_middleware"
|
|
2
|
+
|
|
3
|
+
module FreckleIO
|
|
4
|
+
module Request
|
|
5
|
+
class MultiplePages
|
|
6
|
+
attr_reader :path
|
|
7
|
+
attr_reader :params
|
|
8
|
+
attr_reader :last_responses
|
|
9
|
+
|
|
10
|
+
def get(path, params: {})
|
|
11
|
+
@path ||= path
|
|
12
|
+
@params ||= default_params.merge(params).compact
|
|
13
|
+
|
|
14
|
+
retrieve_all_pages
|
|
15
|
+
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def retrieve_all_pages
|
|
22
|
+
@last_responses ||= client.get_in_parallel(
|
|
23
|
+
path,
|
|
24
|
+
2,
|
|
25
|
+
total_pages,
|
|
26
|
+
params: params
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
@last_responses.unshift([@first_response])
|
|
30
|
+
@last_responses.flatten!
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def total_pages
|
|
34
|
+
@total_pages ||= first_page.total_pages
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def first_page
|
|
38
|
+
@first_page ||= first_single_page.get(
|
|
39
|
+
path,
|
|
40
|
+
params: params
|
|
41
|
+
)
|
|
42
|
+
@first_response = @first_page.last_response
|
|
43
|
+
|
|
44
|
+
@first_page
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def first_single_page
|
|
48
|
+
@first_single_page ||= SinglePage.new
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def client
|
|
52
|
+
@client ||= Connection.new
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def default_params
|
|
56
|
+
{
|
|
57
|
+
per_page: FreckleIO.configuration.per_page
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require "freckle_io/paginator"
|
|
2
|
+
|
|
3
|
+
module FreckleIO
|
|
4
|
+
module Request
|
|
5
|
+
class SinglePage
|
|
6
|
+
attr_reader :last_response
|
|
7
|
+
attr_reader :raw_links
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@last_response = nil
|
|
11
|
+
@raw_links = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(path, params: {}, request_options: {})
|
|
15
|
+
@last_response = client.get(
|
|
16
|
+
path,
|
|
17
|
+
params: params,
|
|
18
|
+
request_options: request_options
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
@raw_links = @last_response.headers["link"] || []
|
|
22
|
+
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def next
|
|
27
|
+
next? ? get(paginator.next) : nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def next?
|
|
31
|
+
paginator.next
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def prev
|
|
35
|
+
prev? ? get(paginator.prev) : nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def prev?
|
|
39
|
+
paginator.prev
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def last
|
|
43
|
+
last? ? get(paginator.last) : nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def last?
|
|
47
|
+
paginator.last
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def first
|
|
51
|
+
first? ? get(paginator.first) : nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def first?
|
|
55
|
+
paginator.first
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def total_pages
|
|
59
|
+
paginator.total_pages.to_i
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def paginator
|
|
65
|
+
@paginator = Paginator.new(raw_links)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def client
|
|
69
|
+
@client = Connection.new
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module FreckleIO
|
|
2
|
+
module Validator
|
|
3
|
+
module Entry
|
|
4
|
+
# rubocop:disable Metrics/MethodLength,
|
|
5
|
+
# rubocop:disable Metrics/AbcSize,
|
|
6
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
|
7
|
+
# rubocop:disable Metrics/BlockLength,
|
|
8
|
+
def self.validation(params, valid_keys)
|
|
9
|
+
Dry::Validation.Schema do
|
|
10
|
+
configure do
|
|
11
|
+
config.messages_file = File.join(
|
|
12
|
+
__dir__, "validation.yml"
|
|
13
|
+
)
|
|
14
|
+
config.namespace = :entry
|
|
15
|
+
|
|
16
|
+
predicates(RestrictedHash)
|
|
17
|
+
|
|
18
|
+
option :allowed_keys
|
|
19
|
+
|
|
20
|
+
def valid_tag_filter_type
|
|
21
|
+
["and", "combination of"]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
restricted_hash?(allowed_keys) do
|
|
26
|
+
optional(:user_ids).filled :str?
|
|
27
|
+
optional(:description).filled :str?
|
|
28
|
+
optional(:project_ids).filled :str?
|
|
29
|
+
optional(:tag_ids).filled :str?
|
|
30
|
+
optional(:tag_filter_type).filled :str?
|
|
31
|
+
optional(
|
|
32
|
+
:tag_filter_type
|
|
33
|
+
).value(included_in?: valid_tag_filter_type)
|
|
34
|
+
optional(:invoice_ids).filled :str?
|
|
35
|
+
optional(:import_ids).filled :str?
|
|
36
|
+
optional(:from).filled :str?
|
|
37
|
+
optional(:to).filled :str?
|
|
38
|
+
optional(:invoiced).filled :bool?
|
|
39
|
+
optional(:invoiced_at_from).filled :str?
|
|
40
|
+
optional(:invoiced_at_to).filled :str?
|
|
41
|
+
optional(:updated_from).filled :str?
|
|
42
|
+
optional(:updated_to).filled :str?
|
|
43
|
+
optional(:billable).filled :bool?
|
|
44
|
+
optional(:approved_at_from).filled :str?
|
|
45
|
+
optional(:approved_at_to).filled :str?
|
|
46
|
+
optional(:approved_by_ids).filled :str?
|
|
47
|
+
optional(:per_page).filled :int?
|
|
48
|
+
end
|
|
49
|
+
end.with(allowed_keys: valid_keys).call(params)
|
|
50
|
+
end
|
|
51
|
+
# rubocop:enable Metrics/MethodLength,
|
|
52
|
+
# rubocop:enable Metrics/AbcSize,
|
|
53
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
|
54
|
+
# rubocop:enable Metrics/BlockLength,
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module FreckleIO
|
|
2
|
+
module Validator
|
|
3
|
+
module Project
|
|
4
|
+
# rubocop:disable Metrics/MethodLength,
|
|
5
|
+
# rubocop:disable Metrics/AbcSize,
|
|
6
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
|
7
|
+
def self.validation(params, valid_keys)
|
|
8
|
+
Dry::Validation.Schema do
|
|
9
|
+
configure do
|
|
10
|
+
config.messages_file = File.join(
|
|
11
|
+
__dir__, "validation.yml"
|
|
12
|
+
)
|
|
13
|
+
config.namespace = :project
|
|
14
|
+
|
|
15
|
+
predicates(RestrictedHash)
|
|
16
|
+
|
|
17
|
+
option :allowed_keys
|
|
18
|
+
|
|
19
|
+
def valid_increment
|
|
20
|
+
%w(1 5 6 10 15)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def valid_increment?(_options, values)
|
|
24
|
+
values.split(",").all? do |value|
|
|
25
|
+
valid_increment.include?(value)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
restricted_hash?(allowed_keys) do
|
|
31
|
+
optional(:name).filled :str?
|
|
32
|
+
optional(:project_group_ids).filled :str?
|
|
33
|
+
optional(:billing_increment).filled :str?
|
|
34
|
+
optional(:billing_increment).filled(valid_increment?: {})
|
|
35
|
+
optional(:enabled).filled :bool?
|
|
36
|
+
optional(:billable).filled :bool?
|
|
37
|
+
optional(:per_page).filled :int?
|
|
38
|
+
end
|
|
39
|
+
end.with(allowed_keys: valid_keys).call(params)
|
|
40
|
+
end
|
|
41
|
+
# rubocop:enable Metrics/MethodLength,
|
|
42
|
+
# rubocop:enable Metrics/AbcSize,
|
|
43
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module FreckleIO
|
|
2
|
+
module Validator
|
|
3
|
+
module ProjectGroup
|
|
4
|
+
# rubocop:disable Metrics/MethodLength,
|
|
5
|
+
# rubocop:disable Metrics/AbcSize,
|
|
6
|
+
def self.validation(params, valid_keys)
|
|
7
|
+
Dry::Validation.Schema do
|
|
8
|
+
configure do
|
|
9
|
+
config.messages_file = File.join(
|
|
10
|
+
__dir__, "validation.yml"
|
|
11
|
+
)
|
|
12
|
+
config.namespace = :project_group
|
|
13
|
+
|
|
14
|
+
predicates(RestrictedHash)
|
|
15
|
+
|
|
16
|
+
option :allowed_keys
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
restricted_hash?(allowed_keys) do
|
|
20
|
+
optional(:name).filled :str?
|
|
21
|
+
optional(:project_ids).filled :str?
|
|
22
|
+
optional(:per_page).filled :int?
|
|
23
|
+
end
|
|
24
|
+
end.with(allowed_keys: valid_keys).call(params)
|
|
25
|
+
end
|
|
26
|
+
# rubocop:enable Metrics/MethodLength,
|
|
27
|
+
# rubocop:enable Metrics/AbcSize,
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module FreckleIO
|
|
2
|
+
module Validator
|
|
3
|
+
module Tag
|
|
4
|
+
# rubocop:disable Metrics/MethodLength,
|
|
5
|
+
# rubocop:disable Metrics/AbcSize,
|
|
6
|
+
def self.validation(params, valid_keys)
|
|
7
|
+
Dry::Validation.Schema do
|
|
8
|
+
configure do
|
|
9
|
+
config.messages_file = File.join(
|
|
10
|
+
__dir__, "validation.yml"
|
|
11
|
+
)
|
|
12
|
+
config.namespace = :tag
|
|
13
|
+
|
|
14
|
+
predicates(RestrictedHash)
|
|
15
|
+
|
|
16
|
+
option :allowed_keys
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
restricted_hash?(allowed_keys) do
|
|
20
|
+
optional(:name).filled :str?
|
|
21
|
+
optional(:billable).filled :bool?
|
|
22
|
+
optional(:per_page).filled :int?
|
|
23
|
+
end
|
|
24
|
+
end.with(allowed_keys: valid_keys).call(params)
|
|
25
|
+
end
|
|
26
|
+
# rubocop:enable Metrics/MethodLength,
|
|
27
|
+
# rubocop:enable Metrics/AbcSize,
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module FreckleIO
|
|
2
|
+
module Validator
|
|
3
|
+
module User
|
|
4
|
+
# rubocop:disable Metrics/MethodLength,
|
|
5
|
+
# rubocop:disable Metrics/AbcSize,
|
|
6
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
|
7
|
+
def self.validation(params, valid_keys)
|
|
8
|
+
Dry::Validation.Schema do
|
|
9
|
+
configure do
|
|
10
|
+
config.messages_file = File.join(
|
|
11
|
+
__dir__, "validation.yml"
|
|
12
|
+
)
|
|
13
|
+
config.namespace = :user
|
|
14
|
+
|
|
15
|
+
predicates(RestrictedHash)
|
|
16
|
+
|
|
17
|
+
option :allowed_keys
|
|
18
|
+
|
|
19
|
+
def valid_roles
|
|
20
|
+
%w(supervisor leader coworker contractor)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def valid_states
|
|
24
|
+
%w(disabled pending active suspended)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
restricted_hash?(allowed_keys) do
|
|
29
|
+
optional(:name).filled :str?
|
|
30
|
+
optional(:email).filled :str?
|
|
31
|
+
optional(:email).value(format?: /\A[^@\s]+@[^@\s]+\z/)
|
|
32
|
+
optional(:role).value(included_in?: valid_roles)
|
|
33
|
+
optional(:state).value(included_in?: valid_states)
|
|
34
|
+
optional(:per_page).filled :int?
|
|
35
|
+
end
|
|
36
|
+
end.with(allowed_keys: valid_keys).call(params)
|
|
37
|
+
end
|
|
38
|
+
# rubocop:enable Metrics/MethodLength,
|
|
39
|
+
# rubocop:enable Metrics/AbcSize,
|
|
40
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
en:
|
|
2
|
+
errors:
|
|
3
|
+
rules:
|
|
4
|
+
user:
|
|
5
|
+
restricted_hash?: "has unknown keys. Allowed keys are: name, email, role, state and per_page"
|
|
6
|
+
project:
|
|
7
|
+
restricted_hash?: "has unknown keys. Allowed keys are: name, project_group_ids, billing_increment, enabled, billable and per_page"
|
|
8
|
+
valid_increment?: "has invalid values. Allowed values are: 1, 5, 6, 10, 15"
|
|
9
|
+
entry:
|
|
10
|
+
restricted_hash?: "has unknown keys. Allowed keys are: user_ids, description, project_ids, tag_ids, tag_filter_type, invoice_ids, import_ids, from, to, invoiced, invoice_at_from, invoice_at_to, updated_from, updated_to, billable, approved_at_from, approved_at_to, approved_by_ids and per_page"
|
|
11
|
+
tag:
|
|
12
|
+
restricted_hash?: "has unknown keys. Allowed keys are: name, billable and per_page"
|
|
13
|
+
project_group:
|
|
14
|
+
restricted_hash?: "has unknown keys. Allowed keys are: name, project ids and per_page"
|
data/lib/freckle_io.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require "dry-validation"
|
|
2
|
+
|
|
3
|
+
require "freckle_io/configuration"
|
|
4
|
+
require "freckle_io/connection"
|
|
5
|
+
require "freckle_io/params"
|
|
6
|
+
require "freckle_io/errors/configuration"
|
|
7
|
+
require "freckle_io/errors/connection"
|
|
8
|
+
require "freckle_io/errors/params"
|
|
9
|
+
require "freckle_io/request/multiple_pages"
|
|
10
|
+
require "freckle_io/request/single_page"
|
|
11
|
+
require "freckle_io/validator/restricted_hash"
|
|
12
|
+
require "freckle_io/validator/user"
|
|
13
|
+
require "freckle_io/validator/project"
|
|
14
|
+
require "freckle_io/validator/entry"
|
|
15
|
+
require "freckle_io/validator/tag"
|
|
16
|
+
require "freckle_io/validator/project_group"
|
|
17
|
+
require "freckle_io/client/users"
|
|
18
|
+
require "freckle_io/client/projects"
|
|
19
|
+
require "freckle_io/client/entries"
|
|
20
|
+
require "freckle_io/client/tags"
|
|
21
|
+
require "freckle_io/client/project_groups"
|
|
22
|
+
|
|
23
|
+
module FreckleIO
|
|
24
|
+
def self.configuration
|
|
25
|
+
@configuration ||= Configuration.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.reset
|
|
29
|
+
@configuration = Configuration.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.configure
|
|
33
|
+
yield(configuration)
|
|
34
|
+
end
|
|
35
|
+
end
|