├── .gitignore ├── Rakefile ├── config └── routes.rb ├── spec ├── factories │ ├── issue_status.rb │ ├── tracker.rb │ ├── project.rb │ ├── user.rb │ ├── issue.rb │ └── mapping.rb ├── fixtures │ ├── custom_fields_projects.yml │ ├── trackers.yml │ ├── issue_priorities.yml │ ├── issue_statuses.yml │ ├── custom_fields_trackers.yml │ ├── mappings.yml │ ├── projects_trackers.yml │ ├── json │ │ ├── story_subject_update.json │ │ ├── story_description_update.json │ │ ├── story_restarted.json │ │ ├── story_status_update.json │ │ └── story_started.json │ ├── email_addresses.yml │ ├── projects.yml │ ├── issues.yml │ ├── custom_values.yml │ ├── custom_fields.yml │ └── users.yml ├── unit │ ├── project_patch_spec.rb │ ├── issue_patch_spec.rb │ ├── lib │ │ ├── trackmine │ │ │ ├── issues_creator_spec.rb │ │ │ ├── activity_spec.rb │ │ │ ├── story_finisher_spec.rb │ │ │ ├── pivotal_project_spec.rb │ │ │ ├── issues_updater_spec.rb │ │ │ ├── activity_reader_spec.rb │ │ │ └── issue_creator_spec.rb │ │ ├── pivotal_handler_spec.rb │ │ └── trackmine_spec.rb │ ├── trackmine_mailer_spec.rb │ └── mapping_spec.rb ├── spec_helper.rb └── cassettes │ ├── projects.yml │ ├── finish_story.yml │ ├── updating_story.yml │ ├── participant_email.yml │ ├── trackmine_activity.yml │ ├── issue_creator.yml │ ├── restarting_story.yml │ ├── pivotal_project.yml │ └── issues_creator.yml ├── app ├── views │ ├── trackmine_mailer │ │ └── error_mail.html.erb │ └── mappings │ │ ├── edit.html.erb │ │ ├── _config.html.erb │ │ ├── new.html.erb │ │ └── index.html.erb ├── models │ ├── trackmine_mailer.rb │ └── mapping.rb └── controllers │ └── mappings_controller.rb ├── lib ├── project_patch.rb ├── trackmine │ ├── credentials.rb │ ├── issues_creator.rb │ ├── story_finisher.rb │ ├── issues_restarter.rb │ ├── configuration.rb │ ├── activity_reader.rb │ ├── authentication.rb │ ├── issues_updater.rb │ ├── pivotal_project.rb │ ├── activity.rb │ ├── custom_values_creator.rb │ └── issue_creator.rb ├── pivotal_handler.rb ├── trackmine.rb └── issue_patch.rb ├── db └── migrate │ ├── 002_add_estimations_to_mappings.rb │ ├── 003_add_story_types_to_mappings.rb │ ├── 005_add_issue_id_to_story_projects.rb │ ├── 004_create_story_projects.rb │ ├── 006_remove_story_project.rb │ └── 001_create_mappings.rb ├── Gemfile ├── assets └── javascripts │ └── mappings.js ├── init.rb ├── LICENSE └── README.rdoc /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/testtask' 3 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | resources :mappings, except: :show 2 | 3 | get 'mappings/update_labels', to: 'mappings#update_labels' -------------------------------------------------------------------------------- /spec/factories/issue_status.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :issue_status do 3 | sequence(:name) { |n| "Status #{n}" } 4 | end 5 | end 6 | 7 | -------------------------------------------------------------------------------- /app/views/trackmine_mailer/error_mail.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Got error from Trackmine:

4 |

<%=h @error_message %>

5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/factories/tracker.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :tracker do 3 | sequence(:name) { |n| "Tracker #{n}" } 4 | default_status factory: :issue_status 5 | end 6 | end 7 | 8 | -------------------------------------------------------------------------------- /spec/factories/project.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :project do 3 | sequence(:name) { |n| "Redmine project #{n}" } 4 | sequence(:identifier) { |n| "project#{n}"} 5 | end 6 | end 7 | 8 | -------------------------------------------------------------------------------- /spec/factories/user.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :user do 3 | sequence(:login) { |n| "user#{n}"} 4 | sequence(:firstname) { |n| "foo#{n}"} 5 | sequence(:lastname) { |n| "bar#{n}"} 6 | admin false 7 | end 8 | 9 | end 10 | 11 | -------------------------------------------------------------------------------- /lib/project_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'project' 2 | 3 | module ProjectPatch 4 | 5 | def self.included(klass) # :nodoc: 6 | klass.class_eval do 7 | unloadable 8 | has_many :mappings, dependent: :destroy 9 | end 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/002_add_estimations_to_mappings.rb: -------------------------------------------------------------------------------- 1 | class AddEstimationsToMappings < ActiveRecord::Migration 2 | def self.up 3 | add_column :mappings, :estimations, :text 4 | end 5 | 6 | def self.down 7 | remove_column :mappings, :estimations 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/003_add_story_types_to_mappings.rb: -------------------------------------------------------------------------------- 1 | class AddStoryTypesToMappings < ActiveRecord::Migration 2 | def self.up 3 | add_column :mappings, :story_types, :text 4 | end 5 | 6 | def self.down 7 | remove_column :mappings, :story_types 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/fixtures/custom_fields_projects.yml: -------------------------------------------------------------------------------- 1 | --- 2 | custom_fields_projects_001: 3 | custom_field_id: 9 4 | project_id: 1 5 | custom_fields_projects_002: 6 | custom_field_id: 12 7 | project_id: 1 8 | custom_fields_projects_003: 9 | custom_field_id: 13 10 | project_id: 1 11 | -------------------------------------------------------------------------------- /db/migrate/005_add_issue_id_to_story_projects.rb: -------------------------------------------------------------------------------- 1 | class AddIssueIdToStoryProjects < ActiveRecord::Migration 2 | def self.up 3 | add_column :story_projects, :issue_id, :integer 4 | end 5 | 6 | def self.down 7 | remove_column :story_projects, :issue_id 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/mappings/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @mapping, :url => { :controller => :mappings, :action => :update, :id => @mapping } do |f|%> 2 | <%= render :partial => 'config' %> 3 |

4 | <%= f.submit 'Update' %> 5 | <%= link_to 'Cancel', :action => :index %> 6 |

7 | <% end %> 8 | 9 | -------------------------------------------------------------------------------- /spec/factories/issue.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :issue do 3 | project_id 1 4 | sequence(:subject) { |n| "Foo subject #{n}"} 5 | author_id 1 6 | sequence(:description) { |n| "Factory description #{n}"} 7 | priority_id 5 8 | tracker_id 1 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /spec/unit/project_patch_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe 'ProjectPatch' do 4 | context 'After loading init.rb' do 5 | context 'Project' do 6 | subject { Project.first } 7 | it { should have_many(:mappings) } 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'sinatra' 4 | gem 'pivotal-tracker', '~>0.5.13' 5 | gem 'unicode' 6 | 7 | group :development, :test do 8 | gem 'pry' 9 | end 10 | 11 | group :test do 12 | gem 'vcr' 13 | gem 'rspec' 14 | gem 'factory_girl' 15 | gem 'shoulda-matchers' 16 | gem 'webmock' 17 | end 18 | -------------------------------------------------------------------------------- /spec/unit/issue_patch_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe 'IssuePatch' do 4 | context 'After loading init.rb' do 5 | context 'Issue' do 6 | it 'has find_by_story_id method' do 7 | expect(Issue.respond_to? :find_by_story_id) 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/004_create_story_projects.rb: -------------------------------------------------------------------------------- 1 | class CreateStoryProjects < ActiveRecord::Migration 2 | def self.up 3 | create_table :story_projects do |t| 4 | t.column :story_id, :integer 5 | t.column :tracker_project_id, :integer 6 | end 7 | end 8 | 9 | def self.down 10 | drop_table :story_projects 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/models/trackmine_mailer.rb: -------------------------------------------------------------------------------- 1 | class TrackmineMailer < ActionMailer::Base 2 | 3 | def error_mail(error_message) 4 | @error_message = error_message 5 | 6 | mail( 7 | to: Trackmine.error_notification['recipient'], 8 | subject: 'Trackmine error occurred', 9 | from: Trackmine.error_notification['from'] 10 | ) 11 | end 12 | end 13 | 14 | 15 | -------------------------------------------------------------------------------- /db/migrate/006_remove_story_project.rb: -------------------------------------------------------------------------------- 1 | class RemoveStoryProject < ActiveRecord::Migration 2 | def self.up 3 | drop_table :story_projects 4 | end 5 | 6 | def self.down 7 | create_table :story_projects do |t| 8 | t.column :story_id, :integer 9 | t.column :tracker_project_id, :integer 10 | t.column :issue_id, :integer 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/fixtures/trackers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | trackers_001: 3 | name: Bug 4 | id: 1 5 | is_in_chlog: true 6 | default_status_id: 1 7 | position: 1 8 | trackers_002: 9 | name: Feature 10 | id: 2 11 | is_in_chlog: true 12 | default_status_id: 1 13 | position: 2 14 | trackers_003: 15 | name: Support 16 | id: 3 17 | is_in_chlog: false 18 | default_status_id: 1 19 | position: 3 20 | -------------------------------------------------------------------------------- /db/migrate/001_create_mappings.rb: -------------------------------------------------------------------------------- 1 | class CreateMappings < ActiveRecord::Migration 2 | def self.up 3 | create_table :mappings do |t| 4 | t.column :project_id, :integer 5 | t.column :tracker_project_id, :integer 6 | t.column :tracker_project_name, :text 7 | t.column :label, :text 8 | end 9 | end 10 | 11 | def self.down 12 | drop_table :mappings 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/factories/mapping.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :mapping do 3 | project_id 1 4 | sequence(:tracker_project_id) { |n| n} 5 | sequence(:tracker_project_name) { |n| "Foo tracker project #{n}"} 6 | sequence(:label){|n| "foolabel#{n}"} 7 | estimations { |e| e = { '1' => '1', '2' => '4', '3' => '10' } } 8 | story_types { |st| st = { 'feature' => 'Feature', 'bug' => 'Bug', 'chore' => 'Support' } } 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /assets/javascripts/mappings.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function ($) { 2 | $('#tracker_project_id').on('change', function() { 3 | var tracker_project_id = $(this).val(); 4 | $.ajax('/mappings/update_labels', { 5 | success: function(response) { 6 | $.each(response, function(index, label){ 7 | $('#mapping_label').append(""); 8 | }); 9 | }, 10 | data: { 'tracker_project_id': tracker_project_id } 11 | }); 12 | }); 13 | }); -------------------------------------------------------------------------------- /app/models/mapping.rb: -------------------------------------------------------------------------------- 1 | class Mapping < ActiveRecord::Base 2 | unloadable 3 | 4 | attr_accessible :project_id, :label, :estimations, :story_types, :tracker_project_id 5 | 6 | belongs_to :project 7 | 8 | validates :project_id, presence:true 9 | validates :tracker_project_id, presence:true 10 | validates :estimations, presence:true 11 | validates :story_types, presence:true 12 | 13 | validates_uniqueness_of :tracker_project_id, scope: :label 14 | 15 | serialize :estimations 16 | serialize :story_types 17 | end 18 | -------------------------------------------------------------------------------- /lib/trackmine/credentials.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class Credentials 3 | 4 | def initialize(user, configuration) 5 | self.user = user 6 | self.configuration = configuration 7 | end 8 | 9 | def password 10 | credentials_for_user['password'] 11 | end 12 | 13 | def token 14 | credentials_for_user['token'] 15 | end 16 | 17 | def email 18 | credentials_for_user['email'] 19 | end 20 | 21 | private 22 | 23 | attr_accessor :user, :configuration 24 | 25 | def credentials_for_user 26 | configuration[user] 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /spec/unit/lib/trackmine/issues_creator_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | 3 | describe Trackmine::IssuesCreator, vcr: { cassette_name: 'issues_creator' } do 4 | let(:activity_body) { JSON.parse(File.read(json_path(activity_name))) } 5 | let(:activity) { Trackmine::Activity.new(activity_body) } 6 | let(:creator) { Trackmine::IssuesCreator.new(activity) } 7 | 8 | describe '#run' do 9 | let(:activity_name) { 'story_started' } 10 | let(:project) { Project.find(1) } 11 | 12 | it 'create issues' do 13 | expect{ creator.run }.to change { Issue.count }.by(3) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/fixtures/issue_priorities.yml: -------------------------------------------------------------------------------- 1 | --- 2 | issue_priority_01: 3 | id: 1 4 | name: 'Low' 5 | position: 1 6 | is_default: false 7 | type: 'IssuePriority' 8 | active: true 9 | project_id: nil 10 | parent_id: nil 11 | position_name: 'lowest' 12 | issue_priority_02: 13 | name: 'Normal' 14 | position: 1 15 | is_default: false 16 | type: 'IssuePriority' 17 | active: true 18 | project_id: nil 19 | parent_id: nil 20 | position_name: 'lowest' 21 | issue_priority_03: 22 | name: 'High' 23 | position: 3 24 | is_default: false 25 | type: 'IssuePriority' 26 | active: true 27 | project_id: nil 28 | parent_id: nil 29 | position_name: 'highest' 30 | -------------------------------------------------------------------------------- /lib/trackmine/issues_creator.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class IssuesCreator 3 | 4 | def initialize(activity) 5 | self.activity = activity 6 | end 7 | 8 | def story 9 | activity.story 10 | end 11 | 12 | def labels 13 | story.labels.to_s.split(',') || [''] 14 | end 15 | 16 | def issue_attributes 17 | { 18 | project_id: activity.project_id, 19 | story: story, 20 | author: activity.author 21 | } 22 | end 23 | 24 | def run 25 | labels.each { |label| IssueCreator.new(label, issue_attributes).run } 26 | end 27 | 28 | private 29 | 30 | attr_accessor :activity 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/trackmine/story_finisher.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class StoryFinisher 3 | 4 | def initialize(story) 5 | self.story = story 6 | end 7 | 8 | def run 9 | finish_story 10 | rescue => e 11 | raise PivotalTrackerError, "Can't finish the story id:#{story_id}. #{e}" 12 | end 13 | 14 | def finish_story 15 | case story.story_type 16 | when 'feature' then story.update(current_state: 'finished') 17 | when 'bug' then story.update(current_state: 'finished') 18 | when 'chore' then story.update(current_state: 'accepted') 19 | end 20 | end 21 | 22 | private 23 | 24 | attr_accessor :story 25 | end 26 | end -------------------------------------------------------------------------------- /lib/trackmine/issues_restarter.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class IssuesRestarter 3 | 4 | def initialize(issues, activity) 5 | self.issues = issues 6 | self.activity = activity 7 | end 8 | 9 | def run 10 | issues_updater.update_issues(params) 11 | end 12 | 13 | def issues_updater 14 | Trackmine::IssuesUpdater.new(issues, activity) 15 | end 16 | 17 | private 18 | 19 | attr_accessor :issues, :activity 20 | 21 | def author 22 | activity.author 23 | end 24 | 25 | def status 26 | IssueStatus.find_by_name(ACCEPTED_STATUS) 27 | end 28 | 29 | def params 30 | {status: status, assigned_to: author} 31 | end 32 | end 33 | end -------------------------------------------------------------------------------- /spec/fixtures/issue_statuses.yml: -------------------------------------------------------------------------------- 1 | --- 2 | issue_statuses_001: 3 | id: 1 4 | name: New 5 | is_closed: false 6 | position: 1 7 | issue_statuses_002: 8 | id: 2 9 | name: Assigned 10 | is_closed: false 11 | position: 2 12 | issue_statuses_003: 13 | id: 3 14 | name: Resolved 15 | is_closed: false 16 | position: 3 17 | issue_statuses_004: 18 | name: Feedback 19 | id: 4 20 | is_closed: false 21 | position: 4 22 | issue_statuses_005: 23 | id: 5 24 | name: Closed 25 | is_closed: true 26 | position: 5 27 | issue_statuses_006: 28 | id: 6 29 | name: Rejected 30 | is_closed: true 31 | position: 6 32 | issue_statuses_007: 33 | id: 7 34 | name: Accepted 35 | is_closed: true 36 | position: 7 37 | -------------------------------------------------------------------------------- /spec/unit/trackmine_mailer_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../spec_helper' 2 | 3 | describe TrackmineMailer do 4 | describe '.error_email' do 5 | let(:error_message) { 'Something went wrong' } 6 | let(:email) { TrackmineMailer.error_mail(error_message) } 7 | 8 | it 'delivers email' do 9 | expect {email.deliver }.to change { ActionMailer::Base.deliveries.count } 10 | end 11 | 12 | it 'has right content' do 13 | expect(email.to.first).to eql Trackmine.error_notification['recipient'] 14 | expect(email.subject).to eql 'Trackmine error occurred' 15 | expect(email.body).to match /Got error from Trackmine/ 16 | expect(email.body).to match /Something went wrong/ 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/fixtures/custom_fields_trackers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | custom_fields_trackers_001: 3 | custom_field_id: 1 4 | tracker_id: 1 5 | custom_fields_trackers_002: 6 | custom_field_id: 2 7 | tracker_id: 1 8 | custom_fields_trackers_003: 9 | custom_field_id: 2 10 | tracker_id: 3 11 | custom_fields_trackers_004: 12 | custom_field_id: 6 13 | tracker_id: 1 14 | custom_fields_trackers_005: 15 | custom_field_id: 6 16 | tracker_id: 2 17 | custom_fields_trackers_006: 18 | custom_field_id: 6 19 | tracker_id: 3 20 | custom_fields_trackers_007: 21 | custom_field_id: 8 22 | tracker_id: 1 23 | custom_fields_trackers_008: 24 | custom_field_id: 8 25 | tracker_id: 2 26 | custom_fields_trackers_009: 27 | custom_field_id: 8 28 | tracker_id: 3 29 | -------------------------------------------------------------------------------- /lib/trackmine/configuration.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class Configuration 3 | 4 | def initialize 5 | load_configuration 6 | end 7 | 8 | def trackmine_path 9 | @trackmine_path ||= File.join(Rails.root, 'config', 'trackmine.yml') 10 | end 11 | 12 | def load_configuration 13 | unless File.exist?(trackmine_path) 14 | raise MissingTrackmineConfig, 'Missing trackmine.yml configuration file in /config' 15 | end 16 | self.config = YAML.load_file(trackmine_path) 17 | end 18 | 19 | def error_notification 20 | config['error_notification'] 21 | end 22 | 23 | def credentials(user) 24 | Trackmine::Credentials.new(user, config) 25 | end 26 | 27 | private 28 | 29 | attr_accessor :config 30 | end 31 | end -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require 'redmine' 2 | require 'pivotal_tracker' 3 | require 'unicode' 4 | 5 | Rails.logger.info 'Starting Trackmine Plugin for Redmine' 6 | 7 | require_dependency 'project_patch' 8 | require_dependency 'issue_patch' 9 | 10 | Rails.application.config.middleware.insert_before(Rack::Runtime, 'PivotalHandler' ) 11 | 12 | Issue.send(:include, IssuePatch) 13 | Project.send(:include, ProjectPatch) 14 | 15 | Trackmine.set_error_notification 16 | 17 | Redmine::Plugin.register :redmine_trackmine do 18 | name 'Redmine Trackmine plugin' 19 | author 'Piotr Brudny' 20 | description 'This plugin integrates Redmine projects with Pivotal Tracker' 21 | version '2.0.1' 22 | 23 | menu :admin_menu, :mapping, {controller: :mappings, action: 'index'}, caption: 'Trackmine', last: true 24 | end 25 | -------------------------------------------------------------------------------- /lib/trackmine/activity_reader.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class ActivityReader 3 | 4 | def initialize(activity) 5 | self.activity = activity 6 | end 7 | 8 | def run 9 | if issues.present? 10 | update_issues 11 | elsif activity.story_started? 12 | Trackmine::IssuesCreator.new(activity).run 13 | end 14 | end 15 | 16 | def update_issues 17 | if activity.story_started? 18 | Trackmine::IssuesRestarter.new(issues, activity).run 19 | elsif activity.story_edited? 20 | Trackmine::IssuesUpdater.new(issues, activity).run 21 | end 22 | end 23 | 24 | def issues 25 | @issues ||= Issue.find_by_story_id(activity.story_id) 26 | end 27 | 28 | private 29 | 30 | attr_accessor :activity 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /spec/unit/mapping_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../spec_helper', __FILE__) 2 | 3 | describe Mapping do 4 | let(:mapping) { create :mapping } 5 | 6 | it { should belong_to(:project) } 7 | 8 | describe 'validations' do 9 | it { should validate_presence_of(:project_id) } 10 | it { should validate_presence_of(:tracker_project_id) } 11 | it { should validate_uniqueness_of(:tracker_project_id).scoped_to(:label) } 12 | it { should validate_presence_of(:estimations) } 13 | it { should validate_presence_of(:story_types) } 14 | end 15 | 16 | it 'stores hash in estimations attribute' do 17 | expect(mapping.estimations['2']).to eql '4' 18 | end 19 | 20 | it 'stores hash in story_types attribute' do 21 | expect(mapping.story_types['feature']).to eql 'Feature' 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /spec/unit/lib/trackmine/activity_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | 3 | describe Trackmine::Activity, vcr: { cassette_name: 'trackmine_activity' } do 4 | let(:activity_body) { JSON.parse(File.read(json_path(activity_name))) } 5 | let(:activity) { Trackmine::Activity.new(activity_body) } 6 | let(:creator) { Trackmine::IssuesCreator.new(activity) } 7 | 8 | describe '#story' do 9 | context 'correct activity data' do 10 | let(:activity_name) { 'story_started' } 11 | 12 | it 'returns Story object' do 13 | expect(activity.story).to be_kind_of(PivotalTracker::Story) 14 | end 15 | end 16 | 17 | context 'wrong activity data' do 18 | let(:activity_body) { { a:1 }.to_json } 19 | it { expect { activity.story }.to raise_error(Trackmine::PivotalTrackerError) } 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/fixtures/mappings.yml: -------------------------------------------------------------------------------- 1 | --- 2 | mappings_001: 3 | project_id: 1 4 | tracker_project_id: 1327280 5 | tracker_project_name: 6 | label: 'sank' 7 | estimations: 8 | '1': '1' 9 | '2': '4' 10 | '3': '10' 11 | story_types: 12 | feature: 'Feature' 13 | bug: 'Bug' 14 | chore: 'Support' 15 | mappings_002: 16 | project_id: 2 17 | tracker_project_id: 1327280 18 | tracker_project_name: 19 | label: 'snieg' 20 | estimations: 21 | '1': '1' 22 | '2': '4' 23 | '3': '10' 24 | story_types: 25 | feature: 'Feature' 26 | bug: 'Bug' 27 | chore: 'Support' 28 | mappings_003: 29 | project_id: 3 30 | tracker_project_id: 1327280 31 | tracker_project_name: 32 | label: 'zima' 33 | estimations: 34 | '1': '1' 35 | '2': '4' 36 | '3': '10' 37 | story_types: 38 | feature: 'Feature' 39 | bug: 'Bug' 40 | chore: 'Support' -------------------------------------------------------------------------------- /lib/trackmine/authentication.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | module Authentication 3 | class << self 4 | 5 | def set_token(email) 6 | credentials = Trackmine::Configuration.new.credentials(email) || 7 | raise(MissingCredentials, "Missing credentials for #{email} in trackmine.yml") 8 | 9 | credentials.token ? set_token_from_config(credentials) : set_token_from_email(credentials) 10 | PivotalTracker::Client.use_ssl = true 11 | rescue => e 12 | raise WrongCredentials.new("Wrong Pivotal Tracker credentials in trackmine.yml. #{e}") 13 | end 14 | 15 | def set_token_from_config(credentials) 16 | @token = credentials.token 17 | PivotalTracker::Client.token = @token 18 | end 19 | 20 | def set_token_from_email(credentials) 21 | @token = PivotalTracker::Client.token(credentials.email, credentials.password) 22 | end 23 | 24 | end 25 | end 26 | end 27 | 28 | -------------------------------------------------------------------------------- /spec/unit/lib/trackmine/story_finisher_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | 3 | describe Trackmine::StoryFinisher do 4 | let(:story) { double :story, story_type: story_type } 5 | let(:finisher) { Trackmine::StoryFinisher.new(story) } 6 | 7 | context 'when feature' do 8 | let(:story_type) { 'feature' } 9 | it 'changes story state to finished' do 10 | expect(story).to receive(:update).with(current_state: 'finished') 11 | finisher.run 12 | end 13 | end 14 | 15 | context 'when bug' do 16 | let(:story_type) { 'bug' } 17 | it 'changes story state to finished' do 18 | expect(story).to receive(:update).with(current_state: 'finished') 19 | finisher.run 20 | end 21 | end 22 | 23 | context 'when chore' do 24 | let(:story_type) { 'chore' } 25 | it 'changes story state to accepted' do 26 | expect(story).to receive(:update).with(current_state: 'accepted') 27 | finisher.run 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/views/mappings/_config.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | Pivotal Tracker estimation points to Redmine estimated hours 5 |

6 | 7 | <% unless @mapping.estimations.blank? %> 8 | <% @mapping.estimations.each do |pair| %> 9 | 10 |

11 | Estimation points: <%= label_tag pair[0] %> => 12 | <%= text_field_tag "estimations[#{pair[0]}]", pair[1], :size => 1 %> hours 13 |

14 | 15 | <% end %> 16 | <% end %> 17 | 18 |
19 | 20 |
21 | 22 |

23 | Pivotal Tracker story type to Redmine tracker name 24 |

25 | 26 | <% unless @mapping.story_types.blank? %> 27 | <% @mapping.story_types.each do |pair| %> 28 | 29 |

30 |

"<%= label_tag pair[0] %>"

to 31 | <%= select_tag "story_types[#{pair[0]}]", options_for_select(Tracker.all.map { |t| [t.name, t.name] }, pair[1] ) %> 32 |

