giftbit 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/.gitignore +18 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +7 -0
- data/fixtures/vcr_cassettes/account-invalid.yml +54 -0
- data/fixtures/vcr_cassettes/account-valid.yml +61 -0
- data/fixtures/vcr_cassettes/campaign-all.yml +238 -0
- data/fixtures/vcr_cassettes/campaign-by-id.yml +295 -0
- data/fixtures/vcr_cassettes/create_gift.yml +60 -0
- data/fixtures/vcr_cassettes/funds.yml +66 -0
- data/fixtures/vcr_cassettes/get_links.yml +120 -0
- data/fixtures/vcr_cassettes/gifts.yml +80 -0
- data/fixtures/vcr_cassettes/marketplace-all.yml +60 -0
- data/fixtures/vcr_cassettes/marketplace-by-vendor.yml +60 -0
- data/fixtures/vcr_cassettes/regions-all.yml +60 -0
- data/fixtures/vcr_cassettes/request-200.yml +60 -0
- data/fixtures/vcr_cassettes/request-400.yml +57 -0
- data/fixtures/vcr_cassettes/request-500.yml +56 -0
- data/fixtures/vcr_cassettes/resend_gift.yml +173 -0
- data/fixtures/vcr_cassettes/vendors-all.yml +60 -0
- data/giftbit.gemspec +28 -0
- data/lib/giftbit.rb +78 -0
- data/lib/giftbit/base.rb +99 -0
- data/lib/giftbit/version.rb +3 -0
- data/spec/giftbit_spec.rb +284 -0
- metadata +172 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://testbedapp.giftbit.com/papi/v1/marketplace/vendors
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- application/json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip, deflate
|
14
|
+
User-Agent:
|
15
|
+
- rest-client/2.0.0 (darwin16.3.0 x86_64) ruby/2.4.0p0
|
16
|
+
Authorization:
|
17
|
+
- Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJTSEEyNTYifQ==.TFdWYU5VTVhzK3JFaVhHT2p5VDRHNFRsUk1ybnk2V2E4TDNDSW5meEdXdTVERUJNeHlDTEU1K216Qk1hb3Q4QlZaTVJvWndjR2ZoS01xc3gzdnZMUHdQckN1Z0kreG9rYVF3c1JjUjNkNlNLVmROQTlJb0hvMmpHdkx2REh3NXE=.cu7N3bPxGg8fPxFsIUcjyOISwn+29YpB0l7YDHwkMDg=
|
18
|
+
Content-Type:
|
19
|
+
- application/json
|
20
|
+
Host:
|
21
|
+
- testbedapp.giftbit.com
|
22
|
+
response:
|
23
|
+
status:
|
24
|
+
code: 200
|
25
|
+
message: OK
|
26
|
+
headers:
|
27
|
+
Content-Type:
|
28
|
+
- application/json;charset=UTF-8
|
29
|
+
Content-Length:
|
30
|
+
- '6443'
|
31
|
+
Connection:
|
32
|
+
- keep-alive
|
33
|
+
Cache-Control:
|
34
|
+
- no-cache="set-cookie"
|
35
|
+
- private, max-age=60
|
36
|
+
Date:
|
37
|
+
- Tue, 14 Feb 2017 19:41:58 GMT
|
38
|
+
Expires:
|
39
|
+
- Tue, 14 Feb 2017 19:42:59 GMT
|
40
|
+
Last-Modified:
|
41
|
+
- Tue, 14 Feb 2017 19:41:59 GMT
|
42
|
+
P3p:
|
43
|
+
- CP="Giftbit"
|
44
|
+
Server:
|
45
|
+
- Apache-Coyote/1.1
|
46
|
+
X-Frame-Options:
|
47
|
+
- SAMEORIGIN
|
48
|
+
X-Cache:
|
49
|
+
- Miss from cloudfront
|
50
|
+
Via:
|
51
|
+
- 1.1 84760d59a7d324aa7adc2bf9d8f7fd4b.cloudfront.net (CloudFront)
|
52
|
+
X-Amz-Cf-Id:
|
53
|
+
- eB12IajU_EKqKyvMESo_-rMEv8hflBnPBkHRf6R1iGvGRN3O1IRR-g==
|
54
|
+
body:
|
55
|
+
encoding: ASCII-8BIT
|
56
|
+
string: !binary |-
|
57
|
+
eyJ2ZW5kb3JzIjpbeyJpZCI6ODYsIm5hbWUiOiJBbWF6b24uY2EiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8xNDEvYW1hem9uY2EtOWNiMTRjZWYtOGI3OC00YzE1LWJmZDYtMmRhNDFiNDk1Y2E0LmpwZyIsImRpc2NsYWltZXIiOiIqQW1hem9uLmNhIEdpZnQgQ2VydGlmaWNhdGVzIChcIkdDc1wiKSBzb2xkIGJ5IEtpaW5kIEluYy4gREJBIEdpZnRiaXQsIGFuIGF1dGhvcml6ZWQgYW5kIGluZGVwZW5kZW50IHJlc2VsbGVyIG9mIEFtYXpvbi5jYSBHaWZ0IENlcnRpZmljYXRlcy4gR0NzIGFyZSByZWRlZW1hYmxlIG9ubHkgZm9yIGVsaWdpYmxlIHByb2R1Y3RzIG9uIEFtYXpvbi5jYS4gUmV0dXJuIHBvbGljaWVzIGZvciBwcm9kdWN0cyBhcmUgYXZhaWxhYmxlIG9uIEFtYXpvbi5jYS4gRXhjZXB0IGFzIHJlcXVpcmVkIGJ5IGxhdywgR0NzIGNhbm5vdCBiZSByZWxvYWRlZCwgcmVzb2xkLCB0cmFuc2ZlcnJlZCBmb3IgdmFsdWUsIHJlZGVlbWVkIGZvciBjYXNoIG9yIGFwcGxpZWQgdG8gYW55IG90aGVyIGFjY291bnQuIFRvIHZpZXcgYSBHQyBiYWxhbmNlIG9yIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHlvdXIgR0MsIHZpc2l0IFwiWW91ciBBY2NvdW50XCIgb24gQW1hem9uLmNhIG9yIGNvbnRhY3QgdXMgYXQgd3d3LmFtYXpvbi5jYS9jb250YWN0LXVzLiBHQ3MgY2Fubm90IGJlIHVzZWQgdG8gcHVyY2hhc2Ugb3RoZXIgR0NzLiBBbWF6b24gaXMgbm90IHJlc3BvbnNpYmxlIGlmIGEgR0MgaXMgbG9zdCwgc3RvbGVuLCBkZXN0cm95ZWQgb3IgdXNlZCB3aXRob3V0IHBlcm1pc3Npb24uIEZvciBjb21wbGV0ZSB0ZXJtcyBhbmQgY29uZGl0aW9ucywgc2VlIHd3dy5hbWF6b24uY2EvZ2MtbGVnYWwuIEdDcyBhcmUgaXNzdWVkIGJ5IEFtYXpvbi5jb20uY2EsIEluYy4sIGEgRGVsYXdhcmUgY29ycG9yYXRpb24uIEFsbCBBbWF6b24gwq4sIOKEoiAmIMKpIGFyZSBJUCBvZiBBbWF6b24uY29tLCBJbmMuIG9yIGl0cyBhZmZpbGlhdGVzLiBObyBleHBpcmF0aW9uIGRhdGUgb3Igc2VydmljZSBmZWVzLiJ9LHsiaWQiOjc5LCJuYW1lIjoiQW1hem9uIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vODAvYW1hem9uLTMwZWM4ZjdhLTYxNmEtNDZhYS1hYTNmLWNhZmJjYTEwYmRkYy5qcGciLCJkaXNjbGFpbWVyIjoiQW1hem9uLmNvbSBHaWZ0IENhcmRzIChcIkdDc1wiKSBzb2xkIGJ5IEtpaW5kIEluYy4sIGFuIGF1dGhvcml6ZWQgYW5kIGluZGVwZW5kZW50IHJlc2VsbGVyIG9mIEFtYXpvbi5jb20gR2lmdCBDYXJkcy4gRXhjZXB0IGFzIHJlcXVpcmVkIGJ5IGxhdywgR0NzIGNhbm5vdCBiZSB0cmFuc2ZlcnJlZCBmb3IgdmFsdWUgb3IgcmVkZWVtZWQgZm9yIGNhc2guIEdDcyBtYXkgYmUgdXNlZCBvbmx5IGZvciBwdXJjaGFzZXMgb2YgZWxpZ2libGUgZ29vZHMgYXQgQW1hem9uLmNvbSBvciBjZXJ0YWluIG9mIGl0cyBhZmZpbGlhdGVkIHdlYnNpdGVzLiBGb3IgY29tcGxldGUgdGVybXMgYW5kIGNvbmRpdGlvbnMsIHNlZSB3d3cuYW1hem9uLmNvbS9nYy1sZWdhbC4gR0NzIGFyZSBpc3N1ZWQgYnkgQUNJIEdpZnQgQ2FyZHMsIEluYy4sIGEgV2FzaGluZ3RvbiBjb3Jwb3JhdGlvbi4gwqkswq4s4oSiIEFtYXpvbi5jb20gSW5jLiBhbmQvb3IgaXRzIGFmZmlsaWF0ZXMsIDIwMTMuIE5vIGV4cGlyYXRpb24gZGF0ZSBvciBzZXJ2aWNlIGZlZXMuIn0seyJpZCI6MTMxLCJuYW1lIjoiV2FsbWFydCIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzIyNi9XYWxtYXJ0bG9nby04MzIwZWJlMS1hNWJhLTQxYmItYTVlMC0wYjM4OGMwZDY4MzMucG5nIn0seyJpZCI6ODAsIm5hbWUiOiJpVHVuZXMgQ2FuYWRhIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTA1L2lUdW5lczItZTU3MmVhY2ItZTU1MC00MmMxLTk5MWItZGU0YmJiNjQ3NWYwLmpwZyIsImRpc2NsYWltZXIiOiJNYWMgYW5kIE9TIFggYXJlIHRyYWRlbWFya3Mgb2YgQXBwbGUgSW5jLiwgcmVnaXN0ZXJlZCBpbiB0aGUgVS5TLiBhbmQgb3RoZXIgY291bnRyaWVzLiBpVHVuZXMgaXMgYSByZWdpc3RlcmVkIHRyYWRlbWFyayBvZiBBcHBsZSBJbmMuLCBBbGwgcmlnaHRzIHJlc2VydmVkLiJ9LHsiaWQiOjgxLCJuYW1lIjoiaVR1bmVzIFVTQSIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzEwNi9pVHVuZXMyLTQ4OTdjODJhLTViZGQtNDI3Ni04NTUyLTU0MzQzOGU3MDkxYy5qcGciLCJkaXNjbGFpbWVyIjoiTWFjIGFuZCBPUyBYIGFyZSB0cmFkZW1hcmtzIG9mIEFwcGxlIEluYy4sIHJlZ2lzdGVyZWQgaW4gdGhlIFUuUy4gYW5kIG90aGVyIGNvdW50cmllcy4gaVR1bmVzIGlzIGEgcmVnaXN0ZXJlZCB0cmFkZW1hcmsgb2YgQXBwbGUgSW5jLiwgQWxsIHJpZ2h0cyByZXNlcnZlZC4ifSx7ImlkIjoxMDgsIm5hbWUiOiJUYXJnZXQiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8xNjEvdGFyZ2V0LTNhOTlmYjJkLTAwZTgtNDg0ZC1hMjJmLTRlOTY2MTZjMDM0Yi5QTkciLCJkaXNjbGFpbWVyIjoiVGhlIEJ1bGxzZXllIERlc2lnbiwgVGFyZ2V0IGFuZCBUYXJnZXQgR2lmdENhcmRzwq4gYXJlIHJlZ2lzdGVyZWQgdHJhZGVtYXJrcyBvZiBUYXJnZXQgQnJhbmRzLCBJbmMuIFRlcm1zIGFuZCBjb25kaXRpb25zIGFyZSBhcHBsaWVkIHRvIGdpZnQgY2FyZHMuIFRhcmdldCBpcyBub3QgYSBwYXJ0aWNpcGF0aW5nIHBhcnRuZXIgaW4gb3Igc3BvbnNvciBvZiB0aGlzIG9mZmVyLiJ9LHsiaWQiOjY5LCJuYW1lIjoiVGhlIEdhcCIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzcwL2dhcC1iODI0ZmEyMC04MzMwLTQ5NjEtYTIxNy0xOTA4NjY0MWUxYzMucG5nIn0seyJpZCI6MSwibmFtZSI6IjIlIEphenogQ29mZmVlIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMi8ycGVyZ2lmdGRmYTIwMDVmNmFiYzQ5ODVhZGIxODllMmZhNDBjOGEyLTRmM2JlMzllLTQyMGEtNDgwMy04Y2U3LWJlNmZiNDZkOTk0Zi5wbmcifSx7ImlkIjoxMDEsIm5hbWUiOiJNb250YW5hJ3MgQ29va2hvdXNlIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTU1L21vbnRhbmFzLThiNWRmNmUzLWNhOWUtNDFiNi1hZTliLWUxZjc4MWUxYTFjNS5wbmcifSx7ImlkIjo5OSwibmFtZSI6IktlbHNleSdzIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTUwL0tlbHNleXNsb2dvLWFhMWIyMzM3LTUzZjItNDk4ZS1hYTI1LWE0OTY2MmZiN2JjNy5wbmcifSx7ImlkIjo5NiwibmFtZSI6IkhhcnZleSdzIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTUxL0hhcnZleXNMb2dvLTMzYjk4MTJjLWQ5MjMtNDU2MC04ODJkLWY1ZjhmM2JmZWIxMC5wbmcifSx7ImlkIjoxNDAsIm5hbWUiOiJUZXN0IE1lcmNoYW50IiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vTWVyY2gvTWVyY2hhbnRJY29uLWUwNjBmNGQxLWMyMGUtNDc0Ni1iYzc1LTQ5NjgwMDY3MDAzMS5wbmcifSx7ImlkIjo5MCwibmFtZSI6IkNpbmVwbGV4IE9kZW4iLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8xNTYvQ2luZXBsZXgzMDBieTMwMC0yZWE4MWY2Ni0zNTBkLTQ0YWEtYjM0NC0zYzRhNjIwZWRiODMucG5nIn0seyJpZCI6MTM1LCJuYW1lIjoiVmlzYSIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzMxMS9WaXJ0dWFsUmV3YXJkMWY3MzA1YTViY2NmNTQ4NmFhMTJiZGM2OTllMTBkMDU5LWNiN2M1ODg1LTZjYzctNDEzZi1hZjg1LTYwNDhhNmEzNjg0Yy5wbmcifSx7ImlkIjo4NCwibmFtZSI6IlN0YXJidWNrcyIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzEyMS9zdGFyYnVja3MtZjIyZGI1NjQtYWMzYS00YWUxLTljODgtOThlNzlmN2U1NzAyLnBuZyJ9LHsiaWQiOjEyMSwibmFtZSI6IlNvY2lsb2dpY2EgUmV0YWlsZXIxIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTkyL3lvdXJsb2dvaGVyZTI3LWEyNDhmMTE2LWZkMzUtNDg5NS04YjllLWVlYTUzNjk2ZTI4ZC5wbmciLCJkaXNjbGFpbWVyIjoiVGhpcyBpcyBhIGRpc2NsYWltZXIgZm9yIGEgdGVzdGJlZCBnaWZ0In0seyJpZCI6NzYsIm5hbWUiOiJTcGEgV2VlayIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzc3L3NwYXdlZWstOTBmZjQ4ZTQtNTYwMS00ZmRlLWIwYTUtYWIzNjlhNDkzODNhLnBuZyJ9LHsiaWQiOjUwLCJuYW1lIjoiUGFwYSBHaW5vJ3MgUGl6emVyaWEiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS81MS9wYXBhZ2lub3MtYzIyY2M3M2QtYzE1Yy00NjMwLWI3OTctYTc0NWQzNTUzOWUzLnBuZyJ9LHsiaWQiOjEwMiwibmFtZSI6Ik5pa2UiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8xNTgvbmlrZS0yZmZmMTQwNC0xMWVjLTQxMTEtYjgwNi0xZmM1ODEzZjQ5OTcucG5nIiwiZGlzY2xhaW1lciI6IlRoZSBTd29vc2ggZGVzaWduIGlzIGEgcmVnaXN0ZXJlZCB0cmFkZW1hcmsgb2YgTklLRS4gQWxsIHJpZ2h0cyByZXNlcnZlZC4gTklLRSBpcyBub3QgYSBwYXJ0aWNpcGFudCBpbiBvciBzcG9uc29yIG9mIHRoaXMgcHJvbW90aW9uLiJ9LHsiaWQiOjEwMCwibmFtZSI6Ik1pbGVzdG9uZXMgR3JpbGwgYW5kIEJhciIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzE1NC9taWxlc3RvbmVzMS00OTk4NzZkYS03NjZmLTQyZGMtYTZmZi1iZDNmYjVhNzBkZjAuanBnIn0seyJpZCI6OTcsIm5hbWUiOiJIb21lIERlcG90IiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTUyL2hvbWVkZXBvdC1hOTMwZjhiNy03NGIxLTQ3N2QtODk1Ni0xNjlhOGYxMzcxOTYucG5nIn0seyJpZCI6OTUsIm5hbWUiOiJHbG9iYWwgSG90ZWwgQ2FyZCBwb3dlcmVkIGJ5IE9yYml0eiIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzE0Ni9naGNwb3Nob3Jpem9udGFsLTIwNzJiOWY5LTQ0YjgtNDliMi1iNGQ0LWQ0MjE4OThjMmQ4ZS5wbmcifSx7ImlkIjoxMzcsIm5hbWUiOiJNYXN0ZXJDYXJkIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMzI2L21hc3RlcmNhcmRsb2dvMjk3NjRjOTU2OTRhOWNkYWI0NzA2OTBhMDE2NWE3MTExMDViZS1lYzZiY2JmMC1jODBjLTQzZDctYWU0Ny1mYzgyYjY4ZGVkNzkuanBnIiwiZGlzY2xhaW1lciI6IkRpc2NsYWltZXIsIGlmIG5lZWRlZCwgZ29lcyBoZXJlIn0seyJpZCI6MTMyLCJuYW1lIjoiRWFzdCBTaWRlIE1hcmlvJ3MiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8yNDYvRWFzdFNpZGVNYXJpb3Nsb2dvMjAxMy1mZjc4MThjYS1mZDRlLTQ0MWItOWEyNS00NTVhNjdiMmYxYTQucG5nIn0seyJpZCI6MTI4LCJuYW1lIjoiTWVyY2hhbnQgRGVtbyIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tL0dGQlQxMjgvTWVyY2hhbnRJY29uMngtMWM2MzBmZGItYWJmZC00YjAwLTk0OGMtYzRiZGQzYmMwZDZkLnBuZyJ9LHsiaWQiOjExMiwibmFtZSI6IlRhcmdldCIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzE4NC90YXJnZXQtZGMyN2ViYzAtZWFmMS00NmRiLWIzMmYtNDY1Mjg3N2M1MDczLlBORyJ9LHsiaWQiOjEwNywibmFtZSI6IlN3aXNzIENoYWxldCIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzE1Ny9zd2lzcy02ZjU4ZWFhZi1hMzljLTQ3N2MtODZlYS0yYTFkZGNlYTMxYjEucG5nIn1dLCJpbmZvIjp7ImNvZGUiOiJJTkZPX01BUktFVFBMQUNFX1JFVFJJRVZFRF9WRU5ET1JTIiwibmFtZSI6Ik1hcmtldHBsYWNlIFZlbmRvcnMgUmV0cmlldmVkIiwibWVzc2FnZSI6IkEgbGlzdCBvZiBtYXJrZXRwbGFjZSB2ZW5kb3JzIGhhcyBiZWVuIHJldHJpZXZlZC4ifX0=
|
58
|
+
http_version:
|
59
|
+
recorded_at: Tue, 14 Feb 2017 19:41:59 GMT
|
60
|
+
recorded_with: VCR 3.0.3
|
data/giftbit.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'giftbit/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'giftbit'
|
7
|
+
spec.version = Giftbit::VERSION
|
8
|
+
spec.authors = ['Sean Linsley']
|
9
|
+
spec.email = ['sean@modernmsg.com']
|
10
|
+
spec.summary = 'A Ruby gem for the Giftbit API'
|
11
|
+
spec.homepage = 'https://www.github.com/modernmsg/giftbit'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
|
14
|
+
spec.files = `git ls-files`.split($/)
|
15
|
+
spec.test_files = spec.files.grep(/spec\//)
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.required_ruby_version = '>= 2'
|
19
|
+
|
20
|
+
spec.add_dependency 'rest-client'
|
21
|
+
spec.add_dependency 'activesupport'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'rake'
|
24
|
+
spec.add_development_dependency 'rspec'
|
25
|
+
spec.add_development_dependency 'pry'
|
26
|
+
spec.add_development_dependency 'vcr'
|
27
|
+
spec.add_development_dependency 'webmock'
|
28
|
+
end
|
data/lib/giftbit.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'giftbit/version'
|
2
|
+
require 'giftbit/base'
|
3
|
+
|
4
|
+
class Giftbit
|
5
|
+
extend Base
|
6
|
+
include Base
|
7
|
+
|
8
|
+
# Class-level methods only work if you have a single API account. This lets
|
9
|
+
# you instantiate the API for a given account, if you have multiple.
|
10
|
+
def initialize(auth:)
|
11
|
+
fail 'no auths set' unless auths = self.class.auths
|
12
|
+
self.auth = auths.fetch(auth)
|
13
|
+
end
|
14
|
+
|
15
|
+
# This lets you call the same API requests on every account you have.
|
16
|
+
# This is useful e.g. to check the status of every gift in every account.
|
17
|
+
def self.each_auth
|
18
|
+
fail 'no auths set' unless auths
|
19
|
+
auths.each do |name, _|
|
20
|
+
yield new auth: name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
other.is_a?(Giftbit) && auth == other.auth
|
26
|
+
end
|
27
|
+
|
28
|
+
module Methods
|
29
|
+
def account
|
30
|
+
get ''
|
31
|
+
end
|
32
|
+
|
33
|
+
def funds
|
34
|
+
get 'funds'
|
35
|
+
end
|
36
|
+
|
37
|
+
def marketplace(params = {})
|
38
|
+
get 'marketplace', params: params
|
39
|
+
end
|
40
|
+
|
41
|
+
def regions(params = {})
|
42
|
+
get 'marketplace/regions', params: params
|
43
|
+
end
|
44
|
+
|
45
|
+
def vendors(params = {})
|
46
|
+
get 'marketplace/vendors', params: params
|
47
|
+
end
|
48
|
+
|
49
|
+
def campaign(params = {})
|
50
|
+
get 'campaign', params: params
|
51
|
+
end
|
52
|
+
|
53
|
+
def gifts(params = {})
|
54
|
+
get 'gifts', params: params
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_gift(body = {})
|
58
|
+
body[:expiry] ||= (Date.today + 365).to_s
|
59
|
+
|
60
|
+
post 'campaign', body: body
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete_gift(gift_uuid)
|
64
|
+
delete "gifts/#{gift_uuid}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def resend_gift(gift_uuid)
|
68
|
+
put "gifts/#{gift_uuid}", body: {resend: true}
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_links(campaign_id)
|
72
|
+
get "links/#{campaign_id}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
extend Methods
|
77
|
+
include Methods
|
78
|
+
end
|
data/lib/giftbit/base.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'json'
|
3
|
+
require 'rest-client'
|
4
|
+
|
5
|
+
class Giftbit
|
6
|
+
module Base
|
7
|
+
def self.extended(klass)
|
8
|
+
klass.send :cattr_accessor, :endpoint, :auth, :auths
|
9
|
+
klass.endpoint = 'https://testbedapp.giftbit.com/papi/v1/'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(klass)
|
13
|
+
klass.send :attr_accessor, :auth
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_auth
|
17
|
+
fail "auth and auths can't both be set" if inherited_auth && auths?
|
18
|
+
|
19
|
+
if auth = instance? ? self.auth : inherited_auth
|
20
|
+
auth
|
21
|
+
elsif auths?
|
22
|
+
fail 'you must init a new API instance with the auth you want to use'
|
23
|
+
else
|
24
|
+
fail 'you must set an auth token at application boot'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def inherited_auth
|
29
|
+
instance? ? self.class.auth : auth
|
30
|
+
end
|
31
|
+
|
32
|
+
def inherited_endpoint
|
33
|
+
instance? ? self.class.endpoint : endpoint
|
34
|
+
end
|
35
|
+
|
36
|
+
def auths?
|
37
|
+
auths = instance? ? self.class.auths : self.auths
|
38
|
+
auths && auths.any?
|
39
|
+
end
|
40
|
+
|
41
|
+
def instance?
|
42
|
+
self.class == Giftbit
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def default_resource_options
|
48
|
+
{
|
49
|
+
headers: {
|
50
|
+
"Authorization" => "Bearer #{get_auth}",
|
51
|
+
"Accept" => "application/json",
|
52
|
+
"Content-Type" => "json"
|
53
|
+
}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def resource(options = {})
|
58
|
+
RestClient::Resource.new inherited_endpoint, default_resource_options.deep_merge(options)
|
59
|
+
end
|
60
|
+
|
61
|
+
def response(method, resource, options = {})
|
62
|
+
if body = options.delete(:body)
|
63
|
+
json = resource.send(method, JSON.generate(body), options)
|
64
|
+
else
|
65
|
+
json = resource.send(method, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
JSON.parse json
|
69
|
+
rescue RestClient::ExceptionWithResponse => e
|
70
|
+
case e.http_code
|
71
|
+
when 400..499
|
72
|
+
JSON.parse e.response
|
73
|
+
when 500..599
|
74
|
+
{
|
75
|
+
'status' => e.http_code,
|
76
|
+
'error' => { 'message' => e.response }
|
77
|
+
}
|
78
|
+
else
|
79
|
+
raise
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def get(path, request_options = {}, resource_options = {})
|
84
|
+
response :get, resource(resource_options)[path], request_options
|
85
|
+
end
|
86
|
+
|
87
|
+
def delete(path, request_options = {}, resource_options = {})
|
88
|
+
response :delete, resource(resource_options)[path], request_options
|
89
|
+
end
|
90
|
+
|
91
|
+
def post(path, request_options = {}, resource_options = {})
|
92
|
+
response :post, resource(resource_options)[path], request_options
|
93
|
+
end
|
94
|
+
|
95
|
+
def put(path, request_options = {}, resource_options = {})
|
96
|
+
response :put, resource(resource_options)[path], request_options
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'giftbit'
|
2
|
+
require 'vcr'
|
3
|
+
require 'webmock'
|
4
|
+
|
5
|
+
VCR.configure do |config|
|
6
|
+
config.cassette_library_dir = 'fixtures/vcr_cassettes'
|
7
|
+
config.hook_into :webmock
|
8
|
+
config.allow_http_connections_when_no_cassette = true
|
9
|
+
end
|
10
|
+
|
11
|
+
def retry_request(cassette)
|
12
|
+
if cassette.recording?
|
13
|
+
VCR.eject_cassette
|
14
|
+
|
15
|
+
response = yield
|
16
|
+
attempts = 1
|
17
|
+
|
18
|
+
until response || attempts > 12 do
|
19
|
+
sleep 5
|
20
|
+
response = yield
|
21
|
+
attempts += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
VCR.insert_cassette(cassette.name, record: :new_episodes)
|
25
|
+
end
|
26
|
+
|
27
|
+
yield
|
28
|
+
end
|
29
|
+
|
30
|
+
describe Giftbit do
|
31
|
+
let(:auth) { 'eyJ0eXAiOiJKV1QiLCJhbGciOiJTSEEyNTYifQ==.TFdWYU5VTVhzK3JFaVhHT2p5VDRHNFRsUk1ybnk2V2E4TDNDSW5meEdXdTVERUJNeHlDTEU1K216Qk1hb3Q4QlZaTVJvWndjR2ZoS01xc3gzdnZMUHdQckN1Z0kreG9rYVF3c1JjUjNkNlNLVmROQTlJb0hvMmpHdkx2REh3NXE=.cu7N3bPxGg8fPxFsIUcjyOISwn+29YpB0l7YDHwkMDg=' }
|
32
|
+
let :data do
|
33
|
+
{
|
34
|
+
id: "GiftbitGift#{Time.now.utc.to_i}",
|
35
|
+
message: 'Thank you for being an awesome person',
|
36
|
+
subject: 'Present from Community Rewards',
|
37
|
+
contacts: [firstname: 'Sean', lastname: 'Linsley', email: 'sean@modernmsg.com'],
|
38
|
+
marketplace_gifts: [id: 1, price_in_cents: 5000]
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
after do
|
43
|
+
Giftbit.auth = nil
|
44
|
+
Giftbit.auths = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'authentication' do
|
48
|
+
it 'errors without auth' do
|
49
|
+
expect{
|
50
|
+
Giftbit.account
|
51
|
+
}.to raise_error 'you must set an auth token at application boot'
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'errors without auth, but auths set' do
|
55
|
+
Giftbit.auths = {default: auth}
|
56
|
+
|
57
|
+
expect{
|
58
|
+
Giftbit.account
|
59
|
+
}.to raise_error 'you must init a new API instance with the auth you want to use'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'errors for unregistered auth' do
|
63
|
+
Giftbit.auths = {default: auth}
|
64
|
+
|
65
|
+
expect{
|
66
|
+
Giftbit.new(auth: :foo).account
|
67
|
+
}.to raise_error KeyError, 'key not found: :foo'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'errors when both auth and auths are set' do
|
71
|
+
Giftbit.auth = auth
|
72
|
+
Giftbit.auths = {default: auth}
|
73
|
+
|
74
|
+
expect{
|
75
|
+
Giftbit.account
|
76
|
+
}.to raise_error "auth and auths can't both be set"
|
77
|
+
|
78
|
+
expect{
|
79
|
+
Giftbit.new(auth: :default).account
|
80
|
+
}.to raise_error "auth and auths can't both be set"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '.each_auth' do
|
85
|
+
it 'returns each auth' do
|
86
|
+
Giftbit.auths = {default: auth, custom: 'foo'}
|
87
|
+
|
88
|
+
times_called = 0
|
89
|
+
|
90
|
+
Giftbit.each_auth do |api|
|
91
|
+
times_called += 1
|
92
|
+
|
93
|
+
expect(api).to be_an_instance_of Giftbit
|
94
|
+
expect([auth, 'foo']).to include api.auth
|
95
|
+
end
|
96
|
+
|
97
|
+
expect(times_called).to eq 2
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
[:class, :instance].each do |variant|
|
102
|
+
describe "(#{variant})" do
|
103
|
+
before do |example|
|
104
|
+
if variant == :class
|
105
|
+
Giftbit.auth = example.metadata[:invalid_auth] ? 'notavalidtoken' : auth
|
106
|
+
else
|
107
|
+
Giftbit.auths = {default: auth}
|
108
|
+
Giftbit.auths.merge! invalid: 'notavalidtoken' if example.metadata[:invalid_auth]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
let :api do |example|
|
113
|
+
if variant == :class
|
114
|
+
Giftbit
|
115
|
+
else
|
116
|
+
auth = example.metadata[:invalid_auth] ? :invalid : :default
|
117
|
+
Giftbit.new auth: auth
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'requests' do
|
122
|
+
it 'parses JSON for a successful request' do
|
123
|
+
VCR.use_cassette('request-200') do
|
124
|
+
json = api.create_gift(data)
|
125
|
+
|
126
|
+
expect(json['status']).to eql 200
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'parses JSON for a client error' do
|
131
|
+
VCR.use_cassette('request-400') do
|
132
|
+
json = api.create_gift
|
133
|
+
|
134
|
+
expect(json['status']).to eql 422
|
135
|
+
expect(json).to have_key 'error'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'creates JSON for a server error' do
|
140
|
+
VCR.use_cassette('request-500') do
|
141
|
+
json = api.create_gift
|
142
|
+
|
143
|
+
expect(json['status']).to eql 503
|
144
|
+
expect(json.dig('error', 'message')).to include '<html>'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '#account' do
|
150
|
+
it 'returns welcome message with valid credentials' do
|
151
|
+
VCR.use_cassette('account-valid') do
|
152
|
+
res = api.account
|
153
|
+
|
154
|
+
expect(res['info']['name']).to eql 'Credentials are valid'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'returns unauthorized message with invalid credentials', invalid_auth: true do
|
159
|
+
VCR.use_cassette('account-invalid') do
|
160
|
+
res = api.account
|
161
|
+
|
162
|
+
expect(res['text']).to eql 'Unauthorized'
|
163
|
+
expect(res['status']).to eql 401
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#funds' do
|
169
|
+
it 'returns funds info' do
|
170
|
+
VCR.use_cassette('funds') do
|
171
|
+
res = api.funds
|
172
|
+
expect(res['info']['name']).to eql 'Fund information retrieved'
|
173
|
+
expect(res['fundsbycurrency']['USD']['available_in_cents']). to be >= 0
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe '#marketplace' do
|
179
|
+
it 'lists all gifts' do
|
180
|
+
VCR.use_cassette('marketplace-all') do
|
181
|
+
res = api.marketplace
|
182
|
+
|
183
|
+
expect(res['info']['name']).to eql 'Marketplace Gifts Retrieved'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'lists all gifts by vendor' do
|
188
|
+
VCR.use_cassette('marketplace-by-vendor') do
|
189
|
+
res = api.marketplace vendor: 6
|
190
|
+
|
191
|
+
expect(res['total_count']).to be >= 0
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe '#regions' do
|
197
|
+
it 'lists all regions' do
|
198
|
+
VCR.use_cassette('regions-all') do
|
199
|
+
res = api.regions
|
200
|
+
|
201
|
+
expect(res['regions'].count).to eql 4
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe '#vendors' do
|
207
|
+
it 'list all vendors' do
|
208
|
+
VCR.use_cassette('vendors-all') do
|
209
|
+
res = api.vendors
|
210
|
+
|
211
|
+
expect(res['vendors'].count).to eql 27
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#campaign' do
|
217
|
+
it 'list all campaigns created' do
|
218
|
+
VCR.use_cassette('campaign-all') do
|
219
|
+
res = api.campaign
|
220
|
+
|
221
|
+
expect(res['campaigns'].count).to be >= 5
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'can fetch campaign with ID provided' do
|
226
|
+
VCR.use_cassette('campaign-by-id') do
|
227
|
+
gift = api.create_gift(data)
|
228
|
+
|
229
|
+
res = api.campaign(id: gift['info']['id'])
|
230
|
+
|
231
|
+
expect(res['info']['id']).to eql gift['info']['id']
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe '#gifts' do
|
237
|
+
it 'can fetch the gifts for a given campaign' do
|
238
|
+
VCR.use_cassette('gifts') do
|
239
|
+
gifts = api.gifts
|
240
|
+
|
241
|
+
expect(gifts['gifts'].length).to be > 0
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe '#create_gift' do
|
247
|
+
it 'creates a gift' do
|
248
|
+
VCR.use_cassette('create_gift') do
|
249
|
+
gift = api.create_gift(data)
|
250
|
+
|
251
|
+
expect(gift['info']['name']).to eql 'Campaign Created'
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe '#resend_gift' do
|
257
|
+
it 'can re-send an email for a given campaign gift' do
|
258
|
+
VCR.use_cassette('resend_gift') do |cassette|
|
259
|
+
gift = api.create_gift(data)
|
260
|
+
gift = retry_request(cassette) do
|
261
|
+
api.gifts(campaign_uuid: gift['campaign']['uuid'])['gifts'].first
|
262
|
+
end
|
263
|
+
res = api.resend_gift(gift['uuid'])
|
264
|
+
|
265
|
+
expect(res['info']['code']).to eql 'INFO_GIFTS_RESENT'
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
describe '#get_links' do
|
271
|
+
it 'returns link status' do
|
272
|
+
VCR.use_cassette('get_links') do
|
273
|
+
data[:delivery_type] = 'SHORTLINK'
|
274
|
+
gift = api.create_gift(data)
|
275
|
+
|
276
|
+
res = api.get_links(gift['campaign']['id'])
|
277
|
+
|
278
|
+
expect(res['info']['code']).to eql 'INFO_LINKS_GENERATION_IN_PROGRESS'
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|