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.
- checksums.yaml +4 -4
- data/README.md +201 -55
- data/lib/active_project/adapters/base.rb +154 -14
- data/lib/active_project/adapters/basecamp/comments.rb +34 -0
- data/lib/active_project/adapters/basecamp/connection.rb +6 -24
- data/lib/active_project/adapters/basecamp/issues.rb +6 -5
- data/lib/active_project/adapters/basecamp/webhooks.rb +7 -8
- data/lib/active_project/adapters/fizzy/columns.rb +116 -0
- data/lib/active_project/adapters/fizzy/comments.rb +129 -0
- data/lib/active_project/adapters/fizzy/connection.rb +41 -0
- data/lib/active_project/adapters/fizzy/issues.rb +221 -0
- data/lib/active_project/adapters/fizzy/projects.rb +105 -0
- data/lib/active_project/adapters/fizzy_adapter.rb +151 -0
- data/lib/active_project/adapters/github_project/comments.rb +91 -0
- data/lib/active_project/adapters/github_project/connection.rb +58 -0
- data/lib/active_project/adapters/github_project/helpers.rb +100 -0
- data/lib/active_project/adapters/github_project/issues.rb +287 -0
- data/lib/active_project/adapters/github_project/projects.rb +139 -0
- data/lib/active_project/adapters/github_project/webhooks.rb +168 -0
- data/lib/active_project/adapters/github_project.rb +8 -0
- data/lib/active_project/adapters/github_project_adapter.rb +65 -0
- data/lib/active_project/adapters/github_repo/connection.rb +62 -0
- data/lib/active_project/adapters/github_repo/issues.rb +242 -0
- data/lib/active_project/adapters/github_repo/projects.rb +116 -0
- data/lib/active_project/adapters/github_repo/webhooks.rb +354 -0
- data/lib/active_project/adapters/github_repo_adapter.rb +134 -0
- data/lib/active_project/adapters/jira/attribute_normalizer.rb +16 -0
- data/lib/active_project/adapters/jira/comments.rb +41 -0
- data/lib/active_project/adapters/jira/connection.rb +15 -15
- data/lib/active_project/adapters/jira/issues.rb +21 -7
- data/lib/active_project/adapters/jira/projects.rb +3 -1
- data/lib/active_project/adapters/jira/transitions.rb +2 -1
- data/lib/active_project/adapters/jira/webhooks.rb +5 -7
- data/lib/active_project/adapters/jira_adapter.rb +23 -3
- data/lib/active_project/adapters/trello/comments.rb +34 -0
- data/lib/active_project/adapters/trello/connection.rb +12 -9
- data/lib/active_project/adapters/trello/issues.rb +7 -5
- data/lib/active_project/adapters/trello/webhooks.rb +5 -7
- data/lib/active_project/adapters/trello_adapter.rb +5 -3
- data/lib/active_project/association_proxy.rb +3 -2
- data/lib/active_project/configuration.rb +6 -3
- data/lib/active_project/configurations/base_adapter_configuration.rb +102 -0
- data/lib/active_project/configurations/basecamp_configuration.rb +42 -0
- data/lib/active_project/configurations/fizzy_configuration.rb +47 -0
- data/lib/active_project/configurations/github_configuration.rb +57 -0
- data/lib/active_project/configurations/jira_configuration.rb +54 -0
- data/lib/active_project/configurations/trello_configuration.rb +24 -2
- data/lib/active_project/connections/base.rb +35 -0
- data/lib/active_project/connections/graph_ql.rb +83 -0
- data/lib/active_project/connections/http_client.rb +79 -0
- data/lib/active_project/connections/pagination.rb +44 -0
- data/lib/active_project/connections/rest.rb +33 -0
- data/lib/active_project/error_mapper.rb +38 -0
- data/lib/active_project/errors.rb +13 -0
- data/lib/active_project/railtie.rb +1 -3
- data/lib/active_project/resources/base_resource.rb +13 -14
- data/lib/active_project/resources/comment.rb +46 -2
- data/lib/active_project/resources/issue.rb +106 -18
- data/lib/active_project/resources/persistable_resource.rb +47 -0
- data/lib/active_project/resources/project.rb +1 -1
- data/lib/active_project/status_mapper.rb +145 -0
- data/lib/active_project/version.rb +1 -1
- data/lib/active_project/webhook_event.rb +34 -12
- data/lib/activeproject.rb +9 -6
- metadata +74 -16
- data/lib/active_project/adapters/http_client.rb +0 -71
- data/lib/active_project/adapters/pagination.rb +0 -68
|
@@ -2,34 +2,122 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveProject
|
|
4
4
|
module Resources
|
|
5
|
-
#
|
|
6
|
-
class Issue <
|
|
5
|
+
# Whether it's a Jira ticket, Trello card, GitHub Issue, or Basecamp Todo
|
|
6
|
+
class Issue < PersistableResource
|
|
7
7
|
def_members :id, :key, :title, :description, :status, :assignees,
|
|
8
8
|
:reporter, :project_id, :created_at, :updated_at, :due_on,
|
|
9
9
|
:priority, :adapter_source
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
def initialize(adapter, attributes = {})
|
|
12
|
+
super
|
|
13
|
+
@initial_title = attributes[:title]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Persist the record, creating it if it does not yet exist.
|
|
14
17
|
def save
|
|
15
|
-
|
|
18
|
+
unless project_id
|
|
19
|
+
raise ActiveProject::NotImplementedError,
|
|
20
|
+
"#save not supported on transient records"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
attrs = to_h.slice(:title, :description, :status, :assignees,
|
|
24
|
+
:reporter, :due_on, :priority)
|
|
25
|
+
|
|
26
|
+
if @adapter.is_a?(ActiveProject::Adapters::JiraAdapter)
|
|
27
|
+
if id.nil? # first persist ⇒ create_issue
|
|
28
|
+
attrs.delete(:title) # remove :title entirely
|
|
29
|
+
attrs[:summary] = @initial_title # use the ORIGINAL title
|
|
30
|
+
elsif attrs.key?(:title) # later saves ⇒ update_issue
|
|
31
|
+
attrs[:summary] = attrs.delete(:title)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attrs.delete(:status) unless @adapter.status_known?(project_id, attrs[:status])
|
|
36
|
+
|
|
37
|
+
fresh =
|
|
38
|
+
if id.nil?
|
|
39
|
+
adapter.create_issue(project_id, attrs)
|
|
40
|
+
else
|
|
41
|
+
adapter_update_issue(id, attrs)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
copy_from(fresh)
|
|
45
|
+
true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Update attributes and persist them.
|
|
49
|
+
def update(attributes = {})
|
|
50
|
+
unless project_id && id
|
|
51
|
+
raise ActiveProject::NotImplementedError,
|
|
52
|
+
"#update not supported on transient records"
|
|
53
|
+
end
|
|
54
|
+
unless attributes.is_a?(Hash)
|
|
55
|
+
raise ActiveProject::NotImplementedError,
|
|
56
|
+
"attributes must be a Hash"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
ident = key || id
|
|
60
|
+
adapter_update_issue(ident, attributes)
|
|
61
|
+
copy_from(adapter_find_issue(ident))
|
|
62
|
+
true
|
|
16
63
|
end
|
|
17
64
|
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
raise NotImplementedError, "#update not yet implemented for #{self.class.name}"
|
|
65
|
+
# Delete remote record.
|
|
66
|
+
def delete
|
|
67
|
+
raise "project_id missing – can't destroy" unless project_id
|
|
68
|
+
raise "id missing – record not persisted" if id.nil?
|
|
69
|
+
|
|
70
|
+
adapter_delete_issue(id)
|
|
71
|
+
freeze
|
|
72
|
+
true
|
|
27
73
|
end
|
|
74
|
+
alias destroy delete
|
|
28
75
|
|
|
29
|
-
#
|
|
30
|
-
# @return [AssociationProxy<Resources::Comment>]
|
|
76
|
+
# Lazy association proxy for comments.
|
|
31
77
|
def comments
|
|
32
|
-
AssociationProxy.new(owner: self, adapter:
|
|
78
|
+
AssociationProxy.new(owner: self, adapter: adapter, association_name: :comments)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def copy_from(other)
|
|
84
|
+
self.class.members.each { |m| public_send("#{m}=", other.public_send(m)) }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Adapter-aware update_issue call that handles different signatures
|
|
88
|
+
def adapter_update_issue(issue_id, attrs)
|
|
89
|
+
case @adapter
|
|
90
|
+
when ActiveProject::Adapters::BasecampAdapter
|
|
91
|
+
@adapter.update_issue(issue_id, attrs, { project_id: project_id })
|
|
92
|
+
when ActiveProject::Adapters::GithubProjectAdapter
|
|
93
|
+
@adapter.update_issue(project_id, issue_id, attrs)
|
|
94
|
+
else
|
|
95
|
+
# Jira, Trello, GithubRepo: (id, attrs, context)
|
|
96
|
+
@adapter.update_issue(issue_id, attrs, {})
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Adapter-aware find_issue call that handles different signatures
|
|
101
|
+
def adapter_find_issue(issue_id)
|
|
102
|
+
case @adapter
|
|
103
|
+
when ActiveProject::Adapters::BasecampAdapter
|
|
104
|
+
@adapter.find_issue(issue_id, { project_id: project_id })
|
|
105
|
+
else
|
|
106
|
+
@adapter.find_issue(issue_id, {})
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Adapter-aware delete_issue call that handles different signatures
|
|
111
|
+
def adapter_delete_issue(issue_id)
|
|
112
|
+
case @adapter
|
|
113
|
+
when ActiveProject::Adapters::BasecampAdapter
|
|
114
|
+
@adapter.delete_issue(issue_id, { project_id: project_id })
|
|
115
|
+
when ActiveProject::Adapters::GithubProjectAdapter
|
|
116
|
+
@adapter.delete_issue(project_id, issue_id)
|
|
117
|
+
else
|
|
118
|
+
# Jira, Trello, GithubRepo: (id, context)
|
|
119
|
+
@adapter.delete_issue(issue_id, {})
|
|
120
|
+
end
|
|
33
121
|
end
|
|
34
122
|
end
|
|
35
123
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveProject
|
|
4
|
+
module Resources
|
|
5
|
+
class PersistableResource < BaseResource
|
|
6
|
+
# Indicates if the resource has been persisted (typically by checking for an ID)
|
|
7
|
+
def persisted?
|
|
8
|
+
!id.nil? # Assumes an 'id' member
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# These are now expected to be implemented by concrete subclasses
|
|
12
|
+
# like Issue and Comment, or this class could provide a template
|
|
13
|
+
# that calls conventionally named adapter methods.
|
|
14
|
+
def save
|
|
15
|
+
raise NotImplementedError, "#{self.class.name} must implement #save"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def update(attributes)
|
|
19
|
+
raise NotImplementedError, "#{self.class.name} must implement #update"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def delete
|
|
23
|
+
raise NotImplementedError, "#{self.class.name} must implement #delete"
|
|
24
|
+
end
|
|
25
|
+
alias destroy delete
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
# Common logic for copying attributes after an API call
|
|
30
|
+
def copy_from(other_resource)
|
|
31
|
+
# Ensure it's the same type of resource before copying
|
|
32
|
+
return unless other_resource.is_a?(self.class)
|
|
33
|
+
|
|
34
|
+
self.class.members.each do |member_name|
|
|
35
|
+
setter_method = "#{member_name}="
|
|
36
|
+
# Check if both the current resource and the other resource can handle this member
|
|
37
|
+
if respond_to?(setter_method) && other_resource.respond_to?(member_name)
|
|
38
|
+
public_send(setter_method, other_resource.public_send(member_name))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
# Optionally, update raw_data as well if it's part of the contract
|
|
42
|
+
# @raw_data = other_resource.raw_data if other_resource.respond_to?(:raw_data)
|
|
43
|
+
self # Return self for chaining or assignment
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module ActiveProject
|
|
4
4
|
module Resources
|
|
5
5
|
# Represents a Project (e.g., Jira Project, Trello Board, Basecamp Project)
|
|
6
|
-
class Project <
|
|
6
|
+
class Project < PersistableResource
|
|
7
7
|
def_members :id, :key, :name, :adapter_source
|
|
8
8
|
# raw_data and adapter are inherited from BaseResource
|
|
9
9
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveProject
|
|
4
|
+
# Handles bidirectional status mapping between platform-specific statuses
|
|
5
|
+
# and normalized ActiveProject status symbols.
|
|
6
|
+
#
|
|
7
|
+
# Supports:
|
|
8
|
+
# - Standard status symbols (:open, :in_progress, :closed, :blocked, :on_hold)
|
|
9
|
+
# - Platform-specific status preservation
|
|
10
|
+
# - Configurable status mappings per adapter
|
|
11
|
+
# - Fallback to standard status normalization
|
|
12
|
+
class StatusMapper
|
|
13
|
+
# Standard ActiveProject status symbols with their meanings
|
|
14
|
+
STANDARD_STATUSES = {
|
|
15
|
+
open: "New, unstarted work",
|
|
16
|
+
in_progress: "Currently being worked on",
|
|
17
|
+
blocked: "Waiting on external dependency",
|
|
18
|
+
on_hold: "Temporarily paused",
|
|
19
|
+
closed: "Completed or resolved"
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
# Default mapping rules for common status patterns
|
|
23
|
+
DEFAULT_MAPPINGS = {
|
|
24
|
+
# Open/New statuses
|
|
25
|
+
/^(new|open|todo|to do|backlog|ready|created)$/i => :open,
|
|
26
|
+
|
|
27
|
+
# In Progress statuses
|
|
28
|
+
/^(in progress|in_progress|active|working|started|doing)$/i => :in_progress,
|
|
29
|
+
|
|
30
|
+
# Blocked statuses
|
|
31
|
+
/^(blocked|waiting|pending|on hold|on_hold|paused)$/i => :blocked,
|
|
32
|
+
|
|
33
|
+
# Closed statuses
|
|
34
|
+
/^(done|closed|completed|finished|resolved|fixed)$/i => :closed
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
attr_reader :adapter_type, :custom_mappings
|
|
38
|
+
|
|
39
|
+
# @param adapter_type [Symbol] The adapter type (:jira, :trello, etc.)
|
|
40
|
+
# @param custom_mappings [Hash] Custom status mappings from configuration
|
|
41
|
+
def initialize(adapter_type, custom_mappings = {})
|
|
42
|
+
@adapter_type = adapter_type
|
|
43
|
+
@custom_mappings = custom_mappings || {}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Converts a platform-specific status to a normalized status symbol.
|
|
47
|
+
# @param platform_status [String, Symbol] The platform-specific status
|
|
48
|
+
# @param context [Hash] Optional context (e.g., project_id for project-specific mappings)
|
|
49
|
+
# @return [Symbol] Normalized status symbol
|
|
50
|
+
def normalize_status(platform_status, context = {})
|
|
51
|
+
return platform_status if STANDARD_STATUSES.key?(platform_status.to_sym)
|
|
52
|
+
|
|
53
|
+
# Try custom mappings first
|
|
54
|
+
if custom_mappings.is_a?(Hash)
|
|
55
|
+
# Support project-specific mappings (for Trello, GitHub)
|
|
56
|
+
project_mappings = custom_mappings[context[:project_id]] || custom_mappings
|
|
57
|
+
|
|
58
|
+
# Check direct mapping
|
|
59
|
+
if project_mappings[platform_status.to_s]
|
|
60
|
+
return project_mappings[platform_status.to_s].to_sym
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Fall back to pattern matching
|
|
65
|
+
status_str = platform_status.to_s.strip
|
|
66
|
+
DEFAULT_MAPPINGS.each do |pattern, normalized_status|
|
|
67
|
+
return normalized_status if status_str.match?(pattern)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# If no mapping found, return as symbol for platform-specific handling
|
|
71
|
+
platform_status.to_s.downcase.tr(" -", "_").to_sym
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Converts a normalized status symbol back to platform-specific status.
|
|
75
|
+
# @param normalized_status [Symbol] The normalized status symbol
|
|
76
|
+
# @param context [Hash] Optional context for platform-specific conversion
|
|
77
|
+
# @return [String, Symbol] Platform-specific status representation
|
|
78
|
+
def denormalize_status(normalized_status, context = {})
|
|
79
|
+
# If it's already a standard status, delegate to adapter-specific logic
|
|
80
|
+
return normalized_status if STANDARD_STATUSES.key?(normalized_status.to_sym)
|
|
81
|
+
|
|
82
|
+
# Try reverse lookup in custom mappings
|
|
83
|
+
if custom_mappings.is_a?(Hash)
|
|
84
|
+
project_mappings = custom_mappings[context[:project_id]] || custom_mappings
|
|
85
|
+
|
|
86
|
+
# Find the platform status that maps to this normalized status
|
|
87
|
+
project_mappings.each do |platform_status, mapped_status|
|
|
88
|
+
return platform_status if mapped_status.to_sym == normalized_status.to_sym
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Default: return the status as-is for platform handling
|
|
93
|
+
normalized_status
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Checks if a status is known/valid for the given context.
|
|
97
|
+
# @param status [Symbol, String] The status to check
|
|
98
|
+
# @param context [Hash] Optional context for validation
|
|
99
|
+
# @return [Boolean] true if the status is valid
|
|
100
|
+
def status_known?(status, context = {})
|
|
101
|
+
# Handle nil status
|
|
102
|
+
return false if status.nil?
|
|
103
|
+
|
|
104
|
+
# Standard statuses are always known
|
|
105
|
+
return true if STANDARD_STATUSES.key?(status.to_sym)
|
|
106
|
+
|
|
107
|
+
# Check custom mappings
|
|
108
|
+
if custom_mappings.is_a?(Hash)
|
|
109
|
+
project_mappings = custom_mappings[context[:project_id]] || custom_mappings
|
|
110
|
+
return true if project_mappings.key?(status.to_s)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Check if it matches any default patterns
|
|
114
|
+
status_str = status.to_s.strip
|
|
115
|
+
DEFAULT_MAPPINGS.any? { |pattern, _| status_str.match?(pattern) }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Returns all valid statuses for the given context.
|
|
119
|
+
# @param context [Hash] Optional context
|
|
120
|
+
# @return [Array<Symbol>] Array of valid status symbols
|
|
121
|
+
def valid_statuses(context = {})
|
|
122
|
+
statuses = STANDARD_STATUSES.keys.dup
|
|
123
|
+
|
|
124
|
+
# Add custom mapped statuses
|
|
125
|
+
if custom_mappings.is_a?(Hash)
|
|
126
|
+
project_mappings = custom_mappings[context[:project_id]] || custom_mappings
|
|
127
|
+
project_mappings.each do |platform_status, normalized_status|
|
|
128
|
+
statuses << normalized_status.to_sym
|
|
129
|
+
statuses << platform_status.to_s.downcase.tr(" -", "_").to_sym
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
statuses.uniq
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Creates a status mapper instance from adapter configuration.
|
|
137
|
+
# @param adapter_type [Symbol] The adapter type
|
|
138
|
+
# @param config [ActiveProject::Configurations::BaseAdapterConfiguration] Adapter config
|
|
139
|
+
# @return [StatusMapper] Configured status mapper
|
|
140
|
+
def self.from_config(adapter_type, config)
|
|
141
|
+
status_mappings = config.respond_to?(:status_mappings) ? config.status_mappings : {}
|
|
142
|
+
new(adapter_type, status_mappings)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -4,17 +4,39 @@ module ActiveProject
|
|
|
4
4
|
# Represents a standardized event parsed from a webhook payload.
|
|
5
5
|
# Using Struct for simplicity for now. Could be a full class inheriting BaseResource if needed.
|
|
6
6
|
WebhookEvent = Struct.new(
|
|
7
|
-
:
|
|
8
|
-
:
|
|
9
|
-
:
|
|
10
|
-
:
|
|
11
|
-
:
|
|
12
|
-
:
|
|
13
|
-
:
|
|
14
|
-
:
|
|
15
|
-
:
|
|
16
|
-
:
|
|
17
|
-
:raw_data, # The original, parsed webhook payload hash
|
|
7
|
+
:source, # Symbol representing the source platform (e.g., :github, :jira, :trello)
|
|
8
|
+
:webhook_type, # Symbol representing the adapter type (e.g., :github_repo, :github_project, :jira, :basecamp)
|
|
9
|
+
:type, # Symbol representing the event type (e.g., :issue_created, :comment_added)
|
|
10
|
+
:resource_type, # Symbol representing the type of resource involved (e.g., :issue, :comment)
|
|
11
|
+
:resource_id, # String ID of the primary resource
|
|
12
|
+
:project_id, # String ID of the associated project/repository
|
|
13
|
+
:actor, # User resource representing the user who triggered the event (optional)
|
|
14
|
+
:timestamp, # Time object representing when the event occurred (optional)
|
|
15
|
+
:data, # Hash containing event-specific data (e.g., issue, comment, changes)
|
|
16
|
+
:raw_data, # The original, parsed webhook payload hash (optional)
|
|
18
17
|
keyword_init: true
|
|
19
|
-
)
|
|
18
|
+
) do
|
|
19
|
+
# For backward compatibility
|
|
20
|
+
alias_method :event_type, :type
|
|
21
|
+
alias_method :object_kind, :resource_type
|
|
22
|
+
alias_method :event_object_id, :resource_id
|
|
23
|
+
alias_method :adapter_source, :source
|
|
24
|
+
alias_method :object_data, :data
|
|
25
|
+
|
|
26
|
+
# Additional aliases that might be expected
|
|
27
|
+
def object_key
|
|
28
|
+
data&.dig(:object_key) || data&.dig("object_key")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def changes
|
|
32
|
+
changes_data = data&.dig(:changes) || data&.dig("changes")
|
|
33
|
+
changes_data.nil? || changes_data.empty? ? nil : changes_data
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Helper method to get the resource object
|
|
37
|
+
def resource
|
|
38
|
+
return data[resource_type] if data && data.key?(resource_type)
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
20
42
|
end
|
data/lib/activeproject.rb
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "zeitwerk"
|
|
2
4
|
require "concurrent"
|
|
3
5
|
require_relative "active_project/errors"
|
|
4
6
|
require_relative "active_project/version"
|
|
5
7
|
require_relative "active_project/railtie" if defined?(Rails::Railtie)
|
|
8
|
+
require "active_support/concern"
|
|
9
|
+
require "active_support/core_ext/class/attribute"
|
|
6
10
|
|
|
7
11
|
module ActiveProject
|
|
8
12
|
class << self
|
|
@@ -65,19 +69,18 @@ module ActiveProject
|
|
|
65
69
|
raise ArgumentError, error_message
|
|
66
70
|
end
|
|
67
71
|
|
|
68
|
-
adapter_class_name = "ActiveProject::Adapters::#{adapter_type.to_s.
|
|
72
|
+
adapter_class_name = "ActiveProject::Adapters::#{adapter_type.to_s.classify}Adapter"
|
|
69
73
|
|
|
70
74
|
begin
|
|
71
75
|
require "active_project/adapters/#{adapter_type}_adapter"
|
|
72
|
-
rescue LoadError
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
raise LoadError, error_message
|
|
76
|
+
rescue LoadError
|
|
77
|
+
raise LoadError, "Could not load adapter '#{adapter_type}'. " \
|
|
78
|
+
"Expected class #{adapter_class_name} in adapters/#{adapter_type}_adapter.rb"
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
begin
|
|
79
82
|
adapter_class = Object.const_get(adapter_class_name)
|
|
80
|
-
rescue NameError
|
|
83
|
+
rescue NameError
|
|
81
84
|
error_message = "Could not find adapter class #{adapter_class_name}.\n"
|
|
82
85
|
error_message += "Make sure you have defined the class correctly in active_project/adapters/#{adapter_type}_adapter.rb"
|
|
83
86
|
raise NameError, error_message
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeproject
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -30,49 +30,77 @@ dependencies:
|
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
31
|
version: '9.0'
|
|
32
32
|
- !ruby/object:Gem::Dependency
|
|
33
|
-
name:
|
|
33
|
+
name: async
|
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
|
35
35
|
requirements:
|
|
36
36
|
- - ">="
|
|
37
37
|
- !ruby/object:Gem::Version
|
|
38
|
-
version: '2.
|
|
38
|
+
version: '2.35'
|
|
39
39
|
type: :runtime
|
|
40
40
|
prerelease: false
|
|
41
41
|
version_requirements: !ruby/object:Gem::Requirement
|
|
42
42
|
requirements:
|
|
43
43
|
- - ">="
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: '2.
|
|
45
|
+
version: '2.35'
|
|
46
46
|
- !ruby/object:Gem::Dependency
|
|
47
|
-
name:
|
|
47
|
+
name: async-http
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
|
49
49
|
requirements:
|
|
50
50
|
- - ">="
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
|
-
version: '0'
|
|
52
|
+
version: '0.92'
|
|
53
53
|
type: :runtime
|
|
54
54
|
prerelease: false
|
|
55
55
|
version_requirements: !ruby/object:Gem::Requirement
|
|
56
56
|
requirements:
|
|
57
57
|
- - ">="
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: '0'
|
|
59
|
+
version: '0.92'
|
|
60
60
|
- !ruby/object:Gem::Dependency
|
|
61
|
-
name: async
|
|
61
|
+
name: async-http-faraday
|
|
62
62
|
requirement: !ruby/object:Gem::Requirement
|
|
63
63
|
requirements:
|
|
64
64
|
- - ">="
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '0'
|
|
66
|
+
version: '0.22'
|
|
67
67
|
type: :runtime
|
|
68
68
|
prerelease: false
|
|
69
69
|
version_requirements: !ruby/object:Gem::Requirement
|
|
70
70
|
requirements:
|
|
71
71
|
- - ">="
|
|
72
72
|
- !ruby/object:Gem::Version
|
|
73
|
-
version: '0'
|
|
73
|
+
version: '0.22'
|
|
74
74
|
- !ruby/object:Gem::Dependency
|
|
75
|
-
name:
|
|
75
|
+
name: concurrent-ruby
|
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '1.2'
|
|
81
|
+
type: :runtime
|
|
82
|
+
prerelease: false
|
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '1.2'
|
|
88
|
+
- !ruby/object:Gem::Dependency
|
|
89
|
+
name: faraday
|
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '2.0'
|
|
95
|
+
type: :runtime
|
|
96
|
+
prerelease: false
|
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - ">="
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '2.0'
|
|
102
|
+
- !ruby/object:Gem::Dependency
|
|
103
|
+
name: faraday-retry
|
|
76
104
|
requirement: !ruby/object:Gem::Requirement
|
|
77
105
|
requirements:
|
|
78
106
|
- - ">="
|
|
@@ -86,13 +114,13 @@ dependencies:
|
|
|
86
114
|
- !ruby/object:Gem::Version
|
|
87
115
|
version: '0'
|
|
88
116
|
- !ruby/object:Gem::Dependency
|
|
89
|
-
name: async-
|
|
117
|
+
name: async-safe
|
|
90
118
|
requirement: !ruby/object:Gem::Requirement
|
|
91
119
|
requirements:
|
|
92
120
|
- - ">="
|
|
93
121
|
- !ruby/object:Gem::Version
|
|
94
122
|
version: '0'
|
|
95
|
-
type: :
|
|
123
|
+
type: :development
|
|
96
124
|
prerelease: false
|
|
97
125
|
version_requirements: !ruby/object:Gem::Requirement
|
|
98
126
|
requirements:
|
|
@@ -133,7 +161,26 @@ files:
|
|
|
133
161
|
- lib/active_project/adapters/basecamp/projects.rb
|
|
134
162
|
- lib/active_project/adapters/basecamp/webhooks.rb
|
|
135
163
|
- lib/active_project/adapters/basecamp_adapter.rb
|
|
136
|
-
- lib/active_project/adapters/
|
|
164
|
+
- lib/active_project/adapters/fizzy/columns.rb
|
|
165
|
+
- lib/active_project/adapters/fizzy/comments.rb
|
|
166
|
+
- lib/active_project/adapters/fizzy/connection.rb
|
|
167
|
+
- lib/active_project/adapters/fizzy/issues.rb
|
|
168
|
+
- lib/active_project/adapters/fizzy/projects.rb
|
|
169
|
+
- lib/active_project/adapters/fizzy_adapter.rb
|
|
170
|
+
- lib/active_project/adapters/github_project.rb
|
|
171
|
+
- lib/active_project/adapters/github_project/comments.rb
|
|
172
|
+
- lib/active_project/adapters/github_project/connection.rb
|
|
173
|
+
- lib/active_project/adapters/github_project/helpers.rb
|
|
174
|
+
- lib/active_project/adapters/github_project/issues.rb
|
|
175
|
+
- lib/active_project/adapters/github_project/projects.rb
|
|
176
|
+
- lib/active_project/adapters/github_project/webhooks.rb
|
|
177
|
+
- lib/active_project/adapters/github_project_adapter.rb
|
|
178
|
+
- lib/active_project/adapters/github_repo/connection.rb
|
|
179
|
+
- lib/active_project/adapters/github_repo/issues.rb
|
|
180
|
+
- lib/active_project/adapters/github_repo/projects.rb
|
|
181
|
+
- lib/active_project/adapters/github_repo/webhooks.rb
|
|
182
|
+
- lib/active_project/adapters/github_repo_adapter.rb
|
|
183
|
+
- lib/active_project/adapters/jira/attribute_normalizer.rb
|
|
137
184
|
- lib/active_project/adapters/jira/comments.rb
|
|
138
185
|
- lib/active_project/adapters/jira/connection.rb
|
|
139
186
|
- lib/active_project/adapters/jira/issues.rb
|
|
@@ -141,7 +188,6 @@ files:
|
|
|
141
188
|
- lib/active_project/adapters/jira/transitions.rb
|
|
142
189
|
- lib/active_project/adapters/jira/webhooks.rb
|
|
143
190
|
- lib/active_project/adapters/jira_adapter.rb
|
|
144
|
-
- lib/active_project/adapters/pagination.rb
|
|
145
191
|
- lib/active_project/adapters/trello/comments.rb
|
|
146
192
|
- lib/active_project/adapters/trello/connection.rb
|
|
147
193
|
- lib/active_project/adapters/trello/issues.rb
|
|
@@ -153,15 +199,27 @@ files:
|
|
|
153
199
|
- lib/active_project/async.rb
|
|
154
200
|
- lib/active_project/configuration.rb
|
|
155
201
|
- lib/active_project/configurations/base_adapter_configuration.rb
|
|
202
|
+
- lib/active_project/configurations/basecamp_configuration.rb
|
|
203
|
+
- lib/active_project/configurations/fizzy_configuration.rb
|
|
204
|
+
- lib/active_project/configurations/github_configuration.rb
|
|
205
|
+
- lib/active_project/configurations/jira_configuration.rb
|
|
156
206
|
- lib/active_project/configurations/trello_configuration.rb
|
|
207
|
+
- lib/active_project/connections/base.rb
|
|
208
|
+
- lib/active_project/connections/graph_ql.rb
|
|
209
|
+
- lib/active_project/connections/http_client.rb
|
|
210
|
+
- lib/active_project/connections/pagination.rb
|
|
211
|
+
- lib/active_project/connections/rest.rb
|
|
212
|
+
- lib/active_project/error_mapper.rb
|
|
157
213
|
- lib/active_project/errors.rb
|
|
158
214
|
- lib/active_project/railtie.rb
|
|
159
215
|
- lib/active_project/resource_factory.rb
|
|
160
216
|
- lib/active_project/resources/base_resource.rb
|
|
161
217
|
- lib/active_project/resources/comment.rb
|
|
162
218
|
- lib/active_project/resources/issue.rb
|
|
219
|
+
- lib/active_project/resources/persistable_resource.rb
|
|
163
220
|
- lib/active_project/resources/project.rb
|
|
164
221
|
- lib/active_project/resources/user.rb
|
|
222
|
+
- lib/active_project/status_mapper.rb
|
|
165
223
|
- lib/active_project/version.rb
|
|
166
224
|
- lib/active_project/webhook_event.rb
|
|
167
225
|
- lib/activeproject.rb
|
|
@@ -185,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
185
243
|
- !ruby/object:Gem::Version
|
|
186
244
|
version: '0'
|
|
187
245
|
requirements: []
|
|
188
|
-
rubygems_version:
|
|
246
|
+
rubygems_version: 4.0.1
|
|
189
247
|
specification_version: 4
|
|
190
248
|
summary: A standardized Ruby interface for multiple project management APIs (Jira,
|
|
191
249
|
Basecamp, Trello, etc.).
|