33 | 34 | <% end %> 35 | <% end %> 36 | 37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /spec/fixtures/projects_trackers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | projects_trackers_001: 3 | project_id: 4 4 | tracker_id: 3 5 | projects_trackers_002: 6 | project_id: 1 7 | tracker_id: 1 8 | projects_trackers_003: 9 | project_id: 5 10 | tracker_id: 1 11 | projects_trackers_004: 12 | project_id: 1 13 | tracker_id: 2 14 | projects_trackers_005: 15 | project_id: 5 16 | tracker_id: 2 17 | projects_trackers_006: 18 | project_id: 5 19 | tracker_id: 3 20 | projects_trackers_007: 21 | project_id: 2 22 | tracker_id: 1 23 | projects_trackers_008: 24 | project_id: 2 25 | tracker_id: 2 26 | projects_trackers_009: 27 | project_id: 2 28 | tracker_id: 3 29 | projects_trackers_010: 30 | project_id: 3 31 | tracker_id: 2 32 | projects_trackers_011: 33 | project_id: 3 34 | tracker_id: 3 35 | projects_trackers_012: 36 | project_id: 4 37 | tracker_id: 1 38 | projects_trackers_013: 39 | project_id: 4 40 | tracker_id: 2 41 | projects_trackers_014: 42 | project_id: 1 43 | tracker_id: 3 44 | projects_trackers_015: 45 | project_id: 6 46 | tracker_id: 1 47 | -------------------------------------------------------------------------------- /spec/unit/lib/trackmine/pivotal_project_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | 3 | describe Trackmine::PivotalProject, vcr: { cassette_name: 'pivotal_project' } do 4 | let(:pivotal_project) { Trackmine::PivotalProject.new(1325832) } 5 | 6 | describe '#labels' do 7 | let(:labels) { pivotal_project.labels } 8 | 9 | it 'returns an array' do 10 | expect(labels).to be_kind_of(Array) 11 | end 12 | 13 | it 'returns an array of project labels' do 14 | expect(%w(deployment admin epic).in?(labels)) 15 | end 16 | end 17 | 18 | describe '#participant_email', vcr: { cassette_name: 'participant_email' } do 19 | context 'with wrong attributes' do 20 | it 'raises error' do 21 | expect { pivotal_project.participant_email('noname') } 22 | .to raise_error(Trackmine::PivotalTrackerError) 23 | end 24 | end 25 | 26 | context 'with correct attributes' do 27 | it 'returns authors email' do 28 | expect(pivotal_project.participant_email('Pedro')).to eq 'pbrudny@gmail.com' 29 | end 30 | end 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Capita Unternehmensberatung GmbH 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /app/views/mappings/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= javascript_include_tag 'mappings', plugin: 'redmine_trackmine' %> 2 | 3 |

Map Redmine project to Pivotal Tracker project and labels.

4 | <%= form_for :mapping , :url => { controller: :mappings, action: :create } do |f| %> 5 | 6 |
7 | 8 | 11 | 12 |

13 | <%= f.select :project_id, options_for_select(@projects.map { |p| [p.name, p.id]}) %>
14 |

15 | 16 | 17 | 18 |

19 | <%= select_tag 'tracker_project_id', options_for_select(@tracker_projects.map { |p| [p.name, p.id] } ) %>
20 |

21 | 22 | 23 | 24 |

25 | <%= f.select :label, @labels %>
26 |

27 | 28 |
29 | 30 | <%= render :partial => 'config' %> 31 | 32 | 33 | 34 |

35 | <%= submit_tag 'Add mapping',:name=>'save' %> 36 | <%= link_to 'Cancel', :action => :index %> 37 |

38 | 39 | <% end %> 40 | 41 | -------------------------------------------------------------------------------- /lib/pivotal_handler.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper') 2 | require 'sinatra' 3 | 4 | class PivotalHandler < Sinatra::Base 5 | 6 | post '/pivotal_activity.json' do 7 | pivotal_body = JSON.parse(request.body.read.to_s) 8 | return [202, 'It is not a correct Pivotal Tracker message'] if pivotal_body['kind'].nil? 9 | if pivotal_body['kind'] == 'story_update_activity' 10 | begin 11 | handler_logger 'Got the post request from PivotalTracker' 12 | Trackmine.read_activity(Trackmine::Activity.new(pivotal_body)) 13 | rescue => e 14 | handler_logger("Can't consume the request from PivotalTracker: #{e}") 15 | TrackmineMailer.error_mail("Error while reading activity message from Pivotal Tracker: #{e}").deliver 16 | 17 | return [202, 'Not supported activity'] 18 | end 19 | return [200, 'Got the activity'] 20 | else 21 | return [202, 'Not supported event_type'] 22 | end 23 | end 24 | 25 | private 26 | 27 | def handler_logger(message) 28 | Rails.logger.tagged('PivotalHandler') { Rails.logger.info message } 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /spec/unit/lib/trackmine/issues_updater_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | 3 | describe Trackmine::IssuesUpdater, vcr: { cassette_name: 'issues_updater' } do 4 | let(:project) { Project.find 1 } 5 | let(:issue) { Issue.find 1 } 6 | let(:activity) { double :activity, project_id: 888 } 7 | let(:issues_updater) { Trackmine::IssuesUpdater.new([issue], activity) } 8 | 9 | describe '#run' do 10 | context 'with mapping' do 11 | let!(:mapping) { create :mapping, project: project, tracker_project_id: 888 } 12 | 13 | context 'description changed' do 14 | let(:params) { { description: 'new description' } } 15 | 16 | it 'update issues description' do 17 | issues_updater.update_issues(params) 18 | expect(issue.description).to eq 'new description' 19 | end 20 | end 21 | 22 | context 'subject changed' do 23 | let(:params) { { subject: 'new subject' } } 24 | 25 | it 'update issues subject' do 26 | issues_updater.update_issues(params) 27 | expect(issue.subject).to eq 'new subject' 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/views/mappings/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to 'New mapping', {:action => 'new'}, :class => 'icon icon-add' %> 3 |
4 | 5 |

All mappings between Redmine projects and Pivotal Tracker projects (or labels)

6 | 7 | 8 | 9 | 10 | 11 | 14 | 17 | 20 | 22 | 23 | 24 | 25 | 26 | <% @mappings.each do |mapping|%> 27 | 28 | 31 | 34 | 37 | 43 | 44 | <% end %> 45 | 46 | 47 |
12 | Redmine project 13 | 15 | Pivotal Tracker project 16 | 18 | Label 19 | 21 |
29 | <%= mapping.project.name %> 30 | 32 | <%= mapping.tracker_project_name%> 33 | 35 | <%= mapping.label %> 36 | 38 |

39 | <%= link_to image_tag('/images/edit.png')+'Edit', edit_mapping_path(mapping) %> | 40 | <%= link_to image_tag('/images/delete.png')+'Delete', mapping_path(mapping), :method => :delete %> 41 |

