Skip to content

Commit

Permalink
add usages stream
Browse files Browse the repository at this point in the history
  • Loading branch information
gagalago committed Nov 24, 2022
1 parent caa65b6 commit 6a82e52
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ Messages are written to standard output following the Singer specification. The
- Plans Add Ons
- Subscriptions
- Transactions
- Usages

### Full Table

Expand Down
13 changes: 12 additions & 1 deletion tap_recurly/recurly.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Recurly():
""" Simple wrapper for Recurly. """

def __init__(self, subdomain, api_key, start_date=None, user_agent=None, quota_limit=100):
self.headers = {'Accept': 'application/vnd.recurly.v2018-08-09'}
self.headers = {'Accept': 'application/vnd.recurly.v2021-02-25'}
self.site_id = "subdomain-{subdomain}".format(subdomain=subdomain)
self.user_agent = user_agent
self.start_date = start_date
Expand Down Expand Up @@ -147,6 +147,17 @@ def subscriptions_coupon_redemptions(self, subscription_id, column_name):
for item in self._get_all(url):
yield item

def usages(self, subscription_id, add_on_id, column_name):
url = "sites/{site_id}/subscriptions/{subscription_id}/add_ons/{add_on_id}/usage"
url += "?limit={limit}&sort={column_name}&order=asc"
url = url.format(site_id=self.site_id,
subscription_id=subscription_id,
add_on_id=add_on_id,
limit=self.limit,
column_name=column_name)
for item in self._get_all(url):
yield item

def coupons(self, column_name, bookmark):
url = "sites/{site_id}/coupons"
url += "?limit={limit}&sort={column_name}&begin_time={bookmark}&order=asc"
Expand Down
144 changes: 144 additions & 0 deletions tap_recurly/schemas/usages.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"object": {
"type": "string",
"title": "Object type"
},
"merchant_tag": {
"type": "string",
"description": "Custom field for recording the id in your own system associated with the usage, so you can provide auditable usage displays to your customers using a GET on this endpoint."
},
"amount": {
"type": "number",
"format": "float",
"description": "The amount of usage. Can be positive, negative, or 0. If the Decimal Quantity feature is enabled, this value will be rounded to nine decimal places. Otherwise, all digits after the decimal will be stripped. If the usage-based add-on is billed with a percentage, your usage should be a monetary amount formatted in cents (e.g., $5.00 is \"500\")."
},
"usage_type": {
"type": "string",
"enum": ["price", "percentage"],
"title": "Usage Type",
"description": "Type of usage, returns usage type if `add_on_type` is `usage`."
},
"tier_type": {
"type": "string",
"title": "Tier type",
"description": "The pricing model for the add-on. For more information,\n[click here](https://docs.recurly.com/docs/billing-models#section-quantity-based). See our\n[Guide](https://recurly.com/developers/guides/item-addon-guide.html) for an overview of how\nto configure quantity-based pricing models.\n",
"default": "flat",
"enum": ["flat", "tiered", "stairstep", "volume"]
},
"tiers": {
"type": "array",
"title": "Tiers",
"items": {
"type": "object",
"properties": {
"ending_quantity": {
"type": "integer",
"title": "Ending quantity",
"minimum": 1,
"maximum": 999999999,
"default": null,
"description": "Ending quantity for the tier. This represents a unit amount for unit-priced add ons. Must be left empty if it is the final tier."
},
"unit_amount": {
"type": "number",
"format": "float",
"title": "Unit amount",
"minimum": 0,
"maximum": 1000000,
"description": "Allows up to 2 decimal places. Optionally, override the tiers' default unit amount. If add-on's `add_on_type` is `usage` and `usage_type` is `percentage`, cannot be provided."
},
"unit_amount_decimal": {
"type": "string",
"title": "Unit amount decimal",
"description": "Allows up to 9 decimal places. Optionally, override tiers' default unit amount.\nIf `unit_amount_decimal` is provided, `unit_amount` cannot be provided.\nIf add-on's `add_on_type` is `usage` and `usage_type` is `percentage`, cannot be provided.\n"
},
"usage_percentage": {
"type": "string",
"title": "Usage Percentage",
"description": "(deprecated) -- Use the percentage_tiers object instead.",
"deprecated": true
}
}
},
"description": "The tiers and prices of the subscription based on the usage_timestamp. If tier_type = flat, tiers = []"
},
"percentage_tiers": {
"type": "array",
"title": "Percentage Tiers",
"items": {
"type": "object",
"properties": {
"ending_amount": {
"type": "number",
"format": "float",
"title": "Ending amount",
"minimum": 1,
"maximum": 9999999999999.99,
"default": null,
"description": "Ending amount for the tier. Allows up to 2 decimal places. Must be left empty if it is the final tier."
},
"usage_percentage": {
"type": "string",
"title": "Usage Percentage",
"minimum": 0,
"maximum": 100,
"description": "The percentage taken of the monetary amount of usage tracked.\nThis can be up to 4 decimal places represented as a string.\n"
}
}
},
"description": "The percentage tiers of the subscription based on the usage_timestamp. If tier_type = flat, percentage_tiers = []"
},
"measured_unit_id": {
"type": "string",
"description": "The ID of the measured unit associated with the add-on the usage record is for."
},
"recording_timestamp": {
"type": "string",
"format": "date-time",
"description": "When the usage was recorded in your system."
},
"usage_timestamp": {
"type": "string",
"format": "date-time",
"description": "When the usage actually happened. This will define the line item dates this usage is billed under and is important for revenue recognition."
},
"usage_percentage": {
"type": "number",
"format": "float",
"title": "Usage Percentage",
"description": "The percentage taken of the monetary amount of usage tracked. This can be up to 4 decimal places. A value between 0.0 and 100.0."
},
"unit_amount": {
"type": "number",
"format": "float",
"title": "Unit price"
},
"unit_amount_decimal": {
"type": "string",
"title": "Unit Amount Decimal",
"minimum": 0,
"maximum": 1000000,
"description": "Unit price that can optionally support a sub-cent value."
},
"billed_at": {
"type": "string",
"format": "date-time",
"description": "When the usage record was billed on an invoice."
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "When the usage record was created in Recurly."
},
"updated_at": {
"type": "string",
"format": "date-time",
"description": "When the usage record was billed on an invoice."
}
}
}
23 changes: 22 additions & 1 deletion tap_recurly/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,26 @@ class Transactions(Stream):
key_properties = ["id"]


class Usages(Stream):
name = "usages"
replication_method = "INCREMENTAL"
replication_key = "recording_timestamp"

# Needs its own sync function since it's bookmark is off Subscriptions.
def sync(self, state):
get_parent = getattr(self.client, "subscriptions")
get_child = getattr(self.client, self.name)

# use the specific `parent_child` bookmark.
parents = get_parent("created_at", self.get_bookmark(state, self.name))
for parent in parents:
self.update_bookmark(state, parent["created_at"], self.name)
for add_on in parent["add_ons"]:
if add_on["add_on"]["add_on_type"] == "usage":
res = get_child(parent["id"], add_on["add_on"]["id"], self.replication_key)
for item in res:
yield (self.stream, item)
self.update_bookmark(state, None, self.name)

STREAMS = {
"accounts": Accounts,
Expand All @@ -228,5 +248,6 @@ class Transactions(Stream):
"plans": Plans,
"plans_add_ons": PlansAddOns,
"subscriptions": Subscriptions,
"transactions": Transactions
"transactions": Transactions,
"usages": Usages
}

0 comments on commit 6a82e52

Please sign in to comment.