mavenlink 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +3 -0
  6. data/README.md +241 -0
  7. data/Rakefile +28 -0
  8. data/bin/mavenlink-console +18 -0
  9. data/doc/ml_logo_lb-primary.png +0 -0
  10. data/lib/config/specification.yml +1665 -0
  11. data/lib/mavenlink.rb +134 -0
  12. data/lib/mavenlink/account_invitation.rb +4 -0
  13. data/lib/mavenlink/account_membership.rb +4 -0
  14. data/lib/mavenlink/additional_item.rb +4 -0
  15. data/lib/mavenlink/assignment.rb +5 -0
  16. data/lib/mavenlink/attachment.rb +11 -0
  17. data/lib/mavenlink/backup_approver_association.rb +4 -0
  18. data/lib/mavenlink/client.rb +116 -0
  19. data/lib/mavenlink/concerns/custom_fieldable.rb +9 -0
  20. data/lib/mavenlink/concerns/indestructible.rb +11 -0
  21. data/lib/mavenlink/concerns/locked_record.rb +30 -0
  22. data/lib/mavenlink/cost_rate.rb +4 -0
  23. data/lib/mavenlink/custom_field.rb +4 -0
  24. data/lib/mavenlink/custom_field_choice.rb +5 -0
  25. data/lib/mavenlink/custom_field_value.rb +4 -0
  26. data/lib/mavenlink/errors.rb +47 -0
  27. data/lib/mavenlink/expense.rb +4 -0
  28. data/lib/mavenlink/expense_category.rb +4 -0
  29. data/lib/mavenlink/expense_report_submission.rb +7 -0
  30. data/lib/mavenlink/external_payment.rb +4 -0
  31. data/lib/mavenlink/external_reference.rb +24 -0
  32. data/lib/mavenlink/fixed_fee_item.rb +4 -0
  33. data/lib/mavenlink/holiday.rb +4 -0
  34. data/lib/mavenlink/holiday_calendar.rb +4 -0
  35. data/lib/mavenlink/holiday_calendar_association.rb +4 -0
  36. data/lib/mavenlink/holiday_calendar_membership.rb +4 -0
  37. data/lib/mavenlink/invoice.rb +4 -0
  38. data/lib/mavenlink/logger.rb +62 -0
  39. data/lib/mavenlink/model.rb +279 -0
  40. data/lib/mavenlink/organization.rb +4 -0
  41. data/lib/mavenlink/organization_membership.rb +4 -0
  42. data/lib/mavenlink/participation.rb +4 -0
  43. data/lib/mavenlink/post.rb +4 -0
  44. data/lib/mavenlink/project_template.rb +4 -0
  45. data/lib/mavenlink/project_template_assignment.rb +4 -0
  46. data/lib/mavenlink/railtie.rb +7 -0
  47. data/lib/mavenlink/rate_card.rb +4 -0
  48. data/lib/mavenlink/rate_card_role.rb +4 -0
  49. data/lib/mavenlink/rate_card_set.rb +4 -0
  50. data/lib/mavenlink/rate_card_set_version.rb +30 -0
  51. data/lib/mavenlink/rate_card_version.rb +4 -0
  52. data/lib/mavenlink/request.rb +241 -0
  53. data/lib/mavenlink/resolution.rb +4 -0
  54. data/lib/mavenlink/response.rb +22 -0
  55. data/lib/mavenlink/role.rb +5 -0
  56. data/lib/mavenlink/settings.rb +11 -0
  57. data/lib/mavenlink/skill.rb +4 -0
  58. data/lib/mavenlink/skill_category.rb +4 -0
  59. data/lib/mavenlink/skill_membership.rb +4 -0
  60. data/lib/mavenlink/specificators/association.rb +13 -0
  61. data/lib/mavenlink/specificators/attribute.rb +13 -0
  62. data/lib/mavenlink/specificators/base.rb +24 -0
  63. data/lib/mavenlink/specificators/validation.rb +27 -0
  64. data/lib/mavenlink/status_report.rb +4 -0
  65. data/lib/mavenlink/story.rb +8 -0
  66. data/lib/mavenlink/story_allocation_day.rb +5 -0
  67. data/lib/mavenlink/story_dependency.rb +5 -0
  68. data/lib/mavenlink/story_task.rb +5 -0
  69. data/lib/mavenlink/tag.rb +5 -0
  70. data/lib/mavenlink/time_adjustment.rb +4 -0
  71. data/lib/mavenlink/time_entry.rb +4 -0
  72. data/lib/mavenlink/time_off_entry.rb +4 -0
  73. data/lib/mavenlink/timesheet_submission.rb +4 -0
  74. data/lib/mavenlink/user.rb +5 -0
  75. data/lib/mavenlink/vendor.rb +4 -0
  76. data/lib/mavenlink/workspace.rb +21 -0
  77. data/lib/mavenlink/workspace_group.rb +5 -0
  78. data/lib/mavenlink/workspace_invoice_preference.rb +4 -0
  79. data/lib/mavenlink/workweek.rb +4 -0
  80. data/lib/mavenlink/workweek_membership.rb +4 -0
  81. data/mavenlink.gemspec +29 -0
  82. data/spec/lib/mavenlink/account_membership_spec.rb +8 -0
  83. data/spec/lib/mavenlink/assignment_spec.rb +17 -0
  84. data/spec/lib/mavenlink/attachment_spec.rb +30 -0
  85. data/spec/lib/mavenlink/backup_approver_association_spec.rb +9 -0
  86. data/spec/lib/mavenlink/client_spec.rb +187 -0
  87. data/spec/lib/mavenlink/concerns/indestructible_spec.rb +13 -0
  88. data/spec/lib/mavenlink/concerns/locked_record_spec.rb +28 -0
  89. data/spec/lib/mavenlink/cost_rate_spec.rb +9 -0
  90. data/spec/lib/mavenlink/custom_field_value_spec.rb +10 -0
  91. data/spec/lib/mavenlink/expense_report_submission_spec.rb +16 -0
  92. data/spec/lib/mavenlink/expense_spec.rb +23 -0
  93. data/spec/lib/mavenlink/external_references_spec.rb +144 -0
  94. data/spec/lib/mavenlink/holiday_calendar_association_spec.rb +8 -0
  95. data/spec/lib/mavenlink/holiday_calendar_membership_spec.rb +8 -0
  96. data/spec/lib/mavenlink/holiday_spec.rb +7 -0
  97. data/spec/lib/mavenlink/invalid_request_error_spec.rb +9 -0
  98. data/spec/lib/mavenlink/invoice_spec.rb +176 -0
  99. data/spec/lib/mavenlink/model_spec.rb +439 -0
  100. data/spec/lib/mavenlink/post_spec.rb +23 -0
  101. data/spec/lib/mavenlink/rate_card_set_version_spec.rb +119 -0
  102. data/spec/lib/mavenlink/record_invalid_error_spec.rb +16 -0
  103. data/spec/lib/mavenlink/record_not_found_error_spec.rb +9 -0
  104. data/spec/lib/mavenlink/request_spec.rb +381 -0
  105. data/spec/lib/mavenlink/response_spec.rb +50 -0
  106. data/spec/lib/mavenlink/role_spec.rb +9 -0
  107. data/spec/lib/mavenlink/settings_spec.rb +23 -0
  108. data/spec/lib/mavenlink/skill_category_spec.rb +7 -0
  109. data/spec/lib/mavenlink/skill_membership_spec.rb +9 -0
  110. data/spec/lib/mavenlink/skill_spec.rb +8 -0
  111. data/spec/lib/mavenlink/specificators/association_spec.rb +25 -0
  112. data/spec/lib/mavenlink/specificators/attribute_spec.rb +25 -0
  113. data/spec/lib/mavenlink/specificators/validation_spec.rb +39 -0
  114. data/spec/lib/mavenlink/story_allocation_day_spec.rb +64 -0
  115. data/spec/lib/mavenlink/story_dependency_spec.rb +16 -0
  116. data/spec/lib/mavenlink/story_spec.rb +69 -0
  117. data/spec/lib/mavenlink/time_adjustment_spec.rb +13 -0
  118. data/spec/lib/mavenlink/time_entry_spec.rb +43 -0
  119. data/spec/lib/mavenlink/time_off_entry_spec.rb +9 -0
  120. data/spec/lib/mavenlink/user_spec.rb +138 -0
  121. data/spec/lib/mavenlink/workspace_group_spec.rb +25 -0
  122. data/spec/lib/mavenlink/workspace_spec.rb +431 -0
  123. data/spec/lib/mavenlink_spec.rb +43 -0
  124. data/spec/spec_helper.rb +31 -0
  125. data/spec/support/shared_examples.rb +148 -0
  126. metadata +267 -0
