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

docs: add open edx event guide #80

Merged
merged 23 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0aeb2c5
fix: remove sphinx-book-theme incompatible requirement
mariajgrimaldi Jan 18, 2023
120435b
fix: use pinned version of sphinx-book-theme
mariajgrimaldi Jan 18, 2023
52e594f
docs: add draft of create a new event guide
mariajgrimaldi Jul 20, 2022
49093c9
fix: address PR comments
mariajgrimaldi Aug 12, 2022
625f691
fix: use correct indentation for code
mariajgrimaldi Aug 12, 2022
ffc2814
fix: remove enterprise (unsure of its current usage)
mariajgrimaldi Aug 12, 2022
ec12dd9
fix: add example of the package names
mariajgrimaldi Aug 12, 2022
62d6430
fix: add missing line in code blocks
mariajgrimaldi Aug 12, 2022
34d57a9
refactor: add new docs structure
mariajgrimaldi Aug 30, 2022
9457b56
feat: add index files to subfolders
mariajgrimaldi Feb 15, 2023
4d73e19
docs: add latest documentation
mariajgrimaldi Feb 15, 2023
b07bba3
refactor: address PR reviews
mariajgrimaldi Feb 15, 2023
5f8da09
refactor: add latest information
mariajgrimaldi Feb 15, 2023
bd0195b
docs: add documentation to correct indexes
mariajgrimaldi Feb 16, 2023
758060c
fix: add title to event bus documentation
mariajgrimaldi Feb 28, 2023
9c5f6fe
docs: add references for openedx-events
mariajgrimaldi Mar 27, 2023
8d5ebe4
fix: move info to the correct file
mariajgrimaldi Mar 27, 2023
63c8bd2
docs: remove unused subdomains
mariajgrimaldi Mar 27, 2023
9a3e83f
fix: address PR reviews
mariajgrimaldi Mar 28, 2023
3553c61
docs: add warning encouraging tasks
mariajgrimaldi Mar 28, 2023
87cf1d7
docs: add warning when using events to call webhooks
mariajgrimaldi Mar 28, 2023
c269620
fix: fix quality issues with sphinx
mariajgrimaldi Apr 4, 2023
2aa0e05
docs: address PR reviews
mariajgrimaldi Apr 4, 2023
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
45 changes: 45 additions & 0 deletions docs/concepts/hooks-extension-framework.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
Openedx Hooks Extension Framework
=================================

To sustain the growth of the Open edX ecosystem, the business rules of the
platform must be open for extension following the open-closed principle. This
framework allows developers to do just that without needing to fork and modify
the main Open edX platform.

Context
-------

Hooks are predefined places in the Open edX project core where externally defined
functions can take place. In some cases, those functions can alter what the user
sees or experiences in the platform. Other cases are informative only. All cases
are meant to be extended using Open edX plugins and configuration.

Hooks can be of two types, events and filters. Events are in essence signals, in
that they are sent in specific application places and whose listeners can extend
functionality. functionality. On the other hand Filters can be used to act on data before
it is put back in the original application flow. In order to allow
extension developers to use the Events and Filters definitions on their plugins,
both kinds of hooks are defined in lightweight external libraries.

* `openedx-filters`_
* `openedx-events`_

Hooks are designed with stability in mind. The main goal is that developers can
use them to change the functionality of the platform as needed and still be able
to migrate to newer open releases with very little to no development effort. In
the case of the events, this is detailed in the `versioning ADR`_ and the
`payload ADR`_.

A longer description of the framework and it's history can be found in `OEP 50`_.
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved

.. _OEP 50: https://open-edx-proposals.readthedocs.io/en/latest/oep-0050-hooks-extension-framework.html
.. _versioning ADR: https://github.com/eduNEXT/openedx-events/blob/main/docs/decisions/0002-events-naming-and-versioning.rst
.. _payload ADR: https://github.com/eduNEXT/openedx-events/blob/main/docs/decisions/0003-events-payload.rst
.. _openedx-filters: https://github.com/eduNEXT/openedx-filters
.. _openedx-events: https://github.com/eduNEXT/openedx-events

On the technical side events are implemented through django signals which makes
them run in the same python process as the service where this library is installed.
Furthermore, events block the running process. Listeners of an event are encouraged
to monitor the performance or use alternative arch patterns such as receiving the
event and defer to launching async tasks than do the slow processing.
6 changes: 6 additions & 0 deletions docs/concepts/index.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
Concepts
========

.. toctree::
:maxdepth: 1
:caption: Contents:

hooks-extension-framework
2 changes: 1 addition & 1 deletion docs/decisions/0001-purpose-of-this-repo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Decision
--------

In this repository will reside the signals that define the events used by the
edx-platform repo. The same applies to the necessary tooling used by the Hooks
Open edX project. The same applies to the necessary tooling used by the Hooks
Extension Framework to manage the events execution and extra tools.

