tango-api-client 0.1.2 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88450e3c1efb08e7e580777f396ba0204c799c2085f00c9fa0aaa3e8bb88ba3b
4
- data.tar.gz: 3f293079d5ca3722ef14c6a25a605e86abfe3e69ca42618e7ee0ab94900c41e3
3
+ metadata.gz: f7475c292cfcb29d85404d80d86cb94f47c4d0497dcfd97743366be0af176933
4
+ data.tar.gz: 69d61b9a8d860fd5734557d61aec61ae8d969634803c3a97e87f23db9ead35e1
5
5
  SHA512:
6
- metadata.gz: 58cf36892477434d0e0aad46e2e89dfe82cd87e450d6644693d2f710f4117670c9d995297c0a0589ba605dc479458123943911bd12491cfd308a3d9ed1b30d7f
7
- data.tar.gz: 1d183742be3c3599f7df9e7fa5000e65f6f260ad99cd1e5bb77d8335ac2d862b75019bad430ee96d0efcdd459865056ebbc7791133d1a86b9891f9acc4d4e939
6
+ metadata.gz: 1243790a92ce6dba4870e506fb86e00a9436bf7c9c6e206e6d7d908b7011a0a06139ee0ba2decd3f6d0b4dd1341bd856484b66c7ad252886b88f091cb0faebc3
7
+ data.tar.gz: 6205cc363c7e1d2ef985b31fedb377a53c242d9d7339568d1bb5480be0aa8aecfc249fb1b71dc3b05efc4d208ef8e65311c405cb40244f31b0029404fa2262be
data/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  ## [Unreleased]
6
6
  - Nothing yet.
7
7
 
8
+ ## [0.2.0] - 2026-11-09
9
+ - Add Webhooks resource for subscription management: `create`, `list`, `get`, `update`, `delete`, `renew`
10
+ - Add webhook event history and redelivery: `events` (cursor paging, revision range) and `replay` (`fromRevision`/`toRevision` query params)
11
+ - Add `test` to trigger Tango's test deliveries (`ConnectivityTest`, or an event type such as `ItemAvailability` in the sandbox)
12
+ - `Resources::Base#post_without_body` for POST action endpoints that take only path and query parameters
13
+
8
14
  ## [0.1.2] - 2025-11-12
9
15
  - Add Brand Categories resource: `get` method for `/brandCategories` endpoint
10
16
 