data/lib/mavenlink.rb ADDED
@@ -0,0 +1,134 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+ require 'active_support/core_ext/hash/slice'
3
+ require 'active_support/core_ext/object/try'
4
+ require 'active_support/core_ext/string/inflections'
5
+ require 'active_model'
6
+ require 'yaml'
7
+ require 'json'
8
+ require 'brainstem-adaptor'
9
+ require 'faraday'
10
+
11
+ module Mavenlink
12
+ VERSION = '0.0.1'
13
+
14
+ # Returns HTTP framework
15
+ def self.adapter
16
+ @adapter || Faraday.default_adapter
17
+ end
18
+
19
+ # @param value Any valid Faraday adapter
20
+ def self.adapter=(value)
21
+ @adapter = value
22
+ end
23
+
24
+ def self.client
25
+ @client ||= Mavenlink::Client.new
26
+ end
27
+
28
+ # @return [Mavenlink::Settings]
29
+ def self.default_settings
30
+ Mavenlink::Settings[:default]
31
+ end
32
+
33
+ def self.logger
34
+ @logger ||= Mavenlink::Logger.new(nil)
35
+ end
36
+
37
+ def self.logger=(value)
38
+ @logger = value
39
+ end
40
+
41
+ # @param token [String]
42
+ def self.oauth_token=(token)
43
+ default_settings[:oauth_token] = token
44
+ end
45
+
46
+ # Forces models to perform validations if true
47
+ # If false performs requests without running validations described in specification file
48
+ # Default behavior is not to validate anything
49
+ # @param enabled [true, false]
50
+ def self.perform_validations=(enabled)
51
+ default_settings[:perform_validations] = enabled
52
+ end
53
+
54
+ # @param [String] version
55
+ # @return [Api]
56
+ def self.specification
57
+ @specification ||= BrainstemAdaptor::Specification.new(YAML.load_file(File.join(File.dirname(__FILE__), 'config', 'specification.yml')))
58
+ end
59
+
60
+ def self.stub_requests(&block)
61
+ stubbed_requests = Faraday::Adapter::Test::Stubs.new(&block)
62
+ self.adapter = [:test, stubbed_requests]
63
+ end
64
+ end
65
+
66
+ require 'mavenlink/settings'
67
+ require 'mavenlink/errors'
68
+ require 'mavenlink/request'
69
+ require 'mavenlink/response'
70
+ require 'mavenlink/client'
71
+ require 'mavenlink/logger'
72
+ require 'mavenlink/concerns/custom_fieldable'
73
+ require 'mavenlink/concerns/indestructible'
74
+ require 'mavenlink/concerns/locked_record'
75
+ require 'mavenlink/specificators/base'
76
+ require 'mavenlink/specificators/attribute'
77
+ require 'mavenlink/specificators/association'
78
+ require 'mavenlink/specificators/validation'
79
+ require 'mavenlink/model'
80
+ require 'mavenlink/account_invitation'
81
+ require 'mavenlink/account_membership'
82
+ require 'mavenlink/additional_item'
83
+ require 'mavenlink/assignment'
84
+ require 'mavenlink/attachment'
85
+ require 'mavenlink/backup_approver_association'
86
+ require 'mavenlink/cost_rate'
87
+ require 'mavenlink/custom_field'
88
+ require 'mavenlink/custom_field_value'
89
+ require 'mavenlink/custom_field_choice'
90
+ require 'mavenlink/expense'
91
+ require 'mavenlink/expense_category'
92
+ require 'mavenlink/expense_report_submission'
93
+ require 'mavenlink/external_payment'
94
+ require 'mavenlink/fixed_fee_item'
95
+ require 'mavenlink/external_reference'
96
+ require 'mavenlink/holiday'
97
+ require 'mavenlink/holiday_calendar'
98
+ require 'mavenlink/holiday_calendar_association'
99
+ require 'mavenlink/holiday_calendar_membership'
100
+ require 'mavenlink/invoice'
101
+ require 'mavenlink/organization'
102
+ require 'mavenlink/organization_membership'
103
+ require 'mavenlink/participation'
104
+ require 'mavenlink/post'
105
+ require 'mavenlink/project_template'
106
+ require 'mavenlink/project_template_assignment'
107
+ require 'mavenlink/skill'
108
+ require 'mavenlink/skill_category'
109
+ require 'mavenlink/skill_membership'
110
+ require 'mavenlink/status_report'
111
+ require 'mavenlink/story'
112
+ require 'mavenlink/story_allocation_day'
113
+ require 'mavenlink/story_dependency'
114
+ require 'mavenlink/story_task'
115
+ require 'mavenlink/tag'
116
+ require 'mavenlink/time_adjustment'
117
+ require 'mavenlink/time_entry'
118
+ require 'mavenlink/time_off_entry'
119
+ require 'mavenlink/timesheet_submission'
120
+ require 'mavenlink/rate_card'
121
+ require 'mavenlink/rate_card_role'
122
+ require 'mavenlink/rate_card_set'
123
+ require 'mavenlink/rate_card_set_version'
124
+ require 'mavenlink/rate_card_version'
125
+ require 'mavenlink/resolution'
126
+ require 'mavenlink/role' # NOTE(SZ): remove
127
+ require 'mavenlink/user'
128
+ require 'mavenlink/vendor'
129
+ require 'mavenlink/workspace'
130
+ require 'mavenlink/workspace_invoice_preference'
131
+ require 'mavenlink/workspace_group'
132
+ require 'mavenlink/workweek'
133
+ require 'mavenlink/workweek_membership'
134
+ require 'mavenlink/railtie' if defined?(Rails)
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class AccountInvitation < Model
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class AccountMembership < Model
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class AdditionalItem < Model
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module Mavenlink
2
+ class Assignment < Model
3
+ include Concerns::Indestructible
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ module Mavenlink
2
+ class Attachment < Model
3
+ include Concerns::Indestructible
4
+
5
+ protected
6
+
7
+ def update
8
+ raise RecordLockedError, 'The model is locked and cannot be changed'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class BackupApproverAssociation < Model
3
+ end
4
+ end
@@ -0,0 +1,116 @@
1
+ module Mavenlink
2
+ class Client
3
+ ENDPOINT = 'https://api.mavenlink.com/api/v1/'.freeze
4
+
5
+ # @param settings [ActiveSuppport::HashWithIndifferentAccess]
6
+ def initialize(settings = Mavenlink.default_settings)
7
+ @settings = settings
8
+ @oauth_token = settings[:oauth_token] or raise ArgumentError, 'OAuth token is not set'
9
+ @endpoint = settings[:endpoint] || ENDPOINT
10
+ @use_json = settings[:use_json]
11
+ if settings.key?(:user_agent_override)
12
+ @user_agent_override = settings[:user_agent_override]
13
+ end
14
+
15
+ # TODO: implement with method_missing?
16
+ # Declare API calls client.-->>workspaces<<---.create({})
17
+ Mavenlink.specification.keys.each do |collection_name|
18
+ singleton_class.instance_eval do
19
+ define_method collection_name do
20
+ ::Mavenlink::Request.new(collection_name, self)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ # @return [Faraday::Connection]
27
+ def connection
28
+ Faraday.new(connection_options) do |builder|
29
+ if @use_json
30
+ builder.headers['Content-Type'] = 'application/json'
31
+ else
32
+ builder.use Faraday::Request::UrlEncoded
33
+ end
34
+ builder.adapter(*Mavenlink.adapter)
35
+ end
36
+ end
37
+
38
+ # Performs custom GET request
39
+ # @param [String] path
40
+ # @param [Hash] arguments
41
+ def get(path, arguments = {})
42
+ Mavenlink.logger.note "Started GET /#{path} with #{arguments.inspect}"
43
+ parse_request(connection.get(path, arguments).body)
44
+ end
45
+
46
+ # Performs custom POST request
47
+ # @param [String] path
48
+ # @param [Hash] arguments
49
+ def post(path, arguments = {})
50
+ Mavenlink.logger.note "Started POST /#{path} with #{arguments.inspect}"
51
+ parse_request(connection.post(path, arguments).body)
52
+ end
53
+
54
+ # Performs custom PUT request
55
+ # @param [String] path
56
+ # @param [Hash] arguments
57
+ def put(path, arguments = {})
58
+ Mavenlink.logger.note "Started PUT /#{path} with #{arguments.inspect}"
59
+ parse_request(connection.put(path, arguments).body)
60
+ end
61
+
62
+ # Performs custom PUT request
63
+ # @param [String] path
64
+ # @param [Hash] arguments
65
+ def delete(path, arguments = {})
66
+ Mavenlink.logger.note "Started DELETE /#{path} with #{arguments.inspect}"
67
+ parse_request(connection.delete(path, arguments).body)
68
+ end
69
+
70
+ private
71
+
72
+ attr_reader :oauth_token, :endpoint
73
+
74
+ # @return [Hash]
75
+ def connection_options
76
+ if @user_agent_override && @user_agent_override.length > 1
77
+ user_agent = "#{@user_agent_override}"
78
+ else
79
+ user_agent = "Mavenlink Ruby Gem"
80
+ end
81
+ {
82
+ headers: { 'Accept' => "application/json",
83
+ 'User-Agent' => "#{user_agent}",
84
+ 'Authorization' => "Bearer #{oauth_token}" },
85
+ ssl: { verify: false },
86
+ url: endpoint
87
+ }.freeze
88
+ end
89
+
90
+ def parse_request(response)
91
+ if response.present?
92
+ parsed_response = JSON.parse(response)
93
+ else
94
+ return
95
+ end
96
+
97
+ parsed_response.tap do
98
+ Mavenlink.logger.whisper 'Received response:'
99
+ Mavenlink.logger.inspection response
100
+
101
+ case parsed_response
102
+ when Array
103
+ Mavenlink.logger.whisper 'Returned as a plain collection'
104
+ when Hash
105
+ if parsed_response['errors']
106
+ Mavenlink.logger.disappointment 'REQUEST FAILED:'
107
+ Mavenlink.logger.inspection parsed_response['errors']
108
+ raise InvalidRequestError.new(parsed_response)
109
+ end
110
+ end
111
+ end
112
+ rescue JSON::ParserError => e
113
+ raise Mavenlink::InvalidResponseError.new(e.message)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,9 @@
1
+ module Mavenlink
2
+ module Concerns
3
+ module CustomFieldable
4
+ def custom_field_values
5
+ client.custom_field_values.filter(subject_type: self.collection_name.singularize, with_subject_id: self.id)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Mavenlink
2
+ module Concerns
3
+ module Indestructible
4
+
5
+ # @overload
6
+ def destroy
7
+ raise RecordLockedError, 'The model is locked and cannot be deleted'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ module Mavenlink
2
+ module Concerns
3
+ module LockedRecord
4
+ include Indestructible
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class << self
9
+ protected :new
10
+ protected :create
11
+ end
12
+ end
13
+
14
+ # @overload
15
+ def save
16
+ raise RecordLockedError, 'The model is locked and cannot be changed'
17
+ end
18
+
19
+ # @overload
20
+ def new_record?
21
+ false
22
+ end
23
+
24
+ # @overload
25
+ def persisted?
26
+ true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class CostRate < Model
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class CustomField < Model
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module Mavenlink
2
+ class CustomFieldChoice < Model
3
+ include Concerns::LockedRecord
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class CustomFieldValue < Model
3
+ end
4
+ end
@@ -0,0 +1,47 @@
1
+ module Mavenlink
2
+
3
+ # The most generic MavenlinkAPI error
4
+ # Eg. service unavailable etc...
5
+ class Error < StandardError
6
+ end
7
+
8
+ # Happens by internal reasons on server side
9
+ # Eg. client cannot parse response format
10
+ class InvalidResponseError < Error
11
+ end
12
+
13
+ # Another generic API error idenfitying that request is invalid.
14
+ # Eg. invalid parameters passsed in request.
15
+ class InvalidRequestError < Error
16
+ attr_reader :request
17
+
18
+ # @param request [Mavenlink::Request]
19
+ # @param message [String]
20
+ def initialize(request, message = request.inspect)
21
+ @request = request
22
+ super(message)
23
+ end
24
+ end
25
+
26
+ # Identified that a record cannot be changed.
27
+ # Eg. cannot be saved, removed etc.
28
+ class RecordLockedError < Error
29
+ end
30
+
31
+ # Raised when user is attmping to find unexisting record.
32
+ # Eg. user updates record while somebody else already removed this record.
33
+ class RecordNotFoundError < InvalidRequestError
34
+ end
35
+
36
+ # Raised when user is trying to save! record specifying invalid attributes.
37
+ # Eg. create! workspace without title.
38
+ class RecordInvalidError < Error
39
+ attr_reader :record # :nodoc:
40
+
41
+ # @param record [Mavenlink::Model]
42
+ def initialize(record)
43
+ @record = record
44
+ super(@record.errors.full_messages.join(", "))
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class Expense < Model
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mavenlink
2
+ class ExpenseCategory < Model
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module Mavenlink
2
+ class ExpenseReportSubmission < Model
3
+ def reject_submission
4
+ client.put("expense_report_submissions/#{self.id}/reject")
5
+ end
6
+ end
7
+ end