Consequences
Expand Down
4 changes: 2 additions & 2 deletions docs/decisions/0002-events-naming-and-versioning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ Consequences
major versions of this library. Also there will not be a need to make a major
release if there is no breaking for consecutive Open edX releases.

2. Open edX core and in particular edx-platform must emit the signals meant for
public consumption as they are written in this library, changes in edx-platform
2. The Open edX platform must emit the signals meant for
public consumption as they are written in this library, changes in the platform
that require changes in the public signal will require a backwards compatible
addition to this library or an altogether new signal with support for the old
signal until deprecated and removed.
Expand Down
6 changes: 3 additions & 3 deletions docs/decisions/0003-events-payload.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Given their public promise status, event hooks have maintainability as the main
design goal. The contracts we are creating here should be stable enough to
support the growth of the extensions community. That said, things should be
allowed to evolve in a backwards compatible manner. When things inevitable break,
they should break in CI. Which should not require the code of edx-platform to
they should break in CI. Which should not require the code of The Open edX platform to
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved
test integrations.


Expand Down Expand Up @@ -46,7 +46,7 @@ Consequences
------------

1. Extension developers will be able to test their event listeners without the
need to import any edx-platform code.
need to import any Open edX platform code.

2. Consequence of the versioning ADR together with this one, extension developers
will be able to test their code with different versions of the library and thus
Expand All @@ -55,5 +55,5 @@ guarantee that their code will not break when upgrading open releases.
3. The events library will have a dependency on the OpaqueKeys library.

4. Events defined by this library will not be drop-in replacement of current
edx-platform signals. This means some refactoring will be needed when converting
Open edX platform signals. This means some refactoring will be needed when converting
the platform code over to openedx_events.
4 changes: 2 additions & 2 deletions docs/decisions/index.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Decisions
=========
Architectural Decision Records (ADRs)
#####################################

.. toctree::
:maxdepth: 1
Expand Down
39 changes: 39 additions & 0 deletions docs/how-tos/adding-events-to-a-service.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
How to add an Open edX Event to a service
=========================================

The next step after creating your first event in the Open edX Events library, it's to trigger the event in the service
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved
you implemented it for. Here is a checklist of what we've done so far when including a new event to a service:

- Add the openedx-events library to the service project.
- Import the events' data and definition into the place where the event be triggered. Remember the event's purpose when
choosing a place to send the new event.
- Add inline documentation with the ``event_implemented_name``. This matches the ``event_name`` in line documentation from the library.
- Refer to the service project's contribution guidelines and follow the instructions. Then, open a new pull request!

Consider the addition of the event ``STUDENT_REGISTRATION_COMPLETED`` to edx-platform as an example:

.. code-block:: python

# Location openedx/core/djangoapps/user_authn/views/register.py
# .. event_implemented_name: COURSE_ENROLLMENT_CREATED
COURSE_ENROLLMENT_CREATED.send_event(
enrollment=CourseEnrollmentData(
user=UserData(
pii=UserPersonalData(
username=user.username,
email=user.email,
name=user.profile.name,
),
id=user.id,
is_active=user.is_active,
),
course=course_data,
mode=enrollment.mode,
is_active=enrollment.is_active,
creation_date=enrollment.created,
)
)

If you want to know more about how the integration of the first events' batch went, check out the `PR 28266`_.

.. _PR 28266: https://github.com/openedx/edx-platform/pull/28266
14 changes: 14 additions & 0 deletions docs/how-tos/adding-events-to-event-bus.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Using the Open edX Event bus
============================

After creating a new Open edX Event, you might need to send it across services
instead of just within the same process. For this kind of use-cases, you might want
to use the Open edX Event Bus. Here, we list useful information about
adding a new event to the event bus:

- `How to start using the Event Bus`_
- `Sample pull request adding new Open edX Events to the Event Bus`


.. _How to start using the Event Bus: https://openedx.atlassian.net/wiki/spaces/AC/pages/3508699151/How+to+start+using+the+Event+Bus
.. _Sample pull request adding new Open edX Events to the Event Bus: https://github.com/openedx/edx-platform/pull/31350
142 changes: 142 additions & 0 deletions docs/how-tos/creating-new-events.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
How to create a new Open edX Event
==================================

The mechanisms implemented by the Open edX Events library are supported and maintained by the Open edX community.
Therefore, we've put together a guide on how to add a new event to the library so future contributions are effective.


1. Propose the new event to the community
robrap marked this conversation as resolved.
Show resolved Hide resolved
-----------------------------------------

When creating a new event, you must justify its implementation. For example, you could create a post in Discuss,
send a message through slack or open a new issue in the library repository listing your use cases for it. Or even,
if you have time, you could accompany your proposal with the implementation of the event to illustrate its behavior.

2. Place your event in an architecture subdomain
-------------------------------------------------

As specified in the Architectural Decisions Record (ADR) events naming and versioning, the event definition needs an Open edX Architecture
Subdomain for:

- The name of the event: ``{Reverse DNS}.{Architecture Subdomain}.{Subject}.{Action}.{Major Version}``
- The package name where the definition will live, eg. ``learning/`` or ``content_authoring/``.

