camper 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ require 'rubocop/rake_task'
9
+ RuboCop::RakeTask.new(:rubocop) do |task|
10
+ task.options = ['-D', '--parallel']
11
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "camper"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/camper/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'camper'
7
+ spec.version = Camper::VERSION
8
+ spec.authors = ['renehernandez']
9
+
10
+ spec.summary = 'Ruby client for Basecamp 3 API'
11
+ spec.homepage = 'https://github.com/renehernandez/camper'
12
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
13
+ spec.license = 'MIT'
14
+
15
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = spec.homepage
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'httparty', '~> 0.18'
30
+ spec.add_dependency 'rack-oauth2', '~> 1.14'
31
+
32
+ spec.add_development_dependency 'rake', '~> 13.0'
33
+ spec.add_development_dependency 'rspec', '~> 3.9'
34
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'camper'
4
+
5
+ client = Camper.configure do |config|
6
+ config.client_id = ENV['BASECAMP3_CLIENT_ID']
7
+ config.client_secret = ENV['BASECAMP3_CLIENT_SECRET']
8
+ config.account_number = ENV['BASECAMP3_ACCOUNT_NUMBER']
9
+ config.refresh_token = ENV['BASECAMP3_REFRESH_TOKEN']
10
+ config.access_token = ENV['BASECAMP3_ACCESS_TOKEN']
11
+ end
12
+
13
+ projects = client.projects
14
+
15
+ projects.auto_paginate do |p|
16
+ puts "Project: #{p.name}"
17
+
18
+ message_board = client.message_board(p)
19
+ todoset = client.todoset(p)
20
+
21
+ # Message board and Todo set are example resources that doesn't have comments
22
+ puts "Message Board: #{message_board.title}, can be commented on: #{message_board.can_be_commented?}"
23
+ puts "Todo set: #{todoset.title}, can be commented on: #{todoset.can_be_commented?}"
24
+
25
+ # Adds a comment on the first todolist
26
+ list = client.todolists(todoset).first
27
+ puts "Todolist: #{list.title}, can be commented on: #{list.can_be_commented?}"
28
+ client.add_comment(list, 'New <b>comment</b> with <i>HTML support</i>')
29
+ comments = client.comments(list)
30
+ idx = 0
31
+ comments.auto_paginate do |c|
32
+ puts "Comment #{idx} content: #{c.content}"
33
+ idx += 1
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ require 'camper'
2
+
3
+ client = Camper.configure do |config|
4
+ config.client_id = ENV['BASECAMP3_CLIENT_ID']
5
+ config.client_secret = ENV['BASECAMP3_CLIENT_SECRET']
6
+ config.account_number = ENV['BASECAMP3_ACCOUNT_NUMBER']
7
+ config.refresh_token = ENV['BASECAMP3_REFRESH_TOKEN']
8
+ config.access_token = ENV['BASECAMP3_ACCESS_TOKEN']
9
+ end
10
+
11
+ projects = client.projects
12
+
13
+ projects.auto_paginate do |p|
14
+ puts "Project: #{p.name}"
15
+
16
+ message_board = client.message_board(p)
17
+ puts "Message Board: #{message_board.title}"
18
+
19
+ messages = client.messages(message_board)
20
+
21
+ messages.auto_paginate do |msg|
22
+ puts msg.inspect
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ require 'camper'
2
+
3
+ client = Camper.configure do |config|
4
+ config.client_id = ENV['BASECAMP3_CLIENT_ID']
5
+ config.client_secret = ENV['BASECAMP3_CLIENT_SECRET']
6
+ config.redirect_uri = ENV['BASECAMP3_REDIRECT_URI']
7
+ end
8
+
9
+ # Get the authorization uri
10
+ puts client.authorization_uri
11
+
12
+ # Once you have received the auth code from basecamp, you can proceed with the following step
13
+ # This will update the configuration with the corresponding tokens
14
+ # Store the tokens in a safe place
15
+ token = client.authorize! ENV['BASECAMP3_ACCESS_CODE']
16
+
17
+ # Print refresh token
18
+ puts token.refresh_token
19
+
20
+ # Print access token
21
+ puts token.access_token
22
+
@@ -0,0 +1,13 @@
1
+ require 'camper'
2
+
3
+ client = Camper.configure do |config|
4
+ config.client_id = ENV['BASECAMP3_CLIENT_ID']
5
+ config.client_secret = ENV['BASECAMP3_CLIENT_SECRET']
6
+ config.account_number = ENV['BASECAMP3_ACCOUNT_NUMBER']
7
+ config.refresh_token = ENV['BASECAMP3_REFRESH_TOKEN']
8
+ end
9
+
10
+ tokens = client.update_access_token!
11
+
12
+ # Prints the new access token
13
+ puts tokens.access_token
@@ -0,0 +1,27 @@
1
+ require 'camper'
2
+
3
+ client = Camper.configure do |config|
4
+ config.client_id = ENV['BASECAMP3_CLIENT_ID']
5
+ config.client_secret = ENV['BASECAMP3_CLIENT_SECRET']
6
+ config.account_number = ENV['BASECAMP3_ACCOUNT_NUMBER']
7
+ config.refresh_token = ENV['BASECAMP3_REFRESH_TOKEN']
8
+ config.access_token = ENV['BASECAMP3_ACCESS_TOKEN']
9
+ end
10
+
11
+ projects = client.projects
12
+
13
+ projects.auto_paginate do |p|
14
+ puts "Project: #{p.inspect}"
15
+
16
+ puts "Todo set: #{p.todoset.inspect}"
17
+
18
+ todoset = client.todoset(p)
19
+
20
+ client.todolists(todoset).auto_paginate(5) do |list|
21
+ puts "Todolist: #{list.title}"
22
+
23
+ client.todos(list).auto_paginate do |todo|
24
+ puts todo.inspect
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'rack/oauth2'
5
+
6
+ require 'camper/version'
7
+ require 'camper/logging'
8
+ require 'camper/error'
9
+ require 'camper/configuration'
10
+ require 'camper/authorization'
11
+ require 'camper/resource'
12
+ require 'camper/pagination_data'
13
+ require 'camper/paginated_response'
14
+ require 'camper/request'
15
+ require 'camper/client'
16
+
17
+ module Camper
18
+ # Alias for Camper::Client.new
19
+ #
20
+ # @return [Camper::Client]
21
+ def self.client(options = {})
22
+ Camper::Client.new(options)
23
+ end
24
+
25
+ # Delegates to Camper::Client configure method
26
+ #
27
+ # @return [Camper::Client]
28
+ def self.configure(&block)
29
+ client.configure(&block)
30
+ end
31
+
32
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Camper::Client
4
+ module CommentAPI
5
+ def add_comment(resource, content)
6
+ post(resource.comments_url, override_path: true, body: { content: content }.to_json)
7
+ end
8
+
9
+ def comments(resource)
10
+ get(resource.comments_url, override_path: true)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Camper::Client
4
+ module MessageAPI
5
+ def messages(message_board)
6
+ get(message_board.messages_url, override_path: true)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Camper::Client
4
+ module ProjectAPI
5
+
6
+ def projects(options = {})
7
+ get("/projects", options)
8
+ end
9
+
10
+ def message_board(project)
11
+ board = project.message_board
12
+ get(board.url, override_path: true)
13
+ end
14
+
15
+ def todoset(project)
16
+ todoset = project.todoset
17
+ get(todoset.url, override_path: true)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Camper::Client
4
+ module ResourceAPI
5
+
6
+ def resource(url)
7
+ get(url_transform(url), override_path: true)
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Camper::Client
4
+ module TodoAPI
5
+
6
+ def todolists(todoset)
7
+ get(todoset.todolists_url, override_path: true)
8
+ end
9
+
10
+ def todos(todolist, options={})
11
+ get(todolist.todos_url, options.merge(override_path: true))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Camper
4
+ module Authorization
5
+
6
+ def authorization_uri
7
+ client = authz_client
8
+
9
+ client.authorization_uri type: :web_server
10
+ end
11
+
12
+ def authz_client
13
+ Rack::OAuth2::Client.new(
14
+ identifier: @config.client_id,
15
+ secret: @config.client_secret,
16
+ redirect_uri: @config.redirect_uri,
17
+ authorization_endpoint: @config.authz_endpoint,
18
+ token_endpoint: @config.token_endpoint,
19
+ )
20
+ end
21
+
22
+ def authorize!(auth_code)
23
+ client = authz_client
24
+ client.authorization_code = auth_code
25
+
26
+ # Passing secrets as query string
27
+ token = client.access_token!(
28
+ client_auth_method: nil,
29
+ client_id: @config.client_id,
30
+ client_secret: @config.client_secret,
31
+ type: :web_server
32
+ )
33
+
34
+ store_tokens(token)
35
+
36
+ token
37
+ end
38
+
39
+ def update_access_token!
40
+ logger.debug "Update access token using refresh token"
41
+
42
+ client = authz_client
43
+ client.refresh_token = @config.refresh_token
44
+
45
+ token = client.access_token!(
46
+ client_auth_method: nil,
47
+ client_id: @config.client_id,
48
+ client_secret: @config.client_secret,
49
+ type: :refresh
50
+ )
51
+
52
+ store_tokens(token)
53
+
54
+ token
55
+ end
56
+
57
+ private
58
+
59
+ def store_tokens(token)
60
+ @config.access_token = token.access_token
61
+ @config.refresh_token = token.refresh_token
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Camper
4
+ # Wrapper for the Camper REST API.
5
+ class Client
6
+ Dir[File.expand_path('api/*.rb', __dir__)].each { |f| require f }
7
+
8
+ extend Forwardable
9
+
10
+ def_delegators :@config, *(Configuration::VALID_OPTIONS_KEYS)
11
+ def_delegators :@config, :authz_endpoint, :token_endpoint, :api_endpoint, :base_api_endpoint
12
+
13
+ # Keep in alphabetical order
14
+ include Authorization
15
+ include CommentAPI
16
+ include Logging
17
+ include MessageAPI
18
+ include ProjectAPI
19
+ include ResourceAPI
20
+ include TodoAPI
21
+
22
+ # Creates a new API.
23
+ # @raise [Error:MissingCredentials]
24
+ def initialize(options = {})
25
+ @config = Configuration.new(options)
26
+ end
27
+
28
+ %w[get post put delete].each do |method|
29
+ define_method method do |path, options = {}|
30
+ response, result = new_request.send(method, path, options)
31
+ return response unless result == Request::Result::AccessTokenExpired
32
+
33
+ update_access_token!
34
+
35
+ response, = new_request.send(method, path, options)
36
+ response
37
+ end
38
+ end
39
+
40
+ # Allows setting configuration values for this client
41
+ # returns the client instance being configured
42
+ def configure
43
+ yield @config
44
+
45
+ self
46
+ end
47
+
48
+ # Text representation of the client, masking private token.
49
+ #
50
+ # @return [String]
51
+ def inspect
52
+ inspected = super
53
+ inspected.sub! @config.access_token, only_show_last_four_chars(@config.access_token) if @config.access_token
54
+ inspected
55
+ end
56
+
57
+ # Utility method for URL encoding of a string.
58
+ # Copied from https://ruby-doc.org/stdlib-2.7.0/libdoc/erb/rdoc/ERB/Util.html
59
+ #
60
+ # @return [String]
61
+ def url_encode(url)
62
+ url.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/n) { |m| sprintf('%%%02X', m.unpack1('C')) } # rubocop:disable Style/FormatString, Style/FormatStringToken
63
+ end
64
+
65
+ private
66
+
67
+ def new_request
68
+ Request.new(@config.access_token, @config.user_agent, self)
69
+ end
70
+
71
+ def only_show_last_four_chars(token)
72
+ "#{'*' * (token.size - 4)}#{token[-4..-1]}"
73
+ end
74
+
75
+ # Utility method for transforming Basecamp Web URLs into API URIs
76
+ # e.g 'https://3.basecamp.com/1/buckets/2/todos/3' will be
77
+ # converted into 'https://3.basecampapi.com/1/buckets/2/todos/3.json'
78
+ #
79
+ # @return [String]
80
+ def url_transform(url)
81
+ api_uri = url.gsub('3.basecamp.com', '3.basecampapi.com')
82
+ api_uri += '.json' unless url.end_with? '.json'
83
+ api_uri
84
+ end
85
+ end
86
+ end