moco-ruby 0.1.2 → 1.0.0.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -3
- data/CHANGELOG.md +65 -4
- data/Gemfile +2 -1
- data/Gemfile.lock +70 -23
- data/README.md +199 -55
- data/examples/v2_api_example.rb +73 -0
- data/lib/moco/client.rb +47 -0
- data/lib/moco/collection_proxy.rb +190 -0
- data/lib/moco/connection.rb +62 -0
- data/lib/moco/entities/activity.rb +96 -0
- data/lib/moco/entities/base_entity.rb +303 -0
- data/lib/moco/entities/company.rb +28 -0
- data/lib/moco/entities/deal.rb +24 -0
- data/lib/moco/entities/expense.rb +29 -0
- data/lib/moco/entities/holiday.rb +25 -0
- data/lib/moco/entities/invoice.rb +53 -0
- data/lib/moco/entities/planning_entry.rb +26 -0
- data/lib/moco/entities/presence.rb +30 -0
- data/lib/moco/entities/project.rb +39 -0
- data/lib/moco/entities/schedule.rb +26 -0
- data/lib/moco/entities/task.rb +20 -0
- data/lib/moco/entities/user.rb +33 -0
- data/lib/moco/entities/web_hook.rb +27 -0
- data/lib/moco/entities.rb +11 -4
- data/lib/moco/entity_collection.rb +59 -0
- data/lib/moco/helpers.rb +1 -0
- data/lib/moco/nested_collection_proxy.rb +40 -0
- data/lib/moco/sync.rb +70 -29
- data/lib/moco/version.rb +1 -1
- data/lib/moco.rb +26 -2
- data/mocurl.rb +51 -34
- data/sync_activity.rb +4 -4
- metadata +43 -10
- data/lib/moco/api.rb +0 -194
data/lib/moco/version.rb
CHANGED
data/lib/moco.rb
CHANGED
@@ -1,8 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/core_ext"
|
5
|
+
require "active_support/inflector"
|
6
|
+
|
3
7
|
require_relative "moco/version"
|
4
|
-
|
5
|
-
|
8
|
+
|
9
|
+
# New API (v2)
|
10
|
+
require_relative "moco/entities/base_entity"
|
11
|
+
require_relative "moco/entities/project"
|
12
|
+
require_relative "moco/entities/activity"
|
13
|
+
require_relative "moco/entities/user"
|
14
|
+
require_relative "moco/entities/company"
|
15
|
+
require_relative "moco/entities/task"
|
16
|
+
require_relative "moco/entities/invoice"
|
17
|
+
require_relative "moco/entities/deal"
|
18
|
+
require_relative "moco/entities/expense"
|
19
|
+
require_relative "moco/entities/web_hook"
|
20
|
+
require_relative "moco/entities/schedule"
|
21
|
+
require_relative "moco/entities/presence"
|
22
|
+
require_relative "moco/entities/holiday"
|
23
|
+
require_relative "moco/entities/planning_entry"
|
24
|
+
require_relative "moco/client"
|
25
|
+
require_relative "moco/connection"
|
26
|
+
require_relative "moco/collection_proxy"
|
27
|
+
require_relative "moco/nested_collection_proxy"
|
28
|
+
require_relative "moco/entity_collection"
|
29
|
+
|
6
30
|
require_relative "moco/sync"
|
7
31
|
|
8
32
|
module MOCO
|
data/mocurl.rb
CHANGED
@@ -58,42 +58,59 @@ end
|
|
58
58
|
|
59
59
|
# Load default API key from config
|
60
60
|
config = YAML.load_file("config.yml")
|
61
|
-
options[:api_key] ||= config["instances"].
|
61
|
+
options[:api_key] ||= config["instances"].dig(subdomain, "api_key")
|
62
62
|
|
63
63
|
warn "Error: No API key found for `#{subdomain}' and none given, continuing without" if options[:api_key].nil?
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
65
|
+
client = MOCO::Client.new(subdomain: subdomain, api_key: options[:api_key])
|
66
|
+
|
67
|
+
# Extract path from URL
|
68
|
+
path = url.gsub(%r{https?://#{subdomain}\.mocoapp\.com/api/v1/}, "")
|
69
|
+
|
70
|
+
begin
|
71
|
+
# Make request using the client's connection directly
|
72
|
+
result = case options[:method]
|
73
|
+
when "GET"
|
74
|
+
client.connection.get(path)
|
75
|
+
when "DELETE"
|
76
|
+
client.connection.delete(path)
|
77
|
+
when "POST"
|
78
|
+
client.connection.post(path, options[:data])
|
79
|
+
when "PUT"
|
80
|
+
client.connection.put(path, options[:data])
|
81
|
+
when "PATCH"
|
82
|
+
client.connection.patch(path, options[:data])
|
83
|
+
else
|
84
|
+
puts "Error: Invalid HTTP Method: #{options[:method]}"
|
85
|
+
exit 1
|
86
|
+
end
|
87
|
+
|
88
|
+
if options[:verbose]
|
89
|
+
puts "> #{options[:method]} #{url}"
|
90
|
+
# Print request details if available
|
91
|
+
if result.env&.request_headers
|
92
|
+
puts(result.env.request_headers.map do |k, v|
|
93
|
+
"> #{k}: #{k == "Authorization" ? "#{v[0...16]}<REDACTED>#{v[-4..]}" : v}"
|
94
|
+
end)
|
95
|
+
puts ">"
|
96
|
+
puts result.env.request_body.split.map { |l| "> #{l}" }.join if result.env.request_body
|
97
|
+
puts "---"
|
98
|
+
puts "< #{result.status} #{result.reason_phrase}"
|
99
|
+
puts(result.headers.map { |k, v| "< #{k}: #{v}" })
|
100
|
+
else
|
101
|
+
puts "> Request details not available in this response format"
|
102
|
+
end
|
103
|
+
puts ""
|
104
|
+
end
|
82
105
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
puts "
|
92
|
-
|
93
|
-
puts ""
|
94
|
-
end
|
95
|
-
if options[:no_format]
|
96
|
-
puts result.body.to_json
|
97
|
-
else
|
98
|
-
puts JSON.pretty_generate(result.body)
|
106
|
+
# Format the response
|
107
|
+
response_data = result.body
|
108
|
+
if options[:no_format]
|
109
|
+
puts response_data.to_json
|
110
|
+
else
|
111
|
+
puts JSON.pretty_generate(response_data)
|
112
|
+
end
|
113
|
+
rescue StandardError => e
|
114
|
+
puts "Error: #{e.message}"
|
115
|
+
exit 1
|
99
116
|
end
|
data/sync_activity.rb
CHANGED
@@ -64,12 +64,12 @@ config = YAML.load_file("config.yml")
|
|
64
64
|
source_config = config["instances"].fetch(source_instance, nil)
|
65
65
|
target_config = config["instances"].fetch(target_instance, nil)
|
66
66
|
|
67
|
-
|
68
|
-
|
67
|
+
source_client = MOCO::Client.new(subdomain: source_instance, api_key: source_config["api_key"])
|
68
|
+
target_client = MOCO::Client.new(subdomain: target_instance, api_key: target_config["api_key"])
|
69
69
|
|
70
70
|
syncer = MOCO::Sync.new(
|
71
|
-
|
72
|
-
|
71
|
+
source_client,
|
72
|
+
target_client,
|
73
73
|
project_match_threshold: options[:match_project_threshold],
|
74
74
|
task_match_threshold: options[:match_task_threshold],
|
75
75
|
filters: {
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moco-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.alpha
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Teal Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: faraday
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,21 +69,40 @@ files:
|
|
55
69
|
- README.md
|
56
70
|
- Rakefile
|
57
71
|
- config.yml.sample
|
72
|
+
- examples/v2_api_example.rb
|
58
73
|
- lib/moco.rb
|
59
|
-
- lib/moco/
|
74
|
+
- lib/moco/client.rb
|
75
|
+
- lib/moco/collection_proxy.rb
|
76
|
+
- lib/moco/connection.rb
|
60
77
|
- lib/moco/entities.rb
|
78
|
+
- lib/moco/entities/activity.rb
|
79
|
+
- lib/moco/entities/base_entity.rb
|
80
|
+
- lib/moco/entities/company.rb
|
81
|
+
- lib/moco/entities/deal.rb
|
82
|
+
- lib/moco/entities/expense.rb
|
83
|
+
- lib/moco/entities/holiday.rb
|
84
|
+
- lib/moco/entities/invoice.rb
|
85
|
+
- lib/moco/entities/planning_entry.rb
|
86
|
+
- lib/moco/entities/presence.rb
|
87
|
+
- lib/moco/entities/project.rb
|
88
|
+
- lib/moco/entities/schedule.rb
|
89
|
+
- lib/moco/entities/task.rb
|
90
|
+
- lib/moco/entities/user.rb
|
91
|
+
- lib/moco/entities/web_hook.rb
|
92
|
+
- lib/moco/entity_collection.rb
|
61
93
|
- lib/moco/helpers.rb
|
94
|
+
- lib/moco/nested_collection_proxy.rb
|
62
95
|
- lib/moco/sync.rb
|
63
96
|
- lib/moco/version.rb
|
64
97
|
- mocurl.rb
|
65
98
|
- sync_activity.rb
|
66
|
-
homepage: https://github.com/
|
99
|
+
homepage: https://github.com/starsong-consulting/moco-ruby
|
67
100
|
licenses:
|
68
101
|
- Apache-2.0
|
69
102
|
metadata:
|
70
|
-
homepage_uri: https://github.com/
|
71
|
-
source_code_uri: https://github.com/
|
72
|
-
changelog_uri: https://github.com/
|
103
|
+
homepage_uri: https://github.com/starsong-consulting/moco-ruby
|
104
|
+
source_code_uri: https://github.com/starsong-consulting/moco-ruby
|
105
|
+
changelog_uri: https://github.com/starsong-consulting/moco-ruby/blob/main/CHANGELOG.md
|
73
106
|
rubygems_mfa_required: 'true'
|
74
107
|
post_install_message:
|
75
108
|
rdoc_options: []
|
@@ -79,12 +112,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
112
|
requirements:
|
80
113
|
- - ">="
|
81
114
|
- !ruby/object:Gem::Version
|
82
|
-
version: 2.
|
115
|
+
version: 3.2.0
|
83
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
117
|
requirements:
|
85
|
-
- - "
|
118
|
+
- - ">"
|
86
119
|
- !ruby/object:Gem::Version
|
87
|
-
version:
|
120
|
+
version: 1.3.1
|
88
121
|
requirements: []
|
89
122
|
rubygems_version: 3.4.1
|
90
123
|
signing_key:
|
data/lib/moco/api.rb
DELETED
@@ -1,194 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "faraday"
|
4
|
-
require_relative "entities"
|
5
|
-
|
6
|
-
module MOCO
|
7
|
-
# MOCO::API abstracts access to the MOCO API and its entities
|
8
|
-
class API
|
9
|
-
def initialize(subdomain, api_key)
|
10
|
-
@subdomain = subdomain
|
11
|
-
@api_key = api_key
|
12
|
-
@conn = Faraday.new do |f|
|
13
|
-
f.request :json
|
14
|
-
f.response :json
|
15
|
-
f.request :authorization, "Token", "token=#{@api_key}" if @api_key
|
16
|
-
f.url_prefix = "https://#{@subdomain}.mocoapp.com/api/v1"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
%w[get post put patch delete].each do |method|
|
21
|
-
define_method(method) do |path, *args|
|
22
|
-
@conn.send(method, path, *args)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def get_projects(**args)
|
27
|
-
response = @conn.get("projects?#{Faraday::Utils.build_query(args)}")
|
28
|
-
parse_projects_response(response.body)
|
29
|
-
end
|
30
|
-
|
31
|
-
def get_assigned_projects(**args)
|
32
|
-
response = @conn.get("projects/assigned?#{Faraday::Utils.build_query(args)}")
|
33
|
-
parse_projects_response(response.body)
|
34
|
-
end
|
35
|
-
|
36
|
-
def get_activities(filters = {})
|
37
|
-
response = @conn.get("activities?#{Faraday::Utils.build_query(filters)}")
|
38
|
-
parse_activities_response(response.body)
|
39
|
-
end
|
40
|
-
|
41
|
-
def get_activity(id)
|
42
|
-
response = @conn.get("activities/#{id}")
|
43
|
-
parse_activities_response([response.body]).first
|
44
|
-
end
|
45
|
-
|
46
|
-
def create_activities_bulk(activities)
|
47
|
-
api_entities = activities.map do |activity|
|
48
|
-
activity.to_h.except(:id, :project, :user, :customer).tap do |h|
|
49
|
-
h[:project_id] = activity.project.id
|
50
|
-
h[:task_id] = activity.task.id
|
51
|
-
end
|
52
|
-
end
|
53
|
-
@conn.post("activities/bulk", { activities: api_entities })
|
54
|
-
end
|
55
|
-
|
56
|
-
def create_activity(activity)
|
57
|
-
api_entity = activity.to_h.except(:id, :project, :user, :customer).tap do |h|
|
58
|
-
h[:project_id] = activity.project.id
|
59
|
-
h[:task_id] = activity.task.id
|
60
|
-
end
|
61
|
-
@conn.post("activities", api_entity)
|
62
|
-
end
|
63
|
-
|
64
|
-
def update_activity(activity)
|
65
|
-
api_entity = activity.to_h.except(:project, :user, :customer).tap do |h|
|
66
|
-
h[:project_id] = activity.project.id
|
67
|
-
h[:task_id] = activity.task.id
|
68
|
-
end
|
69
|
-
@conn.put("activities/#{activity.id}", api_entity)
|
70
|
-
end
|
71
|
-
|
72
|
-
def start_activity_timer(activity_id)
|
73
|
-
@conn.patch("activities/#{activity_id}/start_timer")
|
74
|
-
end
|
75
|
-
|
76
|
-
def stop_activity_timer(activity_id)
|
77
|
-
@conn.patch("activities/#{activity_id}/stop_timer")
|
78
|
-
end
|
79
|
-
|
80
|
-
def disregard_activities(reason:, activity_ids:, company_id:, project_id: nil)
|
81
|
-
payload = {
|
82
|
-
reason: reason,
|
83
|
-
activity_ids: activity_ids,
|
84
|
-
company_id: company_id
|
85
|
-
}
|
86
|
-
payload[:project_id] = project_id if project_id
|
87
|
-
@conn.post("activities/disregard", payload)
|
88
|
-
end
|
89
|
-
|
90
|
-
def delete_activity(activity_id)
|
91
|
-
@conn.delete("activities/#{activity_id}")
|
92
|
-
end
|
93
|
-
|
94
|
-
def archive_project(project_id)
|
95
|
-
@conn.put("projects/#{project_id}/archive")
|
96
|
-
end
|
97
|
-
|
98
|
-
def unarchive_project(project_id)
|
99
|
-
@conn.put("projects/#{project_id}/unarchive")
|
100
|
-
end
|
101
|
-
|
102
|
-
def get_project_report(project_id)
|
103
|
-
@conn.get("projects/#{project_id}/report")
|
104
|
-
end
|
105
|
-
|
106
|
-
def share_project_report(project_id)
|
107
|
-
@conn.put("projects/#{project_id}/share")
|
108
|
-
end
|
109
|
-
|
110
|
-
def disable_project_report_sharing(project_id)
|
111
|
-
@conn.put("projects/#{project_id}/disable_share")
|
112
|
-
end
|
113
|
-
|
114
|
-
def assign_project_to_group(project_id, project_group_id)
|
115
|
-
@conn.put("projects/#{project_id}/assign_project_group", { project_group_id: project_group_id })
|
116
|
-
end
|
117
|
-
|
118
|
-
def unassign_project_from_group(project_id)
|
119
|
-
@conn.put("projects/#{project_id}/unassign_project_group")
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
125
|
-
def parse_projects_response(data)
|
126
|
-
data.map do |project_data|
|
127
|
-
Project.new.tap do |project|
|
128
|
-
project.id = project_data["id"]
|
129
|
-
project.name = project_data["name"]
|
130
|
-
project.customer = parse_customer_reference(project_data["customer"])
|
131
|
-
project.tasks = project_data["tasks"].map do |task_data|
|
132
|
-
Task.new.tap do |task|
|
133
|
-
task.id = task_data["id"]
|
134
|
-
task.name = task_data["name"]
|
135
|
-
task.project_id = task_data["project_id"]
|
136
|
-
task.billable = task_data["billable"]
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
143
|
-
|
144
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
145
|
-
def parse_activities_response(data)
|
146
|
-
data.map do |activity_data|
|
147
|
-
Activity.new.tap do |activity|
|
148
|
-
activity.id = activity_data["id"]
|
149
|
-
activity.date = activity_data["date"]
|
150
|
-
activity.description = activity_data["description"]
|
151
|
-
activity.user = parse_user_reference(activity_data["user"])
|
152
|
-
activity.customer = parse_customer_reference(activity_data["customer"])
|
153
|
-
activity.project = parse_project_reference(activity_data["project"])
|
154
|
-
activity.task = parse_task_reference(activity_data["task"])
|
155
|
-
activity.hours = activity_data["hours"]
|
156
|
-
activity.seconds = activity_data["seconds"]
|
157
|
-
activity.billable = activity_data["billable"]
|
158
|
-
activity.billed = activity_data["billed"]
|
159
|
-
activity.tag = activity_data["tag"]
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
164
|
-
|
165
|
-
def parse_project_reference(project_data)
|
166
|
-
Project.new.tap do |project|
|
167
|
-
project.id = project_data["id"]
|
168
|
-
project.name = project_data["name"]
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def parse_task_reference(task_data)
|
173
|
-
Task.new.tap do |task|
|
174
|
-
task.id = task_data["id"]
|
175
|
-
task.name = task_data["name"]
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def parse_user_reference(user_data)
|
180
|
-
User.new.tap do |user|
|
181
|
-
user.id = user_data["id"]
|
182
|
-
user.firstname = user_data["firstname"]
|
183
|
-
user.lastname = user_data["lastname"]
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
def parse_customer_reference(customer_data)
|
188
|
-
Customer.new.tap do |customer|
|
189
|
-
customer.id = customer_data["id"]
|
190
|
-
customer.name = customer_data["name"]
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|