data/README.md CHANGED
@@ -115,6 +115,19 @@ client.orders.create(
115
115
 
116
116
  # Resend with Idempotency-Key (prevents duplicate resends)
117
117
  client.orders.resend('ORDER-123', idempotency_key: idempotency)
118
+
119
+ # Webhook subscriptions (HMAC-signed ItemAvailability events)
120
+ sub = client.webhooks.create(
121
+ url: 'https://example.com/v1/tango/webhooks',
122
+ eventTypes: ['ItemAvailability'],
123
+ headers: [{ name: 'X-Subscription-Generation', value: '1' }],
124
+ payloadVerificationMethod: 'HMAC',
125
+ hmacSharedSecretKey: Base64.strict_encode64(SecureRandom.bytes(32))
126
+ )
127
+ client.webhooks.renew(sub['webhookId']) # => { 'webhookId', 'expiresAt' }
128
+ client.webhooks.events(sub['webhookId'], fromRevision: 0, maxResults: 100)
129
+ client.webhooks.replay(sub['webhookId'], from_revision: 0) # redeliver up to 3 months back
130
+ client.webhooks.test(sub['webhookId'], 'ItemAvailability') # sandbox only
118
131
  ```
119
132
 
120
133
  ### Resources overview
@@ -130,6 +143,7 @@ client.orders.resend('ORDER-123', idempotency_key: idempotency)
130
143
  | Status | `Tango::Api::Resources::Status` | `get` | GET `/status` |
131
144
  | Exchange Rates | `Tango::Api::Resources::ExchangeRates` | `get(params = {})`, `get_for_utid(utid, params = {})` | GET `/exchangeRates`; GET `/exchangeRates/{utid}` |
132
145
  | Choice Products | `Tango::Api::Resources::ChoiceProducts` | `get(params = {})`, `get_for_utid(utid)`, `catalog(choice_product_utid, params = {})` | GET `/choiceProducts`; GET `/choiceProducts/{utid}`; GET `/choiceProducts/{utid}/catalog` |
146
+ | Webhooks | `Tango::Api::Resources::Webhooks` | `create(body)`, `list(params = {})`, `get(webhook_id)`, `update(webhook_id, body)`, `delete(webhook_id)`, `renew(webhook_id)`, `events(webhook_id, params = {})`, `replay(webhook_id, from_revision:, to_revision: nil)`, `test(webhook_id, test_name)` | POST/GET `/webhooks`; GET/PATCH/DELETE `/webhooks/{webhookId}`; POST `/webhooks/{webhookId}/renew`; GET `/webhooks/{webhookId}/events`; POST `/webhooks/{webhookId}/replay`; POST `/webhooks/{webhookId}/tests/{testName}` |
133
147
 
134
148
  ### Smoke test
135
149
 
@@ -3,7 +3,7 @@
3
3
  module Tango
4
4
  module Api
5
5
  class Client
6
- VERSION = "0.1.2"
6
+ VERSION = "0.2.0"
7
7
  end
8
8
  end
9
9
  end
@@ -20,6 +20,7 @@ require_relative "resources/status"
20
20
  require_relative "resources/exchange_rates"
21
21
  require_relative "resources/choice_products"
22
22
  require_relative "resources/brand_categories"
23
+ require_relative "resources/webhooks"
23
24
 
24
25
  module Tango
25
26
  module Api
@@ -83,6 +84,10 @@ module Tango
83
84
  Resources::BrandCategories.new(@conn)
84
85
  end
85
86
 
87
+ def webhooks
88
+ Resources::Webhooks.new(@conn)
89
+ end
90
+
86
91
  private
87
92
 
88
93
  def build_connection
@@ -53,6 +53,19 @@ module Tango
53
53
  raise map_faraday_error(e)
54
54
  end
55
55
 
56
+ # POST with no request body, for action endpoints that take only path and query
57
+ # parameters (e.g. webhook renew, replay, tests). Empty or non-JSON success bodies
58
+ # such as "OK" are returned as {} or { "raw" => body }.
59
+ def post_without_body(path, params = nil)
60
+ response = @conn.post(path) { |req| req.params.update(params) if params }
61
+ ensure_success_or_raise(response)
62
+ return {} if response.body.to_s.strip.empty?
63
+
64
+ parse_json_response(response)
65
+ rescue Faraday::Error => e
66
+ raise map_faraday_error(e)
67
+ end
68
+
56
69
  def delete_json(path)
57
70
  response = @conn.delete(path)
58
71
  ensure_success_or_raise(response)
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tango
4
+ module Api
5
+ module Resources
6
+ # Webhook subscriptions: create, manage, renew, and read or redeliver past events.
7
+ class Webhooks < Base
8
+ # POST /webhooks
9
+ # Body keys: url (required), eventTypes and/or categories, headers ([{ name:, value: }]),
10
+ # payloadVerificationMethod ("HMAC", "X509" or "NONE"), hmacSharedSecretKey (base64),
11
+ # signingCertificate (base64).
12
+ # Returns { "webhookId" => ..., "expiresAt" => ... }.
13
+ def create(body)
14
+ post_json("webhooks", body)
15
+ end
16
+
17
+ # GET /webhooks
18
+ # Params may include: prevCursor, nextCursor, maxResults (default 10, max 200), url,
19
+ # headerName, headerValue, categories, eventTypes, expiresAtFrom/To, createdAtFrom/To.
20
+ def list(params = {})
21
+ get_json("webhooks", params)
22
+ end
23
+
24
+ # GET /webhooks/{webhookId}
25
+ def get(webhook_id)
26
+ get_json("webhooks/#{webhook_id}")
27
+ end
28
+
29
+ # PATCH /webhooks/{webhookId}
30
+ # Body keys as for #create; all optional.
31
+ def update(webhook_id, body)
32
+ patch_json("webhooks/#{webhook_id}", body)
33
+ end
34
+
35
+ # DELETE /webhooks/{webhookId}
36
+ def delete(webhook_id)
37
+ delete_json("webhooks/#{webhook_id}")
38
+ end
39
+
40
+ # POST /webhooks/{webhookId}/renew
41
+ # Returns { "webhookId" => ..., "expiresAt" => ... }.
42
+ def renew(webhook_id)
43
+ post_without_body("webhooks/#{webhook_id}/renew")
44
+ end
45
+
46
+ # GET /webhooks/{webhookId}/events
47
+ # Params may include: fromRevision, toRevision, prevCursor, nextCursor, maxResults.
48
+ # Returns a page: { "items" => [...], "nextCursor", "nextPageAvailable", ... }.
49
+ def events(webhook_id, params = {})
50
+ get_json("webhooks/#{webhook_id}/events", params)
51
+ end
52
+
53
+ # POST /webhooks/{webhookId}/replay?fromRevision=&toRevision=
54
+ # Redelivers past events (up to three months old). Omitting to_revision replays
55
+ # through the latest event.
56
+ def replay(webhook_id, from_revision:, to_revision: nil)
57
+ params = { fromRevision: from_revision }
58
+ params[:toRevision] = to_revision unless to_revision.nil?
59
+ post_without_body("webhooks/#{webhook_id}/replay", params)
60
+ end
61
+
62
+ # POST /webhooks/{webhookId}/tests/{testName}
63
+ # testName: ConnectivityTest (any environment) or, in the sandbox only, an event type
64
+ # such as ItemAvailability.
65
+ def test(webhook_id, test_name)
66
+ post_without_body("webhooks/#{webhook_id}/tests/#{test_name}")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tango-api-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puneeth Kumar DN
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-11-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -96,6 +95,7 @@ files:
96
95
  - lib/tango/api/resources/funds.rb
97
96
  - lib/tango/api/resources/orders.rb
98
97
  - lib/tango/api/resources/status.rb
98
+ - lib/tango/api/resources/webhooks.rb
99
99
  - sig/tango/api/client.rbs
100
100
  homepage: https://github.com/Engagedly-Inc/tango-api-client
101
101
  licenses:
@@ -105,7 +105,6 @@ metadata:
105
105
  source_code_uri: https://github.com/Engagedly-Inc/tango-api-client
106
106
  changelog_uri: https://github.com/Engagedly-Inc/tango-api-client/blob/master/CHANGELOG.md
107
107
  rubygems_mfa_required: 'true'
108
- post_install_message:
109
108
  rdoc_options: []
110
109
  require_paths:
111
110
  - lib
@@ -120,8 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
119
  - !ruby/object:Gem::Version
121
120
  version: '0'
122
121
  requirements: []
123
- rubygems_version: 3.3.7
124
- signing_key:
122
+ rubygems_version: 3.6.9
125
123
  specification_version: 4
126
124
  summary: Ruby client for Tango (BHN) API with dual auth and robust errors.
127
125
  test_files: []