For those reasons, after studying your new event purpose, you must place it in one of the subdomains already in use, or introduce a new subdomain:

+------------------+----------------------------------------------------------------------------------------------------+
| Subdomain name | Description |
+==================+====================================================================================================+
| Course Authoring | Allows educators to create, modify, package, annotate (tag), and share learning content. |
robrap marked this conversation as resolved.
Show resolved Hide resolved
+----------------- +----------------------------------------------------------------------------------------------------+
| Learning | Allows learners to consume content and perform actions in a learning activity on the platform. |
+------------------+----------------------------------------------------------------------------------------------------+

New subdomains may require some discussion, because there does not yet exist and agreed upon set on subdomains. So we encourage you to start the conversation
as soon as possible through any of the communication channels available.

Refer to `edX DDD Bounded Contexts <https://openedx.atlassian.net/l/cp/vf8XjRiX>`_ confluence page for more documentation on domain-driven design in the Open edX project.

3. Create the data attributes for the event (OEP-49)
----------------------------------------------------

Events send `data attributes <https://open-edx-proposals.readthedocs.io/en/latest/architectural-decisions/oep-0049-django-app-patterns.html#data-py>`_ when triggered. Therefore, when designing your new event definition you must
decide if an existent data class works for your use case or you must create a new one. If the answer is the latter, then try to answer:

- Which attributes of the object are the most relevant?
- Which type are they?
- Are any of them optional/required?

And with that information, create the new class justifying each decision. The class created in this step must comply
with:

- It should be created in the `data.py` file, as described in the OEP-49, in the corresponding architectural subdomain. Refer to Naming Conventions ADR for more
on events subdomains.
- It should follow the naming conventions used across the other events definitions.

Consider the user data representation as an example:

.. code-block:: python

@attr.s(frozen=True)
class CourseData:
"""
Attributes defined for Open edX Course Overview object.

Arguments:
course_key (str): identifier of the Course object.
display_name (str): display name associated with the course.
start (datetime): start date for the course.
end (datetime): end date for the course.
"""

course_key = attr.ib(type=CourseKey)
display_name = attr.ib(type=str, factory=str)
start = attr.ib(type=datetime, default=None)
end = attr.ib(type=datetime, default=None)


@attr.s(frozen=True)
class CourseEnrollmentData:
"""
Attributes defined for Open edX Course Enrollment object.

Arguments:
user (UserData): user associated with the Course Enrollment.
course (CourseData): course where the user is enrolled in.
mode (str): course mode associated with the course.
is_active (bool): whether the enrollment is active.
creation_date (datetime): creation date of the enrollment.
created_by (UserData): if available, who created the enrollment.
"""

user = attr.ib(type=UserData)
course = attr.ib(type=CourseData)
mode = attr.ib(type=str)
is_active = attr.ib(type=bool)
creation_date = attr.ib(type=datetime)
created_by = attr.ib(type=UserData, default=None)

4. Create the event definition
------------------------------

Open edX Events are instances of the class OpenEdxPublicSignal, this instance represents the event definition that
specifies:

- The event type which should follow the conventions in the Naming Conventions ADR.
- The events' payload, here you must use the class you decided on before.

The definition created in this step must comply with:

- It should be created in the `signals.py` file in the corresponding subdomain. Refer to Naming Conventions ADR for more
on events subdomains.
- It should follow the naming conventions specified in Naming Conventions ADR.
- It must be documented using in-line documentation with at least: `event_type`, `event_name`, `event_description` and
`event_data`:

+-------------------+----------------------------------------------------------------------------------------------------+
| Annotation | Description |
+===================+====================================================================================================+
| event_type | Identifier across services of the event. Should follow the events naming conventions. |
+-------------------+----------------------------------------------------------------------------------------------------+
| event_name | Name of the variable storing the event instance. |
+-------------------+----------------------------------------------------------------------------------------------------+
| event_description | General description which includes when the event should be emitted. |
+-------------------+----------------------------------------------------------------------------------------------------+
| event_data | What type of class attribute the event sends. |
+-------------------+----------------------------------------------------------------------------------------------------+

Consider the following example:

.. code-block:: python

# Location openedx_events/learning/signals.py
# .. event_type: org.openedx.learning.course.enrollment.created.v1
# .. event_name: COURSE_ENROLLMENT_CREATED
# .. event_description: emitted when the user's enrollment process is completed.
# .. event_data: CourseEnrollmentData
COURSE_ENROLLMENT_CREATED = OpenEdxPublicSignal(
event_type="org.openedx.learning.course.enrollment.created.v1",
data={
"enrollment": CourseEnrollmentData,
}
)
9 changes: 9 additions & 0 deletions docs/how-tos/index.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
How-tos
#######

.. toctree::
:maxdepth: 1
:caption: Contents:

creating-new-events
adding-events-to-a-service
adding-events-to-event-bus
using-events
Loading