activeproject 0.3.0 → 0.5.0

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +201 -55
  3. data/lib/active_project/adapters/base.rb +154 -14
  4. data/lib/active_project/adapters/basecamp/comments.rb +34 -0
  5. data/lib/active_project/adapters/basecamp/connection.rb +6 -24
  6. data/lib/active_project/adapters/basecamp/issues.rb +6 -5
  7. data/lib/active_project/adapters/basecamp/webhooks.rb +7 -8
  8. data/lib/active_project/adapters/fizzy/columns.rb +116 -0
  9. data/lib/active_project/adapters/fizzy/comments.rb +129 -0
  10. data/lib/active_project/adapters/fizzy/connection.rb +41 -0
  11. data/lib/active_project/adapters/fizzy/issues.rb +221 -0
  12. data/lib/active_project/adapters/fizzy/projects.rb +105 -0
  13. data/lib/active_project/adapters/fizzy_adapter.rb +151 -0
  14. data/lib/active_project/adapters/github_project/comments.rb +91 -0
  15. data/lib/active_project/adapters/github_project/connection.rb +58 -0
  16. data/lib/active_project/adapters/github_project/helpers.rb +100 -0
  17. data/lib/active_project/adapters/github_project/issues.rb +287 -0
  18. data/lib/active_project/adapters/github_project/projects.rb +139 -0
  19. data/lib/active_project/adapters/github_project/webhooks.rb +168 -0
  20. data/lib/active_project/adapters/github_project.rb +8 -0
  21. data/lib/active_project/adapters/github_project_adapter.rb +65 -0
  22. data/lib/active_project/adapters/github_repo/connection.rb +62 -0
  23. data/lib/active_project/adapters/github_repo/issues.rb +242 -0
  24. data/lib/active_project/adapters/github_repo/projects.rb +116 -0
  25. data/lib/active_project/adapters/github_repo/webhooks.rb +354 -0
  26. data/lib/active_project/adapters/github_repo_adapter.rb +134 -0
  27. data/lib/active_project/adapters/jira/attribute_normalizer.rb +16 -0
  28. data/lib/active_project/adapters/jira/comments.rb +41 -0
  29. data/lib/active_project/adapters/jira/connection.rb +15 -15
  30. data/lib/active_project/adapters/jira/issues.rb +21 -7
  31. data/lib/active_project/adapters/jira/projects.rb +3 -1
  32. data/lib/active_project/adapters/jira/transitions.rb +2 -1
  33. data/lib/active_project/adapters/jira/webhooks.rb +5 -7
  34. data/lib/active_project/adapters/jira_adapter.rb +23 -3
  35. data/lib/active_project/adapters/trello/comments.rb +34 -0
  36. data/lib/active_project/adapters/trello/connection.rb +12 -9
  37. data/lib/active_project/adapters/trello/issues.rb +7 -5
  38. data/lib/active_project/adapters/trello/webhooks.rb +5 -7
  39. data/lib/active_project/adapters/trello_adapter.rb +5 -3
  40. data/lib/active_project/association_proxy.rb +3 -2
  41. data/lib/active_project/configuration.rb +6 -3
  42. data/lib/active_project/configurations/base_adapter_configuration.rb +102 -0
  43. data/lib/active_project/configurations/basecamp_configuration.rb +42 -0
  44. data/lib/active_project/configurations/fizzy_configuration.rb +47 -0
  45. data/lib/active_project/configurations/github_configuration.rb +57 -0
  46. data/lib/active_project/configurations/jira_configuration.rb +54 -0
  47. data/lib/active_project/configurations/trello_configuration.rb +24 -2
  48. data/lib/active_project/connections/base.rb +35 -0
  49. data/lib/active_project/connections/graph_ql.rb +83 -0
  50. data/lib/active_project/connections/http_client.rb +79 -0
  51. data/lib/active_project/connections/pagination.rb +44 -0
  52. data/lib/active_project/connections/rest.rb +33 -0
  53. data/lib/active_project/error_mapper.rb +38 -0
  54. data/lib/active_project/errors.rb +13 -0
  55. data/lib/active_project/railtie.rb +1 -3
  56. data/lib/active_project/resources/base_resource.rb +13 -14
  57. data/lib/active_project/resources/comment.rb +46 -2
  58. data/lib/active_project/resources/issue.rb +106 -18
  59. data/lib/active_project/resources/persistable_resource.rb +47 -0
  60. data/lib/active_project/resources/project.rb +1 -1
  61. data/lib/active_project/status_mapper.rb +145 -0
  62. data/lib/active_project/version.rb +1 -1
  63. data/lib/active_project/webhook_event.rb +34 -12
  64. data/lib/activeproject.rb +9 -6
  65. metadata +74 -16
  66. data/lib/active_project/adapters/http_client.rb +0 -71
  67. data/lib/active_project/adapters/pagination.rb +0 -68
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "faraday"
4
- require "faraday/retry"
5
- require "json"
6
-
7
- module ActiveProject
8
- module Adapters
9
- module HttpClient
10
- include Pagination
11
- DEFAULT_HEADERS = {
12
- "Content-Type" => "application/json",
13
- "Accept" => "application/json",
14
- "User-Agent" => -> { ActiveProject.user_agent }
15
- }.freeze
16
- RETRY_OPTS = { max: 5, interval: 0.5, backoff_factor: 2 }.freeze
17
-
18
- def build_connection(base_url:, auth_middleware:, extra_headers: {})
19
- @connection = Faraday.new(url: base_url) do |conn|
20
- auth_middleware.call(conn) # <-- adapter-specific
21
- conn.request :retry, **RETRY_OPTS
22
- conn.response :raise_error
23
- default_adapter = ENV.fetch("AP_DEFAULT_ADAPTER", "net_http").to_sym
24
- conn.adapter default_adapter
25
- conn.headers.merge!(DEFAULT_HEADERS.transform_values { |v| v.respond_to?(:call) ? v.call : v })
26
- conn.headers.merge!(extra_headers)
27
- yield conn if block_given? # optional extra tweaks
28
- end
29
- end
30
-
31
- def request(method, path, body: nil, query: nil, headers: {})
32
- raise "HTTP connection not initialised" unless @connection
33
-
34
- json_body = body.is_a?(String) ? body : (body ? JSON.generate(body) : nil)
35
- response = @connection.run_request(method, path, json_body, headers) do |req|
36
- req.params.update(query) if query&.any?
37
- end
38
-
39
- return nil if response.status == 204 || response.body.to_s.empty?
40
- JSON.parse(response.body)
41
- rescue Faraday::Error => e
42
- raise translate_faraday_error(e)
43
- rescue JSON::ParserError => e
44
- raise ActiveProject::ApiError.new("Non-JSON response from #{path}",
45
- original_error: e,
46
- status_code: response&.status,
47
- response_body: response&.body)
48
- end
49
-
50
- private
51
-
52
- def translate_faraday_error(err)
53
- status = err.response_status
54
- body = err.response_body.to_s
55
- msg = begin JSON.parse(body)["message"] rescue body end
56
-
57
- case status
58
- when 401, 403 then ActiveProject::AuthenticationError.new(msg)
59
- when 404 then ActiveProject::NotFoundError.new(msg)
60
- when 429 then ActiveProject::RateLimitError.new(msg)
61
- when 400, 422 then ActiveProject::ValidationError.new(msg, status_code: status, response_body: body)
62
- else
63
- ActiveProject::ApiError.new("HTTP #{status || 'N/A'}: #{msg}",
64
- original_error: err,
65
- status_code: status,
66
- response_body: body)
67
- end
68
- end
69
- end
70
- end
71
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveProject
4
- module Adapters
5
- module Pagination
6
- # RFC 5988 Link header parsing. Returns { "next" => url, "prev" => url, … }
7
- def parse_link_header(link_header)
8
- return {} unless link_header
9
-
10
- link_header.split(",").each_with_object({}) do |part, acc|
11
- url, rel = part.split(";")
12
- next unless rel && url
13
-
14
- url = url[/\<([^>]+)\>/, 1] # strip angle brackets
15
- rel = rel[/rel=\"?([^\";]+)\"?/, 1]
16
- acc[rel] = url if url && rel
17
- end
18
- end
19
-
20
- # Synchronous enumerator – works with any Faraday adapter.
21
- #
22
- # @param path [String] first request path or full URL
23
- # @param method [Symbol] :get or :post
24
- # @param body [Hash,String,nil]
25
- # @param query [Hash]
26
- def each_page(path, method: :get, body: nil, query: {})
27
- next_path = path
28
- loop do
29
- data, link_header = perform_page_request(method, next_path, body, query)
30
- yield data
31
- next_path = parse_link_header(link_header)["next"]
32
- break unless next_path
33
- # After the first hop the URL is absolute; zero-out body/query for GETs
34
- body = nil if method == :get
35
- query = {}
36
- end
37
- end
38
-
39
- # Fibre-friendly variant; launches a child task for every hop _after_ the
40
- # first one, so JSON parsing in the current page overlaps the download of
41
- # the next page (requires AP_DEFAULT_ADAPTER=async_http + Async scheduler).
42
- def each_page_async(path, method: :get, body: nil, query: {}, &block)
43
- require "async" # soft-require so callers don’t need Async unless used
44
- Async do |task|
45
- current_path = path
46
- while current_path
47
- data, link = perform_page_request(method, current_path, body, query)
48
- next_url = parse_link_header(link)["next"]
49
- body = nil if method == :get # as above
50
- query = {}
51
- # Prefetch next page while the caller consumes this one
52
- fut = next_url ? task.async { perform_page_request(method, next_url, body, query) } : nil
53
- yield data
54
- current_path, data, link = next_url, *fut&.wait
55
- end
56
- end
57
- end
58
-
59
- private
60
-
61
- # One tiny helper so the logic is not duplicated.
62
- def perform_page_request(method, path, body, query)
63
- json = request(method, path, body: body, query: query)
64
- [ json, @last_response.headers["Link"] ]
65
- end
66
- end
67
- end
68
- end