Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TDL-15168: Use unique_line_item_id for invoice updates' lines value instead of id #134

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions tap_stripe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,42 @@ def sync_sub_stream(sub_stream_name, parent_obj, updates=False):
obj_ad_dict = sub_stream_obj.to_dict_recursive()

if sub_stream_name == "invoice_line_items":
# we will get "unique_id" for default API versions older than "2019-12-03"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a general comment here regarding which field's value moves to another field and all or write one sample example for both records(old and new) with changed field values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment.

# ie. for API version older than "2019-12-03", the value in the field
# "unique_id" is moved to "id" field in the newer API version
# For example:
# OLDER API VERSION
# {
# "id": "ii_testinvoiceitem",
# "object": "line_item",
# "invoice_item": "ii_testinvoiceitem",
# "subscription": "sub_testsubscription",
# "type": "invoiceitem",
# "unique_id": "il_testlineitem"
# }

# NEWER API VERSION
# {
# "id": "il_testlineitem",
# "object": "line_item",
# "invoice_item": "ii_testinvoiceitem",
# "subscription": "sub_testsubscription",
# "type": "invoiceitem",
# }
if updates and obj_ad_dict.get("unique_id"):
# get "unique_id"
object_unique_id = obj_ad_dict.get("unique_id")
# get "id"
object_id = obj_ad_dict.get("id")
# update "id" field with "unique_id" value
obj_ad_dict["id"] = object_unique_id
# if type is invoiceitem, update 'invoice_item' field with 'id' if not present
if obj_ad_dict.get("type") == "invoiceitem" and not obj_ad_dict.get("invoice_item"):
obj_ad_dict["invoice_item"] = object_id
# if type is subscription, update 'subscription' field with 'id' if not present
elif obj_ad_dict.get("type") == "subscription" and not obj_ad_dict.get("subscription"):
obj_ad_dict["subscription"] = object_id

# Synthetic addition of a key to the record we sync
obj_ad_dict["invoice"] = parent_obj.id
elif sub_stream_name == "payout_transactions":
Expand Down
283 changes: 283 additions & 0 deletions tests/unittests/test_invoice_line_item_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
import unittest
from unittest import mock
import tap_stripe

# mock invoice line items
class MockLines:
def __init__(self, data):
self.data = data

def to_dict_recursive(self):
return self.data

# mock invoice
class MockInvoice:
def __init__(self, lines):
self.lines = lines
self.id = "inv_testinvoice"

# mock transform function
def transform(*args, **kwargs):
# return the data with was passed for transformation in the argument
return args[0]

@mock.patch("singer.Transformer.transform")
@mock.patch("tap_stripe.Context.get_catalog_entry")
@mock.patch("tap_stripe.Context.new_counts")
@mock.patch("tap_stripe.Context.updated_counts")
class InvoiceLineItemId(unittest.TestCase):
"""
Test cases to verify the invoice line items 'id' is used as expected when syncing 'event updates'
"""

def test_no_events_updates(self, mocked_new_counts, mocked_updated_counts, mocked_get_catalog_entry, mocked_transform):
"""
Test case to verify no data should be changed when function is not called with 'event updates'
"""
# mock transform
mocked_transform.side_effect = transform
# create line items dummy data
lines = [
MockLines({
"id": "ii_testinvoiceitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem",
"unique_id": "il_testlineitem"
})
]

# function call when 'updates=False'
tap_stripe.sync_sub_stream("invoice_line_items", MockInvoice(lines), False)

# expected data
expected_record = {
"id": "ii_testinvoiceitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem",
"unique_id": "il_testlineitem",
"invoice": "inv_testinvoice"
}
# get args for transform function
args, kwargs = mocked_transform.call_args
# verify the data is not changed as function was not called with updates
self.assertEqual(expected_record, args[0])

def test_no_unique_id(self, mocked_new_counts, mocked_updated_counts, mocked_get_catalog_entry, mocked_transform):
"""
Test case to verify no data should be changed when invoice line item data does not contain 'unique_id'
"""
# mock transform
mocked_transform.side_effect = transform
# create line items dummy data
lines = [
MockLines({
"id": "ii_testinvoiceitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem"
})
]

# function call
tap_stripe.sync_sub_stream("invoice_line_items", MockInvoice(lines), True)

# expected data
expected_record = {
"id": "ii_testinvoiceitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem",
"invoice": "inv_testinvoice"
}
# get args for transform function
args, kwargs = mocked_transform.call_args
# verify the data is not changed as not 'unique_id' is present
self.assertEqual(expected_record, args[0])

def test_no_updates_and_unique_id(self, mocked_new_counts, mocked_updated_counts, mocked_get_catalog_entry, mocked_transform):
"""
Test case to verify no data should be changed when invoice line item data
does not contain 'unique_id' and function is not called with 'event updates'
"""
# mock transform
mocked_transform.side_effect = transform
# create line items dummy data
lines = [
MockLines({
"id": "ii_testinvoiceitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem"
})
]

