dovico 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.dockerignore +1 -0
- data/.gitignore +4 -0
- data/.gitlab-ci.yml +15 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Dockerfile +28 -0
- data/Gemfile +3 -0
- data/Makefile +66 -0
- data/README.md +106 -0
- data/Rakefile +11 -0
- data/bin/console +6 -0
- data/bin/dovico +4 -0
- data/doc/API.md +135 -0
- data/dovico-client.gemspec +39 -0
- data/dovico.yml.example +26 -0
- data/lib/dovico.rb +9 -0
- data/lib/dovico/api_client.rb +67 -0
- data/lib/dovico/app.rb +138 -0
- data/lib/dovico/model/assignment.rb +26 -0
- data/lib/dovico/model/employee.rb +27 -0
- data/lib/dovico/model/project.rb +18 -0
- data/lib/dovico/model/task.rb +6 -0
- data/lib/dovico/model/time_entry.rb +83 -0
- data/lib/dovico/model/time_entry_generator.rb +58 -0
- data/lib/dovico/version.rb +3 -0
- data/spec/helper.rb +24 -0
- data/spec/unit/dovico/api_client_spec.rb +81 -0
- data/spec/unit/dovico/model/assignment_spec.rb +26 -0
- data/spec/unit/dovico/model/employee_spec.rb +44 -0
- data/spec/unit/dovico/model/project_spec.rb +56 -0
- data/spec/unit/dovico/model/task_spec.rb +26 -0
- data/spec/unit/dovico/model/time_entry_generator_spec.rb +86 -0
- data/spec/unit/dovico/model/time_entry_spec.rb +168 -0
- metadata +332 -0
data/spec/helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "simplecov"
|
2
|
+
require "simplecov-rcov"
|
3
|
+
SimpleCov.formatters = [
|
4
|
+
SimpleCov::Formatter::HTMLFormatter,
|
5
|
+
SimpleCov::Formatter::RcovFormatter
|
6
|
+
]
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter "db/"
|
9
|
+
add_filter "spec/"
|
10
|
+
add_group "Dovico", "lib/dovico"
|
11
|
+
end
|
12
|
+
require "rspec"
|
13
|
+
require "rspec/its"
|
14
|
+
require "timecop"
|
15
|
+
require "rack/test"
|
16
|
+
require 'webmock/rspec'
|
17
|
+
|
18
|
+
require 'pry'
|
19
|
+
|
20
|
+
# Load libs
|
21
|
+
require "dovico"
|
22
|
+
|
23
|
+
Timecop.safe_mode = true
|
24
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Dovico
|
4
|
+
describe Dovico::ApiClient do
|
5
|
+
let(:client_token) { "aaaaa-client-token-aaaaa" }
|
6
|
+
let(:user_token) { "zzzzz-user-token-zzzzz" }
|
7
|
+
let(:response_body) do
|
8
|
+
{
|
9
|
+
"Stubbed": "Response"
|
10
|
+
}.to_json
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
Dovico::ApiClient.initialize!(client_token, user_token)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe ".get" do
|
18
|
+
context 'with a succcessful response' do
|
19
|
+
it "passes the correct headers to the request and parses the JSON response" do
|
20
|
+
stub_request(
|
21
|
+
:get,
|
22
|
+
"#{Dovico::ApiClient::API_URL}TimeEntries?filter=1234%205678&version=#{Dovico::ApiClient::API_VERSION}"
|
23
|
+
)
|
24
|
+
.with(
|
25
|
+
headers: {
|
26
|
+
'Accept': 'application/json',
|
27
|
+
'Content-Type': 'application/json',
|
28
|
+
'Authorization': "WRAP access_token=\"client=#{client_token}&user_token=#{user_token}\"",
|
29
|
+
}
|
30
|
+
)
|
31
|
+
.to_return(body: response_body)
|
32
|
+
|
33
|
+
response = Dovico::ApiClient.get(
|
34
|
+
'TimeEntries',
|
35
|
+
params: {
|
36
|
+
filter: '1234 5678'
|
37
|
+
}
|
38
|
+
)
|
39
|
+
|
40
|
+
expect(response).to eq({"Stubbed": "Response"}.stringify_keys)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with a succcessful response' do
|
45
|
+
it "raises an error if the API response is not 200 OK" do
|
46
|
+
stub_request(:get, "#{Dovico::ApiClient::API_URL}TimeEntries?version=#{Dovico::ApiClient::API_VERSION}")
|
47
|
+
.to_return(
|
48
|
+
status: 503,
|
49
|
+
body: response_body
|
50
|
+
)
|
51
|
+
|
52
|
+
expect { Dovico::ApiClient.get('TimeEntries') }.to raise_error 'Error during HTTP request'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".post" do
|
58
|
+
it 'posts the body with a POST request' do
|
59
|
+
stub_request(:post,
|
60
|
+
"#{Dovico::ApiClient::API_URL}TimeEntries?version=#{Dovico::ApiClient::API_VERSION}"
|
61
|
+
)
|
62
|
+
.with(
|
63
|
+
body: '{"Fake":"Object"}',
|
64
|
+
)
|
65
|
+
.to_return(body: response_body)
|
66
|
+
|
67
|
+
Dovico::ApiClient.post('TimeEntries', body: {"Fake": "Object"}.to_json)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe ".put" do
|
72
|
+
it 'perform a PUT request' do
|
73
|
+
stub_request(:put, "#{Dovico::ApiClient::API_URL}TimeEntries?version=#{Dovico::ApiClient::API_VERSION}")
|
74
|
+
.to_return(body: response_body)
|
75
|
+
|
76
|
+
response = Dovico::ApiClient.put('TimeEntries')
|
77
|
+
expect(response).to eq({"Stubbed": "Response"}.stringify_keys)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Dovico
|
4
|
+
describe Dovico::Assignment do
|
5
|
+
let(:assignment_api_hash) do
|
6
|
+
{
|
7
|
+
"ItemID": "123",
|
8
|
+
"AssignmentID": "T456",
|
9
|
+
"Name": "Dovico API Client",
|
10
|
+
"StartDate": "2017-01-01",
|
11
|
+
"FinishDate": "2017-12-31",
|
12
|
+
}.stringify_keys
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".parse" do
|
16
|
+
it "parses the API hash" do
|
17
|
+
assignment = Dovico::Assignment.parse(assignment_api_hash)
|
18
|
+
|
19
|
+
expect(assignment).to be_an(Dovico::Assignment)
|
20
|
+
expect(assignment.id).to eq('123')
|
21
|
+
expect(assignment.assignement_id).to eq('T456')
|
22
|
+
expect(assignment.name).to eq('Dovico API Client')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Dovico
|
4
|
+
describe Dovico::Employee do
|
5
|
+
let(:employee_api_hash) do
|
6
|
+
{
|
7
|
+
"ID": "123",
|
8
|
+
"FirstName": "James",
|
9
|
+
"LastName": "Bond",
|
10
|
+
}.stringify_keys
|
11
|
+
end
|
12
|
+
|
13
|
+
describe ".parse" do
|
14
|
+
it "parses the hash" do
|
15
|
+
employee = Dovico::Employee.parse(employee_api_hash)
|
16
|
+
|
17
|
+
expect(employee).to be_an(Dovico::Employee)
|
18
|
+
expect(employee.id).to eq('123')
|
19
|
+
expect(employee.first_name).to eq('James')
|
20
|
+
expect(employee.last_name).to eq('Bond')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe ".myself" do
|
25
|
+
let(:employee_me_answer) do
|
26
|
+
{
|
27
|
+
"Employees": [ employee_api_hash ]
|
28
|
+
}.stringify_keys
|
29
|
+
end
|
30
|
+
|
31
|
+
before do
|
32
|
+
allow(ApiClient).to receive(:get).and_return(employee_me_answer)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'calls the API and return an Employee object' do
|
36
|
+
myself = Dovico::Employee.myself
|
37
|
+
|
38
|
+
expect(ApiClient).to have_received(:get).with("#{Dovico::Employee::URL_PATH}/Me")
|
39
|
+
expect(myself).to be_an(Dovico::Employee)
|
40
|
+
expect(myself.id).to eq('123')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Dovico
|
4
|
+
describe Dovico::Project do
|
5
|
+
let(:project_api_hash) do
|
6
|
+
{
|
7
|
+
"ItemID": "123",
|
8
|
+
"AssignmentID": "T456",
|
9
|
+
"Name": "Project Dovico API Client",
|
10
|
+
"StartDate": "2017-01-01",
|
11
|
+
"FinishDate": "2017-12-31",
|
12
|
+
}.stringify_keys
|
13
|
+
end
|
14
|
+
let(:projects_api_hash) do
|
15
|
+
{
|
16
|
+
"Assignments": [project_api_hash]
|
17
|
+
}.stringify_keys
|
18
|
+
end
|
19
|
+
let(:task_api_hash) do
|
20
|
+
{
|
21
|
+
"ItemID": "789",
|
22
|
+
"AssignmentID": "E456",
|
23
|
+
"Name": "Task write specs",
|
24
|
+
"StartDate": "2016-10-25",
|
25
|
+
"FinishDate": "2018-05-01",
|
26
|
+
}.stringify_keys
|
27
|
+
end
|
28
|
+
let(:tasks_api_hash) do
|
29
|
+
{
|
30
|
+
"Assignments": [task_api_hash]
|
31
|
+
}.stringify_keys
|
32
|
+
end
|
33
|
+
|
34
|
+
describe ".all" do
|
35
|
+
before do
|
36
|
+
allow(ApiClient).to receive(:get).with(Dovico::Project::URL_PATH).and_return(projects_api_hash)
|
37
|
+
allow(ApiClient).to receive(:get).with("#{Dovico::Project::URL_PATH}T456").and_return(tasks_api_hash)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "lists all the assignements" do
|
41
|
+
projects = Dovico::Project.all
|
42
|
+
|
43
|
+
expect(projects.count).to eq(1)
|
44
|
+
project = projects.first
|
45
|
+
expect(project).to be_an(Dovico::Project)
|
46
|
+
expect(project.id).to eq('123')
|
47
|
+
expect(project.name).to eq('Project Dovico API Client')
|
48
|
+
|
49
|
+
expect(project.tasks.count).to eq(1)
|
50
|
+
task = project.tasks.first
|
51
|
+
expect(task.id).to eq('789')
|
52
|
+
expect(task.name).to eq('Task write specs')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Dovico
|
4
|
+
describe Dovico::Task do
|
5
|
+
let(:task_api_hash) do
|
6
|
+
{
|
7
|
+
"ItemID": "123",
|
8
|
+
"AssignmentID": "E456",
|
9
|
+
"Name": "Dovico API Client",
|
10
|
+
"StartDate": "2017-01-01",
|
11
|
+
"FinishDate": "2017-12-31",
|
12
|
+
}.stringify_keys
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".parse" do
|
16
|
+
it "parses the API hash" do
|
17
|
+
task = Dovico::Task.parse(task_api_hash)
|
18
|
+
|
19
|
+
expect(task).to be_an(Dovico::Task)
|
20
|
+
expect(task.id).to eq('123')
|
21
|
+
expect(task.assignement_id).to eq('E456')
|
22
|
+
expect(task.name).to eq('Dovico API Client')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Dovico
|
4
|
+
describe Dovico::TimeEntryGenerator do
|
5
|
+
subject do
|
6
|
+
Dovico::TimeEntryGenerator.new(
|
7
|
+
employee_id: employee_id,
|
8
|
+
assignments: assignments_config
|
9
|
+
)
|
10
|
+
end
|
11
|
+
let(:employee_id) { "999" }
|
12
|
+
let(:assignment_1) do
|
13
|
+
{
|
14
|
+
"project_id": "1234",
|
15
|
+
"task_id": "456",
|
16
|
+
"hours": 4,
|
17
|
+
}.stringify_keys
|
18
|
+
end
|
19
|
+
let(:assignment_2) do
|
20
|
+
{
|
21
|
+
"project_id": "6789",
|
22
|
+
"task_id": "999",
|
23
|
+
"hours": 3,
|
24
|
+
}.stringify_keys
|
25
|
+
end
|
26
|
+
let(:assignments_config) do
|
27
|
+
{
|
28
|
+
"default_day": [ assignment_1, assignment_2 ]
|
29
|
+
}.stringify_keys
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#generate" do
|
33
|
+
let(:start_date) { Date.parse('2017-01-02') }
|
34
|
+
let(:end_date) { Date.parse('2017-01-06') }
|
35
|
+
|
36
|
+
context 'with minimal configuration' do
|
37
|
+
it 'creates TimeEntries object for the date range' do
|
38
|
+
time_entries = subject.generate(start_date, end_date)
|
39
|
+
|
40
|
+
# 5 days * 2 time entries
|
41
|
+
expect(time_entries.count).to eq(10)
|
42
|
+
first_time_entry = time_entries.first
|
43
|
+
last_time_entry = time_entries.last
|
44
|
+
|
45
|
+
expect(first_time_entry.id).to be_nil
|
46
|
+
expect(first_time_entry.employee_id).to eq(employee_id)
|
47
|
+
expect(first_time_entry.project_id).to eq('1234')
|
48
|
+
expect(first_time_entry.task_id).to eq('456')
|
49
|
+
expect(first_time_entry.date).to eq('2017-01-02')
|
50
|
+
expect(first_time_entry.start_time).to eq('0900')
|
51
|
+
expect(first_time_entry.stop_time).to eq('1300')
|
52
|
+
expect(first_time_entry.total_hours).to eq(4)
|
53
|
+
|
54
|
+
expect(first_time_entry.employee_id).to eq(employee_id)
|
55
|
+
expect(last_time_entry.project_id).to eq('6789')
|
56
|
+
expect(last_time_entry.task_id).to eq('999')
|
57
|
+
expect(last_time_entry.date).to eq('2017-01-06')
|
58
|
+
expect(last_time_entry.start_time).to eq('1300')
|
59
|
+
expect(last_time_entry.stop_time).to eq('1600')
|
60
|
+
expect(last_time_entry.total_hours).to eq(3)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with specific day assignments' do
|
65
|
+
let(:assignments_config) do
|
66
|
+
{
|
67
|
+
'default_day': [ assignment_1, assignment_2 ],
|
68
|
+
'2016-02-29': [],
|
69
|
+
'2016-03-01': [ assignment_2 ],
|
70
|
+
}.stringify_keys
|
71
|
+
end
|
72
|
+
let(:start_date) { Date.parse('2016-02-29') }
|
73
|
+
let(:end_date) { Date.parse('2016-03-01') }
|
74
|
+
|
75
|
+
it 'uses specific day assignments' do
|
76
|
+
time_entries = subject.generate(start_date, end_date)
|
77
|
+
|
78
|
+
expect(time_entries.count).to eq(1)
|
79
|
+
time_entry = time_entries.first
|
80
|
+
expect(time_entry.date).to eq('2016-03-01')
|
81
|
+
expect(time_entry.total_hours).to eq(3)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Dovico
|
4
|
+
describe Dovico::TimeEntry do
|
5
|
+
subject do
|
6
|
+
Dovico::TimeEntry.parse(time_entry_api_hash)
|
7
|
+
end
|
8
|
+
let(:project_hash) do
|
9
|
+
{
|
10
|
+
"ID": "111"
|
11
|
+
}.stringify_keys
|
12
|
+
end
|
13
|
+
let(:task_hash) do
|
14
|
+
{
|
15
|
+
"ID": "222"
|
16
|
+
}.stringify_keys
|
17
|
+
end
|
18
|
+
let(:employee_hash) do
|
19
|
+
{
|
20
|
+
"ID": "333"
|
21
|
+
}.stringify_keys
|
22
|
+
end
|
23
|
+
let(:time_entry_api_hash) do
|
24
|
+
{
|
25
|
+
"ID": "456",
|
26
|
+
"StartTime": "0800",
|
27
|
+
"StopTime": "1100",
|
28
|
+
"Project": project_hash,
|
29
|
+
"Task": task_hash,
|
30
|
+
"Employee": employee_hash,
|
31
|
+
"Date": "2017-01-05",
|
32
|
+
"TotalHours": "3",
|
33
|
+
"Description": "Unit test",
|
34
|
+
}.stringify_keys
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".parse" do
|
38
|
+
it "parses the API hash" do
|
39
|
+
time_entry = Dovico::TimeEntry.parse(time_entry_api_hash)
|
40
|
+
|
41
|
+
expect(time_entry).to be_an(Dovico::TimeEntry)
|
42
|
+
expect(time_entry.start_time).to eq('0800')
|
43
|
+
expect(time_entry.project_id).to eq('111')
|
44
|
+
expect(time_entry.employee_id).to eq('333')
|
45
|
+
expect(time_entry.total_hours).to eq('3')
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with a GUID ID' do
|
49
|
+
it 'removes the first character of the GUID' do
|
50
|
+
time_entry = Dovico::TimeEntry.parse(
|
51
|
+
time_entry_api_hash.merge(
|
52
|
+
{"ID": "T0bb4860f-2669-49ab-ae5e-dd8908366c8b"}.stringify_keys
|
53
|
+
)
|
54
|
+
)
|
55
|
+
|
56
|
+
expect(time_entry.id).to eq('0bb4860f-2669-49ab-ae5e-dd8908366c8b')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe ".get" do
|
62
|
+
let(:time_entry_list_api) do
|
63
|
+
{
|
64
|
+
"TimeEntries": [ time_entry_api_hash ]
|
65
|
+
}.stringify_keys
|
66
|
+
end
|
67
|
+
|
68
|
+
before do
|
69
|
+
allow(ApiClient).to receive(:get).and_return(time_entry_list_api)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'calls the API and return an TimeEntry object' do
|
73
|
+
time_entry = Dovico::TimeEntry.get('999')
|
74
|
+
|
75
|
+
expect(ApiClient).to have_received(:get).with("#{Dovico::TimeEntry::URL_PATH}/999")
|
76
|
+
expect(time_entry).to be_an(Dovico::TimeEntry)
|
77
|
+
expect(time_entry.id).to eq('456')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe ".batch_create!" do
|
82
|
+
let(:assignments) do
|
83
|
+
[ subject ]
|
84
|
+
end
|
85
|
+
|
86
|
+
before do
|
87
|
+
allow(ApiClient).to receive(:post)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'calls the API and post TimeEntries objects' do
|
91
|
+
Dovico::TimeEntry.batch_create!(assignments)
|
92
|
+
|
93
|
+
expect(ApiClient).to have_received(:post).with(
|
94
|
+
"#{Dovico::TimeEntry::URL_PATH}",
|
95
|
+
body: [subject.to_api].to_json
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe ".submit!" do
|
101
|
+
let(:start_date) { Date.parse("2017-01-02") }
|
102
|
+
let(:end_date) { Date.parse("2017-01-12") }
|
103
|
+
let(:employee_id) { "333" }
|
104
|
+
|
105
|
+
before do
|
106
|
+
allow(ApiClient).to receive(:post)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'calls the API and submit a time range' do
|
110
|
+
Dovico::TimeEntry.submit!(employee_id, start_date, end_date)
|
111
|
+
|
112
|
+
expect(ApiClient).to have_received(:post).with(
|
113
|
+
"#{Dovico::TimeEntry::URL_PATH}/Employee/#{employee_id}/Submit",
|
114
|
+
params: {
|
115
|
+
daterange: "2017-01-02 2017-01-12"
|
116
|
+
},
|
117
|
+
body: {}.to_json
|
118
|
+
)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe ".create!" do
|
123
|
+
before do
|
124
|
+
allow(ApiClient).to receive(:post)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'calls the API and post TimeEntries objects' do
|
128
|
+
subject.create!
|
129
|
+
|
130
|
+
expect(ApiClient).to have_received(:post).with(
|
131
|
+
"#{Dovico::TimeEntry::URL_PATH}",
|
132
|
+
body: [subject.to_api].to_json
|
133
|
+
)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe ".update!" do
|
138
|
+
before do
|
139
|
+
allow(ApiClient).to receive(:put)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'calls the API and post TimeEntries objects' do
|
143
|
+
subject.update!
|
144
|
+
|
145
|
+
expect(ApiClient).to have_received(:put).with(
|
146
|
+
"#{Dovico::TimeEntry::URL_PATH}",
|
147
|
+
body: [subject.to_api].to_json
|
148
|
+
)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe ".to_api" do
|
153
|
+
it 'serializes the object' do
|
154
|
+
expect(subject.to_api).to eq(
|
155
|
+
"ID" => "456",
|
156
|
+
"EmployeeID" => "333",
|
157
|
+
"ProjectID" => "111",
|
158
|
+
"TaskID" => "222",
|
159
|
+
"Date" => "2017-01-05",
|
160
|
+
"StartTime" => "0800",
|
161
|
+
"StopTime" => "1100",
|
162
|
+
"TotalHours" => "3",
|
163
|
+
"Description" => "Unit test",
|
164
|
+
)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|