Commit 713c6397 authored by Andy Soiron's avatar Andy Soiron Committed by James Fargher

Add update_sequence_id to JiraConnect workers

In order to make the JiraConnect workers idempotent
we need to make sure they always sent the same
UpdateSequenceId when run with the same arguments.
So the sequence ID needs to be generated at the time
the job is scheduled and not when running the Job.
This is the first part of a multi step release to add
this argument. The next release will remove the default
parent 7f327c37
...@@ -6,11 +6,11 @@ module JiraConnect ...@@ -6,11 +6,11 @@ module JiraConnect
self.project = project self.project = project
end end
def execute(commits: nil, branches: nil, merge_requests: nil) def execute(commits: nil, branches: nil, merge_requests: nil, update_sequence_id: nil)
JiraConnectInstallation.for_project(project).each do |installation| JiraConnectInstallation.for_project(project).each do |installation|
client = Atlassian::JiraConnect::Client.new(installation.base_url, installation.shared_secret) client = Atlassian::JiraConnect::Client.new(installation.base_url, installation.shared_secret)
response = client.store_dev_info(project: project, commits: commits, branches: branches, merge_requests: merge_requests) response = client.store_dev_info(project: project, commits: commits, branches: branches, merge_requests: merge_requests, update_sequence_id: update_sequence_id)
log_response(response) log_response(response)
end end
......
...@@ -8,7 +8,7 @@ module JiraConnect ...@@ -8,7 +8,7 @@ module JiraConnect
feature_category :integrations feature_category :integrations
loggable_arguments 1, 2 loggable_arguments 1, 2
def perform(project_id, branch_name, commit_shas) def perform(project_id, branch_name, commit_shas, update_sequence_id = nil)
project = Project.find_by_id(project_id) project = Project.find_by_id(project_id)
return unless project return unless project
...@@ -16,7 +16,7 @@ module JiraConnect ...@@ -16,7 +16,7 @@ module JiraConnect
branches = [project.repository.find_branch(branch_name)] if branch_name.present? branches = [project.repository.find_branch(branch_name)] if branch_name.present?
commits = project.commits_by(oids: commit_shas) if commit_shas.present? commits = project.commits_by(oids: commit_shas) if commit_shas.present?
JiraConnect::SyncService.new(project).execute(commits: commits, branches: branches) JiraConnect::SyncService.new(project).execute(commits: commits, branches: branches, update_sequence_id: update_sequence_id)
end end
end end
end end
...@@ -7,12 +7,12 @@ module JiraConnect ...@@ -7,12 +7,12 @@ module JiraConnect
queue_namespace :jira_connect queue_namespace :jira_connect
feature_category :integrations feature_category :integrations
def perform(merge_request_id) def perform(merge_request_id, update_sequence_id = nil)
merge_request = MergeRequest.find_by_id(merge_request_id) merge_request = MergeRequest.find_by_id(merge_request_id)
return unless merge_request && merge_request.project return unless merge_request && merge_request.project
JiraConnect::SyncService.new(merge_request.project).execute(merge_requests: [merge_request]) JiraConnect::SyncService.new(merge_request.project).execute(merge_requests: [merge_request], update_sequence_id: update_sequence_id)
end end
end end
end end
...@@ -3,19 +3,24 @@ ...@@ -3,19 +3,24 @@
module Atlassian module Atlassian
module JiraConnect module JiraConnect
class Client < Gitlab::HTTP class Client < Gitlab::HTTP
def self.generate_update_sequence_id
Gitlab::Metrics::System.monotonic_time.to_i
end
def initialize(base_uri, shared_secret) def initialize(base_uri, shared_secret)
@base_uri = base_uri @base_uri = base_uri
@shared_secret = shared_secret @shared_secret = shared_secret
end end
def store_dev_info(project:, commits: nil, branches: nil, merge_requests: nil) def store_dev_info(project:, commits: nil, branches: nil, merge_requests: nil, update_sequence_id: nil)
dev_info_json = { dev_info_json = {
repositories: [ repositories: [
Serializers::RepositoryEntity.represent( Serializers::RepositoryEntity.represent(
project, project,
commits: commits, commits: commits,
branches: branches, branches: branches,
merge_requests: merge_requests merge_requests: merge_requests,
update_sequence_id: update_sequence_id
) )
] ]
}.to_json }.to_json
......
...@@ -9,12 +9,12 @@ module Atlassian ...@@ -9,12 +9,12 @@ module Atlassian
format_with(:string) { |value| value.to_s } format_with(:string) { |value| value.to_s }
expose :monotonic_time, as: :updateSequenceId expose :update_sequence_id, as: :updateSequenceId
private private
def monotonic_time def update_sequence_id
Gitlab::Metrics::System.monotonic_time.to_i options[:update_sequence_id] || Client.generate_update_sequence_id
end end
end end
end end
......
...@@ -15,13 +15,13 @@ module Atlassian ...@@ -15,13 +15,13 @@ module Atlassian
end end
expose :commits do |project, options| expose :commits do |project, options|
JiraConnect::Serializers::CommitEntity.represent options[:commits], project: project JiraConnect::Serializers::CommitEntity.represent options[:commits], project: project, update_sequence_id: options[:update_sequence_id]
end end
expose :branches do |project, options| expose :branches do |project, options|
JiraConnect::Serializers::BranchEntity.represent options[:branches], project: project JiraConnect::Serializers::BranchEntity.represent options[:branches], project: project, update_sequence_id: options[:update_sequence_id]
end end
expose :pullRequests do |project, options| expose :pullRequests do |project, options|
JiraConnect::Serializers::PullRequestEntity.represent options[:merge_requests], project: project JiraConnect::Serializers::PullRequestEntity.represent options[:merge_requests], project: project, update_sequence_id: options[:update_sequence_id]
end end
end end
end end
......
...@@ -11,6 +11,14 @@ RSpec.describe Atlassian::JiraConnect::Client do ...@@ -11,6 +11,14 @@ RSpec.describe Atlassian::JiraConnect::Client do
Timecop.freeze { example.run } Timecop.freeze { example.run }
end end
describe '.generate_update_sequence_id' do
it 'returns monotonic_time converted it to integer' do
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(1.0)
expect(described_class.generate_update_sequence_id).to eq(1)
end
end
describe '#store_dev_info' do describe '#store_dev_info' do
it "calls the API with auth headers" do it "calls the API with auth headers" do
expected_jwt = Atlassian::Jwt.encode( expected_jwt = Atlassian::Jwt.encode(
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Atlassian::JiraConnect::Serializers::BaseEntity do
let(:update_sequence_id) { nil }
subject do
described_class.represent(
anything,
update_sequence_id: update_sequence_id
)
end
it 'generates the update_sequence_id' do
allow(Atlassian::JiraConnect::Client).to receive(:generate_update_sequence_id).and_return(1)
expect(subject.value_for(:updateSequenceId)).to eq(1)
end
context 'with update_sequence_id option' do
let(:update_sequence_id) { 123 }
it 'uses the custom update_sequence_id' do
expect(subject.value_for(:updateSequenceId)).to eq(123)
end
end
end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity do RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity do
let(:update_sequence_id) { nil }
subject do subject do
project = create(:project, :repository) project = create(:project, :repository)
commits = [project.commit] commits = [project.commit]
...@@ -13,9 +15,23 @@ RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity do ...@@ -13,9 +15,23 @@ RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity do
project, project,
commits: commits, commits: commits,
branches: branches, branches: branches,
merge_requests: merge_requests merge_requests: merge_requests,
update_sequence_id: update_sequence_id
).to_json ).to_json
end end
it { is_expected.to match_schema('jira_connect/repository') } it { is_expected.to match_schema('jira_connect/repository') }
context 'with custom update_sequence_id' do
let(:update_sequence_id) { 1.0 }
it 'passes the update_sequence_id on to the nested entities', :aggregate_failures do
parsed_subject = Gitlab::Json.parse(subject)
expect(parsed_subject['updateSequenceId']).to eq(update_sequence_id)
expect(parsed_subject['commits'].first['updateSequenceId']).to eq(update_sequence_id)
expect(parsed_subject['branches'].first['updateSequenceId']).to eq(update_sequence_id)
expect(parsed_subject['pullRequests'].first['updateSequenceId']).to eq(update_sequence_id)
end
end
end end
...@@ -23,7 +23,8 @@ RSpec.describe JiraConnect::SyncService do ...@@ -23,7 +23,8 @@ RSpec.describe JiraConnect::SyncService do
project: project, project: project,
commits: commits, commits: commits,
branches: [instance_of(Gitlab::Git::Branch)], branches: [instance_of(Gitlab::Git::Branch)],
merge_requests: merge_requests merge_requests: merge_requests,
update_sequence_id: anything
).and_return(return_value) ).and_return(return_value)
end end
end end
......
...@@ -4,7 +4,10 @@ require 'spec_helper' ...@@ -4,7 +4,10 @@ require 'spec_helper'
RSpec.describe JiraConnect::SyncBranchWorker do RSpec.describe JiraConnect::SyncBranchWorker do
describe '#perform' do describe '#perform' do
let_it_be(:project) { create(:project, :repository) } let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
let_it_be(:subscription) { create(:jira_connect_subscription, installation: create(:jira_connect_installation), namespace: group) }
let(:project_id) { project.id } let(:project_id) { project.id }
let(:branch_name) { 'master' } let(:branch_name) { 'master' }
let(:commit_shas) { %w(b83d6e3 5a62481) } let(:commit_shas) { %w(b83d6e3 5a62481) }
...@@ -13,7 +16,7 @@ RSpec.describe JiraConnect::SyncBranchWorker do ...@@ -13,7 +16,7 @@ RSpec.describe JiraConnect::SyncBranchWorker do
def expect_jira_sync_service_execute(args) def expect_jira_sync_service_execute(args)
expect_next_instance_of(JiraConnect::SyncService) do |instance| expect_next_instance_of(JiraConnect::SyncService) do |instance|
expect(instance).to receive(:execute).with(args) expect(instance).to receive(:execute).with(args.merge(update_sequence_id: nil))
end end
end end
...@@ -61,5 +64,31 @@ RSpec.describe JiraConnect::SyncBranchWorker do ...@@ -61,5 +64,31 @@ RSpec.describe JiraConnect::SyncBranchWorker do
subject subject
end end
end end
context 'with update_sequence_id' do
let(:update_sequence_id) { 1 }
let(:request_url) { 'https://sample.atlassian.net/rest/devinfo/0.10/bulk' }
let(:request_body) do
{
repositories: [
Atlassian::JiraConnect::Serializers::RepositoryEntity.represent(
project,
commits: project.commits_by(oids: commit_shas),
branches: [project.repository.find_branch(branch_name)],
update_sequence_id: update_sequence_id
)
]
}.to_json
end
subject { described_class.new.perform(project_id, branch_name, commit_shas, update_sequence_id) }
it 'sends the reqeust with custom update_sequence_id' do
expect(Atlassian::JiraConnect::Client).to receive(:post)
.with(URI(request_url), headers: anything, body: request_body)
subject
end
end
end end
end end
...@@ -4,14 +4,18 @@ require 'spec_helper' ...@@ -4,14 +4,18 @@ require 'spec_helper'
RSpec.describe JiraConnect::SyncMergeRequestWorker do RSpec.describe JiraConnect::SyncMergeRequestWorker do
describe '#perform' do describe '#perform' do
let(:merge_request) { create(:merge_request) } let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
let_it_be(:subscription) { create(:jira_connect_subscription, installation: create(:jira_connect_installation), namespace: group) }
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
let(:merge_request_id) { merge_request.id } let(:merge_request_id) { merge_request.id }
subject { described_class.new.perform(merge_request_id) } subject { described_class.new.perform(merge_request_id) }
it 'calls JiraConnect::SyncService#execute' do it 'calls JiraConnect::SyncService#execute' do
expect_next_instance_of(JiraConnect::SyncService) do |service| expect_next_instance_of(JiraConnect::SyncService) do |service|
expect(service).to receive(:execute).with(merge_requests: [merge_request]) expect(service).to receive(:execute).with(merge_requests: [merge_request], update_sequence_id: nil)
end end
subject subject
...@@ -26,5 +30,30 @@ RSpec.describe JiraConnect::SyncMergeRequestWorker do ...@@ -26,5 +30,30 @@ RSpec.describe JiraConnect::SyncMergeRequestWorker do
subject subject
end end
end end
context 'with update_sequence_id' do
let(:update_sequence_id) { 1 }
let(:request_url) { 'https://sample.atlassian.net/rest/devinfo/0.10/bulk' }
let(:request_body) do
{
repositories: [
Atlassian::JiraConnect::Serializers::RepositoryEntity.represent(
project,
merge_requests: [merge_request],
update_sequence_id: update_sequence_id
)
]
}.to_json
end
subject { described_class.new.perform(merge_request_id, update_sequence_id) }
it 'sends the request with custom update_sequence_id' do
expect(Atlassian::JiraConnect::Client).to receive(:post)
.with(URI(request_url), headers: anything, body: request_body)
subject
end
end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment