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

feat: new enterprise transaction data/signals #347

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
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

mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved
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.
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved

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(
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved
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):
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved
"""
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