-
Notifications
You must be signed in to change notification settings - Fork 4.2k
feat: Add top-level parent logic to Upstream/Dowstream links [FC-0097] #37076
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: Add top-level parent logic to Upstream/Dowstream links [FC-0097] #37076
Conversation
…vel parent in an update
|
Thanks for the pull request, @ChrisChV! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
navinkarkera
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisChV Nice work! This works as expected but I have left some suggestions and a doubt in the tests.
| top_level_keys = result.exclude(id__in=non_top_level_ids).values_list( | ||
| 'top_level_parent_usage_key', flat=True | ||
| ).distinct() | ||
|
|
||
| top_level_objects = ContainerLink.filter_links(**{ | ||
| "downstream_usage_key__in": top_level_keys | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, top_level_* variables should be the ones that are at the top, but here they are the objects that are under some other container.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really. The top_level_parent always represents the highest-level parent at import time, not the direct parent. This code retrieves the top-level parent keys and their objects (containers) and returns them instead of their descendant.
| `use_top_level_parents` is an special filter, replace any result with the top-level parent if exists. | ||
| Example: We have linkA and linkB with top-level parent as linkC and linkD without top-level parent. | ||
| After all other filters: | ||
| Case 1: `use_top_level_parents` is False, the result is [linkA, linkB, linkD] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Case 1: `use_top_level_parents` is False, the result is [linkA, linkB, linkD] | |
| Case 1: `use_top_level_parents` is False, the result is [linkA, linkB, linkC, linkD] |
It should list all components no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated: bc484ec
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisChV Just want to confirm whether linkC will not show up if use_top_level_parents is False?
| ) | ||
| assert response.status_code == 200 | ||
| data = response.json() | ||
| self.assertEqual(data["count"], 3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After making the above suggested change, this increases to 4, which seems correct as we are also making a change to top level subsection in line 1096 no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really. According to the requirements, only the top-level parents should be displayed. Even if all containers and components within a section are updated, only the top-level parent, which is the section, should be displayed. In this test, the top-level parent of the subsection and component is the section. This test checks that only the section is displayed and is not duplicated in the result.
The test fails because the code above is not replacing descendants with their top-level parent, it is adding the subsection to the response.
I added another more simple tests for this: bc484ec#diff-cdab0b43d246b3a18252af57261eefce305c7a0ed93bce4549288127324c5bf3R1085
navinkarkera
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisChV Great work, it is not easy to catch all the edge cases here! 👍
- I tested this: (Played around with multiple containers from library in courses)
- I read through the code
- I checked for accessibility issues
- Includes documentation
|
@ChrisChV One more request, could you update the if item["last_published_at"] and merged[key]["last_published_at"] and item["last_published_at"] > merged[key]["last_published_at"]:I was testing #37008 from @rpenido and I think it would be possible to copy unpublished blocks and this line fails in that case. OR we should not allow users to copy unpublished blocks from library, |
It's a good question. I agree with not allowing users to copy unpublished blocks from libraries. @edschema @sdaitzman What do you think? |
For more context, when we copy/paste a component from a library, we are copying the latest draft version and linking it. |
Each time containers with children were synchronized, a new downstream block was created for each child instead of updating the existing one. This occurred because the upstream_key was incorrectly validated as an Opaquekey against a list of key strings. This was fixed by converting the upstream_key to a string before the verification.
Hi @rpenido @ChrisChV and team, thanks for catching this! Users should not be able to reuse unpublished blocks. This is a good case I don't believe we have accounted for. I made an issue to track disabling copy from the context menu for unpublished blocks here: openedx/frontend-app-authoring#2344 If copying a block from a library that has been published but has draft changes, when pasted, that block should show the latest published version in the course. By this I mean:
|
|
Thanks @edschema! I left a comment on the new issue. |
I'm curious, could you achieve the same effect with an Also, when we get to unlinking (openedx/frontend-app-authoring#2164) we'll need to account for recursively updating the |
|
@bradenmacdonald This and openedx/frontend-app-authoring#2271 are ready for another review I will do this openedx/frontend-app-authoring#2168 (comment) in a follow-up PR. I would like to merge this PR to unlock other tasks. |
bradenmacdonald
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @ChrisChV ! Looking good, just one concern about the name "definition" and then once you've renamed that and @navinkarkera confirms he's tested the final version, I will approve.
@navinkarkera It's ready for another review |
|
@bradenmacdonald @navinkarkera I need some help here. These tests are failing because of this change 633e050#diff-5b6284478c70bbd62aaaf2b39e1d5b4b526214ecf8aea0cea9fefc84fdab79e7R277 Adding the |
|
@ChrisChV Are you talking about this? What I would suggest is that in the child handler, when you go to create the link and find that the parent link doesn't exist yet, then create the parent link synchronously and idempotently (call |
|
@bradenmacdonald It works! Thanks! Those changes are ready for review if you wish to take a look: 6cc840a
@navinkarkera It's ready for a final review |
|
@ChrisChV Glad to hear! I'll let @navinkarkera do a final check and then you can go ahead and merge. If he's not able to review by tomorrow though let me know. |
navinkarkera
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisChV Nice work! Tested this locally with subsections and units.
| `use_top_level_parents` is an special filter, replace any result with the top-level parent if exists. | ||
| Example: We have linkA and linkB with top-level parent as linkC and linkD without top-level parent. | ||
| After all other filters: | ||
| Case 1: `use_top_level_parents` is False, the result is [linkA, linkB, linkD] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisChV Just want to confirm whether linkC will not show up if use_top_level_parents is False?
Yes, I updated the comment. Thanks! 5d806b0 |
|
2U Release Notice: This PR has been deployed to the edX staging environment in preparation for a release to production. |
|
2U Release Notice: This PR has been deployed to the edX production environment. |
openedx#37076) - Adds the `top_level_parent_usage_key` to the `EntityLinkBase` - This field is used to save the top-level parent of a component or container when it is imported into a course. Example: A unit with components imported into a course. The unit is the top-level parent of the components. - Updates the `DownstreamListView` to return the top-level parents instead of downstream child, if this parent exists. - Each time containers with children were synchronized, a new downstream block was created for each child instead of updating the existing one. This occurred because the `upstream_key` was incorrectly validated as an `Opaquekey` against a list of key strings. This was fixed by converting the `upstream_key` to a string before the verification. (see openedx@34cd5a4 and openedx@2964783) - Which edX user roles will this change impact? "Course Author", "Developer".
| top_level_downstream_parent_key = Dict( | ||
| help=( | ||
| "The block key ('block_type@block_id') of the downstream block that is the top-level parent of " | ||
| "this block. This is present if the creation of this block is a consequence of " | ||
| "importing a container that has one or more levels of children. " | ||
| "This represents the parent (container) in the top level " | ||
| "at the moment of the import." | ||
| ), | ||
| default=None, scope=Scope.settings, hidden=True, enforce_type=True, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dicts serialize to OLX in a very hard-to-read way:
<problem top_level_downstream_parent_key="{"type": "html", "id": "my-id"}">...</problem>This field could instead be a String, joined with a :, like this:
<problem top_level_downstream_parent_key="html:my-id">...</problem>@ChrisChV @bradenmacdonald Do you think it'd be worth making this change now? After the Ulmo cutoff, we're locked in to the Dict field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I would much prefer something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kdmccormick @bradenmacdonald Created a PR: #37448 which converts this field to a string in this format html@my-id.
Description
top_level_parent_usage_keyto theEntityLinkBaseDownstreamListViewto return the top-level parents instead of downstream child, if this parent exists.upstream_keywas incorrectly validated as anOpaquekeyagainst a list of key strings. This was fixed by converting theupstream_keyto a string before the verification. (see 34cd5a4 and 2964783)Supporting information
Testing instructions
Deadline
None
Other information