Skip to content

Commit c932b73

Browse files
Add CRUD actions for Product and Line Item endpoints (#12)
Co-authored-by: John Butler <johnnybutler7@gmail.com>
1 parent de3773d commit c932b73

17 files changed

+733
-0
lines changed

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ This gem utilizes the `v3` hubspot-api
1212
## CRM Objects
1313
- [Contacts](#contacts)
1414
- [Deals](#deals)
15+
- [Products](#products)
16+
- [Line Items](#line-items)
1517

1618
- [Error Handling](#error-handling)
1719

@@ -138,6 +140,69 @@ Please refrence the [hubspot docs](https://developers.hubspot.com/docs/api/crm/c
138140
EasyHubspot::Deal.delete_deal(123)
139141
```
140142

143+
### Products
144+
Please refrence the [hubspot docs](https://developers.hubspot.com/docs/api/crm/products)
145+
```ruby
146+
# Create a product
147+
# required: body
148+
# returns: parsed hubspot product
149+
EasyHubspot::Product.create_product(properties: { name: '', price: '', etc: ''})
150+
151+
# Update a product
152+
# required: product_id, body
153+
# - product_id: must be a hubspot product_id
154+
# returns: parsed hubspot product
155+
EasyHubspot::Product.update_product(123, properties: { name: '', price: '', etc: ''})
156+
157+
# Get a product
158+
# required: product_id
159+
# - product_id: must be a hubspot product_id
160+
# returns: parsed hubspot product
161+
EasyHubspot::Product.get_product(123)
162+
163+
# Get all products
164+
# returns: parsed hubspot products
165+
EasyHubspot::Product.get_products
166+
167+
# Delete a product
168+
# required: product_id
169+
# - product_id: must be a hubspot product_id
170+
# returns: {status: 'success'}
171+
EasyHubspot::Product.delete_product(123)
172+
```
173+
174+
### Line Items
175+
Please refrence the [hubspot docs](https://developers.hubspot.com/docs/api/crm/line-items)
176+
```ruby
177+
# Create a line item
178+
# required: body
179+
# Use hs_product_id property to base on an existing product
180+
# returns: parsed hubspot line item
181+
EasyHubspot::LineItem.create_line_item(properties: { quantity: '', hs_product_id: '', etc: ''})
182+
183+
# Update a line item
184+
# required: line_item_id, body
185+
# - line_item_id: must be a hubspot line_item_id
186+
# returns: parsed hubspot line item
187+
EasyHubspot::LineItem.update_line_item(123, properties: { quantity: '', etc: ''})
188+
189+
# Get a line item
190+
# required: line_item_id
191+
# - line_item_id: must be a hubspot line_item_id
192+
# returns: parsed hubspot line item
193+
EasyHubspot::LineItem.get_line_item(123)
194+
195+
# Get all line items
196+
# returns: parsed hubspot line items
197+
EasyHubspot::LineItem.get_line_items
198+
199+
# Delete a line item
200+
# required: line_item_id
201+
# - line_item_id: must be a hubspot line_item_id
202+
# returns: {status: 'success'}
203+
EasyHubspot::LineItem.delete_line_item(123)
204+
```
205+
141206
## Error Handling
142207

143208
```ruby

lib/easy_hubspot.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
require 'easy_hubspot/client'
55
require 'easy_hubspot/contact'
66
require 'easy_hubspot/deal'
7+
require 'easy_hubspot/product'
8+
require 'easy_hubspot/line_item'
79
require 'easy_hubspot/version'
810
require 'easy_hubspot/generators/install_generator'
911
require 'easy_hubspot/exceptions'

lib/easy_hubspot/line_item.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# frozen_string_literal: true
2+
3+
module EasyHubspot
4+
# class EasyHubspot::LineItem
5+
class LineItem < EasyHubspot::Base
6+
class << self
7+
LINE_ITEM_ENDPOINT = 'crm/v3/objects/line_items'
8+
9+
def get_line_item(line_item_id)
10+
Client.do_get(line_item_id_endpoint(line_item_id), headers)
11+
end
12+
13+
def get_line_items
14+
Client.do_get(LINE_ITEM_ENDPOINT, headers)
15+
end
16+
17+
def create_line_item(body)
18+
Client.do_post(LINE_ITEM_ENDPOINT, body, headers)
19+
end
20+
21+
def update_line_item(line_item_id, body)
22+
Client.do_patch(line_item_id_endpoint(line_item_id), body, headers)
23+
end
24+
25+
def delete_line_item(line_item_id)
26+
Client.do_delete(line_item_id_endpoint(line_item_id), headers)
27+
end
28+
29+
private
30+
31+
def line_item_id_endpoint(line_item_id)
32+
"#{LINE_ITEM_ENDPOINT}/#{line_item_id}"
33+
end
34+
end
35+
end
36+
end

lib/easy_hubspot/product.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# frozen_string_literal: true
2+
3+
module EasyHubspot
4+
# class EasyHubspot::Product
5+
class Product < EasyHubspot::Base
6+
class << self
7+
PRODUCT_ENDPOINT = 'crm/v3/objects/products'
8+
9+
def get_product(product_id)
10+
Client.do_get(product_id_endpoint(product_id), headers)
11+
end
12+
13+
def get_products
14+
Client.do_get(PRODUCT_ENDPOINT, headers)
15+
end
16+
17+
def create_product(body)
18+
Client.do_post(PRODUCT_ENDPOINT, body, headers)
19+
end
20+
21+
def update_product(product_id, body)
22+
Client.do_patch(product_id_endpoint(product_id), body, headers)
23+
end
24+
25+
def delete_product(product_id)
26+
Client.do_delete(product_id_endpoint(product_id), headers)
27+
end
28+
29+
private
30+
31+
def product_id_endpoint(product_id)
32+
"#{PRODUCT_ENDPOINT}/#{product_id}"
33+
end
34+
end
35+
end
36+
end
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe EasyHubspot::LineItem do
6+
before :all do
7+
EasyHubspot.config do |config|
8+
config.access_token = 'YOUR-PRIVATE-ACCESS-TOKEN'
9+
end
10+
end
11+
12+
describe 'get_line_item' do
13+
context 'when line_item is found using line_item_id' do
14+
before do
15+
stub_request(:get, 'https://api.hubapi.com/crm/v3/objects/line_items/4118976207')
16+
.with(
17+
headers: {
18+
'Accept' => '*/*',
19+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
20+
'Authorization' => 'Bearer YOUR-PRIVATE-ACCESS-TOKEN',
21+
'Content-Type' => 'application/json',
22+
'User-Agent' => 'Ruby'
23+
}
24+
)
25+
.to_return(status: 200, body: load_line_item_json('get_line_item'), headers: {})
26+
end
27+
28+
let(:response) { described_class.get_line_item('4118976207') }
29+
30+
it 'returns a line item
31+
' do
32+
expect(response[:id]).to eq '4118976207'
33+
expect(response[:properties][:amount]).to eq '215.460'
34+
expect(response[:properties][:quantity]).to eq '3'
35+
expect(response[:properties][:hs_product_id]).to eq '1175864298'
36+
end
37+
end
38+
end
39+
40+
describe 'get_line_items' do
41+
context 'when line items are found' do
42+
before do
43+
stub_request(:get, 'https://api.hubapi.com/crm/v3/objects/line_items')
44+
.with(
45+
headers: {
46+
'Accept' => '*/*',
47+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
48+
'Authorization' => 'Bearer YOUR-PRIVATE-ACCESS-TOKEN',
49+
'Content-Type' => 'application/json',
50+
'User-Agent' => 'Ruby'
51+
}
52+
)
53+
.to_return(status: 200, body: load_line_item_json('get_line_items'), headers: {})
54+
end
55+
56+
let(:response) { described_class.get_line_items }
57+
58+
it 'returns a list of line items' do
59+
results = response[:results]
60+
expect(results.count).to eq 2
61+
end
62+
end
63+
end
64+
65+
describe 'create_line_item' do
66+
before do
67+
stub_request(:post, 'https://api.hubapi.com/crm/v3/objects/line_items')
68+
.with(
69+
body: 'properties%5Bname%5D=Blue%20Jeans&properties%5Bhs_product_id%5D=1175864298&properties%5Bprice%5D=71.82&properties%5Bquantity%5D=3',
70+
headers: {
71+
'Accept' => '*/*',
72+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
73+
'Authorization' => 'Bearer YOUR-PRIVATE-ACCESS-TOKEN',
74+
'Content-Type' => 'application/json',
75+
'User-Agent' => 'Ruby'
76+
}
77+
).to_return(status: 200, body: load_line_item_json('create_line_item'), headers: {})
78+
end
79+
80+
let(:body) do
81+
{ properties: { name: 'Blue Jeans', hs_product_id: '1175864298', price: '71.82', quantity: '3' } }
82+
end
83+
84+
let(:response) { described_class.create_line_item(body) }
85+
86+
it 'returns a line item' do
87+
expect(response[:id]).to eq '4118976207'
88+
expect(response[:properties][:price]).to eq '71.82'
89+
expect(response[:properties][:quantity]).to eq '3'
90+
expect(response[:properties][:amount]).to eq '215.460'
91+
expect(response[:properties][:name]).to eq 'Blue Jeans'
92+
end
93+
end
94+
95+
describe 'update_line_item' do
96+
context 'when line item is found using line_item_id' do
97+
before do
98+
stub_request(:patch, 'https://api.hubapi.com/crm/v3/objects/line_items/4120050126')
99+
.with(
100+
body: 'properties%5Bquantity%5D=2',
101+
headers: {
102+
'Accept' => '*/*',
103+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
104+
'Authorization' => 'Bearer YOUR-PRIVATE-ACCESS-TOKEN',
105+
'Content-Type' => 'application/json',
106+
'User-Agent' => 'Ruby'
107+
}
108+
)
109+
.to_return(status: 200, body: load_line_item_json('update_line_item'), headers: {})
110+
end
111+
112+
let(:body) do
113+
{ properties: { quantity: '2' } }
114+
end
115+
116+
let(:response) { described_class.update_line_item(4_120_050_126, body) }
117+
118+
it 'returns a line item' do
119+
expect(response[:id]).to eq '4120050126'
120+
expect(response[:properties][:quantity]).to eq '2'
121+
expect(response[:properties][:price]).to eq '71.82'
122+
expect(response[:properties][:amount]).to eq '143.640'
123+
end
124+
end
125+
end
126+
127+
describe 'delete_line_item' do
128+
let(:success) { { status: 'success' } }
129+
130+
context 'when line item is found using line_item_id' do
131+
before do
132+
stub_request(:delete, 'https://api.hubapi.com/crm/v3/objects/line_items/4120050126')
133+
.with(
134+
headers: {
135+
'Accept' => '*/*',
136+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
137+
'Authorization' => 'Bearer YOUR-PRIVATE-ACCESS-TOKEN',
138+
'Content-Type' => 'application/json',
139+
'User-Agent' => 'Ruby'
140+
}
141+
)
142+
.to_return(status: 204, body: '', headers: {})
143+
end
144+
145+
let(:response) { described_class.delete_line_item('4120050126') }
146+
147+
it 'returns no content' do
148+
expect(response).to eq success
149+
end
150+
end
151+
end
152+
153+
describe 'errors' do
154+
context "when trying to update a line item that doesn't exist" do
155+
before do
156+
stub_request(:patch, 'https://api.hubapi.com/crm/v3/objects/line_items/123')
157+
.with(
158+
body: 'properties%5Bquantity%5D=3',
159+
headers: {
160+
'Accept' => '*/*',
161+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
162+
'Authorization' => 'Bearer YOUR-PRIVATE-ACCESS-TOKEN',
163+
'Content-Type' => 'application/json',
164+
'User-Agent' => 'Ruby'
165+
}
166+
).to_return(status: 200, body: load_line_item_json('not_found'), headers: {})
167+
end
168+
169+
let(:body) do
170+
{ properties: { quantity: '3' } }
171+
end
172+
173+
it 'raises a HubspotApiError' do
174+
expect do
175+
described_class.update_line_item(123, body)
176+
end.to raise_error(EasyHubspot::HubspotApiError)
177+
end
178+
end
179+
180+
context 'when hubspot api returns a 404 reponse code' do
181+
before do
182+
stub_request(:get, 'https://api.hubapi.com/crm/v3/objects/line_items/404')
183+
.with(
184+
headers: {
185+
'Accept' => '*/*',
186+
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
187+
'Authorization' => 'Bearer YOUR-PRIVATE-ACCESS-TOKEN',
188+
'Content-Type' => 'application/json',
189+
'User-Agent' => 'Ruby'
190+
}
191+
)
192+
.to_return(status: 404, body: nil, headers: {})
193+
end
194+
195+
let(:response) { described_class.get_line_item('404') }
196+
197+
it 'returns a 404 status error message' do
198+
expect(response).to eq({ status: 'error', message: '404 Not Found' })
199+
end
200+
end
201+
end
202+
end

0 commit comments

Comments
 (0)