linear_api 0.3.2 → 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.
data/lib/linear_api.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'logger'
3
4
  require_relative 'linear_api/version'
4
5
  require_relative 'linear_api/client'
5
6
  require_relative 'linear_api/result'
@@ -19,23 +20,59 @@ module LinearApi
19
20
  class Error < StandardError; end
20
21
  class AuthenticationError < Error; end
21
22
  class NotFoundError < Error; end
22
- class RateLimitError < Error; end
23
+
24
+ class RateLimitError < Error
25
+ attr_reader :retry_after
26
+
27
+ def initialize(message = 'Rate limited', retry_after: nil)
28
+ @retry_after = retry_after
29
+ super(message)
30
+ end
31
+ end
32
+
33
+ # Sensitive key patterns to redact from issue descriptions
34
+ SENSITIVE_KEY_PATTERN = /password|secret|token|key|credential|ssn|api_key|authorization/i
23
35
 
24
36
  class << self
25
- attr_accessor :api_key, :team_id
37
+ attr_accessor :api_key, :team_id, :workspace_slug
38
+ attr_writer :logger
26
39
 
27
40
  def configure
28
41
  yield self
29
42
  end
30
43
 
44
+ def logger
45
+ @logger ||= if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
46
+ Rails.logger
47
+ else
48
+ Logger.new($stdout, level: Logger::WARN)
49
+ end
50
+ end
51
+
31
52
  def client
32
- raise AuthenticationError, 'API key not configured' unless api_key
53
+ @client_mutex ||= Mutex.new
54
+ @client_mutex.synchronize do
55
+ raise AuthenticationError, 'API key not configured' unless api_key
33
56
 
34
- @client ||= Client.new(api_key: api_key, team_id: team_id)
57
+ @client ||= Client.new(api_key: api_key, team_id: team_id)
58
+ end
35
59
  end
36
60
 
37
61
  def reset_client!
38
- @client = nil
62
+ @client_mutex ||= Mutex.new
63
+ @client_mutex.synchronize do
64
+ @client = nil
65
+ end
66
+ end
67
+
68
+ # Quick health check against the Linear API
69
+ #
70
+ # @return [Boolean] true if the API is reachable and authenticated
71
+ def healthy?
72
+ result = client.query('query { viewer { id } }')
73
+ result.success?
74
+ rescue StandardError
75
+ false
39
76
  end
40
77
 
41
78
  # Rails calls eager_load! on all registered eager_load_namespaces.
@@ -24,7 +24,7 @@ namespace :linear do
24
24
 
25
25
  desc 'Sync all metadata from Linear API (labels, projects, states, users)'
26
26
  task sync_cache: :environment do
27
- puts 'Syncing Linear metadata from API...'
27
+ puts 'Syncing Linear metadata from API (single batched request)...'
28
28
 
29
29
  results = LinearApi::CacheSync.sync_all
30
30
 
@@ -70,12 +70,17 @@ namespace :linear do
70
70
  task export_cache: :environment do
71
71
  output_path = ENV['LINEAR_EXPORT_PATH'] || Rails.root.join('linear_cache.json')
72
72
 
73
+ # Fetch team info for export (avoids hardcoded values)
74
+ team_data = { id: LinearApi.team_id }
75
+ team_result = LinearApi.client.get_team
76
+ if team_result.success?
77
+ team_data[:key] = team_result.data.key
78
+ team_data[:name] = team_result.data.name
79
+ end
80
+
73
81
  data = {
74
82
  last_updated: Time.current.iso8601,
75
- team: {
76
- id: LinearApi.team_id,
77
- key: 'TOS'
78
- },
83
+ team: team_data,
79
84
  workflow_states: export_states,
80
85
  labels: export_labels,
81
86
  projects: export_projects,
@@ -86,10 +91,10 @@ namespace :linear do
86
91
  puts "Exported cache to #{output_path}"
87
92
  end
88
93
 
89
- desc 'Refresh stale synced issues from Linear'
94
+ desc 'Refresh stale synced issues from Linear (batched)'
90
95
  task refresh_issues: :environment do
91
96
  stale_count = LinearApi::SyncedIssue.stale.count
92
- puts "Refreshing #{stale_count} stale issues..."
97
+ puts "Refreshing #{stale_count} stale issues (batched)..."
93
98
 
94
99
  LinearApi::SyncedIssue.refresh_stale_issues!
95
100
 
@@ -119,8 +124,20 @@ namespace :linear do
119
124
  end
120
125
  end
121
126
 
122
- # Helper methods for export
123
- def export_states
127
+ desc 'Health check: verify Linear API connectivity'
128
+ task health: :environment do
129
+ puts 'Checking Linear API connectivity...'
130
+
131
+ if LinearApi.healthy?
132
+ puts ' ✓ Linear API is reachable and authenticated'
133
+ else
134
+ puts ' ✗ Cannot reach Linear API (check LINEAR_API_KEY)'
135
+ exit 1
136
+ end
137
+ end
138
+
139
+ # Helper methods for export (defined as module methods to avoid polluting global scope)
140
+ def self.export_states
124
141
  LinearApi::CachedMetadata.states.each_with_object({}) do |state, hash|
125
142
  hash[state.key] = {
126
143
  id: state.linear_id,
@@ -130,7 +147,7 @@ namespace :linear do
130
147
  end
131
148
  end
132
149
 
133
- def export_labels
150
+ def self.export_labels
134
151
  LinearApi::CachedMetadata.labels.group_by(&:category).transform_values do |labels|
135
152
  labels.each_with_object({}) do |label, hash|
136
153
  key = label.key.split(':').last
@@ -143,7 +160,7 @@ namespace :linear do
143
160
  end
144
161
  end
145
162
 
146
- def export_projects
163
+ def self.export_projects
147
164
  LinearApi::CachedMetadata.projects.each_with_object({}) do |project, hash|
148
165
  hash[project.key] = {
149
166
  id: project.linear_id,
@@ -153,7 +170,7 @@ namespace :linear do
153
170
  end
154
171
  end
155
172
 
156
- def export_users
173
+ def self.export_users
157
174
  LinearApi::CachedMetadata.users.each_with_object({}) do |user, hash|
158
175
  hash[user.key] = {
159
176
  id: user.linear_id,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linear_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TheOwnerStack
@@ -29,6 +29,26 @@ dependencies:
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: '3.0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: faraday-retry
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '1.0'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '3.0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '1.0'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '3.0'
32
52
  - !ruby/object:Gem::Dependency
33
53
  name: rails
34
54
  requirement: !ruby/object:Gem::Requirement
@@ -36,7 +56,7 @@ dependencies:
36
56
  - - ">="
37
57
  - !ruby/object:Gem::Version
38
58
  version: '7.0'
39
- type: :runtime
59
+ type: :development
40
60
  prerelease: false
41
61
  version_requirements: !ruby/object:Gem::Requirement
42
62
  requirements: