collavre_linear 0.1.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 +7 -0
- data/app/controllers/collavre_linear/application_controller.rb +21 -0
- data/app/controllers/collavre_linear/auth_controller.rb +135 -0
- data/app/controllers/collavre_linear/creatives/integrations_controller.rb +271 -0
- data/app/controllers/collavre_linear/webhooks_controller.rb +187 -0
- data/app/javascript/collavre_linear.js +199 -0
- data/app/jobs/collavre_linear/inbound_apply_job.rb +36 -0
- data/app/jobs/collavre_linear/outbound_archive_job.rb +29 -0
- data/app/jobs/collavre_linear/outbound_comment_delete_job.rb +41 -0
- data/app/jobs/collavre_linear/outbound_comment_sync_job.rb +112 -0
- data/app/jobs/collavre_linear/outbound_comment_update_job.rb +55 -0
- data/app/jobs/collavre_linear/outbound_sync_job.rb +39 -0
- data/app/models/collavre_linear/account.rb +29 -0
- data/app/models/collavre_linear/application_record.rb +7 -0
- data/app/models/collavre_linear/comment_link.rb +12 -0
- data/app/models/collavre_linear/issue_link.rb +20 -0
- data/app/models/collavre_linear/project_link.rb +76 -0
- data/app/observers/collavre_linear/comment_sync_observer.rb +97 -0
- data/app/observers/collavre_linear/creative_sync_observer.rb +162 -0
- data/app/services/collavre_linear/client.rb +425 -0
- data/app/services/collavre_linear/comment_formatter.rb +34 -0
- data/app/services/collavre_linear/comment_syncability.rb +33 -0
- data/app/services/collavre_linear/creative_exporter.rb +352 -0
- data/app/services/collavre_linear/echo_guard.rb +36 -0
- data/app/services/collavre_linear/field_mapper.rb +119 -0
- data/app/services/collavre_linear/inbound_applier.rb +599 -0
- data/app/services/collavre_linear/o_auth_token_service.rb +163 -0
- data/app/views/collavre_linear/auth/setup.html.erb +63 -0
- data/app/views/collavre_linear/integrations/_modal.html.erb +200 -0
- data/config/initializers/integration_settings.rb +8 -0
- data/config/locales/en.yml +49 -0
- data/config/locales/ko.yml +49 -0
- data/config/routes.rb +22 -0
- data/db/migrate/20260701000000_create_linear_accounts.rb +20 -0
- data/db/migrate/20260701000001_create_collavre_linear_project_links.rb +18 -0
- data/db/migrate/20260701000002_create_collavre_linear_issue_links.rb +22 -0
- data/db/migrate/20260701000003_create_collavre_linear_comment_links.rb +17 -0
- data/db/migrate/20260701000004_add_last_outbound_at_to_linear_links.rb +6 -0
- data/db/migrate/20260701000005_add_webhook_id_to_linear_project_links.rb +5 -0
- data/db/migrate/20260701000006_add_lookup_indexes_to_linear_project_links.rb +17 -0
- data/db/migrate/20260701000007_enforce_one_project_link_per_creative.rb +19 -0
- data/db/migrate/20260701000008_allow_null_webhook_secret_on_linear_project_links.rb +8 -0
- data/lib/collavre_linear/engine.rb +122 -0
- data/lib/collavre_linear/version.rb +3 -0
- data/lib/collavre_linear.rb +5 -0
- metadata +115 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Inbound webhooks resolve the ProjectLink by team_id and by linear_project_id
|
|
4
|
+
# on every delivery. The pre-existing composite [creative_id, linear_project_id]
|
|
5
|
+
# index can't serve a team_id lookup and doesn't lead with linear_project_id, so
|
|
6
|
+
# both hot-path lookups full-scanned. Add covering single-column indexes.
|
|
7
|
+
class AddLookupIndexesToLinearProjectLinks < ActiveRecord::Migration[8.0]
|
|
8
|
+
def change
|
|
9
|
+
add_index :linear_project_links, :team_id
|
|
10
|
+
# UNIQUE: one Linear project maps to exactly one Collavre root. Inbound
|
|
11
|
+
# webhooks resolve the project via an unscoped find_by(linear_project_id:),
|
|
12
|
+
# so a second link on a disjoint creative would route imports to whichever
|
|
13
|
+
# row the DB returns. The controller's check-then-save can't stop two
|
|
14
|
+
# concurrent link requests from both inserting; enforce it at the DB layer.
|
|
15
|
+
add_index :linear_project_links, :linear_project_id, unique: true
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# One Collavre root maps to exactly one Linear project. The controller's
|
|
4
|
+
# origin_conflict check is check-then-save, so two concurrent link requests for
|
|
5
|
+
# the SAME root to DIFFERENT projects can both pass it and insert — the existing
|
|
6
|
+
# unique indexes don't stop it: linear_project_id is unique per project (not per
|
|
7
|
+
# root) and the composite [creative_id, linear_project_id] permits two different
|
|
8
|
+
# projects on one creative. resolve_project_link then does find_by(creative_id:)
|
|
9
|
+
# and would sync against an arbitrary project. Enforce the invariant at the DB
|
|
10
|
+
# layer: promote the creative_id index to UNIQUE (which subsumes and replaces the
|
|
11
|
+
# now-redundant composite).
|
|
12
|
+
class EnforceOneProjectLinkPerCreative < ActiveRecord::Migration[8.0]
|
|
13
|
+
def change
|
|
14
|
+
remove_index :linear_project_links,
|
|
15
|
+
name: "index_linear_project_links_on_creative_and_project"
|
|
16
|
+
remove_index :linear_project_links, :creative_id
|
|
17
|
+
add_index :linear_project_links, :creative_id, unique: true
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class AllowNullWebhookSecretOnLinearProjectLinks < ActiveRecord::Migration[8.0]
|
|
2
|
+
# Linear generates the webhook signing secret when the webhook is created and
|
|
3
|
+
# won't let us pick it, so a ProjectLink is created without one and the admin
|
|
4
|
+
# pastes Linear's value in afterward. Relax the original NOT NULL accordingly.
|
|
5
|
+
def change
|
|
6
|
+
change_column_null :linear_project_links, :webhook_secret, true
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
module CollavreLinear
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace CollavreLinear
|
|
4
|
+
|
|
5
|
+
# Path this engine is mounted at in the host app (see the
|
|
6
|
+
# `collavre_linear.routes` initializer below). Because the engine is
|
|
7
|
+
# `isolate_namespace`d and detached, its own `url_helpers` generate
|
|
8
|
+
# engine-internal paths WITHOUT this prefix. Anything that needs a URL the
|
|
9
|
+
# host (or an external service like Linear) will actually reach must fold
|
|
10
|
+
# this prefix back in — pass it as `script_name:` to the engine url_helper.
|
|
11
|
+
MOUNT_PATH = "/linear"
|
|
12
|
+
|
|
13
|
+
config.generators do |g|
|
|
14
|
+
g.test_framework :minitest
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.javascript_path
|
|
18
|
+
root.join("app/javascript")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.stylesheet_path
|
|
22
|
+
root.join("app/assets/stylesheets")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
config.i18n.load_path += Dir[root.join("config", "locales", "*.yml")]
|
|
26
|
+
|
|
27
|
+
initializer "collavre_linear.routes", before: :add_routing_paths do |app|
|
|
28
|
+
app.routes.append do
|
|
29
|
+
mount CollavreLinear::Engine => CollavreLinear::Engine::MOUNT_PATH, as: :linear_engine
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
initializer "collavre_linear.assets" do |app|
|
|
34
|
+
if app.config.respond_to?(:assets) && app.config.assets.respond_to?(:paths)
|
|
35
|
+
app.config.assets.paths << root.join("app/assets/stylesheets")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
initializer "collavre_linear.migrations" do |app|
|
|
40
|
+
unless app.root.to_s.match?(root.to_s)
|
|
41
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
|
42
|
+
app.config.paths["db/migrate"] << expanded_path
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
initializer "collavre_linear.register_integration", after: :load_config_initializers do
|
|
48
|
+
Rails.application.config.to_prepare do
|
|
49
|
+
if defined?(Collavre::IntegrationRegistry)
|
|
50
|
+
Collavre::IntegrationRegistry.register(:linear, {
|
|
51
|
+
label: I18n.t("collavre_linear.integration.label", default: "Linear"),
|
|
52
|
+
icon: "linear",
|
|
53
|
+
description: I18n.t("collavre_linear.integration.description", default: "Two-way sync of projects, issues and comments"),
|
|
54
|
+
routes: CollavreLinear::Engine.routes.url_helpers,
|
|
55
|
+
creative_menu_partial: "collavre_linear/integrations/modal"
|
|
56
|
+
})
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
initializer "collavre_linear.user_associations", after: :load_config_initializers do
|
|
62
|
+
Rails.application.config.to_prepare do
|
|
63
|
+
user_class = Collavre.user_class rescue nil
|
|
64
|
+
next unless user_class
|
|
65
|
+
|
|
66
|
+
unless user_class.reflect_on_association(:linear_account)
|
|
67
|
+
user_class.has_one :linear_account,
|
|
68
|
+
class_name: "CollavreLinear::Account",
|
|
69
|
+
foreign_key: :user_id,
|
|
70
|
+
dependent: :destroy
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
initializer "collavre_linear.creative_associations", after: :load_config_initializers do
|
|
76
|
+
Rails.application.config.to_prepare do
|
|
77
|
+
creative_class = Collavre::Creative rescue nil
|
|
78
|
+
next unless creative_class
|
|
79
|
+
|
|
80
|
+
unless creative_class.reflect_on_association(:linear_project_links)
|
|
81
|
+
creative_class.has_many :linear_project_links,
|
|
82
|
+
class_name: "CollavreLinear::ProjectLink",
|
|
83
|
+
foreign_key: :creative_id,
|
|
84
|
+
dependent: :destroy
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
unless creative_class.reflect_on_association(:linear_issue_links)
|
|
88
|
+
creative_class.has_many :linear_issue_links,
|
|
89
|
+
class_name: "CollavreLinear::IssueLink",
|
|
90
|
+
foreign_key: :creative_id,
|
|
91
|
+
dependent: :destroy
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if creative_class.respond_to?(:register_reserved_metadata_key)
|
|
95
|
+
creative_class.register_reserved_metadata_key("linear")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
initializer "collavre_linear.creative_sync_observer", after: :load_config_initializers do
|
|
101
|
+
Rails.application.config.to_prepare do
|
|
102
|
+
creative_class = Collavre::Creative rescue nil
|
|
103
|
+
next unless creative_class
|
|
104
|
+
|
|
105
|
+
unless creative_class.include?(CollavreLinear::CreativeSyncObserver)
|
|
106
|
+
creative_class.include CollavreLinear::CreativeSyncObserver
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
initializer "collavre_linear.comment_sync_observer", after: :load_config_initializers do
|
|
112
|
+
Rails.application.config.to_prepare do
|
|
113
|
+
comment_class = Collavre::Comment rescue nil
|
|
114
|
+
next unless comment_class
|
|
115
|
+
|
|
116
|
+
unless comment_class.include?(CollavreLinear::CommentSyncObserver)
|
|
117
|
+
comment_class.include CollavreLinear::CommentSyncObserver
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: collavre_linear
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Collavre
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: collavre
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
description: Plugin engine to connect Collavre creatives with Linear projects, issues
|
|
41
|
+
and comments.
|
|
42
|
+
email:
|
|
43
|
+
- support@collavre.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- app/controllers/collavre_linear/application_controller.rb
|
|
49
|
+
- app/controllers/collavre_linear/auth_controller.rb
|
|
50
|
+
- app/controllers/collavre_linear/creatives/integrations_controller.rb
|
|
51
|
+
- app/controllers/collavre_linear/webhooks_controller.rb
|
|
52
|
+
- app/javascript/collavre_linear.js
|
|
53
|
+
- app/jobs/collavre_linear/inbound_apply_job.rb
|
|
54
|
+
- app/jobs/collavre_linear/outbound_archive_job.rb
|
|
55
|
+
- app/jobs/collavre_linear/outbound_comment_delete_job.rb
|
|
56
|
+
- app/jobs/collavre_linear/outbound_comment_sync_job.rb
|
|
57
|
+
- app/jobs/collavre_linear/outbound_comment_update_job.rb
|
|
58
|
+
- app/jobs/collavre_linear/outbound_sync_job.rb
|
|
59
|
+
- app/models/collavre_linear/account.rb
|
|
60
|
+
- app/models/collavre_linear/application_record.rb
|
|
61
|
+
- app/models/collavre_linear/comment_link.rb
|
|
62
|
+
- app/models/collavre_linear/issue_link.rb
|
|
63
|
+
- app/models/collavre_linear/project_link.rb
|
|
64
|
+
- app/observers/collavre_linear/comment_sync_observer.rb
|
|
65
|
+
- app/observers/collavre_linear/creative_sync_observer.rb
|
|
66
|
+
- app/services/collavre_linear/client.rb
|
|
67
|
+
- app/services/collavre_linear/comment_formatter.rb
|
|
68
|
+
- app/services/collavre_linear/comment_syncability.rb
|
|
69
|
+
- app/services/collavre_linear/creative_exporter.rb
|
|
70
|
+
- app/services/collavre_linear/echo_guard.rb
|
|
71
|
+
- app/services/collavre_linear/field_mapper.rb
|
|
72
|
+
- app/services/collavre_linear/inbound_applier.rb
|
|
73
|
+
- app/services/collavre_linear/o_auth_token_service.rb
|
|
74
|
+
- app/views/collavre_linear/auth/setup.html.erb
|
|
75
|
+
- app/views/collavre_linear/integrations/_modal.html.erb
|
|
76
|
+
- config/initializers/integration_settings.rb
|
|
77
|
+
- config/locales/en.yml
|
|
78
|
+
- config/locales/ko.yml
|
|
79
|
+
- config/routes.rb
|
|
80
|
+
- db/migrate/20260701000000_create_linear_accounts.rb
|
|
81
|
+
- db/migrate/20260701000001_create_collavre_linear_project_links.rb
|
|
82
|
+
- db/migrate/20260701000002_create_collavre_linear_issue_links.rb
|
|
83
|
+
- db/migrate/20260701000003_create_collavre_linear_comment_links.rb
|
|
84
|
+
- db/migrate/20260701000004_add_last_outbound_at_to_linear_links.rb
|
|
85
|
+
- db/migrate/20260701000005_add_webhook_id_to_linear_project_links.rb
|
|
86
|
+
- db/migrate/20260701000006_add_lookup_indexes_to_linear_project_links.rb
|
|
87
|
+
- db/migrate/20260701000007_enforce_one_project_link_per_creative.rb
|
|
88
|
+
- db/migrate/20260701000008_allow_null_webhook_secret_on_linear_project_links.rb
|
|
89
|
+
- lib/collavre_linear.rb
|
|
90
|
+
- lib/collavre_linear/engine.rb
|
|
91
|
+
- lib/collavre_linear/version.rb
|
|
92
|
+
homepage: https://collavre.com
|
|
93
|
+
licenses:
|
|
94
|
+
- AGPL
|
|
95
|
+
metadata:
|
|
96
|
+
homepage_uri: https://collavre.com
|
|
97
|
+
source_code_uri: https://github.com/sh1nj1/plan42
|
|
98
|
+
rdoc_options: []
|
|
99
|
+
require_paths:
|
|
100
|
+
- lib
|
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
102
|
+
requirements:
|
|
103
|
+
- - ">="
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: '0'
|
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
requirements: []
|
|
112
|
+
rubygems_version: 3.6.7
|
|
113
|
+
specification_version: 4
|
|
114
|
+
summary: Linear integration for Collavre
|
|
115
|
+
test_files: []
|