diff --git a/app/models/namespaces/user_namespace.rb b/app/models/namespaces/user_namespace.rb index 14b867b26074e761ebf4d578d2888d83cf26f5e3..408acb6dcce03d100009f9c2186962cc58a8f855 100644 --- a/app/models/namespaces/user_namespace.rb +++ b/app/models/namespaces/user_namespace.rb @@ -25,5 +25,9 @@ class UserNamespace < Namespace def self.sti_name 'User' end + + def owners + Array.wrap(owner) + end end end diff --git a/app/models/project.rb b/app/models/project.rb index 1b920ab57b024e6dc0880bdf661923a8e1dc88f9..abe072a3a6d8ea1e1335ffc9cb99a688e590aafe 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1520,8 +1520,17 @@ def owner group || namespace.try(:owner) end + def deprecated_owner + # Kept in order to maintain webhook structures until we remove owner_name and owner_email + # See https://gitlab.com/gitlab-org/gitlab/-/issues/350603 + group || namespace.try(:owner) + end + def owners - Array.wrap(owner) + # This will be phased out and replaced with `owners` relationship + # backed by memberships with direct/inherited Owner access roles + # See https://gitlab.com/groups/gitlab-org/-/epics/7405 + team.owners end def first_owner diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index 4442975474730500a95979ec0a873a51ed397c8a..71d7e7f142688938b2f79f2530004b7ddbc6f6b5 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -79,8 +79,12 @@ X-Gitlab-Event: System Hook "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_create", "name": "StoreCloud", - "owner_email": "johnsmith@gmail.com", + "owner_email": "johnsmith@example.com", "owner_name": "John Smith", + "owners": [{ + "name": "John", + "email": "user1@example.com" + }], "path": "storecloud", "path_with_namespace": "jsmith/storecloud", "project_id": 74, @@ -96,8 +100,12 @@ X-Gitlab-Event: System Hook "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_destroy", "name": "Underscore", - "owner_email": "johnsmith@gmail.com", + "owner_email": "johnsmith@example.com", "owner_name": "John Smith", + "owners": [{ + "name": "John", + "email": "user1@example.com" + }], "path": "underscore", "path_with_namespace": "jsmith/underscore", "project_id": 73, @@ -117,7 +125,11 @@ X-Gitlab-Event: System Hook "path_with_namespace": "jsmith/underscore", "project_id": 73, "owner_name": "John Smith", - "owner_email": "johnsmith@gmail.com", + "owner_email": "johnsmith@example.com", + "owners": [{ + "name": "John", + "email": "user1@example.com" + }], "project_visibility": "internal", "old_path_with_namespace": "jsmith/overscore" } @@ -138,7 +150,11 @@ Please refer to `group_rename` and `user_rename` for that case. "path_with_namespace": "scores/underscore", "project_id": 73, "owner_name": "John Smith", - "owner_email": "johnsmith@gmail.com", + "owner_email": "johnsmith@example.com", + "owners": [{ + "name": "John", + "email": "user1@example.com" + }], "project_visibility": "internal", "old_path_with_namespace": "jsmith/overscore" } @@ -152,8 +168,12 @@ Please refer to `group_rename` and `user_rename` for that case. "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_update", "name": "StoreCloud", - "owner_email": "johnsmith@gmail.com", + "owner_email": "johnsmith@example.com", "owner_name": "John Smith", + "owners": [{ + "name": "John", + "email": "user1@example.com" + }], "path": "storecloud", "path_with_namespace": "jsmith/storecloud", "project_id": 74, @@ -173,7 +193,7 @@ Please refer to `group_rename` and `user_rename` for that case. "project_name": "StoreCloud", "project_path": "storecloud", "project_path_with_namespace": "jsmith/storecloud", - "user_email": "johnsmith@gmail.com", + "user_email": "johnsmith@example.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41, @@ -193,7 +213,7 @@ Please refer to `group_rename` and `user_rename` for that case. "project_name": "StoreCloud", "project_path": "storecloud", "project_path_with_namespace": "jsmith/storecloud", - "user_email": "johnsmith@gmail.com", + "user_email": "johnsmith@example.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41, @@ -213,7 +233,7 @@ Please refer to `group_rename` and `user_rename` for that case. "project_name": "StoreCloud", "project_path": "storecloud", "project_path_with_namespace": "jsmith/storecloud", - "user_email": "johnsmith@gmail.com", + "user_email": "johnsmith@example.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41, @@ -360,7 +380,7 @@ If the user is blocked via LDAP, `state` is `ldap_blocked`. "group_id": 78, "group_name": "StoreCloud", "group_path": "storecloud", - "user_email": "johnsmith@gmail.com", + "user_email": "johnsmith@example.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41 @@ -378,7 +398,7 @@ If the user is blocked via LDAP, `state` is `ldap_blocked`. "group_id": 78, "group_name": "StoreCloud", "group_path": "storecloud", - "user_email": "johnsmith@gmail.com", + "user_email": "johnsmith@example.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41 @@ -396,7 +416,7 @@ If the user is blocked via LDAP, `state` is `ldap_blocked`. "group_id": 78, "group_name": "StoreCloud", "group_path": "storecloud", - "user_email": "johnsmith@gmail.com", + "user_email": "johnsmith@example.com", "user_name": "John Smith", "user_username": "johnsmith", "user_id": 41 diff --git a/lib/gitlab/hook_data/project_builder.rb b/lib/gitlab/hook_data/project_builder.rb index 65c237f743f43db9024de93c5b710b82a7c10043..ebd97d3ab1b5dd11cdf66e7a253b52fcd3390b80 100644 --- a/lib/gitlab/hook_data/project_builder.rb +++ b/lib/gitlab/hook_data/project_builder.rb @@ -16,6 +16,7 @@ class ProjectBuilder < BaseBuilder # project_id: 1, # owner_name: "John", # owner_email: "user1@example.org", + # owners: [name: "John", email: "user1@example.org"], # project_visibility: "internal", # old_path_with_namespace: "old-path-with-namespace" # } @@ -32,19 +33,33 @@ def build(event) private def project_data - owner = project.owner + owners = project.owners.compact + # When this is removed, also remove the `deprecated_owner` method + # See https://gitlab.com/gitlab-org/gitlab/-/issues/350603 + owner = project.deprecated_owner { name: project.name, path: project.path, path_with_namespace: project.full_path, project_id: project.id, - owner_name: owner.name, - owner_email: owner.respond_to?(:email) ? owner.email : "", + owner_name: owner.try(:name), + owner_email: user_email(owner), + owners: owners.map do |owner| + owner_data(owner) + end, project_visibility: project.visibility.downcase } end + def owner_data(user) + { name: user.name, email: user_email(user) } + end + + def user_email(user) + user.respond_to?(:email) ? user.email : "" + end + def event_specific_project_data(event) return {} unless event == :rename || event == :transfer diff --git a/spec/lib/gitlab/hook_data/project_builder_spec.rb b/spec/lib/gitlab/hook_data/project_builder_spec.rb index 672dbab918f9f0b5ef1fda9fe58b936d480123b0..e86ac66b1ad22b2779e0e90ff0111e20efb13098 100644 --- a/spec/lib/gitlab/hook_data/project_builder_spec.rb +++ b/spec/lib/gitlab/hook_data/project_builder_spec.rb @@ -13,7 +13,7 @@ let(:attributes) do [ :event_name, :created_at, :updated_at, :name, :path, :path_with_namespace, :project_id, - :owner_name, :owner_email, :project_visibility + :owners, :owner_name, :owner_email, :project_visibility ] end @@ -30,6 +30,7 @@ expect(data[:project_id]).to eq(project.id) expect(data[:owner_name]).to eq('John') expect(data[:owner_email]).to eq('john@example.com') + expect(data[:owners]).to contain_exactly({ name: 'John', email: 'john@example.com' }) expect(data[:project_visibility]).to eq('internal') end end diff --git a/spec/models/namespaces/user_namespace_spec.rb b/spec/models/namespaces/user_namespace_spec.rb index 7c00a5977564832efd24a368afe1a299e3e54520..fb9e75716667fddcca5635a0985584a27edffb12 100644 --- a/spec/models/namespaces/user_namespace_spec.rb +++ b/spec/models/namespaces/user_namespace_spec.rb @@ -9,4 +9,13 @@ describe 'validations' do it { is_expected.to validate_presence_of(:owner) } end + + describe '#owners' do + let(:owner) { build(:user) } + let(:namespace) { build(:namespace, owner: owner) } + + specify do + expect(namespace.owners).to match_array([owner]) + end + end end