42 |
48 | -------------------------------------------------------------------------------- /lib/trackmine/issues_updater.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class IssuesUpdater 3 | 4 | def initialize(issues, activity) 5 | self.issues = issues 6 | self.activity = activity 7 | end 8 | 9 | def run 10 | story_update 11 | end 12 | 13 | def story_update 14 | update_issues(params_changed) 15 | end 16 | 17 | def params_changed 18 | if new_value['description'].present? 19 | { description: "#{story_url}\r\n#{new_value['description']}" } 20 | elsif new_value['name'].present? 21 | { subject: new_value['name'] } 22 | end 23 | end 24 | 25 | def update_issues(params) 26 | issues.each { |issue| issue.update_attributes!(params) if mapping_still_exists?(issue) } 27 | end 28 | 29 | def mapping_still_exists?(issue) 30 | issue.project.mappings.where(tracker_project_id: pivotal_project_id).present? 31 | end 32 | 33 | private 34 | 35 | attr_accessor :issues, :activity 36 | 37 | def story_url 38 | activity.story.url 39 | end 40 | 41 | def new_value 42 | activity.new_value 43 | end 44 | 45 | def pivotal_project_id 46 | activity.project_id 47 | end 48 | end 49 | end -------------------------------------------------------------------------------- /spec/fixtures/json/story_subject_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "story_update_activity", 3 | "guid": "1327280_9", 4 | "project_version": 9, 5 | "message": "Pedro edited this feature", 6 | "highlight": "edited", 7 | "changes": [ 8 | { 9 | "kind": "story", 10 | "change_type": "update", 11 | "id": 94184406, 12 | "original_values": { 13 | "name": "jazda na sankach", 14 | "updated_at": 1431098967000 15 | }, 16 | "new_values": { 17 | "name": "jazda na sankach w parach", 18 | "updated_at": 1431099317000 19 | }, 20 | "name": "jazda na sankach w parach", 21 | "story_type": "feature" 22 | } 23 | ], 24 | "primary_resources": [ 25 | { 26 | "kind": "story", 27 | "id": 94184406, 28 | "name": "jazda na sankach w parach", 29 | "story_type": "feature", 30 | "url": "https://www.pivotaltracker.com/story/show/94184406" 31 | } 32 | ], 33 | "project": { 34 | "kind": "project", 35 | "id": 1327280, 36 | "name": "ziomal" 37 | }, 38 | "performed_by": { 39 | "kind": "person", 40 | "id": 1651216, 41 | "name": "Pedro", 42 | "initials": "PE" 43 | }, 44 | "occurred_at": 1431099317000 45 | } -------------------------------------------------------------------------------- /lib/trackmine/pivotal_project.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class PivotalProject 3 | 4 | def initialize(project_id) 5 | self.project_id = project_id 6 | set_super_token 7 | end 8 | 9 | def project 10 | PivotalTracker::Project.find(project_id) 11 | rescue => e 12 | raise PivotalTrackerError, "Can't get Pivotal Project id: #{project_id}. #{e}" 13 | end 14 | 15 | def story(story_id) 16 | project.stories.find(story_id) 17 | rescue => e 18 | raise PivotalTrackerError, "Can't get story: #{story_id} from Pivotal Tracker project: #{project_id}. #{e}" 19 | end 20 | 21 | def labels 22 | project.stories.all 23 | .select { |s| !s.labels.nil?} 24 | .map { |s| Unicode.downcase(s.labels) }.join(',').split(',').uniq 25 | end 26 | 27 | def participant_email(name) 28 | project.memberships.all.select { |m| m.name == name }[0].email 29 | rescue => e 30 | raise PivotalTrackerError, "Can't get Tracker user: #{name} in project id: #{project_id}. #{e}" 31 | end 32 | 33 | private 34 | 35 | attr_accessor :project_id 36 | 37 | def set_super_token 38 | Trackmine::Authentication.set_token('super_user') if @token.nil? 39 | end 40 | end 41 | end -------------------------------------------------------------------------------- /spec/unit/lib/trackmine/activity_reader_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | 3 | describe Trackmine::ActivityReader, vcr: { cassette_name: 'activity_reader' } do 4 | let(:reader) { Trackmine::ActivityReader.new(activity) } 5 | 6 | context 'story started' do 7 | context 'no issues yet' do 8 | let(:activity) { double :activity, :'story_edited?'=> false, story_id: 1, :'story_started?'=> true } 9 | it 'runs Trackmine::IssuesCreator' do 10 | expect_any_instance_of(Trackmine::IssuesCreator).to receive(:run) 11 | reader.run 12 | end 13 | end 14 | 15 | context 'issues exist' do 16 | let(:activity) { double :activity, :'story_edited?'=> false, story_id: 92844256, :'story_started?'=> true } 17 | it 'runs Trackmine::IssuesRestarter' do 18 | expect_any_instance_of(Trackmine::IssuesRestarter).to receive(:run) 19 | reader.run 20 | end 21 | end 22 | end 23 | 24 | context 'story edited' do 25 | let(:activity) { double :activity, :'story_edited?'=> true, story_id: 92844256, :'story_started?'=> false } 26 | it 'runs Trackmine::IssuesUpdater' do 27 | expect_any_instance_of(Trackmine::IssuesUpdater).to receive(:run) 28 | reader.run 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/fixtures/json/story_description_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "story_update_activity", 3 | "guid": "1327280_10", 4 | "project_version": 10, 5 | "message": "Pedro edited this feature", 6 | "highlight": "edited", 7 | "changes": [ 8 | { 9 | "kind": "story", 10 | "change_type": "update", 11 | "id": 94184406, 12 | "original_values": { 13 | "description": "jazda z gorki w dol po sniegu", 14 | "updated_at": 1431099317000 15 | }, 16 | "new_values": { 17 | "description": "jazda z gorki w dol po sniegu w parach czyli we dwoje\n", 18 | "updated_at": 1431099420000 19 | }, 20 | "name": "jazda na sankach w parach", 21 | "story_type": "feature" 22 | } 23 | ], 24 | "primary_resources": [ 25 | { 26 | "kind": "story", 27 | "id": 94184406, 28 | "name": "jazda na sankach w parach", 29 | "story_type": "feature", 30 | "url": "https://www.pivotaltracker.com/story/show/94184406" 31 | } 32 | ], 33 | "project": { 34 | "kind": "project", 35 | "id": 1327280, 36 | "name": "ziomal" 37 | }, 38 | "performed_by": { 39 | "kind": "person", 40 | "id": 1651216, 41 | "name": "Pedro", 42 | "initials": "PE" 43 | }, 44 | "occurred_at": 1431099420000 45 | } -------------------------------------------------------------------------------- /lib/trackmine/activity.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class Activity 3 | 4 | def initialize(activity) 5 | self.activity = activity 6 | end 7 | 8 | def project_id 9 | activity['project']['id'] if activity['project'] 10 | end 11 | 12 | def story_id 13 | activity['primary_resources'].select { |r| r['kind'] == 'story' }.first['id'] if activity['primary_resources'] 14 | end 15 | 16 | def story_started? 17 | activity['highlight'] == 'started' && activity['kind'] == 'story_update_activity' 18 | end 19 | 20 | def story_edited? 21 | activity['highlight'] == 'edited' && activity['kind'] == 'story_update_activity' 22 | end 23 | 24 | def author_name 25 | activity['performed_by']['name'] 26 | end 27 | 28 | def author 29 | email = project.participant_email(author_name) 30 | User.find_by_mail(email) 31 | end 32 | 33 | def project 34 | @pivotal_project ||= Trackmine::PivotalProject.new(project_id) 35 | end 36 | 37 | def new_value 38 | activity['changes'].select { |r| r['kind'] == 'story' }.first['new_values'] if activity['changes'] 39 | end 40 | 41 | def story 42 | @story ||= project.story(story_id) 43 | end 44 | 45 | private 46 | 47 | attr_accessor :activity 48 | 49 | end 50 | end -------------------------------------------------------------------------------- /spec/unit/lib/pivotal_handler_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../spec_helper' 2 | require 'rack/test' 3 | 4 | describe PivotalHandler do 5 | include Rack::Test::Methods 6 | 7 | def app 8 | PivotalHandler.new 9 | end 10 | 11 | context 'pivotal_callback' do 12 | 13 | describe 'POST /pivotal_activity.json' do 14 | 15 | context 'when wrong body' do 16 | let!(:post_request) { post '/pivotal_activity.json', { a:1 }.to_json } 17 | 18 | it 'returns 202' do 19 | expect(last_response.status).to be == 202 20 | end 21 | 22 | it 'returns wrong message' do 23 | expect(last_response.body).to be == 'It is not a correct Pivotal Tracker message' 24 | end 25 | end 26 | 27 | context 'when proper body' do 28 | before { expect(Trackmine).to receive(:read_activity) } 29 | 30 | let(:story_update) do 31 | { kind: 'story_update_activity', highlight: 'started', guid: '1327280_10' } 32 | end 33 | 34 | let!(:post_request) { post('/pivotal_activity.json', story_update.to_json, content_type: :json) } 35 | 36 | it 'returns 200' do 37 | expect(last_response.status).to be == 200 38 | end 39 | 40 | it 'returns success message' do 41 | expect(last_response.body).to be == 'Got the activity' 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/trackmine/custom_values_creator.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class CustomValuesCreator 3 | 4 | def initialize(project_id, story_id, issue_id) 5 | self.project_id = project_id 6 | self.story_id = story_id 7 | self.issue_id = issue_id 8 | end 9 | 10 | def run 11 | create_custom_values 12 | end 13 | 14 | def custom_field_pivotal_story_id 15 | CustomField.find_by(name: 'Pivotal Story ID').id 16 | end 17 | 18 | def custom_field_pivotal_project_id 19 | CustomField.find_by(name: 'Pivotal Project ID').id 20 | end 21 | 22 | def create_custom_values 23 | CustomValue.create!( 24 | customized_type: Issue, 25 | custom_field_id: custom_field_pivotal_project_id, 26 | customized_id: issue_id, 27 | value: project_id 28 | ) 29 | 30 | CustomValue.create!( 31 | customized_type: Issue, 32 | custom_field_id: custom_field_pivotal_story_id, 33 | customized_id: issue_id, 34 | value: story_id 35 | ) 36 | end 37 | 38 | def add_comments 39 | story.notes.all.each do |note| 40 | user = User.find_by_mail(get_user_email(story.project_id, note.author)) 41 | journal = issue.journals.new(notes: note.text) 42 | journal.user_id = user.id unless user.nil? 43 | journal.save 44 | end 45 | end 46 | 47 | private 48 | 49 | attr_accessor :project_id, :story_id, :issue_id 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # Load test_helper from Redmine main project 2 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper') 3 | require 'bundler' 4 | Bundler.setup(:default, :spec) 5 | require 'factory_girl' 6 | require 'rack/test' 7 | require 'vcr' 8 | 9 | # load factories manually. Otherwise load it from redmine app. 10 | # if (!FactoryGirl.factories || FactoryGirl.factories.empty?) 11 | Dir.glob(File.dirname(__FILE__) + "/factories/*.rb").each do |factory| 12 | require factory 13 | end 14 | 15 | RSpec.configure do |config| 16 | config.color = true 17 | config.tty = true 18 | config.formatter = :documentation 19 | config.include FactoryGirl::Syntax::Methods 20 | config.treat_symbols_as_metadata_keys_with_true_values = true 21 | end 22 | 23 | VCR.configure do |c| 24 | c.cassette_library_dir = File.dirname(__FILE__) + '/cassettes' 25 | c.hook_into :webmock 26 | c.configure_rspec_metadata! 27 | end 28 | 29 | def json_path(fixture) 30 | File.dirname(__FILE__) + "/fixtures/json/#{fixture}.json" 31 | end 32 | 33 | 34 | def fixtures 35 | %i( 36 | users 37 | email_addresses 38 | projects 39 | custom_fields 40 | custom_fields_projects 41 | custom_fields_trackers 42 | custom_values 43 | trackers 44 | projects_trackers 45 | issue_statuses 46 | issue_priorities 47 | issues 48 | mappings 49 | ) 50 | end 51 | 52 | ActiveRecord::FixtureSet.create_fixtures(File.dirname(__FILE__) + '/fixtures/', fixtures) 53 | -------------------------------------------------------------------------------- /spec/fixtures/email_addresses.yml: -------------------------------------------------------------------------------- 1 | --- 2 | email_address_001: 3 | id: 1 4 | user_id: 1 5 | address: admin@somenet.foo 6 | is_default: true 7 | created_on: 2006-07-19 19:34:07 +02:00 8 | updated_on: 2006-07-19 19:34:07 +02:00 9 | email_address_002: 10 | id: 2 11 | user_id: 2 12 | address: pbrudny@gmail.com 13 | is_default: true 14 | created_on: 2006-07-19 19:34:07 +02:00 15 | updated_on: 2006-07-19 19:34:07 +02:00 16 | email_address_003: 17 | id: 3 18 | user_id: 3 19 | address: dlopper@somenet.foo 20 | is_default: true 21 | created_on: 2006-07-19 19:34:07 +02:00 22 | updated_on: 2006-07-19 19:34:07 +02:00 23 | email_address_004: 24 | id: 4 25 | user_id: 4 26 | address: rhill@somenet.foo 27 | is_default: true 28 | created_on: 2006-07-19 19:34:07 +02:00 29 | updated_on: 2006-07-19 19:34:07 +02:00 30 | email_address_005: 31 | id: 5 32 | user_id: 5 33 | address: dlopper2@somenet.foo 34 | is_default: true 35 | created_on: 2006-07-19 19:34:07 +02:00 36 | updated_on: 2006-07-19 19:34:07 +02:00 37 | email_address_007: 38 | id: 7 39 | user_id: 7 40 | address: someone@foo.bar 41 | is_default: true 42 | created_on: 2006-07-19 19:34:07 +02:00 43 | updated_on: 2006-07-19 19:34:07 +02:00 44 | email_address_008: 45 | id: 8 46 | user_id: 8 47 | address: miscuser8@foo.bar 48 | is_default: true 49 | created_on: 2006-07-19 19:34:07 +02:00 50 | updated_on: 2006-07-19 19:34:07 +02:00 51 | email_address_009: 52 | id: 9 53 | user_id: 9 54 | address: miscuser9@foo.bar 55 | is_default: true 56 | created_on: 2006-07-19 19:34:07 +02:00 57 | updated_on: 2006-07-19 19:34:07 +02:00 58 | -------------------------------------------------------------------------------- /spec/unit/lib/trackmine/issue_creator_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../../spec_helper' 2 | 3 | describe Trackmine::IssueCreator, vcr: { cassette_name: 'issue_creator' } do 4 | let(:label) { 'zima' } 5 | let(:author) { User.find(2) } 6 | let(:project_id) { '1327280' } 7 | let(:story) { Trackmine::PivotalProject.new(project_id).story(94184406) } 8 | let(:issue_attributes) do 9 | { 10 | project_id: '1327280', 11 | story: story, 12 | author: author 13 | } 14 | end 15 | let(:creator) { Trackmine::IssueCreator.new(label, issue_attributes) } 16 | 17 | describe '#run' do 18 | let(:issue) { Issue.find_by_story_id(94184406).last } 19 | it 'creates custom values' do 20 | expect{ creator.run }.to change { CustomValue.count }.by(4) 21 | end 22 | 23 | it 'runs CustomValueCreator' do 24 | expect_any_instance_of(Trackmine::CustomValuesCreator).to receive(:run) 25 | creator.run 26 | end 27 | 28 | it 'create a proper Feature issue' do 29 | expect(issue).to be_an_instance_of Issue 30 | expect(issue.subject).to eq 'jazda na sankach w trojkach' 31 | expect(issue.description).to eq "https://www.pivotaltracker.com/story/show/94184406\r\njazda z gorki w dol po sniegu w parach czyli w trzy osoby\r\n" 32 | expect(issue.tracker.name).to eq 'Feature' 33 | expect(issue.status.name).to eq 'Accepted' 34 | expect(issue.estimated_hours).to eq 1.0 35 | expect(issue.author.mail).to eq 'pbrudny@gmail.com' 36 | expect(issue.author_id).to eq issue.assigned_to_id 37 | expect(issue.pivotal_project_id).to eq 1327280 38 | expect(issue.pivotal_story_id).to eq 94184406 39 | expect(issue.journals.size).to eq 0 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/trackmine.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | ACCEPTED_STATUS = 'Accepted' 3 | 4 | WrongActivityData = Class.new(StandardError) 5 | MissingTrackmineConfig = Class.new(StandardError) 6 | MissingCredentials = Class.new(StandardError) 7 | WrongCredentials = Class.new(StandardError) 8 | MissingTrackmineMapping = Class.new(StandardError) 9 | WrongTrackmineConfiguration = Class.new(StandardError) 10 | PivotalTrackerError = Class.new(StandardError) 11 | 12 | class << self 13 | attr_writer :error_notification 14 | 15 | def set_error_notification 16 | @error_notification = Trackmine::Configuration.new.error_notification 17 | end 18 | 19 | def error_notification 20 | @error_notification 21 | end 22 | 23 | def projects 24 | PivotalTracker::Project.all 25 | end 26 | 27 | def set_token(email) 28 | Trackmine::Authentication.set_token(email) 29 | end 30 | 31 | def project_labels(tracker_project_id) 32 | Trackmine::PivotalProject.new(tracker_project_id).labels 33 | end 34 | 35 | def read_activity(activity) 36 | Trackmine::ActivityReader.new(activity).run 37 | end 38 | 39 | def create_issues(activity) 40 | Trackmine::IssuesCreator.new(activity).run 41 | end 42 | 43 | def get_user_email(project_id, name) 44 | Trackmine::PivotalProject.new(project_id).participant_email(name) 45 | end 46 | 47 | def get_mapping(tracker_project_id, label) 48 | Mapping.where(['tracker_project_id=? AND label=? ', tracker_project_id, label.to_s]).first 49 | end 50 | 51 | def finish_story(project_id, story_id) 52 | story = Trackmine::PivotalProject.new(project_id).story(story_id) 53 | Trackmine::StoryFinisher.new(story).run 54 | end 55 | end 56 | end 57 | 58 | -------------------------------------------------------------------------------- /lib/issue_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'issue' 2 | 3 | module IssuePatch 4 | 5 | def self.included(klass) # :nodoc: 6 | 7 | klass.class_eval do 8 | unloadable # Send unloadable so it will not be unloaded in development 9 | 10 | before_update :finish_story_when_closed_or_rejected 11 | 12 | def self.find_by_story_id(story_id) 13 | Issue.joins({custom_values: :custom_field}) 14 | .where("custom_fields.name=? AND custom_values.value=?", 'Pivotal Story ID', story_id.to_s) 15 | end 16 | 17 | def pivotal_custom_value(name) 18 | CustomValue.joins(:custom_field).where(custom_fields: {name: name}, customized_id: self.id).first 19 | end 20 | 21 | def pivotal_project_id=(project_id) 22 | pivotal_custom_value('Pivotal Project ID').update_attributes!(value: project_id.to_s) 23 | end 24 | 25 | def pivotal_project_id 26 | pivotal_custom_value('Pivotal Project ID').try(:value).to_i 27 | end 28 | 29 | def pivotal_story_id=(story_id) 30 | pivotal_custom_value('Pivotal Story ID').update_attributes!(value: story_id.to_s) 31 | end 32 | 33 | def pivotal_story_id 34 | pivotal_custom_value('Pivotal Story ID').try(:value).to_i 35 | end 36 | 37 | def finish_story_when_closed_or_rejected 38 | Trackmine.finish_story(pivotal_project_id, pivotal_story_id) if issue_closed? && pivotal_assigned? 39 | rescue => e 40 | error_message = "Error while closing Story ID:'#{pivotal_story_id}' in Project ID:'#{pivotal_project_id}' : #{e}" 41 | TrackmineMailer.deliver_error_mail(error_message) 42 | end 43 | 44 | def issue_closed? 45 | status_id_changed? && status.is_closed? 46 | end 47 | 48 | def pivotal_assigned? 49 | pivotal_story_id != 0 || pivotal_project_id != 0 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/fixtures/projects.yml: -------------------------------------------------------------------------------- 1 | --- 2 | projects_001: 3 | created_on: 2006-07-19 19:13:59 +02:00 4 | name: eCookbook 5 | updated_on: 2006-07-19 22:53:01 +02:00 6 | id: 1 7 | description: Recipes management application 8 | homepage: http://ecookbook.somenet.foo/ 9 | is_public: true 10 | identifier: ecookbook 11 | parent_id: 12 | lft: 1 13 | rgt: 10 14 | projects_002: 15 | created_on: 2006-07-19 19:14:19 +02:00 16 | name: OnlineStore 17 | updated_on: 2006-07-19 19:14:19 +02:00 18 | id: 2 19 | description: E-commerce web site 20 | homepage: "" 21 | is_public: false 22 | identifier: onlinestore 23 | parent_id: 24 | lft: 11 25 | rgt: 12 26 | projects_003: 27 | created_on: 2006-07-19 19:15:21 +02:00 28 | name: eCookbook Subproject 1 29 | updated_on: 2006-07-19 19:18:12 +02:00 30 | id: 3 31 | description: eCookBook Subproject 1 32 | homepage: "" 33 | is_public: true 34 | identifier: subproject1 35 | parent_id: 1 36 | lft: 6 37 | rgt: 7 38 | projects_004: 39 | created_on: 2006-07-19 19:15:51 +02:00 40 | name: eCookbook Subproject 2 41 | updated_on: 2006-07-19 19:17:07 +02:00 42 | id: 4 43 | description: eCookbook Subproject 2 44 | homepage: "" 45 | is_public: true 46 | identifier: subproject2 47 | parent_id: 1 48 | lft: 8 49 | rgt: 9 50 | projects_005: 51 | created_on: 2006-07-19 19:15:51 +02:00 52 | name: Private child of eCookbook 53 | updated_on: 2006-07-19 19:17:07 +02:00 54 | id: 5 55 | description: This is a private subproject of a public project 56 | homepage: "" 57 | is_public: false 58 | identifier: private-child 59 | parent_id: 1 60 | lft: 2 61 | rgt: 5 62 | projects_006: 63 | created_on: 2006-07-19 19:15:51 +02:00 64 | name: Child of private child 65 | updated_on: 2006-07-19 19:17:07 +02:00 66 | id: 6 67 | description: This is a public subproject of a private project 68 | homepage: "" 69 | is_public: true 70 | identifier: project6 71 | parent_id: 5 72 | lft: 3 73 | rgt: 4 74 | -------------------------------------------------------------------------------- /spec/fixtures/issues.yml: -------------------------------------------------------------------------------- 1 | --- 2 | issues_001: 3 | created_on: <%= 3.days.ago.to_s(:db) %> 4 | project_id: 1 5 | updated_on: <%= 1.day.ago.to_s(:db) %> 6 | priority_id: 4 7 | subject: Cannot print recipes 8 | id: 1 9 | fixed_version_id: 10 | category_id: 1 11 | description: Unable to print recipes 12 | tracker_id: 1 13 | assigned_to_id: 14 | author_id: 2 15 | status_id: 1 16 | start_date: <%= 1.day.ago.to_date.to_s(:db) %> 17 | due_date: <%= 10.day.from_now.to_date.to_s(:db) %> 18 | root_id: 1 19 | lft: 1 20 | rgt: 2 21 | lock_version: 3 22 | issues_002: 23 | created_on: 2006-07-19 21:04:21 +02:00 24 | project_id: 1 25 | updated_on: 2006-07-19 21:09:50 +02:00 26 | priority_id: 5 27 | subject: Add ingredients categories 28 | id: 2 29 | fixed_version_id: 2 30 | category_id: 31 | description: Ingredients of the recipe should be classified by categories 32 | tracker_id: 2 33 | assigned_to_id: 3 34 | author_id: 2 35 | status_id: 2 36 | start_date: <%= 2.day.ago.to_date.to_s(:db) %> 37 | due_date: 38 | root_id: 2 39 | lft: 1 40 | rgt: 2 41 | lock_version: 3 42 | done_ratio: 30 43 | issues_003: 44 | created_on: 2006-07-19 21:07:27 +02:00 45 | project_id: 1 46 | updated_on: 2006-07-19 21:07:27 +02:00 47 | priority_id: 4 48 | subject: Error 281 when updating a recipe 49 | id: 3 50 | fixed_version_id: 51 | category_id: 52 | description: Error 281 is encountered when saving a recipe 53 | tracker_id: 1 54 | assigned_to_id: 3 55 | author_id: 2 56 | status_id: 1 57 | start_date: <%= 15.day.ago.to_date.to_s(:db) %> 58 | due_date: <%= 5.day.ago.to_date.to_s(:db) %> 59 | root_id: 3 60 | lft: 1 61 | rgt: 2 62 | issues_004: 63 | created_on: <%= 5.days.ago.to_s(:db) %> 64 | project_id: 2 65 | updated_on: <%= 2.days.ago.to_s(:db) %> 66 | priority_id: 4 67 | subject: Issue on project 2 68 | id: 4 69 | fixed_version_id: 70 | category_id: 71 | description: Issue on project 2 72 | tracker_id: 1 73 | assigned_to_id: 2 74 | author_id: 2 75 | status_id: 1 76 | root_id: 4 77 | lft: 1 78 | rgt: 2 -------------------------------------------------------------------------------- /lib/trackmine/issue_creator.rb: -------------------------------------------------------------------------------- 1 | module Trackmine 2 | class IssueCreator 3 | 4 | def initialize(label, issue_attributes) 5 | self.label = label 6 | self.issue_attributes = issue_attributes 7 | end 8 | 9 | def run 10 | create_issue 11 | add_comments 12 | end 13 | 14 | def description 15 | story.url + "\r\n" + story.description 16 | end 17 | 18 | def status 19 | IssueStatus.find_by(name: ACCEPTED_STATUS) || 20 | raise(WrongTrackmineConfiguration, "Can't find Redmine IssueStatus: #{ACCEPTED_STATUS} ") 21 | end 22 | 23 | def issue_params 24 | { 25 | subject: story.name, 26 | description: description, 27 | author_id: author.id, 28 | assigned_to_id: author.id, 29 | status_id: status.id, 30 | priority_id: 1, 31 | } 32 | end 33 | 34 | def story 35 | issue_attributes[:story] 36 | end 37 | 38 | def project_id 39 | issue_attributes[:project_id] 40 | end 41 | 42 | def author 43 | issue_attributes[:author] 44 | end 45 | 46 | def mapping_params 47 | { 48 | tracker_id: tracker.id, 49 | estimated_hours: estimated_hours 50 | } 51 | end 52 | 53 | def mapping 54 | @mapping ||= Trackmine.get_mapping(project_id, Unicode.downcase(label)) 55 | end 56 | 57 | def tracker 58 | Tracker.find_by_name(mapping.story_types[story.story_type]) 59 | end 60 | 61 | def estimated_hours 62 | mapping.estimations[story.estimate.to_s].to_i 63 | end 64 | 65 | def add_comments 66 | story.notes.all.each do |note| 67 | user = User.find_by_mail(get_user_email(story.project_id, note.author)) 68 | journal = issue.journals.create(notes: note.text, user: user) 69 | end 70 | end 71 | 72 | def create_issue 73 | return if mapping.try(:project).nil? 74 | return if tracker.nil? 75 | issue = mapping.project.issues.create!(issue_params.merge(mapping_params)) 76 | Trackmine::CustomValuesCreator.new(project_id, story.id, issue.id).run 77 | end 78 | 79 | private 80 | 81 | attr_accessor :label, :issue_attributes 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /app/controllers/mappings_controller.rb: -------------------------------------------------------------------------------- 1 | class MappingsController < ApplicationController 2 | unloadable 3 | 4 | before_filter :require_admin 5 | before_filter :set_token 6 | before_filter :set_mapping, only: [:edit, :update, :destroy] 7 | 8 | def index 9 | @mappings = Mapping.all 10 | end 11 | 12 | def new 13 | @mapping = Mapping.new 14 | @mapping.estimations = { 1 => 1, 2 => 4, 3 => 10 } 15 | @mapping.story_types = { 'feature' => 'Feature', 'bug' => 'Bug', 'chore' => 'Support' } 16 | @projects = Project.all 17 | @tracker_projects = Trackmine.projects 18 | @labels = [['..choose..','']] 19 | end 20 | 21 | def edit 22 | end 23 | 24 | def create 25 | @mapping = Mapping.new(mapping_params) 26 | 27 | @mapping.tracker_project_id = tracker_project_id 28 | @mapping.tracker_project_name = PivotalTracker::Project.find(tracker_project_id.to_i).name 29 | if @mapping.save 30 | flash[:notice] = 'Mapping was successfully added.' 31 | redirect_to action: 'index' 32 | else 33 | flash[:error] = "Can't map these projects. #{error_message}" 34 | redirect_to action: 'new' 35 | end 36 | end 37 | 38 | def update 39 | if @mapping.update_attributes(estimations: params[:estimations], story_types: params[:story_types]) 40 | flash[:notice] = 'Updated successfully.' 41 | redirect_to action: 'index' 42 | else 43 | flash[:error] = "Can't save that configuration. #{error_message}" 44 | redirect_to action: 'new' 45 | end 46 | end 47 | 48 | def destroy 49 | if @mapping.destroy 50 | flash[:notice] = 'Mapping removed.' 51 | else 52 | flash[:error] = 'Mapping could not be removed.' 53 | end 54 | redirect_to action: 'index', project_id: @project 55 | end 56 | 57 | def update_labels 58 | @labels = Trackmine.project_labels(tracker_project_id.to_i) 59 | respond_to do |format| 60 | format.json { render json: @labels } 61 | end 62 | end 63 | 64 | private 65 | 66 | def set_token 67 | Trackmine::Authentication.set_token(User.current.mail) 68 | end 69 | 70 | def mapping_params 71 | params.require(:mapping).merge(estimations: params[:estimations], story_types: params[:story_types]) 72 | end 73 | 74 | def tracker_project_id 75 | params.require(:tracker_project_id) 76 | end 77 | 78 | def set_mapping 79 | @mapping = Mapping.find(params[:id]) 80 | end 81 | 82 | def error_message 83 | @mapping.errors.full_messages.to_sentence 84 | end 85 | 86 | end 87 | -------------------------------------------------------------------------------- /spec/fixtures/custom_values.yml: -------------------------------------------------------------------------------- 1 | --- 2 | custom_values_006: 3 | customized_type: Issue 4 | custom_field_id: 2 5 | customized_id: 3 6 | id: 6 7 | value: "125" 8 | custom_values_007: 9 | customized_type: Project 10 | custom_field_id: 3 11 | customized_id: 1 12 | id: 7 13 | value: Stable 14 | custom_values_001: 15 | customized_type: Principal 16 | custom_field_id: 4 17 | customized_id: 3 18 | id: 1 19 | value: "" 20 | custom_values_002: 21 | customized_type: Principal 22 | custom_field_id: 4 23 | customized_id: 4 24 | id: 2 25 | value: 01 23 45 67 89 26 | custom_values_003: 27 | customized_type: Principal 28 | custom_field_id: 4 29 | customized_id: 2 30 | id: 3 31 | value: "01 42 50 00 00" 32 | custom_values_004: 33 | customized_type: Issue 34 | custom_field_id: 2 35 | customized_id: 1 36 | id: 4 37 | value: "125" 38 | custom_values_005: 39 | customized_type: Issue 40 | custom_field_id: 2 41 | customized_id: 2 42 | id: 5 43 | value: "" 44 | custom_values_008: 45 | customized_type: Issue 46 | custom_field_id: 1 47 | customized_id: 3 48 | id: 8 49 | value: "MySQL" 50 | custom_values_009: 51 | customized_type: Issue 52 | custom_field_id: 2 53 | customized_id: 7 54 | id: 9 55 | value: "this is a stringforcustomfield search" 56 | custom_values_010: 57 | customized_type: Issue 58 | custom_field_id: 6 59 | customized_id: 1 60 | id: 10 61 | value: "2.1" 62 | custom_values_011: 63 | customized_type: Issue 64 | custom_field_id: 6 65 | customized_id: 2 66 | id: 11 67 | value: "2.05" 68 | custom_values_012: 69 | customized_type: Issue 70 | custom_field_id: 6 71 | customized_id: 3 72 | id: 12 73 | value: "11.65" 74 | custom_values_013: 75 | customized_type: Issue 76 | custom_field_id: 6 77 | customized_id: 7 78 | id: 13 79 | value: "" 80 | custom_values_014: 81 | customized_type: Issue 82 | custom_field_id: 6 83 | customized_id: 5 84 | id: 14 85 | value: "-7.6" 86 | custom_values_015: 87 | customized_type: Enumeration 88 | custom_field_id: 7 89 | customized_id: 10 90 | id: 15 91 | value: true 92 | custom_values_016: 93 | customized_type: Enumeration 94 | custom_field_id: 7 95 | customized_id: 11 96 | id: 16 97 | value: '1' 98 | custom_values_017: 99 | customized_type: Issue 100 | custom_field_id: 8 101 | customized_id: 1 102 | id: 17 103 | value: '2009-12-01' 104 | custom_values_018: 105 | customized_type: Issue 106 | custom_field_id: 12 107 | customized_id: 1 108 | id: 18 109 | value: '1327280' 110 | custom_values_019: 111 | customized_type: Issue 112 | custom_field_id: 13 113 | customized_id: 1 114 | id: 19 115 | value: '94184406' 116 | custom_values_020: 117 | customized_type: Issue 118 | custom_field_id: 12 119 | customized_id: 2 120 | id: 20 121 | value: '1327280' 122 | custom_values_021: 123 | customized_type: Issue 124 | custom_field_id: 13 125 | customized_id: 2 126 | id: 21 127 | value: '94184406' 128 | custom_values_022: 129 | customized_type: Issue 130 | custom_field_id: 12 131 | customized_id: 3 132 | id: 22 133 | value: '1327280' 134 | custom_values_023: 135 | customized_type: Issue 136 | custom_field_id: 13 137 | customized_id: 3 138 | id: 23 139 | value: '92844256' 140 | -------------------------------------------------------------------------------- /spec/unit/lib/trackmine_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../../spec_helper' 2 | 3 | describe Trackmine do 4 | let(:activity_body) { JSON.parse(File.read(json_path(activity_name))) } 5 | let(:activity) { Trackmine::Activity.new(activity_body) } 6 | let(:read_activity) { Trackmine.read_activity(activity) } 7 | 8 | describe '.projects', vcr: { cassette_name: 'projects' } do 9 | before { Trackmine::Authentication.set_token('pbrudny@gmail.com') } 10 | let(:projects) { Trackmine.projects } 11 | 12 | it 'returns an array of available projects' do 13 | expect(projects).to be_kind_of(Array) 14 | end 15 | 16 | it 'be a project instance' do 17 | expect(projects.first).to be_an_instance_of(PivotalTracker::Project) 18 | end 19 | end 20 | 21 | describe '.get_mapping' do 22 | context 'when no Redmine project mapped' do 23 | it 'does not raise an error' do 24 | expect(Trackmine.get_mapping(1325832, 'match')).not_to raise_exception 25 | end 26 | end 27 | 28 | context 'when there is a mapping for the Redmine project' do 29 | let(:mapping) { create(:mapping, label: '') } 30 | 31 | it('return a mapping object') do 32 | expect(Trackmine.get_mapping(mapping.tracker_project_id, '')).to eq mapping 33 | end 34 | end 35 | end 36 | 37 | context 'finish_story', vcr: { cassette_name: 'finish_story' } do 38 | let(:story_id) { 94184406 } 39 | let(:project_id) { 1327280 } 40 | let(:wrong_id) { -1 } 41 | let(:story) { PivotalTracker::Story.find(story_id, project_id) } 42 | 43 | it("get response with a current_state 'finished'") do 44 | expect_any_instance_of(PivotalTracker::Story).to receive(:update).with({current_state: 'finished'}) 45 | Trackmine.finish_story(project_id, story_id) 46 | end 47 | 48 | it('raise an errors when wrong story_id given') do 49 | expect {Trackmine.finish_story(wrong_id, wrong_id)}.to raise_error(Trackmine::PivotalTrackerError) 50 | end 51 | end 52 | 53 | describe 'updating story', vcr: { cassette_name: 'updating_story' } do 54 | let(:issues) { Issue.find([2])} 55 | 56 | context 'description' do 57 | let(:activity_name) { 'story_description_update' } 58 | let(:new_description) do 59 | "https://www.pivotaltracker.com/story/show/94184406" +"\r\n"+ "jazda z gorki w dol po sniegu w parach czyli we dwoje\r\n" 60 | end 61 | 62 | it 'change an issue description in each issue' do 63 | read_activity 64 | 65 | issues.each do |issue| 66 | expect(issue.reload.description).to eq new_description 67 | end 68 | end 69 | end 70 | end 71 | 72 | describe 'restarting a story', vcr: { cassette_name: 'restarting_story' } do 73 | let(:activity_name) { 'story_restarted' } 74 | 75 | context 'there is an associated Redmine issue' do 76 | let(:issues) { Issue.find([3])} 77 | 78 | it 'change an issues status for "Accepted" in each issue' do 79 | read_activity 80 | issues.each do |issue| 81 | expect(issue.reload.status.name).to eq 'Accepted' 82 | end 83 | end 84 | 85 | it 'assigned issue to user who restarted a story' do 86 | issues.each do |issue| 87 | expect(issue.reload.assigned_to.try(:mail)).to eq 'pbrudny@gmail.com' 88 | end 89 | end 90 | end 91 | end 92 | end 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /spec/cassettes/projects.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://www.pivotaltracker.com/services/v3/projects 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | X-Trackertoken: 15 | - b545bcaeafc773ced5b1a853fffd1fc9 16 | Content-Type: 17 | - application/xml 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Cache-Control: 34 | - max-age=0, private, must-revalidate 35 | Set-Cookie: 36 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTIwNTc2YzM2YjBmYWZlN2NkZjRjYmZkZjhhMTEyOTI1BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgNz7O78KOg1uYW5vX251bWkCuQM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgeVMDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxVnpCbUsrdGVhNDRwVGV4VTh1MEJ6WFdjRWNQQkR6eW80aDRtekNyTkk0OD0GOwBG--a4ebaa53212295b92e5455c2bff063ced53ace91; 37 | path=/; HttpOnly 38 | X-Request-Id: 39 | - b6507eb6bbe029c0bdd77ec1fabf9960 40 | X-Runtime: 41 | - '0.275097' 42 | Date: 43 | - Thu, 28 May 2015 09:47:52 GMT 44 | X-Rack-Cache: 45 | - miss 46 | X-Powered-By: 47 | - Phusion Passenger 4.0.41 48 | Server: 49 | - nginx/1.6.0 + Phusion Passenger 4.0.41 50 | X-Tracker-Client-Pinger-Interval: 51 | - '12' 52 | Content-Encoding: 53 | - gzip 54 | body: 55 | encoding: ASCII-8BIT 56 | string: !binary |- 57 | H4sIAAAAAAAAA+xY3ZPbJhB/z1/B+JkGWbbvbI9OaZtp3zK9mSTPGiStZWoE 58 | GkB39f31XQH+ODvXtL3mpZFHM4ZlWZb9+LGQvfujleQBjBVa3U2mb5MJAVXp 59 | WqjmbvL5068/LCfv8jdZZ/TvUDlL3L6Duwk3hu8n+RtCDiNDG3uizqezdLGc 60 | pRnDdiAq3kL+YU8+8raTQO6DrIx5epznwHCHOhQSVOO2cR2hHDRgJvkUxV2w 61 | xJmPALvCOm5cUfN9/kEr/MvYBTms0mkUWNiKS8gTOqUpnWXsnBjYeFXpXrn8 62 | Z8MfgLz3xsjYgRp4NsJYV5x0Cho40UJUveYOhu4kT5PpgiUzNktIkqz9Rz5/ 63 | ep+xl2XEvVW9MYAan5ZRfVuCubTOKmMvsUZJoHgpoXDc7g4+LLWWwNUkd6aH 64 | jJ1zxEkPIHUl3B4ttgV01U8YJ7wBojdkRo462YxdMgYTHVQ6jObT5KTnkRj9 65 | r4QTXBbPeMUlMYaTt0GhN0WtFZyMYwunC7vVj/kUoy9Y6q+4gjTJS5A253Ur 66 | FC2lbmiFsURxx9VO947W0Em9b9EN2LSiURQ6UdENcNcbqAlmRt1jZlABN1QB 67 | 1JbUwla9HTKKalNjblEDnTYOw4ha4KbaUlSz69CRMahsIHgGXKLvCCNDA1Xq 68 | LS+FRC9gCyc0oIZMwYUrjcmhXMbiDg7bwbDklRMPg+O4eyEa52x6S6a362S5 69 | Tm5CNEr+fGYMAi6lfkRBjlfbwQo2xss1PeZYX0pR5RsuLUZVF3phCDdQbJ3r 70 | 7GH0RAgMZd/Ygqu6qLbaADYNFGAxi7gbwvcw7WtsMfx02wpXtLo+TqzOSIGp 71 | hSGW7FZ019DmAe3EEOxxQLnFbLVMVssTyvkBdKnV6sSJJGi5kHlXmr5W+x+b 72 | ofcW9cCE8wNejfDLPB7eQ230OTbGwZgMNr//BRcNmWHPdGKXa2cG8zv/7RHj 73 | JWO+HdfK2OWmzilRZuahN2Dyl0wz6HBiGESjswOw+85Z+3gs3Ka3ZwYL2706 74 | FsiQu9/TwTBnmIHf6GBAc48HgxoPBkzBa7wO5/41PSBjRO7/PY7Pk3Q5vxlx 75 | fCi+PfZ/Fcfny5sEK7nn5T2WQuI7A+4FS+ckTb9JRY/3nRG4XwfceHmhT7zm 76 | aofl1L+pkdG/WCMv1gmWyfHGNtbIF9d/X/VelpOeiPCwWCSrVTLWyP5h4+9h 77 | a3qbLq+w9UnolssRXf+z95IRXX1Evua9RAkoBdgdpxYRllrsN/QJb+qvwNpk 78 | tV7M1vhM5l/HRqz9R1jr69gRa7+ItceSFkH4TwAAAP//AwA79QzZ8hYAAA== 79 | http_version: 80 | recorded_at: Thu, 28 May 2015 09:47:52 GMT 81 | recorded_with: VCR 2.9.3 82 | -------------------------------------------------------------------------------- /spec/fixtures/custom_fields.yml: -------------------------------------------------------------------------------- 1 | --- 2 | custom_fields_001: 3 | name: Database 4 | regexp: "" 5 | is_for_all: true 6 | is_filter: true 7 | type: IssueCustomField 8 | possible_values: 9 | - MySQL 10 | - PostgreSQL 11 | - Oracle 12 | id: 1 13 | is_required: false 14 | field_format: list 15 | default_value: "" 16 | editable: true 17 | position: 2 18 | custom_fields_002: 19 | name: Searchable field 20 | min_length: 1 21 | regexp: "" 22 | is_for_all: true 23 | is_filter: true 24 | type: IssueCustomField 25 | max_length: 100 26 | possible_values: "" 27 | id: 2 28 | is_required: false 29 | field_format: string 30 | searchable: true 31 | default_value: "Default string" 32 | editable: true 33 | position: 1 34 | custom_fields_003: 35 | name: Development status 36 | regexp: "" 37 | is_for_all: false 38 | is_filter: true 39 | type: ProjectCustomField 40 | possible_values: 41 | - Stable 42 | - Beta 43 | - Alpha 44 | - Planning 45 | id: 3 46 | is_required: false 47 | field_format: list 48 | default_value: "" 49 | editable: true 50 | position: 1 51 | custom_fields_004: 52 | name: Phone number 53 | regexp: "" 54 | is_for_all: false 55 | type: UserCustomField 56 | possible_values: "" 57 | id: 4 58 | is_required: false 59 | field_format: string 60 | default_value: "" 61 | editable: true 62 | position: 1 63 | custom_fields_005: 64 | name: Money 65 | regexp: "" 66 | is_for_all: false 67 | type: UserCustomField 68 | possible_values: "" 69 | id: 5 70 | is_required: false 71 | field_format: float 72 | default_value: "" 73 | editable: true 74 | position: 2 75 | custom_fields_006: 76 | name: Float field 77 | regexp: "" 78 | is_for_all: true 79 | type: IssueCustomField 80 | possible_values: "" 81 | id: 6 82 | is_required: false 83 | field_format: float 84 | default_value: "" 85 | editable: true 86 | position: 3 87 | custom_fields_007: 88 | name: Billable 89 | regexp: "" 90 | is_for_all: false 91 | is_filter: true 92 | type: TimeEntryActivityCustomField 93 | possible_values: "" 94 | id: 7 95 | is_required: false 96 | field_format: bool 97 | default_value: "" 98 | editable: true 99 | position: 1 100 | custom_fields_008: 101 | name: Custom date 102 | regexp: "" 103 | is_for_all: true 104 | is_filter: false 105 | type: IssueCustomField 106 | possible_values: "" 107 | id: 8 108 | is_required: false 109 | field_format: date 110 | default_value: "" 111 | editable: true 112 | position: 4 113 | custom_fields_009: 114 | name: Project 1 cf 115 | regexp: "" 116 | is_for_all: false 117 | is_filter: true 118 | type: IssueCustomField 119 | possible_values: "" 120 | id: 9 121 | is_required: false 122 | field_format: date 123 | default_value: "" 124 | editable: true 125 | position: 5 126 | custom_fields_010: 127 | name: Overtime 128 | regexp: "" 129 | is_for_all: false 130 | is_filter: false 131 | type: TimeEntryCustomField 132 | possible_values: "" 133 | id: 10 134 | is_required: false 135 | field_format: bool 136 | default_value: 0 137 | editable: true 138 | position: 1 139 | custom_fields_011: 140 | id: 11 141 | name: Binary 142 | type: CustomField 143 | possible_values: 144 | - !binary | 145 | SGXDqWzDp2prc2Tigqw2NTTDuQ== 146 | - Other value 147 | field_format: list 148 | custom_fields_012: 149 | id: 12 150 | name: Pivotal Project ID 151 | type: CustomField 152 | possible_values: "" 153 | field_format: string 154 | custom_fields_013: 155 | id: 13 156 | name: Pivotal Story ID 157 | type: CustomField 158 | possible_values: "" 159 | field_format: string 160 | 161 | -------------------------------------------------------------------------------- /spec/fixtures/json/story_restarted.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "story_update_activity", 3 | "guid": "1327280_21", 4 | "project_version": 21, 5 | "message": "Pedro started this feature", 6 | "highlight": "started", 7 | "changes": [ 8 | { 9 | "kind": "label", 10 | "change_type": "update", 11 | "id": 11449356, 12 | "original_values": { 13 | "counts": { 14 | "number_of_zero_point_stories_by_state": { 15 | "accepted": 0, 16 | "started": 0, 17 | "finished": 0, 18 | "unstarted": 0, 19 | "planned": 0, 20 | "delivered": 0, 21 | "unscheduled": 0, 22 | "rejected": 0, 23 | "kind": "counts_by_story_state" 24 | }, 25 | "sum_of_story_estimates_by_state": { 26 | "accepted": 1, 27 | "started": 0, 28 | "finished": 0, 29 | "unstarted": 0, 30 | "planned": 0, 31 | "delivered": 0, 32 | "unscheduled": 0, 33 | "rejected": 0, 34 | "kind": "counts_by_story_state" 35 | }, 36 | "number_of_stories_by_state": { 37 | "accepted": 1, 38 | "started": 0, 39 | "finished": 0, 40 | "unstarted": 0, 41 | "planned": 0, 42 | "delivered": 0, 43 | "unscheduled": 0, 44 | "rejected": 0, 45 | "kind": "counts_by_story_state" 46 | }, 47 | "kind": "story_counts" 48 | } 49 | }, 50 | "new_values": { 51 | "counts": { 52 | "number_of_zero_point_stories_by_state": { 53 | "accepted": 0, 54 | "started": 0, 55 | "finished": 0, 56 | "unstarted": 0, 57 | "planned": 0, 58 | "delivered": 0, 59 | "unscheduled": 0, 60 | "rejected": 0, 61 | "kind": "counts_by_story_state" 62 | }, 63 | "sum_of_story_estimates_by_state": { 64 | "accepted": 0, 65 | "started": 1, 66 | "finished": 0, 67 | "unstarted": 0, 68 | "planned": 0, 69 | "delivered": 0, 70 | "unscheduled": 0, 71 | "rejected": 0, 72 | "kind": "counts_by_story_state" 73 | }, 74 | "number_of_stories_by_state": { 75 | "accepted": 0, 76 | "started": 1, 77 | "finished": 0, 78 | "unstarted": 0, 79 | "planned": 0, 80 | "delivered": 0, 81 | "unscheduled": 0, 82 | "rejected": 0, 83 | "kind": "counts_by_story_state" 84 | }, 85 | "kind": "story_counts" 86 | } 87 | }, 88 | "name": "niebieska" 89 | }, 90 | { 91 | "kind": "story", 92 | "change_type": "update", 93 | "id": 92844256, 94 | "original_values": { 95 | "current_state": "accepted", 96 | "accepted_at": 1432020688000, 97 | "updated_at": 1432020689000 98 | }, 99 | "new_values": { 100 | "current_state": "started", 101 | "accepted_at": null, 102 | "updated_at": 1432020710000 103 | }, 104 | "name": "tolek lolek", 105 | "story_type": "feature" 106 | } 107 | ], 108 | "primary_resources": [ 109 | { 110 | "kind": "story", 111 | "id": 92844256, 112 | "name": "tolek lolek", 113 | "story_type": "feature", 114 | "url": "https://www.pivotaltracker.com/story/show/92844256" 115 | } 116 | ], 117 | "project": { 118 | "kind": "project", 119 | "id": 1327280, 120 | "name": "ziomal" 121 | }, 122 | "performed_by": { 123 | "kind": "person", 124 | "id": 1651216, 125 | "name": "Pedro", 126 | "initials": "PE" 127 | }, 128 | "occurred_at": 1432020710000 129 | } -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Redmine Trackmine Plugin 2 | 3 | Redmine is an excellent multi-project issue tracking tool focussing on 4 | collaboration in the implementation and integration parts of software 5 | development. Pivotal Tracker, on the other hand, is a superb planning 6 | tool and extremely well suited for requirements capturing and customer 7 | feedback. 8 | 9 | The Trackmine plugin provides simple but effective bi-directional 10 | communication between Pivotal Tracker and Redmine by outsourcing the 11 | workflow between Pivotal's "Started" and "Finished" states to Redmine. The triggers for teleporting 12 | between the tools are starting a story in Pivotal and closing an issue 13 | in Redmine. The workflow in Redmine remains customizable and can be 14 | tailored to individual needs. {Step by step configuration tutorial}[http://piotrbrudny.com/post/120177000661/how-to-integrate-redmine-with-pivotal-tracker] 15 | 16 | [UPGRADED to work with Pivotal Tracker API v5 and Redmine 3] 17 | 18 | 19 | == Features 20 | 21 | * Starting a story in a Pivotal Tracker automatically creates a Redmine issue with status "Accepted". 22 | * Updating a story in a Pivotal Tracker updates the Redmine issue attributes (subject, description, status). 23 | * Closing a Redmine issue finishes the Pivotal Tracker story. 24 | * Mapping between Pivotal Tracker and Redmine attributes can be configured 25 | 26 | == Setup 27 | 28 | === Installation 29 | 30 | In Redmine app run: 31 | 32 | $ bin/plugin install https://github.com/capita/redmine_trackmine.git 33 | 34 | or clone it into /vendor/plugins/ 35 | 36 | $ git clone git@github.com:capita/redmine_trackmine.git 37 | 38 | Install gems 39 | 40 | $ bundle install 41 | 42 | Run plugin migrations 43 | 44 | $ rake redmine:plugins:migrate 45 | 46 | === Configuration- Redmine side 47 | 48 | Make sure you have following custom fields for Redmine Issue: 49 | * 'Pivotal Project ID' (field_format: int, Trackers: Bug, Support, Feature) . 50 | * 'Pivotal Story ID' (field_format: int, Trackers: Bug, Support, Feature) . 51 | 52 | And there is IssueStatus 'Accepted' 53 | 54 | Add valid Pivotal Tracker credentials in [redmine_app]/config/trackmine.yml 55 | 56 | 57 | Syntax: 58 | 59 | [redmine_email]: 60 | email: [pivotal_tracker_email] 61 | password: [pivotal_tracker_password] 62 | 63 | 64 | super_user: 65 | # You can use an api token or email/password for the PT api. 66 | token: [pivotal_tracker_api_token] 67 | 68 | error_notification: 69 | recipient: [email_for_error_notifications] 70 | from: "[information to emails FROM field]" 71 | 72 | trackmine.yml 73 | 74 | foouser@example.net: 75 | token: 1234 76 | 77 | baruser@example.net: 78 | email: baruser2@fake.net 79 | password: 2222 80 | 81 | super_user: 82 | email: admin@net.org 83 | password: 1234 84 | 85 | error_notification: 86 | recipient: errors@examplet.net 87 | from: "Trackmine Notifications " 88 | 89 | Users mentioned in trackmine.yml should have administration rights in redmine. 90 | The user named 'super_user' should have access to all Pivotal Tracker projects you want to map to. 91 | It might be the Pivotal Tracker admin. Its credentials are used to handle Pivotal Tracker hooks. 92 | 93 | === Configuration- Pivotal Tracker side 94 | 95 | Add Web Hook Url pointing to your Redmine app. To do that: 96 | 97 | * On your project page choose 'Project -> Configure Integrations' 98 | * Find Activity Web Hook section 99 | * In 'Web Hook Url' put [redmine_app_url]/pivotal_activity.json 100 | 101 | Example: 102 | 103 | http://my-company-redmine-site.org/pivotal_activity.json 104 | 105 | 106 | == Usage 107 | 108 | === Mapping 109 | 110 | * As a Redmine administrator go to 'Administration -> Trackmine' section. 111 | * Use link 'Add mapping' 112 | * Select Redmine project and Pivotal project (or label) 113 | 114 | === Mapping Configuration 115 | 116 | You can configure: 117 | 118 | * Mapping between Pivotal story estimation points and Redmine estimated_hours 119 | Default: 120 | 1 point => 1 hour 121 | 2 points => 4 hours 122 | 3 points => 10 hours 123 | 124 | 125 | * Mapping between Pivotal story_types and Redmine trackers 126 | Default: 127 | 'feature' => 'Feature' 128 | 'bug' => 'Bug' 129 | 'chore' => 'Support' 130 | 131 | === Testing 132 | 133 | Trackmine has RSpec test coverage. Run specs with: 134 | 135 | rake redmine:plugins:test 136 | 137 | == Copyright 138 | 139 | Copyright (c) 2010-2015 Capita Unternehmensberatung GmbH. See LICENSE for details. 140 | 141 | -------------------------------------------------------------------------------- /spec/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | --- 2 | users_001: 3 | created_on: 2006-07-19 19:12:21 +02:00 4 | status: 1 5 | last_login_on: 2006-07-19 22:57:52 +02:00 6 | language: en 7 | # password = admin 8 | salt: 82090c953c4a0000a7db253b0691a6b4 9 | hashed_password: b5b6ff9543bf1387374cdfa27a54c96d236a7150 10 | updated_on: 2006-07-19 22:57:52 +02:00 11 | admin: true 12 | lastname: Admin 13 | firstname: Redmine 14 | id: 1 15 | auth_source_id: 16 | mail_notification: all 17 | login: admin 18 | type: User 19 | users_002: 20 | created_on: 2006-07-19 19:32:09 +02:00 21 | status: 1 22 | last_login_on: 2006-07-19 22:42:15 +02:00 23 | language: en 24 | # password = jsmith 25 | salt: 67eb4732624d5a7753dcea7ce0bb7d7d 26 | hashed_password: bfbe06043353a677d0215b26a5800d128d5413bc 27 | updated_on: 2006-07-19 22:42:15 +02:00 28 | admin: false 29 | lastname: Smith 30 | firstname: John 31 | id: 2 32 | auth_source_id: 33 | mail_notification: all 34 | login: jsmith 35 | type: User 36 | users_003: 37 | created_on: 2006-07-19 19:33:19 +02:00 38 | status: 1 39 | last_login_on: 40 | language: en 41 | # password = foo 42 | salt: 7599f9963ec07b5a3b55b354407120c0 43 | hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed 44 | updated_on: 2006-07-19 19:33:19 +02:00 45 | admin: false 46 | lastname: Lopper 47 | firstname: Dave 48 | id: 3 49 | auth_source_id: 50 | mail_notification: all 51 | login: dlopper 52 | type: User 53 | users_004: 54 | created_on: 2006-07-19 19:34:07 +02:00 55 | status: 1 56 | last_login_on: 57 | language: en 58 | # password = foo 59 | salt: 3126f764c3c5ac61cbfc103f25f934cf 60 | hashed_password: 9e4dd7eeb172c12a0691a6d9d3a269f7e9fe671b 61 | updated_on: 2006-07-19 19:34:07 +02:00 62 | admin: false 63 | lastname: Hill 64 | firstname: Robert 65 | id: 4 66 | auth_source_id: 67 | mail_notification: all 68 | login: rhill 69 | type: User 70 | users_005: 71 | id: 5 72 | created_on: 2006-07-19 19:33:19 +02:00 73 | # Locked 74 | status: 3 75 | last_login_on: 76 | language: en 77 | hashed_password: 1 78 | updated_on: 2006-07-19 19:33:19 +02:00 79 | admin: false 80 | lastname: Lopper2 81 | firstname: Dave2 82 | auth_source_id: 83 | mail_notification: all 84 | login: dlopper2 85 | type: User 86 | users_006: 87 | id: 6 88 | created_on: 2006-07-19 19:33:19 +02:00 89 | status: 0 90 | last_login_on: 91 | language: '' 92 | hashed_password: 1 93 | updated_on: 2006-07-19 19:33:19 +02:00 94 | admin: false 95 | lastname: Anonymous 96 | firstname: '' 97 | auth_source_id: 98 | mail_notification: only_my_events 99 | login: '' 100 | type: AnonymousUser 101 | users_007: 102 | # A user who does not belong to any project 103 | id: 7 104 | created_on: 2006-07-19 19:33:19 +02:00 105 | status: 1 106 | last_login_on: 107 | language: 'en' 108 | # password = foo 109 | salt: 7599f9963ec07b5a3b55b354407120c0 110 | hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed 111 | updated_on: 2006-07-19 19:33:19 +02:00 112 | admin: false 113 | lastname: One 114 | firstname: Some 115 | auth_source_id: 116 | mail_notification: only_my_events 117 | login: someone 118 | type: User 119 | users_008: 120 | id: 8 121 | created_on: 2006-07-19 19:33:19 +02:00 122 | status: 1 123 | last_login_on: 124 | language: 'it' 125 | # password = foo 126 | salt: 7599f9963ec07b5a3b55b354407120c0 127 | hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed 128 | updated_on: 2006-07-19 19:33:19 +02:00 129 | admin: false 130 | lastname: Misc 131 | firstname: User 132 | auth_source_id: 133 | mail_notification: only_my_events 134 | login: miscuser8 135 | type: User 136 | users_009: 137 | id: 9 138 | created_on: 2006-07-19 19:33:19 +02:00 139 | status: 1 140 | last_login_on: 141 | language: 'it' 142 | hashed_password: 1 143 | updated_on: 2006-07-19 19:33:19 +02:00 144 | admin: false 145 | lastname: Misc 146 | firstname: User 147 | auth_source_id: 148 | mail_notification: only_my_events 149 | login: miscuser9 150 | type: User 151 | groups_010: 152 | id: 10 153 | lastname: A Team 154 | type: Group 155 | status: 1 156 | groups_011: 157 | id: 11 158 | lastname: B Team 159 | type: Group 160 | status: 1 161 | groups_non_member: 162 | id: 12 163 | lastname: Non member users 164 | type: GroupNonMember 165 | status: 1 166 | groups_anonymous: 167 | id: 13 168 | lastname: Anonymous users 169 | type: GroupAnonymous 170 | status: 1 171 | 172 | -------------------------------------------------------------------------------- /spec/cassettes/finish_story.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 6 | body: 7 | encoding: US-ASCII 8 | string: username=foobar%40gmail.com&password=secret&timeout=60 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | Content-Length: 15 | - '55' 16 | Content-Type: 17 | - application/x-www-form-urlencoded 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Etag: 34 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 35 | Cache-Control: 36 | - max-age=0, private, must-revalidate 37 | Set-Cookie: 38 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJWRmYWUwZTUxZTdmOTYxOTVhZGM0Y2Y4YjIxYTNjYTI1BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgMYqT78KOg1uYW5vX251bWkCRQM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgeDcDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBU--a7ed1dba66217366553182275dbeeaecb65a9fb7; 39 | path=/; HttpOnly 40 | X-Request-Id: 41 | - c4daac0925bfa1d7e944ac9b571749fd 42 | X-Runtime: 43 | - '0.111316' 44 | Date: 45 | - Thu, 28 May 2015 09:47:53 GMT 46 | X-Rack-Cache: 47 | - invalidate, pass 48 | X-Powered-By: 49 | - Phusion Passenger 4.0.41 50 | Server: 51 | - nginx/1.6.0 + Phusion Passenger 4.0.41 52 | X-Tracker-Client-Pinger-Interval: 53 | - '12' 54 | body: 55 | encoding: UTF-8 56 | string: | 57 | 58 | 59 | b545bcaeafc773ced5b1a853fffd1fc9 60 | 838284 61 | 62 | http_version: 63 | recorded_at: Thu, 28 May 2015 09:47:54 GMT 64 | - request: 65 | method: get 66 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/94184406 67 | body: 68 | encoding: US-ASCII 69 | string: '' 70 | headers: 71 | Accept: 72 | - "*/*; q=0.5, application/xml" 73 | Accept-Encoding: 74 | - gzip, deflate 75 | X-Trackertoken: 76 | - b545bcaeafc773ced5b1a853fffd1fc9 77 | Content-Type: 78 | - application/xml 79 | User-Agent: 80 | - Ruby 81 | response: 82 | status: 83 | code: 200 84 | message: OK 85 | headers: 86 | Content-Type: 87 | - application/xml; charset=utf-8 88 | Transfer-Encoding: 89 | - chunked 90 | Status: 91 | - 200 OK 92 | X-Ua-Compatible: 93 | - IE=Edge,chrome=1 94 | Cache-Control: 95 | - max-age=0, private, must-revalidate 96 | Set-Cookie: 97 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWU3ZDk5NjEzYmViZjAxNmY0NGEwNjZhNDQxMjg3ZGQwBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgNOPXr8KOg1uYW5vX251bWkB6zoNbmFub19kZW5pBjoNc3VibWljcm8iByNQOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjE2SURrcGlmYTlOUUdVbENWTFNqNTNTbUNUVjBKS28zM3FGd1hROFZxYWZRPQY7AEY%3D--87cd2da68923f5a2b7209959bf31ce17f9a6913f; 98 | path=/; HttpOnly 99 | X-Request-Id: 100 | - 7c66e557b09835d3855b6f1271d1bc1f 101 | X-Runtime: 102 | - '0.074891' 103 | Date: 104 | - Thu, 28 May 2015 09:47:54 GMT 105 | X-Rack-Cache: 106 | - miss 107 | X-Powered-By: 108 | - Phusion Passenger 4.0.41 109 | Server: 110 | - nginx/1.6.0 + Phusion Passenger 4.0.41 111 | X-Tracker-Client-Pinger-Interval: 112 | - '12' 113 | Content-Encoding: 114 | - gzip 115 | body: 116 | encoding: ASCII-8BIT 117 | string: !binary |- 118 | H4sIAAAAAAAAA3ySy27CMBBF93yFlTXFScojRMYsKnXdBayRiQcwJHZqO6TJ 119 | 19c2lEcrVcrCM3Pu1fg6ZPlVlegM2gglF1EyiiMEslBcyP0iWq/eX7JoSQfE 120 | WKU7OkCICI5sV8MiEtLCHnRE5+MkG4/jKcGCB6TW6giF3fxFk9d0lmYxwXck 121 | KIL9xvvSHTDbaCD4oeeRRpf0YG1tcozbth3V4qwsK61mxQn0qFDVRYHNQbX4 122 | vpPXeT0YKypm4ff2CcE/o8AVjdYg7cZYB9OdkMIcgBP83PckB1NoUVuXHD2y 123 | njPUo73SJ4FaxFWJaoWMFLBvXF0zt+cBFX1X+rHVfYeUUdtuQPCjj/eVrIKr 124 | oWTIMHnyUi9SR38kOBAe1fDZuO2Bb7Yd/QCuFcFPPQ+pVj4BtzpcV7u83ZjZ 125 | azDclS4piGgaJxMcuy9DySRPs3wyRevVm4vipglLNLXX/GuROItZnsT5ZH6x 126 | uGuCRcm2UBrq7zoMmQ1791gEX/supOsP+A0AAP//AwBvV9t2sAIAAA== 127 | http_version: 128 | recorded_at: Thu, 28 May 2015 09:47:54 GMT 129 | recorded_with: VCR 2.9.3 130 | -------------------------------------------------------------------------------- /spec/cassettes/updating_story.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 6 | body: 7 | encoding: US-ASCII 8 | string: username=foobar%40gmail.com&password=secret&timeout=60 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | Content-Length: 15 | - '55' 16 | Content-Type: 17 | - application/x-www-form-urlencoded 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Etag: 34 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 35 | Cache-Control: 36 | - max-age=0, private, must-revalidate 37 | Set-Cookie: 38 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJThkOTE2MDIyNjkzODU0M2EyZDg0MGI4ZjEzN2Q3N2Y4BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgAF7bb8KOg1uYW5vX251bWkC7gE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdJQDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBU--d87ffeac0f19d54c583426be587b3b3d5f2ce869; 39 | path=/; HttpOnly 40 | X-Request-Id: 41 | - df52e7dcb79a6c6a8e71ee0d55e51d68 42 | X-Runtime: 43 | - '0.116613' 44 | Date: 45 | - Thu, 28 May 2015 09:47:54 GMT 46 | X-Rack-Cache: 47 | - invalidate, pass 48 | X-Powered-By: 49 | - Phusion Passenger 4.0.41 50 | Server: 51 | - nginx/1.6.0 + Phusion Passenger 4.0.41 52 | X-Tracker-Client-Pinger-Interval: 53 | - '12' 54 | body: 55 | encoding: UTF-8 56 | string: | 57 | 58 | 59 | b545bcaeafc773ced5b1a853fffd1fc9 60 | 838284 61 | 62 | http_version: 63 | recorded_at: Thu, 28 May 2015 09:47:55 GMT 64 | - request: 65 | method: get 66 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/94184406 67 | body: 68 | encoding: US-ASCII 69 | string: '' 70 | headers: 71 | Accept: 72 | - "*/*; q=0.5, application/xml" 73 | Accept-Encoding: 74 | - gzip, deflate 75 | X-Trackertoken: 76 | - b545bcaeafc773ced5b1a853fffd1fc9 77 | Content-Type: 78 | - application/xml 79 | User-Agent: 80 | - Ruby 81 | response: 82 | status: 83 | code: 200 84 | message: OK 85 | headers: 86 | Content-Type: 87 | - application/xml; charset=utf-8 88 | Transfer-Encoding: 89 | - chunked 90 | Status: 91 | - 200 OK 92 | X-Ua-Compatible: 93 | - IE=Edge,chrome=1 94 | Cache-Control: 95 | - max-age=0, private, must-revalidate 96 | Set-Cookie: 97 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWY5MDQwZDU5MmVmZTRiMDhlYjA1MTZlYzQzMDlhNTE5BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgFzqfL8KOg1uYW5vX251bWkCWwI6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdgMDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxaEg3M2VmZGo1K0IrZnNjOERzSjlSMlVEMnFTUzZPeVFpVGhhUXJyVjNnMD0GOwBG--e9b56f2f11a1d4ba710345466469ca61660065ee; 98 | path=/; HttpOnly 99 | X-Request-Id: 100 | - 98793c5509a55dc88b97ed531cf1cc38 101 | X-Runtime: 102 | - '0.059728' 103 | Date: 104 | - Thu, 28 May 2015 09:47:55 GMT 105 | X-Rack-Cache: 106 | - miss 107 | X-Powered-By: 108 | - Phusion Passenger 4.0.41 109 | Server: 110 | - nginx/1.6.0 + Phusion Passenger 4.0.41 111 | X-Tracker-Client-Pinger-Interval: 112 | - '12' 113 | Content-Encoding: 114 | - gzip 115 | body: 116 | encoding: ASCII-8BIT 117 | string: !binary |- 118 | H4sIAAAAAAAAAwIAAAD//3ySy27CMBBF93yFlTXFScojRMYsKnXdBayRiQcw 119 | JHZqO6TJ19c2lEcrVcrCM3Pu1fg6ZPlVlegM2gglF1EyiiMEslBcyP0iWq/e 120 | X7JoSQfEWKU7OkCICI5sV8MiEtLCHnRE5+MkG4/jKcGCB6TW6giF3fxFk9d0 121 | lmYxwXckKIL9xvvSHTDbaCD4oeeRRpf0YG1tcozbth3V4qwsK61mxQn0qFDV 122 | RYHNQbX4vpPXeT0YKypm4ff2CcE/o8AVjdYg7cZYB9OdkMIcgBP83PckB1No 123 | UVuXHD2ynjPUo73SJ4FaxFWJaoWMFLBvXF0zt+cBFX1X+rHVfYeUUdtuQPCj 124 | j/eVrIKroWTIMHnyUi9SR38kOBAe1fDZuO2Bb7Yd/QCuFcFPPQ+pVj4Btzpc 125 | V7u83ZjZazDclS4piGgaJxMcuy9DySRPs3wyRevVm4vipglLNLXX/GuROItZ 126 | nsT5ZH6xuGuCRcm2UBrq7zoMmQ1791gEX/supOsP+A0AAP//AwBvV9t2sAIA 127 | AA== 128 | http_version: 129 | recorded_at: Thu, 28 May 2015 09:47:56 GMT 130 | recorded_with: VCR 2.9.3 131 | -------------------------------------------------------------------------------- /spec/cassettes/participant_email.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 6 | body: 7 | encoding: US-ASCII 8 | string: username=foobar%40gmail.com&password=secret&timeout=60 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | Content-Length: 15 | - '55' 16 | Content-Type: 17 | - application/x-www-form-urlencoded 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Etag: 34 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 35 | Cache-Control: 36 | - max-age=0, private, must-revalidate 37 | Set-Cookie: 38 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJTQ3NDlhNGQ2ZmU0N2VmY2I0MDQ2NmFkMTZiMGViMTM0BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgNgSD78KOg1uYW5vX251bWkC3QM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgeYkDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBU--b0cee2909c5980b949bd8feca9953b1f847492f5; 39 | path=/; HttpOnly 40 | X-Request-Id: 41 | - bd361017ecc35193e7afbbef8d5c05ec 42 | X-Runtime: 43 | - '0.124677' 44 | Date: 45 | - Thu, 28 May 2015 09:47:49 GMT 46 | X-Rack-Cache: 47 | - invalidate, pass 48 | X-Powered-By: 49 | - Phusion Passenger 4.0.41 50 | Server: 51 | - nginx/1.6.0 + Phusion Passenger 4.0.41 52 | X-Tracker-Client-Pinger-Interval: 53 | - '12' 54 | body: 55 | encoding: UTF-8 56 | string: | 57 | 58 | 59 | b545bcaeafc773ced5b1a853fffd1fc9 60 | 838284 61 | 62 | http_version: 63 | recorded_at: Thu, 28 May 2015 09:47:50 GMT 64 | - request: 65 | method: get 66 | uri: https://www.pivotaltracker.com/services/v3/projects/1325832 67 | body: 68 | encoding: US-ASCII 69 | string: '' 70 | headers: 71 | Accept: 72 | - "*/*; q=0.5, application/xml" 73 | Accept-Encoding: 74 | - gzip, deflate 75 | X-Trackertoken: 76 | - b545bcaeafc773ced5b1a853fffd1fc9 77 | Content-Type: 78 | - application/xml 79 | User-Agent: 80 | - Ruby 81 | response: 82 | status: 83 | code: 200 84 | message: OK 85 | headers: 86 | Content-Type: 87 | - application/xml; charset=utf-8 88 | Transfer-Encoding: 89 | - chunked 90 | Status: 91 | - 200 OK 92 | X-Ua-Compatible: 93 | - IE=Edge,chrome=1 94 | Cache-Control: 95 | - max-age=0, private, must-revalidate 96 | Set-Cookie: 97 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWU5ZWYwNjMwOTFlZGU3NjlhZWJhY2EwMTdiZGMxYmNjBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgBHJHr8KOg1uYW5vX251bWkCgwE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgc4cDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxdDJ6YTF2V21uNEloN3IybGVhRytSdWV5V2l6VUJJS0hWMW5ZdUxPamVpTT0GOwBG--2a64118a16e7640d4bd919c23eac16064510b777; 98 | path=/; HttpOnly 99 | X-Request-Id: 100 | - 5973b18dc0f505df424dea44a7622895 101 | X-Runtime: 102 | - '0.043785' 103 | Date: 104 | - Thu, 28 May 2015 09:47:50 GMT 105 | X-Rack-Cache: 106 | - miss 107 | X-Powered-By: 108 | - Phusion Passenger 4.0.41 109 | Server: 110 | - nginx/1.6.0 + Phusion Passenger 4.0.41 111 | X-Tracker-Client-Pinger-Interval: 112 | - '12' 113 | Content-Encoding: 114 | - gzip 115 | body: 116 | encoding: ASCII-8BIT 117 | string: !binary |- 118 | H4sIAAAAAAAAA4RUW0/bMBR+51dYec5w0g4oKJhtaHtDQxo8R45zmng4dmQ7 119 | 7frvd+ykTVuEkCrV+c537pfi4V+nyAask0bfJ/lllhDQwtRSN/fJ68uvL6vk 120 | gV0UvTV/QXh2QUgha5YvF1er5aKg+A6Q5h2wpx35w7teAXke2QWNeNTxYLlH 121 | H6UC3fiW+F0P94nUHhqwCcvR1Bkl6m0B3krnufVlzXfsyWj8K+gZHJi9QWOl 122 | E1wBy9I8XaTLgh6DgcSFMIP27IflGyCPMc2C7tHAWEvrfDnHMvr2soMp5Jp7 123 | CJ8JW2T5Fc2WdJmRLLuLP/L68ljQj23EnMRgLWCssxM9dBXY85rcFvQjarQD 124 | mlcKSs/dm5t0K2MUcJ0wbwco6DEjqmxAGSH9DuvUArbmO3aeN0DMmizJIR5X 125 | 0HPicdx7GcuzOcIDGJhSSy+5Kk+Y78DAHDMvzbqsjYa5JK70pnSt2bIcp+xz 126 | VrCleAXKMV53UqeVMk0qcG5SzFS8mcGnNfTK7DosPT6dbHQKvRTpGrgfLNQE 127 | Z7wehHephOtUA9SO1NKJwYXdSI2tcUtSC72xHscmdcCtaFMMsu+xedMQuRGI 128 | BHQx9ISS8MCQBscrqbD6+EKFBnTYCXQsDK6B9gWdMhiTwTHkwstNaBf3H0zf 129 | V5rfkPzmLlvdZdfj9Cl+qhkbz5UyWzTjuWhDBdw0Ie/xwO6HSknB1lw5nKLp 130 | Kwgw8LL1vnd72QwEcTU0ruS6LkVrLODTQgkOt4X7MKp7pc9owZQwXSd92Zn6 131 | oHYMBUoHYWtcK/v9+HNr+S4JCZ+IRwAhvFZXy9tVdrvaX67IxPY5o/csBKDj 132 | UrG+skOtd9+a8HWJ3nGhoiDaj5rxvj1Dbc186ybRNO6OPf9EZ+NCuEMk9NRn 133 | YXFv2e8tTkRB43vMgc45xjYefUdbuGl4QMfL+r4Iwe8sxitOD2f8PwAAAP// 134 | AwBMbtil+AUAAA== 135 | http_version: 136 | recorded_at: Thu, 28 May 2015 09:47:50 GMT 137 | - request: 138 | method: get 139 | uri: https://www.pivotaltracker.com/services/v3/projects/1325832/memberships 140 | body: 141 | encoding: US-ASCII 142 | string: '' 143 | headers: 144 | Accept: 145 | - "*/*; q=0.5, application/xml" 146 | Accept-Encoding: 147 | - gzip, deflate 148 | X-Trackertoken: 149 | - b545bcaeafc773ced5b1a853fffd1fc9 150 | Content-Type: 151 | - application/xml 152 | User-Agent: 153 | - Ruby 154 | response: 155 | status: 156 | code: 200 157 | message: OK 158 | headers: 159 | Content-Type: 160 | - application/xml; charset=utf-8 161 | Transfer-Encoding: 162 | - chunked 163 | Status: 164 | - 200 OK 165 | X-Ua-Compatible: 166 | - IE=Edge,chrome=1 167 | Cache-Control: 168 | - max-age=0, private, must-revalidate 169 | Set-Cookie: 170 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWNlY2U0ZDMzNjU0MzVjMTRkYzlmNWI5Yzc1YTdhN2Q4BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgMsnLb8HOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFMZm1ZVjh5dmNIdmQxbTdvYW1nTVRsOG5RejNueXUrOURlNmJ4WWZjZkFNPQY7AEY%3D--5b3039948707980ee562be50c469c2f1dd3ad75d; 171 | path=/; HttpOnly 172 | X-Request-Id: 173 | - 4b8e5231cce0d667528cf7a5c68b991c 174 | X-Runtime: 175 | - '0.055731' 176 | Date: 177 | - Thu, 28 May 2015 09:47:50 GMT 178 | X-Rack-Cache: 179 | - miss 180 | X-Powered-By: 181 | - Phusion Passenger 4.0.41 182 | Server: 183 | - nginx/1.6.0 + Phusion Passenger 4.0.41 184 | X-Tracker-Client-Pinger-Interval: 185 | - '12' 186 | Content-Encoding: 187 | - gzip 188 | body: 189 | encoding: ASCII-8BIT 190 | string: !binary |- 191 | H4sIAAAAAAAAA0xQy27CMBC85yss38FAhBSkzdILvVWN1PYDTLwCV37JCZT8 192 | PSZulNx2dmZ2RgvHhzXsTrHT3tV8u95wRq71SrtLzX++31cVP2IBluw5ia46 193 | dKwfAtVcxigHjgVjC/IF00Ir3JeHanOoQKQ5L0Pye5dB0pCV2mA4x5tyw9vl 194 | hdattyAyMXqSzElL2JCKHsQ4T4R2utfSdNicUsgEcpRYZkH0hvDzz1EEMc7/ 195 | faL/pbafDyrclrt9Ve7m0lOBj4F9SRsMsSa7lmVAhPkUiPlV6W8L1GHxBAAA 196 | //8DADPEdB9uAQAA 197 | http_version: 198 | recorded_at: Thu, 28 May 2015 09:47:51 GMT 199 | recorded_with: VCR 2.9.3 200 | -------------------------------------------------------------------------------- /spec/cassettes/trackmine_activity.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 6 | body: 7 | encoding: US-ASCII 8 | string: username=foobar%40gmail.com&password=secret&timeout=60 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | Content-Length: 15 | - '55' 16 | Content-Type: 17 | - application/x-www-form-urlencoded 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Etag: 34 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 35 | Cache-Control: 36 | - max-age=0, private, must-revalidate 37 | Set-Cookie: 38 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJTkxMTlkZjhmZjRjNGQ5YjRmYWM1OGZjMjA3YzQ1MjdmBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgARXyL0KOg1uYW5vX251bWkC6wE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdJEDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBU--cf9558dc92bf97e3c9e34e2fcbdccdce52b7577f; 39 | path=/; HttpOnly 40 | X-Request-Id: 41 | - cdf60f89de718eac3c1430e9129c59d6 42 | X-Runtime: 43 | - '0.124102' 44 | Date: 45 | - Thu, 28 May 2015 09:47:28 GMT 46 | X-Rack-Cache: 47 | - invalidate, pass 48 | X-Powered-By: 49 | - Phusion Passenger 4.0.41 50 | Server: 51 | - nginx/1.6.0 + Phusion Passenger 4.0.41 52 | X-Tracker-Client-Pinger-Interval: 53 | - '12' 54 | body: 55 | encoding: UTF-8 56 | string: | 57 | 58 | 59 | b545bcaeafc773ced5b1a853fffd1fc9 60 | 838284 61 | 62 | http_version: 63 | recorded_at: Thu, 28 May 2015 09:47:29 GMT 64 | - request: 65 | method: get 66 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280 67 | body: 68 | encoding: US-ASCII 69 | string: '' 70 | headers: 71 | Accept: 72 | - "*/*; q=0.5, application/xml" 73 | Accept-Encoding: 74 | - gzip, deflate 75 | X-Trackertoken: 76 | - b545bcaeafc773ced5b1a853fffd1fc9 77 | Content-Type: 78 | - application/xml 79 | User-Agent: 80 | - Ruby 81 | response: 82 | status: 83 | code: 200 84 | message: OK 85 | headers: 86 | Content-Type: 87 | - application/xml; charset=utf-8 88 | Transfer-Encoding: 89 | - chunked 90 | Status: 91 | - 200 OK 92 | X-Ua-Compatible: 93 | - IE=Edge,chrome=1 94 | Cache-Control: 95 | - max-age=0, private, must-revalidate 96 | Set-Cookie: 97 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWEwNThkMzc4NGE5ZjcyNTQ2NWU5NThmYjM1NWMxM2YxBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgHqo2b0KOg1uYW5vX251bWkChwE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgc5EDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxSU00TllqN3BQQTBrSDd1V01Uc0M5SVB0N2NneFlEWmdLY3ZhUWhHOUkxOD0GOwBG--49435869bab7edf32b25cafba6126671f83d06e2; 98 | path=/; HttpOnly 99 | X-Request-Id: 100 | - 0b7b9573b754e4d59dec8dba43d6aa13 101 | X-Runtime: 102 | - '0.152584' 103 | Date: 104 | - Thu, 28 May 2015 09:47:29 GMT 105 | X-Rack-Cache: 106 | - miss 107 | X-Powered-By: 108 | - Phusion Passenger 4.0.41 109 | Server: 110 | - nginx/1.6.0 + Phusion Passenger 4.0.41 111 | X-Tracker-Client-Pinger-Interval: 112 | - '12' 113 | Content-Encoding: 114 | - gzip 115 | body: 116 | encoding: ASCII-8BIT 117 | string: !binary |- 118 | H4sIAAAAAAAAA4RUS2/bMAy+91cYPmeV4zRoVrjqtmK7DethPQu0zdhaZMmQ 119 | 5GTprx+l2Hm06wYEiPjx45tm8fC7U8kWrZNG36fz6yxNUFemlrq5T59/fvuw 120 | Sh/4VdFb8wsrz6+SpJA1ny/y23yVFYzeAdLQIX+RpgNVsChEokcLnhwLhbrx 121 | beL3Pd6nUnts0KZ8TvavKNFuh7gRzoP1ooY9/240/RXsFRyYvSFnwlWgkGez 122 | +SyfLQp2DgYSVJUZtOdfLGwxeYy1FWxCA2MtrfPilMshtpcdjinX4DGIKc+z 123 | +ZJlS5bfJHl+l2X0S55/PhbsfR+xpmqwFinXUxA9dCXav/TkPWr0gxpKhcKD 124 | 27jRtjRGIeiUeztgwc4Z0WSLylTS76lPLdJoPtO4ocHErJNFcszHFew18Tzv 125 | ScfnNPUpwyMYmFJLL0GJC+YbMDAPlQuzFrXReGqJE94I15odn+e0RrE//2IF 126 | XwpKVI5riaVEt4GZA72ZOZKb2YvsoGAj40CmMUPl5Ta0A/z7071Nso93y8Xd 127 | YpyugkvL2FhQyuzIjYeq7Wi4bpzAWzyw+6FUsuJrUI6mNEpBMTgUrfe9m3Qn 128 | IKjLoXECdC2q1likp0WBjrYRfFiFyeh/tOCqMl0nvehMfTQ7hwKlw9B118p+ 129 | Wi+wFvZpKPhCfQAIohOwvMny1c1qOgeR2ZMXoycWAdiBVLwv7VDr/acmSNcU 130 | nRY2KqL/aBnvxxPW1pxuyaga18nxp68U7LBw7pgJu4xZWPou+I+dRluw+D7U 131 | wE41xjGeydEXbTIdqMPletuEEPekptPIjrfxDwAAAP//AwDXq2nATQUAAA== 132 | http_version: 133 | recorded_at: Thu, 28 May 2015 09:47:30 GMT 134 | - request: 135 | method: get 136 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/94184406 137 | body: 138 | encoding: US-ASCII 139 | string: '' 140 | headers: 141 | Accept: 142 | - "*/*; q=0.5, application/xml" 143 | Accept-Encoding: 144 | - gzip, deflate 145 | X-Trackertoken: 146 | - b545bcaeafc773ced5b1a853fffd1fc9 147 | Content-Type: 148 | - application/xml 149 | User-Agent: 150 | - Ruby 151 | response: 152 | status: 153 | code: 200 154 | message: OK 155 | headers: 156 | Content-Type: 157 | - application/xml; charset=utf-8 158 | Transfer-Encoding: 159 | - chunked 160 | Status: 161 | - 200 OK 162 | X-Ua-Compatible: 163 | - IE=Edge,chrome=1 164 | Cache-Control: 165 | - max-age=0, private, must-revalidate 166 | Set-Cookie: 167 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTVhYWZjYjQ0NjQ3MzQyNGYxY2RkNDAzZGM4ZTUzZDI0BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgGVK6r0KOg1uYW5vX251bWkiOg1uYW5vX2RlbmkGOg1zdWJtaWNybyIHApA6C29mZnNldGkAOgl6b25lSSIIVVRDBjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMWxmQ1d5dXJGdWNtUXd6Wk5ha3hHeVFxYmVYQU5iTzVYNjJEMHdUOVNuSU09BjsARg%3D%3D--84f2a23397829b6cba7c60502ee452cde2b3ce25; 168 | path=/; HttpOnly 169 | X-Request-Id: 170 | - 39bc3111246e044bb53de46f6c45cf61 171 | X-Runtime: 172 | - '0.074896' 173 | Date: 174 | - Thu, 28 May 2015 09:47:30 GMT 175 | X-Rack-Cache: 176 | - miss 177 | X-Powered-By: 178 | - Phusion Passenger 4.0.41 179 | Server: 180 | - nginx/1.6.0 + Phusion Passenger 4.0.41 181 | X-Tracker-Client-Pinger-Interval: 182 | - '12' 183 | Content-Encoding: 184 | - gzip 185 | body: 186 | encoding: ASCII-8BIT 187 | string: !binary |- 188 | H4sIAAAAAAAAAwIAAAD//3ySy27CMBBF93yFlTXFScojRMYsKnXdBayRiQcw 189 | JHZqO6TJ19c2lEcrVcrCM3Pu1fg6ZPlVlegM2gglF1EyiiMEslBcyP0iWq/e 190 | X7JoSQfEWKU7OkCICI5sV8MiEtLCHnRE5+MkG4/jKcGCB6TW6giF3fxFk9d0 191 | lmYxwXckKIL9xvvSHTDbaCD4oeeRRpf0YG1tcozbth3V4qwsK61mxQn0qFDV 192 | RYHNQbX4vpPXeT0YKypm4ff2CcE/o8AVjdYg7cZYB9OdkMIcgBP83PckB1No 193 | UVuXHD2ynjPUo73SJ4FaxFWJaoWMFLBvXF0zt+cBFX1X+rHVfYeUUdtuQPCj 194 | j/eVrIKroWTIMHnyUi9SR38kOBAe1fDZuO2Bb7Yd/QCuFcFPPQ+pVj4Btzpc 195 | V7u83ZjZazDclS4piGgaJxMcuy9DySRPs3wyRevVm4vipglLNLXX/GuROItZ 196 | nsT5ZH6xuGuCRcm2UBrq7zoMmQ1791gEX/supOsP+A0AAP//AwBvV9t2sAIA 197 | AA== 198 | http_version: 199 | recorded_at: Thu, 28 May 2015 09:47:31 GMT 200 | recorded_with: VCR 2.9.3 201 | -------------------------------------------------------------------------------- /spec/fixtures/json/story_status_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "story_update_activity", 3 | "guid": "1327280_11", 4 | "project_version": 11, 5 | "message": "Pedro finished this feature", 6 | "highlight": "finished", 7 | "changes": [ 8 | { 9 | "kind": "label", 10 | "change_type": "update", 11 | "id": 11622596, 12 | "original_values": { 13 | "counts": { 14 | "number_of_zero_point_stories_by_state": { 15 | "accepted": 0, 16 | "started": 0, 17 | "finished": 0, 18 | "unstarted": 0, 19 | "planned": 0, 20 | "delivered": 0, 21 | "unscheduled": 0, 22 | "rejected": 0, 23 | "kind": "counts_by_story_state" 24 | }, 25 | "sum_of_story_estimates_by_state": { 26 | "accepted": 0, 27 | "started": 1, 28 | "finished": 0, 29 | "unstarted": 0, 30 | "planned": 0, 31 | "delivered": 0, 32 | "unscheduled": 0, 33 | "rejected": 0, 34 | "kind": "counts_by_story_state" 35 | }, 36 | "number_of_stories_by_state": { 37 | "accepted": 0, 38 | "started": 1, 39 | "finished": 0, 40 | "unstarted": 0, 41 | "planned": 0, 42 | "delivered": 0, 43 | "unscheduled": 0, 44 | "rejected": 0, 45 | "kind": "counts_by_story_state" 46 | }, 47 | "kind": "story_counts" 48 | } 49 | }, 50 | "new_values": { 51 | "counts": { 52 | "number_of_zero_point_stories_by_state": { 53 | "accepted": 0, 54 | "started": 0, 55 | "finished": 0, 56 | "unstarted": 0, 57 | "planned": 0, 58 | "delivered": 0, 59 | "unscheduled": 0, 60 | "rejected": 0, 61 | "kind": "counts_by_story_state" 62 | }, 63 | "sum_of_story_estimates_by_state": { 64 | "accepted": 0, 65 | "started": 0, 66 | "finished": 1, 67 | "unstarted": 0, 68 | "planned": 0, 69 | "delivered": 0, 70 | "unscheduled": 0, 71 | "rejected": 0, 72 | "kind": "counts_by_story_state" 73 | }, 74 | "number_of_stories_by_state": { 75 | "accepted": 0, 76 | "started": 0, 77 | "finished": 1, 78 | "unstarted": 0, 79 | "planned": 0, 80 | "delivered": 0, 81 | "unscheduled": 0, 82 | "rejected": 0, 83 | "kind": "counts_by_story_state" 84 | }, 85 | "kind": "story_counts" 86 | } 87 | }, 88 | "name": "sank" 89 | }, 90 | { 91 | "kind": "label", 92 | "change_type": "update", 93 | "id": 11622598, 94 | "original_values": { 95 | "counts": { 96 | "number_of_zero_point_stories_by_state": { 97 | "accepted": 0, 98 | "started": 0, 99 | "finished": 0, 100 | "unstarted": 0, 101 | "planned": 0, 102 | "delivered": 0, 103 | "unscheduled": 0, 104 | "rejected": 0, 105 | "kind": "counts_by_story_state" 106 | }, 107 | "sum_of_story_estimates_by_state": { 108 | "accepted": 0, 109 | "started": 1, 110 | "finished": 0, 111 | "unstarted": 0, 112 | "planned": 0, 113 | "delivered": 0, 114 | "unscheduled": 0, 115 | "rejected": 0, 116 | "kind": "counts_by_story_state" 117 | }, 118 | "number_of_stories_by_state": { 119 | "accepted": 0, 120 | "started": 1, 121 | "finished": 0, 122 | "unstarted": 0, 123 | "planned": 0, 124 | "delivered": 0, 125 | "unscheduled": 0, 126 | "rejected": 0, 127 | "kind": "counts_by_story_state" 128 | }, 129 | "kind": "story_counts" 130 | } 131 | }, 132 | "new_values": { 133 | "counts": { 134 | "number_of_zero_point_stories_by_state": { 135 | "accepted": 0, 136 | "started": 0, 137 | "finished": 0, 138 | "unstarted": 0, 139 | "planned": 0, 140 | "delivered": 0, 141 | "unscheduled": 0, 142 | "rejected": 0, 143 | "kind": "counts_by_story_state" 144 | }, 145 | "sum_of_story_estimates_by_state": { 146 | "accepted": 0, 147 | "started": 0, 148 | "finished": 1, 149 | "unstarted": 0, 150 | "planned": 0, 151 | "delivered": 0, 152 | "unscheduled": 0, 153 | "rejected": 0, 154 | "kind": "counts_by_story_state" 155 | }, 156 | "number_of_stories_by_state": { 157 | "accepted": 0, 158 | "started": 0, 159 | "finished": 1, 160 | "unstarted": 0, 161 | "planned": 0, 162 | "delivered": 0, 163 | "unscheduled": 0, 164 | "rejected": 0, 165 | "kind": "counts_by_story_state" 166 | }, 167 | "kind": "story_counts" 168 | } 169 | }, 170 | "name": "snieg" 171 | }, 172 | { 173 | "kind": "label", 174 | "change_type": "update", 175 | "id": 11622600, 176 | "original_values": { 177 | "counts": { 178 | "number_of_zero_point_stories_by_state": { 179 | "accepted": 0, 180 | "started": 0, 181 | "finished": 0, 182 | "unstarted": 0, 183 | "planned": 0, 184 | "delivered": 0, 185 | "unscheduled": 0, 186 | "rejected": 0, 187 | "kind": "counts_by_story_state" 188 | }, 189 | "sum_of_story_estimates_by_state": { 190 | "accepted": 0, 191 | "started": 1, 192 | "finished": 0, 193 | "unstarted": 0, 194 | "planned": 0, 195 | "delivered": 0, 196 | "unscheduled": 0, 197 | "rejected": 0, 198 | "kind": "counts_by_story_state" 199 | }, 200 | "number_of_stories_by_state": { 201 | "accepted": 0, 202 | "started": 1, 203 | "finished": 0, 204 | "unstarted": 0, 205 | "planned": 0, 206 | "delivered": 0, 207 | "unscheduled": 0, 208 | "rejected": 0, 209 | "kind": "counts_by_story_state" 210 | }, 211 | "kind": "story_counts" 212 | } 213 | }, 214 | "new_values": { 215 | "counts": { 216 | "number_of_zero_point_stories_by_state": { 217 | "accepted": 0, 218 | "started": 0, 219 | "finished": 0, 220 | "unstarted": 0, 221 | "planned": 0, 222 | "delivered": 0, 223 | "unscheduled": 0, 224 | "rejected": 0, 225 | "kind": "counts_by_story_state" 226 | }, 227 | "sum_of_story_estimates_by_state": { 228 | "accepted": 0, 229 | "started": 0, 230 | "finished": 1, 231 | "unstarted": 0, 232 | "planned": 0, 233 | "delivered": 0, 234 | "unscheduled": 0, 235 | "rejected": 0, 236 | "kind": "counts_by_story_state" 237 | }, 238 | "number_of_stories_by_state": { 239 | "accepted": 0, 240 | "started": 0, 241 | "finished": 1, 242 | "unstarted": 0, 243 | "planned": 0, 244 | "delivered": 0, 245 | "unscheduled": 0, 246 | "rejected": 0, 247 | "kind": "counts_by_story_state" 248 | }, 249 | "kind": "story_counts" 250 | } 251 | }, 252 | "name": "zima" 253 | }, 254 | { 255 | "kind": "story", 256 | "change_type": "update", 257 | "id": 94184406, 258 | "original_values": { 259 | "current_state": "started", 260 | "updated_at": 1431099420000 261 | }, 262 | "new_values": { 263 | "current_state": "finished", 264 | "updated_at": 1431099477000 265 | }, 266 | "name": "jazda na sankach w parach", 267 | "story_type": "feature" 268 | } 269 | ], 270 | "primary_resources": [ 271 | { 272 | "kind": "story", 273 | "id": 94184406, 274 | "name": "jazda na sankach w parach", 275 | "story_type": "feature", 276 | "url": "https://www.pivotaltracker.com/story/show/94184406" 277 | } 278 | ], 279 | "project": { 280 | "kind": "project", 281 | "id": 1327280, 282 | "name": "ziomal" 283 | }, 284 | "performed_by": { 285 | "kind": "person", 286 | "id": 1651216, 287 | "name": "Pedro", 288 | "initials": "PE" 289 | }, 290 | "occurred_at": 1431099477000 -------------------------------------------------------------------------------- /spec/fixtures/json/story_started.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "story_update_activity", 3 | "guid": "1327280_8", 4 | "project_version": 8, 5 | "message": "Pedro started this feature", 6 | "highlight": "started", 7 | "changes": [ 8 | { 9 | "kind": "label", 10 | "change_type": "update", 11 | "id": 11622596, 12 | "original_values": { 13 | "counts": { 14 | "number_of_zero_point_stories_by_state": { 15 | "accepted": 0, 16 | "started": 0, 17 | "finished": 0, 18 | "unstarted": 0, 19 | "planned": 0, 20 | "delivered": 0, 21 | "unscheduled": 0, 22 | "rejected": 0, 23 | "kind": "counts_by_story_state" 24 | }, 25 | "sum_of_story_estimates_by_state": { 26 | "accepted": 0, 27 | "started": 0, 28 | "finished": 0, 29 | "unstarted": 0, 30 | "planned": 0, 31 | "delivered": 0, 32 | "unscheduled": 1, 33 | "rejected": 0, 34 | "kind": "counts_by_story_state" 35 | }, 36 | "number_of_stories_by_state": { 37 | "accepted": 0, 38 | "started": 0, 39 | "finished": 0, 40 | "unstarted": 0, 41 | "planned": 0, 42 | "delivered": 0, 43 | "unscheduled": 1, 44 | "rejected": 0, 45 | "kind": "counts_by_story_state" 46 | }, 47 | "kind": "story_counts" 48 | } 49 | }, 50 | "new_values": { 51 | "counts": { 52 | "number_of_zero_point_stories_by_state": { 53 | "accepted": 0, 54 | "started": 0, 55 | "finished": 0, 56 | "unstarted": 0, 57 | "planned": 0, 58 | "delivered": 0, 59 | "unscheduled": 0, 60 | "rejected": 0, 61 | "kind": "counts_by_story_state" 62 | }, 63 | "sum_of_story_estimates_by_state": { 64 | "accepted": 0, 65 | "started": 1, 66 | "finished": 0, 67 | "unstarted": 0, 68 | "planned": 0, 69 | "delivered": 0, 70 | "unscheduled": 0, 71 | "rejected": 0, 72 | "kind": "counts_by_story_state" 73 | }, 74 | "number_of_stories_by_state": { 75 | "accepted": 0, 76 | "started": 1, 77 | "finished": 0, 78 | "unstarted": 0, 79 | "planned": 0, 80 | "delivered": 0, 81 | "unscheduled": 0, 82 | "rejected": 0, 83 | "kind": "counts_by_story_state" 84 | }, 85 | "kind": "story_counts" 86 | } 87 | }, 88 | "name": "sank" 89 | }, 90 | { 91 | "kind": "label", 92 | "change_type": "update", 93 | "id": 11622598, 94 | "original_values": { 95 | "counts": { 96 | "number_of_zero_point_stories_by_state": { 97 | "accepted": 0, 98 | "started": 0, 99 | "finished": 0, 100 | "unstarted": 0, 101 | "planned": 0, 102 | "delivered": 0, 103 | "unscheduled": 0, 104 | "rejected": 0, 105 | "kind": "counts_by_story_state" 106 | }, 107 | "sum_of_story_estimates_by_state": { 108 | "accepted": 0, 109 | "started": 0, 110 | "finished": 0, 111 | "unstarted": 0, 112 | "planned": 0, 113 | "delivered": 0, 114 | "unscheduled": 1, 115 | "rejected": 0, 116 | "kind": "counts_by_story_state" 117 | }, 118 | "number_of_stories_by_state": { 119 | "accepted": 0, 120 | "started": 0, 121 | "finished": 0, 122 | "unstarted": 0, 123 | "planned": 0, 124 | "delivered": 0, 125 | "unscheduled": 1, 126 | "rejected": 0, 127 | "kind": "counts_by_story_state" 128 | }, 129 | "kind": "story_counts" 130 | } 131 | }, 132 | "new_values": { 133 | "counts": { 134 | "number_of_zero_point_stories_by_state": { 135 | "accepted": 0, 136 | "started": 0, 137 | "finished": 0, 138 | "unstarted": 0, 139 | "planned": 0, 140 | "delivered": 0, 141 | "unscheduled": 0, 142 | "rejected": 0, 143 | "kind": "counts_by_story_state" 144 | }, 145 | "sum_of_story_estimates_by_state": { 146 | "accepted": 0, 147 | "started": 1, 148 | "finished": 0, 149 | "unstarted": 0, 150 | "planned": 0, 151 | "delivered": 0, 152 | "unscheduled": 0, 153 | "rejected": 0, 154 | "kind": "counts_by_story_state" 155 | }, 156 | "number_of_stories_by_state": { 157 | "accepted": 0, 158 | "started": 1, 159 | "finished": 0, 160 | "unstarted": 0, 161 | "planned": 0, 162 | "delivered": 0, 163 | "unscheduled": 0, 164 | "rejected": 0, 165 | "kind": "counts_by_story_state" 166 | }, 167 | "kind": "story_counts" 168 | } 169 | }, 170 | "name": "snieg" 171 | }, 172 | { 173 | "kind": "label", 174 | "change_type": "update", 175 | "id": 11622600, 176 | "original_values": { 177 | "counts": { 178 | "number_of_zero_point_stories_by_state": { 179 | "accepted": 0, 180 | "started": 0, 181 | "finished": 0, 182 | "unstarted": 0, 183 | "planned": 0, 184 | "delivered": 0, 185 | "unscheduled": 0, 186 | "rejected": 0, 187 | "kind": "counts_by_story_state" 188 | }, 189 | "sum_of_story_estimates_by_state": { 190 | "accepted": 0, 191 | "started": 0, 192 | "finished": 0, 193 | "unstarted": 0, 194 | "planned": 0, 195 | "delivered": 0, 196 | "unscheduled": 1, 197 | "rejected": 0, 198 | "kind": "counts_by_story_state" 199 | }, 200 | "number_of_stories_by_state": { 201 | "accepted": 0, 202 | "started": 0, 203 | "finished": 0, 204 | "unstarted": 0, 205 | "planned": 0, 206 | "delivered": 0, 207 | "unscheduled": 1, 208 | "rejected": 0, 209 | "kind": "counts_by_story_state" 210 | }, 211 | "kind": "story_counts" 212 | } 213 | }, 214 | "new_values": { 215 | "counts": { 216 | "number_of_zero_point_stories_by_state": { 217 | "accepted": 0, 218 | "started": 0, 219 | "finished": 0, 220 | "unstarted": 0, 221 | "planned": 0, 222 | "delivered": 0, 223 | "unscheduled": 0, 224 | "rejected": 0, 225 | "kind": "counts_by_story_state" 226 | }, 227 | "sum_of_story_estimates_by_state": { 228 | "accepted": 0, 229 | "started": 1, 230 | "finished": 0, 231 | "unstarted": 0, 232 | "planned": 0, 233 | "delivered": 0, 234 | "unscheduled": 0, 235 | "rejected": 0, 236 | "kind": "counts_by_story_state" 237 | }, 238 | "number_of_stories_by_state": { 239 | "accepted": 0, 240 | "started": 1, 241 | "finished": 0, 242 | "unstarted": 0, 243 | "planned": 0, 244 | "delivered": 0, 245 | "unscheduled": 0, 246 | "rejected": 0, 247 | "kind": "counts_by_story_state" 248 | }, 249 | "kind": "story_counts" 250 | } 251 | }, 252 | "name": "zima" 253 | }, 254 | { 255 | "kind": "story", 256 | "change_type": "update", 257 | "id": 94184406, 258 | "original_values": { 259 | "current_state": "unscheduled", 260 | "owned_by_id": null, 261 | "owner_ids": [ 262 | 263 | ], 264 | "updated_at": 1431098936000 265 | }, 266 | "new_values": { 267 | "current_state": "started", 268 | "owned_by_id": 1651216, 269 | "owner_ids": [ 270 | 1651216 271 | ], 272 | "updated_at": 1431098967000 273 | }, 274 | "name": "jazda na sankach", 275 | "story_type": "feature" 276 | } 277 | ], 278 | "primary_resources": [ 279 | { 280 | "kind": "story", 281 | "id": 94184406, 282 | "name": "jazda na sankach", 283 | "story_type": "feature", 284 | "url": "https://www.pivotaltracker.com/story/show/94184406" 285 | } 286 | ], 287 | "project": { 288 | "kind": "project", 289 | "id": 1327280, 290 | "name": "ziomal" 291 | }, 292 | "performed_by": { 293 | "kind": "person", 294 | "id": 1651216, 295 | "name": "Pedro", 296 | "initials": "PE" 297 | }, 298 | "occurred_at": 1431098967000 299 | } -------------------------------------------------------------------------------- /spec/cassettes/issue_creator.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 6 | body: 7 | encoding: US-ASCII 8 | string: username=foobar%40gmail.com&password=secret&timeout=60 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | Content-Length: 15 | - '55' 16 | Content-Type: 17 | - application/x-www-form-urlencoded 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Etag: 34 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 35 | Cache-Control: 36 | - max-age=0, private, must-revalidate 37 | Set-Cookie: 38 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJTVlMzBhN2ZlOTI5NzcwMzJlMjg1NDUzNjA4MzQwNmZkBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgEvw%2Bb0KOg1uYW5vX251bWkcOg1uYW5vX2RlbmkGOg1zdWJtaWNybyIHAjA6C29mZnNldGkAOgl6b25lSSIIVVRDBjsAVA%3D%3D--6ce0a4774f0175f092d741642ca5c36153d702a6; 39 | path=/; HttpOnly 40 | X-Request-Id: 41 | - 5b65ff86d7ea2faf9adca467ffd578a5 42 | X-Runtime: 43 | - '0.120306' 44 | Date: 45 | - Thu, 28 May 2015 09:47:31 GMT 46 | X-Rack-Cache: 47 | - invalidate, pass 48 | X-Powered-By: 49 | - Phusion Passenger 4.0.41 50 | Server: 51 | - nginx/1.6.0 + Phusion Passenger 4.0.41 52 | X-Tracker-Client-Pinger-Interval: 53 | - '12' 54 | body: 55 | encoding: UTF-8 56 | string: | 57 | 58 | 59 | b545bcaeafc773ced5b1a853fffd1fc9 60 | 838284 61 | 62 | http_version: 63 | recorded_at: Thu, 28 May 2015 09:47:32 GMT 64 | - request: 65 | method: get 66 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280 67 | body: 68 | encoding: US-ASCII 69 | string: '' 70 | headers: 71 | Accept: 72 | - "*/*; q=0.5, application/xml" 73 | Accept-Encoding: 74 | - gzip, deflate 75 | X-Trackertoken: 76 | - b545bcaeafc773ced5b1a853fffd1fc9 77 | Content-Type: 78 | - application/xml 79 | User-Agent: 80 | - Ruby 81 | response: 82 | status: 83 | code: 200 84 | message: OK 85 | headers: 86 | Content-Type: 87 | - application/xml; charset=utf-8 88 | Transfer-Encoding: 89 | - chunked 90 | Status: 91 | - 200 OK 92 | X-Ua-Compatible: 93 | - IE=Edge,chrome=1 94 | Cache-Control: 95 | - max-age=0, private, must-revalidate 96 | Set-Cookie: 97 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWE3NWM1OTk5ZDZkMzVmZDFkMjEwNzBhYjA1ZDcwNWE0BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgBhuCr4KOg1uYW5vX251bWkC0AM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgeXYDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxVklYNmdKR29KM09BaUV3NFVkTWhPYW1SaU90dzF1bEhJL1RaVk02Y0xRRT0GOwBG--75477a5b294d13f13736a05594beb173aed85d3c; 98 | path=/; HttpOnly 99 | X-Request-Id: 100 | - 2d68b8ab3ff4f4b2888710ed76566314 101 | X-Runtime: 102 | - '0.049506' 103 | Date: 104 | - Thu, 28 May 2015 09:47:32 GMT 105 | X-Rack-Cache: 106 | - miss 107 | X-Powered-By: 108 | - Phusion Passenger 4.0.41 109 | Server: 110 | - nginx/1.6.0 + Phusion Passenger 4.0.41 111 | X-Tracker-Client-Pinger-Interval: 112 | - '12' 113 | Content-Encoding: 114 | - gzip 115 | body: 116 | encoding: ASCII-8BIT 117 | string: !binary |- 118 | H4sIAAAAAAAAAwIAAAD//4RUS2/bMAy+91cYPmeV4zRoVrjqtmK7DethPQu0 119 | zdhaZMmQ5GTprx+l2Hm06wYEiPjx45tm8fC7U8kWrZNG36fz6yxNUFemlrq5 120 | T59/fvuwSh/4VdFb8wsrz6+SpJA1ny/y23yVFYzeAdLQIX+RpgNVsChEokcL 121 | nhwLhbrxbeL3Pd6nUnts0KZ8TvavKNFuh7gRzoP1ooY9/240/RXsFRyYvSFn 122 | wlWgkGez+SyfLQp2DgYSVJUZtOdfLGwxeYy1FWxCA2MtrfPilMshtpcdjinX 123 | 4DGIKc+z+ZJlS5bfJHl+l2X0S55/PhbsfR+xpmqwFinXUxA9dCXav/TkPWr0 124 | gxpKhcKD27jRtjRGIeiUeztgwc4Z0WSLylTS76lPLdJoPtO4ocHErJNFcszH 125 | Few18TzvScfnNPUpwyMYmFJLL0GJC+YbMDAPlQuzFrXReGqJE94I15odn+e0 126 | RrE//2IFXwpKVI5riaVEt4GZA72ZOZKb2YvsoGAj40CmMUPl5Ta0A/z7071N 127 | so93y8XdYpyugkvL2FhQyuzIjYeq7Wi4bpzAWzyw+6FUsuJrUI6mNEpBMTgU 128 | rfe9m3QnIKjLoXECdC2q1likp0WBjrYRfFiFyeh/tOCqMl0nvehMfTQ7hwKl 129 | w9B118p+Wi+wFvZpKPhCfQAIohOwvMny1c1qOgeR2ZMXoycWAdiBVLwv7VDr 130 | /acmSNcUnRY2KqL/aBnvxxPW1pxuyaga18nxp68U7LBw7pgJu4xZWPou+I+d 131 | Rluw+D7UwE41xjGeydEXbTIdqMPletuEEPekptPIjrfxDwAAAP//AwDXq2nA 132 | TQUAAA== 133 | http_version: 134 | recorded_at: Thu, 28 May 2015 09:47:33 GMT 135 | - request: 136 | method: get 137 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/94184406 138 | body: 139 | encoding: US-ASCII 140 | string: '' 141 | headers: 142 | Accept: 143 | - "*/*; q=0.5, application/xml" 144 | Accept-Encoding: 145 | - gzip, deflate 146 | X-Trackertoken: 147 | - b545bcaeafc773ced5b1a853fffd1fc9 148 | Content-Type: 149 | - application/xml 150 | User-Agent: 151 | - Ruby 152 | response: 153 | status: 154 | code: 200 155 | message: OK 156 | headers: 157 | Content-Type: 158 | - application/xml; charset=utf-8 159 | Transfer-Encoding: 160 | - chunked 161 | Status: 162 | - 200 OK 163 | X-Ua-Compatible: 164 | - IE=Edge,chrome=1 165 | Cache-Control: 166 | - max-age=0, private, must-revalidate 167 | Set-Cookie: 168 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTcyMTQyMDI4MGQyZjQyZGQ5NmM3NjEzOTg1OTZhOGRmBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgGE8Gb4KOg1uYW5vX251bWkCuAI6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdpYDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxN2JuSEZLTXVWbDhjZ01WTmUxaE1nVkl4V3lmVzB2ODlSLzU3c2U0TWFEaz0GOwBG--1af22d30a4633ccaa6d8421c7a724edde7dccc9e; 169 | path=/; HttpOnly 170 | X-Request-Id: 171 | - 94ca736c0f14ef4e0e7c8747cdfad7fd 172 | X-Runtime: 173 | - '0.065170' 174 | Date: 175 | - Thu, 28 May 2015 09:47:33 GMT 176 | X-Rack-Cache: 177 | - miss 178 | X-Powered-By: 179 | - Phusion Passenger 4.0.41 180 | Server: 181 | - nginx/1.6.0 + Phusion Passenger 4.0.41 182 | X-Tracker-Client-Pinger-Interval: 183 | - '12' 184 | Content-Encoding: 185 | - gzip 186 | body: 187 | encoding: ASCII-8BIT 188 | string: !binary |- 189 | H4sIAAAAAAAAAwIAAAD//3ySy27CMBBF93yFlTXFScojRMYsKnXdBayRiQcw 190 | JHZqO6TJ19c2lEcrVcrCM3Pu1fg6ZPlVlegM2gglF1EyiiMEslBcyP0iWq/e 191 | X7JoSQfEWKU7OkCICI5sV8MiEtLCHnRE5+MkG4/jKcGCB6TW6giF3fxFk9d0 192 | lmYxwXckKIL9xvvSHTDbaCD4oeeRRpf0YG1tcozbth3V4qwsK61mxQn0qFDV 193 | RYHNQbX4vpPXeT0YKypm4ff2CcE/o8AVjdYg7cZYB9OdkMIcgBP83PckB1No 194 | UVuXHD2ynjPUo73SJ4FaxFWJaoWMFLBvXF0zt+cBFX1X+rHVfYeUUdtuQPCj 195 | j/eVrIKroWTIMHnyUi9SR38kOBAe1fDZuO2Bb7Yd/QCuFcFPPQ+pVj4Btzpc 196 | V7u83ZjZazDclS4piGgaJxMcuy9DySRPs3wyRevVm4vipglLNLXX/GuROItZ 197 | nsT5ZH6xuGuCRcm2UBrq7zoMmQ1791gEX/supOsP+A0AAP//AwBvV9t2sAIA 198 | AA== 199 | http_version: 200 | recorded_at: Thu, 28 May 2015 09:47:34 GMT 201 | - request: 202 | method: get 203 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/94184406/notes 204 | body: 205 | encoding: US-ASCII 206 | string: '' 207 | headers: 208 | Accept: 209 | - "*/*; q=0.5, application/xml" 210 | Accept-Encoding: 211 | - gzip, deflate 212 | X-Trackertoken: 213 | - b545bcaeafc773ced5b1a853fffd1fc9 214 | Content-Type: 215 | - application/xml 216 | User-Agent: 217 | - Ruby 218 | response: 219 | status: 220 | code: 200 221 | message: OK 222 | headers: 223 | Content-Type: 224 | - application/xml; charset=utf-8 225 | Transfer-Encoding: 226 | - chunked 227 | Status: 228 | - 200 OK 229 | X-Ua-Compatible: 230 | - IE=Edge,chrome=1 231 | Cache-Control: 232 | - max-age=0, private, must-revalidate 233 | Set-Cookie: 234 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTE2M2FmMmQ4NTA4YWM0ZTI2MDgyZGU1ZjIyM2NlM2VlBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgMWhML4KOg1uYW5vX251bWkClQE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdAUDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxS2trajNJMk04elNacEZHOXhOS2hYbW5acVNtMHFDQ2oyeG1nV2VHUUxZWT0GOwBG--94aecbc773b702832f19a373792aae04e73e3726; 235 | path=/; HttpOnly 236 | X-Request-Id: 237 | - b7414806172d6ab80c453be3f31b0fb7 238 | X-Runtime: 239 | - '0.041954' 240 | Date: 241 | - Thu, 28 May 2015 09:47:35 GMT 242 | X-Rack-Cache: 243 | - miss 244 | X-Powered-By: 245 | - Phusion Passenger 4.0.41 246 | Server: 247 | - nginx/1.6.0 + Phusion Passenger 4.0.41 248 | X-Tracker-Client-Pinger-Interval: 249 | - '12' 250 | Content-Encoding: 251 | - gzip 252 | body: 253 | encoding: ASCII-8BIT 254 | string: !binary |- 255 | H4sIAAAAAAAAA7Kxr8jNUShLLSrOzM+zVTLUM1BSSM1Lzk/JzEu3VQoNcdO1 256 | ULK347LJyy9JLVYoqSxItVVKLCpKrFTSt+MCAAAA//8DAPeRVfM9AAAA 257 | http_version: 258 | recorded_at: Thu, 28 May 2015 09:47:36 GMT 259 | recorded_with: VCR 2.9.3 260 | -------------------------------------------------------------------------------- /spec/cassettes/restarting_story.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 6 | body: 7 | encoding: US-ASCII 8 | string: username=foobar%40gmail.com&password=secret&timeout=60 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | Content-Length: 15 | - '55' 16 | Content-Type: 17 | - application/x-www-form-urlencoded 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Etag: 34 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 35 | Cache-Control: 36 | - max-age=0, private, must-revalidate 37 | Set-Cookie: 38 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJTQ1NGZlODQyODUwYTQwNzRjMmRiNWE0ZTgxMTk5MmRjBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgOl6mL8KOg1uYW5vX251bWkCIwM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgeAMDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBU--d62f62c4321c8efd59aeab89b696aa34a2af5867; 39 | path=/; HttpOnly 40 | X-Request-Id: 41 | - 8364ff82affe35560ad3efa12c14fefd 42 | X-Runtime: 43 | - '0.120550' 44 | Date: 45 | - Thu, 28 May 2015 09:47:57 GMT 46 | X-Rack-Cache: 47 | - invalidate, pass 48 | X-Powered-By: 49 | - Phusion Passenger 4.0.41 50 | Server: 51 | - nginx/1.6.0 + Phusion Passenger 4.0.41 52 | X-Tracker-Client-Pinger-Interval: 53 | - '12' 54 | body: 55 | encoding: UTF-8 56 | string: | 57 | 58 | 59 | b545bcaeafc773ced5b1a853fffd1fc9 60 | 838284 61 | 62 | http_version: 63 | recorded_at: Thu, 28 May 2015 09:47:58 GMT 64 | - request: 65 | method: get 66 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/memberships 67 | body: 68 | encoding: US-ASCII 69 | string: '' 70 | headers: 71 | Accept: 72 | - "*/*; q=0.5, application/xml" 73 | Accept-Encoding: 74 | - gzip, deflate 75 | X-Trackertoken: 76 | - b545bcaeafc773ced5b1a853fffd1fc9 77 | Content-Type: 78 | - application/xml 79 | User-Agent: 80 | - Ruby 81 | response: 82 | status: 83 | code: 200 84 | message: OK 85 | headers: 86 | Content-Type: 87 | - application/xml; charset=utf-8 88 | Transfer-Encoding: 89 | - chunked 90 | Status: 91 | - 200 OK 92 | X-Ua-Compatible: 93 | - IE=Edge,chrome=1 94 | Cache-Control: 95 | - max-age=0, private, must-revalidate 96 | Set-Cookie: 97 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWU4M2M4OWMzNTM1NWJiYmY3MTE0NmQ2NTMxNTkxY2FkBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgF%2F4p78KOg1uYW5vX251bWkCGQE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgcoEDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxWDZpTk1NR3JrTkdLelRaYThyQk9CRll5dkt0dklkYk9jMDZXL2lnRWw4ND0GOwBG--1fc426d1629f831698d291a9887d7485da64ea91; 98 | path=/; HttpOnly 99 | X-Request-Id: 100 | - 54bcdc593fe63f9fab85ab19e782d200 101 | X-Runtime: 102 | - '0.049570' 103 | Date: 104 | - Thu, 28 May 2015 09:47:58 GMT 105 | X-Rack-Cache: 106 | - miss 107 | X-Powered-By: 108 | - Phusion Passenger 4.0.41 109 | Server: 110 | - nginx/1.6.0 + Phusion Passenger 4.0.41 111 | X-Tracker-Client-Pinger-Interval: 112 | - '12' 113 | Content-Encoding: 114 | - gzip 115 | body: 116 | encoding: ASCII-8BIT 117 | string: !binary |- 118 | H4sIAAAAAAAAAwIAAAD//0xQu27DMAzc/RWC9kaOk6IeaKZLuyZD+wGKRSQs 119 | 9ILsJnW/vopdw954vDvegXD4cVbcKHUcfCO3m1IK8m0w7C+N/Px4f6rlAQtw 120 | 5M5ZdOXYiX6I1Eidkh4kFkKsyAfMCzb4vC+rel+DyvO0jNkf/ASyhpxmi/Gc 121 | vo0fXi8PtGmDAzURoyfLvHaEJzIpgBrnmWDPPWvb4ekth8xgilLrLEjBEh7v 122 | nhKocf7vk8IXtf1y0OB2V71UdbmUngv8cnDarhuAiosf1PKf/KwV6rD4AwAA 123 | //8DAHDPkbBjAQAA 124 | http_version: 125 | recorded_at: Thu, 28 May 2015 09:47:59 GMT 126 | - request: 127 | method: post 128 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 129 | body: 130 | encoding: US-ASCII 131 | string: username=foobar%40gmail.com&password=secret&timeout=60 132 | headers: 133 | Accept: 134 | - "*/*; q=0.5, application/xml" 135 | Accept-Encoding: 136 | - gzip, deflate 137 | Content-Length: 138 | - '55' 139 | Content-Type: 140 | - application/x-www-form-urlencoded 141 | User-Agent: 142 | - Ruby 143 | response: 144 | status: 145 | code: 200 146 | message: OK 147 | headers: 148 | Content-Type: 149 | - application/xml; charset=utf-8 150 | Transfer-Encoding: 151 | - chunked 152 | Status: 153 | - 200 OK 154 | X-Ua-Compatible: 155 | - IE=Edge,chrome=1 156 | Etag: 157 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 158 | Cache-Control: 159 | - max-age=0, private, must-revalidate 160 | Set-Cookie: 161 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJWRmMjgzODY4YzRkMTNiZDkzZWFkODU2YTViNTM2ZjVjBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgButtr8KOg1uYW5vX251bWkCFwI6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdTUDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBU--d998dc6034eb0ae3593e17d2a2a4cf580bb97bdf; 162 | path=/; HttpOnly 163 | X-Request-Id: 164 | - c27a1de69e56911f53ca46e743b948b8 165 | X-Runtime: 166 | - '0.123150' 167 | Date: 168 | - Thu, 28 May 2015 09:47:59 GMT 169 | X-Rack-Cache: 170 | - invalidate, pass 171 | X-Powered-By: 172 | - Phusion Passenger 4.0.41 173 | Server: 174 | - nginx/1.6.0 + Phusion Passenger 4.0.41 175 | X-Tracker-Client-Pinger-Interval: 176 | - '12' 177 | body: 178 | encoding: UTF-8 179 | string: | 180 | 181 | 182 | b545bcaeafc773ced5b1a853fffd1fc9 183 | 838284 184 | 185 | http_version: 186 | recorded_at: Thu, 28 May 2015 09:48:00 GMT 187 | - request: 188 | method: get 189 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/92844256 190 | body: 191 | encoding: US-ASCII 192 | string: '' 193 | headers: 194 | Accept: 195 | - "*/*; q=0.5, application/xml" 196 | Accept-Encoding: 197 | - gzip, deflate 198 | X-Trackertoken: 199 | - b545bcaeafc773ced5b1a853fffd1fc9 200 | Content-Type: 201 | - application/xml 202 | User-Agent: 203 | - Ruby 204 | response: 205 | status: 206 | code: 200 207 | message: OK 208 | headers: 209 | Content-Type: 210 | - application/xml; charset=utf-8 211 | Transfer-Encoding: 212 | - chunked 213 | Status: 214 | - 200 OK 215 | X-Ua-Compatible: 216 | - IE=Edge,chrome=1 217 | Cache-Control: 218 | - max-age=0, private, must-revalidate 219 | Set-Cookie: 220 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWM0ZDQ0OThkYjA5MWMxYzA4MDIzYWQzMTEwNjU4ZGM5BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgI%2B9BsAKOg1uYW5vX251bWkCggI6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdkIDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxWDRQaVpZYjdCcWpxb2FPbEZsSzYwWHllTiswcU1JbDFCY3A5a0lVOGF4VT0GOwBG--7f6ecd1c00c604b20dba09c60951763a6a12cccd; 221 | path=/; HttpOnly 222 | X-Request-Id: 223 | - 0112f6aea4b5ef3db4cf17153a0d1f78 224 | X-Runtime: 225 | - '0.058097' 226 | Date: 227 | - Thu, 28 May 2015 09:48:00 GMT 228 | X-Rack-Cache: 229 | - miss 230 | X-Powered-By: 231 | - Phusion Passenger 4.0.41 232 | Server: 233 | - nginx/1.6.0 + Phusion Passenger 4.0.41 234 | X-Tracker-Client-Pinger-Interval: 235 | - '12' 236 | Content-Encoding: 237 | - gzip 238 | body: 239 | encoding: ASCII-8BIT 240 | string: !binary |- 241 | H4sIAAAAAAAAAwIAAAD//3yRy27CMBBF93yFlX1xEhIekTGLSl13AevIxAOY 242 | JHZqO6T062sbyqOVurE0d869mhmT1WfboBNoI5RcRsk4jhDISnEh98tos357 243 | mUcrOiLGKn2mI4SI4MieO1hGQlrYg47oIp1nWZpPCRY8IJ1WR6hs+RdNJuks 244 | nccE35HgCPGlz6U7YLbXQPCD5pFeN/RgbWcKjIdhGHfipCxrrGZVDXpcqfbi 245 | wOagBnyfyfu8H4wVLbPwe/qE4J9W4Kpea5C2NNbBdCekMAfgBD/rnuRgKi06 246 | 6y5HoVHoS6i2FqhllQC0Y0cJiAdlcK1WuJUeHT5BshaoVQ3UqPEvwUHxLQ0f 247 | vZsLeLk903fgWhH8pHlIDfIJuNVhEe0u6drMXlfmrnQ3gIimcZLjOMNpjJJp 248 | kWdFlqHN+tUtefOEIfrOe/6JyHE6Q/GiyCfFJL5E3D0homFbaAyVArYCTM0I 249 | viqj6w/T0TcAAAD//wMAn+5QTIQCAAA= 250 | http_version: 251 | recorded_at: Thu, 28 May 2015 09:48:01 GMT 252 | - request: 253 | method: put 254 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/92844256 255 | body: 256 | encoding: UTF-8 257 | string: | 258 | 259 | 260 | tolek lolek 261 | elo ziomki macie fajne domki w zimie 262 | feature 263 | 1 264 | finished 265 | Pedro 266 | Pedro 267 | niebieska 268 | 1327280 269 | 2015-04-20T16:54:44+00:00 270 | 271 | headers: 272 | Accept: 273 | - "*/*; q=0.5, application/xml" 274 | Accept-Encoding: 275 | - gzip, deflate 276 | X-Trackertoken: 277 | - b545bcaeafc773ced5b1a853fffd1fc9 278 | Content-Type: 279 | - application/xml 280 | Content-Length: 281 | - '417' 282 | User-Agent: 283 | - Ruby 284 | response: 285 | status: 286 | code: 200 287 | message: OK 288 | headers: 289 | Content-Type: 290 | - application/xml; charset=utf-8 291 | Transfer-Encoding: 292 | - chunked 293 | Status: 294 | - 200 OK 295 | X-Ua-Compatible: 296 | - IE=Edge,chrome=1 297 | Etag: 298 | - '"2d16160707383764f5c482483ad97cc9"' 299 | Cache-Control: 300 | - max-age=0, private, must-revalidate 301 | Set-Cookie: 302 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJThiM2YyZDkxZTliMzFlODUwZWFmNjFkOGMyMGI0ZTZmBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgO5rFcAKOg1uYW5vX251bWkBpjoNbmFub19kZW5pBjoNc3VibWljcm8iBxZgOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFXeWNtSmpWbFJuUnQ2ZzZ5ZWhXNm9kOUJUdW5XNFlhd3hkSTNjSjVxdGtzPQY7AEY%3D--9862b2f80a258212743cc49f8ac7d9b6be8013b4; 303 | path=/; HttpOnly 304 | X-Request-Id: 305 | - 5db582d677c63b9e97ffa8f35cabc6ab 306 | X-Runtime: 307 | - '0.179818' 308 | Date: 309 | - Thu, 28 May 2015 09:48:01 GMT 310 | X-Rack-Cache: 311 | - invalidate, pass 312 | X-Powered-By: 313 | - Phusion Passenger 4.0.41 314 | Server: 315 | - nginx/1.6.0 + Phusion Passenger 4.0.41 316 | X-Tracker-Client-Pinger-Interval: 317 | - '12' 318 | body: 319 | encoding: UTF-8 320 | string: | 321 | 322 | 323 | 92844256 324 | 1327280 325 | feature 326 | https://www.pivotaltracker.com/story/show/92844256 327 | 1 328 | finished 329 | elo ziomki macie fajne domki w zimie 330 | tolek lolek 331 | Pedro 332 | Pedro 333 | 2015/04/20 16:54:44 UTC 334 | 2015/05/27 09:53:30 UTC 335 | niebieska 336 | 337 | http_version: 338 | recorded_at: Thu, 28 May 2015 09:48:02 GMT 339 | recorded_with: VCR 2.9.3 340 | -------------------------------------------------------------------------------- /spec/cassettes/pivotal_project.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 6 | body: 7 | encoding: US-ASCII 8 | string: username=foobar%40gmail.com&password=secret&timeout=60 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | Content-Length: 15 | - '55' 16 | Content-Type: 17 | - application/x-www-form-urlencoded 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Etag: 34 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 35 | Cache-Control: 36 | - max-age=0, private, must-revalidate 37 | Set-Cookie: 38 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJWUxYzkwMzJhZWJkMDQzY2QwODU1N2M5MmE2MDc0MmIyBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgIQh3b4KOg1uYW5vX251bWkC%2FgE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgZROgtvZmZzZXRpADoJem9uZUkiCFVUQwY7AFQ%3D--51a0c5441a874401478866fe9ff13e930b741366; 39 | path=/; HttpOnly 40 | X-Request-Id: 41 | - 1d633f68f48590a6bd056290af844462 42 | X-Runtime: 43 | - '0.121398' 44 | Date: 45 | - Thu, 28 May 2015 09:47:45 GMT 46 | X-Rack-Cache: 47 | - invalidate, pass 48 | X-Powered-By: 49 | - Phusion Passenger 4.0.41 50 | Server: 51 | - nginx/1.6.0 + Phusion Passenger 4.0.41 52 | X-Tracker-Client-Pinger-Interval: 53 | - '12' 54 | body: 55 | encoding: UTF-8 56 | string: | 57 | 58 | 59 | b545bcaeafc773ced5b1a853fffd1fc9 60 | 838284 61 | 62 | http_version: 63 | recorded_at: Thu, 28 May 2015 09:47:46 GMT 64 | - request: 65 | method: get 66 | uri: https://www.pivotaltracker.com/services/v3/projects/1325832 67 | body: 68 | encoding: US-ASCII 69 | string: '' 70 | headers: 71 | Accept: 72 | - "*/*; q=0.5, application/xml" 73 | Accept-Encoding: 74 | - gzip, deflate 75 | X-Trackertoken: 76 | - b545bcaeafc773ced5b1a853fffd1fc9 77 | Content-Type: 78 | - application/xml 79 | User-Agent: 80 | - Ruby 81 | response: 82 | status: 83 | code: 200 84 | message: OK 85 | headers: 86 | Content-Type: 87 | - application/xml; charset=utf-8 88 | Transfer-Encoding: 89 | - chunked 90 | Status: 91 | - 200 OK 92 | X-Ua-Compatible: 93 | - IE=Edge,chrome=1 94 | Cache-Control: 95 | - max-age=0, private, must-revalidate 96 | Set-Cookie: 97 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTQ5MmRmZTg4Y2NiMGEwODI3NmY2MzJkZDA5ZDNjMjljBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgD%2BT7L4KOg1uYW5vX251bWlROg1uYW5vX2RlbmkGOg1zdWJtaWNybyIHB2A6C29mZnNldGkAOgl6b25lSSIIVVRDBjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMW1YeER0QUlxVlBaUXlJY2FPSkE5S3dQQUlXVGsrWHdlMysxYS91TUd2RlU9BjsARg%3D%3D--2fb68dc0951b437d5324c6a76adbe8235110e4f9; 98 | path=/; HttpOnly 99 | X-Request-Id: 100 | - bc6a97c2b2b3804adb9a735098057e71 101 | X-Runtime: 102 | - '0.113725' 103 | Date: 104 | - Thu, 28 May 2015 09:47:46 GMT 105 | X-Rack-Cache: 106 | - miss 107 | X-Powered-By: 108 | - Phusion Passenger 4.0.41 109 | Server: 110 | - nginx/1.6.0 + Phusion Passenger 4.0.41 111 | X-Tracker-Client-Pinger-Interval: 112 | - '12' 113 | Content-Encoding: 114 | - gzip 115 | body: 116 | encoding: ASCII-8BIT 117 | string: !binary |- 118 | H4sIAAAAAAAAAwIAAAD//4RUW0/bMBR+51dYec5w0g4oKJhtaHtDQxo8R45z 119 | mng4dmQ77frvd+ykTVuEkCrV+c537pfi4V+nyAask0bfJ/lllhDQwtRSN/fJ 120 | 68uvL6vkgV0UvTV/QXh2QUgha5YvF1er5aKg+A6Q5h2wpx35w7teAXke2QWN 121 | eNTxYLlHH6UC3fiW+F0P94nUHhqwCcvR1Bkl6m0B3krnufVlzXfsyWj8K+gZ 122 | HJi9QWOlE1wBy9I8XaTLgh6DgcSFMIP27IflGyCPMc2C7tHAWEvrfDnHMvr2 123 | soMp5Jp7CJ8JW2T5Fc2WdJmRLLuLP/L68ljQj23EnMRgLWCssxM9dBXY85rc 124 | FvQjarQDmlcKSs/dm5t0K2MUcJ0wbwco6DEjqmxAGSH9DuvUArbmO3aeN0DM 125 | mizJIR5X0HPicdx7GcuzOcIDGJhSSy+5Kk+Y78DAHDMvzbqsjYa5JK70pnSt 126 | 2bIcp+xzVrCleAXKMV53UqeVMk0qcG5SzFS8mcGnNfTK7DosPT6dbHQKvRTp 127 | GrgfLNQEZ7wehHephOtUA9SO1NKJwYXdSI2tcUtSC72xHscmdcCtaFMMsu+x 128 | edMQuRGIBHQx9ISS8MCQBscrqbD6+EKFBnTYCXQsDK6B9gWdMhiTwTHkwstN 129 | aBf3H0zfV5rfkPzmLlvdZdfj9Cl+qhkbz5UyWzTjuWhDBdw0Ie/xwO6HSknB 130 | 1lw5nKLpKwgw8LL1vnd72QwEcTU0ruS6LkVrLODTQgkOt4X7MKp7pc9owZQw 131 | XSd92Zn6oHYMBUoHYWtcK/v9+HNr+S4JCZ+IRwAhvFZXy9tVdrvaX67IxPY5 132 | o/csBKDjUrG+skOtd9+a8HWJ3nGhoiDaj5rxvj1Dbc186ybRNO6OPf9EZ+NC 133 | uEMk9NRnYXFv2e8tTkRB43vMgc45xjYefUdbuGl4QMfL+r4Iwe8sxitOD2f8 134 | PwAAAP//AwBMbtil+AUAAA== 135 | http_version: 136 | recorded_at: Thu, 28 May 2015 09:47:47 GMT 137 | - request: 138 | method: get 139 | uri: https://www.pivotaltracker.com/services/v3/projects/1325832/stories 140 | body: 141 | encoding: US-ASCII 142 | string: '' 143 | headers: 144 | Accept: 145 | - "*/*; q=0.5, application/xml" 146 | Accept-Encoding: 147 | - gzip, deflate 148 | X-Trackertoken: 149 | - b545bcaeafc773ced5b1a853fffd1fc9 150 | Content-Type: 151 | - application/xml 152 | User-Agent: 153 | - Ruby 154 | response: 155 | status: 156 | code: 200 157 | message: OK 158 | headers: 159 | Content-Type: 160 | - application/xml; charset=utf-8 161 | Transfer-Encoding: 162 | - chunked 163 | Status: 164 | - 200 OK 165 | X-Ua-Compatible: 166 | - IE=Edge,chrome=1 167 | Cache-Control: 168 | - max-age=0, private, must-revalidate 169 | Set-Cookie: 170 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTBiZDcyODIzMTI2MTM3NGE2YTYyOTg3MmQ4OWY0ODg5BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgDcD%2FL4KOg1uYW5vX251bWkCngE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdBQDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxYktEamZoM3Q0MGx2ZG5vdUk0QlkzdTZOb2RPS0h1K1o5TTA4Q0NvNlp2QT0GOwBG--70b829be8745e791b48a969593b6954d437554e1; 171 | path=/; HttpOnly 172 | X-Request-Id: 173 | - 8f37853feee0bd0d358e9c1bb62c0c97 174 | X-Runtime: 175 | - '0.196941' 176 | Date: 177 | - Thu, 28 May 2015 09:47:47 GMT 178 | X-Rack-Cache: 179 | - miss 180 | X-Powered-By: 181 | - Phusion Passenger 4.0.41 182 | Server: 183 | - nginx/1.6.0 + Phusion Passenger 4.0.41 184 | X-Tracker-Client-Pinger-Interval: 185 | - '12' 186 | Content-Encoding: 187 | - gzip 188 | body: 189 | encoding: ASCII-8BIT 190 | string: !binary |- 191 | H4sIAAAAAAAAAwIAAAD//+xbXW/bNhR9768g/LIXL7LzZTtwXHQdBnTDsAJp 192 | 170FtETHXCRRI6m47q/f4YcsO6lqO6ZTpwgQILJESlfU4eU5914OX3/OUnLH 193 | pOIiv2x1jzotwvJYJDy/uWx9/PDbz/3W69GrodJCcqaInhfsskWlpPMWiUWZ 194 | 68vW2aBFtNA0tYejV4TY5nNzhGOe+F481+yGydZocNzr9gdnp8OIJ75RIcW/ 195 | LNbXDxt3T47P+ifHw6hu4vvYh1wbi0bxVEg2jJbOuGeXMh1NtS7URRTNZrOj 196 | gt8ZQ7Wk8S2TR7HIXJ9ITcUsqu0y/dwd4lJKlutrpalmIxrHrNAsGUar513b 197 | hKlY8kJjKEefGMkZS8gxyWg85TnGTjFNymIYLTdzHXOasdEV02VBEnbHUlFk 198 | eCY+xB2XIjfHw8i2cc0l+69kCmZcj+ej9yyRYhitnPOmSwabk2uq/QdI8FPz 199 | jLVGx53uWdQ5iU46pNO5sH/k44e3eK1FH/+ssjC9vnGT06jbI93eRefsotNz 200 | N6n7+JtUw/ZNU7r3TFnq5G+TC30Pgu4CQGYuVT++jrn+8Rle+rzGnAWnZp/1 201 | 6NOU6p8UEaUk4zK5wWeaCEn0lC2+3ethZFvagbUdaamBuWr0/a/6srFnu0Gr 202 | eixeKarfCV/fvLq55EFuDzeaZUtvPDykWQa7dp9lV1NRpgkZM2LQohQfp4xM 203 | pMjwMbXiCbNfMWd6JuRtm8y4npIxVTwm5outnYqZwKSV8I4v0w9wS+mYpWqU 204 | sCIVc+eU/KnH4LIfzvtP4OfKcP4fltXIhJ/lGTzg/TUMC1J1yXunx64Ub5KM 205 | 5wQLUIVkogoWc5qSEuCzzz0iWE5imhPnni2oJ1wqTajtbBsmXGIFTecEJ0yD 206 | X39pw51pkjLj3LCoy3mBlQBXCqoUJkRy1DgBHthk5pUWJBU3PH+ZDPVksOM/ 207 | jB4/D847hzoPjGXr50E32Dx4L0VSxhrwxeqLKQcWheM4LcGXiOY6ZW2wowXD 208 | apNC8hjnrv74uD2Q/UTK2Qy3sc99QXVIVMM9huL2Yb37OSxbj+pw3n1rF1ti 209 | daVJhUpSTIUGvX9h/xX92N3jBtSdgbEJy9Zj8ySYx30sNrMy1bwAI/Cu04FU 210 | EZonUEzylogchBxO23ANCfok5z86gk8jiN9VKf1Qv3qWsDuCA2q6wAheUXX7 211 | 586NCIYwLAqQYk+rFWMk5eDLYlKBVnk56AHqMOxQW2bjnPL0BbM1I8A4FgVi 212 | gjtR3YOVfOcbSb5wVLcRthUHHksxUxjuJVlYUEgvE4rzuO12FkgmBum4jgjo 213 | j80TtvGyIRDbO1hxZixbTxWegMb+SW9rHrCArQerkXBvfn/zz8vyb0LGfvmv 214 | gNkuFR3zlGuMTh0/QLsAYe4lT2uj1TZ4XTkXuxAad2EiOu6/pLniVnH7BRM2 215 | EIlEBJSI/agLB/NMw+C9gxWkxrL1M/k7kn6eFUJqsiD9SzET5WLtb6/+Rjgy 216 | fVl+lmb5ziS/d7Ay1Vi2HrFPwJbukXyTBfJx6jjlsZGhhFYkqW3VqdEBNE0X 217 | wjVhGkQfUsAFGg3jsgEXtRcmBfpyfiD5Vpu13VCvBmFSBytZextJ1u+KZprU 218 | 8UAs2dX3QEZI7idsfWhAXS0vaAysmPFoB0HrwSrV3kZKNRzvf4shJTy2qRjQ 219 | xYJIfjPVqPyROZNeiVKSI2IChsjzhMdI3MCLopwGocAcyUjNMmU6O7A21b6s 220 | hmuWPPkdR4oGz9cogkGJxuTZob93cXry6LKYRXEN7nLq7rJ/9PcPVvUay3Zn 221 | HglLOWo6NqniagzUNFMPyTJxV0tiW4zyA3lsr1SDudr+wQozY9nuYJvwnKvp 222 | TlizPhjOlBdl6tLiXqTDTe4vuvJkHCA4ogIKp3GJiHeQqtL+imharRVCbanc 223 | qKi02R2J7EE2zoRwVExTFKEiT4cQMUqDZlOWu4COlTqu8uGZi517YbVdMhT9 224 | g1UpxrLdnVGZbww1BAszlIuZQliWQSOj0iyRqLC0MhpgUiKnKf8CcGU4i2Bi 225 | G+Ial1xjW2hZZYe9yHbpYde4GciNRBDFbSJD1WFSa3oTv6QIQXGcfR6Cvd/A 226 | BENiOKB2CVlW319RLqsOcAtcNiIHGmHCb1AGSpRIpQXupESoxwSsAU0qTRU+ 227 | vPkeMmTBl8p1MLFvs4ujGxwsxTeWrXd068LhWwDqL9SqOHgghs1SpFf9L0+y 228 | UiFuUckiRXlTuzQDonvViMYvohSxEZ/NiqF6OhC7z1rE74PSIPGYQUCRgKwW 229 | oyrUZiFjWQ3X8F7tXY60HErBE6hKs9zx/A5VNUI+E872VUe27RaWQUBCH7bc 230 | yVj2pM7qEd4FcTOE5lDvnHDE7ahMVgqsbVJkym1tT0XxnsEa2b9AJsVuWHuw 231 | 16ySk1MW32ITUBj/E1AXhORUg29sotpiCWxE1TuzY1Oa3S+O0FO77Yfc4MyM 232 | 7qeo46lXqTgoTgJy78CeaqO8QUBa1YipTyYEYQOipjyzHGfcxPVlYtIJbhsl 233 | VKXbT7xQkDQzO41dsUHxI4PQbBlW2Mml4hKbGUX+Ve8VpERoed9RXSIEkmp2 234 | oc1AiUEykCu3u1z5Fxd2xLZw7wns99rnplhPHPayKXbQ6Ryo/HGWrWcU67LR 235 | m/v+/wEAAP//7F3BTttQEPwVfwCUEEIlLpXaqpV6bi89GuKEqCmJYkDK33dm 236 | 9z3HCTUO8tpsLF9LEc/yeHdnd3bep8rv9AdGbVssmf9NZpRISK8loIEbhJoJ 237 | 9MMEE1rk62W6TbLNBiwmdIT4K+FDP4GSAuvr/61Wd8I9w1SBF+20qNWTuYHg 238 | fgBawEThSRa90doBUeeUWUJRnECznJVGUFwgHKBYZ8SBF25Y3VpWLXoyH1D8 239 | ycYzstFiJgoH6CG00Q1BggIQ4e8uk+qYgVK3gnvQapQnObONfE6LZMDtqCK5 240 | TlxjkXyrm4XyKlCdwfFA5DCKvlukXvJ8sniiTmYt5cjYiwQsD4TO104t/7ZW 241 | 0s3o0m3hx5N1GuqEhwmaZM9oH1CSW8sLdPcpNC3c51yJvZEEufwPpm/iRIR/ 242 | +ZD8umd6VgJRGgXGcFi9o18Ndv6J+Aeg7UU7R0E9oBkOWkCzYVvcNnHzZJ2i 243 | uZLGqJMJvVM2mWgJKf/WyBiRpTNtgSwm3OvVQ07DiYK7nHKr/cDS4cwgghry 244 | FrtWKL6FV/Q1JhlZzNuSbw/YwsyS3+yjh2GdFIMtO7V13RW18HjCOzEkFpZj 245 | Oz1Zm2O7L9grSZbp0wPH9KdMA6ZZOl0S8Wr8eOggCBPBycEaSfyNt5vV4bW4 246 | pQY8WX1Ce19qkC/msAJbi/QF+Q5Ccemay+hmT8N12pCMWik8LR72IuFjN/Hg 247 | uhmN3bICnqwedl20g6uLdNRM6pg5Xz1iS6NwlxtQVva7BcrcVus8mXeUwXmQ 248 | np4DqA5AZVhi2VLAcdeK5UoKuN/poCbkiO6GJFPZeWPbY4GMqul0AOABAN2W 249 | bOOjSjbD3IkUGcZRsvC4TGGz8XGUTNNt9JpCJQ0tqXQW8D+XHFxFRl61GAkY 250 | QmOvBrHlhlxpRTI0NrSpgSYcbdH7AdPQgwyfXqPO75XbGo8nq8++htTia0kL 251 | qJu7Rd8WqBLhB/Y41C+Yi2sNoJmzbVzWHnIuETdJykavcDAWlMdJ04DfvdsS 252 | bka8AcGjo6qerB6/hnG2OSLjZhJC8Sx9hsAJ5voD4g4RZ9jptS0tr46SXXtB 253 | XJw7yHjh5W74AMCXd77wIhYEFkNuYzlq4LnabB5j9fKZV1fMIAa9xUU1rBiR 254 | g1kJgobIFJRLSRf4OUZZkOkhfPUieulDNpnvX7llIzxZfZbsQoD9eb3GJnj+ 255 | uAUbmcsgHxPROO6fpXeyGY6FdJGXcORPfOEqCSxNQVOCvnKOfl9wfouwHOAn 256 | EWvilmTwZPXwMyQZjYq09YoOleE+CM2f/ajP5KKWeYZATiff6GvUJOJN3PIC 257 | nqwecm6qNG45Bcgh2G3BRFcrsWC+PL+mPOqkVy53OpAWEOiWJ0zc8IS9pSfI 258 | 5HZGycGCJYjZ0S7Z0Ji3iH3qpsauSfBYQ04OARHXmsHZAPk7wwWDvdocbwGj 259 | hlTClstO3IxJXpvwwgY6ok7UBQPcZGeuirlO3NIQnqw+KZ93kZWr7piTG2QK 260 | hPFGjvDF9cPjRzcq4iPFD6nRbOPaLe3gyZzA7bteElnINouZR4qxWopBnN6a 261 | SLKL0R2cTIUGt3SnRtd6Tlu4uaUc10dRjneNbuHiQVZ04shxS2ELui2Lu2VP 262 | ZhAa4PhcTXit5e3gthUbT+YkplWlUBm77iGLs9XpJp3BSBRL/L1o1xnhzC0z 263 | 4F3lrePsHwAAAP//7J3dagIxEIXvfYp9g/amgiAFLyr0btEn8CewgV1Saq19 264 | fM9MsibrTxt1aJOld6I3Kx4nyWTO+cbh5f7Fnl25XQIagxTbI1E5t9fpCtoL 265 | gd1dwpI9BTylcwoolSGSpFNSMNWEaBhi/eIdtDi4ln3qRTGbz9nbOvkwzb/I 266 | cOWQKj/aPlki9evSOsmGwAZwgYpuxBCVvEGbg2hHvZCWXSDtF+Lo09t900RN 267 | TnP6KI7n/KdbfvXFAC076Un9jJaXFVyqkrGqAIUNvV9v3bem7Dy0OPo+jk9S 268 | i4L3DZJjIcPOuUA+CnSm2LBcVCCw+MQbD7Rleh+4VwrTxtSkpex3/tvn4D/8 269 | QT4yftSh4F5f1o9KT+bXSnntTCkJO/Ar52RNPSuNa4M/iPArtYBJQSGwRers 270 | w09+dgxDr7fgN4wfuh8N+PIh7uTmhijOkVkJELEz75z/gfbA6wtE2INaoRW+ 271 | x+1bnVSZv6PHOOZvzFZHQFhH0bDIaTMNz7RYGwpF7wMJWnMils0RzFhc11ab 272 | VGmz0JBYh1xAQ8CZA8RV6LIySBhAF8lNI8Ic1Q5AHOoWJ/u5rN6MleQHcdSb 273 | Xt1VpwS3wbIN8jg+7C/VqSli+ZaEcIC8EDBc12ZH6x2NmLiQnwVybtsh/OA6 274 | NtuV8FCs7AutNs+DPQAAAP//AwCPuqWQ55UAAA== 275 | http_version: 276 | recorded_at: Thu, 28 May 2015 09:47:48 GMT 277 | recorded_with: VCR 2.9.3 278 | -------------------------------------------------------------------------------- /spec/cassettes/issues_creator.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://www.pivotaltracker.com/services/v3/tokens/active 6 | body: 7 | encoding: US-ASCII 8 | string: username=foobar%40gmail.com&password=secret&timeout=60 9 | headers: 10 | Accept: 11 | - "*/*; q=0.5, application/xml" 12 | Accept-Encoding: 13 | - gzip, deflate 14 | Content-Length: 15 | - '55' 16 | Content-Type: 17 | - application/x-www-form-urlencoded 18 | User-Agent: 19 | - Ruby 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | Content-Type: 26 | - application/xml; charset=utf-8 27 | Transfer-Encoding: 28 | - chunked 29 | Status: 30 | - 200 OK 31 | X-Ua-Compatible: 32 | - IE=Edge,chrome=1 33 | Etag: 34 | - '"2aca2abf73441eff33bb5c8599b6a03b"' 35 | Cache-Control: 36 | - max-age=0, private, must-revalidate 37 | Set-Cookie: 38 | - t_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJWFkMjRjYzRiN2M2OWI2Njg5ZDhjMGE3NmZmNDJjM2UzBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgLb%2FQL4KOg1uYW5vX251bWkCswM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgeUcDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBU--b48c3dc1ad62501e4218916805ff139ad72aa8b2; 39 | path=/; HttpOnly 40 | X-Request-Id: 41 | - f58e40a0acaf98ff68e2cbcc272fdb9d 42 | X-Runtime: 43 | - '0.116360' 44 | Date: 45 | - Thu, 28 May 2015 09:47:36 GMT 46 | X-Rack-Cache: 47 | - invalidate, pass 48 | X-Powered-By: 49 | - Phusion Passenger 4.0.41 50 | Server: 51 | - nginx/1.6.0 + Phusion Passenger 4.0.41 52 | X-Tracker-Client-Pinger-Interval: 53 | - '12' 54 | body: 55 | encoding: UTF-8 56 | string: | 57 | 58 | 59 | b545bcaeafc773ced5b1a853fffd1fc9 60 | 838284 61 | 62 | http_version: 63 | recorded_at: Thu, 28 May 2015 09:47:37 GMT 64 | - request: 65 | method: get 66 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280 67 | body: 68 | encoding: US-ASCII 69 | string: '' 70 | headers: 71 | Accept: 72 | - "*/*; q=0.5, application/xml" 73 | Accept-Encoding: 74 | - gzip, deflate 75 | X-Trackertoken: 76 | - b545bcaeafc773ced5b1a853fffd1fc9 77 | Content-Type: 78 | - application/xml 79 | User-Agent: 80 | - Ruby 81 | response: 82 | status: 83 | code: 200 84 | message: OK 85 | headers: 86 | Content-Type: 87 | - application/xml; charset=utf-8 88 | Transfer-Encoding: 89 | - chunked 90 | Status: 91 | - 200 OK 92 | X-Ua-Compatible: 93 | - IE=Edge,chrome=1 94 | Cache-Control: 95 | - max-age=0, private, must-revalidate 96 | Set-Cookie: 97 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTE3ZmU5ZGVhOGIxMjI3MTY0MzdmNzAwOGE1MzU0MTFlBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgJ9AUL4KOg1uYW5vX251bWkCwwI6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdwcDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxVUcwV0lXRHZRa1NCaVdBMzl5TUVWTDVyUVpTUjJ5L3paYUljSjhzaVoxTT0GOwBG--553f206d4c82266dc939a8f77b8facd988966979; 98 | path=/; HttpOnly 99 | X-Request-Id: 100 | - 1ef6a9033afd16fbbc77c724ee628fc8 101 | X-Runtime: 102 | - '0.045315' 103 | Date: 104 | - Thu, 28 May 2015 09:47:37 GMT 105 | X-Rack-Cache: 106 | - miss 107 | X-Powered-By: 108 | - Phusion Passenger 4.0.41 109 | Server: 110 | - nginx/1.6.0 + Phusion Passenger 4.0.41 111 | X-Tracker-Client-Pinger-Interval: 112 | - '12' 113 | Content-Encoding: 114 | - gzip 115 | body: 116 | encoding: ASCII-8BIT 117 | string: !binary |- 118 | H4sIAAAAAAAAAwIAAAD//4RUS2/bMAy+91cYPmeV4zRoVrjqtmK7DethPQu0 119 | zdhaZMmQ5GTprx+l2Hm06wYEiPjx45tm8fC7U8kWrZNG36fz6yxNUFemlrq5 120 | T59/fvuwSh/4VdFb8wsrz6+SpJA1ny/y23yVFYzeAdLQIX+RpgNVsChEokcL 121 | nhwLhbrxbeL3Pd6nUnts0KZ8TvavKNFuh7gRzoP1ooY9/240/RXsFRyYvSFn 122 | wlWgkGez+SyfLQp2DgYSVJUZtOdfLGwxeYy1FWxCA2MtrfPilMshtpcdjinX 123 | 4DGIKc+z+ZJlS5bfJHl+l2X0S55/PhbsfR+xpmqwFinXUxA9dCXav/TkPWr0 124 | gxpKhcKD27jRtjRGIeiUeztgwc4Z0WSLylTS76lPLdJoPtO4ocHErJNFcszH 125 | Few18TzvScfnNPUpwyMYmFJLL0GJC+YbMDAPlQuzFrXReGqJE94I15odn+e0 126 | RrE//2IFXwpKVI5riaVEt4GZA72ZOZKb2YvsoGAj40CmMUPl5Ta0A/z7071N 127 | so93y8XdYpyugkvL2FhQyuzIjYeq7Wi4bpzAWzyw+6FUsuJrUI6mNEpBMTgU 128 | rfe9m3QnIKjLoXECdC2q1likp0WBjrYRfFiFyeh/tOCqMl0nvehMfTQ7hwKl 129 | w9B118p+Wi+wFvZpKPhCfQAIohOwvMny1c1qOgeR2ZMXoycWAdiBVLwv7VDr 130 | /acmSNcUnRY2KqL/aBnvxxPW1pxuyaga18nxp68U7LBw7pgJu4xZWPou+I+d 131 | Rluw+D7UwE41xjGeydEXbTIdqMPletuEEPekptPIjrfxDwAAAP//AwDXq2nA 132 | TQUAAA== 133 | http_version: 134 | recorded_at: Thu, 28 May 2015 09:47:37 GMT 135 | - request: 136 | method: get 137 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/94184406 138 | body: 139 | encoding: US-ASCII 140 | string: '' 141 | headers: 142 | Accept: 143 | - "*/*; q=0.5, application/xml" 144 | Accept-Encoding: 145 | - gzip, deflate 146 | X-Trackertoken: 147 | - b545bcaeafc773ced5b1a853fffd1fc9 148 | Content-Type: 149 | - application/xml 150 | User-Agent: 151 | - Ruby 152 | response: 153 | status: 154 | code: 200 155 | message: OK 156 | headers: 157 | Content-Type: 158 | - application/xml; charset=utf-8 159 | Transfer-Encoding: 160 | - chunked 161 | Status: 162 | - 200 OK 163 | X-Ua-Compatible: 164 | - IE=Edge,chrome=1 165 | Cache-Control: 166 | - max-age=0, private, must-revalidate 167 | Set-Cookie: 168 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWJjMGIyOTYxZmQ4YTVkYzdjMWI0ZmY5OWY2M2UzZmE0BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgKEXXr4KOg1uYW5vX251bWkCyAE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdFYDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxRi9Pa2tCK0lvcGd4dklXVFRrNDZKRGRJR3VLZVhxMUNzZDBGeUJOVGtybz0GOwBG--a0f12da7802c2cb3335d2da51db121409aaf6e26; 169 | path=/; HttpOnly 170 | X-Request-Id: 171 | - bd5fe652c1b94d8c9d291f817b3dd20e 172 | X-Runtime: 173 | - '0.081793' 174 | Date: 175 | - Thu, 28 May 2015 09:47:37 GMT 176 | X-Rack-Cache: 177 | - miss 178 | X-Powered-By: 179 | - Phusion Passenger 4.0.41 180 | Server: 181 | - nginx/1.6.0 + Phusion Passenger 4.0.41 182 | X-Tracker-Client-Pinger-Interval: 183 | - '12' 184 | Content-Encoding: 185 | - gzip 186 | body: 187 | encoding: ASCII-8BIT 188 | string: !binary |- 189 | H4sIAAAAAAAAAwIAAAD//3ySy27CMBBF93yFlTXFScojRMYsKnXdBayRiQcw 190 | JHZqO6TJ19c2lEcrVcrCM3Pu1fg6ZPlVlegM2gglF1EyiiMEslBcyP0iWq/e 191 | X7JoSQfEWKU7OkCICI5sV8MiEtLCHnRE5+MkG4/jKcGCB6TW6giF3fxFk9d0 192 | lmYxwXckKIL9xvvSHTDbaCD4oeeRRpf0YG1tcozbth3V4qwsK61mxQn0qFDV 193 | RYHNQbX4vpPXeT0YKypm4ff2CcE/o8AVjdYg7cZYB9OdkMIcgBP83PckB1No 194 | UVuXHD2ynjPUo73SJ4FaxFWJaoWMFLBvXF0zt+cBFX1X+rHVfYeUUdtuQPCj 195 | j/eVrIKroWTIMHnyUi9SR38kOBAe1fDZuO2Bb7Yd/QCuFcFPPQ+pVj4Btzpc 196 | V7u83ZjZazDclS4piGgaJxMcuy9DySRPs3wyRevVm4vipglLNLXX/GuROItZ 197 | nsT5ZH6xuGuCRcm2UBrq7zoMmQ1791gEX/supOsP+A0AAP//AwBvV9t2sAIA 198 | AA== 199 | http_version: 200 | recorded_at: Thu, 28 May 2015 09:47:38 GMT 201 | - request: 202 | method: get 203 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280 204 | body: 205 | encoding: US-ASCII 206 | string: '' 207 | headers: 208 | Accept: 209 | - "*/*; q=0.5, application/xml" 210 | Accept-Encoding: 211 | - gzip, deflate 212 | X-Trackertoken: 213 | - b545bcaeafc773ced5b1a853fffd1fc9 214 | Content-Type: 215 | - application/xml 216 | User-Agent: 217 | - Ruby 218 | response: 219 | status: 220 | code: 200 221 | message: OK 222 | headers: 223 | Content-Type: 224 | - application/xml; charset=utf-8 225 | Transfer-Encoding: 226 | - chunked 227 | Status: 228 | - 200 OK 229 | X-Ua-Compatible: 230 | - IE=Edge,chrome=1 231 | Cache-Control: 232 | - max-age=0, private, must-revalidate 233 | Set-Cookie: 234 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTFhZTRlYTJlYjE0Mzc1MTNmYzlkNjUzZDJhYjYxMjU5BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgFptcb4KOg1uYW5vX251bWkCDQM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgd4EDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxVVZsOHhYTXhXeXdSUkdsaElEaW94STRISFR0ZUhmM2xPNVpvTkUwMExWWT0GOwBG--a673b61634a02cc3a56610966803372682277242; 235 | path=/; HttpOnly 236 | X-Request-Id: 237 | - b1942a716eaca43dd8a979f709fcca2b 238 | X-Runtime: 239 | - '0.045435' 240 | Date: 241 | - Thu, 28 May 2015 09:47:39 GMT 242 | X-Rack-Cache: 243 | - miss 244 | X-Powered-By: 245 | - Phusion Passenger 4.0.41 246 | Server: 247 | - nginx/1.6.0 + Phusion Passenger 4.0.41 248 | X-Tracker-Client-Pinger-Interval: 249 | - '12' 250 | Content-Encoding: 251 | - gzip 252 | body: 253 | encoding: ASCII-8BIT 254 | string: !binary |- 255 | H4sIAAAAAAAAAwIAAAD//4RUS2/bMAy+91cYPmeV4zRoVrjqtmK7DethPQu0 256 | zdhaZMmQ5GTprx+l2Hm06wYEiPjx45tm8fC7U8kWrZNG36fz6yxNUFemlrq5 257 | T59/fvuwSh/4VdFb8wsrz6+SpJA1ny/y23yVFYzeAdLQIX+RpgNVsChEokcL 258 | nhwLhbrxbeL3Pd6nUnts0KZ8TvavKNFuh7gRzoP1ooY9/240/RXsFRyYvSFn 259 | wlWgkGez+SyfLQp2DgYSVJUZtOdfLGwxeYy1FWxCA2MtrfPilMshtpcdjinX 260 | 4DGIKc+z+ZJlS5bfJHl+l2X0S55/PhbsfR+xpmqwFinXUxA9dCXav/TkPWr0 261 | gxpKhcKD27jRtjRGIeiUeztgwc4Z0WSLylTS76lPLdJoPtO4ocHErJNFcszH 262 | Few18TzvScfnNPUpwyMYmFJLL0GJC+YbMDAPlQuzFrXReGqJE94I15odn+e0 263 | RrE//2IFXwpKVI5riaVEt4GZA72ZOZKb2YvsoGAj40CmMUPl5Ta0A/z7071N 264 | so93y8XdYpyugkvL2FhQyuzIjYeq7Wi4bpzAWzyw+6FUsuJrUI6mNEpBMTgU 265 | rfe9m3QnIKjLoXECdC2q1likp0WBjrYRfFiFyeh/tOCqMl0nvehMfTQ7hwKl 266 | w9B118p+Wi+wFvZpKPhCfQAIohOwvMny1c1qOgeR2ZMXoycWAdiBVLwv7VDr 267 | /acmSNcUnRY2KqL/aBnvxxPW1pxuyaga18nxp68U7LBw7pgJu4xZWPou+I+d 268 | Rluw+D7UwE41xjGeydEXbTIdqMPletuEEPekptPIjrfxDwAAAP//AwDXq2nA 269 | TQUAAA== 270 | http_version: 271 | recorded_at: Thu, 28 May 2015 09:47:40 GMT 272 | - request: 273 | method: get 274 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/memberships 275 | body: 276 | encoding: US-ASCII 277 | string: '' 278 | headers: 279 | Accept: 280 | - "*/*; q=0.5, application/xml" 281 | Accept-Encoding: 282 | - gzip, deflate 283 | X-Trackertoken: 284 | - b545bcaeafc773ced5b1a853fffd1fc9 285 | Content-Type: 286 | - application/xml 287 | User-Agent: 288 | - Ruby 289 | response: 290 | status: 291 | code: 200 292 | message: OK 293 | headers: 294 | Content-Type: 295 | - application/xml; charset=utf-8 296 | Transfer-Encoding: 297 | - chunked 298 | Status: 299 | - 200 OK 300 | X-Ua-Compatible: 301 | - IE=Edge,chrome=1 302 | Cache-Control: 303 | - max-age=0, private, must-revalidate 304 | Set-Cookie: 305 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWJiMDkzMzk3NTIwMmZmYmFiZWRmMGY2NmJiYTM1ODliBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgJwLgL4KOg1uYW5vX251bWkCPgI6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgdXQDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxZEhoV2UzaURvdFIzTFdLV2NsSjRLUTc3WURKL3h1ZWpuMGtJdVJvN2ZZUT0GOwBG--0381d32eed6f2bc7bfc5e58aab576336bd567ec4; 306 | path=/; HttpOnly 307 | X-Request-Id: 308 | - 60f9285f776afaf2efe37f46c8d84ca0 309 | X-Runtime: 310 | - '0.061025' 311 | Date: 312 | - Thu, 28 May 2015 09:47:40 GMT 313 | X-Rack-Cache: 314 | - miss 315 | X-Powered-By: 316 | - Phusion Passenger 4.0.41 317 | Server: 318 | - nginx/1.6.0 + Phusion Passenger 4.0.41 319 | X-Tracker-Client-Pinger-Interval: 320 | - '12' 321 | Content-Encoding: 322 | - gzip 323 | body: 324 | encoding: ASCII-8BIT 325 | string: !binary |- 326 | H4sIAAAAAAAAAwIAAAD//0xQu27DMAzc/RWC9kaOk6IeaKZLuyZD+wGKRSQs 327 | 9ILsJnW/vopdw954vDvegXD4cVbcKHUcfCO3m1IK8m0w7C+N/Px4f6rlAQtw 328 | 5M5ZdOXYiX6I1Eidkh4kFkKsyAfMCzb4vC+rel+DyvO0jNkf/ASyhpxmi/Gc 329 | vo0fXi8PtGmDAzURoyfLvHaEJzIpgBrnmWDPPWvb4ekth8xgilLrLEjBEh7v 330 | nhKocf7vk8IXtf1y0OB2V71UdbmUngv8cnDarhuAiosf1PKf/KwV6rD4AwAA 331 | //8DAHDPkbBjAQAA 332 | http_version: 333 | recorded_at: Thu, 28 May 2015 09:47:40 GMT 334 | - request: 335 | method: get 336 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/stories/94184406/notes 337 | body: 338 | encoding: US-ASCII 339 | string: '' 340 | headers: 341 | Accept: 342 | - "*/*; q=0.5, application/xml" 343 | Accept-Encoding: 344 | - gzip, deflate 345 | X-Trackertoken: 346 | - b545bcaeafc773ced5b1a853fffd1fc9 347 | Content-Type: 348 | - application/xml 349 | User-Agent: 350 | - Ruby 351 | response: 352 | status: 353 | code: 200 354 | message: OK 355 | headers: 356 | Content-Type: 357 | - application/xml; charset=utf-8 358 | Transfer-Encoding: 359 | - chunked 360 | Status: 361 | - 200 OK 362 | X-Ua-Compatible: 363 | - IE=Edge,chrome=1 364 | Cache-Control: 365 | - max-age=0, private, must-revalidate 366 | Set-Cookie: 367 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTk0OTk4Y2M4MDc5MGRjMTUyZmNlODE0NzkxMDZkMmM1BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgBMoj74KOg1uYW5vX251bWkCHAM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgd5YDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxZm4rbUZNRFRCWUVjWE9xZ0poT1NYbGNBa0N4OWVFRlBkOFVkM3B5dXppZz0GOwBG--19b09618dbbb41c7f1807447ec76d33645f9f190; 368 | path=/; HttpOnly 369 | X-Request-Id: 370 | - ebdf3b2349e5239d68682c8f4b1dc96f 371 | X-Runtime: 372 | - '0.043605' 373 | Date: 374 | - Thu, 28 May 2015 09:47:41 GMT 375 | X-Rack-Cache: 376 | - miss 377 | X-Powered-By: 378 | - Phusion Passenger 4.0.41 379 | Server: 380 | - nginx/1.6.0 + Phusion Passenger 4.0.41 381 | X-Tracker-Client-Pinger-Interval: 382 | - '12' 383 | Content-Encoding: 384 | - gzip 385 | body: 386 | encoding: ASCII-8BIT 387 | string: !binary |- 388 | H4sIAAAAAAAAA7Kxr8jNUShLLSrOzM+zVTLUM1BSSM1Lzk/JzEu3VQoNcdO1 389 | ULK347LJyy9JLVYoqSxItVVKLCpKrFTSt+MCAAAA//8DAPeRVfM9AAAA 390 | http_version: 391 | recorded_at: Thu, 28 May 2015 09:47:41 GMT 392 | - request: 393 | method: get 394 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280 395 | body: 396 | encoding: US-ASCII 397 | string: '' 398 | headers: 399 | Accept: 400 | - "*/*; q=0.5, application/xml" 401 | Accept-Encoding: 402 | - gzip, deflate 403 | X-Trackertoken: 404 | - b545bcaeafc773ced5b1a853fffd1fc9 405 | Content-Type: 406 | - application/xml 407 | User-Agent: 408 | - Ruby 409 | response: 410 | status: 411 | code: 200 412 | message: OK 413 | headers: 414 | Content-Type: 415 | - application/xml; charset=utf-8 416 | Transfer-Encoding: 417 | - chunked 418 | Status: 419 | - 200 OK 420 | X-Ua-Compatible: 421 | - IE=Edge,chrome=1 422 | Cache-Control: 423 | - max-age=0, private, must-revalidate 424 | Set-Cookie: 425 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJWE5N2Q4YmRkMDQxOTJjNTBkMzY5NjA3NDlhMjA5ODVkBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgK6jnb4KOg1uYW5vX251bWkC2gI6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgZzOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFwdFB4SXJiRTdlanJGYnJQdXMrVGdOYUlHcFBCam1XZGdLaHBpRVRIUUI0PQY7AEY%3D--4bf494df7675f8de9bcd69624bbb4c9a7f796d85; 426 | path=/; HttpOnly 427 | X-Request-Id: 428 | - c10274b779d0ec2a4e0866312495bc34 429 | X-Runtime: 430 | - '0.040416' 431 | Date: 432 | - Thu, 28 May 2015 09:47:41 GMT 433 | X-Rack-Cache: 434 | - miss 435 | X-Powered-By: 436 | - Phusion Passenger 4.0.41 437 | Server: 438 | - nginx/1.6.0 + Phusion Passenger 4.0.41 439 | X-Tracker-Client-Pinger-Interval: 440 | - '12' 441 | Content-Encoding: 442 | - gzip 443 | body: 444 | encoding: ASCII-8BIT 445 | string: !binary |- 446 | H4sIAAAAAAAAA4RUS2/bMAy+91cYPmeV4zRoVrjqtmK7DethPQu0zdhaZMmQ 447 | 5GTprx+l2Hm06wYEiPjx45tm8fC7U8kWrZNG36fz6yxNUFemlrq5T59/fvuw 448 | Sh/4VdFb8wsrz6+SpJA1ny/y23yVFYzeAdLQIX+RpgNVsChEokcLnhwLhbrx 449 | beL3Pd6nUnts0KZ8TvavKNFuh7gRzoP1ooY9/240/RXsFRyYvSFnwlWgkGez 450 | +SyfLQp2DgYSVJUZtOdfLGwxeYy1FWxCA2MtrfPilMshtpcdjinX4DGIKc+z 451 | +ZJlS5bfJHl+l2X0S55/PhbsfR+xpmqwFinXUxA9dCXav/TkPWr0gxpKhcKD 452 | 27jRtjRGIeiUeztgwc4Z0WSLylTS76lPLdJoPtO4ocHErJNFcszHFew18Tzv 453 | ScfnNPUpwyMYmFJLL0GJC+YbMDAPlQuzFrXReGqJE94I15odn+e0RrE//2IF 454 | XwpKVI5riaVEt4GZA72ZOZKb2YvsoGAj40CmMUPl5Ta0A/z7071Nso93y8Xd 455 | YpyugkvL2FhQyuzIjYeq7Wi4bpzAWzyw+6FUsuJrUI6mNEpBMTgUrfe9m3Qn 456 | IKjLoXECdC2q1likp0WBjrYRfFiFyeh/tOCqMl0nvehMfTQ7hwKlw9B118p+ 457 | Wi+wFvZpKPhCfQAIohOwvMny1c1qOgeR2ZMXoycWAdiBVLwv7VDr/acmSNcU 458 | nRY2KqL/aBnvxxPW1pxuyaga18nxp68U7LBw7pgJu4xZWPou+I+dRluw+D7U 459 | wE41xjGeydEXbTIdqMPletuEEPekptPIjrfxDwAAAP//AwDXq2nATQUAAA== 460 | http_version: 461 | recorded_at: Thu, 28 May 2015 09:47:42 GMT 462 | - request: 463 | method: get 464 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/memberships 465 | body: 466 | encoding: US-ASCII 467 | string: '' 468 | headers: 469 | Accept: 470 | - "*/*; q=0.5, application/xml" 471 | Accept-Encoding: 472 | - gzip, deflate 473 | X-Trackertoken: 474 | - b545bcaeafc773ced5b1a853fffd1fc9 475 | Content-Type: 476 | - application/xml 477 | User-Agent: 478 | - Ruby 479 | response: 480 | status: 481 | code: 200 482 | message: OK 483 | headers: 484 | Content-Type: 485 | - application/xml; charset=utf-8 486 | Transfer-Encoding: 487 | - chunked 488 | Status: 489 | - 200 OK 490 | X-Ua-Compatible: 491 | - IE=Edge,chrome=1 492 | Cache-Control: 493 | - max-age=0, private, must-revalidate 494 | Set-Cookie: 495 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTczYjc5NjY3NjhhOWRmMjMyMjlkMDNiYzBkZGFhODYzBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgFLbq74KOg1uYW5vX251bWkCXwM6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgeGMDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxMkd5ZENSSExSNkxtZUUvSXlrcGVkZmF3MHExMWFPZU9lbUx5YlA4c1U4RT0GOwBG--38356172ce14b72f533c1df03d0b8e81f4a79e06; 496 | path=/; HttpOnly 497 | X-Request-Id: 498 | - 036bc3a68117b751df2ce2d02f2d6fbd 499 | X-Runtime: 500 | - '0.058892' 501 | Date: 502 | - Thu, 28 May 2015 09:47:42 GMT 503 | X-Rack-Cache: 504 | - miss 505 | X-Powered-By: 506 | - Phusion Passenger 4.0.41 507 | Server: 508 | - nginx/1.6.0 + Phusion Passenger 4.0.41 509 | X-Tracker-Client-Pinger-Interval: 510 | - '12' 511 | Content-Encoding: 512 | - gzip 513 | body: 514 | encoding: ASCII-8BIT 515 | string: !binary |- 516 | H4sIAAAAAAAAAwIAAAD//0xQu27DMAzc/RWC9kaOk6IeaKZLuyZD+wGKRSQs 517 | 9ILsJnW/vopdw954vDvegXD4cVbcKHUcfCO3m1IK8m0w7C+N/Px4f6rlAQtw 518 | 5M5ZdOXYiX6I1Eidkh4kFkKsyAfMCzb4vC+rel+DyvO0jNkf/ASyhpxmi/Gc 519 | vo0fXi8PtGmDAzURoyfLvHaEJzIpgBrnmWDPPWvb4ekth8xgilLrLEjBEh7v 520 | nhKocf7vk8IXtf1y0OB2V71UdbmUngv8cnDarhuAiosf1PKf/KwV6rD4AwAA 521 | //8DAHDPkbBjAQAA 522 | http_version: 523 | recorded_at: Thu, 28 May 2015 09:47:43 GMT 524 | - request: 525 | method: get 526 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280 527 | body: 528 | encoding: US-ASCII 529 | string: '' 530 | headers: 531 | Accept: 532 | - "*/*; q=0.5, application/xml" 533 | Accept-Encoding: 534 | - gzip, deflate 535 | X-Trackertoken: 536 | - b545bcaeafc773ced5b1a853fffd1fc9 537 | Content-Type: 538 | - application/xml 539 | User-Agent: 540 | - Ruby 541 | response: 542 | status: 543 | code: 200 544 | message: OK 545 | headers: 546 | Content-Type: 547 | - application/xml; charset=utf-8 548 | Transfer-Encoding: 549 | - chunked 550 | Status: 551 | - 200 OK 552 | X-Ua-Compatible: 553 | - IE=Edge,chrome=1 554 | Cache-Control: 555 | - max-age=0, private, must-revalidate 556 | Set-Cookie: 557 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTBlNmZkOGE5Y2M3ZTNjNjM2M2M2N2YxMDA1MDI0NWQyBjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgLWmvL4KOg1uYW5vX251bWkByToNbmFub19kZW5pBjoNc3VibWljcm8iByAQOgtvZmZzZXRpADoJem9uZUkiCFVUQwY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjE5UFllWHNTMk0zb2hvSVo2QlplSGl0bXdQL3VnZWlFc1BYQ1QwdnZOQllnPQY7AEY%3D--476fdc692bf5ef3742e33bfb6487e7212a93a8f2; 558 | path=/; HttpOnly 559 | X-Request-Id: 560 | - af0e96de8ce8ac0761b2d5087dc099fc 561 | X-Runtime: 562 | - '0.043550' 563 | Date: 564 | - Thu, 28 May 2015 09:47:43 GMT 565 | X-Rack-Cache: 566 | - miss 567 | X-Powered-By: 568 | - Phusion Passenger 4.0.41 569 | Server: 570 | - nginx/1.6.0 + Phusion Passenger 4.0.41 571 | X-Tracker-Client-Pinger-Interval: 572 | - '12' 573 | Content-Encoding: 574 | - gzip 575 | body: 576 | encoding: ASCII-8BIT 577 | string: !binary |- 578 | H4sIAAAAAAAAAwIAAAD//4RUS2/bMAy+91cYPmeV4zRoVrjqtmK7DethPQu0 579 | zdhaZMmQ5GTprx+l2Hm06wYEiPjx45tm8fC7U8kWrZNG36fz6yxNUFemlrq5 580 | T59/fvuwSh/4VdFb8wsrz6+SpJA1ny/y23yVFYzeAdLQIX+RpgNVsChEokcL 581 | nhwLhbrxbeL3Pd6nUnts0KZ8TvavKNFuh7gRzoP1ooY9/240/RXsFRyYvSFn 582 | wlWgkGez+SyfLQp2DgYSVJUZtOdfLGwxeYy1FWxCA2MtrfPilMshtpcdjinX 583 | 4DGIKc+z+ZJlS5bfJHl+l2X0S55/PhbsfR+xpmqwFinXUxA9dCXav/TkPWr0 584 | gxpKhcKD27jRtjRGIeiUeztgwc4Z0WSLylTS76lPLdJoPtO4ocHErJNFcszH 585 | Few18TzvScfnNPUpwyMYmFJLL0GJC+YbMDAPlQuzFrXReGqJE94I15odn+e0 586 | RrE//2IFXwpKVI5riaVEt4GZA72ZOZKb2YvsoGAj40CmMUPl5Ta0A/z7071N 587 | so93y8XdYpyugkvL2FhQyuzIjYeq7Wi4bpzAWzyw+6FUsuJrUI6mNEpBMTgU 588 | rfe9m3QnIKjLoXECdC2q1likp0WBjrYRfFiFyeh/tOCqMl0nvehMfTQ7hwKl 589 | w9B118p+Wi+wFvZpKPhCfQAIohOwvMny1c1qOgeR2ZMXoycWAdiBVLwv7VDr 590 | /acmSNcUnRY2KqL/aBnvxxPW1pxuyaga18nxp68U7LBw7pgJu4xZWPou+I+d 591 | Rluw+D7UwE41xjGeydEXbTIdqMPletuEEPekptPIjrfxDwAAAP//AwDXq2nA 592 | TQUAAA== 593 | http_version: 594 | recorded_at: Thu, 28 May 2015 09:47:44 GMT 595 | - request: 596 | method: get 597 | uri: https://www.pivotaltracker.com/services/v3/projects/1327280/memberships 598 | body: 599 | encoding: US-ASCII 600 | string: '' 601 | headers: 602 | Accept: 603 | - "*/*; q=0.5, application/xml" 604 | Accept-Encoding: 605 | - gzip, deflate 606 | X-Trackertoken: 607 | - b545bcaeafc773ced5b1a853fffd1fc9 608 | Content-Type: 609 | - application/xml 610 | User-Agent: 611 | - Ruby 612 | response: 613 | status: 614 | code: 200 615 | message: OK 616 | headers: 617 | Content-Type: 618 | - application/xml; charset=utf-8 619 | Transfer-Encoding: 620 | - chunked 621 | Status: 622 | - 200 OK 623 | X-Ua-Compatible: 624 | - IE=Edge,chrome=1 625 | Cache-Control: 626 | - max-age=0, private, must-revalidate 627 | Set-Cookie: 628 | - t_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTc1NjAzZmRkYzZjYzczOGIyOWE3NmJiODZlYzMzY2Y4BjsAVEkiD2V4cGlyZXNfYXQGOwBGSXU6CVRpbWUNidQcgOvzyr4KOg1uYW5vX251bWkCSAE6DW5hbm9fZGVuaQY6DXN1Ym1pY3JvIgcygDoLb2Zmc2V0aQA6CXpvbmVJIghVVEMGOwBUSSIQX2NzcmZfdG9rZW4GOwBGSSIxbDM3aWJqVGdsUHdYYUVDWlVqK1hacGpyeGpwSWEwQkZiUHdYOVdDdC9Raz0GOwBG--3f8c4073c3a9f3a2f67951f9e6ba8163c9935285; 629 | path=/; HttpOnly 630 | X-Request-Id: 631 | - c9b1e9efbbd2d958e3770f6afd3f550f 632 | X-Runtime: 633 | - '0.049405' 634 | Date: 635 | - Thu, 28 May 2015 09:47:44 GMT 636 | X-Rack-Cache: 637 | - miss 638 | X-Powered-By: 639 | - Phusion Passenger 4.0.41 640 | Server: 641 | - nginx/1.6.0 + Phusion Passenger 4.0.41 642 | X-Tracker-Client-Pinger-Interval: 643 | - '12' 644 | Content-Encoding: 645 | - gzip 646 | body: 647 | encoding: ASCII-8BIT 648 | string: !binary |- 649 | H4sIAAAAAAAAA0xQu27DMAzc/RWC9kaOk6IeaKZLuyZD+wGKRSQs9ILsJnW/ 650 | vopdw954vDvegXD4cVbcKHUcfCO3m1IK8m0w7C+N/Px4f6rlAQtw5M5ZdOXY 651 | iX6I1Eidkh4kFkKsyAfMCzb4vC+rel+DyvO0jNkf/ASyhpxmi/Gcvo0fXi8P 652 | tGmDAzURoyfLvHaEJzIpgBrnmWDPPWvb4ekth8xgilLrLEjBEh7vnhKocf7v 653 | k8IXtf1y0OB2V71UdbmUngv8cnDarhuAiosf1PKf/KwV6rD4AwAA//8DAHDP 654 | kbBjAQAA 655 | http_version: 656 | recorded_at: Thu, 28 May 2015 09:47:45 GMT 657 | recorded_with: VCR 2.9.3 658 | --------------------------------------------------------------------------------