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
self.project = project
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|
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)
end
......
......@@ -8,7 +8,7 @@ module JiraConnect
feature_category :integrations
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)
return unless project
......@@ -16,7 +16,7 @@ module JiraConnect
branches = [project.repository.find_branch(branch_name)] if branch_name.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
......@@ -7,12 +7,12 @@ module JiraConnect
queue_namespace :jira_connect
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)
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
......@@ -3,19 +3,24 @@
module Atlassian
module JiraConnect
class Client < Gitlab::HTTP
def self.generate_update_sequence_id
Gitlab::Metrics::System.monotonic_time.to_i
end
def initialize(base_uri, shared_secret)
@base_uri = base_uri
@shared_secret = shared_secret
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 = {
repositories: [
Serializers::RepositoryEntity.represent(
project,
commits: commits,
branches: branches,
merge_requests: merge_requests
merge_requests: merge_requests,
update_sequence_id: update_sequence_id
)
]
}.to_json
......
......@@ -9,12 +9,12 @@ module Atlassian
format_with(:string) { |value| value.to_s }
expose :monotonic_time, as: :updateSequenceId
expose :update_sequence_id, as: :updateSequenceId
private
def monotonic_time
Gitlab::Metrics::System.monotonic_time.to_i
def update_sequence_id
options[:update_sequence_id] || Client.generate_update_sequence_id
end
end
end
......
......@@ -15,13 +15,13 @@ module Atlassian
end
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
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
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
......
......@@ -11,6 +11,14 @@ RSpec.describe Atlassian::JiraConnect::Client do
Timecop.freeze { example.run }
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
it "calls the API with auth headers" do
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 @@
require 'spec_helper'
RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity do
let(:update_sequence_id) { nil }
subject do
project = create(:project, :repository)
commits = [project.commit]
......@@ -13,9 +15,23 @@ RSpec.describe Atlassian::JiraConnect::Serializers::RepositoryEntity do
project,
commits: commits,
branches: branches,
merge_requests: merge_requests
merge_requests: merge_requests,
update_sequence_id: update_sequence_id
).to_json
end
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
......@@ -23,7 +23,8 @@ RSpec.describe JiraConnect::SyncService do
project: project,
commits: commits,
branches: [instance_of(Gitlab::Git::Branch)],
merge_requests: merge_requests
merge_requests: merge_requests,
update_sequence_id: anything
).and_return(return_value)
end
end
......
......@@ -4,7 +4,10 @@ require 'spec_helper'
RSpec.describe JiraConnect::SyncBranchWorker 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(:branch_name) { 'master' }
let(:commit_shas) { %w(b83d6e3 5a62481) }
......@@ -13,7 +16,7 @@ RSpec.describe JiraConnect::SyncBranchWorker do
def expect_jira_sync_service_execute(args)
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
......@@ -61,5 +64,31 @@ RSpec.describe JiraConnect::SyncBranchWorker do
subject
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
......@@ -4,14 +4,18 @@ require 'spec_helper'
RSpec.describe JiraConnect::SyncMergeRequestWorker 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 }
subject { described_class.new.perform(merge_request_id) }
it 'calls JiraConnect::SyncService#execute' do
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
subject
......@@ -26,5 +30,30 @@ RSpec.describe JiraConnect::SyncMergeRequestWorker do
subject
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
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