contactually-api 0.0.1
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 +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +62 -0
- data/Rakefile +7 -0
- data/lib/contactually-api.rb +34 -0
- data/lib/contactually/accounts.rb +31 -0
- data/lib/contactually/api.rb +70 -0
- data/lib/contactually/contact_groupings.rb +16 -0
- data/lib/contactually/contacts.rb +57 -0
- data/lib/contactually/contents.rb +38 -0
- data/lib/contactually/errors.rb +18 -0
- data/lib/contactually/groupings.rb +49 -0
- data/lib/contactually/middleware/error_detector.rb +27 -0
- data/lib/contactually/notes.rb +39 -0
- data/lib/contactually/representer/account_representer.rb +13 -0
- data/lib/contactually/representer/contact_representer.rb +46 -0
- data/lib/contactually/representer/content_representer.rb +16 -0
- data/lib/contactually/representer/grouping_representer.rb +28 -0
- data/lib/contactually/representer/grouping_statistics_representer.rb +12 -0
- data/lib/contactually/representer/note_representer.rb +13 -0
- data/lib/contactually/representer/task_representer.rb +19 -0
- data/lib/contactually/tasks.rb +12 -0
- data/lib/contactually/version.rb +3 -0
- data/spec/accounts_spec.rb +57 -0
- data/spec/api_spec.rb +87 -0
- data/spec/contact_groupings_spec.rb +43 -0
- data/spec/contacts_spec.rb +117 -0
- data/spec/contents_spec.rb +81 -0
- data/spec/fixtures/account.json +10 -0
- data/spec/fixtures/accounts_index.json +16 -0
- data/spec/fixtures/contact.json +38 -0
- data/spec/fixtures/contacts_index.json +169 -0
- data/spec/fixtures/content.json +10 -0
- data/spec/fixtures/contents_index.json +26 -0
- data/spec/fixtures/grouping.json +19 -0
- data/spec/fixtures/groupings_index.json +24 -0
- data/spec/fixtures/groupings_statistics.json +46 -0
- data/spec/fixtures/note.json +7 -0
- data/spec/fixtures/note_destroy.json +4 -0
- data/spec/fixtures/notes_index.json +27 -0
- data/spec/fixtures/task.json +27 -0
- data/spec/groupings_spec.rb +100 -0
- data/spec/notes_spec.rb +89 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/tasks_spec.rb +36 -0
- metadata +194 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module Contactually
|
2
|
+
class Notes
|
3
|
+
def initialize(master)
|
4
|
+
@master = master
|
5
|
+
end
|
6
|
+
|
7
|
+
def index(params = {})
|
8
|
+
hash = @master.call('notes.json', :get, params)
|
9
|
+
notes_hash_to_objects(hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
def show(id, params = {})
|
13
|
+
hash = @master.call("notes/#{id}.json", :get, params)
|
14
|
+
NoteRepresenter.new(Note.new).from_hash(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(params = {})
|
18
|
+
hash = @master.call('notes.json', :post, params)
|
19
|
+
NoteRepresenter.new(Note.new).from_hash(hash)
|
20
|
+
end
|
21
|
+
|
22
|
+
def destroy(id, params = {})
|
23
|
+
@master.call("notes/#{id}.json", :delete, params)
|
24
|
+
end
|
25
|
+
|
26
|
+
def update(id, params = {})
|
27
|
+
hash = @master.call("notes/#{id}.json", :put, params)
|
28
|
+
NoteRepresenter.new(Note.new).from_hash(hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def notes_hash_to_objects(hash)
|
34
|
+
hash['notes'].inject([]) do |arr, note|
|
35
|
+
arr << NoteRepresenter.new(Note.new).from_hash(note)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Contactually
|
2
|
+
class Contact < OpenStruct
|
3
|
+
end
|
4
|
+
|
5
|
+
class ContactRepresenter < Roar::Decorator
|
6
|
+
include Roar::Representer::JSON
|
7
|
+
property :id
|
8
|
+
property :user_id
|
9
|
+
property :first_name
|
10
|
+
property :last_name
|
11
|
+
property :full_name
|
12
|
+
property :initials
|
13
|
+
property :title
|
14
|
+
property :company
|
15
|
+
property :email
|
16
|
+
property :avatar
|
17
|
+
property :avatar_url
|
18
|
+
property :last_contacted
|
19
|
+
property :visible
|
20
|
+
property :twitter
|
21
|
+
property :facebook_url
|
22
|
+
property :linkedin_url
|
23
|
+
property :first_contacted
|
24
|
+
property :created_at
|
25
|
+
property :updated_at
|
26
|
+
property :hits
|
27
|
+
property :team_parent_id
|
28
|
+
property :snoozed_at
|
29
|
+
property :snooze_days
|
30
|
+
property :email_addresses
|
31
|
+
property :tags
|
32
|
+
property :contact_status
|
33
|
+
property :team_last_contacted
|
34
|
+
property :team_last_contacted_by
|
35
|
+
property :phone_numbers
|
36
|
+
property :addresses
|
37
|
+
property :social_profiles
|
38
|
+
property :websites
|
39
|
+
property :custom_fields
|
40
|
+
property :sent
|
41
|
+
property :received
|
42
|
+
property :link
|
43
|
+
property :content
|
44
|
+
collection :groupings, extend: GroupingRepresenter, class: Grouping
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Contactually
|
2
|
+
class Content < OpenStruct
|
3
|
+
end
|
4
|
+
|
5
|
+
class ContentRepresenter < Roar::Decorator
|
6
|
+
include Roar::Representer::JSON
|
7
|
+
property :id
|
8
|
+
property :user_id
|
9
|
+
property :url
|
10
|
+
property :title
|
11
|
+
property :description
|
12
|
+
property :article
|
13
|
+
property :original_content_id
|
14
|
+
collection :groupings, extend: GroupingRepresenter, class: Grouping
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Contactually
|
2
|
+
class Grouping < OpenStruct
|
3
|
+
end
|
4
|
+
|
5
|
+
class GroupingRepresenter < Roar::Decorator
|
6
|
+
include Roar::Representer::JSON
|
7
|
+
property :id
|
8
|
+
property :type
|
9
|
+
property :name
|
10
|
+
property :stub
|
11
|
+
property :user_id
|
12
|
+
property :domain_id
|
13
|
+
property :editable
|
14
|
+
property :conversable
|
15
|
+
property :locked
|
16
|
+
property :derived_from_id
|
17
|
+
property :created_at
|
18
|
+
property :updated_at
|
19
|
+
property :has_followups
|
20
|
+
property :num_days_to_followup
|
21
|
+
property :program_id
|
22
|
+
property :objective
|
23
|
+
property :sort_order
|
24
|
+
property :accounts
|
25
|
+
property :number_of_contacts
|
26
|
+
property :goal
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Contactually
|
2
|
+
class GroupingStatistics < OpenStruct
|
3
|
+
end
|
4
|
+
|
5
|
+
class GroupingStatisticsRepresenter < Roar::Decorator
|
6
|
+
include Roar::Representer::JSON
|
7
|
+
property :messages_sent
|
8
|
+
property :messages_received
|
9
|
+
property :status
|
10
|
+
collection :contacts, extend: ContactRepresenter, class: Contact
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Contactually
|
2
|
+
class Task < OpenStruct
|
3
|
+
end
|
4
|
+
|
5
|
+
class TaskRepresenter < Roar::Decorator
|
6
|
+
include Roar::Representer::JSON
|
7
|
+
property :id
|
8
|
+
property :title
|
9
|
+
property :due_date
|
10
|
+
property :completed_at
|
11
|
+
property :deleted_at
|
12
|
+
property :is_follow_up
|
13
|
+
property :last_contacted
|
14
|
+
property :contact_id
|
15
|
+
property :ignored
|
16
|
+
property :completed_via
|
17
|
+
property :approval_data
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Contactually::Accounts do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
Contactually.configure { |c| c.api_key = 'VALID_API_KEY' }
|
7
|
+
@master = Contactually::API.new
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { described_class.new @master }
|
11
|
+
describe '#initialize' do
|
12
|
+
specify do
|
13
|
+
expect(subject).to be_kind_of Contactually::Accounts
|
14
|
+
end
|
15
|
+
|
16
|
+
specify do
|
17
|
+
expect(subject.instance_variable_get(:@master)).to be_kind_of Contactually::API
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#index' do
|
22
|
+
it 'calls the api with correct params' do
|
23
|
+
allow(@master).to receive(:call).with('accounts.json', :get, { foo: :bar }).and_return({ 'accounts' => [] })
|
24
|
+
subject.index({ foo: :bar })
|
25
|
+
expect(@master).to have_received(:call)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns accounts from json response' do
|
29
|
+
json = File.read(File.join(File.dirname(__FILE__),"fixtures/accounts_index.json"))
|
30
|
+
allow(@master).to receive(:call).with('accounts.json', :get, {}).and_return(JSON.load(json))
|
31
|
+
expect(subject.index({})).to be_kind_of Array
|
32
|
+
expect(subject.index({})[0]).to be_kind_of Contactually::Account
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#show' do
|
37
|
+
it 'calls the api with correct params' do
|
38
|
+
allow(@master).to receive(:call).with('accounts/1.json', :get, { foo: :bar }).and_return({ id: 1 })
|
39
|
+
subject.show(1, { foo: :bar })
|
40
|
+
expect(@master).to have_received(:call)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'returns an account' do
|
44
|
+
json = File.read(File.join(File.dirname(__FILE__),"fixtures/account.json"))
|
45
|
+
allow(@master).to receive(:call).with('accounts/1.json', :get, { foo: :bar }).and_return(JSON.load(json))
|
46
|
+
expect(subject.show(1, { foo: :bar })).to be_kind_of Contactually::Account
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#destroy' do
|
51
|
+
it 'calls the api with correct params' do
|
52
|
+
allow(@master).to receive(:call).with('accounts/1.json', :delete, {})
|
53
|
+
subject.destroy(1)
|
54
|
+
expect(@master).to have_received(:call)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/api_spec.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Contactually::API do
|
4
|
+
|
5
|
+
describe 'missing configuration' do
|
6
|
+
before(:all) { Contactually.configure { |c| c.api_key = nil } }
|
7
|
+
specify do
|
8
|
+
expect{ subject }.to raise_error Contactually::ConfigMissingApiKeyError
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'valid configuration' do
|
13
|
+
before(:all) { Contactually.configure { |c| c.api_key = 'VALID_API_KEY' } }
|
14
|
+
|
15
|
+
describe '#initialize' do
|
16
|
+
it 'initializes correctly' do
|
17
|
+
expect{ subject }.not_to raise_error
|
18
|
+
expect(subject).to be_kind_of Contactually::API
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'sets instance variables' do
|
22
|
+
expect(subject.instance_variable_get(:@api_key)).to eq 'VALID_API_KEY'
|
23
|
+
expect(subject.instance_variable_get(:@base_url)).to eq Contactually.contactually_url
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#call' do
|
28
|
+
it 'redirects get request to get' do
|
29
|
+
allow(subject).to receive(:get).and_return(Struct.new(:status, :body).new(200, {}))
|
30
|
+
subject.call('bla', :get, {})
|
31
|
+
expect(subject).to have_received(:get).with('bla', {})
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'redirects post request to post' do
|
35
|
+
allow(subject).to receive(:post).and_return(Struct.new(:status, :body).new(200, {}))
|
36
|
+
subject.call('bla', :post, {})
|
37
|
+
expect(subject).to have_received(:post).with('bla', {})
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#call_params' do
|
42
|
+
specify do
|
43
|
+
expect(subject.send(:call_params, { foo: :bar })).to eql({ api_key: 'VALID_API_KEY', foo: :bar })
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#base_url' do
|
48
|
+
specify do
|
49
|
+
expect(subject.send(:base_url, 'foo/bar')).to eql("#{Contactually.contactually_url}foo/bar")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#contacts' do
|
54
|
+
specify do
|
55
|
+
expect(subject.contacts).to be_kind_of Contactually::Contacts
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#notes' do
|
60
|
+
specify do
|
61
|
+
expect(subject.notes).to be_kind_of Contactually::Notes
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#groupings' do
|
66
|
+
specify do
|
67
|
+
expect(subject.groupings).to be_kind_of Contactually::Groupings
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#get' do
|
72
|
+
it 'parses from json response' do
|
73
|
+
allow(subject.connection).to receive(:get).
|
74
|
+
with('https://www.contactually.com/api/v1/url', { foo: :bar, api_key: 'VALID_API_KEY' }).
|
75
|
+
and_return(Struct.new(:status, :body).new(200, "{ \"foo\": \"bar\" }"))
|
76
|
+
expect(subject.call('url', :get, { foo: :bar })).to be_kind_of Hash
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#post' do
|
81
|
+
it 'parses from json response' do
|
82
|
+
allow(subject.connection).to receive(:post).and_return(Struct.new(:status, :body).new(200, "{ \"foo\": \"bar\" }"))
|
83
|
+
expect(subject.call('url', :post, { foo: :bar })).to be_kind_of Hash
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Contactually::ContactGroupings do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
Contactually.configure { |c| c.api_key = 'VALID_API_KEY' }
|
7
|
+
@master = Contactually::API.new
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { described_class.new @master }
|
11
|
+
describe '#initialize' do
|
12
|
+
specify do
|
13
|
+
expect(subject).to be_kind_of Contactually::ContactGroupings
|
14
|
+
end
|
15
|
+
|
16
|
+
specify do
|
17
|
+
expect(subject.instance_variable_get(:@master)).to be_kind_of Contactually::API
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
describe '#create' do
|
23
|
+
it 'calls the api with correct params' do
|
24
|
+
allow(@master).to receive(:call).with('contacts/1/groupings.json', :post, { grouping_id: 512 }).and_return({ 'id' => 1234 })
|
25
|
+
subject.create(1, { grouping_id: 512 })
|
26
|
+
expect(@master).to have_received(:call)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns grouping from json response' do
|
30
|
+
json = File.read(File.join(File.dirname(__FILE__),"fixtures/grouping.json"))
|
31
|
+
allow(@master).to receive(:call).with('contacts/1/groupings.json', :post, { grouping_id: 512 }).and_return(JSON.load(json))
|
32
|
+
expect(subject.create(1, { grouping_id: 512 })).to be_kind_of Contactually::Grouping
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#destroy' do
|
37
|
+
it 'calls the api with correct params' do
|
38
|
+
allow(@master).to receive(:call).with('contacts/1/groupings/15.json', :delete, { foo: :bar }).and_return({ 'success' => true })
|
39
|
+
subject.destroy(1, 15, { foo: :bar })
|
40
|
+
expect(@master).to have_received(:call)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Contactually::Contacts do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
Contactually.configure { |c| c.api_key = 'VALID_API_KEY' }
|
7
|
+
@master = Contactually::API.new
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { described_class.new @master }
|
11
|
+
describe '#initialize' do
|
12
|
+
specify do
|
13
|
+
expect(subject).to be_kind_of Contactually::Contacts
|
14
|
+
end
|
15
|
+
|
16
|
+
specify do
|
17
|
+
expect(subject.instance_variable_get(:@master)).to be_kind_of Contactually::API
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#create' do
|
22
|
+
it 'calls the api with correct params' do
|
23
|
+
json = File.read(File.join(File.dirname(__FILE__),"fixtures/contact.json"))
|
24
|
+
allow(@master).to receive(:call).with('contacts.json', :post, { contact: { foo: :bar }}).and_return(JSON.load(json))
|
25
|
+
subject.create({ contact: { foo: :bar }})
|
26
|
+
expect(@master).to have_received(:call)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns a contact' do
|
30
|
+
json = File.read(File.join(File.dirname(__FILE__),"fixtures/contact.json"))
|
31
|
+
allow(@master).to receive(:call).with('contacts.json', :post, { contact: { foo: :bar }}).and_return(JSON.load(json))
|
32
|
+
expect(subject.create({ contact: { foo: :bar } })).to be_kind_of Contactually::Contact
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#destroy' do
|
37
|
+
it 'calls the api with correct params' do
|
38
|
+
allow(@master).to receive(:call).with('contacts/1.json', :delete, {})
|
39
|
+
subject.destroy(1)
|
40
|
+
expect(@master).to have_received(:call)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#destroy_multiple' do
|
45
|
+
it 'calls the api with correct params' do
|
46
|
+
allow(@master).to receive(:call).with('contacts.json', :delete, { ids: [ 1, 2, 3 ]})
|
47
|
+
subject.destroy_multiple({ ids: [ 1, 2, 3 ]})
|
48
|
+
expect(@master).to have_received(:call)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#show' do
|
53
|
+
it 'calls the api with correct params' do
|
54
|
+
allow(@master).to receive(:call).with('contacts/1.json', :get, { foo: :bar }).and_return({ id: 1 })
|
55
|
+
subject.show(1, { foo: :bar })
|
56
|
+
expect(@master).to have_received(:call)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns a contact' do
|
60
|
+
json = File.read(File.join(File.dirname(__FILE__),"fixtures/contact.json"))
|
61
|
+
allow(@master).to receive(:call).with('contacts/1.json', :get, { foo: :bar }).and_return(JSON.load(json))
|
62
|
+
expect(subject.show(1, { foo: :bar })).to be_kind_of Contactually::Contact
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#tags' do
|
67
|
+
it 'calls the api with correct params - Array' do
|
68
|
+
allow(@master).to receive(:call).with('contacts/1/tags.json', :post, { tags: 'lol, haha' })
|
69
|
+
subject.tags(1, { tags: [ 'lol', 'haha' ]})
|
70
|
+
expect(@master).to have_received(:call)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'calls the api with correct params - String' do
|
74
|
+
allow(@master).to receive(:call).with('contacts/1/tags.json', :post, { tags: 'lol, haha' })
|
75
|
+
subject.tags(1, { tags: 'lol, haha' })
|
76
|
+
expect(@master).to have_received(:call)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#update' do
|
81
|
+
it 'calls the api with correct params' do
|
82
|
+
allow(@master).to receive(:call).with('contacts/1.json', :put, { contact: { foo: :bar }})
|
83
|
+
subject.update(1, { contact: { foo: :bar } })
|
84
|
+
expect(@master).to have_received(:call)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#index' do
|
89
|
+
it 'calls the api with correct params' do
|
90
|
+
allow(@master).to receive(:call).with('contacts.json', :get, { foo: :bar }).and_return({ 'contacts' => [] })
|
91
|
+
subject.index({ foo: :bar })
|
92
|
+
expect(@master).to have_received(:call)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'returns contacts from json response' do
|
96
|
+
json = File.read(File.join(File.dirname(__FILE__),"fixtures/contacts_index.json"))
|
97
|
+
allow(@master).to receive(:call).with('contacts.json', :get, {}).and_return(JSON.load(json))
|
98
|
+
expect(subject.index({})).to be_kind_of Array
|
99
|
+
expect(subject.index({})[0]).to be_kind_of Contactually::Contact
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#search' do
|
104
|
+
it 'calls the api with correct params' do
|
105
|
+
allow(@master).to receive(:call).with('contacts/search.json', :get, { term: :foo_bar }).and_return({ 'contacts' => [] })
|
106
|
+
subject.search({ term: :foo_bar})
|
107
|
+
expect(@master).to have_received(:call)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'returns contacts from json response' do
|
111
|
+
json = File.read(File.join(File.dirname(__FILE__),"fixtures/contacts_index.json"))
|
112
|
+
allow(@master).to receive(:call).with('contacts/search.json', :get, { term: :foo_bar }).and_return(JSON.load(json))
|
113
|
+
expect(subject.search({ term: :foo_bar })).to be_kind_of Array
|
114
|
+
expect(subject.search({ term: :foo_bar })[0]).to be_kind_of Contactually::Contact
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|