Skip to content

Commit

Permalink
feat: new enterprise transaction data/signals (#347)
Browse files Browse the repository at this point in the history
* Added new enterprise signals `LEDGER_TRANSACTION_CREATED`, `LEDGER_TRANSACTION_COMMITTED`,
  `LEDGER_TRANSACTION_FAILED`, and `LEDGER_TRANSACTION_REVERSED`.
* Added a `UuidAvroSerializer` to serialize uuid fields.
* Added `isort` make target.
  • Loading branch information
iloveagent57 committed Jun 20, 2024
1 parent f3062f2 commit c8fe32e
Show file tree
Hide file tree
Showing 13 changed files with 634 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ requirements/private.txt

# IDA cruft
.idea

# emacs backup files
*~
11 changes: 11 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ Change Log
Unreleased
----------

[9.11.0] - 2024-05-15
---------------------

Added
~~~~~~~

* Added new enterprise signals ``LEDGER_TRANSACTION_CREATED``, ``LEDGER_TRANSACTION_COMMITTED``,
``LEDGER_TRANSACTION_FAILED``, and ``LEDGER_TRANSACTION_REVERSED``.
* Added a ``UuidAvroSerializer`` to serialize uuid fields.
* Added ``isort`` make target.

[9.10.0] - 2024-05-08
---------------------

Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ test-all: quality ## run tests on every supported Python/Django combination

validate: quality test ## run tests and quality checks

isort: ## fix improperly sorted imports
isort test_utils openedx_events manage.py setup.py

selfcheck: ## check that the Makefile is well-formed
@echo "The Makefile is well-formed."

Expand Down
2 changes: 1 addition & 1 deletion openedx_events/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
more information about the project.
"""

__version__ = "9.10.0"
__version__ = "9.11.0"
72 changes: 72 additions & 0 deletions openedx_events/enterprise/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
These attributes follow the form of attr objects specified in OEP-49 data
pattern.
"""
from datetime import datetime
from uuid import UUID

import attr
from opaque_keys.edx.keys import CourseKey


@attr.s(frozen=True)
Expand All @@ -22,3 +25,72 @@ class SubsidyRedemption:
subsidy_identifier = attr.ib(type=str)
content_key = attr.ib(type=str)
lms_user_id = attr.ib(type=int)


@attr.s(frozen=True)
class BaseLedgerTransaction:
"""
Defines the common attributes of the transaction classes below.
"""

uuid = attr.ib(type=UUID)
created = attr.ib(type=datetime)
modified = attr.ib(type=datetime)
idempotency_key = attr.ib(type=str)
quantity = attr.ib(type=int)
state = attr.ib(type=str)


@attr.s(frozen=True)
class LedgerTransactionReversal(BaseLedgerTransaction):
"""
Attributes of an ``openedx_ledger.Reversal`` record.
A ``Reversal`` is a model that represents the "undo-ing" of a ``Transaction`` (see below). It's primarily
used within the domain of edX Enterprise for recording unenrollments and refunds of subsidized
enterprise enrollments.
https://github.com/openedx/openedx-ledger/blob/master/openedx_ledger/models.py
Arguments:
uuid (str): Primary identifier of the record.
created (datetime): When the record was created.
modified (datetime): When the record was last modified.
idempotency_key (str): Client-generated unique value to achieve idempotency of operations.
quantity (int): How many units of value this reversal represents (e.g. USD cents).
state (str): Current lifecyle state of the record, one of (created, pending, committed, failed).
"""


@attr.s(frozen=True)
class LedgerTransaction(BaseLedgerTransaction):
"""
Attributes of an ``openedx_ledger.Transaction`` record.
A ``Transaction`` is a model that represents value moving in or out of a ``Ledger``. It's primarily
used within the domain of edX Enterprise for recording the redemption of subsidized enrollments.
https://github.com/openedx/openedx-ledger/blob/master/openedx_ledger/models.py
Arguments:
uuid (UUID): Primary identifier of the Transaction.
created (datetime): When the record was created.
modified (datetime): When the record was last modified.
idempotency_key (str): Client-generated unique value to achieve idempotency of operations.
quantity (int): How many units of value this transaction represents (e.g. USD cents).
state (str): Current lifecyle state of the record, one of (created, pending, committed, failed).
ledger_uuid (UUID): The primary identifier of this Transaction's ledger object.
subsidy_access_policy_uuid (UUID): The primary identifier of the subsidy access policy for this transaction.
lms_user_id (int): The LMS user id of the user associated with this transaction.
content_key (CourseKey): The course (run) key associated with this transaction.
parent_content_key (str): The parent (just course, not run) key for the course key.
fulfillment_identifier (str): The identifier of the subsidized enrollment record for a learner,
generated durning enrollment.
reversal (LedgerTransactionReversal): Any reversal associated with this transaction.
"""

ledger_uuid = attr.ib(type=UUID)
subsidy_access_policy_uuid = attr.ib(type=UUID)
lms_user_id = attr.ib(type=int)
content_key = attr.ib(type=CourseKey)
parent_content_key = attr.ib(type=str, default=None)
fulfillment_identifier = attr.ib(type=str, default=None)
reversal = attr.ib(type=LedgerTransactionReversal, default=None)
54 changes: 53 additions & 1 deletion openedx_events/enterprise/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
docs/decisions/0003-events-payload.rst
"""

from openedx_events.enterprise.data import SubsidyRedemption
from openedx_events.enterprise.data import LedgerTransaction, SubsidyRedemption
from openedx_events.tooling import OpenEdxPublicSignal

# .. event_type: org.openedx.enterprise.subsidy.redeemed.v1
Expand All @@ -32,3 +32,55 @@
"redemption": SubsidyRedemption,
}
)


# .. event_type: org.openedx.enterprise.subsidy_ledger_transaction.created.v1
# .. event_name: LEDGER_TRANSACTION_CREATED
# .. event_description: emitted when an enterprise ledger transaction is created.
# See: https://github.com/openedx/openedx-ledger/tree/main/docs/decisions
# .. event_data: LedgerTransaction
LEDGER_TRANSACTION_CREATED = OpenEdxPublicSignal(
event_type="org.openedx.enterprise.subsidy_ledger_transaction.created.v1",
data={
"ledger_transaction": LedgerTransaction,
}
)


# .. event_type: org.openedx.enterprise.subsidy_ledger_transaction.committed.v1
# .. event_name: LEDGER_TRANSACTION_COMMITTED
# .. event_description: emitted when an enterprise ledger transaction is committed.
# See: https://github.com/openedx/openedx-ledger/tree/main/docs/decisions
# .. event_data: LedgerTransaction
LEDGER_TRANSACTION_COMMITTED = OpenEdxPublicSignal(
event_type="org.openedx.enterprise.subsidy_ledger_transaction.committed.v1",
data={
"ledger_transaction": LedgerTransaction,
}
)


# .. event_type: org.openedx.enterprise.subsidy_ledger_transaction.failed.v1
# .. event_name: LEDGER_TRANSACTION_FAILED
# .. event_description: emitted when an enterprise ledger transaction fails.
# See: https://github.com/openedx/openedx-ledger/tree/main/docs/decisions
# .. event_data: LedgerTransaction
LEDGER_TRANSACTION_FAILED = OpenEdxPublicSignal(
event_type="org.openedx.enterprise.subsidy_ledger_transaction.failed.v1",
data={
"ledger_transaction": LedgerTransaction,
}
)


# .. event_type: org.openedx.enterprise.subsidy_ledger_transaction.reversed.v1
# .. event_name: LEDGER_TRANSACTION_REVERSED
# .. event_description: emitted when an enterprise ledger transaction is reversed.
# See: https://github.com/openedx/openedx-ledger/tree/main/docs/decisions
# .. event_data: LedgerTransaction
LEDGER_TRANSACTION_REVERSED = OpenEdxPublicSignal(
event_type="org.openedx.enterprise.subsidy_ledger_transaction.reversed.v1",
data={
"ledger_transaction": LedgerTransaction,
}
)
23 changes: 23 additions & 0 deletions openedx_events/event_bus/avro/custom_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""
from abc import ABC, abstractmethod
from datetime import datetime
from uuid import UUID

from ccx_keys.locator import CCXLocator
from opaque_keys.edx.keys import CourseKey, UsageKey
Expand Down Expand Up @@ -149,11 +150,33 @@ def deserialize(data: str):
return LibraryUsageLocatorV2.from_string(data)


class UuidAvroSerializer(BaseCustomTypeAvroSerializer):
"""
CustomTypeAvroSerializer for the UUID class.
https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/avro-format.md#21-type-system-mapping
"""

cls = UUID
field_type = PYTHON_TYPE_TO_AVRO_MAPPING[str]

@staticmethod
def serialize(obj) -> str:
"""Serialize obj into string."""
return str(obj)

@staticmethod
def deserialize(data: str):
"""Deserialize string into obj."""
return UUID(data)


DEFAULT_CUSTOM_SERIALIZERS = [
CourseKeyAvroSerializer,
CcxCourseLocatorAvroSerializer,
DatetimeAvroSerializer,
LibraryLocatorV2AvroSerializer,
LibraryUsageLocatorV2AvroSerializer,
UsageKeyAvroSerializer,
UuidAvroSerializer,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{
"name": "CloudEvent",
"type": "record",
"doc": "Avro Event Format for CloudEvents created with openedx_events/schema",
"fields": [
{
"name": "ledger_transaction",
"type": {
"name": "LedgerTransaction",
"type": "record",
"fields": [
{
"name": "uuid",
"type": "string"
},
{
"name": "created",
"type": "string"
},
{
"name": "modified",
"type": "string"
},
{
"name": "idempotency_key",
"type": "string"
},
{
"name": "quantity",
"type": "long"
},
{
"name": "state",
"type": "string"
},
{
"name": "ledger_uuid",
"type": "string"
},
{
"name": "subsidy_access_policy_uuid",
"type": "string"
},
{
"name": "lms_user_id",
"type": "long"
},
{
"name": "content_key",
"type": "string"
},
{
"name": "parent_content_key",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "fulfillment_identifier",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "reversal",
"type": [
"null",
{
"name": "LedgerTransactionReversal",
"type": "record",
"fields": [
{
"name": "uuid",
"type": "string"
},
{
"name": "created",
"type": "string"
},
{
"name": "modified",
"type": "string"
},
{
"name": "idempotency_key",
"type": "string"
},
{
"name": "quantity",
"type": "long"
},
{
"name": "state",
"type": "string"
}
]
}
],
"default": null
}
]
}
}
],
"namespace": "org.openedx.enterprise.subsidy_ledger_transaction.committed.v1"
}
Loading

0 comments on commit c8fe32e

Please sign in to comment.