# function call with 'updates=False'
tap_stripe.sync_sub_stream("invoice_line_items", MockInvoice(lines), False)

# expected data
expected_record = {
"id": "ii_testinvoiceitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem",
"invoice": "inv_testinvoice"
}
# get args for tranform function
args, kwargs = mocked_transform.call_args
# verify the data is not changed as the function was not called with updates and not unique_id is present
self.assertEqual(expected_record, args[0])

def test_invoiceitem_with_invoice_item(self, mocked_new_counts, mocked_updated_counts, mocked_get_catalog_entry, mocked_transform):
"""
Test case to verify 'unique_id' is used as 'id' value when invoice line item type is 'invoiceitem'
"""
# mock transform
mocked_transform.side_effect = transform
# create line items dummy data
lines = [
MockLines({
"id": "ii_testinvoiceitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem",
"unique_id": "il_testlineitem"
})
]

# function call with updates
tap_stripe.sync_sub_stream("invoice_line_items", MockInvoice(lines), True)

# expected data
expected_record = {
"id": "il_testlineitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem",
"unique_id": "il_testlineitem",
"invoice": "inv_testinvoice"
}
# get args for transform function
args, kwargs = mocked_transform.call_args
# verify the unique_id's value is used as 'id'
self.assertEqual(expected_record, args[0])

def test_invoiceitem_without_invoice_item(self, mocked_new_counts, mocked_updated_counts, mocked_get_catalog_entry, mocked_transform):
"""
Test case to verify 'unique_id' is used as 'id' and 'invoice_item' field
contains 'id' value when invoice line item type is 'invoiceitem'
"""
# mock transform
mocked_transform.side_effect = transform
# create line items dummy data
lines = [
MockLines({
"id": "ii_testinvoiceitem",
"object": "line_item",
"invoice_item": None,
"subscription": "sub_testsubscription",
"type": "invoiceitem",
"unique_id": "il_testlineitem"
})
]

# function call with updates
tap_stripe.sync_sub_stream("invoice_line_items", MockInvoice(lines), True)

# expected data
expected_record = {
"id": "il_testlineitem",
"object": "line_item",
"invoice_item": "ii_testinvoiceitem",
"subscription": "sub_testsubscription",
"type": "invoiceitem",
"unique_id": "il_testlineitem",
"invoice": "inv_testinvoice"
}
# get args for transform function
args, kwargs = mocked_transform.call_args
# verify the unique_id's value is used as 'id' and id's value is used as 'invoice_item' value
self.assertEqual(expected_record, args[0])

def test_subscription_without_subscription(self, mocked_new_counts, mocked_updated_counts, mocked_get_catalog_entry, mocked_transform):
"""
Test case to verify 'unique_id' is used as 'id' and 'subscription' field
contains the 'id' value when invoice line item type is 'subscription'
"""
# mock transform
mocked_transform.side_effect = transform
# create line items dummy data
lines = [
MockLines({
"id": "sub_testsubscription",
"object": "line_item",
"subscription": None,
"type": "subscription",
"unique_id": "il_testlineitem",
"unique_line_item_id": "sli_testsublineitem"
})
]

# function call with updates
tap_stripe.sync_sub_stream("invoice_line_items", MockInvoice(lines), True)

# expected data
expected_record = {
"id": "il_testlineitem",
"object": "line_item",
"subscription": "sub_testsubscription",
"type": "subscription",
"unique_id": "il_testlineitem",
"unique_line_item_id": "sli_testsublineitem",
"invoice": "inv_testinvoice"
}
# get args for transform function
args, kwargs = mocked_transform.call_args
# verify the unique_id's value is used as 'id' and id's value is used as 'subscription' value
self.assertEqual(expected_record, args[0])

def test_subscription_with_subscription(self, mocked_new_counts, mocked_updated_counts, mocked_get_catalog_entry, mocked_transform):
"""
Test case to verify 'unique_id' is used as 'id' and 'subscription'
field is not updated when invoice line item type is 'subscription'
"""
# mock transform
mocked_transform.side_effect = transform
# create line items dummy data
lines = [
MockLines({
"id": "sli_1KJvqbDcBSxinnbLvE4qMiJV",
"object": "line_item",
"subscription": "sub_testsubscription",
"type": "subscription",
"unique_id": "il_testlineitem",
"unique_line_item_id": "sli_testsublineitem"
})
]

# function call with updates
tap_stripe.sync_sub_stream("invoice_line_items", MockInvoice(lines), True)

# expected data
expected_record = {
"id": "il_testlineitem",
"object": "line_item",
"subscription": "sub_testsubscription",
"type": "subscription",
"unique_id": "il_testlineitem",
"unique_line_item_id": "sli_testsublineitem",
"invoice": "inv_testinvoice"
}
# get args for transform function
args, kwargs = mocked_transform.call_args
# verify the unique_id's value is used as 'id'
self.assertEqual(expected_record, args[0])